Fix Driver 76 not creating adhoc sockets

Driver 76 uses sceKernelGetModuleIdList to get a module list
after calling sceUtilityLoadNetModule, then go module by module
with sceKernelQueryModuleInfo to check if at least pspnet_adhoc.prx
was loaded.

Load modules during sceUtilityLoadNetModule, expand success lying
modules to have real names, add adhoc modules to the success lying
list, list lied modules during sceKernelGetModuleIdList.
This commit is contained in:
Katharine Chui committed 2026-02-15 22:24:27 +01:00
1 parent 07e5fc1ce0
commit f4be7a990b
3 files changed
+60 -14

No files matched your search

+35 -14
View File
@@ -89,19 +89,39 @@ enum : u32 {
NID_MODULE_SDK_VERSION = 0x11B97506,
};
// This is a workaround for misbehaving homebrew (like TBL's Suicide Barbie (Final)).
static const char * const lieAboutSuccessModules[] = {
"flash0:/kd/audiocodec.prx",
"flash0:/kd/audiocodec_260.prx",
"flash0:/kd/libatrac3plus.prx",
"disc0:/PSP_GAME/SYSDIR/UPDATE/EBOOT.BIN",
"flash0:/kd/ifhandle.prx",
"flash0:/kd/pspnet.prx",
"flash0:/kd/pspnet_inet.prx",
"flash0:/kd/pspnet_apctl.prx",
"flash0:/kd/pspnet_resolver.prx",
struct LieModuleEntry {
const char *path;
const char *name;
};
// This is a workaround for misbehaving homebrew (like TBL's Suicide Barbie (Final)) and games (like Drive 76).
// In the case of Driver 76, it uses sceKernelGetModuleIdList and sceKernelQueryModuleInfo to check if adhoc modules are loaded
static const struct LieModuleEntry lieAboutSuccessModules[] = {
{ "flash0:/kd/audiocodec.prx", "sceAudiocodec_Driver" },
{ "flash0:/kd/audiocodec_260.prx", "sceAudiocodec_Driver" },
{ "flash0:/kd/libatrac3plus.prx", "sceATRAC3plus_Library" },
{ "disc0:/PSP_GAME/SYSDIR/UPDATE/EBOOT.BIN", "" },
{ "flash0:/kd/ifhandle.prx", "sceNet_Service" },
{ "flash0:/kd/pspnet.prx", "sceNet_Library" },
{ "flash0:/kd/pspnet_inet.prx", "sceNetInet_Library" },
{ "flash0:/kd/pspnet_apctl.prx", "sceNetApctl_Library" },
{ "flash0:/kd/pspnet_resolver.prx", "sceNetResolver_Library" },
{ "flash0:/kd/pspnet_adhoc.prx", "sceNetAdhoc_Library" },
{ "flash0:/kd/pspnet_adhocctl.prx", "sceNetAdhocctl_Library" },
{ "flash0:/kd/pspnet_adhoc_matching.prx", "sceNetAdhocMatching_Library" },
{ "flash0:/kd/pspnet_adhoc_download.prx", "sceNetAdhocDownload_Library" },
{ "flash0:/kd/pspnet_adhoc_discover.prx", "sceNetAdhocDiscover_Library" },
};
static const bool liedAboutThisModule(PSPModule *mod) {
for (int i = 0 ; i < ARRAY_SIZE(lieAboutSuccessModules) ; i++) {
if (strcmp(lieAboutSuccessModules[i].name, mod->GetName()) == 0){
return true;
}
}
return false;
}
const char *NativeModuleStatusToString(NativeModuleStatus status) {
switch (status) {
case MODULE_STATUS_STARTING: return "STARTING";
@@ -1908,11 +1928,12 @@ u32 sceKernelLoadModule(const char *name, u32 flags, u32 optionAddr) {
}
for (size_t i = 0; i < ARRAY_SIZE(lieAboutSuccessModules); i++) {
if (!strcmp(name, lieAboutSuccessModules[i])) {
if (!strcmp(name, lieAboutSuccessModules[i].path)) {
PSPModule *module = new PSPModule();
kernelObjects.Create(module);
loadedModules.insert(module->GetUID());
memset(&module->nm, 0, sizeof(module->nm));
strcpy(module->nm.name, lieAboutSuccessModules[i].name);
module->isFake = true;
module->nm.entry_addr = -1;
module->nm.gp_value = -1;
@@ -2067,7 +2088,7 @@ int __KernelStartModule(SceUID moduleId, u32 argsize, u32 argAddr, u32 returnVal
return moduleId;
}
static u32 sceKernelStartModule(u32 moduleId, u32 argsize, u32 argAddr, u32 returnValueAddr, u32 optionAddr) {
u32 sceKernelStartModule(u32 moduleId, u32 argsize, u32 argAddr, u32 returnValueAddr, u32 optionAddr) {
u32 error;
PSPModule *module = kernelObjects.Get<PSPModule>(moduleId, error);
if (!module) {
@@ -2578,7 +2599,7 @@ static u32 sceKernelGetModuleIdList(u32 resultBuffer, u32 resultBufferSize, u32
u32 error;
for (SceUID moduleId : loadedModules) {
PSPModule *module = kernelObjects.Get<PSPModule>(moduleId, error);
if (!module->isFake) {
if (!module->isFake || liedAboutThisModule(module)) {
if (resultBufferOffset < resultBufferSize) {
Memory::Write_U32(module->GetUID(), resultBuffer + resultBufferOffset);
resultBufferOffset += 4;
+1
View File
@@ -240,3 +240,4 @@ void Register_ModuleMgrForKernel();
// Expose for use by KUBridge.
u32 sceKernelLoadModule(const char *name, u32 flags, u32 optionAddr);
u32 sceKernelStartModule(u32 moduleId, u32 argsize, u32 argAddr, u32 returnValueAddr, u32 optionAddr);
+24
View File
@@ -41,6 +41,7 @@
#include "Core/HLE/sceKernelInterrupt.h"
#include "Core/HLE/sceKernelMemory.h"
#include "Core/HLE/sceKernelThread.h"
#include "Core/HLE/sceKernelModule.h"
#include "Core/HLE/scePower.h"
#include "Core/HLE/sceAtrac.h"
#include "Core/HLE/sceUtility.h"
@@ -1303,6 +1304,29 @@ static u32 sceUtilityGetSystemParamInt(u32 id, u32 destaddr) {
}
static u32 sceUtilityLoadNetModule(u32 module) {
// Driver 76 actually checks using sceKernelGetModuleIdList, so we load the HLE module here for checking
std::vector<const char *> mod_list;
if (module == 1) {
// PSP_NET_MODULE_COMMON on pspsdk
mod_list.push_back("flash0:/kd/ifhandle.prx");
mod_list.push_back("flash0:/kd/pspnet.prx");
} else if (module == 2) {
// PSP_NET_MODULE_ADHOC on pspsdk
mod_list.push_back("flash0:/kd/pspnet_adhoc.prx");
mod_list.push_back("flash0:/kd/pspnet_adhocctl.prx");
mod_list.push_back("flash0:/kd/pspnet_adhoc_matching.prx");
mod_list.push_back("flash0:/kd/pspnet_adhoc_download.prx");
mod_list.push_back("flash0:/kd/pspnet_adhoc_discover.prx");
}
for (const char *mod_path : mod_list) {
u32 modid = hleCall(ModuleMgrForUser, u32, sceKernelLoadModule, mod_path, 0, 0);
if (modid >= 0) {
hleCall(ModuleMgrForUser, u32, sceKernelStartModule, modid, 0, 0, 0, 0);
}
}
return hleLogInfo(Log::sceUtility, 0, "FAKE");
}