summaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
authorGravatar bunnei2017-10-10 17:32:14 -0400
committerGravatar bunnei2017-10-10 17:32:14 -0400
commit0906de9a14b735d1d409290ca050eb7d2c2d3d84 (patch)
tree79bb57d3a4dc4ca377e7a62744c3941de29e785b /src/core
parentMerge remote-tracking branch 'upstream/master' into nx (diff)
downloadyuzu-0906de9a14b735d1d409290ca050eb7d2c2d3d84.tar.gz
yuzu-0906de9a14b735d1d409290ca050eb7d2c2d3d84.tar.xz
yuzu-0906de9a14b735d1d409290ca050eb7d2c2d3d84.zip
hle: Remove a large amount of 3ds-specific service code.
Diffstat (limited to 'src/core')
-rw-r--r--src/core/CMakeLists.txt190
-rw-r--r--src/core/frontend/camera/blank_camera.cpp31
-rw-r--r--src/core/frontend/camera/blank_camera.h28
-rw-r--r--src/core/frontend/camera/factory.cpp32
-rw-r--r--src/core/frontend/camera/factory.h41
-rw-r--r--src/core/frontend/camera/interface.cpp11
-rw-r--r--src/core/frontend/camera/interface.h61
-rw-r--r--src/core/hle/applets/applet.cpp130
-rw-r--r--src/core/hle/applets/applet.h83
-rw-r--r--src/core/hle/applets/erreula.cpp72
-rw-r--r--src/core/hle/applets/erreula.h29
-rw-r--r--src/core/hle/applets/mii_selector.cpp86
-rw-r--r--src/core/hle/applets/mii_selector.h78
-rw-r--r--src/core/hle/applets/mint.cpp72
-rw-r--r--src/core/hle/applets/mint.h29
-rw-r--r--src/core/hle/applets/swkbd.cpp118
-rw-r--r--src/core/hle/applets/swkbd.h85
-rw-r--r--src/core/hle/service/ac/ac.cpp186
-rw-r--r--src/core/hle/service/ac/ac.h134
-rw-r--r--src/core/hle/service/ac/ac_i.cpp39
-rw-r--r--src/core/hle/service/ac/ac_i.h22
-rw-r--r--src/core/hle/service/ac/ac_u.cpp39
-rw-r--r--src/core/hle/service/ac/ac_u.h22
-rw-r--r--src/core/hle/service/act/act.cpp18
-rw-r--r--src/core/hle/service/act/act.h14
-rw-r--r--src/core/hle/service/act/act_a.cpp30
-rw-r--r--src/core/hle/service/act/act_a.h22
-rw-r--r--src/core/hle/service/act/act_u.cpp26
-rw-r--r--src/core/hle/service/act/act_u.h22
-rw-r--r--src/core/hle/service/am/am.cpp193
-rw-r--r--src/core/hle/service/am/am.h164
-rw-r--r--src/core/hle/service/am/am_app.cpp32
-rw-r--r--src/core/hle/service/am/am_app.h22
-rw-r--r--src/core/hle/service/am/am_net.cpp129
-rw-r--r--src/core/hle/service/am/am_net.h22
-rw-r--r--src/core/hle/service/am/am_sys.cpp77
-rw-r--r--src/core/hle/service/am/am_sys.h22
-rw-r--r--src/core/hle/service/am/am_u.cpp89
-rw-r--r--src/core/hle/service/am/am_u.h22
-rw-r--r--src/core/hle/service/apt/apt.cpp1124
-rw-r--r--src/core/hle/service/apt/apt.h533
-rw-r--r--src/core/hle/service/apt/apt_a.cpp110
-rw-r--r--src/core/hle/service/apt/apt_a.h22
-rw-r--r--src/core/hle/service/apt/apt_s.cpp110
-rw-r--r--src/core/hle/service/apt/apt_s.h29
-rw-r--r--src/core/hle/service/apt/apt_u.cpp107
-rw-r--r--src/core/hle/service/apt/apt_u.h29
-rw-r--r--src/core/hle/service/apt/bcfnt/bcfnt.cpp110
-rw-r--r--src/core/hle/service/apt/bcfnt/bcfnt.h92
-rw-r--r--src/core/hle/service/boss/boss.cpp994
-rw-r--r--src/core/hle/service/boss/boss.h802
-rw-r--r--src/core/hle/service/boss/boss_p.cpp86
-rw-r--r--src/core/hle/service/boss/boss_p.h22
-rw-r--r--src/core/hle/service/boss/boss_u.cpp74
-rw-r--r--src/core/hle/service/boss/boss_u.h22
-rw-r--r--src/core/hle/service/cam/cam.cpp1106
-rw-r--r--src/core/hle/service/cam/cam.h686
-rw-r--r--src/core/hle/service/cam/cam_c.cpp18
-rw-r--r--src/core/hle/service/cam/cam_c.h22
-rw-r--r--src/core/hle/service/cam/cam_q.cpp18
-rw-r--r--src/core/hle/service/cam/cam_q.h22
-rw-r--r--src/core/hle/service/cam/cam_s.cpp18
-rw-r--r--src/core/hle/service/cam/cam_s.h22
-rw-r--r--src/core/hle/service/cam/cam_u.cpp81
-rw-r--r--src/core/hle/service/cam/cam_u.h22
-rw-r--r--src/core/hle/service/cecd/cecd.cpp66
-rw-r--r--src/core/hle/service/cecd/cecd.h60
-rw-r--r--src/core/hle/service/cecd/cecd_ndm.cpp23
-rw-r--r--src/core/hle/service/cecd/cecd_ndm.h22
-rw-r--r--src/core/hle/service/cecd/cecd_s.cpp36
-rw-r--r--src/core/hle/service/cecd/cecd_s.h22
-rw-r--r--src/core/hle/service/cecd/cecd_u.cpp36
-rw-r--r--src/core/hle/service/cecd/cecd_u.h22
-rw-r--r--src/core/hle/service/csnd_snd.cpp143
-rw-r--r--src/core/hle/service/csnd_snd.h22
-rw-r--r--src/core/hle/service/dlp/dlp.cpp23
-rw-r--r--src/core/hle/service/dlp/dlp.h17
-rw-r--r--src/core/hle/service/dlp/dlp_clnt.cpp38
-rw-r--r--src/core/hle/service/dlp/dlp_clnt.h22
-rw-r--r--src/core/hle/service/dlp/dlp_fkcl.cpp35
-rw-r--r--src/core/hle/service/dlp/dlp_fkcl.h22
-rw-r--r--src/core/hle/service/dlp/dlp_srvr.cpp47
-rw-r--r--src/core/hle/service/dlp/dlp_srvr.h22
-rw-r--r--src/core/hle/service/err_f.cpp266
-rw-r--r--src/core/hle/service/err_f.h22
-rw-r--r--src/core/hle/service/frd/frd.cpp174
-rw-r--r--src/core/hle/service/frd/frd.h127
-rw-r--r--src/core/hle/service/frd/frd_a.cpp18
-rw-r--r--src/core/hle/service/frd/frd_a.h22
-rw-r--r--src/core/hle/service/frd/frd_u.cpp72
-rw-r--r--src/core/hle/service/frd/frd_u.h22
-rw-r--r--src/core/hle/service/http_c.cpp72
-rw-r--r--src/core/hle/service/http_c.h22
-rw-r--r--src/core/hle/service/ir/extra_hid.cpp231
-rw-r--r--src/core/hle/service/ir/extra_hid.h48
-rw-r--r--src/core/hle/service/ir/ir.cpp35
-rw-r--r--src/core/hle/service/ir/ir.h23
-rw-r--r--src/core/hle/service/ir/ir_rst.cpp221
-rw-r--r--src/core/hle/service/ir/ir_rst.h28
-rw-r--r--src/core/hle/service/ir/ir_u.cpp38
-rw-r--r--src/core/hle/service/ir/ir_u.h22
-rw-r--r--src/core/hle/service/ir/ir_user.cpp558
-rw-r--r--src/core/hle/service/ir/ir_user.h58
-rw-r--r--src/core/hle/service/ldr_ro/cro_helper.cpp1495
-rw-r--r--src/core/hle/service/ldr_ro/cro_helper.h714
-rw-r--r--src/core/hle/service/ldr_ro/ldr_ro.cpp718
-rw-r--r--src/core/hle/service/ldr_ro/ldr_ro.h22
-rw-r--r--src/core/hle/service/ldr_ro/memory_synchronizer.cpp42
-rw-r--r--src/core/hle/service/ldr_ro/memory_synchronizer.h42
-rw-r--r--src/core/hle/service/mic_u.cpp346
-rw-r--r--src/core/hle/service/mic_u.h23
-rw-r--r--src/core/hle/service/mvd/mvd.cpp17
-rw-r--r--src/core/hle/service/mvd/mvd.h14
-rw-r--r--src/core/hle/service/mvd/mvd_std.cpp32
-rw-r--r--src/core/hle/service/mvd/mvd_std.h22
-rw-r--r--src/core/hle/service/ndm/ndm.cpp239
-rw-r--r--src/core/hle/service/ndm/ndm.h251
-rw-r--r--src/core/hle/service/ndm/ndm_u.cpp42
-rw-r--r--src/core/hle/service/ndm/ndm_u.h22
-rw-r--r--src/core/hle/service/news/news.cpp25
-rw-r--r--src/core/hle/service/news/news.h17
-rw-r--r--src/core/hle/service/news/news_s.cpp32
-rw-r--r--src/core/hle/service/news/news_s.h22
-rw-r--r--src/core/hle/service/news/news_u.cpp19
-rw-r--r--src/core/hle/service/news/news_u.h22
-rw-r--r--src/core/hle/service/nfc/nfc.cpp145
-rw-r--r--src/core/hle/service/nfc/nfc.h153
-rw-r--r--src/core/hle/service/nfc/nfc_m.cpp47
-rw-r--r--src/core/hle/service/nfc/nfc_m.h22
-rw-r--r--src/core/hle/service/nfc/nfc_u.cpp44
-rw-r--r--src/core/hle/service/nfc/nfc_u.h22
-rw-r--r--src/core/hle/service/nim/nim.cpp54
-rw-r--r--src/core/hle/service/nim/nim.h41
-rw-r--r--src/core/hle/service/nim/nim_aoc.cpp26
-rw-r--r--src/core/hle/service/nim/nim_aoc.h22
-rw-r--r--src/core/hle/service/nim/nim_s.cpp23
-rw-r--r--src/core/hle/service/nim/nim_s.h22
-rw-r--r--src/core/hle/service/nim/nim_u.cpp26
-rw-r--r--src/core/hle/service/nim/nim_u.h22
-rw-r--r--src/core/hle/service/nwm/nwm.cpp28
-rw-r--r--src/core/hle/service/nwm/nwm.h14
-rw-r--r--src/core/hle/service/nwm/nwm_cec.cpp19
-rw-r--r--src/core/hle/service/nwm/nwm_cec.h22
-rw-r--r--src/core/hle/service/nwm/nwm_ext.cpp19
-rw-r--r--src/core/hle/service/nwm/nwm_ext.h22
-rw-r--r--src/core/hle/service/nwm/nwm_inf.cpp21
-rw-r--r--src/core/hle/service/nwm/nwm_inf.h22
-rw-r--r--src/core/hle/service/nwm/nwm_sap.cpp20
-rw-r--r--src/core/hle/service/nwm/nwm_sap.h22
-rw-r--r--src/core/hle/service/nwm/nwm_soc.cpp20
-rw-r--r--src/core/hle/service/nwm/nwm_soc.h22
-rw-r--r--src/core/hle/service/nwm/nwm_tst.cpp20
-rw-r--r--src/core/hle/service/nwm/nwm_tst.h22
-rw-r--r--src/core/hle/service/nwm/nwm_uds.cpp1035
-rw-r--r--src/core/hle/service/nwm/nwm_uds.h111
-rw-r--r--src/core/hle/service/nwm/uds_beacon.cpp329
-rw-r--r--src/core/hle/service/nwm/uds_beacon.h140
-rw-r--r--src/core/hle/service/nwm/uds_connection.cpp88
-rw-r--r--src/core/hle/service/nwm/uds_connection.h56
-rw-r--r--src/core/hle/service/nwm/uds_data.cpp373
-rw-r--r--src/core/hle/service/nwm/uds_data.h164
-rw-r--r--src/core/hle/service/pm_app.cpp33
-rw-r--r--src/core/hle/service/pm_app.h22
-rw-r--r--src/core/hle/service/ptm/ptm.cpp166
-rw-r--r--src/core/hle/service/ptm/ptm.h118
-rw-r--r--src/core/hle/service/ptm/ptm_gets.cpp37
-rw-r--r--src/core/hle/service/ptm/ptm_gets.h22
-rw-r--r--src/core/hle/service/ptm/ptm_play.cpp40
-rw-r--r--src/core/hle/service/ptm/ptm_play.h22
-rw-r--r--src/core/hle/service/ptm/ptm_sets.cpp20
-rw-r--r--src/core/hle/service/ptm/ptm_sets.h22
-rw-r--r--src/core/hle/service/ptm/ptm_sysm.cpp71
-rw-r--r--src/core/hle/service/ptm/ptm_sysm.h31
-rw-r--r--src/core/hle/service/ptm/ptm_u.cpp34
-rw-r--r--src/core/hle/service/ptm/ptm_u.h22
-rw-r--r--src/core/hle/service/qtm/qtm.cpp21
-rw-r--r--src/core/hle/service/qtm/qtm.h14
-rw-r--r--src/core/hle/service/qtm/qtm_s.cpp23
-rw-r--r--src/core/hle/service/qtm/qtm_s.h22
-rw-r--r--src/core/hle/service/qtm/qtm_sp.cpp23
-rw-r--r--src/core/hle/service/qtm/qtm_sp.h22
-rw-r--r--src/core/hle/service/qtm/qtm_u.cpp23
-rw-r--r--src/core/hle/service/qtm/qtm_u.h22
-rw-r--r--src/core/hle/service/service.cpp72
-rw-r--r--src/core/hle/service/soc_u.cpp919
-rw-r--r--src/core/hle/service/soc_u.h24
-rw-r--r--src/core/hle/service/ssl_c.cpp92
-rw-r--r--src/core/hle/service/ssl_c.h22
-rw-r--r--src/core/hle/service/y2r_u.cpp787
-rw-r--r--src/core/hle/service/y2r_u.h139
-rw-r--r--src/core/hle/shared_page.cpp3
-rw-r--r--src/core/hw/y2r.cpp382
-rw-r--r--src/core/hw/y2r.h17
-rw-r--r--src/core/settings.cpp2
-rw-r--r--src/core/settings.h5
195 files changed, 2 insertions, 22288 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 8b25eaf0a..fa0379946 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -32,18 +32,10 @@ set(SRCS
32 file_sys/path_parser.cpp 32 file_sys/path_parser.cpp
33 file_sys/savedata_archive.cpp 33 file_sys/savedata_archive.cpp
34 file_sys/title_metadata.cpp 34 file_sys/title_metadata.cpp
35 frontend/camera/blank_camera.cpp
36 frontend/camera/factory.cpp
37 frontend/camera/interface.cpp
38 frontend/emu_window.cpp 35 frontend/emu_window.cpp
39 frontend/framebuffer_layout.cpp 36 frontend/framebuffer_layout.cpp
40 gdbstub/gdbstub.cpp 37 gdbstub/gdbstub.cpp
41 hle/config_mem.cpp 38 hle/config_mem.cpp
42 hle/applets/applet.cpp
43 hle/applets/erreula.cpp
44 hle/applets/mii_selector.cpp
45 hle/applets/mint.cpp
46 hle/applets/swkbd.cpp
47 hle/kernel/address_arbiter.cpp 39 hle/kernel/address_arbiter.cpp
48 hle/kernel/client_port.cpp 40 hle/kernel/client_port.cpp
49 hle/kernel/client_session.cpp 41 hle/kernel/client_session.cpp
@@ -65,49 +57,12 @@ set(SRCS
65 hle/kernel/wait_object.cpp 57 hle/kernel/wait_object.cpp
66 hle/lock.cpp 58 hle/lock.cpp
67 hle/romfs.cpp 59 hle/romfs.cpp
68 hle/service/ac/ac.cpp
69 hle/service/ac/ac_i.cpp
70 hle/service/ac/ac_u.cpp
71 hle/service/act/act.cpp
72 hle/service/act/act_a.cpp
73 hle/service/act/act_u.cpp
74 hle/service/am/am.cpp
75 hle/service/am/am_app.cpp
76 hle/service/am/am_net.cpp
77 hle/service/am/am_sys.cpp
78 hle/service/am/am_u.cpp
79 hle/service/apt/apt.cpp
80 hle/service/apt/apt_a.cpp
81 hle/service/apt/apt_s.cpp
82 hle/service/apt/apt_u.cpp
83 hle/service/apt/bcfnt/bcfnt.cpp
84 hle/service/boss/boss.cpp
85 hle/service/boss/boss_p.cpp
86 hle/service/boss/boss_u.cpp
87 hle/service/cam/cam.cpp
88 hle/service/cam/cam_c.cpp
89 hle/service/cam/cam_q.cpp
90 hle/service/cam/cam_s.cpp
91 hle/service/cam/cam_u.cpp
92 hle/service/cecd/cecd.cpp
93 hle/service/cecd/cecd_ndm.cpp
94 hle/service/cecd/cecd_s.cpp
95 hle/service/cecd/cecd_u.cpp
96 hle/service/cfg/cfg.cpp 60 hle/service/cfg/cfg.cpp
97 hle/service/cfg/cfg_i.cpp 61 hle/service/cfg/cfg_i.cpp
98 hle/service/cfg/cfg_nor.cpp 62 hle/service/cfg/cfg_nor.cpp
99 hle/service/cfg/cfg_s.cpp 63 hle/service/cfg/cfg_s.cpp
100 hle/service/cfg/cfg_u.cpp 64 hle/service/cfg/cfg_u.cpp
101 hle/service/csnd_snd.cpp
102 hle/service/dlp/dlp.cpp
103 hle/service/dlp/dlp_clnt.cpp
104 hle/service/dlp/dlp_fkcl.cpp
105 hle/service/dlp/dlp_srvr.cpp
106 hle/service/dsp_dsp.cpp 65 hle/service/dsp_dsp.cpp
107 hle/service/err_f.cpp
108 hle/service/frd/frd.cpp
109 hle/service/frd/frd_a.cpp
110 hle/service/frd/frd_u.cpp
111 hle/service/fs/archive.cpp 66 hle/service/fs/archive.cpp
112 hle/service/fs/fs_user.cpp 67 hle/service/fs/fs_user.cpp
113 hle/service/gsp_gpu.cpp 68 hle/service/gsp_gpu.cpp
@@ -115,60 +70,11 @@ set(SRCS
115 hle/service/hid/hid.cpp 70 hle/service/hid/hid.cpp
116 hle/service/hid/hid_spvr.cpp 71 hle/service/hid/hid_spvr.cpp
117 hle/service/hid/hid_user.cpp 72 hle/service/hid/hid_user.cpp
118 hle/service/http_c.cpp
119 hle/service/ir/extra_hid.cpp
120 hle/service/ir/ir.cpp
121 hle/service/ir/ir_rst.cpp
122 hle/service/ir/ir_u.cpp
123 hle/service/ir/ir_user.cpp
124 hle/service/ldr_ro/cro_helper.cpp
125 hle/service/ldr_ro/ldr_ro.cpp
126 hle/service/ldr_ro/memory_synchronizer.cpp
127 hle/service/mic_u.cpp
128 hle/service/mvd/mvd.cpp
129 hle/service/mvd/mvd_std.cpp
130 hle/service/ndm/ndm.cpp
131 hle/service/ndm/ndm_u.cpp
132 hle/service/nfc/nfc.cpp
133 hle/service/nfc/nfc_m.cpp
134 hle/service/nfc/nfc_u.cpp
135 hle/service/news/news.cpp
136 hle/service/news/news_s.cpp
137 hle/service/news/news_u.cpp
138 hle/service/nim/nim.cpp
139 hle/service/nim/nim_aoc.cpp
140 hle/service/nim/nim_s.cpp
141 hle/service/nim/nim_u.cpp
142 hle/service/ns/ns.cpp 73 hle/service/ns/ns.cpp
143 hle/service/ns/ns_s.cpp 74 hle/service/ns/ns_s.cpp
144 hle/service/nwm/nwm.cpp
145 hle/service/nwm/nwm_cec.cpp
146 hle/service/nwm/nwm_ext.cpp
147 hle/service/nwm/nwm_inf.cpp
148 hle/service/nwm/nwm_sap.cpp
149 hle/service/nwm/nwm_soc.cpp
150 hle/service/nwm/nwm_tst.cpp
151 hle/service/nwm/nwm_uds.cpp
152 hle/service/nwm/uds_beacon.cpp
153 hle/service/nwm/uds_connection.cpp
154 hle/service/nwm/uds_data.cpp
155 hle/service/pm_app.cpp
156 hle/service/ptm/ptm.cpp
157 hle/service/ptm/ptm_gets.cpp
158 hle/service/ptm/ptm_play.cpp
159 hle/service/ptm/ptm_sets.cpp
160 hle/service/ptm/ptm_sysm.cpp
161 hle/service/ptm/ptm_u.cpp
162 hle/service/qtm/qtm.cpp
163 hle/service/qtm/qtm_s.cpp
164 hle/service/qtm/qtm_sp.cpp
165 hle/service/qtm/qtm_u.cpp
166 hle/service/service.cpp 75 hle/service/service.cpp
167 hle/service/sm/sm.cpp 76 hle/service/sm/sm.cpp
168 hle/service/sm/srv.cpp 77 hle/service/sm/srv.cpp
169 hle/service/soc_u.cpp
170 hle/service/ssl_c.cpp
171 hle/service/y2r_u.cpp
172 hle/shared_page.cpp 78 hle/shared_page.cpp
173 hle/svc.cpp 79 hle/svc.cpp
174 hw/aes/arithmetic128.cpp 80 hw/aes/arithmetic128.cpp
@@ -177,7 +83,6 @@ set(SRCS
177 hw/gpu.cpp 83 hw/gpu.cpp
178 hw/hw.cpp 84 hw/hw.cpp
179 hw/lcd.cpp 85 hw/lcd.cpp
180 hw/y2r.cpp
181 loader/3dsx.cpp 86 loader/3dsx.cpp
182 loader/elf.cpp 87 loader/elf.cpp
183 loader/linker.cpp 88 loader/linker.cpp
@@ -231,9 +136,6 @@ set(HEADERS
231 file_sys/ivfc_archive.h 136 file_sys/ivfc_archive.h
232 file_sys/path_parser.h 137 file_sys/path_parser.h
233 file_sys/savedata_archive.h 138 file_sys/savedata_archive.h
234 frontend/camera/blank_camera.h
235 frontend/camera/factory.h
236 frontend/camera/interface.h
237 frontend/emu_window.h 139 frontend/emu_window.h
238 frontend/framebuffer_layout.h 140 frontend/framebuffer_layout.h
239 frontend/input.h 141 frontend/input.h
@@ -242,11 +144,6 @@ set(HEADERS
242 hle/function_wrappers.h 144 hle/function_wrappers.h
243 hle/ipc.h 145 hle/ipc.h
244 hle/ipc_helpers.h 146 hle/ipc_helpers.h
245 hle/applets/applet.h
246 hle/applets/erreula.h
247 hle/applets/mii_selector.h
248 hle/applets/mint.h
249 hle/applets/swkbd.h
250 hle/kernel/address_arbiter.h 147 hle/kernel/address_arbiter.h
251 hle/kernel/client_port.h 148 hle/kernel/client_port.h
252 hle/kernel/client_session.h 149 hle/kernel/client_session.h
@@ -271,49 +168,12 @@ set(HEADERS
271 hle/lock.h 168 hle/lock.h
272 hle/result.h 169 hle/result.h
273 hle/romfs.h 170 hle/romfs.h
274 hle/service/ac/ac.h
275 hle/service/ac/ac_i.h
276 hle/service/ac/ac_u.h
277 hle/service/act/act.h
278 hle/service/act/act_a.h
279 hle/service/act/act_u.h
280 hle/service/am/am.h
281 hle/service/am/am_app.h
282 hle/service/am/am_net.h
283 hle/service/am/am_sys.h
284 hle/service/am/am_u.h
285 hle/service/apt/apt.h
286 hle/service/apt/apt_a.h
287 hle/service/apt/apt_s.h
288 hle/service/apt/apt_u.h
289 hle/service/apt/bcfnt/bcfnt.h
290 hle/service/boss/boss.h
291 hle/service/boss/boss_p.h
292 hle/service/boss/boss_u.h
293 hle/service/cam/cam.h
294 hle/service/cam/cam_c.h
295 hle/service/cam/cam_q.h
296 hle/service/cam/cam_s.h
297 hle/service/cam/cam_u.h
298 hle/service/cecd/cecd.h
299 hle/service/cecd/cecd_ndm.h
300 hle/service/cecd/cecd_s.h
301 hle/service/cecd/cecd_u.h
302 hle/service/cfg/cfg.h 171 hle/service/cfg/cfg.h
303 hle/service/cfg/cfg_i.h 172 hle/service/cfg/cfg_i.h
304 hle/service/cfg/cfg_nor.h 173 hle/service/cfg/cfg_nor.h
305 hle/service/cfg/cfg_s.h 174 hle/service/cfg/cfg_s.h
306 hle/service/cfg/cfg_u.h 175 hle/service/cfg/cfg_u.h
307 hle/service/csnd_snd.h
308 hle/service/dlp/dlp.h
309 hle/service/dlp/dlp_clnt.h
310 hle/service/dlp/dlp_fkcl.h
311 hle/service/dlp/dlp_srvr.h
312 hle/service/dsp_dsp.h 176 hle/service/dsp_dsp.h
313 hle/service/err_f.h
314 hle/service/frd/frd.h
315 hle/service/frd/frd_a.h
316 hle/service/frd/frd_u.h
317 hle/service/fs/archive.h 177 hle/service/fs/archive.h
318 hle/service/fs/fs_user.h 178 hle/service/fs/fs_user.h
319 hle/service/gsp_gpu.h 179 hle/service/gsp_gpu.h
@@ -321,60 +181,11 @@ set(HEADERS
321 hle/service/hid/hid.h 181 hle/service/hid/hid.h
322 hle/service/hid/hid_spvr.h 182 hle/service/hid/hid_spvr.h
323 hle/service/hid/hid_user.h 183 hle/service/hid/hid_user.h
324 hle/service/http_c.h
325 hle/service/ir/extra_hid.h
326 hle/service/ir/ir.h
327 hle/service/ir/ir_rst.h
328 hle/service/ir/ir_u.h
329 hle/service/ir/ir_user.h
330 hle/service/ldr_ro/cro_helper.h
331 hle/service/ldr_ro/ldr_ro.h
332 hle/service/ldr_ro/memory_synchronizer.h
333 hle/service/mic_u.h
334 hle/service/mvd/mvd.h
335 hle/service/mvd/mvd_std.h
336 hle/service/ndm/ndm.h
337 hle/service/ndm/ndm_u.h
338 hle/service/nfc/nfc.h
339 hle/service/nfc/nfc_m.h
340 hle/service/nfc/nfc_u.h
341 hle/service/news/news.h
342 hle/service/news/news_s.h
343 hle/service/news/news_u.h
344 hle/service/nim/nim.h
345 hle/service/nim/nim_aoc.h
346 hle/service/nim/nim_s.h
347 hle/service/nim/nim_u.h
348 hle/service/ns/ns.h 184 hle/service/ns/ns.h
349 hle/service/ns/ns_s.h 185 hle/service/ns/ns_s.h
350 hle/service/nwm/nwm.h
351 hle/service/nwm/nwm_cec.h
352 hle/service/nwm/nwm_ext.h
353 hle/service/nwm/nwm_inf.h
354 hle/service/nwm/nwm_sap.h
355 hle/service/nwm/nwm_soc.h
356 hle/service/nwm/nwm_tst.h
357 hle/service/nwm/nwm_uds.h
358 hle/service/nwm/uds_beacon.h
359 hle/service/nwm/uds_connection.h
360 hle/service/nwm/uds_data.h
361 hle/service/pm_app.h
362 hle/service/ptm/ptm.h
363 hle/service/ptm/ptm_gets.h
364 hle/service/ptm/ptm_play.h
365 hle/service/ptm/ptm_sets.h
366 hle/service/ptm/ptm_sysm.h
367 hle/service/ptm/ptm_u.h
368 hle/service/qtm/qtm.h
369 hle/service/qtm/qtm_s.h
370 hle/service/qtm/qtm_sp.h
371 hle/service/qtm/qtm_u.h
372 hle/service/service.h 186 hle/service/service.h
373 hle/service/sm/sm.h 187 hle/service/sm/sm.h
374 hle/service/sm/srv.h 188 hle/service/sm/srv.h
375 hle/service/soc_u.h
376 hle/service/ssl_c.h
377 hle/service/y2r_u.h
378 hle/shared_page.h 189 hle/shared_page.h
379 hle/svc.h 190 hle/svc.h
380 hw/aes/arithmetic128.h 191 hw/aes/arithmetic128.h
@@ -383,7 +194,6 @@ set(HEADERS
383 hw/gpu.h 194 hw/gpu.h
384 hw/hw.h 195 hw/hw.h
385 hw/lcd.h 196 hw/lcd.h
386 hw/y2r.h
387 loader/3dsx.h 197 loader/3dsx.h
388 loader/elf.h 198 loader/elf.h
389 loader/linker.h 199 loader/linker.h
diff --git a/src/core/frontend/camera/blank_camera.cpp b/src/core/frontend/camera/blank_camera.cpp
deleted file mode 100644
index 7995abcbd..000000000
--- a/src/core/frontend/camera/blank_camera.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/frontend/camera/blank_camera.h"
6
7namespace Camera {
8
9void BlankCamera::StartCapture() {}
10
11void BlankCamera::StopCapture() {}
12
13void BlankCamera::SetFormat(Service::CAM::OutputFormat output_format) {
14 output_rgb = output_format == Service::CAM::OutputFormat::RGB565;
15}
16
17void BlankCamera::SetResolution(const Service::CAM::Resolution& resolution) {
18 width = resolution.width;
19 height = resolution.height;
20};
21
22void BlankCamera::SetFlip(Service::CAM::Flip) {}
23
24void BlankCamera::SetEffect(Service::CAM::Effect) {}
25
26std::vector<u16> BlankCamera::ReceiveFrame() const {
27 // Note: 0x80008000 stands for two black pixels in YUV422
28 return std::vector<u16>(width * height, output_rgb ? 0 : 0x8000);
29}
30
31} // namespace Camera
diff --git a/src/core/frontend/camera/blank_camera.h b/src/core/frontend/camera/blank_camera.h
deleted file mode 100644
index c6619bd88..000000000
--- a/src/core/frontend/camera/blank_camera.h
+++ /dev/null
@@ -1,28 +0,0 @@
1// Copyright 2016 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 "core/frontend/camera/factory.h"
8#include "core/frontend/camera/interface.h"
9
10namespace Camera {
11
12class BlankCamera final : public CameraInterface {
13public:
14 void StartCapture() override;
15 void StopCapture() override;
16 void SetResolution(const Service::CAM::Resolution&) override;
17 void SetFlip(Service::CAM::Flip) override;
18 void SetEffect(Service::CAM::Effect) override;
19 void SetFormat(Service::CAM::OutputFormat) override;
20 std::vector<u16> ReceiveFrame() const override;
21
22private:
23 int width = 0;
24 int height = 0;
25 bool output_rgb = false;
26};
27
28} // namespace Camera
diff --git a/src/core/frontend/camera/factory.cpp b/src/core/frontend/camera/factory.cpp
deleted file mode 100644
index 4b4da50dd..000000000
--- a/src/core/frontend/camera/factory.cpp
+++ /dev/null
@@ -1,32 +0,0 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <unordered_map>
6#include "common/logging/log.h"
7#include "core/frontend/camera/blank_camera.h"
8#include "core/frontend/camera/factory.h"
9
10namespace Camera {
11
12static std::unordered_map<std::string, std::unique_ptr<CameraFactory>> factories;
13
14CameraFactory::~CameraFactory() = default;
15
16void RegisterFactory(const std::string& name, std::unique_ptr<CameraFactory> factory) {
17 factories[name] = std::move(factory);
18}
19
20std::unique_ptr<CameraInterface> CreateCamera(const std::string& name, const std::string& config) {
21 auto pair = factories.find(name);
22 if (pair != factories.end()) {
23 return pair->second->Create(config);
24 }
25
26 if (name != "blank") {
27 LOG_ERROR(Service_CAM, "Unknown camera \"%s\"", name.c_str());
28 }
29 return std::make_unique<BlankCamera>();
30}
31
32} // namespace Camera
diff --git a/src/core/frontend/camera/factory.h b/src/core/frontend/camera/factory.h
deleted file mode 100644
index f46413fa7..000000000
--- a/src/core/frontend/camera/factory.h
+++ /dev/null
@@ -1,41 +0,0 @@
1// Copyright 2016 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 <memory>
8#include <string>
9#include "core/frontend/camera/interface.h"
10
11namespace Camera {
12
13class CameraFactory {
14public:
15 virtual ~CameraFactory();
16
17 /**
18 * Creates a camera object based on the configuration string.
19 * @param config Configuration string to create the camera. The implementation can decide the
20 * meaning of this string.
21 * @returns a unique_ptr to the created camera object.
22 */
23 virtual std::unique_ptr<CameraInterface> Create(const std::string& config) const = 0;
24};
25
26/**
27 * Registers an external camera factory.
28 * @param name Identifier of the camera factory.
29 * @param factory Camera factory to register.
30 */
31void RegisterFactory(const std::string& name, std::unique_ptr<CameraFactory> factory);
32
33/**
34 * Creates a camera from the factory.
35 * @param name Identifier of the camera factory.
36 * @param config Configuration string to create the camera. The meaning of this string is
37 * defined by the factory.
38 */
39std::unique_ptr<CameraInterface> CreateCamera(const std::string& name, const std::string& config);
40
41} // namespace Camera
diff --git a/src/core/frontend/camera/interface.cpp b/src/core/frontend/camera/interface.cpp
deleted file mode 100644
index 9aec9e7f1..000000000
--- a/src/core/frontend/camera/interface.cpp
+++ /dev/null
@@ -1,11 +0,0 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/frontend/camera/interface.h"
6
7namespace Camera {
8
9CameraInterface::~CameraInterface() = default;
10
11} // namespace Camera
diff --git a/src/core/frontend/camera/interface.h b/src/core/frontend/camera/interface.h
deleted file mode 100644
index a55a495c9..000000000
--- a/src/core/frontend/camera/interface.h
+++ /dev/null
@@ -1,61 +0,0 @@
1// Copyright 2016 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 <vector>
8#include "common/common_types.h"
9#include "core/hle/service/cam/cam.h"
10
11namespace Camera {
12
13/// An abstract class standing for a camera. All camera implementations should inherit from this.
14class CameraInterface {
15public:
16 virtual ~CameraInterface();
17
18 /// Starts the camera for video capturing.
19 virtual void StartCapture() = 0;
20
21 /// Stops the camera for video capturing.
22 virtual void StopCapture() = 0;
23
24 /**
25 * Sets the video resolution from raw CAM service parameters.
26 * For the meaning of the parameters, please refer to Service::CAM::Resolution. Note that the
27 * actual camera implementation doesn't need to respect all the parameters. However, the width
28 * and the height parameters must be respected and be used to determine the size of output
29 * frames.
30 * @param resolution The resolution parameters to set
31 */
32 virtual void SetResolution(const Service::CAM::Resolution& resolution) = 0;
33
34 /**
35 * Configures how received frames should be flipped by the camera.
36 * @param flip Flip applying to the frame
37 */
38 virtual void SetFlip(Service::CAM::Flip flip) = 0;
39
40 /**
41 * Configures what effect should be applied to received frames by the camera.
42 * @param effect Effect applying to the frame
43 */
44 virtual void SetEffect(Service::CAM::Effect effect) = 0;
45
46 /**
47 * Sets the output format of the all frames received after this function is called.
48 * @param format Output format of the frame
49 */
50 virtual void SetFormat(Service::CAM::OutputFormat format) = 0;
51
52 /**
53 * Receives a frame from the camera.
54 * This function should be only called between a StartCapture call and a StopCapture call.
55 * @returns A std::vector<u16> containing pixels. The total size of the vector is width * height
56 * where width and height are set by a call to SetResolution.
57 */
58 virtual std::vector<u16> ReceiveFrame() const = 0;
59};
60
61} // namespace Camera
diff --git a/src/core/hle/applets/applet.cpp b/src/core/hle/applets/applet.cpp
deleted file mode 100644
index 9c43ed2fd..000000000
--- a/src/core/hle/applets/applet.cpp
+++ /dev/null
@@ -1,130 +0,0 @@
1// Copyright 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <cstddef>
6#include <memory>
7#include <type_traits>
8#include <unordered_map>
9#include "common/assert.h"
10#include "common/common_types.h"
11#include "core/core_timing.h"
12#include "core/hle/applets/applet.h"
13#include "core/hle/applets/erreula.h"
14#include "core/hle/applets/mii_selector.h"
15#include "core/hle/applets/mint.h"
16#include "core/hle/applets/swkbd.h"
17#include "core/hle/result.h"
18#include "core/hle/service/apt/apt.h"
19
20////////////////////////////////////////////////////////////////////////////////////////////////////
21
22// Specializes std::hash for AppletId, so that we can use it in std::unordered_map.
23// Workaround for libstdc++ bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60970
24namespace std {
25template <>
26struct hash<Service::APT::AppletId> {
27 typedef Service::APT::AppletId argument_type;
28 typedef std::size_t result_type;
29
30 result_type operator()(const argument_type& id_code) const {
31 typedef std::underlying_type<argument_type>::type Type;
32 return std::hash<Type>()(static_cast<Type>(id_code));
33 }
34};
35}
36
37namespace HLE {
38namespace Applets {
39
40static std::unordered_map<Service::APT::AppletId, std::shared_ptr<Applet>> applets;
41static u32 applet_update_event =
42 -1; ///< The CoreTiming event identifier for the Applet update callback.
43/// The interval at which the Applet update callback will be called, 16.6ms
44static const u64 applet_update_interval_us = 16666;
45
46ResultCode Applet::Create(Service::APT::AppletId id) {
47 switch (id) {
48 case Service::APT::AppletId::SoftwareKeyboard1:
49 case Service::APT::AppletId::SoftwareKeyboard2:
50 applets[id] = std::make_shared<SoftwareKeyboard>(id);
51 break;
52 case Service::APT::AppletId::Ed1:
53 case Service::APT::AppletId::Ed2:
54 applets[id] = std::make_shared<MiiSelector>(id);
55 break;
56 case Service::APT::AppletId::Error:
57 case Service::APT::AppletId::Error2:
58 applets[id] = std::make_shared<ErrEula>(id);
59 break;
60 case Service::APT::AppletId::Mint:
61 case Service::APT::AppletId::Mint2:
62 applets[id] = std::make_shared<Mint>(id);
63 break;
64 default:
65 LOG_ERROR(Service_APT, "Could not create applet %u", id);
66 // TODO(Subv): Find the right error code
67 return ResultCode(ErrorDescription::NotFound, ErrorModule::Applet,
68 ErrorSummary::NotSupported, ErrorLevel::Permanent);
69 }
70
71 return RESULT_SUCCESS;
72}
73
74std::shared_ptr<Applet> Applet::Get(Service::APT::AppletId id) {
75 auto itr = applets.find(id);
76 if (itr != applets.end())
77 return itr->second;
78 return nullptr;
79}
80
81/// Handles updating the current Applet every time it's called.
82static void AppletUpdateEvent(u64 applet_id, int cycles_late) {
83 Service::APT::AppletId id = static_cast<Service::APT::AppletId>(applet_id);
84 std::shared_ptr<Applet> applet = Applet::Get(id);
85 ASSERT_MSG(applet != nullptr, "Applet doesn't exist! applet_id=%08X", id);
86
87 applet->Update();
88
89 // If the applet is still running after the last update, reschedule the event
90 if (applet->IsRunning()) {
91 CoreTiming::ScheduleEvent(usToCycles(applet_update_interval_us) - cycles_late,
92 applet_update_event, applet_id);
93 } else {
94 // Otherwise the applet has terminated, in which case we should clean it up
95 applets[id] = nullptr;
96 }
97}
98
99ResultCode Applet::Start(const Service::APT::AppletStartupParameter& parameter) {
100 ResultCode result = StartImpl(parameter);
101 if (result.IsError())
102 return result;
103 // Schedule the update event
104 CoreTiming::ScheduleEvent(usToCycles(applet_update_interval_us), applet_update_event,
105 static_cast<u64>(id));
106 return result;
107}
108
109bool Applet::IsRunning() const {
110 return is_running;
111}
112
113bool IsLibraryAppletRunning() {
114 // Check the applets map for instances of any applet
115 for (auto itr = applets.begin(); itr != applets.end(); ++itr)
116 if (itr->second != nullptr)
117 return true;
118 return false;
119}
120
121void Init() {
122 // Register the applet update callback
123 applet_update_event = CoreTiming::RegisterEvent("HLE Applet Update Event", AppletUpdateEvent);
124}
125
126void Shutdown() {
127 CoreTiming::RemoveEvent(applet_update_event);
128}
129}
130} // namespace
diff --git a/src/core/hle/applets/applet.h b/src/core/hle/applets/applet.h
deleted file mode 100644
index ebeed9813..000000000
--- a/src/core/hle/applets/applet.h
+++ /dev/null
@@ -1,83 +0,0 @@
1// Copyright 2015 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 <memory>
8#include "core/hle/result.h"
9#include "core/hle/service/apt/apt.h"
10
11namespace HLE {
12namespace Applets {
13
14class Applet {
15public:
16 virtual ~Applet() = default;
17
18 /**
19 * Creates an instance of the Applet subclass identified by the parameter.
20 * and stores it in a global map.
21 * @param id Id of the applet to create.
22 * @returns ResultCode Whether the operation was successful or not.
23 */
24 static ResultCode Create(Service::APT::AppletId id);
25
26 /**
27 * Retrieves the Applet instance identified by the specified id.
28 * @param id Id of the Applet to retrieve.
29 * @returns Requested Applet or nullptr if not found.
30 */
31 static std::shared_ptr<Applet> Get(Service::APT::AppletId id);
32
33 /**
34 * Handles a parameter from the application.
35 * @param parameter Parameter data to handle.
36 * @returns ResultCode Whether the operation was successful or not.
37 */
38 virtual ResultCode ReceiveParameter(const Service::APT::MessageParameter& parameter) = 0;
39
40 /**
41 * Handles the Applet start event, triggered from the application.
42 * @param parameter Parameter data to handle.
43 * @returns ResultCode Whether the operation was successful or not.
44 */
45 ResultCode Start(const Service::APT::AppletStartupParameter& parameter);
46
47 /**
48 * Whether the applet is currently executing instead of the host application or not.
49 */
50 bool IsRunning() const;
51
52 /**
53 * Handles an update tick for the Applet, lets it update the screen, send commands, etc.
54 */
55 virtual void Update() = 0;
56
57protected:
58 explicit Applet(Service::APT::AppletId id) : id(id) {}
59
60 /**
61 * Handles the Applet start event, triggered from the application.
62 * @param parameter Parameter data to handle.
63 * @returns ResultCode Whether the operation was successful or not.
64 */
65 virtual ResultCode StartImpl(const Service::APT::AppletStartupParameter& parameter) = 0;
66
67 Service::APT::AppletId id; ///< Id of this Applet
68 std::shared_ptr<std::vector<u8>> heap_memory; ///< Heap memory for this Applet
69
70 /// Whether this applet is currently running instead of the host application or not.
71 bool is_running = false;
72};
73
74/// Returns whether a library applet is currently running
75bool IsLibraryAppletRunning();
76
77/// Initializes the HLE applets
78void Init();
79
80/// Shuts down the HLE applets
81void Shutdown();
82}
83} // namespace
diff --git a/src/core/hle/applets/erreula.cpp b/src/core/hle/applets/erreula.cpp
deleted file mode 100644
index 518f371f5..000000000
--- a/src/core/hle/applets/erreula.cpp
+++ /dev/null
@@ -1,72 +0,0 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/string_util.h"
6#include "core/hle/applets/erreula.h"
7#include "core/hle/service/apt/apt.h"
8
9namespace HLE {
10namespace Applets {
11
12ResultCode ErrEula::ReceiveParameter(const Service::APT::MessageParameter& parameter) {
13 if (parameter.signal != static_cast<u32>(Service::APT::SignalType::Request)) {
14 LOG_ERROR(Service_APT, "unsupported signal %u", parameter.signal);
15 UNIMPLEMENTED();
16 // TODO(Subv): Find the right error code
17 return ResultCode(-1);
18 }
19
20 // The LibAppJustStarted message contains a buffer with the size of the framebuffer shared
21 // memory.
22 // Create the SharedMemory that will hold the framebuffer data
23 Service::APT::CaptureBufferInfo capture_info;
24 ASSERT(sizeof(capture_info) == parameter.buffer.size());
25
26 memcpy(&capture_info, parameter.buffer.data(), sizeof(capture_info));
27
28 // TODO: allocated memory never released
29 using Kernel::MemoryPermission;
30 // Allocate a heap block of the required size for this applet.
31 heap_memory = std::make_shared<std::vector<u8>>(capture_info.size);
32 // Create a SharedMemory that directly points to this heap block.
33 framebuffer_memory = Kernel::SharedMemory::CreateForApplet(
34 heap_memory, 0, capture_info.size, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite,
35 "ErrEula Memory");
36
37 // Send the response message with the newly created SharedMemory
38 Service::APT::MessageParameter result;
39 result.signal = static_cast<u32>(Service::APT::SignalType::Response);
40 result.buffer.clear();
41 result.destination_id = static_cast<u32>(Service::APT::AppletId::Application);
42 result.sender_id = static_cast<u32>(id);
43 result.object = framebuffer_memory;
44
45 Service::APT::SendParameter(result);
46 return RESULT_SUCCESS;
47}
48
49ResultCode ErrEula::StartImpl(const Service::APT::AppletStartupParameter& parameter) {
50 is_running = true;
51
52 // TODO(Subv): Set the expected fields in the response buffer before resending it to the
53 // application.
54 // TODO(Subv): Reverse the parameter format for the ErrEula applet
55
56 // Let the application know that we're closing
57 Service::APT::MessageParameter message;
58 message.buffer.resize(parameter.buffer.size());
59 std::fill(message.buffer.begin(), message.buffer.end(), 0);
60 message.signal = static_cast<u32>(Service::APT::SignalType::WakeupByExit);
61 message.destination_id = static_cast<u32>(Service::APT::AppletId::Application);
62 message.sender_id = static_cast<u32>(id);
63 Service::APT::SendParameter(message);
64
65 is_running = false;
66 return RESULT_SUCCESS;
67}
68
69void ErrEula::Update() {}
70
71} // namespace Applets
72} // namespace HLE
diff --git a/src/core/hle/applets/erreula.h b/src/core/hle/applets/erreula.h
deleted file mode 100644
index 681bbea0c..000000000
--- a/src/core/hle/applets/erreula.h
+++ /dev/null
@@ -1,29 +0,0 @@
1// Copyright 2016 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 "core/hle/applets/applet.h"
8#include "core/hle/kernel/shared_memory.h"
9
10namespace HLE {
11namespace Applets {
12
13class ErrEula final : public Applet {
14public:
15 explicit ErrEula(Service::APT::AppletId id) : Applet(id) {}
16
17 ResultCode ReceiveParameter(const Service::APT::MessageParameter& parameter) override;
18 ResultCode StartImpl(const Service::APT::AppletStartupParameter& parameter) override;
19 void Update() override;
20
21private:
22 /// This SharedMemory will be created when we receive the LibAppJustStarted message.
23 /// It holds the framebuffer info retrieved by the application with
24 /// GSPGPU::ImportDisplayCaptureInfo
25 Kernel::SharedPtr<Kernel::SharedMemory> framebuffer_memory;
26};
27
28} // namespace Applets
29} // namespace HLE
diff --git a/src/core/hle/applets/mii_selector.cpp b/src/core/hle/applets/mii_selector.cpp
deleted file mode 100644
index f225c23a5..000000000
--- a/src/core/hle/applets/mii_selector.cpp
+++ /dev/null
@@ -1,86 +0,0 @@
1// Copyright 2016 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 <string>
7#include "common/assert.h"
8#include "common/logging/log.h"
9#include "common/string_util.h"
10#include "core/hle/applets/mii_selector.h"
11#include "core/hle/kernel/kernel.h"
12#include "core/hle/kernel/shared_memory.h"
13#include "core/hle/result.h"
14
15////////////////////////////////////////////////////////////////////////////////////////////////////
16
17namespace HLE {
18namespace Applets {
19
20ResultCode MiiSelector::ReceiveParameter(const Service::APT::MessageParameter& parameter) {
21 if (parameter.signal != static_cast<u32>(Service::APT::SignalType::Request)) {
22 LOG_ERROR(Service_APT, "unsupported signal %u", parameter.signal);
23 UNIMPLEMENTED();
24 // TODO(Subv): Find the right error code
25 return ResultCode(-1);
26 }
27
28 // The LibAppJustStarted message contains a buffer with the size of the framebuffer shared
29 // memory.
30 // Create the SharedMemory that will hold the framebuffer data
31 Service::APT::CaptureBufferInfo capture_info;
32 ASSERT(sizeof(capture_info) == parameter.buffer.size());
33
34 memcpy(&capture_info, parameter.buffer.data(), sizeof(capture_info));
35
36 using Kernel::MemoryPermission;
37 // Allocate a heap block of the required size for this applet.
38 heap_memory = std::make_shared<std::vector<u8>>(capture_info.size);
39 // Create a SharedMemory that directly points to this heap block.
40 framebuffer_memory = Kernel::SharedMemory::CreateForApplet(
41 heap_memory, 0, capture_info.size, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite,
42 "MiiSelector Memory");
43
44 // Send the response message with the newly created SharedMemory
45 Service::APT::MessageParameter result;
46 result.signal = static_cast<u32>(Service::APT::SignalType::Response);
47 result.buffer.clear();
48 result.destination_id = static_cast<u32>(Service::APT::AppletId::Application);
49 result.sender_id = static_cast<u32>(id);
50 result.object = framebuffer_memory;
51
52 Service::APT::SendParameter(result);
53 return RESULT_SUCCESS;
54}
55
56ResultCode MiiSelector::StartImpl(const Service::APT::AppletStartupParameter& parameter) {
57 is_running = true;
58
59 // TODO(Subv): Set the expected fields in the response buffer before resending it to the
60 // application.
61 // TODO(Subv): Reverse the parameter format for the Mii Selector
62
63 memcpy(&config, parameter.buffer.data(), parameter.buffer.size());
64
65 // TODO(Subv): Find more about this structure, result code 0 is enough to let most games
66 // continue.
67 MiiResult result;
68 memset(&result, 0, sizeof(result));
69 result.return_code = 0;
70
71 // Let the application know that we're closing
72 Service::APT::MessageParameter message;
73 message.buffer.resize(sizeof(MiiResult));
74 std::memcpy(message.buffer.data(), &result, message.buffer.size());
75 message.signal = static_cast<u32>(Service::APT::SignalType::WakeupByExit);
76 message.destination_id = static_cast<u32>(Service::APT::AppletId::Application);
77 message.sender_id = static_cast<u32>(id);
78 Service::APT::SendParameter(message);
79
80 is_running = false;
81 return RESULT_SUCCESS;
82}
83
84void MiiSelector::Update() {}
85} // namespace Applets
86} // namespace HLE
diff --git a/src/core/hle/applets/mii_selector.h b/src/core/hle/applets/mii_selector.h
deleted file mode 100644
index 136ce8948..000000000
--- a/src/core/hle/applets/mii_selector.h
+++ /dev/null
@@ -1,78 +0,0 @@
1// Copyright 2016 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 "common/common_funcs.h"
8#include "common/common_types.h"
9#include "core/hle/applets/applet.h"
10#include "core/hle/kernel/kernel.h"
11#include "core/hle/kernel/shared_memory.h"
12#include "core/hle/result.h"
13#include "core/hle/service/apt/apt.h"
14
15namespace HLE {
16namespace Applets {
17
18struct MiiConfig {
19 u8 enable_cancel_button;
20 u8 enable_guest_mii;
21 u8 show_on_top_screen;
22 INSERT_PADDING_BYTES(5);
23 u16 title[0x40];
24 INSERT_PADDING_BYTES(4);
25 u8 show_guest_miis;
26 INSERT_PADDING_BYTES(3);
27 u32 initially_selected_mii_index;
28 u8 guest_mii_whitelist[6];
29 u8 user_mii_whitelist[0x64];
30 INSERT_PADDING_BYTES(2);
31 u32 magic_value;
32};
33static_assert(sizeof(MiiConfig) == 0x104, "MiiConfig structure has incorrect size");
34#define ASSERT_REG_POSITION(field_name, position) \
35 static_assert(offsetof(MiiConfig, field_name) == position, \
36 "Field " #field_name " has invalid position")
37ASSERT_REG_POSITION(title, 0x08);
38ASSERT_REG_POSITION(show_guest_miis, 0x8C);
39ASSERT_REG_POSITION(initially_selected_mii_index, 0x90);
40ASSERT_REG_POSITION(guest_mii_whitelist, 0x94);
41#undef ASSERT_REG_POSITION
42
43struct MiiResult {
44 u32 return_code;
45 u32 is_guest_mii_selected;
46 u32 selected_guest_mii_index;
47 // TODO(mailwl): expand to Mii Format structure: https://www.3dbrew.org/wiki/Mii
48 u8 selected_mii_data[0x5C];
49 INSERT_PADDING_BYTES(2);
50 u16 mii_data_checksum;
51 u16 guest_mii_name[0xC];
52};
53static_assert(sizeof(MiiResult) == 0x84, "MiiResult structure has incorrect size");
54#define ASSERT_REG_POSITION(field_name, position) \
55 static_assert(offsetof(MiiResult, field_name) == position, \
56 "Field " #field_name " has invalid position")
57ASSERT_REG_POSITION(selected_mii_data, 0x0C);
58ASSERT_REG_POSITION(guest_mii_name, 0x6C);
59#undef ASSERT_REG_POSITION
60
61class MiiSelector final : public Applet {
62public:
63 MiiSelector(Service::APT::AppletId id) : Applet(id) {}
64
65 ResultCode ReceiveParameter(const Service::APT::MessageParameter& parameter) override;
66 ResultCode StartImpl(const Service::APT::AppletStartupParameter& parameter) override;
67 void Update() override;
68
69private:
70 /// This SharedMemory will be created when we receive the LibAppJustStarted message.
71 /// It holds the framebuffer info retrieved by the application with
72 /// GSPGPU::ImportDisplayCaptureInfo
73 Kernel::SharedPtr<Kernel::SharedMemory> framebuffer_memory;
74
75 MiiConfig config;
76};
77} // namespace Applets
78} // namespace HLE
diff --git a/src/core/hle/applets/mint.cpp b/src/core/hle/applets/mint.cpp
deleted file mode 100644
index 50d79190b..000000000
--- a/src/core/hle/applets/mint.cpp
+++ /dev/null
@@ -1,72 +0,0 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/string_util.h"
6#include "core/hle/applets/mint.h"
7#include "core/hle/service/apt/apt.h"
8
9namespace HLE {
10namespace Applets {
11
12ResultCode Mint::ReceiveParameter(const Service::APT::MessageParameter& parameter) {
13 if (parameter.signal != static_cast<u32>(Service::APT::SignalType::Request)) {
14 LOG_ERROR(Service_APT, "unsupported signal %u", parameter.signal);
15 UNIMPLEMENTED();
16 // TODO(Subv): Find the right error code
17 return ResultCode(-1);
18 }
19
20 // The Request message contains a buffer with the size of the framebuffer shared
21 // memory.
22 // Create the SharedMemory that will hold the framebuffer data
23 Service::APT::CaptureBufferInfo capture_info;
24 ASSERT(sizeof(capture_info) == parameter.buffer.size());
25
26 memcpy(&capture_info, parameter.buffer.data(), sizeof(capture_info));
27
28 // TODO: allocated memory never released
29 using Kernel::MemoryPermission;
30 // Allocate a heap block of the required size for this applet.
31 heap_memory = std::make_shared<std::vector<u8>>(capture_info.size);
32 // Create a SharedMemory that directly points to this heap block.
33 framebuffer_memory = Kernel::SharedMemory::CreateForApplet(
34 heap_memory, 0, capture_info.size, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite,
35 "Mint Memory");
36
37 // Send the response message with the newly created SharedMemory
38 Service::APT::MessageParameter result;
39 result.signal = static_cast<u32>(Service::APT::SignalType::Response);
40 result.buffer.clear();
41 result.destination_id = static_cast<u32>(Service::APT::AppletId::Application);
42 result.sender_id = static_cast<u32>(id);
43 result.object = framebuffer_memory;
44
45 Service::APT::SendParameter(result);
46 return RESULT_SUCCESS;
47}
48
49ResultCode Mint::StartImpl(const Service::APT::AppletStartupParameter& parameter) {
50 is_running = true;
51
52 // TODO(Subv): Set the expected fields in the response buffer before resending it to the
53 // application.
54 // TODO(Subv): Reverse the parameter format for the Mint applet
55
56 // Let the application know that we're closing
57 Service::APT::MessageParameter message;
58 message.buffer.resize(parameter.buffer.size());
59 std::fill(message.buffer.begin(), message.buffer.end(), 0);
60 message.signal = static_cast<u32>(Service::APT::SignalType::WakeupByExit);
61 message.destination_id = static_cast<u32>(Service::APT::AppletId::Application);
62 message.sender_id = static_cast<u32>(id);
63 Service::APT::SendParameter(message);
64
65 is_running = false;
66 return RESULT_SUCCESS;
67}
68
69void Mint::Update() {}
70
71} // namespace Applets
72} // namespace HLE
diff --git a/src/core/hle/applets/mint.h b/src/core/hle/applets/mint.h
deleted file mode 100644
index d23dc40f9..000000000
--- a/src/core/hle/applets/mint.h
+++ /dev/null
@@ -1,29 +0,0 @@
1// Copyright 2016 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 "core/hle/applets/applet.h"
8#include "core/hle/kernel/shared_memory.h"
9
10namespace HLE {
11namespace Applets {
12
13class Mint final : public Applet {
14public:
15 explicit Mint(Service::APT::AppletId id) : Applet(id) {}
16
17 ResultCode ReceiveParameter(const Service::APT::MessageParameter& parameter) override;
18 ResultCode StartImpl(const Service::APT::AppletStartupParameter& parameter) override;
19 void Update() override;
20
21private:
22 /// This SharedMemory will be created when we receive the Request message.
23 /// It holds the framebuffer info retrieved by the application with
24 /// GSPGPU::ImportDisplayCaptureInfo
25 Kernel::SharedPtr<Kernel::SharedMemory> framebuffer_memory;
26};
27
28} // namespace Applets
29} // namespace HLE
diff --git a/src/core/hle/applets/swkbd.cpp b/src/core/hle/applets/swkbd.cpp
deleted file mode 100644
index 0bc471a3a..000000000
--- a/src/core/hle/applets/swkbd.cpp
+++ /dev/null
@@ -1,118 +0,0 @@
1// Copyright 2015 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 <string>
7#include "common/assert.h"
8#include "common/logging/log.h"
9#include "common/string_util.h"
10#include "core/hle/applets/swkbd.h"
11#include "core/hle/kernel/kernel.h"
12#include "core/hle/kernel/shared_memory.h"
13#include "core/hle/result.h"
14#include "core/hle/service/gsp_gpu.h"
15#include "core/hle/service/hid/hid.h"
16#include "core/memory.h"
17
18////////////////////////////////////////////////////////////////////////////////////////////////////
19
20namespace HLE {
21namespace Applets {
22
23ResultCode SoftwareKeyboard::ReceiveParameter(Service::APT::MessageParameter const& parameter) {
24 if (parameter.signal != static_cast<u32>(Service::APT::SignalType::Request)) {
25 LOG_ERROR(Service_APT, "unsupported signal %u", parameter.signal);
26 UNIMPLEMENTED();
27 // TODO(Subv): Find the right error code
28 return ResultCode(-1);
29 }
30
31 // The LibAppJustStarted message contains a buffer with the size of the framebuffer shared
32 // memory.
33 // Create the SharedMemory that will hold the framebuffer data
34 Service::APT::CaptureBufferInfo capture_info;
35 ASSERT(sizeof(capture_info) == parameter.buffer.size());
36
37 memcpy(&capture_info, parameter.buffer.data(), sizeof(capture_info));
38
39 using Kernel::MemoryPermission;
40 // Allocate a heap block of the required size for this applet.
41 heap_memory = std::make_shared<std::vector<u8>>(capture_info.size);
42 // Create a SharedMemory that directly points to this heap block.
43 framebuffer_memory = Kernel::SharedMemory::CreateForApplet(
44 heap_memory, 0, capture_info.size, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite,
45 "SoftwareKeyboard Memory");
46
47 // Send the response message with the newly created SharedMemory
48 Service::APT::MessageParameter result;
49 result.signal = static_cast<u32>(Service::APT::SignalType::Response);
50 result.buffer.clear();
51 result.destination_id = static_cast<u32>(Service::APT::AppletId::Application);
52 result.sender_id = static_cast<u32>(id);
53 result.object = framebuffer_memory;
54
55 Service::APT::SendParameter(result);
56 return RESULT_SUCCESS;
57}
58
59ResultCode SoftwareKeyboard::StartImpl(Service::APT::AppletStartupParameter const& parameter) {
60 ASSERT_MSG(parameter.buffer.size() == sizeof(config),
61 "The size of the parameter (SoftwareKeyboardConfig) is wrong");
62
63 memcpy(&config, parameter.buffer.data(), parameter.buffer.size());
64 text_memory =
65 boost::static_pointer_cast<Kernel::SharedMemory, Kernel::Object>(parameter.object);
66
67 // TODO(Subv): Verify if this is the correct behavior
68 memset(text_memory->GetPointer(), 0, text_memory->size);
69
70 DrawScreenKeyboard();
71
72 is_running = true;
73 return RESULT_SUCCESS;
74}
75
76void SoftwareKeyboard::Update() {
77 // TODO(Subv): Handle input using the touch events from the HID module
78
79 // TODO(Subv): Remove this hardcoded text
80 std::u16string text = Common::UTF8ToUTF16("Citra");
81 memcpy(text_memory->GetPointer(), text.c_str(), text.length() * sizeof(char16_t));
82
83 // TODO(Subv): Ask for input and write it to the shared memory
84 // TODO(Subv): Find out what are the possible values for the return code,
85 // some games seem to check for a hardcoded 2
86 config.return_code = 2;
87 config.text_length = 6;
88 config.text_offset = 0;
89
90 // TODO(Subv): We're finalizing the applet immediately after it's started,
91 // but we should defer this call until after all the input has been collected.
92 Finalize();
93}
94
95void SoftwareKeyboard::DrawScreenKeyboard() {
96 auto bottom_screen = Service::GSP::GetFrameBufferInfo(0, 1);
97 auto info = bottom_screen->framebuffer_info[bottom_screen->index];
98
99 // TODO(Subv): Draw the HLE keyboard, for now just zero-fill the framebuffer
100 Memory::ZeroBlock(info.address_left, info.stride * 320);
101
102 Service::GSP::SetBufferSwap(1, info);
103}
104
105void SoftwareKeyboard::Finalize() {
106 // Let the application know that we're closing
107 Service::APT::MessageParameter message;
108 message.buffer.resize(sizeof(SoftwareKeyboardConfig));
109 std::memcpy(message.buffer.data(), &config, message.buffer.size());
110 message.signal = static_cast<u32>(Service::APT::SignalType::WakeupByExit);
111 message.destination_id = static_cast<u32>(Service::APT::AppletId::Application);
112 message.sender_id = static_cast<u32>(id);
113 Service::APT::SendParameter(message);
114
115 is_running = false;
116}
117}
118} // namespace
diff --git a/src/core/hle/applets/swkbd.h b/src/core/hle/applets/swkbd.h
deleted file mode 100644
index cc92a8f19..000000000
--- a/src/core/hle/applets/swkbd.h
+++ /dev/null
@@ -1,85 +0,0 @@
1// Copyright 2015 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 "common/common_funcs.h"
8#include "common/common_types.h"
9#include "core/hle/applets/applet.h"
10#include "core/hle/kernel/kernel.h"
11#include "core/hle/kernel/shared_memory.h"
12#include "core/hle/result.h"
13#include "core/hle/service/apt/apt.h"
14
15namespace HLE {
16namespace Applets {
17
18struct SoftwareKeyboardConfig {
19 INSERT_PADDING_WORDS(0x8);
20
21 u16 max_text_length; ///< Maximum length of the input text
22
23 INSERT_PADDING_BYTES(0x6E);
24
25 char16_t display_text[65]; ///< Text to display when asking the user for input
26
27 INSERT_PADDING_BYTES(0xE);
28
29 u32 default_text_offset; ///< Offset of the default text in the output SharedMemory
30
31 INSERT_PADDING_WORDS(0x3);
32
33 u32 shared_memory_size; ///< Size of the SharedMemory
34
35 INSERT_PADDING_WORDS(0x1);
36
37 u32 return_code; ///< Return code of the SoftwareKeyboard, usually 2, other values are unknown
38
39 INSERT_PADDING_WORDS(0x2);
40
41 u32 text_offset; ///< Offset in the SharedMemory where the output text starts
42 u16 text_length; ///< Length in characters of the output text
43
44 INSERT_PADDING_BYTES(0x2B6);
45};
46
47/**
48 * The size of this structure (0x400) has been verified via reverse engineering of multiple games
49 * that use the software keyboard.
50 */
51static_assert(sizeof(SoftwareKeyboardConfig) == 0x400, "Software Keyboard Config size is wrong");
52
53class SoftwareKeyboard final : public Applet {
54public:
55 SoftwareKeyboard(Service::APT::AppletId id) : Applet(id) {}
56
57 ResultCode ReceiveParameter(const Service::APT::MessageParameter& parameter) override;
58 ResultCode StartImpl(const Service::APT::AppletStartupParameter& parameter) override;
59 void Update() override;
60
61 /**
62 * Draws a keyboard to the current bottom screen framebuffer.
63 */
64 void DrawScreenKeyboard();
65
66 /**
67 * Sends the LibAppletClosing signal to the application,
68 * along with the relevant data buffers.
69 */
70 void Finalize();
71
72private:
73 /// This SharedMemory will be created when we receive the LibAppJustStarted message.
74 /// It holds the framebuffer info retrieved by the application with
75 /// GSPGPU::ImportDisplayCaptureInfo
76 Kernel::SharedPtr<Kernel::SharedMemory> framebuffer_memory;
77
78 /// SharedMemory where the output text will be stored
79 Kernel::SharedPtr<Kernel::SharedMemory> text_memory;
80
81 /// Configuration of this instance of the SoftwareKeyboard, as received from the application
82 SoftwareKeyboardConfig config;
83};
84}
85} // namespace
diff --git a/src/core/hle/service/ac/ac.cpp b/src/core/hle/service/ac/ac.cpp
deleted file mode 100644
index e3dd23949..000000000
--- a/src/core/hle/service/ac/ac.cpp
+++ /dev/null
@@ -1,186 +0,0 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <array>
6
7#include "common/common_types.h"
8#include "common/logging/log.h"
9#include "core/hle/ipc.h"
10#include "core/hle/kernel/event.h"
11#include "core/hle/kernel/handle_table.h"
12#include "core/hle/result.h"
13#include "core/hle/service/ac/ac.h"
14#include "core/hle/service/ac/ac_i.h"
15#include "core/hle/service/ac/ac_u.h"
16#include "core/memory.h"
17
18namespace Service {
19namespace AC {
20
21struct ACConfig {
22 std::array<u8, 0x200> data;
23};
24
25static ACConfig default_config{};
26
27static bool ac_connected = false;
28
29static Kernel::SharedPtr<Kernel::Event> close_event;
30static Kernel::SharedPtr<Kernel::Event> connect_event;
31static Kernel::SharedPtr<Kernel::Event> disconnect_event;
32
33void CreateDefaultConfig(Interface* self) {
34 u32* cmd_buff = Kernel::GetCommandBuffer();
35
36 u32 ac_config_addr = cmd_buff[65];
37
38 ASSERT_MSG(cmd_buff[64] == (sizeof(ACConfig) << 14 | 2),
39 "Output buffer size not equal ACConfig size");
40
41 Memory::WriteBlock(ac_config_addr, &default_config, sizeof(ACConfig));
42 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
43
44 LOG_WARNING(Service_AC, "(STUBBED) called");
45}
46
47void ConnectAsync(Interface* self) {
48 u32* cmd_buff = Kernel::GetCommandBuffer();
49
50 connect_event = Kernel::g_handle_table.Get<Kernel::Event>(cmd_buff[4]);
51 if (connect_event) {
52 connect_event->name = "AC:connect_event";
53 connect_event->Signal();
54 ac_connected = true;
55 }
56 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
57
58 LOG_WARNING(Service_AC, "(STUBBED) called");
59}
60
61void GetConnectResult(Interface* self) {
62 u32* cmd_buff = Kernel::GetCommandBuffer();
63
64 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
65
66 LOG_WARNING(Service_AC, "(STUBBED) called");
67}
68
69void CloseAsync(Interface* self) {
70 u32* cmd_buff = Kernel::GetCommandBuffer();
71
72 if (ac_connected && disconnect_event) {
73 disconnect_event->Signal();
74 }
75
76 close_event = Kernel::g_handle_table.Get<Kernel::Event>(cmd_buff[4]);
77 if (close_event) {
78 close_event->name = "AC:close_event";
79 close_event->Signal();
80 }
81
82 ac_connected = false;
83
84 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
85 LOG_WARNING(Service_AC, "(STUBBED) called");
86}
87
88void GetCloseResult(Interface* self) {
89 u32* cmd_buff = Kernel::GetCommandBuffer();
90
91 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
92
93 LOG_WARNING(Service_AC, "(STUBBED) called");
94}
95
96void GetWifiStatus(Interface* self) {
97 u32* cmd_buff = Kernel::GetCommandBuffer();
98
99 // TODO(purpasmart96): This function is only a stub,
100 // it returns a valid result without implementing full functionality.
101
102 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
103 cmd_buff[2] = 0; // Connection type set to none
104
105 LOG_WARNING(Service_AC, "(STUBBED) called");
106}
107
108void GetInfraPriority(Interface* self) {
109 u32* cmd_buff = Kernel::GetCommandBuffer();
110
111 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
112 cmd_buff[2] = 0; // Infra Priority, default 0
113
114 LOG_WARNING(Service_AC, "(STUBBED) called");
115}
116
117void SetRequestEulaVersion(Interface* self) {
118 u32* cmd_buff = Kernel::GetCommandBuffer();
119
120 u32 major = cmd_buff[1] & 0xFF;
121 u32 minor = cmd_buff[2] & 0xFF;
122
123 ASSERT_MSG(cmd_buff[3] == (sizeof(ACConfig) << 14 | 2),
124 "Input buffer size not equal ACConfig size");
125 ASSERT_MSG(cmd_buff[64] == (sizeof(ACConfig) << 14 | 2),
126 "Output buffer size not equal ACConfig size");
127
128 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
129 cmd_buff[2] = 0; // Infra Priority
130
131 LOG_WARNING(Service_AC, "(STUBBED) called, major=%u, minor=%u", major, minor);
132}
133
134void RegisterDisconnectEvent(Interface* self) {
135 u32* cmd_buff = Kernel::GetCommandBuffer();
136
137 disconnect_event = Kernel::g_handle_table.Get<Kernel::Event>(cmd_buff[4]);
138 if (disconnect_event) {
139 disconnect_event->name = "AC:disconnect_event";
140 }
141 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
142
143 LOG_WARNING(Service_AC, "(STUBBED) called");
144}
145
146void IsConnected(Interface* self) {
147 u32* cmd_buff = Kernel::GetCommandBuffer();
148
149 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
150 cmd_buff[2] = ac_connected;
151
152 LOG_WARNING(Service_AC, "(STUBBED) called");
153}
154
155void SetClientVersion(Interface* self) {
156 u32* cmd_buff = Kernel::GetCommandBuffer();
157
158 const u32 version = cmd_buff[1];
159 self->SetVersion(version);
160
161 LOG_WARNING(Service_AC, "(STUBBED) called, version: 0x%08X", version);
162
163 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
164}
165
166void Init() {
167 AddService(new AC_I);
168 AddService(new AC_U);
169
170 ac_connected = false;
171
172 close_event = nullptr;
173 connect_event = nullptr;
174 disconnect_event = nullptr;
175}
176
177void Shutdown() {
178 ac_connected = false;
179
180 close_event = nullptr;
181 connect_event = nullptr;
182 disconnect_event = nullptr;
183}
184
185} // namespace AC
186} // namespace Service
diff --git a/src/core/hle/service/ac/ac.h b/src/core/hle/service/ac/ac.h
deleted file mode 100644
index 6185faf9b..000000000
--- a/src/core/hle/service/ac/ac.h
+++ /dev/null
@@ -1,134 +0,0 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7namespace Service {
8
9class Interface;
10
11namespace AC {
12
13/**
14 * AC::CreateDefaultConfig service function
15 * Inputs:
16 * 64 : ACConfig size << 14 | 2
17 * 65 : pointer to ACConfig struct
18 * Outputs:
19 * 1 : Result of function, 0 on success, otherwise error code
20 */
21void CreateDefaultConfig(Interface* self);
22
23/**
24 * AC::ConnectAsync service function
25 * Inputs:
26 * 1 : ProcessId Header
27 * 3 : Copy Handle Header
28 * 4 : Connection Event handle
29 * 5 : ACConfig size << 14 | 2
30 * 6 : pointer to ACConfig struct
31 * Outputs:
32 * 1 : Result of function, 0 on success, otherwise error code
33 */
34void ConnectAsync(Interface* self);
35
36/**
37 * AC::GetConnectResult service function
38 * Inputs:
39 * 1 : ProcessId Header
40 * Outputs:
41 * 1 : Result of function, 0 on success, otherwise error code
42 */
43void GetConnectResult(Interface* self);
44
45/**
46 * AC::CloseAsync service function
47 * Inputs:
48 * 1 : ProcessId Header
49 * 3 : Copy Handle Header
50 * 4 : Event handle, should be signaled when AC connection is closed
51 * Outputs:
52 * 1 : Result of function, 0 on success, otherwise error code
53 */
54void CloseAsync(Interface* self);
55
56/**
57 * AC::GetCloseResult service function
58 * Inputs:
59 * 1 : ProcessId Header
60 * Outputs:
61 * 1 : Result of function, 0 on success, otherwise error code
62 */
63void GetCloseResult(Interface* self);
64
65/**
66 * AC::GetWifiStatus service function
67 * Outputs:
68 * 1 : Result of function, 0 on success, otherwise error code
69 * 2 : Output connection type, 0 = none, 1 = Old3DS Internet, 2 = New3DS Internet.
70 */
71void GetWifiStatus(Interface* self);
72
73/**
74 * AC::GetInfraPriority service function
75 * Inputs:
76 * 1 : ACConfig size << 14 | 2
77 * 2 : pointer to ACConfig struct
78 * Outputs:
79 * 1 : Result of function, 0 on success, otherwise error code
80 * 2 : Infra Priority
81 */
82void GetInfraPriority(Interface* self);
83
84/**
85 * AC::SetRequestEulaVersion service function
86 * Inputs:
87 * 1 : Eula Version major
88 * 2 : Eula Version minor
89 * 3 : ACConfig size << 14 | 2
90 * 4 : Input pointer to ACConfig struct
91 * 64 : ACConfig size << 14 | 2
92 * 65 : Output pointer to ACConfig struct
93 * Outputs:
94 * 1 : Result of function, 0 on success, otherwise error code
95 * 2 : Infra Priority
96 */
97void SetRequestEulaVersion(Interface* self);
98
99/**
100 * AC::RegisterDisconnectEvent service function
101 * Inputs:
102 * 1 : ProcessId Header
103 * 3 : Copy Handle Header
104 * 4 : Event handle, should be signaled when AC connection is closed
105 * Outputs:
106 * 1 : Result of function, 0 on success, otherwise error code
107 */
108void RegisterDisconnectEvent(Interface* self);
109
110/**
111 * AC::IsConnected service function
112 * Outputs:
113 * 1 : Result of function, 0 on success, otherwise error code
114 * 2 : bool, is connected
115 */
116void IsConnected(Interface* self);
117
118/**
119 * AC::SetClientVersion service function
120 * Inputs:
121 * 1 : Used SDK Version
122 * Outputs:
123 * 1 : Result of function, 0 on success, otherwise error code
124 */
125void SetClientVersion(Interface* self);
126
127/// Initialize AC service
128void Init();
129
130/// Shutdown AC service
131void Shutdown();
132
133} // namespace AC
134} // namespace Service
diff --git a/src/core/hle/service/ac/ac_i.cpp b/src/core/hle/service/ac/ac_i.cpp
deleted file mode 100644
index b22fe3698..000000000
--- a/src/core/hle/service/ac/ac_i.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/ac/ac.h"
6#include "core/hle/service/ac/ac_i.h"
7
8namespace Service {
9namespace AC {
10
11const Interface::FunctionInfo FunctionTable[] = {
12 {0x00010000, CreateDefaultConfig, "CreateDefaultConfig"},
13 {0x00040006, ConnectAsync, "ConnectAsync"},
14 {0x00050002, GetConnectResult, "GetConnectResult"},
15 {0x00070002, nullptr, "CancelConnectAsync"},
16 {0x00080004, CloseAsync, "CloseAsync"},
17 {0x00090002, GetCloseResult, "GetCloseResult"},
18 {0x000A0000, nullptr, "GetLastErrorCode"},
19 {0x000C0000, nullptr, "GetStatus"},
20 {0x000D0000, GetWifiStatus, "GetWifiStatus"},
21 {0x000E0042, nullptr, "GetCurrentAPInfo"},
22 {0x00100042, nullptr, "GetCurrentNZoneInfo"},
23 {0x00110042, nullptr, "GetNZoneApNumService"},
24 {0x001D0042, nullptr, "ScanAPs"},
25 {0x00240042, nullptr, "AddDenyApType"},
26 {0x00270002, GetInfraPriority, "GetInfraPriority"},
27 {0x002D0082, SetRequestEulaVersion, "SetRequestEulaVersion"},
28 {0x00300004, RegisterDisconnectEvent, "RegisterDisconnectEvent"},
29 {0x003C0042, nullptr, "GetAPSSIDList"},
30 {0x003E0042, IsConnected, "IsConnected"},
31 {0x00400042, SetClientVersion, "SetClientVersion"},
32};
33
34AC_I::AC_I() {
35 Register(FunctionTable);
36}
37
38} // namespace AC
39} // namespace Service
diff --git a/src/core/hle/service/ac/ac_i.h b/src/core/hle/service/ac/ac_i.h
deleted file mode 100644
index 465bba59c..000000000
--- a/src/core/hle/service/ac/ac_i.h
+++ /dev/null
@@ -1,22 +0,0 @@
1// Copyright 2016 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 "core/hle/service/service.h"
8
9namespace Service {
10namespace AC {
11
12class AC_I final : public Interface {
13public:
14 AC_I();
15
16 std::string GetPortName() const override {
17 return "ac:i";
18 }
19};
20
21} // namespace AC
22} // namespace Service
diff --git a/src/core/hle/service/ac/ac_u.cpp b/src/core/hle/service/ac/ac_u.cpp
deleted file mode 100644
index 346671b4a..000000000
--- a/src/core/hle/service/ac/ac_u.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/ac/ac.h"
6#include "core/hle/service/ac/ac_u.h"
7
8namespace Service {
9namespace AC {
10
11const Interface::FunctionInfo FunctionTable[] = {
12 {0x00010000, CreateDefaultConfig, "CreateDefaultConfig"},
13 {0x00040006, ConnectAsync, "ConnectAsync"},
14 {0x00050002, GetConnectResult, "GetConnectResult"},
15 {0x00070002, nullptr, "CancelConnectAsync"},
16 {0x00080004, CloseAsync, "CloseAsync"},
17 {0x00090002, GetCloseResult, "GetCloseResult"},
18 {0x000A0000, nullptr, "GetLastErrorCode"},
19 {0x000C0000, nullptr, "GetStatus"},
20 {0x000D0000, GetWifiStatus, "GetWifiStatus"},
21 {0x000E0042, nullptr, "GetCurrentAPInfo"},
22 {0x00100042, nullptr, "GetCurrentNZoneInfo"},
23 {0x00110042, nullptr, "GetNZoneApNumService"},
24 {0x001D0042, nullptr, "ScanAPs"},
25 {0x00240042, nullptr, "AddDenyApType"},
26 {0x00270002, GetInfraPriority, "GetInfraPriority"},
27 {0x002D0082, SetRequestEulaVersion, "SetRequestEulaVersion"},
28 {0x00300004, RegisterDisconnectEvent, "RegisterDisconnectEvent"},
29 {0x003C0042, nullptr, "GetAPSSIDList"},
30 {0x003E0042, IsConnected, "IsConnected"},
31 {0x00400042, SetClientVersion, "SetClientVersion"},
32};
33
34AC_U::AC_U() {
35 Register(FunctionTable);
36}
37
38} // namespace AC
39} // namespace Service
diff --git a/src/core/hle/service/ac/ac_u.h b/src/core/hle/service/ac/ac_u.h
deleted file mode 100644
index f9d21e112..000000000
--- a/src/core/hle/service/ac/ac_u.h
+++ /dev/null
@@ -1,22 +0,0 @@
1// Copyright 2014 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 "core/hle/service/service.h"
8
9namespace Service {
10namespace AC {
11
12class AC_U final : public Interface {
13public:
14 AC_U();
15
16 std::string GetPortName() const override {
17 return "ac:u";
18 }
19};
20
21} // namespace AC
22} // namespace Service
diff --git a/src/core/hle/service/act/act.cpp b/src/core/hle/service/act/act.cpp
deleted file mode 100644
index 9600036c0..000000000
--- a/src/core/hle/service/act/act.cpp
+++ /dev/null
@@ -1,18 +0,0 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/act/act.h"
6#include "core/hle/service/act/act_a.h"
7#include "core/hle/service/act/act_u.h"
8
9namespace Service {
10namespace ACT {
11
12void Init() {
13 AddService(new ACT_A);
14 AddService(new ACT_U);
15}
16
17} // namespace ACT
18} // namespace Service
diff --git a/src/core/hle/service/act/act.h b/src/core/hle/service/act/act.h
deleted file mode 100644
index 1425291aa..000000000
--- a/src/core/hle/service/act/act.h
+++ /dev/null
@@ -1,14 +0,0 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7namespace Service {
8namespace ACT {
9
10/// Initializes all ACT services
11void Init();
12
13} // namespace ACT
14} // namespace Service
diff --git a/src/core/hle/service/act/act_a.cpp b/src/core/hle/service/act/act_a.cpp
deleted file mode 100644
index 5c523368f..000000000
--- a/src/core/hle/service/act/act_a.cpp
+++ /dev/null
@@ -1,30 +0,0 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/act/act.h"
6#include "core/hle/service/act/act_a.h"
7
8namespace Service {
9namespace ACT {
10
11const Interface::FunctionInfo FunctionTable[] = {
12 // act:u shared commands
13 {0x00010084, nullptr, "Initialize"},
14 {0x00020040, nullptr, "GetErrorCode"},
15 {0x000600C2, nullptr, "GetAccountDataBlock"},
16 {0x000B0042, nullptr, "AcquireEulaList"},
17 {0x000D0040, nullptr, "GenerateUuid"},
18 // act:a
19 {0x041300C2, nullptr, "UpdateMiiImage"},
20 {0x041B0142, nullptr, "AgreeEula"},
21 {0x04210042, nullptr, "UploadMii"},
22 {0x04230082, nullptr, "ValidateMailAddress"},
23};
24
25ACT_A::ACT_A() {
26 Register(FunctionTable);
27}
28
29} // namespace ACT
30} // namespace Service
diff --git a/src/core/hle/service/act/act_a.h b/src/core/hle/service/act/act_a.h
deleted file mode 100644
index e3adb03e5..000000000
--- a/src/core/hle/service/act/act_a.h
+++ /dev/null
@@ -1,22 +0,0 @@
1// Copyright 2016 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 "core/hle/service/service.h"
8
9namespace Service {
10namespace ACT {
11
12class ACT_A final : public Service::Interface {
13public:
14 ACT_A();
15
16 std::string GetPortName() const override {
17 return "act:a";
18 }
19};
20
21} // namespace ACT
22} // namespace Service
diff --git a/src/core/hle/service/act/act_u.cpp b/src/core/hle/service/act/act_u.cpp
deleted file mode 100644
index cf98aa1d6..000000000
--- a/src/core/hle/service/act/act_u.cpp
+++ /dev/null
@@ -1,26 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/act/act.h"
6#include "core/hle/service/act/act_u.h"
7
8namespace Service {
9namespace ACT {
10
11const Interface::FunctionInfo FunctionTable[] = {
12 // clang-format off
13 {0x00010084, nullptr, "Initialize"},
14 {0x00020040, nullptr, "GetErrorCode"},
15 {0x000600C2, nullptr, "GetAccountDataBlock"},
16 {0x000B0042, nullptr, "AcquireEulaList"},
17 {0x000D0040, nullptr, "GenerateUuid"},
18 // clang-format on
19};
20
21ACT_U::ACT_U() {
22 Register(FunctionTable);
23}
24
25} // namespace ACT
26} // namespace Service
diff --git a/src/core/hle/service/act/act_u.h b/src/core/hle/service/act/act_u.h
deleted file mode 100644
index 9d8538fbf..000000000
--- a/src/core/hle/service/act/act_u.h
+++ /dev/null
@@ -1,22 +0,0 @@
1// Copyright 2014 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 "core/hle/service/service.h"
8
9namespace Service {
10namespace ACT {
11
12class ACT_U final : public Interface {
13public:
14 ACT_U();
15
16 std::string GetPortName() const override {
17 return "act:u";
18 }
19};
20
21} // namespace ACT
22} // namespace Service
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
deleted file mode 100644
index 961305e9f..000000000
--- a/src/core/hle/service/am/am.cpp
+++ /dev/null
@@ -1,193 +0,0 @@
1// Copyright 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <array>
6#include <cinttypes>
7#include "common/common_types.h"
8#include "common/logging/log.h"
9#include "core/hle/ipc.h"
10#include "core/hle/result.h"
11#include "core/hle/service/am/am.h"
12#include "core/hle/service/am/am_app.h"
13#include "core/hle/service/am/am_net.h"
14#include "core/hle/service/am/am_sys.h"
15#include "core/hle/service/am/am_u.h"
16#include "core/hle/service/service.h"
17
18namespace Service {
19namespace AM {
20
21static std::array<u32, 3> am_content_count = {0, 0, 0};
22static std::array<u32, 3> am_titles_count = {0, 0, 0};
23static std::array<u32, 3> am_titles_list_count = {0, 0, 0};
24static u32 am_ticket_count = 0;
25static u32 am_ticket_list_count = 0;
26
27void GetNumPrograms(Service::Interface* self) {
28 u32* cmd_buff = Kernel::GetCommandBuffer();
29
30 u32 media_type = cmd_buff[1] & 0xFF;
31
32 cmd_buff[1] = RESULT_SUCCESS.raw;
33 cmd_buff[2] = am_titles_count[media_type];
34 LOG_WARNING(Service_AM, "(STUBBED) media_type=%u, title_count=0x%08x", media_type,
35 am_titles_count[media_type]);
36}
37
38void FindContentInfos(Service::Interface* self) {
39 u32* cmd_buff = Kernel::GetCommandBuffer();
40
41 u32 media_type = cmd_buff[1] & 0xFF;
42 u64 title_id = (static_cast<u64>(cmd_buff[3]) << 32) | cmd_buff[2];
43 u32 content_ids_pointer = cmd_buff[6];
44 u32 content_info_pointer = cmd_buff[8];
45
46 am_content_count[media_type] = cmd_buff[4];
47
48 cmd_buff[1] = RESULT_SUCCESS.raw;
49 LOG_WARNING(Service_AM, "(STUBBED) media_type=%u, title_id=0x%016llx, content_cound=%u, "
50 "content_ids_pointer=0x%08x, content_info_pointer=0x%08x",
51 media_type, title_id, am_content_count[media_type], content_ids_pointer,
52 content_info_pointer);
53}
54
55void ListContentInfos(Service::Interface* self) {
56 u32* cmd_buff = Kernel::GetCommandBuffer();
57
58 u32 media_type = cmd_buff[2] & 0xFF;
59 u64 title_id = (static_cast<u64>(cmd_buff[4]) << 32) | cmd_buff[3];
60 u32 start_index = cmd_buff[5];
61 u32 content_info_pointer = cmd_buff[7];
62
63 am_content_count[media_type] = cmd_buff[1];
64
65 cmd_buff[1] = RESULT_SUCCESS.raw;
66 cmd_buff[2] = am_content_count[media_type];
67 LOG_WARNING(Service_AM, "(STUBBED) media_type=%u, content_count=%u, title_id=0x%016" PRIx64
68 ", start_index=0x%08x, content_info_pointer=0x%08X",
69 media_type, am_content_count[media_type], title_id, start_index,
70 content_info_pointer);
71}
72
73void DeleteContents(Service::Interface* self) {
74 u32* cmd_buff = Kernel::GetCommandBuffer();
75
76 u32 media_type = cmd_buff[1] & 0xFF;
77 u64 title_id = (static_cast<u64>(cmd_buff[3]) << 32) | cmd_buff[2];
78 u32 content_ids_pointer = cmd_buff[6];
79
80 am_content_count[media_type] = cmd_buff[4];
81
82 cmd_buff[1] = RESULT_SUCCESS.raw;
83 LOG_WARNING(Service_AM, "(STUBBED) media_type=%u, title_id=0x%016" PRIx64
84 ", content_count=%u, content_ids_pointer=0x%08x",
85 media_type, title_id, am_content_count[media_type], content_ids_pointer);
86}
87
88void GetProgramList(Service::Interface* self) {
89 u32* cmd_buff = Kernel::GetCommandBuffer();
90
91 u32 media_type = cmd_buff[2] & 0xFF;
92 u32 title_ids_output_pointer = cmd_buff[4];
93
94 am_titles_list_count[media_type] = cmd_buff[1];
95
96 cmd_buff[1] = RESULT_SUCCESS.raw;
97 cmd_buff[2] = am_titles_list_count[media_type];
98 LOG_WARNING(
99 Service_AM,
100 "(STUBBED) media_type=%u, titles_list_count=0x%08X, title_ids_output_pointer=0x%08X",
101 media_type, am_titles_list_count[media_type], title_ids_output_pointer);
102}
103
104void GetProgramInfos(Service::Interface* self) {
105 u32* cmd_buff = Kernel::GetCommandBuffer();
106
107 u32 media_type = cmd_buff[1] & 0xFF;
108 u32 title_id_list_pointer = cmd_buff[4];
109 u32 title_list_pointer = cmd_buff[6];
110
111 am_titles_count[media_type] = cmd_buff[2];
112
113 cmd_buff[1] = RESULT_SUCCESS.raw;
114 LOG_WARNING(Service_AM, "(STUBBED) media_type=%u, total_titles=0x%08X, "
115 "title_id_list_pointer=0x%08X, title_list_pointer=0x%08X",
116 media_type, am_titles_count[media_type], title_id_list_pointer, title_list_pointer);
117}
118
119void GetDataTitleInfos(Service::Interface* self) {
120 GetProgramInfos(self);
121
122 LOG_WARNING(Service_AM, "(STUBBED) called");
123}
124
125void ListDataTitleTicketInfos(Service::Interface* self) {
126 u32* cmd_buff = Kernel::GetCommandBuffer();
127
128 u64 title_id = (static_cast<u64>(cmd_buff[3]) << 32) | cmd_buff[2];
129 u32 start_index = cmd_buff[4];
130 u32 ticket_info_pointer = cmd_buff[6];
131
132 am_ticket_count = cmd_buff[1];
133
134 cmd_buff[1] = RESULT_SUCCESS.raw;
135 cmd_buff[2] = am_ticket_count;
136 LOG_WARNING(Service_AM, "(STUBBED) ticket_count=0x%08X, title_id=0x%016" PRIx64
137 ", start_index=0x%08X, ticket_info_pointer=0x%08X",
138 am_ticket_count, title_id, start_index, ticket_info_pointer);
139}
140
141void GetNumContentInfos(Service::Interface* self) {
142 u32* cmd_buff = Kernel::GetCommandBuffer();
143
144 cmd_buff[1] = RESULT_SUCCESS.raw;
145 cmd_buff[2] = 1; // Number of content infos plus one
146 LOG_WARNING(Service_AM, "(STUBBED) called");
147}
148
149void DeleteTicket(Service::Interface* self) {
150 u32* cmd_buff = Kernel::GetCommandBuffer();
151
152 u64 title_id = (static_cast<u64>(cmd_buff[2]) << 32) | cmd_buff[1];
153
154 cmd_buff[1] = RESULT_SUCCESS.raw;
155 LOG_WARNING(Service_AM, "(STUBBED) called title_id=0x%016" PRIx64 "", title_id);
156}
157
158void GetNumTickets(Service::Interface* self) {
159 u32* cmd_buff = Kernel::GetCommandBuffer();
160
161 cmd_buff[1] = RESULT_SUCCESS.raw;
162 cmd_buff[2] = am_ticket_count;
163 LOG_WARNING(Service_AM, "(STUBBED) called ticket_count=0x%08x", am_ticket_count);
164}
165
166void GetTicketList(Service::Interface* self) {
167 u32* cmd_buff = Kernel::GetCommandBuffer();
168
169 u32 num_of_skip = cmd_buff[2];
170 u32 ticket_list_pointer = cmd_buff[4];
171
172 am_ticket_list_count = cmd_buff[1];
173
174 cmd_buff[1] = RESULT_SUCCESS.raw;
175 cmd_buff[2] = am_ticket_list_count;
176 LOG_WARNING(
177 Service_AM,
178 "(STUBBED) ticket_list_count=0x%08x, num_of_skip=0x%08x, ticket_list_pointer=0x%08x",
179 am_ticket_list_count, num_of_skip, ticket_list_pointer);
180}
181
182void Init() {
183 AddService(new AM_APP_Interface);
184 AddService(new AM_NET_Interface);
185 AddService(new AM_SYS_Interface);
186 AddService(new AM_U_Interface);
187}
188
189void Shutdown() {}
190
191} // namespace AM
192
193} // namespace Service
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
deleted file mode 100644
index 9bc2ca305..000000000
--- a/src/core/hle/service/am/am.h
+++ /dev/null
@@ -1,164 +0,0 @@
1// Copyright 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7namespace Service {
8
9class Interface;
10
11namespace AM {
12
13/**
14 * AM::GetNumPrograms service function
15 * Gets the number of installed titles in the requested media type
16 * Inputs:
17 * 0 : Command header (0x00010040)
18 * 1 : Media type to load the titles from
19 * Outputs:
20 * 1 : Result, 0 on success, otherwise error code
21 * 2 : The number of titles in the requested media type
22 */
23void GetNumPrograms(Service::Interface* self);
24
25/**
26 * AM::FindContentInfos service function
27 * Inputs:
28 * 1 : MediaType
29 * 2-3 : u64, Title ID
30 * 4 : Content count
31 * 6 : Content IDs pointer
32 * 8 : Content Infos pointer
33 * Outputs:
34 * 1 : Result, 0 on success, otherwise error code
35 */
36void FindContentInfos(Service::Interface* self);
37
38/**
39 * AM::ListContentInfos service function
40 * Inputs:
41 * 1 : Content count
42 * 2 : MediaType
43 * 3-4 : u64, Title ID
44 * 5 : Start Index
45 * 7 : Content Infos pointer
46 * Outputs:
47 * 1 : Result, 0 on success, otherwise error code
48 * 2 : Number of content infos returned
49 */
50void ListContentInfos(Service::Interface* self);
51
52/**
53 * AM::DeleteContents service function
54 * Inputs:
55 * 1 : MediaType
56 * 2-3 : u64, Title ID
57 * 4 : Content count
58 * 6 : Content IDs pointer
59 * Outputs:
60 * 1 : Result, 0 on success, otherwise error code
61 */
62void DeleteContents(Service::Interface* self);
63
64/**
65 * AM::GetProgramList service function
66 * Loads information about the desired number of titles from the desired media type into an array
67 * Inputs:
68 * 1 : Title count
69 * 2 : Media type to load the titles from
70 * 4 : Title IDs output pointer
71 * Outputs:
72 * 1 : Result, 0 on success, otherwise error code
73 * 2 : The number of titles loaded from the requested media type
74 */
75void GetProgramList(Service::Interface* self);
76
77/**
78 * AM::GetProgramInfos service function
79 * Inputs:
80 * 1 : u8 Mediatype
81 * 2 : Total titles
82 * 4 : TitleIDList pointer
83 * 6 : TitleList pointer
84 * Outputs:
85 * 1 : Result, 0 on success, otherwise error code
86 */
87void GetProgramInfos(Service::Interface* self);
88
89/**
90 * AM::GetDataTitleInfos service function
91 * Wrapper for AM::GetProgramInfos
92 * Inputs:
93 * 1 : u8 Mediatype
94 * 2 : Total titles
95 * 4 : TitleIDList pointer
96 * 6 : TitleList pointer
97 * Outputs:
98 * 1 : Result, 0 on success, otherwise error code
99 */
100void GetDataTitleInfos(Service::Interface* self);
101
102/**
103 * AM::ListDataTitleTicketInfos service function
104 * Inputs:
105 * 1 : Ticket count
106 * 2-3 : u64, Title ID
107 * 4 : Start Index?
108 * 5 : (TicketCount * 24) << 8 | 0x4
109 * 6 : Ticket Infos pointer
110 * Outputs:
111 * 1 : Result, 0 on success, otherwise error code
112 * 2 : Number of ticket infos returned
113 */
114void ListDataTitleTicketInfos(Service::Interface* self);
115
116/**
117 * AM::GetNumContentInfos service function
118 * Inputs:
119 * 0 : Command header (0x100100C0)
120 * 1 : MediaType
121 * 2-3 : u64, Title ID
122 * Outputs:
123 * 1 : Result, 0 on success, otherwise error code
124 * 2 : Number of content infos plus one
125 */
126void GetNumContentInfos(Service::Interface* self);
127
128/**
129 * AM::DeleteTicket service function
130 * Inputs:
131 * 1-2 : u64, Title ID
132 * Outputs:
133 * 1 : Result, 0 on success, otherwise error code
134 */
135void DeleteTicket(Service::Interface* self);
136
137/**
138 * AM::GetNumTickets service function
139 * Outputs:
140 * 1 : Result, 0 on success, otherwise error code
141 * 2 : Number of tickets
142 */
143void GetNumTickets(Service::Interface* self);
144
145/**
146 * AM::GetTicketList service function
147 * Inputs:
148 * 1 : Number of TicketList
149 * 2 : Number to skip
150 * 4 : TicketList pointer
151 * Outputs:
152 * 1 : Result, 0 on success, otherwise error code
153 * 2 : Total TicketList
154 */
155void GetTicketList(Service::Interface* self);
156
157/// Initialize AM service
158void Init();
159
160/// Shutdown AM service
161void Shutdown();
162
163} // namespace AM
164} // namespace Service
diff --git a/src/core/hle/service/am/am_app.cpp b/src/core/hle/service/am/am_app.cpp
deleted file mode 100644
index 218375c8f..000000000
--- a/src/core/hle/service/am/am_app.cpp
+++ /dev/null
@@ -1,32 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/am/am.h"
6#include "core/hle/service/am/am_app.h"
7
8namespace Service {
9namespace AM {
10
11const Interface::FunctionInfo FunctionTable[] = {
12 {0x100100C0, GetNumContentInfos, "GetNumContentInfos"},
13 {0x10020104, FindContentInfos, "FindContentInfos"},
14 {0x10030142, ListContentInfos, "ListContentInfos"},
15 {0x10040102, DeleteContents, "DeleteContents"},
16 {0x10050084, GetDataTitleInfos, "GetDataTitleInfos"},
17 {0x10060080, nullptr, "GetNumDataTitleTickets"},
18 {0x10070102, ListDataTitleTicketInfos, "ListDataTitleTicketInfos"},
19 {0x100801C2, nullptr, "GetItemRights"},
20 {0x100900C0, nullptr, "IsDataTitleInUse"},
21 {0x100A0000, nullptr, "IsExternalTitleDatabaseInitialized"},
22 {0x100B00C0, nullptr, "GetNumExistingContentInfos"},
23 {0x100C0142, nullptr, "ListExistingContentInfos"},
24 {0x100D0084, nullptr, "GetPatchTitleInfos"},
25};
26
27AM_APP_Interface::AM_APP_Interface() {
28 Register(FunctionTable);
29}
30
31} // namespace AM
32} // namespace Service
diff --git a/src/core/hle/service/am/am_app.h b/src/core/hle/service/am/am_app.h
deleted file mode 100644
index fd6017d14..000000000
--- a/src/core/hle/service/am/am_app.h
+++ /dev/null
@@ -1,22 +0,0 @@
1// Copyright 2014 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 "core/hle/service/service.h"
8
9namespace Service {
10namespace AM {
11
12class AM_APP_Interface : public Service::Interface {
13public:
14 AM_APP_Interface();
15
16 std::string GetPortName() const override {
17 return "am:app";
18 }
19};
20
21} // namespace AM
22} // namespace Service
diff --git a/src/core/hle/service/am/am_net.cpp b/src/core/hle/service/am/am_net.cpp
deleted file mode 100644
index f3cd1d23f..000000000
--- a/src/core/hle/service/am/am_net.cpp
+++ /dev/null
@@ -1,129 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/am/am.h"
6#include "core/hle/service/am/am_net.h"
7
8namespace Service {
9namespace AM {
10
11const Interface::FunctionInfo FunctionTable[] = {
12 {0x00010040, GetNumPrograms, "GetNumPrograms"},
13 {0x00020082, GetProgramList, "GetProgramList"},
14 {0x00030084, GetProgramInfos, "GetProgramInfos"},
15 {0x000400C0, nullptr, "DeleteUserProgram"},
16 {0x000500C0, nullptr, "GetProductCode"},
17 {0x000600C0, nullptr, "GetStorageId"},
18 {0x00070080, DeleteTicket, "DeleteTicket"},
19 {0x00080000, GetNumTickets, "GetNumTickets"},
20 {0x00090082, GetTicketList, "GetTicketList"},
21 {0x000A0000, nullptr, "GetDeviceID"},
22 {0x000B0040, nullptr, "GetNumImportTitleContexts"},
23 {0x000C0082, nullptr, "GetImportTitleContextList"},
24 {0x000D0084, nullptr, "GetImportTitleContexts"},
25 {0x000E00C0, nullptr, "DeleteImportTitleContext"},
26 {0x000F00C0, nullptr, "GetNumImportContentContexts"},
27 {0x00100102, nullptr, "GetImportContentContextList"},
28 {0x00110104, nullptr, "GetImportContentContexts"},
29 {0x00120102, nullptr, "DeleteImportContentContexts"},
30 {0x00130040, nullptr, "NeedsCleanup"},
31 {0x00140040, nullptr, "DoCleanup"},
32 {0x00150040, nullptr, "DeleteAllImportContexts"},
33 {0x00160000, nullptr, "DeleteAllTemporaryPrograms"},
34 {0x00170044, nullptr, "ImportTwlBackupLegacy"},
35 {0x00180080, nullptr, "InitializeTitleDatabase"},
36 {0x00190040, nullptr, "QueryAvailableTitleDatabase"},
37 {0x001A00C0, nullptr, "CalcTwlBackupSize"},
38 {0x001B0144, nullptr, "ExportTwlBackup"},
39 {0x001C0084, nullptr, "ImportTwlBackup"},
40 {0x001D0000, nullptr, "DeleteAllTwlUserPrograms"},
41 {0x001E00C8, nullptr, "ReadTwlBackupInfo"},
42 {0x001F0040, nullptr, "DeleteAllExpiredUserPrograms"},
43 {0x00200000, nullptr, "GetTwlArchiveResourceInfo"},
44 {0x00210042, nullptr, "GetPersonalizedTicketInfoList"},
45 {0x00220080, nullptr, "DeleteAllImportContextsFiltered"},
46 {0x00230080, nullptr, "GetNumImportTitleContextsFiltered"},
47 {0x002400C2, nullptr, "GetImportTitleContextListFiltered"},
48 {0x002500C0, nullptr, "CheckContentRights"},
49 {0x00260044, nullptr, "GetTicketLimitInfos"},
50 {0x00270044, nullptr, "GetDemoLaunchInfos"},
51 {0x00280108, nullptr, "ReadTwlBackupInfoEx"},
52 {0x00290082, nullptr, "DeleteUserProgramsAtomically"},
53 {0x002A00C0, nullptr, "GetNumExistingContentInfosSystem"},
54 {0x002B0142, nullptr, "ListExistingContentInfosSystem"},
55 {0x002C0084, nullptr, "GetProgramInfosIgnorePlatform"},
56 {0x002D00C0, nullptr, "CheckContentRightsIgnorePlatform"},
57 {0x04010080, nullptr, "UpdateFirmwareTo"},
58 {0x04020040, nullptr, "BeginImportProgram"},
59 {0x04030000, nullptr, "BeginImportProgramTemporarily"},
60 {0x04040002, nullptr, "CancelImportProgram"},
61 {0x04050002, nullptr, "EndImportProgram"},
62 {0x04060002, nullptr, "EndImportProgramWithoutCommit"},
63 {0x040700C2, nullptr, "CommitImportPrograms"},
64 {0x04080042, nullptr, "GetProgramInfoFromCia"},
65 {0x04090004, nullptr, "GetSystemMenuDataFromCia"},
66 {0x040A0002, nullptr, "GetDependencyListFromCia"},
67 {0x040B0002, nullptr, "GetTransferSizeFromCia"},
68 {0x040C0002, nullptr, "GetCoreVersionFromCia"},
69 {0x040D0042, nullptr, "GetRequiredSizeFromCia"},
70 {0x040E00C2, nullptr, "CommitImportProgramsAndUpdateFirmwareAuto"},
71 {0x040F0000, nullptr, "UpdateFirmwareAuto"},
72 {0x041000C0, nullptr, "DeleteProgram"},
73 {0x04110044, nullptr, "GetTwlProgramListForReboot"},
74 {0x04120000, nullptr, "GetSystemUpdaterMutex"},
75 {0x04130002, nullptr, "GetMetaSizeFromCia"},
76 {0x04140044, nullptr, "GetMetaDataFromCia"},
77 {0x04150080, nullptr, "CheckDemoLaunchRights"},
78 {0x041600C0, nullptr, "GetInternalTitleLocationInfo"},
79 {0x041700C0, nullptr, "PerpetuateAgbSaveData"},
80 {0x04180040, nullptr, "BeginImportProgramForOverWrite"},
81 {0x04190000, nullptr, "BeginImportSystemProgram"},
82 {0x08010000, nullptr, "BeginImportTicket"},
83 {0x08020002, nullptr, "CancelImportTicket"},
84 {0x08030002, nullptr, "EndImportTicket"},
85 {0x08040100, nullptr, "BeginImportTitle"},
86 {0x08050000, nullptr, "StopImportTitle"},
87 {0x080600C0, nullptr, "ResumeImportTitle"},
88 {0x08070000, nullptr, "CancelImportTitle"},
89 {0x08080000, nullptr, "EndImportTitle"},
90 {0x080900C2, nullptr, "CommitImportTitles"},
91 {0x080A0000, nullptr, "BeginImportTmd"},
92 {0x080B0002, nullptr, "CancelImportTmd"},
93 {0x080C0042, nullptr, "EndImportTmd"},
94 {0x080D0042, nullptr, "CreateImportContentContexts"},
95 {0x080E0040, nullptr, "BeginImportContent"},
96 {0x080F0002, nullptr, "StopImportContent"},
97 {0x08100040, nullptr, "ResumeImportContent"},
98 {0x08110002, nullptr, "CancelImportContent"},
99 {0x08120002, nullptr, "EndImportContent"},
100 {0x08130000, nullptr, "GetNumCurrentImportContentContexts"},
101 {0x08140042, nullptr, "GetCurrentImportContentContextList"},
102 {0x08150044, nullptr, "GetCurrentImportContentContexts"},
103 {0x08160146, nullptr, "Sign"},
104 {0x08170146, nullptr, "Verify"},
105 {0x08180042, nullptr, "GetDeviceCert"},
106 {0x08190108, nullptr, "ImportCertificates"},
107 {0x081A0042, nullptr, "ImportCertificate"},
108 {0x081B00C2, nullptr, "CommitImportTitlesAndUpdateFirmwareAuto"},
109 {0x081C0100, nullptr, "DeleteTicketId"},
110 {0x081D0080, nullptr, "GetNumTicketIds"},
111 {0x081E0102, nullptr, "GetTicketIdList"},
112 {0x081F0080, nullptr, "GetNumTicketsOfProgram"},
113 {0x08200102, nullptr, "ListTicketInfos"},
114 {0x08210142, nullptr, "GetRightsOnlyTicketData"},
115 {0x08220000, nullptr, "GetNumCurrentContentInfos"},
116 {0x08230044, nullptr, "FindCurrentContentInfos"},
117 {0x08240082, nullptr, "ListCurrentContentInfos"},
118 {0x08250102, nullptr, "CalculateContextRequiredSize"},
119 {0x08260042, nullptr, "UpdateImportContentContexts"},
120 {0x08270000, nullptr, "DeleteAllDemoLaunchInfos"},
121 {0x082800C0, nullptr, "BeginImportTitleForOverWrite"},
122};
123
124AM_NET_Interface::AM_NET_Interface() {
125 Register(FunctionTable);
126}
127
128} // namespace AM
129} // namespace Service
diff --git a/src/core/hle/service/am/am_net.h b/src/core/hle/service/am/am_net.h
deleted file mode 100644
index 25d2c3f23..000000000
--- a/src/core/hle/service/am/am_net.h
+++ /dev/null
@@ -1,22 +0,0 @@
1// Copyright 2014 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 "core/hle/service/service.h"
8
9namespace Service {
10namespace AM {
11
12class AM_NET_Interface : public Service::Interface {
13public:
14 AM_NET_Interface();
15
16 std::string GetPortName() const override {
17 return "am:net";
18 }
19};
20
21} // namespace AM
22} // namespace Service
diff --git a/src/core/hle/service/am/am_sys.cpp b/src/core/hle/service/am/am_sys.cpp
deleted file mode 100644
index 949b3591d..000000000
--- a/src/core/hle/service/am/am_sys.cpp
+++ /dev/null
@@ -1,77 +0,0 @@
1// Copyright 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/am/am.h"
6#include "core/hle/service/am/am_sys.h"
7
8namespace Service {
9namespace AM {
10
11const Interface::FunctionInfo FunctionTable[] = {
12 {0x00010040, GetNumPrograms, "GetNumPrograms"},
13 {0x00020082, GetProgramList, "GetProgramList"},
14 {0x00030084, GetProgramInfos, "GetProgramInfos"},
15 {0x000400C0, nullptr, "DeleteUserProgram"},
16 {0x000500C0, nullptr, "GetProductCode"},
17 {0x000600C0, nullptr, "GetStorageId"},
18 {0x00070080, DeleteTicket, "DeleteTicket"},
19 {0x00080000, GetNumTickets, "GetNumTickets"},
20 {0x00090082, GetTicketList, "GetTicketList"},
21 {0x000A0000, nullptr, "GetDeviceID"},
22 {0x000B0040, nullptr, "GetNumImportTitleContexts"},
23 {0x000C0082, nullptr, "GetImportTitleContextList"},
24 {0x000D0084, nullptr, "GetImportTitleContexts"},
25 {0x000E00C0, nullptr, "DeleteImportTitleContext"},
26 {0x000F00C0, nullptr, "GetNumImportContentContexts"},
27 {0x00100102, nullptr, "GetImportContentContextList"},
28 {0x00110104, nullptr, "GetImportContentContexts"},
29 {0x00120102, nullptr, "DeleteImportContentContexts"},
30 {0x00130040, nullptr, "NeedsCleanup"},
31 {0x00140040, nullptr, "DoCleanup"},
32 {0x00150040, nullptr, "DeleteAllImportContexts"},
33 {0x00160000, nullptr, "DeleteAllTemporaryPrograms"},
34 {0x00170044, nullptr, "ImportTwlBackupLegacy"},
35 {0x00180080, nullptr, "InitializeTitleDatabase"},
36 {0x00190040, nullptr, "QueryAvailableTitleDatabase"},
37 {0x001A00C0, nullptr, "CalcTwlBackupSize"},
38 {0x001B0144, nullptr, "ExportTwlBackup"},
39 {0x001C0084, nullptr, "ImportTwlBackup"},
40 {0x001D0000, nullptr, "DeleteAllTwlUserPrograms"},
41 {0x001E00C8, nullptr, "ReadTwlBackupInfo"},
42 {0x001F0040, nullptr, "DeleteAllExpiredUserPrograms"},
43 {0x00200000, nullptr, "GetTwlArchiveResourceInfo"},
44 {0x00210042, nullptr, "GetPersonalizedTicketInfoList"},
45 {0x00220080, nullptr, "DeleteAllImportContextsFiltered"},
46 {0x00230080, nullptr, "GetNumImportTitleContextsFiltered"},
47 {0x002400C2, nullptr, "GetImportTitleContextListFiltered"},
48 {0x002500C0, nullptr, "CheckContentRights"},
49 {0x00260044, nullptr, "GetTicketLimitInfos"},
50 {0x00270044, nullptr, "GetDemoLaunchInfos"},
51 {0x00280108, nullptr, "ReadTwlBackupInfoEx"},
52 {0x00290082, nullptr, "DeleteUserProgramsAtomically"},
53 {0x002A00C0, nullptr, "GetNumExistingContentInfosSystem"},
54 {0x002B0142, nullptr, "ListExistingContentInfosSystem"},
55 {0x002C0084, nullptr, "GetProgramInfosIgnorePlatform"},
56 {0x002D00C0, nullptr, "CheckContentRightsIgnorePlatform"},
57 {0x100100C0, GetNumContentInfos, "GetNumContentInfos"},
58 {0x10020104, FindContentInfos, "FindContentInfos"},
59 {0x10030142, ListContentInfos, "ListContentInfos"},
60 {0x10040102, DeleteContents, "DeleteContents"},
61 {0x10050084, GetDataTitleInfos, "GetDataTitleInfos"},
62 {0x10060080, nullptr, "GetNumDataTitleTickets"},
63 {0x10070102, ListDataTitleTicketInfos, "ListDataTitleTicketInfos"},
64 {0x100801C2, nullptr, "GetItemRights"},
65 {0x100900C0, nullptr, "IsDataTitleInUse"},
66 {0x100A0000, nullptr, "IsExternalTitleDatabaseInitialized"},
67 {0x100B00C0, nullptr, "GetNumExistingContentInfos"},
68 {0x100C0142, nullptr, "ListExistingContentInfos"},
69 {0x100D0084, nullptr, "GetPatchTitleInfos"},
70};
71
72AM_SYS_Interface::AM_SYS_Interface() {
73 Register(FunctionTable);
74}
75
76} // namespace AM
77} // namespace Service
diff --git a/src/core/hle/service/am/am_sys.h b/src/core/hle/service/am/am_sys.h
deleted file mode 100644
index b114f1d35..000000000
--- a/src/core/hle/service/am/am_sys.h
+++ /dev/null
@@ -1,22 +0,0 @@
1// Copyright 2015 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 "core/hle/service/service.h"
8
9namespace Service {
10namespace AM {
11
12class AM_SYS_Interface : public Service::Interface {
13public:
14 AM_SYS_Interface();
15
16 std::string GetPortName() const override {
17 return "am:sys";
18 }
19};
20
21} // namespace AM
22} // namespace Service
diff --git a/src/core/hle/service/am/am_u.cpp b/src/core/hle/service/am/am_u.cpp
deleted file mode 100644
index 354d51610..000000000
--- a/src/core/hle/service/am/am_u.cpp
+++ /dev/null
@@ -1,89 +0,0 @@
1// Copyright 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/am/am.h"
6#include "core/hle/service/am/am_u.h"
7
8namespace Service {
9namespace AM {
10
11const Interface::FunctionInfo FunctionTable[] = {
12 {0x00010040, GetNumPrograms, "GetNumPrograms"},
13 {0x00020082, GetProgramList, "GetProgramList"},
14 {0x00030084, GetProgramInfos, "GetProgramInfos"},
15 {0x000400C0, nullptr, "DeleteUserProgram"},
16 {0x000500C0, nullptr, "GetProductCode"},
17 {0x000600C0, nullptr, "GetStorageId"},
18 {0x00070080, DeleteTicket, "DeleteTicket"},
19 {0x00080000, GetNumTickets, "GetNumTickets"},
20 {0x00090082, GetTicketList, "GetTicketList"},
21 {0x000A0000, nullptr, "GetDeviceID"},
22 {0x000B0040, nullptr, "GetNumImportTitleContexts"},
23 {0x000C0082, nullptr, "GetImportTitleContextList"},
24 {0x000D0084, nullptr, "GetImportTitleContexts"},
25 {0x000E00C0, nullptr, "DeleteImportTitleContext"},
26 {0x000F00C0, nullptr, "GetNumImportContentContexts"},
27 {0x00100102, nullptr, "GetImportContentContextList"},
28 {0x00110104, nullptr, "GetImportContentContexts"},
29 {0x00120102, nullptr, "DeleteImportContentContexts"},
30 {0x00130040, nullptr, "NeedsCleanup"},
31 {0x00140040, nullptr, "DoCleanup"},
32 {0x00150040, nullptr, "DeleteAllImportContexts"},
33 {0x00160000, nullptr, "DeleteAllTemporaryPrograms"},
34 {0x00170044, nullptr, "ImportTwlBackupLegacy"},
35 {0x00180080, nullptr, "InitializeTitleDatabase"},
36 {0x00190040, nullptr, "QueryAvailableTitleDatabase"},
37 {0x001A00C0, nullptr, "CalcTwlBackupSize"},
38 {0x001B0144, nullptr, "ExportTwlBackup"},
39 {0x001C0084, nullptr, "ImportTwlBackup"},
40 {0x001D0000, nullptr, "DeleteAllTwlUserPrograms"},
41 {0x001E00C8, nullptr, "ReadTwlBackupInfo"},
42 {0x001F0040, nullptr, "DeleteAllExpiredUserPrograms"},
43 {0x00200000, nullptr, "GetTwlArchiveResourceInfo"},
44 {0x00210042, nullptr, "GetPersonalizedTicketInfoList"},
45 {0x00220080, nullptr, "DeleteAllImportContextsFiltered"},
46 {0x00230080, nullptr, "GetNumImportTitleContextsFiltered"},
47 {0x002400C2, nullptr, "GetImportTitleContextListFiltered"},
48 {0x002500C0, nullptr, "CheckContentRights"},
49 {0x00260044, nullptr, "GetTicketLimitInfos"},
50 {0x00270044, nullptr, "GetDemoLaunchInfos"},
51 {0x00280108, nullptr, "ReadTwlBackupInfoEx"},
52 {0x00290082, nullptr, "DeleteUserProgramsAtomically"},
53 {0x002A00C0, nullptr, "GetNumExistingContentInfosSystem"},
54 {0x002B0142, nullptr, "ListExistingContentInfosSystem"},
55 {0x002C0084, nullptr, "GetProgramInfosIgnorePlatform"},
56 {0x002D00C0, nullptr, "CheckContentRightsIgnorePlatform"},
57 {0x04010080, nullptr, "UpdateFirmwareTo"},
58 {0x04020040, nullptr, "BeginImportProgram"},
59 {0x04030000, nullptr, "BeginImportProgramTemporarily"},
60 {0x04040002, nullptr, "CancelImportProgram"},
61 {0x04050002, nullptr, "EndImportProgram"},
62 {0x04060002, nullptr, "EndImportProgramWithoutCommit"},
63 {0x040700C2, nullptr, "CommitImportPrograms"},
64 {0x04080042, nullptr, "GetProgramInfoFromCia"},
65 {0x04090004, nullptr, "GetSystemMenuDataFromCia"},
66 {0x040A0002, nullptr, "GetDependencyListFromCia"},
67 {0x040B0002, nullptr, "GetTransferSizeFromCia"},
68 {0x040C0002, nullptr, "GetCoreVersionFromCia"},
69 {0x040D0042, nullptr, "GetRequiredSizeFromCia"},
70 {0x040E00C2, nullptr, "CommitImportProgramsAndUpdateFirmwareAuto"},
71 {0x040F0000, nullptr, "UpdateFirmwareAuto"},
72 {0x041000C0, nullptr, "DeleteProgram"},
73 {0x04110044, nullptr, "GetTwlProgramListForReboot"},
74 {0x04120000, nullptr, "GetSystemUpdaterMutex"},
75 {0x04130002, nullptr, "GetMetaSizeFromCia"},
76 {0x04140044, nullptr, "GetMetaDataFromCia"},
77 {0x04150080, nullptr, "CheckDemoLaunchRights"},
78 {0x041600C0, nullptr, "GetInternalTitleLocationInfo"},
79 {0x041700C0, nullptr, "PerpetuateAgbSaveData"},
80 {0x04180040, nullptr, "BeginImportProgramForOverWrite"},
81 {0x04190000, nullptr, "BeginImportSystemProgram"},
82};
83
84AM_U_Interface::AM_U_Interface() {
85 Register(FunctionTable);
86}
87
88} // namespace AM
89} // namespace Service
diff --git a/src/core/hle/service/am/am_u.h b/src/core/hle/service/am/am_u.h
deleted file mode 100644
index 3b2454b6c..000000000
--- a/src/core/hle/service/am/am_u.h
+++ /dev/null
@@ -1,22 +0,0 @@
1// Copyright 2015 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 "core/hle/service/service.h"
8
9namespace Service {
10namespace AM {
11
12class AM_U_Interface : public Service::Interface {
13public:
14 AM_U_Interface();
15
16 std::string GetPortName() const override {
17 return "am:u";
18 }
19};
20
21} // namespace AM
22} // namespace Service
diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp
deleted file mode 100644
index 912ab550d..000000000
--- a/src/core/hle/service/apt/apt.cpp
+++ /dev/null
@@ -1,1124 +0,0 @@
1// Copyright 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <boost/optional.hpp>
6#include "common/common_paths.h"
7#include "common/file_util.h"
8#include "common/logging/log.h"
9#include "core/core.h"
10#include "core/file_sys/file_backend.h"
11#include "core/hle/applets/applet.h"
12#include "core/hle/kernel/event.h"
13#include "core/hle/kernel/mutex.h"
14#include "core/hle/kernel/process.h"
15#include "core/hle/kernel/shared_memory.h"
16#include "core/hle/romfs.h"
17#include "core/hle/service/apt/apt.h"
18#include "core/hle/service/apt/apt_a.h"
19#include "core/hle/service/apt/apt_s.h"
20#include "core/hle/service/apt/apt_u.h"
21#include "core/hle/service/apt/bcfnt/bcfnt.h"
22#include "core/hle/service/cfg/cfg.h"
23#include "core/hle/service/fs/archive.h"
24#include "core/hle/service/ptm/ptm.h"
25#include "core/hle/service/service.h"
26#include "core/hw/aes/ccm.h"
27#include "core/hw/aes/key.h"
28
29namespace Service {
30namespace APT {
31
32/// Handle to shared memory region designated to for shared system font
33static Kernel::SharedPtr<Kernel::SharedMemory> shared_font_mem;
34static bool shared_font_loaded = false;
35static bool shared_font_relocated = false;
36
37static Kernel::SharedPtr<Kernel::Mutex> lock;
38
39static u32 cpu_percent; ///< CPU time available to the running application
40
41// APT::CheckNew3DSApp will check this unknown_ns_state_field to determine processing mode
42static u8 unknown_ns_state_field;
43
44static ScreencapPostPermission screen_capture_post_permission;
45
46/// Parameter data to be returned in the next call to Glance/ReceiveParameter.
47/// TODO(Subv): Use std::optional once we migrate to C++17.
48static boost::optional<MessageParameter> next_parameter;
49
50enum class AppletPos { Application = 0, Library = 1, System = 2, SysLibrary = 3, Resident = 4 };
51
52static constexpr size_t NumAppletSlot = 4;
53
54enum class AppletSlot : u8 {
55 Application,
56 SystemApplet,
57 HomeMenu,
58 LibraryApplet,
59
60 // An invalid tag
61 Error,
62};
63
64union AppletAttributes {
65 u32 raw;
66
67 BitField<0, 3, u32> applet_pos;
68 BitField<29, 1, u32> is_home_menu;
69
70 AppletAttributes() : raw(0) {}
71 AppletAttributes(u32 attributes) : raw(attributes) {}
72};
73
74struct AppletSlotData {
75 AppletId applet_id;
76 AppletSlot slot;
77 bool registered;
78 AppletAttributes attributes;
79 Kernel::SharedPtr<Kernel::Event> notification_event;
80 Kernel::SharedPtr<Kernel::Event> parameter_event;
81};
82
83// Holds data about the concurrently running applets in the system.
84static std::array<AppletSlotData, NumAppletSlot> applet_slots = {};
85
86// This overload returns nullptr if no applet with the specified id has been started.
87static AppletSlotData* GetAppletSlotData(AppletId id) {
88 auto GetSlot = [](AppletSlot slot) -> AppletSlotData* {
89 return &applet_slots[static_cast<size_t>(slot)];
90 };
91
92 if (id == AppletId::Application) {
93 auto* slot = GetSlot(AppletSlot::Application);
94 if (slot->applet_id != AppletId::None)
95 return slot;
96
97 return nullptr;
98 }
99
100 if (id == AppletId::AnySystemApplet) {
101 auto* system_slot = GetSlot(AppletSlot::SystemApplet);
102 if (system_slot->applet_id != AppletId::None)
103 return system_slot;
104
105 // The Home Menu is also a system applet, but it lives in its own slot to be able to run
106 // concurrently with other system applets.
107 auto* home_slot = GetSlot(AppletSlot::HomeMenu);
108 if (home_slot->applet_id != AppletId::None)
109 return home_slot;
110
111 return nullptr;
112 }
113
114 if (id == AppletId::AnyLibraryApplet || id == AppletId::AnySysLibraryApplet) {
115 auto* slot = GetSlot(AppletSlot::LibraryApplet);
116 if (slot->applet_id == AppletId::None)
117 return nullptr;
118
119 u32 applet_pos = slot->attributes.applet_pos;
120
121 if (id == AppletId::AnyLibraryApplet && applet_pos == static_cast<u32>(AppletPos::Library))
122 return slot;
123
124 if (id == AppletId::AnySysLibraryApplet &&
125 applet_pos == static_cast<u32>(AppletPos::SysLibrary))
126 return slot;
127
128 return nullptr;
129 }
130
131 if (id == AppletId::HomeMenu || id == AppletId::AlternateMenu) {
132 auto* slot = GetSlot(AppletSlot::HomeMenu);
133 if (slot->applet_id != AppletId::None)
134 return slot;
135
136 return nullptr;
137 }
138
139 for (auto& slot : applet_slots) {
140 if (slot.applet_id == id)
141 return &slot;
142 }
143
144 return nullptr;
145}
146
147static AppletSlotData* GetAppletSlotData(AppletAttributes attributes) {
148 // Mapping from AppletPos to AppletSlot
149 static constexpr std::array<AppletSlot, 6> applet_position_slots = {
150 AppletSlot::Application, AppletSlot::LibraryApplet, AppletSlot::SystemApplet,
151 AppletSlot::LibraryApplet, AppletSlot::Error, AppletSlot::LibraryApplet};
152
153 u32 applet_pos = attributes.applet_pos;
154 if (applet_pos >= applet_position_slots.size())
155 return nullptr;
156
157 AppletSlot slot = applet_position_slots[applet_pos];
158
159 if (slot == AppletSlot::Error)
160 return nullptr;
161
162 // The Home Menu is a system applet, however, it has its own applet slot so that it can run
163 // concurrently with other system applets.
164 if (slot == AppletSlot::SystemApplet && attributes.is_home_menu)
165 return &applet_slots[static_cast<size_t>(AppletSlot::HomeMenu)];
166
167 return &applet_slots[static_cast<size_t>(slot)];
168}
169
170void SendParameter(const MessageParameter& parameter) {
171 next_parameter = parameter;
172 // Signal the event to let the receiver know that a new parameter is ready to be read
173 auto* const slot_data = GetAppletSlotData(static_cast<AppletId>(parameter.destination_id));
174 if (slot_data == nullptr) {
175 LOG_DEBUG(Service_APT, "No applet was registered with the id %03X",
176 parameter.destination_id);
177 return;
178 }
179
180 slot_data->parameter_event->Signal();
181}
182
183void Initialize(Service::Interface* self) {
184 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x2, 2, 0); // 0x20080
185 u32 app_id = rp.Pop<u32>();
186 u32 attributes = rp.Pop<u32>();
187
188 LOG_DEBUG(Service_APT, "called app_id=0x%08X, attributes=0x%08X", app_id, attributes);
189
190 auto* const slot_data = GetAppletSlotData(attributes);
191
192 // Note: The real NS service does not check if the attributes value is valid before accessing
193 // the data in the array
194 ASSERT_MSG(slot_data, "Invalid application attributes");
195
196 if (slot_data->registered) {
197 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
198 rb.Push(ResultCode(ErrorDescription::AlreadyExists, ErrorModule::Applet,
199 ErrorSummary::InvalidState, ErrorLevel::Status));
200 return;
201 }
202
203 slot_data->applet_id = static_cast<AppletId>(app_id);
204 slot_data->attributes.raw = attributes;
205
206 IPC::RequestBuilder rb = rp.MakeBuilder(1, 3);
207 rb.Push(RESULT_SUCCESS);
208 rb.PushCopyHandles(Kernel::g_handle_table.Create(slot_data->notification_event).Unwrap(),
209 Kernel::g_handle_table.Create(slot_data->parameter_event).Unwrap());
210
211 if (slot_data->applet_id == AppletId::Application ||
212 slot_data->applet_id == AppletId::HomeMenu) {
213 // Initialize the APT parameter to wake up the application.
214 next_parameter.emplace();
215 next_parameter->signal = static_cast<u32>(SignalType::Wakeup);
216 next_parameter->sender_id = static_cast<u32>(AppletId::None);
217 next_parameter->destination_id = app_id;
218 // Not signaling the parameter event will cause the application (or Home Menu) to hang
219 // during startup. In the real console, it is usually the Kernel and Home Menu who cause NS
220 // to signal the HomeMenu and Application parameter events, respectively.
221 slot_data->parameter_event->Signal();
222 }
223}
224
225static u32 DecompressLZ11(const u8* in, u8* out) {
226 u32_le decompressed_size;
227 memcpy(&decompressed_size, in, sizeof(u32));
228 in += 4;
229
230 u8 type = decompressed_size & 0xFF;
231 ASSERT(type == 0x11);
232 decompressed_size >>= 8;
233
234 u32 current_out_size = 0;
235 u8 flags = 0, mask = 1;
236 while (current_out_size < decompressed_size) {
237 if (mask == 1) {
238 flags = *(in++);
239 mask = 0x80;
240 } else {
241 mask >>= 1;
242 }
243
244 if (flags & mask) {
245 u8 byte1 = *(in++);
246 u32 length = byte1 >> 4;
247 u32 offset;
248 if (length == 0) {
249 u8 byte2 = *(in++);
250 u8 byte3 = *(in++);
251 length = (((byte1 & 0x0F) << 4) | (byte2 >> 4)) + 0x11;
252 offset = (((byte2 & 0x0F) << 8) | byte3) + 0x1;
253 } else if (length == 1) {
254 u8 byte2 = *(in++);
255 u8 byte3 = *(in++);
256 u8 byte4 = *(in++);
257 length = (((byte1 & 0x0F) << 12) | (byte2 << 4) | (byte3 >> 4)) + 0x111;
258 offset = (((byte3 & 0x0F) << 8) | byte4) + 0x1;
259 } else {
260 u8 byte2 = *(in++);
261 length = (byte1 >> 4) + 0x1;
262 offset = (((byte1 & 0x0F) << 8) | byte2) + 0x1;
263 }
264
265 for (u32 i = 0; i < length; i++) {
266 *out = *(out - offset);
267 ++out;
268 }
269
270 current_out_size += length;
271 } else {
272 *(out++) = *(in++);
273 current_out_size++;
274 }
275 }
276 return decompressed_size;
277}
278
279static bool LoadSharedFont() {
280 u8 font_region_code;
281 switch (CFG::GetRegionValue()) {
282 case 4: // CHN
283 font_region_code = 2;
284 break;
285 case 5: // KOR
286 font_region_code = 3;
287 break;
288 case 6: // TWN
289 font_region_code = 4;
290 break;
291 default: // JPN/EUR/USA
292 font_region_code = 1;
293 break;
294 }
295
296 const u64_le shared_font_archive_id_low = 0x0004009b00014002 | ((font_region_code - 1) << 8);
297 const u64_le shared_font_archive_id_high = 0x00000001ffffff00;
298 std::vector<u8> shared_font_archive_id(16);
299 std::memcpy(&shared_font_archive_id[0], &shared_font_archive_id_low, sizeof(u64));
300 std::memcpy(&shared_font_archive_id[8], &shared_font_archive_id_high, sizeof(u64));
301 FileSys::Path archive_path(shared_font_archive_id);
302 auto archive_result = Service::FS::OpenArchive(Service::FS::ArchiveIdCode::NCCH, archive_path);
303 if (archive_result.Failed())
304 return false;
305
306 std::vector<u8> romfs_path(20, 0); // 20-byte all zero path for opening RomFS
307 FileSys::Path file_path(romfs_path);
308 FileSys::Mode open_mode = {};
309 open_mode.read_flag.Assign(1);
310 auto file_result = Service::FS::OpenFileFromArchive(*archive_result, file_path, open_mode);
311 if (file_result.Failed())
312 return false;
313
314 auto romfs = std::move(file_result).Unwrap();
315 std::vector<u8> romfs_buffer(romfs->backend->GetSize());
316 romfs->backend->Read(0, romfs_buffer.size(), romfs_buffer.data());
317 romfs->backend->Close();
318
319 const char16_t* file_name[4] = {u"cbf_std.bcfnt.lz", u"cbf_zh-Hans-CN.bcfnt.lz",
320 u"cbf_ko-Hang-KR.bcfnt.lz", u"cbf_zh-Hant-TW.bcfnt.lz"};
321 const u8* font_file =
322 RomFS::GetFilePointer(romfs_buffer.data(), {file_name[font_region_code - 1]});
323 if (font_file == nullptr)
324 return false;
325
326 struct {
327 u32_le status;
328 u32_le region;
329 u32_le decompressed_size;
330 INSERT_PADDING_WORDS(0x1D);
331 } shared_font_header{};
332 static_assert(sizeof(shared_font_header) == 0x80, "shared_font_header has incorrect size");
333
334 shared_font_header.status = 2; // successfully loaded
335 shared_font_header.region = font_region_code;
336 shared_font_header.decompressed_size =
337 DecompressLZ11(font_file, shared_font_mem->GetPointer(0x80));
338 std::memcpy(shared_font_mem->GetPointer(), &shared_font_header, sizeof(shared_font_header));
339 *shared_font_mem->GetPointer(0x83) = 'U'; // Change the magic from "CFNT" to "CFNU"
340
341 return true;
342}
343
344static bool LoadLegacySharedFont() {
345 // This is the legacy method to load shared font.
346 // The expected format is a decrypted, uncompressed BCFNT file with the 0x80 byte header
347 // generated by the APT:U service. The best way to get is by dumping it from RAM. We've provided
348 // a homebrew app to do this: https://github.com/citra-emu/3dsutils. Put the resulting file
349 // "shared_font.bin" in the Citra "sysdata" directory.
350 std::string filepath = FileUtil::GetUserPath(D_SYSDATA_IDX) + SHARED_FONT;
351
352 FileUtil::CreateFullPath(filepath); // Create path if not already created
353 FileUtil::IOFile file(filepath, "rb");
354 if (file.IsOpen()) {
355 file.ReadBytes(shared_font_mem->GetPointer(), file.GetSize());
356 return true;
357 }
358
359 return false;
360}
361
362void GetSharedFont(Service::Interface* self) {
363 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x44, 0, 0); // 0x00440000
364 IPC::RequestBuilder rb = rp.MakeBuilder(2, 2);
365
366 // Log in telemetry if the game uses the shared font
367 Core::Telemetry().AddField(Telemetry::FieldType::Session, "RequiresSharedFont", true);
368
369 if (!shared_font_loaded) {
370 // On real 3DS, font loading happens on booting. However, we load it on demand to coordinate
371 // with CFG region auto configuration, which happens later than APT initialization.
372 if (LoadSharedFont()) {
373 shared_font_loaded = true;
374 } else if (LoadLegacySharedFont()) {
375 LOG_WARNING(Service_APT, "Loaded shared font by legacy method");
376 shared_font_loaded = true;
377 } else {
378 LOG_ERROR(Service_APT, "shared font file missing - go dump it from your 3ds");
379 rb.Push<u32>(-1); // TODO: Find the right error code
380 rb.Skip(1 + 2, true);
381 Core::System::GetInstance().SetStatus(Core::System::ResultStatus::ErrorSharedFont);
382 return;
383 }
384 }
385
386 // The shared font has to be relocated to the new address before being passed to the
387 // application.
388 VAddr target_address =
389 Memory::PhysicalToVirtualAddress(shared_font_mem->linear_heap_phys_address).value();
390 if (!shared_font_relocated) {
391 BCFNT::RelocateSharedFont(shared_font_mem, target_address);
392 shared_font_relocated = true;
393 }
394
395 rb.Push(RESULT_SUCCESS); // No error
396 // Since the SharedMemory interface doesn't provide the address at which the memory was
397 // allocated, the real APT service calculates this address by scanning the entire address space
398 // (using svcQueryMemory) and searches for an allocation of the same size as the Shared Font.
399 rb.Push(target_address);
400 rb.PushCopyHandles(Kernel::g_handle_table.Create(shared_font_mem).Unwrap());
401}
402
403void NotifyToWait(Service::Interface* self) {
404 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x43, 1, 0); // 0x430040
405 u32 app_id = rp.Pop<u32>();
406 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
407 rb.Push(RESULT_SUCCESS); // No error
408 LOG_WARNING(Service_APT, "(STUBBED) app_id=%u", app_id);
409}
410
411void GetLockHandle(Service::Interface* self) {
412 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1, 1, 0); // 0x10040
413
414 // Bits [0:2] are the applet type (System, Library, etc)
415 // Bit 5 tells the application that there's a pending APT parameter,
416 // this will cause the app to wait until parameter_event is signaled.
417 u32 applet_attributes = rp.Pop<u32>();
418 IPC::RequestBuilder rb = rp.MakeBuilder(3, 2);
419 rb.Push(RESULT_SUCCESS); // No error
420
421 // TODO(Subv): The output attributes should have an AppletPos of either Library or System |
422 // Library (depending on the type of the last launched applet) if the input attributes'
423 // AppletPos has the Library bit set.
424
425 rb.Push(applet_attributes); // Applet Attributes, this value is passed to Enable.
426 rb.Push<u32>(0); // Least significant bit = power button state
427 Kernel::Handle handle_copy = Kernel::g_handle_table.Create(lock).Unwrap();
428 rb.PushCopyHandles(handle_copy);
429
430 LOG_WARNING(Service_APT, "(STUBBED) called handle=0x%08X applet_attributes=0x%08X", handle_copy,
431 applet_attributes);
432}
433
434void Enable(Service::Interface* self) {
435 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x3, 1, 0); // 0x30040
436 u32 attributes = rp.Pop<u32>();
437
438 LOG_DEBUG(Service_APT, "called attributes=0x%08X", attributes);
439
440 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
441
442 auto* const slot_data = GetAppletSlotData(attributes);
443
444 if (!slot_data) {
445 rb.Push(ResultCode(ErrCodes::InvalidAppletSlot, ErrorModule::Applet,
446 ErrorSummary::InvalidState, ErrorLevel::Status));
447 return;
448 }
449
450 slot_data->registered = true;
451
452 rb.Push(RESULT_SUCCESS);
453}
454
455void GetAppletManInfo(Service::Interface* self) {
456 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x5, 1, 0); // 0x50040
457 u32 unk = rp.Pop<u32>();
458 IPC::RequestBuilder rb = rp.MakeBuilder(5, 0);
459 rb.Push(RESULT_SUCCESS); // No error
460 rb.Push<u32>(0);
461 rb.Push<u32>(0);
462 rb.Push(static_cast<u32>(AppletId::HomeMenu)); // Home menu AppID
463 rb.Push(static_cast<u32>(AppletId::Application)); // TODO(purpasmart96): Do this correctly
464
465 LOG_WARNING(Service_APT, "(STUBBED) called unk=0x%08X", unk);
466}
467
468void IsRegistered(Service::Interface* self) {
469 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x9, 1, 0); // 0x90040
470 AppletId app_id = static_cast<AppletId>(rp.Pop<u32>());
471 IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
472 rb.Push(RESULT_SUCCESS); // No error
473
474 auto* const slot_data = GetAppletSlotData(app_id);
475
476 // Check if an LLE applet was registered first, then fallback to HLE applets
477 bool is_registered = slot_data && slot_data->registered;
478
479 if (!is_registered) {
480 if (app_id == AppletId::AnyLibraryApplet) {
481 is_registered = HLE::Applets::IsLibraryAppletRunning();
482 } else if (auto applet = HLE::Applets::Applet::Get(app_id)) {
483 // The applet exists, set it as registered.
484 is_registered = true;
485 }
486 }
487
488 rb.Push(is_registered);
489
490 LOG_DEBUG(Service_APT, "called app_id=0x%08X", static_cast<u32>(app_id));
491}
492
493void InquireNotification(Service::Interface* self) {
494 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0xB, 1, 0); // 0xB0040
495 u32 app_id = rp.Pop<u32>();
496 IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
497 rb.Push(RESULT_SUCCESS); // No error
498 rb.Push(static_cast<u32>(SignalType::None)); // Signal type
499 LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X", app_id);
500}
501
502void SendParameter(Service::Interface* self) {
503 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0xC, 4, 4); // 0xC0104
504 u32 src_app_id = rp.Pop<u32>();
505 u32 dst_app_id = rp.Pop<u32>();
506 u32 signal_type = rp.Pop<u32>();
507 u32 buffer_size = rp.Pop<u32>();
508 Kernel::Handle handle = rp.PopHandle();
509 size_t size;
510 VAddr buffer = rp.PopStaticBuffer(&size);
511
512 LOG_DEBUG(Service_APT,
513 "called src_app_id=0x%08X, dst_app_id=0x%08X, signal_type=0x%08X,"
514 "buffer_size=0x%08X, handle=0x%08X, size=0x%08zX, in_param_buffer_ptr=0x%08X",
515 src_app_id, dst_app_id, signal_type, buffer_size, handle, size, buffer);
516
517 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
518
519 // A new parameter can not be sent if the previous one hasn't been consumed yet
520 if (next_parameter) {
521 rb.Push(ResultCode(ErrCodes::ParameterPresent, ErrorModule::Applet,
522 ErrorSummary::InvalidState, ErrorLevel::Status));
523 return;
524 }
525
526 MessageParameter param;
527 param.destination_id = dst_app_id;
528 param.sender_id = src_app_id;
529 param.object = Kernel::g_handle_table.GetGeneric(handle);
530 param.signal = signal_type;
531 param.buffer.resize(buffer_size);
532 Memory::ReadBlock(buffer, param.buffer.data(), param.buffer.size());
533
534 SendParameter(param);
535
536 // If the applet is running in HLE mode, use the HLE interface to communicate with it.
537 if (auto dest_applet = HLE::Applets::Applet::Get(static_cast<AppletId>(dst_app_id))) {
538 rb.Push(dest_applet->ReceiveParameter(param));
539 } else {
540 rb.Push(RESULT_SUCCESS);
541 }
542}
543
544void ReceiveParameter(Service::Interface* self) {
545 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0xD, 2, 0); // 0xD0080
546 u32 app_id = rp.Pop<u32>();
547 u32 buffer_size = rp.Pop<u32>();
548
549 size_t static_buff_size;
550 VAddr buffer = rp.PeekStaticBuffer(0, &static_buff_size);
551 if (buffer_size > static_buff_size)
552 LOG_WARNING(
553 Service_APT,
554 "buffer_size is bigger than the size in the buffer descriptor (0x%08X > 0x%08zX)",
555 buffer_size, static_buff_size);
556
557 LOG_DEBUG(Service_APT, "called app_id=0x%08X, buffer_size=0x%08zX", app_id, buffer_size);
558
559 if (!next_parameter) {
560 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
561 rb.Push(ResultCode(ErrorDescription::NoData, ErrorModule::Applet,
562 ErrorSummary::InvalidState, ErrorLevel::Status));
563 return;
564 }
565
566 if (next_parameter->destination_id != app_id) {
567 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
568 rb.Push(ResultCode(ErrorDescription::NotFound, ErrorModule::Applet, ErrorSummary::NotFound,
569 ErrorLevel::Status));
570 return;
571 }
572
573 IPC::RequestBuilder rb = rp.MakeBuilder(4, 4);
574
575 rb.Push(RESULT_SUCCESS); // No error
576 rb.Push(next_parameter->sender_id);
577 rb.Push(next_parameter->signal); // Signal type
578 ASSERT_MSG(next_parameter->buffer.size() <= buffer_size, "Input static buffer is too small !");
579 rb.Push(static_cast<u32>(next_parameter->buffer.size())); // Parameter buffer size
580
581 rb.PushMoveHandles((next_parameter->object != nullptr)
582 ? Kernel::g_handle_table.Create(next_parameter->object).Unwrap()
583 : 0);
584
585 rb.PushStaticBuffer(buffer, next_parameter->buffer.size(), 0);
586
587 Memory::WriteBlock(buffer, next_parameter->buffer.data(), next_parameter->buffer.size());
588
589 // Clear the parameter
590 next_parameter = boost::none;
591}
592
593void GlanceParameter(Service::Interface* self) {
594 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0xE, 2, 0); // 0xE0080
595 u32 app_id = rp.Pop<u32>();
596 u32 buffer_size = rp.Pop<u32>();
597
598 size_t static_buff_size;
599 VAddr buffer = rp.PeekStaticBuffer(0, &static_buff_size);
600 if (buffer_size > static_buff_size)
601 LOG_WARNING(
602 Service_APT,
603 "buffer_size is bigger than the size in the buffer descriptor (0x%08X > 0x%08zX)",
604 buffer_size, static_buff_size);
605
606 LOG_DEBUG(Service_APT, "called app_id=0x%08X, buffer_size=0x%08zX", app_id, buffer_size);
607
608 if (!next_parameter) {
609 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
610 rb.Push(ResultCode(ErrorDescription::NoData, ErrorModule::Applet,
611 ErrorSummary::InvalidState, ErrorLevel::Status));
612 return;
613 }
614
615 if (next_parameter->destination_id != app_id) {
616 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
617 rb.Push(ResultCode(ErrorDescription::NotFound, ErrorModule::Applet, ErrorSummary::NotFound,
618 ErrorLevel::Status));
619 return;
620 }
621
622 IPC::RequestBuilder rb = rp.MakeBuilder(4, 4);
623 rb.Push(RESULT_SUCCESS); // No error
624 rb.Push(next_parameter->sender_id);
625 rb.Push(next_parameter->signal); // Signal type
626 ASSERT_MSG(next_parameter->buffer.size() <= buffer_size, "Input static buffer is too small !");
627 rb.Push(static_cast<u32>(next_parameter->buffer.size())); // Parameter buffer size
628
629 rb.PushMoveHandles((next_parameter->object != nullptr)
630 ? Kernel::g_handle_table.Create(next_parameter->object).Unwrap()
631 : 0);
632
633 rb.PushStaticBuffer(buffer, next_parameter->buffer.size(), 0);
634
635 Memory::WriteBlock(buffer, next_parameter->buffer.data(), next_parameter->buffer.size());
636
637 // Note: The NS module always clears the DSPSleep and DSPWakeup signals even in GlanceParameter.
638 if (next_parameter->signal == static_cast<u32>(SignalType::DspSleep) ||
639 next_parameter->signal == static_cast<u32>(SignalType::DspWakeup))
640 next_parameter = boost::none;
641}
642
643void CancelParameter(Service::Interface* self) {
644 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0xF, 4, 0); // 0xF0100
645
646 bool check_sender = rp.Pop<bool>();
647 u32 sender_appid = rp.Pop<u32>();
648 bool check_receiver = rp.Pop<bool>();
649 u32 receiver_appid = rp.Pop<u32>();
650
651 bool cancellation_success = true;
652
653 if (!next_parameter) {
654 cancellation_success = false;
655 } else {
656 if (check_sender && next_parameter->sender_id != sender_appid)
657 cancellation_success = false;
658
659 if (check_receiver && next_parameter->destination_id != receiver_appid)
660 cancellation_success = false;
661 }
662
663 if (cancellation_success)
664 next_parameter = boost::none;
665
666 IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
667
668 rb.Push(RESULT_SUCCESS); // No error
669 rb.Push(cancellation_success);
670
671 LOG_DEBUG(Service_APT, "called check_sender=%u, sender_appid=0x%08X, "
672 "check_receiver=%u, receiver_appid=0x%08X",
673 check_sender, sender_appid, check_receiver, receiver_appid);
674}
675
676void PrepareToStartApplication(Service::Interface* self) {
677 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x15, 5, 0); // 0x00150140
678 u32 title_info1 = rp.Pop<u32>();
679 u32 title_info2 = rp.Pop<u32>();
680 u32 title_info3 = rp.Pop<u32>();
681 u32 title_info4 = rp.Pop<u32>();
682 u32 flags = rp.Pop<u32>();
683
684 if (flags & 0x00000100) {
685 unknown_ns_state_field = 1;
686 }
687
688 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
689 rb.Push(RESULT_SUCCESS); // No error
690
691 LOG_WARNING(Service_APT,
692 "(STUBBED) called title_info1=0x%08X, title_info2=0x%08X, title_info3=0x%08X,"
693 "title_info4=0x%08X, flags=0x%08X",
694 title_info1, title_info2, title_info3, title_info4, flags);
695}
696
697void StartApplication(Service::Interface* self) {
698 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1B, 3, 4); // 0x001B00C4
699 u32 buffer1_size = rp.Pop<u32>();
700 u32 buffer2_size = rp.Pop<u32>();
701 u32 flag = rp.Pop<u32>();
702 size_t size1;
703 VAddr buffer1_ptr = rp.PopStaticBuffer(&size1);
704 size_t size2;
705 VAddr buffer2_ptr = rp.PopStaticBuffer(&size2);
706
707 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
708 rb.Push(RESULT_SUCCESS); // No error
709
710 LOG_WARNING(Service_APT,
711 "(STUBBED) called buffer1_size=0x%08X, buffer2_size=0x%08X, flag=0x%08X,"
712 "size1=0x%08zX, buffer1_ptr=0x%08X, size2=0x%08zX, buffer2_ptr=0x%08X",
713 buffer1_size, buffer2_size, flag, size1, buffer1_ptr, size2, buffer2_ptr);
714}
715
716void AppletUtility(Service::Interface* self) {
717 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x4B, 3, 2); // 0x004B00C2
718
719 // These are from 3dbrew - I'm not really sure what they're used for.
720 u32 utility_command = rp.Pop<u32>();
721 u32 input_size = rp.Pop<u32>();
722 u32 output_size = rp.Pop<u32>();
723 VAddr input_addr = rp.PopStaticBuffer();
724
725 VAddr output_addr = rp.PeekStaticBuffer(0);
726
727 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
728 rb.Push(RESULT_SUCCESS); // No error
729
730 LOG_WARNING(Service_APT,
731 "(STUBBED) called command=0x%08X, input_size=0x%08X, output_size=0x%08X, "
732 "input_addr=0x%08X, output_addr=0x%08X",
733 utility_command, input_size, output_size, input_addr, output_addr);
734}
735
736void SetAppCpuTimeLimit(Service::Interface* self) {
737 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x4F, 2, 0); // 0x4F0080
738 u32 value = rp.Pop<u32>();
739 cpu_percent = rp.Pop<u32>();
740
741 if (value != 1) {
742 LOG_ERROR(Service_APT, "This value should be one, but is actually %u!", value);
743 }
744
745 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
746 rb.Push(RESULT_SUCCESS); // No error
747
748 LOG_WARNING(Service_APT, "(STUBBED) called cpu_percent=%u, value=%u", cpu_percent, value);
749}
750
751void GetAppCpuTimeLimit(Service::Interface* self) {
752 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x50, 1, 0); // 0x500040
753 u32 value = rp.Pop<u32>();
754
755 if (value != 1) {
756 LOG_ERROR(Service_APT, "This value should be one, but is actually %u!", value);
757 }
758
759 IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
760 rb.Push(RESULT_SUCCESS); // No error
761 rb.Push(cpu_percent);
762
763 LOG_WARNING(Service_APT, "(STUBBED) called value=%u", value);
764}
765
766void PrepareToStartLibraryApplet(Service::Interface* self) {
767 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x18, 1, 0); // 0x180040
768 AppletId applet_id = static_cast<AppletId>(rp.Pop<u32>());
769
770 LOG_DEBUG(Service_APT, "called applet_id=%08X", applet_id);
771
772 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
773
774 // TODO(Subv): Launch the requested applet application.
775
776 auto applet = HLE::Applets::Applet::Get(applet_id);
777 if (applet) {
778 LOG_WARNING(Service_APT, "applet has already been started id=%08X", applet_id);
779 rb.Push(RESULT_SUCCESS);
780 } else {
781 rb.Push(HLE::Applets::Applet::Create(applet_id));
782 }
783}
784
785void PrepareToStartNewestHomeMenu(Service::Interface* self) {
786 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1A, 0, 0); // 0x1A0000
787 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
788
789 // TODO(Subv): This command can only be called by a System Applet (return 0xC8A0CC04 otherwise).
790
791 // This command must return an error when called, otherwise the Home Menu will try to reboot the
792 // system.
793 rb.Push(ResultCode(ErrorDescription::AlreadyExists, ErrorModule::Applet,
794 ErrorSummary::InvalidState, ErrorLevel::Status));
795
796 LOG_DEBUG(Service_APT, "called");
797}
798
799void PreloadLibraryApplet(Service::Interface* self) {
800 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x16, 1, 0); // 0x160040
801 AppletId applet_id = static_cast<AppletId>(rp.Pop<u32>());
802
803 LOG_DEBUG(Service_APT, "called applet_id=%08X", applet_id);
804
805 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
806
807 // TODO(Subv): Launch the requested applet application.
808
809 auto applet = HLE::Applets::Applet::Get(applet_id);
810 if (applet) {
811 LOG_WARNING(Service_APT, "applet has already been started id=%08X", applet_id);
812 rb.Push(RESULT_SUCCESS);
813 } else {
814 rb.Push(HLE::Applets::Applet::Create(applet_id));
815 }
816}
817
818void StartLibraryApplet(Service::Interface* self) {
819 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1E, 2, 4); // 0x1E0084
820 AppletId applet_id = static_cast<AppletId>(rp.Pop<u32>());
821
822 size_t buffer_size = rp.Pop<u32>();
823 Kernel::Handle handle = rp.PopHandle();
824 VAddr buffer_addr = rp.PopStaticBuffer();
825
826 LOG_DEBUG(Service_APT, "called applet_id=%08X", applet_id);
827
828 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
829
830 // Send the Wakeup signal to the applet
831 MessageParameter param;
832 param.destination_id = static_cast<u32>(applet_id);
833 param.sender_id = static_cast<u32>(AppletId::Application);
834 param.object = Kernel::g_handle_table.GetGeneric(handle);
835 param.signal = static_cast<u32>(SignalType::Wakeup);
836 param.buffer.resize(buffer_size);
837 Memory::ReadBlock(buffer_addr, param.buffer.data(), param.buffer.size());
838 SendParameter(param);
839
840 // In case the applet is being HLEd, attempt to communicate with it.
841 if (auto applet = HLE::Applets::Applet::Get(applet_id)) {
842 AppletStartupParameter parameter;
843 parameter.object = Kernel::g_handle_table.GetGeneric(handle);
844 parameter.buffer.resize(buffer_size);
845 Memory::ReadBlock(buffer_addr, parameter.buffer.data(), parameter.buffer.size());
846 rb.Push(applet->Start(parameter));
847 } else {
848 rb.Push(RESULT_SUCCESS);
849 }
850}
851
852void CancelLibraryApplet(Service::Interface* self) {
853 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x3B, 1, 0); // 0x003B0040
854 bool exiting = rp.Pop<bool>();
855
856 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
857 rb.Push<u32>(1); // TODO: Find the return code meaning
858
859 LOG_WARNING(Service_APT, "(STUBBED) called exiting=%d", exiting);
860}
861
862void SetScreenCapPostPermission(Service::Interface* self) {
863 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x55, 1, 0); // 0x00550040
864
865 screen_capture_post_permission = static_cast<ScreencapPostPermission>(rp.Pop<u32>() & 0xF);
866
867 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
868 rb.Push(RESULT_SUCCESS); // No error
869 LOG_WARNING(Service_APT, "(STUBBED) screen_capture_post_permission=%u",
870 screen_capture_post_permission);
871}
872
873void GetScreenCapPostPermission(Service::Interface* self) {
874 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x56, 0, 0); // 0x00560000
875
876 IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
877 rb.Push(RESULT_SUCCESS); // No error
878 rb.Push(static_cast<u32>(screen_capture_post_permission));
879 LOG_WARNING(Service_APT, "(STUBBED) screen_capture_post_permission=%u",
880 screen_capture_post_permission);
881}
882
883void GetAppletInfo(Service::Interface* self) {
884 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x6, 1, 0); // 0x60040
885 auto app_id = static_cast<AppletId>(rp.Pop<u32>());
886
887 if (auto applet = HLE::Applets::Applet::Get(app_id)) {
888 // TODO(Subv): Get the title id for the current applet and write it in the response[2-3]
889 IPC::RequestBuilder rb = rp.MakeBuilder(7, 0);
890 rb.Push(RESULT_SUCCESS);
891 u64 title_id = 0;
892 rb.Push(title_id);
893 rb.Push(static_cast<u32>(Service::FS::MediaType::NAND));
894 rb.Push(true); // Registered
895 rb.Push(true); // Loaded
896 rb.Push<u32>(0); // Applet Attributes
897 } else {
898 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
899 rb.Push(ResultCode(ErrorDescription::NotFound, ErrorModule::Applet, ErrorSummary::NotFound,
900 ErrorLevel::Status));
901 }
902 LOG_WARNING(Service_APT, "(stubbed) called appid=%u", app_id);
903}
904
905void GetStartupArgument(Service::Interface* self) {
906 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x51, 2, 0); // 0x00510080
907 u32 parameter_size = rp.Pop<u32>();
908 StartupArgumentType startup_argument_type = static_cast<StartupArgumentType>(rp.Pop<u8>());
909
910 if (parameter_size >= 0x300) {
911 LOG_ERROR(
912 Service_APT,
913 "Parameter size is outside the valid range (capped to 0x300): parameter_size=0x%08x",
914 parameter_size);
915 return;
916 }
917
918 size_t static_buff_size;
919 VAddr addr = rp.PeekStaticBuffer(0, &static_buff_size);
920 if (parameter_size > static_buff_size)
921 LOG_WARNING(
922 Service_APT,
923 "parameter_size is bigger than the size in the buffer descriptor (0x%08X > 0x%08zX)",
924 parameter_size, static_buff_size);
925
926 if (addr && parameter_size) {
927 Memory::ZeroBlock(addr, parameter_size);
928 }
929
930 LOG_WARNING(Service_APT, "(stubbed) called startup_argument_type=%u , parameter_size=0x%08x",
931 startup_argument_type, parameter_size);
932
933 IPC::RequestBuilder rb = rp.MakeBuilder(2, 2);
934 rb.Push(RESULT_SUCCESS);
935 rb.Push<u32>(0);
936 rb.PushStaticBuffer(addr, parameter_size, 0);
937}
938
939void Wrap(Service::Interface* self) {
940 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x46, 4, 4);
941 const u32 output_size = rp.Pop<u32>();
942 const u32 input_size = rp.Pop<u32>();
943 const u32 nonce_offset = rp.Pop<u32>();
944 u32 nonce_size = rp.Pop<u32>();
945 size_t desc_size;
946 IPC::MappedBufferPermissions desc_permission;
947 const VAddr input = rp.PopMappedBuffer(&desc_size, &desc_permission);
948 ASSERT(desc_size == input_size && desc_permission == IPC::MappedBufferPermissions::R);
949 const VAddr output = rp.PopMappedBuffer(&desc_size, &desc_permission);
950 ASSERT(desc_size == output_size && desc_permission == IPC::MappedBufferPermissions::W);
951
952 // Note: real 3DS still returns SUCCESS when the sizes don't match. It seems that it doesn't
953 // check the buffer size and writes data with potential overflow.
954 ASSERT_MSG(output_size == input_size + HW::AES::CCM_MAC_SIZE,
955 "input_size (%d) doesn't match to output_size (%d)", input_size, output_size);
956
957 LOG_DEBUG(Service_APT, "called, output_size=%u, input_size=%u, nonce_offset=%u, nonce_size=%u",
958 output_size, input_size, nonce_offset, nonce_size);
959
960 // Note: This weird nonce size modification is verified against real 3DS
961 nonce_size = std::min<u32>(nonce_size & ~3, HW::AES::CCM_NONCE_SIZE);
962
963 // Reads nonce and concatenates the rest of the input as plaintext
964 HW::AES::CCMNonce nonce{};
965 Memory::ReadBlock(input + nonce_offset, nonce.data(), nonce_size);
966 u32 pdata_size = input_size - nonce_size;
967 std::vector<u8> pdata(pdata_size);
968 Memory::ReadBlock(input, pdata.data(), nonce_offset);
969 Memory::ReadBlock(input + nonce_offset + nonce_size, pdata.data() + nonce_offset,
970 pdata_size - nonce_offset);
971
972 // Encrypts the plaintext using AES-CCM
973 auto cipher = HW::AES::EncryptSignCCM(pdata, nonce, HW::AES::KeySlotID::APTWrap);
974
975 // Puts the nonce to the beginning of the output, with ciphertext followed
976 Memory::WriteBlock(output, nonce.data(), nonce_size);
977 Memory::WriteBlock(output + nonce_size, cipher.data(), cipher.size());
978
979 IPC::RequestBuilder rb = rp.MakeBuilder(1, 4);
980 rb.Push(RESULT_SUCCESS);
981
982 // Unmap buffer
983 rb.PushMappedBuffer(input, input_size, IPC::MappedBufferPermissions::R);
984 rb.PushMappedBuffer(output, output_size, IPC::MappedBufferPermissions::W);
985}
986
987void Unwrap(Service::Interface* self) {
988 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x47, 4, 4);
989 const u32 output_size = rp.Pop<u32>();
990 const u32 input_size = rp.Pop<u32>();
991 const u32 nonce_offset = rp.Pop<u32>();
992 u32 nonce_size = rp.Pop<u32>();
993 size_t desc_size;
994 IPC::MappedBufferPermissions desc_permission;
995 const VAddr input = rp.PopMappedBuffer(&desc_size, &desc_permission);
996 ASSERT(desc_size == input_size && desc_permission == IPC::MappedBufferPermissions::R);
997 const VAddr output = rp.PopMappedBuffer(&desc_size, &desc_permission);
998 ASSERT(desc_size == output_size && desc_permission == IPC::MappedBufferPermissions::W);
999
1000 // Note: real 3DS still returns SUCCESS when the sizes don't match. It seems that it doesn't
1001 // check the buffer size and writes data with potential overflow.
1002 ASSERT_MSG(output_size == input_size - HW::AES::CCM_MAC_SIZE,
1003 "input_size (%d) doesn't match to output_size (%d)", input_size, output_size);
1004
1005 LOG_DEBUG(Service_APT, "called, output_size=%u, input_size=%u, nonce_offset=%u, nonce_size=%u",
1006 output_size, input_size, nonce_offset, nonce_size);
1007
1008 // Note: This weird nonce size modification is verified against real 3DS
1009 nonce_size = std::min<u32>(nonce_size & ~3, HW::AES::CCM_NONCE_SIZE);
1010
1011 // Reads nonce and cipher text
1012 HW::AES::CCMNonce nonce{};
1013 Memory::ReadBlock(input, nonce.data(), nonce_size);
1014 u32 cipher_size = input_size - nonce_size;
1015 std::vector<u8> cipher(cipher_size);
1016 Memory::ReadBlock(input + nonce_size, cipher.data(), cipher_size);
1017
1018 // Decrypts the ciphertext using AES-CCM
1019 auto pdata = HW::AES::DecryptVerifyCCM(cipher, nonce, HW::AES::KeySlotID::APTWrap);
1020
1021 IPC::RequestBuilder rb = rp.MakeBuilder(1, 4);
1022 if (!pdata.empty()) {
1023 // Splits the plaintext and put the nonce in between
1024 Memory::WriteBlock(output, pdata.data(), nonce_offset);
1025 Memory::WriteBlock(output + nonce_offset, nonce.data(), nonce_size);
1026 Memory::WriteBlock(output + nonce_offset + nonce_size, pdata.data() + nonce_offset,
1027 pdata.size() - nonce_offset);
1028 rb.Push(RESULT_SUCCESS);
1029 } else {
1030 LOG_ERROR(Service_APT, "Failed to decrypt data");
1031 rb.Push(ResultCode(static_cast<ErrorDescription>(1), ErrorModule::PS,
1032 ErrorSummary::WrongArgument, ErrorLevel::Status));
1033 }
1034
1035 // Unmap buffer
1036 rb.PushMappedBuffer(input, input_size, IPC::MappedBufferPermissions::R);
1037 rb.PushMappedBuffer(output, output_size, IPC::MappedBufferPermissions::W);
1038}
1039
1040void CheckNew3DSApp(Service::Interface* self) {
1041 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x101, 0, 0); // 0x01010000
1042
1043 IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
1044 if (unknown_ns_state_field) {
1045 rb.Push(RESULT_SUCCESS);
1046 rb.Push<u32>(0);
1047 } else {
1048 PTM::CheckNew3DS(rb);
1049 }
1050
1051 LOG_WARNING(Service_APT, "(STUBBED) called");
1052}
1053
1054void CheckNew3DS(Service::Interface* self) {
1055 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x102, 0, 0); // 0x01020000
1056 IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
1057
1058 PTM::CheckNew3DS(rb);
1059
1060 LOG_WARNING(Service_APT, "(STUBBED) called");
1061}
1062
1063void Init() {
1064 AddService(new APT_A_Interface);
1065 AddService(new APT_S_Interface);
1066 AddService(new APT_U_Interface);
1067
1068 HLE::Applets::Init();
1069
1070 using Kernel::MemoryPermission;
1071 shared_font_mem =
1072 Kernel::SharedMemory::Create(nullptr, 0x332000, // 3272 KB
1073 MemoryPermission::ReadWrite, MemoryPermission::Read, 0,
1074 Kernel::MemoryRegion::SYSTEM, "APT:SharedFont");
1075
1076 if (LoadSharedFont()) {
1077 shared_font_loaded = true;
1078 } else if (LoadLegacySharedFont()) {
1079 LOG_WARNING(Service_APT, "Loaded shared font by legacy method");
1080 shared_font_loaded = true;
1081 } else {
1082 LOG_WARNING(Service_APT, "Unable to load shared font");
1083 shared_font_loaded = false;
1084 }
1085
1086 lock = Kernel::Mutex::Create(false, 0, "APT_U:Lock");
1087
1088 cpu_percent = 0;
1089 unknown_ns_state_field = 0;
1090 screen_capture_post_permission =
1091 ScreencapPostPermission::CleanThePermission; // TODO(JamePeng): verify the initial value
1092
1093 for (size_t slot = 0; slot < applet_slots.size(); ++slot) {
1094 auto& slot_data = applet_slots[slot];
1095 slot_data.slot = static_cast<AppletSlot>(slot);
1096 slot_data.applet_id = AppletId::None;
1097 slot_data.attributes.raw = 0;
1098 slot_data.registered = false;
1099 slot_data.notification_event =
1100 Kernel::Event::Create(Kernel::ResetType::OneShot, "APT:Notification");
1101 slot_data.parameter_event =
1102 Kernel::Event::Create(Kernel::ResetType::OneShot, "APT:Parameter");
1103 }
1104}
1105
1106void Shutdown() {
1107 shared_font_mem = nullptr;
1108 shared_font_loaded = false;
1109 shared_font_relocated = false;
1110 lock = nullptr;
1111
1112 for (auto& slot : applet_slots) {
1113 slot.registered = false;
1114 slot.notification_event = nullptr;
1115 slot.parameter_event = nullptr;
1116 }
1117
1118 next_parameter = boost::none;
1119
1120 HLE::Applets::Shutdown();
1121}
1122
1123} // namespace APT
1124} // namespace Service
diff --git a/src/core/hle/service/apt/apt.h b/src/core/hle/service/apt/apt.h
deleted file mode 100644
index 7b79e1f3e..000000000
--- a/src/core/hle/service/apt/apt.h
+++ /dev/null
@@ -1,533 +0,0 @@
1// Copyright 2015 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 <vector>
8#include "common/common_funcs.h"
9#include "common/common_types.h"
10#include "common/swap.h"
11#include "core/hle/kernel/kernel.h"
12
13namespace Service {
14
15class Interface;
16
17namespace APT {
18
19/// Each APT service can only have up to 2 sessions connected at the same time.
20static const u32 MaxAPTSessions = 2;
21
22/// Holds information about the parameters used in Send/Glance/ReceiveParameter
23struct MessageParameter {
24 u32 sender_id = 0;
25 u32 destination_id = 0;
26 u32 signal = 0;
27 Kernel::SharedPtr<Kernel::Object> object = nullptr;
28 std::vector<u8> buffer;
29};
30
31/// Holds information about the parameters used in StartLibraryApplet
32struct AppletStartupParameter {
33 Kernel::SharedPtr<Kernel::Object> object = nullptr;
34 std::vector<u8> buffer;
35};
36
37/// Used by the application to pass information about the current framebuffer to applets.
38struct CaptureBufferInfo {
39 u32_le size;
40 u8 is_3d;
41 INSERT_PADDING_BYTES(0x3); // Padding for alignment
42 u32_le top_screen_left_offset;
43 u32_le top_screen_right_offset;
44 u32_le top_screen_format;
45 u32_le bottom_screen_left_offset;
46 u32_le bottom_screen_right_offset;
47 u32_le bottom_screen_format;
48};
49static_assert(sizeof(CaptureBufferInfo) == 0x20, "CaptureBufferInfo struct has incorrect size");
50
51/// Signals used by APT functions
52enum class SignalType : u32 {
53 None = 0x0,
54 Wakeup = 0x1,
55 Request = 0x2,
56 Response = 0x3,
57 Exit = 0x4,
58 Message = 0x5,
59 HomeButtonSingle = 0x6,
60 HomeButtonDouble = 0x7,
61 DspSleep = 0x8,
62 DspWakeup = 0x9,
63 WakeupByExit = 0xA,
64 WakeupByPause = 0xB,
65 WakeupByCancel = 0xC,
66 WakeupByCancelAll = 0xD,
67 WakeupByPowerButtonClick = 0xE,
68 WakeupToJumpHome = 0xF,
69 RequestForSysApplet = 0x10,
70 WakeupToLaunchApplication = 0x11,
71};
72
73/// App Id's used by APT functions
74enum class AppletId : u32 {
75 None = 0,
76 AnySystemApplet = 0x100,
77 HomeMenu = 0x101,
78 AlternateMenu = 0x103,
79 Camera = 0x110,
80 FriendsList = 0x112,
81 GameNotes = 0x113,
82 InternetBrowser = 0x114,
83 InstructionManual = 0x115,
84 Notifications = 0x116,
85 Miiverse = 0x117,
86 MiiversePost = 0x118,
87 AmiiboSettings = 0x119,
88 AnySysLibraryApplet = 0x200,
89 SoftwareKeyboard1 = 0x201,
90 Ed1 = 0x202,
91 PnoteApp = 0x204,
92 SnoteApp = 0x205,
93 Error = 0x206,
94 Mint = 0x207,
95 Extrapad = 0x208,
96 Memolib = 0x209,
97 Application = 0x300,
98 AnyLibraryApplet = 0x400,
99 SoftwareKeyboard2 = 0x401,
100 Ed2 = 0x402,
101 PnoteApp2 = 0x404,
102 SnoteApp2 = 0x405,
103 Error2 = 0x406,
104 Mint2 = 0x407,
105 Extrapad2 = 0x408,
106 Memolib2 = 0x409,
107};
108
109enum class StartupArgumentType : u32 {
110 OtherApp = 0,
111 Restart = 1,
112 OtherMedia = 2,
113};
114
115enum class ScreencapPostPermission : u32 {
116 CleanThePermission = 0, // TODO(JamePeng): verify what "zero" means
117 NoExplicitSetting = 1,
118 EnableScreenshotPostingToMiiverse = 2,
119 DisableScreenshotPostingToMiiverse = 3
120};
121
122namespace ErrCodes {
123enum {
124 ParameterPresent = 2,
125 InvalidAppletSlot = 4,
126};
127} // namespace ErrCodes
128
129/// Send a parameter to the currently-running application, which will read it via ReceiveParameter
130void SendParameter(const MessageParameter& parameter);
131
132/**
133 * APT::Initialize service function
134 * Service function that initializes the APT process for the running application
135 * Outputs:
136 * 1 : Result of the function, 0 on success, otherwise error code
137 * 3 : Handle to the notification event
138 * 4 : Handle to the pause event
139 */
140void Initialize(Service::Interface* self);
141
142/**
143 * APT::GetSharedFont service function
144 * Outputs:
145 * 1 : Result of function, 0 on success, otherwise error code
146 * 2 : Virtual address of where shared font will be loaded in memory
147 * 4 : Handle to shared font memory
148 */
149void GetSharedFont(Service::Interface* self);
150
151/**
152 * APT::Wrap service function
153 * Inputs:
154 * 1 : Output buffer size
155 * 2 : Input buffer size
156 * 3 : Nonce offset to the input buffer
157 * 4 : Nonce size
158 * 5 : Buffer mapping descriptor ((input_buffer_size << 4) | 0xA)
159 * 6 : Input buffer address
160 * 7 : Buffer mapping descriptor ((input_buffer_size << 4) | 0xC)
161 * 8 : Output buffer address
162 * Outputs:
163 * 1 : Result of function, 0 on success, otherwise error code
164 * 2 : Buffer unmapping descriptor ((input_buffer_size << 4) | 0xA)
165 * 3 : Input buffer address
166 * 4 : Buffer unmapping descriptor ((input_buffer_size << 4) | 0xC)
167 * 5 : Output buffer address
168 */
169void Wrap(Service::Interface* self);
170
171/**
172 * APT::Unwrap service function
173 * Inputs:
174 * 1 : Output buffer size
175 * 2 : Input buffer size
176 * 3 : Nonce offset to the output buffer
177 * 4 : Nonce size
178 * 5 : Buffer mapping descriptor ((input_buffer_size << 4) | 0xA)
179 * 6 : Input buffer address
180 * 7 : Buffer mapping descriptor ((input_buffer_size << 4) | 0xC)
181 * 8 : Output buffer address
182 * Outputs:
183 * 1 : Result of function, 0 on success, otherwise error code
184 * 2 : Buffer unmapping descriptor ((input_buffer_size << 4) | 0xA)
185 * 3 : Input buffer address
186 * 4 : Buffer unmapping descriptor ((input_buffer_size << 4) | 0xC)
187 * 5 : Output buffer address
188 */
189void Unwrap(Service::Interface* self);
190
191/**
192 * APT::NotifyToWait service function
193 * Inputs:
194 * 1 : AppID
195 * Outputs:
196 * 1 : Result of function, 0 on success, otherwise error code
197 */
198void NotifyToWait(Service::Interface* self);
199
200/**
201 * APT::GetLockHandle service function
202 * Inputs:
203 * 1 : Applet attributes
204 * Outputs:
205 * 1 : Result of function, 0 on success, otherwise error code
206 * 2 : Applet attributes
207 * 3 : Power button state
208 * 4 : IPC handle descriptor
209 * 5 : APT mutex handle
210 */
211void GetLockHandle(Service::Interface* self);
212
213/**
214 * APT::Enable service function
215 * Inputs:
216 * 1 : Applet attributes
217 * Outputs:
218 * 1 : Result of function, 0 on success, otherwise error code
219 */
220void Enable(Service::Interface* self);
221
222/**
223 * APT::GetAppletManInfo service function.
224 * Inputs:
225 * 1 : Unknown
226 * Outputs:
227 * 1 : Result of function, 0 on success, otherwise error code
228 * 2 : Unknown u32 value
229 * 3 : Unknown u8 value
230 * 4 : Home Menu AppId
231 * 5 : AppID of currently active app
232 */
233void GetAppletManInfo(Service::Interface* self);
234
235/**
236 * APT::GetAppletInfo service function.
237 * Inputs:
238 * 1 : AppId
239 * Outputs:
240 * 1 : Result of function, 0 on success, otherwise error code
241 * 2-3 : Title ID
242 * 4 : Media Type
243 * 5 : Registered
244 * 6 : Loaded
245 * 7 : Attributes
246 */
247void GetAppletInfo(Service::Interface* self);
248
249/**
250 * APT::IsRegistered service function. This returns whether the specified AppID is registered with
251 * NS yet. An AppID is "registered" once the process associated with the AppID uses APT:Enable. Home
252 * Menu uses this command to determine when the launched process is running and to determine when to
253 * stop using GSP, etc., while displaying the "Nintendo 3DS" loading screen.
254 *
255 * Inputs:
256 * 1 : AppID
257 * Outputs:
258 * 0 : Return header
259 * 1 : Result of function, 0 on success, otherwise error code
260 * 2 : Output, 0 = not registered, 1 = registered.
261 */
262void IsRegistered(Service::Interface* self);
263
264void InquireNotification(Service::Interface* self);
265
266/**
267 * APT::SendParameter service function. This sets the parameter data state.
268 * Inputs:
269 * 1 : Source AppID
270 * 2 : Destination AppID
271 * 3 : Signal type
272 * 4 : Parameter buffer size, max size is 0x1000 (this can be zero)
273 * 5 : Value
274 * 6 : Handle to the destination process, likely used for shared memory (this can be zero)
275 * 7 : (Size<<14) | 2
276 * 8 : Input parameter buffer ptr
277 * Outputs:
278 * 0 : Return Header
279 * 1 : Result of function, 0 on success, otherwise error code
280*/
281void SendParameter(Service::Interface* self);
282
283/**
284 * APT::ReceiveParameter service function. This returns the current parameter data from NS state,
285 * from the source process which set the parameters. Once finished, NS will clear a flag in the NS
286 * state so that this command will return an error if this command is used again if parameters were
287 * not set again. This is called when the second Initialize event is triggered. It returns a signal
288 * type indicating why it was triggered.
289 * Inputs:
290 * 1 : AppID
291 * 2 : Parameter buffer size, max size is 0x1000
292 * Outputs:
293 * 1 : Result of function, 0 on success, otherwise error code
294 * 2 : AppID of the process which sent these parameters
295 * 3 : Signal type
296 * 4 : Actual parameter buffer size, this is <= to the the input size
297 * 5 : Value
298 * 6 : Handle from the source process which set the parameters, likely used for shared memory
299 * 7 : Size
300 * 8 : Output parameter buffer ptr
301 */
302void ReceiveParameter(Service::Interface* self);
303
304/**
305 * APT::GlanceParameter service function. This is exactly the same as APT_U::ReceiveParameter
306 * (except for the word value prior to the output handle), except this will not clear the flag
307 * (except when responseword[3]==8 || responseword[3]==9) in NS state.
308 * Inputs:
309 * 1 : AppID
310 * 2 : Parameter buffer size, max size is 0x1000
311 * Outputs:
312 * 1 : Result of function, 0 on success, otherwise error code
313 * 2 : Unknown, for now assume AppID of the process which sent these parameters
314 * 3 : Unknown, for now assume Signal type
315 * 4 : Actual parameter buffer size, this is <= to the the input size
316 * 5 : Value
317 * 6 : Handle from the source process which set the parameters, likely used for shared memory
318 * 7 : Size
319 * 8 : Output parameter buffer ptr
320 */
321void GlanceParameter(Service::Interface* self);
322
323/**
324 * APT::CancelParameter service function. When the parameter data is available, and when the above
325 * specified fields match the ones in NS state(for the ones where the checks are enabled), this
326 * clears the flag which indicates that parameter data is available
327 * (same flag cleared by APT:ReceiveParameter).
328 * Inputs:
329 * 1 : Flag, when non-zero NS will compare the word after this one with a field in the NS
330 * state.
331 * 2 : Unknown, this is the same as the first unknown field returned by APT:ReceiveParameter.
332 * 3 : Flag, when non-zero NS will compare the word after this one with a field in the NS
333 * state.
334 * 4 : AppID
335 * Outputs:
336 * 0 : Return header
337 * 1 : Result of function, 0 on success, otherwise error code
338 * 2 : Status flag, 0 = failure due to no parameter data being available, or the above enabled
339 * fields don't match the fields in NS state. 1 = success.
340 */
341void CancelParameter(Service::Interface* self);
342
343/**
344 * APT::PrepareToStartApplication service function. When the input title-info programID is zero,
345 * NS will load the actual program ID via AMNet:GetTitleIDList. After doing some checks with the
346 * programID, NS will then set a NS state flag to value 1, then set the programID for AppID
347 * 0x300(application) to the input program ID(or the one from GetTitleIDList). A media-type field
348 * in the NS state is also set to the input media-type value
349 * (other state fields are set at this point as well). With 8.0.0-18, NS will set an u8 NS state
350 * field to value 1 when input flags bit8 is set
351 * Inputs:
352 * 1-4 : 0x10-byte title-info struct
353 * 4 : Flags
354 * Outputs:
355 * 0 : Return header
356 * 1 : Result of function, 0 on success, otherwise error code
357 */
358void PrepareToStartApplication(Service::Interface* self);
359
360/**
361 * APT::StartApplication service function. Buf0 is copied to NS FIRMparams+0x0, then Buf1 is copied
362 * to the NS FIRMparams+0x480. Then the application is launched.
363 * Inputs:
364 * 1 : Buffer 0 size, max size is 0x300
365 * 2 : Buffer 1 size, max size is 0x20 (this can be zero)
366 * 3 : u8 flag
367 * 4 : (Size0<<14) | 2
368 * 5 : Buffer 0 pointer
369 * 6 : (Size1<<14) | 0x802
370 * 7 : Buffer 1 pointer
371 * Outputs:
372 * 0 : Return Header
373 * 1 : Result of function, 0 on success, otherwise error code
374*/
375void StartApplication(Service::Interface* self);
376
377/**
378 * APT::AppletUtility service function
379 * Inputs:
380 * 1 : Unknown, but clearly used for something
381 * 2 : Buffer 1 size (purpose is unknown)
382 * 3 : Buffer 2 size (purpose is unknown)
383 * 5 : Buffer 1 address (purpose is unknown)
384 * 65 : Buffer 2 address (purpose is unknown)
385 * Outputs:
386 * 1 : Result of function, 0 on success, otherwise error code
387 */
388void AppletUtility(Service::Interface* self);
389
390/**
391 * APT::SetAppCpuTimeLimit service function
392 * Inputs:
393 * 1 : Value, must be one
394 * 2 : Percentage of CPU time from 5 to 80
395 * Outputs:
396 * 1 : Result of function, 0 on success, otherwise error code
397 */
398void SetAppCpuTimeLimit(Service::Interface* self);
399
400/**
401 * APT::GetAppCpuTimeLimit service function
402 * Inputs:
403 * 1 : Value, must be one
404 * Outputs:
405 * 0 : Return header
406 * 1 : Result of function, 0 on success, otherwise error code
407 * 2 : System core CPU time percentage
408 */
409void GetAppCpuTimeLimit(Service::Interface* self);
410
411/**
412 * APT::PrepareToStartLibraryApplet service function
413 * Inputs:
414 * 0 : Command header [0x00180040]
415 * 1 : Id of the applet to start
416 * Outputs:
417 * 0 : Return header
418 * 1 : Result of function, 0 on success, otherwise error code
419 */
420void PrepareToStartLibraryApplet(Service::Interface* self);
421
422/**
423 * APT::PrepareToStartNewestHomeMenu service function
424 * Inputs:
425 * 0 : Command header [0x001A0000]
426 * Outputs:
427 * 0 : Return header
428 * 1 : Result of function
429 */
430void PrepareToStartNewestHomeMenu(Service::Interface* self);
431
432/**
433 * APT::PreloadLibraryApplet service function
434 * Inputs:
435 * 0 : Command header [0x00160040]
436 * 1 : Id of the applet to start
437 * Outputs:
438 * 0 : Return header
439 * 1 : Result of function, 0 on success, otherwise error code
440 */
441void PreloadLibraryApplet(Service::Interface* self);
442
443/**
444 * APT::StartLibraryApplet service function
445 * Inputs:
446 * 0 : Command header [0x001E0084]
447 * 1 : Id of the applet to start
448 * 2 : Buffer size
449 * 3 : Always 0?
450 * 4 : Handle passed to the applet
451 * 5 : (Size << 14) | 2
452 * 6 : Input buffer virtual address
453 * Outputs:
454 * 0 : Return header
455 * 1 : Result of function, 0 on success, otherwise error code
456 */
457void StartLibraryApplet(Service::Interface* self);
458
459/**
460 * APT::CancelLibraryApplet service function
461 * Inputs:
462 * 0 : Command header [0x003B0040]
463 * 1 : u8, Application exiting (0 = not exiting, 1 = exiting)
464 * Outputs:
465 * 0 : Header code
466 * 1 : Result code
467 */
468void CancelLibraryApplet(Service::Interface* self);
469
470/**
471 * APT::GetStartupArgument service function
472 * Inputs:
473 * 1 : Parameter Size (capped to 0x300)
474 * 2 : StartupArgumentType
475 * 65 : Output buffer for startup argument
476 * Outputs:
477 * 0 : Return header
478 * 1 : Result of function, 0 on success, otherwise error code
479 * 2 : u8, Exists (0 = does not exist, 1 = exists)
480 */
481void GetStartupArgument(Service::Interface* self);
482
483/**
484 * APT::SetScreenCapPostPermission service function
485 * Inputs:
486 * 0 : Header Code[0x00550040]
487 * 1 : u8 The screenshot posting permission
488 * Outputs:
489 * 1 : Result of function, 0 on success, otherwise error code
490 */
491void SetScreenCapPostPermission(Service::Interface* self);
492
493/**
494 * APT::GetScreenCapPostPermission service function
495 * Inputs:
496 * 0 : Header Code[0x00560000]
497 * Outputs:
498 * 1 : Result of function, 0 on success, otherwise error code
499 * 2 : u8 The screenshot posting permission
500 */
501void GetScreenCapPostPermission(Service::Interface* self);
502
503/**
504 * APT::CheckNew3DSApp service function
505 * Outputs:
506 * 1: Result code, 0 on success, otherwise error code
507 * 2: u8 output: 0 = Old3DS, 1 = New3DS.
508 * Note:
509 * This uses PTMSYSM:CheckNew3DS.
510 * When a certain NS state field is non-zero, the output value is zero,
511 * Otherwise the output is from PTMSYSM:CheckNew3DS.
512 * Normally this NS state field is zero, however this state field is set to 1
513 * when APT:PrepareToStartApplication is used with flags bit8 is set.
514 */
515void CheckNew3DSApp(Service::Interface* self);
516
517/**
518 * Wrapper for PTMSYSM:CheckNew3DS
519 * APT::CheckNew3DS service function
520 * Outputs:
521 * 1: Result code, 0 on success, otherwise error code
522 * 2: u8 output: 0 = Old3DS, 1 = New3DS.
523 */
524void CheckNew3DS(Service::Interface* self);
525
526/// Initialize the APT service
527void Init();
528
529/// Shutdown the APT service
530void Shutdown();
531
532} // namespace APT
533} // namespace Service
diff --git a/src/core/hle/service/apt/apt_a.cpp b/src/core/hle/service/apt/apt_a.cpp
deleted file mode 100644
index c496cba8d..000000000
--- a/src/core/hle/service/apt/apt_a.cpp
+++ /dev/null
@@ -1,110 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/apt/apt.h"
6#include "core/hle/service/apt/apt_a.h"
7
8namespace Service {
9namespace APT {
10
11const Interface::FunctionInfo FunctionTable[] = {
12 {0x00010040, GetLockHandle, "GetLockHandle"},
13 {0x00020080, Initialize, "Initialize"},
14 {0x00030040, Enable, "Enable"},
15 {0x00040040, nullptr, "Finalize"},
16 {0x00050040, GetAppletManInfo, "GetAppletManInfo"},
17 {0x00060040, GetAppletInfo, "GetAppletInfo"},
18 {0x00070000, nullptr, "GetLastSignaledAppletId"},
19 {0x00080000, nullptr, "CountRegisteredApplet"},
20 {0x00090040, IsRegistered, "IsRegistered"},
21 {0x000A0040, nullptr, "GetAttribute"},
22 {0x000B0040, InquireNotification, "InquireNotification"},
23 {0x000C0104, SendParameter, "SendParameter"},
24 {0x000D0080, ReceiveParameter, "ReceiveParameter"},
25 {0x000E0080, GlanceParameter, "GlanceParameter"},
26 {0x000F0100, CancelParameter, "CancelParameter"},
27 {0x001000C2, nullptr, "DebugFunc"},
28 {0x001100C0, nullptr, "MapProgramIdForDebug"},
29 {0x00120040, nullptr, "SetHomeMenuAppletIdForDebug"},
30 {0x00130000, nullptr, "GetPreparationState"},
31 {0x00140040, nullptr, "SetPreparationState"},
32 {0x00150140, PrepareToStartApplication, "PrepareToStartApplication"},
33 {0x00160040, PreloadLibraryApplet, "PreloadLibraryApplet"},
34 {0x00170040, nullptr, "FinishPreloadingLibraryApplet"},
35 {0x00180040, PrepareToStartLibraryApplet, "PrepareToStartLibraryApplet"},
36 {0x00190040, nullptr, "PrepareToStartSystemApplet"},
37 {0x001A0000, nullptr, "PrepareToStartNewestHomeMenu"},
38 {0x001B00C4, nullptr, "StartApplication"},
39 {0x001C0000, nullptr, "WakeupApplication"},
40 {0x001D0000, nullptr, "CancelApplication"},
41 {0x001E0084, StartLibraryApplet, "StartLibraryApplet"},
42 {0x001F0084, nullptr, "StartSystemApplet"},
43 {0x00200044, nullptr, "StartNewestHomeMenu"},
44 {0x00210000, nullptr, "OrderToCloseApplication"},
45 {0x00220040, nullptr, "PrepareToCloseApplication"},
46 {0x00230040, nullptr, "PrepareToJumpToApplication"},
47 {0x00240044, nullptr, "JumpToApplication"},
48 {0x002500C0, nullptr, "PrepareToCloseLibraryApplet"},
49 {0x00260000, nullptr, "PrepareToCloseSystemApplet"},
50 {0x00270044, nullptr, "CloseApplication"},
51 {0x00280044, nullptr, "CloseLibraryApplet"},
52 {0x00290044, nullptr, "CloseSystemApplet"},
53 {0x002A0000, nullptr, "OrderToCloseSystemApplet"},
54 {0x002B0000, nullptr, "PrepareToJumpToHomeMenu"},
55 {0x002C0044, nullptr, "JumpToHomeMenu"},
56 {0x002D0000, nullptr, "PrepareToLeaveHomeMenu"},
57 {0x002E0044, nullptr, "LeaveHomeMenu"},
58 {0x002F0040, nullptr, "PrepareToLeaveResidentApplet"},
59 {0x00300044, nullptr, "LeaveResidentApplet"},
60 {0x00310100, nullptr, "PrepareToDoApplicationJump"},
61 {0x00320084, nullptr, "DoApplicationJump"},
62 {0x00330000, nullptr, "GetProgramIdOnApplicationJump"},
63 {0x00340084, nullptr, "SendDeliverArg"},
64 {0x00350080, nullptr, "ReceiveDeliverArg"},
65 {0x00360040, nullptr, "LoadSysMenuArg"},
66 {0x00370042, nullptr, "StoreSysMenuArg"},
67 {0x00380040, nullptr, "PreloadResidentApplet"},
68 {0x00390040, nullptr, "PrepareToStartResidentApplet"},
69 {0x003A0044, nullptr, "StartResidentApplet"},
70 {0x003B0040, CancelLibraryApplet, "CancelLibraryApplet"},
71 {0x003C0042, nullptr, "SendDspSleep"},
72 {0x003D0042, nullptr, "SendDspWakeUp"},
73 {0x003E0080, nullptr, "ReplySleepQuery"},
74 {0x003F0040, nullptr, "ReplySleepNotificationComplete"},
75 {0x00400042, nullptr, "SendCaptureBufferInfo"},
76 {0x00410040, nullptr, "ReceiveCaptureBufferInfo"},
77 {0x00420080, nullptr, "SleepSystem"},
78 {0x00430040, NotifyToWait, "NotifyToWait"},
79 {0x00440000, GetSharedFont, "GetSharedFont"},
80 {0x00450040, nullptr, "GetWirelessRebootInfo"},
81 {0x00460104, Wrap, "Wrap"},
82 {0x00470104, Unwrap, "Unwrap"},
83 {0x00480100, nullptr, "GetProgramInfo"},
84 {0x00490180, nullptr, "Reboot"},
85 {0x004A0040, nullptr, "GetCaptureInfo"},
86 {0x004B00C2, AppletUtility, "AppletUtility"},
87 {0x004C0000, nullptr, "SetFatalErrDispMode"},
88 {0x004D0080, nullptr, "GetAppletProgramInfo"},
89 {0x004E0000, nullptr, "HardwareResetAsync"},
90 {0x004F0080, SetAppCpuTimeLimit, "SetAppCpuTimeLimit"},
91 {0x00500040, GetAppCpuTimeLimit, "GetAppCpuTimeLimit"},
92 {0x00510080, GetStartupArgument, "GetStartupArgument"},
93 {0x00520104, nullptr, "Wrap1"},
94 {0x00530104, nullptr, "Unwrap1"},
95 {0x00550040, SetScreenCapPostPermission, "SetScreenCapPostPermission"},
96 {0x00560000, GetScreenCapPostPermission, "GetScreenCapPostPermission"},
97 {0x00570044, nullptr, "WakeupApplication2"},
98 {0x00580002, nullptr, "GetProgramID"},
99 {0x01010000, CheckNew3DSApp, "CheckNew3DSApp"},
100 {0x01020000, CheckNew3DS, "CheckNew3DS"},
101 {0x01040000, nullptr, "IsStandardMemoryLayout"},
102 {0x01050100, nullptr, "IsTitleAllowed"},
103};
104
105APT_A_Interface::APT_A_Interface() : Interface(MaxAPTSessions) {
106 Register(FunctionTable);
107}
108
109} // namespace APT
110} // namespace Service
diff --git a/src/core/hle/service/apt/apt_a.h b/src/core/hle/service/apt/apt_a.h
deleted file mode 100644
index 331fb5586..000000000
--- a/src/core/hle/service/apt/apt_a.h
+++ /dev/null
@@ -1,22 +0,0 @@
1// Copyright 2014 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 "core/hle/service/service.h"
8
9namespace Service {
10namespace APT {
11
12class APT_A_Interface : public Service::Interface {
13public:
14 APT_A_Interface();
15
16 std::string GetPortName() const override {
17 return "APT:A";
18 }
19};
20
21} // namespace APT
22} // namespace Service \ No newline at end of file
diff --git a/src/core/hle/service/apt/apt_s.cpp b/src/core/hle/service/apt/apt_s.cpp
deleted file mode 100644
index bb78ee7d7..000000000
--- a/src/core/hle/service/apt/apt_s.cpp
+++ /dev/null
@@ -1,110 +0,0 @@
1// Copyright 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/apt/apt.h"
6#include "core/hle/service/apt/apt_s.h"
7
8namespace Service {
9namespace APT {
10
11const Interface::FunctionInfo FunctionTable[] = {
12 {0x00010040, GetLockHandle, "GetLockHandle"},
13 {0x00020080, Initialize, "Initialize"},
14 {0x00030040, Enable, "Enable"},
15 {0x00040040, nullptr, "Finalize"},
16 {0x00050040, GetAppletManInfo, "GetAppletManInfo"},
17 {0x00060040, GetAppletInfo, "GetAppletInfo"},
18 {0x00070000, nullptr, "GetLastSignaledAppletId"},
19 {0x00080000, nullptr, "CountRegisteredApplet"},
20 {0x00090040, IsRegistered, "IsRegistered"},
21 {0x000A0040, nullptr, "GetAttribute"},
22 {0x000B0040, InquireNotification, "InquireNotification"},
23 {0x000C0104, SendParameter, "SendParameter"},
24 {0x000D0080, ReceiveParameter, "ReceiveParameter"},
25 {0x000E0080, GlanceParameter, "GlanceParameter"},
26 {0x000F0100, nullptr, "CancelParameter"},
27 {0x001000C2, nullptr, "DebugFunc"},
28 {0x001100C0, nullptr, "MapProgramIdForDebug"},
29 {0x00120040, nullptr, "SetHomeMenuAppletIdForDebug"},
30 {0x00130000, nullptr, "GetPreparationState"},
31 {0x00140040, nullptr, "SetPreparationState"},
32 {0x00150140, PrepareToStartApplication, "PrepareToStartApplication"},
33 {0x00160040, PreloadLibraryApplet, "PreloadLibraryApplet"},
34 {0x00170040, nullptr, "FinishPreloadingLibraryApplet"},
35 {0x00180040, PrepareToStartLibraryApplet, "PrepareToStartLibraryApplet"},
36 {0x00190040, nullptr, "PrepareToStartSystemApplet"},
37 {0x001A0000, PrepareToStartNewestHomeMenu, "PrepareToStartNewestHomeMenu"},
38 {0x001B00C4, nullptr, "StartApplication"},
39 {0x001C0000, nullptr, "WakeupApplication"},
40 {0x001D0000, nullptr, "CancelApplication"},
41 {0x001E0084, StartLibraryApplet, "StartLibraryApplet"},
42 {0x001F0084, nullptr, "StartSystemApplet"},
43 {0x00200044, nullptr, "StartNewestHomeMenu"},
44 {0x00210000, nullptr, "OrderToCloseApplication"},
45 {0x00220040, nullptr, "PrepareToCloseApplication"},
46 {0x00230040, nullptr, "PrepareToJumpToApplication"},
47 {0x00240044, nullptr, "JumpToApplication"},
48 {0x002500C0, nullptr, "PrepareToCloseLibraryApplet"},
49 {0x00260000, nullptr, "PrepareToCloseSystemApplet"},
50 {0x00270044, nullptr, "CloseApplication"},
51 {0x00280044, nullptr, "CloseLibraryApplet"},
52 {0x00290044, nullptr, "CloseSystemApplet"},
53 {0x002A0000, nullptr, "OrderToCloseSystemApplet"},
54 {0x002B0000, nullptr, "PrepareToJumpToHomeMenu"},
55 {0x002C0044, nullptr, "JumpToHomeMenu"},
56 {0x002D0000, nullptr, "PrepareToLeaveHomeMenu"},
57 {0x002E0044, nullptr, "LeaveHomeMenu"},
58 {0x002F0040, nullptr, "PrepareToLeaveResidentApplet"},
59 {0x00300044, nullptr, "LeaveResidentApplet"},
60 {0x00310100, nullptr, "PrepareToDoApplicationJump"},
61 {0x00320084, nullptr, "DoApplicationJump"},
62 {0x00330000, nullptr, "GetProgramIdOnApplicationJump"},
63 {0x00340084, nullptr, "SendDeliverArg"},
64 {0x00350080, nullptr, "ReceiveDeliverArg"},
65 {0x00360040, nullptr, "LoadSysMenuArg"},
66 {0x00370042, nullptr, "StoreSysMenuArg"},
67 {0x00380040, nullptr, "PreloadResidentApplet"},
68 {0x00390040, nullptr, "PrepareToStartResidentApplet"},
69 {0x003A0044, nullptr, "StartResidentApplet"},
70 {0x003B0040, nullptr, "CancelLibraryApplet"},
71 {0x003C0042, nullptr, "SendDspSleep"},
72 {0x003D0042, nullptr, "SendDspWakeUp"},
73 {0x003E0080, nullptr, "ReplySleepQuery"},
74 {0x003F0040, nullptr, "ReplySleepNotificationComplete"},
75 {0x00400042, nullptr, "SendCaptureBufferInfo"},
76 {0x00410040, nullptr, "ReceiveCaptureBufferInfo"},
77 {0x00420080, nullptr, "SleepSystem"},
78 {0x00430040, NotifyToWait, "NotifyToWait"},
79 {0x00440000, GetSharedFont, "GetSharedFont"},
80 {0x00450040, nullptr, "GetWirelessRebootInfo"},
81 {0x00460104, Wrap, "Wrap"},
82 {0x00470104, Unwrap, "Unwrap"},
83 {0x00480100, nullptr, "GetProgramInfo"},
84 {0x00490180, nullptr, "Reboot"},
85 {0x004A0040, nullptr, "GetCaptureInfo"},
86 {0x004B00C2, AppletUtility, "AppletUtility"},
87 {0x004C0000, nullptr, "SetFatalErrDispMode"},
88 {0x004D0080, nullptr, "GetAppletProgramInfo"},
89 {0x004E0000, nullptr, "HardwareResetAsync"},
90 {0x004F0080, SetAppCpuTimeLimit, "SetAppCpuTimeLimit"},
91 {0x00500040, GetAppCpuTimeLimit, "GetAppCpuTimeLimit"},
92 {0x00510080, GetStartupArgument, "GetStartupArgument"},
93 {0x00520104, nullptr, "Wrap1"},
94 {0x00530104, nullptr, "Unwrap1"},
95 {0x00550040, SetScreenCapPostPermission, "SetScreenCapPostPermission"},
96 {0x00560000, GetScreenCapPostPermission, "GetScreenCapPostPermission"},
97 {0x00570044, nullptr, "WakeupApplication2"},
98 {0x00580002, nullptr, "GetProgramID"},
99 {0x01010000, CheckNew3DSApp, "CheckNew3DSApp"},
100 {0x01020000, CheckNew3DS, "CheckNew3DS"},
101 {0x01040000, nullptr, "IsStandardMemoryLayout"},
102 {0x01050100, nullptr, "IsTitleAllowed"},
103};
104
105APT_S_Interface::APT_S_Interface() : Interface(MaxAPTSessions) {
106 Register(FunctionTable);
107}
108
109} // namespace APT
110} // namespace Service
diff --git a/src/core/hle/service/apt/apt_s.h b/src/core/hle/service/apt/apt_s.h
deleted file mode 100644
index 8e87b69af..000000000
--- a/src/core/hle/service/apt/apt_s.h
+++ /dev/null
@@ -1,29 +0,0 @@
1// Copyright 2015 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 "core/hle/service/service.h"
8
9namespace Service {
10namespace APT {
11
12// Application and title launching service. These services handle signaling for home/power button as
13// well. Only one session for either APT service can be open at a time, normally processes close the
14// service handle immediately once finished using the service. The commands for APT:U and APT:S are
15// exactly the same, however certain commands are only accessible with APT:S(NS module will call
16// svcBreak when the command isn't accessible). See http://3dbrew.org/wiki/NS#APT_Services.
17
18/// Interface to "APT:S" service
19class APT_S_Interface : public Service::Interface {
20public:
21 APT_S_Interface();
22
23 std::string GetPortName() const override {
24 return "APT:S";
25 }
26};
27
28} // namespace APT
29} // namespace Service \ No newline at end of file
diff --git a/src/core/hle/service/apt/apt_u.cpp b/src/core/hle/service/apt/apt_u.cpp
deleted file mode 100644
index 9dd002590..000000000
--- a/src/core/hle/service/apt/apt_u.cpp
+++ /dev/null
@@ -1,107 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/apt/apt.h"
6#include "core/hle/service/apt/apt_u.h"
7
8namespace Service {
9namespace APT {
10
11const Interface::FunctionInfo FunctionTable[] = {
12 {0x00010040, GetLockHandle, "GetLockHandle"},
13 {0x00020080, Initialize, "Initialize"},
14 {0x00030040, Enable, "Enable"},
15 {0x00040040, nullptr, "Finalize"},
16 {0x00050040, GetAppletManInfo, "GetAppletManInfo"},
17 {0x00060040, GetAppletInfo, "GetAppletInfo"},
18 {0x00070000, nullptr, "GetLastSignaledAppletId"},
19 {0x00080000, nullptr, "CountRegisteredApplet"},
20 {0x00090040, IsRegistered, "IsRegistered"},
21 {0x000A0040, nullptr, "GetAttribute"},
22 {0x000B0040, InquireNotification, "InquireNotification"},
23 {0x000C0104, SendParameter, "SendParameter"},
24 {0x000D0080, ReceiveParameter, "ReceiveParameter"},
25 {0x000E0080, GlanceParameter, "GlanceParameter"},
26 {0x000F0100, CancelParameter, "CancelParameter"},
27 {0x001000C2, nullptr, "DebugFunc"},
28 {0x001100C0, nullptr, "MapProgramIdForDebug"},
29 {0x00120040, nullptr, "SetHomeMenuAppletIdForDebug"},
30 {0x00130000, nullptr, "GetPreparationState"},
31 {0x00140040, nullptr, "SetPreparationState"},
32 {0x00150140, PrepareToStartApplication, "PrepareToStartApplication"},
33 {0x00160040, PreloadLibraryApplet, "PreloadLibraryApplet"},
34 {0x00170040, nullptr, "FinishPreloadingLibraryApplet"},
35 {0x00180040, PrepareToStartLibraryApplet, "PrepareToStartLibraryApplet"},
36 {0x00190040, nullptr, "PrepareToStartSystemApplet"},
37 {0x001A0000, nullptr, "PrepareToStartNewestHomeMenu"},
38 {0x001B00C4, nullptr, "StartApplication"},
39 {0x001C0000, nullptr, "WakeupApplication"},
40 {0x001D0000, nullptr, "CancelApplication"},
41 {0x001E0084, StartLibraryApplet, "StartLibraryApplet"},
42 {0x001F0084, nullptr, "StartSystemApplet"},
43 {0x00200044, nullptr, "StartNewestHomeMenu"},
44 {0x00210000, nullptr, "OrderToCloseApplication"},
45 {0x00220040, nullptr, "PrepareToCloseApplication"},
46 {0x00230040, nullptr, "PrepareToJumpToApplication"},
47 {0x00240044, nullptr, "JumpToApplication"},
48 {0x002500C0, nullptr, "PrepareToCloseLibraryApplet"},
49 {0x00260000, nullptr, "PrepareToCloseSystemApplet"},
50 {0x00270044, nullptr, "CloseApplication"},
51 {0x00280044, nullptr, "CloseLibraryApplet"},
52 {0x00290044, nullptr, "CloseSystemApplet"},
53 {0x002A0000, nullptr, "OrderToCloseSystemApplet"},
54 {0x002B0000, nullptr, "PrepareToJumpToHomeMenu"},
55 {0x002C0044, nullptr, "JumpToHomeMenu"},
56 {0x002D0000, nullptr, "PrepareToLeaveHomeMenu"},
57 {0x002E0044, nullptr, "LeaveHomeMenu"},
58 {0x002F0040, nullptr, "PrepareToLeaveResidentApplet"},
59 {0x00300044, nullptr, "LeaveResidentApplet"},
60 {0x00310100, nullptr, "PrepareToDoApplicationJump"},
61 {0x00320084, nullptr, "DoApplicationJump"},
62 {0x00330000, nullptr, "GetProgramIdOnApplicationJump"},
63 {0x00340084, nullptr, "SendDeliverArg"},
64 {0x00350080, nullptr, "ReceiveDeliverArg"},
65 {0x00360040, nullptr, "LoadSysMenuArg"},
66 {0x00370042, nullptr, "StoreSysMenuArg"},
67 {0x00380040, nullptr, "PreloadResidentApplet"},
68 {0x00390040, nullptr, "PrepareToStartResidentApplet"},
69 {0x003A0044, nullptr, "StartResidentApplet"},
70 {0x003B0040, CancelLibraryApplet, "CancelLibraryApplet"},
71 {0x003C0042, nullptr, "SendDspSleep"},
72 {0x003D0042, nullptr, "SendDspWakeUp"},
73 {0x003E0080, nullptr, "ReplySleepQuery"},
74 {0x003F0040, nullptr, "ReplySleepNotificationComplete"},
75 {0x00400042, nullptr, "SendCaptureBufferInfo"},
76 {0x00410040, nullptr, "ReceiveCaptureBufferInfo"},
77 {0x00420080, nullptr, "SleepSystem"},
78 {0x00430040, NotifyToWait, "NotifyToWait"},
79 {0x00440000, GetSharedFont, "GetSharedFont"},
80 {0x00450040, nullptr, "GetWirelessRebootInfo"},
81 {0x00460104, Wrap, "Wrap"},
82 {0x00470104, Unwrap, "Unwrap"},
83 {0x00480100, nullptr, "GetProgramInfo"},
84 {0x00490180, nullptr, "Reboot"},
85 {0x004A0040, nullptr, "GetCaptureInfo"},
86 {0x004B00C2, AppletUtility, "AppletUtility"},
87 {0x004C0000, nullptr, "SetFatalErrDispMode"},
88 {0x004D0080, nullptr, "GetAppletProgramInfo"},
89 {0x004E0000, nullptr, "HardwareResetAsync"},
90 {0x004F0080, SetAppCpuTimeLimit, "SetAppCpuTimeLimit"},
91 {0x00500040, GetAppCpuTimeLimit, "GetAppCpuTimeLimit"},
92 {0x00510080, GetStartupArgument, "GetStartupArgument"},
93 {0x00520104, nullptr, "Wrap1"},
94 {0x00530104, nullptr, "Unwrap1"},
95 {0x00550040, SetScreenCapPostPermission, "SetScreenCapPostPermission"},
96 {0x00560000, GetScreenCapPostPermission, "GetScreenCapPostPermission"},
97 {0x00580002, nullptr, "GetProgramID"},
98 {0x01010000, CheckNew3DSApp, "CheckNew3DSApp"},
99 {0x01020000, CheckNew3DS, "CheckNew3DS"},
100};
101
102APT_U_Interface::APT_U_Interface() : Interface(MaxAPTSessions) {
103 Register(FunctionTable);
104}
105
106} // namespace APT
107} // namespace Service
diff --git a/src/core/hle/service/apt/apt_u.h b/src/core/hle/service/apt/apt_u.h
deleted file mode 100644
index 8c7fe0ccb..000000000
--- a/src/core/hle/service/apt/apt_u.h
+++ /dev/null
@@ -1,29 +0,0 @@
1// Copyright 2014 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 "core/hle/service/service.h"
8
9namespace Service {
10namespace APT {
11
12// Application and title launching service. These services handle signaling for home/power button as
13// well. Only one session for either APT service can be open at a time, normally processes close the
14// service handle immediately once finished using the service. The commands for APT:U and APT:S are
15// exactly the same, however certain commands are only accessible with APT:S(NS module will call
16// svcBreak when the command isn't accessible). See http://3dbrew.org/wiki/NS#APT_Services.
17
18/// Interface to "APT:U" service
19class APT_U_Interface : public Service::Interface {
20public:
21 APT_U_Interface();
22
23 std::string GetPortName() const override {
24 return "APT:U";
25 }
26};
27
28} // namespace APT
29} // namespace Service \ No newline at end of file
diff --git a/src/core/hle/service/apt/bcfnt/bcfnt.cpp b/src/core/hle/service/apt/bcfnt/bcfnt.cpp
deleted file mode 100644
index 6d2474702..000000000
--- a/src/core/hle/service/apt/bcfnt/bcfnt.cpp
+++ /dev/null
@@ -1,110 +0,0 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/apt/bcfnt/bcfnt.h"
6#include "core/hle/service/service.h"
7
8namespace Service {
9namespace APT {
10namespace BCFNT {
11
12void RelocateSharedFont(Kernel::SharedPtr<Kernel::SharedMemory> shared_font, VAddr new_address) {
13 static const u32 SharedFontStartOffset = 0x80;
14 const u8* cfnt_ptr = shared_font->GetPointer(SharedFontStartOffset);
15
16 CFNT cfnt;
17 memcpy(&cfnt, cfnt_ptr, sizeof(cfnt));
18
19 u32 assumed_cmap_offset = 0;
20 u32 assumed_cwdh_offset = 0;
21 u32 assumed_tglp_offset = 0;
22 u32 first_cmap_offset = 0;
23 u32 first_cwdh_offset = 0;
24 u32 first_tglp_offset = 0;
25
26 // First discover the location of sections so that the rebase offset can be auto-detected
27 u32 current_offset = SharedFontStartOffset + cfnt.header_size;
28 for (unsigned block = 0; block < cfnt.num_blocks; ++block) {
29 const u8* data = shared_font->GetPointer(current_offset);
30
31 SectionHeader section_header;
32 memcpy(&section_header, data, sizeof(section_header));
33
34 if (first_cmap_offset == 0 && memcmp(section_header.magic, "CMAP", 4) == 0) {
35 first_cmap_offset = current_offset;
36 } else if (first_cwdh_offset == 0 && memcmp(section_header.magic, "CWDH", 4) == 0) {
37 first_cwdh_offset = current_offset;
38 } else if (first_tglp_offset == 0 && memcmp(section_header.magic, "TGLP", 4) == 0) {
39 first_tglp_offset = current_offset;
40 } else if (memcmp(section_header.magic, "FINF", 4) == 0) {
41 BCFNT::FINF finf;
42 memcpy(&finf, data, sizeof(finf));
43
44 assumed_cmap_offset = finf.cmap_offset - sizeof(SectionHeader);
45 assumed_cwdh_offset = finf.cwdh_offset - sizeof(SectionHeader);
46 assumed_tglp_offset = finf.tglp_offset - sizeof(SectionHeader);
47 }
48
49 current_offset += section_header.section_size;
50 }
51
52 u32 previous_base = assumed_cmap_offset - first_cmap_offset;
53 ASSERT(previous_base == assumed_cwdh_offset - first_cwdh_offset);
54 ASSERT(previous_base == assumed_tglp_offset - first_tglp_offset);
55
56 u32 offset = new_address - previous_base;
57
58 // Reset pointer back to start of sections and do the actual rebase
59 current_offset = SharedFontStartOffset + cfnt.header_size;
60 for (unsigned block = 0; block < cfnt.num_blocks; ++block) {
61 u8* data = shared_font->GetPointer(current_offset);
62
63 SectionHeader section_header;
64 memcpy(&section_header, data, sizeof(section_header));
65
66 if (memcmp(section_header.magic, "FINF", 4) == 0) {
67 BCFNT::FINF finf;
68 memcpy(&finf, data, sizeof(finf));
69
70 // Relocate the offsets in the FINF section
71 finf.cmap_offset += offset;
72 finf.cwdh_offset += offset;
73 finf.tglp_offset += offset;
74
75 memcpy(data, &finf, sizeof(finf));
76 } else if (memcmp(section_header.magic, "CMAP", 4) == 0) {
77 BCFNT::CMAP cmap;
78 memcpy(&cmap, data, sizeof(cmap));
79
80 // Relocate the offsets in the CMAP section
81 if (cmap.next_cmap_offset != 0)
82 cmap.next_cmap_offset += offset;
83
84 memcpy(data, &cmap, sizeof(cmap));
85 } else if (memcmp(section_header.magic, "CWDH", 4) == 0) {
86 BCFNT::CWDH cwdh;
87 memcpy(&cwdh, data, sizeof(cwdh));
88
89 // Relocate the offsets in the CWDH section
90 if (cwdh.next_cwdh_offset != 0)
91 cwdh.next_cwdh_offset += offset;
92
93 memcpy(data, &cwdh, sizeof(cwdh));
94 } else if (memcmp(section_header.magic, "TGLP", 4) == 0) {
95 BCFNT::TGLP tglp;
96 memcpy(&tglp, data, sizeof(tglp));
97
98 // Relocate the offsets in the TGLP section
99 tglp.sheet_data_offset += offset;
100
101 memcpy(data, &tglp, sizeof(tglp));
102 }
103
104 current_offset += section_header.section_size;
105 }
106}
107
108} // namespace BCFNT
109} // namespace APT
110} // namespace Service \ No newline at end of file
diff --git a/src/core/hle/service/apt/bcfnt/bcfnt.h b/src/core/hle/service/apt/bcfnt/bcfnt.h
deleted file mode 100644
index 453bf7606..000000000
--- a/src/core/hle/service/apt/bcfnt/bcfnt.h
+++ /dev/null
@@ -1,92 +0,0 @@
1// Copyright 2016 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 "common/swap.h"
8#include "core/hle/kernel/shared_memory.h"
9#include "core/hle/service/service.h"
10
11namespace Service {
12namespace APT {
13namespace BCFNT { ///< BCFNT Shared Font file structures
14
15struct CFNT {
16 u8 magic[4];
17 u16_le endianness;
18 u16_le header_size;
19 u32_le version;
20 u32_le file_size;
21 u32_le num_blocks;
22};
23
24struct SectionHeader {
25 u8 magic[4];
26 u32_le section_size;
27};
28
29struct FINF {
30 u8 magic[4];
31 u32_le section_size;
32 u8 font_type;
33 u8 line_feed;
34 u16_le alter_char_index;
35 u8 default_width[3];
36 u8 encoding;
37 u32_le tglp_offset;
38 u32_le cwdh_offset;
39 u32_le cmap_offset;
40 u8 height;
41 u8 width;
42 u8 ascent;
43 u8 reserved;
44};
45
46struct TGLP {
47 u8 magic[4];
48 u32_le section_size;
49 u8 cell_width;
50 u8 cell_height;
51 u8 baseline_position;
52 u8 max_character_width;
53 u32_le sheet_size;
54 u16_le num_sheets;
55 u16_le sheet_image_format;
56 u16_le num_columns;
57 u16_le num_rows;
58 u16_le sheet_width;
59 u16_le sheet_height;
60 u32_le sheet_data_offset;
61};
62
63struct CMAP {
64 u8 magic[4];
65 u32_le section_size;
66 u16_le code_begin;
67 u16_le code_end;
68 u16_le mapping_method;
69 u16_le reserved;
70 u32_le next_cmap_offset;
71};
72
73struct CWDH {
74 u8 magic[4];
75 u32_le section_size;
76 u16_le start_index;
77 u16_le end_index;
78 u32_le next_cwdh_offset;
79};
80
81/**
82 * Relocates the internal addresses of the BCFNT Shared Font to the new base. The current base will
83 * be auto-detected based on the file headers.
84 *
85 * @param shared_font SharedMemory object that contains the Shared Font
86 * @param new_address New base for the offsets in the structure.
87 */
88void RelocateSharedFont(Kernel::SharedPtr<Kernel::SharedMemory> shared_font, VAddr new_address);
89
90} // namespace BCFNT
91} // namespace APT
92} // namespace Service
diff --git a/src/core/hle/service/boss/boss.cpp b/src/core/hle/service/boss/boss.cpp
deleted file mode 100644
index 2bba3aff6..000000000
--- a/src/core/hle/service/boss/boss.cpp
+++ /dev/null
@@ -1,994 +0,0 @@
1// Copyright 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <cinttypes>
6#include "common/logging/log.h"
7#include "core/hle/ipc.h"
8#include "core/hle/result.h"
9#include "core/hle/service/boss/boss.h"
10#include "core/hle/service/boss/boss_p.h"
11#include "core/hle/service/boss/boss_u.h"
12#include "core/hle/service/service.h"
13
14namespace Service {
15namespace BOSS {
16
17static u32 new_arrival_flag;
18static u32 ns_data_new_flag;
19static u32 output_flag;
20
21void InitializeSession(Service::Interface* self) {
22 u32* cmd_buff = Kernel::GetCommandBuffer();
23 // TODO(JamePeng): Figure out the meaning of these parameters
24 u64 unk_param = ((u64)cmd_buff[1] | ((u64)cmd_buff[2] << 32));
25 u32 translation = cmd_buff[3];
26 u32 unk_param4 = cmd_buff[4];
27
28 if (translation != IPC::CallingPidDesc()) {
29 cmd_buff[0] = IPC::MakeHeader(0, 0x1, 0); // 0x40
30 cmd_buff[1] = IPC::ERR_INVALID_BUFFER_DESCRIPTOR.raw;
31 LOG_ERROR(Service_BOSS, "The translation was invalid, translation=0x%08X", translation);
32 return;
33 }
34
35 cmd_buff[0] = IPC::MakeHeader(0x1, 0x1, 0);
36 cmd_buff[1] = RESULT_SUCCESS.raw;
37
38 LOG_WARNING(Service_BOSS,
39 "(STUBBED) unk_param=0x%016" PRIX64 ", translation=0x%08X, unk_param4=0x%08X",
40 unk_param, translation, unk_param4);
41}
42
43void RegisterStorage(Service::Interface* self) {
44 u32* cmd_buff = Kernel::GetCommandBuffer();
45 // TODO(JamePeng): Figure out the meaning of these parameters
46 u32 unk_param1 = cmd_buff[1];
47 u32 unk_param2 = cmd_buff[2];
48 u32 unk_param3 = cmd_buff[3];
49 u32 unk_flag = cmd_buff[4] & 0xFF;
50
51 cmd_buff[0] = IPC::MakeHeader(0x2, 0x1, 0);
52 cmd_buff[1] = RESULT_SUCCESS.raw;
53
54 LOG_WARNING(
55 Service_BOSS,
56 "(STUBBED) unk_param1=0x%08X, unk_param2=0x%08X, unk_param3=0x%08X, unk_flag=0x%08X",
57 unk_param1, unk_param2, unk_param3, unk_flag);
58}
59
60void UnregisterStorage(Service::Interface* self) {
61 u32* cmd_buff = Kernel::GetCommandBuffer();
62
63 cmd_buff[0] = IPC::MakeHeader(0x3, 0x1, 0);
64 cmd_buff[1] = RESULT_SUCCESS.raw;
65
66 LOG_WARNING(Service_BOSS, "(STUBBED) called");
67}
68
69void GetStorageInfo(Service::Interface* self) {
70 u32* cmd_buff = Kernel::GetCommandBuffer();
71
72 cmd_buff[0] = IPC::MakeHeader(0x4, 0x2, 0);
73 cmd_buff[1] = RESULT_SUCCESS.raw;
74 cmd_buff[2] = 0; // stub 0
75
76 LOG_WARNING(Service_BOSS, "(STUBBED) called");
77}
78
79void RegisterPrivateRootCa(Service::Interface* self) {
80 u32* cmd_buff = Kernel::GetCommandBuffer();
81
82 u32 translation = cmd_buff[2];
83 u32 buff_addr = cmd_buff[3];
84 u32 buff_size = (translation >> 4);
85
86 cmd_buff[0] = IPC::MakeHeader(0x5, 0x1, 0x2);
87 cmd_buff[1] = RESULT_SUCCESS.raw;
88 cmd_buff[2] = (buff_size << 4 | 0xA);
89 cmd_buff[3] = buff_addr;
90
91 LOG_WARNING(Service_BOSS, "(STUBBED) translation=0x%08X, buff_addr=0x%08X, buff_size=0x%08X",
92 translation, buff_addr, buff_size);
93}
94
95void RegisterPrivateClientCert(Service::Interface* self) {
96 u32* cmd_buff = Kernel::GetCommandBuffer();
97 // TODO(JamePeng): Figure out the meaning of these parameters
98 u32 unk_param1 = cmd_buff[1];
99 u32 unk_param2 = cmd_buff[2];
100 u32 translation1 = cmd_buff[3];
101 u32 buff1_addr = cmd_buff[4];
102 u32 buff1_size = (translation1 >> 4);
103 u32 translation2 = cmd_buff[5];
104 u32 buff2_addr = cmd_buff[6];
105 u32 buff2_size = (translation2 >> 4);
106
107 cmd_buff[0] = IPC::MakeHeader(0x6, 0x1, 0x4);
108 cmd_buff[1] = RESULT_SUCCESS.raw;
109 cmd_buff[2] = (buff1_size << 4 | 0xA);
110 cmd_buff[3] = buff1_addr;
111 cmd_buff[2] = (buff2_size << 4 | 0xA);
112 cmd_buff[3] = buff2_addr;
113
114 LOG_WARNING(Service_BOSS, "(STUBBED) unk_param1=0x%08X, unk_param2=0x%08X, "
115 "translation1=0x%08X, buff1_addr=0x%08X, buff1_size=0x%08X, "
116 "translation2=0x%08X, buff2_addr=0x%08X, buff2_size=0x%08X",
117 unk_param1, unk_param2, translation1, buff1_addr, buff1_size, translation2,
118 buff2_addr, buff2_size);
119}
120
121void GetNewArrivalFlag(Service::Interface* self) {
122 u32* cmd_buff = Kernel::GetCommandBuffer();
123
124 cmd_buff[0] = IPC::MakeHeader(0x7, 0x2, 0);
125 cmd_buff[1] = RESULT_SUCCESS.raw;
126 cmd_buff[2] = new_arrival_flag;
127
128 LOG_WARNING(Service_BOSS, "(STUBBED) new_arrival_flag=%u", new_arrival_flag);
129}
130
131void RegisterNewArrivalEvent(Service::Interface* self) {
132 u32* cmd_buff = Kernel::GetCommandBuffer();
133 // TODO(JamePeng): Figure out the meaning of these parameters
134 u32 unk_param1 = cmd_buff[1];
135 u32 unk_param2 = cmd_buff[2];
136
137 cmd_buff[0] = IPC::MakeHeader(0x8, 0x1, 0);
138 cmd_buff[1] = RESULT_SUCCESS.raw;
139
140 LOG_WARNING(Service_BOSS, "(STUBBED) unk_param1=0x%08X, unk_param2=0x%08X", unk_param1,
141 unk_param2);
142}
143
144void SetOptoutFlag(Service::Interface* self) {
145 u32* cmd_buff = Kernel::GetCommandBuffer();
146
147 output_flag = cmd_buff[1] & 0xFF;
148
149 cmd_buff[0] = IPC::MakeHeader(0x9, 0x1, 0);
150 cmd_buff[1] = RESULT_SUCCESS.raw;
151
152 LOG_WARNING(Service_BOSS, "output_flag=%u", output_flag);
153}
154
155void GetOptoutFlag(Service::Interface* self) {
156 u32* cmd_buff = Kernel::GetCommandBuffer();
157
158 cmd_buff[0] = IPC::MakeHeader(0xA, 0x2, 0);
159 cmd_buff[1] = RESULT_SUCCESS.raw;
160 cmd_buff[2] = output_flag;
161
162 LOG_WARNING(Service_BOSS, "output_flag=%u", output_flag);
163}
164
165void RegisterTask(Service::Interface* self) {
166 u32* cmd_buff = Kernel::GetCommandBuffer();
167 // TODO(JamePeng): Figure out the meaning of these parameters
168 u32 unk_param1 = cmd_buff[1];
169 u32 unk_param2 = cmd_buff[2] & 0xFF;
170 u32 unk_param3 = cmd_buff[3] & 0xFF;
171 u32 translation = cmd_buff[4];
172 u32 buff_addr = cmd_buff[5];
173 u32 buff_size = (translation >> 4);
174
175 cmd_buff[0] = IPC::MakeHeader(0xB, 0x1, 0x2);
176 cmd_buff[1] = RESULT_SUCCESS.raw;
177 cmd_buff[2] = (buff_size << 4 | 0xA);
178 cmd_buff[3] = buff_addr;
179
180 LOG_WARNING(Service_BOSS, "(STUBBED) unk_param1=0x%08X, unk_param2=0x%08X, unk_param3=0x%08X, "
181 "translation=0x%08X, buff_addr=0x%08X, buff_size=0x%08X",
182 unk_param1, unk_param2, unk_param3, translation, buff_addr, buff_size);
183}
184
185void UnregisterTask(Service::Interface* self) {
186 u32* cmd_buff = Kernel::GetCommandBuffer();
187 // TODO(JamePeng): Figure out the meaning of these parameters
188 u32 unk_param1 = cmd_buff[1];
189 u32 unk_param2 = cmd_buff[2] & 0xFF;
190 u32 translation = cmd_buff[3];
191 u32 buff_addr = cmd_buff[4];
192 u32 buff_size = (translation >> 4);
193
194 cmd_buff[0] = IPC::MakeHeader(0xC, 0x1, 0x2);
195 cmd_buff[1] = RESULT_SUCCESS.raw;
196 cmd_buff[2] = (buff_size << 4 | 0xA);
197 cmd_buff[3] = buff_addr;
198
199 LOG_WARNING(Service_BOSS, "(STUBBED) unk_param1=0x%08X, unk_param2=0x%08X, translation=0x%08X, "
200 "buff_addr=0x%08X, buff_size=0x%08X",
201 unk_param1, unk_param2, translation, buff_addr, buff_size);
202}
203
204void ReconfigureTask(Service::Interface* self) {
205 u32* cmd_buff = Kernel::GetCommandBuffer();
206 // TODO(JamePeng): Figure out the meaning of these parameters
207 u32 unk_param1 = cmd_buff[1];
208 u32 unk_param2 = cmd_buff[2] & 0xFF;
209 u32 translation = cmd_buff[3];
210 u32 buff_addr = cmd_buff[4];
211 u32 buff_size = (translation >> 4);
212
213 cmd_buff[0] = IPC::MakeHeader(0xD, 0x1, 0x2);
214 cmd_buff[1] = RESULT_SUCCESS.raw;
215 cmd_buff[2] = (buff_size << 4 | 0xA);
216 cmd_buff[3] = buff_addr;
217
218 LOG_WARNING(Service_BOSS, "(STUBBED) unk_param1=0x%08X, unk_param2=0x%08X, translation=0x%08X, "
219 "buff_addr=0x%08X, buff_size=0x%08X",
220 unk_param1, unk_param2, translation, buff_addr, buff_size);
221}
222
223void GetTaskIdList(Service::Interface* self) {
224 u32* cmd_buff = Kernel::GetCommandBuffer();
225
226 cmd_buff[0] = IPC::MakeHeader(0xE, 0x1, 0);
227 cmd_buff[1] = RESULT_SUCCESS.raw;
228
229 LOG_WARNING(Service_BOSS, "(STUBBED) called");
230}
231
232void GetStepIdList(Service::Interface* self) {
233 u32* cmd_buff = Kernel::GetCommandBuffer();
234
235 u32 translation = cmd_buff[2];
236 u32 buff_addr = cmd_buff[3];
237 u32 buff_size = (translation >> 4);
238
239 cmd_buff[0] = IPC::MakeHeader(0xF, 0x1, 0x2);
240 cmd_buff[1] = RESULT_SUCCESS.raw;
241 cmd_buff[2] = (buff_size << 4 | 0xA);
242 cmd_buff[3] = buff_addr;
243
244 LOG_WARNING(Service_BOSS, "(STUBBED) translation=0x%08X, buff_addr=0x%08X, buff_size=0x%08X",
245 translation, buff_addr, buff_size);
246}
247
248void GetNsDataIdList(Service::Interface* self) {
249 u32* cmd_buff = Kernel::GetCommandBuffer();
250 // TODO(JamePeng): Figure out the meaning of these parameters
251 u32 unk_param1 = cmd_buff[1];
252 u32 unk_param2 = cmd_buff[2];
253 u32 unk_param3 = cmd_buff[3];
254 u32 unk_param4 = cmd_buff[4];
255 u32 translation = cmd_buff[5];
256 u32 buff_addr = cmd_buff[6];
257 u32 buff_size = (translation >> 4);
258
259 cmd_buff[0] = IPC::MakeHeader(0x10, 0x3, 0x2);
260 cmd_buff[1] = RESULT_SUCCESS.raw;
261 cmd_buff[2] = 0; // stub 0 (16 bit value)
262 cmd_buff[3] = 0; // stub 0 (16 bit value)
263 cmd_buff[4] = (buff_size << 4 | 0xC);
264 cmd_buff[5] = buff_addr;
265
266 LOG_WARNING(Service_BOSS, "(STUBBED) unk_param1=0x%08X, unk_param2=0x%08X, unk_param3=0x%08X, "
267 "unk_param4=0x%08X, translation=0x%08X, "
268 "buff_addr=0x%08X, buff_size=0x%08X",
269 unk_param1, unk_param2, unk_param3, unk_param4, translation, buff_addr, buff_size);
270}
271
272void GetOwnNsDataIdList(Service::Interface* self) {
273 u32* cmd_buff = Kernel::GetCommandBuffer();
274 // TODO(JamePeng): Figure out the meaning of these parameters
275 u32 unk_param1 = cmd_buff[1];
276 u32 unk_param2 = cmd_buff[2];
277 u32 unk_param3 = cmd_buff[3];
278 u32 unk_param4 = cmd_buff[4];
279 u32 translation = cmd_buff[5];
280 u32 buff_addr = cmd_buff[6];
281 u32 buff_size = (translation >> 4);
282
283 cmd_buff[0] = IPC::MakeHeader(0x11, 0x3, 0x2);
284 cmd_buff[1] = RESULT_SUCCESS.raw;
285 cmd_buff[2] = 0; // stub 0 (16 bit value)
286 cmd_buff[3] = 0; // stub 0 (16 bit value)
287 cmd_buff[4] = (buff_size << 4 | 0xC);
288 cmd_buff[5] = buff_addr;
289
290 LOG_WARNING(Service_BOSS, "(STUBBED) unk_param1=0x%08X, unk_param2=0x%08X, unk_param3=0x%08X, "
291 "unk_param4=0x%08X, translation=0x%08X, "
292 "buff_addr=0x%08X, buff_size=0x%08X",
293 unk_param1, unk_param2, unk_param3, unk_param4, translation, buff_addr, buff_size);
294}
295
296void GetNewDataNsDataIdList(Service::Interface* self) {
297 u32* cmd_buff = Kernel::GetCommandBuffer();
298 // TODO(JamePeng): Figure out the meaning of these parameters
299 u32 unk_param1 = cmd_buff[1];
300 u32 unk_param2 = cmd_buff[2];
301 u32 unk_param3 = cmd_buff[3];
302 u32 unk_param4 = cmd_buff[4];
303 u32 translation = cmd_buff[5];
304 u32 buff_addr = cmd_buff[6];
305 u32 buff_size = (translation >> 4);
306
307 cmd_buff[0] = IPC::MakeHeader(0x12, 0x3, 0x2);
308 cmd_buff[1] = RESULT_SUCCESS.raw;
309 cmd_buff[2] = 0; // stub 0 (16 bit value)
310 cmd_buff[3] = 0; // stub 0 (16 bit value)
311 cmd_buff[4] = (buff_size << 4 | 0xC);
312 cmd_buff[5] = buff_addr;
313
314 LOG_WARNING(Service_BOSS, "(STUBBED) unk_param1=0x%08X, unk_param2=0x%08X, unk_param3=0x%08X, "
315 "unk_param4=0x%08X, translation=0x%08X, "
316 "buff_addr=0x%08X, buff_size=0x%08X",
317 unk_param1, unk_param2, unk_param3, unk_param4, translation, buff_addr, buff_size);
318}
319
320void GetOwnNewDataNsDataIdList(Service::Interface* self) {
321 u32* cmd_buff = Kernel::GetCommandBuffer();
322 // TODO(JamePeng): Figure out the meaning of these parameters
323 u32 unk_param1 = cmd_buff[1];
324 u32 unk_param2 = cmd_buff[2];
325 u32 unk_param3 = cmd_buff[3];
326 u32 unk_param4 = cmd_buff[4];
327 u32 translation = cmd_buff[5];
328 u32 buff_addr = cmd_buff[6];
329 u32 buff_size = (translation >> 4);
330
331 cmd_buff[0] = IPC::MakeHeader(0x13, 0x3, 0x2);
332 cmd_buff[1] = RESULT_SUCCESS.raw;
333 cmd_buff[2] = 0; // stub 0 (16 bit value)
334 cmd_buff[3] = 0; // stub 0 (16 bit value)
335 cmd_buff[4] = (buff_size << 4 | 0xC);
336 cmd_buff[5] = buff_addr;
337
338 LOG_WARNING(Service_BOSS, "(STUBBED) unk_param1=0x%08X, unk_param2=0x%08X, unk_param3=0x%08X, "
339 "unk_param4=0x%08X, translation=0x%08X, "
340 "buff_addr=0x%08X, buff_size=0x%08X",
341 unk_param1, unk_param2, unk_param3, unk_param4, translation, buff_addr, buff_size);
342}
343
344void SendProperty(Service::Interface* self) {
345 u32* cmd_buff = Kernel::GetCommandBuffer();
346 // TODO(JamePeng): Figure out the meaning of these parameters
347 u32 unk_param1 = cmd_buff[1];
348 u32 unk_param2 = cmd_buff[2];
349 u32 translation = cmd_buff[3];
350 u32 buff_addr = cmd_buff[4];
351 u32 buff_size = (translation >> 4);
352
353 cmd_buff[0] = IPC::MakeHeader(0x14, 0x1, 0x2);
354 cmd_buff[1] = RESULT_SUCCESS.raw;
355 cmd_buff[2] = (buff_size << 4 | 0xA);
356 cmd_buff[3] = buff_addr;
357
358 LOG_WARNING(Service_BOSS, "(STUBBED) unk_param1=0x%08X, unk_param2=0x%08X, translation=0x%08X, "
359 "buff_addr=0x%08X, buff_size=0x%08X",
360 unk_param1, unk_param2, translation, buff_addr, buff_size);
361}
362
363void SendPropertyHandle(Service::Interface* self) {
364 u32* cmd_buff = Kernel::GetCommandBuffer();
365 // TODO(JamePeng): Figure out the meaning of these parameters
366 u32 unk_param1 = cmd_buff[2] & 0xFF;
367 u32 translation = cmd_buff[3];
368 u32 buff_addr = cmd_buff[4];
369 u32 buff_size = (translation >> 4);
370
371 cmd_buff[0] = IPC::MakeHeader(0x15, 0x1, 0x2);
372 cmd_buff[1] = RESULT_SUCCESS.raw;
373 cmd_buff[2] = (buff_size << 4 | 0xA);
374 cmd_buff[3] = buff_addr;
375
376 LOG_WARNING(Service_BOSS, "(STUBBED) unk_param1=0x%08X, translation=0x%08X, "
377 "buff_addr=0x%08X, buff_size=0x%08X",
378 unk_param1, translation, buff_addr, buff_size);
379}
380
381void ReceiveProperty(Service::Interface* self) {
382 u32* cmd_buff = Kernel::GetCommandBuffer();
383 // TODO(JamePeng): Figure out the meaning of these parameters
384 u32 unk_param1 = cmd_buff[1];
385 u32 buff_size = cmd_buff[2];
386 u32 translation = cmd_buff[3];
387 u32 buff_addr = cmd_buff[4];
388
389 cmd_buff[0] = IPC::MakeHeader(0x16, 0x2, 0x2);
390 cmd_buff[1] = RESULT_SUCCESS.raw;
391 cmd_buff[2] = 0; // stub 0 (32 bit value)
392 cmd_buff[2] = (buff_size << 4 | 0xC);
393 cmd_buff[3] = buff_addr;
394
395 LOG_WARNING(Service_BOSS, "(STUBBED) unk_param1=0x%08X, buff_size=0x%08X, "
396 "translation=0x%08X, buff_addr=0x%08X",
397 unk_param1, buff_size, translation, buff_addr);
398}
399
400void UpdateTaskInterval(Service::Interface* self) {
401 u32* cmd_buff = Kernel::GetCommandBuffer();
402 // TODO(JamePeng): Figure out the meaning of these parameters
403 u32 unk_param1 = cmd_buff[1];
404 u32 unk_param2 = cmd_buff[2] & 0xFF;
405 u32 translation = cmd_buff[3];
406 u32 buff_addr = cmd_buff[4];
407 u32 buff_size = (translation >> 4);
408
409 cmd_buff[0] = IPC::MakeHeader(0x17, 0x1, 0x2);
410 cmd_buff[1] = RESULT_SUCCESS.raw;
411 cmd_buff[2] = (buff_size << 4 | 0xA);
412 cmd_buff[3] = buff_addr;
413
414 LOG_WARNING(Service_BOSS, "(STUBBED) unk_param1=0x%08X, unk_param2=0x%08X, "
415 "translation=0x%08X, buff_addr=0x%08X, buff_size=0x%08X",
416 unk_param1, unk_param2, translation, buff_addr, buff_size);
417}
418
419void UpdateTaskCount(Service::Interface* self) {
420 u32* cmd_buff = Kernel::GetCommandBuffer();
421
422 u32 buff_size = cmd_buff[1];
423 u32 unk_param2 = cmd_buff[2]; // TODO(JamePeng): Figure out the meaning of these parameters
424 u32 translation = cmd_buff[3];
425 u32 buff_addr = cmd_buff[4];
426
427 cmd_buff[0] = IPC::MakeHeader(0x18, 0x1, 0x2);
428 cmd_buff[1] = RESULT_SUCCESS.raw;
429 cmd_buff[2] = (buff_size << 4 | 0xA);
430 cmd_buff[3] = buff_addr;
431
432 LOG_WARNING(Service_BOSS, "(STUBBED) buff_size=0x%08X, unk_param2=0x%08X, "
433 "translation=0x%08X, buff_addr=0x%08X",
434 buff_size, unk_param2, translation, buff_addr);
435}
436
437void GetTaskInterval(Service::Interface* self) {
438 u32* cmd_buff = Kernel::GetCommandBuffer();
439
440 u32 unk_param1 = cmd_buff[1]; // TODO(JamePeng): Figure out the meaning of these parameters
441 u32 translation = cmd_buff[2];
442 u32 buff_addr = cmd_buff[3];
443 u32 buff_size = (translation >> 4);
444
445 cmd_buff[0] = IPC::MakeHeader(0x19, 0x2, 0x2);
446 cmd_buff[1] = RESULT_SUCCESS.raw;
447 cmd_buff[2] = 0; // stub 0 ( 32bit value)
448 cmd_buff[3] = (buff_size << 4 | 0xA);
449 cmd_buff[4] = buff_addr;
450
451 LOG_WARNING(Service_BOSS, "(STUBBED) unk_param1=0x%08X, translation=0x%08X, "
452 "buff_addr=0x%08X, buff_size=0x%08X",
453 unk_param1, translation, buff_addr, buff_size);
454}
455
456void GetTaskCount(Service::Interface* self) {
457 u32* cmd_buff = Kernel::GetCommandBuffer();
458
459 u32 unk_param1 = cmd_buff[1]; // TODO(JamePeng): Figure out the meaning of these parameters
460 u32 translation = cmd_buff[2];
461 u32 buff_addr = cmd_buff[3];
462 u32 buff_size = (translation >> 4);
463
464 cmd_buff[0] = IPC::MakeHeader(0x1A, 0x2, 0x2);
465 cmd_buff[1] = RESULT_SUCCESS.raw;
466 cmd_buff[2] = 0; // stub 0 ( 32bit value)
467 cmd_buff[3] = (buff_size << 4 | 0xA);
468 cmd_buff[4] = buff_addr;
469
470 LOG_WARNING(Service_BOSS, "(STUBBED) unk_param1=0x%08X, translation=0x%08X, "
471 "buff_addr=0x%08X, buff_size=0x%08X",
472 unk_param1, translation, buff_addr, buff_size);
473}
474
475void GetTaskServiceStatus(Service::Interface* self) {
476 u32* cmd_buff = Kernel::GetCommandBuffer();
477
478 u32 unk_param1 = cmd_buff[1]; // TODO(JamePeng): Figure out the meaning of these parameters
479 u32 translation = cmd_buff[2];
480 u32 buff_addr = cmd_buff[3];
481 u32 buff_size = (translation >> 4);
482
483 cmd_buff[0] = IPC::MakeHeader(0x1B, 0x2, 0x2);
484 cmd_buff[1] = RESULT_SUCCESS.raw;
485 cmd_buff[2] = 0; // stub 0 ( 8bit value)
486 cmd_buff[3] = (buff_size << 4 | 0xA);
487 cmd_buff[4] = buff_addr;
488
489 LOG_WARNING(Service_BOSS, "(STUBBED) unk_param1=0x%08X, translation=0x%08X, "
490 "buff_addr=0x%08X, buff_size=0x%08X",
491 unk_param1, translation, buff_addr, buff_size);
492}
493
494void StartTask(Service::Interface* self) {
495 u32* cmd_buff = Kernel::GetCommandBuffer();
496
497 u32 unk_param1 = cmd_buff[1]; // TODO(JamePeng): Figure out the meaning of these parameters
498 u32 translation = cmd_buff[2];
499 u32 buff_addr = cmd_buff[3];
500 u32 buff_size = (translation >> 4);
501
502 cmd_buff[0] = IPC::MakeHeader(0x1C, 0x1, 0x2);
503 cmd_buff[1] = RESULT_SUCCESS.raw;
504 cmd_buff[2] = (buff_size << 4 | 0xA);
505 cmd_buff[3] = buff_addr;
506
507 LOG_WARNING(Service_BOSS, "(STUBBED) unk_param1=0x%08X, translation=0x%08X, "
508 "buff_addr=0x%08X, buff_size=0x%08X",
509 unk_param1, translation, buff_addr, buff_size);
510}
511
512void StartTaskImmediate(Service::Interface* self) {
513 u32* cmd_buff = Kernel::GetCommandBuffer();
514
515 u32 unk_param1 = cmd_buff[1]; // TODO(JamePeng): Figure out the meaning of these parameters
516 u32 translation = cmd_buff[2];
517 u32 buff_addr = cmd_buff[3];
518 u32 buff_size = (translation >> 4);
519
520 cmd_buff[0] = IPC::MakeHeader(0x1D, 0x1, 0x2);
521 cmd_buff[1] = RESULT_SUCCESS.raw;
522 cmd_buff[2] = (buff_size << 4 | 0xA);
523 cmd_buff[3] = buff_addr;
524
525 LOG_WARNING(Service_BOSS, "(STUBBED) unk_param1=0x%08X, translation=0x%08X, "
526 "buff_addr=0x%08X, buff_size=0x%08X",
527 unk_param1, translation, buff_addr, buff_size);
528}
529
530void CancelTask(Service::Interface* self) {
531 u32* cmd_buff = Kernel::GetCommandBuffer();
532 // TODO(JamePeng): Figure out the meaning of these parameters
533 u32 unk_param1 = cmd_buff[1];
534 u32 translation = cmd_buff[2];
535 u32 buff_addr = cmd_buff[3];
536 u32 buff_size = (translation >> 4);
537
538 cmd_buff[0] = IPC::MakeHeader(0x1E, 0x1, 0x2);
539 cmd_buff[1] = RESULT_SUCCESS.raw;
540 cmd_buff[2] = (buff_size << 4 | 0xA);
541 cmd_buff[3] = buff_addr;
542
543 LOG_WARNING(Service_BOSS, "(STUBBED) unk_param1=0x%08X, translation=0x%08X, "
544 "buff_addr=0x%08X, buff_size=0x%08X",
545 unk_param1, translation, buff_addr, buff_size);
546}
547
548void GetTaskFinishHandle(Service::Interface* self) {
549 u32* cmd_buff = Kernel::GetCommandBuffer();
550
551 cmd_buff[0] = IPC::MakeHeader(0x1F, 0x1, 0x2);
552 cmd_buff[1] = RESULT_SUCCESS.raw;
553 cmd_buff[2] = 0;
554 cmd_buff[3] = 0; // stub 0(This should be a handle of task_finish ?)
555
556 LOG_WARNING(Service_BOSS, "(STUBBED) called");
557}
558
559void GetTaskState(Service::Interface* self) {
560 u32* cmd_buff = Kernel::GetCommandBuffer();
561 // TODO(JamePeng): Figure out the meaning of these parameters
562 u32 buff_size = cmd_buff[1];
563 u32 unk_param2 = cmd_buff[2] & 0xFF;
564 u32 translation = cmd_buff[3];
565 u32 buff_addr = cmd_buff[4];
566
567 cmd_buff[0] = IPC::MakeHeader(0x20, 0x4, 0x2);
568 cmd_buff[1] = RESULT_SUCCESS.raw;
569 cmd_buff[2] = 0; // stub 0 (8 bit value)
570 cmd_buff[3] = 0; // stub 0 (32 bit value)
571 cmd_buff[4] = 0; // stub 0 (8 bit value)
572 cmd_buff[5] = (buff_size << 4 | 0xA);
573 cmd_buff[6] = buff_addr;
574
575 LOG_WARNING(Service_BOSS, "(STUBBED) buff_size=0x%08X, unk_param2=0x%08X, "
576 "translation=0x%08X, buff_addr=0x%08X",
577 buff_size, unk_param2, translation, buff_addr);
578}
579
580void GetTaskResult(Service::Interface* self) {
581 u32* cmd_buff = Kernel::GetCommandBuffer();
582 // TODO(JamePeng): Figure out the meaning of these parameters
583 u32 unk_param1 = cmd_buff[1];
584 u32 translation = cmd_buff[2];
585 u32 buff_addr = cmd_buff[3];
586 u32 buff_size = (translation >> 4);
587
588 cmd_buff[0] = IPC::MakeHeader(0x21, 0x4, 0x2);
589 cmd_buff[1] = RESULT_SUCCESS.raw;
590 cmd_buff[2] = 0; // stub 0 (8 bit value)
591 cmd_buff[3] = 0; // stub 0 (32 bit value)
592 cmd_buff[4] = 0; // stub 0 (8 bit value)
593 cmd_buff[5] = (buff_size << 4 | 0xA);
594 cmd_buff[6] = buff_addr;
595
596 LOG_WARNING(Service_BOSS, "(STUBBED) unk_param1=0x%08X, translation=0x%08X, "
597 "buff_addr=0x%08X, buff_size=0x%08X",
598 unk_param1, translation, buff_addr, buff_size);
599}
600
601void GetTaskCommErrorCode(Service::Interface* self) {
602 u32* cmd_buff = Kernel::GetCommandBuffer();
603 // TODO(JamePeng): Figure out the meaning of these parameters
604 u32 unk_param1 = cmd_buff[1];
605 u32 translation = cmd_buff[2];
606 u32 buff_addr = cmd_buff[3];
607 u32 buff_size = (translation >> 4);
608
609 cmd_buff[0] = IPC::MakeHeader(0x22, 0x4, 0x2);
610 cmd_buff[1] = RESULT_SUCCESS.raw;
611 cmd_buff[2] = 0; // stub 0 (32 bit value)
612 cmd_buff[3] = 0; // stub 0 (32 bit value)
613 cmd_buff[4] = 0; // stub 0 (8 bit value)
614 cmd_buff[5] = (buff_size << 4 | 0xA);
615 cmd_buff[6] = buff_addr;
616
617 LOG_WARNING(Service_BOSS, "(STUBBED) unk_param1=0x%08X, translation=0x%08X, "
618 "buff_addr=0x%08X, buff_size=0x%08X",
619 unk_param1, translation, buff_addr, buff_size);
620}
621
622void GetTaskStatus(Service::Interface* self) {
623 u32* cmd_buff = Kernel::GetCommandBuffer();
624 // TODO(JamePeng): Figure out the meaning of these parameters
625 u32 unk_param1 = cmd_buff[1];
626 u32 unk_param2 = cmd_buff[2] & 0xFF;
627 u32 unk_param3 = cmd_buff[3] & 0xFF;
628 u32 translation = cmd_buff[4];
629 u32 buff_addr = cmd_buff[5];
630 u32 buff_size = (translation >> 4);
631
632 cmd_buff[0] = IPC::MakeHeader(0x23, 0x2, 0x2);
633 cmd_buff[1] = RESULT_SUCCESS.raw;
634 cmd_buff[2] = 0; // stub 0 (8 bit value)
635 cmd_buff[3] = (buff_size << 4 | 0xA);
636 cmd_buff[4] = buff_addr;
637
638 LOG_WARNING(Service_BOSS, "(STUBBED) unk_param1=0x%08X, unk_param2=0x%08X, unk_param3=0x%08X, "
639 "translation=0x%08X, buff_addr=0x%08X, buff_size=0x%08X",
640 unk_param1, unk_param2, unk_param3, translation, buff_addr, buff_size);
641}
642
643void GetTaskError(Service::Interface* self) {
644 u32* cmd_buff = Kernel::GetCommandBuffer();
645 // TODO(JamePeng): Figure out the meaning of these parameters
646 u32 unk_param1 = cmd_buff[1];
647 u32 unk_param2 = cmd_buff[2] & 0xFF;
648 u32 translation = cmd_buff[4];
649 u32 buff_addr = cmd_buff[5];
650 u32 buff_size = (translation >> 4);
651
652 cmd_buff[0] = IPC::MakeHeader(0x24, 0x2, 0x2);
653 cmd_buff[1] = RESULT_SUCCESS.raw;
654 cmd_buff[2] = 0; // stub 0 (8 bit value)
655 cmd_buff[3] = (buff_size << 4 | 0xA);
656 cmd_buff[4] = buff_addr;
657
658 LOG_WARNING(Service_BOSS, "(STUBBED) unk_param1=0x%08X, unk_param2=0x%08X, translation=0x%08X, "
659 "buff_addr=0x%08X, buff_size=0x%08X",
660 unk_param1, unk_param2, translation, buff_addr, buff_size);
661}
662
663void GetTaskInfo(Service::Interface* self) {
664 u32* cmd_buff = Kernel::GetCommandBuffer();
665 // TODO(JamePeng): Figure out the meaning of these parameters
666 u32 unk_param1 = cmd_buff[1];
667 u32 unk_param2 = cmd_buff[2] & 0xFF;
668 u32 translation = cmd_buff[4];
669 u32 buff_addr = cmd_buff[5];
670 u32 buff_size = (translation >> 4);
671
672 cmd_buff[0] = IPC::MakeHeader(0x25, 0x1, 0x2);
673 cmd_buff[1] = RESULT_SUCCESS.raw;
674 cmd_buff[2] = (buff_size << 4 | 0xA);
675 cmd_buff[3] = buff_addr;
676
677 LOG_WARNING(Service_BOSS, "(STUBBED) unk_param1=0x%08X, unk_param2=0x%08X, translation=0x%08X, "
678 "buff_addr=0x%08X, buff_size=0x%08X",
679 unk_param1, unk_param2, translation, buff_addr, buff_size);
680}
681
682void DeleteNsData(Service::Interface* self) {
683 u32* cmd_buff = Kernel::GetCommandBuffer();
684 // TODO(JamePeng): Figure out the meaning of these parameters
685 u32 unk_param1 = cmd_buff[1];
686
687 cmd_buff[0] = IPC::MakeHeader(0x26, 0x1, 0);
688 cmd_buff[1] = RESULT_SUCCESS.raw;
689
690 LOG_WARNING(Service_BOSS, "(STUBBED) unk_param1=0x%08X", unk_param1);
691}
692
693void GetNsDataHeaderInfo(Service::Interface* self) {
694 u32* cmd_buff = Kernel::GetCommandBuffer();
695 // TODO(JamePeng): Figure out the meaning of these parameters
696 u32 unk_param1 = cmd_buff[1];
697 u32 unk_param2 = cmd_buff[2] & 0xFF;
698 u32 unk_param3 = cmd_buff[3];
699 u32 translation = cmd_buff[4];
700 u32 buff_addr = cmd_buff[5];
701 u32 buff_size = (translation >> 4);
702
703 cmd_buff[0] = IPC::MakeHeader(0x27, 0x1, 0x2);
704 cmd_buff[1] = RESULT_SUCCESS.raw;
705 cmd_buff[2] = (buff_size << 4 | 0xC);
706 cmd_buff[3] = buff_addr;
707
708 LOG_WARNING(Service_BOSS, "(STUBBED) unk_param1=0x%08X, unk_param2=0x%08X, unk_param3=0x%08X, "
709 "translation=0x%08X, buff_addr=0x%08X, buff_size=0x%08X",
710 unk_param1, unk_param2, unk_param3, translation, buff_addr, buff_size);
711}
712
713void ReadNsData(Service::Interface* self) {
714 u32* cmd_buff = Kernel::GetCommandBuffer();
715 // TODO(JamePeng): Figure out the meaning of these parameters
716 u32 unk_param1 = cmd_buff[1];
717 u32 unk_param2 = cmd_buff[2];
718 u32 unk_param3 = cmd_buff[3];
719 u32 unk_param4 = cmd_buff[4];
720 u32 translation = cmd_buff[5];
721 u32 buff_addr = cmd_buff[6];
722 u32 buff_size = (translation >> 4);
723
724 cmd_buff[0] = IPC::MakeHeader(0x28, 0x3, 0x2);
725 cmd_buff[1] = RESULT_SUCCESS.raw;
726 cmd_buff[2] = 0; // stub 0 (32bit value)
727 cmd_buff[3] = 0; // stub 0 (32bit value)
728 cmd_buff[4] = (buff_size << 4 | 0xC);
729 cmd_buff[5] = buff_addr;
730
731 LOG_WARNING(Service_BOSS, "(STUBBED) unk_param1=0x%08X, unk_param2=0x%08X, unk_param3=0x%08X, "
732 "unk_param4=0x%08X, translation=0x%08X, "
733 "buff_addr=0x%08X, buff_size=0x%08X",
734 unk_param1, unk_param2, unk_param3, unk_param4, translation, buff_addr, buff_size);
735}
736
737void SetNsDataAdditionalInfo(Service::Interface* self) {
738 u32* cmd_buff = Kernel::GetCommandBuffer();
739 // TODO(JamePeng): Figure out the meaning of these parameters
740 u32 unk_param1 = cmd_buff[1];
741 u32 unk_param2 = cmd_buff[2];
742
743 cmd_buff[0] = IPC::MakeHeader(0x29, 0x1, 0);
744 cmd_buff[1] = RESULT_SUCCESS.raw;
745
746 LOG_WARNING(Service_BOSS, "(STUBBED) unk_param1=0x%08X, unk_param2=0x%08X", unk_param1,
747 unk_param2);
748}
749
750void GetNsDataAdditionalInfo(Service::Interface* self) {
751 u32* cmd_buff = Kernel::GetCommandBuffer();
752 // TODO(JamePeng): Figure out the meaning of these parameters
753 u32 unk_param1 = cmd_buff[1];
754
755 cmd_buff[0] = IPC::MakeHeader(0x2A, 0x2, 0);
756 cmd_buff[1] = RESULT_SUCCESS.raw;
757 cmd_buff[2] = 0; // stub 0 (32bit value)
758
759 LOG_WARNING(Service_BOSS, "(STUBBED) unk_param1=0x%08X", unk_param1);
760}
761
762void SetNsDataNewFlag(Service::Interface* self) {
763 u32* cmd_buff = Kernel::GetCommandBuffer();
764 // TODO(JamePeng): Figure out the meaning of these parameters
765 u32 unk_param1 = cmd_buff[1];
766 ns_data_new_flag = cmd_buff[2] & 0xFF;
767
768 cmd_buff[0] = IPC::MakeHeader(0x2B, 0x1, 0);
769 cmd_buff[1] = RESULT_SUCCESS.raw;
770
771 LOG_WARNING(Service_BOSS, "(STUBBED) unk_param1=0x%08X, ns_data_new_flag=0x%08X", unk_param1,
772 ns_data_new_flag);
773}
774
775void GetNsDataNewFlag(Service::Interface* self) {
776 u32* cmd_buff = Kernel::GetCommandBuffer();
777 // TODO(JamePeng): Figure out the meaning of these parameters
778 u32 unk_param1 = cmd_buff[1];
779
780 cmd_buff[0] = IPC::MakeHeader(0x2C, 0x2, 0);
781 cmd_buff[1] = RESULT_SUCCESS.raw;
782 cmd_buff[2] = ns_data_new_flag;
783
784 LOG_WARNING(Service_BOSS, "(STUBBED) unk_param1=0x%08X, ns_data_new_flag=0x%08X", unk_param1,
785 ns_data_new_flag);
786}
787
788void GetNsDataLastUpdate(Service::Interface* self) {
789 u32* cmd_buff = Kernel::GetCommandBuffer();
790 // TODO(JamePeng): Figure out the meaning of these parameters
791 u32 unk_param1 = cmd_buff[1];
792
793 cmd_buff[0] = IPC::MakeHeader(0x2D, 0x3, 0);
794 cmd_buff[1] = RESULT_SUCCESS.raw;
795 cmd_buff[2] = 0; // stub 0 (32bit value)
796 cmd_buff[3] = 0; // stub 0 (32bit value)
797
798 LOG_WARNING(Service_BOSS, "(STUBBED) unk_param1=0x%08X", unk_param1);
799}
800
801void GetErrorCode(Service::Interface* self) {
802 u32* cmd_buff = Kernel::GetCommandBuffer();
803 // TODO(JamePeng): Figure out the meaning of these parameters
804 u32 unk_param1 = cmd_buff[1];
805
806 cmd_buff[0] = IPC::MakeHeader(0x2E, 0x2, 0);
807 cmd_buff[1] = RESULT_SUCCESS.raw;
808 cmd_buff[2] = 0; // stub 0 (32bit value)
809
810 LOG_WARNING(Service_BOSS, "(STUBBED) unk_param1=0x%08X", unk_param1);
811}
812
813void RegisterStorageEntry(Service::Interface* self) {
814 u32* cmd_buff = Kernel::GetCommandBuffer();
815 // TODO(JamePeng): Figure out the meaning of these parameters
816 u32 unk_param1 = cmd_buff[1];
817 u32 unk_param2 = cmd_buff[2];
818 u32 unk_param3 = cmd_buff[3];
819 u32 unk_param4 = cmd_buff[4];
820 u32 unk_param5 = cmd_buff[5] & 0xFF;
821
822 cmd_buff[0] = IPC::MakeHeader(0x2F, 0x1, 0);
823 cmd_buff[1] = RESULT_SUCCESS.raw;
824
825 LOG_WARNING(Service_BOSS, "(STUBBED) unk_param1=0x%08X, unk_param2=0x%08X, unk_param3=0x%08X, "
826 "unk_param4=0x%08X, unk_param5=0x%08X",
827 unk_param1, unk_param2, unk_param3, unk_param4, unk_param5);
828}
829
830void GetStorageEntryInfo(Service::Interface* self) {
831 u32* cmd_buff = Kernel::GetCommandBuffer();
832
833 cmd_buff[0] = IPC::MakeHeader(0x30, 0x3, 0);
834 cmd_buff[1] = RESULT_SUCCESS.raw;
835 cmd_buff[2] = 0; // stub 0 (32bit value)
836 cmd_buff[3] = 0; // stub 0 (16bit value)
837
838 LOG_WARNING(Service_BOSS, "(STUBBED) called");
839}
840
841void SetStorageOption(Service::Interface* self) {
842 u32* cmd_buff = Kernel::GetCommandBuffer();
843 // TODO(JamePeng): Figure out the meaning of these parameters
844 u32 unk_param1 = cmd_buff[1] & 0xFF;
845 u32 unk_param2 = cmd_buff[2];
846 u32 unk_param3 = cmd_buff[3];
847 u32 unk_param4 = cmd_buff[4];
848
849 cmd_buff[0] = IPC::MakeHeader(0x31, 0x1, 0);
850 cmd_buff[1] = RESULT_SUCCESS.raw;
851
852 LOG_WARNING(Service_BOSS, "(STUBBED) unk_param1=0x%08X, unk_param2=0x%08X, "
853 "unk_param3=0x%08X, unk_param4=0x%08X",
854 unk_param1, unk_param2, unk_param3, unk_param4);
855}
856
857void GetStorageOption(Service::Interface* self) {
858 u32* cmd_buff = Kernel::GetCommandBuffer();
859
860 cmd_buff[0] = IPC::MakeHeader(0x32, 0x5, 0);
861 cmd_buff[1] = RESULT_SUCCESS.raw;
862 cmd_buff[2] = 0; // stub 0 (32bit value)
863 cmd_buff[3] = 0; // stub 0 (8bit value)
864 cmd_buff[4] = 0; // stub 0 (16bit value)
865 cmd_buff[5] = 0; // stub 0 (16bit value)
866
867 LOG_WARNING(Service_BOSS, "(STUBBED) called");
868}
869
870void StartBgImmediate(Service::Interface* self) {
871 u32* cmd_buff = Kernel::GetCommandBuffer();
872
873 u32 unk_param1 = cmd_buff[1]; // TODO(JamePeng): Figure out the meaning of these parameters
874 u32 translation = cmd_buff[2];
875 u32 buff_addr = cmd_buff[3];
876 u32 buff_size = (translation >> 4);
877
878 cmd_buff[0] = IPC::MakeHeader(0x33, 0x1, 0x2);
879 cmd_buff[1] = RESULT_SUCCESS.raw;
880 cmd_buff[2] = (buff_size << 4 | 0xA);
881 cmd_buff[3] = buff_addr;
882
883 LOG_WARNING(Service_BOSS, "(STUBBED) buff_size=0x%08X, unk_param2=0x%08X, "
884 "translation=0x%08X, buff_addr=0x%08X",
885 unk_param1, translation, buff_addr, buff_size);
886}
887
888void GetTaskActivePriority(Service::Interface* self) {
889 u32* cmd_buff = Kernel::GetCommandBuffer();
890
891 u32 unk_param1 = cmd_buff[1]; // TODO(JamePeng): Figure out the meaning of these parameters
892 u32 translation = cmd_buff[2];
893 u32 buff_addr = cmd_buff[3];
894 u32 buff_size = (translation >> 4);
895
896 cmd_buff[0] = IPC::MakeHeader(0x34, 0x2, 0x2);
897 cmd_buff[1] = RESULT_SUCCESS.raw;
898 cmd_buff[2] = 0; // stub 0 (8bit value)
899 cmd_buff[3] = (buff_size << 4 | 0xA);
900 cmd_buff[4] = buff_addr;
901
902 LOG_WARNING(Service_BOSS, "(STUBBED) buff_size=0x%08X, unk_param2=0x%08X, "
903 "translation=0x%08X, buff_addr=0x%08X",
904 unk_param1, translation, buff_addr, buff_size);
905}
906
907void RegisterImmediateTask(Service::Interface* self) {
908 u32* cmd_buff = Kernel::GetCommandBuffer();
909 // TODO(JamePeng): Figure out the meaning of these parameters
910 u32 unk_param1 = cmd_buff[1];
911 u32 unk_param2 = cmd_buff[2] & 0xFF;
912 u32 unk_param3 = cmd_buff[3] & 0xFF;
913 u32 translation = cmd_buff[4];
914 u32 buff_addr = cmd_buff[5];
915 u32 buff_size = (translation >> 4);
916
917 cmd_buff[0] = IPC::MakeHeader(0x35, 0x1, 0x2);
918 cmd_buff[1] = RESULT_SUCCESS.raw;
919 cmd_buff[3] = (buff_size << 4 | 0xA);
920 cmd_buff[4] = buff_addr;
921
922 LOG_WARNING(Service_BOSS, "(STUBBED) unk_param1=0x%08X, unk_param2=0x%08X, unk_param3=0x%08X, "
923 "translation=0x%08X, buff_addr=0x%08X, buff_size=0x%08X",
924 unk_param1, unk_param2, unk_param3, translation, buff_addr, buff_size);
925}
926
927void SetTaskQuery(Service::Interface* self) {
928 u32* cmd_buff = Kernel::GetCommandBuffer();
929 // TODO(JamePeng): Figure out the meaning of these parameters
930 u32 unk_param1 = cmd_buff[1];
931 u32 unk_param2 = cmd_buff[2];
932 u32 translation1 = cmd_buff[3];
933 u32 buff1_addr = cmd_buff[4];
934 u32 buff1_size = (translation1 >> 4);
935 u32 translation2 = cmd_buff[5];
936 u32 buff2_addr = cmd_buff[6];
937 u32 buff2_size = (translation2 >> 4);
938
939 cmd_buff[0] = IPC::MakeHeader(0x36, 0x1, 0x4);
940 cmd_buff[1] = RESULT_SUCCESS.raw;
941 cmd_buff[2] = (buff1_size << 4 | 0xA);
942 cmd_buff[3] = buff1_addr;
943 cmd_buff[2] = (buff2_size << 4 | 0xA);
944 cmd_buff[3] = buff2_addr;
945
946 LOG_WARNING(Service_BOSS, "(STUBBED) unk_param1=0x%08X, unk_param2=0x%08X, "
947 "translation1=0x%08X, buff1_addr=0x%08X, buff1_size=0x%08X, "
948 "translation2=0x%08X, buff2_addr=0x%08X, buff2_size=0x%08X",
949 unk_param1, unk_param2, translation1, buff1_addr, buff1_size, translation2,
950 buff2_addr, buff2_size);
951}
952
953void GetTaskQuery(Service::Interface* self) {
954 u32* cmd_buff = Kernel::GetCommandBuffer();
955 // TODO(JamePeng): Figure out the meaning of these parameters
956 u32 unk_param1 = cmd_buff[1];
957 u32 unk_param2 = cmd_buff[2];
958 u32 translation1 = cmd_buff[3];
959 u32 buff1_addr = cmd_buff[4];
960 u32 buff1_size = (translation1 >> 4);
961 u32 translation2 = cmd_buff[5];
962 u32 buff2_addr = cmd_buff[6];
963 u32 buff2_size = (translation2 >> 4);
964
965 cmd_buff[0] = IPC::MakeHeader(0x37, 0x1, 0x4);
966 cmd_buff[1] = RESULT_SUCCESS.raw;
967 cmd_buff[2] = (buff1_size << 4 | 0xA);
968 cmd_buff[3] = buff1_addr;
969 cmd_buff[2] = (buff2_size << 4 | 0xC);
970 cmd_buff[3] = buff2_addr;
971
972 LOG_WARNING(Service_BOSS, "(STUBBED) unk_param1=0x%08X, unk_param2=0x%08X, "
973 "translation1=0x%08X, buff1_addr=0x%08X, buff1_size=0x%08X, "
974 "translation2=0x%08X, buff2_addr=0x%08X, buff2_size=0x%08X",
975 unk_param1, unk_param2, translation1, buff1_addr, buff1_size, translation2,
976 buff2_addr, buff2_size);
977}
978
979void Init() {
980 using namespace Kernel;
981
982 AddService(new BOSS_P_Interface);
983 AddService(new BOSS_U_Interface);
984
985 new_arrival_flag = 0;
986 ns_data_new_flag = 0;
987 output_flag = 0;
988}
989
990void Shutdown() {}
991
992} // namespace BOSS
993
994} // namespace Service
diff --git a/src/core/hle/service/boss/boss.h b/src/core/hle/service/boss/boss.h
deleted file mode 100644
index 8cdc663c8..000000000
--- a/src/core/hle/service/boss/boss.h
+++ /dev/null
@@ -1,802 +0,0 @@
1// Copyright 2015 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 "core/hle/service/service.h"
8
9namespace Service {
10namespace BOSS {
11
12/**
13 * BOSS::InitializeSession service function
14 * Inputs:
15 * 0 : Header Code[0x00010082]
16 * 1 : u32 lower 64bit value
17 * 2 : u32 higher 64bit value
18 * 3 : 0x20
19 * 4 : u32 unknown value
20 * Outputs:
21 * 1 : Result of function, 0 on success, otherwise error code
22 */
23void InitializeSession(Service::Interface* self);
24
25/**
26 * BOSS::RegisterStorage service function
27 * Inputs:
28 * 0 : Header Code[0x00020010]
29 * 1 : u32 unknown1
30 * 2 : u32 unknown2
31 * 3 : u32 unknown3
32 * 4 : u8 unknown_flag
33 * Outputs:
34 * 1 : Result of function, 0 on success, otherwise error code
35 */
36void RegisterStorage(Service::Interface* self);
37
38/**
39 * BOSS::UnregisterStorage service function
40 * Inputs:
41 * 0 : Header Code[0x00030000]
42 * Outputs:
43 * 1 : Result of function, 0 on success, otherwise error code
44 */
45void UnregisterStorage(Service::Interface* self);
46
47/**
48 * BOSS::GetStorageInfo service function
49 * Inputs:
50 * 0 : Header Code[0x00040000]
51 * Outputs:
52 * 1 : Result of function, 0 on success, otherwise error code
53 * 2 : u32 unknown value
54 */
55void GetStorageInfo(Service::Interface* self);
56
57/**
58 * BOSS::RegisterPrivateRootCa service function
59 * Inputs:
60 * 0 : Header Code[0x00050042]
61 * 1 : u32 unknown value
62 * 2 : MappedBufferDesc(permission = R)
63 * 3 : u32 buff_addr
64 * Outputs:
65 * 1 : Result of function, 0 on success, otherwise error code
66 * 2 : buff_size << 4 | 0xA
67 * 3 : u32 buff_addr
68 */
69void RegisterPrivateRootCa(Service::Interface* self);
70
71/**
72 * BOSS::RegisterPrivateClientCert service function
73 * Inputs:
74 * 0 : Header Code[0x00060084]
75 * 1 : u32 unknown value
76 * 2 : u32 unknown value
77 * 3 : MappedBufferDesc1(permission = R)
78 * 4 : u32 buff_addr1
79 * 5 : MappedBufferDesc2(permission = R)
80 * 6 : u32 buff_addr2
81 * Outputs:
82 * 1 : Result of function, 0 on success, otherwise error code
83 * 2 : buff1_size << 4 | 0xA
84 * 3 : u32 buff_addr1
85 * 4 : buff2_size << 4 | 0xA
86 * 5 : u32 buff_addr2
87 */
88void RegisterPrivateClientCert(Service::Interface* self);
89
90/**
91 * BOSS::GetNewArrivalFlag service function
92 * Inputs:
93 * 0 : Header Code[0x00070000]
94 * Outputs:
95 * 1 : Result of function, 0 on success, otherwise error code
96 * 2 : u8 flag
97 */
98void GetNewArrivalFlag(Service::Interface* self);
99
100/**
101 * BOSS::RegisterNewArrivalEvent service function
102 * Inputs:
103 * 0 : Header Code[0x00080002]
104 * 1 : u32 unknown1
105 * 2 : u32 unknown2
106 * Outputs:
107 * 1 : Result of function, 0 on success, otherwise error code
108 */
109void RegisterNewArrivalEvent(Service::Interface* self);
110
111/**
112 * BOSS::SetOptoutFlag service function
113 * Inputs:
114 * 0 : Header Code[0x00090040]
115 * 1 : u8 output_flag
116 * Outputs:
117 * 1 : Result of function, 0 on success, otherwise error code
118 */
119void SetOptoutFlag(Service::Interface* self);
120
121/**
122 * BOSS::GetOptoutFlag service function
123 * Inputs:
124 * 0 : Header Code[0x000A0000]
125 * Outputs:
126 * 1 : Result of function, 0 on success, otherwise error code
127 * 2 : u8 output_flag
128 */
129void GetOptoutFlag(Service::Interface* self);
130
131/**
132 * BOSS::RegisterTask service function
133 * Inputs:
134 * 0 : Header Code[0x000B00C2]
135 * 1 : u32 unknown value
136 * 2 : u8 unknown value
137 * 3 : u8 unknown value
138 * 4 : MappedBufferDesc1(permission = R)
139 * 5 : buff_addr
140 * Outputs:
141 * 1 : Result of function, 0 on success, otherwise error code
142 * 2 : buff_size << 4 | 0xA
143 * 3 : u32 buff_addr
144 */
145void RegisterTask(Service::Interface* self);
146
147/**
148 * BOSS::UnregisterTask service function
149 * Inputs:
150 * 0 : Header Code[0x000C0082]
151 * 1 : u32 unknown value
152 * 2 : u8 unknown value
153 * 3 : MappedBufferDesc1(permission = R)
154 * 4 : buff_addr
155 * Outputs:
156 * 1 : Result of function, 0 on success, otherwise error code
157 * 2 : buff_size << 4 | 0xA
158 * 3 : u32 buff_addr
159 */
160void UnregisterTask(Service::Interface* self);
161
162/**
163 * BOSS::ReconfigureTask service function
164 * Inputs:
165 * 0 : Header Code[0x000D0082]
166 * 1 : u32 unknown value
167 * 2 : u8 unknown value
168 * 3 : MappedBufferDesc1(permission = R)
169 * 4 : buff_addr
170 * Outputs:
171 * 1 : Result of function, 0 on success, otherwise error code
172 * 2 : buff_size << 4 | 0xA
173 * 3 : u32 buff_addr
174 */
175void ReconfigureTask(Service::Interface* self);
176
177/**
178 * BOSS::GetTaskIdList service function
179 * Inputs:
180 * 0 : Header Code[0x000E0000]
181 * Outputs:
182 * 1 : Result of function, 0 on success, otherwise error code
183 */
184void GetTaskIdList(Service::Interface* self);
185
186/**
187 * BOSS::GetStepIdList service function
188 * Inputs:
189 * 0 : Header Code[0x000F0042]
190 * 2 : MappedBufferDesc(permission = R)
191 * 3 : u32 buff_addr
192 *
193 * Outputs:
194 * 1 : Result of function, 0 on success, otherwise error code
195 * 2 : buff_size << 4 | 0xA
196 * 3 : u32 buff_addr
197 */
198void GetStepIdList(Service::Interface* self);
199
200/**
201 * BOSS::GetNsDataIdList service function
202 * Inputs:
203 * 0 : Header Code[0x00100102]
204 * 1 : u32 unknown1
205 * 2 : u32 unknown2
206 * 3 : u32 unknown3
207 * 4 : u32 unknown4
208 * 5 : MappedBufferDesc(permission = W)
209 * 6 : u32 buff_addr
210 * Outputs:
211 * 1 : Result of function, 0 on success, otherwise error code
212 * 2 : u16 unknown value
213 * 3 : u16 unknown value
214 * 4 : buff_size << 4 | 0xC
215 * 5 : u32 buff_addr
216 */
217void GetNsDataIdList(Service::Interface* self);
218
219/**
220 * BOSS::GetOwnNsDataIdList service function
221 * Inputs:
222 * 0 : Header Code[0x00110102]
223 * 1 : u32 unknown1
224 * 2 : u32 unknown2
225 * 3 : u32 unknown3
226 * 4 : u32 unknown4
227 * 5 : MappedBufferDesc(permission = W)
228 * 6 : u32 buff_addr
229 * Outputs:
230 * 1 : Result of function, 0 on success, otherwise error code
231 * 2 : u16 unknown value
232 * 3 : u16 unknown value
233 * 4 : buff_size << 4 | 0xC
234 * 5 : u32 buff_addr
235 */
236void GetOwnNsDataIdList(Service::Interface* self);
237
238/**
239 * BOSS::GetNewDataNsDataIdList service function
240 * Inputs:
241 * 0 : Header Code[0x00120102]
242 * 1 : u32 unknown1
243 * 2 : u32 unknown2
244 * 3 : u32 unknown3
245 * 4 : u32 unknown4
246 * 5 : MappedBufferDesc(permission = W)
247 * 6 : u32 buff_addr
248 * Outputs:
249 * 1 : Result of function, 0 on success, otherwise error code
250 * 2 : u16 unknown value
251 * 3 : u16 unknown value
252 * 4 : buff_size << 4 | 0xC
253 * 5 : u32 buff_addr
254 */
255void GetNewDataNsDataIdList(Service::Interface* self);
256
257/**
258 * BOSS::GetOwnNewDataNsDataIdList service function
259 * Inputs:
260 * 0 : Header Code[0x00130102]
261 * 1 : u32 unknown1
262 * 2 : u32 unknown2
263 * 3 : u32 unknown3
264 * 4 : u32 unknown4
265 * 5 : MappedBufferDesc(permission = W)
266 * 6 : u32 buff_addr
267 * Outputs:
268 * 1 : Result of function, 0 on success, otherwise error code
269 * 2 : u16 unknown value
270 * 3 : u16 unknown value
271
272 */
273void GetOwnNewDataNsDataIdList(Service::Interface* self);
274
275/**
276 * BOSS::SendProperty service function
277 * Inputs:
278 * 0 : Header Code[0x00140082]
279 * 1 : u16 unknown value
280 * 2 : u32 unknown value
281 * 3 : MappedBufferDesc(permission = R)
282 * 4 : u32 buff_addr
283 * Outputs:
284 * 1 : Result of function, 0 on success, otherwise error code
285 * 2 : buff_size << 4 | 0xA
286 * 3 : u32 buff_addr
287 */
288void SendProperty(Service::Interface* self);
289
290/**
291 * BOSS::SendPropertyHandle service function
292 * Inputs:
293 * 0 : Header Code[0x00150042]
294 * 2 : u8 unknown value
295 * 3 : MappedBufferDesc(permission = R)
296 * 4 : u32 buff_addr
297 * Outputs:
298 * 1 : Result of function, 0 on success, otherwise error code
299 * 2 : buff_size << 4 | 0xA
300 * 3 : u32 buff_addr
301 */
302void SendPropertyHandle(Service::Interface* self);
303
304/**
305 * BOSS::ReceiveProperty service function
306 * Inputs:
307 * 0 : Header Code[0x00160082]
308 * 1 : u16 unknown1
309 * 2 : u32 buff_size
310 * 3 : MappedBufferDesc(permission = W)
311 * 4 : u32 buff addr
312 * Outputs:
313 * 1 : Result of function, 0 on success, otherwise error code
314 * 2 : u32 unknown value
315 * 3 : u16 unknown value
316 * 4 : buff_size << 4 | 0xC
317 * 5 : u32 buff_addr
318 */
319void ReceiveProperty(Service::Interface* self);
320
321/**
322 * BOSS::UpdateTaskInterval service function
323 * Inputs:
324 * 0 : Header Code[0x00170082]
325 * 1 : u32 unknown value
326 * 2 : u8 unknown value
327 * 3 : MappedBufferDesc1(permission = R)
328 * 4 : buff_addr
329 * Outputs:
330 * 1 : Result of function, 0 on success, otherwise error code
331 * 2 : buff_size << 4 | 0xA
332 * 3 : u32 buff_addr
333 */
334void UpdateTaskInterval(Service::Interface* self);
335
336/**
337 * BOSS::UpdateTaskCount service function
338 * Inputs:
339 * 0 : Header Code[0x00180082]
340 * 1 : u32 buff_size
341 * 2 : u32 unknown2
342 * 3 : MappedBufferDesc(permission = R)
343 * 4 : u32 buff_addr
344 * Outputs:
345 * 1 : Result of function, 0 on success, otherwise error code
346 * 2 : buff_size << 4 | 0xA
347 * 3 : u32 buff_addr
348 */
349void UpdateTaskCount(Service::Interface* self);
350
351/**
352 * BOSS::GetTaskInterval service function
353 * Inputs:
354 * 0 : Header Code[0x00190042]
355 * 1 : u32 unknown value
356 * 2 : MappedBufferDesc(permission = R)
357 * 3 : u32 buff_addr
358 * Outputs:
359 * 1 : Result of function, 0 on success, otherwise error code
360 * 2 : u32 unknown value
361 * 3 : buff_size << 4 | 0xA
362 * 4 : u32 buff_addr
363 */
364void GetTaskInterval(Service::Interface* self);
365
366/**
367 * BOSS::GetTaskCount service function
368 * Inputs:
369 * 0 : Header Code[0x001A0042]
370 * 1 : u32 unknown value
371 * 2 : MappedBufferDesc(permission = R)
372 * 3 : u32 buff_addr
373 * Outputs:
374 * 1 : Result of function, 0 on success, otherwise error code
375 * 2 : u32 unknown value
376 * 3 : buff_size << 4 | 0xA
377 * 4 : u32 buff_addr
378 */
379void GetTaskCount(Service::Interface* self);
380
381/**
382 * BOSS::GetTaskServiceStatus service function
383 * Inputs:
384 * 0 : Header Code[0x001B0042]
385 * 1 : u32 unknown value
386 * 2 : MappedBufferDesc(permission = R)
387 * 3 : u32 buff_addr
388 * Outputs:
389 * 1 : Result of function, 0 on success, otherwise error code
390 * 2 : u8 unknown value
391 * 3 : buff_size << 4 | 0xA
392 * 4 : u32 buff_addr
393 */
394void GetTaskServiceStatus(Service::Interface* self);
395
396/**
397 * BOSS::StartTask service function
398 * Inputs:
399 * 0 : Header Code[0x001C0042]
400 * 1 : u32 unknown value
401 * 2 : MappedBufferDesc(permission = R)
402 * 3 : u32 buff_addr
403 * Outputs:
404 * 1 : Result of function, 0 on success, otherwise error code
405 * 2 : buff_size << 4 | 0xA
406 * 3 : u32 buff_addr
407 */
408void StartTask(Service::Interface* self);
409
410/**
411 * BOSS::StartTaskImmediate service function
412 * Inputs:
413 * 0 : Header Code[0x001D0042]
414 * 1 : u32 unknown value
415 * 2 : MappedBufferDesc(permission = R)
416 * 3 : u32 buff_addr
417 * Outputs:
418 * 1 : Result of function, 0 on success, otherwise error code
419 * 2 : buff_size << 4 | 0xA
420 * 3 : u32 buff_addr
421 */
422void StartTaskImmediate(Service::Interface* self);
423
424/**
425 * BOSS::CancelTask service function
426 * Inputs:
427 * 0 : Header Code[0x001E0042]
428 * 1 : u32 unknown value
429 * 2 : MappedBufferDesc(permission = R)
430 * 3 : u32 buff_addr
431 * Outputs:
432 * 1 : Result of function, 0 on success, otherwise error code
433 * 2 : buff_size << 4 | 0xA
434 * 3 : u32 buff_addr
435 */
436void CancelTask(Service::Interface* self);
437
438/**
439 * BOSS::GetTaskFinishHandle service function
440 * Inputs:
441 * 0 : Header Code[0x001F0000]
442 * Outputs:
443 * 1 : Result of function, 0 on success, otherwise error code
444 * 2 : 0
445 * 3 : Task Finish Handle
446 */
447void GetTaskFinishHandle(Service::Interface* self);
448
449/**
450 * BOSS::GetTaskState service function
451 * Inputs:
452 * 0 : Header Code[0x00200082]
453 * 1 : u32 buff_size
454 * 2 : u8 unknown value
455 * 3 : MappedBufferDesc(permission = R)
456 * 4 : u32 buff_addr
457 * Outputs:
458 * 1 : Result of function, 0 on success, otherwise error code
459 * 2 : u8 unknown value
460 * 3 : u32 unknown value
461 * 4 : u8 unknown value
462 * 5 : buff_size << 4 | 0xA
463 * 6 : u32 buff_addr
464 */
465void GetTaskState(Service::Interface* self);
466
467/**
468 * BOSS::GetTaskResult service function
469 * Inputs:
470 * 0 : Header Code[0x00210042]
471 * 1 : u32 unknown value
472 * 2 : MappedBufferDesc(permission = R)
473 * 3 : u32 buff_addr
474 * Outputs:
475 * 1 : Result of function, 0 on success, otherwise error code
476 * 2 : u8 unknown value
477 * 3 : u32 unknown value
478 * 4 : u8 unknown value
479 * 5 : buff_size << 4 | 0xA
480 * 6 : u32 buff_addr
481 */
482void GetTaskResult(Service::Interface* self);
483
484/**
485 * BOSS::GetTaskCommErrorCode service function
486 * Inputs:
487 * 0 : Header Code[0x00220042]
488 * 1 : u32 unknown value
489 * 2 : MappedBufferDesc(permission = R)
490 * 3 : u32 buff_addr
491 * Outputs:
492 * 1 : Result of function, 0 on success, otherwise error code
493 * 2 : u32 unknown value
494 * 3 : u32 unknown value
495 * 4 : u8 unknown value
496 * 5 : buff_size << 4 | 0xA
497 * 6 : u32 buff_addr
498 */
499void GetTaskCommErrorCode(Service::Interface* self);
500
501/**
502 * BOSS::GetTaskStatus service function
503 * Inputs:
504 * 0 : Header Code[0x002300C2]
505 * 1 : u32 unknown value
506 * 2 : u8 unknown value
507 * 3 : u8 unknown value
508 * 4 : MappedBufferDesc(permission = R)
509 * 5 : u32 buff_addr
510 * Outputs:
511 * 1 : Result of function, 0 on success, otherwise error code
512 * 2 : u8 unknown value
513 * 3 : buff_size << 4 | 0xA
514 * 4 : u32 buff_addr
515 */
516void GetTaskStatus(Service::Interface* self);
517
518/**
519 * BOSS::GetTaskError service function
520 * Inputs:
521 * 0 : Header Code[0x00240082]
522 * 1 : u32 unknown value
523 * 2 : u8 unknown value
524 * 3 : MappedBufferDesc(permission = R)
525 * 4 : u32 buff_addr
526 * Outputs:
527 * 1 : Result of function, 0 on success, otherwise error code
528 * 2 : u8 unknown value
529 * 3 : buff_size << 4 | 0xA
530 * 4 : u32 buff_addr
531 */
532void GetTaskError(Service::Interface* self);
533
534/**
535 * BOSS::GetTaskInfo service function
536 * Inputs:
537 * 0 : Header Code[0x00250082]
538 * 1 : u32 unknown value
539 * 2 : u8 unknown value
540 * 3 : MappedBufferDesc(permission = R)
541 * 4 : u32 buff_addr
542 * Outputs:
543 * 1 : Result of function, 0 on success, otherwise error code
544 * 2 : buff_size << 4 | 0xA
545 * 3 : u32 buff_addr
546 */
547void GetTaskInfo(Service::Interface* self);
548
549/**
550 * BOSS::DeleteNsData service function
551 * Inputs:
552 * 0 : Header Code[0x00260040]
553 * 1 : u32 unknown value
554 * Outputs:
555 * 1 : Result of function, 0 on success, otherwise error code
556 */
557void DeleteNsData(Service::Interface* self);
558
559/**
560 * BOSS::GetNsDataHeaderInfo service function
561 * Inputs:
562 * 0 : Header Code[0x002700C2]
563 * 1 : u32 unknown value
564 * 2 : u8 unknown value
565 * 3 : u32 unknown value
566 * 4 : MappedBufferDesc(permission = W)
567 * 5 : u32 buff_addr
568 * Outputs:
569 * 1 : Result of function, 0 on success, otherwise error code
570 * 2 : buff_size << 4 | 0xC
571 * 3 : u32 buff_addr
572 */
573void GetNsDataHeaderInfo(Service::Interface* self);
574
575/**
576 * BOSS::ReadNsData service function
577 * Inputs:
578 * 0 : Header Code[0x00280102]
579 * 1 : u32 unknown value
580 * 2 : u32 unknown value
581 * 3 : u32 unknown value
582 * 4 : u32 unknown value
583 * 5 : MappedBufferDesc(permission = W)
584 * 6 : u32 buff_addr
585 * Outputs:
586 * 1 : Result of function, 0 on success, otherwise error code
587 * 2 : u32 unknown value
588 * 3 : u32 unknown value
589 * 4 : buff_size << 4 | 0xC
590 * 5 : u32 buff_addr
591 */
592void ReadNsData(Service::Interface* self);
593
594/**
595 * BOSS::SetNsDataAdditionalInfo service function
596 * Inputs:
597 * 0 : Header Code[0x00290080]
598 * 1 : u32 unknown value
599 * 2 : u32 unknown value
600 * Outputs:
601 * 1 : Result of function, 0 on success, otherwise error code
602 */
603void SetNsDataAdditionalInfo(Service::Interface* self);
604
605/**
606 * BOSS::GetNsDataAdditionalInfo service function
607 * Inputs:
608 * 0 : Header Code[0x002A0040]
609 * 1 : u32 unknown value
610 * Outputs:
611 * 1 : Result of function, 0 on success, otherwise error code
612 * 2 : u32 unknown value
613 */
614void GetNsDataAdditionalInfo(Service::Interface* self);
615
616/**
617 * BOSS::SetNsDataNewFlag service function
618 * Inputs:
619 * 0 : Header Code[0x002B0080]
620 * 1 : u32 unknown value
621 * 2 : u8 flag
622 * Outputs:
623 * 1 : Result of function, 0 on success, otherwise error code
624 */
625void SetNsDataNewFlag(Service::Interface* self);
626
627/**
628 * BOSS::GetNsDataNewFlag service function
629 * Inputs:
630 * 0 : Header Code[0x002C0040]
631 * 1 : u32 unknown value
632 * Outputs:
633 * 1 : Result of function, 0 on success, otherwise error code
634 * 2 : u8 flag
635 */
636void GetNsDataNewFlag(Service::Interface* self);
637
638/**
639 * BOSS::GetNsDataLastUpdate service function
640 * Inputs:
641 * 0 : Header Code[0x002D0040]
642 * 1 : u32 unknown value
643 * Outputs:
644 * 1 : Result of function, 0 on success, otherwise error code
645 * 2 : u32 unknown value
646 * 3 : u32 unknown value
647 */
648void GetNsDataLastUpdate(Service::Interface* self);
649
650/**
651 * BOSS::GetErrorCode service function
652 * Inputs:
653 * 0 : Header Code[0x002E0040]
654 * 1 : u8 unknown value
655 * Outputs:
656 * 1 : Result of function, 0 on success, otherwise error code
657 * 2 : u32 unknown value
658 */
659void GetErrorCode(Service::Interface* self);
660
661/**
662 * BOSS::RegisterStorageEntry service function
663 * Inputs:
664 * 0 : Header Code[0x002F0140]
665 * 1 : u32 unknown value
666 * 2 : u32 unknown value
667 * 3 : u32 unknown value
668 * 4 : u16 unknown value
669 * 5 : u8 unknown value
670 * Outputs:
671 * 1 : Result of function, 0 on success, otherwise error code
672 */
673void RegisterStorageEntry(Service::Interface* self);
674
675/**
676 * BOSS::GetStorageEntryInfo service function
677 * Inputs:
678 * 0 : Header Code[0x00300000]
679 * Outputs:
680 * 1 : Result of function, 0 on success, otherwise error code
681 * 2 : u32 unknown value
682 * 3 : u16 unknown value
683 */
684void GetStorageEntryInfo(Service::Interface* self);
685
686/**
687 * BOSS::SetStorageOption service function
688 * Inputs:
689 * 0 : Header Code[0x00310100]
690 * 1 : u8 unknown value
691 * 2 : u32 unknown value
692 * 3 : u16 unknown value
693 * 4 : u16 unknown value
694 * Outputs:
695 * 1 : Result of function, 0 on success, otherwise error code
696 */
697void SetStorageOption(Service::Interface* self);
698
699/**
700 * BOSS::GetStorageOption service function
701 * Inputs:
702 * 0 : Header Code[0x00320000]
703 * Outputs:
704 * 1 : Result of function, 0 on success, otherwise error code
705 * 2 : u8 unknown value
706 * 3 : u32 unknown value
707 * 4 : u16 unknown value
708 * 5 : u16 unknown value
709 */
710void GetStorageOption(Service::Interface* self);
711
712/**
713 * BOSS::StartBgImmediate service function
714 * Inputs:
715 * 0 : Header Code[0x00330042]
716 * 1 : u32 unknown value
717 * 2 : MappedBufferDesc(permission = R)
718 * 3 : u32 buff_addr
719 * Outputs:
720 * 1 : Result of function, 0 on success, otherwise error code
721 * 2 : buff_size << 4 | 0xA
722 * 3 : u32 buff_addr
723 */
724void StartBgImmediate(Service::Interface* self);
725
726/**
727 * BOSS::GetTaskActivePriority service function
728 * Inputs:
729 * 0 : Header Code[0x00340042]
730 * 1 : u32 unknown value
731 * 2 : MappedBufferDesc(permission = R)
732 * 3 : u32 buff_addr
733 * Outputs:
734 * 1 : Result of function, 0 on success, otherwise error code
735 * 2 : u8 unknown value
736 * 3 : buff_size << 4 | 0xA
737 * 4 : u32 buff_addr
738 */
739void GetTaskActivePriority(Service::Interface* self);
740
741/**
742 * BOSS::RegisterImmediateTask service function
743 * Inputs:
744 * 0 : Header Code[0x003500C2]
745 * 1 : u32 unknown value
746 * 2 : u8 unknown value
747 * 3 : u8 unknown value
748 * 4 : MappedBufferDesc(permission = R)
749 * 5 : u32 buff_addr
750 * Outputs:
751 * 1 : Result of function, 0 on success, otherwise error code
752 * 2 : buff_size << 4 | 0xA
753 * 3 : u32 buff_addr
754 */
755void RegisterImmediateTask(Service::Interface* self);
756
757/**
758 * BOSS::SetTaskQuery service function
759 * Inputs:
760 * 0 : Header Code[0x00360084]
761 * 1 : u32 unknown value
762 * 2 : u32 unknown value
763 * 3 : MappedBufferDesc1(permission = R)
764 * 4 : u32 buff1_addr
765 * 5 : MappedBufferDesc2(permission = R)
766 * 6 : u32 buff2_addr
767 * Outputs:
768 * 1 : Result of function, 0 on success, otherwise error code
769 * 2 : buff1_size << 4 | 0xA
770 * 3 : u32 buff1_addr
771 * 4 : buff2_size << 4 | 0xA
772 * 5 : u32 buff2_addr
773 */
774void SetTaskQuery(Service::Interface* self);
775
776/**
777 * BOSS::GetTaskQuery service function
778 * Inputs:
779 * 0 : Header Code[0x00370084]
780 * 1 : u32 unknown value
781 * 2 : u32 unknown value
782 * 3 : MappedBufferDesc1(permission = R)
783 * 4 : u32 buff1_addr
784 * 5 : MappedBufferDesc2(permission = W)
785 * 6 : u32 buff2_addr
786 * Outputs:
787 * 1 : Result of function, 0 on success, otherwise error code
788 * 2 : buff1_size << 4 | 0xA
789 * 3 : u32 buff1_addr
790 * 4 : buff2_size << 4 | 0xC
791 * 5 : u32 buff2_addr
792 */
793void GetTaskQuery(Service::Interface* self);
794
795/// Initialize BOSS service(s)
796void Init();
797
798/// Shutdown BOSS service(s)
799void Shutdown();
800
801} // namespace BOSS
802} // namespace Service
diff --git a/src/core/hle/service/boss/boss_p.cpp b/src/core/hle/service/boss/boss_p.cpp
deleted file mode 100644
index 3990d0d6e..000000000
--- a/src/core/hle/service/boss/boss_p.cpp
+++ /dev/null
@@ -1,86 +0,0 @@
1// Copyright 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/boss/boss.h"
6#include "core/hle/service/boss/boss_p.h"
7
8namespace Service {
9namespace BOSS {
10
11const Interface::FunctionInfo FunctionTable[] = {
12 // boss:u shared commands
13 {0x00010082, InitializeSession, "InitializeSession"},
14 {0x00020100, RegisterStorage, "RegisterStorage"},
15 {0x00030000, UnregisterStorage, "UnregisterStorage"},
16 {0x00040000, GetStorageInfo, "GetStorageInfo"},
17 {0x00050042, RegisterPrivateRootCa, "RegisterPrivateRootCa"},
18 {0x00060084, RegisterPrivateClientCert, "RegisterPrivateClientCert"},
19 {0x00070000, GetNewArrivalFlag, "GetNewArrivalFlag"},
20 {0x00080002, RegisterNewArrivalEvent, "RegisterNewArrivalEvent"},
21 {0x00090040, SetOptoutFlag, "SetOptoutFlag"},
22 {0x000A0000, GetOptoutFlag, "GetOptoutFlag"},
23 {0x000B00C2, RegisterTask, "RegisterTask"},
24 {0x000C0082, UnregisterTask, "UnregisterTask"},
25 {0x000D0082, ReconfigureTask, "ReconfigureTask"},
26 {0x000E0000, GetTaskIdList, "GetTaskIdList"},
27 {0x000F0042, GetStepIdList, "GetStepIdList"},
28 {0x00100102, GetNsDataIdList, "GetNsDataIdList"},
29 {0x00110102, GetOwnNsDataIdList, "GetOwnNsDataIdList"},
30 {0x00120102, GetNewDataNsDataIdList, "GetNewDataNsDataIdList"},
31 {0x00130102, GetOwnNewDataNsDataIdList, "GetOwnNewDataNsDataIdList"},
32 {0x00140082, SendProperty, "SendProperty"},
33 {0x00150042, SendPropertyHandle, "SendPropertyHandle"},
34 {0x00160082, ReceiveProperty, "ReceiveProperty"},
35 {0x00170082, UpdateTaskInterval, "UpdateTaskInterval"},
36 {0x00180082, UpdateTaskCount, "UpdateTaskCount"},
37 {0x00190042, GetTaskInterval, "GetTaskInterval"},
38 {0x001A0042, GetTaskCount, "GetTaskCount"},
39 {0x001B0042, GetTaskServiceStatus, "GetTaskServiceStatus"},
40 {0x001C0042, StartTask, "StartTask"},
41 {0x001D0042, StartTaskImmediate, "StartTaskImmediate"},
42 {0x001E0042, CancelTask, "CancelTask"},
43 {0x001F0000, GetTaskFinishHandle, "GetTaskFinishHandle"},
44 {0x00200082, GetTaskState, "GetTaskState"},
45 {0x00210042, GetTaskResult, "GetTaskResult"},
46 {0x00220042, GetTaskCommErrorCode, "GetTaskCommErrorCode"},
47 {0x002300C2, GetTaskStatus, "GetTaskStatus"},
48 {0x00240082, GetTaskError, "GetTaskError"},
49 {0x00250082, GetTaskInfo, "GetTaskInfo"},
50 {0x00260040, DeleteNsData, "DeleteNsData"},
51 {0x002700C2, GetNsDataHeaderInfo, "GetNsDataHeaderInfo"},
52 {0x00280102, ReadNsData, "ReadNsData"},
53 {0x00290080, SetNsDataAdditionalInfo, "SetNsDataAdditionalInfo"},
54 {0x002A0040, GetNsDataAdditionalInfo, "GetNsDataAdditionalInfo"},
55 {0x002B0080, SetNsDataNewFlag, "SetNsDataNewFlag"},
56 {0x002C0040, GetNsDataNewFlag, "GetNsDataNewFlag"},
57 {0x002D0040, GetNsDataLastUpdate, "GetNsDataLastUpdate"},
58 {0x002E0040, GetErrorCode, "GetErrorCode"},
59 {0x002F0140, RegisterStorageEntry, "RegisterStorageEntry"},
60 {0x00300000, GetStorageEntryInfo, "GetStorageEntryInfo"},
61 {0x00310100, SetStorageOption, "SetStorageOption"},
62 {0x00320000, GetStorageOption, "GetStorageOption"},
63 {0x00330042, StartBgImmediate, "StartBgImmediate"},
64 {0x00340042, GetTaskActivePriority, "GetTaskActivePriority"},
65 {0x003500C2, RegisterImmediateTask, "RegisterImmediateTask"},
66 {0x00360084, SetTaskQuery, "SetTaskQuery"},
67 {0x00370084, GetTaskQuery, "GetTaskQuery"},
68 // boss:p
69 {0x04010082, nullptr, "InitializeSessionPrivileged"},
70 {0x04040080, nullptr, "GetAppNewFlag"},
71 {0x040D0182, nullptr, "GetNsDataIdListPrivileged"},
72 {0x040E0182, nullptr, "GetNsDataIdListPrivileged1"},
73 {0x04130082, nullptr, "SendPropertyPrivileged"},
74 {0x041500C0, nullptr, "DeleteNsDataPrivileged"},
75 {0x04160142, nullptr, "GetNsDataHeaderInfoPrivileged"},
76 {0x04170182, nullptr, "ReadNsDataPrivileged"},
77 {0x041A0100, nullptr, "SetNsDataNewFlagPrivileged"},
78 {0x041B00C0, nullptr, "GetNsDataNewFlagPrivileged"},
79};
80
81BOSS_P_Interface::BOSS_P_Interface() {
82 Register(FunctionTable);
83}
84
85} // namespace BOSS
86} // namespace Service
diff --git a/src/core/hle/service/boss/boss_p.h b/src/core/hle/service/boss/boss_p.h
deleted file mode 100644
index 32112c251..000000000
--- a/src/core/hle/service/boss/boss_p.h
+++ /dev/null
@@ -1,22 +0,0 @@
1// Copyright 2015 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 "core/hle/service/service.h"
8
9namespace Service {
10namespace BOSS {
11
12class BOSS_P_Interface : public Service::Interface {
13public:
14 BOSS_P_Interface();
15
16 std::string GetPortName() const override {
17 return "boss:P";
18 }
19};
20
21} // namespace BOSS
22} // namespace Service
diff --git a/src/core/hle/service/boss/boss_u.cpp b/src/core/hle/service/boss/boss_u.cpp
deleted file mode 100644
index 371d702e0..000000000
--- a/src/core/hle/service/boss/boss_u.cpp
+++ /dev/null
@@ -1,74 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/boss/boss.h"
6#include "core/hle/service/boss/boss_u.h"
7
8namespace Service {
9namespace BOSS {
10
11const Interface::FunctionInfo FunctionTable[] = {
12 {0x00010082, InitializeSession, "InitializeSession"},
13 {0x00020100, RegisterStorage, "RegisterStorage"},
14 {0x00030000, UnregisterStorage, "UnregisterStorage"},
15 {0x00040000, GetStorageInfo, "GetStorageInfo"},
16 {0x00050042, RegisterPrivateRootCa, "RegisterPrivateRootCa"},
17 {0x00060084, RegisterPrivateClientCert, "RegisterPrivateClientCert"},
18 {0x00070000, GetNewArrivalFlag, "GetNewArrivalFlag"},
19 {0x00080002, RegisterNewArrivalEvent, "RegisterNewArrivalEvent"},
20 {0x00090040, SetOptoutFlag, "SetOptoutFlag"},
21 {0x000A0000, GetOptoutFlag, "GetOptoutFlag"},
22 {0x000B00C2, RegisterTask, "RegisterTask"},
23 {0x000C0082, UnregisterTask, "UnregisterTask"},
24 {0x000D0082, ReconfigureTask, "ReconfigureTask"},
25 {0x000E0000, GetTaskIdList, "GetTaskIdList"},
26 {0x000F0042, GetStepIdList, "GetStepIdList"},
27 {0x00100102, GetNsDataIdList, "GetNsDataIdList"},
28 {0x00110102, GetOwnNsDataIdList, "GetOwnNsDataIdList"},
29 {0x00120102, GetNewDataNsDataIdList, "GetNewDataNsDataIdList"},
30 {0x00130102, GetOwnNewDataNsDataIdList, "GetOwnNewDataNsDataIdList"},
31 {0x00140082, SendProperty, "SendProperty"},
32 {0x00150042, SendPropertyHandle, "SendPropertyHandle"},
33 {0x00160082, ReceiveProperty, "ReceiveProperty"},
34 {0x00170082, UpdateTaskInterval, "UpdateTaskInterval"},
35 {0x00180082, UpdateTaskCount, "UpdateTaskCount"},
36 {0x00190042, GetTaskInterval, "GetTaskInterval"},
37 {0x001A0042, GetTaskCount, "GetTaskCount"},
38 {0x001B0042, GetTaskServiceStatus, "GetTaskServiceStatus"},
39 {0x001C0042, StartTask, "StartTask"},
40 {0x001D0042, StartTaskImmediate, "StartTaskImmediate"},
41 {0x001E0042, CancelTask, "CancelTask"},
42 {0x001F0000, GetTaskFinishHandle, "GetTaskFinishHandle"},
43 {0x00200082, GetTaskState, "GetTaskState"},
44 {0x00210042, GetTaskResult, "GetTaskResult"},
45 {0x00220042, GetTaskCommErrorCode, "GetTaskCommErrorCode"},
46 {0x002300C2, GetTaskStatus, "GetTaskStatus"},
47 {0x00240082, GetTaskError, "GetTaskError"},
48 {0x00250082, GetTaskInfo, "GetTaskInfo"},
49 {0x00260040, DeleteNsData, "DeleteNsData"},
50 {0x002700C2, GetNsDataHeaderInfo, "GetNsDataHeaderInfo"},
51 {0x00280102, ReadNsData, "ReadNsData"},
52 {0x00290080, SetNsDataAdditionalInfo, "SetNsDataAdditionalInfo"},
53 {0x002A0040, GetNsDataAdditionalInfo, "GetNsDataAdditionalInfo"},
54 {0x002B0080, SetNsDataNewFlag, "SetNsDataNewFlag"},
55 {0x002C0040, GetNsDataNewFlag, "GetNsDataNewFlag"},
56 {0x002D0040, GetNsDataLastUpdate, "GetNsDataLastUpdate"},
57 {0x002E0040, GetErrorCode, "GetErrorCode"},
58 {0x002F0140, RegisterStorageEntry, "RegisterStorageEntry"},
59 {0x00300000, GetStorageEntryInfo, "GetStorageEntryInfo"},
60 {0x00310100, SetStorageOption, "SetStorageOption"},
61 {0x00320000, GetStorageOption, "GetStorageOption"},
62 {0x00330042, StartBgImmediate, "StartBgImmediate"},
63 {0x00340042, GetTaskActivePriority, "GetTaskActivePriority"},
64 {0x003500C2, RegisterImmediateTask, "RegisterImmediateTask"},
65 {0x00360084, SetTaskQuery, "SetTaskQuery"},
66 {0x00370084, GetTaskQuery, "GetTaskQuery"},
67};
68
69BOSS_U_Interface::BOSS_U_Interface() {
70 Register(FunctionTable);
71}
72
73} // namespace BOSS
74} // namespace Service
diff --git a/src/core/hle/service/boss/boss_u.h b/src/core/hle/service/boss/boss_u.h
deleted file mode 100644
index d047d8cf2..000000000
--- a/src/core/hle/service/boss/boss_u.h
+++ /dev/null
@@ -1,22 +0,0 @@
1// Copyright 2014 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 "core/hle/service/service.h"
8
9namespace Service {
10namespace BOSS {
11
12class BOSS_U_Interface : public Service::Interface {
13public:
14 BOSS_U_Interface();
15
16 std::string GetPortName() const override {
17 return "boss:U";
18 }
19};
20
21} // namespace BOSS
22} // namespace Service
diff --git a/src/core/hle/service/cam/cam.cpp b/src/core/hle/service/cam/cam.cpp
deleted file mode 100644
index 8172edae8..000000000
--- a/src/core/hle/service/cam/cam.cpp
+++ /dev/null
@@ -1,1106 +0,0 @@
1// Copyright 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <array>
7#include <future>
8#include <memory>
9#include <vector>
10#include "common/bit_set.h"
11#include "common/logging/log.h"
12#include "core/core_timing.h"
13#include "core/frontend/camera/factory.h"
14#include "core/hle/ipc.h"
15#include "core/hle/ipc_helpers.h"
16#include "core/hle/kernel/event.h"
17#include "core/hle/result.h"
18#include "core/hle/service/cam/cam.h"
19#include "core/hle/service/cam/cam_c.h"
20#include "core/hle/service/cam/cam_q.h"
21#include "core/hle/service/cam/cam_s.h"
22#include "core/hle/service/cam/cam_u.h"
23#include "core/hle/service/service.h"
24#include "core/memory.h"
25#include "core/settings.h"
26
27namespace Service {
28namespace CAM {
29
30namespace {
31
32struct ContextConfig {
33 Flip flip;
34 Effect effect;
35 OutputFormat format;
36 Resolution resolution;
37};
38
39struct CameraConfig {
40 std::unique_ptr<Camera::CameraInterface> impl;
41 std::array<ContextConfig, 2> contexts;
42 int current_context;
43 FrameRate frame_rate;
44};
45
46struct PortConfig {
47 int camera_id;
48
49 bool is_active; // set when the port is activated by an Activate call.
50 bool is_pending_receiving; // set if SetReceiving is called when is_busy = false. When
51 // StartCapture is called then, this will trigger a receiving
52 // process and reset itself.
53 bool is_busy; // set when StartCapture is called and reset when StopCapture is called.
54 bool is_receiving; // set when there is an ongoing receiving process.
55
56 bool is_trimming;
57 u16 x0; // x-coordinate of starting position for trimming
58 u16 y0; // y-coordinate of starting position for trimming
59 u16 x1; // x-coordinate of ending position for trimming
60 u16 y1; // y-coordinate of ending position for trimming
61
62 u16 transfer_bytes;
63
64 Kernel::SharedPtr<Kernel::Event> completion_event;
65 Kernel::SharedPtr<Kernel::Event> buffer_error_interrupt_event;
66 Kernel::SharedPtr<Kernel::Event> vsync_interrupt_event;
67
68 std::future<std::vector<u16>> capture_result; // will hold the received frame.
69 VAddr dest; // the destination address of a receiving process
70 u32 dest_size; // the destination size of a receiving process
71
72 void Clear() {
73 completion_event->Clear();
74 buffer_error_interrupt_event->Clear();
75 vsync_interrupt_event->Clear();
76 is_receiving = false;
77 is_active = false;
78 is_pending_receiving = false;
79 is_busy = false;
80 is_trimming = false;
81 x0 = 0;
82 y0 = 0;
83 x1 = 0;
84 y1 = 0;
85 transfer_bytes = 256;
86 }
87};
88
89// built-in resolution parameters
90constexpr std::array<Resolution, 8> PRESET_RESOLUTION{{
91 {640, 480, 0, 0, 639, 479}, // VGA
92 {320, 240, 0, 0, 639, 479}, // QVGA
93 {160, 120, 0, 0, 639, 479}, // QQVGA
94 {352, 288, 26, 0, 613, 479}, // CIF
95 {176, 144, 26, 0, 613, 479}, // QCIF
96 {256, 192, 0, 0, 639, 479}, // DS_LCD
97 {512, 384, 0, 0, 639, 479}, // DS_LCDx4
98 {400, 240, 0, 48, 639, 431}, // CTR_TOP_LCD
99}};
100
101// latency in ms for each frame rate option
102constexpr std::array<int, 13> LATENCY_BY_FRAME_RATE{{
103 67, // Rate_15
104 67, // Rate_15_To_5
105 67, // Rate_15_To_2
106 100, // Rate_10
107 118, // Rate_8_5
108 200, // Rate_5
109 50, // Rate_20
110 50, // Rate_20_To_5
111 33, // Rate_30
112 33, // Rate_30_To_5
113 67, // Rate_15_To_10
114 50, // Rate_20_To_10
115 33, // Rate_30_To_10
116}};
117
118std::array<CameraConfig, NumCameras> cameras;
119std::array<PortConfig, 2> ports;
120int completion_event_callback;
121
122const ResultCode ERROR_INVALID_ENUM_VALUE(ErrorDescription::InvalidEnumValue, ErrorModule::CAM,
123 ErrorSummary::InvalidArgument, ErrorLevel::Usage);
124const ResultCode ERROR_OUT_OF_RANGE(ErrorDescription::OutOfRange, ErrorModule::CAM,
125 ErrorSummary::InvalidArgument, ErrorLevel::Usage);
126
127void CompletionEventCallBack(u64 port_id, int) {
128 PortConfig& port = ports[port_id];
129 const CameraConfig& camera = cameras[port.camera_id];
130 const auto buffer = port.capture_result.get();
131
132 if (port.is_trimming) {
133 u32 trim_width;
134 u32 trim_height;
135 const int original_width = camera.contexts[camera.current_context].resolution.width;
136 const int original_height = camera.contexts[camera.current_context].resolution.height;
137 if (port.x1 <= port.x0 || port.y1 <= port.y0 || port.x1 > original_width ||
138 port.y1 > original_height) {
139 LOG_ERROR(Service_CAM, "Invalid trimming coordinates x0=%u, y0=%u, x1=%u, y1=%u",
140 port.x0, port.y0, port.x1, port.y1);
141 trim_width = 0;
142 trim_height = 0;
143 } else {
144 trim_width = port.x1 - port.x0;
145 trim_height = port.y1 - port.y0;
146 }
147
148 u32 trim_size = (port.x1 - port.x0) * (port.y1 - port.y0) * 2;
149 if (port.dest_size != trim_size) {
150 LOG_ERROR(Service_CAM, "The destination size (%u) doesn't match the source (%u)!",
151 port.dest_size, trim_size);
152 }
153
154 const u32 src_offset = port.y0 * original_width + port.x0;
155 const u16* src_ptr = buffer.data() + src_offset;
156 // Note: src_size_left is int because it can be negative if the buffer size doesn't match.
157 int src_size_left = static_cast<int>((buffer.size() - src_offset) * sizeof(u16));
158 VAddr dest_ptr = port.dest;
159 // Note: dest_size_left and line_bytes are int to match the type of src_size_left.
160 int dest_size_left = static_cast<int>(port.dest_size);
161 const int line_bytes = static_cast<int>(trim_width * sizeof(u16));
162
163 for (u32 y = 0; y < trim_height; ++y) {
164 int copy_length = std::min({line_bytes, dest_size_left, src_size_left});
165 if (copy_length <= 0) {
166 break;
167 }
168 Memory::WriteBlock(dest_ptr, src_ptr, copy_length);
169 dest_ptr += copy_length;
170 dest_size_left -= copy_length;
171 src_ptr += original_width;
172 src_size_left -= original_width * sizeof(u16);
173 }
174 } else {
175 std::size_t buffer_size = buffer.size() * sizeof(u16);
176 if (port.dest_size != buffer_size) {
177 LOG_ERROR(Service_CAM, "The destination size (%u) doesn't match the source (%zu)!",
178 port.dest_size, buffer_size);
179 }
180 Memory::WriteBlock(port.dest, buffer.data(), std::min<size_t>(port.dest_size, buffer_size));
181 }
182
183 port.is_receiving = false;
184 port.completion_event->Signal();
185}
186
187// Starts a receiving process on the specified port. This can only be called when is_busy = true and
188// is_receiving = false.
189void StartReceiving(int port_id) {
190 PortConfig& port = ports[port_id];
191 port.is_receiving = true;
192
193 // launches a capture task asynchronously
194 const CameraConfig& camera = cameras[port.camera_id];
195 port.capture_result =
196 std::async(std::launch::async, &Camera::CameraInterface::ReceiveFrame, camera.impl.get());
197
198 // schedules a completion event according to the frame rate. The event will block on the
199 // capture task if it is not finished within the expected time
200 CoreTiming::ScheduleEvent(
201 msToCycles(LATENCY_BY_FRAME_RATE[static_cast<int>(camera.frame_rate)]),
202 completion_event_callback, port_id);
203}
204
205// Cancels any ongoing receiving processes at the specified port. This is used by functions that
206// stop capturing.
207// TODO: what is the exact behaviour on real 3DS when stopping capture during an ongoing process?
208// Will the completion event still be signaled?
209void CancelReceiving(int port_id) {
210 if (!ports[port_id].is_receiving)
211 return;
212 LOG_WARNING(Service_CAM, "tries to cancel an ongoing receiving process.");
213 CoreTiming::UnscheduleEvent(completion_event_callback, port_id);
214 ports[port_id].capture_result.wait();
215 ports[port_id].is_receiving = false;
216}
217
218// Activates the specified port with the specfied camera.
219static void ActivatePort(int port_id, int camera_id) {
220 if (ports[port_id].is_busy && ports[port_id].camera_id != camera_id) {
221 CancelReceiving(port_id);
222 cameras[ports[port_id].camera_id].impl->StopCapture();
223 ports[port_id].is_busy = false;
224 }
225 ports[port_id].is_active = true;
226 ports[port_id].camera_id = camera_id;
227}
228
229template <int max_index>
230class CommandParamBitSet : public BitSet8 {
231public:
232 explicit CommandParamBitSet(u8 command_param) : BitSet8(command_param) {}
233
234 bool IsValid() const {
235 return m_val < (1 << max_index);
236 }
237
238 bool IsSingle() const {
239 return IsValid() && Count() == 1;
240 }
241};
242
243using PortSet = CommandParamBitSet<2>;
244using ContextSet = CommandParamBitSet<2>;
245using CameraSet = CommandParamBitSet<3>;
246
247} // namespace
248
249void StartCapture(Service::Interface* self) {
250 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x01, 1, 0);
251 const PortSet port_select(rp.Pop<u8>());
252
253 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
254
255 if (port_select.IsValid()) {
256 for (int i : port_select) {
257 if (!ports[i].is_busy) {
258 if (!ports[i].is_active) {
259 // This doesn't return an error, but seems to put the camera in an undefined
260 // state
261 LOG_ERROR(Service_CAM, "port %u hasn't been activated", i);
262 } else {
263 cameras[ports[i].camera_id].impl->StartCapture();
264 ports[i].is_busy = true;
265 if (ports[i].is_pending_receiving) {
266 ports[i].is_pending_receiving = false;
267 StartReceiving(i);
268 }
269 }
270 } else {
271 LOG_WARNING(Service_CAM, "port %u already started", i);
272 }
273 }
274 rb.Push(RESULT_SUCCESS);
275 } else {
276 LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val);
277 rb.Push(ERROR_INVALID_ENUM_VALUE);
278 }
279
280 LOG_DEBUG(Service_CAM, "called, port_select=%u", port_select.m_val);
281}
282
283void StopCapture(Service::Interface* self) {
284 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x02, 1, 0);
285 const PortSet port_select(rp.Pop<u8>());
286
287 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
288
289 if (port_select.IsValid()) {
290 for (int i : port_select) {
291 if (ports[i].is_busy) {
292 CancelReceiving(i);
293 cameras[ports[i].camera_id].impl->StopCapture();
294 ports[i].is_busy = false;
295 } else {
296 LOG_WARNING(Service_CAM, "port %u already stopped", i);
297 }
298 }
299 rb.Push(RESULT_SUCCESS);
300 } else {
301 LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val);
302 rb.Push(ERROR_INVALID_ENUM_VALUE);
303 }
304
305 LOG_DEBUG(Service_CAM, "called, port_select=%u", port_select.m_val);
306}
307
308void IsBusy(Service::Interface* self) {
309 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x03, 1, 0);
310 const PortSet port_select(rp.Pop<u8>());
311
312 IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
313
314 if (port_select.IsValid()) {
315 bool is_busy = true;
316 // Note: the behaviour on no or both ports selected are verified against real 3DS.
317 for (int i : port_select) {
318 is_busy &= ports[i].is_busy;
319 }
320 rb.Push(RESULT_SUCCESS);
321 rb.Push(is_busy);
322 } else {
323 LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val);
324 rb.Push(ERROR_INVALID_ENUM_VALUE);
325 rb.Skip(1, false);
326 }
327
328 LOG_DEBUG(Service_CAM, "called, port_select=%u", port_select.m_val);
329}
330
331void ClearBuffer(Service::Interface* self) {
332 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x04, 1, 0);
333 const PortSet port_select(rp.Pop<u8>());
334
335 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
336 rb.Push(RESULT_SUCCESS);
337
338 LOG_WARNING(Service_CAM, "(STUBBED) called, port_select=%u", port_select.m_val);
339}
340
341void GetVsyncInterruptEvent(Service::Interface* self) {
342 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x05, 1, 0);
343 const PortSet port_select(rp.Pop<u8>());
344
345 IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
346 if (port_select.IsSingle()) {
347 int port = *port_select.begin();
348 rb.Push(RESULT_SUCCESS);
349 rb.PushCopyHandles(
350 Kernel::g_handle_table.Create(ports[port].vsync_interrupt_event).Unwrap());
351 } else {
352 LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val);
353 rb.Push(ERROR_INVALID_ENUM_VALUE);
354 rb.PushCopyHandles(0);
355 }
356
357 LOG_WARNING(Service_CAM, "(STUBBED) called, port_select=%u", port_select.m_val);
358}
359
360void GetBufferErrorInterruptEvent(Service::Interface* self) {
361 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x06, 1, 0);
362 const PortSet port_select(rp.Pop<u8>());
363
364 IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
365 if (port_select.IsSingle()) {
366 int port = *port_select.begin();
367 rb.Push(RESULT_SUCCESS);
368 rb.PushCopyHandles(
369 Kernel::g_handle_table.Create(ports[port].buffer_error_interrupt_event).Unwrap());
370 } else {
371 LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val);
372 rb.Push(ERROR_INVALID_ENUM_VALUE);
373 rb.PushCopyHandles(0);
374 }
375
376 LOG_WARNING(Service_CAM, "(STUBBED) called, port_select=%u", port_select.m_val);
377}
378
379void SetReceiving(Service::Interface* self) {
380 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x07, 4, 2);
381 const VAddr dest = rp.Pop<u32>();
382 const PortSet port_select(rp.Pop<u8>());
383 const u32 image_size = rp.Pop<u32>();
384 const u16 trans_unit = rp.Pop<u16>();
385 rp.PopHandle(); // Handle to destination process. not used
386
387 IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
388 if (port_select.IsSingle()) {
389 int port_id = *port_select.begin();
390 PortConfig& port = ports[port_id];
391 CancelReceiving(port_id);
392 port.completion_event->Clear();
393 port.dest = dest;
394 port.dest_size = image_size;
395
396 if (port.is_busy) {
397 StartReceiving(port_id);
398 } else {
399 port.is_pending_receiving = true;
400 }
401
402 rb.Push(RESULT_SUCCESS);
403 rb.PushCopyHandles(Kernel::g_handle_table.Create(port.completion_event).Unwrap());
404 } else {
405 LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val);
406 rb.Push(ERROR_INVALID_ENUM_VALUE);
407 rb.PushCopyHandles(0);
408 }
409
410 LOG_DEBUG(Service_CAM, "called, addr=0x%X, port_select=%u, image_size=%u, trans_unit=%u", dest,
411 port_select.m_val, image_size, trans_unit);
412}
413
414void IsFinishedReceiving(Service::Interface* self) {
415 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x08, 1, 0);
416 const PortSet port_select(rp.Pop<u8>());
417
418 IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
419 if (port_select.IsSingle()) {
420 int port = *port_select.begin();
421 bool is_busy = ports[port].is_receiving || ports[port].is_pending_receiving;
422 rb.Push(RESULT_SUCCESS);
423 rb.Push(!is_busy);
424 } else {
425 LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val);
426 rb.Push(ERROR_INVALID_ENUM_VALUE);
427 rb.Skip(1, false);
428 }
429
430 LOG_DEBUG(Service_CAM, "called, port_select=%u", port_select.m_val);
431}
432
433void SetTransferLines(Service::Interface* self) {
434 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x09, 4, 0);
435 const PortSet port_select(rp.Pop<u8>());
436 const u16 transfer_lines = rp.Pop<u16>();
437 const u16 width = rp.Pop<u16>();
438 const u16 height = rp.Pop<u16>();
439
440 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
441 if (port_select.IsValid()) {
442 for (int i : port_select) {
443 ports[i].transfer_bytes = transfer_lines * width * 2;
444 }
445 rb.Push(RESULT_SUCCESS);
446 } else {
447 LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val);
448 rb.Push(ERROR_INVALID_ENUM_VALUE);
449 }
450
451 LOG_WARNING(Service_CAM, "(STUBBED) called, port_select=%u, lines=%u, width=%u, height=%u",
452 port_select.m_val, transfer_lines, width, height);
453}
454
455void GetMaxLines(Service::Interface* self) {
456 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x0A, 2, 0);
457 const u16 width = rp.Pop<u16>();
458 const u16 height = rp.Pop<u16>();
459
460 IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
461
462 // Note: the result of the algorithm below are hwtested with width < 640 and with height < 480
463 constexpr u32 MIN_TRANSFER_UNIT = 256;
464 constexpr u32 MAX_BUFFER_SIZE = 2560;
465 if (width * height * 2 % MIN_TRANSFER_UNIT != 0) {
466 rb.Push(ERROR_OUT_OF_RANGE);
467 rb.Skip(1, false);
468 } else {
469 u32 lines = MAX_BUFFER_SIZE / width;
470 if (lines > height) {
471 lines = height;
472 }
473 ResultCode result = RESULT_SUCCESS;
474 while (height % lines != 0 || (lines * width * 2 % MIN_TRANSFER_UNIT != 0)) {
475 --lines;
476 if (lines == 0) {
477 result = ERROR_OUT_OF_RANGE;
478 break;
479 }
480 }
481 rb.Push(result);
482 rb.Push(lines);
483 }
484
485 LOG_DEBUG(Service_CAM, "called, width=%u, height=%u", width, height);
486}
487
488void SetTransferBytes(Service::Interface* self) {
489 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x0B, 4, 0);
490 const PortSet port_select(rp.Pop<u8>());
491 const u16 transfer_bytes = rp.Pop<u16>();
492 const u16 width = rp.Pop<u16>();
493 const u16 height = rp.Pop<u16>();
494
495 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
496 if (port_select.IsValid()) {
497 for (int i : port_select) {
498 ports[i].transfer_bytes = transfer_bytes;
499 }
500 rb.Push(RESULT_SUCCESS);
501 } else {
502 LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val);
503 rb.Push(ERROR_INVALID_ENUM_VALUE);
504 }
505
506 LOG_WARNING(Service_CAM, "(STUBBED)called, port_select=%u, bytes=%u, width=%u, height=%u",
507 port_select.m_val, transfer_bytes, width, height);
508}
509
510void GetTransferBytes(Service::Interface* self) {
511 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x0C, 1, 0);
512 const PortSet port_select(rp.Pop<u8>());
513
514 IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
515 if (port_select.IsSingle()) {
516 int port = *port_select.begin();
517 rb.Push(RESULT_SUCCESS);
518 rb.Push(ports[port].transfer_bytes);
519 } else {
520 LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val);
521 rb.Push(ERROR_INVALID_ENUM_VALUE);
522 rb.Skip(1, false);
523 }
524
525 LOG_WARNING(Service_CAM, "(STUBBED)called, port_select=%u", port_select.m_val);
526}
527
528void GetMaxBytes(Service::Interface* self) {
529 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x0D, 2, 0);
530 const u16 width = rp.Pop<u16>();
531 const u16 height = rp.Pop<u16>();
532
533 IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
534
535 // Note: the result of the algorithm below are hwtested with width < 640 and with height < 480
536 constexpr u32 MIN_TRANSFER_UNIT = 256;
537 constexpr u32 MAX_BUFFER_SIZE = 2560;
538 if (width * height * 2 % MIN_TRANSFER_UNIT != 0) {
539 rb.Push(ERROR_OUT_OF_RANGE);
540 rb.Skip(1, false);
541 } else {
542 u32 bytes = MAX_BUFFER_SIZE;
543
544 while (width * height * 2 % bytes != 0) {
545 bytes -= MIN_TRANSFER_UNIT;
546 }
547
548 rb.Push(RESULT_SUCCESS);
549 rb.Push(bytes);
550 }
551
552 LOG_DEBUG(Service_CAM, "called, width=%u, height=%u", width, height);
553}
554
555void SetTrimming(Service::Interface* self) {
556 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x0E, 2, 0);
557 const PortSet port_select(rp.Pop<u8>());
558 const bool trim = rp.Pop<bool>();
559
560 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
561 if (port_select.IsValid()) {
562 for (int i : port_select) {
563 ports[i].is_trimming = trim;
564 }
565 rb.Push(RESULT_SUCCESS);
566 } else {
567 LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val);
568 rb.Push(ERROR_INVALID_ENUM_VALUE);
569 }
570
571 LOG_DEBUG(Service_CAM, "called, port_select=%u, trim=%d", port_select.m_val, trim);
572}
573
574void IsTrimming(Service::Interface* self) {
575 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x0F, 1, 0);
576 const PortSet port_select(rp.Pop<u8>());
577
578 IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
579 if (port_select.IsSingle()) {
580 int port = *port_select.begin();
581 rb.Push(RESULT_SUCCESS);
582 rb.Push(ports[port].is_trimming);
583 } else {
584 LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val);
585 rb.Push(ERROR_INVALID_ENUM_VALUE);
586 rb.Skip(1, false);
587 }
588
589 LOG_DEBUG(Service_CAM, "called, port_select=%u", port_select.m_val);
590}
591
592void SetTrimmingParams(Service::Interface* self) {
593 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x10, 5, 0);
594 const PortSet port_select(rp.Pop<u8>());
595 const u16 x0 = rp.Pop<u16>();
596 const u16 y0 = rp.Pop<u16>();
597 const u16 x1 = rp.Pop<u16>();
598 const u16 y1 = rp.Pop<u16>();
599
600 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
601 if (port_select.IsValid()) {
602 for (int i : port_select) {
603 ports[i].x0 = x0;
604 ports[i].y0 = y0;
605 ports[i].x1 = x1;
606 ports[i].y1 = y1;
607 }
608 rb.Push(RESULT_SUCCESS);
609 } else {
610 LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val);
611 rb.Push(ERROR_INVALID_ENUM_VALUE);
612 }
613
614 LOG_DEBUG(Service_CAM, "called, port_select=%u, x0=%u, y0=%u, x1=%u, y1=%u", port_select.m_val,
615 x0, y0, x1, y1);
616}
617
618void GetTrimmingParams(Service::Interface* self) {
619 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x11, 1, 0);
620 const PortSet port_select(rp.Pop<u8>());
621
622 IPC::RequestBuilder rb = rp.MakeBuilder(5, 0);
623 if (port_select.IsSingle()) {
624 int port = *port_select.begin();
625 rb.Push(RESULT_SUCCESS);
626 rb.Push(ports[port].x0);
627 rb.Push(ports[port].y0);
628 rb.Push(ports[port].x1);
629 rb.Push(ports[port].y1);
630 } else {
631 LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val);
632 rb.Push(ERROR_INVALID_ENUM_VALUE);
633 rb.Skip(4, false);
634 }
635
636 LOG_DEBUG(Service_CAM, "called, port_select=%u", port_select.m_val);
637}
638
639void SetTrimmingParamsCenter(Service::Interface* self) {
640 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x12, 5, 0);
641 const PortSet port_select(rp.Pop<u8>());
642 const u16 trim_w = rp.Pop<u16>();
643 const u16 trim_h = rp.Pop<u16>();
644 const u16 cam_w = rp.Pop<u16>();
645 const u16 cam_h = rp.Pop<u16>();
646
647 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
648 if (port_select.IsValid()) {
649 for (int i : port_select) {
650 ports[i].x0 = (cam_w - trim_w) / 2;
651 ports[i].y0 = (cam_h - trim_h) / 2;
652 ports[i].x1 = ports[i].x0 + trim_w;
653 ports[i].y1 = ports[i].y0 + trim_h;
654 }
655 rb.Push(RESULT_SUCCESS);
656 } else {
657 LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val);
658 rb.Push(ERROR_INVALID_ENUM_VALUE);
659 }
660
661 LOG_DEBUG(Service_CAM, "called, port_select=%u, trim_w=%u, trim_h=%u, cam_w=%u, cam_h=%u",
662 port_select.m_val, trim_w, trim_h, cam_w, cam_h);
663}
664
665void Activate(Service::Interface* self) {
666 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x13, 1, 0);
667 const CameraSet camera_select(rp.Pop<u8>());
668
669 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
670 if (camera_select.IsValid()) {
671 if (camera_select.m_val == 0) { // deactive all
672 for (int i = 0; i < 2; ++i) {
673 if (ports[i].is_busy) {
674 CancelReceiving(i);
675 cameras[ports[i].camera_id].impl->StopCapture();
676 ports[i].is_busy = false;
677 }
678 ports[i].is_active = false;
679 }
680 rb.Push(RESULT_SUCCESS);
681 } else if (camera_select[0] && camera_select[1]) {
682 LOG_ERROR(Service_CAM, "camera 0 and 1 can't be both activated");
683 rb.Push(ERROR_INVALID_ENUM_VALUE);
684 } else {
685 if (camera_select[0]) {
686 ActivatePort(0, 0);
687 } else if (camera_select[1]) {
688 ActivatePort(0, 1);
689 }
690
691 if (camera_select[2]) {
692 ActivatePort(1, 2);
693 }
694 rb.Push(RESULT_SUCCESS);
695 }
696 } else {
697 LOG_ERROR(Service_CAM, "invalid camera_select=%u", camera_select.m_val);
698 rb.Push(ERROR_INVALID_ENUM_VALUE);
699 }
700
701 LOG_DEBUG(Service_CAM, "called, camera_select=%u", camera_select.m_val);
702}
703
704void SwitchContext(Service::Interface* self) {
705 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x14, 2, 0);
706 const CameraSet camera_select(rp.Pop<u8>());
707 const ContextSet context_select(rp.Pop<u8>());
708
709 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
710 if (camera_select.IsValid() && context_select.IsSingle()) {
711 int context = *context_select.begin();
712 for (int camera : camera_select) {
713 cameras[camera].current_context = context;
714 const ContextConfig& context_config = cameras[camera].contexts[context];
715 cameras[camera].impl->SetFlip(context_config.flip);
716 cameras[camera].impl->SetEffect(context_config.effect);
717 cameras[camera].impl->SetFormat(context_config.format);
718 cameras[camera].impl->SetResolution(context_config.resolution);
719 }
720 rb.Push(RESULT_SUCCESS);
721 } else {
722 LOG_ERROR(Service_CAM, "invalid camera_select=%u, context_select=%u", camera_select.m_val,
723 context_select.m_val);
724 rb.Push(ERROR_INVALID_ENUM_VALUE);
725 }
726
727 LOG_DEBUG(Service_CAM, "called, camera_select=%u, context_select=%u", camera_select.m_val,
728 context_select.m_val);
729}
730
731void FlipImage(Service::Interface* self) {
732 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1D, 3, 0);
733 const CameraSet camera_select(rp.Pop<u8>());
734 const Flip flip = static_cast<Flip>(rp.Pop<u8>());
735 const ContextSet context_select(rp.Pop<u8>());
736
737 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
738 if (camera_select.IsValid() && context_select.IsValid()) {
739 for (int camera : camera_select) {
740 for (int context : context_select) {
741 cameras[camera].contexts[context].flip = flip;
742 if (cameras[camera].current_context == context) {
743 cameras[camera].impl->SetFlip(flip);
744 }
745 }
746 }
747 rb.Push(RESULT_SUCCESS);
748 } else {
749 LOG_ERROR(Service_CAM, "invalid camera_select=%u, context_select=%u", camera_select.m_val,
750 context_select.m_val);
751 rb.Push(ERROR_INVALID_ENUM_VALUE);
752 }
753
754 LOG_DEBUG(Service_CAM, "called, camera_select=%u, flip=%d, context_select=%u",
755 camera_select.m_val, static_cast<int>(flip), context_select.m_val);
756}
757
758void SetDetailSize(Service::Interface* self) {
759 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1E, 8, 0);
760 const CameraSet camera_select(rp.Pop<u8>());
761 Resolution resolution;
762 resolution.width = rp.Pop<u16>();
763 resolution.height = rp.Pop<u16>();
764 resolution.crop_x0 = rp.Pop<u16>();
765 resolution.crop_y0 = rp.Pop<u16>();
766 resolution.crop_x1 = rp.Pop<u16>();
767 resolution.crop_y1 = rp.Pop<u16>();
768 const ContextSet context_select(rp.Pop<u8>());
769
770 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
771 if (camera_select.IsValid() && context_select.IsValid()) {
772 for (int camera : camera_select) {
773 for (int context : context_select) {
774 cameras[camera].contexts[context].resolution = resolution;
775 if (cameras[camera].current_context == context) {
776 cameras[camera].impl->SetResolution(resolution);
777 }
778 }
779 }
780 rb.Push(RESULT_SUCCESS);
781 } else {
782 LOG_ERROR(Service_CAM, "invalid camera_select=%u, context_select=%u", camera_select.m_val,
783 context_select.m_val);
784 rb.Push(ERROR_INVALID_ENUM_VALUE);
785 }
786
787 LOG_DEBUG(Service_CAM, "called, camera_select=%u, width=%u, height=%u, crop_x0=%u, crop_y0=%u, "
788 "crop_x1=%u, crop_y1=%u, context_select=%u",
789 camera_select.m_val, resolution.width, resolution.height, resolution.crop_x0,
790 resolution.crop_y0, resolution.crop_x1, resolution.crop_y1, context_select.m_val);
791}
792
793void SetSize(Service::Interface* self) {
794 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1F, 3, 0);
795 const CameraSet camera_select(rp.Pop<u8>());
796 const u8 size = rp.Pop<u8>();
797 const ContextSet context_select(rp.Pop<u8>());
798
799 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
800 if (camera_select.IsValid() && context_select.IsValid()) {
801 for (int camera : camera_select) {
802 for (int context : context_select) {
803 cameras[camera].contexts[context].resolution = PRESET_RESOLUTION[size];
804 if (cameras[camera].current_context == context) {
805 cameras[camera].impl->SetResolution(PRESET_RESOLUTION[size]);
806 }
807 }
808 }
809 rb.Push(RESULT_SUCCESS);
810 } else {
811 LOG_ERROR(Service_CAM, "invalid camera_select=%u, context_select=%u", camera_select.m_val,
812 context_select.m_val);
813 rb.Push(ERROR_INVALID_ENUM_VALUE);
814 }
815
816 LOG_DEBUG(Service_CAM, "called, camera_select=%u, size=%u, context_select=%u",
817 camera_select.m_val, size, context_select.m_val);
818}
819
820void SetFrameRate(Service::Interface* self) {
821 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x20, 2, 0);
822 const CameraSet camera_select(rp.Pop<u8>());
823 const FrameRate frame_rate = static_cast<FrameRate>(rp.Pop<u8>());
824
825 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
826 if (camera_select.IsValid()) {
827 for (int camera : camera_select) {
828 cameras[camera].frame_rate = frame_rate;
829 // TODO(wwylele): consider hinting the actual camera with the expected frame rate
830 }
831 rb.Push(RESULT_SUCCESS);
832 } else {
833 LOG_ERROR(Service_CAM, "invalid camera_select=%u", camera_select.m_val);
834 rb.Push(ERROR_INVALID_ENUM_VALUE);
835 }
836
837 LOG_WARNING(Service_CAM, "(STUBBED) called, camera_select=%u, frame_rate=%d",
838 camera_select.m_val, static_cast<int>(frame_rate));
839}
840
841void SetEffect(Service::Interface* self) {
842 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x22, 3, 0);
843 const CameraSet camera_select(rp.Pop<u8>());
844 const Effect effect = static_cast<Effect>(rp.Pop<u8>());
845 const ContextSet context_select(rp.Pop<u8>());
846
847 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
848 if (camera_select.IsValid() && context_select.IsValid()) {
849 for (int camera : camera_select) {
850 for (int context : context_select) {
851 cameras[camera].contexts[context].effect = effect;
852 if (cameras[camera].current_context == context) {
853 cameras[camera].impl->SetEffect(effect);
854 }
855 }
856 }
857 rb.Push(RESULT_SUCCESS);
858 } else {
859 LOG_ERROR(Service_CAM, "invalid camera_select=%u, context_select=%u", camera_select.m_val,
860 context_select.m_val);
861 rb.Push(ERROR_INVALID_ENUM_VALUE);
862 }
863
864 LOG_DEBUG(Service_CAM, "called, camera_select=%u, effect=%d, context_select=%u",
865 camera_select.m_val, static_cast<int>(effect), context_select.m_val);
866}
867
868void SetOutputFormat(Service::Interface* self) {
869 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x25, 3, 0);
870 const CameraSet camera_select(rp.Pop<u8>());
871 const OutputFormat format = static_cast<OutputFormat>(rp.Pop<u8>());
872 const ContextSet context_select(rp.Pop<u8>());
873
874 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
875 if (camera_select.IsValid() && context_select.IsValid()) {
876 for (int camera : camera_select) {
877 for (int context : context_select) {
878 cameras[camera].contexts[context].format = format;
879 if (cameras[camera].current_context == context) {
880 cameras[camera].impl->SetFormat(format);
881 }
882 }
883 }
884 rb.Push(RESULT_SUCCESS);
885 } else {
886 LOG_ERROR(Service_CAM, "invalid camera_select=%u, context_select=%u", camera_select.m_val,
887 context_select.m_val);
888 rb.Push(ERROR_INVALID_ENUM_VALUE);
889 }
890
891 LOG_DEBUG(Service_CAM, "called, camera_select=%u, format=%d, context_select=%u",
892 camera_select.m_val, static_cast<int>(format), context_select.m_val);
893}
894
895void SynchronizeVsyncTiming(Service::Interface* self) {
896 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x29, 2, 0);
897 const u8 camera_select1 = rp.Pop<u8>();
898 const u8 camera_select2 = rp.Pop<u8>();
899
900 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
901 rb.Push(RESULT_SUCCESS);
902
903 LOG_WARNING(Service_CAM, "(STUBBED) called, camera_select1=%u, camera_select2=%u",
904 camera_select1, camera_select2);
905}
906
907void GetStereoCameraCalibrationData(Service::Interface* self) {
908 IPC::RequestBuilder rb =
909 IPC::RequestParser(Kernel::GetCommandBuffer(), 0x2B, 0, 0).MakeBuilder(17, 0);
910
911 // Default values taken from yuriks' 3DS. Valid data is required here or games using the
912 // calibration get stuck in an infinite CPU loop.
913 StereoCameraCalibrationData data = {};
914 data.isValidRotationXY = 0;
915 data.scale = 1.001776f;
916 data.rotationZ = 0.008322907f;
917 data.translationX = -87.70484f;
918 data.translationY = -7.640977f;
919 data.rotationX = 0.0f;
920 data.rotationY = 0.0f;
921 data.angleOfViewRight = 64.66875f;
922 data.angleOfViewLeft = 64.76067f;
923 data.distanceToChart = 250.0f;
924 data.distanceCameras = 35.0f;
925 data.imageWidth = 640;
926 data.imageHeight = 480;
927
928 rb.Push(RESULT_SUCCESS);
929 rb.PushRaw(data);
930
931 LOG_TRACE(Service_CAM, "called");
932}
933
934void SetPackageParameterWithoutContext(Service::Interface* self) {
935 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x33, 11, 0);
936
937 PackageParameterWithoutContext package;
938 rp.PopRaw(package);
939
940 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
941 rb.Push(RESULT_SUCCESS);
942
943 LOG_WARNING(Service_CAM, "(STUBBED) called");
944}
945
946template <typename PackageParameterType>
947static ResultCode SetPackageParameter(const PackageParameterType& package) {
948 const CameraSet camera_select(package.camera_select);
949 const ContextSet context_select(package.context_select);
950
951 if (camera_select.IsValid() && context_select.IsValid()) {
952 for (int camera_id : camera_select) {
953 CameraConfig& camera = cameras[camera_id];
954 for (int context_id : context_select) {
955 ContextConfig& context = camera.contexts[context_id];
956 context.effect = package.effect;
957 context.flip = package.flip;
958 context.resolution = package.GetResolution();
959 if (context_id == camera.current_context) {
960 camera.impl->SetEffect(context.effect);
961 camera.impl->SetFlip(context.flip);
962 camera.impl->SetResolution(context.resolution);
963 }
964 }
965 }
966 return RESULT_SUCCESS;
967 } else {
968 LOG_ERROR(Service_CAM, "invalid camera_select=%u, context_select=%u", package.camera_select,
969 package.context_select);
970 return ERROR_INVALID_ENUM_VALUE;
971 }
972}
973
974Resolution PackageParameterWithContext::GetResolution() const {
975 return PRESET_RESOLUTION[static_cast<int>(size)];
976}
977
978void SetPackageParameterWithContext(Service::Interface* self) {
979 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x34, 5, 0);
980
981 PackageParameterWithContext package;
982 rp.PopRaw(package);
983
984 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
985 ResultCode result = SetPackageParameter(package);
986 rb.Push(result);
987
988 LOG_DEBUG(Service_CAM, "called");
989}
990
991void SetPackageParameterWithContextDetail(Service::Interface* self) {
992 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x35, 7, 0);
993
994 PackageParameterWithContextDetail package;
995 rp.PopRaw(package);
996
997 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
998 ResultCode result = SetPackageParameter(package);
999 rb.Push(result);
1000
1001 LOG_DEBUG(Service_CAM, "called");
1002}
1003
1004void GetSuitableY2rStandardCoefficient(Service::Interface* self) {
1005 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x36, 0, 0);
1006 IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
1007 rb.Push(RESULT_SUCCESS);
1008 rb.Push<u32>(0);
1009
1010 LOG_WARNING(Service_CAM, "(STUBBED) called");
1011}
1012
1013void PlayShutterSound(Service::Interface* self) {
1014 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x38, 1, 0);
1015 u8 sound_id = rp.Pop<u8>();
1016
1017 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
1018 rb.Push(RESULT_SUCCESS);
1019
1020 LOG_WARNING(Service_CAM, "(STUBBED) called, sound_id=%d", sound_id);
1021}
1022
1023void DriverInitialize(Service::Interface* self) {
1024 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x39, 0, 0);
1025 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
1026
1027 for (int camera_id = 0; camera_id < NumCameras; ++camera_id) {
1028 CameraConfig& camera = cameras[camera_id];
1029 camera.current_context = 0;
1030 for (int context_id = 0; context_id < 2; ++context_id) {
1031 // Note: the following default values are verified against real 3DS
1032 ContextConfig& context = camera.contexts[context_id];
1033 context.flip = camera_id == 1 ? Flip::Horizontal : Flip::None;
1034 context.effect = Effect::None;
1035 context.format = OutputFormat::YUV422;
1036 context.resolution =
1037 context_id == 0 ? PRESET_RESOLUTION[5 /*DS_LCD*/] : PRESET_RESOLUTION[0 /*VGA*/];
1038 }
1039 camera.impl = Camera::CreateCamera(Settings::values.camera_name[camera_id],
1040 Settings::values.camera_config[camera_id]);
1041 camera.impl->SetFlip(camera.contexts[0].flip);
1042 camera.impl->SetEffect(camera.contexts[0].effect);
1043 camera.impl->SetFormat(camera.contexts[0].format);
1044 camera.impl->SetResolution(camera.contexts[0].resolution);
1045 }
1046
1047 for (PortConfig& port : ports) {
1048 port.Clear();
1049 }
1050
1051 rb.Push(RESULT_SUCCESS);
1052
1053 LOG_DEBUG(Service_CAM, "called");
1054}
1055
1056void DriverFinalize(Service::Interface* self) {
1057 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x3A, 0, 0);
1058 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
1059
1060 CancelReceiving(0);
1061 CancelReceiving(1);
1062
1063 for (CameraConfig& camera : cameras) {
1064 camera.impl = nullptr;
1065 }
1066
1067 rb.Push(RESULT_SUCCESS);
1068
1069 LOG_DEBUG(Service_CAM, "called");
1070}
1071
1072void Init() {
1073 using namespace Kernel;
1074
1075 AddService(new CAM_C_Interface);
1076 AddService(new CAM_Q_Interface);
1077 AddService(new CAM_S_Interface);
1078 AddService(new CAM_U_Interface);
1079
1080 for (PortConfig& port : ports) {
1081 port.completion_event = Event::Create(ResetType::Sticky, "CAM_U::completion_event");
1082 port.buffer_error_interrupt_event =
1083 Event::Create(ResetType::OneShot, "CAM_U::buffer_error_interrupt_event");
1084 port.vsync_interrupt_event =
1085 Event::Create(ResetType::OneShot, "CAM_U::vsync_interrupt_event");
1086 }
1087 completion_event_callback =
1088 CoreTiming::RegisterEvent("CAM_U::CompletionEventCallBack", CompletionEventCallBack);
1089}
1090
1091void Shutdown() {
1092 CancelReceiving(0);
1093 CancelReceiving(1);
1094 for (PortConfig& port : ports) {
1095 port.completion_event = nullptr;
1096 port.buffer_error_interrupt_event = nullptr;
1097 port.vsync_interrupt_event = nullptr;
1098 }
1099 for (CameraConfig& camera : cameras) {
1100 camera.impl = nullptr;
1101 }
1102}
1103
1104} // namespace CAM
1105
1106} // namespace Service
diff --git a/src/core/hle/service/cam/cam.h b/src/core/hle/service/cam/cam.h
deleted file mode 100644
index b6da721d8..000000000
--- a/src/core/hle/service/cam/cam.h
+++ /dev/null
@@ -1,686 +0,0 @@
1// Copyright 2015 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 "common/common_funcs.h"
8#include "common/common_types.h"
9#include "common/swap.h"
10#include "core/hle/kernel/kernel.h"
11#include "core/hle/service/service.h"
12
13namespace Service {
14namespace CAM {
15
16enum CameraIndex {
17 OuterRightCamera = 0,
18 InnerCamera = 1,
19 OuterLeftCamera = 2,
20
21 NumCameras = 3,
22};
23
24enum class Effect : u8 {
25 None = 0,
26 Mono = 1,
27 Sepia = 2,
28 Negative = 3,
29 Negafilm = 4,
30 Sepia01 = 5,
31};
32
33enum class Flip : u8 {
34 None = 0,
35 Horizontal = 1,
36 Vertical = 2,
37 Reverse = 3,
38};
39
40enum class Size : u8 {
41 VGA = 0,
42 QVGA = 1,
43 QQVGA = 2,
44 CIF = 3,
45 QCIF = 4,
46 DS_LCD = 5,
47 DS_LCDx4 = 6,
48 CTR_TOP_LCD = 7,
49 CTR_BOTTOM_LCD = QVGA,
50};
51
52enum class FrameRate : u8 {
53 Rate_15 = 0,
54 Rate_15_To_5 = 1,
55 Rate_15_To_2 = 2,
56 Rate_10 = 3,
57 Rate_8_5 = 4,
58 Rate_5 = 5,
59 Rate_20 = 6,
60 Rate_20_To_5 = 7,
61 Rate_30 = 8,
62 Rate_30_To_5 = 9,
63 Rate_15_To_10 = 10,
64 Rate_20_To_10 = 11,
65 Rate_30_To_10 = 12,
66};
67
68enum class ShutterSoundType : u8 {
69 Normal = 0,
70 Movie = 1,
71 MovieEnd = 2,
72};
73
74enum class WhiteBalance : u8 {
75 BalanceAuto = 0,
76 Balance3200K = 1,
77 Balance4150K = 2,
78 Balance5200K = 3,
79 Balance6000K = 4,
80 Balance7000K = 5,
81 BalanceMax = 6,
82 BalanceNormal = BalanceAuto,
83 BalanceTungsten = Balance3200K,
84 BalanceWhiteFluorescentLight = Balance4150K,
85 BalanceDaylight = Balance5200K,
86 BalanceCloudy = Balance6000K,
87 BalanceHorizon = Balance6000K,
88 BalanceShade = Balance7000K,
89};
90
91enum class PhotoMode : u8 {
92 Normal = 0,
93 Portrait = 1,
94 Landscape = 2,
95 Nightview = 3,
96 Letter0 = 4,
97};
98
99enum class LensCorrection : u8 {
100 Off = 0,
101 On70 = 1,
102 On90 = 2,
103 Dark = Off,
104 Normal = On70,
105 Bright = On90,
106};
107
108enum class Contrast : u8 {
109 Pattern01 = 1,
110 Pattern02 = 2,
111 Pattern03 = 3,
112 Pattern04 = 4,
113 Pattern05 = 5,
114 Pattern06 = 6,
115 Pattern07 = 7,
116 Pattern08 = 8,
117 Pattern09 = 9,
118 Pattern10 = 10,
119 Pattern11 = 11,
120 Low = Pattern05,
121 Normal = Pattern06,
122 High = Pattern07,
123};
124
125enum class OutputFormat : u8 {
126 YUV422 = 0,
127 RGB565 = 1,
128};
129
130/// Stereo camera calibration data.
131struct StereoCameraCalibrationData {
132 u8 isValidRotationXY; ///< Bool indicating whether the X and Y rotation data is valid.
133 INSERT_PADDING_BYTES(3);
134 float_le scale; ///< Scale to match the left camera image with the right.
135 float_le rotationZ; ///< Z axis rotation to match the left camera image with the right.
136 float_le translationX; ///< X axis translation to match the left camera image with the right.
137 float_le translationY; ///< Y axis translation to match the left camera image with the right.
138 float_le rotationX; ///< X axis rotation to match the left camera image with the right.
139 float_le rotationY; ///< Y axis rotation to match the left camera image with the right.
140 float_le angleOfViewRight; ///< Right camera angle of view.
141 float_le angleOfViewLeft; ///< Left camera angle of view.
142 float_le distanceToChart; ///< Distance between cameras and measurement chart.
143 float_le distanceCameras; ///< Distance between left and right cameras.
144 s16_le imageWidth; ///< Image width.
145 s16_le imageHeight; ///< Image height.
146 INSERT_PADDING_BYTES(16);
147};
148static_assert(sizeof(StereoCameraCalibrationData) == 64,
149 "StereoCameraCalibrationData structure size is wrong");
150
151/**
152 * Resolution parameters for the camera.
153 * The native resolution of 3DS camera is 640 * 480. The captured image will be cropped in the
154 * region [crop_x0, crop_x1] * [crop_y0, crop_y1], and then scaled to size width * height as the
155 * output image. Note that all cropping coordinates are inclusive.
156 */
157struct Resolution {
158 u16 width;
159 u16 height;
160 u16 crop_x0;
161 u16 crop_y0;
162 u16 crop_x1;
163 u16 crop_y1;
164};
165
166struct PackageParameterWithoutContext {
167 u8 camera_select;
168 s8 exposure;
169 WhiteBalance white_balance;
170 s8 sharpness;
171 bool auto_exposure;
172 bool auto_white_balance;
173 FrameRate frame_rate;
174 PhotoMode photo_mode;
175 Contrast contrast;
176 LensCorrection lens_correction;
177 bool noise_filter;
178 u8 padding;
179 s16 auto_exposure_window_x;
180 s16 auto_exposure_window_y;
181 s16 auto_exposure_window_width;
182 s16 auto_exposure_window_height;
183 s16 auto_white_balance_window_x;
184 s16 auto_white_balance_window_y;
185 s16 auto_white_balance_window_width;
186 s16 auto_white_balance_window_height;
187 INSERT_PADDING_WORDS(4);
188};
189
190static_assert(sizeof(PackageParameterWithoutContext) == 44,
191 "PackageParameterCameraWithoutContext structure size is wrong");
192
193struct PackageParameterWithContext {
194 u8 camera_select;
195 u8 context_select;
196 Flip flip;
197 Effect effect;
198 Size size;
199 INSERT_PADDING_BYTES(3);
200 INSERT_PADDING_WORDS(3);
201
202 Resolution GetResolution() const;
203};
204
205static_assert(sizeof(PackageParameterWithContext) == 20,
206 "PackageParameterWithContext structure size is wrong");
207
208struct PackageParameterWithContextDetail {
209 u8 camera_select;
210 u8 context_select;
211 Flip flip;
212 Effect effect;
213 Resolution resolution;
214 INSERT_PADDING_WORDS(3);
215
216 Resolution GetResolution() const {
217 return resolution;
218 }
219};
220
221static_assert(sizeof(PackageParameterWithContextDetail) == 28,
222 "PackageParameterWithContextDetail structure size is wrong");
223
224/**
225 * Starts capturing at the selected port.
226 * Inputs:
227 * 0: 0x00010040
228 * 1: u8 selected port
229 * Outputs:
230 * 0: 0x00010040
231 * 1: ResultCode
232 */
233void StartCapture(Service::Interface* self);
234
235/**
236 * Stops capturing from the selected port.
237 * Inputs:
238 * 0: 0x00020040
239 * 1: u8 selected port
240 * Outputs:
241 * 0: 0x00020040
242 * 1: ResultCode
243 */
244void StopCapture(Service::Interface* self);
245
246/**
247 * Gets whether the selected port is currently capturing.
248 * Inputs:
249 * 0: 0x00030040
250 * 1: u8 selected port
251 * Outputs:
252 * 0: 0x00030080
253 * 1: ResultCode
254 * 2: 0 if not capturing, 1 if capturing
255 */
256void IsBusy(Service::Interface* self);
257
258/**
259 * Clears the buffer of selected ports.
260 * Inputs:
261 * 0: 0x00040040
262 * 1: u8 selected port
263 * Outputs:
264 * 0: 0x00040040
265 * 2: ResultCode
266 */
267void ClearBuffer(Service::Interface* self);
268
269/**
270 * Unknown
271 * Inputs:
272 * 0: 0x00050040
273 * 1: u8 selected port
274 * Outputs:
275 * 0: 0x00050042
276 * 1: ResultCode
277 * 2: Descriptor: Handle
278 * 3: Event handle
279 */
280void GetVsyncInterruptEvent(Service::Interface* self);
281
282/**
283 * Unknown
284 * Inputs:
285 * 0: 0x00060040
286 * 1: u8 selected port
287 * Outputs:
288 * 0: 0x00060042
289 * 1: ResultCode
290 * 2: Descriptor: Handle
291 * 3: Event handle
292 */
293void GetBufferErrorInterruptEvent(Service::Interface* self);
294
295/**
296 * Sets the target buffer to receive a frame of image data and starts the transfer. Each camera
297 * port has its own event to signal the end of the transfer.
298 *
299 * Inputs:
300 * 0: 0x00070102
301 * 1: Destination address in calling process
302 * 2: u8 selected port
303 * 3: Image size (in bytes)
304 * 4: u16 Transfer unit size (in bytes)
305 * 5: Descriptor: Handle
306 * 6: Handle to destination process
307 * Outputs:
308 * 0: 0x00070042
309 * 1: ResultCode
310 * 2: Descriptor: Handle
311 * 3: Handle to event signalled when transfer finishes
312 */
313void SetReceiving(Service::Interface* self);
314
315/**
316 * Gets whether the selected port finished receiving a frame.
317 * Inputs:
318 * 0: 0x00080040
319 * 1: u8 selected port
320 * Outputs:
321 * 0: 0x00080080
322 * 1: ResultCode
323 * 2: 0 if not finished, 1 if finished
324 */
325void IsFinishedReceiving(Service::Interface* self);
326
327/**
328 * Sets the number of lines the buffer contains.
329 * Inputs:
330 * 0: 0x00090100
331 * 1: u8 selected port
332 * 2: u16 Number of lines to transfer
333 * 3: u16 Width
334 * 4: u16 Height
335 * Outputs:
336 * 0: 0x00090040
337 * 1: ResultCode
338 * @todo figure out how the "buffer" actually works.
339 */
340void SetTransferLines(Service::Interface* self);
341
342/**
343 * Gets the maximum number of lines that fit in the buffer
344 * Inputs:
345 * 0: 0x000A0080
346 * 1: u16 Width
347 * 2: u16 Height
348 * Outputs:
349 * 0: 0x000A0080
350 * 1: ResultCode
351 * 2: Maximum number of lines that fit in the buffer
352 * @todo figure out how the "buffer" actually works.
353 */
354void GetMaxLines(Service::Interface* self);
355
356/**
357 * Sets the number of bytes the buffer contains.
358 * Inputs:
359 * 0: 0x000B0100
360 * 1: u8 selected port
361 * 2: u16 Number of bytes to transfer
362 * 3: u16 Width
363 * 4: u16 Height
364 * Outputs:
365 * 0: 0x000B0040
366 * 1: ResultCode
367 * @todo figure out how the "buffer" actually works.
368 */
369void SetTransferBytes(Service::Interface* self);
370
371/**
372 * Gets the number of bytes to the buffer contains.
373 * Inputs:
374 * 0: 0x000C0040
375 * 1: u8 selected port
376 * Outputs:
377 * 0: 0x000C0080
378 * 1: ResultCode
379 * 2: The number of bytes the buffer contains
380 * @todo figure out how the "buffer" actually works.
381 */
382void GetTransferBytes(Service::Interface* self);
383
384/**
385 * Gets the maximum number of bytes that fit in the buffer.
386 * Inputs:
387 * 0: 0x000D0080
388 * 1: u16 Width
389 * 2: u16 Height
390 * Outputs:
391 * 0: 0x000D0080
392 * 1: ResultCode
393 * 2: Maximum number of bytes that fit in the buffer
394 * @todo figure out how the "buffer" actually works.
395 */
396void GetMaxBytes(Service::Interface* self);
397
398/**
399 * Enables or disables trimming.
400 * Inputs:
401 * 0: 0x000E0080
402 * 1: u8 selected port
403 * 2: u8 bool Enable trimming if true
404 * Outputs:
405 * 0: 0x000E0040
406 * 1: ResultCode
407 */
408void SetTrimming(Service::Interface* self);
409
410/**
411 * Gets whether trimming is enabled.
412 * Inputs:
413 * 0: 0x000F0040
414 * 1: u8 selected port
415 * Outputs:
416 * 0: 0x000F0080
417 * 1: ResultCode
418 * 2: u8 bool Enable trimming if true
419 */
420void IsTrimming(Service::Interface* self);
421
422/**
423 * Sets the position to trim.
424 * Inputs:
425 * 0: 0x00100140
426 * 1: u8 selected port
427 * 2: x start
428 * 3: y start
429 * 4: x end (exclusive)
430 * 5: y end (exclusive)
431 * Outputs:
432 * 0: 0x00100040
433 * 1: ResultCode
434 */
435void SetTrimmingParams(Service::Interface* self);
436
437/**
438 * Gets the position to trim.
439 * Inputs:
440 * 0: 0x00110040
441 * 1: u8 selected port
442 *
443 * Outputs:
444 * 0: 0x00110140
445 * 1: ResultCode
446 * 2: x start
447 * 3: y start
448 * 4: x end (exclusive)
449 * 5: y end (exclusive)
450 */
451void GetTrimmingParams(Service::Interface* self);
452
453/**
454 * Sets the position to trim by giving the width and height. The trimming window is always at the
455 * center.
456 * Inputs:
457 * 0: 0x00120140
458 * 1: u8 selected port
459 * 2: s16 Trim width
460 * 3: s16 Trim height
461 * 4: s16 Camera width
462 * 5: s16 Camera height
463 * Outputs:
464 * 0: 0x00120040
465 * 1: ResultCode
466 */
467void SetTrimmingParamsCenter(Service::Interface* self);
468
469/**
470 * Selects up to two physical cameras to enable.
471 * Inputs:
472 * 0: 0x00130040
473 * 1: u8 selected camera
474 * Outputs:
475 * 0: 0x00130040
476 * 1: ResultCode
477 */
478void Activate(Service::Interface* self);
479
480/**
481 * Switches the context of camera settings.
482 * Inputs:
483 * 0: 0x00140080
484 * 1: u8 selected camera
485 * 2: u8 selected context
486 * Outputs:
487 * 0: 0x00140040
488 * 1: ResultCode
489 */
490void SwitchContext(Service::Interface* self);
491
492/**
493 * Sets flipping of images
494 * Inputs:
495 * 0: 0x001D00C0
496 * 1: u8 selected camera
497 * 2: u8 Type of flipping to perform (`Flip` enum)
498 * 3: u8 selected context
499 * Outputs:
500 * 0: 0x001D0040
501 * 1: ResultCode
502 */
503void FlipImage(Service::Interface* self);
504
505/**
506 * Sets camera resolution from custom parameters. For more details see the Resolution struct.
507 * Inputs:
508 * 0: 0x001E0200
509 * 1: u8 selected camera
510 * 2: width
511 * 3: height
512 * 4: crop x0
513 * 5: crop y0
514 * 6: crop x1
515 * 7: crop y1
516 * 8: u8 selected context
517 * Outputs:
518 * 0: 0x001E0040
519 * 1: ResultCode
520 */
521void SetDetailSize(Service::Interface* self);
522
523/**
524 * Sets camera resolution from preset resolution parameters.
525 * Inputs:
526 * 0: 0x001F00C0
527 * 1: u8 selected camera
528 * 2: u8 Camera frame resolution (`Size` enum)
529 * 3: u8 selected context
530 * Outputs:
531 * 0: 0x001F0040
532 * 1: ResultCode
533 */
534void SetSize(Service::Interface* self);
535
536/**
537 * Sets camera framerate.
538 * Inputs:
539 * 0: 0x00200080
540 * 1: u8 selected camera
541 * 2: u8 Camera framerate (`FrameRate` enum)
542 * Outputs:
543 * 0: 0x00200040
544 * 1: ResultCode
545 */
546void SetFrameRate(Service::Interface* self);
547
548/**
549 * Sets effect on the output image
550 * Inputs:
551 * 0: 0x002200C0
552 * 1: u8 selected camera
553 * 2: u8 image effect (`Effect` enum)
554 * 3: u8 selected context
555 * Outputs:
556 * 0: 0x00220040
557 * 1: ResultCode
558 */
559void SetEffect(Service::Interface* self);
560
561/**
562 * Sets format of the output image
563 * Inputs:
564 * 0: 0x002500C0
565 * 1: u8 selected camera
566 * 2: u8 image format (`OutputFormat` enum)
567 * 3: u8 selected context
568 * Outputs:
569 * 0: 0x00250040
570 * 1: ResultCode
571 */
572void SetOutputFormat(Service::Interface* self);
573
574/**
575 * Synchronizes the V-Sync timing of two cameras.
576 * Inputs:
577 * 0: 0x00290080
578 * 1: u8 selected camera 1
579 * 2: u8 selected camera 2
580 * Outputs:
581 * 0: 0x00280040
582 * 1: ResultCode
583 */
584void SynchronizeVsyncTiming(Service::Interface* self);
585
586/**
587 * Returns calibration data relating the outside cameras to eachother, for use in AR applications.
588 *
589 * Inputs:
590 * 0: 0x002B0000
591 * Outputs:
592 * 0: 0x002B0440
593 * 1: ResultCode
594 * 2-17: `StereoCameraCalibrationData` structure with calibration values
595 */
596void GetStereoCameraCalibrationData(Service::Interface* self);
597
598/**
599 * Batch-configures context-free settings.
600 *
601 * Inputs:
602 * 0: 0x003302C0
603 * 1-7: struct PachageParameterWithoutContext
604 * 8-11: unused
605 * Outputs:
606 * 0: 0x00330040
607 * 1: ResultCode
608 */
609void SetPackageParameterWithoutContext(Service::Interface* self);
610
611/**
612 * Batch-configures context-related settings with preset resolution parameters.
613 *
614 * Inputs:
615 * 0: 0x00340140
616 * 1-2: struct PackageParameterWithContext
617 * 3-5: unused
618 * Outputs:
619 * 0: 0x00340040
620 * 1: ResultCode
621 */
622void SetPackageParameterWithContext(Service::Interface* self);
623
624/**
625 * Batch-configures context-related settings with custom resolution parameters
626 *
627 * Inputs:
628 * 0: 0x003501C0
629 * 1-4: struct PackageParameterWithContextDetail
630 * 5-7: unused
631 * Outputs:
632 * 0: 0x00350040
633 * 1: ResultCode
634 */
635void SetPackageParameterWithContextDetail(Service::Interface* self);
636
637/**
638 * Unknown
639 * Inputs:
640 * 0: 0x00360000
641 * Outputs:
642 * 0: 0x00360080
643 * 1: ResultCode
644 * 2: ?
645 */
646void GetSuitableY2rStandardCoefficient(Service::Interface* self);
647
648/**
649 * Unknown
650 * Inputs:
651 * 0: 0x00380040
652 * 1: u8 Sound ID
653 * Outputs:
654 * 0: 0x00380040
655 * 1: ResultCode
656 */
657void PlayShutterSound(Service::Interface* self);
658
659/**
660 * Initializes the camera driver. Must be called before using other functions.
661 * Inputs:
662 * 0: 0x00390000
663 * Outputs:
664 * 0: 0x00390040
665 * 1: ResultCode
666 */
667void DriverInitialize(Service::Interface* self);
668
669/**
670 * Shuts down the camera driver.
671 * Inputs:
672 * 0: 0x003A0000
673 * Outputs:
674 * 0: 0x003A0040
675 * 1: ResultCode
676 */
677void DriverFinalize(Service::Interface* self);
678
679/// Initialize CAM service(s)
680void Init();
681
682/// Shutdown CAM service(s)
683void Shutdown();
684
685} // namespace CAM
686} // namespace Service
diff --git a/src/core/hle/service/cam/cam_c.cpp b/src/core/hle/service/cam/cam_c.cpp
deleted file mode 100644
index 93b047c1a..000000000
--- a/src/core/hle/service/cam/cam_c.cpp
+++ /dev/null
@@ -1,18 +0,0 @@
1// Copyright 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/cam/cam_c.h"
6
7namespace Service {
8namespace CAM {
9
10// Empty arrays are illegal -- commented out until an entry is added.
11// const Interface::FunctionInfo FunctionTable[] = { };
12
13CAM_C_Interface::CAM_C_Interface() {
14 // Register(FunctionTable);
15}
16
17} // namespace CAM
18} // namespace Service
diff --git a/src/core/hle/service/cam/cam_c.h b/src/core/hle/service/cam/cam_c.h
deleted file mode 100644
index 6b296c00d..000000000
--- a/src/core/hle/service/cam/cam_c.h
+++ /dev/null
@@ -1,22 +0,0 @@
1// Copyright 2015 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 "core/hle/service/service.h"
8
9namespace Service {
10namespace CAM {
11
12class CAM_C_Interface : public Service::Interface {
13public:
14 CAM_C_Interface();
15
16 std::string GetPortName() const override {
17 return "cam:c";
18 }
19};
20
21} // namespace CAM
22} // namespace Service
diff --git a/src/core/hle/service/cam/cam_q.cpp b/src/core/hle/service/cam/cam_q.cpp
deleted file mode 100644
index 2ba853606..000000000
--- a/src/core/hle/service/cam/cam_q.cpp
+++ /dev/null
@@ -1,18 +0,0 @@
1// Copyright 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/cam/cam_q.h"
6
7namespace Service {
8namespace CAM {
9
10// Empty arrays are illegal -- commented out until an entry is added.
11// const Interface::FunctionInfo FunctionTable[] = { };
12
13CAM_Q_Interface::CAM_Q_Interface() {
14 // Register(FunctionTable);
15}
16
17} // namespace CAM
18} // namespace Service
diff --git a/src/core/hle/service/cam/cam_q.h b/src/core/hle/service/cam/cam_q.h
deleted file mode 100644
index 07cc12534..000000000
--- a/src/core/hle/service/cam/cam_q.h
+++ /dev/null
@@ -1,22 +0,0 @@
1// Copyright 2015 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 "core/hle/service/service.h"
8
9namespace Service {
10namespace CAM {
11
12class CAM_Q_Interface : public Service::Interface {
13public:
14 CAM_Q_Interface();
15
16 std::string GetPortName() const override {
17 return "cam:q";
18 }
19};
20
21} // namespace CAM
22} // namespace Service
diff --git a/src/core/hle/service/cam/cam_s.cpp b/src/core/hle/service/cam/cam_s.cpp
deleted file mode 100644
index f1c6da587..000000000
--- a/src/core/hle/service/cam/cam_s.cpp
+++ /dev/null
@@ -1,18 +0,0 @@
1// Copyright 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/cam/cam_s.h"
6
7namespace Service {
8namespace CAM {
9
10// Empty arrays are illegal -- commented out until an entry is added.
11// const Interface::FunctionInfo FunctionTable[] = { };
12
13CAM_S_Interface::CAM_S_Interface() {
14 // Register(FunctionTable);
15}
16
17} // namespace CAM
18} // namespace Service
diff --git a/src/core/hle/service/cam/cam_s.h b/src/core/hle/service/cam/cam_s.h
deleted file mode 100644
index 0a5d6fca2..000000000
--- a/src/core/hle/service/cam/cam_s.h
+++ /dev/null
@@ -1,22 +0,0 @@
1// Copyright 2015 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 "core/hle/service/service.h"
8
9namespace Service {
10namespace CAM {
11
12class CAM_S_Interface : public Service::Interface {
13public:
14 CAM_S_Interface();
15
16 std::string GetPortName() const override {
17 return "cam:s";
18 }
19};
20
21} // namespace CAM
22} // namespace Service
diff --git a/src/core/hle/service/cam/cam_u.cpp b/src/core/hle/service/cam/cam_u.cpp
deleted file mode 100644
index 251c1e6d4..000000000
--- a/src/core/hle/service/cam/cam_u.cpp
+++ /dev/null
@@ -1,81 +0,0 @@
1// Copyright 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/cam/cam.h"
6#include "core/hle/service/cam/cam_u.h"
7
8namespace Service {
9namespace CAM {
10
11const Interface::FunctionInfo FunctionTable[] = {
12 {0x00010040, StartCapture, "StartCapture"},
13 {0x00020040, StopCapture, "StopCapture"},
14 {0x00030040, IsBusy, "IsBusy"},
15 {0x00040040, ClearBuffer, "ClearBuffer"},
16 {0x00050040, GetVsyncInterruptEvent, "GetVsyncInterruptEvent"},
17 {0x00060040, GetBufferErrorInterruptEvent, "GetBufferErrorInterruptEvent"},
18 {0x00070102, SetReceiving, "SetReceiving"},
19 {0x00080040, IsFinishedReceiving, "IsFinishedReceiving"},
20 {0x00090100, SetTransferLines, "SetTransferLines"},
21 {0x000A0080, GetMaxLines, "GetMaxLines"},
22 {0x000B0100, SetTransferBytes, "SetTransferBytes"},
23 {0x000C0040, GetTransferBytes, "GetTransferBytes"},
24 {0x000D0080, GetMaxBytes, "GetMaxBytes"},
25 {0x000E0080, SetTrimming, "SetTrimming"},
26 {0x000F0040, IsTrimming, "IsTrimming"},
27 {0x00100140, SetTrimmingParams, "SetTrimmingParams"},
28 {0x00110040, GetTrimmingParams, "GetTrimmingParams"},
29 {0x00120140, SetTrimmingParamsCenter, "SetTrimmingParamsCenter"},
30 {0x00130040, Activate, "Activate"},
31 {0x00140080, SwitchContext, "SwitchContext"},
32 {0x00150080, nullptr, "SetExposure"},
33 {0x00160080, nullptr, "SetWhiteBalance"},
34 {0x00170080, nullptr, "SetWhiteBalanceWithoutBaseUp"},
35 {0x00180080, nullptr, "SetSharpness"},
36 {0x00190080, nullptr, "SetAutoExposure"},
37 {0x001A0040, nullptr, "IsAutoExposure"},
38 {0x001B0080, nullptr, "SetAutoWhiteBalance"},
39 {0x001C0040, nullptr, "IsAutoWhiteBalance"},
40 {0x001D00C0, FlipImage, "FlipImage"},
41 {0x001E0200, SetDetailSize, "SetDetailSize"},
42 {0x001F00C0, SetSize, "SetSize"},
43 {0x00200080, SetFrameRate, "SetFrameRate"},
44 {0x00210080, nullptr, "SetPhotoMode"},
45 {0x002200C0, SetEffect, "SetEffect"},
46 {0x00230080, nullptr, "SetContrast"},
47 {0x00240080, nullptr, "SetLensCorrection"},
48 {0x002500C0, SetOutputFormat, "SetOutputFormat"},
49 {0x00260140, nullptr, "SetAutoExposureWindow"},
50 {0x00270140, nullptr, "SetAutoWhiteBalanceWindow"},
51 {0x00280080, nullptr, "SetNoiseFilter"},
52 {0x00290080, SynchronizeVsyncTiming, "SynchronizeVsyncTiming"},
53 {0x002A0080, nullptr, "GetLatestVsyncTiming"},
54 {0x002B0000, GetStereoCameraCalibrationData, "GetStereoCameraCalibrationData"},
55 {0x002C0400, nullptr, "SetStereoCameraCalibrationData"},
56 {0x002D00C0, nullptr, "WriteRegisterI2c"},
57 {0x002E00C0, nullptr, "WriteMcuVariableI2c"},
58 {0x002F0080, nullptr, "ReadRegisterI2cExclusive"},
59 {0x00300080, nullptr, "ReadMcuVariableI2cExclusive"},
60 {0x00310180, nullptr, "SetImageQualityCalibrationData"},
61 {0x00320000, nullptr, "GetImageQualityCalibrationData"},
62 {0x003302C0, SetPackageParameterWithoutContext, "SetPackageParameterWithoutContext"},
63 {0x00340140, SetPackageParameterWithContext, "SetPackageParameterWithContext"},
64 {0x003501C0, SetPackageParameterWithContextDetail, "SetPackageParameterWithContextDetail"},
65 {0x00360000, GetSuitableY2rStandardCoefficient, "GetSuitableY2rStandardCoefficient"},
66 {0x00370202, nullptr, "PlayShutterSoundWithWave"},
67 {0x00380040, PlayShutterSound, "PlayShutterSound"},
68 {0x00390000, DriverInitialize, "DriverInitialize"},
69 {0x003A0000, DriverFinalize, "DriverFinalize"},
70 {0x003B0000, nullptr, "GetActivatedCamera"},
71 {0x003C0000, nullptr, "GetSleepCamera"},
72 {0x003D0040, nullptr, "SetSleepCamera"},
73 {0x003E0040, nullptr, "SetBrightnessSynchronization"},
74};
75
76CAM_U_Interface::CAM_U_Interface() {
77 Register(FunctionTable);
78}
79
80} // namespace CAM
81} // namespace Service
diff --git a/src/core/hle/service/cam/cam_u.h b/src/core/hle/service/cam/cam_u.h
deleted file mode 100644
index 369264037..000000000
--- a/src/core/hle/service/cam/cam_u.h
+++ /dev/null
@@ -1,22 +0,0 @@
1// Copyright 2015 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 "core/hle/service/service.h"
8
9namespace Service {
10namespace CAM {
11
12class CAM_U_Interface : public Service::Interface {
13public:
14 CAM_U_Interface();
15
16 std::string GetPortName() const override {
17 return "cam:u";
18 }
19};
20
21} // namespace CAM
22} // namespace Service
diff --git a/src/core/hle/service/cecd/cecd.cpp b/src/core/hle/service/cecd/cecd.cpp
deleted file mode 100644
index 421006a9e..000000000
--- a/src/core/hle/service/cecd/cecd.cpp
+++ /dev/null
@@ -1,66 +0,0 @@
1// Copyright 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/logging/log.h"
6#include "core/hle/ipc.h"
7#include "core/hle/kernel/event.h"
8#include "core/hle/kernel/handle_table.h"
9#include "core/hle/result.h"
10#include "core/hle/service/cecd/cecd.h"
11#include "core/hle/service/cecd/cecd_ndm.h"
12#include "core/hle/service/cecd/cecd_s.h"
13#include "core/hle/service/cecd/cecd_u.h"
14#include "core/hle/service/service.h"
15
16namespace Service {
17namespace CECD {
18
19static Kernel::SharedPtr<Kernel::Event> cecinfo_event;
20static Kernel::SharedPtr<Kernel::Event> change_state_event;
21
22void GetCecStateAbbreviated(Service::Interface* self) {
23 u32* cmd_buff = Kernel::GetCommandBuffer();
24
25 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
26 cmd_buff[2] = static_cast<u32>(CecStateAbbreviated::CEC_STATE_ABBREV_IDLE);
27
28 LOG_WARNING(Service_CECD, "(STUBBED) called");
29}
30
31void GetCecInfoEventHandle(Service::Interface* self) {
32 u32* cmd_buff = Kernel::GetCommandBuffer();
33
34 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
35 cmd_buff[3] = Kernel::g_handle_table.Create(cecinfo_event).Unwrap(); // Event handle
36
37 LOG_WARNING(Service_CECD, "(STUBBED) called");
38}
39
40void GetChangeStateEventHandle(Service::Interface* self) {
41 u32* cmd_buff = Kernel::GetCommandBuffer();
42
43 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
44 cmd_buff[3] = Kernel::g_handle_table.Create(change_state_event).Unwrap(); // Event handle
45
46 LOG_WARNING(Service_CECD, "(STUBBED) called");
47}
48
49void Init() {
50 AddService(new CECD_NDM);
51 AddService(new CECD_S);
52 AddService(new CECD_U);
53
54 cecinfo_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "CECD::cecinfo_event");
55 change_state_event =
56 Kernel::Event::Create(Kernel::ResetType::OneShot, "CECD::change_state_event");
57}
58
59void Shutdown() {
60 cecinfo_event = nullptr;
61 change_state_event = nullptr;
62}
63
64} // namespace CECD
65
66} // namespace Service
diff --git a/src/core/hle/service/cecd/cecd.h b/src/core/hle/service/cecd/cecd.h
deleted file mode 100644
index ea97f9266..000000000
--- a/src/core/hle/service/cecd/cecd.h
+++ /dev/null
@@ -1,60 +0,0 @@
1// Copyright 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7namespace Service {
8
9class Interface;
10
11namespace CECD {
12
13enum class CecStateAbbreviated {
14 CEC_STATE_ABBREV_IDLE = 1, ///< Corresponds to CEC_STATE_IDLE
15 CEC_STATE_ABBREV_NOT_LOCAL = 2, ///< Corresponds to CEC_STATEs *FINISH*, *POST, and OVER_BOSS
16 CEC_STATE_ABBREV_SCANNING = 3, ///< Corresponds to CEC_STATE_SCANNING
17 CEC_STATE_ABBREV_WLREADY =
18 4, ///< Corresponds to CEC_STATE_WIRELESS_READY when some unknown bool is true
19 CEC_STATE_ABBREV_OTHER = 5, ///< Corresponds to CEC_STATEs besides *FINISH*, *POST, and
20 /// OVER_BOSS and those listed here
21};
22
23/**
24 * GetCecStateAbbreviated service function
25 * Inputs:
26 * 0: 0x000E0000
27 * Outputs:
28 * 1: ResultCode
29 * 2: CecStateAbbreviated
30 */
31void GetCecStateAbbreviated(Service::Interface* self);
32
33/**
34 * GetCecInfoEventHandle service function
35 * Inputs:
36 * 0: 0x000F0000
37 * Outputs:
38 * 1: ResultCode
39 * 3: Event Handle
40 */
41void GetCecInfoEventHandle(Service::Interface* self);
42
43/**
44 * GetChangeStateEventHandle service function
45 * Inputs:
46 * 0: 0x00100000
47 * Outputs:
48 * 1: ResultCode
49 * 3: Event Handle
50 */
51void GetChangeStateEventHandle(Service::Interface* self);
52
53/// Initialize CECD service(s)
54void Init();
55
56/// Shutdown CECD service(s)
57void Shutdown();
58
59} // namespace CECD
60} // namespace Service
diff --git a/src/core/hle/service/cecd/cecd_ndm.cpp b/src/core/hle/service/cecd/cecd_ndm.cpp
deleted file mode 100644
index 7baf93750..000000000
--- a/src/core/hle/service/cecd/cecd_ndm.cpp
+++ /dev/null
@@ -1,23 +0,0 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/cecd/cecd.h"
6#include "core/hle/service/cecd/cecd_ndm.h"
7
8namespace Service {
9namespace CECD {
10
11static const Interface::FunctionInfo FunctionTable[] = {
12 {0x00010000, nullptr, "Initialize"},
13 {0x00020000, nullptr, "Deinitialize"},
14 {0x00030000, nullptr, "ResumeDaemon"},
15 {0x00040040, nullptr, "SuspendDaemon"},
16};
17
18CECD_NDM::CECD_NDM() {
19 Register(FunctionTable);
20}
21
22} // namespace CECD
23} // namespace Service
diff --git a/src/core/hle/service/cecd/cecd_ndm.h b/src/core/hle/service/cecd/cecd_ndm.h
deleted file mode 100644
index 2e2e50ada..000000000
--- a/src/core/hle/service/cecd/cecd_ndm.h
+++ /dev/null
@@ -1,22 +0,0 @@
1// Copyright 2016 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 "core/hle/service/service.h"
8
9namespace Service {
10namespace CECD {
11
12class CECD_NDM : public Interface {
13public:
14 CECD_NDM();
15
16 std::string GetPortName() const override {
17 return "cecd:ndm";
18 }
19};
20
21} // namespace CECD
22} // namespace Service
diff --git a/src/core/hle/service/cecd/cecd_s.cpp b/src/core/hle/service/cecd/cecd_s.cpp
deleted file mode 100644
index eacda7d41..000000000
--- a/src/core/hle/service/cecd/cecd_s.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
1// Copyright 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/cecd/cecd.h"
6#include "core/hle/service/cecd/cecd_s.h"
7
8namespace Service {
9namespace CECD {
10
11static const Interface::FunctionInfo FunctionTable[] = {
12 // cecd:u shared commands
13 {0x000100C2, nullptr, "OpenRawFile"},
14 {0x00020042, nullptr, "ReadRawFile"},
15 {0x00030104, nullptr, "ReadMessage"},
16 {0x00040106, nullptr, "ReadMessageWithHMAC"},
17 {0x00050042, nullptr, "WriteRawFile"},
18 {0x00060104, nullptr, "WriteMessage"},
19 {0x00070106, nullptr, "WriteMessageWithHMAC"},
20 {0x00080102, nullptr, "Delete"},
21 {0x000A00C4, nullptr, "GetSystemInfo"},
22 {0x000B0040, nullptr, "RunCommand"},
23 {0x000C0040, nullptr, "RunCommandAlt"},
24 {0x000E0000, GetCecStateAbbreviated, "GetCecStateAbbreviated"},
25 {0x000F0000, GetCecInfoEventHandle, "GetCecInfoEventHandle"},
26 {0x00100000, GetChangeStateEventHandle, "GetChangeStateEventHandle"},
27 {0x00110104, nullptr, "OpenAndWrite"},
28 {0x00120104, nullptr, "OpenAndRead"},
29};
30
31CECD_S::CECD_S() {
32 Register(FunctionTable);
33}
34
35} // namespace CECD
36} // namespace Service
diff --git a/src/core/hle/service/cecd/cecd_s.h b/src/core/hle/service/cecd/cecd_s.h
deleted file mode 100644
index ab6c6789a..000000000
--- a/src/core/hle/service/cecd/cecd_s.h
+++ /dev/null
@@ -1,22 +0,0 @@
1// Copyright 2015 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 "core/hle/service/service.h"
8
9namespace Service {
10namespace CECD {
11
12class CECD_S : public Interface {
13public:
14 CECD_S();
15
16 std::string GetPortName() const override {
17 return "cecd:s";
18 }
19};
20
21} // namespace CECD
22} // namespace Service
diff --git a/src/core/hle/service/cecd/cecd_u.cpp b/src/core/hle/service/cecd/cecd_u.cpp
deleted file mode 100644
index 3ed864f0b..000000000
--- a/src/core/hle/service/cecd/cecd_u.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/cecd/cecd.h"
6#include "core/hle/service/cecd/cecd_u.h"
7
8namespace Service {
9namespace CECD {
10
11static const Interface::FunctionInfo FunctionTable[] = {
12 // cecd:u shared commands
13 {0x000100C2, nullptr, "OpenRawFile"},
14 {0x00020042, nullptr, "ReadRawFile"},
15 {0x00030104, nullptr, "ReadMessage"},
16 {0x00040106, nullptr, "ReadMessageWithHMAC"},
17 {0x00050042, nullptr, "WriteRawFile"},
18 {0x00060104, nullptr, "WriteMessage"},
19 {0x00070106, nullptr, "WriteMessageWithHMAC"},
20 {0x00080102, nullptr, "Delete"},
21 {0x000A00C4, nullptr, "GetSystemInfo"},
22 {0x000B0040, nullptr, "RunCommand"},
23 {0x000C0040, nullptr, "RunCommandAlt"},
24 {0x000E0000, GetCecStateAbbreviated, "GetCecStateAbbreviated"},
25 {0x000F0000, GetCecInfoEventHandle, "GetCecInfoEventHandle"},
26 {0x00100000, GetChangeStateEventHandle, "GetChangeStateEventHandle"},
27 {0x00110104, nullptr, "OpenAndWrite"},
28 {0x00120104, nullptr, "OpenAndRead"},
29};
30
31CECD_U::CECD_U() {
32 Register(FunctionTable);
33}
34
35} // namespace CECD
36} // namespace Service
diff --git a/src/core/hle/service/cecd/cecd_u.h b/src/core/hle/service/cecd/cecd_u.h
deleted file mode 100644
index 16e874ff5..000000000
--- a/src/core/hle/service/cecd/cecd_u.h
+++ /dev/null
@@ -1,22 +0,0 @@
1// Copyright 2014 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 "core/hle/service/service.h"
8
9namespace Service {
10namespace CECD {
11
12class CECD_U : public Interface {
13public:
14 CECD_U();
15
16 std::string GetPortName() const override {
17 return "cecd:u";
18 }
19};
20
21} // namespace CECD
22} // namespace Service
diff --git a/src/core/hle/service/csnd_snd.cpp b/src/core/hle/service/csnd_snd.cpp
deleted file mode 100644
index aac903ccb..000000000
--- a/src/core/hle/service/csnd_snd.cpp
+++ /dev/null
@@ -1,143 +0,0 @@
1// Copyright 2014 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/alignment.h"
7#include "core/hle/ipc.h"
8#include "core/hle/kernel/handle_table.h"
9#include "core/hle/kernel/mutex.h"
10#include "core/hle/kernel/shared_memory.h"
11#include "core/hle/service/csnd_snd.h"
12#include "core/memory.h"
13
14namespace Service {
15namespace CSND {
16
17struct Type0Command {
18 // command id and next command offset
19 u32 command_id;
20 u32 finished;
21 u32 flags;
22 u8 parameters[20];
23};
24static_assert(sizeof(Type0Command) == 0x20, "Type0Command structure size is wrong");
25
26static Kernel::SharedPtr<Kernel::SharedMemory> shared_memory = nullptr;
27static Kernel::SharedPtr<Kernel::Mutex> mutex = nullptr;
28
29/**
30 * CSND_SND::Initialize service function
31 * Inputs:
32 * 0 : Header Code[0x00010140]
33 * 1 : Shared memory block size, for mem-block creation
34 * Outputs:
35 * 1 : Result of function, 0 on success, otherwise error code
36 * 2 : Handle-list header
37 * 3 : Mutex handle
38 * 4 : Shared memory block handle
39 */
40static void Initialize(Interface* self) {
41 u32* cmd_buff = Kernel::GetCommandBuffer();
42
43 u32 size = Common::AlignUp(cmd_buff[1], Memory::PAGE_SIZE);
44
45 using Kernel::MemoryPermission;
46 shared_memory = Kernel::SharedMemory::Create(nullptr, size, MemoryPermission::ReadWrite,
47 MemoryPermission::ReadWrite, 0,
48 Kernel::MemoryRegion::BASE, "CSND:SharedMemory");
49
50 mutex = Kernel::Mutex::Create(false, 0, "CSND:mutex");
51
52 cmd_buff[1] = RESULT_SUCCESS.raw;
53 cmd_buff[2] = IPC::CopyHandleDesc(2);
54 cmd_buff[3] = Kernel::g_handle_table.Create(mutex).Unwrap();
55 cmd_buff[4] = Kernel::g_handle_table.Create(shared_memory).Unwrap();
56
57 LOG_WARNING(Service_CSND, "(STUBBED) called");
58}
59
60/**
61 * CSND_SND::Shutdown service function
62 * Inputs:
63 * 0 : Header Code[0x00020000]
64 * Outputs:
65 * 1 : Result of function, 0 on success, otherwise error code
66 */
67static void Shutdown(Interface* self) {
68 u32* cmd_buff = Kernel::GetCommandBuffer();
69
70 shared_memory = nullptr;
71 mutex = nullptr;
72
73 cmd_buff[1] = RESULT_SUCCESS.raw;
74 LOG_WARNING(Service_CSND, "(STUBBED) called");
75}
76
77/**
78 * CSND_SND::ExecuteCommands service function
79 * Inputs:
80 * 0 : Header Code[0x00030040]
81 * 1 : Command offset in shared memory.
82 * Outputs:
83 * 1 : Result of function, 0 on success, otherwise error code
84 * 2 : Available channel bit mask
85 */
86static void ExecuteCommands(Interface* self) {
87 u32* cmd_buff = Kernel::GetCommandBuffer();
88
89 if (shared_memory == nullptr) {
90 cmd_buff[1] = 1;
91 LOG_ERROR(Service_CSND, "called, shared memory not allocated");
92 return;
93 }
94
95 VAddr addr = cmd_buff[1];
96 u8* ptr = shared_memory->GetPointer(addr);
97
98 Type0Command command;
99 std::memcpy(&command, ptr, sizeof(Type0Command));
100 command.finished |= 1;
101 std::memcpy(ptr, &command, sizeof(Type0Command));
102
103 cmd_buff[1] = RESULT_SUCCESS.raw;
104
105 LOG_WARNING(Service_CSND, "(STUBBED) called, addr=0x%08X", addr);
106}
107
108/**
109 * CSND_SND::AcquireSoundChannels service function
110 * Inputs:
111 * 0 : Header Code[0x00050000]
112 * Outputs:
113 * 1 : Result of function, 0 on success, otherwise error code
114 * 2 : Available channel bit mask
115 */
116static void AcquireSoundChannels(Interface* self) {
117 u32* cmd_buff = Kernel::GetCommandBuffer();
118 cmd_buff[1] = RESULT_SUCCESS.raw;
119 cmd_buff[2] = 0xFFFFFF00;
120 LOG_WARNING(Service_CSND, "(STUBBED) called");
121}
122
123const Interface::FunctionInfo FunctionTable[] = {
124 {0x00010140, Initialize, "Initialize"},
125 {0x00020000, Shutdown, "Shutdown"},
126 {0x00030040, ExecuteCommands, "ExecuteCommands"},
127 {0x00040080, nullptr, "ExecuteType1Commands"},
128 {0x00050000, AcquireSoundChannels, "AcquireSoundChannels"},
129 {0x00060000, nullptr, "ReleaseSoundChannels"},
130 {0x00070000, nullptr, "AcquireCaptureDevice"},
131 {0x00080040, nullptr, "ReleaseCaptureDevice"},
132 {0x00090082, nullptr, "FlushDataCache"},
133 {0x000A0082, nullptr, "StoreDataCache"},
134 {0x000B0082, nullptr, "InvalidateDataCache"},
135 {0x000C0000, nullptr, "Reset"},
136};
137
138CSND_SND::CSND_SND() {
139 Register(FunctionTable);
140}
141
142} // namespace CSND
143} // namespace Service
diff --git a/src/core/hle/service/csnd_snd.h b/src/core/hle/service/csnd_snd.h
deleted file mode 100644
index ca6d4513e..000000000
--- a/src/core/hle/service/csnd_snd.h
+++ /dev/null
@@ -1,22 +0,0 @@
1// Copyright 2014 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 "core/hle/service/service.h"
8
9namespace Service {
10namespace CSND {
11
12class CSND_SND final : public Interface {
13public:
14 CSND_SND();
15
16 std::string GetPortName() const override {
17 return "csnd:SND";
18 }
19};
20
21} // namespace CSND
22} // namespace Service
diff --git a/src/core/hle/service/dlp/dlp.cpp b/src/core/hle/service/dlp/dlp.cpp
deleted file mode 100644
index 8f4b67a5d..000000000
--- a/src/core/hle/service/dlp/dlp.cpp
+++ /dev/null
@@ -1,23 +0,0 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/dlp/dlp.h"
6#include "core/hle/service/dlp/dlp_clnt.h"
7#include "core/hle/service/dlp/dlp_fkcl.h"
8#include "core/hle/service/dlp/dlp_srvr.h"
9#include "core/hle/service/service.h"
10
11namespace Service {
12namespace DLP {
13
14void Init() {
15 AddService(new DLP_CLNT_Interface);
16 AddService(new DLP_FKCL_Interface);
17 AddService(new DLP_SRVR_Interface);
18}
19
20void Shutdown() {}
21
22} // namespace DLP
23} // namespace Service
diff --git a/src/core/hle/service/dlp/dlp.h b/src/core/hle/service/dlp/dlp.h
deleted file mode 100644
index 3185fe322..000000000
--- a/src/core/hle/service/dlp/dlp.h
+++ /dev/null
@@ -1,17 +0,0 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7namespace Service {
8namespace DLP {
9
10/// Initializes the DLP services.
11void Init();
12
13/// Shuts down the DLP services.
14void Shutdown();
15
16} // namespace DLP
17} // namespace Service
diff --git a/src/core/hle/service/dlp/dlp_clnt.cpp b/src/core/hle/service/dlp/dlp_clnt.cpp
deleted file mode 100644
index 6f2bf2061..000000000
--- a/src/core/hle/service/dlp/dlp_clnt.cpp
+++ /dev/null
@@ -1,38 +0,0 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/dlp/dlp_clnt.h"
6
7namespace Service {
8namespace DLP {
9
10const Interface::FunctionInfo FunctionTable[] = {
11 {0x000100C3, nullptr, "Initialize"},
12 {0x00020000, nullptr, "Finalize"},
13 {0x00030000, nullptr, "GetEventDesc"},
14 {0x00040000, nullptr, "GetChannel"},
15 {0x00050180, nullptr, "StartScan"},
16 {0x00060000, nullptr, "StopScan"},
17 {0x00070080, nullptr, "GetServerInfo"},
18 {0x00080100, nullptr, "GetTitleInfo"},
19 {0x00090040, nullptr, "GetTitleInfoInOrder"},
20 {0x000A0080, nullptr, "DeleteScanInfo"},
21 {0x000B0100, nullptr, "PrepareForSystemDownload"},
22 {0x000C0000, nullptr, "StartSystemDownload"},
23 {0x000D0100, nullptr, "StartTitleDownload"},
24 {0x000E0000, nullptr, "GetMyStatus"},
25 {0x000F0040, nullptr, "GetConnectingNodes"},
26 {0x00100040, nullptr, "GetNodeInfo"},
27 {0x00110000, nullptr, "GetWirelessRebootPassphrase"},
28 {0x00120000, nullptr, "StopSession"},
29 {0x00130100, nullptr, "GetCupVersion"},
30 {0x00140100, nullptr, "GetDupAvailability"},
31};
32
33DLP_CLNT_Interface::DLP_CLNT_Interface() {
34 Register(FunctionTable);
35}
36
37} // namespace DLP
38} // namespace Service
diff --git a/src/core/hle/service/dlp/dlp_clnt.h b/src/core/hle/service/dlp/dlp_clnt.h
deleted file mode 100644
index 067f11e37..000000000
--- a/src/core/hle/service/dlp/dlp_clnt.h
+++ /dev/null
@@ -1,22 +0,0 @@
1// Copyright 2016 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 "core/hle/service/service.h"
8
9namespace Service {
10namespace DLP {
11
12class DLP_CLNT_Interface final : public Interface {
13public:
14 DLP_CLNT_Interface();
15
16 std::string GetPortName() const override {
17 return "dlp:CLNT";
18 }
19};
20
21} // namespace DLP
22} // namespace Service
diff --git a/src/core/hle/service/dlp/dlp_fkcl.cpp b/src/core/hle/service/dlp/dlp_fkcl.cpp
deleted file mode 100644
index fe6be7d32..000000000
--- a/src/core/hle/service/dlp/dlp_fkcl.cpp
+++ /dev/null
@@ -1,35 +0,0 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/dlp/dlp_fkcl.h"
6
7namespace Service {
8namespace DLP {
9
10const Interface::FunctionInfo FunctionTable[] = {
11 {0x00010083, nullptr, "Initialize"},
12 {0x00020000, nullptr, "Finalize"},
13 {0x00030000, nullptr, "GetEventDesc"},
14 {0x00040000, nullptr, "GetChannels"},
15 {0x00050180, nullptr, "StartScan"},
16 {0x00060000, nullptr, "StopScan"},
17 {0x00070080, nullptr, "GetServerInfo"},
18 {0x00080100, nullptr, "GetTitleInfo"},
19 {0x00090040, nullptr, "GetTitleInfoInOrder"},
20 {0x000A0080, nullptr, "DeleteScanInfo"},
21 {0x000B0100, nullptr, "StartFakeSession"},
22 {0x000C0000, nullptr, "GetMyStatus"},
23 {0x000D0040, nullptr, "GetConnectingNodes"},
24 {0x000E0040, nullptr, "GetNodeInfo"},
25 {0x000F0000, nullptr, "GetWirelessRebootPassphrase"},
26 {0x00100000, nullptr, "StopSession"},
27 {0x00110203, nullptr, "Initialize2"},
28};
29
30DLP_FKCL_Interface::DLP_FKCL_Interface() {
31 Register(FunctionTable);
32}
33
34} // namespace DLP
35} // namespace Service
diff --git a/src/core/hle/service/dlp/dlp_fkcl.h b/src/core/hle/service/dlp/dlp_fkcl.h
deleted file mode 100644
index e4837a167..000000000
--- a/src/core/hle/service/dlp/dlp_fkcl.h
+++ /dev/null
@@ -1,22 +0,0 @@
1// Copyright 2016 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 "core/hle/service/service.h"
8
9namespace Service {
10namespace DLP {
11
12class DLP_FKCL_Interface final : public Interface {
13public:
14 DLP_FKCL_Interface();
15
16 std::string GetPortName() const override {
17 return "dlp:FKCL";
18 }
19};
20
21} // namespace DLP
22} // namespace Service
diff --git a/src/core/hle/service/dlp/dlp_srvr.cpp b/src/core/hle/service/dlp/dlp_srvr.cpp
deleted file mode 100644
index 1bcea43d3..000000000
--- a/src/core/hle/service/dlp/dlp_srvr.cpp
+++ /dev/null
@@ -1,47 +0,0 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/common_types.h"
6#include "common/logging/log.h"
7#include "core/hle/ipc.h"
8#include "core/hle/result.h"
9#include "core/hle/service/dlp/dlp_srvr.h"
10
11namespace Service {
12namespace DLP {
13
14static void IsChild(Interface* self) {
15 u32* cmd_buff = Kernel::GetCommandBuffer();
16
17 cmd_buff[1] = RESULT_SUCCESS.raw;
18 cmd_buff[2] = 0;
19
20 LOG_WARNING(Service_DLP, "(STUBBED) called");
21}
22
23const Interface::FunctionInfo FunctionTable[] = {
24 {0x00010183, nullptr, "Initialize"},
25 {0x00020000, nullptr, "Finalize"},
26 {0x00030000, nullptr, "GetServerState"},
27 {0x00040000, nullptr, "GetEventDescription"},
28 {0x00050080, nullptr, "StartAccepting"},
29 {0x00060000, nullptr, "EndAccepting"},
30 {0x00070000, nullptr, "StartDistribution"},
31 {0x000800C0, nullptr, "SendWirelessRebootPassphrase"},
32 {0x00090040, nullptr, "AcceptClient"},
33 {0x000A0040, nullptr, "DisconnectClient"},
34 {0x000B0042, nullptr, "GetConnectingClients"},
35 {0x000C0040, nullptr, "GetClientInfo"},
36 {0x000D0040, nullptr, "GetClientState"},
37 {0x000E0040, IsChild, "IsChild"},
38 {0x000F0303, nullptr, "InitializeWithName"},
39 {0x00100000, nullptr, "GetDupNoticeNeed"},
40};
41
42DLP_SRVR_Interface::DLP_SRVR_Interface() {
43 Register(FunctionTable);
44}
45
46} // namespace DLP
47} // namespace Service
diff --git a/src/core/hle/service/dlp/dlp_srvr.h b/src/core/hle/service/dlp/dlp_srvr.h
deleted file mode 100644
index 19fe17840..000000000
--- a/src/core/hle/service/dlp/dlp_srvr.h
+++ /dev/null
@@ -1,22 +0,0 @@
1// Copyright 2016 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 "core/hle/service/service.h"
8
9namespace Service {
10namespace DLP {
11
12class DLP_SRVR_Interface final : public Interface {
13public:
14 DLP_SRVR_Interface();
15
16 std::string GetPortName() const override {
17 return "dlp:SRVR";
18 }
19};
20
21} // namespace DLP
22} // namespace Service
diff --git a/src/core/hle/service/err_f.cpp b/src/core/hle/service/err_f.cpp
deleted file mode 100644
index 7c8f4339f..000000000
--- a/src/core/hle/service/err_f.cpp
+++ /dev/null
@@ -1,266 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <array>
6#include <chrono>
7#include <iomanip>
8#include <sstream>
9#include "common/bit_field.h"
10#include "common/common_types.h"
11#include "common/logging/log.h"
12#include "core/core.h"
13#include "core/hle/ipc.h"
14#include "core/hle/result.h"
15#include "core/hle/service/err_f.h"
16
17namespace Service {
18namespace ERR {
19
20enum class FatalErrType : u32 {
21 Generic = 0,
22 Corrupted = 1,
23 CardRemoved = 2,
24 Exception = 3,
25 ResultFailure = 4,
26 Logged = 5,
27};
28
29enum class ExceptionType : u32 {
30 PrefetchAbort = 0,
31 DataAbort = 1,
32 Undefined = 2,
33 VectorFP = 3,
34};
35
36struct ExceptionInfo {
37 u8 exception_type;
38 INSERT_PADDING_BYTES(3);
39 u32 sr;
40 u32 ar;
41 u32 fpexc;
42 u32 fpinst;
43 u32 fpinst2;
44};
45static_assert(sizeof(ExceptionInfo) == 0x18, "ExceptionInfo struct has incorrect size");
46
47struct ExceptionContext final {
48 std::array<u32, 16> arm_regs;
49 u32 cpsr;
50};
51static_assert(sizeof(ExceptionContext) == 0x44, "ExceptionContext struct has incorrect size");
52
53struct ExceptionData {
54 ExceptionInfo exception_info;
55 ExceptionContext exception_context;
56 INSERT_PADDING_WORDS(1);
57};
58static_assert(sizeof(ExceptionData) == 0x60, "ExceptionData struct has incorrect size");
59
60// This is used instead of ResultCode from result.h
61// because we can't have non-trivial data members in unions.
62union RSL {
63 u32 raw;
64
65 BitField<0, 10, u32> description;
66 BitField<10, 8, u32> module;
67 BitField<21, 6, u32> summary;
68 BitField<27, 5, u32> level;
69};
70
71struct ErrInfo {
72 struct ErrInfoCommon {
73 u8 specifier; // 0x0
74 u8 rev_high; // 0x1
75 u16 rev_low; // 0x2
76 RSL result_code; // 0x4
77 u32 pc_address; // 0x8
78 u32 pid; // 0xC
79 u32 title_id_low; // 0x10
80 u32 title_id_high; // 0x14
81 u32 app_title_id_low; // 0x18
82 u32 app_title_id_high; // 0x1C
83 } errinfo_common;
84 static_assert(sizeof(ErrInfoCommon) == 0x20, "ErrInfoCommon struct has incorrect size");
85
86 union {
87 struct {
88 char data[0x60]; // 0x20
89 } generic;
90
91 struct {
92 ExceptionData exception_data; // 0x20
93 } exception;
94
95 struct {
96 char message[0x60]; // 0x20
97 } result_failure;
98 };
99};
100
101static std::string GetErrType(u8 type_code) {
102 switch (static_cast<FatalErrType>(type_code)) {
103 case FatalErrType::Generic:
104 return "Generic";
105 case FatalErrType::Corrupted:
106 return "Corrupted";
107 case FatalErrType::CardRemoved:
108 return "CardRemoved";
109 case FatalErrType::Exception:
110 return "Exception";
111 case FatalErrType::ResultFailure:
112 return "ResultFailure";
113 case FatalErrType::Logged:
114 return "Logged";
115 default:
116 return "Unknown Error Type";
117 }
118}
119
120static std::string GetExceptionType(u8 type_code) {
121 switch (static_cast<ExceptionType>(type_code)) {
122 case ExceptionType::PrefetchAbort:
123 return "Prefetch Abort";
124 case ExceptionType::DataAbort:
125 return "Data Abort";
126 case ExceptionType::Undefined:
127 return "Undefined Exception";
128 case ExceptionType::VectorFP:
129 return "Vector Floating Point Exception";
130 default:
131 return "Unknown Exception Type";
132 }
133}
134
135static std::string GetCurrentSystemTime() {
136 auto now = std::chrono::system_clock::now();
137 auto time = std::chrono::system_clock::to_time_t(now);
138
139 std::stringstream time_stream;
140 time_stream << std::put_time(std::localtime(&time), "%Y/%m/%d %H:%M:%S");
141 return time_stream.str();
142}
143
144static void LogGenericInfo(const ErrInfo::ErrInfoCommon& errinfo_common) {
145 LOG_CRITICAL(Service_ERR, "PID: 0x%08X", errinfo_common.pid);
146 LOG_CRITICAL(Service_ERR, "REV: 0x%08X_0x%08X", errinfo_common.rev_high,
147 errinfo_common.rev_low);
148 LOG_CRITICAL(Service_ERR, "TID: 0x%08X_0x%08X", errinfo_common.title_id_high,
149 errinfo_common.title_id_low);
150 LOG_CRITICAL(Service_ERR, "AID: 0x%08X_0x%08X", errinfo_common.app_title_id_high,
151 errinfo_common.app_title_id_low);
152 LOG_CRITICAL(Service_ERR, "ADR: 0x%08X", errinfo_common.pc_address);
153
154 LOG_CRITICAL(Service_ERR, "RSL: 0x%08X", errinfo_common.result_code.raw);
155 LOG_CRITICAL(Service_ERR, " Level: %u", errinfo_common.result_code.level.Value());
156 LOG_CRITICAL(Service_ERR, " Summary: %u", errinfo_common.result_code.summary.Value());
157 LOG_CRITICAL(Service_ERR, " Module: %u", errinfo_common.result_code.module.Value());
158 LOG_CRITICAL(Service_ERR, " Desc: %u", errinfo_common.result_code.description.Value());
159}
160
161/* ThrowFatalError function
162 * Inputs:
163 * 0 : Header code [0x00010800]
164 * 1-32 : FatalErrInfo
165 * Outputs:
166 * 0 : Header code
167 * 1 : Result code
168 */
169static void ThrowFatalError(Interface* self) {
170 u32* cmd_buff = Kernel::GetCommandBuffer();
171
172 LOG_CRITICAL(Service_ERR, "Fatal error");
173 const ErrInfo* errinfo = reinterpret_cast<ErrInfo*>(&cmd_buff[1]);
174 LOG_CRITICAL(Service_ERR, "Fatal error type: %s",
175 GetErrType(errinfo->errinfo_common.specifier).c_str());
176 Core::System::GetInstance().SetStatus(Core::System::ResultStatus::ErrorUnknown);
177
178 // Generic Info
179 LogGenericInfo(errinfo->errinfo_common);
180
181 switch (static_cast<FatalErrType>(errinfo->errinfo_common.specifier)) {
182 case FatalErrType::Generic:
183 case FatalErrType::Corrupted:
184 case FatalErrType::CardRemoved:
185 case FatalErrType::Logged: {
186 LOG_CRITICAL(Service_ERR, "Datetime: %s", GetCurrentSystemTime().c_str());
187 break;
188 }
189 case FatalErrType::Exception: {
190 const auto& errtype = errinfo->exception;
191
192 // Register Info
193 LOG_CRITICAL(Service_ERR, "ARM Registers:");
194 for (u32 index = 0; index < errtype.exception_data.exception_context.arm_regs.size();
195 ++index) {
196 if (index < 13) {
197 LOG_DEBUG(Service_ERR, "r%u=0x%08X", index,
198 errtype.exception_data.exception_context.arm_regs.at(index));
199 } else if (index == 13) {
200 LOG_CRITICAL(Service_ERR, "SP=0x%08X",
201 errtype.exception_data.exception_context.arm_regs.at(index));
202 } else if (index == 14) {
203 LOG_CRITICAL(Service_ERR, "LR=0x%08X",
204 errtype.exception_data.exception_context.arm_regs.at(index));
205 } else if (index == 15) {
206 LOG_CRITICAL(Service_ERR, "PC=0x%08X",
207 errtype.exception_data.exception_context.arm_regs.at(index));
208 }
209 }
210 LOG_CRITICAL(Service_ERR, "CPSR=0x%08X", errtype.exception_data.exception_context.cpsr);
211
212 // Exception Info
213 LOG_CRITICAL(
214 Service_ERR, "EXCEPTION TYPE: %s",
215 GetExceptionType(errtype.exception_data.exception_info.exception_type).c_str());
216 switch (static_cast<ExceptionType>(errtype.exception_data.exception_info.exception_type)) {
217 case ExceptionType::PrefetchAbort:
218 LOG_CRITICAL(Service_ERR, "IFSR: 0x%08X", errtype.exception_data.exception_info.sr);
219 LOG_CRITICAL(Service_ERR, "r15: 0x%08X", errtype.exception_data.exception_info.ar);
220 case ExceptionType::DataAbort:
221 LOG_CRITICAL(Service_ERR, "DFSR: 0x%08X", errtype.exception_data.exception_info.sr);
222 LOG_CRITICAL(Service_ERR, "DFAR: 0x%08X", errtype.exception_data.exception_info.ar);
223 break;
224 case ExceptionType::VectorFP:
225 LOG_CRITICAL(Service_ERR, "FPEXC: 0x%08X",
226 errtype.exception_data.exception_info.fpinst);
227 LOG_CRITICAL(Service_ERR, "FINST: 0x%08X",
228 errtype.exception_data.exception_info.fpinst);
229 LOG_CRITICAL(Service_ERR, "FINST2: 0x%08X",
230 errtype.exception_data.exception_info.fpinst2);
231 break;
232 case ExceptionType::Undefined:
233 break; // Not logging exception_info for this case
234 }
235 LOG_CRITICAL(Service_ERR, "Datetime: %s", GetCurrentSystemTime().c_str());
236 break;
237 }
238
239 case FatalErrType::ResultFailure: {
240 const auto& errtype = errinfo->result_failure;
241
242 // Failure Message
243 LOG_CRITICAL(Service_ERR, "Failure Message: %s", errtype.message);
244 LOG_CRITICAL(Service_ERR, "Datetime: %s", GetCurrentSystemTime().c_str());
245 break;
246 }
247
248 } // switch FatalErrType
249
250 cmd_buff[0] = IPC::MakeHeader(0x1, 1, 0);
251 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
252}
253
254const Interface::FunctionInfo FunctionTable[] = {
255 // clang-format off
256 {0x00010800, ThrowFatalError, "ThrowFatalError"},
257 {0x00020042, nullptr, "SetUserString"},
258 // clang-format on
259};
260
261ERR_F::ERR_F() {
262 Register(FunctionTable);
263}
264
265} // namespace ERR
266} // namespace Service
diff --git a/src/core/hle/service/err_f.h b/src/core/hle/service/err_f.h
deleted file mode 100644
index 5b27fc871..000000000
--- a/src/core/hle/service/err_f.h
+++ /dev/null
@@ -1,22 +0,0 @@
1// Copyright 2014 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 "core/hle/service/service.h"
8
9namespace Service {
10namespace ERR {
11
12class ERR_F final : public Interface {
13public:
14 ERR_F();
15
16 std::string GetPortName() const override {
17 return "err:f";
18 }
19};
20
21} // namespace ERR
22} // namespace Service
diff --git a/src/core/hle/service/frd/frd.cpp b/src/core/hle/service/frd/frd.cpp
deleted file mode 100644
index 7ad7798da..000000000
--- a/src/core/hle/service/frd/frd.cpp
+++ /dev/null
@@ -1,174 +0,0 @@
1// Copyright 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/assert.h"
6#include "common/logging/log.h"
7#include "common/string_util.h"
8#include "core/hle/ipc.h"
9#include "core/hle/ipc_helpers.h"
10#include "core/hle/result.h"
11#include "core/hle/service/frd/frd.h"
12#include "core/hle/service/frd/frd_a.h"
13#include "core/hle/service/frd/frd_u.h"
14#include "core/hle/service/service.h"
15#include "core/memory.h"
16
17namespace Service {
18namespace FRD {
19
20static FriendKey my_friend_key = {0, 0, 0ull};
21static MyPresence my_presence = {};
22
23void GetMyPresence(Service::Interface* self) {
24 u32* cmd_buff = Kernel::GetCommandBuffer();
25
26 u32 shifted_out_size = cmd_buff[64];
27 u32 my_presence_addr = cmd_buff[65];
28
29 ASSERT(shifted_out_size == ((sizeof(MyPresence) << 14) | 2));
30
31 Memory::WriteBlock(my_presence_addr, &my_presence, sizeof(MyPresence));
32
33 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
34
35 LOG_WARNING(Service_FRD, "(STUBBED) called");
36}
37
38void GetFriendKeyList(Service::Interface* self) {
39 u32* cmd_buff = Kernel::GetCommandBuffer();
40
41 u32 unknown = cmd_buff[1];
42 u32 frd_count = cmd_buff[2];
43 u32 frd_key_addr = cmd_buff[65];
44
45 FriendKey zero_key = {};
46 for (u32 i = 0; i < frd_count; ++i) {
47 Memory::WriteBlock(frd_key_addr + i * sizeof(FriendKey), &zero_key, sizeof(FriendKey));
48 }
49
50 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
51 cmd_buff[2] = 0; // 0 friends
52 LOG_WARNING(Service_FRD, "(STUBBED) called, unknown=%d, frd_count=%d, frd_key_addr=0x%08X",
53 unknown, frd_count, frd_key_addr);
54}
55
56void GetFriendProfile(Service::Interface* self) {
57 u32* cmd_buff = Kernel::GetCommandBuffer();
58
59 u32 count = cmd_buff[1];
60 u32 frd_key_addr = cmd_buff[3];
61 u32 profiles_addr = cmd_buff[65];
62
63 Profile zero_profile = {};
64 for (u32 i = 0; i < count; ++i) {
65 Memory::WriteBlock(profiles_addr + i * sizeof(Profile), &zero_profile, sizeof(Profile));
66 }
67
68 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
69 LOG_WARNING(Service_FRD,
70 "(STUBBED) called, count=%d, frd_key_addr=0x%08X, profiles_addr=0x%08X", count,
71 frd_key_addr, profiles_addr);
72}
73
74void GetFriendAttributeFlags(Service::Interface* self) {
75 u32* cmd_buff = Kernel::GetCommandBuffer();
76
77 u32 count = cmd_buff[1];
78 u32 frd_key_addr = cmd_buff[3];
79 u32 attr_flags_addr = cmd_buff[65];
80
81 for (u32 i = 0; i < count; ++i) {
82 // TODO:(mailwl) figure out AttributeFlag size and zero all buffer. Assume 1 byte
83 Memory::Write8(attr_flags_addr + i, 0);
84 }
85
86 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
87 LOG_WARNING(Service_FRD,
88 "(STUBBED) called, count=%d, frd_key_addr=0x%08X, attr_flags_addr=0x%08X", count,
89 frd_key_addr, attr_flags_addr);
90}
91
92void GetMyFriendKey(Service::Interface* self) {
93 u32* cmd_buff = Kernel::GetCommandBuffer();
94
95 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
96 std::memcpy(&cmd_buff[2], &my_friend_key, sizeof(FriendKey));
97 LOG_WARNING(Service_FRD, "(STUBBED) called");
98}
99
100void GetMyScreenName(Service::Interface* self) {
101 u32* cmd_buff = Kernel::GetCommandBuffer();
102
103 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
104 // TODO: (mailwl) get the name from config
105 Common::UTF8ToUTF16("Citra").copy(reinterpret_cast<char16_t*>(&cmd_buff[2]), 11);
106 LOG_WARNING(Service_FRD, "(STUBBED) called");
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
151void SetClientSdkVersion(Service::Interface* self) {
152 u32* cmd_buff = Kernel::GetCommandBuffer();
153
154 const u32 version = cmd_buff[1];
155
156 self->SetVersion(version);
157
158 LOG_WARNING(Service_FRD, "(STUBBED) called, version: 0x%08X", version);
159
160 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
161}
162
163void Init() {
164 using namespace Kernel;
165
166 AddService(new FRD_A_Interface);
167 AddService(new FRD_U_Interface);
168}
169
170void Shutdown() {}
171
172} // namespace FRD
173
174} // namespace Service
diff --git a/src/core/hle/service/frd/frd.h b/src/core/hle/service/frd/frd.h
deleted file mode 100644
index 66a87c8cd..000000000
--- a/src/core/hle/service/frd/frd.h
+++ /dev/null
@@ -1,127 +0,0 @@
1// Copyright 2015 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 "common/common_types.h"
8
9namespace Service {
10
11class Interface;
12
13namespace FRD {
14
15struct FriendKey {
16 u32 friend_id;
17 u32 unknown;
18 u64 friend_code;
19};
20
21struct MyPresence {
22 u8 unknown[0x12C];
23};
24
25struct Profile {
26 u8 region;
27 u8 country;
28 u8 area;
29 u8 language;
30 u32 unknown;
31};
32
33/**
34 * FRD::GetMyPresence service function
35 * Inputs:
36 * 64 : sizeof (MyPresence) << 14 | 2
37 * 65 : Address of MyPresence structure
38 * Outputs:
39 * 1 : Result of function, 0 on success, otherwise error code
40 */
41void GetMyPresence(Service::Interface* self);
42
43/**
44 * FRD::GetFriendKeyList service function
45 * Inputs:
46 * 1 : Unknown
47 * 2 : Max friends count
48 * 65 : Address of FriendKey List
49 * Outputs:
50 * 1 : Result of function, 0 on success, otherwise error code
51 * 2 : FriendKey count filled
52 */
53void GetFriendKeyList(Service::Interface* self);
54
55/**
56 * FRD::GetFriendProfile service function
57 * Inputs:
58 * 1 : Friends count
59 * 2 : Friends count << 18 | 2
60 * 3 : Address of FriendKey List
61 * 64 : (count * sizeof (Profile)) << 10 | 2
62 * 65 : Address of Profiles List
63 * Outputs:
64 * 1 : Result of function, 0 on success, otherwise error code
65 */
66void GetFriendProfile(Service::Interface* self);
67
68/**
69 * FRD::GetFriendAttributeFlags service function
70 * Inputs:
71 * 1 : Friends count
72 * 2 : Friends count << 18 | 2
73 * 3 : Address of FriendKey List
74 * 65 : Address of AttributeFlags
75 * Outputs:
76 * 1 : Result of function, 0 on success, otherwise error code
77 */
78void GetFriendAttributeFlags(Service::Interface* self);
79
80/**
81 * FRD::GetMyFriendKey service function
82 * Inputs:
83 * none
84 * Outputs:
85 * 1 : Result of function, 0 on success, otherwise error code
86 * 2-5 : FriendKey
87 */
88void GetMyFriendKey(Service::Interface* self);
89
90/**
91 * FRD::GetMyScreenName service function
92 * Outputs:
93 * 1 : Result of function, 0 on success, otherwise error code
94 * 2 : UTF16 encoded name (max 11 symbols)
95 */
96void GetMyScreenName(Service::Interface* self);
97
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/**
112 * FRD::SetClientSdkVersion service function
113 * Inputs:
114 * 1 : Used SDK Version
115 * Outputs:
116 * 1 : Result of function, 0 on success, otherwise error code
117 */
118void SetClientSdkVersion(Service::Interface* self);
119
120/// Initialize FRD service(s)
121void Init();
122
123/// Shutdown FRD service(s)
124void Shutdown();
125
126} // namespace FRD
127} // namespace Service
diff --git a/src/core/hle/service/frd/frd_a.cpp b/src/core/hle/service/frd/frd_a.cpp
deleted file mode 100644
index cfc37210b..000000000
--- a/src/core/hle/service/frd/frd_a.cpp
+++ /dev/null
@@ -1,18 +0,0 @@
1// Copyright 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/frd/frd_a.h"
6
7namespace Service {
8namespace FRD {
9
10// Empty arrays are illegal -- commented out until an entry is added.
11// const Interface::FunctionInfo FunctionTable[] = { };
12
13FRD_A_Interface::FRD_A_Interface() {
14 // Register(FunctionTable);
15}
16
17} // namespace FRD
18} // namespace Service
diff --git a/src/core/hle/service/frd/frd_a.h b/src/core/hle/service/frd/frd_a.h
deleted file mode 100644
index 006d1cadd..000000000
--- a/src/core/hle/service/frd/frd_a.h
+++ /dev/null
@@ -1,22 +0,0 @@
1// Copyright 2015 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 "core/hle/service/service.h"
8
9namespace Service {
10namespace FRD {
11
12class FRD_A_Interface : public Service::Interface {
13public:
14 FRD_A_Interface();
15
16 std::string GetPortName() const override {
17 return "frd:a";
18 }
19};
20
21} // namespace FRD
22} // namespace Service
diff --git a/src/core/hle/service/frd/frd_u.cpp b/src/core/hle/service/frd/frd_u.cpp
deleted file mode 100644
index 6970ff768..000000000
--- a/src/core/hle/service/frd/frd_u.cpp
+++ /dev/null
@@ -1,72 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/frd/frd.h"
6#include "core/hle/service/frd/frd_u.h"
7
8namespace Service {
9namespace FRD {
10
11const Interface::FunctionInfo FunctionTable[] = {
12 {0x00010000, nullptr, "HasLoggedIn"},
13 {0x00020000, nullptr, "IsOnline"},
14 {0x00030000, nullptr, "Login"},
15 {0x00040000, nullptr, "Logout"},
16 {0x00050000, GetMyFriendKey, "GetMyFriendKey"},
17 {0x00060000, nullptr, "GetMyPreference"},
18 {0x00070000, nullptr, "GetMyProfile"},
19 {0x00080000, GetMyPresence, "GetMyPresence"},
20 {0x00090000, GetMyScreenName, "GetMyScreenName"},
21 {0x000A0000, nullptr, "GetMyMii"},
22 {0x000B0000, nullptr, "GetMyLocalAccountId"},
23 {0x000C0000, nullptr, "GetMyPlayingGame"},
24 {0x000D0000, nullptr, "GetMyFavoriteGame"},
25 {0x000E0000, nullptr, "GetMyNcPrincipalId"},
26 {0x000F0000, nullptr, "GetMyComment"},
27 {0x00100040, nullptr, "GetMyPassword"},
28 {0x00110080, GetFriendKeyList, "GetFriendKeyList"},
29 {0x00120042, nullptr, "GetFriendPresence"},
30 {0x00130142, nullptr, "GetFriendScreenName"},
31 {0x00140044, nullptr, "GetFriendMii"},
32 {0x00150042, GetFriendProfile, "GetFriendProfile"},
33 {0x00160042, nullptr, "GetFriendRelationship"},
34 {0x00170042, GetFriendAttributeFlags, "GetFriendAttributeFlags"},
35 {0x00180044, nullptr, "GetFriendPlayingGame"},
36 {0x00190042, nullptr, "GetFriendFavoriteGame"},
37 {0x001A00C4, nullptr, "GetFriendInfo"},
38 {0x001B0080, nullptr, "IsIncludedInFriendList"},
39 {0x001C0042, UnscrambleLocalFriendCode, "UnscrambleLocalFriendCode"},
40 {0x001D0002, nullptr, "UpdateGameModeDescription"},
41 {0x001E02C2, nullptr, "UpdateGameMode"},
42 {0x001F0042, nullptr, "SendInvitation"},
43 {0x00200002, nullptr, "AttachToEventNotification"},
44 {0x00210040, nullptr, "SetNotificationMask"},
45 {0x00220040, nullptr, "GetEventNotification"},
46 {0x00230000, nullptr, "GetLastResponseResult"},
47 {0x00240040, nullptr, "PrincipalIdToFriendCode"},
48 {0x00250080, nullptr, "FriendCodeToPrincipalId"},
49 {0x00260080, nullptr, "IsValidFriendCode"},
50 {0x00270040, nullptr, "ResultToErrorCode"},
51 {0x00280244, nullptr, "RequestGameAuthentication"},
52 {0x00290000, nullptr, "GetGameAuthenticationData"},
53 {0x002A0204, nullptr, "RequestServiceLocator"},
54 {0x002B0000, nullptr, "GetServiceLocatorData"},
55 {0x002C0002, nullptr, "DetectNatProperties"},
56 {0x002D0000, nullptr, "GetNatProperties"},
57 {0x002E0000, nullptr, "GetServerTimeInterval"},
58 {0x002F0040, nullptr, "AllowHalfAwake"},
59 {0x00300000, nullptr, "GetServerTypes"},
60 {0x00310082, nullptr, "GetFriendComment"},
61 {0x00320042, SetClientSdkVersion, "SetClientSdkVersion"},
62 {0x00330000, nullptr, "GetMyApproachContext"},
63 {0x00340046, nullptr, "AddFriendWithApproach"},
64 {0x00350082, nullptr, "DecryptApproachContext"},
65};
66
67FRD_U_Interface::FRD_U_Interface() {
68 Register(FunctionTable);
69}
70
71} // namespace FRD
72} // namespace Service
diff --git a/src/core/hle/service/frd/frd_u.h b/src/core/hle/service/frd/frd_u.h
deleted file mode 100644
index 07e43f155..000000000
--- a/src/core/hle/service/frd/frd_u.h
+++ /dev/null
@@ -1,22 +0,0 @@
1// Copyright 2014 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 "core/hle/service/service.h"
8
9namespace Service {
10namespace FRD {
11
12class FRD_U_Interface : public Service::Interface {
13public:
14 FRD_U_Interface();
15
16 std::string GetPortName() const override {
17 return "frd:u";
18 }
19};
20
21} // namespace FRD
22} // namespace Service
diff --git a/src/core/hle/service/http_c.cpp b/src/core/hle/service/http_c.cpp
deleted file mode 100644
index b01d6e031..000000000
--- a/src/core/hle/service/http_c.cpp
+++ /dev/null
@@ -1,72 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/http_c.h"
6
7namespace Service {
8namespace HTTP {
9
10const Interface::FunctionInfo FunctionTable[] = {
11 {0x00010044, nullptr, "Initialize"},
12 {0x00020082, nullptr, "CreateContext"},
13 {0x00030040, nullptr, "CloseContext"},
14 {0x00040040, nullptr, "CancelConnection"},
15 {0x00050040, nullptr, "GetRequestState"},
16 {0x00060040, nullptr, "GetDownloadSizeState"},
17 {0x00070040, nullptr, "GetRequestError"},
18 {0x00080042, nullptr, "InitializeConnectionSession"},
19 {0x00090040, nullptr, "BeginRequest"},
20 {0x000A0040, nullptr, "BeginRequestAsync"},
21 {0x000B0082, nullptr, "ReceiveData"},
22 {0x000C0102, nullptr, "ReceiveDataTimeout"},
23 {0x000D0146, nullptr, "SetProxy"},
24 {0x000E0040, nullptr, "SetProxyDefault"},
25 {0x000F00C4, nullptr, "SetBasicAuthorization"},
26 {0x00100080, nullptr, "SetSocketBufferSize"},
27 {0x001100C4, nullptr, "AddRequestHeader"},
28 {0x001200C4, nullptr, "AddPostDataAscii"},
29 {0x001300C4, nullptr, "AddPostDataBinary"},
30 {0x00140082, nullptr, "AddPostDataRaw"},
31 {0x00150080, nullptr, "SetPostDataType"},
32 {0x001600C4, nullptr, "SendPostDataAscii"},
33 {0x00170144, nullptr, "SendPostDataAsciiTimeout"},
34 {0x001800C4, nullptr, "SendPostDataBinary"},
35 {0x00190144, nullptr, "SendPostDataBinaryTimeout"},
36 {0x001A0082, nullptr, "SendPostDataRaw"},
37 {0x001B0102, nullptr, "SendPOSTDataRawTimeout"},
38 {0x001C0080, nullptr, "SetPostDataEncoding"},
39 {0x001D0040, nullptr, "NotifyFinishSendPostData"},
40 {0x001E00C4, nullptr, "GetResponseHeader"},
41 {0x001F0144, nullptr, "GetResponseHeaderTimeout"},
42 {0x00200082, nullptr, "GetResponseData"},
43 {0x00210102, nullptr, "GetResponseDataTimeout"},
44 {0x00220040, nullptr, "GetResponseStatusCode"},
45 {0x002300C0, nullptr, "GetResponseStatusCodeTimeout"},
46 {0x00240082, nullptr, "AddTrustedRootCA"},
47 {0x00250080, nullptr, "AddDefaultCert"},
48 {0x00260080, nullptr, "SelectRootCertChain"},
49 {0x002700C4, nullptr, "SetClientCert"},
50 {0x002B0080, nullptr, "SetSSLOpt"},
51 {0x002C0080, nullptr, "SetSSLClearOpt"},
52 {0x002D0000, nullptr, "CreateRootCertChain"},
53 {0x002E0040, nullptr, "DestroyRootCertChain"},
54 {0x002F0082, nullptr, "RootCertChainAddCert"},
55 {0x00300080, nullptr, "RootCertChainAddDefaultCert"},
56 {0x00310080, nullptr, "RootCertChainRemoveCert"},
57 {0x00320084, nullptr, "OpenClientCertContext"},
58 {0x00330040, nullptr, "OpenDefaultClientCertContext"},
59 {0x00340040, nullptr, "CloseClientCertContext"},
60 {0x00350186, nullptr, "SetDefaultProxy"},
61 {0x00360000, nullptr, "ClearDNSCache"},
62 {0x00370080, nullptr, "SetKeepAlive"},
63 {0x003800C0, nullptr, "SetPostDataTypeSize"},
64 {0x00390000, nullptr, "Finalize"},
65};
66
67HTTP_C::HTTP_C() {
68 Register(FunctionTable);
69}
70
71} // namespace HTTP
72} // namespace Service
diff --git a/src/core/hle/service/http_c.h b/src/core/hle/service/http_c.h
deleted file mode 100644
index cff279c02..000000000
--- a/src/core/hle/service/http_c.h
+++ /dev/null
@@ -1,22 +0,0 @@
1// Copyright 2014 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 "core/hle/service/service.h"
8
9namespace Service {
10namespace HTTP {
11
12class HTTP_C final : public Interface {
13public:
14 HTTP_C();
15
16 std::string GetPortName() const override {
17 return "http:C";
18 }
19};
20
21} // namespace HTTP
22} // namespace Service
diff --git a/src/core/hle/service/ir/extra_hid.cpp b/src/core/hle/service/ir/extra_hid.cpp
deleted file mode 100644
index e7acc17a5..000000000
--- a/src/core/hle/service/ir/extra_hid.cpp
+++ /dev/null
@@ -1,231 +0,0 @@
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 "common/alignment.h"
6#include "common/bit_field.h"
7#include "common/string_util.h"
8#include "core/core_timing.h"
9#include "core/hle/service/ir/extra_hid.h"
10#include "core/settings.h"
11
12namespace Service {
13namespace IR {
14
15enum class RequestID : u8 {
16 /**
17 * ConfigureHIDPolling request
18 * Starts HID input polling, or changes the polling interval if it is already started.
19 * Inputs:
20 * byte 0: request ID
21 * byte 1: polling interval in ms
22 * byte 2: unknown
23 */
24 ConfigureHIDPolling = 1,
25
26 /**
27 * ReadCalibrationData request
28 * Reads the calibration data stored in circle pad pro.
29 * Inputs:
30 * byte 0: request ID
31 * byte 1: expected response time in ms?
32 * byte 2-3: data offset (aligned to 0x10)
33 * byte 4-5: data size (aligned to 0x10)
34 */
35 ReadCalibrationData = 2,
36
37 // TODO(wwylele): there are three more request types (id = 3, 4 and 5)
38};
39
40enum class ResponseID : u8 {
41
42 /**
43 * PollHID response
44 * Sends current HID status
45 * Output:
46 * byte 0: response ID
47 * byte 1-3: Right circle pad position. This three bytes are two little-endian 12-bit
48 * fields. The first one is for x-axis and the second one is for y-axis.
49 * byte 4: bit[0:4] battery level; bit[5] ZL button; bit[6] ZR button; bit[7] R button
50 * Note that for the three button fields, the bit is set when the button is NOT pressed.
51 * byte 5: unknown
52 */
53 PollHID = 0x10,
54
55 /**
56 * ReadCalibrationData response
57 * Sends the calibration data reads from circle pad pro.
58 * Output:
59 * byte 0: resonse ID
60 * byte 1-2: data offset (aligned to 0x10)
61 * byte 3-4: data size (aligned to 0x10)
62 * byte 5-...: calibration data
63 */
64 ReadCalibrationData = 0x11,
65};
66
67ExtraHID::ExtraHID(SendFunc send_func) : IRDevice(send_func) {
68 LoadInputDevices();
69
70 // The data below was retrieved from a New 3DS
71 // TODO(wwylele): this data is probably writable (via request 3?) and thus should be saved to
72 // and loaded from somewhere.
73 calibration_data = std::array<u8, 0x40>{{
74 // 0x00
75 0x00, 0x00, 0x08, 0x80, 0x85, 0xEB, 0x11, 0x3F,
76 // 0x08
77 0x85, 0xEB, 0x11, 0x3F, 0xFF, 0xFF, 0xFF, 0xF5,
78 // 0x10
79 0xFF, 0x00, 0x08, 0x80, 0x85, 0xEB, 0x11, 0x3F,
80 // 0x18
81 0x85, 0xEB, 0x11, 0x3F, 0xFF, 0xFF, 0xFF, 0x65,
82 // 0x20
83 0xFF, 0x00, 0x08, 0x80, 0x85, 0xEB, 0x11, 0x3F,
84 // 0x28
85 0x85, 0xEB, 0x11, 0x3F, 0xFF, 0xFF, 0xFF, 0x65,
86 // 0x30
87 0xFF, 0x00, 0x08, 0x80, 0x85, 0xEB, 0x11, 0x3F,
88 // 0x38
89 0x85, 0xEB, 0x11, 0x3F, 0xFF, 0xFF, 0xFF, 0x65,
90 }};
91
92 hid_polling_callback_id =
93 CoreTiming::RegisterEvent("ExtraHID::SendHIDStatus", [this](u64, int cycles_late) {
94 SendHIDStatus();
95 CoreTiming::ScheduleEvent(msToCycles(hid_period) - cycles_late,
96 hid_polling_callback_id);
97 });
98}
99
100ExtraHID::~ExtraHID() {
101 OnDisconnect();
102}
103
104void ExtraHID::OnConnect() {}
105
106void ExtraHID::OnDisconnect() {
107 CoreTiming::UnscheduleEvent(hid_polling_callback_id, 0);
108}
109
110void ExtraHID::HandleConfigureHIDPollingRequest(const std::vector<u8>& request) {
111 if (request.size() != 3) {
112 LOG_ERROR(Service_IR, "Wrong request size (%zu): %s", request.size(),
113 Common::ArrayToString(request.data(), request.size()).c_str());
114 return;
115 }
116
117 // Change HID input polling interval
118 CoreTiming::UnscheduleEvent(hid_polling_callback_id, 0);
119 hid_period = request[1];
120 CoreTiming::ScheduleEvent(msToCycles(hid_period), hid_polling_callback_id);
121}
122
123void ExtraHID::HandleReadCalibrationDataRequest(const std::vector<u8>& request_buf) {
124 struct ReadCalibrationDataRequest {
125 RequestID request_id;
126 u8 expected_response_time;
127 u16_le offset;
128 u16_le size;
129 };
130 static_assert(sizeof(ReadCalibrationDataRequest) == 6,
131 "ReadCalibrationDataRequest has wrong size");
132
133 if (request_buf.size() != sizeof(ReadCalibrationDataRequest)) {
134 LOG_ERROR(Service_IR, "Wrong request size (%zu): %s", request_buf.size(),
135 Common::ArrayToString(request_buf.data(), request_buf.size()).c_str());
136 return;
137 }
138
139 ReadCalibrationDataRequest request;
140 std::memcpy(&request, request_buf.data(), sizeof(request));
141
142 const u16 offset = Common::AlignDown(request.offset, 16);
143 const u16 size = Common::AlignDown(request.size, 16);
144
145 if (offset + size > calibration_data.size()) {
146 LOG_ERROR(Service_IR, "Read beyond the end of calibration data! (offset=%u, size=%u)",
147 offset, size);
148 return;
149 }
150
151 std::vector<u8> response(5);
152 response[0] = static_cast<u8>(ResponseID::ReadCalibrationData);
153 std::memcpy(&response[1], &request.offset, sizeof(request.offset));
154 std::memcpy(&response[3], &request.size, sizeof(request.size));
155 response.insert(response.end(), calibration_data.begin() + offset,
156 calibration_data.begin() + offset + size);
157 Send(response);
158}
159
160void ExtraHID::OnReceive(const std::vector<u8>& data) {
161 switch (static_cast<RequestID>(data[0])) {
162 case RequestID::ConfigureHIDPolling:
163 HandleConfigureHIDPollingRequest(data);
164 break;
165 case RequestID::ReadCalibrationData:
166 HandleReadCalibrationDataRequest(data);
167 break;
168 default:
169 LOG_ERROR(Service_IR, "Unknown request: %s",
170 Common::ArrayToString(data.data(), data.size()).c_str());
171 break;
172 }
173}
174
175void ExtraHID::SendHIDStatus() {
176 if (is_device_reload_pending.exchange(false))
177 LoadInputDevices();
178
179 struct {
180 union {
181 BitField<0, 8, u32_le> header;
182 BitField<8, 12, u32_le> c_stick_x;
183 BitField<20, 12, u32_le> c_stick_y;
184 } c_stick;
185 union {
186 BitField<0, 5, u8> battery_level;
187 BitField<5, 1, u8> zl_not_held;
188 BitField<6, 1, u8> zr_not_held;
189 BitField<7, 1, u8> r_not_held;
190 } buttons;
191 u8 unknown;
192 } response;
193 static_assert(sizeof(response) == 6, "HID status response has wrong size!");
194
195 constexpr int C_STICK_CENTER = 0x800;
196 // TODO(wwylele): this value is not accurately measured. We currently assume that the axis can
197 // take values in the whole range of a 12-bit integer.
198 constexpr int C_STICK_RADIUS = 0x7FF;
199
200 float x, y;
201 std::tie(x, y) = c_stick->GetStatus();
202
203 response.c_stick.header.Assign(static_cast<u8>(ResponseID::PollHID));
204 response.c_stick.c_stick_x.Assign(static_cast<u32>(C_STICK_CENTER + C_STICK_RADIUS * x));
205 response.c_stick.c_stick_y.Assign(static_cast<u32>(C_STICK_CENTER + C_STICK_RADIUS * y));
206 response.buttons.battery_level.Assign(0x1F);
207 response.buttons.zl_not_held.Assign(!zl->GetStatus());
208 response.buttons.zr_not_held.Assign(!zr->GetStatus());
209 response.buttons.r_not_held.Assign(1);
210 response.unknown = 0;
211
212 std::vector<u8> response_buffer(sizeof(response));
213 memcpy(response_buffer.data(), &response, sizeof(response));
214 Send(response_buffer);
215}
216
217void ExtraHID::RequestInputDevicesReload() {
218 is_device_reload_pending.store(true);
219}
220
221void ExtraHID::LoadInputDevices() {
222 zl = Input::CreateDevice<Input::ButtonDevice>(
223 Settings::values.buttons[Settings::NativeButton::ZL]);
224 zr = Input::CreateDevice<Input::ButtonDevice>(
225 Settings::values.buttons[Settings::NativeButton::ZR]);
226 c_stick = Input::CreateDevice<Input::AnalogDevice>(
227 Settings::values.analogs[Settings::NativeAnalog::CStick]);
228}
229
230} // namespace IR
231} // namespace Service
diff --git a/src/core/hle/service/ir/extra_hid.h b/src/core/hle/service/ir/extra_hid.h
deleted file mode 100644
index a2459a73a..000000000
--- a/src/core/hle/service/ir/extra_hid.h
+++ /dev/null
@@ -1,48 +0,0 @@
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 <array>
8#include <atomic>
9#include "core/frontend/input.h"
10#include "core/hle/service/ir/ir_user.h"
11
12namespace Service {
13namespace IR {
14
15/**
16 * An IRDevice emulating Circle Pad Pro or New 3DS additional HID hardware.
17 * This device sends periodic udates at a rate configured by the 3DS, and sends calibration data if
18 * requested.
19 */
20class ExtraHID final : public IRDevice {
21public:
22 explicit ExtraHID(SendFunc send_func);
23 ~ExtraHID();
24
25 void OnConnect() override;
26 void OnDisconnect() override;
27 void OnReceive(const std::vector<u8>& data) override;
28
29 /// Requests input devices reload from current settings. Called when the input settings change.
30 void RequestInputDevicesReload();
31
32private:
33 void SendHIDStatus();
34 void HandleConfigureHIDPollingRequest(const std::vector<u8>& request);
35 void HandleReadCalibrationDataRequest(const std::vector<u8>& request);
36 void LoadInputDevices();
37
38 u8 hid_period;
39 int hid_polling_callback_id;
40 std::array<u8, 0x40> calibration_data;
41 std::unique_ptr<Input::ButtonDevice> zl;
42 std::unique_ptr<Input::ButtonDevice> zr;
43 std::unique_ptr<Input::AnalogDevice> c_stick;
44 std::atomic<bool> is_device_reload_pending;
45};
46
47} // namespace IR
48} // namespace Service
diff --git a/src/core/hle/service/ir/ir.cpp b/src/core/hle/service/ir/ir.cpp
deleted file mode 100644
index f06dd552f..000000000
--- a/src/core/hle/service/ir/ir.cpp
+++ /dev/null
@@ -1,35 +0,0 @@
1// Copyright 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/ir/ir.h"
6#include "core/hle/service/ir/ir_rst.h"
7#include "core/hle/service/ir/ir_u.h"
8#include "core/hle/service/ir/ir_user.h"
9#include "core/hle/service/service.h"
10
11namespace Service {
12namespace IR {
13
14void Init() {
15 AddService(new IR_RST_Interface);
16 AddService(new IR_U_Interface);
17 AddService(new IR_User_Interface);
18
19 InitUser();
20 InitRST();
21}
22
23void Shutdown() {
24 ShutdownUser();
25 ShutdownRST();
26}
27
28void ReloadInputDevices() {
29 ReloadInputDevicesUser();
30 ReloadInputDevicesRST();
31}
32
33} // namespace IR
34
35} // namespace Service
diff --git a/src/core/hle/service/ir/ir.h b/src/core/hle/service/ir/ir.h
deleted file mode 100644
index 6be3e950c..000000000
--- a/src/core/hle/service/ir/ir.h
+++ /dev/null
@@ -1,23 +0,0 @@
1// Copyright 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7namespace Service {
8
9class Interface;
10
11namespace IR {
12
13/// Initialize IR service
14void Init();
15
16/// Shutdown IR service
17void Shutdown();
18
19/// Reload input devices. Used when input configuration changed
20void ReloadInputDevices();
21
22} // namespace IR
23} // namespace Service
diff --git a/src/core/hle/service/ir/ir_rst.cpp b/src/core/hle/service/ir/ir_rst.cpp
deleted file mode 100644
index 0912d5756..000000000
--- a/src/core/hle/service/ir/ir_rst.cpp
+++ /dev/null
@@ -1,221 +0,0 @@
1// Copyright 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <atomic>
6#include "common/bit_field.h"
7#include "core/core_timing.h"
8#include "core/frontend/input.h"
9#include "core/hle/ipc_helpers.h"
10#include "core/hle/kernel/event.h"
11#include "core/hle/kernel/shared_memory.h"
12#include "core/hle/service/hid/hid.h"
13#include "core/hle/service/ir/ir.h"
14#include "core/hle/service/ir/ir_rst.h"
15#include "core/settings.h"
16
17namespace Service {
18namespace IR {
19
20union PadState {
21 u32_le hex{};
22
23 BitField<14, 1, u32_le> zl;
24 BitField<15, 1, u32_le> zr;
25
26 BitField<24, 1, u32_le> c_stick_right;
27 BitField<25, 1, u32_le> c_stick_left;
28 BitField<26, 1, u32_le> c_stick_up;
29 BitField<27, 1, u32_le> c_stick_down;
30};
31
32struct PadDataEntry {
33 PadState current_state;
34 PadState delta_additions;
35 PadState delta_removals;
36
37 s16_le c_stick_x;
38 s16_le c_stick_y;
39};
40
41struct SharedMem {
42 u64_le index_reset_ticks; ///< CPU tick count for when HID module updated entry index 0
43 u64_le index_reset_ticks_previous; ///< Previous `index_reset_ticks`
44 u32_le index;
45 INSERT_PADDING_WORDS(1);
46 std::array<PadDataEntry, 8> entries; ///< Last 8 pad entries
47};
48
49static_assert(sizeof(SharedMem) == 0x98, "SharedMem has wrong size!");
50
51static Kernel::SharedPtr<Kernel::Event> update_event;
52static Kernel::SharedPtr<Kernel::SharedMemory> shared_memory;
53static u32 next_pad_index;
54static int update_callback_id;
55static std::unique_ptr<Input::ButtonDevice> zl_button;
56static std::unique_ptr<Input::ButtonDevice> zr_button;
57static std::unique_ptr<Input::AnalogDevice> c_stick;
58static std::atomic<bool> is_device_reload_pending;
59static bool raw_c_stick;
60static int update_period;
61
62static void LoadInputDevices() {
63 zl_button = Input::CreateDevice<Input::ButtonDevice>(
64 Settings::values.buttons[Settings::NativeButton::ZL]);
65 zr_button = Input::CreateDevice<Input::ButtonDevice>(
66 Settings::values.buttons[Settings::NativeButton::ZR]);
67 c_stick = Input::CreateDevice<Input::AnalogDevice>(
68 Settings::values.analogs[Settings::NativeAnalog::CStick]);
69}
70
71static void UnloadInputDevices() {
72 zl_button = nullptr;
73 zr_button = nullptr;
74 c_stick = nullptr;
75}
76
77static void UpdateCallback(u64 userdata, int cycles_late) {
78 SharedMem* mem = reinterpret_cast<SharedMem*>(shared_memory->GetPointer());
79
80 if (is_device_reload_pending.exchange(false))
81 LoadInputDevices();
82
83 PadState state;
84 state.zl.Assign(zl_button->GetStatus());
85 state.zr.Assign(zr_button->GetStatus());
86
87 // Get current c-stick position and update c-stick direction
88 float c_stick_x_f, c_stick_y_f;
89 std::tie(c_stick_x_f, c_stick_y_f) = c_stick->GetStatus();
90 constexpr int MAX_CSTICK_RADIUS = 0x9C; // Max value for a c-stick radius
91 const s16 c_stick_x = static_cast<s16>(c_stick_x_f * MAX_CSTICK_RADIUS);
92 const s16 c_stick_y = static_cast<s16>(c_stick_y_f * MAX_CSTICK_RADIUS);
93
94 if (!raw_c_stick) {
95 const HID::DirectionState direction = HID::GetStickDirectionState(c_stick_x, c_stick_y);
96 state.c_stick_up.Assign(direction.up);
97 state.c_stick_down.Assign(direction.down);
98 state.c_stick_left.Assign(direction.left);
99 state.c_stick_right.Assign(direction.right);
100 }
101
102 // TODO (wwylele): implement raw C-stick data for raw_c_stick = true
103
104 const u32 last_entry_index = mem->index;
105 mem->index = next_pad_index;
106 next_pad_index = (next_pad_index + 1) % mem->entries.size();
107
108 // Get the previous Pad state
109 PadState old_state{mem->entries[last_entry_index].current_state};
110
111 // Compute bitmask with 1s for bits different from the old state
112 PadState changed = {state.hex ^ old_state.hex};
113
114 // Get the current Pad entry
115 PadDataEntry& pad_entry = mem->entries[mem->index];
116
117 // Update entry properties
118 pad_entry.current_state.hex = state.hex;
119 pad_entry.delta_additions.hex = changed.hex & state.hex;
120 pad_entry.delta_removals.hex = changed.hex & old_state.hex;
121 pad_entry.c_stick_x = c_stick_x;
122 pad_entry.c_stick_y = c_stick_y;
123
124 // If we just updated index 0, provide a new timestamp
125 if (mem->index == 0) {
126 mem->index_reset_ticks_previous = mem->index_reset_ticks;
127 mem->index_reset_ticks = CoreTiming::GetTicks();
128 }
129
130 update_event->Signal();
131
132 // Reschedule recurrent event
133 CoreTiming::ScheduleEvent(msToCycles(update_period) - cycles_late, update_callback_id);
134}
135
136/**
137 * IR::GetHandles service function
138 * Outputs:
139 * 1 : Result of function, 0 on success, otherwise error code
140 * 2 : Translate header, used by the ARM11-kernel
141 * 3 : Shared memory handle
142 * 4 : Event handle
143 */
144static void GetHandles(Interface* self) {
145 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x01, 0, 0);
146 IPC::RequestBuilder rb = rp.MakeBuilder(1, 3);
147 rb.Push(RESULT_SUCCESS);
148 rb.PushMoveHandles(Kernel::g_handle_table.Create(Service::IR::shared_memory).Unwrap(),
149 Kernel::g_handle_table.Create(Service::IR::update_event).Unwrap());
150}
151
152/**
153 * IR::Initialize service function
154 * Inputs:
155 * 1 : pad state update period in ms
156 * 2 : bool output raw c-stick data
157 */
158static void Initialize(Interface* self) {
159 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x02, 2, 0);
160 update_period = static_cast<int>(rp.Pop<u32>());
161 raw_c_stick = rp.Pop<bool>();
162
163 if (raw_c_stick)
164 LOG_ERROR(Service_IR, "raw C-stick data is not implemented!");
165
166 next_pad_index = 0;
167 is_device_reload_pending.store(true);
168 CoreTiming::ScheduleEvent(msToCycles(update_period), update_callback_id);
169
170 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
171 rb.Push(RESULT_SUCCESS);
172
173 LOG_DEBUG(Service_IR, "called. update_period=%d, raw_c_stick=%d", update_period, raw_c_stick);
174}
175
176static void Shutdown(Interface* self) {
177 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x03, 1, 0);
178
179 CoreTiming::UnscheduleEvent(update_callback_id, 0);
180 UnloadInputDevices();
181
182 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
183 rb.Push(RESULT_SUCCESS);
184 LOG_DEBUG(Service_IR, "called");
185}
186
187const Interface::FunctionInfo FunctionTable[] = {
188 {0x00010000, GetHandles, "GetHandles"},
189 {0x00020080, Initialize, "Initialize"},
190 {0x00030000, Shutdown, "Shutdown"},
191 {0x00090000, nullptr, "WriteToTwoFields"},
192};
193
194IR_RST_Interface::IR_RST_Interface() {
195 Register(FunctionTable);
196}
197
198void InitRST() {
199 using namespace Kernel;
200 // Note: these two kernel objects are even available before Initialize service function is
201 // called.
202 shared_memory =
203 SharedMemory::Create(nullptr, 0x1000, MemoryPermission::ReadWrite, MemoryPermission::Read,
204 0, MemoryRegion::BASE, "IRRST:SharedMemory");
205 update_event = Event::Create(ResetType::OneShot, "IRRST:UpdateEvent");
206
207 update_callback_id = CoreTiming::RegisterEvent("IRRST:UpdateCallBack", UpdateCallback);
208}
209
210void ShutdownRST() {
211 shared_memory = nullptr;
212 update_event = nullptr;
213 UnloadInputDevices();
214}
215
216void ReloadInputDevicesRST() {
217 is_device_reload_pending.store(true);
218}
219
220} // namespace IR
221} // namespace Service
diff --git a/src/core/hle/service/ir/ir_rst.h b/src/core/hle/service/ir/ir_rst.h
deleted file mode 100644
index d932bb7e5..000000000
--- a/src/core/hle/service/ir/ir_rst.h
+++ /dev/null
@@ -1,28 +0,0 @@
1// Copyright 2014 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 "core/hle/service/service.h"
8
9namespace Service {
10namespace IR {
11
12class IR_RST_Interface : public Service::Interface {
13public:
14 IR_RST_Interface();
15
16 std::string GetPortName() const override {
17 return "ir:rst";
18 }
19};
20
21void InitRST();
22void ShutdownRST();
23
24/// Reload input devices. Used when input configuration changed
25void ReloadInputDevicesRST();
26
27} // namespace IR
28} // namespace Service
diff --git a/src/core/hle/service/ir/ir_u.cpp b/src/core/hle/service/ir/ir_u.cpp
deleted file mode 100644
index ce00d5732..000000000
--- a/src/core/hle/service/ir/ir_u.cpp
+++ /dev/null
@@ -1,38 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/ir/ir_u.h"
6
7namespace Service {
8namespace IR {
9
10const Interface::FunctionInfo FunctionTable[] = {
11 // clang-format off
12 {0x00010000, nullptr, "Initialize"},
13 {0x00020000, nullptr, "Shutdown"},
14 {0x00030042, nullptr, "StartSendTransfer"},
15 {0x00040000, nullptr, "WaitSendTransfer"},
16 {0x000500C2, nullptr, "StartRecvTransfer"},
17 {0x00060000, nullptr, "WaitRecvTransfer"},
18 {0x00070080, nullptr, "GetRecvTransferCount"},
19 {0x00080000, nullptr, "GetSendState"},
20 {0x00090040, nullptr, "SetBitRate"},
21 {0x000A0000, nullptr, "GetBitRate"},
22 {0x000B0040, nullptr, "SetIRLEDState"},
23 {0x000C0000, nullptr, "GetIRLEDRecvState"},
24 {0x000D0000, nullptr, "GetSendFinishedEvent"},
25 {0x000E0000, nullptr, "GetRecvFinishedEvent"},
26 {0x000F0000, nullptr, "GetTransferState"},
27 {0x00100000, nullptr, "GetErrorStatus"},
28 {0x00110040, nullptr, "SetSleepModeActive"},
29 {0x00120040, nullptr, "SetSleepModeState"},
30 // clang-format on
31};
32
33IR_U_Interface::IR_U_Interface() {
34 Register(FunctionTable);
35}
36
37} // namespace IR
38} // namespace Service
diff --git a/src/core/hle/service/ir/ir_u.h b/src/core/hle/service/ir/ir_u.h
deleted file mode 100644
index 056d2ce1a..000000000
--- a/src/core/hle/service/ir/ir_u.h
+++ /dev/null
@@ -1,22 +0,0 @@
1// Copyright 2014 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 "core/hle/service/service.h"
8
9namespace Service {
10namespace IR {
11
12class IR_U_Interface : public Service::Interface {
13public:
14 IR_U_Interface();
15
16 std::string GetPortName() const override {
17 return "ir:u";
18 }
19};
20
21} // namespace IR
22} // namespace Service
diff --git a/src/core/hle/service/ir/ir_user.cpp b/src/core/hle/service/ir/ir_user.cpp
deleted file mode 100644
index fbdf7a465..000000000
--- a/src/core/hle/service/ir/ir_user.cpp
+++ /dev/null
@@ -1,558 +0,0 @@
1// Copyright 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <memory>
6#include <boost/crc.hpp>
7#include <boost/optional.hpp>
8#include "common/string_util.h"
9#include "common/swap.h"
10#include "core/hle/ipc_helpers.h"
11#include "core/hle/kernel/event.h"
12#include "core/hle/kernel/shared_memory.h"
13#include "core/hle/service/ir/extra_hid.h"
14#include "core/hle/service/ir/ir.h"
15#include "core/hle/service/ir/ir_user.h"
16
17namespace Service {
18namespace IR {
19
20// This is a header that will present in the ir:USER shared memory if it is initialized with
21// InitializeIrNopShared service function. Otherwise the shared memory doesn't have this header if
22// it is initialized with InitializeIrNop service function.
23struct SharedMemoryHeader {
24 u32_le latest_receive_error_result;
25 u32_le latest_send_error_result;
26 // TODO(wwylele): for these fields below, make them enum when the meaning of values is known.
27 u8 connection_status;
28 u8 trying_to_connect_status;
29 u8 connection_role;
30 u8 machine_id;
31 u8 connected;
32 u8 network_id;
33 u8 initialized;
34 u8 unknown;
35
36 // This is not the end of the shared memory. It is followed by a receive buffer and a send
37 // buffer. We handle receive buffer in the BufferManager class. For the send buffer, because
38 // games usually don't access it, we don't emulate it.
39};
40static_assert(sizeof(SharedMemoryHeader) == 16, "SharedMemoryHeader has wrong size!");
41
42/**
43 * A manager of the send/receive buffers in the shared memory. Currently it is only used for the
44 * receive buffer.
45 *
46 * A buffer consists of three parts:
47 * - BufferInfo: stores available count of packets, and their position in the PacketInfo
48 * circular queue.
49 * - PacketInfo circular queue: stores the position of each avaiable packets in the Packet data
50 * buffer. Each entry is a pair of {offset, size}.
51 * - Packet data circular buffer: stores the actual data of packets.
52 *
53 * IR packets can be put into and get from the buffer.
54 *
55 * When a new packet is put into the buffer, its data is put into the data circular buffer,
56 * following the end of previous packet data. A new entry is also added to the PacketInfo circular
57 * queue pointing to the added packet data. Then BufferInfo is updated.
58 *
59 * Packets can be released from the other end of the buffer. When releasing a packet, the front
60 * entry in thePacketInfo circular queue is removed, and as a result the corresponding memory in the
61 * data circular buffer is also released. BufferInfo is updated as well.
62 *
63 * The client application usually has a similar manager constructed over the same shared memory
64 * region, performing the same put/get/release operation. This way the client and the service
65 * communicate via a pair of manager of the same buffer.
66 *
67 * TODO(wwylele): implement Get function, which is used by ReceiveIrnop service function.
68 */
69class BufferManager {
70public:
71 BufferManager(Kernel::SharedPtr<Kernel::SharedMemory> shared_memory_, u32 info_offset_,
72 u32 buffer_offset_, u32 max_packet_count_, u32 buffer_size)
73 : shared_memory(shared_memory_), info_offset(info_offset_), buffer_offset(buffer_offset_),
74 max_packet_count(max_packet_count_),
75 max_data_size(buffer_size - sizeof(PacketInfo) * max_packet_count_) {
76 UpdateBufferInfo();
77 }
78
79 /**
80 * Puts a packet to the head of the buffer.
81 * @params packet The data of the packet to put.
82 * @returns whether the operation is successful.
83 */
84 bool Put(const std::vector<u8>& packet) {
85 if (info.packet_count == max_packet_count)
86 return false;
87
88 u32 write_offset;
89
90 // finds free space offset in data buffer
91 if (info.packet_count == 0) {
92 write_offset = 0;
93 if (packet.size() > max_data_size)
94 return false;
95 } else {
96 const u32 last_index = (info.end_index + max_packet_count - 1) % max_packet_count;
97 const PacketInfo first = GetPacketInfo(info.begin_index);
98 const PacketInfo last = GetPacketInfo(last_index);
99 write_offset = (last.offset + last.size) % max_data_size;
100 const u32 free_space = (first.offset + max_data_size - write_offset) % max_data_size;
101 if (packet.size() > free_space)
102 return false;
103 }
104
105 // writes packet info
106 PacketInfo packet_info{write_offset, static_cast<u32>(packet.size())};
107 SetPacketInfo(info.end_index, packet_info);
108
109 // writes packet data
110 for (size_t i = 0; i < packet.size(); ++i) {
111 *GetDataBufferPointer((write_offset + i) % max_data_size) = packet[i];
112 }
113
114 // updates buffer info
115 info.end_index++;
116 info.end_index %= max_packet_count;
117 info.packet_count++;
118 UpdateBufferInfo();
119 return true;
120 }
121
122 /**
123 * Release packets from the tail of the buffer
124 * @params count Numbers of packets to release.
125 * @returns whether the operation is successful.
126 */
127 bool Release(u32 count) {
128 if (info.packet_count < count)
129 return false;
130
131 info.packet_count -= count;
132 info.begin_index += count;
133 info.begin_index %= max_packet_count;
134 UpdateBufferInfo();
135 return true;
136 }
137
138private:
139 struct BufferInfo {
140 u32_le begin_index;
141 u32_le end_index;
142 u32_le packet_count;
143 u32_le unknown;
144 };
145 static_assert(sizeof(BufferInfo) == 16, "BufferInfo has wrong size!");
146
147 struct PacketInfo {
148 u32_le offset;
149 u32_le size;
150 };
151 static_assert(sizeof(PacketInfo) == 8, "PacketInfo has wrong size!");
152
153 u8* GetPacketInfoPointer(u32 index) {
154 return shared_memory->GetPointer(buffer_offset + sizeof(PacketInfo) * index);
155 }
156
157 void SetPacketInfo(u32 index, const PacketInfo& packet_info) {
158 memcpy(GetPacketInfoPointer(index), &packet_info, sizeof(PacketInfo));
159 }
160
161 PacketInfo GetPacketInfo(u32 index) {
162 PacketInfo packet_info;
163 memcpy(&packet_info, GetPacketInfoPointer(index), sizeof(PacketInfo));
164 return packet_info;
165 }
166
167 u8* GetDataBufferPointer(u32 offset) {
168 return shared_memory->GetPointer(buffer_offset + sizeof(PacketInfo) * max_packet_count +
169 offset);
170 }
171
172 void UpdateBufferInfo() {
173 if (info_offset) {
174 memcpy(shared_memory->GetPointer(info_offset), &info, sizeof(info));
175 }
176 }
177
178 BufferInfo info{0, 0, 0, 0};
179 Kernel::SharedPtr<Kernel::SharedMemory> shared_memory;
180 u32 info_offset;
181 u32 buffer_offset;
182 u32 max_packet_count;
183 u32 max_data_size;
184};
185
186static Kernel::SharedPtr<Kernel::Event> conn_status_event, send_event, receive_event;
187static Kernel::SharedPtr<Kernel::SharedMemory> shared_memory;
188static std::unique_ptr<ExtraHID> extra_hid;
189static IRDevice* connected_device;
190static boost::optional<BufferManager> receive_buffer;
191
192/// Wraps the payload into packet and puts it to the receive buffer
193static void PutToReceive(const std::vector<u8>& payload) {
194 LOG_TRACE(Service_IR, "called, data=%s",
195 Common::ArrayToString(payload.data(), payload.size()).c_str());
196 size_t size = payload.size();
197
198 std::vector<u8> packet;
199
200 // Builds packet header. For the format info:
201 // https://www.3dbrew.org/wiki/IRUSER_Shared_Memory#Packet_structure
202
203 // fixed value
204 packet.push_back(0xA5);
205 // destination network ID
206 u8 network_id = *(shared_memory->GetPointer(offsetof(SharedMemoryHeader, network_id)));
207 packet.push_back(network_id);
208
209 // puts the size info.
210 // The highest bit of the first byte is unknown, which is set to zero here. The second highest
211 // bit is a flag that determines whether the size info is in extended form. If the packet size
212 // can be represent within 6 bits, the short form (1 byte) of size info is chosen, the size is
213 // put to the lower bits of this byte, and the flag is clear. If the packet size cannot be
214 // represent within 6 bits, the extended form (2 bytes) is chosen, the lower 8 bits of the size
215 // is put to the second byte, the higher bits of the size is put to the lower bits of the first
216 // byte, and the flag is set. Note that the packet size must be within 14 bits due to this
217 // format restriction, or it will overlap with the flag bit.
218 if (size < 0x40) {
219 packet.push_back(static_cast<u8>(size));
220 } else if (size < 0x4000) {
221 packet.push_back(static_cast<u8>(size >> 8) | 0x40);
222 packet.push_back(static_cast<u8>(size));
223 } else {
224 ASSERT(false);
225 }
226
227 // puts the payload
228 packet.insert(packet.end(), payload.begin(), payload.end());
229
230 // calculates CRC and puts to the end
231 packet.push_back(boost::crc<8, 0x07, 0, 0, false, false>(packet.data(), packet.size()));
232
233 if (receive_buffer->Put(packet)) {
234 receive_event->Signal();
235 } else {
236 LOG_ERROR(Service_IR, "receive buffer is full!");
237 }
238}
239
240/**
241 * IR::InitializeIrNopShared service function
242 * Initializes ir:USER service with a user provided shared memory. The shared memory is configured
243 * to shared mode (with SharedMemoryHeader at the beginning of the shared memory).
244 * Inputs:
245 * 1 : Size of shared memory
246 * 2 : Recv buffer size
247 * 3 : Recv buffer packet count
248 * 4 : Send buffer size
249 * 5 : Send buffer packet count
250 * 6 : BaudRate (u8)
251 * 7 : 0 (Handle descriptor)
252 * 8 : Handle of shared memory
253 * Outputs:
254 * 1 : Result of function, 0 on success, otherwise error code
255 */
256static void InitializeIrNopShared(Interface* self) {
257 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x18, 6, 2);
258 const u32 shared_buff_size = rp.Pop<u32>();
259 const u32 recv_buff_size = rp.Pop<u32>();
260 const u32 recv_buff_packet_count = rp.Pop<u32>();
261 const u32 send_buff_size = rp.Pop<u32>();
262 const u32 send_buff_packet_count = rp.Pop<u32>();
263 const u8 baud_rate = rp.Pop<u8>();
264 const Kernel::Handle handle = rp.PopHandle();
265
266 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
267
268 shared_memory = Kernel::g_handle_table.Get<Kernel::SharedMemory>(handle);
269 if (!shared_memory) {
270 LOG_CRITICAL(Service_IR, "invalid shared memory handle 0x%08X", handle);
271 rb.Push(IPC::ERR_INVALID_HANDLE);
272 return;
273 }
274 shared_memory->name = "IR_USER: shared memory";
275
276 receive_buffer =
277 BufferManager(shared_memory, 0x10, 0x20, recv_buff_packet_count, recv_buff_size);
278 SharedMemoryHeader shared_memory_init{};
279 shared_memory_init.initialized = 1;
280 std::memcpy(shared_memory->GetPointer(), &shared_memory_init, sizeof(SharedMemoryHeader));
281
282 rb.Push(RESULT_SUCCESS);
283
284 LOG_INFO(Service_IR, "called, shared_buff_size=%u, recv_buff_size=%u, "
285 "recv_buff_packet_count=%u, send_buff_size=%u, "
286 "send_buff_packet_count=%u, baud_rate=%u, handle=0x%08X",
287 shared_buff_size, recv_buff_size, recv_buff_packet_count, send_buff_size,
288 send_buff_packet_count, baud_rate, handle);
289}
290
291/**
292 * IR::RequireConnection service function
293 * Searches for an IR device and connects to it. After connecting to the device, applications can
294 * use SendIrNop function, ReceiveIrNop function (or read from the buffer directly) to communicate
295 * with the device.
296 * Inputs:
297 * 1 : device ID? always 1 for circle pad pro
298 * Outputs:
299 * 1 : Result of function, 0 on success, otherwise error code
300 */
301static void RequireConnection(Interface* self) {
302 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x06, 1, 0);
303 const u8 device_id = rp.Pop<u8>();
304
305 u8* shared_memory_ptr = shared_memory->GetPointer();
306 if (device_id == 1) {
307 // These values are observed on a New 3DS. The meaning of them is unclear.
308 // TODO (wwylele): should assign network_id a (random?) number
309 shared_memory_ptr[offsetof(SharedMemoryHeader, connection_status)] = 2;
310 shared_memory_ptr[offsetof(SharedMemoryHeader, connection_role)] = 2;
311 shared_memory_ptr[offsetof(SharedMemoryHeader, connected)] = 1;
312
313 connected_device = extra_hid.get();
314 connected_device->OnConnect();
315 conn_status_event->Signal();
316 } else {
317 LOG_WARNING(Service_IR, "unknown device id %u. Won't connect.", device_id);
318 shared_memory_ptr[offsetof(SharedMemoryHeader, connection_status)] = 1;
319 shared_memory_ptr[offsetof(SharedMemoryHeader, trying_to_connect_status)] = 2;
320 }
321
322 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
323 rb.Push(RESULT_SUCCESS);
324
325 LOG_INFO(Service_IR, "called, device_id = %u", device_id);
326}
327
328/**
329 * IR::GetReceiveEvent service function
330 * Gets an event that is signaled when a packet is received from the IR device.
331 * Outputs:
332 * 1 : Result of function, 0 on success, otherwise error code
333 * 2 : 0 (Handle descriptor)
334 * 3 : Receive event handle
335 */
336void GetReceiveEvent(Interface* self) {
337 IPC::RequestBuilder rb(Kernel::GetCommandBuffer(), 0x0A, 1, 2);
338
339 rb.Push(RESULT_SUCCESS);
340 rb.PushCopyHandles(Kernel::g_handle_table.Create(Service::IR::receive_event).Unwrap());
341
342 LOG_INFO(Service_IR, "called");
343}
344
345/**
346 * IR::GetSendEvent service function
347 * Gets an event that is signaled when the sending of a packet is complete
348 * Outputs:
349 * 1 : Result of function, 0 on success, otherwise error code
350 * 2 : 0 (Handle descriptor)
351 * 3 : Send event handle
352 */
353void GetSendEvent(Interface* self) {
354 IPC::RequestBuilder rb(Kernel::GetCommandBuffer(), 0x0B, 1, 2);
355
356 rb.Push(RESULT_SUCCESS);
357 rb.PushCopyHandles(Kernel::g_handle_table.Create(Service::IR::send_event).Unwrap());
358
359 LOG_INFO(Service_IR, "called");
360}
361
362/**
363 * IR::Disconnect service function
364 * Disconnects from the current connected IR device.
365 * Outputs:
366 * 1 : Result of function, 0 on success, otherwise error code
367 */
368static void Disconnect(Interface* self) {
369 if (connected_device) {
370 connected_device->OnDisconnect();
371 connected_device = nullptr;
372 conn_status_event->Signal();
373 }
374
375 u8* shared_memory_ptr = shared_memory->GetPointer();
376 shared_memory_ptr[offsetof(SharedMemoryHeader, connection_status)] = 0;
377 shared_memory_ptr[offsetof(SharedMemoryHeader, connected)] = 0;
378
379 IPC::RequestBuilder rb(Kernel::GetCommandBuffer(), 0x09, 1, 0);
380 rb.Push(RESULT_SUCCESS);
381
382 LOG_INFO(Service_IR, "called");
383}
384
385/**
386 * IR::GetConnectionStatusEvent service function
387 * Gets an event that is signaled when the connection status is changed
388 * Outputs:
389 * 1 : Result of function, 0 on success, otherwise error code
390 * 2 : 0 (Handle descriptor)
391 * 3 : Connection Status Event handle
392 */
393static void GetConnectionStatusEvent(Interface* self) {
394 IPC::RequestBuilder rb(Kernel::GetCommandBuffer(), 0x0C, 1, 2);
395
396 rb.Push(RESULT_SUCCESS);
397 rb.PushCopyHandles(Kernel::g_handle_table.Create(Service::IR::conn_status_event).Unwrap());
398
399 LOG_INFO(Service_IR, "called");
400}
401
402/**
403 * IR::FinalizeIrNop service function
404 * Finalize ir:USER service.
405 * Outputs:
406 * 1 : Result of function, 0 on success, otherwise error code
407 */
408static void FinalizeIrNop(Interface* self) {
409 if (connected_device) {
410 connected_device->OnDisconnect();
411 connected_device = nullptr;
412 }
413
414 shared_memory = nullptr;
415 receive_buffer = boost::none;
416
417 IPC::RequestBuilder rb(Kernel::GetCommandBuffer(), 0x02, 1, 0);
418 rb.Push(RESULT_SUCCESS);
419
420 LOG_INFO(Service_IR, "called");
421}
422
423/**
424 * IR::SendIrNop service function
425 * Sends a packet to the connected IR device
426 * Inpus:
427 * 1 : Size of data to send
428 * 2 : 2 + (size << 14) (Static buffer descriptor)
429 * 3 : Data buffer address
430 * Outputs:
431 * 1 : Result of function, 0 on success, otherwise error code
432 */
433static void SendIrNop(Interface* self) {
434 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x0D, 1, 2);
435 const u32 size = rp.Pop<u32>();
436 const VAddr address = rp.PopStaticBuffer();
437
438 std::vector<u8> buffer(size);
439 Memory::ReadBlock(address, buffer.data(), size);
440
441 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
442 if (connected_device) {
443 connected_device->OnReceive(buffer);
444 send_event->Signal();
445 rb.Push(RESULT_SUCCESS);
446 } else {
447 LOG_ERROR(Service_IR, "not connected");
448 rb.Push(ResultCode(static_cast<ErrorDescription>(13), ErrorModule::IR,
449 ErrorSummary::InvalidState, ErrorLevel::Status));
450 }
451
452 LOG_TRACE(Service_IR, "called, data=%s", Common::ArrayToString(buffer.data(), size).c_str());
453}
454
455/**
456 * IR::ReleaseReceivedData function
457 * Release a specified amount of packet from the receive buffer. This is called after the
458 * application reads received packet from the buffer directly, to release the buffer space for
459 * future packets.
460 * Inpus:
461 * 1 : Number of packets to release
462 * Outputs:
463 * 1 : Result of function, 0 on success, otherwise error code
464 */
465static void ReleaseReceivedData(Interface* self) {
466 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x19, 1, 0);
467 u32 count = rp.Pop<u32>();
468
469 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
470
471 if (receive_buffer->Release(count)) {
472 rb.Push(RESULT_SUCCESS);
473 } else {
474 LOG_ERROR(Service_IR, "failed to release %u packets", count);
475 rb.Push(ResultCode(ErrorDescription::NoData, ErrorModule::IR, ErrorSummary::NotFound,
476 ErrorLevel::Status));
477 }
478
479 LOG_TRACE(Service_IR, "called, count=%u", count);
480}
481
482const Interface::FunctionInfo FunctionTable[] = {
483 {0x00010182, nullptr, "InitializeIrNop"},
484 {0x00020000, FinalizeIrNop, "FinalizeIrNop"},
485 {0x00030000, nullptr, "ClearReceiveBuffer"},
486 {0x00040000, nullptr, "ClearSendBuffer"},
487 {0x000500C0, nullptr, "WaitConnection"},
488 {0x00060040, RequireConnection, "RequireConnection"},
489 {0x000702C0, nullptr, "AutoConnection"},
490 {0x00080000, nullptr, "AnyConnection"},
491 {0x00090000, Disconnect, "Disconnect"},
492 {0x000A0000, GetReceiveEvent, "GetReceiveEvent"},
493 {0x000B0000, GetSendEvent, "GetSendEvent"},
494 {0x000C0000, GetConnectionStatusEvent, "GetConnectionStatusEvent"},
495 {0x000D0042, SendIrNop, "SendIrNop"},
496 {0x000E0042, nullptr, "SendIrNopLarge"},
497 {0x000F0040, nullptr, "ReceiveIrnop"},
498 {0x00100042, nullptr, "ReceiveIrnopLarge"},
499 {0x00110040, nullptr, "GetLatestReceiveErrorResult"},
500 {0x00120040, nullptr, "GetLatestSendErrorResult"},
501 {0x00130000, nullptr, "GetConnectionStatus"},
502 {0x00140000, nullptr, "GetTryingToConnectStatus"},
503 {0x00150000, nullptr, "GetReceiveSizeFreeAndUsed"},
504 {0x00160000, nullptr, "GetSendSizeFreeAndUsed"},
505 {0x00170000, nullptr, "GetConnectionRole"},
506 {0x00180182, InitializeIrNopShared, "InitializeIrNopShared"},
507 {0x00190040, ReleaseReceivedData, "ReleaseReceivedData"},
508 {0x001A0040, nullptr, "SetOwnMachineId"},
509};
510
511IR_User_Interface::IR_User_Interface() {
512 Register(FunctionTable);
513}
514
515void InitUser() {
516 using namespace Kernel;
517
518 shared_memory = nullptr;
519
520 conn_status_event = Event::Create(ResetType::OneShot, "IR:ConnectionStatusEvent");
521 send_event = Event::Create(ResetType::OneShot, "IR:SendEvent");
522 receive_event = Event::Create(ResetType::OneShot, "IR:ReceiveEvent");
523
524 receive_buffer = boost::none;
525
526 extra_hid = std::make_unique<ExtraHID>(PutToReceive);
527
528 connected_device = nullptr;
529}
530
531void ShutdownUser() {
532 if (connected_device) {
533 connected_device->OnDisconnect();
534 connected_device = nullptr;
535 }
536
537 extra_hid = nullptr;
538 receive_buffer = boost::none;
539 shared_memory = nullptr;
540 conn_status_event = nullptr;
541 send_event = nullptr;
542 receive_event = nullptr;
543}
544
545void ReloadInputDevicesUser() {
546 if (extra_hid)
547 extra_hid->RequestInputDevicesReload();
548}
549
550IRDevice::IRDevice(SendFunc send_func_) : send_func(send_func_) {}
551IRDevice::~IRDevice() = default;
552
553void IRDevice::Send(const std::vector<u8>& data) {
554 send_func(data);
555}
556
557} // namespace IR
558} // namespace Service
diff --git a/src/core/hle/service/ir/ir_user.h b/src/core/hle/service/ir/ir_user.h
deleted file mode 100644
index 930650406..000000000
--- a/src/core/hle/service/ir/ir_user.h
+++ /dev/null
@@ -1,58 +0,0 @@
1// Copyright 2015 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 <functional>
8#include "core/hle/service/service.h"
9
10namespace Service {
11namespace IR {
12
13/// An interface representing a device that can communicate with 3DS via ir:USER service
14class IRDevice {
15public:
16 /**
17 * A function object that implements the method to send data to the 3DS, which takes a vector of
18 * data to send.
19 */
20 using SendFunc = std::function<void(const std::vector<u8>& data)>;
21
22 explicit IRDevice(SendFunc send_func);
23 virtual ~IRDevice();
24
25 /// Called when connected with 3DS
26 virtual void OnConnect() = 0;
27
28 /// Called when disconnected from 3DS
29 virtual void OnDisconnect() = 0;
30
31 /// Called when data is received from the 3DS. This is invoked by the ir:USER send function.
32 virtual void OnReceive(const std::vector<u8>& data) = 0;
33
34protected:
35 /// Sends data to the 3DS. The actual sending method is specified in the constructor
36 void Send(const std::vector<u8>& data);
37
38private:
39 const SendFunc send_func;
40};
41
42class IR_User_Interface : public Service::Interface {
43public:
44 IR_User_Interface();
45
46 std::string GetPortName() const override {
47 return "ir:USER";
48 }
49};
50
51void InitUser();
52void ShutdownUser();
53
54/// Reload input devices. Used when input configuration changed
55void ReloadInputDevicesUser();
56
57} // namespace IR
58} // namespace Service
diff --git a/src/core/hle/service/ldr_ro/cro_helper.cpp b/src/core/hle/service/ldr_ro/cro_helper.cpp
deleted file mode 100644
index 6128f8a6c..000000000
--- a/src/core/hle/service/ldr_ro/cro_helper.cpp
+++ /dev/null
@@ -1,1495 +0,0 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/alignment.h"
6#include "common/logging/log.h"
7#include "common/scope_exit.h"
8#include "core/hle/service/ldr_ro/cro_helper.h"
9
10namespace Service {
11namespace LDR {
12
13static const ResultCode ERROR_BUFFER_TOO_SMALL = // 0xE0E12C1F
14 ResultCode(static_cast<ErrorDescription>(31), ErrorModule::RO, ErrorSummary::InvalidArgument,
15 ErrorLevel::Usage);
16
17static ResultCode CROFormatError(u32 description) {
18 return ResultCode(static_cast<ErrorDescription>(description), ErrorModule::RO,
19 ErrorSummary::WrongArgument, ErrorLevel::Permanent);
20}
21
22const std::array<int, 17> CROHelper::ENTRY_SIZE{{
23 1, // code
24 1, // data
25 1, // module name
26 sizeof(SegmentEntry), sizeof(ExportNamedSymbolEntry), sizeof(ExportIndexedSymbolEntry),
27 1, // export strings
28 sizeof(ExportTreeEntry), sizeof(ImportModuleEntry), sizeof(ExternalRelocationEntry),
29 sizeof(ImportNamedSymbolEntry), sizeof(ImportIndexedSymbolEntry),
30 sizeof(ImportAnonymousSymbolEntry),
31 1, // import strings
32 sizeof(StaticAnonymousSymbolEntry), sizeof(InternalRelocationEntry),
33 sizeof(StaticRelocationEntry),
34}};
35
36const std::array<CROHelper::HeaderField, 4> CROHelper::FIX_BARRIERS{{
37 Fix0Barrier, Fix1Barrier, Fix2Barrier, Fix3Barrier,
38}};
39
40VAddr CROHelper::SegmentTagToAddress(SegmentTag segment_tag) const {
41 u32 segment_num = GetField(SegmentNum);
42
43 if (segment_tag.segment_index >= segment_num)
44 return 0;
45
46 SegmentEntry entry;
47 GetEntry(segment_tag.segment_index, entry);
48
49 if (segment_tag.offset_into_segment >= entry.size)
50 return 0;
51
52 return entry.offset + segment_tag.offset_into_segment;
53}
54
55ResultCode CROHelper::ApplyRelocation(VAddr target_address, RelocationType relocation_type,
56 u32 addend, u32 symbol_address, u32 target_future_address) {
57
58 switch (relocation_type) {
59 case RelocationType::Nothing:
60 break;
61 case RelocationType::AbsoluteAddress:
62 case RelocationType::AbsoluteAddress2:
63 Memory::Write32(target_address, symbol_address + addend);
64 break;
65 case RelocationType::RelativeAddress:
66 Memory::Write32(target_address, symbol_address + addend - target_future_address);
67 break;
68 case RelocationType::ThumbBranch:
69 case RelocationType::ArmBranch:
70 case RelocationType::ModifyArmBranch:
71 case RelocationType::AlignedRelativeAddress:
72 // TODO(wwylele): implement other types
73 UNIMPLEMENTED();
74 break;
75 default:
76 return CROFormatError(0x22);
77 }
78 return RESULT_SUCCESS;
79}
80
81ResultCode CROHelper::ClearRelocation(VAddr target_address, RelocationType relocation_type) {
82 switch (relocation_type) {
83 case RelocationType::Nothing:
84 break;
85 case RelocationType::AbsoluteAddress:
86 case RelocationType::AbsoluteAddress2:
87 case RelocationType::RelativeAddress:
88 Memory::Write32(target_address, 0);
89 break;
90 case RelocationType::ThumbBranch:
91 case RelocationType::ArmBranch:
92 case RelocationType::ModifyArmBranch:
93 case RelocationType::AlignedRelativeAddress:
94 // TODO(wwylele): implement other types
95 UNIMPLEMENTED();
96 break;
97 default:
98 return CROFormatError(0x22);
99 }
100 return RESULT_SUCCESS;
101}
102
103ResultCode CROHelper::ApplyRelocationBatch(VAddr batch, u32 symbol_address, bool reset) {
104 if (symbol_address == 0 && !reset)
105 return CROFormatError(0x10);
106
107 VAddr relocation_address = batch;
108 while (true) {
109 RelocationEntry relocation;
110 Memory::ReadBlock(relocation_address, &relocation, sizeof(RelocationEntry));
111
112 VAddr relocation_target = SegmentTagToAddress(relocation.target_position);
113 if (relocation_target == 0) {
114 return CROFormatError(0x12);
115 }
116
117 ResultCode result = ApplyRelocation(relocation_target, relocation.type, relocation.addend,
118 symbol_address, relocation_target);
119 if (result.IsError()) {
120 LOG_ERROR(Service_LDR, "Error applying relocation %08X", result.raw);
121 return result;
122 }
123
124 if (relocation.is_batch_end)
125 break;
126
127 relocation_address += sizeof(RelocationEntry);
128 }
129
130 RelocationEntry relocation;
131 Memory::ReadBlock(batch, &relocation, sizeof(RelocationEntry));
132 relocation.is_batch_resolved = reset ? 0 : 1;
133 Memory::WriteBlock(batch, &relocation, sizeof(RelocationEntry));
134 return RESULT_SUCCESS;
135}
136
137VAddr CROHelper::FindExportNamedSymbol(const std::string& name) const {
138 if (!GetField(ExportTreeNum))
139 return 0;
140
141 std::size_t len = name.size();
142 ExportTreeEntry entry;
143 GetEntry(0, entry);
144 ExportTreeEntry::Child next;
145 next.raw = entry.left.raw;
146 u32 found_id;
147
148 while (true) {
149 GetEntry(next.next_index, entry);
150
151 if (next.is_end) {
152 found_id = entry.export_table_index;
153 break;
154 }
155
156 u16 test_byte = entry.test_bit >> 3;
157 u16 test_bit_in_byte = entry.test_bit & 7;
158
159 if (test_byte >= len) {
160 next.raw = entry.left.raw;
161 } else if ((name[test_byte] >> test_bit_in_byte) & 1) {
162 next.raw = entry.right.raw;
163 } else {
164 next.raw = entry.left.raw;
165 }
166 }
167
168 u32 export_named_symbol_num = GetField(ExportNamedSymbolNum);
169
170 if (found_id >= export_named_symbol_num)
171 return 0;
172
173 u32 export_strings_size = GetField(ExportStringsSize);
174 ExportNamedSymbolEntry symbol_entry;
175 GetEntry(found_id, symbol_entry);
176
177 if (Memory::ReadCString(symbol_entry.name_offset, export_strings_size) != name)
178 return 0;
179
180 return SegmentTagToAddress(symbol_entry.symbol_position);
181}
182
183ResultCode CROHelper::RebaseHeader(u32 cro_size) {
184 ResultCode error = CROFormatError(0x11);
185
186 // verifies magic
187 if (GetField(Magic) != MAGIC_CRO0)
188 return error;
189
190 // verifies not registered
191 if (GetField(NextCRO) != 0 || GetField(PreviousCRO) != 0)
192 return error;
193
194 // This seems to be a hard limit set by the RO module
195 if (GetField(FileSize) > 0x10000000 || GetField(BssSize) > 0x10000000)
196 return error;
197
198 // verifies not fixed
199 if (GetField(FixedSize) != 0)
200 return error;
201
202 if (GetField(CodeOffset) < CRO_HEADER_SIZE)
203 return error;
204
205 // verifies that all offsets are in the correct order
206 constexpr std::array<HeaderField, 18> OFFSET_ORDER = {{
207 CodeOffset, ModuleNameOffset, SegmentTableOffset, ExportNamedSymbolTableOffset,
208 ExportTreeTableOffset, ExportIndexedSymbolTableOffset, ExportStringsOffset,
209 ImportModuleTableOffset, ExternalRelocationTableOffset, ImportNamedSymbolTableOffset,
210 ImportIndexedSymbolTableOffset, ImportAnonymousSymbolTableOffset, ImportStringsOffset,
211 StaticAnonymousSymbolTableOffset, InternalRelocationTableOffset,
212 StaticRelocationTableOffset, DataOffset, FileSize,
213 }};
214
215 u32 prev_offset = GetField(OFFSET_ORDER[0]);
216 u32 cur_offset;
217 for (std::size_t i = 1; i < OFFSET_ORDER.size(); ++i) {
218 cur_offset = GetField(OFFSET_ORDER[i]);
219 if (cur_offset < prev_offset)
220 return error;
221 prev_offset = cur_offset;
222 }
223
224 // rebases offsets
225 u32 offset = GetField(NameOffset);
226 if (offset != 0)
227 SetField(NameOffset, offset + module_address);
228
229 for (int field = CodeOffset; field < Fix0Barrier; field += 2) {
230 HeaderField header_field = static_cast<HeaderField>(field);
231 offset = GetField(header_field);
232 if (offset != 0)
233 SetField(header_field, offset + module_address);
234 }
235
236 // verifies everything is not beyond the buffer
237 u32 file_end = module_address + cro_size;
238 for (int field = CodeOffset, i = 0; field < Fix0Barrier; field += 2, ++i) {
239 HeaderField offset_field = static_cast<HeaderField>(field);
240 HeaderField size_field = static_cast<HeaderField>(field + 1);
241 if (GetField(offset_field) + GetField(size_field) * ENTRY_SIZE[i] > file_end)
242 return error;
243 }
244
245 return RESULT_SUCCESS;
246}
247
248ResultVal<VAddr> CROHelper::RebaseSegmentTable(u32 cro_size, VAddr data_segment_address,
249 u32 data_segment_size, VAddr bss_segment_address,
250 u32 bss_segment_size) {
251
252 u32 prev_data_segment = 0;
253 u32 segment_num = GetField(SegmentNum);
254 for (u32 i = 0; i < segment_num; ++i) {
255 SegmentEntry segment;
256 GetEntry(i, segment);
257 if (segment.type == SegmentType::Data) {
258 if (segment.size != 0) {
259 if (segment.size > data_segment_size)
260 return ERROR_BUFFER_TOO_SMALL;
261 prev_data_segment = segment.offset;
262 segment.offset = data_segment_address;
263 }
264 } else if (segment.type == SegmentType::BSS) {
265 if (segment.size != 0) {
266 if (segment.size > bss_segment_size)
267 return ERROR_BUFFER_TOO_SMALL;
268 segment.offset = bss_segment_address;
269 }
270 } else if (segment.offset != 0) {
271 segment.offset += module_address;
272 if (segment.offset > module_address + cro_size)
273 return CROFormatError(0x19);
274 }
275 SetEntry(i, segment);
276 }
277 return MakeResult<VAddr>(prev_data_segment + module_address);
278}
279
280ResultCode CROHelper::RebaseExportNamedSymbolTable() {
281 VAddr export_strings_offset = GetField(ExportStringsOffset);
282 VAddr export_strings_end = export_strings_offset + GetField(ExportStringsSize);
283
284 u32 export_named_symbol_num = GetField(ExportNamedSymbolNum);
285 for (u32 i = 0; i < export_named_symbol_num; ++i) {
286 ExportNamedSymbolEntry entry;
287 GetEntry(i, entry);
288
289 if (entry.name_offset != 0) {
290 entry.name_offset += module_address;
291 if (entry.name_offset < export_strings_offset ||
292 entry.name_offset >= export_strings_end) {
293 return CROFormatError(0x11);
294 }
295 }
296
297 SetEntry(i, entry);
298 }
299 return RESULT_SUCCESS;
300}
301
302ResultCode CROHelper::VerifyExportTreeTable() const {
303 u32 tree_num = GetField(ExportTreeNum);
304 for (u32 i = 0; i < tree_num; ++i) {
305 ExportTreeEntry entry;
306 GetEntry(i, entry);
307
308 if (entry.left.next_index >= tree_num || entry.right.next_index >= tree_num) {
309 return CROFormatError(0x11);
310 }
311 }
312 return RESULT_SUCCESS;
313}
314
315ResultCode CROHelper::RebaseImportModuleTable() {
316 VAddr import_strings_offset = GetField(ImportStringsOffset);
317 VAddr import_strings_end = import_strings_offset + GetField(ImportStringsSize);
318 VAddr import_indexed_symbol_table_offset = GetField(ImportIndexedSymbolTableOffset);
319 VAddr index_import_table_end =
320 import_indexed_symbol_table_offset +
321 GetField(ImportIndexedSymbolNum) * sizeof(ImportIndexedSymbolEntry);
322 VAddr import_anonymous_symbol_table_offset = GetField(ImportAnonymousSymbolTableOffset);
323 VAddr offset_import_table_end =
324 import_anonymous_symbol_table_offset +
325 GetField(ImportAnonymousSymbolNum) * sizeof(ImportAnonymousSymbolEntry);
326
327 u32 module_num = GetField(ImportModuleNum);
328 for (u32 i = 0; i < module_num; ++i) {
329 ImportModuleEntry entry;
330 GetEntry(i, entry);
331
332 if (entry.name_offset != 0) {
333 entry.name_offset += module_address;
334 if (entry.name_offset < import_strings_offset ||
335 entry.name_offset >= import_strings_end) {
336 return CROFormatError(0x18);
337 }
338 }
339
340 if (entry.import_indexed_symbol_table_offset != 0) {
341 entry.import_indexed_symbol_table_offset += module_address;
342 if (entry.import_indexed_symbol_table_offset < import_indexed_symbol_table_offset ||
343 entry.import_indexed_symbol_table_offset > index_import_table_end) {
344 return CROFormatError(0x18);
345 }
346 }
347
348 if (entry.import_anonymous_symbol_table_offset != 0) {
349 entry.import_anonymous_symbol_table_offset += module_address;
350 if (entry.import_anonymous_symbol_table_offset < import_anonymous_symbol_table_offset ||
351 entry.import_anonymous_symbol_table_offset > offset_import_table_end) {
352 return CROFormatError(0x18);
353 }
354 }
355
356 SetEntry(i, entry);
357 }
358 return RESULT_SUCCESS;
359}
360
361ResultCode CROHelper::RebaseImportNamedSymbolTable() {
362 VAddr import_strings_offset = GetField(ImportStringsOffset);
363 VAddr import_strings_end = import_strings_offset + GetField(ImportStringsSize);
364 VAddr external_relocation_table_offset = GetField(ExternalRelocationTableOffset);
365 VAddr external_relocation_table_end =
366 external_relocation_table_offset +
367 GetField(ExternalRelocationNum) * sizeof(ExternalRelocationEntry);
368
369 u32 num = GetField(ImportNamedSymbolNum);
370 for (u32 i = 0; i < num; ++i) {
371 ImportNamedSymbolEntry entry;
372 GetEntry(i, entry);
373
374 if (entry.name_offset != 0) {
375 entry.name_offset += module_address;
376 if (entry.name_offset < import_strings_offset ||
377 entry.name_offset >= import_strings_end) {
378 return CROFormatError(0x1B);
379 }
380 }
381
382 if (entry.relocation_batch_offset != 0) {
383 entry.relocation_batch_offset += module_address;
384 if (entry.relocation_batch_offset < external_relocation_table_offset ||
385 entry.relocation_batch_offset > external_relocation_table_end) {
386 return CROFormatError(0x1B);
387 }
388 }
389
390 SetEntry(i, entry);
391 }
392 return RESULT_SUCCESS;
393}
394
395ResultCode CROHelper::RebaseImportIndexedSymbolTable() {
396 VAddr external_relocation_table_offset = GetField(ExternalRelocationTableOffset);
397 VAddr external_relocation_table_end =
398 external_relocation_table_offset +
399 GetField(ExternalRelocationNum) * sizeof(ExternalRelocationEntry);
400
401 u32 num = GetField(ImportIndexedSymbolNum);
402 for (u32 i = 0; i < num; ++i) {
403 ImportIndexedSymbolEntry entry;
404 GetEntry(i, entry);
405
406 if (entry.relocation_batch_offset != 0) {
407 entry.relocation_batch_offset += module_address;
408 if (entry.relocation_batch_offset < external_relocation_table_offset ||
409 entry.relocation_batch_offset > external_relocation_table_end) {
410 return CROFormatError(0x14);
411 }
412 }
413
414 SetEntry(i, entry);
415 }
416 return RESULT_SUCCESS;
417}
418
419ResultCode CROHelper::RebaseImportAnonymousSymbolTable() {
420 VAddr external_relocation_table_offset = GetField(ExternalRelocationTableOffset);
421 VAddr external_relocation_table_end =
422 external_relocation_table_offset +
423 GetField(ExternalRelocationNum) * sizeof(ExternalRelocationEntry);
424
425 u32 num = GetField(ImportAnonymousSymbolNum);
426 for (u32 i = 0; i < num; ++i) {
427 ImportAnonymousSymbolEntry entry;
428 GetEntry(i, entry);
429
430 if (entry.relocation_batch_offset != 0) {
431 entry.relocation_batch_offset += module_address;
432 if (entry.relocation_batch_offset < external_relocation_table_offset ||
433 entry.relocation_batch_offset > external_relocation_table_end) {
434 return CROFormatError(0x17);
435 }
436 }
437
438 SetEntry(i, entry);
439 }
440 return RESULT_SUCCESS;
441}
442
443VAddr CROHelper::GetOnUnresolvedAddress() {
444 return SegmentTagToAddress(SegmentTag(GetField(OnUnresolvedSegmentTag)));
445}
446
447ResultCode CROHelper::ResetExternalRelocations() {
448 u32 unresolved_symbol = GetOnUnresolvedAddress();
449 u32 external_relocation_num = GetField(ExternalRelocationNum);
450 ExternalRelocationEntry relocation;
451
452 // Verifies that the last relocation is the end of a batch
453 GetEntry(external_relocation_num - 1, relocation);
454 if (!relocation.is_batch_end) {
455 return CROFormatError(0x12);
456 }
457
458 bool batch_begin = true;
459 for (u32 i = 0; i < external_relocation_num; ++i) {
460 GetEntry(i, relocation);
461 VAddr relocation_target = SegmentTagToAddress(relocation.target_position);
462
463 if (relocation_target == 0) {
464 return CROFormatError(0x12);
465 }
466
467 ResultCode result = ApplyRelocation(relocation_target, relocation.type, relocation.addend,
468 unresolved_symbol, relocation_target);
469 if (result.IsError()) {
470 LOG_ERROR(Service_LDR, "Error applying relocation %08X", result.raw);
471 return result;
472 }
473
474 if (batch_begin) {
475 // resets to unresolved state
476 relocation.is_batch_resolved = 0;
477 SetEntry(i, relocation);
478 }
479
480 // if current is an end, then the next is a beginning
481 batch_begin = relocation.is_batch_end != 0;
482 }
483
484 return RESULT_SUCCESS;
485}
486
487ResultCode CROHelper::ClearExternalRelocations() {
488 u32 external_relocation_num = GetField(ExternalRelocationNum);
489 ExternalRelocationEntry relocation;
490
491 bool batch_begin = true;
492 for (u32 i = 0; i < external_relocation_num; ++i) {
493 GetEntry(i, relocation);
494 VAddr relocation_target = SegmentTagToAddress(relocation.target_position);
495
496 if (relocation_target == 0) {
497 return CROFormatError(0x12);
498 }
499
500 ResultCode result = ClearRelocation(relocation_target, relocation.type);
501 if (result.IsError()) {
502 LOG_ERROR(Service_LDR, "Error clearing relocation %08X", result.raw);
503 return result;
504 }
505
506 if (batch_begin) {
507 // resets to unresolved state
508 relocation.is_batch_resolved = 0;
509 SetEntry(i, relocation);
510 }
511
512 // if current is an end, then the next is a beginning
513 batch_begin = relocation.is_batch_end != 0;
514 }
515
516 return RESULT_SUCCESS;
517}
518
519ResultCode CROHelper::ApplyStaticAnonymousSymbolToCRS(VAddr crs_address) {
520 VAddr static_relocation_table_offset = GetField(StaticRelocationTableOffset);
521 VAddr static_relocation_table_end =
522 static_relocation_table_offset +
523 GetField(StaticRelocationNum) * sizeof(StaticRelocationEntry);
524
525 CROHelper crs(crs_address);
526 u32 offset_export_num = GetField(StaticAnonymousSymbolNum);
527 LOG_INFO(Service_LDR, "CRO \"%s\" exports %d static anonymous symbols", ModuleName().data(),
528 offset_export_num);
529 for (u32 i = 0; i < offset_export_num; ++i) {
530 StaticAnonymousSymbolEntry entry;
531 GetEntry(i, entry);
532 u32 batch_address = entry.relocation_batch_offset + module_address;
533
534 if (batch_address < static_relocation_table_offset ||
535 batch_address > static_relocation_table_end) {
536 return CROFormatError(0x16);
537 }
538
539 u32 symbol_address = SegmentTagToAddress(entry.symbol_position);
540 LOG_TRACE(Service_LDR, "CRO \"%s\" exports 0x%08X to the static module",
541 ModuleName().data(), symbol_address);
542 ResultCode result = crs.ApplyRelocationBatch(batch_address, symbol_address);
543 if (result.IsError()) {
544 LOG_ERROR(Service_LDR, "Error applying relocation batch %08X", result.raw);
545 return result;
546 }
547 }
548 return RESULT_SUCCESS;
549}
550
551ResultCode CROHelper::ApplyInternalRelocations(u32 old_data_segment_address) {
552 u32 segment_num = GetField(SegmentNum);
553 u32 internal_relocation_num = GetField(InternalRelocationNum);
554 for (u32 i = 0; i < internal_relocation_num; ++i) {
555 InternalRelocationEntry relocation;
556 GetEntry(i, relocation);
557 VAddr target_addressB = SegmentTagToAddress(relocation.target_position);
558 if (target_addressB == 0) {
559 return CROFormatError(0x15);
560 }
561
562 VAddr target_address;
563 SegmentEntry target_segment;
564 GetEntry(relocation.target_position.segment_index, target_segment);
565
566 if (target_segment.type == SegmentType::Data) {
567 // If the relocation is to the .data segment, we need to relocate it in the old buffer
568 target_address =
569 old_data_segment_address + relocation.target_position.offset_into_segment;
570 } else {
571 target_address = target_addressB;
572 }
573
574 if (relocation.symbol_segment >= segment_num) {
575 return CROFormatError(0x15);
576 }
577
578 SegmentEntry symbol_segment;
579 GetEntry(relocation.symbol_segment, symbol_segment);
580 LOG_TRACE(Service_LDR, "Internally relocates 0x%08X with 0x%08X", target_address,
581 symbol_segment.offset);
582 ResultCode result = ApplyRelocation(target_address, relocation.type, relocation.addend,
583 symbol_segment.offset, target_addressB);
584 if (result.IsError()) {
585 LOG_ERROR(Service_LDR, "Error applying relocation %08X", result.raw);
586 return result;
587 }
588 }
589 return RESULT_SUCCESS;
590}
591
592ResultCode CROHelper::ClearInternalRelocations() {
593 u32 internal_relocation_num = GetField(InternalRelocationNum);
594 for (u32 i = 0; i < internal_relocation_num; ++i) {
595 InternalRelocationEntry relocation;
596 GetEntry(i, relocation);
597 VAddr target_address = SegmentTagToAddress(relocation.target_position);
598
599 if (target_address == 0) {
600 return CROFormatError(0x15);
601 }
602
603 ResultCode result = ClearRelocation(target_address, relocation.type);
604 if (result.IsError()) {
605 LOG_ERROR(Service_LDR, "Error clearing relocation %08X", result.raw);
606 return result;
607 }
608 }
609 return RESULT_SUCCESS;
610}
611
612void CROHelper::UnrebaseImportAnonymousSymbolTable() {
613 u32 num = GetField(ImportAnonymousSymbolNum);
614 for (u32 i = 0; i < num; ++i) {
615 ImportAnonymousSymbolEntry entry;
616 GetEntry(i, entry);
617
618 if (entry.relocation_batch_offset != 0) {
619 entry.relocation_batch_offset -= module_address;
620 }
621
622 SetEntry(i, entry);
623 }
624}
625
626void CROHelper::UnrebaseImportIndexedSymbolTable() {
627 u32 num = GetField(ImportIndexedSymbolNum);
628 for (u32 i = 0; i < num; ++i) {
629 ImportIndexedSymbolEntry entry;
630 GetEntry(i, entry);
631
632 if (entry.relocation_batch_offset != 0) {
633 entry.relocation_batch_offset -= module_address;
634 }
635
636 SetEntry(i, entry);
637 }
638}
639
640void CROHelper::UnrebaseImportNamedSymbolTable() {
641 u32 num = GetField(ImportNamedSymbolNum);
642 for (u32 i = 0; i < num; ++i) {
643 ImportNamedSymbolEntry entry;
644 GetEntry(i, entry);
645
646 if (entry.name_offset != 0) {
647 entry.name_offset -= module_address;
648 }
649
650 if (entry.relocation_batch_offset) {
651 entry.relocation_batch_offset -= module_address;
652 }
653
654 SetEntry(i, entry);
655 }
656}
657
658void CROHelper::UnrebaseImportModuleTable() {
659 u32 module_num = GetField(ImportModuleNum);
660 for (u32 i = 0; i < module_num; ++i) {
661 ImportModuleEntry entry;
662 GetEntry(i, entry);
663
664 if (entry.name_offset != 0) {
665 entry.name_offset -= module_address;
666 }
667
668 if (entry.import_indexed_symbol_table_offset) {
669 entry.import_indexed_symbol_table_offset -= module_address;
670 }
671
672 if (entry.import_anonymous_symbol_table_offset) {
673 entry.import_anonymous_symbol_table_offset -= module_address;
674 }
675
676 SetEntry(i, entry);
677 }
678}
679
680void CROHelper::UnrebaseExportNamedSymbolTable() {
681 u32 export_named_symbol_num = GetField(ExportNamedSymbolNum);
682 for (u32 i = 0; i < export_named_symbol_num; ++i) {
683 ExportNamedSymbolEntry entry;
684 GetEntry(i, entry);
685
686 if (entry.name_offset != 0) {
687 entry.name_offset -= module_address;
688 }
689
690 SetEntry(i, entry);
691 }
692}
693
694void CROHelper::UnrebaseSegmentTable() {
695 u32 segment_num = GetField(SegmentNum);
696 for (u32 i = 0; i < segment_num; ++i) {
697 SegmentEntry segment;
698 GetEntry(i, segment);
699
700 if (segment.type == SegmentType::BSS) {
701 segment.offset = 0;
702 } else if (segment.offset != 0) {
703 segment.offset -= module_address;
704 }
705
706 SetEntry(i, segment);
707 }
708}
709
710void CROHelper::UnrebaseHeader() {
711 u32 offset = GetField(NameOffset);
712 if (offset != 0)
713 SetField(NameOffset, offset - module_address);
714
715 for (int field = CodeOffset; field < Fix0Barrier; field += 2) {
716 HeaderField header_field = static_cast<HeaderField>(field);
717 offset = GetField(header_field);
718 if (offset != 0)
719 SetField(header_field, offset - module_address);
720 }
721}
722
723ResultCode CROHelper::ApplyImportNamedSymbol(VAddr crs_address) {
724 u32 import_strings_size = GetField(ImportStringsSize);
725 u32 symbol_import_num = GetField(ImportNamedSymbolNum);
726 for (u32 i = 0; i < symbol_import_num; ++i) {
727 ImportNamedSymbolEntry entry;
728 GetEntry(i, entry);
729 VAddr relocation_addr = entry.relocation_batch_offset;
730 ExternalRelocationEntry relocation_entry;
731 Memory::ReadBlock(relocation_addr, &relocation_entry, sizeof(ExternalRelocationEntry));
732
733 if (!relocation_entry.is_batch_resolved) {
734 ResultCode result =
735 ForEachAutoLinkCRO(crs_address, [&](CROHelper source) -> ResultVal<bool> {
736 std::string symbol_name =
737 Memory::ReadCString(entry.name_offset, import_strings_size);
738 u32 symbol_address = source.FindExportNamedSymbol(symbol_name);
739
740 if (symbol_address != 0) {
741 LOG_TRACE(Service_LDR, "CRO \"%s\" imports \"%s\" from \"%s\"",
742 ModuleName().data(), symbol_name.data(),
743 source.ModuleName().data());
744
745 ResultCode result = ApplyRelocationBatch(relocation_addr, symbol_address);
746 if (result.IsError()) {
747 LOG_ERROR(Service_LDR, "Error applying relocation batch %08X",
748 result.raw);
749 return result;
750 }
751
752 return MakeResult<bool>(false);
753 }
754
755 return MakeResult<bool>(true);
756 });
757 if (result.IsError()) {
758 return result;
759 }
760 }
761 }
762 return RESULT_SUCCESS;
763}
764
765ResultCode CROHelper::ResetImportNamedSymbol() {
766 u32 unresolved_symbol = GetOnUnresolvedAddress();
767
768 u32 symbol_import_num = GetField(ImportNamedSymbolNum);
769 for (u32 i = 0; i < symbol_import_num; ++i) {
770 ImportNamedSymbolEntry entry;
771 GetEntry(i, entry);
772 VAddr relocation_addr = entry.relocation_batch_offset;
773 ExternalRelocationEntry relocation_entry;
774 Memory::ReadBlock(relocation_addr, &relocation_entry, sizeof(ExternalRelocationEntry));
775
776 ResultCode result = ApplyRelocationBatch(relocation_addr, unresolved_symbol, true);
777 if (result.IsError()) {
778 LOG_ERROR(Service_LDR, "Error reseting relocation batch %08X", result.raw);
779 return result;
780 }
781 }
782 return RESULT_SUCCESS;
783}
784
785ResultCode CROHelper::ResetImportIndexedSymbol() {
786 u32 unresolved_symbol = GetOnUnresolvedAddress();
787
788 u32 import_num = GetField(ImportIndexedSymbolNum);
789 for (u32 i = 0; i < import_num; ++i) {
790 ImportIndexedSymbolEntry entry;
791 GetEntry(i, entry);
792 VAddr relocation_addr = entry.relocation_batch_offset;
793 ExternalRelocationEntry relocation_entry;
794 Memory::ReadBlock(relocation_addr, &relocation_entry, sizeof(ExternalRelocationEntry));
795
796 ResultCode result = ApplyRelocationBatch(relocation_addr, unresolved_symbol, true);
797 if (result.IsError()) {
798 LOG_ERROR(Service_LDR, "Error reseting relocation batch %08X", result.raw);
799 return result;
800 }
801 }
802 return RESULT_SUCCESS;
803}
804
805ResultCode CROHelper::ResetImportAnonymousSymbol() {
806 u32 unresolved_symbol = GetOnUnresolvedAddress();
807
808 u32 import_num = GetField(ImportAnonymousSymbolNum);
809 for (u32 i = 0; i < import_num; ++i) {
810 ImportAnonymousSymbolEntry entry;
811 GetEntry(i, entry);
812 VAddr relocation_addr = entry.relocation_batch_offset;
813 ExternalRelocationEntry relocation_entry;
814 Memory::ReadBlock(relocation_addr, &relocation_entry, sizeof(ExternalRelocationEntry));
815
816 ResultCode result = ApplyRelocationBatch(relocation_addr, unresolved_symbol, true);
817 if (result.IsError()) {
818 LOG_ERROR(Service_LDR, "Error reseting relocation batch %08X", result.raw);
819 return result;
820 }
821 }
822 return RESULT_SUCCESS;
823}
824
825ResultCode CROHelper::ApplyModuleImport(VAddr crs_address) {
826 u32 import_strings_size = GetField(ImportStringsSize);
827
828 u32 import_module_num = GetField(ImportModuleNum);
829 for (u32 i = 0; i < import_module_num; ++i) {
830 ImportModuleEntry entry;
831 GetEntry(i, entry);
832 std::string want_cro_name = Memory::ReadCString(entry.name_offset, import_strings_size);
833
834 ResultCode result =
835 ForEachAutoLinkCRO(crs_address, [&](CROHelper source) -> ResultVal<bool> {
836 if (want_cro_name == source.ModuleName()) {
837 LOG_INFO(Service_LDR, "CRO \"%s\" imports %d indexed symbols from \"%s\"",
838 ModuleName().data(), entry.import_indexed_symbol_num,
839 source.ModuleName().data());
840 for (u32 j = 0; j < entry.import_indexed_symbol_num; ++j) {
841 ImportIndexedSymbolEntry im;
842 entry.GetImportIndexedSymbolEntry(j, im);
843 ExportIndexedSymbolEntry ex;
844 source.GetEntry(im.index, ex);
845 u32 symbol_address = source.SegmentTagToAddress(ex.symbol_position);
846 LOG_TRACE(Service_LDR, " Imports 0x%08X", symbol_address);
847 ResultCode result =
848 ApplyRelocationBatch(im.relocation_batch_offset, symbol_address);
849 if (result.IsError()) {
850 LOG_ERROR(Service_LDR, "Error applying relocation batch %08X",
851 result.raw);
852 return result;
853 }
854 }
855 LOG_INFO(Service_LDR, "CRO \"%s\" imports %d anonymous symbols from \"%s\"",
856 ModuleName().data(), entry.import_anonymous_symbol_num,
857 source.ModuleName().data());
858 for (u32 j = 0; j < entry.import_anonymous_symbol_num; ++j) {
859 ImportAnonymousSymbolEntry im;
860 entry.GetImportAnonymousSymbolEntry(j, im);
861 u32 symbol_address = source.SegmentTagToAddress(im.symbol_position);
862 LOG_TRACE(Service_LDR, " Imports 0x%08X", symbol_address);
863 ResultCode result =
864 ApplyRelocationBatch(im.relocation_batch_offset, symbol_address);
865 if (result.IsError()) {
866 LOG_ERROR(Service_LDR, "Error applying relocation batch %08X",
867 result.raw);
868 return result;
869 }
870 }
871 return MakeResult<bool>(false);
872 }
873 return MakeResult<bool>(true);
874 });
875 if (result.IsError()) {
876 return result;
877 }
878 }
879 return RESULT_SUCCESS;
880}
881
882ResultCode CROHelper::ApplyExportNamedSymbol(CROHelper target) {
883 LOG_DEBUG(Service_LDR, "CRO \"%s\" exports named symbols to \"%s\"", ModuleName().data(),
884 target.ModuleName().data());
885 u32 target_import_strings_size = target.GetField(ImportStringsSize);
886 u32 target_symbol_import_num = target.GetField(ImportNamedSymbolNum);
887 for (u32 i = 0; i < target_symbol_import_num; ++i) {
888 ImportNamedSymbolEntry entry;
889 target.GetEntry(i, entry);
890 VAddr relocation_addr = entry.relocation_batch_offset;
891 ExternalRelocationEntry relocation_entry;
892 Memory::ReadBlock(relocation_addr, &relocation_entry, sizeof(ExternalRelocationEntry));
893
894 if (!relocation_entry.is_batch_resolved) {
895 std::string symbol_name =
896 Memory::ReadCString(entry.name_offset, target_import_strings_size);
897 u32 symbol_address = FindExportNamedSymbol(symbol_name);
898 if (symbol_address != 0) {
899 LOG_TRACE(Service_LDR, " exports symbol \"%s\"", symbol_name.data());
900 ResultCode result = target.ApplyRelocationBatch(relocation_addr, symbol_address);
901 if (result.IsError()) {
902 LOG_ERROR(Service_LDR, "Error applying relocation batch %08X", result.raw);
903 return result;
904 }
905 }
906 }
907 }
908 return RESULT_SUCCESS;
909}
910
911ResultCode CROHelper::ResetExportNamedSymbol(CROHelper target) {
912 LOG_DEBUG(Service_LDR, "CRO \"%s\" unexports named symbols to \"%s\"", ModuleName().data(),
913 target.ModuleName().data());
914 u32 unresolved_symbol = target.GetOnUnresolvedAddress();
915 u32 target_import_strings_size = target.GetField(ImportStringsSize);
916 u32 target_symbol_import_num = target.GetField(ImportNamedSymbolNum);
917 for (u32 i = 0; i < target_symbol_import_num; ++i) {
918 ImportNamedSymbolEntry entry;
919 target.GetEntry(i, entry);
920 VAddr relocation_addr = entry.relocation_batch_offset;
921 ExternalRelocationEntry relocation_entry;
922 Memory::ReadBlock(relocation_addr, &relocation_entry, sizeof(ExternalRelocationEntry));
923
924 if (relocation_entry.is_batch_resolved) {
925 std::string symbol_name =
926 Memory::ReadCString(entry.name_offset, target_import_strings_size);
927 u32 symbol_address = FindExportNamedSymbol(symbol_name);
928 if (symbol_address != 0) {
929 LOG_TRACE(Service_LDR, " unexports symbol \"%s\"", symbol_name.data());
930 ResultCode result =
931 target.ApplyRelocationBatch(relocation_addr, unresolved_symbol, true);
932 if (result.IsError()) {
933 LOG_ERROR(Service_LDR, "Error applying relocation batch %08X", result.raw);
934 return result;
935 }
936 }
937 }
938 }
939 return RESULT_SUCCESS;
940}
941
942ResultCode CROHelper::ApplyModuleExport(CROHelper target) {
943 std::string module_name = ModuleName();
944 u32 target_import_string_size = target.GetField(ImportStringsSize);
945 u32 target_import_module_num = target.GetField(ImportModuleNum);
946 for (u32 i = 0; i < target_import_module_num; ++i) {
947 ImportModuleEntry entry;
948 target.GetEntry(i, entry);
949
950 if (Memory::ReadCString(entry.name_offset, target_import_string_size) != module_name)
951 continue;
952
953 LOG_INFO(Service_LDR, "CRO \"%s\" exports %d indexed symbols to \"%s\"", module_name.data(),
954 entry.import_indexed_symbol_num, target.ModuleName().data());
955 for (u32 j = 0; j < entry.import_indexed_symbol_num; ++j) {
956 ImportIndexedSymbolEntry im;
957 entry.GetImportIndexedSymbolEntry(j, im);
958 ExportIndexedSymbolEntry ex;
959 GetEntry(im.index, ex);
960 u32 symbol_address = SegmentTagToAddress(ex.symbol_position);
961 LOG_TRACE(Service_LDR, " exports symbol 0x%08X", symbol_address);
962 ResultCode result =
963 target.ApplyRelocationBatch(im.relocation_batch_offset, symbol_address);
964 if (result.IsError()) {
965 LOG_ERROR(Service_LDR, "Error applying relocation batch %08X", result.raw);
966 return result;
967 }
968 }
969
970 LOG_INFO(Service_LDR, "CRO \"%s\" exports %d anonymous symbols to \"%s\"",
971 module_name.data(), entry.import_anonymous_symbol_num, target.ModuleName().data());
972 for (u32 j = 0; j < entry.import_anonymous_symbol_num; ++j) {
973 ImportAnonymousSymbolEntry im;
974 entry.GetImportAnonymousSymbolEntry(j, im);
975 u32 symbol_address = SegmentTagToAddress(im.symbol_position);
976 LOG_TRACE(Service_LDR, " exports symbol 0x%08X", symbol_address);
977 ResultCode result =
978 target.ApplyRelocationBatch(im.relocation_batch_offset, symbol_address);
979 if (result.IsError()) {
980 LOG_ERROR(Service_LDR, "Error applying relocation batch %08X", result.raw);
981 return result;
982 }
983 }
984 }
985
986 return RESULT_SUCCESS;
987}
988
989ResultCode CROHelper::ResetModuleExport(CROHelper target) {
990 u32 unresolved_symbol = target.GetOnUnresolvedAddress();
991
992 std::string module_name = ModuleName();
993 u32 target_import_string_size = target.GetField(ImportStringsSize);
994 u32 target_import_module_num = target.GetField(ImportModuleNum);
995 for (u32 i = 0; i < target_import_module_num; ++i) {
996 ImportModuleEntry entry;
997 target.GetEntry(i, entry);
998
999 if (Memory::ReadCString(entry.name_offset, target_import_string_size) != module_name)
1000 continue;
1001
1002 LOG_DEBUG(Service_LDR, "CRO \"%s\" unexports indexed symbols to \"%s\"", module_name.data(),
1003 target.ModuleName().data());
1004 for (u32 j = 0; j < entry.import_indexed_symbol_num; ++j) {
1005 ImportIndexedSymbolEntry im;
1006 entry.GetImportIndexedSymbolEntry(j, im);
1007 ResultCode result =
1008 target.ApplyRelocationBatch(im.relocation_batch_offset, unresolved_symbol, true);
1009 if (result.IsError()) {
1010 LOG_ERROR(Service_LDR, "Error applying relocation batch %08X", result.raw);
1011 return result;
1012 }
1013 }
1014
1015 LOG_DEBUG(Service_LDR, "CRO \"%s\" unexports anonymous symbols to \"%s\"",
1016 module_name.data(), target.ModuleName().data());
1017 for (u32 j = 0; j < entry.import_anonymous_symbol_num; ++j) {
1018 ImportAnonymousSymbolEntry im;
1019 entry.GetImportAnonymousSymbolEntry(j, im);
1020 ResultCode result =
1021 target.ApplyRelocationBatch(im.relocation_batch_offset, unresolved_symbol, true);
1022 if (result.IsError()) {
1023 LOG_ERROR(Service_LDR, "Error applying relocation batch %08X", result.raw);
1024 return result;
1025 }
1026 }
1027 }
1028
1029 return RESULT_SUCCESS;
1030}
1031
1032ResultCode CROHelper::ApplyExitRelocations(VAddr crs_address) {
1033 u32 import_strings_size = GetField(ImportStringsSize);
1034 u32 symbol_import_num = GetField(ImportNamedSymbolNum);
1035 for (u32 i = 0; i < symbol_import_num; ++i) {
1036 ImportNamedSymbolEntry entry;
1037 GetEntry(i, entry);
1038 VAddr relocation_addr = entry.relocation_batch_offset;
1039 ExternalRelocationEntry relocation_entry;
1040 Memory::ReadBlock(relocation_addr, &relocation_entry, sizeof(ExternalRelocationEntry));
1041
1042 if (Memory::ReadCString(entry.name_offset, import_strings_size) == "__aeabi_atexit") {
1043 ResultCode result =
1044 ForEachAutoLinkCRO(crs_address, [&](CROHelper source) -> ResultVal<bool> {
1045 u32 symbol_address = source.FindExportNamedSymbol("nnroAeabiAtexit_");
1046
1047 if (symbol_address != 0) {
1048 LOG_DEBUG(Service_LDR, "CRO \"%s\" import exit function from \"%s\"",
1049 ModuleName().data(), source.ModuleName().data());
1050
1051 ResultCode result = ApplyRelocationBatch(relocation_addr, symbol_address);
1052 if (result.IsError()) {
1053 LOG_ERROR(Service_LDR, "Error applying relocation batch %08X",
1054 result.raw);
1055 return result;
1056 }
1057
1058 return MakeResult<bool>(false);
1059 }
1060
1061 return MakeResult<bool>(true);
1062 });
1063 if (result.IsError()) {
1064 LOG_ERROR(Service_LDR, "Error applying exit relocation %08X", result.raw);
1065 return result;
1066 }
1067 }
1068 }
1069 return RESULT_SUCCESS;
1070}
1071
1072/**
1073 * Verifies a string or a string table matching a predicted size (i.e. terminated by 0)
1074 * if it is not empty. There can be many other nulls in the string table because
1075 * they are composed by many sub strings. This function is to check whether the
1076 * whole string (table) is terminated properly, despite that it is not actually one string.
1077 * @param address the virtual address of the string (table)
1078 * @param size the size of the string (table), including the terminating 0
1079 * @returns ResultCode RESULT_SUCCESS if the size matches, otherwise error code.
1080 */
1081static ResultCode VerifyStringTableLength(VAddr address, u32 size) {
1082 if (size != 0) {
1083 if (Memory::Read8(address + size - 1) != 0)
1084 return CROFormatError(0x0B);
1085 }
1086 return RESULT_SUCCESS;
1087}
1088
1089ResultCode CROHelper::Rebase(VAddr crs_address, u32 cro_size, VAddr data_segment_addresss,
1090 u32 data_segment_size, VAddr bss_segment_address, u32 bss_segment_size,
1091 bool is_crs) {
1092
1093 ResultCode result = RebaseHeader(cro_size);
1094 if (result.IsError()) {
1095 LOG_ERROR(Service_LDR, "Error rebasing header %08X", result.raw);
1096 return result;
1097 }
1098
1099 result = VerifyStringTableLength(GetField(ModuleNameOffset), GetField(ModuleNameSize));
1100 if (result.IsError()) {
1101 LOG_ERROR(Service_LDR, "Error verifying module name %08X", result.raw);
1102 return result;
1103 }
1104
1105 u32 prev_data_segment_address = 0;
1106 if (!is_crs) {
1107 auto result_val = RebaseSegmentTable(cro_size, data_segment_addresss, data_segment_size,
1108 bss_segment_address, bss_segment_size);
1109 if (result_val.Failed()) {
1110 LOG_ERROR(Service_LDR, "Error rebasing segment table %08X", result_val.Code().raw);
1111 return result_val.Code();
1112 }
1113 prev_data_segment_address = *result_val;
1114 }
1115
1116 result = RebaseExportNamedSymbolTable();
1117 if (result.IsError()) {
1118 LOG_ERROR(Service_LDR, "Error rebasing symbol export table %08X", result.raw);
1119 return result;
1120 }
1121
1122 result = VerifyExportTreeTable();
1123 if (result.IsError()) {
1124 LOG_ERROR(Service_LDR, "Error verifying export tree %08X", result.raw);
1125 return result;
1126 }
1127
1128 result = VerifyStringTableLength(GetField(ExportStringsOffset), GetField(ExportStringsSize));
1129 if (result.IsError()) {
1130 LOG_ERROR(Service_LDR, "Error verifying export strings %08X", result.raw);
1131 return result;
1132 }
1133
1134 result = RebaseImportModuleTable();
1135 if (result.IsError()) {
1136 LOG_ERROR(Service_LDR, "Error rebasing object table %08X", result.raw);
1137 return result;
1138 }
1139
1140 result = ResetExternalRelocations();
1141 if (result.IsError()) {
1142 LOG_ERROR(Service_LDR, "Error resetting all external relocations %08X", result.raw);
1143 return result;
1144 }
1145
1146 result = RebaseImportNamedSymbolTable();
1147 if (result.IsError()) {
1148 LOG_ERROR(Service_LDR, "Error rebasing symbol import table %08X", result.raw);
1149 return result;
1150 }
1151
1152 result = RebaseImportIndexedSymbolTable();
1153 if (result.IsError()) {
1154 LOG_ERROR(Service_LDR, "Error rebasing index import table %08X", result.raw);
1155 return result;
1156 }
1157
1158 result = RebaseImportAnonymousSymbolTable();
1159 if (result.IsError()) {
1160 LOG_ERROR(Service_LDR, "Error rebasing offset import table %08X", result.raw);
1161 return result;
1162 }
1163
1164 result = VerifyStringTableLength(GetField(ImportStringsOffset), GetField(ImportStringsSize));
1165 if (result.IsError()) {
1166 LOG_ERROR(Service_LDR, "Error verifying import strings %08X", result.raw);
1167 return result;
1168 }
1169
1170 if (!is_crs) {
1171 result = ApplyStaticAnonymousSymbolToCRS(crs_address);
1172 if (result.IsError()) {
1173 LOG_ERROR(Service_LDR, "Error applying offset export to CRS %08X", result.raw);
1174 return result;
1175 }
1176 }
1177
1178 result = ApplyInternalRelocations(prev_data_segment_address);
1179 if (result.IsError()) {
1180 LOG_ERROR(Service_LDR, "Error applying internal relocations %08X", result.raw);
1181 return result;
1182 }
1183
1184 if (!is_crs) {
1185 result = ApplyExitRelocations(crs_address);
1186 if (result.IsError()) {
1187 LOG_ERROR(Service_LDR, "Error applying exit relocations %08X", result.raw);
1188 return result;
1189 }
1190 }
1191
1192 return RESULT_SUCCESS;
1193}
1194
1195void CROHelper::Unrebase(bool is_crs) {
1196 UnrebaseImportAnonymousSymbolTable();
1197 UnrebaseImportIndexedSymbolTable();
1198 UnrebaseImportNamedSymbolTable();
1199 UnrebaseImportModuleTable();
1200 UnrebaseExportNamedSymbolTable();
1201
1202 if (!is_crs)
1203 UnrebaseSegmentTable();
1204
1205 SetNextModule(0);
1206 SetPreviousModule(0);
1207
1208 SetField(FixedSize, 0);
1209
1210 UnrebaseHeader();
1211}
1212
1213ResultCode CROHelper::VerifyHash(u32 cro_size, VAddr crr) const {
1214 // TODO(wwylele): actually verify the hash
1215 return RESULT_SUCCESS;
1216}
1217
1218ResultCode CROHelper::Link(VAddr crs_address, bool link_on_load_bug_fix) {
1219 ResultCode result = RESULT_SUCCESS;
1220
1221 {
1222 VAddr data_segment_address;
1223 if (link_on_load_bug_fix) {
1224 // this is a bug fix introduced by 7.2.0-17's LoadCRO_New
1225 // The bug itself is:
1226 // If a relocation target is in .data segment, it will relocate to the
1227 // user-specified buffer. But if this is linking during loading,
1228 // the .data segment hasn't been transfer from CRO to the buffer,
1229 // thus the relocation will be overwritten by data transfer.
1230 // To fix this bug, we need temporarily restore the old .data segment
1231 // offset and apply imported symbols.
1232
1233 // RO service seems assuming segment_index == segment_type,
1234 // so we do the same
1235 if (GetField(SegmentNum) >= 2) { // means we have .data segment
1236 SegmentEntry entry;
1237 GetEntry(2, entry);
1238 ASSERT(entry.type == SegmentType::Data);
1239 data_segment_address = entry.offset;
1240 entry.offset = GetField(DataOffset);
1241 SetEntry(2, entry);
1242 }
1243 }
1244 SCOPE_EXIT({
1245 // Restore the new .data segment address after importing
1246 if (link_on_load_bug_fix) {
1247 if (GetField(SegmentNum) >= 2) {
1248 SegmentEntry entry;
1249 GetEntry(2, entry);
1250 entry.offset = data_segment_address;
1251 SetEntry(2, entry);
1252 }
1253 }
1254 });
1255
1256 // Imports named symbols from other modules
1257 result = ApplyImportNamedSymbol(crs_address);
1258 if (result.IsError()) {
1259 LOG_ERROR(Service_LDR, "Error applying symbol import %08X", result.raw);
1260 return result;
1261 }
1262
1263 // Imports indexed and anonymous symbols from other modules
1264 result = ApplyModuleImport(crs_address);
1265 if (result.IsError()) {
1266 LOG_ERROR(Service_LDR, "Error applying module import %08X", result.raw);
1267 return result;
1268 }
1269 }
1270
1271 // Exports symbols to other modules
1272 result = ForEachAutoLinkCRO(crs_address, [this](CROHelper target) -> ResultVal<bool> {
1273 ResultCode result = ApplyExportNamedSymbol(target);
1274 if (result.IsError())
1275 return result;
1276
1277 result = ApplyModuleExport(target);
1278 if (result.IsError())
1279 return result;
1280
1281 return MakeResult<bool>(true);
1282 });
1283 if (result.IsError()) {
1284 LOG_ERROR(Service_LDR, "Error applying export %08X", result.raw);
1285 return result;
1286 }
1287
1288 return RESULT_SUCCESS;
1289}
1290
1291ResultCode CROHelper::Unlink(VAddr crs_address) {
1292
1293 // Resets all imported named symbols
1294 ResultCode result = ResetImportNamedSymbol();
1295 if (result.IsError()) {
1296 LOG_ERROR(Service_LDR, "Error resetting symbol import %08X", result.raw);
1297 return result;
1298 }
1299
1300 // Resets all imported indexed symbols
1301 result = ResetImportIndexedSymbol();
1302 if (result.IsError()) {
1303 LOG_ERROR(Service_LDR, "Error resetting indexed import %08X", result.raw);
1304 return result;
1305 }
1306
1307 // Resets all imported anonymous symbols
1308 result = ResetImportAnonymousSymbol();
1309 if (result.IsError()) {
1310 LOG_ERROR(Service_LDR, "Error resetting anonymous import %08X", result.raw);
1311 return result;
1312 }
1313
1314 // Resets all symbols in other modules imported from this module
1315 // Note: the RO service seems only searching in auto-link modules
1316 result = ForEachAutoLinkCRO(crs_address, [this](CROHelper target) -> ResultVal<bool> {
1317 ResultCode result = ResetExportNamedSymbol(target);
1318 if (result.IsError())
1319 return result;
1320
1321 result = ResetModuleExport(target);
1322 if (result.IsError())
1323 return result;
1324
1325 return MakeResult<bool>(true);
1326 });
1327 if (result.IsError()) {
1328 LOG_ERROR(Service_LDR, "Error resetting export %08X", result.raw);
1329 return result;
1330 }
1331
1332 return RESULT_SUCCESS;
1333}
1334
1335ResultCode CROHelper::ClearRelocations() {
1336 ResultCode result = ClearExternalRelocations();
1337 if (result.IsError()) {
1338 LOG_ERROR(Service_LDR, "Error clearing external relocations %08X", result.raw);
1339 return result;
1340 }
1341
1342 result = ClearInternalRelocations();
1343 if (result.IsError()) {
1344 LOG_ERROR(Service_LDR, "Error clearing internal relocations %08X", result.raw);
1345 return result;
1346 }
1347 return RESULT_SUCCESS;
1348}
1349
1350void CROHelper::InitCRS() {
1351 SetNextModule(0);
1352 SetPreviousModule(0);
1353}
1354
1355void CROHelper::Register(VAddr crs_address, bool auto_link) {
1356 CROHelper crs(crs_address);
1357 CROHelper head(auto_link ? crs.NextModule() : crs.PreviousModule());
1358
1359 if (head.module_address) {
1360 // there are already CROs registered
1361 // register as the new tail
1362 CROHelper tail(head.PreviousModule());
1363
1364 // link with the old tail
1365 ASSERT(tail.NextModule() == 0);
1366 SetPreviousModule(tail.module_address);
1367 tail.SetNextModule(module_address);
1368
1369 // set previous of the head pointing to the new tail
1370 head.SetPreviousModule(module_address);
1371 } else {
1372 // register as the first CRO
1373 // set previous to self as tail
1374 SetPreviousModule(module_address);
1375
1376 // set self as head
1377 if (auto_link)
1378 crs.SetNextModule(module_address);
1379 else
1380 crs.SetPreviousModule(module_address);
1381 }
1382
1383 // the new one is the tail
1384 SetNextModule(0);
1385}
1386
1387void CROHelper::Unregister(VAddr crs_address) {
1388 CROHelper crs(crs_address);
1389 CROHelper next_head(crs.NextModule()), previous_head(crs.PreviousModule());
1390 CROHelper next(NextModule()), previous(PreviousModule());
1391
1392 if (module_address == next_head.module_address ||
1393 module_address == previous_head.module_address) {
1394 // removing head
1395 if (next.module_address) {
1396 // the next is new head
1397 // let its previous point to the tail
1398 next.SetPreviousModule(previous.module_address);
1399 }
1400
1401 // set new head
1402 if (module_address == previous_head.module_address) {
1403 crs.SetPreviousModule(next.module_address);
1404 } else {
1405 crs.SetNextModule(next.module_address);
1406 }
1407 } else if (next.module_address) {
1408 // link previous and next
1409 previous.SetNextModule(next.module_address);
1410 next.SetPreviousModule(previous.module_address);
1411 } else {
1412 // removing tail
1413 // set previous as new tail
1414 previous.SetNextModule(0);
1415
1416 // let head's previous point to the new tail
1417 if (next_head.module_address && next_head.PreviousModule() == module_address) {
1418 next_head.SetPreviousModule(previous.module_address);
1419 } else if (previous_head.module_address &&
1420 previous_head.PreviousModule() == module_address) {
1421 previous_head.SetPreviousModule(previous.module_address);
1422 } else {
1423 UNREACHABLE();
1424 }
1425 }
1426
1427 // unlink self
1428 SetNextModule(0);
1429 SetPreviousModule(0);
1430}
1431
1432u32 CROHelper::GetFixEnd(u32 fix_level) const {
1433 u32 end = CRO_HEADER_SIZE;
1434 end = std::max<u32>(end, GetField(CodeOffset) + GetField(CodeSize));
1435
1436 u32 entry_size_i = 2;
1437 int field = ModuleNameOffset;
1438 while (true) {
1439 end = std::max<u32>(end, GetField(static_cast<HeaderField>(field)) +
1440 GetField(static_cast<HeaderField>(field + 1)) *
1441 ENTRY_SIZE[entry_size_i]);
1442
1443 ++entry_size_i;
1444 field += 2;
1445
1446 if (field == FIX_BARRIERS[fix_level])
1447 return end;
1448 }
1449}
1450
1451u32 CROHelper::Fix(u32 fix_level) {
1452 u32 fix_end = GetFixEnd(fix_level);
1453
1454 if (fix_level != 0) {
1455 SetField(Magic, MAGIC_FIXD);
1456
1457 for (int field = FIX_BARRIERS[fix_level]; field < Fix0Barrier; field += 2) {
1458 SetField(static_cast<HeaderField>(field), fix_end);
1459 SetField(static_cast<HeaderField>(field + 1), 0);
1460 }
1461 }
1462
1463 fix_end = Common::AlignUp(fix_end, Memory::PAGE_SIZE);
1464
1465 u32 fixed_size = fix_end - module_address;
1466 SetField(FixedSize, fixed_size);
1467 return fixed_size;
1468}
1469
1470bool CROHelper::IsLoaded() const {
1471 u32 magic = GetField(Magic);
1472 if (magic != MAGIC_CRO0 && magic != MAGIC_FIXD)
1473 return false;
1474
1475 // TODO(wwylele): verify memory state here after memory aliasing is implemented
1476
1477 return true;
1478}
1479
1480std::tuple<VAddr, u32> CROHelper::GetExecutablePages() const {
1481 u32 segment_num = GetField(SegmentNum);
1482 for (u32 i = 0; i < segment_num; ++i) {
1483 SegmentEntry entry;
1484 GetEntry(i, entry);
1485 if (entry.type == SegmentType::Code && entry.size != 0) {
1486 VAddr begin = Common::AlignDown(entry.offset, Memory::PAGE_SIZE);
1487 VAddr end = Common::AlignUp(entry.offset + entry.size, Memory::PAGE_SIZE);
1488 return std::make_tuple(begin, end - begin);
1489 }
1490 }
1491 return std::make_tuple(0, 0);
1492}
1493
1494} // namespace LDR
1495} // namespace Service
diff --git a/src/core/hle/service/ldr_ro/cro_helper.h b/src/core/hle/service/ldr_ro/cro_helper.h
deleted file mode 100644
index 57b4fb6df..000000000
--- a/src/core/hle/service/ldr_ro/cro_helper.h
+++ /dev/null
@@ -1,714 +0,0 @@
1// Copyright 2016 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 <array>
8#include <tuple>
9#include "common/common_types.h"
10#include "common/swap.h"
11#include "core/hle/result.h"
12#include "core/memory.h"
13
14namespace Service {
15namespace LDR {
16
17// GCC versions < 5.0 do not implement std::is_trivially_copyable.
18// Excluding MSVC because it has weird behaviour for std::is_trivially_copyable.
19#if (__GNUC__ >= 5) || defined(__clang__)
20#define ASSERT_CRO_STRUCT(name, size) \
21 static_assert(std::is_standard_layout<name>::value, \
22 "CRO structure " #name " doesn't use standard layout"); \
23 static_assert(std::is_trivially_copyable<name>::value, \
24 "CRO structure " #name " isn't trivially copyable"); \
25 static_assert(sizeof(name) == (size), "Unexpected struct size for CRO structure " #name)
26#else
27#define ASSERT_CRO_STRUCT(name, size) \
28 static_assert(std::is_standard_layout<name>::value, \
29 "CRO structure " #name " doesn't use standard layout"); \
30 static_assert(sizeof(name) == (size), "Unexpected struct size for CRO structure " #name)
31#endif
32
33static constexpr u32 CRO_HEADER_SIZE = 0x138;
34static constexpr u32 CRO_HASH_SIZE = 0x80;
35
36/// Represents a loaded module (CRO) with interfaces manipulating it.
37class CROHelper final {
38public:
39 explicit CROHelper(VAddr cro_address) : module_address(cro_address) {}
40
41 std::string ModuleName() const {
42 return Memory::ReadCString(GetField(ModuleNameOffset), GetField(ModuleNameSize));
43 }
44
45 u32 GetFileSize() const {
46 return GetField(FileSize);
47 }
48
49 /**
50 * Rebases the module according to its address.
51 * @param crs_address the virtual address of the static module
52 * @param cro_size the size of the CRO file
53 * @param data_segment_address buffer address for .data segment
54 * @param data_segment_size the buffer size for .data segment
55 * @param bss_segment_address the buffer address for .bss segment
56 * @param bss_segment_size the buffer size for .bss segment
57 * @param is_crs true if the module itself is the static module
58 * @returns ResultCode RESULT_SUCCESS on success, otherwise error code.
59 */
60 ResultCode Rebase(VAddr crs_address, u32 cro_size, VAddr data_segment_address,
61 u32 data_segment_size, VAddr bss_segment_address, u32 bss_segment_size,
62 bool is_crs);
63
64 /**
65 * Unrebases the module.
66 * @param is_crs true if the module itself is the static module
67 */
68 void Unrebase(bool is_crs);
69
70 /**
71 * Verifies module hash by CRR.
72 * @param cro_size the size of the CRO
73 * @param crr the virtual address of the CRR
74 * @returns ResultCode RESULT_SUCCESS on success, otherwise error code.
75 */
76 ResultCode VerifyHash(u32 cro_size, VAddr crr) const;
77
78 /**
79 * Links this module with all registered auto-link module.
80 * @param crs_address the virtual address of the static module
81 * @param link_on_load_bug_fix true if links when loading and fixes the bug
82 * @returns ResultCode RESULT_SUCCESS on success, otherwise error code.
83 */
84 ResultCode Link(VAddr crs_address, bool link_on_load_bug_fix);
85
86 /**
87 * Unlinks this module with other modules.
88 * @param crs_address the virtual address of the static module
89 * @returns ResultCode RESULT_SUCCESS on success, otherwise error code.
90 */
91 ResultCode Unlink(VAddr crs_address);
92
93 /**
94 * Clears all relocations to zero.
95 * @returns ResultCode RESULT_SUCCESS on success, otherwise error code.
96 */
97 ResultCode ClearRelocations();
98
99 /// Initialize this module as the static module (CRS)
100 void InitCRS();
101
102 /**
103 * Registers this module and adds it to the module list.
104 * @param crs_address the virtual address of the static module
105 * @param auto_link whether to register as an auto link module
106 */
107 void Register(VAddr crs_address, bool auto_link);
108
109 /**
110 * Unregisters this module and removes from the module list.
111 * @param crs_address the virtual address of the static module
112 */
113 void Unregister(VAddr crs_address);
114
115 /**
116 * Gets the end of reserved data according to the fix level.
117 * @param fix_level fix level from 0 to 3
118 * @returns the end of reserved data.
119 */
120 u32 GetFixEnd(u32 fix_level) const;
121
122 /**
123 * Zeros offsets to cropped data according to the fix level and marks as fixed.
124 * @param fix_level fix level from 0 to 3
125 * @returns page-aligned size of the module after fixing.
126 */
127 u32 Fix(u32 fix_level);
128
129 bool IsFixed() const {
130 return GetField(Magic) == MAGIC_FIXD;
131 }
132
133 u32 GetFixedSize() const {
134 return GetField(FixedSize);
135 }
136
137 bool IsLoaded() const;
138
139 /**
140 * Gets the page address and size of the code segment.
141 * @returns a tuple of (address, size); (0, 0) if the code segment doesn't exist.
142 */
143 std::tuple<VAddr, u32> GetExecutablePages() const;
144
145private:
146 const VAddr module_address; ///< the virtual address of this module
147
148 /**
149 * Each item in this enum represents a u32 field in the header begin from address+0x80,
150 * successively. We don't directly use a struct here, to avoid GetPointer, reinterpret_cast, or
151 * Read/WriteBlock repeatedly.
152 */
153 enum HeaderField {
154 Magic = 0,
155 NameOffset,
156 NextCRO,
157 PreviousCRO,
158 FileSize,
159 BssSize,
160 FixedSize,
161 UnknownZero,
162 UnkSegmentTag,
163 OnLoadSegmentTag,
164 OnExitSegmentTag,
165 OnUnresolvedSegmentTag,
166
167 CodeOffset,
168 CodeSize,
169 DataOffset,
170 DataSize,
171 ModuleNameOffset,
172 ModuleNameSize,
173 SegmentTableOffset,
174 SegmentNum,
175
176 ExportNamedSymbolTableOffset,
177 ExportNamedSymbolNum,
178 ExportIndexedSymbolTableOffset,
179 ExportIndexedSymbolNum,
180 ExportStringsOffset,
181 ExportStringsSize,
182 ExportTreeTableOffset,
183 ExportTreeNum,
184
185 ImportModuleTableOffset,
186 ImportModuleNum,
187 ExternalRelocationTableOffset,
188 ExternalRelocationNum,
189 ImportNamedSymbolTableOffset,
190 ImportNamedSymbolNum,
191 ImportIndexedSymbolTableOffset,
192 ImportIndexedSymbolNum,
193 ImportAnonymousSymbolTableOffset,
194 ImportAnonymousSymbolNum,
195 ImportStringsOffset,
196 ImportStringsSize,
197
198 StaticAnonymousSymbolTableOffset,
199 StaticAnonymousSymbolNum,
200 InternalRelocationTableOffset,
201 InternalRelocationNum,
202 StaticRelocationTableOffset,
203 StaticRelocationNum,
204 Fix0Barrier,
205
206 Fix3Barrier = ExportNamedSymbolTableOffset,
207 Fix2Barrier = ImportModuleTableOffset,
208 Fix1Barrier = StaticAnonymousSymbolTableOffset,
209 };
210 static_assert(Fix0Barrier == (CRO_HEADER_SIZE - CRO_HASH_SIZE) / 4,
211 "CRO Header fields are wrong!");
212
213 enum class SegmentType : u32 {
214 Code = 0,
215 ROData = 1,
216 Data = 2,
217 BSS = 3,
218 };
219
220 /**
221 * Identifies a program location inside of a segment.
222 * Required to refer to program locations because individual segments may be relocated
223 * independently of each other.
224 */
225 union SegmentTag {
226 u32_le raw;
227 BitField<0, 4, u32_le> segment_index;
228 BitField<4, 28, u32_le> offset_into_segment;
229
230 SegmentTag() = default;
231 explicit SegmentTag(u32 raw_) : raw(raw_) {}
232 };
233
234 /// Information of a segment in this module.
235 struct SegmentEntry {
236 u32_le offset;
237 u32_le size;
238 SegmentType type;
239
240 static constexpr HeaderField TABLE_OFFSET_FIELD = SegmentTableOffset;
241 };
242 ASSERT_CRO_STRUCT(SegmentEntry, 12);
243
244 /// Identifies a named symbol exported from this module.
245 struct ExportNamedSymbolEntry {
246 u32_le name_offset; // pointing to a substring in ExportStrings
247 SegmentTag symbol_position; // to self's segment
248
249 static constexpr HeaderField TABLE_OFFSET_FIELD = ExportNamedSymbolTableOffset;
250 };
251 ASSERT_CRO_STRUCT(ExportNamedSymbolEntry, 8);
252
253 /// Identifies an indexed symbol exported from this module.
254 struct ExportIndexedSymbolEntry {
255 SegmentTag symbol_position; // to self's segment
256
257 static constexpr HeaderField TABLE_OFFSET_FIELD = ExportIndexedSymbolTableOffset;
258 };
259 ASSERT_CRO_STRUCT(ExportIndexedSymbolEntry, 4);
260
261 /// A tree node in the symbol lookup tree.
262 struct ExportTreeEntry {
263 u16_le test_bit; // bit address into the name to test
264 union Child {
265 u16_le raw;
266 BitField<0, 15, u16_le> next_index;
267 BitField<15, 1, u16_le> is_end;
268 } left, right;
269 u16_le export_table_index; // index of an ExportNamedSymbolEntry
270
271 static constexpr HeaderField TABLE_OFFSET_FIELD = ExportTreeTableOffset;
272 };
273 ASSERT_CRO_STRUCT(ExportTreeEntry, 8);
274
275 /// Identifies a named symbol imported from another module.
276 struct ImportNamedSymbolEntry {
277 u32_le name_offset; // pointing to a substring in ImportStrings
278 u32_le relocation_batch_offset; // pointing to a relocation batch in ExternalRelocationTable
279
280 static constexpr HeaderField TABLE_OFFSET_FIELD = ImportNamedSymbolTableOffset;
281 };
282 ASSERT_CRO_STRUCT(ImportNamedSymbolEntry, 8);
283
284 /// Identifies an indexed symbol imported from another module.
285 struct ImportIndexedSymbolEntry {
286 u32_le index; // index of an ExportIndexedSymbolEntry in the exporting module
287 u32_le relocation_batch_offset; // pointing to a relocation batch in ExternalRelocationTable
288
289 static constexpr HeaderField TABLE_OFFSET_FIELD = ImportIndexedSymbolTableOffset;
290 };
291 ASSERT_CRO_STRUCT(ImportIndexedSymbolEntry, 8);
292
293 /// Identifies an anonymous symbol imported from another module.
294 struct ImportAnonymousSymbolEntry {
295 SegmentTag symbol_position; // in the exporting segment
296 u32_le relocation_batch_offset; // pointing to a relocation batch in ExternalRelocationTable
297
298 static constexpr HeaderField TABLE_OFFSET_FIELD = ImportAnonymousSymbolTableOffset;
299 };
300 ASSERT_CRO_STRUCT(ImportAnonymousSymbolEntry, 8);
301
302 /// Information of a imported module and symbols imported from it.
303 struct ImportModuleEntry {
304 u32_le name_offset; // pointing to a substring in ImportStrings
305 u32_le import_indexed_symbol_table_offset; // pointing to a subtable in
306 // ImportIndexedSymbolTable
307 u32_le import_indexed_symbol_num;
308 u32_le import_anonymous_symbol_table_offset; // pointing to a subtable in
309 // ImportAnonymousSymbolTable
310 u32_le import_anonymous_symbol_num;
311
312 static constexpr HeaderField TABLE_OFFSET_FIELD = ImportModuleTableOffset;
313
314 void GetImportIndexedSymbolEntry(u32 index, ImportIndexedSymbolEntry& entry) {
315 Memory::ReadBlock(import_indexed_symbol_table_offset +
316 index * sizeof(ImportIndexedSymbolEntry),
317 &entry, sizeof(ImportIndexedSymbolEntry));
318 }
319
320 void GetImportAnonymousSymbolEntry(u32 index, ImportAnonymousSymbolEntry& entry) {
321 Memory::ReadBlock(import_anonymous_symbol_table_offset +
322 index * sizeof(ImportAnonymousSymbolEntry),
323 &entry, sizeof(ImportAnonymousSymbolEntry));
324 }
325 };
326 ASSERT_CRO_STRUCT(ImportModuleEntry, 20);
327
328 enum class RelocationType : u8 {
329 Nothing = 0,
330 AbsoluteAddress = 2,
331 RelativeAddress = 3,
332 ThumbBranch = 10,
333 ArmBranch = 28,
334 ModifyArmBranch = 29,
335 AbsoluteAddress2 = 38,
336 AlignedRelativeAddress = 42,
337 };
338
339 struct RelocationEntry {
340 SegmentTag target_position; // to self's segment as an ExternalRelocationEntry; to static
341 // module segment as a StaticRelocationEntry
342 RelocationType type;
343 u8 is_batch_end;
344 u8 is_batch_resolved; // set at a batch beginning if the batch is resolved
345 INSERT_PADDING_BYTES(1);
346 u32_le addend;
347 };
348
349 /// Identifies a normal cross-module relocation.
350 struct ExternalRelocationEntry : RelocationEntry {
351 static constexpr HeaderField TABLE_OFFSET_FIELD = ExternalRelocationTableOffset;
352 };
353 ASSERT_CRO_STRUCT(ExternalRelocationEntry, 12);
354
355 /// Identifies a special static relocation (no game is known using this).
356 struct StaticRelocationEntry : RelocationEntry {
357 static constexpr HeaderField TABLE_OFFSET_FIELD = StaticRelocationTableOffset;
358 };
359 ASSERT_CRO_STRUCT(StaticRelocationEntry, 12);
360
361 /// Identifies a in-module relocation.
362 struct InternalRelocationEntry {
363 SegmentTag target_position; // to self's segment
364 RelocationType type;
365 u8 symbol_segment;
366 INSERT_PADDING_BYTES(2);
367 u32_le addend;
368
369 static constexpr HeaderField TABLE_OFFSET_FIELD = InternalRelocationTableOffset;
370 };
371 ASSERT_CRO_STRUCT(InternalRelocationEntry, 12);
372
373 /// Identifies a special static anonymous symbol (no game is known using this).
374 struct StaticAnonymousSymbolEntry {
375 SegmentTag symbol_position; // to self's segment
376 u32_le relocation_batch_offset; // pointing to a relocation batch in StaticRelocationTable
377
378 static constexpr HeaderField TABLE_OFFSET_FIELD = StaticAnonymousSymbolTableOffset;
379 };
380 ASSERT_CRO_STRUCT(StaticAnonymousSymbolEntry, 8);
381
382 /**
383 * Entry size of each table, from Code to StaticRelocationTable.
384 * Byte string contents (such as Code) are treated with entries of size 1.
385 * This is used for verifying the size of each table and calculating the fix end.
386 */
387 static const std::array<int, 17> ENTRY_SIZE;
388
389 /// The offset field of the table where to crop for each fix level
390 static const std::array<HeaderField, 4> FIX_BARRIERS;
391
392 static constexpr u32 MAGIC_CRO0 = 0x304F5243;
393 static constexpr u32 MAGIC_FIXD = 0x44584946;
394
395 VAddr Field(HeaderField field) const {
396 return module_address + CRO_HASH_SIZE + field * 4;
397 }
398
399 u32 GetField(HeaderField field) const {
400 return Memory::Read32(Field(field));
401 }
402
403 void SetField(HeaderField field, u32 value) {
404 Memory::Write32(Field(field), value);
405 }
406
407 /**
408 * Reads an entry in one of module tables.
409 * @param index index of the entry
410 * @param data where to put the read entry
411 * @note the entry type must have the static member TABLE_OFFSET_FIELD
412 * indicating which table the entry is in.
413 */
414 template <typename T>
415 void GetEntry(std::size_t index, T& data) const {
416 Memory::ReadBlock(GetField(T::TABLE_OFFSET_FIELD) + static_cast<u32>(index * sizeof(T)),
417 &data, sizeof(T));
418 }
419
420 /**
421 * Writes an entry to one of module tables.
422 * @param index index of the entry
423 * @param data the entry data to write
424 * @note the entry type must have the static member TABLE_OFFSET_FIELD
425 * indicating which table the entry is in.
426 */
427 template <typename T>
428 void SetEntry(std::size_t index, const T& data) {
429 Memory::WriteBlock(GetField(T::TABLE_OFFSET_FIELD) + static_cast<u32>(index * sizeof(T)),
430 &data, sizeof(T));
431 }
432
433 /**
434 * Converts a segment tag to virtual address in this module.
435 * @param segment_tag the segment tag to convert
436 * @returns VAddr the virtual address the segment tag points to; 0 if invalid.
437 */
438 VAddr SegmentTagToAddress(SegmentTag segment_tag) const;
439
440 VAddr NextModule() const {
441 return GetField(NextCRO);
442 }
443
444 VAddr PreviousModule() const {
445 return GetField(PreviousCRO);
446 }
447
448 void SetNextModule(VAddr next) {
449 SetField(NextCRO, next);
450 }
451
452 void SetPreviousModule(VAddr previous) {
453 SetField(PreviousCRO, previous);
454 }
455
456 /**
457 * A helper function iterating over all registered auto-link modules, including the static
458 * module.
459 * @param crs_address the virtual address of the static module
460 * @param func a function object to operate on a module. It accepts one parameter
461 * CROHelper and returns ResultVal<bool>. It should return true to continue the
462 * iteration,
463 * false to stop the iteration, or an error code (which will also stop the iteration).
464 * @returns ResultCode indicating the result of the operation, RESULT_SUCCESS if all iteration
465 * success,
466 * otherwise error code of the last iteration.
467 */
468 template <typename FunctionObject>
469 static ResultCode ForEachAutoLinkCRO(VAddr crs_address, FunctionObject func) {
470 VAddr current = crs_address;
471 while (current != 0) {
472 CROHelper cro(current);
473 CASCADE_RESULT(bool next, func(cro));
474 if (!next)
475 break;
476 current = cro.NextModule();
477 }
478 return RESULT_SUCCESS;
479 }
480
481 /**
482 * Applies a relocation
483 * @param target_address where to apply the relocation
484 * @param relocation_type the type of the relocation
485 * @param addend address addend applied to the relocated symbol
486 * @param symbol_address the symbol address to be relocated with
487 * @param target_future_address the future address of the target.
488 * Usually equals to target_address, but will be different for a target in .data segment
489 * @returns ResultCode RESULT_SUCCESS on success, otherwise error code.
490 */
491 ResultCode ApplyRelocation(VAddr target_address, RelocationType relocation_type, u32 addend,
492 u32 symbol_address, u32 target_future_address);
493
494 /**
495 * Clears a relocation to zero
496 * @param target_address where to apply the relocation
497 * @param relocation_type the type of the relocation
498 * @returns ResultCode RESULT_SUCCESS on success, otherwise error code.
499 */
500 ResultCode ClearRelocation(VAddr target_address, RelocationType relocation_type);
501
502 /**
503 * Applies or resets a batch of relocations
504 * @param batch the virtual address of the first relocation in the batch
505 * @param symbol_address the symbol address to be relocated with
506 * @param reset false to set the batch to resolved state, true to reset the batch to unresolved
507 * state
508 * @returns ResultCode RESULT_SUCCESS on success, otherwise error code.
509 */
510 ResultCode ApplyRelocationBatch(VAddr batch, u32 symbol_address, bool reset = false);
511
512 /**
513 * Finds an exported named symbol in this module.
514 * @param name the name of the symbol to find
515 * @return VAddr the virtual address of the symbol; 0 if not found.
516 */
517 VAddr FindExportNamedSymbol(const std::string& name) const;
518
519 /**
520 * Rebases offsets in module header according to module address.
521 * @param cro_size the size of the CRO file
522 * @returns ResultCode RESULT_SUCCESS if all offsets are verified as valid, otherwise error
523 * code.
524 */
525 ResultCode RebaseHeader(u32 cro_size);
526
527 /**
528 * Rebases offsets in segment table according to module address.
529 * @param cro_size the size of the CRO file
530 * @param data_segment_address the buffer address for .data segment
531 * @param data_segment_size the buffer size for .data segment
532 * @param bss_segment_address the buffer address for .bss segment
533 * @param bss_segment_size the buffer size for .bss segment
534 * @returns ResultVal<VAddr> with the virtual address of .data segment in CRO.
535 */
536 ResultVal<VAddr> RebaseSegmentTable(u32 cro_size, VAddr data_segment_address,
537 u32 data_segment_size, VAddr bss_segment_address,
538 u32 bss_segment_size);
539
540 /**
541 * Rebases offsets in exported named symbol table according to module address.
542 * @returns ResultCode RESULT_SUCCESS if all offsets are verified as valid, otherwise error
543 * code.
544 */
545 ResultCode RebaseExportNamedSymbolTable();
546
547 /**
548 * Verifies indices in export tree table.
549 * @returns ResultCode RESULT_SUCCESS if all indices are verified as valid, otherwise error
550 * code.
551 */
552 ResultCode VerifyExportTreeTable() const;
553
554 /**
555 * Rebases offsets in exported module table according to module address.
556 * @returns ResultCode RESULT_SUCCESS if all offsets are verified as valid, otherwise error
557 * code.
558 */
559 ResultCode RebaseImportModuleTable();
560
561 /**
562 * Rebases offsets in imported named symbol table according to module address.
563 * @returns ResultCode RESULT_SUCCESS if all offsets are verified as valid, otherwise error
564 * code.
565 */
566 ResultCode RebaseImportNamedSymbolTable();
567
568 /**
569 * Rebases offsets in imported indexed symbol table according to module address.
570 * @returns ResultCode RESULT_SUCCESS if all offsets are verified as valid, otherwise error
571 * code.
572 */
573 ResultCode RebaseImportIndexedSymbolTable();
574
575 /**
576 * Rebases offsets in imported anonymous symbol table according to module address.
577 * @returns ResultCode RESULT_SUCCESS if all offsets are verified as valid, otherwise error
578 * code.
579 */
580 ResultCode RebaseImportAnonymousSymbolTable();
581
582 /**
583 * Gets the address of OnUnresolved function in this module.
584 * Used as the applied symbol for reset relocation.
585 * @returns the virtual address of OnUnresolved. 0 if not provided.
586 */
587 VAddr GetOnUnresolvedAddress();
588
589 /**
590 * Resets all external relocations to unresolved state.
591 * @returns ResultCode RESULT_SUCCESS on success, otherwise error code.
592 */
593 ResultCode ResetExternalRelocations();
594
595 /**
596 * Clears all external relocations to zero.
597 * @returns ResultCode RESULT_SUCCESS on success, otherwise error code.
598 */
599 ResultCode ClearExternalRelocations();
600
601 /**
602 * Applies all static anonymous symbol to the static module.
603 * @param crs_address the virtual address of the static module
604 * @returns ResultCode RESULT_SUCCESS on success, otherwise error code.
605 */
606 ResultCode ApplyStaticAnonymousSymbolToCRS(VAddr crs_address);
607
608 /**
609 * Applies all internal relocations to the module itself.
610 * @param old_data_segment_address the virtual address of data segment in CRO buffer
611 * @returns ResultCode RESULT_SUCCESS on success, otherwise error code.
612 */
613 ResultCode ApplyInternalRelocations(u32 old_data_segment_address);
614
615 /**
616 * Clears all internal relocations to zero.
617 * @returns ResultCode RESULT_SUCCESS on success, otherwise error code.
618 */
619 ResultCode ClearInternalRelocations();
620
621 /// Unrebases offsets in imported anonymous symbol table
622 void UnrebaseImportAnonymousSymbolTable();
623
624 /// Unrebases offsets in imported indexed symbol table
625 void UnrebaseImportIndexedSymbolTable();
626
627 /// Unrebases offsets in imported named symbol table
628 void UnrebaseImportNamedSymbolTable();
629
630 /// Unrebases offsets in imported module table
631 void UnrebaseImportModuleTable();
632
633 /// Unrebases offsets in exported named symbol table
634 void UnrebaseExportNamedSymbolTable();
635
636 /// Unrebases offsets in segment table
637 void UnrebaseSegmentTable();
638
639 /// Unrebases offsets in module header
640 void UnrebaseHeader();
641
642 /**
643 * Looks up all imported named symbols of this module in all registered auto-link modules, and
644 * resolves them if found.
645 * @param crs_address the virtual address of the static module
646 * @returns ResultCode RESULT_SUCCESS on success, otherwise error code.
647 */
648 ResultCode ApplyImportNamedSymbol(VAddr crs_address);
649
650 /**
651 * Resets all imported named symbols of this module to unresolved state.
652 * @returns ResultCode RESULT_SUCCESS on success, otherwise error code.
653 */
654 ResultCode ResetImportNamedSymbol();
655
656 /**
657 * Resets all imported indexed symbols of this module to unresolved state.
658 * @returns ResultCode RESULT_SUCCESS on success, otherwise error code.
659 */
660 ResultCode ResetImportIndexedSymbol();
661
662 /**
663 * Resets all imported anonymous symbols of this module to unresolved state.
664 * @returns ResultCode RESULT_SUCCESS on success, otherwise error code.
665 */
666 ResultCode ResetImportAnonymousSymbol();
667
668 /**
669 * Finds registered auto-link modules that this module imports, and resolves indexed and
670 * anonymous symbols exported by them.
671 * @param crs_address the virtual address of the static module
672 * @returns ResultCode RESULT_SUCCESS on success, otherwise error code.
673 */
674 ResultCode ApplyModuleImport(VAddr crs_address);
675
676 /**
677 * Resolves target module's imported named symbols that exported by this module.
678 * @param target the module to resolve.
679 * @returns ResultCode RESULT_SUCCESS on success, otherwise error code.
680 */
681 ResultCode ApplyExportNamedSymbol(CROHelper target);
682
683 /**
684 * Resets target's named symbols imported from this module to unresolved state.
685 * @param target the module to reset.
686 * @returns ResultCode RESULT_SUCCESS on success, otherwise error code.
687 */
688 ResultCode ResetExportNamedSymbol(CROHelper target);
689
690 /**
691 * Resolves imported indexed and anonymous symbols in the target module which imports this
692 * module.
693 * @param target the module to resolve.
694 * @returns ResultCode RESULT_SUCCESS on success, otherwise error code.
695 */
696 ResultCode ApplyModuleExport(CROHelper target);
697
698 /**
699 * Resets target's indexed and anonymous symbol imported from this module to unresolved state.
700 * @param target the module to reset.
701 * @returns ResultCode RESULT_SUCCESS on success, otherwise error code.
702 */
703 ResultCode ResetModuleExport(CROHelper target);
704
705 /**
706 * Resolves the exit function in this module
707 * @param crs_address the virtual address of the static module.
708 * @returns ResultCode RESULT_SUCCESS on success, otherwise error code.
709 */
710 ResultCode ApplyExitRelocations(VAddr crs_address);
711};
712
713} // namespace LDR
714} // namespace Service
diff --git a/src/core/hle/service/ldr_ro/ldr_ro.cpp b/src/core/hle/service/ldr_ro/ldr_ro.cpp
deleted file mode 100644
index 7255ea026..000000000
--- a/src/core/hle/service/ldr_ro/ldr_ro.cpp
+++ /dev/null
@@ -1,718 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/alignment.h"
6#include "common/common_types.h"
7#include "common/logging/log.h"
8#include "core/arm/arm_interface.h"
9#include "core/core.h"
10#include "core/hle/ipc_helpers.h"
11#include "core/hle/kernel/process.h"
12#include "core/hle/kernel/vm_manager.h"
13#include "core/hle/service/ldr_ro/cro_helper.h"
14#include "core/hle/service/ldr_ro/ldr_ro.h"
15#include "core/hle/service/ldr_ro/memory_synchronizer.h"
16
17namespace Service {
18namespace LDR {
19
20static const ResultCode ERROR_ALREADY_INITIALIZED = // 0xD9612FF9
21 ResultCode(ErrorDescription::AlreadyInitialized, ErrorModule::RO, ErrorSummary::Internal,
22 ErrorLevel::Permanent);
23static const ResultCode ERROR_NOT_INITIALIZED = // 0xD9612FF8
24 ResultCode(ErrorDescription::NotInitialized, ErrorModule::RO, ErrorSummary::Internal,
25 ErrorLevel::Permanent);
26static const ResultCode ERROR_BUFFER_TOO_SMALL = // 0xE0E12C1F
27 ResultCode(static_cast<ErrorDescription>(31), ErrorModule::RO, ErrorSummary::InvalidArgument,
28 ErrorLevel::Usage);
29static const ResultCode ERROR_MISALIGNED_ADDRESS = // 0xD9012FF1
30 ResultCode(ErrorDescription::MisalignedAddress, ErrorModule::RO, ErrorSummary::WrongArgument,
31 ErrorLevel::Permanent);
32static const ResultCode ERROR_MISALIGNED_SIZE = // 0xD9012FF2
33 ResultCode(ErrorDescription::MisalignedSize, ErrorModule::RO, ErrorSummary::WrongArgument,
34 ErrorLevel::Permanent);
35static const ResultCode ERROR_ILLEGAL_ADDRESS = // 0xE1612C0F
36 ResultCode(static_cast<ErrorDescription>(15), ErrorModule::RO, ErrorSummary::Internal,
37 ErrorLevel::Usage);
38static const ResultCode ERROR_INVALID_MEMORY_STATE = // 0xD8A12C08
39 ResultCode(static_cast<ErrorDescription>(8), ErrorModule::RO, ErrorSummary::InvalidState,
40 ErrorLevel::Permanent);
41static const ResultCode ERROR_NOT_LOADED = // 0xD8A12C0D
42 ResultCode(static_cast<ErrorDescription>(13), ErrorModule::RO, ErrorSummary::InvalidState,
43 ErrorLevel::Permanent);
44
45static MemorySynchronizer memory_synchronizer;
46
47// TODO(wwylele): this should be in the per-client storage when we implement multi-process
48static VAddr loaded_crs; ///< the virtual address of the static module
49
50static bool VerifyBufferState(VAddr buffer_ptr, u32 size) {
51 auto vma = Kernel::g_current_process->vm_manager.FindVMA(buffer_ptr);
52 return vma != Kernel::g_current_process->vm_manager.vma_map.end() &&
53 vma->second.base + vma->second.size >= buffer_ptr + size &&
54 vma->second.permissions == Kernel::VMAPermission::ReadWrite &&
55 vma->second.meminfo_state == Kernel::MemoryState::Private;
56}
57
58/**
59 * LDR_RO::Initialize service function
60 * Inputs:
61 * 0 : 0x000100C2
62 * 1 : CRS buffer pointer
63 * 2 : CRS Size
64 * 3 : Process memory address where the CRS will be mapped
65 * 4 : handle translation descriptor (zero)
66 * 5 : KProcess handle
67 * Outputs:
68 * 0 : Return header
69 * 1 : Result of function, 0 on success, otherwise error code
70 */
71static void Initialize(Interface* self) {
72 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x01, 3, 2);
73 VAddr crs_buffer_ptr = rp.Pop<u32>();
74 u32 crs_size = rp.Pop<u32>();
75 VAddr crs_address = rp.Pop<u32>();
76 // TODO (wwylele): RO service checks the descriptor here and return error 0xD9001830 for
77 // incorrect descriptor. This error return should be probably built in IPC::RequestParser.
78 // All other service functions below have the same issue.
79 Kernel::Handle process = rp.PopHandle();
80
81 LOG_DEBUG(Service_LDR,
82 "called, crs_buffer_ptr=0x%08X, crs_address=0x%08X, crs_size=0x%X, process=0x%08X",
83 crs_buffer_ptr, crs_address, crs_size, process);
84
85 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
86
87 if (loaded_crs != 0) {
88 LOG_ERROR(Service_LDR, "Already initialized");
89 rb.Push(ERROR_ALREADY_INITIALIZED);
90 return;
91 }
92
93 if (crs_size < CRO_HEADER_SIZE) {
94 LOG_ERROR(Service_LDR, "CRS is too small");
95 rb.Push(ERROR_BUFFER_TOO_SMALL);
96 return;
97 }
98
99 if (crs_buffer_ptr & Memory::PAGE_MASK) {
100 LOG_ERROR(Service_LDR, "CRS original address is not aligned");
101 rb.Push(ERROR_MISALIGNED_ADDRESS);
102 return;
103 }
104
105 if (crs_address & Memory::PAGE_MASK) {
106 LOG_ERROR(Service_LDR, "CRS mapping address is not aligned");
107 rb.Push(ERROR_MISALIGNED_ADDRESS);
108 return;
109 }
110
111 if (crs_size & Memory::PAGE_MASK) {
112 LOG_ERROR(Service_LDR, "CRS size is not aligned");
113 rb.Push(ERROR_MISALIGNED_SIZE);
114 return;
115 }
116
117 if (!VerifyBufferState(crs_buffer_ptr, crs_size)) {
118 LOG_ERROR(Service_LDR, "CRS original buffer is in invalid state");
119 rb.Push(ERROR_INVALID_MEMORY_STATE);
120 return;
121 }
122
123 if (crs_address < Memory::PROCESS_IMAGE_VADDR ||
124 crs_address + crs_size > Memory::PROCESS_IMAGE_VADDR_END) {
125 LOG_ERROR(Service_LDR, "CRS mapping address is not in the process image region");
126 rb.Push(ERROR_ILLEGAL_ADDRESS);
127 return;
128 }
129
130 ResultCode result = RESULT_SUCCESS;
131
132 if (crs_buffer_ptr != crs_address) {
133 // TODO(wwylele): should be memory aliasing
134 std::shared_ptr<std::vector<u8>> crs_mem = std::make_shared<std::vector<u8>>(crs_size);
135 Memory::ReadBlock(crs_buffer_ptr, crs_mem->data(), crs_size);
136 result = Kernel::g_current_process->vm_manager
137 .MapMemoryBlock(crs_address, crs_mem, 0, crs_size, Kernel::MemoryState::Code)
138 .Code();
139 if (result.IsError()) {
140 LOG_ERROR(Service_LDR, "Error mapping memory block %08X", result.raw);
141 rb.Push(result);
142 return;
143 }
144
145 result = Kernel::g_current_process->vm_manager.ReprotectRange(crs_address, crs_size,
146 Kernel::VMAPermission::Read);
147 if (result.IsError()) {
148 LOG_ERROR(Service_LDR, "Error reprotecting memory block %08X", result.raw);
149 rb.Push(result);
150 return;
151 }
152
153 memory_synchronizer.AddMemoryBlock(crs_address, crs_buffer_ptr, crs_size);
154 } else {
155 // Do nothing if buffer_ptr == address
156 // TODO(wwylele): verify this behaviour. This is only seen in the web browser app,
157 // and the actual behaviour is unclear. "Do nothing" is probably an incorrect implement.
158 // There is also a chance that another issue causes the app passing wrong arguments.
159 LOG_WARNING(Service_LDR, "crs_buffer_ptr == crs_address (0x%08X)", crs_address);
160 }
161
162 CROHelper crs(crs_address);
163 crs.InitCRS();
164
165 result = crs.Rebase(0, crs_size, 0, 0, 0, 0, true);
166 if (result.IsError()) {
167 LOG_ERROR(Service_LDR, "Error rebasing CRS 0x%08X", result.raw);
168 rb.Push(result);
169 return;
170 }
171
172 memory_synchronizer.SynchronizeOriginalMemory();
173
174 loaded_crs = crs_address;
175
176 rb.Push(RESULT_SUCCESS);
177}
178
179/**
180 * LDR_RO::LoadCRR service function
181 * Inputs:
182 * 0 : 0x00020082
183 * 1 : CRR buffer pointer
184 * 2 : CRR Size
185 * 3 : handle translation descriptor (zero)
186 * 4 : KProcess handle
187 * Outputs:
188 * 0 : Return header
189 * 1 : Result of function, 0 on success, otherwise error code
190 */
191static void LoadCRR(Interface* self) {
192 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x02, 2, 2);
193 VAddr crr_buffer_ptr = rp.Pop<u32>();
194 u32 crr_size = rp.Pop<u32>();
195 Kernel::Handle process = rp.PopHandle();
196
197 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
198 rb.Push(RESULT_SUCCESS);
199
200 LOG_WARNING(Service_LDR,
201 "(STUBBED) called, crr_buffer_ptr=0x%08X, crr_size=0x%08X, process=0x%08X",
202 crr_buffer_ptr, crr_size, process);
203}
204
205/**
206 * LDR_RO::UnloadCRR service function
207 * Inputs:
208 * 0 : 0x00030042
209 * 1 : CRR buffer pointer
210 * 2 : handle translation descriptor (zero)
211 * 3 : KProcess handle
212 * Outputs:
213 * 0 : Return header
214 * 1 : Result of function, 0 on success, otherwise error code
215 */
216static void UnloadCRR(Interface* self) {
217 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x03, 1, 2);
218 u32 crr_buffer_ptr = rp.Pop<u32>();
219 Kernel::Handle process = rp.PopHandle();
220
221 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
222 rb.Push(RESULT_SUCCESS);
223
224 LOG_WARNING(Service_LDR, "(STUBBED) called, crr_buffer_ptr=0x%08X, process=0x%08X",
225 crr_buffer_ptr, process);
226}
227
228/**
229 * LDR_RO::LoadCRO service function
230 * Inputs:
231 * 0 : 0x000402C2 (old) / 0x000902C2 (new)
232 * 1 : CRO buffer pointer
233 * 2 : memory address where the CRO will be mapped
234 * 3 : CRO Size
235 * 4 : .data segment buffer pointer
236 * 5 : must be zero
237 * 6 : .data segment buffer size
238 * 7 : .bss segment buffer pointer
239 * 8 : .bss segment buffer size
240 * 9 : (bool) register CRO as auto-link module
241 * 10 : fix level
242 * 11 : CRR address (zero if use loaded CRR)
243 * 12 : handle translation descriptor (zero)
244 * 13 : KProcess handle
245 * Outputs:
246 * 0 : Return header
247 * 1 : Result of function, 0 on success, otherwise error code
248 * 2 : CRO fixed size
249 * Note:
250 * This service function has two versions. The function defined here is a
251 * unified one of two, with an additional parameter link_on_load_bug_fix.
252 * There is a dispatcher template below.
253 */
254static void LoadCRO(Interface* self, bool link_on_load_bug_fix) {
255 IPC::RequestParser rp(Kernel::GetCommandBuffer(), link_on_load_bug_fix ? 0x09 : 0x04, 11, 2);
256 VAddr cro_buffer_ptr = rp.Pop<u32>();
257 VAddr cro_address = rp.Pop<u32>();
258 u32 cro_size = rp.Pop<u32>();
259 VAddr data_segment_address = rp.Pop<u32>();
260 u32 zero = rp.Pop<u32>();
261 u32 data_segment_size = rp.Pop<u32>();
262 u32 bss_segment_address = rp.Pop<u32>();
263 u32 bss_segment_size = rp.Pop<u32>();
264 bool auto_link = rp.Pop<bool>();
265 u32 fix_level = rp.Pop<u32>();
266 VAddr crr_address = rp.Pop<u32>();
267 Kernel::Handle process = rp.PopHandle();
268
269 LOG_DEBUG(Service_LDR, "called (%s), cro_buffer_ptr=0x%08X, cro_address=0x%08X, cro_size=0x%X, "
270 "data_segment_address=0x%08X, zero=%d, data_segment_size=0x%X, "
271 "bss_segment_address=0x%08X, bss_segment_size=0x%X, auto_link=%s, "
272 "fix_level=%d, crr_address=0x%08X, process=0x%08X",
273 link_on_load_bug_fix ? "new" : "old", cro_buffer_ptr, cro_address, cro_size,
274 data_segment_address, zero, data_segment_size, bss_segment_address, bss_segment_size,
275 auto_link ? "true" : "false", fix_level, crr_address, process);
276
277 IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
278
279 if (loaded_crs == 0) {
280 LOG_ERROR(Service_LDR, "Not initialized");
281 rb.Push(ERROR_NOT_INITIALIZED);
282 rb.Push<u32>(0);
283 return;
284 }
285
286 if (cro_size < CRO_HEADER_SIZE) {
287 LOG_ERROR(Service_LDR, "CRO too small");
288 rb.Push(ERROR_BUFFER_TOO_SMALL);
289 rb.Push<u32>(0);
290 return;
291 }
292
293 if (cro_buffer_ptr & Memory::PAGE_MASK) {
294 LOG_ERROR(Service_LDR, "CRO original address is not aligned");
295 rb.Push(ERROR_MISALIGNED_ADDRESS);
296 rb.Push<u32>(0);
297 return;
298 }
299
300 if (cro_address & Memory::PAGE_MASK) {
301 LOG_ERROR(Service_LDR, "CRO mapping address is not aligned");
302 rb.Push(ERROR_MISALIGNED_ADDRESS);
303 rb.Push<u32>(0);
304 return;
305 }
306
307 if (cro_size & Memory::PAGE_MASK) {
308 LOG_ERROR(Service_LDR, "CRO size is not aligned");
309 rb.Push(ERROR_MISALIGNED_SIZE);
310 rb.Push<u32>(0);
311 return;
312 }
313
314 if (!VerifyBufferState(cro_buffer_ptr, cro_size)) {
315 LOG_ERROR(Service_LDR, "CRO original buffer is in invalid state");
316 rb.Push(ERROR_INVALID_MEMORY_STATE);
317 rb.Push<u32>(0);
318 return;
319 }
320
321 if (cro_address < Memory::PROCESS_IMAGE_VADDR ||
322 cro_address + cro_size > Memory::PROCESS_IMAGE_VADDR_END) {
323 LOG_ERROR(Service_LDR, "CRO mapping address is not in the process image region");
324 rb.Push(ERROR_ILLEGAL_ADDRESS);
325 rb.Push<u32>(0);
326 return;
327 }
328
329 if (zero) {
330 LOG_ERROR(Service_LDR, "Zero is not zero %d", zero);
331 rb.Push(ResultCode(static_cast<ErrorDescription>(29), ErrorModule::RO,
332 ErrorSummary::Internal, ErrorLevel::Usage));
333 rb.Push<u32>(0);
334 return;
335 }
336
337 ResultCode result = RESULT_SUCCESS;
338
339 if (cro_buffer_ptr != cro_address) {
340 // TODO(wwylele): should be memory aliasing
341 std::shared_ptr<std::vector<u8>> cro_mem = std::make_shared<std::vector<u8>>(cro_size);
342 Memory::ReadBlock(cro_buffer_ptr, cro_mem->data(), cro_size);
343 result = Kernel::g_current_process->vm_manager
344 .MapMemoryBlock(cro_address, cro_mem, 0, cro_size, Kernel::MemoryState::Code)
345 .Code();
346 if (result.IsError()) {
347 LOG_ERROR(Service_LDR, "Error mapping memory block %08X", result.raw);
348 rb.Push(result);
349 rb.Push<u32>(0);
350 return;
351 }
352
353 result = Kernel::g_current_process->vm_manager.ReprotectRange(cro_address, cro_size,
354 Kernel::VMAPermission::Read);
355 if (result.IsError()) {
356 LOG_ERROR(Service_LDR, "Error reprotecting memory block %08X", result.raw);
357 Kernel::g_current_process->vm_manager.UnmapRange(cro_address, cro_size);
358 rb.Push(result);
359 rb.Push<u32>(0);
360 return;
361 }
362
363 memory_synchronizer.AddMemoryBlock(cro_address, cro_buffer_ptr, cro_size);
364 } else {
365 // Do nothing if buffer_ptr == address
366 // TODO(wwylele): verify this behaviour.
367 // This is derived from the case of LoadCRS with buffer_ptr==address,
368 // and is never seen in any game. "Do nothing" is probably an incorrect implement.
369 // There is also a chance that this case is just prohibited.
370 LOG_WARNING(Service_LDR, "cro_buffer_ptr == cro_address (0x%08X)", cro_address);
371 }
372
373 CROHelper cro(cro_address);
374
375 result = cro.VerifyHash(cro_size, crr_address);
376 if (result.IsError()) {
377 LOG_ERROR(Service_LDR, "Error verifying CRO in CRR %08X", result.raw);
378 Kernel::g_current_process->vm_manager.UnmapRange(cro_address, cro_size);
379 rb.Push(result);
380 rb.Push<u32>(0);
381 return;
382 }
383
384 result = cro.Rebase(loaded_crs, cro_size, data_segment_address, data_segment_size,
385 bss_segment_address, bss_segment_size, false);
386 if (result.IsError()) {
387 LOG_ERROR(Service_LDR, "Error rebasing CRO %08X", result.raw);
388 Kernel::g_current_process->vm_manager.UnmapRange(cro_address, cro_size);
389 rb.Push(result);
390 rb.Push<u32>(0);
391 return;
392 }
393
394 result = cro.Link(loaded_crs, link_on_load_bug_fix);
395 if (result.IsError()) {
396 LOG_ERROR(Service_LDR, "Error linking CRO %08X", result.raw);
397 Kernel::g_current_process->vm_manager.UnmapRange(cro_address, cro_size);
398 rb.Push(result);
399 rb.Push<u32>(0);
400 return;
401 }
402
403 cro.Register(loaded_crs, auto_link);
404
405 u32 fix_size = cro.Fix(fix_level);
406
407 memory_synchronizer.SynchronizeOriginalMemory();
408
409 // TODO(wwylele): verify the behaviour when buffer_ptr == address
410 if (cro_buffer_ptr != cro_address) {
411 if (fix_size != cro_size) {
412 result = Kernel::g_current_process->vm_manager.UnmapRange(cro_address + fix_size,
413 cro_size - fix_size);
414 if (result.IsError()) {
415 LOG_ERROR(Service_LDR, "Error unmapping memory block %08X", result.raw);
416 Kernel::g_current_process->vm_manager.UnmapRange(cro_address, cro_size);
417 rb.Push(result);
418 rb.Push<u32>(0);
419 return;
420 }
421 }
422
423 // Changes the block size
424 memory_synchronizer.ResizeMemoryBlock(cro_address, cro_buffer_ptr, fix_size);
425 }
426
427 VAddr exe_begin;
428 u32 exe_size;
429 std::tie(exe_begin, exe_size) = cro.GetExecutablePages();
430 if (exe_begin) {
431 result = Kernel::g_current_process->vm_manager.ReprotectRange(
432 exe_begin, exe_size, Kernel::VMAPermission::ReadExecute);
433 if (result.IsError()) {
434 LOG_ERROR(Service_LDR, "Error reprotecting memory block %08X", result.raw);
435 Kernel::g_current_process->vm_manager.UnmapRange(cro_address, fix_size);
436 rb.Push(result);
437 rb.Push<u32>(0);
438 return;
439 }
440 }
441
442 Core::CPU().ClearInstructionCache();
443
444 LOG_INFO(Service_LDR, "CRO \"%s\" loaded at 0x%08X, fixed_end=0x%08X", cro.ModuleName().data(),
445 cro_address, cro_address + fix_size);
446
447 rb.Push(RESULT_SUCCESS, fix_size);
448}
449
450template <bool link_on_load_bug_fix>
451static void LoadCRO(Interface* self) {
452 LoadCRO(self, link_on_load_bug_fix);
453}
454
455/**
456 * LDR_RO::UnloadCRO service function
457 * Inputs:
458 * 0 : 0x000500C2
459 * 1 : mapped CRO pointer
460 * 2 : zero? (RO service doesn't care)
461 * 3 : original CRO pointer
462 * 4 : handle translation descriptor (zero)
463 * 5 : KProcess handle
464 * Outputs:
465 * 0 : Return header
466 * 1 : Result of function, 0 on success, otherwise error code
467 */
468static void UnloadCRO(Interface* self) {
469 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x05, 3, 2);
470 VAddr cro_address = rp.Pop<u32>();
471 u32 zero = rp.Pop<u32>();
472 VAddr cro_buffer_ptr = rp.Pop<u32>();
473 Kernel::Handle process = rp.PopHandle();
474
475 LOG_DEBUG(Service_LDR,
476 "called, cro_address=0x%08X, zero=%d, cro_buffer_ptr=0x%08X, process=0x%08X",
477 cro_address, zero, cro_buffer_ptr, process);
478
479 CROHelper cro(cro_address);
480
481 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
482
483 if (loaded_crs == 0) {
484 LOG_ERROR(Service_LDR, "Not initialized");
485 rb.Push(ERROR_NOT_INITIALIZED);
486 return;
487 }
488
489 if (cro_address & Memory::PAGE_MASK) {
490 LOG_ERROR(Service_LDR, "CRO address is not aligned");
491 rb.Push(ERROR_MISALIGNED_ADDRESS);
492 return;
493 }
494
495 if (!cro.IsLoaded()) {
496 LOG_ERROR(Service_LDR, "Invalid or not loaded CRO");
497 rb.Push(ERROR_NOT_LOADED);
498 return;
499 }
500
501 LOG_INFO(Service_LDR, "Unloading CRO \"%s\"", cro.ModuleName().data());
502
503 u32 fixed_size = cro.GetFixedSize();
504
505 cro.Unregister(loaded_crs);
506
507 ResultCode result = cro.Unlink(loaded_crs);
508 if (result.IsError()) {
509 LOG_ERROR(Service_LDR, "Error unlinking CRO %08X", result.raw);
510 rb.Push(result);
511 return;
512 }
513
514 // If the module is not fixed, clears all external/internal relocations
515 // to restore the state before loading, so that it can be loaded again(?)
516 if (!cro.IsFixed()) {
517 result = cro.ClearRelocations();
518 if (result.IsError()) {
519 LOG_ERROR(Service_LDR, "Error clearing relocations %08X", result.raw);
520 rb.Push(result);
521 return;
522 }
523 }
524
525 cro.Unrebase(false);
526
527 memory_synchronizer.SynchronizeOriginalMemory();
528
529 // TODO(wwylele): verify the behaviour when buffer_ptr == address
530 if (cro_address != cro_buffer_ptr) {
531 result = Kernel::g_current_process->vm_manager.UnmapRange(cro_address, fixed_size);
532 if (result.IsError()) {
533 LOG_ERROR(Service_LDR, "Error unmapping CRO %08X", result.raw);
534 }
535 memory_synchronizer.RemoveMemoryBlock(cro_address, cro_buffer_ptr);
536 }
537
538 Core::CPU().ClearInstructionCache();
539
540 rb.Push(result);
541}
542
543/**
544 * LDR_RO::LinkCRO service function
545 * Inputs:
546 * 0 : 0x00060042
547 * 1 : mapped CRO pointer
548 * 2 : handle translation descriptor (zero)
549 * 3 : KProcess handle
550 * Outputs:
551 * 0 : Return header
552 * 1 : Result of function, 0 on success, otherwise error code
553 */
554static void LinkCRO(Interface* self) {
555 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x06, 1, 2);
556 VAddr cro_address = rp.Pop<u32>();
557 Kernel::Handle process = rp.PopHandle();
558
559 LOG_DEBUG(Service_LDR, "called, cro_address=0x%08X, process=0x%08X", cro_address, process);
560
561 CROHelper cro(cro_address);
562
563 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
564
565 if (loaded_crs == 0) {
566 LOG_ERROR(Service_LDR, "Not initialized");
567 rb.Push(ERROR_NOT_INITIALIZED);
568 return;
569 }
570
571 if (cro_address & Memory::PAGE_MASK) {
572 LOG_ERROR(Service_LDR, "CRO address is not aligned");
573 rb.Push(ERROR_MISALIGNED_ADDRESS);
574 return;
575 }
576
577 if (!cro.IsLoaded()) {
578 LOG_ERROR(Service_LDR, "Invalid or not loaded CRO");
579 rb.Push(ERROR_NOT_LOADED);
580 return;
581 }
582
583 LOG_INFO(Service_LDR, "Linking CRO \"%s\"", cro.ModuleName().data());
584
585 ResultCode result = cro.Link(loaded_crs, false);
586 if (result.IsError()) {
587 LOG_ERROR(Service_LDR, "Error linking CRO %08X", result.raw);
588 }
589
590 memory_synchronizer.SynchronizeOriginalMemory();
591 Core::CPU().ClearInstructionCache();
592
593 rb.Push(result);
594}
595
596/**
597 * LDR_RO::UnlinkCRO service function
598 * Inputs:
599 * 0 : 0x00070042
600 * 1 : mapped CRO pointer
601 * 2 : handle translation descriptor (zero)
602 * 3 : KProcess handle
603 * Outputs:
604 * 0 : Return header
605 * 1 : Result of function, 0 on success, otherwise error code
606 */
607static void UnlinkCRO(Interface* self) {
608 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x07, 1, 2);
609 VAddr cro_address = rp.Pop<u32>();
610 Kernel::Handle process = rp.PopHandle();
611
612 LOG_DEBUG(Service_LDR, "called, cro_address=0x%08X, process=0x%08X", cro_address, process);
613
614 CROHelper cro(cro_address);
615
616 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
617
618 if (loaded_crs == 0) {
619 LOG_ERROR(Service_LDR, "Not initialized");
620 rb.Push(ERROR_NOT_INITIALIZED);
621 return;
622 }
623
624 if (cro_address & Memory::PAGE_MASK) {
625 LOG_ERROR(Service_LDR, "CRO address is not aligned");
626 rb.Push(ERROR_MISALIGNED_ADDRESS);
627 return;
628 }
629
630 if (!cro.IsLoaded()) {
631 LOG_ERROR(Service_LDR, "Invalid or not loaded CRO");
632 rb.Push(ERROR_NOT_LOADED);
633 return;
634 }
635
636 LOG_INFO(Service_LDR, "Unlinking CRO \"%s\"", cro.ModuleName().data());
637
638 ResultCode result = cro.Unlink(loaded_crs);
639 if (result.IsError()) {
640 LOG_ERROR(Service_LDR, "Error unlinking CRO %08X", result.raw);
641 }
642
643 memory_synchronizer.SynchronizeOriginalMemory();
644 Core::CPU().ClearInstructionCache();
645
646 rb.Push(result);
647}
648
649/**
650 * LDR_RO::Shutdown service function
651 * Inputs:
652 * 0 : 0x00080042
653 * 1 : original CRS buffer pointer
654 * 2 : handle translation descriptor (zero)
655 * 3 : KProcess handle
656 * Outputs:
657 * 0 : Return header
658 * 1 : Result of function, 0 on success, otherwise error code
659 */
660static void Shutdown(Interface* self) {
661 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x08, 1, 2);
662 VAddr crs_buffer_ptr = rp.Pop<u32>();
663 Kernel::Handle process = rp.PopHandle();
664
665 LOG_DEBUG(Service_LDR, "called, crs_buffer_ptr=0x%08X, process=0x%08X", crs_buffer_ptr,
666 process);
667
668 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
669
670 if (loaded_crs == 0) {
671 LOG_ERROR(Service_LDR, "Not initialized");
672 rb.Push(ERROR_NOT_INITIALIZED);
673 return;
674 }
675
676 CROHelper crs(loaded_crs);
677 crs.Unrebase(true);
678
679 memory_synchronizer.SynchronizeOriginalMemory();
680
681 ResultCode result = RESULT_SUCCESS;
682
683 // TODO(wwylele): verify the behaviour when buffer_ptr == address
684 if (loaded_crs != crs_buffer_ptr) {
685 result = Kernel::g_current_process->vm_manager.UnmapRange(loaded_crs, crs.GetFileSize());
686 if (result.IsError()) {
687 LOG_ERROR(Service_LDR, "Error unmapping CRS %08X", result.raw);
688 }
689 memory_synchronizer.RemoveMemoryBlock(loaded_crs, crs_buffer_ptr);
690 }
691
692 loaded_crs = 0;
693 rb.Push(result);
694}
695
696const Interface::FunctionInfo FunctionTable[] = {
697 // clang-format off
698 {0x000100C2, Initialize, "Initialize"},
699 {0x00020082, LoadCRR, "LoadCRR"},
700 {0x00030042, UnloadCRR, "UnloadCRR"},
701 {0x000402C2, LoadCRO<false>, "LoadCRO"},
702 {0x000500C2, UnloadCRO, "UnloadCRO"},
703 {0x00060042, LinkCRO, "LinkCRO"},
704 {0x00070042, UnlinkCRO, "UnlinkCRO"},
705 {0x00080042, Shutdown, "Shutdown"},
706 {0x000902C2, LoadCRO<true>, "LoadCRO_New"},
707 // clang-format on
708};
709
710LDR_RO::LDR_RO() {
711 Register(FunctionTable);
712
713 loaded_crs = 0;
714 memory_synchronizer.Clear();
715}
716
717} // namespace LDR
718} // namespace Service
diff --git a/src/core/hle/service/ldr_ro/ldr_ro.h b/src/core/hle/service/ldr_ro/ldr_ro.h
deleted file mode 100644
index 0f6fe7b60..000000000
--- a/src/core/hle/service/ldr_ro/ldr_ro.h
+++ /dev/null
@@ -1,22 +0,0 @@
1// Copyright 2014 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 "core/hle/service/service.h"
8
9namespace Service {
10namespace LDR {
11
12class LDR_RO final : public Interface {
13public:
14 LDR_RO();
15
16 std::string GetPortName() const override {
17 return "ldr:ro";
18 }
19};
20
21} // namespace LDR
22} // namespace Service
diff --git a/src/core/hle/service/ldr_ro/memory_synchronizer.cpp b/src/core/hle/service/ldr_ro/memory_synchronizer.cpp
deleted file mode 100644
index 0d44bf6bd..000000000
--- a/src/core/hle/service/ldr_ro/memory_synchronizer.cpp
+++ /dev/null
@@ -1,42 +0,0 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include "common/assert.h"
7#include "core/hle/service/ldr_ro/memory_synchronizer.h"
8
9namespace Service {
10namespace LDR {
11
12auto MemorySynchronizer::FindMemoryBlock(VAddr mapping, VAddr original) {
13 auto block = std::find_if(memory_blocks.begin(), memory_blocks.end(),
14 [=](MemoryBlock& b) { return b.original == original; });
15 ASSERT(block->mapping == mapping);
16 return block;
17}
18
19void MemorySynchronizer::Clear() {
20 memory_blocks.clear();
21}
22
23void MemorySynchronizer::AddMemoryBlock(VAddr mapping, VAddr original, u32 size) {
24 memory_blocks.push_back(MemoryBlock{mapping, original, size});
25}
26
27void MemorySynchronizer::ResizeMemoryBlock(VAddr mapping, VAddr original, u32 size) {
28 FindMemoryBlock(mapping, original)->size = size;
29}
30
31void MemorySynchronizer::RemoveMemoryBlock(VAddr mapping, VAddr original) {
32 memory_blocks.erase(FindMemoryBlock(mapping, original));
33}
34
35void MemorySynchronizer::SynchronizeOriginalMemory() {
36 for (auto& block : memory_blocks) {
37 Memory::CopyBlock(block.original, block.mapping, block.size);
38 }
39}
40
41} // namespace LDR
42} // namespace Service
diff --git a/src/core/hle/service/ldr_ro/memory_synchronizer.h b/src/core/hle/service/ldr_ro/memory_synchronizer.h
deleted file mode 100644
index 438293a58..000000000
--- a/src/core/hle/service/ldr_ro/memory_synchronizer.h
+++ /dev/null
@@ -1,42 +0,0 @@
1// Copyright 2016 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 <vector>
8#include "core/memory.h"
9
10namespace Service {
11namespace LDR {
12
13/**
14 * This is a work-around before we implement memory aliasing.
15 * CRS and CRO are mapped (aliased) to another memory when loading. Games can read
16 * from both the original buffer and the mapping memory. So we use this to synchronize
17 * all original buffers with mapping memory after modifying the content.
18 */
19class MemorySynchronizer {
20public:
21 void Clear();
22
23 void AddMemoryBlock(VAddr mapping, VAddr original, u32 size);
24 void ResizeMemoryBlock(VAddr mapping, VAddr original, u32 size);
25 void RemoveMemoryBlock(VAddr mapping, VAddr original);
26
27 void SynchronizeOriginalMemory();
28
29private:
30 struct MemoryBlock {
31 VAddr mapping;
32 VAddr original;
33 u32 size;
34 };
35
36 std::vector<MemoryBlock> memory_blocks;
37
38 auto FindMemoryBlock(VAddr mapping, VAddr original);
39};
40
41} // namespace LDR
42} // namespace Service
diff --git a/src/core/hle/service/mic_u.cpp b/src/core/hle/service/mic_u.cpp
deleted file mode 100644
index 23e1ff094..000000000
--- a/src/core/hle/service/mic_u.cpp
+++ /dev/null
@@ -1,346 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/logging/log.h"
6#include "core/hle/ipc.h"
7#include "core/hle/kernel/event.h"
8#include "core/hle/kernel/handle_table.h"
9#include "core/hle/kernel/kernel.h"
10#include "core/hle/kernel/shared_memory.h"
11#include "core/hle/service/mic_u.h"
12
13namespace Service {
14namespace MIC {
15
16enum class Encoding : u8 {
17 PCM8 = 0,
18 PCM16 = 1,
19 PCM8Signed = 2,
20 PCM16Signed = 3,
21};
22
23enum class SampleRate : u8 {
24 SampleRate32730 = 0,
25 SampleRate16360 = 1,
26 SampleRate10910 = 2,
27 SampleRate8180 = 3
28};
29
30static Kernel::SharedPtr<Kernel::Event> buffer_full_event;
31static Kernel::SharedPtr<Kernel::SharedMemory> shared_memory;
32static u8 mic_gain = 0;
33static bool mic_power = false;
34static bool is_sampling = false;
35static bool allow_shell_closed;
36static bool clamp = false;
37static Encoding encoding;
38static SampleRate sample_rate;
39static s32 audio_buffer_offset;
40static u32 audio_buffer_size;
41static bool audio_buffer_loop;
42
43/**
44 * MIC::MapSharedMem service function
45 * Inputs:
46 * 0 : Header Code[0x00010042]
47 * 1 : Shared-mem size
48 * 2 : CopyHandleDesc
49 * 3 : Shared-mem handle
50 * Outputs:
51 * 1 : Result of function, 0 on success, otherwise error code
52 */
53static void MapSharedMem(Interface* self) {
54 u32* cmd_buff = Kernel::GetCommandBuffer();
55 u32 size = cmd_buff[1];
56 Kernel::Handle mem_handle = cmd_buff[3];
57 shared_memory = Kernel::g_handle_table.Get<Kernel::SharedMemory>(mem_handle);
58 if (shared_memory) {
59 shared_memory->name = "MIC_U:shared_memory";
60 }
61 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
62 LOG_WARNING(Service_MIC, "called, size=0x%X, mem_handle=0x%08X", size, mem_handle);
63}
64
65/**
66 * MIC::UnmapSharedMem service function
67 * Inputs:
68 * 0 : Header Code[0x00020000]
69 * Outputs:
70 * 1 : Result of function, 0 on success, otherwise error code
71 */
72static void UnmapSharedMem(Interface* self) {
73 u32* cmd_buff = Kernel::GetCommandBuffer();
74
75 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
76 LOG_WARNING(Service_MIC, "called");
77}
78
79/**
80 * MIC::StartSampling service function
81 * Inputs:
82 * 0 : Header Code[0x00030140]
83 * 1 : Encoding
84 * 2 : SampleRate
85 * 3 : Base offset for audio data in sharedmem
86 * 4 : Size of the audio data in sharedmem
87 * 5 : Loop at end of buffer
88 * Outputs:
89 * 1 : Result of function, 0 on success, otherwise error code
90 */
91static void StartSampling(Interface* self) {
92 u32* cmd_buff = Kernel::GetCommandBuffer();
93
94 encoding = static_cast<Encoding>(cmd_buff[1] & 0xFF);
95 sample_rate = static_cast<SampleRate>(cmd_buff[2] & 0xFF);
96 audio_buffer_offset = cmd_buff[3];
97 audio_buffer_size = cmd_buff[4];
98 audio_buffer_loop = (cmd_buff[5] & 0xFF) != 0;
99
100 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
101 is_sampling = true;
102 LOG_WARNING(Service_MIC, "(STUBBED) called, encoding=%u, sample_rate=%u, "
103 "audio_buffer_offset=%d, audio_buffer_size=%u, audio_buffer_loop=%u",
104 static_cast<u32>(encoding), static_cast<u32>(sample_rate), audio_buffer_offset,
105 audio_buffer_size, audio_buffer_loop);
106}
107
108/**
109 * MIC::AdjustSampling service function
110 * Inputs:
111 * 0 : Header Code[0x00040040]
112 * 1 : SampleRate
113 * Outputs:
114 * 1 : Result of function, 0 on success, otherwise error code
115 */
116static void AdjustSampling(Interface* self) {
117 u32* cmd_buff = Kernel::GetCommandBuffer();
118 sample_rate = static_cast<SampleRate>(cmd_buff[1] & 0xFF);
119 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
120 LOG_WARNING(Service_MIC, "(STUBBED) called, sample_rate=%u", static_cast<u32>(sample_rate));
121}
122
123/**
124 * MIC::StopSampling service function
125 * Inputs:
126 * 0 : Header Code[0x00050000]
127 * Outputs:
128 * 1 : Result of function, 0 on success, otherwise error code
129 */
130static void StopSampling(Interface* self) {
131 u32* cmd_buff = Kernel::GetCommandBuffer();
132 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
133 is_sampling = false;
134 LOG_WARNING(Service_MIC, "(STUBBED) called");
135}
136
137/**
138 * MIC::IsSampling service function
139 * Inputs:
140 * 0 : Header Code[0x00060000]
141 * Outputs:
142 * 1 : Result of function, 0 on success, otherwise error code
143 * 2 : 0 = sampling, non-zero = sampling
144 */
145static void IsSampling(Interface* self) {
146 u32* cmd_buff = Kernel::GetCommandBuffer();
147 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
148 cmd_buff[2] = is_sampling;
149 LOG_WARNING(Service_MIC, "(STUBBED) called");
150}
151
152/**
153 * MIC::GetBufferFullEvent service function
154 * Inputs:
155 * 0 : Header Code[0x00070000]
156 * Outputs:
157 * 1 : Result of function, 0 on success, otherwise error code
158 * 3 : Event handle
159 */
160static void GetBufferFullEvent(Interface* self) {
161 u32* cmd_buff = Kernel::GetCommandBuffer();
162 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
163 cmd_buff[3] = Kernel::g_handle_table.Create(buffer_full_event).Unwrap();
164 LOG_WARNING(Service_MIC, "(STUBBED) called");
165}
166
167/**
168 * MIC::SetGain service function
169 * Inputs:
170 * 0 : Header Code[0x00080040]
171 * 1 : Gain
172 * Outputs:
173 * 1 : Result of function, 0 on success, otherwise error code
174 */
175static void SetGain(Interface* self) {
176 u32* cmd_buff = Kernel::GetCommandBuffer();
177 mic_gain = cmd_buff[1] & 0xFF;
178 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
179 LOG_WARNING(Service_MIC, "(STUBBED) called, mic_gain=%u", mic_gain);
180}
181
182/**
183 * MIC::GetGain service function
184 * Inputs:
185 * 0 : Header Code[0x00090000]
186 * Outputs:
187 * 1 : Result of function, 0 on success, otherwise error code
188 * 2 : Gain
189 */
190static void GetGain(Interface* self) {
191 u32* cmd_buff = Kernel::GetCommandBuffer();
192 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
193 cmd_buff[2] = mic_gain;
194 LOG_WARNING(Service_MIC, "(STUBBED) called");
195}
196
197/**
198 * MIC::SetPower service function
199 * Inputs:
200 * 0 : Header Code[0x000A0040]
201 * 1 : Power (0 = off, 1 = on)
202 * Outputs:
203 * 1 : Result of function, 0 on success, otherwise error code
204 */
205static void SetPower(Interface* self) {
206 u32* cmd_buff = Kernel::GetCommandBuffer();
207 mic_power = (cmd_buff[1] & 0xFF) != 0;
208 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
209 LOG_WARNING(Service_MIC, "(STUBBED) called, mic_power=%u", mic_power);
210}
211
212/**
213 * MIC::GetPower service function
214 * Inputs:
215 * 0 : Header Code[0x000B0000]
216 * Outputs:
217 * 1 : Result of function, 0 on success, otherwise error code
218 * 2 : Power
219 */
220static void GetPower(Interface* self) {
221 u32* cmd_buff = Kernel::GetCommandBuffer();
222 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
223 cmd_buff[2] = mic_power;
224 LOG_WARNING(Service_MIC, "(STUBBED) called");
225}
226
227/**
228 * MIC::SetIirFilterMic service function
229 * Inputs:
230 * 0 : Header Code[0x000C0042]
231 * 1 : Size
232 * 2 : (Size << 4) | 0xA
233 * 3 : Pointer to IIR Filter Data
234 * Outputs:
235 * 1 : Result of function, 0 on success, otherwise error code
236 */
237static void SetIirFilterMic(Interface* self) {
238 u32* cmd_buff = Kernel::GetCommandBuffer();
239
240 u32 size = cmd_buff[1];
241 VAddr buffer = cmd_buff[3];
242
243 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
244 LOG_WARNING(Service_MIC, "(STUBBED) called, size=0x%X, buffer=0x%08X", size, buffer);
245}
246
247/**
248 * MIC::SetClamp service function
249 * Inputs:
250 * 0 : Header Code[0x000D0040]
251 * 1 : Clamp (0 = don't clamp, non-zero = clamp)
252 * Outputs:
253 * 1 : Result of function, 0 on success, otherwise error code
254 */
255static void SetClamp(Interface* self) {
256 u32* cmd_buff = Kernel::GetCommandBuffer();
257 clamp = (cmd_buff[1] & 0xFF) != 0;
258 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
259 LOG_WARNING(Service_MIC, "(STUBBED) called, clamp=%u", clamp);
260}
261
262/**
263 * MIC::GetClamp service function
264 * Inputs:
265 * 0 : Header Code[0x000E0000]
266 * Outputs:
267 * 1 : Result of function, 0 on success, otherwise error code
268 * 2 : Clamp (0 = don't clamp, non-zero = clamp)
269 */
270static void GetClamp(Interface* self) {
271 u32* cmd_buff = Kernel::GetCommandBuffer();
272 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
273 cmd_buff[2] = clamp;
274 LOG_WARNING(Service_MIC, "(STUBBED) called");
275}
276
277/**
278 * MIC::SetAllowShellClosed service function
279 * Inputs:
280 * 0 : Header Code[0x000D0040]
281 * 1 : Sampling allowed while shell closed (0 = disallow, non-zero = allow)
282 * Outputs:
283 * 1 : Result of function, 0 on success, otherwise error code
284 */
285static void SetAllowShellClosed(Interface* self) {
286 u32* cmd_buff = Kernel::GetCommandBuffer();
287 allow_shell_closed = (cmd_buff[1] & 0xFF) != 0;
288 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
289 LOG_WARNING(Service_MIC, "(STUBBED) called, allow_shell_closed=%u", allow_shell_closed);
290}
291
292/**
293 * MIC_U::SetClientVersion service function
294 * Inputs:
295 * 1 : Used SDK Version
296 * Outputs:
297 * 1 : Result of function, 0 on success, otherwise error code
298 */
299static void SetClientVersion(Interface* self) {
300 u32* cmd_buff = Kernel::GetCommandBuffer();
301
302 const u32 version = cmd_buff[1];
303 self->SetVersion(version);
304
305 LOG_WARNING(Service_MIC, "(STUBBED) called, version: 0x%08X", version);
306
307 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
308}
309
310const Interface::FunctionInfo FunctionTable[] = {
311 {0x00010042, MapSharedMem, "MapSharedMem"},
312 {0x00020000, UnmapSharedMem, "UnmapSharedMem"},
313 {0x00030140, StartSampling, "StartSampling"},
314 {0x00040040, AdjustSampling, "AdjustSampling"},
315 {0x00050000, StopSampling, "StopSampling"},
316 {0x00060000, IsSampling, "IsSampling"},
317 {0x00070000, GetBufferFullEvent, "GetBufferFullEvent"},
318 {0x00080040, SetGain, "SetGain"},
319 {0x00090000, GetGain, "GetGain"},
320 {0x000A0040, SetPower, "SetPower"},
321 {0x000B0000, GetPower, "GetPower"},
322 {0x000C0042, SetIirFilterMic, "SetIirFilterMic"},
323 {0x000D0040, SetClamp, "SetClamp"},
324 {0x000E0000, GetClamp, "GetClamp"},
325 {0x000F0040, SetAllowShellClosed, "SetAllowShellClosed"},
326 {0x00100040, SetClientVersion, "SetClientVersion"},
327};
328
329MIC_U::MIC_U() {
330 Register(FunctionTable);
331 shared_memory = nullptr;
332 buffer_full_event =
333 Kernel::Event::Create(Kernel::ResetType::OneShot, "MIC_U::buffer_full_event");
334 mic_gain = 0;
335 mic_power = false;
336 is_sampling = false;
337 clamp = false;
338}
339
340MIC_U::~MIC_U() {
341 shared_memory = nullptr;
342 buffer_full_event = nullptr;
343}
344
345} // namespace MIC
346} // namespace Service
diff --git a/src/core/hle/service/mic_u.h b/src/core/hle/service/mic_u.h
deleted file mode 100644
index ec2b67ab8..000000000
--- a/src/core/hle/service/mic_u.h
+++ /dev/null
@@ -1,23 +0,0 @@
1// Copyright 2014 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 "core/hle/service/service.h"
8
9namespace Service {
10namespace MIC {
11
12class MIC_U final : public Interface {
13public:
14 MIC_U();
15 ~MIC_U();
16
17 std::string GetPortName() const override {
18 return "mic:u";
19 }
20};
21
22} // namespace MIC
23} // namespace Service
diff --git a/src/core/hle/service/mvd/mvd.cpp b/src/core/hle/service/mvd/mvd.cpp
deleted file mode 100644
index 9416fe5d6..000000000
--- a/src/core/hle/service/mvd/mvd.cpp
+++ /dev/null
@@ -1,17 +0,0 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/mvd/mvd.h"
6#include "core/hle/service/mvd/mvd_std.h"
7#include "core/hle/service/service.h"
8
9namespace Service {
10namespace MVD {
11
12void Init() {
13 AddService(new MVD_STD());
14}
15
16} // namespace MVD
17} // namespace Service
diff --git a/src/core/hle/service/mvd/mvd.h b/src/core/hle/service/mvd/mvd.h
deleted file mode 100644
index 7b212e839..000000000
--- a/src/core/hle/service/mvd/mvd.h
+++ /dev/null
@@ -1,14 +0,0 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7namespace Service {
8namespace MVD {
9
10/// Initializes all MVD services.
11void Init();
12
13} // namespace MVD
14} // namespace Service
diff --git a/src/core/hle/service/mvd/mvd_std.cpp b/src/core/hle/service/mvd/mvd_std.cpp
deleted file mode 100644
index fd7ca87d3..000000000
--- a/src/core/hle/service/mvd/mvd_std.cpp
+++ /dev/null
@@ -1,32 +0,0 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/mvd/mvd_std.h"
6
7namespace Service {
8namespace MVD {
9
10const Interface::FunctionInfo FunctionTable[] = {
11 // clang-format off
12 {0x00010082, nullptr, "Initialize"},
13 {0x00020000, nullptr, "Shutdown"},
14 {0x00030300, nullptr, "CalculateWorkBufSize"},
15 {0x000400C0, nullptr, "CalculateImageSize"},
16 {0x00080142, nullptr, "ProcessNALUnit"},
17 {0x00090042, nullptr, "ControlFrameRendering"},
18 {0x000A0000, nullptr, "GetStatus"},
19 {0x000B0000, nullptr, "GetStatusOther"},
20 {0x001D0042, nullptr, "GetConfig"},
21 {0x001E0044, nullptr, "SetConfig"},
22 {0x001F0902, nullptr, "SetOutputBuffer"},
23 {0x00210100, nullptr, "OverrideOutputBuffers"}
24 // clang-format on
25};
26
27MVD_STD::MVD_STD() {
28 Register(FunctionTable);
29}
30
31} // namespace MVD
32} // namespace Service
diff --git a/src/core/hle/service/mvd/mvd_std.h b/src/core/hle/service/mvd/mvd_std.h
deleted file mode 100644
index 7db9e2e50..000000000
--- a/src/core/hle/service/mvd/mvd_std.h
+++ /dev/null
@@ -1,22 +0,0 @@
1// Copyright 2016 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 "core/hle/service/service.h"
8
9namespace Service {
10namespace MVD {
11
12class MVD_STD final : public Interface {
13public:
14 MVD_STD();
15
16 std::string GetPortName() const override {
17 return "mvd:std";
18 }
19};
20
21} // namespace MVD
22} // namespace Service
diff --git a/src/core/hle/service/ndm/ndm.cpp b/src/core/hle/service/ndm/ndm.cpp
deleted file mode 100644
index 096c0cdac..000000000
--- a/src/core/hle/service/ndm/ndm.cpp
+++ /dev/null
@@ -1,239 +0,0 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <array>
6#include "common/common_types.h"
7#include "common/logging/log.h"
8#include "core/hle/ipc.h"
9#include "core/hle/service/ndm/ndm.h"
10#include "core/hle/service/ndm/ndm_u.h"
11#include "core/hle/service/service.h"
12
13namespace Service {
14namespace NDM {
15
16enum : u32 {
17 DEFAULT_RETRY_INTERVAL = 10,
18 DEFAULT_SCAN_INTERVAL = 30,
19};
20
21static DaemonMask daemon_bit_mask = DaemonMask::Default;
22static DaemonMask default_daemon_bit_mask = DaemonMask::Default;
23static std::array<DaemonStatus, 4> daemon_status = {
24 DaemonStatus::Idle, DaemonStatus::Idle, DaemonStatus::Idle, DaemonStatus::Idle,
25};
26static ExclusiveState exclusive_state = ExclusiveState::None;
27static u32 scan_interval = DEFAULT_SCAN_INTERVAL;
28static u32 retry_interval = DEFAULT_RETRY_INTERVAL;
29static bool daemon_lock_enabled = false;
30
31void EnterExclusiveState(Service::Interface* self) {
32 u32* cmd_buff = Kernel::GetCommandBuffer();
33 exclusive_state = static_cast<ExclusiveState>(cmd_buff[1]);
34
35 cmd_buff[0] = IPC::MakeHeader(0x1, 1, 0);
36 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
37 LOG_WARNING(Service_NDM, "(STUBBED) exclusive_state=0x%08X ", exclusive_state);
38}
39
40void LeaveExclusiveState(Service::Interface* self) {
41 u32* cmd_buff = Kernel::GetCommandBuffer();
42 exclusive_state = ExclusiveState::None;
43
44 cmd_buff[0] = IPC::MakeHeader(0x2, 1, 0);
45 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
46 LOG_WARNING(Service_NDM, "(STUBBED) exclusive_state=0x%08X ", exclusive_state);
47}
48
49void QueryExclusiveMode(Service::Interface* self) {
50 u32* cmd_buff = Kernel::GetCommandBuffer();
51
52 cmd_buff[0] = IPC::MakeHeader(0x3, 2, 0);
53 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
54 cmd_buff[2] = static_cast<u32>(exclusive_state);
55 LOG_WARNING(Service_NDM, "(STUBBED) exclusive_state=0x%08X ", exclusive_state);
56}
57
58void LockState(Service::Interface* self) {
59 u32* cmd_buff = Kernel::GetCommandBuffer();
60 daemon_lock_enabled = true;
61
62 cmd_buff[0] = IPC::MakeHeader(0x4, 1, 0);
63 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
64 LOG_WARNING(Service_NDM, "(STUBBED) daemon_lock_enabled=0x%08X ", daemon_lock_enabled);
65}
66
67void UnlockState(Service::Interface* self) {
68 u32* cmd_buff = Kernel::GetCommandBuffer();
69 daemon_lock_enabled = false;
70
71 cmd_buff[0] = IPC::MakeHeader(0x5, 1, 0);
72 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
73 LOG_WARNING(Service_NDM, "(STUBBED) daemon_lock_enabled=0x%08X ", daemon_lock_enabled);
74}
75
76void SuspendDaemons(Service::Interface* self) {
77 u32* cmd_buff = Kernel::GetCommandBuffer();
78 u32 bit_mask = cmd_buff[1] & 0xF;
79 daemon_bit_mask =
80 static_cast<DaemonMask>(static_cast<u32>(default_daemon_bit_mask) & ~bit_mask);
81 for (size_t index = 0; index < daemon_status.size(); ++index) {
82 if (bit_mask & (1 << index)) {
83 daemon_status[index] = DaemonStatus::Suspended;
84 }
85 }
86
87 cmd_buff[0] = IPC::MakeHeader(0x6, 1, 0);
88 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
89 LOG_WARNING(Service_NDM, "(STUBBED) daemon_bit_mask=0x%08X ", daemon_bit_mask);
90}
91
92void ResumeDaemons(Service::Interface* self) {
93 u32* cmd_buff = Kernel::GetCommandBuffer();
94 u32 bit_mask = cmd_buff[1] & 0xF;
95 daemon_bit_mask = static_cast<DaemonMask>(static_cast<u32>(daemon_bit_mask) | bit_mask);
96 for (size_t index = 0; index < daemon_status.size(); ++index) {
97 if (bit_mask & (1 << index)) {
98 daemon_status[index] = DaemonStatus::Idle;
99 }
100 }
101
102 cmd_buff[0] = IPC::MakeHeader(0x7, 1, 0);
103 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
104 LOG_WARNING(Service_NDM, "(STUBBED) daemon_bit_mask=0x%08X ", daemon_bit_mask);
105}
106
107void SuspendScheduler(Service::Interface* self) {
108 u32* cmd_buff = Kernel::GetCommandBuffer();
109
110 cmd_buff[0] = IPC::MakeHeader(0x8, 1, 0);
111 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
112 LOG_WARNING(Service_NDM, "(STUBBED) called");
113}
114
115void ResumeScheduler(Service::Interface* self) {
116 u32* cmd_buff = Kernel::GetCommandBuffer();
117
118 cmd_buff[0] = IPC::MakeHeader(0x9, 1, 0);
119 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
120 LOG_WARNING(Service_NDM, "(STUBBED) called");
121}
122
123void QueryStatus(Service::Interface* self) {
124 u32* cmd_buff = Kernel::GetCommandBuffer();
125 u32 daemon = cmd_buff[1] & 0xF;
126
127 cmd_buff[0] = IPC::MakeHeader(0xD, 2, 0);
128 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
129 cmd_buff[2] = static_cast<u32>(daemon_status.at(daemon));
130 LOG_WARNING(Service_NDM, "(STUBBED) daemon=0x%08X, daemon_status=0x%08X", daemon, cmd_buff[2]);
131}
132
133void GetDaemonDisableCount(Service::Interface* self) {
134 u32* cmd_buff = Kernel::GetCommandBuffer();
135 u32 daemon = cmd_buff[1] & 0xF;
136
137 cmd_buff[0] = IPC::MakeHeader(0xE, 3, 0);
138 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
139 cmd_buff[2] = 0;
140 cmd_buff[3] = 0;
141 LOG_WARNING(Service_NDM, "(STUBBED) daemon=0x%08X", daemon);
142}
143
144void GetSchedulerDisableCount(Service::Interface* self) {
145 u32* cmd_buff = Kernel::GetCommandBuffer();
146
147 cmd_buff[0] = IPC::MakeHeader(0xF, 3, 0);
148 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
149 cmd_buff[2] = 0;
150 cmd_buff[3] = 0;
151 LOG_WARNING(Service_NDM, "(STUBBED) called");
152}
153
154void SetScanInterval(Service::Interface* self) {
155 u32* cmd_buff = Kernel::GetCommandBuffer();
156 scan_interval = cmd_buff[1];
157
158 cmd_buff[0] = IPC::MakeHeader(0x10, 1, 0);
159 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
160 LOG_WARNING(Service_NDM, "(STUBBED) scan_interval=0x%08X ", scan_interval);
161}
162
163void GetScanInterval(Service::Interface* self) {
164 u32* cmd_buff = Kernel::GetCommandBuffer();
165
166 cmd_buff[0] = IPC::MakeHeader(0x11, 2, 0);
167 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
168 cmd_buff[2] = scan_interval;
169 LOG_WARNING(Service_NDM, "(STUBBED) scan_interval=0x%08X ", scan_interval);
170}
171
172void SetRetryInterval(Service::Interface* self) {
173 u32* cmd_buff = Kernel::GetCommandBuffer();
174 retry_interval = cmd_buff[1];
175
176 cmd_buff[0] = IPC::MakeHeader(0x12, 1, 0);
177 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
178 LOG_WARNING(Service_NDM, "(STUBBED) retry_interval=0x%08X ", retry_interval);
179}
180
181void GetRetryInterval(Service::Interface* self) {
182 u32* cmd_buff = Kernel::GetCommandBuffer();
183
184 cmd_buff[0] = IPC::MakeHeader(0x13, 2, 0);
185 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
186 cmd_buff[2] = retry_interval;
187 LOG_WARNING(Service_NDM, "(STUBBED) retry_interval=0x%08X ", retry_interval);
188}
189
190void OverrideDefaultDaemons(Service::Interface* self) {
191 u32* cmd_buff = Kernel::GetCommandBuffer();
192 u32 bit_mask = cmd_buff[1] & 0xF;
193 default_daemon_bit_mask = static_cast<DaemonMask>(bit_mask);
194 daemon_bit_mask = default_daemon_bit_mask;
195 for (size_t index = 0; index < daemon_status.size(); ++index) {
196 if (bit_mask & (1 << index)) {
197 daemon_status[index] = DaemonStatus::Idle;
198 }
199 }
200
201 cmd_buff[0] = IPC::MakeHeader(0x14, 1, 0);
202 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
203 LOG_WARNING(Service_NDM, "(STUBBED) default_daemon_bit_mask=0x%08X ", default_daemon_bit_mask);
204}
205
206void ResetDefaultDaemons(Service::Interface* self) {
207 u32* cmd_buff = Kernel::GetCommandBuffer();
208 default_daemon_bit_mask = DaemonMask::Default;
209
210 cmd_buff[0] = IPC::MakeHeader(0x15, 1, 0);
211 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
212 LOG_WARNING(Service_NDM, "(STUBBED) default_daemon_bit_mask=0x%08X ", default_daemon_bit_mask);
213}
214
215void GetDefaultDaemons(Service::Interface* self) {
216 u32* cmd_buff = Kernel::GetCommandBuffer();
217
218 cmd_buff[0] = IPC::MakeHeader(0x16, 2, 0);
219 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
220 cmd_buff[2] = static_cast<u32>(default_daemon_bit_mask);
221 LOG_WARNING(Service_NDM, "(STUBBED) default_daemon_bit_mask=0x%08X ", default_daemon_bit_mask);
222}
223
224void ClearHalfAwakeMacFilter(Service::Interface* self) {
225 u32* cmd_buff = Kernel::GetCommandBuffer();
226
227 cmd_buff[0] = IPC::MakeHeader(0x17, 1, 0);
228 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
229 LOG_WARNING(Service_NDM, "(STUBBED) called");
230}
231
232void Init() {
233 AddService(new NDM_U_Interface);
234}
235
236void Shutdown() {}
237
238} // namespace NDM
239} // namespace Service
diff --git a/src/core/hle/service/ndm/ndm.h b/src/core/hle/service/ndm/ndm.h
deleted file mode 100644
index 979e7fcf1..000000000
--- a/src/core/hle/service/ndm/ndm.h
+++ /dev/null
@@ -1,251 +0,0 @@
1// Copyright 2016 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 "common/common_types.h"
8
9namespace Service {
10
11class Interface;
12
13namespace NDM {
14
15enum class Daemon : u32 {
16 Cec = 0,
17 Boss = 1,
18 Nim = 2,
19 Friend = 3,
20};
21
22enum class DaemonMask : u32 {
23 None = 0,
24 Cec = (1 << static_cast<u32>(Daemon::Cec)),
25 Boss = (1 << static_cast<u32>(Daemon::Boss)),
26 Nim = (1 << static_cast<u32>(Daemon::Nim)),
27 Friend = (1 << static_cast<u32>(Daemon::Friend)),
28 Default = Cec | Friend,
29 All = Cec | Boss | Nim | Friend,
30};
31
32enum class DaemonStatus : u32 { Busy = 0, Idle = 1, Suspending = 2, Suspended = 3 };
33
34enum class ExclusiveState : u32 {
35 None = 0,
36 Infrastructure = 1,
37 LocalCommunications = 2,
38 Streetpass = 3,
39 StreetpassData = 4,
40};
41
42/**
43 * NDM::EnterExclusiveState service function
44 * Inputs:
45 * 0 : Header code [0x00010042]
46 * 1 : Exclusive State
47 * 2 : 0x20
48 * Outputs:
49 * 1 : Result, 0 on success, otherwise error code
50 */
51void EnterExclusiveState(Service::Interface* self);
52
53/**
54 * NDM::LeaveExclusiveState service function
55 * Inputs:
56 * 0 : Header code [0x00020002]
57 * 1 : 0x20
58 * Outputs:
59 * 1 : Result, 0 on success, otherwise error code
60 */
61void LeaveExclusiveState(Service::Interface* self);
62
63/**
64 * NDM::QueryExclusiveMode service function
65 * Inputs:
66 * 0 : Header code [0x00030000]
67 * Outputs:
68 * 1 : Result, 0 on success, otherwise error code
69 * 2 : Current Exclusive State
70 */
71void QueryExclusiveMode(Service::Interface* self);
72
73/**
74 * NDM::LockState service function
75 * Inputs:
76 * 0 : Header code [0x00040002]
77 * Outputs:
78 * 1 : Result, 0 on success, otherwise error code
79 */
80void LockState(Service::Interface* self);
81
82/**
83 * NDM::UnlockState service function
84 * Inputs:
85 * 0 : Header code [0x00050002]
86 * Outputs:
87 * 1 : Result, 0 on success, otherwise error code
88 */
89void UnlockState(Service::Interface* self);
90
91/**
92 * NDM::SuspendDaemons service function
93 * Inputs:
94 * 0 : Header code [0x00060040]
95 * 1 : Daemon bit mask
96 * Outputs:
97 * 1 : Result, 0 on success, otherwise error code
98 */
99void SuspendDaemons(Service::Interface* self);
100
101/**
102 * NDM::ResumeDaemons service function
103 * Inputs:
104 * 0 : Header code [0x00070040]
105 * 1 : Daemon bit mask
106 * Outputs:
107 * 1 : Result, 0 on success, otherwise error code
108 */
109void ResumeDaemons(Service::Interface* self);
110
111/**
112 * NDM::SuspendScheduler service function
113 * Inputs:
114 * 0 : Header code [0x00080040]
115 * Outputs:
116 * 1 : Result, 0 on success, otherwise error code
117 */
118void SuspendScheduler(Service::Interface* self);
119
120/**
121 * NDM::ResumeScheduler service function
122 * Inputs:
123 * 0 : Header code [0x00090000]
124 * Outputs:
125 * 1 : Result, 0 on success, otherwise error code
126 */
127void ResumeScheduler(Service::Interface* self);
128
129/**
130 * NDM::QueryStatus service function
131 * Inputs:
132 * 0 : Header code [0x000D0040]
133 * 1 : Daemon
134 * Outputs:
135 * 1 : Result, 0 on success, otherwise error code
136 * 2 : Daemon status
137 */
138void QueryStatus(Service::Interface* self);
139
140/**
141 * NDM::GetDaemonDisableCount service function
142 * Inputs:
143 * 0 : Header code [0x000E0040]
144 * 1 : Daemon
145 * Outputs:
146 * 1 : Result, 0 on success, otherwise error code
147 * 2 : Current process disable count
148 * 3 : Total disable count
149 */
150void GetDaemonDisableCount(Service::Interface* self);
151
152/**
153 * NDM::GetSchedulerDisableCount service function
154 * Inputs:
155 * 0 : Header code [0x000F0000]
156 * Outputs:
157 * 1 : Result, 0 on success, otherwise error code
158 * 2 : Current process disable count
159 * 3 : Total disable count
160 */
161void GetSchedulerDisableCount(Service::Interface* self);
162
163/**
164 * NDM::SetScanInterval service function
165 * Inputs:
166 * 0 : Header code [0x00100040]
167 * 1 : Interval (default = 30)
168 * Outputs:
169 * 1 : Result, 0 on success, otherwise error code
170 */
171void SetScanInterval(Service::Interface* self);
172
173/**
174 * NDM::GetScanInterval service function
175 * Inputs:
176 * 0 : Header code [0x00110000]
177 * Outputs:
178 * 1 : Result, 0 on success, otherwise error code
179 * 2 : Interval (default = 30)
180 */
181void GetScanInterval(Service::Interface* self);
182
183/**
184 * NDM::SetRetryInterval service function
185 * Inputs:
186 * 0 : Header code [0x00120040]
187 * 1 : Interval (default = 10)
188 * Outputs:
189 * 1 : Result, 0 on success, otherwise error code
190 */
191void SetRetryInterval(Service::Interface* self);
192
193/**
194 * NDM::GetRetryInterval service function
195 * Inputs:
196 * 0 : Header code [0x00130000]
197 * Outputs:
198 * 1 : Result, 0 on success, otherwise error code
199 * 2 : Interval (default = 10)
200 */
201void GetRetryInterval(Service::Interface* self);
202
203/**
204 * NDM::OverrideDefaultDaemons service function
205 * Inputs:
206 * 0 : Header code [0x00140040]
207 * 1 : Daemon bit mask
208 * Outputs:
209 * 1 : Result, 0 on success, otherwise error code
210 */
211void OverrideDefaultDaemons(Service::Interface* self);
212
213/**
214 * NDM::ResetDefaultDaemons service function
215 * Inputs:
216 * 0 : Header code [0x00150000]
217 * Outputs:
218 * 1 : Result, 0 on success, otherwise error code
219 */
220void ResetDefaultDaemons(Service::Interface* self);
221
222/**
223 * NDM::GetDefaultDaemons service function
224 * Inputs:
225 * 0 : Header code [0x00160000]
226 * Outputs:
227 * 1 : Result, 0 on success, otherwise error code
228 * 2 : Daemon bit mask
229 * Note:
230 * Gets the current default daemon bit mask. The default value is (DAEMONMASK_CEC |
231 * DAEMONMASK_FRIENDS)
232 */
233void GetDefaultDaemons(Service::Interface* self);
234
235/**
236 * NDM::ClearHalfAwakeMacFilter service function
237 * Inputs:
238 * 0 : Header code [0x00170000]
239 * Outputs:
240 * 1 : Result, 0 on success, otherwise error code
241 */
242void ClearHalfAwakeMacFilter(Service::Interface* self);
243
244/// Initialize NDM service
245void Init();
246
247/// Shutdown NDM service
248void Shutdown();
249
250} // namespace NDM
251} // namespace Service
diff --git a/src/core/hle/service/ndm/ndm_u.cpp b/src/core/hle/service/ndm/ndm_u.cpp
deleted file mode 100644
index f5c7a341a..000000000
--- a/src/core/hle/service/ndm/ndm_u.cpp
+++ /dev/null
@@ -1,42 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/ndm/ndm.h"
6#include "core/hle/service/ndm/ndm_u.h"
7
8namespace Service {
9namespace NDM {
10
11const Interface::FunctionInfo FunctionTable[] = {
12 {0x00010042, EnterExclusiveState, "EnterExclusiveState"},
13 {0x00020002, LeaveExclusiveState, "LeaveExclusiveState"},
14 {0x00030000, QueryExclusiveMode, "QueryExclusiveMode"},
15 {0x00040002, LockState, "LockState"},
16 {0x00050002, UnlockState, "UnlockState"},
17 {0x00060040, SuspendDaemons, "SuspendDaemons"},
18 {0x00070040, ResumeDaemons, "ResumeDaemons"},
19 {0x00080040, SuspendScheduler, "SuspendScheduler"},
20 {0x00090000, ResumeScheduler, "ResumeScheduler"},
21 {0x000A0000, nullptr, "GetCurrentState"},
22 {0x000B0000, nullptr, "GetTargetState"},
23 {0x000C0000, nullptr, "<Stubbed>"},
24 {0x000D0040, QueryStatus, "QueryStatus"},
25 {0x000E0040, GetDaemonDisableCount, "GetDaemonDisableCount"},
26 {0x000F0000, GetSchedulerDisableCount, "GetSchedulerDisableCount"},
27 {0x00100040, SetScanInterval, "SetScanInterval"},
28 {0x00110000, GetScanInterval, "GetScanInterval"},
29 {0x00120040, SetRetryInterval, "SetRetryInterval"},
30 {0x00130000, GetRetryInterval, "GetRetryInterval"},
31 {0x00140040, OverrideDefaultDaemons, "OverrideDefaultDaemons"},
32 {0x00150000, ResetDefaultDaemons, "ResetDefaultDaemons"},
33 {0x00160000, GetDefaultDaemons, "GetDefaultDaemons"},
34 {0x00170000, ClearHalfAwakeMacFilter, "ClearHalfAwakeMacFilter"},
35};
36
37NDM_U_Interface::NDM_U_Interface() {
38 Register(FunctionTable);
39}
40
41} // namespace NDM
42} // namespace Service
diff --git a/src/core/hle/service/ndm/ndm_u.h b/src/core/hle/service/ndm/ndm_u.h
deleted file mode 100644
index d567abc84..000000000
--- a/src/core/hle/service/ndm/ndm_u.h
+++ /dev/null
@@ -1,22 +0,0 @@
1// Copyright 2014 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 "core/hle/service/service.h"
8
9namespace Service {
10namespace NDM {
11
12class NDM_U_Interface : public Service::Interface {
13public:
14 NDM_U_Interface();
15
16 std::string GetPortName() const override {
17 return "ndm:u";
18 }
19};
20
21} // namespace NDM
22} // namespace Service
diff --git a/src/core/hle/service/news/news.cpp b/src/core/hle/service/news/news.cpp
deleted file mode 100644
index 8b70ec45b..000000000
--- a/src/core/hle/service/news/news.cpp
+++ /dev/null
@@ -1,25 +0,0 @@
1// Copyright 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/logging/log.h"
6#include "core/hle/service/news/news.h"
7#include "core/hle/service/news/news_s.h"
8#include "core/hle/service/news/news_u.h"
9#include "core/hle/service/service.h"
10
11namespace Service {
12namespace NEWS {
13
14void Init() {
15 using namespace Kernel;
16
17 AddService(new NEWS_S_Interface);
18 AddService(new NEWS_U_Interface);
19}
20
21void Shutdown() {}
22
23} // namespace NEWS
24
25} // namespace Service
diff --git a/src/core/hle/service/news/news.h b/src/core/hle/service/news/news.h
deleted file mode 100644
index 46a3ffcb5..000000000
--- a/src/core/hle/service/news/news.h
+++ /dev/null
@@ -1,17 +0,0 @@
1// Copyright 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7namespace Service {
8namespace NEWS {
9
10/// Initialize NEWS service(s)
11void Init();
12
13/// Shutdown NEWS service(s)
14void Shutdown();
15
16} // namespace NEWS
17} // namespace Service
diff --git a/src/core/hle/service/news/news_s.cpp b/src/core/hle/service/news/news_s.cpp
deleted file mode 100644
index dda3d0f6a..000000000
--- a/src/core/hle/service/news/news_s.cpp
+++ /dev/null
@@ -1,32 +0,0 @@
1// Copyright 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/news/news.h"
6#include "core/hle/service/news/news_s.h"
7
8namespace Service {
9namespace NEWS {
10
11const Interface::FunctionInfo FunctionTable[] = {
12 {0x000100C6, nullptr, "AddNotification"},
13 {0x00050000, nullptr, "GetTotalNotifications"},
14 {0x00060042, nullptr, "SetNewsDBHeader"},
15 {0x00070082, nullptr, "SetNotificationHeader"},
16 {0x00080082, nullptr, "SetNotificationMessage"},
17 {0x00090082, nullptr, "SetNotificationImage"},
18 {0x000A0042, nullptr, "GetNewsDBHeader"},
19 {0x000B0082, nullptr, "GetNotificationHeader"},
20 {0x000C0082, nullptr, "GetNotificationMessage"},
21 {0x000D0082, nullptr, "GetNotificationImage"},
22 {0x000E0040, nullptr, "SetInfoLEDPattern"},
23 {0x00120082, nullptr, "GetNotificationHeaderOther"},
24 {0x00130000, nullptr, "WriteNewsDBSavedata"},
25};
26
27NEWS_S_Interface::NEWS_S_Interface() {
28 Register(FunctionTable);
29}
30
31} // namespace NEWS
32} // namespace Service
diff --git a/src/core/hle/service/news/news_s.h b/src/core/hle/service/news/news_s.h
deleted file mode 100644
index f58b969a8..000000000
--- a/src/core/hle/service/news/news_s.h
+++ /dev/null
@@ -1,22 +0,0 @@
1// Copyright 2015 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 "core/hle/service/service.h"
8
9namespace Service {
10namespace NEWS {
11
12class NEWS_S_Interface : public Service::Interface {
13public:
14 NEWS_S_Interface();
15
16 std::string GetPortName() const override {
17 return "news:s";
18 }
19};
20
21} // namespace NEWS
22} // namespace Service
diff --git a/src/core/hle/service/news/news_u.cpp b/src/core/hle/service/news/news_u.cpp
deleted file mode 100644
index a07e466de..000000000
--- a/src/core/hle/service/news/news_u.cpp
+++ /dev/null
@@ -1,19 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/news/news_u.h"
6
7namespace Service {
8namespace NEWS {
9
10const Interface::FunctionInfo FunctionTable[] = {
11 {0x000100C6, nullptr, "AddNotification"},
12};
13
14NEWS_U_Interface::NEWS_U_Interface() {
15 Register(FunctionTable);
16}
17
18} // namespace NEWS
19} // namespace Service
diff --git a/src/core/hle/service/news/news_u.h b/src/core/hle/service/news/news_u.h
deleted file mode 100644
index 2720053d0..000000000
--- a/src/core/hle/service/news/news_u.h
+++ /dev/null
@@ -1,22 +0,0 @@
1// Copyright 2014 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 "core/hle/service/service.h"
8
9namespace Service {
10namespace NEWS {
11
12class NEWS_U_Interface : public Service::Interface {
13public:
14 NEWS_U_Interface();
15
16 std::string GetPortName() const override {
17 return "news:u";
18 }
19};
20
21} // namespace NEWS
22} // namespace Service
diff --git a/src/core/hle/service/nfc/nfc.cpp b/src/core/hle/service/nfc/nfc.cpp
deleted file mode 100644
index cb09ed0b7..000000000
--- a/src/core/hle/service/nfc/nfc.cpp
+++ /dev/null
@@ -1,145 +0,0 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/ipc.h"
6#include "core/hle/kernel/event.h"
7#include "core/hle/kernel/handle_table.h"
8#include "core/hle/service/nfc/nfc.h"
9#include "core/hle/service/nfc/nfc_m.h"
10#include "core/hle/service/nfc/nfc_u.h"
11
12namespace Service {
13namespace NFC {
14
15static Kernel::SharedPtr<Kernel::Event> tag_in_range_event;
16static Kernel::SharedPtr<Kernel::Event> tag_out_of_range_event;
17static TagState nfc_tag_state = TagState::NotInitialized;
18static CommunicationStatus nfc_status = CommunicationStatus::NfcInitialized;
19
20void Initialize(Interface* self) {
21 u32* cmd_buff = Kernel::GetCommandBuffer();
22
23 u8 param = static_cast<u8>(cmd_buff[1] & 0xFF);
24
25 nfc_tag_state = TagState::NotScanning;
26
27 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
28 LOG_WARNING(Service_NFC, "(STUBBED) called, param=%u", param);
29}
30
31void Shutdown(Interface* self) {
32 u32* cmd_buff = Kernel::GetCommandBuffer();
33
34 u8 param = static_cast<u8>(cmd_buff[1] & 0xFF);
35 nfc_tag_state = TagState::NotInitialized;
36
37 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
38 LOG_WARNING(Service_NFC, "(STUBBED) called, param=%u", param);
39}
40
41void StartCommunication(Interface* self) {
42 u32* cmd_buff = Kernel::GetCommandBuffer();
43
44 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
45 LOG_WARNING(Service_NFC, "(STUBBED) called");
46}
47
48void StopCommunication(Interface* self) {
49 u32* cmd_buff = Kernel::GetCommandBuffer();
50
51 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
52 LOG_WARNING(Service_NFC, "(STUBBED) called");
53}
54
55void StartTagScanning(Interface* self) {
56 u32* cmd_buff = Kernel::GetCommandBuffer();
57
58 nfc_tag_state = TagState::TagInRange;
59 tag_in_range_event->Signal();
60
61 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
62 LOG_WARNING(Service_NFC, "(STUBBED) called");
63}
64
65void StopTagScanning(Interface* self) {
66 u32* cmd_buff = Kernel::GetCommandBuffer();
67
68 nfc_tag_state = TagState::NotScanning;
69
70 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
71 LOG_WARNING(Service_NFC, "(STUBBED) called");
72}
73
74void LoadAmiiboData(Interface* self) {
75 u32* cmd_buff = Kernel::GetCommandBuffer();
76
77 nfc_tag_state = TagState::TagDataLoaded;
78
79 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
80 LOG_WARNING(Service_NFC, "(STUBBED) called");
81}
82
83void ResetTagScanState(Interface* self) {
84 u32* cmd_buff = Kernel::GetCommandBuffer();
85
86 nfc_tag_state = TagState::NotScanning;
87
88 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
89 LOG_WARNING(Service_NFC, "(STUBBED) called");
90}
91
92void GetTagInRangeEvent(Interface* self) {
93 u32* cmd_buff = Kernel::GetCommandBuffer();
94
95 cmd_buff[0] = IPC::MakeHeader(0xB, 1, 2);
96 cmd_buff[1] = RESULT_SUCCESS.raw;
97 cmd_buff[2] = IPC::CopyHandleDesc();
98 cmd_buff[3] = Kernel::g_handle_table.Create(tag_in_range_event).Unwrap();
99 LOG_WARNING(Service_NFC, "(STUBBED) called");
100}
101
102void GetTagOutOfRangeEvent(Interface* self) {
103 u32* cmd_buff = Kernel::GetCommandBuffer();
104
105 cmd_buff[0] = IPC::MakeHeader(0xC, 1, 2);
106 cmd_buff[1] = RESULT_SUCCESS.raw;
107 cmd_buff[2] = IPC::CopyHandleDesc();
108 cmd_buff[3] = Kernel::g_handle_table.Create(tag_out_of_range_event).Unwrap();
109 LOG_WARNING(Service_NFC, "(STUBBED) called");
110}
111
112void GetTagState(Interface* self) {
113 u32* cmd_buff = Kernel::GetCommandBuffer();
114
115 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
116 cmd_buff[2] = static_cast<u8>(nfc_tag_state);
117 LOG_DEBUG(Service_NFC, "(STUBBED) called");
118}
119
120void CommunicationGetStatus(Interface* self) {
121 u32* cmd_buff = Kernel::GetCommandBuffer();
122
123 cmd_buff[1] = RESULT_SUCCESS.raw; // No error
124 cmd_buff[2] = static_cast<u8>(nfc_status);
125 LOG_DEBUG(Service_NFC, "(STUBBED) called");
126}
127
128void Init() {
129 AddService(new NFC_M());
130 AddService(new NFC_U());
131
132 tag_in_range_event =
133 Kernel::Event::Create(Kernel::ResetType::OneShot, "NFC::tag_in_range_event");
134 tag_out_of_range_event =
135 Kernel::Event::Create(Kernel::ResetType::OneShot, "NFC::tag_out_range_event");
136 nfc_tag_state = TagState::NotInitialized;
137}
138
139void Shutdown() {
140 tag_in_range_event = nullptr;
141 tag_out_of_range_event = nullptr;
142}
143
144} // namespace NFC
145} // namespace Service
diff --git a/src/core/hle/service/nfc/nfc.h b/src/core/hle/service/nfc/nfc.h
deleted file mode 100644
index a013bdae7..000000000
--- a/src/core/hle/service/nfc/nfc.h
+++ /dev/null
@@ -1,153 +0,0 @@
1// Copyright 2016 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 "common/common_types.h"
8
9namespace Service {
10
11class Interface;
12
13namespace NFC {
14
15enum class TagState : u8 {
16 NotInitialized = 0,
17 NotScanning = 1,
18 Scanning = 2,
19 TagInRange = 3,
20 TagOutOfRange = 4,
21 TagDataLoaded = 5,
22};
23
24enum class CommunicationStatus : u8 {
25 AttemptInitialize = 1,
26 NfcInitialized = 2,
27};
28
29/**
30 * NFC::Initialize service function
31 * Inputs:
32 * 0 : Header code [0x00010040]
33 * 1 : (u8) unknown parameter. Can be either value 0x1 or 0x2
34 * Outputs:
35 * 1 : Result of function, 0 on success, otherwise error code
36 */
37void Initialize(Interface* self);
38
39/**
40 * NFC::Shutdown service function
41 * Inputs:
42 * 0 : Header code [0x00020040]
43 * 1 : (u8) unknown parameter
44 * Outputs:
45 * 1 : Result of function, 0 on success, otherwise error code
46 */
47void Shutdown(Interface* self);
48
49/**
50 * NFC::StartCommunication service function
51 * Inputs:
52 * 0 : Header code [0x00030000]
53 * Outputs:
54 * 1 : Result of function, 0 on success, otherwise error code
55 */
56void StartCommunication(Interface* self);
57
58/**
59 * NFC::StopCommunication service function
60 * Inputs:
61 * 0 : Header code [0x00040000]
62 * Outputs:
63 * 1 : Result of function, 0 on success, otherwise error code
64 */
65void StopCommunication(Interface* self);
66
67/**
68 * NFC::StartTagScanning service function
69 * Inputs:
70 * 0 : Header code [0x00050040]
71 * 1 : (u16) unknown. This is normally 0x0
72 * Outputs:
73 * 1 : Result of function, 0 on success, otherwise error code
74 */
75void StartTagScanning(Interface* self);
76
77/**
78 * NFC::StopTagScanning service function
79 * Inputs:
80 * 0 : Header code [0x00060000]
81 * Outputs:
82 * 1 : Result of function, 0 on success, otherwise error code
83 */
84void StopTagScanning(Interface* self);
85
86/**
87 * NFC::LoadAmiiboData service function
88 * Inputs:
89 * 0 : Header code [0x00070000]
90 * Outputs:
91 * 1 : Result of function, 0 on success, otherwise error code
92 */
93void LoadAmiiboData(Interface* self);
94
95/**
96 * NFC::ResetTagScanState service function
97 * Inputs:
98 * 0 : Header code [0x00080000]
99 * Outputs:
100 * 1 : Result of function, 0 on success, otherwise error code
101 */
102void ResetTagScanState(Interface* self);
103
104/**
105 * NFC::GetTagInRangeEvent service function
106 * Inputs:
107 * 0 : Header code [0x000B0000]
108 * Outputs:
109 * 1 : Result of function, 0 on success, otherwise error code
110 * 2 : Copy handle descriptor
111 * 3 : Event Handle
112 */
113void GetTagInRangeEvent(Interface* self);
114
115/**
116 * NFC::GetTagOutOfRangeEvent service function
117 * Inputs:
118 * 0 : Header code [0x000C0000]
119 * Outputs:
120 * 1 : Result of function, 0 on success, otherwise error code
121 * 2 : Copy handle descriptor
122 * 3 : Event Handle
123 */
124void GetTagOutOfRangeEvent(Interface* self);
125
126/**
127 * NFC::GetTagState service function
128 * Inputs:
129 * 0 : Header code [0x000D0000]
130 * Outputs:
131 * 1 : Result of function, 0 on success, otherwise error code
132 * 2 : (u8) Tag state
133 */
134void GetTagState(Interface* self);
135
136/**
137 * NFC::CommunicationGetStatus service function
138 * Inputs:
139 * 0 : Header code [0x000F0000]
140 * Outputs:
141 * 1 : Result of function, 0 on success, otherwise error code
142 * 2 : (u8) Communication state
143 */
144void CommunicationGetStatus(Interface* self);
145
146/// Initialize all NFC services.
147void Init();
148
149/// Shutdown all NFC services.
150void Shutdown();
151
152} // namespace NFC
153} // namespace Service
diff --git a/src/core/hle/service/nfc/nfc_m.cpp b/src/core/hle/service/nfc/nfc_m.cpp
deleted file mode 100644
index ebe637650..000000000
--- a/src/core/hle/service/nfc/nfc_m.cpp
+++ /dev/null
@@ -1,47 +0,0 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/nfc/nfc.h"
6#include "core/hle/service/nfc/nfc_m.h"
7
8namespace Service {
9namespace NFC {
10
11const Interface::FunctionInfo FunctionTable[] = {
12 // clang-format off
13 // nfc:u shared commands
14 {0x00010040, Initialize, "Initialize"},
15 {0x00020040, Shutdown, "Shutdown"},
16 {0x00030000, StartCommunication, "StartCommunication"},
17 {0x00040000, StopCommunication, "StopCommunication"},
18 {0x00050040, StartTagScanning, "StartTagScanning"},
19 {0x00060000, StopTagScanning, "StopTagScanning"},
20 {0x00070000, LoadAmiiboData, "LoadAmiiboData"},
21 {0x00080000, ResetTagScanState, "ResetTagScanState"},
22 {0x00090002, nullptr, "UpdateStoredAmiiboData"},
23 {0x000B0000, GetTagInRangeEvent, "GetTagInRangeEvent"},
24 {0x000C0000, GetTagOutOfRangeEvent, "GetTagOutOfRangeEvent"},
25 {0x000D0000, GetTagState, "GetTagState"},
26 {0x000F0000, CommunicationGetStatus, "CommunicationGetStatus"},
27 {0x00100000, nullptr, "GetTagInfo2"},
28 {0x00110000, nullptr, "GetTagInfo"},
29 {0x00120000, nullptr, "CommunicationGetResult"},
30 {0x00130040, nullptr, "OpenAppData"},
31 {0x00140384, nullptr, "InitializeWriteAppData"},
32 {0x00150040, nullptr, "ReadAppData"},
33 {0x00160242, nullptr, "WriteAppData"},
34 {0x00170000, nullptr, "GetAmiiboSettings"},
35 {0x00180000, nullptr, "GetAmiiboConfig"},
36 {0x00190000, nullptr, "GetAppDataInitStruct"},
37 // nfc:m
38 {0x04040A40, nullptr, "SetAmiiboSettings"}
39 // clang-format on
40};
41
42NFC_M::NFC_M() {
43 Register(FunctionTable);
44}
45
46} // namespace NFC
47} // namespace Service
diff --git a/src/core/hle/service/nfc/nfc_m.h b/src/core/hle/service/nfc/nfc_m.h
deleted file mode 100644
index fae75535b..000000000
--- a/src/core/hle/service/nfc/nfc_m.h
+++ /dev/null
@@ -1,22 +0,0 @@
1// Copyright 2016 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 "core/hle/service/service.h"
8
9namespace Service {
10namespace NFC {
11
12class NFC_M final : public Interface {
13public:
14 NFC_M();
15
16 std::string GetPortName() const override {
17 return "nfc:m";
18 }
19};
20
21} // namespace NFC
22} // namespace Service
diff --git a/src/core/hle/service/nfc/nfc_u.cpp b/src/core/hle/service/nfc/nfc_u.cpp
deleted file mode 100644
index 5a40c7874..000000000
--- a/src/core/hle/service/nfc/nfc_u.cpp
+++ /dev/null
@@ -1,44 +0,0 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/nfc/nfc.h"
6#include "core/hle/service/nfc/nfc_u.h"
7
8namespace Service {
9namespace NFC {
10
11const Interface::FunctionInfo FunctionTable[] = {
12 // clang-format off
13 {0x00010040, Initialize, "Initialize"},
14 {0x00020040, Shutdown, "Shutdown"},
15 {0x00030000, StartCommunication, "StartCommunication"},
16 {0x00040000, StopCommunication, "StopCommunication"},
17 {0x00050040, StartTagScanning, "StartTagScanning"},
18 {0x00060000, StopTagScanning, "StopTagScanning"},
19 {0x00070000, LoadAmiiboData, "LoadAmiiboData"},
20 {0x00080000, ResetTagScanState, "ResetTagScanState"},
21 {0x00090002, nullptr, "UpdateStoredAmiiboData"},
22 {0x000B0000, GetTagInRangeEvent, "GetTagInRangeEvent"},
23 {0x000C0000, GetTagOutOfRangeEvent, "GetTagOutOfRangeEvent"},
24 {0x000D0000, GetTagState, "GetTagState"},
25 {0x000F0000, CommunicationGetStatus, "CommunicationGetStatus"},
26 {0x00100000, nullptr, "GetTagInfo2"},
27 {0x00110000, nullptr, "GetTagInfo"},
28 {0x00120000, nullptr, "CommunicationGetResult"},
29 {0x00130040, nullptr, "OpenAppData"},
30 {0x00140384, nullptr, "InitializeWriteAppData"},
31 {0x00150040, nullptr, "ReadAppData"},
32 {0x00160242, nullptr, "WriteAppData"},
33 {0x00170000, nullptr, "GetAmiiboSettings"},
34 {0x00180000, nullptr, "GetAmiiboConfig"},
35 {0x00190000, nullptr, "GetAppDataInitStruct"},
36 // clang-format on
37};
38
39NFC_U::NFC_U() {
40 Register(FunctionTable);
41}
42
43} // namespace NFC
44} // namespace Service
diff --git a/src/core/hle/service/nfc/nfc_u.h b/src/core/hle/service/nfc/nfc_u.h
deleted file mode 100644
index eb7507314..000000000
--- a/src/core/hle/service/nfc/nfc_u.h
+++ /dev/null
@@ -1,22 +0,0 @@
1// Copyright 2016 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 "core/hle/service/service.h"
8
9namespace Service {
10namespace NFC {
11
12class NFC_U final : public Interface {
13public:
14 NFC_U();
15
16 std::string GetPortName() const override {
17 return "nfc:u";
18 }
19};
20
21} // namespace NFC
22} // namespace Service
diff --git a/src/core/hle/service/nim/nim.cpp b/src/core/hle/service/nim/nim.cpp
deleted file mode 100644
index b10d5852b..000000000
--- a/src/core/hle/service/nim/nim.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
1// Copyright 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/common_types.h"
6#include "common/logging/log.h"
7#include "core/hle/ipc.h"
8#include "core/hle/ipc_helpers.h"
9#include "core/hle/kernel/event.h"
10#include "core/hle/service/nim/nim.h"
11#include "core/hle/service/nim/nim_aoc.h"
12#include "core/hle/service/nim/nim_s.h"
13#include "core/hle/service/nim/nim_u.h"
14#include "core/hle/service/service.h"
15
16namespace Service {
17namespace NIM {
18
19static Kernel::SharedPtr<Kernel::Event> nim_system_update_event;
20
21void CheckForSysUpdateEvent(Service::Interface* self) {
22 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x5, 0, 0); // 0x50000
23 IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
24 rb.Push(RESULT_SUCCESS);
25 rb.PushCopyHandles(Kernel::g_handle_table.Create(nim_system_update_event).Unwrap());
26 LOG_TRACE(Service_NIM, "called");
27}
28
29void CheckSysUpdateAvailable(Service::Interface* self) {
30 u32* cmd_buff = Kernel::GetCommandBuffer();
31
32 cmd_buff[1] = RESULT_SUCCESS.raw;
33 cmd_buff[2] = 0; // No update available
34
35 LOG_WARNING(Service_NIM, "(STUBBED) called");
36}
37
38void Init() {
39 using namespace Kernel;
40
41 AddService(new NIM_AOC_Interface);
42 AddService(new NIM_S_Interface);
43 AddService(new NIM_U_Interface);
44
45 nim_system_update_event = Kernel::Event::Create(ResetType::OneShot, "NIM System Update Event");
46}
47
48void Shutdown() {
49 nim_system_update_event = nullptr;
50}
51
52} // namespace NIM
53
54} // namespace Service
diff --git a/src/core/hle/service/nim/nim.h b/src/core/hle/service/nim/nim.h
deleted file mode 100644
index dbf605e5a..000000000
--- a/src/core/hle/service/nim/nim.h
+++ /dev/null
@@ -1,41 +0,0 @@
1// Copyright 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7namespace Service {
8
9class Interface;
10
11namespace NIM {
12
13/**
14 * NIM::CheckForSysUpdateEvent service function
15 * Inputs:
16 * 1 : None
17 * Outputs:
18 * 1 : Result of function, 0 on success, otherwise error code
19 * 2 : Copy handle descriptor
20 * 3 : System Update event handle
21 */
22void CheckForSysUpdateEvent(Service::Interface* self);
23
24/**
25 * NIM::CheckSysUpdateAvailable service function
26 * Inputs:
27 * 1 : None
28 * Outputs:
29 * 1 : Result of function, 0 on success, otherwise error code
30 * 2 : flag, 0 = no system update available, 1 = system update available.
31 */
32void CheckSysUpdateAvailable(Service::Interface* self);
33
34/// Initialize NIM service(s)
35void Init();
36
37/// Shutdown NIM service(s)
38void Shutdown();
39
40} // namespace NIM
41} // namespace Service
diff --git a/src/core/hle/service/nim/nim_aoc.cpp b/src/core/hle/service/nim/nim_aoc.cpp
deleted file mode 100644
index 2d0fb6fc4..000000000
--- a/src/core/hle/service/nim/nim_aoc.cpp
+++ /dev/null
@@ -1,26 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/nim/nim_aoc.h"
6
7namespace Service {
8namespace NIM {
9
10const Interface::FunctionInfo FunctionTable[] = {
11 {0x00030042, nullptr, "SetApplicationId"},
12 {0x00040042, nullptr, "SetTin"},
13 {0x000902D0, nullptr, "ListContentSetsEx"},
14 {0x00180000, nullptr, "GetBalance"},
15 {0x001D0000, nullptr, "GetCustomerSupportCode"},
16 {0x00210000, nullptr, "Initialize"},
17 {0x00240282, nullptr, "CalculateContentsRequiredSize"},
18 {0x00250000, nullptr, "RefreshServerTime"},
19};
20
21NIM_AOC_Interface::NIM_AOC_Interface() {
22 Register(FunctionTable);
23}
24
25} // namespace NIM
26} // namespace Service
diff --git a/src/core/hle/service/nim/nim_aoc.h b/src/core/hle/service/nim/nim_aoc.h
deleted file mode 100644
index aace45b5a..000000000
--- a/src/core/hle/service/nim/nim_aoc.h
+++ /dev/null
@@ -1,22 +0,0 @@
1// Copyright 2014 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 "core/hle/service/service.h"
8
9namespace Service {
10namespace NIM {
11
12class NIM_AOC_Interface : public Service::Interface {
13public:
14 NIM_AOC_Interface();
15
16 std::string GetPortName() const override {
17 return "nim:aoc";
18 }
19};
20
21} // namespace NIM
22} // namespace Service
diff --git a/src/core/hle/service/nim/nim_s.cpp b/src/core/hle/service/nim/nim_s.cpp
deleted file mode 100644
index 28b87e6f7..000000000
--- a/src/core/hle/service/nim/nim_s.cpp
+++ /dev/null
@@ -1,23 +0,0 @@
1// Copyright 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/nim/nim_s.h"
6
7namespace Service {
8namespace NIM {
9
10const Interface::FunctionInfo FunctionTable[] = {
11 {0x000A0000, nullptr, "CheckSysupdateAvailableSOAP"},
12 {0x0016020A, nullptr, "ListTitles"},
13 {0x00290000, nullptr, "AccountCheckBalanceSOAP"},
14 {0x002D0042, nullptr, "DownloadTickets"},
15 {0x00420240, nullptr, "StartDownload"},
16};
17
18NIM_S_Interface::NIM_S_Interface() {
19 Register(FunctionTable);
20}
21
22} // namespace NIM
23} // namespace Service
diff --git a/src/core/hle/service/nim/nim_s.h b/src/core/hle/service/nim/nim_s.h
deleted file mode 100644
index f4bf73d26..000000000
--- a/src/core/hle/service/nim/nim_s.h
+++ /dev/null
@@ -1,22 +0,0 @@
1// Copyright 2015 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 "core/hle/service/service.h"
8
9namespace Service {
10namespace NIM {
11
12class NIM_S_Interface : public Service::Interface {
13public:
14 NIM_S_Interface();
15
16 std::string GetPortName() const override {
17 return "nim:s";
18 }
19};
20
21} // namespace NIM
22} // namespace Service
diff --git a/src/core/hle/service/nim/nim_u.cpp b/src/core/hle/service/nim/nim_u.cpp
deleted file mode 100644
index 569660278..000000000
--- a/src/core/hle/service/nim/nim_u.cpp
+++ /dev/null
@@ -1,26 +0,0 @@
1// Copyright 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/nim/nim.h"
6#include "core/hle/service/nim/nim_u.h"
7
8namespace Service {
9namespace NIM {
10
11const Interface::FunctionInfo FunctionTable[] = {
12 {0x00010000, nullptr, "StartSysUpdate"},
13 {0x00020000, nullptr, "GetUpdateDownloadProgress"},
14 {0x00040000, nullptr, "FinishTitlesInstall"},
15 {0x00050000, CheckForSysUpdateEvent, "CheckForSysUpdateEvent"},
16 {0x00090000, CheckSysUpdateAvailable, "CheckSysUpdateAvailable"},
17 {0x000A0000, nullptr, "GetState"},
18 {0x000B0000, nullptr, "GetSystemTitleHash"},
19};
20
21NIM_U_Interface::NIM_U_Interface() {
22 Register(FunctionTable);
23}
24
25} // namespace NIM
26} // namespace Service
diff --git a/src/core/hle/service/nim/nim_u.h b/src/core/hle/service/nim/nim_u.h
deleted file mode 100644
index c4b74985a..000000000
--- a/src/core/hle/service/nim/nim_u.h
+++ /dev/null
@@ -1,22 +0,0 @@
1// Copyright 2015 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 "core/hle/service/service.h"
8
9namespace Service {
10namespace NIM {
11
12class NIM_U_Interface : public Service::Interface {
13public:
14 NIM_U_Interface();
15
16 std::string GetPortName() const override {
17 return "nim:u";
18 }
19};
20
21} // namespace NIM
22} // namespace Service
diff --git a/src/core/hle/service/nwm/nwm.cpp b/src/core/hle/service/nwm/nwm.cpp
deleted file mode 100644
index 9f1994dc3..000000000
--- a/src/core/hle/service/nwm/nwm.cpp
+++ /dev/null
@@ -1,28 +0,0 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/nwm/nwm.h"
6#include "core/hle/service/nwm/nwm_cec.h"
7#include "core/hle/service/nwm/nwm_ext.h"
8#include "core/hle/service/nwm/nwm_inf.h"
9#include "core/hle/service/nwm/nwm_sap.h"
10#include "core/hle/service/nwm/nwm_soc.h"
11#include "core/hle/service/nwm/nwm_tst.h"
12#include "core/hle/service/nwm/nwm_uds.h"
13
14namespace Service {
15namespace NWM {
16
17void Init() {
18 AddService(new NWM_CEC);
19 AddService(new NWM_EXT);
20 AddService(new NWM_INF);
21 AddService(new NWM_SAP);
22 AddService(new NWM_SOC);
23 AddService(new NWM_TST);
24 AddService(new NWM_UDS);
25}
26
27} // namespace NWM
28} // namespace Service
diff --git a/src/core/hle/service/nwm/nwm.h b/src/core/hle/service/nwm/nwm.h
deleted file mode 100644
index 6926b29a6..000000000
--- a/src/core/hle/service/nwm/nwm.h
+++ /dev/null
@@ -1,14 +0,0 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7namespace Service {
8namespace NWM {
9
10/// Initialize all NWM services
11void Init();
12
13} // namespace NWM
14} // namespace Service
diff --git a/src/core/hle/service/nwm/nwm_cec.cpp b/src/core/hle/service/nwm/nwm_cec.cpp
deleted file mode 100644
index 7f03987df..000000000
--- a/src/core/hle/service/nwm/nwm_cec.cpp
+++ /dev/null
@@ -1,19 +0,0 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/nwm/nwm_cec.h"
6
7namespace Service {
8namespace NWM {
9
10const Interface::FunctionInfo FunctionTable[] = {
11 {0x000D0082, nullptr, "SendProbeRequest"},
12};
13
14NWM_CEC::NWM_CEC() {
15 Register(FunctionTable);
16}
17
18} // namespace NWM
19} // namespace Service
diff --git a/src/core/hle/service/nwm/nwm_cec.h b/src/core/hle/service/nwm/nwm_cec.h
deleted file mode 100644
index 07b6addb5..000000000
--- a/src/core/hle/service/nwm/nwm_cec.h
+++ /dev/null
@@ -1,22 +0,0 @@
1// Copyright 2016 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 "core/hle/service/service.h"
8
9namespace Service {
10namespace NWM {
11
12class NWM_CEC final : public Interface {
13public:
14 NWM_CEC();
15
16 std::string GetPortName() const override {
17 return "nwm::CEC";
18 }
19};
20
21} // namespace NWM
22} // namespace Service
diff --git a/src/core/hle/service/nwm/nwm_ext.cpp b/src/core/hle/service/nwm/nwm_ext.cpp
deleted file mode 100644
index 605640a13..000000000
--- a/src/core/hle/service/nwm/nwm_ext.cpp
+++ /dev/null
@@ -1,19 +0,0 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/nwm/nwm_ext.h"
6
7namespace Service {
8namespace NWM {
9
10const Interface::FunctionInfo FunctionTable[] = {
11 {0x00080040, nullptr, "ControlWirelessEnabled"},
12};
13
14NWM_EXT::NWM_EXT() {
15 Register(FunctionTable);
16}
17
18} // namespace NWM
19} // namespace Service
diff --git a/src/core/hle/service/nwm/nwm_ext.h b/src/core/hle/service/nwm/nwm_ext.h
deleted file mode 100644
index 51d39d9ea..000000000
--- a/src/core/hle/service/nwm/nwm_ext.h
+++ /dev/null
@@ -1,22 +0,0 @@
1// Copyright 2016 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 "core/hle/service/service.h"
8
9namespace Service {
10namespace NWM {
11
12class NWM_EXT final : public Interface {
13public:
14 NWM_EXT();
15
16 std::string GetPortName() const override {
17 return "nwm::EXT";
18 }
19};
20
21} // namespace NWM
22} // namespace Service
diff --git a/src/core/hle/service/nwm/nwm_inf.cpp b/src/core/hle/service/nwm/nwm_inf.cpp
deleted file mode 100644
index c8470589b..000000000
--- a/src/core/hle/service/nwm/nwm_inf.cpp
+++ /dev/null
@@ -1,21 +0,0 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/nwm/nwm_inf.h"
6
7namespace Service {
8namespace NWM {
9
10const Interface::FunctionInfo FunctionTable[] = {
11 {0x000603C4, nullptr, "RecvBeaconBroadcastData"},
12 {0x00070742, nullptr, "ConnectToEncryptedAP"},
13 {0x00080302, nullptr, "ConnectToAP"},
14};
15
16NWM_INF::NWM_INF() {
17 Register(FunctionTable);
18}
19
20} // namespace NWM
21} // namespace Service
diff --git a/src/core/hle/service/nwm/nwm_inf.h b/src/core/hle/service/nwm/nwm_inf.h
deleted file mode 100644
index 0043d769c..000000000
--- a/src/core/hle/service/nwm/nwm_inf.h
+++ /dev/null
@@ -1,22 +0,0 @@
1// Copyright 2016 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 "core/hle/service/service.h"
8
9namespace Service {
10namespace NWM {
11
12class NWM_INF final : public Interface {
13public:
14 NWM_INF();
15
16 std::string GetPortName() const override {
17 return "nwm::INF";
18 }
19};
20
21} // namespace NWM
22} // namespace Service
diff --git a/src/core/hle/service/nwm/nwm_sap.cpp b/src/core/hle/service/nwm/nwm_sap.cpp
deleted file mode 100644
index fd29ed761..000000000
--- a/src/core/hle/service/nwm/nwm_sap.cpp
+++ /dev/null
@@ -1,20 +0,0 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/nwm/nwm_sap.h"
6
7namespace Service {
8namespace NWM {
9
10/*
11const Interface::FunctionInfo FunctionTable[] = {
12};
13*/
14
15NWM_SAP::NWM_SAP() {
16 // Register(FunctionTable);
17}
18
19} // namespace NWM
20} // namespace Service
diff --git a/src/core/hle/service/nwm/nwm_sap.h b/src/core/hle/service/nwm/nwm_sap.h
deleted file mode 100644
index f692e06d4..000000000
--- a/src/core/hle/service/nwm/nwm_sap.h
+++ /dev/null
@@ -1,22 +0,0 @@
1// Copyright 2016 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 "core/hle/service/service.h"
8
9namespace Service {
10namespace NWM {
11
12class NWM_SAP final : public Interface {
13public:
14 NWM_SAP();
15
16 std::string GetPortName() const override {
17 return "nwm::SAP";
18 }
19};
20
21} // namespace NWM
22} // namespace Service
diff --git a/src/core/hle/service/nwm/nwm_soc.cpp b/src/core/hle/service/nwm/nwm_soc.cpp
deleted file mode 100644
index fdffcb925..000000000
--- a/src/core/hle/service/nwm/nwm_soc.cpp
+++ /dev/null
@@ -1,20 +0,0 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/nwm/nwm_soc.h"
6
7namespace Service {
8namespace NWM {
9
10/*
11const Interface::FunctionInfo FunctionTable[] = {
12};
13*/
14
15NWM_SOC::NWM_SOC() {
16 // Register(FunctionTable);
17}
18
19} // namespace NWM
20} // namespace Service
diff --git a/src/core/hle/service/nwm/nwm_soc.h b/src/core/hle/service/nwm/nwm_soc.h
deleted file mode 100644
index 594941d7e..000000000
--- a/src/core/hle/service/nwm/nwm_soc.h
+++ /dev/null
@@ -1,22 +0,0 @@
1// Copyright 2016 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 "core/hle/service/service.h"
8
9namespace Service {
10namespace NWM {
11
12class NWM_SOC final : public Interface {
13public:
14 NWM_SOC();
15
16 std::string GetPortName() const override {
17 return "nwm::SOC";
18 }
19};
20
21} // namespace NWM
22} // namespace Service
diff --git a/src/core/hle/service/nwm/nwm_tst.cpp b/src/core/hle/service/nwm/nwm_tst.cpp
deleted file mode 100644
index 5f292e5db..000000000
--- a/src/core/hle/service/nwm/nwm_tst.cpp
+++ /dev/null
@@ -1,20 +0,0 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/nwm/nwm_tst.h"
6
7namespace Service {
8namespace NWM {
9
10/*
11const Interface::FunctionInfo FunctionTable[] = {
12};
13*/
14
15NWM_TST::NWM_TST() {
16 // Register(FunctionTable);
17}
18
19} // namespace NWM
20} // namespace Service
diff --git a/src/core/hle/service/nwm/nwm_tst.h b/src/core/hle/service/nwm/nwm_tst.h
deleted file mode 100644
index 8deca3216..000000000
--- a/src/core/hle/service/nwm/nwm_tst.h
+++ /dev/null
@@ -1,22 +0,0 @@
1// Copyright 2016 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 "core/hle/service/service.h"
8
9namespace Service {
10namespace NWM {
11
12class NWM_TST final : public Interface {
13public:
14 NWM_TST();
15
16 std::string GetPortName() const override {
17 return "nwm::TST";
18 }
19};
20
21} // namespace NWM
22} // namespace Service
diff --git a/src/core/hle/service/nwm/nwm_uds.cpp b/src/core/hle/service/nwm/nwm_uds.cpp
deleted file mode 100644
index 87a6b0eca..000000000
--- a/src/core/hle/service/nwm/nwm_uds.cpp
+++ /dev/null
@@ -1,1035 +0,0 @@
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 <algorithm>
6#include <array>
7#include <cstring>
8#include <list>
9#include <mutex>
10#include <unordered_map>
11#include <vector>
12#include "common/common_types.h"
13#include "common/logging/log.h"
14#include "core/core_timing.h"
15#include "core/hle/ipc_helpers.h"
16#include "core/hle/kernel/event.h"
17#include "core/hle/kernel/shared_memory.h"
18#include "core/hle/lock.h"
19#include "core/hle/result.h"
20#include "core/hle/service/nwm/nwm_uds.h"
21#include "core/hle/service/nwm/uds_beacon.h"
22#include "core/hle/service/nwm/uds_connection.h"
23#include "core/hle/service/nwm/uds_data.h"
24#include "core/memory.h"
25#include "network/network.h"
26
27namespace Service {
28namespace NWM {
29
30// Event that is signaled every time the connection status changes.
31static Kernel::SharedPtr<Kernel::Event> connection_status_event;
32
33// Shared memory provided by the application to store the receive buffer.
34// This is not currently used.
35static Kernel::SharedPtr<Kernel::SharedMemory> recv_buffer_memory;
36
37// Connection status of this 3DS.
38static ConnectionStatus connection_status{};
39
40/* Node information about the current network.
41 * The amount of elements in this vector is always the maximum number
42 * of nodes specified in the network configuration.
43 * The first node is always the host.
44 */
45static NodeList node_info;
46
47// Node information about our own system.
48static NodeInfo current_node;
49
50// Mapping of bind node ids to their respective events.
51static std::unordered_map<u32, Kernel::SharedPtr<Kernel::Event>> bind_node_events;
52
53// The WiFi network channel that the network is currently on.
54// Since we're not actually interacting with physical radio waves, this is just a dummy value.
55static u8 network_channel = DefaultNetworkChannel;
56
57// Information about the network that we're currently connected to.
58static NetworkInfo network_info;
59
60// Event that will generate and send the 802.11 beacon frames.
61static int beacon_broadcast_event;
62
63// Mutex to synchronize access to the connection status between the emulation thread and the
64// network thread.
65static std::mutex connection_status_mutex;
66
67// Mutex to synchronize access to the list of received beacons between the emulation thread and the
68// network thread.
69static std::mutex beacon_mutex;
70
71// Number of beacons to store before we start dropping the old ones.
72// TODO(Subv): Find a more accurate value for this limit.
73constexpr size_t MaxBeaconFrames = 15;
74
75// List of the last <MaxBeaconFrames> beacons received from the network.
76static std::list<Network::WifiPacket> received_beacons;
77
78/**
79 * Returns a list of received 802.11 beacon frames from the specified sender since the last call.
80 */
81std::list<Network::WifiPacket> GetReceivedBeacons(const MacAddress& sender) {
82 std::lock_guard<std::mutex> lock(beacon_mutex);
83 if (sender != Network::BroadcastMac) {
84 std::list<Network::WifiPacket> filtered_list;
85 const auto beacon = std::find_if(received_beacons.begin(), received_beacons.end(),
86 [&sender](const Network::WifiPacket& packet) {
87 return packet.transmitter_address == sender;
88 });
89 if (beacon != received_beacons.end()) {
90 filtered_list.push_back(*beacon);
91 // TODO(B3N30): Check if the complete deque is cleared or just the fetched entries
92 received_beacons.erase(beacon);
93 }
94 return filtered_list;
95 }
96 return std::move(received_beacons);
97}
98
99/// Sends a WifiPacket to the room we're currently connected to.
100void SendPacket(Network::WifiPacket& packet) {
101 // TODO(Subv): Implement.
102}
103
104/*
105 * Returns an available index in the nodes array for the
106 * currently-hosted UDS network.
107 */
108static u16 GetNextAvailableNodeId() {
109 for (u16 index = 0; index < connection_status.max_nodes; ++index) {
110 if ((connection_status.node_bitmask & (1 << index)) == 0)
111 return index;
112 }
113
114 // Any connection attempts to an already full network should have been refused.
115 ASSERT_MSG(false, "No available connection slots in the network");
116}
117
118// Inserts the received beacon frame in the beacon queue and removes any older beacons if the size
119// limit is exceeded.
120void HandleBeaconFrame(const Network::WifiPacket& packet) {
121 std::lock_guard<std::mutex> lock(beacon_mutex);
122 const auto unique_beacon =
123 std::find_if(received_beacons.begin(), received_beacons.end(),
124 [&packet](const Network::WifiPacket& new_packet) {
125 return new_packet.transmitter_address == packet.transmitter_address;
126 });
127 if (unique_beacon != received_beacons.end()) {
128 // We already have a beacon from the same mac in the deque, remove the old one;
129 received_beacons.erase(unique_beacon);
130 }
131
132 received_beacons.emplace_back(packet);
133
134 // Discard old beacons if the buffer is full.
135 if (received_beacons.size() > MaxBeaconFrames)
136 received_beacons.pop_front();
137}
138
139void HandleAssociationResponseFrame(const Network::WifiPacket& packet) {
140 auto assoc_result = GetAssociationResult(packet.data);
141
142 ASSERT_MSG(std::get<AssocStatus>(assoc_result) == AssocStatus::Successful,
143 "Could not join network");
144 {
145 std::lock_guard<std::mutex> lock(connection_status_mutex);
146 ASSERT(connection_status.status == static_cast<u32>(NetworkStatus::Connecting));
147 }
148
149 // Send the EAPoL-Start packet to the server.
150 using Network::WifiPacket;
151 WifiPacket eapol_start;
152 eapol_start.channel = network_channel;
153 eapol_start.data = GenerateEAPoLStartFrame(std::get<u16>(assoc_result), current_node);
154 // TODO(B3N30): Encrypt the packet.
155 eapol_start.destination_address = packet.transmitter_address;
156 eapol_start.type = WifiPacket::PacketType::Data;
157
158 SendPacket(eapol_start);
159}
160
161static void HandleEAPoLPacket(const Network::WifiPacket& packet) {
162 std::lock_guard<std::mutex> lock(connection_status_mutex);
163
164 if (GetEAPoLFrameType(packet.data) == EAPoLStartMagic) {
165 if (connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsHost)) {
166 LOG_DEBUG(Service_NWM, "Connection sequence aborted, because connection status is %u",
167 connection_status.status);
168 return;
169 }
170
171 auto node = DeserializeNodeInfoFromFrame(packet.data);
172
173 if (connection_status.max_nodes == connection_status.total_nodes) {
174 // Reject connection attempt
175 LOG_ERROR(Service_NWM, "Reached maximum nodes, but reject packet wasn't sent.");
176 // TODO(B3N30): Figure out what packet is sent here
177 return;
178 }
179
180 // Get an unused network node id
181 u16 node_id = GetNextAvailableNodeId();
182 node.network_node_id = node_id + 1;
183
184 connection_status.node_bitmask |= 1 << node_id;
185 connection_status.changed_nodes |= 1 << node_id;
186 connection_status.nodes[node_id] = node.network_node_id;
187 connection_status.total_nodes++;
188
189 u8 current_nodes = network_info.total_nodes;
190 node_info[current_nodes] = node;
191
192 network_info.total_nodes++;
193
194 // Send the EAPoL-Logoff packet.
195 using Network::WifiPacket;
196 WifiPacket eapol_logoff;
197 eapol_logoff.channel = network_channel;
198 eapol_logoff.data =
199 GenerateEAPoLLogoffFrame(packet.transmitter_address, node.network_node_id, node_info,
200 network_info.max_nodes, network_info.total_nodes);
201 // TODO(Subv): Encrypt the packet.
202 eapol_logoff.destination_address = packet.transmitter_address;
203 eapol_logoff.type = WifiPacket::PacketType::Data;
204
205 SendPacket(eapol_logoff);
206 // TODO(B3N30): Broadcast updated node list
207 // The 3ds does this presumably to support spectators.
208 std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
209 connection_status_event->Signal();
210 } else {
211 if (connection_status.status != static_cast<u32>(NetworkStatus::NotConnected)) {
212 LOG_DEBUG(Service_NWM, "Connection sequence aborted, because connection status is %u",
213 connection_status.status);
214 return;
215 }
216 auto logoff = ParseEAPoLLogoffFrame(packet.data);
217
218 network_info.total_nodes = logoff.connected_nodes;
219 network_info.max_nodes = logoff.max_nodes;
220
221 connection_status.network_node_id = logoff.assigned_node_id;
222 connection_status.total_nodes = logoff.connected_nodes;
223 connection_status.max_nodes = logoff.max_nodes;
224
225 node_info.clear();
226 node_info.reserve(network_info.max_nodes);
227 for (size_t index = 0; index < logoff.connected_nodes; ++index) {
228 connection_status.node_bitmask |= 1 << index;
229 connection_status.changed_nodes |= 1 << index;
230 connection_status.nodes[index] = logoff.nodes[index].network_node_id;
231
232 node_info.emplace_back(DeserializeNodeInfo(logoff.nodes[index]));
233 }
234
235 // We're now connected, signal the application
236 connection_status.status = static_cast<u32>(NetworkStatus::ConnectedAsClient);
237 // Some games require ConnectToNetwork to block, for now it doesn't
238 // If blocking is implemented this lock needs to be changed,
239 // otherwise it might cause deadlocks
240 std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
241 connection_status_event->Signal();
242 }
243}
244
245/*
246 * Start a connection sequence with an UDS server. The sequence starts by sending an 802.11
247 * authentication frame with SEQ1.
248 */
249void StartConnectionSequence(const MacAddress& server) {
250 using Network::WifiPacket;
251 WifiPacket auth_request;
252 {
253 std::lock_guard<std::mutex> lock(connection_status_mutex);
254 ASSERT(connection_status.status == static_cast<u32>(NetworkStatus::NotConnected));
255
256 // TODO(Subv): Handle timeout.
257
258 // Send an authentication frame with SEQ1
259 auth_request.channel = network_channel;
260 auth_request.data = GenerateAuthenticationFrame(AuthenticationSeq::SEQ1);
261 auth_request.destination_address = server;
262 auth_request.type = WifiPacket::PacketType::Authentication;
263 }
264
265 SendPacket(auth_request);
266}
267
268/// Sends an Association Response frame to the specified mac address
269void SendAssociationResponseFrame(const MacAddress& address) {
270 using Network::WifiPacket;
271 WifiPacket assoc_response;
272
273 {
274 std::lock_guard<std::mutex> lock(connection_status_mutex);
275 if (connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsHost)) {
276 LOG_ERROR(Service_NWM, "Connection sequence aborted, because connection status is %u",
277 connection_status.status);
278 return;
279 }
280
281 assoc_response.channel = network_channel;
282 // TODO(Subv): This will cause multiple clients to end up with the same association id, but
283 // we're not using that for anything.
284 u16 association_id = 1;
285 assoc_response.data = GenerateAssocResponseFrame(AssocStatus::Successful, association_id,
286 network_info.network_id);
287 assoc_response.destination_address = address;
288 assoc_response.type = WifiPacket::PacketType::AssociationResponse;
289 }
290
291 SendPacket(assoc_response);
292}
293
294/*
295 * Handles the authentication request frame and sends the authentication response and association
296 * response frames. Once an Authentication frame with SEQ1 is received by the server, it responds
297 * with an Authentication frame containing SEQ2, and immediately sends an Association response frame
298 * containing the details of the access point and the assigned association id for the new client.
299 */
300void HandleAuthenticationFrame(const Network::WifiPacket& packet) {
301 // Only the SEQ1 auth frame is handled here, the SEQ2 frame doesn't need any special behavior
302 if (GetAuthenticationSeqNumber(packet.data) == AuthenticationSeq::SEQ1) {
303 using Network::WifiPacket;
304 WifiPacket auth_request;
305 {
306 std::lock_guard<std::mutex> lock(connection_status_mutex);
307 if (connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsHost)) {
308 LOG_ERROR(Service_NWM,
309 "Connection sequence aborted, because connection status is %u",
310 connection_status.status);
311 return;
312 }
313
314 // Respond with an authentication response frame with SEQ2
315 auth_request.channel = network_channel;
316 auth_request.data = GenerateAuthenticationFrame(AuthenticationSeq::SEQ2);
317 auth_request.destination_address = packet.transmitter_address;
318 auth_request.type = WifiPacket::PacketType::Authentication;
319 }
320 SendPacket(auth_request);
321
322 SendAssociationResponseFrame(packet.transmitter_address);
323 }
324}
325
326static void HandleDataFrame(const Network::WifiPacket& packet) {
327 switch (GetFrameEtherType(packet.data)) {
328 case EtherType::EAPoL:
329 HandleEAPoLPacket(packet);
330 break;
331 case EtherType::SecureData:
332 // TODO(B3N30): Handle SecureData packets
333 break;
334 }
335}
336
337/// Callback to parse and handle a received wifi packet.
338void OnWifiPacketReceived(const Network::WifiPacket& packet) {
339 switch (packet.type) {
340 case Network::WifiPacket::PacketType::Beacon:
341 HandleBeaconFrame(packet);
342 break;
343 case Network::WifiPacket::PacketType::Authentication:
344 HandleAuthenticationFrame(packet);
345 break;
346 case Network::WifiPacket::PacketType::AssociationResponse:
347 HandleAssociationResponseFrame(packet);
348 break;
349 case Network::WifiPacket::PacketType::Data:
350 HandleDataFrame(packet);
351 break;
352 }
353}
354
355/**
356 * NWM_UDS::Shutdown service function
357 * Inputs:
358 * 1 : None
359 * Outputs:
360 * 0 : Return header
361 * 1 : Result of function, 0 on success, otherwise error code
362 */
363static void Shutdown(Interface* self) {
364 u32* cmd_buff = Kernel::GetCommandBuffer();
365
366 // TODO(purpasmart): Verify return header on HW
367
368 cmd_buff[1] = RESULT_SUCCESS.raw;
369
370 LOG_WARNING(Service_NWM, "(STUBBED) called");
371}
372
373/**
374 * NWM_UDS::RecvBeaconBroadcastData service function
375 * Returns the raw beacon data for nearby networks that match the supplied WlanCommId.
376 * Inputs:
377 * 1 : Output buffer max size
378 * 2-3 : Unknown
379 * 4-5 : Host MAC address.
380 * 6-14 : Unused
381 * 15 : WLan Comm Id
382 * 16 : Id
383 * 17 : Value 0
384 * 18 : Input handle
385 * 19 : (Size<<4) | 12
386 * 20 : Output buffer ptr
387 * Outputs:
388 * 0 : Return header
389 * 1 : Result of function, 0 on success, otherwise error code
390 */
391static void RecvBeaconBroadcastData(Interface* self) {
392 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x0F, 16, 4);
393
394 u32 out_buffer_size = rp.Pop<u32>();
395 u32 unk1 = rp.Pop<u32>();
396 u32 unk2 = rp.Pop<u32>();
397
398 MacAddress mac_address;
399 rp.PopRaw(mac_address);
400
401 rp.Skip(9, false);
402
403 u32 wlan_comm_id = rp.Pop<u32>();
404 u32 id = rp.Pop<u32>();
405 Kernel::Handle input_handle = rp.PopHandle();
406
407 size_t desc_size;
408 const VAddr out_buffer_ptr = rp.PopMappedBuffer(&desc_size);
409 ASSERT(desc_size == out_buffer_size);
410
411 VAddr current_buffer_pos = out_buffer_ptr;
412 u32 total_size = sizeof(BeaconDataReplyHeader);
413
414 // Retrieve all beacon frames that were received from the desired mac address.
415 auto beacons = GetReceivedBeacons(mac_address);
416
417 BeaconDataReplyHeader data_reply_header{};
418 data_reply_header.total_entries = static_cast<u32>(beacons.size());
419 data_reply_header.max_output_size = out_buffer_size;
420
421 Memory::WriteBlock(current_buffer_pos, &data_reply_header, sizeof(BeaconDataReplyHeader));
422 current_buffer_pos += sizeof(BeaconDataReplyHeader);
423
424 // Write each of the received beacons into the buffer
425 for (const auto& beacon : beacons) {
426 BeaconEntryHeader entry{};
427 // TODO(Subv): Figure out what this size is used for.
428 entry.unk_size = static_cast<u32>(sizeof(BeaconEntryHeader) + beacon.data.size());
429 entry.total_size = static_cast<u32>(sizeof(BeaconEntryHeader) + beacon.data.size());
430 entry.wifi_channel = beacon.channel;
431 entry.header_size = sizeof(BeaconEntryHeader);
432 entry.mac_address = beacon.transmitter_address;
433
434 ASSERT(current_buffer_pos < out_buffer_ptr + out_buffer_size);
435
436 Memory::WriteBlock(current_buffer_pos, &entry, sizeof(BeaconEntryHeader));
437 current_buffer_pos += sizeof(BeaconEntryHeader);
438
439 Memory::WriteBlock(current_buffer_pos, beacon.data.data(), beacon.data.size());
440 current_buffer_pos += static_cast<VAddr>(beacon.data.size());
441
442 total_size += static_cast<u32>(sizeof(BeaconEntryHeader) + beacon.data.size());
443 }
444
445 // Update the total size in the structure and write it to the buffer again.
446 data_reply_header.total_size = total_size;
447 Memory::WriteBlock(out_buffer_ptr, &data_reply_header, sizeof(BeaconDataReplyHeader));
448
449 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
450 rb.Push(RESULT_SUCCESS);
451
452 LOG_DEBUG(Service_NWM, "called out_buffer_size=0x%08X, wlan_comm_id=0x%08X, id=0x%08X,"
453 "input_handle=0x%08X, out_buffer_ptr=0x%08X, unk1=0x%08X, unk2=0x%08X",
454 out_buffer_size, wlan_comm_id, id, input_handle, out_buffer_ptr, unk1, unk2);
455}
456
457/**
458 * NWM_UDS::Initialize service function
459 * Inputs:
460 * 1 : Shared memory size
461 * 2-11 : Input NodeInfo Structure
462 * 12 : 2-byte Version
463 * 13 : Value 0
464 * 14 : Shared memory handle
465 * Outputs:
466 * 0 : Return header
467 * 1 : Result of function, 0 on success, otherwise error code
468 * 2 : Value 0
469 * 3 : Output event handle
470 */
471static void InitializeWithVersion(Interface* self) {
472 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1B, 12, 2);
473
474 u32 sharedmem_size = rp.Pop<u32>();
475
476 // Update the node information with the data the game gave us.
477 rp.PopRaw(current_node);
478
479 u16 version = rp.Pop<u16>();
480
481 Kernel::Handle sharedmem_handle = rp.PopHandle();
482
483 recv_buffer_memory = Kernel::g_handle_table.Get<Kernel::SharedMemory>(sharedmem_handle);
484
485 ASSERT_MSG(recv_buffer_memory->size == sharedmem_size, "Invalid shared memory size.");
486
487 {
488 std::lock_guard<std::mutex> lock(connection_status_mutex);
489
490 // Reset the connection status, it contains all zeros after initialization,
491 // except for the actual status value.
492 connection_status = {};
493 connection_status.status = static_cast<u32>(NetworkStatus::NotConnected);
494 }
495
496 IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
497 rb.Push(RESULT_SUCCESS);
498 rb.PushCopyHandles(Kernel::g_handle_table.Create(connection_status_event).Unwrap());
499
500 // TODO(Subv): Connect the OnWifiPacketReceived function to the wifi packet received callback of
501 // the room we're currently in.
502
503 LOG_DEBUG(Service_NWM, "called sharedmem_size=0x%08X, version=0x%08X, sharedmem_handle=0x%08X",
504 sharedmem_size, version, sharedmem_handle);
505}
506
507/**
508 * NWM_UDS::GetConnectionStatus service function.
509 * Returns the connection status structure for the currently open network connection.
510 * This structure contains information about the connection,
511 * like the number of connected nodes, etc.
512 * Inputs:
513 * 0 : Command header.
514 * Outputs:
515 * 0 : Return header
516 * 1 : Result of function, 0 on success, otherwise error code
517 * 2-13 : Channel of the current WiFi network connection.
518 */
519static void GetConnectionStatus(Interface* self) {
520 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0xB, 0, 0);
521 IPC::RequestBuilder rb = rp.MakeBuilder(13, 0);
522
523 rb.Push(RESULT_SUCCESS);
524 {
525 std::lock_guard<std::mutex> lock(connection_status_mutex);
526 rb.PushRaw(connection_status);
527
528 // Reset the bitmask of changed nodes after each call to this
529 // function to prevent falsely informing games of outstanding
530 // changes in subsequent calls.
531 // TODO(Subv): Find exactly where the NWM module resets this value.
532 connection_status.changed_nodes = 0;
533 }
534
535 LOG_DEBUG(Service_NWM, "called");
536}
537
538/**
539 * NWM_UDS::Bind service function.
540 * Binds a BindNodeId to a data channel and retrieves a data event.
541 * Inputs:
542 * 1 : BindNodeId
543 * 2 : Receive buffer size.
544 * 3 : u8 Data channel to bind to.
545 * 4 : Network node id.
546 * Outputs:
547 * 0 : Return header
548 * 1 : Result of function, 0 on success, otherwise error code
549 * 2 : Copy handle descriptor.
550 * 3 : Data available event handle.
551 */
552static void Bind(Interface* self) {
553 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x12, 4, 0);
554
555 u32 bind_node_id = rp.Pop<u32>();
556 u32 recv_buffer_size = rp.Pop<u32>();
557 u8 data_channel = rp.Pop<u8>();
558 u16 network_node_id = rp.Pop<u16>();
559
560 // TODO(Subv): Store the data channel and verify it when receiving data frames.
561
562 LOG_DEBUG(Service_NWM, "called");
563
564 if (data_channel == 0) {
565 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
566 rb.Push(ResultCode(ErrorDescription::NotAuthorized, ErrorModule::UDS,
567 ErrorSummary::WrongArgument, ErrorLevel::Usage));
568 return;
569 }
570
571 // Create a new event for this bind node.
572 // TODO(Subv): Signal this event when new data is received on this data channel.
573 auto event = Kernel::Event::Create(Kernel::ResetType::OneShot,
574 "NWM::BindNodeEvent" + std::to_string(bind_node_id));
575 bind_node_events[bind_node_id] = event;
576
577 IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
578
579 rb.Push(RESULT_SUCCESS);
580 rb.PushCopyHandles(Kernel::g_handle_table.Create(event).Unwrap());
581}
582
583/**
584 * NWM_UDS::BeginHostingNetwork service function.
585 * Creates a network and starts broadcasting its presence.
586 * Inputs:
587 * 1 : Passphrase buffer size.
588 * 3 : VAddr of the NetworkInfo structure.
589 * 5 : VAddr of the passphrase.
590 * Outputs:
591 * 0 : Return header
592 * 1 : Result of function, 0 on success, otherwise error code
593 */
594static void BeginHostingNetwork(Interface* self) {
595 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1D, 1, 4);
596
597 const u32 passphrase_size = rp.Pop<u32>();
598
599 size_t desc_size;
600 const VAddr network_info_address = rp.PopStaticBuffer(&desc_size, false);
601 ASSERT(desc_size == sizeof(NetworkInfo));
602 const VAddr passphrase_address = rp.PopStaticBuffer(&desc_size, false);
603 ASSERT(desc_size == passphrase_size);
604
605 // TODO(Subv): Store the passphrase and verify it when attempting a connection.
606
607 LOG_DEBUG(Service_NWM, "called");
608
609 Memory::ReadBlock(network_info_address, &network_info, sizeof(NetworkInfo));
610
611 // The real UDS module throws a fatal error if this assert fails.
612 ASSERT_MSG(network_info.max_nodes > 1, "Trying to host a network of only one member.");
613
614 {
615 std::lock_guard<std::mutex> lock(connection_status_mutex);
616 connection_status.status = static_cast<u32>(NetworkStatus::ConnectedAsHost);
617
618 // Ensure the application data size is less than the maximum value.
619 ASSERT_MSG(network_info.application_data_size <= ApplicationDataSize,
620 "Data size is too big.");
621
622 // Set up basic information for this network.
623 network_info.oui_value = NintendoOUI;
624 network_info.oui_type = static_cast<u8>(NintendoTagId::NetworkInfo);
625
626 connection_status.max_nodes = network_info.max_nodes;
627
628 // Resize the nodes list to hold max_nodes.
629 node_info.resize(network_info.max_nodes);
630
631 // There's currently only one node in the network (the host).
632 connection_status.total_nodes = 1;
633 network_info.total_nodes = 1;
634 // The host is always the first node
635 connection_status.network_node_id = 1;
636 current_node.network_node_id = 1;
637 connection_status.nodes[0] = connection_status.network_node_id;
638 // Set the bit 0 in the nodes bitmask to indicate that node 1 is already taken.
639 connection_status.node_bitmask |= 1;
640 // Notify the application that the first node was set.
641 connection_status.changed_nodes |= 1;
642 node_info[0] = current_node;
643 }
644
645 // If the game has a preferred channel, use that instead.
646 if (network_info.channel != 0)
647 network_channel = network_info.channel;
648
649 connection_status_event->Signal();
650
651 // Start broadcasting the network, send a beacon frame every 102.4ms.
652 CoreTiming::ScheduleEvent(msToCycles(DefaultBeaconInterval * MillisecondsPerTU),
653 beacon_broadcast_event, 0);
654
655 LOG_WARNING(Service_NWM,
656 "An UDS network has been created, but broadcasting it is unimplemented.");
657
658 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
659 rb.Push(RESULT_SUCCESS);
660}
661
662/**
663 * NWM_UDS::DestroyNetwork service function.
664 * Closes the network that we're currently hosting.
665 * Inputs:
666 * 0 : Command header.
667 * Outputs:
668 * 0 : Return header
669 * 1 : Result of function, 0 on success, otherwise error code
670 */
671static void DestroyNetwork(Interface* self) {
672 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x08, 0, 0);
673
674 // TODO(Subv): Find out what happens if this is called while
675 // no network is being hosted.
676
677 // Unschedule the beacon broadcast event.
678 CoreTiming::UnscheduleEvent(beacon_broadcast_event, 0);
679
680 {
681 std::lock_guard<std::mutex> lock(connection_status_mutex);
682
683 // TODO(Subv): Check if connection_status is indeed reset after this call.
684 connection_status = {};
685 connection_status.status = static_cast<u8>(NetworkStatus::NotConnected);
686 }
687 connection_status_event->Signal();
688
689 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
690
691 rb.Push(RESULT_SUCCESS);
692
693 LOG_WARNING(Service_NWM, "called");
694}
695
696/**
697 * NWM_UDS::SendTo service function.
698 * Sends a data frame to the UDS network we're connected to.
699 * Inputs:
700 * 0 : Command header.
701 * 1 : Unknown.
702 * 2 : u16 Destination network node id.
703 * 3 : u8 Data channel.
704 * 4 : Buffer size >> 2
705 * 5 : Data size
706 * 6 : Flags
707 * 7 : Input buffer descriptor
708 * 8 : Input buffer address
709 * Outputs:
710 * 0 : Return header
711 * 1 : Result of function, 0 on success, otherwise error code
712 */
713static void SendTo(Interface* self) {
714 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x17, 6, 2);
715
716 rp.Skip(1, false);
717 u16 dest_node_id = rp.Pop<u16>();
718 u8 data_channel = rp.Pop<u8>();
719 rp.Skip(1, false);
720 u32 data_size = rp.Pop<u32>();
721 u32 flags = rp.Pop<u32>();
722
723 size_t desc_size;
724 const VAddr input_address = rp.PopStaticBuffer(&desc_size, false);
725 ASSERT(desc_size == data_size);
726
727 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
728
729 u16 network_node_id;
730
731 {
732 std::lock_guard<std::mutex> lock(connection_status_mutex);
733 if (connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsClient) &&
734 connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsHost)) {
735 rb.Push(ResultCode(ErrorDescription::NotAuthorized, ErrorModule::UDS,
736 ErrorSummary::InvalidState, ErrorLevel::Status));
737 return;
738 }
739
740 if (dest_node_id == connection_status.network_node_id) {
741 rb.Push(ResultCode(ErrorDescription::NotFound, ErrorModule::UDS,
742 ErrorSummary::WrongArgument, ErrorLevel::Status));
743 return;
744 }
745
746 network_node_id = connection_status.network_node_id;
747 }
748
749 // TODO(Subv): Do something with the flags.
750
751 constexpr size_t MaxSize = 0x5C6;
752 if (data_size > MaxSize) {
753 rb.Push(ResultCode(ErrorDescription::TooLarge, ErrorModule::UDS,
754 ErrorSummary::WrongArgument, ErrorLevel::Usage));
755 return;
756 }
757
758 std::vector<u8> data(data_size);
759 Memory::ReadBlock(input_address, data.data(), data.size());
760
761 // TODO(Subv): Increment the sequence number after each sent packet.
762 u16 sequence_number = 0;
763 std::vector<u8> data_payload =
764 GenerateDataPayload(data, data_channel, dest_node_id, network_node_id, sequence_number);
765
766 // TODO(Subv): Retrieve the MAC address of the dest_node_id and our own to encrypt
767 // and encapsulate the payload.
768
769 // TODO(Subv): Send the frame.
770
771 rb.Push(RESULT_SUCCESS);
772
773 LOG_WARNING(Service_NWM, "(STUB) called dest_node_id=%u size=%u flags=%u channel=%u",
774 static_cast<u32>(dest_node_id), data_size, flags, static_cast<u32>(data_channel));
775}
776
777/**
778 * NWM_UDS::GetChannel service function.
779 * Returns the WiFi channel in which the network we're connected to is transmitting.
780 * Inputs:
781 * 0 : Command header.
782 * Outputs:
783 * 0 : Return header
784 * 1 : Result of function, 0 on success, otherwise error code
785 * 2 : Channel of the current WiFi network connection.
786 */
787static void GetChannel(Interface* self) {
788 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1A, 0, 0);
789 IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
790
791 std::lock_guard<std::mutex> lock(connection_status_mutex);
792 bool is_connected = connection_status.status != static_cast<u32>(NetworkStatus::NotConnected);
793
794 u8 channel = is_connected ? network_channel : 0;
795
796 rb.Push(RESULT_SUCCESS);
797 rb.Push(channel);
798
799 LOG_DEBUG(Service_NWM, "called");
800}
801
802/**
803 * NWM_UDS::SetApplicationData service function.
804 * Updates the application data that is being broadcast in the beacon frames
805 * for the network that we're hosting.
806 * Inputs:
807 * 1 : Data size.
808 * 3 : VAddr of the data.
809 * Outputs:
810 * 0 : Return header
811 * 1 : Result of function, 0 on success, otherwise error code
812 * 2 : Channel of the current WiFi network connection.
813 */
814static void SetApplicationData(Interface* self) {
815 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1A, 1, 2);
816
817 u32 size = rp.Pop<u32>();
818
819 size_t desc_size;
820 const VAddr address = rp.PopStaticBuffer(&desc_size, false);
821 ASSERT(desc_size == size);
822
823 LOG_DEBUG(Service_NWM, "called");
824
825 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
826
827 if (size > ApplicationDataSize) {
828 rb.Push(ResultCode(ErrorDescription::TooLarge, ErrorModule::UDS,
829 ErrorSummary::WrongArgument, ErrorLevel::Usage));
830 return;
831 }
832
833 network_info.application_data_size = size;
834 Memory::ReadBlock(address, network_info.application_data.data(), size);
835
836 rb.Push(RESULT_SUCCESS);
837}
838
839/**
840 * NWM_UDS::DecryptBeaconData service function.
841 * Decrypts the encrypted data tags contained in the 802.11 beacons.
842 * Inputs:
843 * 1 : Input network struct buffer descriptor.
844 * 2 : Input network struct buffer ptr.
845 * 3 : Input tag0 encrypted buffer descriptor.
846 * 4 : Input tag0 encrypted buffer ptr.
847 * 5 : Input tag1 encrypted buffer descriptor.
848 * 6 : Input tag1 encrypted buffer ptr.
849 * 64 : Output buffer descriptor.
850 * 65 : Output buffer ptr.
851 * Outputs:
852 * 0 : Return header
853 * 1 : Result of function, 0 on success, otherwise error code
854 */
855static void DecryptBeaconData(Interface* self) {
856 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1F, 0, 6);
857
858 size_t desc_size;
859 const VAddr network_struct_addr = rp.PopStaticBuffer(&desc_size);
860 ASSERT(desc_size == sizeof(NetworkInfo));
861
862 size_t data0_size;
863 const VAddr encrypted_data0_addr = rp.PopStaticBuffer(&data0_size);
864
865 size_t data1_size;
866 const VAddr encrypted_data1_addr = rp.PopStaticBuffer(&data1_size);
867
868 size_t output_buffer_size;
869 const VAddr output_buffer_addr = rp.PeekStaticBuffer(0, &output_buffer_size);
870
871 // This size is hardcoded in the 3DS UDS code.
872 ASSERT(output_buffer_size == sizeof(NodeInfo) * UDSMaxNodes);
873
874 LOG_WARNING(Service_NWM, "called in0=%08X in1=%08X out=%08X", encrypted_data0_addr,
875 encrypted_data1_addr, output_buffer_addr);
876
877 NetworkInfo net_info;
878 Memory::ReadBlock(network_struct_addr, &net_info, sizeof(net_info));
879
880 // Read the encrypted data.
881 // The first 4 bytes should be the OUI and the OUI Type of the tags.
882 std::array<u8, 3> oui;
883 Memory::ReadBlock(encrypted_data0_addr, oui.data(), oui.size());
884 ASSERT_MSG(oui == NintendoOUI, "Unexpected OUI");
885 Memory::ReadBlock(encrypted_data1_addr, oui.data(), oui.size());
886 ASSERT_MSG(oui == NintendoOUI, "Unexpected OUI");
887
888 ASSERT_MSG(Memory::Read8(encrypted_data0_addr + 3) ==
889 static_cast<u8>(NintendoTagId::EncryptedData0),
890 "Unexpected tag id");
891 ASSERT_MSG(Memory::Read8(encrypted_data1_addr + 3) ==
892 static_cast<u8>(NintendoTagId::EncryptedData1),
893 "Unexpected tag id");
894
895 std::vector<u8> beacon_data(data0_size + data1_size);
896 Memory::ReadBlock(encrypted_data0_addr + 4, beacon_data.data(), data0_size);
897 Memory::ReadBlock(encrypted_data1_addr + 4, beacon_data.data() + data0_size, data1_size);
898
899 // Decrypt the data
900 DecryptBeaconData(net_info, beacon_data);
901
902 // The beacon data header contains the MD5 hash of the data.
903 BeaconData beacon_header;
904 std::memcpy(&beacon_header, beacon_data.data(), sizeof(beacon_header));
905
906 // TODO(Subv): Verify the MD5 hash of the data and return 0xE1211005 if invalid.
907
908 u8 num_nodes = net_info.max_nodes;
909
910 std::vector<NodeInfo> nodes;
911
912 for (int i = 0; i < num_nodes; ++i) {
913 BeaconNodeInfo info;
914 std::memcpy(&info, beacon_data.data() + sizeof(beacon_header) + i * sizeof(info),
915 sizeof(info));
916
917 // Deserialize the node information.
918 NodeInfo node{};
919 node.friend_code_seed = info.friend_code_seed;
920 node.network_node_id = info.network_node_id;
921 for (int i = 0; i < info.username.size(); ++i)
922 node.username[i] = info.username[i];
923
924 nodes.push_back(node);
925 }
926
927 Memory::ZeroBlock(output_buffer_addr, sizeof(NodeInfo) * UDSMaxNodes);
928 Memory::WriteBlock(output_buffer_addr, nodes.data(), sizeof(NodeInfo) * nodes.size());
929
930 IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
931 rb.PushStaticBuffer(output_buffer_addr, output_buffer_size, 0);
932 rb.Push(RESULT_SUCCESS);
933}
934
935// Sends a 802.11 beacon frame with information about the current network.
936static void BeaconBroadcastCallback(u64 userdata, int cycles_late) {
937 // Don't do anything if we're not actually hosting a network
938 if (connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsHost))
939 return;
940
941 std::vector<u8> frame = GenerateBeaconFrame(network_info, node_info);
942
943 using Network::WifiPacket;
944 WifiPacket packet;
945 packet.type = WifiPacket::PacketType::Beacon;
946 packet.data = std::move(frame);
947 packet.destination_address = Network::BroadcastMac;
948 packet.channel = network_channel;
949
950 SendPacket(packet);
951
952 // Start broadcasting the network, send a beacon frame every 102.4ms.
953 CoreTiming::ScheduleEvent(msToCycles(DefaultBeaconInterval * MillisecondsPerTU) - cycles_late,
954 beacon_broadcast_event, 0);
955}
956
957/*
958 * Called when a client connects to an UDS network we're hosting,
959 * updates the connection status and signals the update event.
960 * @param network_node_id Network Node Id of the connecting client.
961 */
962void OnClientConnected(u16 network_node_id) {
963 std::lock_guard<std::mutex> lock(connection_status_mutex);
964 ASSERT_MSG(connection_status.status == static_cast<u32>(NetworkStatus::ConnectedAsHost),
965 "Can not accept clients if we're not hosting a network");
966 ASSERT_MSG(connection_status.total_nodes < connection_status.max_nodes,
967 "Can not accept connections on a full network");
968
969 u32 node_id = GetNextAvailableNodeId();
970 connection_status.node_bitmask |= 1 << node_id;
971 connection_status.changed_nodes |= 1 << node_id;
972 connection_status.nodes[node_id] = network_node_id;
973 connection_status.total_nodes++;
974 connection_status_event->Signal();
975}
976
977const Interface::FunctionInfo FunctionTable[] = {
978 {0x000102C2, nullptr, "Initialize (deprecated)"},
979 {0x00020000, nullptr, "Scrap"},
980 {0x00030000, Shutdown, "Shutdown"},
981 {0x00040402, nullptr, "CreateNetwork (deprecated)"},
982 {0x00050040, nullptr, "EjectClient"},
983 {0x00060000, nullptr, "EjectSpectator"},
984 {0x00070080, nullptr, "UpdateNetworkAttribute"},
985 {0x00080000, DestroyNetwork, "DestroyNetwork"},
986 {0x00090442, nullptr, "ConnectNetwork (deprecated)"},
987 {0x000A0000, nullptr, "DisconnectNetwork"},
988 {0x000B0000, GetConnectionStatus, "GetConnectionStatus"},
989 {0x000D0040, nullptr, "GetNodeInformation"},
990 {0x000E0006, nullptr, "DecryptBeaconData (deprecated)"},
991 {0x000F0404, RecvBeaconBroadcastData, "RecvBeaconBroadcastData"},
992 {0x00100042, SetApplicationData, "SetApplicationData"},
993 {0x00110040, nullptr, "GetApplicationData"},
994 {0x00120100, Bind, "Bind"},
995 {0x00130040, nullptr, "Unbind"},
996 {0x001400C0, nullptr, "PullPacket"},
997 {0x00150080, nullptr, "SetMaxSendDelay"},
998 {0x00170182, SendTo, "SendTo"},
999 {0x001A0000, GetChannel, "GetChannel"},
1000 {0x001B0302, InitializeWithVersion, "InitializeWithVersion"},
1001 {0x001D0044, BeginHostingNetwork, "BeginHostingNetwork"},
1002 {0x001E0084, nullptr, "ConnectToNetwork"},
1003 {0x001F0006, DecryptBeaconData, "DecryptBeaconData"},
1004 {0x00200040, nullptr, "Flush"},
1005 {0x00210080, nullptr, "SetProbeResponseParam"},
1006 {0x00220402, nullptr, "ScanOnConnection"},
1007};
1008
1009NWM_UDS::NWM_UDS() {
1010 connection_status_event =
1011 Kernel::Event::Create(Kernel::ResetType::OneShot, "NWM::connection_status_event");
1012
1013 Register(FunctionTable);
1014
1015 beacon_broadcast_event =
1016 CoreTiming::RegisterEvent("UDS::BeaconBroadcastCallback", BeaconBroadcastCallback);
1017}
1018
1019NWM_UDS::~NWM_UDS() {
1020 network_info = {};
1021 bind_node_events.clear();
1022 connection_status_event = nullptr;
1023 recv_buffer_memory = nullptr;
1024
1025 {
1026 std::lock_guard<std::mutex> lock(connection_status_mutex);
1027 connection_status = {};
1028 connection_status.status = static_cast<u32>(NetworkStatus::NotConnected);
1029 }
1030
1031 CoreTiming::UnscheduleEvent(beacon_broadcast_event, 0);
1032}
1033
1034} // namespace NWM
1035} // namespace Service
diff --git a/src/core/hle/service/nwm/nwm_uds.h b/src/core/hle/service/nwm/nwm_uds.h
deleted file mode 100644
index f1caaf974..000000000
--- a/src/core/hle/service/nwm/nwm_uds.h
+++ /dev/null
@@ -1,111 +0,0 @@
1// Copyright 2014 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 <array>
8#include <cstddef>
9#include <vector>
10#include "common/common_types.h"
11#include "common/swap.h"
12#include "core/hle/service/service.h"
13
14// Local-WLAN service
15
16namespace Service {
17namespace NWM {
18
19const size_t ApplicationDataSize = 0xC8;
20const u8 DefaultNetworkChannel = 11;
21
22// Number of milliseconds in a TU.
23const double MillisecondsPerTU = 1.024;
24// Interval measured in TU, the default value is 100TU = 102.4ms
25const u16 DefaultBeaconInterval = 100;
26
27/// The maximum number of nodes that can exist in an UDS session.
28constexpr u32 UDSMaxNodes = 16;
29
30struct NodeInfo {
31 u64_le friend_code_seed;
32 std::array<u16_le, 10> username;
33 INSERT_PADDING_BYTES(4);
34 u16_le network_node_id;
35 INSERT_PADDING_BYTES(6);
36};
37
38static_assert(sizeof(NodeInfo) == 40, "NodeInfo has incorrect size.");
39
40using NodeList = std::vector<NodeInfo>;
41
42enum class NetworkStatus {
43 NotConnected = 3,
44 ConnectedAsHost = 6,
45 Connecting = 7,
46 ConnectedAsClient = 9,
47 ConnectedAsSpectator = 10,
48};
49
50struct ConnectionStatus {
51 u32_le status;
52 INSERT_PADDING_WORDS(1);
53 u16_le network_node_id;
54 u16_le changed_nodes;
55 u16_le nodes[UDSMaxNodes];
56 u8 total_nodes;
57 u8 max_nodes;
58 u16_le node_bitmask;
59};
60
61static_assert(sizeof(ConnectionStatus) == 0x30, "ConnectionStatus has incorrect size.");
62
63struct NetworkInfo {
64 std::array<u8, 6> host_mac_address;
65 u8 channel;
66 INSERT_PADDING_BYTES(1);
67 u8 initialized;
68 INSERT_PADDING_BYTES(3);
69 std::array<u8, 3> oui_value;
70 u8 oui_type;
71 // This field is received as BigEndian from the game.
72 u32_be wlan_comm_id;
73 u8 id;
74 INSERT_PADDING_BYTES(1);
75 u16_be attributes;
76 u32_be network_id;
77 u8 total_nodes;
78 u8 max_nodes;
79 INSERT_PADDING_BYTES(2);
80 INSERT_PADDING_BYTES(0x1F);
81 u8 application_data_size;
82 std::array<u8, ApplicationDataSize> application_data;
83};
84
85static_assert(offsetof(NetworkInfo, oui_value) == 0xC, "oui_value is at the wrong offset.");
86static_assert(offsetof(NetworkInfo, wlan_comm_id) == 0x10, "wlancommid is at the wrong offset.");
87static_assert(sizeof(NetworkInfo) == 0x108, "NetworkInfo has incorrect size.");
88
89/// Additional block tag ids in the Beacon and Association Response frames
90enum class TagId : u8 {
91 SSID = 0,
92 SupportedRates = 1,
93 DSParameterSet = 2,
94 TrafficIndicationMap = 5,
95 CountryInformation = 7,
96 ERPInformation = 42,
97 VendorSpecific = 221
98};
99
100class NWM_UDS final : public Interface {
101public:
102 NWM_UDS();
103 ~NWM_UDS() override;
104
105 std::string GetPortName() const override {
106 return "nwm::UDS";
107 }
108};
109
110} // namespace NWM
111} // namespace Service
diff --git a/src/core/hle/service/nwm/uds_beacon.cpp b/src/core/hle/service/nwm/uds_beacon.cpp
deleted file mode 100644
index 73a80d940..000000000
--- a/src/core/hle/service/nwm/uds_beacon.cpp
+++ /dev/null
@@ -1,329 +0,0 @@
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 <cryptopp/aes.h>
7#include <cryptopp/md5.h>
8#include <cryptopp/modes.h>
9#include <cryptopp/sha.h>
10#include "common/assert.h"
11#include "core/hle/service/nwm/nwm_uds.h"
12#include "core/hle/service/nwm/uds_beacon.h"
13
14namespace Service {
15namespace NWM {
16
17// 802.11 broadcast MAC address
18constexpr MacAddress BroadcastMac = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
19
20constexpr u64 DefaultNetworkUptime = 900000000; // 15 minutes in microseconds.
21
22// Note: These values were taken from a packet capture of an o3DS XL
23// broadcasting a Super Smash Bros. 4 lobby.
24constexpr u16 DefaultExtraCapabilities = 0x0431;
25
26// Size of the SSID broadcast by an UDS beacon frame.
27constexpr u8 UDSBeaconSSIDSize = 8;
28
29// The maximum size of the data stored in the EncryptedData0 tag (24).
30constexpr u32 EncryptedDataSizeCutoff = 0xFA;
31
32/**
33 * NWM Beacon data encryption key, taken from the NWM module code.
34 * We stub this with an all-zeros key as that is enough for Citra's purpose.
35 * The real key can be used here to generate beacons that will be accepted by
36 * a real 3ds.
37 */
38constexpr std::array<u8, CryptoPP::AES::BLOCKSIZE> nwm_beacon_key = {};
39
40/**
41 * Generates a buffer with the fixed parameters of an 802.11 Beacon frame
42 * using dummy values.
43 * @returns A buffer with the fixed parameters of the beacon frame.
44 */
45std::vector<u8> GenerateFixedParameters() {
46 std::vector<u8> buffer(sizeof(BeaconFrameHeader));
47
48 BeaconFrameHeader header{};
49 // Use a fixed default time for now.
50 // TODO(Subv): Perhaps use the difference between now and the time the network was started?
51 header.timestamp = DefaultNetworkUptime;
52 header.beacon_interval = DefaultBeaconInterval;
53 header.capabilities = DefaultExtraCapabilities;
54
55 std::memcpy(buffer.data(), &header, sizeof(header));
56
57 return buffer;
58}
59
60/**
61 * Generates an SSID tag of an 802.11 Beacon frame with an 8-byte all-zero SSID value.
62 * @returns A buffer with the SSID tag.
63 */
64std::vector<u8> GenerateSSIDTag() {
65 std::vector<u8> buffer(sizeof(TagHeader) + UDSBeaconSSIDSize);
66
67 TagHeader tag_header{};
68 tag_header.tag_id = static_cast<u8>(TagId::SSID);
69 tag_header.length = UDSBeaconSSIDSize;
70
71 std::memcpy(buffer.data(), &tag_header, sizeof(TagHeader));
72
73 // The rest of the buffer is already filled with zeros.
74
75 return buffer;
76}
77
78/**
79 * Generates a buffer with the basic tagged parameters of an 802.11 Beacon frame
80 * such as SSID, Rate Information, Country Information, etc.
81 * @returns A buffer with the tagged parameters of the beacon frame.
82 */
83std::vector<u8> GenerateBasicTaggedParameters() {
84 // Append the SSID tag
85 std::vector<u8> buffer = GenerateSSIDTag();
86
87 // TODO(Subv): Add the SupportedRates tag.
88 // TODO(Subv): Add the DSParameterSet tag.
89 // TODO(Subv): Add the TrafficIndicationMap tag.
90 // TODO(Subv): Add the CountryInformation tag.
91 // TODO(Subv): Add the ERPInformation tag.
92
93 return buffer;
94}
95
96/**
97 * Generates a buffer with the Dummy Nintendo tag.
98 * It is currently unknown what this tag does.
99 * TODO(Subv): Figure out if this is needed and what it does.
100 * @returns A buffer with the Nintendo tagged parameters of the beacon frame.
101 */
102std::vector<u8> GenerateNintendoDummyTag() {
103 // Note: These values were taken from a packet capture of an o3DS XL
104 // broadcasting a Super Smash Bros. 4 lobby.
105 constexpr std::array<u8, 3> dummy_data = {0x0A, 0x00, 0x00};
106
107 DummyTag tag{};
108 tag.header.tag_id = static_cast<u8>(TagId::VendorSpecific);
109 tag.header.length = sizeof(DummyTag) - sizeof(TagHeader);
110 tag.oui_type = static_cast<u8>(NintendoTagId::Dummy);
111 tag.oui = NintendoOUI;
112 tag.data = dummy_data;
113
114 std::vector<u8> buffer(sizeof(DummyTag));
115 std::memcpy(buffer.data(), &tag, sizeof(DummyTag));
116
117 return buffer;
118}
119
120/**
121 * Generates a buffer with the Network Info Nintendo tag.
122 * This tag contains the network information of the network that is being broadcast.
123 * It also contains the application data provided by the application that opened the network.
124 * @returns A buffer with the Nintendo network info parameter of the beacon frame.
125 */
126std::vector<u8> GenerateNintendoNetworkInfoTag(const NetworkInfo& network_info) {
127 NetworkInfoTag tag{};
128 tag.header.tag_id = static_cast<u8>(TagId::VendorSpecific);
129 tag.header.length =
130 sizeof(NetworkInfoTag) - sizeof(TagHeader) + network_info.application_data_size;
131 tag.appdata_size = network_info.application_data_size;
132 // Set the hash to zero initially, it will be updated once we calculate it.
133 tag.sha_hash = {};
134
135 // Ensure the network structure has the correct OUI and OUI type.
136 ASSERT(network_info.oui_type == static_cast<u8>(NintendoTagId::NetworkInfo));
137 ASSERT(network_info.oui_value == NintendoOUI);
138
139 // Ensure the application data size is less than the maximum value.
140 ASSERT_MSG(network_info.application_data_size <= ApplicationDataSize, "Data size is too big.");
141
142 // This tag contains the network info structure starting at the OUI.
143 std::memcpy(tag.network_info.data(), &network_info.oui_value, tag.network_info.size());
144
145 // Copy the tag and the data so we can calculate the SHA1 over it.
146 std::vector<u8> buffer(sizeof(tag) + network_info.application_data_size);
147 std::memcpy(buffer.data(), &tag, sizeof(tag));
148 std::memcpy(buffer.data() + sizeof(tag), network_info.application_data.data(),
149 network_info.application_data_size);
150
151 // Calculate the SHA1 of the contents of the tag.
152 std::array<u8, CryptoPP::SHA1::DIGESTSIZE> hash;
153 CryptoPP::SHA1().CalculateDigest(hash.data(),
154 buffer.data() + offsetof(NetworkInfoTag, network_info),
155 buffer.size() - sizeof(TagHeader));
156
157 // Copy it directly into the buffer, overwriting the zeros that we had previously placed there.
158 std::memcpy(buffer.data() + offsetof(NetworkInfoTag, sha_hash), hash.data(), hash.size());
159
160 return buffer;
161}
162
163/*
164 * Calculates the CTR used for the AES-CTR encryption of the data stored in the
165 * EncryptedDataTags.
166 * @returns The CTR used for beacon crypto.
167 */
168std::array<u8, CryptoPP::AES::BLOCKSIZE> GetBeaconCryptoCTR(const NetworkInfo& network_info) {
169 BeaconDataCryptoCTR data{};
170
171 data.host_mac = network_info.host_mac_address;
172 data.wlan_comm_id = network_info.wlan_comm_id;
173 data.id = network_info.id;
174 data.network_id = network_info.network_id;
175
176 std::array<u8, CryptoPP::AES::BLOCKSIZE> hash;
177 std::memcpy(hash.data(), &data, sizeof(data));
178
179 return hash;
180}
181
182/*
183 * Serializes the node information into the format needed for network transfer,
184 * and then encrypts it with the NWM key.
185 * @returns The serialized and encrypted node information.
186 */
187std::vector<u8> GeneratedEncryptedData(const NetworkInfo& network_info, const NodeList& nodes) {
188 std::vector<u8> buffer(sizeof(BeaconData));
189
190 BeaconData data{};
191 std::memcpy(buffer.data(), &data, sizeof(BeaconData));
192
193 for (const NodeInfo& node : nodes) {
194 // Serialize each node and convert the data from
195 // host byte-order to Big Endian.
196 BeaconNodeInfo info{};
197 info.friend_code_seed = node.friend_code_seed;
198 info.network_node_id = node.network_node_id;
199 for (int i = 0; i < info.username.size(); ++i)
200 info.username[i] = node.username[i];
201
202 buffer.insert(buffer.end(), reinterpret_cast<u8*>(&info),
203 reinterpret_cast<u8*>(&info) + sizeof(info));
204 }
205
206 // Calculate the MD5 hash of the data in the buffer, not including the hash field.
207 std::array<u8, CryptoPP::MD5::DIGESTSIZE> hash;
208 CryptoPP::MD5().CalculateDigest(hash.data(), buffer.data() + offsetof(BeaconData, bitmask),
209 buffer.size() - sizeof(data.md5_hash));
210
211 // Copy the hash into the buffer.
212 std::memcpy(buffer.data(), hash.data(), hash.size());
213
214 // Encrypt the data using AES-CTR and the NWM beacon key.
215 using CryptoPP::AES;
216 std::array<u8, AES::BLOCKSIZE> counter = GetBeaconCryptoCTR(network_info);
217 CryptoPP::CTR_Mode<AES>::Encryption aes;
218 aes.SetKeyWithIV(nwm_beacon_key.data(), AES::BLOCKSIZE, counter.data());
219 aes.ProcessData(buffer.data(), buffer.data(), buffer.size());
220
221 return buffer;
222}
223
224void DecryptBeaconData(const NetworkInfo& network_info, std::vector<u8>& buffer) {
225 // Decrypt the data using AES-CTR and the NWM beacon key.
226 using CryptoPP::AES;
227 std::array<u8, AES::BLOCKSIZE> counter = GetBeaconCryptoCTR(network_info);
228 CryptoPP::CTR_Mode<AES>::Decryption aes;
229 aes.SetKeyWithIV(nwm_beacon_key.data(), AES::BLOCKSIZE, counter.data());
230 aes.ProcessData(buffer.data(), buffer.data(), buffer.size());
231}
232
233/**
234 * Generates a buffer with the Network Info Nintendo tag.
235 * This tag contains the first portion of the encrypted payload in the 802.11 beacon frame.
236 * The encrypted payload contains information about the nodes currently connected to the network.
237 * @returns A buffer with the first Nintendo encrypted data parameters of the beacon frame.
238 */
239std::vector<u8> GenerateNintendoFirstEncryptedDataTag(const NetworkInfo& network_info,
240 const NodeList& nodes) {
241 const size_t payload_size =
242 std::min<size_t>(EncryptedDataSizeCutoff, nodes.size() * sizeof(NodeInfo));
243
244 EncryptedDataTag tag{};
245 tag.header.tag_id = static_cast<u8>(TagId::VendorSpecific);
246 tag.header.length = static_cast<u8>(sizeof(tag) - sizeof(TagHeader) + payload_size);
247 tag.oui_type = static_cast<u8>(NintendoTagId::EncryptedData0);
248 tag.oui = NintendoOUI;
249
250 std::vector<u8> buffer(sizeof(tag) + payload_size);
251 std::memcpy(buffer.data(), &tag, sizeof(tag));
252
253 std::vector<u8> encrypted_data = GeneratedEncryptedData(network_info, nodes);
254 std::memcpy(buffer.data() + sizeof(tag), encrypted_data.data(), payload_size);
255
256 return buffer;
257}
258
259/**
260 * Generates a buffer with the Network Info Nintendo tag.
261 * This tag contains the second portion of the encrypted payload in the 802.11 beacon frame.
262 * The encrypted payload contains information about the nodes currently connected to the network.
263 * This tag is only present if the payload size is greater than EncryptedDataSizeCutoff (0xFA)
264 * bytes.
265 * @returns A buffer with the second Nintendo encrypted data parameters of the beacon frame.
266 */
267std::vector<u8> GenerateNintendoSecondEncryptedDataTag(const NetworkInfo& network_info,
268 const NodeList& nodes) {
269 // This tag is only present if the payload is larger than EncryptedDataSizeCutoff (0xFA).
270 if (nodes.size() * sizeof(NodeInfo) <= EncryptedDataSizeCutoff)
271 return {};
272
273 const size_t payload_size = nodes.size() * sizeof(NodeInfo) - EncryptedDataSizeCutoff;
274
275 const size_t tag_length = sizeof(EncryptedDataTag) - sizeof(TagHeader) + payload_size;
276
277 // TODO(Subv): What does the 3DS do when a game has too much data to fit into the tag?
278 ASSERT_MSG(tag_length <= 255, "Data is too big.");
279
280 EncryptedDataTag tag{};
281 tag.header.tag_id = static_cast<u8>(TagId::VendorSpecific);
282 tag.header.length = static_cast<u8>(tag_length);
283 tag.oui_type = static_cast<u8>(NintendoTagId::EncryptedData1);
284 tag.oui = NintendoOUI;
285
286 std::vector<u8> buffer(sizeof(tag) + payload_size);
287 std::memcpy(buffer.data(), &tag, sizeof(tag));
288
289 std::vector<u8> encrypted_data = GeneratedEncryptedData(network_info, nodes);
290 std::memcpy(buffer.data() + sizeof(tag), encrypted_data.data() + EncryptedDataSizeCutoff,
291 payload_size);
292
293 return buffer;
294}
295
296/**
297 * Generates a buffer with the Nintendo tagged parameters of an 802.11 Beacon frame
298 * for UDS communication.
299 * @returns A buffer with the Nintendo tagged parameters of the beacon frame.
300 */
301std::vector<u8> GenerateNintendoTaggedParameters(const NetworkInfo& network_info,
302 const NodeList& nodes) {
303 ASSERT_MSG(network_info.max_nodes == nodes.size(), "Inconsistent network state.");
304
305 std::vector<u8> buffer = GenerateNintendoDummyTag();
306 std::vector<u8> network_info_tag = GenerateNintendoNetworkInfoTag(network_info);
307 std::vector<u8> first_data_tag = GenerateNintendoFirstEncryptedDataTag(network_info, nodes);
308 std::vector<u8> second_data_tag = GenerateNintendoSecondEncryptedDataTag(network_info, nodes);
309
310 buffer.insert(buffer.end(), network_info_tag.begin(), network_info_tag.end());
311 buffer.insert(buffer.end(), first_data_tag.begin(), first_data_tag.end());
312 buffer.insert(buffer.end(), second_data_tag.begin(), second_data_tag.end());
313
314 return buffer;
315}
316
317std::vector<u8> GenerateBeaconFrame(const NetworkInfo& network_info, const NodeList& nodes) {
318 std::vector<u8> buffer = GenerateFixedParameters();
319 std::vector<u8> basic_tags = GenerateBasicTaggedParameters();
320 std::vector<u8> nintendo_tags = GenerateNintendoTaggedParameters(network_info, nodes);
321
322 buffer.insert(buffer.end(), basic_tags.begin(), basic_tags.end());
323 buffer.insert(buffer.end(), nintendo_tags.begin(), nintendo_tags.end());
324
325 return buffer;
326}
327
328} // namespace NWM
329} // namespace Service
diff --git a/src/core/hle/service/nwm/uds_beacon.h b/src/core/hle/service/nwm/uds_beacon.h
deleted file mode 100644
index 50cc76da2..000000000
--- a/src/core/hle/service/nwm/uds_beacon.h
+++ /dev/null
@@ -1,140 +0,0 @@
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 <array>
8#include <deque>
9#include <vector>
10#include "common/common_types.h"
11#include "common/swap.h"
12#include "core/hle/service/service.h"
13
14namespace Service {
15namespace NWM {
16
17using MacAddress = std::array<u8, 6>;
18constexpr std::array<u8, 3> NintendoOUI = {0x00, 0x1F, 0x32};
19
20/**
21 * Internal vendor-specific tag ids as stored inside
22 * VendorSpecific blocks in the Beacon frames.
23 */
24enum class NintendoTagId : u8 {
25 Dummy = 20,
26 NetworkInfo = 21,
27 EncryptedData0 = 24,
28 EncryptedData1 = 25,
29};
30
31struct BeaconEntryHeader {
32 u32_le total_size;
33 INSERT_PADDING_BYTES(1);
34 u8 wifi_channel;
35 INSERT_PADDING_BYTES(2);
36 MacAddress mac_address;
37 INSERT_PADDING_BYTES(6);
38 u32_le unk_size;
39 u32_le header_size;
40};
41
42static_assert(sizeof(BeaconEntryHeader) == 0x1C, "BeaconEntryHeader has incorrect size.");
43
44struct BeaconDataReplyHeader {
45 u32_le max_output_size;
46 u32_le total_size;
47 u32_le total_entries;
48};
49
50static_assert(sizeof(BeaconDataReplyHeader) == 12, "BeaconDataReplyHeader has incorrect size.");
51
52#pragma pack(push, 1)
53struct BeaconFrameHeader {
54 // Number of microseconds the AP has been active.
55 u64_le timestamp;
56 // Interval between beacon transmissions, expressed in TU.
57 u16_le beacon_interval;
58 // Indicates the presence of optional capabilities.
59 u16_le capabilities;
60};
61#pragma pack(pop)
62
63static_assert(sizeof(BeaconFrameHeader) == 12, "BeaconFrameHeader has incorrect size.");
64
65struct TagHeader {
66 u8 tag_id;
67 u8 length;
68};
69
70static_assert(sizeof(TagHeader) == 2, "TagHeader has incorrect size.");
71
72struct DummyTag {
73 TagHeader header;
74 std::array<u8, 3> oui;
75 u8 oui_type;
76 std::array<u8, 3> data;
77};
78
79static_assert(sizeof(DummyTag) == 9, "DummyTag has incorrect size.");
80
81struct NetworkInfoTag {
82 TagHeader header;
83 std::array<u8, 0x1F> network_info;
84 std::array<u8, 0x14> sha_hash;
85 u8 appdata_size;
86};
87
88static_assert(sizeof(NetworkInfoTag) == 54, "NetworkInfoTag has incorrect size.");
89
90struct EncryptedDataTag {
91 TagHeader header;
92 std::array<u8, 3> oui;
93 u8 oui_type;
94};
95
96static_assert(sizeof(EncryptedDataTag) == 6, "EncryptedDataTag has incorrect size.");
97
98#pragma pack(push, 1)
99// The raw bytes of this structure are the CTR used in the encryption (AES-CTR)
100// of the beacon data stored in the EncryptedDataTags.
101struct BeaconDataCryptoCTR {
102 MacAddress host_mac;
103 u32_le wlan_comm_id;
104 u8 id;
105 INSERT_PADDING_BYTES(1);
106 u32_le network_id;
107};
108
109static_assert(sizeof(BeaconDataCryptoCTR) == 0x10, "BeaconDataCryptoCTR has incorrect size.");
110
111struct BeaconNodeInfo {
112 u64_be friend_code_seed;
113 std::array<u16_be, 10> username;
114 u16_be network_node_id;
115};
116
117static_assert(sizeof(BeaconNodeInfo) == 0x1E, "BeaconNodeInfo has incorrect size.");
118
119struct BeaconData {
120 std::array<u8, 0x10> md5_hash;
121 u16_be bitmask;
122};
123#pragma pack(pop)
124
125static_assert(sizeof(BeaconData) == 0x12, "BeaconData has incorrect size.");
126
127/**
128 * Decrypts the beacon data buffer for the network described by `network_info`.
129 */
130void DecryptBeaconData(const NetworkInfo& network_info, std::vector<u8>& buffer);
131
132/**
133 * Generates an 802.11 beacon frame starting at the management frame header.
134 * This frame contains information about the network and its connected clients.
135 * @returns The generated frame.
136 */
137std::vector<u8> GenerateBeaconFrame(const NetworkInfo& network_info, const NodeList& nodes);
138
139} // namespace NWM
140} // namespace Service
diff --git a/src/core/hle/service/nwm/uds_connection.cpp b/src/core/hle/service/nwm/uds_connection.cpp
deleted file mode 100644
index c74f51253..000000000
--- a/src/core/hle/service/nwm/uds_connection.cpp
+++ /dev/null
@@ -1,88 +0,0 @@
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 "core/hle/service/nwm/nwm_uds.h"
6#include "core/hle/service/nwm/uds_connection.h"
7#include "fmt/format.h"
8
9namespace Service {
10namespace NWM {
11
12// Note: These values were taken from a packet capture of an o3DS XL
13// broadcasting a Super Smash Bros. 4 lobby.
14constexpr u16 DefaultExtraCapabilities = 0x0431;
15
16std::vector<u8> GenerateAuthenticationFrame(AuthenticationSeq seq) {
17 AuthenticationFrame frame{};
18 frame.auth_seq = static_cast<u16>(seq);
19
20 std::vector<u8> data(sizeof(frame));
21 std::memcpy(data.data(), &frame, sizeof(frame));
22
23 return data;
24}
25
26AuthenticationSeq GetAuthenticationSeqNumber(const std::vector<u8>& body) {
27 AuthenticationFrame frame;
28 std::memcpy(&frame, body.data(), sizeof(frame));
29
30 return static_cast<AuthenticationSeq>(frame.auth_seq);
31}
32
33/**
34 * Generates an SSID tag of an 802.11 Beacon frame with an 8-byte character representation of the
35 * specified network id as the SSID value.
36 * @param network_id The network id to use.
37 * @returns A buffer with the SSID tag.
38 */
39static std::vector<u8> GenerateSSIDTag(u32 network_id) {
40 constexpr u8 SSIDSize = 8;
41
42 struct {
43 u8 id = static_cast<u8>(TagId::SSID);
44 u8 size = SSIDSize;
45 } tag_header;
46
47 std::vector<u8> buffer(sizeof(tag_header) + SSIDSize);
48
49 std::memcpy(buffer.data(), &tag_header, sizeof(tag_header));
50
51 std::string network_name = fmt::format("{0:08X}", network_id);
52
53 std::memcpy(buffer.data() + sizeof(tag_header), network_name.c_str(), SSIDSize);
54
55 return buffer;
56}
57
58std::vector<u8> GenerateAssocResponseFrame(AssocStatus status, u16 association_id, u32 network_id) {
59 AssociationResponseFrame frame{};
60 frame.capabilities = DefaultExtraCapabilities;
61 frame.status_code = static_cast<u16>(status);
62 // The association id is ORed with this magic value (0xC000)
63 constexpr u16 AssociationIdMagic = 0xC000;
64 frame.assoc_id = association_id | AssociationIdMagic;
65
66 std::vector<u8> data(sizeof(frame));
67 std::memcpy(data.data(), &frame, sizeof(frame));
68
69 auto ssid_tag = GenerateSSIDTag(network_id);
70 data.insert(data.end(), ssid_tag.begin(), ssid_tag.end());
71
72 // TODO(Subv): Add the SupportedRates tag.
73 // TODO(Subv): Add the DSParameterSet tag.
74 // TODO(Subv): Add the ERPInformation tag.
75 return data;
76}
77
78std::tuple<AssocStatus, u16> GetAssociationResult(const std::vector<u8>& body) {
79 AssociationResponseFrame frame;
80 memcpy(&frame, body.data(), sizeof(frame));
81
82 constexpr u16 AssociationIdMask = 0x3FFF;
83 return std::make_tuple(static_cast<AssocStatus>(frame.status_code),
84 frame.assoc_id & AssociationIdMask);
85}
86
87} // namespace NWM
88} // namespace Service
diff --git a/src/core/hle/service/nwm/uds_connection.h b/src/core/hle/service/nwm/uds_connection.h
deleted file mode 100644
index a664f8471..000000000
--- a/src/core/hle/service/nwm/uds_connection.h
+++ /dev/null
@@ -1,56 +0,0 @@
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 <tuple>
8#include <vector>
9#include "common/common_types.h"
10#include "common/swap.h"
11#include "core/hle/service/service.h"
12
13namespace Service {
14namespace NWM {
15
16/// Sequence number of the 802.11 authentication frames.
17enum class AuthenticationSeq : u16 { SEQ1 = 1, SEQ2 = 2 };
18
19enum class AuthAlgorithm : u16 { OpenSystem = 0 };
20
21enum class AuthStatus : u16 { Successful = 0 };
22
23enum class AssocStatus : u16 { Successful = 0 };
24
25struct AuthenticationFrame {
26 u16_le auth_algorithm = static_cast<u16>(AuthAlgorithm::OpenSystem);
27 u16_le auth_seq;
28 u16_le status_code = static_cast<u16>(AuthStatus::Successful);
29};
30
31static_assert(sizeof(AuthenticationFrame) == 6, "AuthenticationFrame has wrong size");
32
33struct AssociationResponseFrame {
34 u16_le capabilities;
35 u16_le status_code;
36 u16_le assoc_id;
37};
38
39static_assert(sizeof(AssociationResponseFrame) == 6, "AssociationResponseFrame has wrong size");
40
41/// Generates an 802.11 authentication frame, starting at the frame body.
42std::vector<u8> GenerateAuthenticationFrame(AuthenticationSeq seq);
43
44/// Returns the sequence number from the body of an Authentication frame.
45AuthenticationSeq GetAuthenticationSeqNumber(const std::vector<u8>& body);
46
47/// Generates an 802.11 association response frame with the specified status, association id and
48/// network id, starting at the frame body.
49std::vector<u8> GenerateAssocResponseFrame(AssocStatus status, u16 association_id, u32 network_id);
50
51/// Returns a tuple of (association status, association id) from the body of an AssociationResponse
52/// frame.
53std::tuple<AssocStatus, u16> GetAssociationResult(const std::vector<u8>& body);
54
55} // namespace NWM
56} // namespace Service
diff --git a/src/core/hle/service/nwm/uds_data.cpp b/src/core/hle/service/nwm/uds_data.cpp
deleted file mode 100644
index 4b389710f..000000000
--- a/src/core/hle/service/nwm/uds_data.cpp
+++ /dev/null
@@ -1,373 +0,0 @@
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 <algorithm>
6#include <cstring>
7#include <cryptopp/aes.h>
8#include <cryptopp/ccm.h>
9#include <cryptopp/filters.h>
10#include <cryptopp/md5.h>
11#include <cryptopp/modes.h>
12#include "core/hle/service/nwm/nwm_uds.h"
13#include "core/hle/service/nwm/uds_data.h"
14#include "core/hw/aes/key.h"
15
16namespace Service {
17namespace NWM {
18
19using MacAddress = std::array<u8, 6>;
20
21/*
22 * Generates a SNAP-enabled 802.2 LLC header for the specified protocol.
23 * @returns a buffer with the bytes of the generated header.
24 */
25static std::vector<u8> GenerateLLCHeader(EtherType protocol) {
26 LLCHeader header{};
27 header.protocol = static_cast<u16>(protocol);
28
29 std::vector<u8> buffer(sizeof(header));
30 memcpy(buffer.data(), &header, sizeof(header));
31
32 return buffer;
33}
34
35/*
36 * Generates a Nintendo UDS SecureData header with the specified parameters.
37 * @returns a buffer with the bytes of the generated header.
38 */
39static std::vector<u8> GenerateSecureDataHeader(u16 data_size, u8 channel, u16 dest_node_id,
40 u16 src_node_id, u16 sequence_number) {
41 SecureDataHeader header{};
42 header.protocol_size = data_size + sizeof(SecureDataHeader);
43 // Note: This size includes everything except the first 4 bytes of the structure,
44 // reinforcing the hypotheses that the first 4 bytes are actually the header of
45 // another container protocol.
46 header.securedata_size = data_size + sizeof(SecureDataHeader) - 4;
47 // Frames sent by the emulated application are never UDS management frames
48 header.is_management = 0;
49 header.data_channel = channel;
50 header.sequence_number = sequence_number;
51 header.dest_node_id = dest_node_id;
52 header.src_node_id = src_node_id;
53
54 std::vector<u8> buffer(sizeof(header));
55 memcpy(buffer.data(), &header, sizeof(header));
56
57 return buffer;
58}
59
60/*
61 * Calculates the CTR used for the AES-CTR process that calculates
62 * the CCMP crypto key for data frames.
63 * @returns The CTR used for data frames crypto key generation.
64 */
65static std::array<u8, CryptoPP::MD5::DIGESTSIZE> GetDataCryptoCTR(const NetworkInfo& network_info) {
66 DataFrameCryptoCTR data{};
67
68 data.host_mac = network_info.host_mac_address;
69 data.wlan_comm_id = network_info.wlan_comm_id;
70 data.id = network_info.id;
71 data.network_id = network_info.network_id;
72
73 std::array<u8, CryptoPP::MD5::DIGESTSIZE> hash;
74 CryptoPP::MD5().CalculateDigest(hash.data(), reinterpret_cast<u8*>(&data), sizeof(data));
75
76 return hash;
77}
78
79/*
80 * Generates the key used for encrypting the 802.11 data frames generated by UDS.
81 * @returns The key used for data frames crypto.
82 */
83static std::array<u8, CryptoPP::AES::BLOCKSIZE> GenerateDataCCMPKey(
84 const std::vector<u8>& passphrase, const NetworkInfo& network_info) {
85 // Calculate the MD5 hash of the input passphrase.
86 std::array<u8, CryptoPP::MD5::DIGESTSIZE> passphrase_hash;
87 CryptoPP::MD5().CalculateDigest(passphrase_hash.data(), passphrase.data(), passphrase.size());
88
89 std::array<u8, CryptoPP::AES::BLOCKSIZE> ccmp_key;
90
91 // The CCMP key is the result of encrypting the MD5 hash of the passphrase with AES-CTR using
92 // keyslot 0x2D.
93 using CryptoPP::AES;
94 std::array<u8, CryptoPP::MD5::DIGESTSIZE> counter = GetDataCryptoCTR(network_info);
95 std::array<u8, AES::BLOCKSIZE> key = HW::AES::GetNormalKey(HW::AES::KeySlotID::UDSDataKey);
96 CryptoPP::CTR_Mode<AES>::Encryption aes;
97 aes.SetKeyWithIV(key.data(), AES::BLOCKSIZE, counter.data());
98 aes.ProcessData(ccmp_key.data(), passphrase_hash.data(), passphrase_hash.size());
99
100 return ccmp_key;
101}
102
103/*
104 * Generates the Additional Authenticated Data (AAD) for an UDS 802.11 encrypted data frame.
105 * @returns a buffer with the bytes of the AAD.
106 */
107static std::vector<u8> GenerateCCMPAAD(const MacAddress& sender, const MacAddress& receiver,
108 const MacAddress& bssid, u16 frame_control) {
109 // Reference: IEEE 802.11-2007
110
111 // 8.3.3.3.2 Construct AAD (22-30 bytes)
112 // The AAD is constructed from the MPDU header. The AAD does not include the header Duration
113 // field, because the Duration field value can change due to normal IEEE 802.11 operation (e.g.,
114 // a rate change during retransmission). For similar reasons, several subfields in the Frame
115 // Control field are masked to 0.
116 struct {
117 u16_be FC; // MPDU Frame Control field
118 MacAddress A1;
119 MacAddress A2;
120 MacAddress A3;
121 u16_be SC; // MPDU Sequence Control field
122 } aad_struct{};
123
124 constexpr u16 AADFrameControlMask = 0x8FC7;
125 aad_struct.FC = frame_control & AADFrameControlMask;
126 aad_struct.SC = 0;
127
128 bool to_ds = (frame_control & (1 << 0)) != 0;
129 bool from_ds = (frame_control & (1 << 1)) != 0;
130 // In the 802.11 standard, ToDS = 1 and FromDS = 1 is a valid configuration,
131 // however, the 3DS doesn't seem to transmit frames with such combination.
132 ASSERT_MSG(to_ds != from_ds, "Invalid combination");
133
134 // The meaning of the address fields depends on the ToDS and FromDS fields.
135 if (from_ds) {
136 aad_struct.A1 = receiver;
137 aad_struct.A2 = bssid;
138 aad_struct.A3 = sender;
139 }
140
141 if (to_ds) {
142 aad_struct.A1 = bssid;
143 aad_struct.A2 = sender;
144 aad_struct.A3 = receiver;
145 }
146
147 std::vector<u8> aad(sizeof(aad_struct));
148 std::memcpy(aad.data(), &aad_struct, sizeof(aad_struct));
149
150 return aad;
151}
152
153/*
154 * Decrypts the payload of an encrypted 802.11 data frame using the specified key.
155 * @returns The decrypted payload.
156 */
157static std::vector<u8> DecryptDataFrame(const std::vector<u8>& encrypted_payload,
158 const std::array<u8, CryptoPP::AES::BLOCKSIZE>& ccmp_key,
159 const MacAddress& sender, const MacAddress& receiver,
160 const MacAddress& bssid, u16 sequence_number,
161 u16 frame_control) {
162
163 // Reference: IEEE 802.11-2007
164
165 std::vector<u8> aad = GenerateCCMPAAD(sender, receiver, bssid, frame_control);
166
167 std::vector<u8> packet_number{0,
168 0,
169 0,
170 0,
171 static_cast<u8>((sequence_number >> 8) & 0xFF),
172 static_cast<u8>(sequence_number & 0xFF)};
173
174 // 8.3.3.3.3 Construct CCM nonce (13 bytes)
175 std::vector<u8> nonce;
176 nonce.push_back(0); // priority
177 nonce.insert(nonce.end(), sender.begin(), sender.end()); // Address 2
178 nonce.insert(nonce.end(), packet_number.begin(), packet_number.end()); // PN
179
180 try {
181 CryptoPP::CCM<CryptoPP::AES, 8>::Decryption d;
182 d.SetKeyWithIV(ccmp_key.data(), ccmp_key.size(), nonce.data(), nonce.size());
183 d.SpecifyDataLengths(aad.size(), encrypted_payload.size() - 8, 0);
184
185 CryptoPP::AuthenticatedDecryptionFilter df(
186 d, nullptr, CryptoPP::AuthenticatedDecryptionFilter::MAC_AT_END |
187 CryptoPP::AuthenticatedDecryptionFilter::THROW_EXCEPTION);
188 // put aad
189 df.ChannelPut(CryptoPP::AAD_CHANNEL, aad.data(), aad.size());
190
191 // put cipher with mac
192 df.ChannelPut(CryptoPP::DEFAULT_CHANNEL, encrypted_payload.data(),
193 encrypted_payload.size() - 8);
194 df.ChannelPut(CryptoPP::DEFAULT_CHANNEL,
195 encrypted_payload.data() + encrypted_payload.size() - 8, 8);
196
197 df.ChannelMessageEnd(CryptoPP::AAD_CHANNEL);
198 df.ChannelMessageEnd(CryptoPP::DEFAULT_CHANNEL);
199 df.SetRetrievalChannel(CryptoPP::DEFAULT_CHANNEL);
200
201 size_t size = df.MaxRetrievable();
202
203 std::vector<u8> pdata(size);
204 df.Get(pdata.data(), size);
205 return pdata;
206 } catch (CryptoPP::Exception&) {
207 LOG_ERROR(Service_NWM, "failed to decrypt");
208 }
209
210 return {};
211}
212
213/*
214 * Encrypts the payload of an 802.11 data frame using the specified key.
215 * @returns The encrypted payload.
216 */
217static std::vector<u8> EncryptDataFrame(const std::vector<u8>& payload,
218 const std::array<u8, CryptoPP::AES::BLOCKSIZE>& ccmp_key,
219 const MacAddress& sender, const MacAddress& receiver,
220 const MacAddress& bssid, u16 sequence_number,
221 u16 frame_control) {
222 // Reference: IEEE 802.11-2007
223
224 std::vector<u8> aad = GenerateCCMPAAD(sender, receiver, bssid, frame_control);
225
226 std::vector<u8> packet_number{0,
227 0,
228 0,
229 0,
230 static_cast<u8>((sequence_number >> 8) & 0xFF),
231 static_cast<u8>(sequence_number & 0xFF)};
232
233 // 8.3.3.3.3 Construct CCM nonce (13 bytes)
234 std::vector<u8> nonce;
235 nonce.push_back(0); // priority
236 nonce.insert(nonce.end(), sender.begin(), sender.end()); // Address 2
237 nonce.insert(nonce.end(), packet_number.begin(), packet_number.end()); // PN
238
239 try {
240 CryptoPP::CCM<CryptoPP::AES, 8>::Encryption d;
241 d.SetKeyWithIV(ccmp_key.data(), ccmp_key.size(), nonce.data(), nonce.size());
242 d.SpecifyDataLengths(aad.size(), payload.size(), 0);
243
244 CryptoPP::AuthenticatedEncryptionFilter df(d);
245 // put aad
246 df.ChannelPut(CryptoPP::AAD_CHANNEL, aad.data(), aad.size());
247 df.ChannelMessageEnd(CryptoPP::AAD_CHANNEL);
248
249 // put plaintext
250 df.ChannelPut(CryptoPP::DEFAULT_CHANNEL, payload.data(), payload.size());
251 df.ChannelMessageEnd(CryptoPP::DEFAULT_CHANNEL);
252
253 df.SetRetrievalChannel(CryptoPP::DEFAULT_CHANNEL);
254
255 size_t size = df.MaxRetrievable();
256
257 std::vector<u8> cipher(size);
258 df.Get(cipher.data(), size);
259 return cipher;
260 } catch (CryptoPP::Exception&) {
261 LOG_ERROR(Service_NWM, "failed to encrypt");
262 }
263
264 return {};
265}
266
267std::vector<u8> GenerateDataPayload(const std::vector<u8>& data, u8 channel, u16 dest_node,
268 u16 src_node, u16 sequence_number) {
269 std::vector<u8> buffer = GenerateLLCHeader(EtherType::SecureData);
270 std::vector<u8> securedata_header = GenerateSecureDataHeader(
271 static_cast<u16>(data.size()), channel, dest_node, src_node, sequence_number);
272
273 buffer.insert(buffer.end(), securedata_header.begin(), securedata_header.end());
274 buffer.insert(buffer.end(), data.begin(), data.end());
275 return buffer;
276}
277
278std::vector<u8> GenerateEAPoLStartFrame(u16 association_id, const NodeInfo& node_info) {
279 EAPoLStartPacket eapol_start{};
280 eapol_start.association_id = association_id;
281 eapol_start.node.friend_code_seed = node_info.friend_code_seed;
282
283 std::copy(node_info.username.begin(), node_info.username.end(),
284 eapol_start.node.username.begin());
285
286 // Note: The network_node_id and unknown bytes seem to be uninitialized in the NWM module.
287 // TODO(B3N30): The last 8 bytes seem to have a fixed value of 07 88 15 00 04 e9 13 00 in
288 // EAPoL-Start packets from different 3DSs to the same host during a Super Smash Bros. 4 game.
289 // Find out what that means.
290
291 std::vector<u8> eapol_buffer(sizeof(EAPoLStartPacket));
292 std::memcpy(eapol_buffer.data(), &eapol_start, sizeof(eapol_start));
293
294 std::vector<u8> buffer = GenerateLLCHeader(EtherType::EAPoL);
295 buffer.insert(buffer.end(), eapol_buffer.begin(), eapol_buffer.end());
296 return buffer;
297}
298
299EtherType GetFrameEtherType(const std::vector<u8>& frame) {
300 LLCHeader header;
301 std::memcpy(&header, frame.data(), sizeof(header));
302
303 u16 ethertype = header.protocol;
304 return static_cast<EtherType>(ethertype);
305}
306
307u16 GetEAPoLFrameType(const std::vector<u8>& frame) {
308 // Ignore the LLC header
309 u16_be eapol_type;
310 std::memcpy(&eapol_type, frame.data() + sizeof(LLCHeader), sizeof(eapol_type));
311 return eapol_type;
312}
313
314NodeInfo DeserializeNodeInfoFromFrame(const std::vector<u8>& frame) {
315 EAPoLStartPacket eapol_start;
316
317 // Skip the LLC header
318 std::memcpy(&eapol_start, frame.data() + sizeof(LLCHeader), sizeof(eapol_start));
319
320 NodeInfo node{};
321 node.friend_code_seed = eapol_start.node.friend_code_seed;
322
323 std::copy(eapol_start.node.username.begin(), eapol_start.node.username.end(),
324 node.username.begin());
325
326 return node;
327}
328
329NodeInfo DeserializeNodeInfo(const EAPoLNodeInfo& node) {
330 NodeInfo node_info{};
331 node_info.friend_code_seed = node.friend_code_seed;
332 node_info.network_node_id = node.network_node_id;
333
334 std::copy(node.username.begin(), node.username.end(), node_info.username.begin());
335
336 return node_info;
337}
338
339std::vector<u8> GenerateEAPoLLogoffFrame(const MacAddress& mac_address, u16 network_node_id,
340 const NodeList& nodes, u8 max_nodes, u8 total_nodes) {
341 EAPoLLogoffPacket eapol_logoff{};
342 eapol_logoff.assigned_node_id = network_node_id;
343 eapol_logoff.connected_nodes = total_nodes;
344 eapol_logoff.max_nodes = max_nodes;
345
346 for (size_t index = 0; index < total_nodes; ++index) {
347 const auto& node_info = nodes[index];
348 auto& node = eapol_logoff.nodes[index];
349
350 node.friend_code_seed = node_info.friend_code_seed;
351 node.network_node_id = node_info.network_node_id;
352
353 std::copy(node_info.username.begin(), node_info.username.end(), node.username.begin());
354 }
355
356 std::vector<u8> eapol_buffer(sizeof(EAPoLLogoffPacket));
357 std::memcpy(eapol_buffer.data(), &eapol_logoff, sizeof(eapol_logoff));
358
359 std::vector<u8> buffer = GenerateLLCHeader(EtherType::EAPoL);
360 buffer.insert(buffer.end(), eapol_buffer.begin(), eapol_buffer.end());
361 return buffer;
362}
363
364EAPoLLogoffPacket ParseEAPoLLogoffFrame(const std::vector<u8>& frame) {
365 EAPoLLogoffPacket eapol_logoff;
366
367 // Skip the LLC header
368 std::memcpy(&eapol_logoff, frame.data() + sizeof(LLCHeader), sizeof(eapol_logoff));
369 return eapol_logoff;
370}
371
372} // namespace NWM
373} // namespace Service
diff --git a/src/core/hle/service/nwm/uds_data.h b/src/core/hle/service/nwm/uds_data.h
deleted file mode 100644
index 76bccb1bf..000000000
--- a/src/core/hle/service/nwm/uds_data.h
+++ /dev/null
@@ -1,164 +0,0 @@
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 <array>
8#include <vector>
9#include "common/common_types.h"
10#include "common/swap.h"
11#include "core/hle/service/nwm/uds_beacon.h"
12#include "core/hle/service/service.h"
13
14namespace Service {
15namespace NWM {
16
17enum class SAP : u8 { SNAPExtensionUsed = 0xAA };
18
19enum class PDUControl : u8 { UnnumberedInformation = 3 };
20
21enum class EtherType : u16 { SecureData = 0x876D, EAPoL = 0x888E };
22
23/*
24 * 802.2 header, UDS packets always use SNAP for these headers,
25 * which means the dsap and ssap are always SNAPExtensionUsed (0xAA)
26 * and the OUI is always 0.
27 */
28struct LLCHeader {
29 u8 dsap = static_cast<u8>(SAP::SNAPExtensionUsed);
30 u8 ssap = static_cast<u8>(SAP::SNAPExtensionUsed);
31 u8 control = static_cast<u8>(PDUControl::UnnumberedInformation);
32 std::array<u8, 3> OUI = {};
33 u16_be protocol;
34};
35
36static_assert(sizeof(LLCHeader) == 8, "LLCHeader has the wrong size");
37
38/*
39 * Nintendo SecureData header, every UDS packet contains one,
40 * it is used to store metadata about the transmission such as
41 * the source and destination network node ids.
42 */
43struct SecureDataHeader {
44 // TODO(Subv): It is likely that the first 4 bytes of this header are
45 // actually part of another container protocol.
46 u16_be protocol_size;
47 INSERT_PADDING_BYTES(2);
48 u16_be securedata_size;
49 u8 is_management;
50 u8 data_channel;
51 u16_be sequence_number;
52 u16_be dest_node_id;
53 u16_be src_node_id;
54};
55
56static_assert(sizeof(SecureDataHeader) == 14, "SecureDataHeader has the wrong size");
57
58/*
59 * The raw bytes of this structure are the CTR used in the encryption (AES-CTR)
60 * process used to generate the CCMP key for data frame encryption.
61 */
62struct DataFrameCryptoCTR {
63 u32_le wlan_comm_id;
64 u32_le network_id;
65 std::array<u8, 6> host_mac;
66 u16_le id;
67};
68
69static_assert(sizeof(DataFrameCryptoCTR) == 16, "DataFrameCryptoCTR has the wrong size");
70
71struct EAPoLNodeInfo {
72 u64_be friend_code_seed;
73 std::array<u16_be, 10> username;
74 INSERT_PADDING_BYTES(4);
75 u16_be network_node_id;
76 INSERT_PADDING_BYTES(6);
77};
78
79static_assert(sizeof(EAPoLNodeInfo) == 0x28, "EAPoLNodeInfo has the wrong size");
80
81constexpr u16 EAPoLStartMagic = 0x201;
82
83/*
84 * Nintendo EAPoLStartPacket, is used to initaliaze a connection between client and host
85 */
86struct EAPoLStartPacket {
87 u16_be magic = EAPoLStartMagic;
88 u16_be association_id;
89 // This value is hardcoded to 1 in the NWM module.
90 u16_be unknown = 1;
91 INSERT_PADDING_BYTES(2);
92 EAPoLNodeInfo node;
93};
94
95static_assert(sizeof(EAPoLStartPacket) == 0x30, "EAPoLStartPacket has the wrong size");
96
97constexpr u16 EAPoLLogoffMagic = 0x202;
98
99struct EAPoLLogoffPacket {
100 u16_be magic = EAPoLLogoffMagic;
101 INSERT_PADDING_BYTES(2);
102 u16_be assigned_node_id;
103 MacAddress client_mac_address;
104 INSERT_PADDING_BYTES(6);
105 u8 connected_nodes;
106 u8 max_nodes;
107 INSERT_PADDING_BYTES(4);
108
109 std::array<EAPoLNodeInfo, UDSMaxNodes> nodes;
110};
111
112static_assert(sizeof(EAPoLLogoffPacket) == 0x298, "EAPoLLogoffPacket has the wrong size");
113
114/**
115 * Generates an unencrypted 802.11 data payload.
116 * @returns The generated frame payload.
117 */
118std::vector<u8> GenerateDataPayload(const std::vector<u8>& data, u8 channel, u16 dest_node,
119 u16 src_node, u16 sequence_number);
120
121/*
122 * Generates an unencrypted 802.11 data frame body with the EAPoL-Start format for UDS
123 * communication.
124 * @returns The generated frame body.
125 */
126std::vector<u8> GenerateEAPoLStartFrame(u16 association_id, const NodeInfo& node_info);
127
128/*
129 * Returns the EtherType of the specified 802.11 frame.
130 */
131EtherType GetFrameEtherType(const std::vector<u8>& frame);
132
133/*
134 * Returns the EAPoL type (Start / Logoff) of the specified 802.11 frame.
135 * Note: The frame *must* be an EAPoL frame.
136 */
137u16 GetEAPoLFrameType(const std::vector<u8>& frame);
138
139/*
140 * Returns a deserialized NodeInfo structure from the information inside an EAPoL-Start packet
141 * encapsulated in an 802.11 data frame.
142 */
143NodeInfo DeserializeNodeInfoFromFrame(const std::vector<u8>& frame);
144
145/*
146 * Returns a NodeInfo constructed from the data in the specified EAPoLNodeInfo.
147 */
148NodeInfo DeserializeNodeInfo(const EAPoLNodeInfo& node);
149
150/*
151 * Generates an unencrypted 802.11 data frame body with the EAPoL-Logoff format for UDS
152 * communication.
153 * @returns The generated frame body.
154 */
155std::vector<u8> GenerateEAPoLLogoffFrame(const MacAddress& mac_address, u16 network_node_id,
156 const NodeList& nodes, u8 max_nodes, u8 total_nodes);
157
158/*
159 * Returns a EAPoLLogoffPacket representing the specified 802.11-encapsulated data frame.
160 */
161EAPoLLogoffPacket ParseEAPoLLogoffFrame(const std::vector<u8>& frame);
162
163} // namespace NWM
164} // namespace Service
diff --git a/src/core/hle/service/pm_app.cpp b/src/core/hle/service/pm_app.cpp
deleted file mode 100644
index caa16f952..000000000
--- a/src/core/hle/service/pm_app.cpp
+++ /dev/null
@@ -1,33 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/pm_app.h"
6
7namespace Service {
8namespace PM {
9
10const Interface::FunctionInfo FunctionTable[] = {
11 // clang-format off
12 {0x00010140, nullptr, "LaunchTitle"},
13 {0x00020082, nullptr, "LaunchFIRM"},
14 {0x00030080, nullptr, "TerminateApplication"},
15 {0x00040100, nullptr, "TerminateTitle"},
16 {0x000500C0, nullptr, "TerminateProcess"},
17 {0x00060082, nullptr, "PrepareForReboot"},
18 {0x00070042, nullptr, "GetFIRMLaunchParams"},
19 {0x00080100, nullptr, "GetTitleExheaderFlags"},
20 {0x00090042, nullptr, "SetFIRMLaunchParams"},
21 {0x000A0140, nullptr, "SetAppResourceLimit"},
22 {0x000B0140, nullptr, "GetAppResourceLimit"},
23 {0x000C0080, nullptr, "UnregisterProcess"},
24 {0x000D0240, nullptr, "LaunchTitleUpdate"},
25 // clang-format on
26};
27
28PM_APP::PM_APP() {
29 Register(FunctionTable);
30}
31
32} // namespace PM
33} // namespace Service
diff --git a/src/core/hle/service/pm_app.h b/src/core/hle/service/pm_app.h
deleted file mode 100644
index 151c69f3d..000000000
--- a/src/core/hle/service/pm_app.h
+++ /dev/null
@@ -1,22 +0,0 @@
1// Copyright 2014 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 "core/hle/service/service.h"
8
9namespace Service {
10namespace PM {
11
12class PM_APP final : public Interface {
13public:
14 PM_APP();
15
16 std::string GetPortName() const override {
17 return "pm:app";
18 }
19};
20
21} // namespace PM
22} // namespace Service
diff --git a/src/core/hle/service/ptm/ptm.cpp b/src/core/hle/service/ptm/ptm.cpp
deleted file mode 100644
index a0b959797..000000000
--- a/src/core/hle/service/ptm/ptm.cpp
+++ /dev/null
@@ -1,166 +0,0 @@
1// Copyright 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/logging/log.h"
6#include "core/file_sys/errors.h"
7#include "core/file_sys/file_backend.h"
8#include "core/hle/service/fs/archive.h"
9#include "core/hle/service/ptm/ptm.h"
10#include "core/hle/service/ptm/ptm_gets.h"
11#include "core/hle/service/ptm/ptm_play.h"
12#include "core/hle/service/ptm/ptm_sets.h"
13#include "core/hle/service/ptm/ptm_sysm.h"
14#include "core/hle/service/ptm/ptm_u.h"
15#include "core/hle/service/service.h"
16#include "core/settings.h"
17
18namespace Service {
19namespace PTM {
20
21/// Values for the default gamecoin.dat file
22static const GameCoin default_game_coin = {0x4F00, 42, 0, 0, 0, 2014, 12, 29};
23
24/// Id of the SharedExtData archive used by the PTM process
25static const std::vector<u8> ptm_shared_extdata_id = {0, 0, 0, 0, 0x0B, 0, 0, 0xF0, 0, 0, 0, 0};
26
27static bool shell_open;
28
29static bool battery_is_charging;
30
31static bool pedometer_is_counting;
32
33void GetAdapterState(Service::Interface* self) {
34 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x5, 0, 0);
35
36 IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
37 rb.Push(RESULT_SUCCESS);
38 rb.Push(battery_is_charging);
39
40 LOG_WARNING(Service_PTM, "(STUBBED) called");
41}
42
43void GetShellState(Service::Interface* self) {
44 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x6, 0, 0);
45
46 IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
47 rb.Push(RESULT_SUCCESS);
48 rb.Push(shell_open);
49}
50
51void GetBatteryLevel(Service::Interface* self) {
52 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x7, 0, 0);
53
54 IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
55 rb.Push(RESULT_SUCCESS);
56 rb.Push(static_cast<u32>(ChargeLevels::CompletelyFull)); // Set to a completely full battery
57
58 LOG_WARNING(Service_PTM, "(STUBBED) called");
59}
60
61void GetBatteryChargeState(Service::Interface* self) {
62 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x8, 0, 0);
63
64 IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
65 rb.Push(RESULT_SUCCESS);
66 rb.Push(battery_is_charging);
67
68 LOG_WARNING(Service_PTM, "(STUBBED) called");
69}
70
71void GetPedometerState(Service::Interface* self) {
72 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x9, 0, 0);
73
74 IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
75 rb.Push(RESULT_SUCCESS);
76 rb.Push(pedometer_is_counting);
77
78 LOG_WARNING(Service_PTM, "(STUBBED) called");
79}
80
81void GetTotalStepCount(Service::Interface* self) {
82 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0xC, 0, 0);
83
84 IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
85 rb.Push(RESULT_SUCCESS);
86 rb.Push<u32>(0);
87
88 LOG_WARNING(Service_PTM, "(STUBBED) called");
89}
90
91void GetSoftwareClosedFlag(Service::Interface* self) {
92 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x80F, 0, 0);
93
94 IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
95 rb.Push(RESULT_SUCCESS);
96 rb.Push(false);
97
98 LOG_WARNING(Service_PTM, "(STUBBED) called");
99}
100
101void CheckNew3DS(IPC::RequestBuilder& rb) {
102 const bool is_new_3ds = Settings::values.is_new_3ds;
103
104 if (is_new_3ds) {
105 LOG_CRITICAL(Service_PTM, "The option 'is_new_3ds' is enabled as part of the 'System' "
106 "settings. Citra does not fully support New 3DS emulation yet!");
107 }
108
109 rb.Push(RESULT_SUCCESS);
110 rb.Push(is_new_3ds);
111
112 LOG_WARNING(Service_PTM, "(STUBBED) called isNew3DS = 0x%08x", static_cast<u32>(is_new_3ds));
113}
114
115void CheckNew3DS(Service::Interface* self) {
116 IPC::RequestBuilder rb(Kernel::GetCommandBuffer(), 0x40A, 0, 0); // 0x040A0000
117 CheckNew3DS(rb);
118}
119
120void Init() {
121 AddService(new PTM_Gets);
122 AddService(new PTM_Play);
123 AddService(new PTM_S);
124 AddService(new PTM_Sets);
125 AddService(new PTM_Sysm);
126 AddService(new PTM_U);
127
128 shell_open = true;
129 battery_is_charging = true;
130 pedometer_is_counting = false;
131
132 // Open the SharedExtSaveData archive 0xF000000B and create the gamecoin.dat file if it doesn't
133 // exist
134 FileSys::Path archive_path(ptm_shared_extdata_id);
135 auto archive_result =
136 Service::FS::OpenArchive(Service::FS::ArchiveIdCode::SharedExtSaveData, archive_path);
137 // If the archive didn't exist, create the files inside
138 if (archive_result.Code() == FileSys::ERR_NOT_FORMATTED) {
139 // Format the archive to create the directories
140 Service::FS::FormatArchive(Service::FS::ArchiveIdCode::SharedExtSaveData,
141 FileSys::ArchiveFormatInfo(), archive_path);
142 // Open it again to get a valid archive now that the folder exists
143 archive_result =
144 Service::FS::OpenArchive(Service::FS::ArchiveIdCode::SharedExtSaveData, archive_path);
145 ASSERT_MSG(archive_result.Succeeded(), "Could not open the PTM SharedExtSaveData archive!");
146
147 FileSys::Path gamecoin_path("/gamecoin.dat");
148 Service::FS::CreateFileInArchive(*archive_result, gamecoin_path, sizeof(GameCoin));
149 FileSys::Mode open_mode = {};
150 open_mode.write_flag.Assign(1);
151 // Open the file and write the default gamecoin information
152 auto gamecoin_result =
153 Service::FS::OpenFileFromArchive(*archive_result, gamecoin_path, open_mode);
154 if (gamecoin_result.Succeeded()) {
155 auto gamecoin = std::move(gamecoin_result).Unwrap();
156 gamecoin->backend->Write(0, sizeof(GameCoin), true,
157 reinterpret_cast<const u8*>(&default_game_coin));
158 gamecoin->backend->Close();
159 }
160 }
161}
162
163void Shutdown() {}
164
165} // namespace PTM
166} // namespace Service
diff --git a/src/core/hle/service/ptm/ptm.h b/src/core/hle/service/ptm/ptm.h
deleted file mode 100644
index e17e59835..000000000
--- a/src/core/hle/service/ptm/ptm.h
+++ /dev/null
@@ -1,118 +0,0 @@
1// Copyright 2015 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 "common/common_types.h"
8#include "core/hle/ipc_helpers.h"
9
10namespace Service {
11
12class Interface;
13
14namespace PTM {
15
16/// Charge levels used by PTM functions
17enum class ChargeLevels : u32 {
18 CriticalBattery = 1,
19 LowBattery = 2,
20 HalfFull = 3,
21 MostlyFull = 4,
22 CompletelyFull = 5,
23};
24
25/**
26 * Represents the gamecoin file structure in the SharedExtData archive
27 * More information in 3dbrew
28 * (http://www.3dbrew.org/wiki/Extdata#Shared_Extdata_0xf000000b_gamecoin.dat)
29 */
30struct GameCoin {
31 u32 magic; ///< Magic number: 0x4F00
32 u16 total_coins; ///< Total Play Coins
33 u16 total_coins_on_date; ///< Total Play Coins obtained on the date stored below.
34 u32 step_count; ///< Total step count at the time a new Play Coin was obtained.
35 u32 last_step_count; ///< Step count for the day the last Play Coin was obtained
36 u16 year;
37 u8 month;
38 u8 day;
39};
40
41/**
42 * It is unknown if GetAdapterState is the same as GetBatteryChargeState,
43 * it is likely to just be a duplicate function of GetBatteryChargeState
44 * that controls another part of the HW.
45 * PTM::GetAdapterState service function
46 * Outputs:
47 * 1 : Result of function, 0 on success, otherwise error code
48 * 2 : Output of function, 0 = not charging, 1 = charging.
49 */
50void GetAdapterState(Interface* self);
51
52/**
53 * PTM::GetShellState service function.
54 * Outputs:
55 * 1 : Result of function, 0 on success, otherwise error code
56 * 2 : Whether the 3DS's physical shell casing is open (1) or closed (0)
57 */
58void GetShellState(Interface* self);
59
60/**
61 * PTM::GetBatteryLevel service function
62 * Outputs:
63 * 1 : Result of function, 0 on success, otherwise error code
64 * 2 : Battery level, 5 = completely full battery, 4 = mostly full battery,
65 * 3 = half full battery, 2 = low battery, 1 = critical battery.
66 */
67void GetBatteryLevel(Interface* self);
68
69/**
70 * PTM::GetBatteryChargeState service function
71 * Outputs:
72 * 1 : Result of function, 0 on success, otherwise error code
73 * 2 : Output of function, 0 = not charging, 1 = charging.
74 */
75void GetBatteryChargeState(Interface* self);
76
77/**
78 * PTM::GetPedometerState service function
79 * Outputs:
80 * 1 : Result of function, 0 on success, otherwise error code
81 * 2 : Output of function, 0 = not counting steps, 1 = counting steps.
82 */
83void GetPedometerState(Interface* self);
84
85/**
86 * PTM::GetTotalStepCount service function
87 * Outputs:
88 * 1 : Result of function, 0 on success, otherwise error code
89 * 2 : Output of function, * = total step count
90 */
91void GetTotalStepCount(Interface* self);
92
93/**
94 * PTM::GetSoftwareClosedFlag service function
95 * Outputs:
96 * 1: Result code, 0 on success, otherwise error code
97 * 2: Whether or not the "software closed" dialog was requested by the last FIRM
98 * and should be displayed.
99 */
100void GetSoftwareClosedFlag(Interface* self);
101
102/**
103 * PTM::CheckNew3DS service function
104 * Outputs:
105 * 1: Result code, 0 on success, otherwise error code
106 * 2: u8 output: 0 = Old3DS, 1 = New3DS.
107 */
108void CheckNew3DS(Interface* self);
109void CheckNew3DS(IPC::RequestBuilder& rb);
110
111/// Initialize the PTM service
112void Init();
113
114/// Shutdown the PTM service
115void Shutdown();
116
117} // namespace PTM
118} // namespace Service
diff --git a/src/core/hle/service/ptm/ptm_gets.cpp b/src/core/hle/service/ptm/ptm_gets.cpp
deleted file mode 100644
index b23e508d6..000000000
--- a/src/core/hle/service/ptm/ptm_gets.cpp
+++ /dev/null
@@ -1,37 +0,0 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/ptm/ptm.h"
6#include "core/hle/service/ptm/ptm_gets.h"
7
8namespace Service {
9namespace PTM {
10
11const Interface::FunctionInfo FunctionTable[] = {
12 // ptm:u common commands
13 {0x00010002, nullptr, "RegisterAlarmClient"},
14 {0x00020080, nullptr, "SetRtcAlarm"},
15 {0x00030000, nullptr, "GetRtcAlarm"},
16 {0x00040000, nullptr, "CancelRtcAlarm"},
17 {0x00050000, GetAdapterState, "GetAdapterState"},
18 {0x00060000, GetShellState, "GetShellState"},
19 {0x00070000, GetBatteryLevel, "GetBatteryLevel"},
20 {0x00080000, GetBatteryChargeState, "GetBatteryChargeState"},
21 {0x00090000, nullptr, "GetPedometerState"},
22 {0x000A0042, nullptr, "GetStepHistoryEntry"},
23 {0x000B00C2, nullptr, "GetStepHistory"},
24 {0x000C0000, GetTotalStepCount, "GetTotalStepCount"},
25 {0x000D0040, nullptr, "SetPedometerRecordingMode"},
26 {0x000E0000, nullptr, "GetPedometerRecordingMode"},
27 {0x000F0084, nullptr, "GetStepHistoryAll"},
28 // ptm:gets
29 {0x04010000, nullptr, "GetSystemTime"},
30};
31
32PTM_Gets::PTM_Gets() {
33 Register(FunctionTable);
34}
35
36} // namespace PTM
37} // namespace Service
diff --git a/src/core/hle/service/ptm/ptm_gets.h b/src/core/hle/service/ptm/ptm_gets.h
deleted file mode 100644
index 5552c9eff..000000000
--- a/src/core/hle/service/ptm/ptm_gets.h
+++ /dev/null
@@ -1,22 +0,0 @@
1// Copyright 2016 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 "core/hle/service/service.h"
8
9namespace Service {
10namespace PTM {
11
12class PTM_Gets final : public Interface {
13public:
14 PTM_Gets();
15
16 std::string GetPortName() const override {
17 return "ptm:gets";
18 }
19};
20
21} // namespace PTM
22} // namespace Service
diff --git a/src/core/hle/service/ptm/ptm_play.cpp b/src/core/hle/service/ptm/ptm_play.cpp
deleted file mode 100644
index bcb00e0d4..000000000
--- a/src/core/hle/service/ptm/ptm_play.cpp
+++ /dev/null
@@ -1,40 +0,0 @@
1// Copyright 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/ptm/ptm.h"
6#include "core/hle/service/ptm/ptm_play.h"
7
8namespace Service {
9namespace PTM {
10
11const Interface::FunctionInfo FunctionTable[] = {
12 // ptm:u common commands
13 {0x00010002, nullptr, "RegisterAlarmClient"},
14 {0x00020080, nullptr, "SetRtcAlarm"},
15 {0x00030000, nullptr, "GetRtcAlarm"},
16 {0x00040000, nullptr, "CancelRtcAlarm"},
17 {0x00050000, GetAdapterState, "GetAdapterState"},
18 {0x00060000, GetShellState, "GetShellState"},
19 {0x00070000, GetBatteryLevel, "GetBatteryLevel"},
20 {0x00080000, GetBatteryChargeState, "GetBatteryChargeState"},
21 {0x00090000, nullptr, "GetPedometerState"},
22 {0x000A0042, nullptr, "GetStepHistoryEntry"},
23 {0x000B00C2, nullptr, "GetStepHistory"},
24 {0x000C0000, GetTotalStepCount, "GetTotalStepCount"},
25 {0x000D0040, nullptr, "SetPedometerRecordingMode"},
26 {0x000E0000, nullptr, "GetPedometerRecordingMode"},
27 {0x000F0084, nullptr, "GetStepHistoryAll"},
28 // ptm:play
29 {0x08070082, nullptr, "GetPlayHistory"},
30 {0x08080000, nullptr, "GetPlayHistoryStart"},
31 {0x08090000, nullptr, "GetPlayHistoryLength"},
32 {0x080B0080, nullptr, "CalcPlayHistoryStart"},
33};
34
35PTM_Play::PTM_Play() {
36 Register(FunctionTable);
37}
38
39} // namespace PTM
40} // namespace Service \ No newline at end of file
diff --git a/src/core/hle/service/ptm/ptm_play.h b/src/core/hle/service/ptm/ptm_play.h
deleted file mode 100644
index 663faabee..000000000
--- a/src/core/hle/service/ptm/ptm_play.h
+++ /dev/null
@@ -1,22 +0,0 @@
1// Copyright 2015 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 "core/hle/service/service.h"
8
9namespace Service {
10namespace PTM {
11
12class PTM_Play final : public Interface {
13public:
14 PTM_Play();
15
16 std::string GetPortName() const override {
17 return "ptm:play";
18 }
19};
20
21} // namespace PTM
22} // namespace Service
diff --git a/src/core/hle/service/ptm/ptm_sets.cpp b/src/core/hle/service/ptm/ptm_sets.cpp
deleted file mode 100644
index a8c6cf227..000000000
--- a/src/core/hle/service/ptm/ptm_sets.cpp
+++ /dev/null
@@ -1,20 +0,0 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/ptm/ptm_sets.h"
6
7namespace Service {
8namespace PTM {
9
10const Interface::FunctionInfo FunctionTable[] = {
11 // Note that this service does not have access to ptm:u's common commands
12 {0x00010080, nullptr, "SetSystemTime"},
13};
14
15PTM_Sets::PTM_Sets() {
16 Register(FunctionTable);
17}
18
19} // namespace PTM
20} // namespace Service
diff --git a/src/core/hle/service/ptm/ptm_sets.h b/src/core/hle/service/ptm/ptm_sets.h
deleted file mode 100644
index d33b047e5..000000000
--- a/src/core/hle/service/ptm/ptm_sets.h
+++ /dev/null
@@ -1,22 +0,0 @@
1// Copyright 2016 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 "core/hle/service/service.h"
8
9namespace Service {
10namespace PTM {
11
12class PTM_Sets final : public Interface {
13public:
14 PTM_Sets();
15
16 std::string GetPortName() const override {
17 return "ptm:sets";
18 }
19};
20
21} // namespace PTM
22} // namespace Service
diff --git a/src/core/hle/service/ptm/ptm_sysm.cpp b/src/core/hle/service/ptm/ptm_sysm.cpp
deleted file mode 100644
index f95dfdbb1..000000000
--- a/src/core/hle/service/ptm/ptm_sysm.cpp
+++ /dev/null
@@ -1,71 +0,0 @@
1// Copyright 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/ptm/ptm.h"
6#include "core/hle/service/ptm/ptm_sysm.h"
7
8namespace Service {
9namespace PTM {
10
11const Interface::FunctionInfo FunctionTable[] = {
12 // ptm:u common commands
13 {0x00010002, nullptr, "RegisterAlarmClient"},
14 {0x00020080, nullptr, "SetRtcAlarm"},
15 {0x00030000, nullptr, "GetRtcAlarm"},
16 {0x00040000, nullptr, "CancelRtcAlarm"},
17 {0x00050000, GetAdapterState, "GetAdapterState"},
18 {0x00060000, GetShellState, "GetShellState"},
19 {0x00070000, GetBatteryLevel, "GetBatteryLevel"},
20 {0x00080000, GetBatteryChargeState, "GetBatteryChargeState"},
21 {0x00090000, nullptr, "GetPedometerState"},
22 {0x000A0042, nullptr, "GetStepHistoryEntry"},
23 {0x000B00C2, nullptr, "GetStepHistory"},
24 {0x000C0000, GetTotalStepCount, "GetTotalStepCount"},
25 {0x000D0040, nullptr, "SetPedometerRecordingMode"},
26 {0x000E0000, nullptr, "GetPedometerRecordingMode"},
27 {0x000F0084, nullptr, "GetStepHistoryAll"},
28 // ptm:sysm
29 {0x040100C0, nullptr, "SetRtcAlarmEx"},
30 {0x04020042, nullptr, "ReplySleepQuery"},
31 {0x04030042, nullptr, "NotifySleepPreparationComplete"},
32 {0x04040102, nullptr, "SetWakeupTrigger"},
33 {0x04050000, nullptr, "GetAwakeReason"},
34 {0x04060000, nullptr, "RequestSleep"},
35 {0x040700C0, nullptr, "ShutdownAsync"},
36 {0x04080000, nullptr, "Awake"},
37 {0x04090080, nullptr, "RebootAsync"},
38 {0x040A0000, CheckNew3DS, "CheckNew3DS"},
39 {0x08010640, nullptr, "SetInfoLEDPattern"},
40 {0x08020040, nullptr, "SetInfoLEDPatternHeader"},
41 {0x08030000, nullptr, "GetInfoLEDStatus"},
42 {0x08040040, nullptr, "SetBatteryEmptyLEDPattern"},
43 {0x08050000, nullptr, "ClearStepHistory"},
44 {0x080600C2, nullptr, "SetStepHistory"},
45 {0x08070082, nullptr, "GetPlayHistory"},
46 {0x08080000, nullptr, "GetPlayHistoryStart"},
47 {0x08090000, nullptr, "GetPlayHistoryLength"},
48 {0x080A0000, nullptr, "ClearPlayHistory"},
49 {0x080B0080, nullptr, "CalcPlayHistoryStart"},
50 {0x080C0080, nullptr, "SetUserTime"},
51 {0x080D0000, nullptr, "InvalidateSystemTime"},
52 {0x080E0140, nullptr, "NotifyPlayEvent"},
53 {0x080F0000, GetSoftwareClosedFlag, "GetSoftwareClosedFlag"},
54 {0x08100000, nullptr, "ClearSoftwareClosedFlag"},
55 {0x08110000, GetShellState, "GetShellState"},
56 {0x08120000, nullptr, "IsShutdownByBatteryEmpty"},
57 {0x08130000, nullptr, "FormatSavedata"},
58 {0x08140000, nullptr, "GetLegacyJumpProhibitedFlag"},
59 {0x08180040, nullptr, "ConfigureNew3DSCPU"},
60};
61
62PTM_S::PTM_S() {
63 Register(FunctionTable);
64}
65
66PTM_Sysm::PTM_Sysm() {
67 Register(FunctionTable);
68}
69
70} // namespace PTM
71} // namespace Service
diff --git a/src/core/hle/service/ptm/ptm_sysm.h b/src/core/hle/service/ptm/ptm_sysm.h
deleted file mode 100644
index 8afcebbba..000000000
--- a/src/core/hle/service/ptm/ptm_sysm.h
+++ /dev/null
@@ -1,31 +0,0 @@
1// Copyright 2015 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 "core/hle/service/service.h"
8
9namespace Service {
10namespace PTM {
11
12class PTM_S final : public Interface {
13public:
14 PTM_S();
15
16 std::string GetPortName() const override {
17 return "ptm:s";
18 }
19};
20
21class PTM_Sysm final : public Interface {
22public:
23 PTM_Sysm();
24
25 std::string GetPortName() const override {
26 return "ptm:sysm";
27 }
28};
29
30} // namespace PTM
31} // namespace Service
diff --git a/src/core/hle/service/ptm/ptm_u.cpp b/src/core/hle/service/ptm/ptm_u.cpp
deleted file mode 100644
index 696a58a36..000000000
--- a/src/core/hle/service/ptm/ptm_u.cpp
+++ /dev/null
@@ -1,34 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/ptm/ptm.h"
6#include "core/hle/service/ptm/ptm_u.h"
7
8namespace Service {
9namespace PTM {
10
11const Interface::FunctionInfo FunctionTable[] = {
12 {0x00010002, nullptr, "RegisterAlarmClient"},
13 {0x00020080, nullptr, "SetRtcAlarm"},
14 {0x00030000, nullptr, "GetRtcAlarm"},
15 {0x00040000, nullptr, "CancelRtcAlarm"},
16 {0x00050000, GetAdapterState, "GetAdapterState"},
17 {0x00060000, GetShellState, "GetShellState"},
18 {0x00070000, GetBatteryLevel, "GetBatteryLevel"},
19 {0x00080000, GetBatteryChargeState, "GetBatteryChargeState"},
20 {0x00090000, GetPedometerState, "GetPedometerState"},
21 {0x000A0042, nullptr, "GetStepHistoryEntry"},
22 {0x000B00C2, nullptr, "GetStepHistory"},
23 {0x000C0000, GetTotalStepCount, "GetTotalStepCount"},
24 {0x000D0040, nullptr, "SetPedometerRecordingMode"},
25 {0x000E0000, nullptr, "GetPedometerRecordingMode"},
26 {0x000F0084, nullptr, "GetStepHistoryAll"},
27};
28
29PTM_U::PTM_U() {
30 Register(FunctionTable);
31}
32
33} // namespace PTM
34} // namespace Service
diff --git a/src/core/hle/service/ptm/ptm_u.h b/src/core/hle/service/ptm/ptm_u.h
deleted file mode 100644
index 7b75d6e49..000000000
--- a/src/core/hle/service/ptm/ptm_u.h
+++ /dev/null
@@ -1,22 +0,0 @@
1// Copyright 2014 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 "core/hle/service/service.h"
8
9namespace Service {
10namespace PTM {
11
12class PTM_U final : public Interface {
13public:
14 PTM_U();
15
16 std::string GetPortName() const override {
17 return "ptm:u";
18 }
19};
20
21} // namespace PTM
22} // namespace Service \ No newline at end of file
diff --git a/src/core/hle/service/qtm/qtm.cpp b/src/core/hle/service/qtm/qtm.cpp
deleted file mode 100644
index f11542263..000000000
--- a/src/core/hle/service/qtm/qtm.cpp
+++ /dev/null
@@ -1,21 +0,0 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/qtm/qtm.h"
6#include "core/hle/service/qtm/qtm_s.h"
7#include "core/hle/service/qtm/qtm_sp.h"
8#include "core/hle/service/qtm/qtm_u.h"
9#include "core/hle/service/service.h"
10
11namespace Service {
12namespace QTM {
13
14void Init() {
15 AddService(new QTM_S());
16 AddService(new QTM_SP());
17 AddService(new QTM_U());
18}
19
20} // namespace QTM
21} // namespace Service
diff --git a/src/core/hle/service/qtm/qtm.h b/src/core/hle/service/qtm/qtm.h
deleted file mode 100644
index 33b774c79..000000000
--- a/src/core/hle/service/qtm/qtm.h
+++ /dev/null
@@ -1,14 +0,0 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7namespace Service {
8namespace QTM {
9
10/// Initializes all QTM services.
11void Init();
12
13} // namespace QTM
14} // namespace Service
diff --git a/src/core/hle/service/qtm/qtm_s.cpp b/src/core/hle/service/qtm/qtm_s.cpp
deleted file mode 100644
index ad7df24a0..000000000
--- a/src/core/hle/service/qtm/qtm_s.cpp
+++ /dev/null
@@ -1,23 +0,0 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/qtm/qtm_s.h"
6
7namespace Service {
8namespace QTM {
9
10const Interface::FunctionInfo FunctionTable[] = {
11 // clang-format off
12 // qtm common commands
13 {0x00010080, nullptr, "GetHeadtrackingInfoRaw"},
14 {0x00020080, nullptr, "GetHeadtrackingInfo"},
15 // clang-format on
16};
17
18QTM_S::QTM_S() {
19 Register(FunctionTable);
20}
21
22} // namespace QTM
23} // namespace Service
diff --git a/src/core/hle/service/qtm/qtm_s.h b/src/core/hle/service/qtm/qtm_s.h
deleted file mode 100644
index e66138ed0..000000000
--- a/src/core/hle/service/qtm/qtm_s.h
+++ /dev/null
@@ -1,22 +0,0 @@
1// Copyright 2016 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 "core/hle/service/service.h"
8
9namespace Service {
10namespace QTM {
11
12class QTM_S final : public Interface {
13public:
14 QTM_S();
15
16 std::string GetPortName() const override {
17 return "qtm:s";
18 }
19};
20
21} // namespace QTM
22} // namespace Service
diff --git a/src/core/hle/service/qtm/qtm_sp.cpp b/src/core/hle/service/qtm/qtm_sp.cpp
deleted file mode 100644
index 6e0695d34..000000000
--- a/src/core/hle/service/qtm/qtm_sp.cpp
+++ /dev/null
@@ -1,23 +0,0 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/qtm/qtm_sp.h"
6
7namespace Service {
8namespace QTM {
9
10const Interface::FunctionInfo FunctionTable[] = {
11 // clang-format off
12 // qtm common commands
13 {0x00010080, nullptr, "GetHeadtrackingInfoRaw"},
14 {0x00020080, nullptr, "GetHeadtrackingInfo"},
15 // clang-format on
16};
17
18QTM_SP::QTM_SP() {
19 Register(FunctionTable);
20}
21
22} // namespace QTM
23} // namespace Service
diff --git a/src/core/hle/service/qtm/qtm_sp.h b/src/core/hle/service/qtm/qtm_sp.h
deleted file mode 100644
index 0ae0618fc..000000000
--- a/src/core/hle/service/qtm/qtm_sp.h
+++ /dev/null
@@ -1,22 +0,0 @@
1// Copyright 2016 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 "core/hle/service/service.h"
8
9namespace Service {
10namespace QTM {
11
12class QTM_SP final : public Interface {
13public:
14 QTM_SP();
15
16 std::string GetPortName() const override {
17 return "qtm:sp";
18 }
19};
20
21} // namespace QTM
22} // namespace Service
diff --git a/src/core/hle/service/qtm/qtm_u.cpp b/src/core/hle/service/qtm/qtm_u.cpp
deleted file mode 100644
index a0f808432..000000000
--- a/src/core/hle/service/qtm/qtm_u.cpp
+++ /dev/null
@@ -1,23 +0,0 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/qtm/qtm_u.h"
6
7namespace Service {
8namespace QTM {
9
10const Interface::FunctionInfo FunctionTable[] = {
11 // clang-format off
12 // qtm common commands
13 {0x00010080, nullptr, "GetHeadtrackingInfoRaw"},
14 {0x00020080, nullptr, "GetHeadtrackingInfo"},
15 // clang-format on
16};
17
18QTM_U::QTM_U() {
19 Register(FunctionTable);
20}
21
22} // namespace QTM
23} // namespace Service
diff --git a/src/core/hle/service/qtm/qtm_u.h b/src/core/hle/service/qtm/qtm_u.h
deleted file mode 100644
index 1ed4c0adc..000000000
--- a/src/core/hle/service/qtm/qtm_u.h
+++ /dev/null
@@ -1,22 +0,0 @@
1// Copyright 2016 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 "core/hle/service/service.h"
8
9namespace Service {
10namespace QTM {
11
12class QTM_U final : public Interface {
13public:
14 QTM_U();
15
16 std::string GetPortName() const override {
17 return "qtm:u";
18 }
19};
20
21} // namespace QTM
22} // namespace Service
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index f267aad74..afd27d446 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -12,43 +12,17 @@
12#include "core/hle/kernel/process.h" 12#include "core/hle/kernel/process.h"
13#include "core/hle/kernel/server_port.h" 13#include "core/hle/kernel/server_port.h"
14#include "core/hle/kernel/server_session.h" 14#include "core/hle/kernel/server_session.h"
15#include "core/hle/service/ac/ac.h" 15#include "core/hle/kernel/handle_table.h"
16#include "core/hle/service/act/act.h"
17#include "core/hle/service/am/am.h"
18#include "core/hle/service/apt/apt.h"
19#include "core/hle/service/boss/boss.h"
20#include "core/hle/service/cam/cam.h"
21#include "core/hle/service/cecd/cecd.h"
22#include "core/hle/service/cfg/cfg.h" 16#include "core/hle/service/cfg/cfg.h"
23#include "core/hle/service/csnd_snd.h"
24#include "core/hle/service/dlp/dlp.h"
25#include "core/hle/service/dsp_dsp.h" 17#include "core/hle/service/dsp_dsp.h"
26#include "core/hle/service/err_f.h"
27#include "core/hle/service/frd/frd.h"
28#include "core/hle/service/fs/archive.h" 18#include "core/hle/service/fs/archive.h"
29#include "core/hle/service/gsp_gpu.h" 19#include "core/hle/service/gsp_gpu.h"
30#include "core/hle/service/gsp_lcd.h" 20#include "core/hle/service/gsp_lcd.h"
31#include "core/hle/service/hid/hid.h" 21#include "core/hle/service/hid/hid.h"
32#include "core/hle/service/http_c.h"
33#include "core/hle/service/ir/ir.h"
34#include "core/hle/service/ldr_ro/ldr_ro.h"
35#include "core/hle/service/mic_u.h"
36#include "core/hle/service/mvd/mvd.h"
37#include "core/hle/service/ndm/ndm.h"
38#include "core/hle/service/news/news.h"
39#include "core/hle/service/nfc/nfc.h"
40#include "core/hle/service/nim/nim.h"
41#include "core/hle/service/ns/ns.h" 22#include "core/hle/service/ns/ns.h"
42#include "core/hle/service/nwm/nwm.h"
43#include "core/hle/service/pm_app.h"
44#include "core/hle/service/ptm/ptm.h"
45#include "core/hle/service/qtm/qtm.h"
46#include "core/hle/service/service.h" 23#include "core/hle/service/service.h"
47#include "core/hle/service/sm/sm.h" 24#include "core/hle/service/sm/sm.h"
48#include "core/hle/service/sm/srv.h" 25#include "core/hle/service/sm/srv.h"
49#include "core/hle/service/soc_u.h"
50#include "core/hle/service/ssl_c.h"
51#include "core/hle/service/y2r_u.h"
52 26
53using Kernel::ClientPort; 27using Kernel::ClientPort;
54using Kernel::ServerPort; 28using Kernel::ServerPort;
@@ -217,63 +191,21 @@ void Init() {
217 191
218 NS::InstallInterfaces(*SM::g_service_manager); 192 NS::InstallInterfaces(*SM::g_service_manager);
219 193
220 AddNamedPort(new ERR::ERR_F);
221
222 FS::ArchiveInit(); 194 FS::ArchiveInit();
223 AC::Init();
224 ACT::Init();
225 AM::Init();
226 APT::Init();
227 BOSS::Init();
228 CAM::Init();
229 CECD::Init();
230 CFG::Init(); 195 CFG::Init();
231 DLP::Init();
232 FRD::Init();
233 HID::Init(); 196 HID::Init();
234 IR::Init(); 197
235 MVD::Init();
236 NDM::Init();
237 NEWS::Init();
238 NFC::Init();
239 NIM::Init();
240 NWM::Init();
241 PTM::Init();
242 QTM::Init();
243
244 AddService(new CSND::CSND_SND);
245 AddService(new DSP_DSP::Interface); 198 AddService(new DSP_DSP::Interface);
246 AddService(new GSP::GSP_GPU); 199 AddService(new GSP::GSP_GPU);
247 AddService(new GSP::GSP_LCD); 200 AddService(new GSP::GSP_LCD);
248 AddService(new HTTP::HTTP_C);
249 AddService(new LDR::LDR_RO);
250 AddService(new MIC::MIC_U);
251 AddService(new PM::PM_APP);
252 AddService(new SOC::SOC_U);
253 AddService(new SSL::SSL_C);
254 AddService(new Y2R::Y2R_U);
255 201
256 LOG_DEBUG(Service, "initialized OK"); 202 LOG_DEBUG(Service, "initialized OK");
257} 203}
258 204
259/// Shutdown ServiceManager 205/// Shutdown ServiceManager
260void Shutdown() { 206void Shutdown() {
261 PTM::Shutdown();
262 NFC::Shutdown();
263 NIM::Shutdown();
264 NEWS::Shutdown();
265 NDM::Shutdown();
266 IR::Shutdown();
267 HID::Shutdown(); 207 HID::Shutdown();
268 FRD::Shutdown();
269 DLP::Shutdown();
270 CFG::Shutdown(); 208 CFG::Shutdown();
271 CECD::Shutdown();
272 CAM::Shutdown();
273 BOSS::Shutdown();
274 APT::Shutdown();
275 AM::Shutdown();
276 AC::Shutdown();
277 FS::ArchiveShutdown(); 209 FS::ArchiveShutdown();
278 210
279 SM::g_service_manager = nullptr; 211 SM::g_service_manager = nullptr;
diff --git a/src/core/hle/service/soc_u.cpp b/src/core/hle/service/soc_u.cpp
deleted file mode 100644
index 3d215d42d..000000000
--- a/src/core/hle/service/soc_u.cpp
+++ /dev/null
@@ -1,919 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <cstring>
7#include <unordered_map>
8#include <vector>
9#include "common/assert.h"
10#include "common/bit_field.h"
11#include "common/common_types.h"
12#include "common/logging/log.h"
13#include "common/scope_exit.h"
14#include "core/hle/ipc.h"
15#include "core/hle/kernel/server_session.h"
16#include "core/hle/result.h"
17#include "core/hle/service/soc_u.h"
18#include "core/memory.h"
19
20#ifdef _WIN32
21#include <winsock2.h>
22#include <ws2tcpip.h>
23
24// MinGW does not define several errno constants
25#ifndef _MSC_VER
26#define EBADMSG 104
27#define ENODATA 120
28#define ENOMSG 122
29#define ENOSR 124
30#define ENOSTR 125
31#define ETIME 137
32#define EIDRM 2001
33#define ENOLINK 2002
34#endif // _MSC_VER
35#else
36#include <cerrno>
37#include <fcntl.h>
38#include <netdb.h>
39#include <netinet/in.h>
40#include <poll.h>
41#include <sys/socket.h>
42#include <unistd.h>
43#endif
44
45#ifdef _WIN32
46#define WSAEAGAIN WSAEWOULDBLOCK
47#define WSAEMULTIHOP -1 // Invalid dummy value
48#define ERRNO(x) WSA##x
49#define GET_ERRNO WSAGetLastError()
50#define poll(x, y, z) WSAPoll(x, y, z);
51#else
52#define ERRNO(x) x
53#define GET_ERRNO errno
54#define closesocket(x) close(x)
55#endif
56
57namespace Service {
58namespace SOC {
59
60const s32 SOCKET_ERROR_VALUE = -1;
61
62/// Holds the translation from system network errors to 3DS network errors
63static const std::unordered_map<int, int> error_map = {{
64 {E2BIG, 1},
65 {ERRNO(EACCES), 2},
66 {ERRNO(EADDRINUSE), 3},
67 {ERRNO(EADDRNOTAVAIL), 4},
68 {ERRNO(EAFNOSUPPORT), 5},
69 {ERRNO(EAGAIN), 6},
70 {ERRNO(EALREADY), 7},
71 {ERRNO(EBADF), 8},
72 {EBADMSG, 9},
73 {EBUSY, 10},
74 {ECANCELED, 11},
75 {ECHILD, 12},
76 {ERRNO(ECONNABORTED), 13},
77 {ERRNO(ECONNREFUSED), 14},
78 {ERRNO(ECONNRESET), 15},
79 {EDEADLK, 16},
80 {ERRNO(EDESTADDRREQ), 17},
81 {EDOM, 18},
82 {ERRNO(EDQUOT), 19},
83 {EEXIST, 20},
84 {ERRNO(EFAULT), 21},
85 {EFBIG, 22},
86 {ERRNO(EHOSTUNREACH), 23},
87 {EIDRM, 24},
88 {EILSEQ, 25},
89 {ERRNO(EINPROGRESS), 26},
90 {ERRNO(EINTR), 27},
91 {ERRNO(EINVAL), 28},
92 {EIO, 29},
93 {ERRNO(EISCONN), 30},
94 {EISDIR, 31},
95 {ERRNO(ELOOP), 32},
96 {ERRNO(EMFILE), 33},
97 {EMLINK, 34},
98 {ERRNO(EMSGSIZE), 35},
99 {ERRNO(EMULTIHOP), 36},
100 {ERRNO(ENAMETOOLONG), 37},
101 {ERRNO(ENETDOWN), 38},
102 {ERRNO(ENETRESET), 39},
103 {ERRNO(ENETUNREACH), 40},
104 {ENFILE, 41},
105 {ERRNO(ENOBUFS), 42},
106#ifdef ENODATA
107 {ENODATA, 43},
108#endif
109 {ENODEV, 44},
110 {ENOENT, 45},
111 {ENOEXEC, 46},
112 {ENOLCK, 47},
113 {ENOLINK, 48},
114 {ENOMEM, 49},
115 {ENOMSG, 50},
116 {ERRNO(ENOPROTOOPT), 51},
117 {ENOSPC, 52},
118#ifdef ENOSR
119 {ENOSR, 53},
120#endif
121#ifdef ENOSTR
122 {ENOSTR, 54},
123#endif
124 {ENOSYS, 55},
125 {ERRNO(ENOTCONN), 56},
126 {ENOTDIR, 57},
127 {ERRNO(ENOTEMPTY), 58},
128 {ERRNO(ENOTSOCK), 59},
129 {ENOTSUP, 60},
130 {ENOTTY, 61},
131 {ENXIO, 62},
132 {ERRNO(EOPNOTSUPP), 63},
133 {EOVERFLOW, 64},
134 {EPERM, 65},
135 {EPIPE, 66},
136 {EPROTO, 67},
137 {ERRNO(EPROTONOSUPPORT), 68},
138 {ERRNO(EPROTOTYPE), 69},
139 {ERANGE, 70},
140 {EROFS, 71},
141 {ESPIPE, 72},
142 {ESRCH, 73},
143 {ERRNO(ESTALE), 74},
144#ifdef ETIME
145 {ETIME, 75},
146#endif
147 {ERRNO(ETIMEDOUT), 76},
148}};
149
150/// Converts a network error from platform-specific to 3ds-specific
151static int TranslateError(int error) {
152 auto found = error_map.find(error);
153 if (found != error_map.end())
154 return -found->second;
155
156 return error;
157}
158
159/// Holds the translation from system network socket options to 3DS network socket options
160/// Note: -1 = No effect/unavailable
161static const std::unordered_map<int, int> sockopt_map = {{
162 {0x0004, SO_REUSEADDR},
163 {0x0080, -1},
164 {0x0100, -1},
165 {0x1001, SO_SNDBUF},
166 {0x1002, SO_RCVBUF},
167 {0x1003, -1},
168#ifdef _WIN32
169 /// Unsupported in WinSock2
170 {0x1004, -1},
171#else
172 {0x1004, SO_RCVLOWAT},
173#endif
174 {0x1008, SO_TYPE},
175 {0x1009, SO_ERROR},
176}};
177
178/// Converts a socket option from 3ds-specific to platform-specific
179static int TranslateSockOpt(int console_opt_name) {
180 auto found = sockopt_map.find(console_opt_name);
181 if (found != sockopt_map.end()) {
182 return found->second;
183 }
184 return console_opt_name;
185}
186
187/// Holds information about a particular socket
188struct SocketHolder {
189 u32 socket_fd; ///< The socket descriptor
190 bool blocking; ///< Whether the socket is blocking or not, it is only read on Windows.
191};
192
193/// Structure to represent the 3ds' pollfd structure, which is different than most implementations
194struct CTRPollFD {
195 u32 fd; ///< Socket handle
196
197 union Events {
198 u32 hex; ///< The complete value formed by the flags
199 BitField<0, 1, u32> pollin;
200 BitField<1, 1, u32> pollpri;
201 BitField<2, 1, u32> pollhup;
202 BitField<3, 1, u32> pollerr;
203 BitField<4, 1, u32> pollout;
204 BitField<5, 1, u32> pollnval;
205
206 Events& operator=(const Events& other) {
207 hex = other.hex;
208 return *this;
209 }
210
211 /// Translates the resulting events of a Poll operation from platform-specific to 3ds
212 /// specific
213 static Events TranslateTo3DS(u32 input_event) {
214 Events ev = {};
215 if (input_event & POLLIN)
216 ev.pollin.Assign(1);
217 if (input_event & POLLPRI)
218 ev.pollpri.Assign(1);
219 if (input_event & POLLHUP)
220 ev.pollhup.Assign(1);
221 if (input_event & POLLERR)
222 ev.pollerr.Assign(1);
223 if (input_event & POLLOUT)
224 ev.pollout.Assign(1);
225 if (input_event & POLLNVAL)
226 ev.pollnval.Assign(1);
227 return ev;
228 }
229
230 /// Translates the resulting events of a Poll operation from 3ds specific to platform
231 /// specific
232 static u32 TranslateToPlatform(Events input_event) {
233 u32 ret = 0;
234 if (input_event.pollin)
235 ret |= POLLIN;
236 if (input_event.pollpri)
237 ret |= POLLPRI;
238 if (input_event.pollhup)
239 ret |= POLLHUP;
240 if (input_event.pollerr)
241 ret |= POLLERR;
242 if (input_event.pollout)
243 ret |= POLLOUT;
244 if (input_event.pollnval)
245 ret |= POLLNVAL;
246 return ret;
247 }
248 };
249 Events events; ///< Events to poll for (input)
250 Events revents; ///< Events received (output)
251
252 /// Converts a platform-specific pollfd to a 3ds specific structure
253 static CTRPollFD FromPlatform(pollfd const& fd) {
254 CTRPollFD result;
255 result.events.hex = Events::TranslateTo3DS(fd.events).hex;
256 result.revents.hex = Events::TranslateTo3DS(fd.revents).hex;
257 result.fd = static_cast<u32>(fd.fd);
258 return result;
259 }
260
261 /// Converts a 3ds specific pollfd to a platform-specific structure
262 static pollfd ToPlatform(CTRPollFD const& fd) {
263 pollfd result;
264 result.events = Events::TranslateToPlatform(fd.events);
265 result.revents = Events::TranslateToPlatform(fd.revents);
266 result.fd = fd.fd;
267 return result;
268 }
269};
270
271/// Union to represent the 3ds' sockaddr structure
272union CTRSockAddr {
273 /// Structure to represent a raw sockaddr
274 struct {
275 u8 len; ///< The length of the entire structure, only the set fields count
276 u8 sa_family; ///< The address family of the sockaddr
277 u8 sa_data[0x1A]; ///< The extra data, this varies, depending on the address family
278 } raw;
279
280 /// Structure to represent the 3ds' sockaddr_in structure
281 struct CTRSockAddrIn {
282 u8 len; ///< The length of the entire structure
283 u8 sin_family; ///< The address family of the sockaddr_in
284 u16 sin_port; ///< The port associated with this sockaddr_in
285 u32 sin_addr; ///< The actual address of the sockaddr_in
286 } in;
287
288 /// Convert a 3DS CTRSockAddr to a platform-specific sockaddr
289 static sockaddr ToPlatform(CTRSockAddr const& ctr_addr) {
290 sockaddr result;
291 result.sa_family = ctr_addr.raw.sa_family;
292 memset(result.sa_data, 0, sizeof(result.sa_data));
293
294 // We can not guarantee ABI compatibility between platforms so we copy the fields manually
295 switch (result.sa_family) {
296 case AF_INET: {
297 sockaddr_in* result_in = reinterpret_cast<sockaddr_in*>(&result);
298 result_in->sin_port = ctr_addr.in.sin_port;
299 result_in->sin_addr.s_addr = ctr_addr.in.sin_addr;
300 memset(result_in->sin_zero, 0, sizeof(result_in->sin_zero));
301 break;
302 }
303 default:
304 ASSERT_MSG(false, "Unhandled address family (sa_family) in CTRSockAddr::ToPlatform");
305 break;
306 }
307 return result;
308 }
309
310 /// Convert a platform-specific sockaddr to a 3DS CTRSockAddr
311 static CTRSockAddr FromPlatform(sockaddr const& addr) {
312 CTRSockAddr result;
313 result.raw.sa_family = static_cast<u8>(addr.sa_family);
314 // We can not guarantee ABI compatibility between platforms so we copy the fields manually
315 switch (result.raw.sa_family) {
316 case AF_INET: {
317 sockaddr_in const* addr_in = reinterpret_cast<sockaddr_in const*>(&addr);
318 result.raw.len = sizeof(CTRSockAddrIn);
319 result.in.sin_port = addr_in->sin_port;
320 result.in.sin_addr = addr_in->sin_addr.s_addr;
321 break;
322 }
323 default:
324 ASSERT_MSG(false, "Unhandled address family (sa_family) in CTRSockAddr::ToPlatform");
325 break;
326 }
327 return result;
328 }
329};
330
331/// Holds info about the currently open sockets
332static std::unordered_map<u32, SocketHolder> open_sockets;
333
334/// Close all open sockets
335static void CleanupSockets() {
336 for (auto sock : open_sockets)
337 closesocket(sock.second.socket_fd);
338 open_sockets.clear();
339}
340
341static void Socket(Interface* self) {
342 u32* cmd_buffer = Kernel::GetCommandBuffer();
343 u32 domain = cmd_buffer[1]; // Address family
344 u32 type = cmd_buffer[2];
345 u32 protocol = cmd_buffer[3];
346
347 // Only 0 is allowed according to 3dbrew, using 0 will let the OS decide which protocol to use
348 if (protocol != 0) {
349 cmd_buffer[1] =
350 UnimplementedFunction(ErrorModule::SOC).raw; // TODO(Subv): Correct error code
351 return;
352 }
353
354 if (domain != AF_INET) {
355 cmd_buffer[1] =
356 UnimplementedFunction(ErrorModule::SOC).raw; // TODO(Subv): Correct error code
357 return;
358 }
359
360 if (type != SOCK_DGRAM && type != SOCK_STREAM) {
361 cmd_buffer[1] =
362 UnimplementedFunction(ErrorModule::SOC).raw; // TODO(Subv): Correct error code
363 return;
364 }
365
366 u32 ret = static_cast<u32>(::socket(domain, type, protocol));
367
368 if ((s32)ret != SOCKET_ERROR_VALUE)
369 open_sockets[ret] = {ret, true};
370
371 int result = 0;
372 if ((s32)ret == SOCKET_ERROR_VALUE)
373 ret = TranslateError(GET_ERRNO);
374
375 cmd_buffer[0] = IPC::MakeHeader(2, 2, 0);
376 cmd_buffer[1] = result;
377 cmd_buffer[2] = ret;
378}
379
380static void Bind(Interface* self) {
381 u32* cmd_buffer = Kernel::GetCommandBuffer();
382 u32 socket_handle = cmd_buffer[1];
383 u32 len = cmd_buffer[2];
384
385 // Virtual address of the sock_addr structure
386 VAddr sock_addr_addr = cmd_buffer[6];
387 if (!Memory::IsValidVirtualAddress(sock_addr_addr)) {
388 cmd_buffer[1] = -1; // TODO(Subv): Correct code
389 return;
390 }
391
392 CTRSockAddr ctr_sock_addr;
393 Memory::ReadBlock(sock_addr_addr, reinterpret_cast<u8*>(&ctr_sock_addr), sizeof(CTRSockAddr));
394
395 sockaddr sock_addr = CTRSockAddr::ToPlatform(ctr_sock_addr);
396
397 int ret = ::bind(socket_handle, &sock_addr, std::max<u32>(sizeof(sock_addr), len));
398
399 int result = 0;
400 if (ret != 0)
401 ret = TranslateError(GET_ERRNO);
402
403 cmd_buffer[0] = IPC::MakeHeader(5, 2, 0);
404 cmd_buffer[1] = result;
405 cmd_buffer[2] = ret;
406}
407
408static void Fcntl(Interface* self) {
409 u32* cmd_buffer = Kernel::GetCommandBuffer();
410 u32 socket_handle = cmd_buffer[1];
411 u32 ctr_cmd = cmd_buffer[2];
412 u32 ctr_arg = cmd_buffer[3];
413
414 int result = 0;
415 u32 posix_ret = 0; // TODO: Check what hardware returns for F_SETFL (unspecified by POSIX)
416 SCOPE_EXIT({
417 cmd_buffer[1] = result;
418 cmd_buffer[2] = posix_ret;
419 });
420
421 if (ctr_cmd == 3) { // F_GETFL
422#ifdef _WIN32
423 posix_ret = 0;
424 auto iter = open_sockets.find(socket_handle);
425 if (iter != open_sockets.end() && iter->second.blocking == false)
426 posix_ret |= 4; // O_NONBLOCK
427#else
428 int ret = ::fcntl(socket_handle, F_GETFL, 0);
429 if (ret == SOCKET_ERROR_VALUE) {
430 posix_ret = TranslateError(GET_ERRNO);
431 return;
432 }
433 posix_ret = 0;
434 if (ret & O_NONBLOCK)
435 posix_ret |= 4; // O_NONBLOCK
436#endif
437 } else if (ctr_cmd == 4) { // F_SETFL
438#ifdef _WIN32
439 unsigned long tmp = (ctr_arg & 4 /* O_NONBLOCK */) ? 1 : 0;
440 int ret = ioctlsocket(socket_handle, FIONBIO, &tmp);
441 if (ret == SOCKET_ERROR_VALUE) {
442 posix_ret = TranslateError(GET_ERRNO);
443 return;
444 }
445 auto iter = open_sockets.find(socket_handle);
446 if (iter != open_sockets.end())
447 iter->second.blocking = (tmp == 0);
448#else
449 int flags = ::fcntl(socket_handle, F_GETFL, 0);
450 if (flags == SOCKET_ERROR_VALUE) {
451 posix_ret = TranslateError(GET_ERRNO);
452 return;
453 }
454
455 flags &= ~O_NONBLOCK;
456 if (ctr_arg & 4) // O_NONBLOCK
457 flags |= O_NONBLOCK;
458
459 int ret = ::fcntl(socket_handle, F_SETFL, flags);
460 if (ret == SOCKET_ERROR_VALUE) {
461 posix_ret = TranslateError(GET_ERRNO);
462 return;
463 }
464#endif
465 } else {
466 LOG_ERROR(Service_SOC, "Unsupported command (%d) in fcntl call", ctr_cmd);
467 posix_ret = TranslateError(EINVAL); // TODO: Find the correct error
468 return;
469 }
470}
471
472static void Listen(Interface* self) {
473 u32* cmd_buffer = Kernel::GetCommandBuffer();
474 u32 socket_handle = cmd_buffer[1];
475 u32 backlog = cmd_buffer[2];
476
477 int ret = ::listen(socket_handle, backlog);
478 int result = 0;
479 if (ret != 0)
480 ret = TranslateError(GET_ERRNO);
481
482 cmd_buffer[0] = IPC::MakeHeader(3, 2, 0);
483 cmd_buffer[1] = result;
484 cmd_buffer[2] = ret;
485}
486
487static void Accept(Interface* self) {
488 // TODO(Subv): Calling this function on a blocking socket will block the emu thread,
489 // preventing graceful shutdown when closing the emulator, this can be fixed by always
490 // performing nonblocking operations and spinlock until the data is available
491 u32* cmd_buffer = Kernel::GetCommandBuffer();
492 u32 socket_handle = cmd_buffer[1];
493 socklen_t max_addr_len = static_cast<socklen_t>(cmd_buffer[2]);
494 sockaddr addr;
495 socklen_t addr_len = sizeof(addr);
496 u32 ret = static_cast<u32>(::accept(socket_handle, &addr, &addr_len));
497
498 if ((s32)ret != SOCKET_ERROR_VALUE)
499 open_sockets[ret] = {ret, true};
500
501 int result = 0;
502 if ((s32)ret == SOCKET_ERROR_VALUE) {
503 ret = TranslateError(GET_ERRNO);
504 } else {
505 CTRSockAddr ctr_addr = CTRSockAddr::FromPlatform(addr);
506 Memory::WriteBlock(cmd_buffer[0x104 >> 2], &ctr_addr, sizeof(ctr_addr));
507 }
508
509 cmd_buffer[0] = IPC::MakeHeader(4, 2, 2);
510 cmd_buffer[1] = result;
511 cmd_buffer[2] = ret;
512 cmd_buffer[3] = IPC::StaticBufferDesc(static_cast<u32>(max_addr_len), 0);
513}
514
515static void GetHostId(Interface* self) {
516 u32* cmd_buffer = Kernel::GetCommandBuffer();
517
518 char name[128];
519 gethostname(name, sizeof(name));
520 addrinfo hints = {};
521 addrinfo* res;
522
523 hints.ai_family = AF_INET;
524 getaddrinfo(name, nullptr, &hints, &res);
525 sockaddr_in* sock_addr = reinterpret_cast<sockaddr_in*>(res->ai_addr);
526 in_addr* addr = &sock_addr->sin_addr;
527
528 cmd_buffer[2] = addr->s_addr;
529 cmd_buffer[1] = 0;
530 freeaddrinfo(res);
531}
532
533static void Close(Interface* self) {
534 u32* cmd_buffer = Kernel::GetCommandBuffer();
535 u32 socket_handle = cmd_buffer[1];
536
537 int ret = 0;
538 open_sockets.erase(socket_handle);
539
540 ret = closesocket(socket_handle);
541
542 int result = 0;
543 if (ret != 0)
544 ret = TranslateError(GET_ERRNO);
545
546 cmd_buffer[2] = ret;
547 cmd_buffer[1] = result;
548}
549
550static void SendTo(Interface* self) {
551 u32* cmd_buffer = Kernel::GetCommandBuffer();
552 u32 socket_handle = cmd_buffer[1];
553 u32 len = cmd_buffer[2];
554 u32 flags = cmd_buffer[3];
555 u32 addr_len = cmd_buffer[4];
556
557 VAddr input_buff_address = cmd_buffer[8];
558 if (!Memory::IsValidVirtualAddress(input_buff_address)) {
559 cmd_buffer[1] = -1; // TODO(Subv): Find the right error code
560 return;
561 }
562
563 // Memory address of the dest_addr structure
564 VAddr dest_addr_addr = cmd_buffer[10];
565 if (!Memory::IsValidVirtualAddress(dest_addr_addr)) {
566 cmd_buffer[1] = -1; // TODO(Subv): Find the right error code
567 return;
568 }
569
570 std::vector<u8> input_buff(len);
571 Memory::ReadBlock(input_buff_address, input_buff.data(), input_buff.size());
572
573 CTRSockAddr ctr_dest_addr;
574 Memory::ReadBlock(dest_addr_addr, &ctr_dest_addr, sizeof(ctr_dest_addr));
575
576 int ret = -1;
577 if (addr_len > 0) {
578 sockaddr dest_addr = CTRSockAddr::ToPlatform(ctr_dest_addr);
579 ret = ::sendto(socket_handle, reinterpret_cast<const char*>(input_buff.data()), len, flags,
580 &dest_addr, sizeof(dest_addr));
581 } else {
582 ret = ::sendto(socket_handle, reinterpret_cast<const char*>(input_buff.data()), len, flags,
583 nullptr, 0);
584 }
585
586 int result = 0;
587 if (ret == SOCKET_ERROR_VALUE)
588 ret = TranslateError(GET_ERRNO);
589
590 cmd_buffer[2] = ret;
591 cmd_buffer[1] = result;
592}
593
594static void RecvFrom(Interface* self) {
595 // TODO(Subv): Calling this function on a blocking socket will block the emu thread,
596 // preventing graceful shutdown when closing the emulator, this can be fixed by always
597 // performing nonblocking operations and spinlock until the data is available
598 u32* cmd_buffer = Kernel::GetCommandBuffer();
599 u32 socket_handle = cmd_buffer[1];
600 u32 len = cmd_buffer[2];
601 u32 flags = cmd_buffer[3];
602
603 struct {
604 u32 output_buffer_descriptor;
605 u32 output_buffer_addr;
606 u32 address_buffer_descriptor;
607 u32 output_src_address_buffer;
608 } buffer_parameters;
609
610 std::memcpy(&buffer_parameters, &cmd_buffer[64], sizeof(buffer_parameters));
611
612 if (!Memory::IsValidVirtualAddress(buffer_parameters.output_buffer_addr)) {
613 cmd_buffer[1] = -1; // TODO(Subv): Find the right error code
614 return;
615 }
616
617 if (!Memory::IsValidVirtualAddress(buffer_parameters.output_src_address_buffer)) {
618 cmd_buffer[1] = -1; // TODO(Subv): Find the right error code
619 return;
620 }
621
622 std::vector<u8> output_buff(len);
623 sockaddr src_addr;
624 socklen_t src_addr_len = sizeof(src_addr);
625 int ret = ::recvfrom(socket_handle, reinterpret_cast<char*>(output_buff.data()), len, flags,
626 &src_addr, &src_addr_len);
627
628 if (ret >= 0 && buffer_parameters.output_src_address_buffer != 0 && src_addr_len > 0) {
629 CTRSockAddr ctr_src_addr = CTRSockAddr::FromPlatform(src_addr);
630 Memory::WriteBlock(buffer_parameters.output_src_address_buffer, &ctr_src_addr,
631 sizeof(ctr_src_addr));
632 }
633
634 int result = 0;
635 int total_received = ret;
636 if (ret == SOCKET_ERROR_VALUE) {
637 ret = TranslateError(GET_ERRNO);
638 total_received = 0;
639 } else {
640 // Write only the data we received to avoid overwriting parts of the buffer with zeros
641 Memory::WriteBlock(buffer_parameters.output_buffer_addr, output_buff.data(),
642 total_received);
643 }
644
645 cmd_buffer[1] = result;
646 cmd_buffer[2] = ret;
647 cmd_buffer[3] = total_received;
648}
649
650static void Poll(Interface* self) {
651 u32* cmd_buffer = Kernel::GetCommandBuffer();
652 u32 nfds = cmd_buffer[1];
653 int timeout = cmd_buffer[2];
654
655 VAddr input_fds_addr = cmd_buffer[6];
656 VAddr output_fds_addr = cmd_buffer[0x104 >> 2];
657 if (!Memory::IsValidVirtualAddress(input_fds_addr) ||
658 !Memory::IsValidVirtualAddress(output_fds_addr)) {
659 cmd_buffer[1] = -1; // TODO(Subv): Find correct error code.
660 return;
661 }
662
663 std::vector<CTRPollFD> ctr_fds(nfds);
664 Memory::ReadBlock(input_fds_addr, ctr_fds.data(), nfds * sizeof(CTRPollFD));
665
666 // The 3ds_pollfd and the pollfd structures may be different (Windows/Linux have different
667 // sizes)
668 // so we have to copy the data
669 std::vector<pollfd> platform_pollfd(nfds);
670 std::transform(ctr_fds.begin(), ctr_fds.end(), platform_pollfd.begin(), CTRPollFD::ToPlatform);
671
672 int ret = ::poll(platform_pollfd.data(), nfds, timeout);
673
674 // Now update the output pollfd structure
675 std::transform(platform_pollfd.begin(), platform_pollfd.end(), ctr_fds.begin(),
676 CTRPollFD::FromPlatform);
677
678 Memory::WriteBlock(output_fds_addr, ctr_fds.data(), nfds * sizeof(CTRPollFD));
679
680 int result = 0;
681 if (ret == SOCKET_ERROR_VALUE)
682 ret = TranslateError(GET_ERRNO);
683
684 cmd_buffer[1] = result;
685 cmd_buffer[2] = ret;
686}
687
688static void GetSockName(Interface* self) {
689 u32* cmd_buffer = Kernel::GetCommandBuffer();
690 u32 socket_handle = cmd_buffer[1];
691
692 // Memory address of the ctr_dest_addr structure
693 VAddr ctr_dest_addr_addr = cmd_buffer[0x104 >> 2];
694
695 sockaddr dest_addr;
696 socklen_t dest_addr_len = sizeof(dest_addr);
697 int ret = ::getsockname(socket_handle, &dest_addr, &dest_addr_len);
698
699 if (ctr_dest_addr_addr != 0 && Memory::IsValidVirtualAddress(ctr_dest_addr_addr)) {
700 CTRSockAddr ctr_dest_addr = CTRSockAddr::FromPlatform(dest_addr);
701 Memory::WriteBlock(ctr_dest_addr_addr, &ctr_dest_addr, sizeof(ctr_dest_addr));
702 } else {
703 cmd_buffer[1] = -1; // TODO(Subv): Verify error
704 return;
705 }
706
707 int result = 0;
708 if (ret != 0)
709 ret = TranslateError(GET_ERRNO);
710
711 cmd_buffer[2] = ret;
712 cmd_buffer[1] = result;
713}
714
715static void Shutdown(Interface* self) {
716 u32* cmd_buffer = Kernel::GetCommandBuffer();
717 u32 socket_handle = cmd_buffer[1];
718 int how = cmd_buffer[2];
719
720 int ret = ::shutdown(socket_handle, how);
721 int result = 0;
722 if (ret != 0)
723 ret = TranslateError(GET_ERRNO);
724 cmd_buffer[2] = ret;
725 cmd_buffer[1] = result;
726}
727
728static void GetPeerName(Interface* self) {
729 u32* cmd_buffer = Kernel::GetCommandBuffer();
730 u32 socket_handle = cmd_buffer[1];
731
732 // Memory address of the ctr_dest_addr structure
733 VAddr ctr_dest_addr_addr = cmd_buffer[0x104 >> 2];
734
735 sockaddr dest_addr;
736 socklen_t dest_addr_len = sizeof(dest_addr);
737 int ret = ::getpeername(socket_handle, &dest_addr, &dest_addr_len);
738
739 if (ctr_dest_addr_addr != 0 && Memory::IsValidVirtualAddress(ctr_dest_addr_addr)) {
740 CTRSockAddr ctr_dest_addr = CTRSockAddr::FromPlatform(dest_addr);
741 Memory::WriteBlock(ctr_dest_addr_addr, &ctr_dest_addr, sizeof(ctr_dest_addr));
742 } else {
743 cmd_buffer[1] = -1;
744 return;
745 }
746
747 int result = 0;
748 if (ret != 0)
749 ret = TranslateError(GET_ERRNO);
750
751 cmd_buffer[2] = ret;
752 cmd_buffer[1] = result;
753}
754
755static void Connect(Interface* self) {
756 // TODO(Subv): Calling this function on a blocking socket will block the emu thread,
757 // preventing graceful shutdown when closing the emulator, this can be fixed by always
758 // performing nonblocking operations and spinlock until the data is available
759 u32* cmd_buffer = Kernel::GetCommandBuffer();
760 u32 socket_handle = cmd_buffer[1];
761
762 // Memory address of the ctr_input_addr structure
763 VAddr ctr_input_addr_addr = cmd_buffer[6];
764 if (!Memory::IsValidVirtualAddress(ctr_input_addr_addr)) {
765 cmd_buffer[1] = -1; // TODO(Subv): Verify error
766 return;
767 }
768
769 CTRSockAddr ctr_input_addr;
770 Memory::ReadBlock(ctr_input_addr_addr, &ctr_input_addr, sizeof(ctr_input_addr));
771
772 sockaddr input_addr = CTRSockAddr::ToPlatform(ctr_input_addr);
773 int ret = ::connect(socket_handle, &input_addr, sizeof(input_addr));
774 int result = 0;
775 if (ret != 0)
776 ret = TranslateError(GET_ERRNO);
777
778 cmd_buffer[0] = IPC::MakeHeader(6, 2, 0);
779 cmd_buffer[1] = result;
780 cmd_buffer[2] = ret;
781}
782
783static void InitializeSockets(Interface* self) {
784// TODO(Subv): Implement
785#ifdef _WIN32
786 WSADATA data;
787 WSAStartup(MAKEWORD(2, 2), &data);
788#endif
789
790 u32* cmd_buffer = Kernel::GetCommandBuffer();
791 cmd_buffer[0] = IPC::MakeHeader(1, 1, 0);
792 cmd_buffer[1] = RESULT_SUCCESS.raw;
793}
794
795static void ShutdownSockets(Interface* self) {
796 // TODO(Subv): Implement
797 CleanupSockets();
798
799#ifdef _WIN32
800 WSACleanup();
801#endif
802
803 u32* cmd_buffer = Kernel::GetCommandBuffer();
804 cmd_buffer[1] = 0;
805}
806
807static void GetSockOpt(Interface* self) {
808 u32* cmd_buffer = Kernel::GetCommandBuffer();
809 u32 socket_handle = cmd_buffer[1];
810 u32 level = cmd_buffer[2];
811 int optname = TranslateSockOpt(cmd_buffer[3]);
812 socklen_t optlen = (socklen_t)cmd_buffer[4];
813
814 int ret = 0;
815 int err = 0;
816
817 if (optname < 0) {
818#ifdef _WIN32
819 err = WSAEINVAL;
820#else
821 err = EINVAL;
822#endif
823 } else {
824 // 0x100 = static buffer offset (bytes)
825 // + 0x4 = 2nd pointer (u32) position
826 // >> 2 = convert to u32 offset instead of byte offset (cmd_buffer = u32*)
827 char* optval = reinterpret_cast<char*>(Memory::GetPointer(cmd_buffer[0x104 >> 2]));
828
829 err = ::getsockopt(socket_handle, level, optname, optval, &optlen);
830 if (err == SOCKET_ERROR_VALUE) {
831 err = TranslateError(GET_ERRNO);
832 }
833 }
834
835 cmd_buffer[0] = IPC::MakeHeader(0x11, 4, 2);
836 cmd_buffer[1] = ret;
837 cmd_buffer[2] = err;
838 cmd_buffer[3] = optlen;
839}
840
841static void SetSockOpt(Interface* self) {
842 u32* cmd_buffer = Kernel::GetCommandBuffer();
843 u32 socket_handle = cmd_buffer[1];
844 u32 level = cmd_buffer[2];
845 int optname = TranslateSockOpt(cmd_buffer[3]);
846
847 int ret = 0;
848 int err = 0;
849
850 if (optname < 0) {
851#ifdef _WIN32
852 err = WSAEINVAL;
853#else
854 err = EINVAL;
855#endif
856 } else {
857 socklen_t optlen = static_cast<socklen_t>(cmd_buffer[4]);
858 const char* optval = reinterpret_cast<const char*>(Memory::GetPointer(cmd_buffer[8]));
859
860 err = static_cast<u32>(::setsockopt(socket_handle, level, optname, optval, optlen));
861 if (err == SOCKET_ERROR_VALUE) {
862 err = TranslateError(GET_ERRNO);
863 }
864 }
865
866 cmd_buffer[0] = IPC::MakeHeader(0x12, 4, 4);
867 cmd_buffer[1] = ret;
868 cmd_buffer[2] = err;
869}
870
871const Interface::FunctionInfo FunctionTable[] = {
872 {0x00010044, InitializeSockets, "InitializeSockets"},
873 {0x000200C2, Socket, "Socket"},
874 {0x00030082, Listen, "Listen"},
875 {0x00040082, Accept, "Accept"},
876 {0x00050084, Bind, "Bind"},
877 {0x00060084, Connect, "Connect"},
878 {0x00070104, nullptr, "recvfrom_other"},
879 {0x00080102, RecvFrom, "RecvFrom"},
880 {0x00090106, nullptr, "sendto_other"},
881 {0x000A0106, SendTo, "SendTo"},
882 {0x000B0042, Close, "Close"},
883 {0x000C0082, Shutdown, "Shutdown"},
884 {0x000D0082, nullptr, "GetHostByName"},
885 {0x000E00C2, nullptr, "GetHostByAddr"},
886 {0x000F0106, nullptr, "GetAddrInfo"},
887 {0x00100102, nullptr, "GetNameInfo"},
888 {0x00110102, GetSockOpt, "GetSockOpt"},
889 {0x00120104, SetSockOpt, "SetSockOpt"},
890 {0x001300C2, Fcntl, "Fcntl"},
891 {0x00140084, Poll, "Poll"},
892 {0x00150042, nullptr, "SockAtMark"},
893 {0x00160000, GetHostId, "GetHostId"},
894 {0x00170082, GetSockName, "GetSockName"},
895 {0x00180082, GetPeerName, "GetPeerName"},
896 {0x00190000, ShutdownSockets, "ShutdownSockets"},
897 {0x001A00C0, nullptr, "GetNetworkOpt"},
898 {0x001B0040, nullptr, "ICMPSocket"},
899 {0x001C0104, nullptr, "ICMPPing"},
900 {0x001D0040, nullptr, "ICMPCancel"},
901 {0x001E0040, nullptr, "ICMPClose"},
902 {0x001F0040, nullptr, "GetResolverInfo"},
903 {0x00210002, nullptr, "CloseSockets"},
904 {0x00230040, nullptr, "AddGlobalSocket"},
905};
906
907SOC_U::SOC_U() {
908 Register(FunctionTable);
909}
910
911SOC_U::~SOC_U() {
912 CleanupSockets();
913#ifdef _WIN32
914 WSACleanup();
915#endif
916}
917
918} // namespace SOC
919} // namespace Service
diff --git a/src/core/hle/service/soc_u.h b/src/core/hle/service/soc_u.h
deleted file mode 100644
index 5f829fc1c..000000000
--- a/src/core/hle/service/soc_u.h
+++ /dev/null
@@ -1,24 +0,0 @@
1// Copyright 2014 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 "core/hle/service/service.h"
9
10namespace Service {
11namespace SOC {
12
13class SOC_U final : public Interface {
14public:
15 SOC_U();
16 ~SOC_U();
17
18 std::string GetPortName() const override {
19 return "soc:U";
20 }
21};
22
23} // namespace SOC
24} // namespace Service
diff --git a/src/core/hle/service/ssl_c.cpp b/src/core/hle/service/ssl_c.cpp
deleted file mode 100644
index 300acca75..000000000
--- a/src/core/hle/service/ssl_c.cpp
+++ /dev/null
@@ -1,92 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <random>
6#include "common/common_types.h"
7#include "core/hle/ipc.h"
8#include "core/hle/service/ssl_c.h"
9#include "core/memory.h"
10
11namespace Service {
12namespace SSL {
13
14// TODO: Implement a proper CSPRNG in the future when actual security is needed
15static std::mt19937 rand_gen;
16
17static void Initialize(Interface* self) {
18 u32* cmd_buff = Kernel::GetCommandBuffer();
19
20 // Seed random number generator when the SSL service is initialized
21 std::random_device rand_device;
22 rand_gen.seed(rand_device());
23
24 // Stub, return success
25 cmd_buff[1] = RESULT_SUCCESS.raw;
26}
27
28static void GenerateRandomData(Interface* self) {
29 u32* cmd_buff = Kernel::GetCommandBuffer();
30
31 u32 size = cmd_buff[1];
32 VAddr address = cmd_buff[3];
33
34 // Fill the output buffer with random data.
35 u32 data = 0;
36 u32 i = 0;
37 while (i < size) {
38 if ((i % 4) == 0) {
39 // The random number generator returns 4 bytes worth of data, so generate new random
40 // data when i == 0 and when i is divisible by 4
41 data = rand_gen();
42 }
43
44 if (size > 4) {
45 // Use up the entire 4 bytes of the random data for as long as possible
46 Memory::Write32(address + i, data);
47 i += 4;
48 } else if (size == 2) {
49 Memory::Write16(address + i, static_cast<u16>(data & 0xffff));
50 i += 2;
51 } else {
52 Memory::Write8(address + i, static_cast<u8>(data & 0xff));
53 i++;
54 }
55 }
56
57 // Stub, return success
58 cmd_buff[1] = RESULT_SUCCESS.raw;
59}
60
61const Interface::FunctionInfo FunctionTable[] = {
62 {0x00010002, Initialize, "Initialize"},
63 {0x000200C2, nullptr, "CreateContext"},
64 {0x00030000, nullptr, "CreateRootCertChain"},
65 {0x00040040, nullptr, "DestroyRootCertChain"},
66 {0x00050082, nullptr, "AddTrustedRootCA"},
67 {0x00060080, nullptr, "RootCertChainAddDefaultCert"},
68 {0x00070080, nullptr, "RootCertChainRemoveCert"},
69 {0x000D0084, nullptr, "OpenClientCertContext"},
70 {0x000E0040, nullptr, "OpenDefaultClientCertContext"},
71 {0x000F0040, nullptr, "CloseClientCertContext"},
72 {0x00110042, GenerateRandomData, "GenerateRandomData"},
73 {0x00120042, nullptr, "InitializeConnectionSession"},
74 {0x00130040, nullptr, "StartConnection"},
75 {0x00140040, nullptr, "StartConnectionGetOut"},
76 {0x00150082, nullptr, "Read"},
77 {0x00160082, nullptr, "ReadPeek"},
78 {0x00170082, nullptr, "Write"},
79 {0x00180080, nullptr, "ContextSetRootCertChain"},
80 {0x00190080, nullptr, "ContextSetClientCert"},
81 {0x001B0080, nullptr, "ContextClearOpt"},
82 {0x001C00C4, nullptr, "ContextGetProtocolCipher"},
83 {0x001E0040, nullptr, "DestroyContext"},
84 {0x001F0082, nullptr, "ContextInitSharedmem"},
85};
86
87SSL_C::SSL_C() {
88 Register(FunctionTable);
89}
90
91} // namespace SSL_C
92} // namespace Service
diff --git a/src/core/hle/service/ssl_c.h b/src/core/hle/service/ssl_c.h
deleted file mode 100644
index fc50a2eb2..000000000
--- a/src/core/hle/service/ssl_c.h
+++ /dev/null
@@ -1,22 +0,0 @@
1// Copyright 2014 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 "core/hle/service/service.h"
8
9namespace Service {
10namespace SSL {
11
12class SSL_C final : public Interface {
13public:
14 SSL_C();
15
16 std::string GetPortName() const override {
17 return "ssl:C";
18 }
19};
20
21} // namespace SSL
22} // namespace Service
diff --git a/src/core/hle/service/y2r_u.cpp b/src/core/hle/service/y2r_u.cpp
deleted file mode 100644
index 57172ddd6..000000000
--- a/src/core/hle/service/y2r_u.cpp
+++ /dev/null
@@ -1,787 +0,0 @@
1// Copyright 2015 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/common_funcs.h"
7#include "common/common_types.h"
8#include "common/logging/log.h"
9#include "core/hle/ipc.h"
10#include "core/hle/ipc_helpers.h"
11#include "core/hle/kernel/event.h"
12#include "core/hle/kernel/kernel.h"
13#include "core/hle/service/y2r_u.h"
14#include "core/hw/y2r.h"
15
16namespace Service {
17namespace Y2R {
18
19struct ConversionParameters {
20 InputFormat input_format;
21 OutputFormat output_format;
22 Rotation rotation;
23 BlockAlignment block_alignment;
24 u16 input_line_width;
25 u16 input_lines;
26 StandardCoefficient standard_coefficient;
27 u8 padding;
28 u16 alpha;
29};
30static_assert(sizeof(ConversionParameters) == 12, "ConversionParameters struct has incorrect size");
31
32static Kernel::SharedPtr<Kernel::Event> completion_event;
33static ConversionConfiguration conversion;
34static DitheringWeightParams dithering_weight_params;
35static u32 temporal_dithering_enabled = 0;
36static u32 transfer_end_interrupt_enabled = 0;
37static u32 spacial_dithering_enabled = 0;
38
39static const CoefficientSet standard_coefficients[4] = {
40 {{0x100, 0x166, 0xB6, 0x58, 0x1C5, -0x166F, 0x10EE, -0x1C5B}}, // ITU_Rec601
41 {{0x100, 0x193, 0x77, 0x2F, 0x1DB, -0x1933, 0xA7C, -0x1D51}}, // ITU_Rec709
42 {{0x12A, 0x198, 0xD0, 0x64, 0x204, -0x1BDE, 0x10F2, -0x229B}}, // ITU_Rec601_Scaling
43 {{0x12A, 0x1CA, 0x88, 0x36, 0x21C, -0x1F04, 0x99C, -0x2421}}, // ITU_Rec709_Scaling
44};
45
46ResultCode ConversionConfiguration::SetInputLineWidth(u16 width) {
47 if (width == 0 || width > 1024 || width % 8 != 0) {
48 return ResultCode(ErrorDescription::OutOfRange, ErrorModule::CAM,
49 ErrorSummary::InvalidArgument, ErrorLevel::Usage); // 0xE0E053FD
50 }
51
52 // Note: The hardware uses the register value 0 to represent a width of 1024, so for a width of
53 // 1024 the `camera` module would set the value 0 here, but we don't need to emulate this
54 // internal detail.
55 this->input_line_width = width;
56 return RESULT_SUCCESS;
57}
58
59ResultCode ConversionConfiguration::SetInputLines(u16 lines) {
60 if (lines == 0 || lines > 1024) {
61 return ResultCode(ErrorDescription::OutOfRange, ErrorModule::CAM,
62 ErrorSummary::InvalidArgument, ErrorLevel::Usage); // 0xE0E053FD
63 }
64
65 // Note: In what appears to be a bug, the `camera` module does not set the hardware register at
66 // all if `lines` is 1024, so the conversion uses the last value that was set. The intention
67 // was probably to set it to 0 like in SetInputLineWidth.
68 if (lines != 1024) {
69 this->input_lines = lines;
70 }
71 return RESULT_SUCCESS;
72}
73
74ResultCode ConversionConfiguration::SetStandardCoefficient(
75 StandardCoefficient standard_coefficient) {
76 size_t index = static_cast<size_t>(standard_coefficient);
77 if (index >= ARRAY_SIZE(standard_coefficients)) {
78 return ResultCode(ErrorDescription::InvalidEnumValue, ErrorModule::CAM,
79 ErrorSummary::InvalidArgument, ErrorLevel::Usage); // 0xE0E053ED
80 }
81
82 std::memcpy(coefficients.data(), standard_coefficients[index].data(), sizeof(coefficients));
83 return RESULT_SUCCESS;
84}
85
86static void SetInputFormat(Interface* self) {
87 u32* cmd_buff = Kernel::GetCommandBuffer();
88
89 conversion.input_format = static_cast<InputFormat>(cmd_buff[1]);
90
91 cmd_buff[0] = IPC::MakeHeader(0x1, 1, 0);
92 cmd_buff[1] = RESULT_SUCCESS.raw;
93
94 LOG_DEBUG(Service_Y2R, "called input_format=%hhu", conversion.input_format);
95}
96
97static void GetInputFormat(Interface* self) {
98 u32* cmd_buff = Kernel::GetCommandBuffer();
99
100 cmd_buff[0] = IPC::MakeHeader(0x2, 2, 0);
101 cmd_buff[1] = RESULT_SUCCESS.raw;
102 cmd_buff[2] = static_cast<u32>(conversion.input_format);
103
104 LOG_DEBUG(Service_Y2R, "called input_format=%hhu", conversion.input_format);
105}
106
107static void SetOutputFormat(Interface* self) {
108 u32* cmd_buff = Kernel::GetCommandBuffer();
109
110 conversion.output_format = static_cast<OutputFormat>(cmd_buff[1]);
111
112 cmd_buff[0] = IPC::MakeHeader(0x3, 1, 0);
113 cmd_buff[1] = RESULT_SUCCESS.raw;
114
115 LOG_DEBUG(Service_Y2R, "called output_format=%hhu", conversion.output_format);
116}
117
118static void GetOutputFormat(Interface* self) {
119 u32* cmd_buff = Kernel::GetCommandBuffer();
120
121 cmd_buff[0] = IPC::MakeHeader(0x4, 2, 0);
122 cmd_buff[1] = RESULT_SUCCESS.raw;
123 cmd_buff[2] = static_cast<u32>(conversion.output_format);
124
125 LOG_DEBUG(Service_Y2R, "called output_format=%hhu", conversion.output_format);
126}
127
128static void SetRotation(Interface* self) {
129 u32* cmd_buff = Kernel::GetCommandBuffer();
130
131 conversion.rotation = static_cast<Rotation>(cmd_buff[1]);
132
133 cmd_buff[0] = IPC::MakeHeader(0x5, 1, 0);
134 cmd_buff[1] = RESULT_SUCCESS.raw;
135
136 LOG_DEBUG(Service_Y2R, "called rotation=%hhu", conversion.rotation);
137}
138
139static void GetRotation(Interface* self) {
140 u32* cmd_buff = Kernel::GetCommandBuffer();
141
142 cmd_buff[0] = IPC::MakeHeader(0x6, 2, 0);
143 cmd_buff[1] = RESULT_SUCCESS.raw;
144 cmd_buff[2] = static_cast<u32>(conversion.rotation);
145
146 LOG_DEBUG(Service_Y2R, "called rotation=%hhu", conversion.rotation);
147}
148
149static void SetBlockAlignment(Interface* self) {
150 u32* cmd_buff = Kernel::GetCommandBuffer();
151
152 conversion.block_alignment = static_cast<BlockAlignment>(cmd_buff[1]);
153
154 cmd_buff[0] = IPC::MakeHeader(0x7, 1, 0);
155 cmd_buff[1] = RESULT_SUCCESS.raw;
156
157 LOG_DEBUG(Service_Y2R, "called block_alignment=%hhu", conversion.block_alignment);
158}
159
160static void GetBlockAlignment(Interface* self) {
161 u32* cmd_buff = Kernel::GetCommandBuffer();
162
163 cmd_buff[0] = IPC::MakeHeader(0x8, 2, 0);
164 cmd_buff[1] = RESULT_SUCCESS.raw;
165 cmd_buff[2] = static_cast<u32>(conversion.block_alignment);
166
167 LOG_DEBUG(Service_Y2R, "called block_alignment=%hhu", conversion.block_alignment);
168}
169
170/**
171 * Y2R_U::SetSpacialDithering service function
172 * Inputs:
173 * 1 : u8, 0 = Disabled, 1 = Enabled
174 * Outputs:
175 * 1 : Result of function, 0 on success, otherwise error code
176 */
177static void SetSpacialDithering(Interface* self) {
178 u32* cmd_buff = Kernel::GetCommandBuffer();
179 spacial_dithering_enabled = cmd_buff[1] & 0xF;
180
181 cmd_buff[0] = IPC::MakeHeader(0x9, 1, 0);
182 cmd_buff[1] = RESULT_SUCCESS.raw;
183
184 LOG_WARNING(Service_Y2R, "(STUBBED) called");
185}
186
187/**
188 * Y2R_U::GetSpacialDithering service function
189 * Outputs:
190 * 1 : Result of function, 0 on success, otherwise error code
191 * 2 : u8, 0 = Disabled, 1 = Enabled
192 */
193static void GetSpacialDithering(Interface* self) {
194 IPC::RequestBuilder rb(Kernel::GetCommandBuffer(), 0xA, 2, 0);
195 rb.Push(RESULT_SUCCESS);
196 rb.Push(spacial_dithering_enabled != 0);
197
198 LOG_WARNING(Service_Y2R, "(STUBBED) called");
199}
200
201/**
202 * Y2R_U::SetTemporalDithering service function
203 * Inputs:
204 * 1 : u8, 0 = Disabled, 1 = Enabled
205 * Outputs:
206 * 1 : Result of function, 0 on success, otherwise error code
207 */
208static void SetTemporalDithering(Interface* self) {
209 u32* cmd_buff = Kernel::GetCommandBuffer();
210 temporal_dithering_enabled = cmd_buff[1] & 0xF;
211
212 cmd_buff[0] = IPC::MakeHeader(0xB, 1, 0);
213 cmd_buff[1] = RESULT_SUCCESS.raw;
214
215 LOG_WARNING(Service_Y2R, "(STUBBED) called");
216}
217
218/**
219 * Y2R_U::GetTemporalDithering service function
220 * Outputs:
221 * 1 : Result of function, 0 on success, otherwise error code
222 * 2 : u8, 0 = Disabled, 1 = Enabled
223 */
224static void GetTemporalDithering(Interface* self) {
225 u32* cmd_buff = Kernel::GetCommandBuffer();
226
227 cmd_buff[0] = IPC::MakeHeader(0xC, 2, 0);
228 cmd_buff[1] = RESULT_SUCCESS.raw;
229 cmd_buff[2] = temporal_dithering_enabled;
230
231 LOG_WARNING(Service_Y2R, "(STUBBED) called");
232}
233
234/**
235 * Y2R_U::SetTransferEndInterrupt service function
236 * Inputs:
237 * 1 : u8, 0 = Disabled, 1 = Enabled
238 * Outputs:
239 * 1 : Result of function, 0 on success, otherwise error code
240 */
241static void SetTransferEndInterrupt(Interface* self) {
242 u32* cmd_buff = Kernel::GetCommandBuffer();
243 transfer_end_interrupt_enabled = cmd_buff[1] & 0xf;
244
245 cmd_buff[0] = IPC::MakeHeader(0xD, 1, 0);
246 cmd_buff[1] = RESULT_SUCCESS.raw;
247
248 LOG_WARNING(Service_Y2R, "(STUBBED) called");
249}
250
251/**
252 * Y2R_U::GetTransferEndInterrupt service function
253 * Outputs:
254 * 1 : Result of function, 0 on success, otherwise error code
255 * 2 : u8, 0 = Disabled, 1 = Enabled
256 */
257static void GetTransferEndInterrupt(Interface* self) {
258 u32* cmd_buff = Kernel::GetCommandBuffer();
259
260 cmd_buff[0] = IPC::MakeHeader(0xE, 2, 0);
261 cmd_buff[1] = RESULT_SUCCESS.raw;
262 cmd_buff[2] = transfer_end_interrupt_enabled;
263
264 LOG_WARNING(Service_Y2R, "(STUBBED) called");
265}
266
267/**
268 * Y2R_U::GetTransferEndEvent service function
269 * Outputs:
270 * 1 : Result of function, 0 on success, otherwise error code
271 * 3 : The handle of the completion event
272 */
273static void GetTransferEndEvent(Interface* self) {
274 u32* cmd_buff = Kernel::GetCommandBuffer();
275
276 cmd_buff[0] = IPC::MakeHeader(0xF, 2, 0);
277 cmd_buff[1] = RESULT_SUCCESS.raw;
278 cmd_buff[3] = Kernel::g_handle_table.Create(completion_event).Unwrap();
279
280 LOG_DEBUG(Service_Y2R, "called");
281}
282
283static void SetSendingY(Interface* self) {
284 // The helper should be passed by argument to the function
285 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x00100102);
286 conversion.src_Y.address = rp.Pop<u32>();
287 conversion.src_Y.image_size = rp.Pop<u32>();
288 conversion.src_Y.transfer_unit = rp.Pop<u32>();
289 conversion.src_Y.gap = rp.Pop<u32>();
290 Kernel::Handle src_process_handle = rp.PopHandle();
291
292 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
293 rb.Push(RESULT_SUCCESS);
294
295 LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, "
296 "src_process_handle=0x%08X",
297 conversion.src_Y.image_size, conversion.src_Y.transfer_unit, conversion.src_Y.gap,
298 src_process_handle);
299}
300
301static void SetSendingU(Interface* self) {
302 // The helper should be passed by argument to the function
303 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x00110102);
304 conversion.src_U.address = rp.Pop<u32>();
305 conversion.src_U.image_size = rp.Pop<u32>();
306 conversion.src_U.transfer_unit = rp.Pop<u32>();
307 conversion.src_U.gap = rp.Pop<u32>();
308 Kernel::Handle src_process_handle = rp.PopHandle();
309
310 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
311 rb.Push(RESULT_SUCCESS);
312
313 LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, "
314 "src_process_handle=0x%08X",
315 conversion.src_U.image_size, conversion.src_U.transfer_unit, conversion.src_U.gap,
316 src_process_handle);
317}
318
319static void SetSendingV(Interface* self) {
320 u32* cmd_buff = Kernel::GetCommandBuffer();
321
322 conversion.src_V.address = cmd_buff[1];
323 conversion.src_V.image_size = cmd_buff[2];
324 conversion.src_V.transfer_unit = cmd_buff[3];
325 conversion.src_V.gap = cmd_buff[4];
326
327 cmd_buff[0] = IPC::MakeHeader(0x12, 1, 0);
328 cmd_buff[1] = RESULT_SUCCESS.raw;
329
330 LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, "
331 "src_process_handle=0x%08X",
332 conversion.src_V.image_size, conversion.src_V.transfer_unit, conversion.src_V.gap,
333 cmd_buff[6]);
334}
335
336static void SetSendingYUYV(Interface* self) {
337 u32* cmd_buff = Kernel::GetCommandBuffer();
338
339 conversion.src_YUYV.address = cmd_buff[1];
340 conversion.src_YUYV.image_size = cmd_buff[2];
341 conversion.src_YUYV.transfer_unit = cmd_buff[3];
342 conversion.src_YUYV.gap = cmd_buff[4];
343
344 cmd_buff[0] = IPC::MakeHeader(0x13, 1, 0);
345 cmd_buff[1] = RESULT_SUCCESS.raw;
346
347 LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, "
348 "src_process_handle=0x%08X",
349 conversion.src_YUYV.image_size, conversion.src_YUYV.transfer_unit,
350 conversion.src_YUYV.gap, cmd_buff[6]);
351}
352
353/**
354 * Y2R::IsFinishedSendingYuv service function
355 * Output:
356 * 1 : Result of the function, 0 on success, otherwise error code
357 * 2 : u8, 0 = Not Finished, 1 = Finished
358 */
359static void IsFinishedSendingYuv(Interface* self) {
360 u32* cmd_buff = Kernel::GetCommandBuffer();
361
362 cmd_buff[0] = IPC::MakeHeader(0x14, 2, 0);
363 cmd_buff[1] = RESULT_SUCCESS.raw;
364 cmd_buff[2] = 1;
365
366 LOG_WARNING(Service_Y2R, "(STUBBED) called");
367}
368
369/**
370 * Y2R::IsFinishedSendingY service function
371 * Output:
372 * 1 : Result of the function, 0 on success, otherwise error code
373 * 2 : u8, 0 = Not Finished, 1 = Finished
374 */
375static void IsFinishedSendingY(Interface* self) {
376 u32* cmd_buff = Kernel::GetCommandBuffer();
377
378 cmd_buff[0] = IPC::MakeHeader(0x15, 2, 0);
379 cmd_buff[1] = RESULT_SUCCESS.raw;
380 cmd_buff[2] = 1;
381
382 LOG_WARNING(Service_Y2R, "(STUBBED) called");
383}
384
385/**
386 * Y2R::IsFinishedSendingU service function
387 * Output:
388 * 1 : Result of the function, 0 on success, otherwise error code
389 * 2 : u8, 0 = Not Finished, 1 = Finished
390 */
391static void IsFinishedSendingU(Interface* self) {
392 u32* cmd_buff = Kernel::GetCommandBuffer();
393
394 cmd_buff[0] = IPC::MakeHeader(0x16, 2, 0);
395 cmd_buff[1] = RESULT_SUCCESS.raw;
396 cmd_buff[2] = 1;
397
398 LOG_WARNING(Service_Y2R, "(STUBBED) called");
399}
400
401/**
402 * Y2R::IsFinishedSendingV service function
403 * Output:
404 * 1 : Result of the function, 0 on success, otherwise error code
405 * 2 : u8, 0 = Not Finished, 1 = Finished
406 */
407static void IsFinishedSendingV(Interface* self) {
408 u32* cmd_buff = Kernel::GetCommandBuffer();
409
410 cmd_buff[0] = IPC::MakeHeader(0x17, 2, 0);
411 cmd_buff[1] = RESULT_SUCCESS.raw;
412 cmd_buff[2] = 1;
413
414 LOG_WARNING(Service_Y2R, "(STUBBED) called");
415}
416
417static void SetReceiving(Interface* self) {
418 u32* cmd_buff = Kernel::GetCommandBuffer();
419
420 conversion.dst.address = cmd_buff[1];
421 conversion.dst.image_size = cmd_buff[2];
422 conversion.dst.transfer_unit = cmd_buff[3];
423 conversion.dst.gap = cmd_buff[4];
424
425 cmd_buff[0] = IPC::MakeHeader(0x18, 1, 0);
426 cmd_buff[1] = RESULT_SUCCESS.raw;
427
428 LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, "
429 "dst_process_handle=0x%08X",
430 conversion.dst.image_size, conversion.dst.transfer_unit, conversion.dst.gap,
431 cmd_buff[6]);
432}
433
434/**
435 * Y2R::IsFinishedReceiving service function
436 * Output:
437 * 1 : Result of the function, 0 on success, otherwise error code
438 * 2 : u8, 0 = Not Finished, 1 = Finished
439 */
440static void IsFinishedReceiving(Interface* self) {
441 u32* cmd_buff = Kernel::GetCommandBuffer();
442
443 cmd_buff[0] = IPC::MakeHeader(0x19, 2, 0);
444 cmd_buff[1] = RESULT_SUCCESS.raw;
445 cmd_buff[2] = 1;
446
447 LOG_WARNING(Service_Y2R, "(STUBBED) called");
448}
449
450static void SetInputLineWidth(Interface* self) {
451 u32* cmd_buff = Kernel::GetCommandBuffer();
452
453 cmd_buff[0] = IPC::MakeHeader(0x1A, 1, 0);
454 cmd_buff[1] = conversion.SetInputLineWidth(cmd_buff[1]).raw;
455
456 LOG_DEBUG(Service_Y2R, "called input_line_width=%u", cmd_buff[1]);
457}
458
459static void GetInputLineWidth(Interface* self) {
460 u32* cmd_buff = Kernel::GetCommandBuffer();
461
462 cmd_buff[0] = IPC::MakeHeader(0x1B, 2, 0);
463 cmd_buff[1] = RESULT_SUCCESS.raw;
464 cmd_buff[2] = conversion.input_line_width;
465
466 LOG_DEBUG(Service_Y2R, "called input_line_width=%u", conversion.input_line_width);
467}
468
469static void SetInputLines(Interface* self) {
470 u32* cmd_buff = Kernel::GetCommandBuffer();
471
472 cmd_buff[0] = IPC::MakeHeader(0x1C, 1, 0);
473 cmd_buff[1] = conversion.SetInputLines(cmd_buff[1]).raw;
474
475 LOG_DEBUG(Service_Y2R, "called input_lines=%u", cmd_buff[1]);
476}
477
478static void GetInputLines(Interface* self) {
479 u32* cmd_buff = Kernel::GetCommandBuffer();
480
481 cmd_buff[0] = IPC::MakeHeader(0x1D, 2, 0);
482 cmd_buff[1] = RESULT_SUCCESS.raw;
483 cmd_buff[2] = static_cast<u32>(conversion.input_lines);
484
485 LOG_DEBUG(Service_Y2R, "called input_lines=%u", conversion.input_lines);
486}
487
488static void SetCoefficient(Interface* self) {
489 u32* cmd_buff = Kernel::GetCommandBuffer();
490
491 const u16* coefficients = reinterpret_cast<const u16*>(&cmd_buff[1]);
492 std::memcpy(conversion.coefficients.data(), coefficients, sizeof(CoefficientSet));
493
494 cmd_buff[0] = IPC::MakeHeader(0x1E, 1, 0);
495 cmd_buff[1] = RESULT_SUCCESS.raw;
496
497 LOG_DEBUG(Service_Y2R, "called coefficients=[%hX, %hX, %hX, %hX, %hX, %hX, %hX, %hX]",
498 coefficients[0], coefficients[1], coefficients[2], coefficients[3], coefficients[4],
499 coefficients[5], coefficients[6], coefficients[7]);
500}
501
502static void GetCoefficient(Interface* self) {
503 u32* cmd_buff = Kernel::GetCommandBuffer();
504
505 cmd_buff[0] = IPC::MakeHeader(0x1F, 5, 0);
506 cmd_buff[1] = RESULT_SUCCESS.raw;
507 std::memcpy(&cmd_buff[2], conversion.coefficients.data(), sizeof(CoefficientSet));
508
509 LOG_DEBUG(Service_Y2R, "called");
510}
511
512static void SetStandardCoefficient(Interface* self) {
513 u32* cmd_buff = Kernel::GetCommandBuffer();
514
515 u32 index = cmd_buff[1];
516
517 cmd_buff[0] = IPC::MakeHeader(0x20, 1, 0);
518 cmd_buff[1] = conversion.SetStandardCoefficient((StandardCoefficient)index).raw;
519
520 LOG_DEBUG(Service_Y2R, "called standard_coefficient=%u", index);
521}
522
523static void GetStandardCoefficient(Interface* self) {
524 u32* cmd_buff = Kernel::GetCommandBuffer();
525
526 u32 index = cmd_buff[1];
527
528 if (index < ARRAY_SIZE(standard_coefficients)) {
529 cmd_buff[0] = IPC::MakeHeader(0x21, 5, 0);
530 cmd_buff[1] = RESULT_SUCCESS.raw;
531 std::memcpy(&cmd_buff[2], &standard_coefficients[index], sizeof(CoefficientSet));
532
533 LOG_DEBUG(Service_Y2R, "called standard_coefficient=%u ", index);
534 } else {
535 cmd_buff[0] = IPC::MakeHeader(0x21, 1, 0);
536 cmd_buff[1] = ResultCode(ErrorDescription::InvalidEnumValue, ErrorModule::CAM,
537 ErrorSummary::InvalidArgument, ErrorLevel::Usage)
538 .raw;
539
540 LOG_ERROR(Service_Y2R, "called standard_coefficient=%u The argument is invalid!", index);
541 }
542}
543
544static void SetAlpha(Interface* self) {
545 u32* cmd_buff = Kernel::GetCommandBuffer();
546
547 conversion.alpha = cmd_buff[1];
548
549 cmd_buff[0] = IPC::MakeHeader(0x22, 1, 0);
550 cmd_buff[1] = RESULT_SUCCESS.raw;
551
552 LOG_DEBUG(Service_Y2R, "called alpha=%hu", conversion.alpha);
553}
554
555static void GetAlpha(Interface* self) {
556 u32* cmd_buff = Kernel::GetCommandBuffer();
557
558 cmd_buff[0] = IPC::MakeHeader(0x23, 2, 0);
559 cmd_buff[1] = RESULT_SUCCESS.raw;
560 cmd_buff[2] = conversion.alpha;
561
562 LOG_DEBUG(Service_Y2R, "called alpha=%hu", conversion.alpha);
563}
564
565static void SetDitheringWeightParams(Interface* self) {
566 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x24, 8, 0); // 0x240200
567 rp.PopRaw(dithering_weight_params);
568 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
569 rb.Push(RESULT_SUCCESS);
570
571 LOG_DEBUG(Service_Y2R, "called");
572}
573
574static void GetDitheringWeightParams(Interface* self) {
575 u32* cmd_buff = Kernel::GetCommandBuffer();
576
577 cmd_buff[0] = IPC::MakeHeader(0x25, 9, 0);
578 cmd_buff[1] = RESULT_SUCCESS.raw;
579 std::memcpy(&cmd_buff[2], &dithering_weight_params, sizeof(DitheringWeightParams));
580
581 LOG_DEBUG(Service_Y2R, "called");
582}
583
584static void StartConversion(Interface* self) {
585 u32* cmd_buff = Kernel::GetCommandBuffer();
586
587 // dst_image_size would seem to be perfect for this, but it doesn't include the gap :(
588 u32 total_output_size =
589 conversion.input_lines * (conversion.dst.transfer_unit + conversion.dst.gap);
590 Memory::RasterizerFlushVirtualRegion(conversion.dst.address, total_output_size,
591 Memory::FlushMode::FlushAndInvalidate);
592
593 HW::Y2R::PerformConversion(conversion);
594
595 completion_event->Signal();
596
597 cmd_buff[0] = IPC::MakeHeader(0x26, 1, 0);
598 cmd_buff[1] = RESULT_SUCCESS.raw;
599
600 LOG_DEBUG(Service_Y2R, "called");
601}
602
603static void StopConversion(Interface* self) {
604 u32* cmd_buff = Kernel::GetCommandBuffer();
605
606 cmd_buff[0] = IPC::MakeHeader(0x27, 1, 0);
607 cmd_buff[1] = RESULT_SUCCESS.raw;
608
609 LOG_DEBUG(Service_Y2R, "called");
610}
611
612/**
613 * Y2R_U::IsBusyConversion service function
614 * Outputs:
615 * 1 : Result of function, 0 on success, otherwise error code
616 * 2 : 1 if there's a conversion running, otherwise 0.
617 */
618static void IsBusyConversion(Interface* self) {
619 u32* cmd_buff = Kernel::GetCommandBuffer();
620
621 cmd_buff[0] = IPC::MakeHeader(0x28, 2, 0);
622 cmd_buff[1] = RESULT_SUCCESS.raw;
623 cmd_buff[2] = 0; // StartConversion always finishes immediately
624
625 LOG_DEBUG(Service_Y2R, "called");
626}
627
628/**
629 * Y2R_U::SetPackageParameter service function
630 */
631static void SetPackageParameter(Interface* self) {
632 u32* cmd_buff = Kernel::GetCommandBuffer();
633
634 auto params = reinterpret_cast<const ConversionParameters*>(&cmd_buff[1]);
635
636 conversion.input_format = params->input_format;
637 conversion.output_format = params->output_format;
638 conversion.rotation = params->rotation;
639 conversion.block_alignment = params->block_alignment;
640
641 ResultCode result = conversion.SetInputLineWidth(params->input_line_width);
642
643 if (result.IsError())
644 goto cleanup;
645
646 result = conversion.SetInputLines(params->input_lines);
647
648 if (result.IsError())
649 goto cleanup;
650
651 result = conversion.SetStandardCoefficient(params->standard_coefficient);
652
653 if (result.IsError())
654 goto cleanup;
655
656 conversion.padding = params->padding;
657 conversion.alpha = params->alpha;
658
659cleanup:
660 cmd_buff[0] = IPC::MakeHeader(0x29, 1, 0);
661 cmd_buff[1] = result.raw;
662
663 LOG_DEBUG(
664 Service_Y2R,
665 "called input_format=%hhu output_format=%hhu rotation=%hhu block_alignment=%hhu "
666 "input_line_width=%hu input_lines=%hu standard_coefficient=%hhu reserved=%hhu alpha=%hX",
667 params->input_format, params->output_format, params->rotation, params->block_alignment,
668 params->input_line_width, params->input_lines, params->standard_coefficient,
669 params->padding, params->alpha);
670}
671
672static void PingProcess(Interface* self) {
673 u32* cmd_buff = Kernel::GetCommandBuffer();
674
675 cmd_buff[0] = IPC::MakeHeader(0x2A, 2, 0);
676 cmd_buff[1] = RESULT_SUCCESS.raw;
677 cmd_buff[2] = 0;
678
679 LOG_WARNING(Service_Y2R, "(STUBBED) called");
680}
681
682static void DriverInitialize(Interface* self) {
683 u32* cmd_buff = Kernel::GetCommandBuffer();
684
685 conversion.input_format = InputFormat::YUV422_Indiv8;
686 conversion.output_format = OutputFormat::RGBA8;
687 conversion.rotation = Rotation::None;
688 conversion.block_alignment = BlockAlignment::Linear;
689 conversion.coefficients.fill(0);
690 conversion.SetInputLineWidth(1024);
691 conversion.SetInputLines(1024);
692 conversion.alpha = 0;
693
694 ConversionBuffer zero_buffer = {};
695 conversion.src_Y = zero_buffer;
696 conversion.src_U = zero_buffer;
697 conversion.src_V = zero_buffer;
698 conversion.dst = zero_buffer;
699
700 completion_event->Clear();
701
702 cmd_buff[0] = IPC::MakeHeader(0x2B, 1, 0);
703 cmd_buff[1] = RESULT_SUCCESS.raw;
704
705 LOG_DEBUG(Service_Y2R, "called");
706}
707
708static void DriverFinalize(Interface* self) {
709 u32* cmd_buff = Kernel::GetCommandBuffer();
710
711 cmd_buff[0] = IPC::MakeHeader(0x2C, 1, 0);
712 cmd_buff[1] = RESULT_SUCCESS.raw;
713
714 LOG_DEBUG(Service_Y2R, "called");
715}
716
717static void GetPackageParameter(Interface* self) {
718 u32* cmd_buff = Kernel::GetCommandBuffer();
719
720 cmd_buff[0] = IPC::MakeHeader(0x2D, 4, 0);
721 cmd_buff[1] = RESULT_SUCCESS.raw;
722 std::memcpy(&cmd_buff[2], &conversion, sizeof(ConversionParameters));
723
724 LOG_DEBUG(Service_Y2R, "called");
725}
726
727const Interface::FunctionInfo FunctionTable[] = {
728 {0x00010040, SetInputFormat, "SetInputFormat"},
729 {0x00020000, GetInputFormat, "GetInputFormat"},
730 {0x00030040, SetOutputFormat, "SetOutputFormat"},
731 {0x00040000, GetOutputFormat, "GetOutputFormat"},
732 {0x00050040, SetRotation, "SetRotation"},
733 {0x00060000, GetRotation, "GetRotation"},
734 {0x00070040, SetBlockAlignment, "SetBlockAlignment"},
735 {0x00080000, GetBlockAlignment, "GetBlockAlignment"},
736 {0x00090040, SetSpacialDithering, "SetSpacialDithering"},
737 {0x000A0000, GetSpacialDithering, "GetSpacialDithering"},
738 {0x000B0040, SetTemporalDithering, "SetTemporalDithering"},
739 {0x000C0000, GetTemporalDithering, "GetTemporalDithering"},
740 {0x000D0040, SetTransferEndInterrupt, "SetTransferEndInterrupt"},
741 {0x000E0000, GetTransferEndInterrupt, "GetTransferEndInterrupt"},
742 {0x000F0000, GetTransferEndEvent, "GetTransferEndEvent"},
743 {0x00100102, SetSendingY, "SetSendingY"},
744 {0x00110102, SetSendingU, "SetSendingU"},
745 {0x00120102, SetSendingV, "SetSendingV"},
746 {0x00130102, SetSendingYUYV, "SetSendingYUYV"},
747 {0x00140000, IsFinishedSendingYuv, "IsFinishedSendingYuv"},
748 {0x00150000, IsFinishedSendingY, "IsFinishedSendingY"},
749 {0x00160000, IsFinishedSendingU, "IsFinishedSendingU"},
750 {0x00170000, IsFinishedSendingV, "IsFinishedSendingV"},
751 {0x00180102, SetReceiving, "SetReceiving"},
752 {0x00190000, IsFinishedReceiving, "IsFinishedReceiving"},
753 {0x001A0040, SetInputLineWidth, "SetInputLineWidth"},
754 {0x001B0000, GetInputLineWidth, "GetInputLineWidth"},
755 {0x001C0040, SetInputLines, "SetInputLines"},
756 {0x001D0000, GetInputLines, "GetInputLines"},
757 {0x001E0100, SetCoefficient, "SetCoefficient"},
758 {0x001F0000, GetCoefficient, "GetCoefficient"},
759 {0x00200040, SetStandardCoefficient, "SetStandardCoefficient"},
760 {0x00210040, GetStandardCoefficient, "GetStandardCoefficient"},
761 {0x00220040, SetAlpha, "SetAlpha"},
762 {0x00230000, GetAlpha, "GetAlpha"},
763 {0x00240200, SetDitheringWeightParams, "SetDitheringWeightParams"},
764 {0x00250000, GetDitheringWeightParams, "GetDitheringWeightParams"},
765 {0x00260000, StartConversion, "StartConversion"},
766 {0x00270000, StopConversion, "StopConversion"},
767 {0x00280000, IsBusyConversion, "IsBusyConversion"},
768 {0x002901C0, SetPackageParameter, "SetPackageParameter"},
769 {0x002A0000, PingProcess, "PingProcess"},
770 {0x002B0000, DriverInitialize, "DriverInitialize"},
771 {0x002C0000, DriverFinalize, "DriverFinalize"},
772 {0x002D0000, GetPackageParameter, "GetPackageParameter"},
773};
774
775Y2R_U::Y2R_U() {
776 completion_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "Y2R:Completed");
777 std::memset(&conversion, 0, sizeof(conversion));
778
779 Register(FunctionTable);
780}
781
782Y2R_U::~Y2R_U() {
783 completion_event = nullptr;
784}
785
786} // namespace Y2R
787} // namespace Service \ No newline at end of file
diff --git a/src/core/hle/service/y2r_u.h b/src/core/hle/service/y2r_u.h
deleted file mode 100644
index dddeed0be..000000000
--- a/src/core/hle/service/y2r_u.h
+++ /dev/null
@@ -1,139 +0,0 @@
1// Copyright 2014 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 <array>
8#include <string>
9#include "common/common_types.h"
10#include "core/hle/result.h"
11#include "core/hle/service/service.h"
12
13namespace Service {
14namespace Y2R {
15
16enum class InputFormat : u8 {
17 /// 8-bit input, with YUV components in separate planes and 4:2:2 subsampling.
18 YUV422_Indiv8 = 0,
19 /// 8-bit input, with YUV components in separate planes and 4:2:0 subsampling.
20 YUV420_Indiv8 = 1,
21
22 /// 16-bit input (only LSB used), with YUV components in separate planes and 4:2:2 subsampling.
23 YUV422_Indiv16 = 2,
24 /// 16-bit input (only LSB used), with YUV components in separate planes and 4:2:0 subsampling.
25 YUV420_Indiv16 = 3,
26
27 /// 8-bit input, with a single interleaved stream in YUYV format and 4:2:2 subsampling.
28 YUYV422_Interleaved = 4,
29};
30
31enum class OutputFormat : u8 {
32 RGBA8 = 0,
33 RGB8 = 1,
34 RGB5A1 = 2,
35 RGB565 = 3,
36};
37
38enum class Rotation : u8 {
39 None = 0,
40 Clockwise_90 = 1,
41 Clockwise_180 = 2,
42 Clockwise_270 = 3,
43};
44
45enum class BlockAlignment : u8 {
46 /// Image is output in linear format suitable for use as a framebuffer.
47 Linear = 0,
48 /// Image is output in tiled PICA format, suitable for use as a texture.
49 Block8x8 = 1,
50};
51
52enum class StandardCoefficient : u8 {
53 /// ITU Rec. BT.601 primaries, with PC ranges.
54 ITU_Rec601 = 0,
55 /// ITU Rec. BT.709 primaries, with PC ranges.
56 ITU_Rec709 = 1,
57 /// ITU Rec. BT.601 primaries, with TV ranges.
58 ITU_Rec601_Scaling = 2,
59 /// ITU Rec. BT.709 primaries, with TV ranges.
60 ITU_Rec709_Scaling = 3,
61};
62
63/**
64 * A set of coefficients configuring the RGB to YUV conversion. Coefficients 0-4 are unsigned 2.8
65 * fixed pointer numbers representing entries on the conversion matrix, while coefficient 5-7 are
66 * signed 11.5 fixed point numbers added as offsets to the RGB result.
67 *
68 * The overall conversion process formula is:
69 * ```
70 * R = trunc((c_0 * Y + c_1 * V) + c_5 + 0.75)
71 * G = trunc((c_0 * Y - c_3 * U - c_2 * V) + c_6 + 0.75)
72 * B = trunc((c_0 * Y + c_4 * U ) + c_7 + 0.75)
73 * ```
74 */
75using CoefficientSet = std::array<s16, 8>;
76
77struct ConversionBuffer {
78 /// Current reading/writing address of this buffer.
79 VAddr address;
80 /// Remaining amount of bytes to be DMAed, does not include the inter-trasfer gap.
81 u32 image_size;
82 /// Size of a single DMA transfer.
83 u16 transfer_unit;
84 /// Amount of bytes to be skipped between copying each `transfer_unit` bytes.
85 u16 gap;
86};
87
88struct ConversionConfiguration {
89 InputFormat input_format;
90 OutputFormat output_format;
91 Rotation rotation;
92 BlockAlignment block_alignment;
93 u16 input_line_width;
94 u16 input_lines;
95 CoefficientSet coefficients;
96 u8 padding;
97 u16 alpha;
98
99 /// Input parameters for the Y (luma) plane
100 ConversionBuffer src_Y, src_U, src_V, src_YUYV;
101 /// Output parameters for the conversion results
102 ConversionBuffer dst;
103
104 ResultCode SetInputLineWidth(u16 width);
105 ResultCode SetInputLines(u16 lines);
106 ResultCode SetStandardCoefficient(StandardCoefficient standard_coefficient);
107};
108
109struct DitheringWeightParams {
110 u16 w0_xEven_yEven;
111 u16 w0_xOdd_yEven;
112 u16 w0_xEven_yOdd;
113 u16 w0_xOdd_yOdd;
114 u16 w1_xEven_yEven;
115 u16 w1_xOdd_yEven;
116 u16 w1_xEven_yOdd;
117 u16 w1_xOdd_yOdd;
118 u16 w2_xEven_yEven;
119 u16 w2_xOdd_yEven;
120 u16 w2_xEven_yOdd;
121 u16 w2_xOdd_yOdd;
122 u16 w3_xEven_yEven;
123 u16 w3_xOdd_yEven;
124 u16 w3_xEven_yOdd;
125 u16 w3_xOdd_yOdd;
126};
127
128class Y2R_U final : public Interface {
129public:
130 Y2R_U();
131 ~Y2R_U() override;
132
133 std::string GetPortName() const override {
134 return "y2r:u";
135 }
136};
137
138} // namespace Y2R
139} // namespace Service
diff --git a/src/core/hle/shared_page.cpp b/src/core/hle/shared_page.cpp
index 5978ccdd4..833dc5ec9 100644
--- a/src/core/hle/shared_page.cpp
+++ b/src/core/hle/shared_page.cpp
@@ -6,7 +6,6 @@
6#include <cstring> 6#include <cstring>
7#include <ctime> 7#include <ctime>
8#include "core/core_timing.h" 8#include "core/core_timing.h"
9#include "core/hle/service/ptm/ptm.h"
10#include "core/hle/shared_page.h" 9#include "core/hle/shared_page.h"
11 10
12//////////////////////////////////////////////////////////////////////////////////////////////////// 11////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -75,8 +74,6 @@ void Init() {
75 shared_page.unknown_value = 0x1; 74 shared_page.unknown_value = 0x1;
76 75
77 // Set to a completely full battery 76 // Set to a completely full battery
78 shared_page.battery_state.charge_level.Assign(
79 static_cast<u8>(Service::PTM::ChargeLevels::CompletelyFull));
80 shared_page.battery_state.is_adapter_connected.Assign(1); 77 shared_page.battery_state.is_adapter_connected.Assign(1);
81 shared_page.battery_state.is_charging.Assign(1); 78 shared_page.battery_state.is_charging.Assign(1);
82 79
diff --git a/src/core/hw/y2r.cpp b/src/core/hw/y2r.cpp
deleted file mode 100644
index e697f84b3..000000000
--- a/src/core/hw/y2r.cpp
+++ /dev/null
@@ -1,382 +0,0 @@
1// Copyright 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <array>
7#include <cstddef>
8#include <memory>
9#include "common/assert.h"
10#include "common/color.h"
11#include "common/common_types.h"
12#include "common/math_util.h"
13#include "common/vector_math.h"
14#include "core/hle/service/y2r_u.h"
15#include "core/hw/y2r.h"
16#include "core/memory.h"
17
18namespace HW {
19namespace Y2R {
20
21using namespace Service::Y2R;
22
23static const size_t MAX_TILES = 1024 / 8;
24static const size_t TILE_SIZE = 8 * 8;
25using ImageTile = std::array<u32, TILE_SIZE>;
26
27/// Converts a image strip from the source YUV format into individual 8x8 RGB32 tiles.
28static void ConvertYUVToRGB(InputFormat input_format, const u8* input_Y, const u8* input_U,
29 const u8* input_V, ImageTile output[], unsigned int width,
30 unsigned int height, const CoefficientSet& coefficients) {
31
32 for (unsigned int y = 0; y < height; ++y) {
33 for (unsigned int x = 0; x < width; ++x) {
34 s32 Y = 0;
35 s32 U = 0;
36 s32 V = 0;
37 switch (input_format) {
38 case InputFormat::YUV422_Indiv8:
39 case InputFormat::YUV422_Indiv16:
40 Y = input_Y[y * width + x];
41 U = input_U[(y * width + x) / 2];
42 V = input_V[(y * width + x) / 2];
43 break;
44 case InputFormat::YUV420_Indiv8:
45 case InputFormat::YUV420_Indiv16:
46 Y = input_Y[y * width + x];
47 U = input_U[((y / 2) * width + x) / 2];
48 V = input_V[((y / 2) * width + x) / 2];
49 break;
50 case InputFormat::YUYV422_Interleaved:
51 Y = input_Y[(y * width + x) * 2];
52 U = input_Y[(y * width + (x / 2) * 2) * 2 + 1];
53 V = input_Y[(y * width + (x / 2) * 2) * 2 + 3];
54 break;
55 }
56
57 // This conversion process is bit-exact with hardware, as far as could be tested.
58 auto& c = coefficients;
59 s32 cY = c[0] * Y;
60
61 s32 r = cY + c[1] * V;
62 s32 g = cY - c[2] * V - c[3] * U;
63 s32 b = cY + c[4] * U;
64
65 const s32 rounding_offset = 0x18;
66 r = (r >> 3) + c[5] + rounding_offset;
67 g = (g >> 3) + c[6] + rounding_offset;
68 b = (b >> 3) + c[7] + rounding_offset;
69
70 unsigned int tile = x / 8;
71 unsigned int tile_x = x % 8;
72 u32* out = &output[tile][y * 8 + tile_x];
73
74 using MathUtil::Clamp;
75 *out = ((u32)Clamp(r >> 5, 0, 0xFF) << 24) | ((u32)Clamp(g >> 5, 0, 0xFF) << 16) |
76 ((u32)Clamp(b >> 5, 0, 0xFF) << 8);
77 }
78 }
79}
80
81/// Simulates an incoming CDMA transfer. The N parameter is used to automatically convert 16-bit
82/// formats to 8-bit.
83template <size_t N>
84static void ReceiveData(u8* output, ConversionBuffer& buf, size_t amount_of_data) {
85 const u8* input = Memory::GetPointer(buf.address);
86
87 size_t output_unit = buf.transfer_unit / N;
88 ASSERT(amount_of_data % output_unit == 0);
89
90 while (amount_of_data > 0) {
91 for (size_t i = 0; i < output_unit; ++i) {
92 output[i] = input[i * N];
93 }
94
95 output += output_unit;
96 input += buf.transfer_unit + buf.gap;
97
98 buf.address += buf.transfer_unit + buf.gap;
99 buf.image_size -= buf.transfer_unit;
100 amount_of_data -= output_unit;
101 }
102}
103
104/// Convert intermediate RGB32 format to the final output format while simulating an outgoing CDMA
105/// transfer.
106static void SendData(const u32* input, ConversionBuffer& buf, int amount_of_data,
107 OutputFormat output_format, u8 alpha) {
108
109 u8* output = Memory::GetPointer(buf.address);
110
111 while (amount_of_data > 0) {
112 u8* unit_end = output + buf.transfer_unit;
113 while (output < unit_end) {
114 u32 color = *input++;
115 Math::Vec4<u8> col_vec{(u8)(color >> 24), (u8)(color >> 16), (u8)(color >> 8), alpha};
116
117 switch (output_format) {
118 case OutputFormat::RGBA8:
119 Color::EncodeRGBA8(col_vec, output);
120 output += 4;
121 break;
122 case OutputFormat::RGB8:
123 Color::EncodeRGB8(col_vec, output);
124 output += 3;
125 break;
126 case OutputFormat::RGB5A1:
127 Color::EncodeRGB5A1(col_vec, output);
128 output += 2;
129 break;
130 case OutputFormat::RGB565:
131 Color::EncodeRGB565(col_vec, output);
132 output += 2;
133 break;
134 }
135
136 amount_of_data -= 1;
137 }
138
139 output += buf.gap;
140 buf.address += buf.transfer_unit + buf.gap;
141 buf.image_size -= buf.transfer_unit;
142 }
143}
144
145static const u8 linear_lut[TILE_SIZE] = {
146 // clang-format off
147 0, 1, 2, 3, 4, 5, 6, 7,
148 8, 9, 10, 11, 12, 13, 14, 15,
149 16, 17, 18, 19, 20, 21, 22, 23,
150 24, 25, 26, 27, 28, 29, 30, 31,
151 32, 33, 34, 35, 36, 37, 38, 39,
152 40, 41, 42, 43, 44, 45, 46, 47,
153 48, 49, 50, 51, 52, 53, 54, 55,
154 56, 57, 58, 59, 60, 61, 62, 63,
155 // clang-format on
156};
157
158static const u8 morton_lut[TILE_SIZE] = {
159 // clang-format off
160 0, 1, 4, 5, 16, 17, 20, 21,
161 2, 3, 6, 7, 18, 19, 22, 23,
162 8, 9, 12, 13, 24, 25, 28, 29,
163 10, 11, 14, 15, 26, 27, 30, 31,
164 32, 33, 36, 37, 48, 49, 52, 53,
165 34, 35, 38, 39, 50, 51, 54, 55,
166 40, 41, 44, 45, 56, 57, 60, 61,
167 42, 43, 46, 47, 58, 59, 62, 63,
168 // clang-format on
169};
170
171static void RotateTile0(const ImageTile& input, ImageTile& output, int height,
172 const u8 out_map[64]) {
173 for (int i = 0; i < height * 8; ++i) {
174 output[out_map[i]] = input[i];
175 }
176}
177
178static void RotateTile90(const ImageTile& input, ImageTile& output, int height,
179 const u8 out_map[64]) {
180 int out_i = 0;
181 for (int x = 0; x < 8; ++x) {
182 for (int y = height - 1; y >= 0; --y) {
183 output[out_map[out_i++]] = input[y * 8 + x];
184 }
185 }
186}
187
188static void RotateTile180(const ImageTile& input, ImageTile& output, int height,
189 const u8 out_map[64]) {
190 int out_i = 0;
191 for (int i = height * 8 - 1; i >= 0; --i) {
192 output[out_map[out_i++]] = input[i];
193 }
194}
195
196static void RotateTile270(const ImageTile& input, ImageTile& output, int height,
197 const u8 out_map[64]) {
198 int out_i = 0;
199 for (int x = 8 - 1; x >= 0; --x) {
200 for (int y = 0; y < height; ++y) {
201 output[out_map[out_i++]] = input[y * 8 + x];
202 }
203 }
204}
205
206static void WriteTileToOutput(u32* output, const ImageTile& tile, int height, int line_stride) {
207 for (int y = 0; y < height; ++y) {
208 for (int x = 0; x < 8; ++x) {
209 output[y * line_stride + x] = tile[y * 8 + x];
210 }
211 }
212}
213
214/**
215 * Performs a Y2R colorspace conversion.
216 *
217 * The Y2R hardware implements hardware-accelerated YUV to RGB colorspace conversions. It is most
218 * commonly used for video playback or to display camera input to the screen.
219 *
220 * The conversion process is quite configurable, and can be divided in distinct steps. From
221 * observation, it appears that the hardware buffers a single 8-pixel tall strip of image data
222 * internally and converts it in one go before writing to the output and loading the next strip.
223 *
224 * The steps taken to convert one strip of image data are:
225 *
226 * - The hardware receives data via CDMA (http://3dbrew.org/wiki/Corelink_DMA_Engines), which is
227 * presumably stored in one or more internal buffers. This process can be done in several separate
228 * transfers, as long as they don't exceed the size of the internal image buffer. This allows
229 * flexibility in input strides.
230 * - The input data is decoded into a YUV tuple. Several formats are suported, see the `InputFormat`
231 * enum.
232 * - The YUV tuple is converted, using fixed point calculations, to RGB. This step can be configured
233 * using a set of coefficients to support different colorspace standards. See `CoefficientSet`.
234 * - The strip can be optionally rotated 90, 180 or 270 degrees. Since each strip is processed
235 * independently, this notably rotates each *strip*, not the entire image. This means that for 90
236 * or 270 degree rotations, the output will be in terms of several 8 x height images, and for any
237 * non-zero rotation the strips will have to be re-arranged so that the parts of the image will
238 * not be shuffled together. This limitation makes this a feature of somewhat dubious utility. 90
239 * or 270 degree rotations in images with non-even height don't seem to work properly.
240 * - The data is converted to the output RGB format. See the `OutputFormat` enum.
241 * - The data can be output either linearly line-by-line or in the swizzled 8x8 tile format used by
242 * the PICA. This is decided by the `BlockAlignment` enum. If 8x8 alignment is used, then the
243 * image must have a height divisible by 8. The image width must always be divisible by 8.
244 * - The final data is then CDMAed out to main memory and the next image strip is processed. This
245 * offers the same flexibility as the input stage.
246 *
247 * In this implementation, to avoid the combinatorial explosion of parameter combinations, common
248 * intermediate formats are used and where possible tables or parameters are used instead of
249 * diverging code paths to keep the amount of branches in check. Some steps are also merged to
250 * increase efficiency.
251 *
252 * Output for all valid settings combinations matches hardware, however output in some edge-cases
253 * differs:
254 *
255 * - `Block8x8` alignment with non-mod8 height produces different garbage patterns on the last
256 * strip, especially when combined with rotation.
257 * - Hardware, when using `Linear` alignment with a non-even height and 90 or 270 degree rotation
258 * produces misaligned output on the last strip. This implmentation produces output with the
259 * correct "expected" alignment.
260 *
261 * Hardware behaves strangely (doesn't fire the completion interrupt, for example) in these cases,
262 * so they are believed to be invalid configurations anyway.
263 */
264void PerformConversion(ConversionConfiguration& cvt) {
265 ASSERT(cvt.input_line_width % 8 == 0);
266 ASSERT(cvt.block_alignment != BlockAlignment::Block8x8 || cvt.input_lines % 8 == 0);
267 // Tiles per row
268 size_t num_tiles = cvt.input_line_width / 8;
269 ASSERT(num_tiles <= MAX_TILES);
270
271 // Buffer used as a CDMA source/target.
272 std::unique_ptr<u8[]> data_buffer(new u8[cvt.input_line_width * 8 * 4]);
273 // Intermediate storage for decoded 8x8 image tiles. Always stored as RGB32.
274 std::unique_ptr<ImageTile[]> tiles(new ImageTile[num_tiles]);
275 ImageTile tmp_tile;
276
277 // LUT used to remap writes to a tile. Used to allow linear or swizzled output without
278 // requiring two different code paths.
279 const u8* tile_remap = nullptr;
280 switch (cvt.block_alignment) {
281 case BlockAlignment::Linear:
282 tile_remap = linear_lut;
283 break;
284 case BlockAlignment::Block8x8:
285 tile_remap = morton_lut;
286 break;
287 }
288
289 for (unsigned int y = 0; y < cvt.input_lines; y += 8) {
290 unsigned int row_height = std::min(cvt.input_lines - y, 8u);
291
292 // Total size in pixels of incoming data required for this strip.
293 const size_t row_data_size = row_height * cvt.input_line_width;
294
295 u8* input_Y = data_buffer.get();
296 u8* input_U = input_Y + 8 * cvt.input_line_width;
297 u8* input_V = input_U + 8 * cvt.input_line_width / 2;
298
299 switch (cvt.input_format) {
300 case InputFormat::YUV422_Indiv8:
301 ReceiveData<1>(input_Y, cvt.src_Y, row_data_size);
302 ReceiveData<1>(input_U, cvt.src_U, row_data_size / 2);
303 ReceiveData<1>(input_V, cvt.src_V, row_data_size / 2);
304 break;
305 case InputFormat::YUV420_Indiv8:
306 ReceiveData<1>(input_Y, cvt.src_Y, row_data_size);
307 ReceiveData<1>(input_U, cvt.src_U, row_data_size / 4);
308 ReceiveData<1>(input_V, cvt.src_V, row_data_size / 4);
309 break;
310 case InputFormat::YUV422_Indiv16:
311 ReceiveData<2>(input_Y, cvt.src_Y, row_data_size);
312 ReceiveData<2>(input_U, cvt.src_U, row_data_size / 2);
313 ReceiveData<2>(input_V, cvt.src_V, row_data_size / 2);
314 break;
315 case InputFormat::YUV420_Indiv16:
316 ReceiveData<2>(input_Y, cvt.src_Y, row_data_size);
317 ReceiveData<2>(input_U, cvt.src_U, row_data_size / 4);
318 ReceiveData<2>(input_V, cvt.src_V, row_data_size / 4);
319 break;
320 case InputFormat::YUYV422_Interleaved:
321 input_U = nullptr;
322 input_V = nullptr;
323 ReceiveData<1>(input_Y, cvt.src_YUYV, row_data_size * 2);
324 break;
325 }
326
327 // Note(yuriks): If additional optimization is required, input_format can be moved to a
328 // template parameter, so that its dispatch can be moved to outside the inner loop.
329 ConvertYUVToRGB(cvt.input_format, input_Y, input_U, input_V, tiles.get(),
330 cvt.input_line_width, row_height, cvt.coefficients);
331
332 u32* output_buffer = reinterpret_cast<u32*>(data_buffer.get());
333
334 for (size_t i = 0; i < num_tiles; ++i) {
335 int image_strip_width = 0;
336 int output_stride = 0;
337
338 switch (cvt.rotation) {
339 case Rotation::None:
340 RotateTile0(tiles[i], tmp_tile, row_height, tile_remap);
341 image_strip_width = cvt.input_line_width;
342 output_stride = 8;
343 break;
344 case Rotation::Clockwise_90:
345 RotateTile90(tiles[i], tmp_tile, row_height, tile_remap);
346 image_strip_width = 8;
347 output_stride = 8 * row_height;
348 break;
349 case Rotation::Clockwise_180:
350 // For 180 and 270 degree rotations we also invert the order of tiles in the strip,
351 // since the rotates are done individually on each tile.
352 RotateTile180(tiles[num_tiles - i - 1], tmp_tile, row_height, tile_remap);
353 image_strip_width = cvt.input_line_width;
354 output_stride = 8;
355 break;
356 case Rotation::Clockwise_270:
357 RotateTile270(tiles[num_tiles - i - 1], tmp_tile, row_height, tile_remap);
358 image_strip_width = 8;
359 output_stride = 8 * row_height;
360 break;
361 }
362
363 switch (cvt.block_alignment) {
364 case BlockAlignment::Linear:
365 WriteTileToOutput(output_buffer, tmp_tile, row_height, image_strip_width);
366 output_buffer += output_stride;
367 break;
368 case BlockAlignment::Block8x8:
369 WriteTileToOutput(output_buffer, tmp_tile, 8, 8);
370 output_buffer += TILE_SIZE;
371 break;
372 }
373 }
374
375 // Note(yuriks): If additional optimization is required, output_format can be moved to a
376 // template parameter, so that its dispatch can be moved to outside the inner loop.
377 SendData(reinterpret_cast<u32*>(data_buffer.get()), cvt.dst, (int)row_data_size,
378 cvt.output_format, (u8)cvt.alpha);
379 }
380}
381}
382}
diff --git a/src/core/hw/y2r.h b/src/core/hw/y2r.h
deleted file mode 100644
index 25fcd781c..000000000
--- a/src/core/hw/y2r.h
+++ /dev/null
@@ -1,17 +0,0 @@
1// Copyright 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7namespace Service {
8namespace Y2R {
9struct ConversionConfiguration;
10}
11}
12
13namespace HW {
14namespace Y2R {
15void PerformConversion(Service::Y2R::ConversionConfiguration& cvt);
16}
17}
diff --git a/src/core/settings.cpp b/src/core/settings.cpp
index efcf1267d..012f7e6c3 100644
--- a/src/core/settings.cpp
+++ b/src/core/settings.cpp
@@ -5,7 +5,6 @@
5#include "audio_core/audio_core.h" 5#include "audio_core/audio_core.h"
6#include "core/gdbstub/gdbstub.h" 6#include "core/gdbstub/gdbstub.h"
7#include "core/hle/service/hid/hid.h" 7#include "core/hle/service/hid/hid.h"
8#include "core/hle/service/ir/ir.h"
9#include "core/settings.h" 8#include "core/settings.h"
10#include "video_core/video_core.h" 9#include "video_core/video_core.h"
11 10
@@ -33,7 +32,6 @@ void Apply() {
33 AudioCore::EnableStretching(values.enable_audio_stretching); 32 AudioCore::EnableStretching(values.enable_audio_stretching);
34 33
35 Service::HID::ReloadInputDevices(); 34 Service::HID::ReloadInputDevices();
36 Service::IR::ReloadInputDevices();
37} 35}
38 36
39} // namespace Settings 37} // namespace Settings
diff --git a/src/core/settings.h b/src/core/settings.h
index 8d78cb424..912b2c885 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -7,7 +7,6 @@
7#include <array> 7#include <array>
8#include <string> 8#include <string>
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "core/hle/service/cam/cam.h"
11 10
12namespace Settings { 11namespace Settings {
13 12
@@ -122,10 +121,6 @@ struct Values {
122 bool enable_audio_stretching; 121 bool enable_audio_stretching;
123 std::string audio_device_id; 122 std::string audio_device_id;
124 123
125 // Camera
126 std::array<std::string, Service::CAM::NumCameras> camera_name;
127 std::array<std::string, Service::CAM::NumCameras> camera_config;
128
129 // Debugging 124 // Debugging
130 bool use_gdbstub; 125 bool use_gdbstub;
131 u16 gdbstub_port; 126 u16 gdbstub_port;