Bug Summary

File:Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_es.cpp
Location:line 487, column 8
Description:Value stored to 'TitleID' during its initialization is never read

Annotated Source Code

1// Copyright 2013 Dolphin Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5
6
7// =======================================================
8// File description
9// -------------
10/* Here we handle /dev/es requests. We have cases for these functions, the exact
11 DevKitPro/libogc name is in parenthesis:
12
13 0x20 GetTitleID (ES_GetTitleID) (Input: none, Output: 8 bytes)
14 0x1d GetDataDir (ES_GetDataDir) (Input: 8 bytes, Output: 30 bytes)
15
16 0x1b DiGetTicketView (Input: none, Output: 216 bytes)
17 0x16 GetConsumption (Input: 8 bytes, Output: 0 bytes, 4 bytes) // there are two output buffers
18
19 0x12 GetNumTicketViews (ES_GetNumTicketViews) (Input: 8 bytes, Output: 4 bytes)
20 0x14 GetTMDViewSize (ES_GetTMDViewSize) (Input: ?, Output: ?) // I don't get this anymore,
21 it used to come after 0x12
22
23 but only the first two are correctly supported. For the other four we ignore any potential
24 input and only write zero to the out buffer. However, most games only use first two,
25 but some Nintendo developed games use the other ones to:
26
27 0x1b: Mario Galaxy, Mario Kart, SSBB
28 0x16: Mario Galaxy, Mario Kart, SSBB
29 0x12: Mario Kart
30 0x14: Mario Kart: But only if we don't return a zeroed out buffer for the 0x12 question,
31 and instead answer for example 1 will this question appear.
32
33*/
34// =============
35
36#include "WII_IPC_HLE_Device_es.h"
37
38#include "../PowerPC/PowerPC.h"
39#include "../VolumeHandler.h"
40#include "FileUtil.h"
41#include "Crypto/aes.h"
42#include "ConfigManager.h"
43
44#include "../Boot/Boot_DOL.h"
45#include "NandPaths.h"
46#include "CommonPaths.h"
47#include "IPC_HLE/WII_IPC_HLE_Device_usb.h"
48#include "../Movie.h"
49#include "StringUtil.h"
50
51#include "ec_wii.h"
52
53#ifdef _WIN32
54#include <Windows.h>
55#endif
56
57std::string CWII_IPC_HLE_Device_es::m_ContentFile;
58
59CWII_IPC_HLE_Device_es::CWII_IPC_HLE_Device_es(u32 _DeviceID, const std::string& _rDeviceName)
60 : IWII_IPC_HLE_Device(_DeviceID, _rDeviceName)
61 , m_pContentLoader(NULL__null)
62 , m_TitleID(-1)
63 , m_AccessIdentID(0x6000000)
64{
65}
66
67static u8 key_sd [0x10] = {0xab, 0x01, 0xb9, 0xd8, 0xe1, 0x62, 0x2b, 0x08, 0xaf, 0xba, 0xd8, 0x4d, 0xbf, 0xc2, 0xa5, 0x5d};
68static u8 key_ecc [0x1e] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01};
69static u8 key_empty[0x10] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
70
71// default key table
72u8* CWII_IPC_HLE_Device_es::keyTable[11] = {
73 key_ecc, // ECC Private Key
74 key_empty, // Console ID
75 key_empty, // NAND AES Key
76 key_empty, // NAND HMAC
77 key_empty, // Common Key
78 key_empty, // PRNG seed
79 key_sd, // SD Key
80 key_empty, // Unknown
81 key_empty, // Unknown
82 key_empty, // Unknown
83 key_empty, // Unknown
84};
85
86CWII_IPC_HLE_Device_es::~CWII_IPC_HLE_Device_es()
87{}
88
89void CWII_IPC_HLE_Device_es::LoadWAD(const std::string& _rContentFile)
90{
91 m_ContentFile = _rContentFile;
92}
93
94void CWII_IPC_HLE_Device_es::OpenInternal()
95{
96 m_pContentLoader = &DiscIO::CNANDContentManager::Access().GetNANDLoader(m_ContentFile);
97
98 // check for cd ...
99 if (m_pContentLoader->IsValid())
100 {
101 m_TitleID = m_pContentLoader->GetTitleID();
102
103 m_TitleIDs.clear();
104 DiscIO::cUIDsys::AccessInstance().GetTitleIDs(m_TitleIDs);
105 // uncomment if ES_GetOwnedTitlesCount / ES_GetOwnedTitles is implemented
106 // m_TitleIDsOwned.clear();
107 // DiscIO::cUIDsys::AccessInstance().GetTitleIDs(m_TitleIDsOwned, true);
108 }
109 else if (VolumeHandler::IsValid())
110 {
111 // blindly grab the titleID from the disc - it's unencrypted at:
112 // offset 0x0F8001DC and 0x0F80044C
113 VolumeHandler::GetVolume()->GetTitleID((u8*)&m_TitleID);
114 m_TitleID = Common::swap64(m_TitleID);
115 }
116 else
117 {
118 m_TitleID = ((u64)0x00010000 << 32) | 0xF00DBEEF;
119 }
120
121 INFO_LOG(WII_IPC_ES, "Set default title to %08x/%08x", (u32)(m_TitleID>>32), (u32)m_TitleID)do { { if (LogTypes::LINFO <= 3) GenericLog(LogTypes::LINFO
, LogTypes::WII_IPC_ES, "/home/anal/dolphin-emu/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_es.cpp"
, 121, "Set default title to %08x/%08x", (u32)(m_TitleID>>
32), (u32)m_TitleID); } } while (0)
;
122}
123
124void CWII_IPC_HLE_Device_es::DoState(PointerWrap& p)
125{
126 IWII_IPC_HLE_Device::DoState(p);
127 p.Do(m_ContentFile);
128 OpenInternal();
129 p.Do(m_AccessIdentID);
130 p.Do(m_TitleIDs);
131
132 u32 Count = m_ContentAccessMap.size();
133 p.Do(Count);
134
135 u32 CFD, Position;
136 u64 TitleID;
137 u16 Index;
138 if (p.GetMode() == PointerWrap::MODE_READ)
139 {
140 for (u32 i = 0; i < Count; i++)
141 {
142 p.Do(CFD);
143 p.Do(Position);
144 p.Do(TitleID);
145 p.Do(Index);
146 CFD = OpenTitleContent(CFD, TitleID, Index);
147 if (CFD != 0xffffffff)
148 {
149 m_ContentAccessMap[CFD].m_Position = Position;
150 }
151 }
152 }
153 else
154 {
155 for (auto itr = m_ContentAccessMap.begin(); itr != m_ContentAccessMap.end(); ++itr)
156 {
157 CFD = itr->first;
158 SContentAccess& Access = itr->second;
159 Position = Access.m_Position;
160 TitleID = Access.m_TitleID;
161 Index = Access.m_pContent->m_Index;
162 p.Do(CFD);
163 p.Do(Position);
164 p.Do(TitleID);
165 p.Do(Index);
166 }
167 }
168}
169
170bool CWII_IPC_HLE_Device_es::Open(u32 _CommandAddress, u32 _Mode)
171{
172 OpenInternal();
173
174 Memory::Write_U32(GetDeviceID(), _CommandAddress+4);
175 if (m_Active)
176 INFO_LOG(WII_IPC_ES, "Device was re-opened.")do { { if (LogTypes::LINFO <= 3) GenericLog(LogTypes::LINFO
, LogTypes::WII_IPC_ES, "/home/anal/dolphin-emu/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_es.cpp"
, 176, "Device was re-opened."); } } while (0)
;
177 m_Active = true;
178 return true;
179}
180
181bool CWII_IPC_HLE_Device_es::Close(u32 _CommandAddress, bool _bForce)
182{
183 // Leave deletion of the INANDContentLoader objects to CNANDContentManager, don't do it here!
184 m_NANDContent.clear();
185 m_ContentAccessMap.clear();
186 m_pContentLoader = NULL__null;
187 m_TitleIDs.clear();
188 m_TitleID = -1;
189 m_AccessIdentID = 0x6000000;
190
191 INFO_LOG(WII_IPC_ES, "ES: Close")do { { if (LogTypes::LINFO <= 3) GenericLog(LogTypes::LINFO
, LogTypes::WII_IPC_ES, "/home/anal/dolphin-emu/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_es.cpp"
, 191, "ES: Close"); } } while (0)
;
192 if (!_bForce)
193 Memory::Write_U32(0, _CommandAddress + 4);
194 m_Active = false;
195 return true;
196}
197
198u32 CWII_IPC_HLE_Device_es::OpenTitleContent(u32 CFD, u64 TitleID, u16 Index)
199{
200 const DiscIO::SNANDContent* pContent = AccessContentDevice(TitleID).GetContentByIndex(Index);
201
202 if (pContent == NULL__null)
203 {
204 return 0xffffffff; //TODO: what is the correct error value here?
205 }
206
207 SContentAccess Access;
208 Access.m_Position = 0;
209 Access.m_pContent = pContent;
210 Access.m_TitleID = TitleID;
211
212 if (!pContent->m_pData)
213 {
214 std::string Filename = pContent->m_Filename;
215 INFO_LOG(WII_IPC_ES, "ES: load %s", Filename.c_str())do { { if (LogTypes::LINFO <= 3) GenericLog(LogTypes::LINFO
, LogTypes::WII_IPC_ES, "/home/anal/dolphin-emu/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_es.cpp"
, 215, "ES: load %s", Filename.c_str()); } } while (0)
;
216
217 Access.m_File.Open(Filename, "rb");
218 if (!Access.m_File.IsGood())
219 {
220 WARN_LOG(WII_IPC_ES, "ES: couldn't load %s", Filename.c_str())do { { if (LogTypes::LWARNING <= 3) GenericLog(LogTypes::LWARNING
, LogTypes::WII_IPC_ES, "/home/anal/dolphin-emu/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_es.cpp"
, 220, "ES: couldn't load %s", Filename.c_str()); } } while (
0)
;
221 return 0xffffffff;
222 }
223 }
224
225 m_ContentAccessMap[CFD] = std::move(Access);
226 return CFD;
227}
228
229bool CWII_IPC_HLE_Device_es::IOCtlV(u32 _CommandAddress)
230{
231 SIOCtlVBuffer Buffer(_CommandAddress);
232
233 DEBUG_LOG(WII_IPC_ES, "%s (0x%x)", GetDeviceName().c_str(), Buffer.Parameter)do { { if (LogTypes::LDEBUG <= 3) GenericLog(LogTypes::LDEBUG
, LogTypes::WII_IPC_ES, "/home/anal/dolphin-emu/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_es.cpp"
, 233, "%s (0x%x)", GetDeviceName().c_str(), Buffer.Parameter
); } } while (0)
;
234
235 // Prepare the out buffer(s) with zeroes as a safety precaution
236 // to avoid returning bad values
237 for (u32 i = 0; i < Buffer.NumberPayloadBuffer; i++)
238 {
239 Memory::Memset(Buffer.PayloadBuffer[i].m_Address, 0,
240 Buffer.PayloadBuffer[i].m_Size);
241 }
242
243 switch (Buffer.Parameter)
244 {
245 case IOCTL_ES_GETDEVICEID:
246 {
247 _dbg_assert_msg_(WII_IPC_ES, Buffer.NumberPayloadBuffer == 1, "IOCTL_ES_GETDEVICEID no out buffer"){};
248
249 EcWii &ec = EcWii::GetInstance();
250 INFO_LOG(WII_IPC_ES, "IOCTL_ES_GETDEVICEID %08X", ec.getNgId())do { { if (LogTypes::LINFO <= 3) GenericLog(LogTypes::LINFO
, LogTypes::WII_IPC_ES, "/home/anal/dolphin-emu/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_es.cpp"
, 250, "IOCTL_ES_GETDEVICEID %08X", ec.getNgId()); } } while (
0)
;
251 Memory::Write_U32(ec.getNgId(), Buffer.PayloadBuffer[0].m_Address);
252 Memory::Write_U32(0, _CommandAddress + 0x4);
253 return true;
254 }
255 break;
256
257 case IOCTL_ES_GETTITLECONTENTSCNT:
258 {
259 _dbg_assert_(WII_IPC_ES, Buffer.NumberInBuffer == 1){};
260 _dbg_assert_(WII_IPC_ES, Buffer.NumberPayloadBuffer == 1){};
261
262 u64 TitleID = Memory::Read_U64(Buffer.InBuffer[0].m_Address);
263
264 const DiscIO::INANDContentLoader& rNANDContent = AccessContentDevice(TitleID);
265 u16 NumberOfPrivateContent = 0;
266 if (rNANDContent.IsValid()) // Not sure if dolphin will ever fail this check
267 {
268 NumberOfPrivateContent = rNANDContent.GetNumEntries();
269
270 if ((u32)(TitleID>>32) == 0x00010000)
271 Memory::Write_U32(0, Buffer.PayloadBuffer[0].m_Address);
272 else
273 Memory::Write_U32(NumberOfPrivateContent, Buffer.PayloadBuffer[0].m_Address);
274
275 Memory::Write_U32(0, _CommandAddress + 0x4);
276 }
277 else
278 Memory::Write_U32((u32)rNANDContent.GetContentSize(), _CommandAddress + 0x4);
279
280 INFO_LOG(WII_IPC_ES, "IOCTL_ES_GETTITLECONTENTSCNT: TitleID: %08x/%08x content count %i",do { { if (LogTypes::LINFO <= 3) GenericLog(LogTypes::LINFO
, LogTypes::WII_IPC_ES, "/home/anal/dolphin-emu/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_es.cpp"
, 281, "IOCTL_ES_GETTITLECONTENTSCNT: TitleID: %08x/%08x content count %i"
, (u32)(TitleID>>32), (u32)TitleID, rNANDContent.IsValid
() ? NumberOfPrivateContent : (u32)rNANDContent.GetContentSize
()); } } while (0)
281 (u32)(TitleID>>32), (u32)TitleID, rNANDContent.IsValid() ? NumberOfPrivateContent : (u32)rNANDContent.GetContentSize())do { { if (LogTypes::LINFO <= 3) GenericLog(LogTypes::LINFO
, LogTypes::WII_IPC_ES, "/home/anal/dolphin-emu/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_es.cpp"
, 281, "IOCTL_ES_GETTITLECONTENTSCNT: TitleID: %08x/%08x content count %i"
, (u32)(TitleID>>32), (u32)TitleID, rNANDContent.IsValid
() ? NumberOfPrivateContent : (u32)rNANDContent.GetContentSize
()); } } while (0)
;
282
283 return true;
284 }
285 break;
286
287 case IOCTL_ES_GETTITLECONTENTS:
288 {
289 _dbg_assert_msg_(WII_IPC_ES, Buffer.NumberInBuffer == 2, "IOCTL_ES_GETTITLECONTENTS bad in buffer"){};
290 _dbg_assert_msg_(WII_IPC_ES, Buffer.NumberPayloadBuffer == 1, "IOCTL_ES_GETTITLECONTENTS bad out buffer"){};
291
292 u64 TitleID = Memory::Read_U64(Buffer.InBuffer[0].m_Address);
293
294 const DiscIO::INANDContentLoader& rNANDCOntent = AccessContentDevice(TitleID);
295 if (rNANDCOntent.IsValid()) // Not sure if dolphin will ever fail this check
296 {
297 for (u16 i = 0; i < rNANDCOntent.GetNumEntries(); i++)
298 {
299 Memory::Write_U32(rNANDCOntent.GetContentByIndex(i)->m_ContentID, Buffer.PayloadBuffer[0].m_Address + i*4);
300 INFO_LOG(WII_IPC_ES, "IOCTL_ES_GETTITLECONTENTS: Index %d: %08x", i, rNANDCOntent.GetContentByIndex(i)->m_ContentID)do { { if (LogTypes::LINFO <= 3) GenericLog(LogTypes::LINFO
, LogTypes::WII_IPC_ES, "/home/anal/dolphin-emu/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_es.cpp"
, 300, "IOCTL_ES_GETTITLECONTENTS: Index %d: %08x", i, rNANDCOntent
.GetContentByIndex(i)->m_ContentID); } } while (0)
;
301 }
302 Memory::Write_U32(0, _CommandAddress + 0x4);
303 }
304 else
305 {
306 Memory::Write_U32((u32)rNANDCOntent.GetContentSize(), _CommandAddress + 0x4);
307 INFO_LOG(WII_IPC_ES,do { { if (LogTypes::LINFO <= 3) GenericLog(LogTypes::LINFO
, LogTypes::WII_IPC_ES, "/home/anal/dolphin-emu/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_es.cpp"
, 311, "IOCTL_ES_GETTITLECONTENTS: " "Unable to open content %lu"
, (unsigned long)rNANDCOntent. GetContentSize()); } } while (
0)
308 "IOCTL_ES_GETTITLECONTENTS: "do { { if (LogTypes::LINFO <= 3) GenericLog(LogTypes::LINFO
, LogTypes::WII_IPC_ES, "/home/anal/dolphin-emu/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_es.cpp"
, 311, "IOCTL_ES_GETTITLECONTENTS: " "Unable to open content %lu"
, (unsigned long)rNANDCOntent. GetContentSize()); } } while (
0)
309 "Unable to open content %lu",do { { if (LogTypes::LINFO <= 3) GenericLog(LogTypes::LINFO
, LogTypes::WII_IPC_ES, "/home/anal/dolphin-emu/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_es.cpp"
, 311, "IOCTL_ES_GETTITLECONTENTS: " "Unable to open content %lu"
, (unsigned long)rNANDCOntent. GetContentSize()); } } while (
0)
310 (unsigned long)rNANDCOntent.\do { { if (LogTypes::LINFO <= 3) GenericLog(LogTypes::LINFO
, LogTypes::WII_IPC_ES, "/home/anal/dolphin-emu/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_es.cpp"
, 311, "IOCTL_ES_GETTITLECONTENTS: " "Unable to open content %lu"
, (unsigned long)rNANDCOntent. GetContentSize()); } } while (
0)
311 GetContentSize())do { { if (LogTypes::LINFO <= 3) GenericLog(LogTypes::LINFO
, LogTypes::WII_IPC_ES, "/home/anal/dolphin-emu/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_es.cpp"
, 311, "IOCTL_ES_GETTITLECONTENTS: " "Unable to open content %lu"
, (unsigned long)rNANDCOntent. GetContentSize()); } } while (
0)
;
312 }
313
314 return true;
315 }
316 break;
317
318
319 case IOCTL_ES_OPENTITLECONTENT:
320 {
321 _dbg_assert_(WII_IPC_ES, Buffer.NumberInBuffer == 3){};
322 _dbg_assert_(WII_IPC_ES, Buffer.NumberPayloadBuffer == 0){};
323
324 u64 TitleID = Memory::Read_U64(Buffer.InBuffer[0].m_Address);
325 u32 Index = Memory::Read_U32(Buffer.InBuffer[2].m_Address);
326
327 u32 CFD = OpenTitleContent(m_AccessIdentID++, TitleID, Index);
328 Memory::Write_U32(CFD, _CommandAddress + 0x4);
329
330 INFO_LOG(WII_IPC_ES, "IOCTL_ES_OPENTITLECONTENT: TitleID: %08x/%08x Index %i -> got CFD %x", (u32)(TitleID>>32), (u32)TitleID, Index, CFD)do { { if (LogTypes::LINFO <= 3) GenericLog(LogTypes::LINFO
, LogTypes::WII_IPC_ES, "/home/anal/dolphin-emu/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_es.cpp"
, 330, "IOCTL_ES_OPENTITLECONTENT: TitleID: %08x/%08x Index %i -> got CFD %x"
, (u32)(TitleID>>32), (u32)TitleID, Index, CFD); } } while
(0)
;
331
332 return true;
333 }
334 break;
335
336 case IOCTL_ES_OPENCONTENT:
337 {
338 _dbg_assert_(WII_IPC_ES, Buffer.NumberInBuffer == 1){};
339 _dbg_assert_(WII_IPC_ES, Buffer.NumberPayloadBuffer == 0){};
340 u32 Index = Memory::Read_U32(Buffer.InBuffer[0].m_Address);
341
342 u32 CFD = OpenTitleContent(m_AccessIdentID++, m_TitleID, Index);
343 Memory::Write_U32(CFD, _CommandAddress + 0x4);
344 INFO_LOG(WII_IPC_ES, "IOCTL_ES_OPENCONTENT: Index %i -> got CFD %x", Index, CFD)do { { if (LogTypes::LINFO <= 3) GenericLog(LogTypes::LINFO
, LogTypes::WII_IPC_ES, "/home/anal/dolphin-emu/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_es.cpp"
, 344, "IOCTL_ES_OPENCONTENT: Index %i -> got CFD %x", Index
, CFD); } } while (0)
;
345
346 return true;
347 }
348 break;
349
350 case IOCTL_ES_READCONTENT:
351 {
352 _dbg_assert_(WII_IPC_ES, Buffer.NumberInBuffer == 1){};
353 _dbg_assert_(WII_IPC_ES, Buffer.NumberPayloadBuffer == 1){};
354
355 u32 CFD = Memory::Read_U32(Buffer.InBuffer[0].m_Address);
356 u32 Size = Buffer.PayloadBuffer[0].m_Size;
357 u32 Addr = Buffer.PayloadBuffer[0].m_Address;
358
359 auto itr = m_ContentAccessMap.find(CFD);
360 if (itr == m_ContentAccessMap.end())
361 {
362 Memory::Write_U32(-1, _CommandAddress + 0x4);
363 return true;
364 }
365 SContentAccess& rContent = itr->second;
366
367 _dbg_assert_(WII_IPC_ES, rContent.m_pContent->m_pData != NULL){};
368
369 u8* pDest = Memory::GetPointer(Addr);
370
371 if (rContent.m_Position + Size > rContent.m_pContent->m_Size)
372 {
373 Size = rContent.m_pContent->m_Size-rContent.m_Position;
374 }
375
376 if (Size > 0)
377 {
378 if (pDest) {
379 if (rContent.m_pContent->m_pData)
380 {
381 u8* pSrc = &rContent.m_pContent->m_pData[rContent.m_Position];
382 memcpy(pDest, pSrc, Size);
383 }
384 else
385 {
386 File::IOFile* pFile = &rContent.m_File;
387 pFile->Seek(rContent.m_Position, SEEK_SET0);
388 pFile->ReadBytes(pDest, Size);
389 }
390 rContent.m_Position += Size;
391 } else {
392 PanicAlertT("IOCTL_ES_READCONTENT - bad destination")MsgAlert(false, WARNING, "IOCTL_ES_READCONTENT - bad destination"
)
;
393 }
394 }
395
396 INFO_LOG(WII_IPC_ES, "IOCTL_ES_READCONTENT: CFD %x, Address 0x%x, Size %i -> stream pos %i (Index %i)", CFD, Addr, Size, rContent.m_Position, rContent.m_pContent->m_Index)do { { if (LogTypes::LINFO <= 3) GenericLog(LogTypes::LINFO
, LogTypes::WII_IPC_ES, "/home/anal/dolphin-emu/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_es.cpp"
, 396, "IOCTL_ES_READCONTENT: CFD %x, Address 0x%x, Size %i -> stream pos %i (Index %i)"
, CFD, Addr, Size, rContent.m_Position, rContent.m_pContent->
m_Index); } } while (0)
;
397
398 Memory::Write_U32(Size, _CommandAddress + 0x4);
399 return true;
400 }
401 break;
402
403 case IOCTL_ES_CLOSECONTENT:
404 {
405 _dbg_assert_(WII_IPC_ES, Buffer.NumberInBuffer == 1){};
406 _dbg_assert_(WII_IPC_ES, Buffer.NumberPayloadBuffer == 0){};
407
408 u32 CFD = Memory::Read_U32(Buffer.InBuffer[0].m_Address);
409
410 m_ContentAccessMap.erase(CFD);
411
412 INFO_LOG(WII_IPC_ES, "IOCTL_ES_CLOSECONTENT: CFD %x", CFD)do { { if (LogTypes::LINFO <= 3) GenericLog(LogTypes::LINFO
, LogTypes::WII_IPC_ES, "/home/anal/dolphin-emu/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_es.cpp"
, 412, "IOCTL_ES_CLOSECONTENT: CFD %x", CFD); } } while (0)
;
413
414 Memory::Write_U32(0, _CommandAddress + 0x4);
415 return true;
416 }
417 break;
418
419 case IOCTL_ES_SEEKCONTENT:
420 {
421 _dbg_assert_(WII_IPC_ES, Buffer.NumberInBuffer == 3){};
422 _dbg_assert_(WII_IPC_ES, Buffer.NumberPayloadBuffer == 0){};
423
424 u32 CFD = Memory::Read_U32(Buffer.InBuffer[0].m_Address);
425 u32 Addr = Memory::Read_U32(Buffer.InBuffer[1].m_Address);
426 u32 Mode = Memory::Read_U32(Buffer.InBuffer[2].m_Address);
427
428 auto itr = m_ContentAccessMap.find(CFD);
429 if (itr == m_ContentAccessMap.end())
430 {
431 Memory::Write_U32(-1, _CommandAddress + 0x4);
432 return true;
433 }
434 SContentAccess& rContent = itr->second;
435
436 switch (Mode)
437 {
438 case 0: // SET
439 rContent.m_Position = Addr;
440 break;
441
442 case 1: // CUR
443 rContent.m_Position += Addr;
444 break;
445
446 case 2: // END
447 rContent.m_Position = rContent.m_pContent->m_Size + Addr;
448 break;
449 }
450
451 INFO_LOG(WII_IPC_ES, "IOCTL_ES_SEEKCONTENT: CFD %x, Address 0x%x, Mode %i -> Pos %i", CFD, Addr, Mode, rContent.m_Position)do { { if (LogTypes::LINFO <= 3) GenericLog(LogTypes::LINFO
, LogTypes::WII_IPC_ES, "/home/anal/dolphin-emu/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_es.cpp"
, 451, "IOCTL_ES_SEEKCONTENT: CFD %x, Address 0x%x, Mode %i -> Pos %i"
, CFD, Addr, Mode, rContent.m_Position); } } while (0)
;
452
453 Memory::Write_U32(rContent.m_Position, _CommandAddress + 0x4);
454 return true;
455 }
456 break;
457
458 case IOCTL_ES_GETTITLEDIR:
459 {
460 _dbg_assert_(WII_IPC_ES, Buffer.NumberInBuffer == 1){};
461 _dbg_assert_(WII_IPC_ES, Buffer.NumberPayloadBuffer == 1){};
462
463 u64 TitleID = Memory::Read_U64(Buffer.InBuffer[0].m_Address);
464
465 char* Path = (char*)Memory::GetPointer(Buffer.PayloadBuffer[0].m_Address);
466 sprintf(Path, "/title/%08x/%08x/data", (u32)(TitleID >> 32), (u32)TitleID);
467
468 INFO_LOG(WII_IPC_ES, "IOCTL_ES_GETTITLEDIR: %s", Path)do { { if (LogTypes::LINFO <= 3) GenericLog(LogTypes::LINFO
, LogTypes::WII_IPC_ES, "/home/anal/dolphin-emu/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_es.cpp"
, 468, "IOCTL_ES_GETTITLEDIR: %s", Path); } } while (0)
;
469 }
470 break;
471
472 case IOCTL_ES_GETTITLEID:
473 {
474 _dbg_assert_(WII_IPC_ES, Buffer.NumberInBuffer == 0){};
475 _dbg_assert_msg_(WII_IPC_ES, Buffer.NumberPayloadBuffer == 1, "IOCTL_ES_GETTITLEID no out buffer"){};
476
477 Memory::Write_U64(m_TitleID, Buffer.PayloadBuffer[0].m_Address);
478 INFO_LOG(WII_IPC_ES, "IOCTL_ES_GETTITLEID: %08x/%08x", (u32)(m_TitleID>>32), (u32)m_TitleID)do { { if (LogTypes::LINFO <= 3) GenericLog(LogTypes::LINFO
, LogTypes::WII_IPC_ES, "/home/anal/dolphin-emu/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_es.cpp"
, 478, "IOCTL_ES_GETTITLEID: %08x/%08x", (u32)(m_TitleID>>
32), (u32)m_TitleID); } } while (0)
;
479 }
480 break;
481
482 case IOCTL_ES_SETUID:
483 {
484 _dbg_assert_msg_(WII_IPC_ES, Buffer.NumberInBuffer == 1, "IOCTL_ES_SETUID no in buffer"){};
485 _dbg_assert_msg_(WII_IPC_ES, Buffer.NumberPayloadBuffer == 0, "IOCTL_ES_SETUID has a payload, it shouldn't"){};
486 // TODO: fs permissions based on this
487 u64 TitleID = Memory::Read_U64(Buffer.InBuffer[0].m_Address);
Value stored to 'TitleID' during its initialization is never read
488 INFO_LOG(WII_IPC_ES, "IOCTL_ES_SETUID titleID: %08x/%08x", (u32)(TitleID>>32), (u32)TitleID)do { { if (LogTypes::LINFO <= 3) GenericLog(LogTypes::LINFO
, LogTypes::WII_IPC_ES, "/home/anal/dolphin-emu/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_es.cpp"
, 488, "IOCTL_ES_SETUID titleID: %08x/%08x", (u32)(TitleID>>
32), (u32)TitleID); } } while (0)
;
489 }
490 break;
491
492 case IOCTL_ES_GETTITLECNT:
493 {
494 _dbg_assert_msg_(WII_IPC_ES, Buffer.NumberInBuffer == 0, "IOCTL_ES_GETTITLECNT has an in buffer"){};
495 _dbg_assert_msg_(WII_IPC_ES, Buffer.NumberPayloadBuffer == 1, "IOCTL_ES_GETTITLECNT has no out buffer"){};
496 _dbg_assert_msg_(WII_IPC_ES, Buffer.PayloadBuffer[0].m_Size == 4, "IOCTL_ES_GETTITLECNT payload[0].size != 4"){};
497
498 Memory::Write_U32((u32)m_TitleIDs.size(), Buffer.PayloadBuffer[0].m_Address);
499
500 INFO_LOG(WII_IPC_ES, "IOCTL_ES_GETTITLECNT: Number of Titles %lu",do { { if (LogTypes::LINFO <= 3) GenericLog(LogTypes::LINFO
, LogTypes::WII_IPC_ES, "/home/anal/dolphin-emu/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_es.cpp"
, 501, "IOCTL_ES_GETTITLECNT: Number of Titles %lu", (unsigned
long)m_TitleIDs.size()); } } while (0)
501 (unsigned long)m_TitleIDs.size())do { { if (LogTypes::LINFO <= 3) GenericLog(LogTypes::LINFO
, LogTypes::WII_IPC_ES, "/home/anal/dolphin-emu/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_es.cpp"
, 501, "IOCTL_ES_GETTITLECNT: Number of Titles %lu", (unsigned
long)m_TitleIDs.size()); } } while (0)
;
502
503 Memory::Write_U32(0, _CommandAddress + 0x4);
504
505 return true;
506 }
507 break;
508
509
510 case IOCTL_ES_GETTITLES:
511 {
512 _dbg_assert_msg_(WII_IPC_ES, Buffer.NumberInBuffer == 1, "IOCTL_ES_GETTITLES has an in buffer"){};
513 _dbg_assert_msg_(WII_IPC_ES, Buffer.NumberPayloadBuffer == 1, "IOCTL_ES_GETTITLES has no out buffer"){};
514
515 u32 MaxCount = Memory::Read_U32(Buffer.InBuffer[0].m_Address);
516 u32 Count = 0;
517 for (int i = 0; i < (int)m_TitleIDs.size(); i++)
518 {
519 Memory::Write_U64(m_TitleIDs[i], Buffer.PayloadBuffer[0].m_Address + i*8);
520 INFO_LOG(WII_IPC_ES, "IOCTL_ES_GETTITLES: %08x/%08x", (u32)(m_TitleIDs[i] >> 32), (u32)m_TitleIDs[i])do { { if (LogTypes::LINFO <= 3) GenericLog(LogTypes::LINFO
, LogTypes::WII_IPC_ES, "/home/anal/dolphin-emu/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_es.cpp"
, 520, "IOCTL_ES_GETTITLES: %08x/%08x", (u32)(m_TitleIDs[i] >>
32), (u32)m_TitleIDs[i]); } } while (0)
;
521 Count++;
522 if (Count >= MaxCount)
523 break;
524 }
525
526 INFO_LOG(WII_IPC_ES, "IOCTL_ES_GETTITLES: Number of titles returned %i", Count)do { { if (LogTypes::LINFO <= 3) GenericLog(LogTypes::LINFO
, LogTypes::WII_IPC_ES, "/home/anal/dolphin-emu/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_es.cpp"
, 526, "IOCTL_ES_GETTITLES: Number of titles returned %i", Count
); } } while (0)
;
527 Memory::Write_U32(0, _CommandAddress + 0x4);
528 return true;
529 }
530 break;
531
532
533 case IOCTL_ES_GETVIEWCNT:
534 {
535 _dbg_assert_msg_(WII_IPC_ES, Buffer.NumberInBuffer == 1, "IOCTL_ES_GETVIEWCNT no in buffer"){};
536 _dbg_assert_msg_(WII_IPC_ES, Buffer.NumberPayloadBuffer == 1, "IOCTL_ES_GETVIEWCNT no out buffer"){};
537
538 u64 TitleID = Memory::Read_U64(Buffer.InBuffer[0].m_Address);
539
540 u32 retVal = 0;
541 const DiscIO::INANDContentLoader& Loader = AccessContentDevice(TitleID);
542 u32 ViewCount = Loader.GetTIKSize() / DiscIO::INANDContentLoader::TICKET_SIZE;
543
544 if (!ViewCount)
545 {
546 std::string TicketFilename = Common::GetTicketFileName(TitleID);
547 if (File::Exists(TicketFilename))
548 {
549 u32 FileSize = (u32)File::GetSize(TicketFilename);
550 _dbg_assert_msg_(WII_IPC_ES, (FileSize % DiscIO::INANDContentLoader::TICKET_SIZE) == 0, "IOCTL_ES_GETVIEWCNT ticket file size seems to be wrong"){};
551
552 ViewCount = FileSize / DiscIO::INANDContentLoader::TICKET_SIZE;
553 _dbg_assert_msg_(WII_IPC_ES, (ViewCount>0) && (ViewCount<=4), "IOCTL_ES_GETVIEWCNT ticket count seems to be wrong"){};
554 }
555 else
556 {
557 if (TitleID == TITLEID_SYSMENU0x0000000100000002ull)
558 {
559 PanicAlertT("There must be a ticket for 00000001/00000002. Your NAND dump is probably incomplete.")MsgAlert(false, WARNING, "There must be a ticket for 00000001/00000002. Your NAND dump is probably incomplete."
)
;
560 }
561 ViewCount = 0;
562 //retVal = ES_NO_TICKET_INSTALLED;
563 }
564 }
565
566 INFO_LOG(WII_IPC_ES, "IOCTL_ES_GETVIEWCNT for titleID: %08x/%08x (View Count = %i)", (u32)(TitleID>>32), (u32)TitleID, ViewCount)do { { if (LogTypes::LINFO <= 3) GenericLog(LogTypes::LINFO
, LogTypes::WII_IPC_ES, "/home/anal/dolphin-emu/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_es.cpp"
, 566, "IOCTL_ES_GETVIEWCNT for titleID: %08x/%08x (View Count = %i)"
, (u32)(TitleID>>32), (u32)TitleID, ViewCount); } } while
(0)
;
567
568 Memory::Write_U32(ViewCount, Buffer.PayloadBuffer[0].m_Address);
569 Memory::Write_U32(retVal, _CommandAddress + 0x4);
570 return true;
571 }
572 break;
573
574 case IOCTL_ES_GETVIEWS:
575 {
576 _dbg_assert_msg_(WII_IPC_ES, Buffer.NumberInBuffer == 2, "IOCTL_ES_GETVIEWS no in buffer"){};
577 _dbg_assert_msg_(WII_IPC_ES, Buffer.NumberPayloadBuffer == 1, "IOCTL_ES_GETVIEWS no out buffer"){};
578
579 u64 TitleID = Memory::Read_U64(Buffer.InBuffer[0].m_Address);
580 u32 maxViews = Memory::Read_U32(Buffer.InBuffer[1].m_Address);
581 u32 retVal = 0;
582
583 const DiscIO::INANDContentLoader& Loader = AccessContentDevice(TitleID);
584
585 const u8 *Ticket = Loader.GetTIK();
586 if (Ticket)
587 {
588 u32 viewCnt = Loader.GetTIKSize() / DiscIO::INANDContentLoader::TICKET_SIZE;
589 for (unsigned int View = 0; View != maxViews && View < viewCnt; ++View)
590 {
591 Memory::Write_U32(View, Buffer.PayloadBuffer[0].m_Address + View * 0xD8);
592 Memory::WriteBigEData(Ticket + 0x1D0 + (View * DiscIO::INANDContentLoader::TICKET_SIZE),
593 Buffer.PayloadBuffer[0].m_Address + 4 + View * 0xD8, 212);
594 }
595 }
596 else
597 {
598 std::string TicketFilename = Common::GetTicketFileName(TitleID);
599 if (File::Exists(TicketFilename))
600 {
601 File::IOFile pFile(TicketFilename, "rb");
602 if (pFile)
603 {
604 u8 FileTicket[DiscIO::INANDContentLoader::TICKET_SIZE];
605 for (unsigned int View = 0; View != maxViews && pFile.ReadBytes(FileTicket, DiscIO::INANDContentLoader::TICKET_SIZE); ++View)
606 {
607 Memory::Write_U32(View, Buffer.PayloadBuffer[0].m_Address + View * 0xD8);
608 Memory::WriteBigEData(FileTicket+0x1D0, Buffer.PayloadBuffer[0].m_Address + 4 + View * 0xD8, 212);
609 }
610 }
611 }
612 else
613 {
614 //retVal = ES_NO_TICKET_INSTALLED;
615 PanicAlertT("IOCTL_ES_GETVIEWS: Tried to get data from an unknown ticket: %08x/%08x", (u32)(TitleID >> 32), (u32)TitleID)MsgAlert(false, WARNING, "IOCTL_ES_GETVIEWS: Tried to get data from an unknown ticket: %08x/%08x"
, (u32)(TitleID >> 32), (u32)TitleID)
;
616 }
617 }
618 INFO_LOG(WII_IPC_ES, "IOCTL_ES_GETVIEWS for titleID: %08x/%08x (MaxViews = %i)", (u32)(TitleID >> 32), (u32)TitleID, maxViews)do { { if (LogTypes::LINFO <= 3) GenericLog(LogTypes::LINFO
, LogTypes::WII_IPC_ES, "/home/anal/dolphin-emu/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_es.cpp"
, 618, "IOCTL_ES_GETVIEWS for titleID: %08x/%08x (MaxViews = %i)"
, (u32)(TitleID >> 32), (u32)TitleID, maxViews); } } while
(0)
;
619
620 Memory::Write_U32(retVal, _CommandAddress + 0x4);
621 return true;
622 }
623 break;
624
625 case IOCTL_ES_GETTMDVIEWCNT:
626 {
627 _dbg_assert_msg_(WII_IPC_ES, Buffer.NumberInBuffer == 1, "IOCTL_ES_GETTMDVIEWCNT no in buffer"){};
628 _dbg_assert_msg_(WII_IPC_ES, Buffer.NumberPayloadBuffer == 1, "IOCTL_ES_GETTMDVIEWCNT no out buffer"){};
629
630 u64 TitleID = Memory::Read_U64(Buffer.InBuffer[0].m_Address);
631
632 const DiscIO::INANDContentLoader& Loader = AccessContentDevice(TitleID);
633
634 u32 TMDViewCnt = 0;
635 if (Loader.IsValid())
636 {
637 TMDViewCnt += DiscIO::INANDContentLoader::TMD_VIEW_SIZE;
638 TMDViewCnt += 2; // title version
639 TMDViewCnt += 2; // num entries
640 TMDViewCnt += (u32)Loader.GetContentSize() * (4+2+2+8); // content id, index, type, size
641 }
642 Memory::Write_U32(TMDViewCnt, Buffer.PayloadBuffer[0].m_Address);
643
644 Memory::Write_U32(0, _CommandAddress + 0x4);
645
646 INFO_LOG(WII_IPC_ES, "IOCTL_ES_GETTMDVIEWCNT: title: %08x/%08x (view size %i)", (u32)(TitleID >> 32), (u32)TitleID, TMDViewCnt)do { { if (LogTypes::LINFO <= 3) GenericLog(LogTypes::LINFO
, LogTypes::WII_IPC_ES, "/home/anal/dolphin-emu/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_es.cpp"
, 646, "IOCTL_ES_GETTMDVIEWCNT: title: %08x/%08x (view size %i)"
, (u32)(TitleID >> 32), (u32)TitleID, TMDViewCnt); } } while
(0)
;
647 return true;
648 }
649 break;
650
651 case IOCTL_ES_GETTMDVIEWS:
652 {
653 _dbg_assert_msg_(WII_IPC_ES, Buffer.NumberInBuffer == 2, "IOCTL_ES_GETTMDVIEWCNT no in buffer"){};
654 _dbg_assert_msg_(WII_IPC_ES, Buffer.NumberPayloadBuffer == 1, "IOCTL_ES_GETTMDVIEWCNT no out buffer"){};
655
656 u64 TitleID = Memory::Read_U64(Buffer.InBuffer[0].m_Address);
657 u32 MaxCount = Memory::Read_U32(Buffer.InBuffer[1].m_Address);
658
659 const DiscIO::INANDContentLoader& Loader = AccessContentDevice(TitleID);
660
661 INFO_LOG(WII_IPC_ES, "IOCTL_ES_GETTMDVIEWCNT: title: %08x/%08x buffer size: %i", (u32)(TitleID >> 32), (u32)TitleID, MaxCount)do { { if (LogTypes::LINFO <= 3) GenericLog(LogTypes::LINFO
, LogTypes::WII_IPC_ES, "/home/anal/dolphin-emu/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_es.cpp"
, 661, "IOCTL_ES_GETTMDVIEWCNT: title: %08x/%08x buffer size: %i"
, (u32)(TitleID >> 32), (u32)TitleID, MaxCount); } } while
(0)
;
662
663 if (Loader.IsValid())
664 {
665 u32 Address = Buffer.PayloadBuffer[0].m_Address;
666
667 Memory::WriteBigEData(Loader.GetTMDView(), Address, DiscIO::INANDContentLoader::TMD_VIEW_SIZE);
668 Address += DiscIO::INANDContentLoader::TMD_VIEW_SIZE;
669
670 Memory::Write_U16(Loader.GetTitleVersion(), Address); Address += 2;
671 Memory::Write_U16(Loader.GetNumEntries(), Address); Address += 2;
672
673 const std::vector<DiscIO::SNANDContent>& rContent = Loader.GetContent();
674 for (size_t i=0; i<Loader.GetContentSize(); i++)
675 {
676 Memory::Write_U32(rContent[i].m_ContentID, Address); Address += 4;
677 Memory::Write_U16(rContent[i].m_Index, Address); Address += 2;
678 Memory::Write_U16(rContent[i].m_Type, Address); Address += 2;
679 Memory::Write_U64(rContent[i].m_Size, Address); Address += 8;
680 }
681
682 _dbg_assert_(WII_IPC_ES, (Address-Buffer.PayloadBuffer[0].m_Address) == Buffer.PayloadBuffer[0].m_Size){};
683 }
684 Memory::Write_U32(0, _CommandAddress + 0x4);
685
686 INFO_LOG(WII_IPC_ES, "IOCTL_ES_GETTMDVIEWS: title: %08x/%08x (buffer size: %i)", (u32)(TitleID >> 32), (u32)TitleID, MaxCount)do { { if (LogTypes::LINFO <= 3) GenericLog(LogTypes::LINFO
, LogTypes::WII_IPC_ES, "/home/anal/dolphin-emu/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_es.cpp"
, 686, "IOCTL_ES_GETTMDVIEWS: title: %08x/%08x (buffer size: %i)"
, (u32)(TitleID >> 32), (u32)TitleID, MaxCount); } } while
(0)
;
687 return true;
688 }
689 break;
690
691 case IOCTL_ES_GETCONSUMPTION: // This is at least what crediar's ES module does
692 Memory::Write_U32(0, Buffer.PayloadBuffer[1].m_Address);
693 Memory::Write_U32(0, _CommandAddress + 0x4);
694 WARN_LOG(WII_IPC_ES, "IOCTL_ES_GETCONSUMPTION:%d", Memory::Read_U32(_CommandAddress+4))do { { if (LogTypes::LWARNING <= 3) GenericLog(LogTypes::LWARNING
, LogTypes::WII_IPC_ES, "/home/anal/dolphin-emu/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_es.cpp"
, 694, "IOCTL_ES_GETCONSUMPTION:%d", Memory::Read_U32(_CommandAddress
+4)); } } while (0)
;
695 return true;
696
697 case IOCTL_ES_DELETETICKET:
698 {
699 u64 TitleID = Memory::Read_U64(Buffer.InBuffer[0].m_Address);
700 INFO_LOG(WII_IPC_ES, "IOCTL_ES_DELETETICKET: title: %08x/%08x", (u32)(TitleID >> 32), (u32)TitleID)do { { if (LogTypes::LINFO <= 3) GenericLog(LogTypes::LINFO
, LogTypes::WII_IPC_ES, "/home/anal/dolphin-emu/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_es.cpp"
, 700, "IOCTL_ES_DELETETICKET: title: %08x/%08x", (u32)(TitleID
>> 32), (u32)TitleID); } } while (0)
;
701 if (File::Delete(Common::GetTicketFileName(TitleID)))
702 {
703 Memory::Write_U32(0, _CommandAddress + 0x4);
704 }
705 else
706 {
707 // Presumably return -1017 when delete fails
708 Memory::Write_U32(ES_PARAMTER_SIZE_OR_ALIGNMENT, _CommandAddress + 0x4);
709 }
710 }
711 break;
712 case IOCTL_ES_DELETETITLECONTENT:
713 {
714 u64 TitleID = Memory::Read_U64(Buffer.InBuffer[0].m_Address);
715 INFO_LOG(WII_IPC_ES, "IOCTL_ES_DELETETITLECONTENT: title: %08x/%08x", (u32)(TitleID >> 32), (u32)TitleID)do { { if (LogTypes::LINFO <= 3) GenericLog(LogTypes::LINFO
, LogTypes::WII_IPC_ES, "/home/anal/dolphin-emu/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_es.cpp"
, 715, "IOCTL_ES_DELETETITLECONTENT: title: %08x/%08x", (u32)
(TitleID >> 32), (u32)TitleID); } } while (0)
;
716 if (DiscIO::CNANDContentManager::Access().RemoveTitle(TitleID))
717 {
718 Memory::Write_U32(0, _CommandAddress + 0x4);
719 }
720 else
721 {
722 // Presumably return -1017 when title not installed TODO verify
723 Memory::Write_U32(ES_PARAMTER_SIZE_OR_ALIGNMENT, _CommandAddress + 0x4);
724 }
725
726 }
727 case IOCTL_ES_GETSTOREDTMDSIZE:
728 {
729 _dbg_assert_msg_(WII_IPC_ES, Buffer.NumberInBuffer == 1, "IOCTL_ES_GETSTOREDTMDSIZE no in buffer"){};
730 // _dbg_assert_msg_(WII_IPC_ES, Buffer.NumberPayloadBuffer == 1, "IOCTL_ES_ES_GETSTOREDTMDSIZE no out buffer");
731
732 u64 TitleID = Memory::Read_U64(Buffer.InBuffer[0].m_Address);
733 const DiscIO::INANDContentLoader& Loader = AccessContentDevice(TitleID);
734
735 _dbg_assert_(WII_IPC_ES, Loader.IsValid()){};
736 u32 TMDCnt = 0;
737 if (Loader.IsValid())
738 {
739 TMDCnt += DiscIO::INANDContentLoader::TMD_HEADER_SIZE;
740 TMDCnt += (u32)Loader.GetContentSize() * DiscIO::INANDContentLoader::CONTENT_HEADER_SIZE;
741 }
742 if(Buffer.NumberPayloadBuffer)
743 Memory::Write_U32(TMDCnt, Buffer.PayloadBuffer[0].m_Address);
744
745 Memory::Write_U32(0, _CommandAddress + 0x4);
746
747 INFO_LOG(WII_IPC_ES, "IOCTL_ES_GETSTOREDTMDSIZE: title: %08x/%08x (view size %i)", (u32)(TitleID >> 32), (u32)TitleID, TMDCnt)do { { if (LogTypes::LINFO <= 3) GenericLog(LogTypes::LINFO
, LogTypes::WII_IPC_ES, "/home/anal/dolphin-emu/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_es.cpp"
, 747, "IOCTL_ES_GETSTOREDTMDSIZE: title: %08x/%08x (view size %i)"
, (u32)(TitleID >> 32), (u32)TitleID, TMDCnt); } } while
(0)
;
748 return true;
749 }
750 break;
751 case IOCTL_ES_GETSTOREDTMD:
752 {
753 _dbg_assert_msg_(WII_IPC_ES, Buffer.NumberInBuffer > 0, "IOCTL_ES_GETSTOREDTMD no in buffer"){};
754 // requires 1 inbuffer and no outbuffer, presumably outbuffer required when second inbuffer is used for maxcount (allocated mem?)
755 // called with 1 inbuffer after deleting a titleid
756 //_dbg_assert_msg_(WII_IPC_ES, Buffer.NumberPayloadBuffer == 1, "IOCTL_ES_GETSTOREDTMD no out buffer");
757
758 u64 TitleID = Memory::Read_U64(Buffer.InBuffer[0].m_Address);
759 u32 MaxCount = 0;
760 if (Buffer.NumberInBuffer > 1)
761 {
762 // TODO: actually use this param in when writing to the outbuffer :/
763 MaxCount = Memory::Read_U32(Buffer.InBuffer[1].m_Address);
764 }
765 const DiscIO::INANDContentLoader& Loader = AccessContentDevice(TitleID);
766
767
768 INFO_LOG(WII_IPC_ES, "IOCTL_ES_GETSTOREDTMD: title: %08x/%08x buffer size: %i", (u32)(TitleID >> 32), (u32)TitleID, MaxCount)do { { if (LogTypes::LINFO <= 3) GenericLog(LogTypes::LINFO
, LogTypes::WII_IPC_ES, "/home/anal/dolphin-emu/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_es.cpp"
, 768, "IOCTL_ES_GETSTOREDTMD: title: %08x/%08x buffer size: %i"
, (u32)(TitleID >> 32), (u32)TitleID, MaxCount); } } while
(0)
;
769
770 if (Loader.IsValid() && Buffer.NumberPayloadBuffer)
771 {
772 u32 Address = Buffer.PayloadBuffer[0].m_Address;
773
774 Memory::WriteBigEData(Loader.GetTMDHeader(), Address, DiscIO::INANDContentLoader::TMD_HEADER_SIZE);
775 Address += DiscIO::INANDContentLoader::TMD_HEADER_SIZE;
776
777 const std::vector<DiscIO::SNANDContent>& rContent = Loader.GetContent();
778 for (size_t i=0; i<Loader.GetContentSize(); i++)
779 {
780 Memory::WriteBigEData(rContent[i].m_Header, Address, DiscIO::INANDContentLoader::CONTENT_HEADER_SIZE);
781 Address += DiscIO::INANDContentLoader::CONTENT_HEADER_SIZE;
782 }
783
784 _dbg_assert_(WII_IPC_ES, (Address-Buffer.PayloadBuffer[0].m_Address) == Buffer.PayloadBuffer[0].m_Size){};
785 }
786 Memory::Write_U32(0, _CommandAddress + 0x4);
787
788 INFO_LOG(WII_IPC_ES, "IOCTL_ES_GETSTOREDTMD: title: %08x/%08x (buffer size: %i)", (u32)(TitleID >> 32), (u32)TitleID, MaxCount)do { { if (LogTypes::LINFO <= 3) GenericLog(LogTypes::LINFO
, LogTypes::WII_IPC_ES, "/home/anal/dolphin-emu/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_es.cpp"
, 788, "IOCTL_ES_GETSTOREDTMD: title: %08x/%08x (buffer size: %i)"
, (u32)(TitleID >> 32), (u32)TitleID, MaxCount); } } while
(0)
;
789 return true;
790 }
791 break;
792
793 case IOCTL_ES_ENCRYPT:
794 {
795 u32 keyIndex = Memory::Read_U32(Buffer.InBuffer[0].m_Address);
796 u8* IV = Memory::GetPointer(Buffer.InBuffer[1].m_Address);
797 u8* source = Memory::GetPointer(Buffer.InBuffer[2].m_Address);
798 u32 size = Buffer.InBuffer[2].m_Size;
799 u8* destination = Memory::GetPointer(Buffer.PayloadBuffer[1].m_Address);
800
801 AES_KEY AESKey;
802 AES_set_encrypt_key(keyTable[keyIndex], 128, &AESKey);
803 AES_cbc_encrypt(source, destination, size, &AESKey, IV, AES_ENCRYPT1);
804
805 _dbg_assert_msg_(WII_IPC_ES, keyIndex == 6, "IOCTL_ES_ENCRYPT: Key type is not SD, data will be crap"){};
806 }
807 break;
808
809 case IOCTL_ES_DECRYPT:
810 {
811 u32 keyIndex = Memory::Read_U32(Buffer.InBuffer[0].m_Address);
812 u8* IV = Memory::GetPointer(Buffer.InBuffer[1].m_Address);
813 u8* source = Memory::GetPointer(Buffer.InBuffer[2].m_Address);
814 u32 size = Buffer.InBuffer[2].m_Size;
815 u8* destination = Memory::GetPointer(Buffer.PayloadBuffer[1].m_Address);
816
817 AES_KEY AESKey;
818 AES_set_decrypt_key(keyTable[keyIndex], 128, &AESKey);
819 AES_cbc_encrypt(source, destination, size, &AESKey, IV, AES_DECRYPT0);
820
821 _dbg_assert_msg_(WII_IPC_ES, keyIndex == 6, "IOCTL_ES_DECRYPT: Key type is not SD, data will be crap"){};
822 }
823 break;
824
825
826 case IOCTL_ES_LAUNCH:
827 {
828 _dbg_assert_(WII_IPC_ES, Buffer.NumberInBuffer == 2){};
829 bool bSuccess = false;
830 u16 IOSv = 0xffff;
831
832 u64 TitleID = Memory::Read_U64(Buffer.InBuffer[0].m_Address);
833 u32 view = Memory::Read_U32(Buffer.InBuffer[1].m_Address);
834 u64 ticketid = Memory::Read_U64(Buffer.InBuffer[1].m_Address+4);
835 u32 devicetype = Memory::Read_U32(Buffer.InBuffer[1].m_Address+12);
836 u64 titleid = Memory::Read_U64(Buffer.InBuffer[1].m_Address+16);
837 u16 access = Memory::Read_U16(Buffer.InBuffer[1].m_Address+24);
838
839
840 if ((u32)(TitleID>>32) != 0x00000001 || TitleID == TITLEID_SYSMENU0x0000000100000002ull)
841 {
842 const DiscIO::INANDContentLoader& ContentLoader = AccessContentDevice(TitleID);
843 if (ContentLoader.IsValid())
844 {
845 u32 bootInd = ContentLoader.GetBootIndex();
846 const DiscIO::SNANDContent* pContent = ContentLoader.GetContentByIndex(bootInd);
847 if (pContent)
848 {
849 LoadWAD(Common::GetTitleContentPath(TitleID));
850 std::unique_ptr<CDolLoader> pDolLoader;
851 if (pContent->m_pData)
852 {
853 pDolLoader.reset(new CDolLoader(pContent->m_pData, pContent->m_Size));
854 }
855 else
856 {
857 pDolLoader.reset(new CDolLoader(pContent->m_Filename.c_str()));
858 }
859 pDolLoader->Load(); // TODO: Check why sysmenu does not load the DOL correctly
860 PCPowerPC::ppcState.pc = pDolLoader->GetEntryPoint() | 0x80000000;
861 IOSv = ContentLoader.GetIosVersion();
862 bSuccess = true;
863 }
864 }
865 }
866 else // IOS, MIOS, BC etc
867 {
868 //TODO: fixme
869 // The following is obviously a hack
870 // Lie to mem about loading a different IOS
871 // someone with an affected game should test
872 IOSv = TitleID & 0xffff;
873 }
874 if (!bSuccess && IOSv >= 30 && IOSv != 0xffff)
875 {
876 PanicAlertT("IOCTL_ES_LAUNCH: Game tried to reload an IOS or a title that is not available in your NAND dump\n"MsgAlert(false, WARNING, "IOCTL_ES_LAUNCH: Game tried to reload an IOS or a title that is not available in your NAND dump\n"
"TitleID %016llx.\n Dolphin will likely hang now.", TitleID)
877 "TitleID %016llx.\n Dolphin will likely hang now.", TitleID)MsgAlert(false, WARNING, "IOCTL_ES_LAUNCH: Game tried to reload an IOS or a title that is not available in your NAND dump\n"
"TitleID %016llx.\n Dolphin will likely hang now.", TitleID)
;
878 }
879 else
880 {
881 CWII_IPC_HLE_Device_usb_oh1_57e_305* s_Usb = GetUsbPointer();
882 size_t size = s_Usb->m_WiiMotes.size();
883 bool* wiiMoteConnected = new bool[size];
884 for (unsigned int i = 0; i < size; i++)
885 wiiMoteConnected[i] = s_Usb->m_WiiMotes[i].IsConnected();
886
887 std::string tContentFile(m_ContentFile.c_str());
888
889 WII_IPC_HLE_Interface::Reset(true);
890 WII_IPC_HLE_Interface::Init();
891 s_Usb = GetUsbPointer();
892 for (unsigned int i = 0; i < s_Usb->m_WiiMotes.size(); i++)
893 {
894 if (wiiMoteConnected[i])
895 {
896 s_Usb->m_WiiMotes[i].Activate(false);
897 s_Usb->m_WiiMotes[i].Activate(true);
898 }
899 else
900 {
901 s_Usb->m_WiiMotes[i].Activate(false);
902 }
903 }
904
905 delete[] wiiMoteConnected;
906 WII_IPC_HLE_Interface::SetDefaultContentFile(tContentFile);
907 }
908 // Pass the "#002 check"
909 // Apploader should write the IOS version and revision to 0x3140, and compare it
910 // to 0x3188 to pass the check, but we don't do it, and i don't know where to read the IOS rev...
911 // Currently we just write 0xFFFF for the revision, copy manually and it works fine :p
912 // TODO : figure it correctly : where should we read the IOS rev that the wad "needs" ?
913 Memory::Write_U16(IOSv, 0x00003140);
914 Memory::Write_U16(0xFFFF, 0x00003142);
915 Memory::Write_U32(Memory::Read_U32(0x00003140), 0x00003188);
916
917 //TODO: provide correct return code when bSuccess= false
918 Memory::Write_U32(0, _CommandAddress + 0x4);
919
920 ERROR_LOG(WII_IPC_ES, "IOCTL_ES_LAUNCH %016llx %08x %016llx %08x %016llx %04x", TitleID,view,ticketid,devicetype,titleid,access)do { { if (LogTypes::LERROR <= 3) GenericLog(LogTypes::LERROR
, LogTypes::WII_IPC_ES, "/home/anal/dolphin-emu/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_es.cpp"
, 920, "IOCTL_ES_LAUNCH %016llx %08x %016llx %08x %016llx %04x"
, TitleID,view,ticketid,devicetype,titleid,access); } } while
(0)
;
921 // IOCTL_ES_LAUNCH 0001000248414341 00000001 0001c0fef3df2cfa 00000000 0001000248414341 ffff
922
923 //We have to handle the reply ourselves as this handle is not valid anymore
924
925
926 // It seems that the original hardware overwrites the command after it has been
927 // executed. We write 8 which is not any valid command, and what IOS does
928 Memory::Write_U32(8, _CommandAddress);
929 // IOS seems to write back the command that was responded to
930 Memory::Write_U32(6, _CommandAddress + 8);
931
932 // Generate a reply to the IPC command
933 WII_IPC_HLE_Interface::EnqReply(_CommandAddress, 0);
934
935 return false;
936 }
937 break;
938
939 case IOCTL_ES_CHECKKOREAREGION: //note by DacoTaco : name is unknown, i just tried to name it SOMETHING
940 //IOS70 has this to let system menu 4.2 check if the console is region changed. it returns -1017
941 //if the IOS didn't find the Korean keys and 0 if it does. 0 leads to a error 003
942 WARN_LOG(WII_IPC_ES,"IOCTL_ES_CHECKKOREAREGION: Title checked for Korean keys.")do { { if (LogTypes::LWARNING <= 3) GenericLog(LogTypes::LWARNING
, LogTypes::WII_IPC_ES, "/home/anal/dolphin-emu/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_es.cpp"
, 942, "IOCTL_ES_CHECKKOREAREGION: Title checked for Korean keys."
); } } while (0)
;
943 Memory::Write_U32(ES_PARAMTER_SIZE_OR_ALIGNMENT , _CommandAddress + 0x4);
944 return true;
945
946 case IOCTL_ES_GETDEVICECERT: // (Input: none, Output: 384 bytes)
947 {
948 WARN_LOG(WII_IPC_ES, "IOCTL_ES_GETDEVICECERT")do { { if (LogTypes::LWARNING <= 3) GenericLog(LogTypes::LWARNING
, LogTypes::WII_IPC_ES, "/home/anal/dolphin-emu/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_es.cpp"
, 948, "IOCTL_ES_GETDEVICECERT"); } } while (0)
;
949 _dbg_assert_(WII_IPC_ES, Buffer.NumberPayloadBuffer == 1){};
950 u8* destination = Memory::GetPointer(Buffer.PayloadBuffer[0].m_Address);
951
952 EcWii &ec = EcWii::GetInstance();
953 get_ng_cert(destination, ec.getNgId(), ec.getNgKeyId(), ec.getNgPriv(), ec.getNgSig());
954 }
955 break;
956
957 case IOCTL_ES_SIGN:
958 {
959 WARN_LOG(WII_IPC_ES, "IOCTL_ES_SIGN")do { { if (LogTypes::LWARNING <= 3) GenericLog(LogTypes::LWARNING
, LogTypes::WII_IPC_ES, "/home/anal/dolphin-emu/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_es.cpp"
, 959, "IOCTL_ES_SIGN"); } } while (0)
;
960 u8 *ap_cert_out = Memory::GetPointer(Buffer.PayloadBuffer[1].m_Address);
961 u8 *data = Memory::GetPointer(Buffer.InBuffer[0].m_Address);
962 u32 data_size = Buffer.InBuffer[0].m_Size;
963 u8 *sig_out = Memory::GetPointer(Buffer.PayloadBuffer[0].m_Address);
964
965 EcWii &ec = EcWii::GetInstance();
966 get_ap_sig_and_cert(sig_out, ap_cert_out, m_TitleID, data, data_size, ec.getNgPriv(), ec.getNgId());
967 }
968 break;
969
970 case IOCTL_ES_GETBOOT2VERSION:
971 {
972 WARN_LOG(WII_IPC_ES, "IOCTL_ES_GETBOOT2VERSION")do { { if (LogTypes::LWARNING <= 3) GenericLog(LogTypes::LWARNING
, LogTypes::WII_IPC_ES, "/home/anal/dolphin-emu/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_es.cpp"
, 972, "IOCTL_ES_GETBOOT2VERSION"); } } while (0)
;
973
974 Memory::Write_U32(4, Buffer.PayloadBuffer[0].m_Address); // as of 26/02/2012, this was latest bootmii version
975 }
976 break;
977
978 // ===============================================================================================
979 // unsupported functions
980 // ===============================================================================================
981 case IOCTL_ES_DIGETTICKETVIEW: // (Input: none, Output: 216 bytes) bug crediar :D
982 WARN_LOG(WII_IPC_ES, "IOCTL_ES_DIGETTICKETVIEW: this looks really wrong...")do { { if (LogTypes::LWARNING <= 3) GenericLog(LogTypes::LWARNING
, LogTypes::WII_IPC_ES, "/home/anal/dolphin-emu/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_es.cpp"
, 982, "IOCTL_ES_DIGETTICKETVIEW: this looks really wrong..."
); } } while (0)
;
983 break;
984
985 case IOCTL_ES_GETOWNEDTITLECNT:
986 WARN_LOG(WII_IPC_ES, "IOCTL_ES_GETOWNEDTITLECNT")do { { if (LogTypes::LWARNING <= 3) GenericLog(LogTypes::LWARNING
, LogTypes::WII_IPC_ES, "/home/anal/dolphin-emu/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_es.cpp"
, 986, "IOCTL_ES_GETOWNEDTITLECNT"); } } while (0)
;
987 Memory::Write_U32(0, Buffer.PayloadBuffer[0].m_Address);
988 break;
989
990 default:
991 WARN_LOG(WII_IPC_ES, "CWII_IPC_HLE_Device_es: 0x%x", Buffer.Parameter)do { { if (LogTypes::LWARNING <= 3) GenericLog(LogTypes::LWARNING
, LogTypes::WII_IPC_ES, "/home/anal/dolphin-emu/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_es.cpp"
, 991, "CWII_IPC_HLE_Device_es: 0x%x", Buffer.Parameter); } }
while (0)
;
992 DumpCommands(_CommandAddress, 8, LogTypes::WII_IPC_ES);
993 INFO_LOG(WII_IPC_ES, "command.Parameter: 0x%08x", Buffer.Parameter)do { { if (LogTypes::LINFO <= 3) GenericLog(LogTypes::LINFO
, LogTypes::WII_IPC_ES, "/home/anal/dolphin-emu/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_es.cpp"
, 993, "command.Parameter: 0x%08x", Buffer.Parameter); } } while
(0)
;
994 break;
995 }
996
997 // Write return value (0 means OK)
998 Memory::Write_U32(0, _CommandAddress + 0x4);
999
1000 return true;
1001}
1002
1003const DiscIO::INANDContentLoader& CWII_IPC_HLE_Device_es::AccessContentDevice(u64 _TitleID)
1004{
1005 if (m_pContentLoader->IsValid() && m_pContentLoader->GetTitleID() == _TitleID)
1006 return *m_pContentLoader;
1007
1008 CTitleToContentMap::iterator itr = m_NANDContent.find(_TitleID);
1009 if (itr != m_NANDContent.end())
1010 return *itr->second;
1011
1012 m_NANDContent[_TitleID] = &DiscIO::CNANDContentManager::Access().GetNANDLoader(_TitleID);
1013
1014 _dbg_assert_msg_(WII_IPC_ES, ((u32)(_TitleID >> 32) == 0x00010000) || m_NANDContent[_TitleID]->IsValid(), "NandContent not valid for TitleID %08x/%08x", (u32)(_TitleID >> 32), (u32)_TitleID){};
1015 return *m_NANDContent[_TitleID];
1016}
1017
1018bool CWII_IPC_HLE_Device_es::IsValid(u64 _TitleID) const
1019{
1020 if (m_pContentLoader->IsValid() && m_pContentLoader->GetTitleID() == _TitleID)
1021 return true;
1022
1023 return false;
1024}
1025
1026
1027u32 CWII_IPC_HLE_Device_es::ES_DIVerify(u8* _pTMD, u32 _sz)
1028{
1029 u64 titleID = 0xDEADBEEFDEADBEEFull;
1030 u64 tmdTitleID = Common::swap64(*(u64*)(_pTMD+0x18c));
1031 VolumeHandler::GetVolume()->GetTitleID((u8*)&titleID);
1032 if (Common::swap64(titleID) != tmdTitleID)
1033 {
1034 return -1;
1035 }
1036 std::string tmdPath = Common::GetTMDFileName(tmdTitleID);
1037
1038 File::CreateFullPath(tmdPath);
1039 File::CreateFullPath(Common::GetTitleDataPath(tmdTitleID));
1040
1041 Movie::g_titleID = tmdTitleID;
1042 std::string savePath = Common::GetTitleDataPath(tmdTitleID);
1043 if (Movie::IsRecordingInput())
1044 {
1045 // TODO: Check for the actual save data
1046 if (File::Exists((savePath + "banner.bin").c_str()))
1047 Movie::g_bClearSave = false;
1048 else
1049 Movie::g_bClearSave = true;
1050 }
1051
1052 // TODO: Force the game to save to another location, instead of moving the user's save.
1053 if (Movie::IsPlayingInput() && Movie::IsConfigSaved() && Movie::IsStartingFromClearSave())
1054 {
1055 if (File::Exists((savePath + "banner.bin").c_str()))
1056 {
1057 if (File::Exists((savePath + "../backup/").c_str()))
1058 {
1059 // The last run of this game must have been to play back a movie, so their save is already backed up.
1060 File::DeleteDirRecursively(savePath.c_str());
1061 }
1062 else
1063 {
1064 #ifdef _WIN32
1065 MoveFile(UTF8ToTStr(savePath).c_str(), UTF8ToTStr(savePath + "../backup/").c_str());
1066 #else
1067 File::CopyDir(savePath.c_str(),(savePath + "../backup/").c_str());
1068 File::DeleteDirRecursively(savePath.c_str());
1069 #endif
1070 }
1071 }
1072 }
1073 else if (File::Exists((savePath + "../backup/").c_str()))
1074 {
1075 // Delete the save made by a previous movie, and copy back the user's save.
1076 if (File::Exists((savePath + "banner.bin").c_str()))
1077 File::DeleteDirRecursively(savePath);
1078 #ifdef _WIN32
1079 MoveFile(UTF8ToTStr(savePath + "../backup/").c_str(), UTF8ToTStr(savePath).c_str());
1080 #else
1081 File::CopyDir((savePath + "../backup/").c_str(), savePath.c_str());
1082 File::DeleteDirRecursively((savePath + "../backup/").c_str());
1083 #endif
1084 }
1085
1086 if(!File::Exists(tmdPath))
1087 {
1088 File::IOFile _pTMDFile(tmdPath, "wb");
1089 if (!_pTMDFile.WriteBytes(_pTMD, _sz))
1090 ERROR_LOG(WII_IPC_ES, "DIVerify failed to write disc TMD to NAND.")do { { if (LogTypes::LERROR <= 3) GenericLog(LogTypes::LERROR
, LogTypes::WII_IPC_ES, "/home/anal/dolphin-emu/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_es.cpp"
, 1090, "DIVerify failed to write disc TMD to NAND."); } } while
(0)
;
1091 }
1092 DiscIO::cUIDsys::AccessInstance().AddTitle(tmdTitleID);
1093 return 0;
1094}