mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-01 14:58:14 +00:00
Replaces the temporary fix that did the hidden modes' IO inside Update. The IO thread read and wrote the dialog's request, display state and save list, all shared with the emulator thread, which kept using them to draw the dialog and reload the request from the game. Now the IO thread works on its own copy of the request, its own SavedataParam and directory names resolved up front, and shares nothing else with the emulator thread but the (locked) file system, MemoryStick_FreeSpace's cached use and sceChnnlsv's scratch buffer and kirk state, the last two now under locks too. It still reads and writes the game's buffers directly, like a PSP's utility threads and sceIoReadAsync do, so a savestate waits for it before it saves or loads memory. Save bookkeeping, the save indicator and display changes happen on the emulator thread when the results are taken, and only the request fields the IO changed are copied back, so a game's own edits in the meantime survive. Hidden modes take the results at the next Update (or, with Host IO timing, the first Update that finds them done). The visible dialogs keep drawing and take them once the IO is done; save and load used to stall the emulator thread for the whole operation. Savestates keep results that haven't been taken yet. When the results land in PSP memory doesn't matter to games, so utility/savedata/filelist now only prints them once the utility has finished. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
574 lines
15 KiB
C++
574 lines
15 KiB
C++
// Copyright (c) 2012- PPSSPP Project.
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, version 2.0 or later versions.
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License 2.0 for more details.
|
|
|
|
// A copy of the GPL 2.0 should have been included with the program.
|
|
// If not, see http://www.gnu.org/licenses/
|
|
|
|
// Official git repository and contact information can be found at
|
|
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
|
|
|
#include <mutex>
|
|
|
|
#include "Core/MemMapHelpers.h"
|
|
#include "Core/HLE/HLE.h"
|
|
#include "Core/HLE/FunctionWrappers.h"
|
|
|
|
#include "Core/HLE/sceChnnlsv.h"
|
|
#include "Core/HLE/sceKernel.h"
|
|
|
|
static KirkState g_kirk;
|
|
|
|
KirkState *__ChnnlsvKirkState() {
|
|
return &g_kirk;
|
|
}
|
|
|
|
// The savedata IO thread uses these through the sceSd functions below, while the game can call them
|
|
// (and the kirk ones) on the emulator thread.
|
|
static std::mutex g_lock;
|
|
static u8 dataBuf[2048+20];
|
|
static u8 *dataBuf2 = dataBuf + 20;
|
|
|
|
static const u8 hash198C[16] = {0xFA, 0xAA, 0x50, 0xEC, 0x2F, 0xDE, 0x54, 0x93, 0xAD, 0x14, 0xB2, 0xCE, 0xA5, 0x30, 0x05, 0xDF};
|
|
static const u8 hash19BC[16] = {0xCB, 0x15, 0xF4, 0x07, 0xF9, 0x6A, 0x52, 0x3C, 0x04, 0xB9, 0xB2, 0xEE, 0x5C, 0x53, 0xFA, 0x86};
|
|
|
|
static const u8 key19CC[16] = {0x70, 0x44, 0xA3, 0xAE, 0xEF, 0x5D, 0xA5, 0xF2, 0x85, 0x7F, 0xF2, 0xD6, 0x94, 0xF5, 0x36, 0x3B};
|
|
static const u8 key19DC[16] = {0xEC, 0x6D, 0x29, 0x59, 0x26, 0x35, 0xA5, 0x7F, 0x97, 0x2A, 0x0D, 0xBC, 0xA3, 0x26, 0x33, 0x00};
|
|
static const u8 key199C[16] = {0x36, 0xA5, 0x3E, 0xAC, 0xC5, 0x26, 0x9E, 0xA3, 0x83, 0xD9, 0xEC, 0x25, 0x6C, 0x48, 0x48, 0x72};
|
|
static const u8 key19AC[16] = {0xD8, 0xC0, 0xB0, 0xF3, 0x3E, 0x6B, 0x76, 0x85, 0xFD, 0xFB, 0x4D, 0x7D, 0x45, 0x1E, 0x92, 0x03};
|
|
|
|
static void *memxor(void * dest, const void * src, size_t n)
|
|
{
|
|
char const *s = (char const*)src;
|
|
char *d = (char*)dest;
|
|
|
|
for (; n > 0; n--)
|
|
*d++ ^= *s++;
|
|
|
|
return dest;
|
|
}
|
|
|
|
// The reason for the values from *FromMode calculations are not known.
|
|
static int numFromMode(int mode)
|
|
{
|
|
int num = 0;
|
|
switch(mode)
|
|
{
|
|
case 1:
|
|
num = 3;
|
|
break;
|
|
case 2:
|
|
num = 5;
|
|
break;
|
|
case 3:
|
|
num = 12;
|
|
break;
|
|
case 4:
|
|
num = 13;
|
|
break;
|
|
case 6:
|
|
num = 17;
|
|
break;
|
|
default:
|
|
num = 16;
|
|
break;
|
|
}
|
|
return num;
|
|
}
|
|
static int numFromMode2(int mode)
|
|
{
|
|
int num = 18;
|
|
if (mode == 1)
|
|
num = 4;
|
|
else if (mode == 3)
|
|
num = 14;
|
|
return num;
|
|
}
|
|
|
|
static int typeFromMode(int mode)
|
|
{
|
|
return (mode == 1 || mode == 2) ? 83 :
|
|
((mode == 3 || mode == 4) ? 87 : 100);
|
|
}
|
|
|
|
static int kirkSendCmd(KirkState *kirk, u8* data, int length, int num, bool encrypt)
|
|
{
|
|
*(int*)(data+0) = encrypt ? KIRK_MODE_ENCRYPT_CBC : KIRK_MODE_DECRYPT_CBC;
|
|
*(int*)(data+4) = 0;
|
|
*(int*)(data+8) = 0;
|
|
*(int*)(data+12) = num;
|
|
*(int*)(data+16) = length;
|
|
|
|
if (kirk_sceUtilsBufferCopyWithRange(kirk, data, length + 20, data, length + 20, encrypt ? KIRK_CMD_ENCRYPT_IV_0 : KIRK_CMD_DECRYPT_IV_0))
|
|
return -257;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int kirkSendFuseCmd(KirkState *kirk, u8* data, int length, bool encrypt)
|
|
{
|
|
*(int*)(data+0) = encrypt ? KIRK_MODE_ENCRYPT_CBC : KIRK_MODE_DECRYPT_CBC;
|
|
*(int*)(data+4) = 0;
|
|
*(int*)(data+8) = 0;
|
|
*(int*)(data+12) = 256;
|
|
*(int*)(data+16) = length;
|
|
|
|
// Note: CMD 5 and 8 are not available, will always return -1
|
|
if (kirk_sceUtilsBufferCopyWithRange(kirk, data, length + 20, data, length + 20, encrypt ? KIRK_CMD_ENCRYPT_IV_FUSE : KIRK_CMD_DECRYPT_IV_FUSE))
|
|
return -258;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int sub_15B0(KirkState *kirk, u8* data, int alignedLen, u8* buf, int val)
|
|
{
|
|
u8 sp0[16];
|
|
memcpy(sp0, data+alignedLen+4, 16);
|
|
|
|
int res = kirkSendCmd(kirk, data, alignedLen, val, false);
|
|
if (res)
|
|
return res;
|
|
|
|
memxor(data, buf, 16);
|
|
memcpy(buf, sp0, 16);
|
|
return 0;
|
|
}
|
|
|
|
static int sub_0000(KirkState *kirk, u8* data_out, u8* data, int alignedLen, const u8* data2, int& data3, int mode)
|
|
{
|
|
memcpy(data_out+20, data2, 16);
|
|
// Mode 1:2 is 83, 3:4 is 87, 5:6 is 100
|
|
int type = typeFromMode(mode);
|
|
int res;
|
|
|
|
if (type == 87)
|
|
memxor(data_out+20, key19AC, 16);
|
|
else if (type == 100)
|
|
memxor(data_out+20, key19DC, 16);
|
|
|
|
// Odd is Cmd, Even is FuseCmd
|
|
switch(mode)
|
|
{
|
|
case 2: case 4: case 6: res = kirkSendFuseCmd(kirk, data_out, 16, false);
|
|
break;
|
|
case 1: case 3: default:res = kirkSendCmd(kirk, data_out, 16, numFromMode2(mode), false);
|
|
break;
|
|
}
|
|
|
|
if (type == 87)
|
|
memxor(data_out, key199C, 16);
|
|
else if (type == 100)
|
|
memxor(data_out, key19CC, 16);
|
|
|
|
if (res)
|
|
return res;
|
|
|
|
u8 sp0[16], sp16[16];
|
|
memcpy(sp16, data_out, 16);
|
|
if (data3 == 1)
|
|
{
|
|
memset(sp0, 0, 16);
|
|
}
|
|
else
|
|
{
|
|
memcpy(sp0, sp16, 12);
|
|
*(u32*)(sp0+12) = data3-1;
|
|
}
|
|
|
|
if (alignedLen > 0)
|
|
{
|
|
for(int i = 20; i < alignedLen + 20; i += 16)
|
|
{
|
|
memcpy(data_out+i, sp16, 12);
|
|
*(u32*)(data_out+12+i) = data3;
|
|
data3++;
|
|
}
|
|
}
|
|
|
|
res = sub_15B0(kirk, data_out, alignedLen, sp0, type);
|
|
if (res)
|
|
return res;
|
|
|
|
if (alignedLen > 0)
|
|
memxor(data, data_out, alignedLen);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int sub_1510(KirkState *kirk, u8* data, int size, u8* result , int num)
|
|
{
|
|
memxor(data+20, result, 16);
|
|
|
|
int res = kirkSendCmd(kirk, data, size, num, true);
|
|
if(res)
|
|
return res;
|
|
|
|
memcpy(result, data+size+4, 16);
|
|
return 0;
|
|
}
|
|
|
|
static int sub_17A8(KirkState *kirk, u8* data)
|
|
{
|
|
if (kirk_sceUtilsBufferCopyWithRange(kirk, data, 20, 0, 0, 14) == 0)
|
|
return 0;
|
|
return -261;
|
|
}
|
|
|
|
static int sceSdGetLastIndex(u32 addressCtx, u32 addressHash, u32 addressKey) {
|
|
auto ctx = PSPPointer<pspChnnlsvContext1>::Create(addressCtx);
|
|
u8 *hash = Memory::GetPointerWriteOrException(addressHash);
|
|
if (!ctx.IsValid() || !hash)
|
|
return hleLogError(Log::sceMisc, 0, "Invalid pointer");
|
|
return hleLogDebug(Log::sceMisc, sceSdMacFinal(*ctx, hash, Memory::GetPointerWriteOrException(addressKey)));
|
|
}
|
|
|
|
int sceSdMacFinal(pspChnnlsvContext1& ctx, u8* in_hash, const u8* in_key)
|
|
{
|
|
std::lock_guard<std::mutex> guard(g_lock);
|
|
if(ctx.keyLength >= 17)
|
|
return -1026;
|
|
|
|
int num = numFromMode(ctx.mode);
|
|
|
|
memset(dataBuf2, 0, 16);
|
|
|
|
int res = kirkSendCmd(&g_kirk, dataBuf, 16, num, true);
|
|
if(res)
|
|
return res;
|
|
|
|
u8 data1[16], data2[16];
|
|
|
|
memcpy(data1, dataBuf2, 16);
|
|
int tmp1 = (data1[0] & 0x80) ? 135 : 0;
|
|
|
|
for(int i = 0; i < 15; i++)
|
|
{
|
|
u8 val1 = data1[i] << 1;
|
|
u8 val2 = data1[i+1] >> 7;
|
|
data1[i] = val1 | val2;
|
|
}
|
|
|
|
u8 tmp2 = data1[15] << 1;
|
|
tmp2 = tmp1 ^ tmp2;
|
|
data1[15] = tmp2;
|
|
|
|
if(ctx.keyLength < 16)
|
|
{
|
|
tmp1 = 0;
|
|
if((s8)data1[0] < 0)
|
|
{
|
|
tmp1 = 135;
|
|
}
|
|
for(int i = 0; i < 15; i++)
|
|
{
|
|
u8 val1 = data1[i] << 1;
|
|
u8 val2 = data1[i+1] >> 7;
|
|
data1[i] = val1 | val2;
|
|
}
|
|
u8 tmp2 = data1[15] << 1;
|
|
tmp2 = tmp1 ^ tmp2;
|
|
data1[15] = tmp2;
|
|
|
|
int oldKeyLength = ctx.keyLength;
|
|
*(s8*)(ctx.key + ctx.keyLength) = -128;
|
|
int i = oldKeyLength + 1;
|
|
if(i < 16)
|
|
memset(ctx.key + i, 0, 16 - i);
|
|
}
|
|
|
|
memxor(ctx.key, data1, 16);
|
|
memcpy(dataBuf2, ctx.key, 16);
|
|
memcpy(data2, ctx.result, 16);
|
|
|
|
int ret = sub_1510(&g_kirk, dataBuf, 16, data2, num);
|
|
if(ret)
|
|
return ret;
|
|
|
|
if(ctx.mode == 3 || ctx.mode == 4)
|
|
memxor(data2, hash198C, 16);
|
|
else if(ctx.mode == 5 || ctx.mode == 6)
|
|
memxor(data2, hash19BC, 16);
|
|
|
|
int cond = ((ctx.mode ^ 0x2) < 1 || (ctx.mode ^ 0x4) < 1 || ctx.mode == 6);
|
|
if(cond != 0)
|
|
{
|
|
memcpy(dataBuf2, data2, 16);
|
|
int ret = kirkSendFuseCmd(&g_kirk, dataBuf, 16, true);
|
|
if(ret)
|
|
return ret;
|
|
|
|
int res = kirkSendCmd(&g_kirk, dataBuf, 16, num, true);
|
|
if(res)
|
|
return res;
|
|
|
|
memcpy(data2, dataBuf2, 16);
|
|
}
|
|
|
|
if(in_key != 0)
|
|
{
|
|
for(int i = 0; i < 16; i++)
|
|
{
|
|
data2[i] = in_key[i] ^ data2[i];
|
|
}
|
|
|
|
memcpy(dataBuf2, data2, 16);
|
|
|
|
int res = kirkSendCmd(&g_kirk, dataBuf, 16, num, true);
|
|
if(res)
|
|
return res;
|
|
|
|
memcpy(data2, dataBuf2, 16);
|
|
}
|
|
memcpy(in_hash, data2, 16);
|
|
sceSdMacInit(ctx, 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int sceSdSetIndex(u32 addressCtx, int value) {
|
|
auto ctx = PSPPointer<pspChnnlsvContext1>::Create(addressCtx);
|
|
if (!ctx.IsValid())
|
|
return hleLogError(Log::sceMisc, 0, "Invalid pointer");
|
|
return hleLogDebug(Log::sceMisc, sceSdMacInit(*ctx, value));
|
|
}
|
|
|
|
int sceSdMacInit(pspChnnlsvContext1& ctx, int value)
|
|
{
|
|
ctx.mode = value;
|
|
memset(ctx.result, 0, 16);
|
|
memset(ctx.key, 0, 16);
|
|
ctx.keyLength = 0;
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int sceSdRemoveValue(u32 addressCtx, u32 addressData, int length) {
|
|
auto ctx = PSPPointer<pspChnnlsvContext1>::Create(addressCtx);
|
|
if (!ctx.IsValid() || !Memory::IsValidAddress(addressData))
|
|
return hleLogError(Log::sceMisc, 0, "Invalid pointer");
|
|
return hleLogDebug(Log::sceMisc, sceSdMacUpdate(*ctx, Memory::GetPointerWriteOrException(addressData), length));
|
|
}
|
|
|
|
int sceSdMacUpdate(pspChnnlsvContext1& ctx, const u8* data, int length)
|
|
{
|
|
std::lock_guard<std::mutex> guard(g_lock);
|
|
if(ctx.keyLength >= 17)
|
|
return -1026;
|
|
|
|
if(ctx.keyLength + length < 17)
|
|
{
|
|
memcpy(ctx.key+ctx.keyLength, data, length);
|
|
ctx.keyLength = ctx.keyLength + length;
|
|
return 0;
|
|
}
|
|
int num = numFromMode(ctx.mode);
|
|
|
|
memset(dataBuf2, 0, 2048);
|
|
memcpy(dataBuf2, ctx.key, ctx.keyLength);
|
|
|
|
int len = (ctx.keyLength + length) & 0xF;
|
|
if(len == 0) len = 16;
|
|
|
|
int newSize = ctx.keyLength;
|
|
ctx.keyLength = len;
|
|
|
|
int diff = length - len;
|
|
memcpy(ctx.key, data+diff, len);
|
|
for(int i = 0; i < diff; i++)
|
|
{
|
|
if(newSize == 2048)
|
|
{
|
|
int res = sub_1510(&g_kirk, dataBuf, 2048, ctx.result, num);
|
|
if(res)
|
|
return res;
|
|
newSize = 0;
|
|
}
|
|
dataBuf2[newSize] = data[i];
|
|
newSize++;
|
|
}
|
|
if(newSize)
|
|
sub_1510(&g_kirk, dataBuf, newSize, ctx.result, num);
|
|
// The RE code showed this always returning 0. I suspect it would want to return res instead.
|
|
return 0;
|
|
}
|
|
|
|
static int sceSdCreateList(u32 ctx2Addr, int mode, int unkwn, u32 dataAddr, u32 cryptkeyAddr) {
|
|
auto ctx2 = PSPPointer<pspChnnlsvContext2>::Create(ctx2Addr);
|
|
u8* data = Memory::GetPointerWriteOrException(dataAddr);
|
|
u8* cryptkey = Memory::GetPointerWriteOrException(cryptkeyAddr);
|
|
if (!ctx2.IsValid() || !data)
|
|
return hleLogError(Log::sceMisc, 0, "Invalid pointer");
|
|
|
|
return hleLogDebug(Log::sceMisc, sceSdCipherInit(*ctx2, mode, unkwn, data, cryptkey));
|
|
}
|
|
|
|
int sceSdCipherInit(pspChnnlsvContext2& ctx2, int mode, int uknw, u8* data, const u8* cryptkey)
|
|
{
|
|
std::lock_guard<std::mutex> guard(g_lock);
|
|
ctx2.mode = mode;
|
|
ctx2.unkn = 1;
|
|
if (uknw == 2)
|
|
{
|
|
memcpy(ctx2.cryptedData, data, 16);
|
|
if (cryptkey)
|
|
memxor(ctx2.cryptedData, cryptkey, 16);
|
|
|
|
return 0;
|
|
}
|
|
else if (uknw == 1)
|
|
{
|
|
u8 kirkHeader[37];
|
|
u8* kirkData = kirkHeader+20;
|
|
int res = sub_17A8(&g_kirk, kirkHeader);
|
|
if (res)
|
|
return res;
|
|
|
|
memcpy(kirkHeader+20, kirkHeader, 16);
|
|
memset(kirkHeader+32, 0, 4);
|
|
|
|
int type = typeFromMode(mode);
|
|
if (type == 87)
|
|
memxor(kirkData, key199C, 16);
|
|
else if (type == 100)
|
|
memxor(kirkData, key19CC, 16);
|
|
|
|
switch (mode)
|
|
{
|
|
case 2: case 4: case 6: res = kirkSendFuseCmd(&g_kirk, kirkHeader, 16, true);
|
|
break;
|
|
case 1: case 3: default:res = kirkSendCmd(&g_kirk, kirkHeader, 16, numFromMode2(mode), true);
|
|
break;
|
|
}
|
|
|
|
if (type == 87)
|
|
memxor(kirkData, key19AC, 16);
|
|
else if (type == 100)
|
|
memxor(kirkData, key19DC, 16);
|
|
|
|
if (res)
|
|
return res;
|
|
|
|
memcpy(ctx2.cryptedData, kirkData, 16);
|
|
memcpy(data, kirkData, 16);
|
|
if (cryptkey)
|
|
memxor(ctx2.cryptedData, cryptkey, 16);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int sceSdSetMember(u32 ctxAddr, u32 dataAddr, int alignedLen) {
|
|
auto ctx = PSPPointer<pspChnnlsvContext2>::Create(ctxAddr);
|
|
u8 *data = Memory::GetPointerWriteOrException(dataAddr);
|
|
if (!ctx.IsValid() || !data)
|
|
return hleLogError(Log::sceMisc, 0, "Invalid pointer");
|
|
|
|
return hleLogDebug(Log::sceMisc, sceSdCipherUpdate(*ctx, data, alignedLen));
|
|
}
|
|
|
|
int sceSdCipherUpdate(pspChnnlsvContext2& ctx, u8* data, int alignedLen)
|
|
{
|
|
std::lock_guard<std::mutex> guard(g_lock);
|
|
if (alignedLen == 0)
|
|
{
|
|
return 0;
|
|
}
|
|
if ((alignedLen & 0xF) != 0)
|
|
{
|
|
return -1025;
|
|
}
|
|
int i = 0;
|
|
u8 kirkData[20+2048];
|
|
if ((u32)alignedLen >= (u32)2048)
|
|
{
|
|
for(i = 0; alignedLen >= 2048; i += 2048)
|
|
{
|
|
int ctx_unkn = ctx.unkn;
|
|
int res = sub_0000(&g_kirk, kirkData, data + i, 2048, ctx.cryptedData, ctx_unkn, ctx.mode);
|
|
ctx.unkn = ctx_unkn;
|
|
alignedLen -= 2048;
|
|
if (res)
|
|
return res;
|
|
}
|
|
}
|
|
if (alignedLen == 0)
|
|
{
|
|
return 0;
|
|
}
|
|
int ctx_unkn = ctx.unkn;
|
|
int res = sub_0000(&g_kirk, kirkData, data + i, alignedLen, ctx.cryptedData, ctx_unkn, ctx.mode);
|
|
ctx.unkn = ctx_unkn;
|
|
return res;
|
|
}
|
|
|
|
static int sceSdCleanList(u32 ctxAddr) {
|
|
auto ctx = PSPPointer<pspChnnlsvContext2>::Create(ctxAddr);
|
|
if (!ctx.IsValid())
|
|
return hleLogError(Log::sceMisc, 0, "Invalid pointer");
|
|
return hleLogDebug(Log::sceMisc, sceSdCipherFinal(*ctx));
|
|
}
|
|
|
|
int sceSdCipherFinal(pspChnnlsvContext2& ctx)
|
|
{
|
|
memset(ctx.cryptedData, 0, 16);
|
|
ctx.unkn = 0;
|
|
ctx.mode = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
const HLEFunction sceChnnlsv[] =
|
|
{
|
|
{0XE7833020, &WrapI_UI<sceSdSetIndex>, "sceSdSetIndex", 'i', "xi" },
|
|
{0XF21A1FCA, &WrapI_UUI<sceSdRemoveValue>, "sceSdRemoveValue", 'i', "xxi" },
|
|
{0XC4C494F8, &WrapI_UUU<sceSdGetLastIndex>, "sceSdGetLastIndex", 'i', "xxx" },
|
|
{0XABFDFC8B, &WrapI_UIIUU<sceSdCreateList>, "sceSdCreateList", 'i', "xiixx"},
|
|
{0X850A7FA1, &WrapI_UUI<sceSdSetMember>, "sceSdSetMember", 'i', "xxi" },
|
|
{0X21BE78B4, &WrapI_U<sceSdCleanList>, "sceSdCleanList", 'i', "x" },
|
|
};
|
|
|
|
void Register_sceChnnlsv()
|
|
{
|
|
RegisterHLEModule("sceChnnlsv", ARRAY_SIZE(sceChnnlsv), sceChnnlsv);
|
|
kirk_init(&g_kirk);
|
|
}
|
|
|
|
// The below functions don't really belong to sceKernelSemaphore. They are the core crypto functionality,
|
|
// exposed through the confusingly named "sceUtilsBufferCopyWithRange" name, which Sony placed in the
|
|
// not-at-all-suspicious "semaphore" library, which has nothing to do with semaphores.
|
|
|
|
static u32 sceUtilsBufferCopyWithRange(u32 outAddr, int outSize, u32 inAddr, int inSize, int cmd) {
|
|
u8 *outAddress = Memory::IsValidRange(outAddr, outSize) ? Memory::GetPointerWriteUnchecked(outAddr) : nullptr;
|
|
u8 *inAddress = Memory::IsValidRange(inAddr, inSize) ? Memory::GetPointerWriteUnchecked(inAddr) : nullptr;
|
|
std::lock_guard<std::mutex> guard(g_lock);
|
|
int temp = kirk_sceUtilsBufferCopyWithRange(&g_kirk, outAddress, outSize, inAddress, inSize, cmd);
|
|
if (temp != 0) {
|
|
ERROR_LOG(Log::sceKernel, "hleUtilsBufferCopyWithRange: Failed with %d", temp);
|
|
}
|
|
return hleNoLog(0);
|
|
}
|
|
|
|
// Note sure what difference there is between this and sceUtilsBufferCopyWithRange.
|
|
static int sceUtilsBufferCopyByPollingWithRange(u32 outAddr, int outSize, u32 inAddr, int inSize, int cmd) {
|
|
u8 *outAddress = Memory::IsValidRange(outAddr, outSize) ? Memory::GetPointerWriteUnchecked(outAddr) : nullptr;
|
|
u8 *inAddress = Memory::IsValidRange(inAddr, inSize) ? Memory::GetPointerWriteUnchecked(inAddr) : nullptr;
|
|
std::lock_guard<std::mutex> guard(g_lock);
|
|
return hleNoLog(kirk_sceUtilsBufferCopyWithRange(&g_kirk, outAddress, outSize, inAddress, inSize, cmd));
|
|
}
|
|
|
|
const HLEFunction semaphore[] = {
|
|
{0x4C537C72, &WrapU_UIUII<sceUtilsBufferCopyWithRange>, "sceUtilsBufferCopyWithRange", 'x', "xixii" },
|
|
{0x77E97079, &WrapI_UIUII<sceUtilsBufferCopyByPollingWithRange>, "sceUtilsBufferCopyByPollingWithRange", 'i', "xixii" },
|
|
};
|
|
|
|
void Register_semaphore() {
|
|
RegisterHLEModule("semaphore", ARRAY_SIZE(semaphore), semaphore);
|
|
}
|