Merge branch 'cemu-project:main' into main

This commit is contained in:
rcaridade145 2026-06-06 11:07:13 +01:00 committed by GitHub
commit 11903d09c2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
31 changed files with 448 additions and 297 deletions

2
dependencies/vcpkg vendored

@ -1 +1 @@
Subproject commit c3867e714dd3a51c272826eea77267876517ed99
Subproject commit 9849e070ad4c3383890fb1c80ae0c54325e2484a

View File

@ -165,7 +165,7 @@ void LoadMainExecutable()
else
{
// RPX
RPLLoader_AddDependency(_pathToExecutable.c_str());
RPLLoader_AddDependency(_pathToExecutable.c_str(), true);
applicationRPX = RPLLoader_LoadFromMemory(rpxData, rpxSize, (char*)_pathToExecutable.c_str());
if (!applicationRPX)
{

View File

@ -420,7 +420,7 @@ void PatchEntryInstruction::undoPatch()
uint8* patchAddr = (uint8*)memory_base + addr;
memcpy(patchAddr, m_dataBackup, m_length);
PPCRecompiler_invalidateRange(addr, addr + m_length);
rplSymbolStorage_removeRange(addr, m_length);
rplSymbolStorage_removeRange(addr, m_length, RPL_STORED_SYMBOL_PATCH);
DebugSymbolStorage::ClearRange(addr, m_length);
}
@ -434,7 +434,7 @@ bool registerU32Variable(PatchContext_t& ctx, std::string& name, uint32 value, P
}
ctx.map_values[name] = value;
// keep track of address symbols for the debugger
rplSymbolStorage_store(ctx.graphicPack->GetName().data(), name.data(), value);
rplSymbolStorage_store(ctx.graphicPack->GetName().data(), name.data(), value, RPL_STORED_SYMBOL_PATCH);
return true;
}
@ -688,7 +688,7 @@ void GraphicPack2::UndoPatchGroups(std::vector<PatchGroup*>& groups, const RPLMo
void GraphicPack2::NotifyModuleLoaded(const RPLModule* rpl)
{
cemuLog_log(LogType::Force, "Loaded module \'{}\' with checksum 0x{:08x}", rpl->moduleName2, rpl->patchCRC);
cemuLog_log(LogType::Force, "Loaded module \'{}\' with checksum 0x{:08x}", rpl->moduleName, rpl->patchCRC);
std::lock_guard<std::recursive_mutex> lock(mtx_patches);
list_modules.emplace_back(rpl);

View File

@ -756,7 +756,7 @@ void debugger_addParserSymbols(class ExpressionParser& ep)
if (module)
{
moduleTmp[i] = (double)module->regionMappingBase_text.GetMPTR();
ep.AddConstant(module->moduleName2, moduleTmp[i]);
ep.AddConstant(module->moduleName, moduleTmp[i]);
}
}
PPCInterpreter_t* hCPU = debugger_lockDebugSession();

View File

@ -556,7 +556,7 @@ void GDBServer::HandleQuery(std::unique_ptr<CommandContext>& context) const
const auto module_list = RPLLoader_GetModuleList();
for (sint32 i = 0; i < module_count; i++)
{
library_list += fmt::format(R"(<library name="{}"><segment address="{:#x}"/></library>)", CommandContext::EscapeXMLString(module_list[i]->moduleName2), module_list[i]->regionMappingBase_text.GetMPTR());
library_list += fmt::format(R"(<library name="{}"><segment address="{:#x}"/></library>)", CommandContext::EscapeXMLString(module_list[i]->moduleName), module_list[i]->regionMappingBase_text.GetMPTR());
}
library_list += "</library-list>";

View File

@ -21,6 +21,8 @@ void LatteTextureReadback_StartTransfer(LatteTextureView* textureView)
HRTick currentTick = HighResolutionTimer().now().getTick();
// create info entry and store in ordered linked list
LatteTextureReadbackInfo* readbackInfo = g_renderer->texture_createReadback(textureView);
if (!readbackInfo)
return;
sTextureActiveReadbackQueue.push(readbackInfo);
readbackInfo->StartTransfer();
readbackInfo->transferStartTime = currentTick;

View File

@ -243,7 +243,7 @@ namespace LatteDecompiler
cemu_assert_debug(decompilerContext->output->resourceMappingVK.attributeMapping[i] >= 0);
cemu_assert_debug(decompilerContext->output->resourceMappingGL.attributeMapping[i] == decompilerContext->output->resourceMappingVK.attributeMapping[i]);
shaderSrc->addFmt("ATTR_LAYOUT({}, {}) in uvec4 attrDataSem{};" _CRLF, (sint32)decompilerContext->output->resourceMappingVK.setIndex, (sint32)decompilerContext->output->resourceMappingVK.attributeMapping[i], i);
shaderSrc->addFmt("ATTR_LAYOUT({}) in uvec4 attrDataSem{};" _CRLF, (sint32)decompilerContext->output->resourceMappingVK.attributeMapping[i], i);
}
}
}
@ -255,7 +255,7 @@ namespace LatteDecompiler
// OpenGL/Vulkan ifdefs
src->add("#ifdef VULKAN" _CRLF);
// Vulkan defines
src->add("#define ATTR_LAYOUT(__vkSet, __location) layout(set = __vkSet, location = __location)" _CRLF);
src->add("#define ATTR_LAYOUT(__location) layout(location = __location)" _CRLF);
src->add("#define UNIFORM_BUFFER_LAYOUT(__glLocation, __vkSet, __vkLocation) layout(set = __vkSet, binding = __vkLocation, std140)" _CRLF);
src->add("#define TEXTURE_LAYOUT(__glLocation, __vkSet, __vkLocation) layout(set = __vkSet, binding = __vkLocation)" _CRLF);
if (decompilerContext->shaderType == LatteConst::ShaderType::Vertex || decompilerContext->shaderType == LatteConst::ShaderType::Geometry)
@ -293,7 +293,7 @@ namespace LatteDecompiler
}
src->add("#else" _CRLF);
// OpenGL defines
src->add("#define ATTR_LAYOUT(__vkSet, __location) layout(location = __location)" _CRLF);
src->add("#define ATTR_LAYOUT(__location) layout(location = __location)" _CRLF);
src->add("#define UNIFORM_BUFFER_LAYOUT(__glLocation, __vkSet, __vkLocation) layout(binding = __glLocation, std140) " _CRLF);
src->add("#define TEXTURE_LAYOUT(__glLocation, __vkSet, __vkLocation) layout(binding = __glLocation)" _CRLF);
if (decompilerContext->shaderType == LatteConst::ShaderType::Vertex || decompilerContext->shaderType == LatteConst::ShaderType::Geometry)

View File

@ -192,11 +192,6 @@ VKFUNC_DEVICE(vkCmdEndRenderingKHR);
// khr_present_wait
VKFUNC_DEVICE(vkWaitForPresentKHR);
// transform feedback extension
VKFUNC_DEVICE(vkCmdBindTransformFeedbackBuffersEXT);
VKFUNC_DEVICE(vkCmdBeginTransformFeedbackEXT);
VKFUNC_DEVICE(vkCmdEndTransformFeedbackEXT);
// query
VKFUNC_DEVICE(vkCreateQueryPool);
VKFUNC_DEVICE(vkDestroyQueryPool);

View File

@ -646,10 +646,7 @@ VulkanRenderer::VulkanRenderer()
deviceFeatures.robustBufferAccess = VK_TRUE;
}
if (m_featureControl.mode.useTFEmulationViaSSBO)
{
deviceFeatures.vertexPipelineStoresAndAtomics = true;
}
deviceFeatures.vertexPipelineStoresAndAtomics = true;
void* deviceExtensionFeatures = nullptr;
@ -787,7 +784,8 @@ VulkanRenderer::VulkanRenderer()
m_textureReadbackBufferPtr = (uint8*)bufferPtr;
// transform feedback ringbuffer
memoryManager->CreateBuffer(LatteStreamout_GetRingBufferSize(), VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT | (m_featureControl.mode.useTFEmulationViaSSBO ? VK_BUFFER_USAGE_STORAGE_BUFFER_BIT : 0), 0, m_xfbRingBuffer, m_xfbRingBufferMemory);
VkBufferUsageFlags xfbRingBufferUsage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
memoryManager->CreateBuffer(LatteStreamout_GetRingBufferSize(), xfbRingBufferUsage, 0, m_xfbRingBuffer, m_xfbRingBufferMemory);
// occlusion query result buffer
if (!memoryManager->CreateBuffer(OCCLUSION_QUERY_POOL_SIZE * sizeof(uint64), VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT, m_occlusionQueries.bufferQueryResults, m_occlusionQueries.memoryQueryResults))
@ -1367,7 +1365,6 @@ bool VulkanRenderer::CheckDeviceExtensionSupport(const VkPhysicalDevice device,
}
info.deviceExtensions.tooling_info = isExtensionAvailable(VK_EXT_TOOLING_INFO_EXTENSION_NAME);
info.deviceExtensions.transform_feedback = isExtensionAvailable(VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME);
info.deviceExtensions.depth_range_unrestricted = isExtensionAvailable(VK_EXT_DEPTH_RANGE_UNRESTRICTED_EXTENSION_NAME);
info.deviceExtensions.nv_fill_rectangle = isExtensionAvailable(VK_NV_FILL_RECTANGLE_EXTENSION_NAME);
info.deviceExtensions.pipeline_feedback = isExtensionAvailable(VK_EXT_PIPELINE_CREATION_FEEDBACK_EXTENSION_NAME);
@ -3673,14 +3670,14 @@ void VulkanRenderer::texture_copyImageSubData(LatteTexture* src, sint32 srcMip,
LatteTextureReadbackInfo* VulkanRenderer::texture_createReadback(LatteTextureView* textureView)
{
auto* result = new LatteTextureReadbackInfoVk(m_logicalDevice, textureView);
LatteTextureVk* vkTex = (LatteTextureVk*)textureView->baseTexture;
VkMemoryRequirements memRequirements;
vkGetImageMemoryRequirements(m_logicalDevice, vkTex->GetImageObj()->m_image, &memRequirements);
const uint32 linearImageSize = result->GetImageSize();
const uint32 uploadSize = (linearImageSize == 0) ? memRequirements.size : linearImageSize;
if (linearImageSize == 0)
{
delete result;
return nullptr;
}
const uint32 uploadSize = linearImageSize;
const uint32 uploadAlignment = 256; // todo - use Vk optimalBufferCopyOffsetAlignment
m_textureReadbackBufferWriteIndex = (m_textureReadbackBufferWriteIndex + uploadAlignment - 1) & ~(uploadAlignment - 1);
@ -3714,46 +3711,14 @@ void VulkanRenderer::streamout_setupXfbBuffer(uint32 bufferIndex, sint32 ringBuf
void VulkanRenderer::streamout_begin()
{
if (m_featureControl.mode.useTFEmulationViaSSBO)
return;
if (m_state.hasActiveXfb == false)
m_state.hasActiveXfb = true;
}
void VulkanRenderer::streamout_applyTransformFeedbackState()
{
if (m_featureControl.mode.useTFEmulationViaSSBO)
return;
cemu_assert_debug(m_state.hasActiveXfb == false);
if (m_state.hasActiveXfb)
{
// set buffers
for (sint32 i = 0; i < LATTE_NUM_STREAMOUT_BUFFER; i++)
{
if (m_streamoutState.buffer[i].enabled)
{
VkBuffer tfBuffer = m_xfbRingBuffer;
VkDeviceSize tfBufferOffset = m_streamoutState.buffer[i].ringBufferOffset;
VkDeviceSize tfBufferSize = VK_WHOLE_SIZE;
vkCmdBindTransformFeedbackBuffersEXT(m_state.currentCommandBuffer, i, 1, &tfBuffer, &tfBufferOffset, &tfBufferSize);
}
}
// begin transform feedback
vkCmdBeginTransformFeedbackEXT(m_state.currentCommandBuffer, 0, 0, nullptr, nullptr);
}
}
void VulkanRenderer::streamout_rendererFinishDrawcall()
{
if (m_state.hasActiveXfb)
{
vkCmdEndTransformFeedbackEXT(m_state.currentCommandBuffer, 0, 0, nullptr, nullptr);
m_streamoutState.buffer[0].enabled = false;
m_streamoutState.buffer[1].enabled = false;
m_streamoutState.buffer[2].enabled = false;
m_streamoutState.buffer[3].enabled = false;
m_state.hasActiveXfb = false;
}
m_streamoutState.buffer[0].enabled = false;
m_streamoutState.buffer[1].enabled = false;
m_streamoutState.buffer[2].enabled = false;
m_streamoutState.buffer[3].enabled = false;
}

View File

@ -380,9 +380,6 @@ private:
uint32 offset;
}currentVertexBinding[LATTE_MAX_VERTEX_BUFFERS]{};
// transform feedback
bool hasActiveXfb{};
// index buffer
Renderer::INDEX_TYPE activeIndexType{};
uint32 activeIndexBufferIndex{};
@ -442,7 +439,6 @@ private:
{
// if using new optional extensions add to CheckDeviceExtensionSupport and CreateDeviceCreateInfo
bool tooling_info = false; // VK_EXT_tooling_info
bool transform_feedback = false;
bool depth_range_unrestricted = false;
bool nv_fill_rectangle = false; // NV_fill_rectangle
bool pipeline_feedback = false;
@ -470,11 +466,6 @@ private:
bool debug_utils = false; // VK_EXT_DEBUG_UTILS
}instanceExtensions;
struct
{
bool useTFEmulationViaSSBO = true; // emulate transform feedback via shader writes to a storage buffer
}mode;
struct
{
uint32 minUniformBufferOffsetAlignment = 256;
@ -521,7 +512,10 @@ private:
void DeleteFontTextures() override;
bool BeginFrame(bool mainWindow) override;
bool UseTFViaSSBO() const override { return m_featureControl.mode.useTFEmulationViaSSBO; }
bool UseTFViaSSBO() const override
{
return true;
}
// drawcall emulation
PipelineInfo* draw_createGraphicsPipeline(uint32 indexCount);
@ -567,7 +561,6 @@ private:
// streamout
void streamout_setupXfbBuffer(uint32 bufferIndex, sint32 ringBufferOffset, uint32 rangeAddr, uint32 rangeSize) override;
void streamout_begin() override;
void streamout_applyTransformFeedbackState();
void bufferCache_copyStreamoutToMainBuffer(uint32 srcOffset, uint32 dstOffset, uint32 size) override;
void streamout_rendererFinishDrawcall() override;

View File

@ -5,7 +5,6 @@
#include "Cafe/OS/RPL/rpl.h"
#include "Cafe/OS/RPL/rpl_structs.h"
#include "Cafe/OS/RPL/rpl_symbol_storage.h"
#include "util/VirtualHeap/VirtualHeap.h"
#include "Cafe/HW/Espresso/Recompiler/PPCRecompiler.h"
#include "Cafe/HW/Espresso/Debugger/Debugger.h"
#include "Cafe/GraphicPack/GraphicPack2.h"
@ -272,10 +271,7 @@ bool RPLLoader_ProcessHeaders(std::string_view moduleName, uint8* rplData, uint3
// init section address table
rplLoaderContext->sectionAddressTable2.resize(sectionCount);
// init modulename
rplLoaderContext->moduleName2.assign(moduleName);
// convert modulename to lower-case
for(auto& c : rplLoaderContext->moduleName2)
c = _ansiToLower(c);
rplLoaderContext->moduleName.assign(moduleName);
// load CRC section
uint32 crcTableExpectedSize = sectionCount * sizeof(uint32be);
@ -525,7 +521,7 @@ bool RPLLoader_LoadSections(sint32 aProcId, RPLModule* rplLoaderContext)
PPCRecompiler_allocateRange(rplLoaderContext->regionMappingBase_text.GetMPTR(), regionTextSize + 0x1000);
// workaround for DKC Tropical Freeze
if (boost::iequals(rplLoaderContext->moduleName2, "rs10_production"))
if (rplLoaderContext->moduleName == "rs10_production")
{
// allocate additional 12MB of unused data to get below a size of 0x3E200000 for the main ExpHeap
// otherwise the game will assume it's running on a Devkit unit with 2GB of RAM and subtract 1GB from available space
@ -536,6 +532,10 @@ bool RPLLoader_LoadSections(sint32 aProcId, RPLModule* rplLoaderContext)
rplLoaderContext->regionSize_loaderInfo = regionLoaderinfoSize;
rplLoaderContext->regionSize_text = regionTextSize;
// set original base addresses
rplLoaderContext->regionOrigAddr_text = regionMappingTable.region[RPL_MAPPING_REGION_TEXT].baseAddress;
rplLoaderContext->regionOrigAddr_data = regionMappingTable.region[RPL_MAPPING_REGION_DATA].baseAddress;
// load data sections
for (sint32 i = 0; i < (sint32)rplLoaderContext->rplHeader.sectionTableEntryCount; i++)
{
@ -844,7 +844,7 @@ MPTR _findHLEExport(RPLModule* rplLoaderContext, RPLSharedImportTracking* shared
MPTR weakExportAddr = osLib_getPointer(libname, symbolName);
if (weakExportAddr != 0xFFFFFFFF)
return weakExportAddr;
cemuLog_logDebug(LogType::Force, "Unsupported data export ({}): {}.{}", rplLoaderContext->moduleName2, libname, symbolName);
cemuLog_logDebug(LogType::Force, "Unsupported data export ({}): {}.{}", rplLoaderContext->moduleName, libname, symbolName);
return MPTR_NULL;
}
else
@ -1281,7 +1281,7 @@ bool RPLLoader_ApplyRelocs(RPLModule* rplLoaderContext, sint32 relaSectionIndex,
uint32 crc = rplLoaderContext->GetSectionCRC(relaSectionIndex);
if (calcCRC != crc)
{
cemuLog_log(LogType::Force, "RPLLoader {} - Relocation section {} has CRC mismatch - Calc: {:08x} Actual: {:08x}", rplLoaderContext->moduleName2.c_str(), relaSectionIndex, calcCRC, crc);
cemuLog_log(LogType::Force, "RPLLoader {} - Relocation section {} has CRC mismatch - Calc: {:08x} Actual: {:08x}", rplLoaderContext->moduleName.c_str(), relaSectionIndex, calcCRC, crc);
}
// process relocations
sint32 relocCount = relocSize / sizeof(rplRelocNew_t);
@ -1379,7 +1379,7 @@ bool RPLLoader_HandleRelocs(RPLModule* rplLoaderContext, std::span<RPLSharedImpo
return true;
}
void _RPLLoader_ExtractModuleNameFromPath(char* output, std::string_view input)
std::string _RPLLoader_ExtractModuleNameFromPath(std::string_view input)
{
// scan to last '/'
cemu_assert(!input.empty());
@ -1404,10 +1404,11 @@ void _RPLLoader_ExtractModuleNameFromPath(char* output, std::string_view input)
size_t nameLen = endIndex - startIndex;
cemu_assert(nameLen != 0);
nameLen = std::min<size_t>(nameLen, RPL_MODULE_NAME_LENGTH-1);
memcpy(output, input.data() + startIndex, nameLen);
output[nameLen] = '\0';
std::string output;
output.append(input.data() + startIndex, nameLen);
// convert to lower case
std::for_each(output, output + nameLen, [](char& c) {c = _ansiToLower(c);});
std::for_each(output.begin(), output.end(), [](char& c) {c = _ansiToLower(c);});
return output;
}
void RPLLoader_InitState()
@ -1581,8 +1582,7 @@ void RPLLoader_InitModuleAllocator(RPLModule* rpl)
// map rpl into memory, but do not resolve relocs and imports yet
RPLModule* RPLLoader_LoadFromMemory(uint8* rplData, sint32 size, std::string_view name)
{
char moduleName[RPL_MODULE_NAME_LENGTH];
_RPLLoader_ExtractModuleNameFromPath(moduleName, name);
std::string moduleName = _RPLLoader_ExtractModuleNameFromPath(name);
RPLModule* rpl = nullptr;
if (RPLLoader_ProcessHeaders({ moduleName }, rplData, size, &rpl) == false)
{
@ -1596,9 +1596,7 @@ RPLModule* RPLLoader_LoadFromMemory(uint8* rplData, sint32 size, std::string_vie
delete rpl;
return nullptr;
}
cemuLog_logDebug(LogType::Force, "Load {} Code-Offset: -0x{:x}", name, rpl->regionMappingBase_text.GetMPTR() - 0x02000000);
// sdata (r2/r13)
uint32 sdataBaseAddress = rpl->fileInfo.sdataBase1; // base + 0x8000
uint32 sdataBaseAddress2 = rpl->fileInfo.sdataBase2; // base + 0x8000
@ -1693,16 +1691,14 @@ void RPLLoader_LinkSingleModule(RPLModule* rplLoaderContext, bool resolveOnlyExp
{
if( rplLoaderContext->sectionTablePtr[i].type != (uint32be)SHT_RPL_IMPORTS )
continue;
cemu_assert(rplLoaderContext->sectionTablePtr[i].sectionSize >= 9);
char* libName = (char*)((uint8*)rplLoaderContext->sectionAddressTable2[i].ptr + 8);
// make module name
char _importModuleName[RPL_MODULE_NAME_LENGTH];
_RPLLoader_ExtractModuleNameFromPath(_importModuleName, libName);
// find in loaded module list
std::string importModuleName{_importModuleName};
std::string importModuleName = _RPLLoader_ExtractModuleNameFromPath(libName);
bool foundModule = false;
for (sint32 f = 0; f < rplModuleCount; f++)
{
if (boost::iequals(rplModuleList[f]->moduleName2, importModuleName))
if (rplModuleList[f]->moduleName == importModuleName)
{
sharedImportTracking[i].rplLoaderContext = rplModuleList[f];
memset(sharedImportTracking[i].modulename, 0, sizeof(sharedImportTracking[i].modulename));
@ -1766,7 +1762,7 @@ void RPLLoader_LoadSectionDebugSymbols(RPLModule* rplLoaderContext, rplSectionEn
char* symbolName = (char*)strtabData + nameOffset;
if (sym->info == 0x12)
{
rplSymbolStorage_store(rplLoaderContext->moduleName2.c_str(), symbolName, sym->symbolAddress);
rplSymbolStorage_store(rplLoaderContext->moduleName.c_str(), symbolName, sym->symbolAddress);
}
}
}
@ -1860,7 +1856,7 @@ void RPLLoader_FixModuleTLSIndex(RPLModule* rplLoaderContext)
sint16 tlsModuleIndex = -1;
for (auto& dep : rplDependencyList)
{
if (boost::iequals(rplLoaderContext->moduleName2, dep->modulename))
if (rplLoaderContext->moduleName == dep->moduleName)
{
tlsModuleIndex = dep->tlsModuleIndex;
break;
@ -1928,23 +1924,21 @@ COSModule* RPLLoader_GetHLECafeOSModule(std::string_view moduleName)
std::span<COSModule*> cosModules = GetCOSModules();
for (auto& module : cosModules)
{
if (boost::iequals(module->GetName(), moduleName))
if (module->GetName() == moduleName)
return module;
}
return nullptr;
}
// increment reference counter for module
void RPLLoader_AddDependency(std::string_view name)
void RPLLoader_AddDependency(std::string_view name, bool isMainExecutable)
{
cemu_assert(!name.empty());
// get module name from path
char moduleName[RPL_MODULE_NAME_LENGTH];
_RPLLoader_ExtractModuleNameFromPath(moduleName, name);
std::string moduleName = _RPLLoader_ExtractModuleNameFromPath(name);
// check if dependency already exists
for (auto& dep : rplDependencyList)
{
if (strcmp(moduleName, dep->modulename) == 0)
if (moduleName == dep->moduleName)
{
dep->referenceCount++;
return;
@ -1952,7 +1946,8 @@ void RPLLoader_AddDependency(std::string_view name)
}
// add new entry
RPLDependency* newDependency = new RPLDependency();
strcpy(newDependency->modulename, moduleName);
newDependency->isMainExecutable = isMainExecutable;
newDependency->moduleName = moduleName;
newDependency->referenceCount = 1;
newDependency->coreinitHandle = rplLoader_currentHandleCounter;
newDependency->tlsModuleIndex = rplLoader_currentTlsModuleIndex;
@ -1961,21 +1956,13 @@ void RPLLoader_AddDependency(std::string_view name)
rplLoader_currentHandleCounter++;
if (rplLoader_currentTlsModuleIndex == 0x7FFF)
cemuLog_log(LogType::Force, "RPLLoader: Exhausted TLS module indices pool");
// convert name to path/filename if it isn't already one
if (name.find_first_of('.') != std::string_view::npos)
{
newDependency->filepath = name;
}
else
{
newDependency->filepath = name;
newDependency->filepath.append(".rpl");
}
if (newDependency->filepath.size() >= RPL_MODULE_PATH_LENGTH)
cemuLog_log(LogType::Force, "RPLLoader_AddDependency(): RPL path too long \"{}\"", newDependency->filepath);
if (moduleName.size() >= RPL_MODULE_PATH_LENGTH)
cemuLog_log(LogType::Force, "RPLLoader_AddDependency(): RPL module name too long \"{}\"", moduleName);
std::string fileName = moduleName;
fileName.append(isMainExecutable ? ".rpx" : ".rpl");
// if no CafeLibs RPL is present then try to load as a HLE module
// we dont check for isCafeOSModule == true here because the user might want to replace application RPLs in some cases
const auto cafeLibsFilePath = ActiveSettings::GetUserDataPath("cafeLibs/{}", newDependency->filepath);
const auto cafeLibsFilePath = ActiveSettings::GetUserDataPath("cafeLibs/{}", fileName);
std::error_code ec;
if (!fs::exists(cafeLibsFilePath, ec))
newDependency->rplHLEModule = RPLLoader_GetHLECafeOSModule(moduleName);
@ -1985,14 +1972,14 @@ void RPLLoader_AddDependency(std::string_view name)
// decrement reference counter for dependency by module path
void RPLLoader_RemoveDependency(std::string_view name)
{
cemu_assert(!name.empty());
// get module name from path
char moduleName[RPL_MODULE_NAME_LENGTH];
_RPLLoader_ExtractModuleNameFromPath(moduleName, name);
cemu_assert_debug(!name.empty());
if (name.empty())
return;
std::string moduleName = _RPLLoader_ExtractModuleNameFromPath(name);
// find dependency and decrement ref count
for (auto& dep : rplDependencyList)
{
if (strcmp(moduleName, dep->modulename) == 0)
if (dep->moduleName == moduleName)
{
dep->referenceCount--;
return;
@ -2002,11 +1989,12 @@ void RPLLoader_RemoveDependency(std::string_view name)
bool RPLLoader_HasDependency(std::string_view name)
{
char moduleName[RPL_MODULE_NAME_LENGTH];
_RPLLoader_ExtractModuleNameFromPath(moduleName, name);
if (name.empty())
return false;
std::string moduleName = _RPLLoader_ExtractModuleNameFromPath(name);
for (const auto& dep : rplDependencyList)
{
if (strcmp(moduleName, dep->modulename) == 0)
if (dep->moduleName == moduleName)
return true;
}
return false;
@ -2041,13 +2029,11 @@ RPLDependency* RPLLoader_GetDependencyByRPLModule(RPLModule* rpl)
uint32 RPLLoader_GetHandleByModuleName(const char* name)
{
// get module name from path
char moduleName[RPL_MODULE_NAME_LENGTH];
_RPLLoader_ExtractModuleNameFromPath(moduleName, name);
std::string moduleName = _RPLLoader_ExtractModuleNameFromPath(name);
// search for existing dependency
for (auto& dep : rplDependencyList)
{
if (strcmp(moduleName, dep->modulename) == 0)
if (dep->moduleName == moduleName)
{
cemu_assert_debug(dep->loadAttempted);
if (!dep->isCafeOSModule && !dep->rplLoaderContext)
@ -2107,7 +2093,7 @@ void RPLLoader_LoadDependency(RPLDependency* dependency)
{
dependency->rplHLEModule->RPLMapped();
// load chained dependencies
// this is necessary for something like GX2.rpl which uses TCL.rpl functions
// this is necessary for something like HLE GX2.rpl which uses TCL.rpl functions
auto depList = dependency->rplHLEModule->GetDependencies();
for (const auto& dep : depList)
RPLLoader_AddDependency(dep);
@ -2116,43 +2102,36 @@ void RPLLoader_LoadDependency(RPLDependency* dependency)
// check if module is already loaded
for (sint32 i = 0; i < rplModuleCount; i++)
{
if(!boost::iequals(rplModuleList[i]->moduleName2, dependency->modulename))
if (rplModuleList[i]->moduleName != dependency->moduleName)
continue;
dependency->rplLoaderContext = rplModuleList[i];
return;
}
//char filePath[RPL_MODULE_PATH_LENGTH];
std::string rplPath;
// check if path is absolute
if (!dependency->filepath.empty() && dependency->filepath.front() == '/')
{
rplPath = dependency->filepath;
RPLLoader_LoadFromVirtualPath(dependency, rplPath);
return;
}
// attempt to load rpl from code directory of current title
rplPath = "/internal/current_title/code/";
rplPath.append(dependency->filepath);
std::string rplFilename = dependency->moduleName;
rplFilename.append(dependency->isMainExecutable ? ".rpx" : ".rpl");
std::string rplPath = "/internal/current_title/code/";
rplPath.append(rplFilename);
// except if it is blacklisted
bool isBlacklisted = false;
if (boost::iequals(dependency->filepath, "erreula.rpl"))
if (dependency->moduleName == "erreula")
{
if (fsc_doesFileExist(rplPath.c_str()))
isBlacklisted = true;
}
if (isBlacklisted)
cemuLog_log(LogType::Force, fmt::format("Game tried to load \"{}\" but it is blacklisted (using Cemu's implementation instead)", rplPath));
cemuLog_log(LogType::Force, "Game tried to load code/{} but it is blacklisted (using Cemu's implementation instead)", rplFilename);
else if (RPLLoader_LoadFromVirtualPath(dependency, rplPath))
return;
// attempt to load rpl from Cemu's /cafeLibs/ directory
if (ActiveSettings::LoadSharedLibrariesEnabled())
{
const auto cafeLibsFilePath = ActiveSettings::GetUserDataPath("cafeLibs/{}", dependency->filepath);
const auto cafeLibsFilePath = ActiveSettings::GetUserDataPath("cafeLibs/{}", rplFilename);
auto fileData = FileStream::LoadIntoMemory(cafeLibsFilePath);
if (fileData)
{
cemuLog_log(LogType::Force, "Loading RPL: /cafeLibs/{}", dependency->filepath);
dependency->rplLoaderContext = RPLLoader_LoadFromMemory(fileData->data(), fileData->size(), dependency->filepath);
cemuLog_log(LogType::Force, "Loading RPL: /cafeLibs/{}", rplFilename);
dependency->rplLoaderContext = RPLLoader_LoadFromMemory(fileData->data(), fileData->size(), rplFilename);
return;
}
}
@ -2213,7 +2192,7 @@ void RPLLoader_LoadCoreinit()
RPLLoader_AddDependency("coreinit");
for (auto& dep : rplDependencyList)
{
if (strcmp(dep->modulename, "coreinit") == 0)
if (dep->moduleName == "coreinit")
{
dep->loadAttempted = true;
RPLLoader_LoadDependency(dep);
@ -2276,7 +2255,7 @@ RPLModule* RPLLoader_FindModuleByName(std::string module)
{
for (sint32 i = 0; i < rplModuleCount; i++)
{
if (rplModuleList[i]->moduleName2 == module) return rplModuleList[i];
if (rplModuleList[i]->moduleName == module) return rplModuleList[i];
}
return nullptr;
}
@ -2298,7 +2277,7 @@ void RPLLoader_CallEntrypoints()
{
if (rplModuleList[i]->entrypointCalled)
continue;
uint32 moduleHandle = RPLLoader_GetHandleByModuleName(rplModuleList[i]->moduleName2.c_str());
uint32 moduleHandle = RPLLoader_GetHandleByModuleName(rplModuleList[i]->moduleName.c_str());
MPTR entryPoint = RPLLoader_GetModuleEntrypoint(rplModuleList[i]);
PPCCoreCallback(entryPoint, moduleHandle, 1); // 1 -> load, 2 -> unload
rplModuleList[i]->entrypointCalled = true;
@ -2311,7 +2290,7 @@ void RPLLoader_CallCoreinitEntrypoint()
// for HLE modules we need to check the dependency list
for (auto& dependency : rplDependencyList)
{
if (strcmp(dependency->modulename, "coreinit") != 0)
if (dependency->moduleName != "coreinit")
continue;
if (!dependency->rplHLEModule)
continue;
@ -2354,13 +2333,13 @@ uint32 RPLLoader_FindModuleOrHLEExport(uint32 moduleHandle, bool isData, const c
// attempt to find HLE export
if (isData)
{
MPTR weakExportAddr = osLib_getPointer(dependency->modulename, exportName);
MPTR weakExportAddr = osLib_getPointer(dependency->moduleName.c_str(), exportName);
cemu_assert_debug(weakExportAddr != 0xFFFFFFFF);
exportResult = weakExportAddr;
}
else
{
exportResult = rpl_mapHLEImport(rplLoaderContext, dependency->modulename, exportName, true);
exportResult = rpl_mapHLEImport(rplLoaderContext, dependency->moduleName.c_str(), exportName, true);
}
}
@ -2482,8 +2461,6 @@ void RPLLoader_UnloadAll()
cemu_assert_debug(dependency->referenceCount >= 0); // sanity check for ref count
if (!dependency->rplHLEModule)
continue;
if (dependency->referenceCount <= 0)
continue;
cemu_assert_debug(dependency->hleEntrypointCalled); // entrypoint should have been called
dependency->rplHLEModule->rpl_entry(dependency->coreinitHandle, coreinit::RplEntryReason::Unloaded);
dependency->rplHLEModule->RPLUnmapped();

View File

@ -28,7 +28,7 @@ void RPLLoader_CallEntrypoints();
void RPLLoader_CallCoreinitEntrypoint();
void RPLLoader_NotifyControlPassedToApplication();
void RPLLoader_AddDependency(std::string_view name);
void RPLLoader_AddDependency(std::string_view name, bool isMainExecutable = false);
void RPLLoader_RemoveDependency(uint32 handle);
bool RPLLoader_HasDependency(std::string_view name);
void RPLLoader_UpdateDependencies();

View File

@ -164,14 +164,16 @@ struct RPLModule
uint32 exportFCount;
rplExportTableEntry_t* exportFDataPtr;
std::string moduleName2;
std::string moduleName;
std::vector<rplSectionAddressEntry_t> sectionAddressTable2;
uint32 tlsStartAddress;
uint32 tlsEndAddress;
uint32 regionSize_text;
uint32 regionOrigAddr_text;
uint32 regionSize_data;
uint32 regionOrigAddr_data;
uint32 regionSize_loaderInfo;
uint32 patchCRC; // Cemuhook style module crc for patches.txt
@ -234,11 +236,11 @@ struct RPLModule
struct RPLDependency
{
char modulename[RPL_MODULE_NAME_LENGTH];
std::string filepath;
bool loadAttempted;
std::string moduleName;
bool isMainExecutable{false};
bool loadAttempted{false};
bool hleEntrypointCalled{false};
bool isCafeOSModule; // name is a known Cafe OS system RPL
bool isCafeOSModule{false}; // name is a known Cafe OS system RPL
RPLModule* rplLoaderContext{}; // context of loaded module, can be nullptr for HLE COS modules
class COSModule* rplHLEModule{}; // set if this is a HLE module
sint32 referenceCount;

View File

@ -63,7 +63,7 @@ char* rplSymbolStorage_storeLibname(const char* libName)
return libEntry->libName;
}
RPLStoredSymbol* rplSymbolStorage_store(const char* libName, const char* symbolName, MPTR address)
RPLStoredSymbol* rplSymbolStorage_store(const char* libName, const char* symbolName, MPTR address, uint32 type)
{
std::unique_lock<std::mutex> lck(rplSymbolStorage.m_symbolStorageMutex);
char* libNameStorage = rplSymbolStorage_storeLibname(libName);
@ -72,7 +72,11 @@ RPLStoredSymbol* rplSymbolStorage_store(const char* libName, const char* symbolN
storedSymbol->address = address;
storedSymbol->libName = libNameStorage;
storedSymbol->symbolName = symbolNameStorage;
storedSymbol->flags = 0;
storedSymbol->flags = type;
storedSymbol->previous = nullptr;
auto it = rplSymbolStorage.map_symbolByAddress.find(address);
if (it != rplSymbolStorage.map_symbolByAddress.end())
storedSymbol->previous = it->second;
rplSymbolStorage.map_symbolByAddress[address] = storedSymbol;
return storedSymbol;
}
@ -80,7 +84,10 @@ RPLStoredSymbol* rplSymbolStorage_store(const char* libName, const char* symbolN
RPLStoredSymbol* rplSymbolStorage_getByAddress(MPTR address)
{
std::unique_lock<std::mutex> lck(rplSymbolStorage.m_symbolStorageMutex);
return rplSymbolStorage.map_symbolByAddress[address];
auto it = rplSymbolStorage.map_symbolByAddress.find(address);
if (it == rplSymbolStorage.map_symbolByAddress.end())
return nullptr;
return it->second;
}
RPLStoredSymbol* rplSymbolStorage_getByClosestAddress(MPTR address)
@ -89,7 +96,8 @@ RPLStoredSymbol* rplSymbolStorage_getByClosestAddress(MPTR address)
std::unique_lock<std::mutex> lck(rplSymbolStorage.m_symbolStorageMutex);
for(uint32 i=0; i<4096; i++)
{
RPLStoredSymbol* symbol = rplSymbolStorage.map_symbolByAddress[address];
auto it = rplSymbolStorage.map_symbolByAddress.find(address);
RPLStoredSymbol* symbol = (it == rplSymbolStorage.map_symbolByAddress.end()) ? nullptr : it->second;
if(symbol)
return symbol;
address -= 4;
@ -100,18 +108,63 @@ RPLStoredSymbol* rplSymbolStorage_getByClosestAddress(MPTR address)
void rplSymbolStorage_remove(RPLStoredSymbol* storedSymbol)
{
std::unique_lock<std::mutex> lck(rplSymbolStorage.m_symbolStorageMutex);
if (rplSymbolStorage.map_symbolByAddress[storedSymbol->address] == storedSymbol)
rplSymbolStorage.map_symbolByAddress[storedSymbol->address] = nullptr;
auto it = rplSymbolStorage.map_symbolByAddress.find(storedSymbol->address);
if (it != rplSymbolStorage.map_symbolByAddress.end())
{
if (it->second == storedSymbol)
{
if (storedSymbol->previous)
it->second = storedSymbol->previous;
else
rplSymbolStorage.map_symbolByAddress.erase(it);
}
else
{
RPLStoredSymbol* parentSymbol = it->second;
while (parentSymbol && parentSymbol->previous != storedSymbol)
parentSymbol = parentSymbol->previous;
if (parentSymbol)
parentSymbol->previous = storedSymbol->previous;
}
}
delete storedSymbol;
}
void rplSymbolStorage_removeRange(MPTR address, sint32 length)
void rplSymbolStorage_removeRange(MPTR address, sint32 length, uint32 type)
{
std::unique_lock<std::mutex> lck(rplSymbolStorage.m_symbolStorageMutex);
while (length > 0)
{
RPLStoredSymbol* symbol = rplSymbolStorage_getByAddress(address);
if (symbol)
rplSymbolStorage_remove(symbol);
auto it = rplSymbolStorage.map_symbolByAddress.find(address);
if (it != rplSymbolStorage.map_symbolByAddress.end())
{
RPLStoredSymbol* current = it->second;
RPLStoredSymbol* newHead = current;
RPLStoredSymbol* previousKept = nullptr;
while (current)
{
RPLStoredSymbol* next = current->previous;
if ((current->flags & type) == type)
{
if (previousKept)
previousKept->previous = next;
else
newHead = next;
delete current;
}
else
{
previousKept = current;
}
current = next;
}
if (newHead)
it->second = newHead;
else
rplSymbolStorage.map_symbolByAddress.erase(it);
}
address += 4;
length -= 4;
}
@ -145,7 +198,15 @@ void rplSymbolStorage_unloadAll()
{
// free symbols
for (auto& it : rplSymbolStorage.map_symbolByAddress)
delete it.second;
{
RPLStoredSymbol* symbol = it.second;
while (symbol)
{
RPLStoredSymbol* previous = symbol->previous;
delete symbol;
symbol = previous;
}
}
rplSymbolStorage.map_symbolByAddress.clear();
// free libs
rplSymbolLib_t* lib = rplSymbolStorage.libs;

View File

@ -1,19 +1,27 @@
enum RPLStoredSymbolType : uint32
{
RPL_STORED_SYMBOL_NONE = 0,
RPL_STORED_SYMBOL_MAP = 1 << 0,
RPL_STORED_SYMBOL_PATCH = 1 << 1,
};
struct RPLStoredSymbol
{
MPTR address;
void* libName;
void* symbolName;
uint32 flags;
RPLStoredSymbol* previous;
};
void rplSymbolStorage_init();
void rplSymbolStorage_unloadAll();
RPLStoredSymbol* rplSymbolStorage_store(const char* libName, const char* symbolName, MPTR address);
RPLStoredSymbol* rplSymbolStorage_store(const char* libName, const char* symbolName, MPTR address, uint32 type = RPL_STORED_SYMBOL_NONE);
void rplSymbolStorage_remove(RPLStoredSymbol* storedSymbol);
void rplSymbolStorage_removeRange(MPTR address, sint32 length);
void rplSymbolStorage_removeRange(MPTR address, sint32 length, uint32 type = RPL_STORED_SYMBOL_NONE);
RPLStoredSymbol* rplSymbolStorage_getByAddress(MPTR address);
RPLStoredSymbol* rplSymbolStorage_getByClosestAddress(MPTR address);
void rplSymbolStorage_createJumpProxySymbol(MPTR jumpAddress, MPTR destAddress);
std::unordered_map<uint32, RPLStoredSymbol*>& rplSymbolStorage_lockSymbolMap();
void rplSymbolStorage_unlockSymbolMap();
void rplSymbolStorage_unlockSymbolMap();

View File

@ -5,6 +5,9 @@
#if BOOST_OS_WINDOWS
#include <iphlpapi.h>
#elif BOOST_OS_LINUX
#include <ifaddrs.h>
#include <net/if.h>
#endif
// AC lib (manages internet connection)
@ -81,6 +84,40 @@ void _GetLocalIPAndSubnetMask(uint32& localIp, uint32& subnetMask)
cemuLog_logDebug(LogType::Force, "_GetLocalIPAndSubnetMask(): Failed to find network IP and subnet mask");
_GetLocalIPAndSubnetMaskFallback(localIp, subnetMask);
}
#elif BOOST_OS_LINUX
void _GetLocalIPAndSubnetMask(uint32& localIp, uint32& subnetMask)
{
struct ifaddrs *ifaddr;
if (getifaddrs(&ifaddr) == -1)
{
cemuLog_log(LogType::Force, "Failed to acquire local IP and subnet mask");
_GetLocalIPAndSubnetMaskFallback(localIp, subnetMask);
}
stdx::scope_exit _ifa([&]{ freeifaddrs(ifaddr); });
for (const struct ifaddrs* ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next)
{
if (ifa->ifa_addr == nullptr || ifa->ifa_addr->sa_family != AF_INET)
continue;
if (!(ifa->ifa_flags & IFF_UP) || !(ifa->ifa_flags & IFF_RUNNING))
continue;
if (ifa->ifa_flags & IFF_LOOPBACK || ifa->ifa_flags & IFF_POINTOPOINT)
continue;
if (boost::starts_with(ifa->ifa_name, "br-") || boost::starts_with(ifa->ifa_name, "docker"))
continue;
const auto* addr_in = reinterpret_cast<struct sockaddr_in*>(ifa->ifa_addr);
localIp = ntohl(addr_in->sin_addr.s_addr);
const auto* mask_in = reinterpret_cast<struct sockaddr_in*>(ifa->ifa_netmask);
subnetMask = ntohl(mask_in->sin_addr.s_addr);
return;
}
cemuLog_logDebug(LogType::Force, "_GetLocalIPAndSubnetMask(): Failed to find network IP and subnet mask");
_GetLocalIPAndSubnetMaskFallback(localIp, subnetMask);
}
#else
void _GetLocalIPAndSubnetMask(uint32& localIp, uint32& subnetMask)
{

View File

@ -355,6 +355,8 @@ namespace proc_ui
{
if (!s_isInitialized.exchange(false))
return;
if (!OSIsSchedulerActive())
return; // CafeSystem shutdown in progress, OS functions shouldn't be called anymore. reset() will clean up state on next re-init
if ( !s_isInForeground )
CancelBackgroundAlarms();
for (sint32 i = 0; i < Espresso::CORE_COUNT; i++)

View File

@ -121,12 +121,6 @@ DownloadCustomGraphicPackWindow::~DownloadCustomGraphicPackWindow()
int DownloadCustomGraphicPackWindow::ShowModal()
{
if (CafeSystem::IsTitleRunning())
{
wxMessageBox(_("Graphic packs cannot be updated while a game is running."), _("Graphic packs"), 5, this);
return wxID_CANCEL;
}
wxDialog::ShowModal();
return wxID_OK;
}

View File

@ -304,13 +304,13 @@ GraphicPacksWindow2::GraphicPacksWindow2(wxWindow* parent, uint64_t title_id_fil
auto* row = new wxBoxSizer(wxHORIZONTAL);
m_download_from_url = new wxButton(m_right_panel, wxID_ANY, _("Download pack from URL"));
m_download_from_url->Bind(wxEVT_BUTTON, &GraphicPacksWindow2::OnClickCustomDownload, this);
row->Add(m_download_from_url, 0, wxALL, 5);
m_update_graphicPacks = new wxButton(m_right_panel, wxID_ANY, _("Download latest community graphic packs"));
m_update_graphicPacks->Bind(wxEVT_BUTTON, &GraphicPacksWindow2::OnCheckForUpdates, this);
row->Add(m_update_graphicPacks, 0, wxALL, 5);
row->Add(m_update_graphicPacks, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5);
m_download_from_url = new wxHyperlinkCtrl(m_right_panel, wxID_ANY, _("Or download pack from URL..."), _(""));
m_download_from_url->Bind(wxEVT_HYPERLINK, &GraphicPacksWindow2::OnClickCustomDownload, this);
row->Add(m_download_from_url, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5);
sizer->Add(row, 0, wxALL | wxEXPAND, 5);
@ -608,7 +608,7 @@ void GraphicPacksWindow2::OnReloadShaders(wxCommandEvent& event)
void GraphicPacksWindow2::OnClickCustomDownload(wxCommandEvent& event)
{
DownloadCustomGraphicPackWindow frame(this);
if (frame.ShowModal() == wxID_OK && !CafeSystem::IsTitleRunning())
if (frame.ShowModal() == wxID_OK)
{
RefreshGraphicPacks();
FillGraphicPackList();
@ -700,10 +700,17 @@ void GraphicPacksWindow2::OnInstalledGamesChanged(wxCommandEvent& event)
void GraphicPacksWindow2::UpdateTitleRunning(bool running)
{
m_update_graphicPacks->Enable(!running);
m_download_from_url->Enable(!running);
if(running)
{
m_download_from_url->SetToolTip(_("Graphic packs cannot be updated while a game is running."));
m_update_graphicPacks->SetToolTip(_("Graphic packs cannot be updated while a game is running."));
}
else
{
m_update_graphicPacks->SetToolTip(nullptr);
m_download_from_url->SetToolTip(nullptr);
}
}
void GraphicPacksWindow2::ReloadPack(const GraphicPackPtr& graphic_pack) const

View File

@ -1,9 +1,9 @@
#pragma once
#include <wx/frame.h>
#include <wx/dialog.h>
#include <wx/scrolwin.h>
#include <wx/infobar.h>
#include <wx/hyperlink.h>
#include "wxcomponents/checktree.h"
@ -46,7 +46,7 @@ private:
wxBoxSizer* m_preset_sizer;
std::vector<wxChoice*> m_active_preset;
wxButton* m_reload_shaders;
wxButton* m_download_from_url;
wxHyperlinkCtrl* m_download_from_url;
wxButton* m_update_graphicPacks;
wxInfoBar* m_info_bar;

View File

@ -3,11 +3,12 @@
#include "wxHelper.h"
#include <filesystem>
#include "Common/FileStream.h"
#include "config/ActiveSettings.h"
#include "Cafe/HW/MMU/MMU.h"
#include "Cafe/OS/RPL/rpl_structs.h"
#include "Cafe/OS/RPL/rpl_debug_symbols.h"
#include "Cafe/OS/RPL/rpl_symbol_storage.h"
#include "wxgui/debugger/RegisterWindow.h"
#include "wxgui/debugger/DumpWindow.h"
@ -20,6 +21,7 @@
#include "wxgui/debugger/BreakpointWindow.h"
#include "wxgui/debugger/ModuleWindow.h"
#include "util/helpers/helpers.h"
#include "util/helpers/StringHelpers.h"
#include "Cafe/HW/Espresso/Recompiler/PPCRecompiler.h"
#include "Cemu/Logging/CemuLogging.h"
@ -81,13 +83,14 @@ wxEND_EVENT_TABLE()
DebuggerModuleInfo::DebuggerModuleInfo(RPLModule* module)
{
moduleName = module->moduleName2;
moduleName = module->moduleName;
patchCRC = module->patchCRC;
textArea.base = module->regionMappingBase_text.GetMPTR();
textArea.origBase = module->regionOrigAddr_text;
textArea.size = module->regionSize_text;
dataArea.base = module->regionMappingBase_data;
dataArea.origBase = module->regionOrigAddr_data;
dataArea.size = module->regionSize_data;
}
struct DebuggerModuleInfoNotify : public wxClientData
@ -99,6 +102,65 @@ struct DebuggerModuleInfoNotify : public wxClientData
DebuggerWindow2* s_debuggerWindow;
static bool TryParseMapAddress(std::string_view token, uint32& value)
{
trim(token, "\t\n\v\f\r ,:;()[]");
if (const auto colonIndex = token.find_last_of(':'); colonIndex != std::string_view::npos)
token = token.substr(colonIndex + 1);
trim(token, "\t\n\v\f\r ,:;()[]");
if (!token.empty() && (token.back() == 'h' || token.back() == 'H'))
token.remove_suffix(1);
if (token.size() > 2 && token[0] == '0' && (token[1] == 'x' || token[1] == 'X'))
token.remove_prefix(2);
if (token.empty())
return false;
uint64 parsedValue = 0;
const auto result = std::from_chars(token.data(), token.data() + token.size(), parsedValue, 16);
if (result.ec != std::errc() || result.ptr != (token.data() + token.size()) || parsedValue > std::numeric_limits<uint32>::max())
return false;
value = static_cast<uint32>(parsedValue);
return true;
}
static bool TryParseMapSymbolLine(std::string_view rawLine, uint32& address, std::string& symbolName)
{
auto line = rawLine;
trim(line);
if (line.empty())
return false;
const auto addressEnd = line.find_first_of(" \t");
if (addressEnd == std::string_view::npos)
return false;
if (!TryParseMapAddress(line.substr(0, addressEnd), address))
return false;
line = line.substr(addressEnd + 1);
trim(line);
if (line.empty())
return false;
const auto symbolEnd = line.find_first_of(" \t");
auto symbolToken = symbolEnd == std::string_view::npos ? line : line.substr(0, symbolEnd);
trim(symbolToken);
if (symbolToken.empty())
return false;
symbolName.assign(symbolToken);
return true;
}
static std::optional<MPTR> GetRelocatedMapAddress(const DebuggerModuleInfo& moduleInfo, uint32 mapAddress)
{
if (moduleInfo.textArea.ContainsOriginalAddress(mapAddress))
return moduleInfo.textArea.base + (mapAddress - moduleInfo.textArea.origBase);
if (moduleInfo.dataArea.ContainsOriginalAddress(mapAddress))
return moduleInfo.dataArea.base + (mapAddress - moduleInfo.dataArea.origBase);
return std::nullopt;
}
void DebuggerConfig::Load(XMLConfigParser& parser)
{
pin_to_main = parser.get("PinToMainWindow", true);
@ -282,26 +344,79 @@ void DebuggerWindow2::LoadModuleStorage(const DebuggerModuleInfo& moduleInfo)
bool already_loaded = std::any_of(m_modulesStorage.begin(), m_modulesStorage.end(), [path](const std::unique_ptr<XMLDebuggerModuleConfig>& debug) { return debug->GetFilename() == path; });
if (!path.empty() && !already_loaded)
{
m_modulesStorage.emplace_back(new XMLDebuggerModuleConfig(path, { moduleInfo, false }))->Load();
auto& moduleStorage = m_modulesStorage.emplace_back(new XMLDebuggerModuleConfig(path, { moduleInfo, false }));
moduleStorage->Load();
LoadModuleMap(moduleStorage->data());
}
}
void DebuggerWindow2::SaveModuleStorage(const DebuggerModuleInfo& moduleInfo, bool deleteModuleStorage)
{
auto path = GetModuleStoragePath(moduleInfo.moduleName, moduleInfo.patchCRC);
for (auto& moduleStorage : m_modulesStorage)
for (auto it = m_modulesStorage.begin(); it != m_modulesStorage.end(); ++it)
{
auto& moduleStorage = *it;
if (moduleStorage->data().moduleInfo.moduleName == moduleInfo.moduleName && moduleStorage->data().moduleInfo.patchCRC == moduleInfo.patchCRC)
{
moduleStorage->data().delete_breakpoints_after_saving = deleteModuleStorage; // make sure breakpoints are deleted when the storage is removed
moduleStorage->Save(path);
if (deleteModuleStorage)
m_modulesStorage.erase(std::find(m_modulesStorage.begin(), m_modulesStorage.end(), moduleStorage));
break;
{
UnloadModuleMap(moduleStorage->data());
m_modulesStorage.erase(it);
}
return;
}
}
}
void DebuggerWindow2::LoadModuleMap(DebuggerModuleStorage& moduleStorage)
{
UnloadModuleMap(moduleStorage);
const auto mapPath = GetModuleMapPath(moduleStorage.moduleInfo.moduleName, moduleStorage.moduleInfo.patchCRC);
if (mapPath.empty())
return;
std::unique_ptr<FileStream> fileStream(FileStream::openFile(mapPath.c_str()));
if (!fileStream)
return;
const auto fileSize = fileStream->GetSize();
if (fileSize == 0 || fileSize > std::numeric_limits<uint32>::max())
return;
const auto readSize = static_cast<uint32>(fileSize);
std::vector<char> fileData(readSize);
if (fileStream->readData(fileData.data(), readSize) != readSize)
return;
std::string_view fileView(fileData.data(), fileData.size());
for (const auto& line : StringHelpers::StringLineIterator(fileView))
{
uint32 mapAddress = 0;
std::string symbolName;
if (TryParseMapSymbolLine(line, mapAddress, symbolName))
{
const auto relocatedAddress = GetRelocatedMapAddress(moduleStorage.moduleInfo, mapAddress);
if (relocatedAddress)
{
moduleStorage.loaded_map_symbols.emplace_back(rplSymbolStorage_store(moduleStorage.moduleInfo.moduleName.c_str(), symbolName.c_str(), *relocatedAddress, RPL_STORED_SYMBOL_MAP));
}
}
}
}
void DebuggerWindow2::UnloadModuleMap(DebuggerModuleStorage& moduleStorage)
{
for (auto* symbol : moduleStorage.loaded_map_symbols)
{
if (symbol)
rplSymbolStorage_remove(symbol);
}
moduleStorage.loaded_map_symbols.clear();
}
DebuggerWindow2::DebuggerWindow2(wxFrame& parent, const wxRect& display_size)
: wxFrame(&parent, wxID_ANY, _("PPC Debugger"), wxDefaultPosition, wxSize(1280, 300), wxMINIMIZE_BOX | wxMAXIMIZE_BOX | wxSYSTEM_MENU | wxCAPTION | wxCLOSE_BOX | wxCLIP_CHILDREN | wxRESIZE_BORDER | wxFRAME_FLOAT_ON_PARENT),
m_module_address(0)
@ -341,7 +456,7 @@ DebuggerWindow2::DebuggerWindow2(wxFrame& parent, const wxRect& display_size)
{
RPLModule* currentModule = RPLLoader_FindModuleByCodeAddr(MEMORY_CODEAREA_ADDR);
if (currentModule)
label_text = wxString::Format("> %s", currentModule->moduleName2.c_str());
label_text = wxString::Format("> %s", currentModule->moduleName.c_str());
else
label_text = _("> unknown module");
}
@ -497,7 +612,7 @@ void DebuggerWindow2::OnGameLoaded()
RPLModule* current_rpl_module = RPLLoader_FindModuleByCodeAddr(MEMORY_CODEAREA_ADDR);
if(current_rpl_module)
m_module_label->SetLabel(wxString::Format("> %s", current_rpl_module->moduleName2.c_str()));
m_module_label->SetLabel(wxString::Format("> %s", current_rpl_module->moduleName.c_str()));
this->SendSizeEvent();
}
@ -537,6 +652,12 @@ std::wstring DebuggerWindow2::GetModuleStoragePath(std::string module_name, uint
return ActiveSettings::GetConfigPath("debugger/{}_{:#10x}.xml", module_name, crc_hash).generic_wstring();
}
std::wstring DebuggerWindow2::GetModuleMapPath(std::string module_name, uint32_t crc_hash) const
{
if (module_name.empty() || crc_hash == 0) return {};
return ActiveSettings::GetConfigPath("debugger/{}_{:#10x}.map", module_name, crc_hash).generic_wstring();
}
void DebuggerWindow2::OnBreakpointHit(wxCommandEvent& event)
{
PPCInterpreter_t* hCPU = debugger_lockDebugSession();
@ -758,7 +879,7 @@ void DebuggerWindow2::UpdateModuleLabel(uint32 address)
RPLModule* module = RPLLoader_FindModuleByCodeAddr(address);
if (module && m_module_address != module->regionMappingBase_text.GetMPTR())
{
m_module_label->SetLabel(wxString::Format("> %s", module->moduleName2.c_str()));
m_module_label->SetLabel(wxString::Format("> %s", module->moduleName.c_str()));
m_module_address = module->regionMappingBase_text.GetMPTR();
}
else if (address >= mmuRange_CODECAVE.getBase() && address < mmuRange_CODECAVE.getEnd())

View File

@ -16,6 +16,7 @@ class DumpWindow;
class ModuleWindow;
class SymbolWindow;
class wxStaticText;
struct RPLStoredSymbol;
wxDECLARE_EVENT(wxEVT_UPDATE_VIEW, wxCommandEvent);
wxDECLARE_EVENT(wxEVT_BREAKPOINT_HIT, wxCommandEvent);
@ -54,12 +55,18 @@ struct DebuggerModuleInfo
struct ModuleArea
{
MPTR base;
MPTR origBase;
uint32 size;
bool ContainsAddress(MPTR addr)
bool ContainsAddress(MPTR addr) const
{
return addr >= base && (addr < (base+size));
}
bool ContainsOriginalAddress(MPTR addr) const
{
return addr >= origBase && (addr < (origBase + size));
}
};
std::string moduleName;
@ -74,6 +81,7 @@ struct DebuggerModuleStorage
{
DebuggerModuleInfo moduleInfo;
bool delete_breakpoints_after_saving;
std::vector<RPLStoredSymbol*> loaded_map_symbols;
void Load(XMLConfigParser& parser);
void Save(XMLConfigParser& parser);
@ -126,6 +134,9 @@ private:
void CreateMenuBar();
void UpdateModuleLabel(uint32 address = 0);
void LoadModuleMap(DebuggerModuleStorage& moduleStorage);
void UnloadModuleMap(DebuggerModuleStorage& moduleStorage);
std::wstring GetModuleMapPath(std::string module_name, uint32_t crc_hash) const;
void UpdateViewThreadsafe() override;
void NotifyDebugBreakpointHit() override;

View File

@ -14,6 +14,8 @@
#include "Cemu/ExpressionParser/ExpressionParser.h"
#include "Cafe/HW/Espresso/Debugger/DebugSymbolStorage.h"
#include <wx/clipbrd.h>
wxDEFINE_EVENT(wxEVT_DISASMCTRL_NOTIFY_GOTO_ADDRESS, wxCommandEvent);
#define MAX_SYMBOL_LEN (120)
@ -699,20 +701,11 @@ void DisasmCtrl::OnMouseDClick(const wxPoint& position, uint32 line)
void DisasmCtrl::CopyToClipboard(std::string text)
{
#if BOOST_OS_WINDOWS
if (OpenClipboard(nullptr))
if (wxClipboard::Get()->Open())
{
EmptyClipboard();
const HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE, text.size() + 1);
if (hGlobal)
{
memcpy(GlobalLock(hGlobal), text.c_str(), text.size() + 1);
GlobalUnlock(hGlobal);
SetClipboardData(CF_TEXT, hGlobal);
}
CloseClipboard();
wxClipboard::Get()->SetData(new wxTextDataObject(text));
wxClipboard::Get()->Close();
}
#endif
}
static uint32 GetUnrelocatedAddress(MPTR address)

View File

@ -89,7 +89,7 @@ void ModuleWindow::OnGameLoaded()
{
wxListItem item;
item.SetId(i);
item.SetText(module->moduleName2);
item.SetText(module->moduleName);
const auto index = m_modules->InsertItem(item);
m_modules->SetItem(index, ColumnAddress, wxString::Format("%08x", module->regionMappingBase_text.GetMPTR()));

View File

@ -222,7 +222,7 @@ void RegisterWindow::UpdateIntegerRegister(wxTextCtrl* label, wxTextCtrl* value,
RPLModule* code_module = RPLLoader_FindModuleByCodeAddr(registerValue);
if (code_module)
{
label->ChangeValue(wxString::Format("<%s> + %x", code_module->moduleName2.c_str(), registerValue - code_module->regionMappingBase_text.GetMPTR()));
label->ChangeValue(wxString::Format("<%s> + %x", code_module->moduleName.c_str(), registerValue - code_module->regionMappingBase_text.GetMPTR()));
return;
}

View File

@ -2,6 +2,7 @@
#include "Cafe/OS/RPL/rpl_symbol_storage.h"
#include "Cafe/HW/Espresso/Debugger/Debugger.h"
#include <wx/listctrl.h>
#include <wx/clipbrd.h>
enum ItemColumns
{
@ -38,13 +39,12 @@ SymbolListCtrl::SymbolListCtrl(wxWindow* parent, const wxWindowID& id, const wxP
m_list_filter.Clear();
OnGameLoaded();
SetItemCount(m_data.size());
}
void SymbolListCtrl::OnGameLoaded()
{
m_data.clear();
m_visible_items.clear();
const auto symbol_map = rplSymbolStorage_lockSymbolMap();
for (auto const& [address, symbol_info] : symbol_map)
{
@ -56,48 +56,30 @@ void SymbolListCtrl::OnGameLoaded()
wxString searchNameWX = libNameWX + symbolNameWX;
searchNameWX.MakeLower();
auto new_entry = m_data.try_emplace(
symbol_info->address,
symbolNameWX,
m_data.try_emplace(
address,
symbolNameWX,
libNameWX,
searchNameWX,
false
searchNameWX
);
if (m_list_filter.IsEmpty())
new_entry.first->second.visible = true;
else if (new_entry.first->second.searchName.Contains(m_list_filter))
new_entry.first->second.visible = true;
}
rplSymbolStorage_unlockSymbolMap();
SetItemCount(m_data.size());
if (m_data.size() > 0)
RefreshItems(GetTopItem(), std::min<long>(m_data.size() - 1, GetTopItem() + GetCountPerPage() + 1));
RebuildVisibleItems();
}
wxString SymbolListCtrl::OnGetItemText(long item, long column) const
{
if (item >= GetItemCount())
if (item < 0 || item >= static_cast<long>(m_visible_items.size()))
return wxEmptyString;
long visible_idx = 0;
for (const auto& [address, symbol] : m_data)
{
if (!symbol.visible)
continue;
if (item == visible_idx)
{
if (column == ColumnName)
return wxString(symbol.name);
if (column == ColumnAddress)
return wxString::Format("%08x", address);
if (column == ColumnModule)
return wxString(symbol.libName);
}
visible_idx++;
}
const auto& [address, symbol] = *m_visible_items[item];
if (column == ColumnName)
return symbol.name;
if (column == ColumnAddress)
return wxString::Format("%08x", address);
if (column == ColumnModule)
return symbol.libName;
return wxEmptyString;
}
@ -106,10 +88,9 @@ wxString SymbolListCtrl::OnGetItemText(long item, long column) const
void SymbolListCtrl::OnLeftDClick(wxListEvent& event)
{
long selected = GetFirstSelected();
if (selected == wxNOT_FOUND)
if (selected == wxNOT_FOUND || selected >= static_cast<long>(m_visible_items.size()))
return;
const auto text = GetItemText(selected, ColumnAddress);
const auto address = std::stoul(text.ToStdString(), nullptr, 16);
const auto address = m_visible_items[selected]->first;
if (address == 0)
return;
debugger_jumpToAddressInDisasm(address);
@ -118,46 +99,40 @@ void SymbolListCtrl::OnLeftDClick(wxListEvent& event)
void SymbolListCtrl::OnRightClick(wxListEvent& event)
{
long selected = GetFirstSelected();
if (selected == wxNOT_FOUND)
if (selected == wxNOT_FOUND || selected >= static_cast<long>(m_visible_items.size()))
return;
auto text = GetItemText(selected, ColumnAddress);
text = "0x" + text;
#if BOOST_OS_WINDOWS
if (OpenClipboard(nullptr))
if (wxClipboard::Get()->Open())
{
EmptyClipboard();
const HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE, text.size()+1);
if (hGlobal)
{
memcpy(GlobalLock(hGlobal), text.c_str(), text.size() + 1);
GlobalUnlock(hGlobal);
SetClipboardData(CF_TEXT, hGlobal);
GlobalFree(hGlobal);
}
CloseClipboard();
wxClipboard::Get()->SetData(new wxTextDataObject(wxString::Format("0x%08x", m_visible_items[selected]->first)));
wxClipboard::Get()->Close();
}
#endif
}
void SymbolListCtrl::RebuildVisibleItems()
{
m_visible_items.clear();
m_visible_items.reserve(m_data.size());
const bool has_filter = !m_list_filter.IsEmpty();
for (auto it = m_data.cbegin(); it != m_data.cend(); ++it)
{
if (has_filter && !it->second.searchName.Contains(m_list_filter))
continue;
m_visible_items.emplace_back(it);
}
SetItemCount(static_cast<long>(m_visible_items.size()));
if (!m_visible_items.empty())
{
const auto top_item = std::min<long>(GetTopItem(), static_cast<long>(m_visible_items.size() - 1));
RefreshItems(top_item, std::min<long>(static_cast<long>(m_visible_items.size() - 1), top_item + GetCountPerPage() + 1));
}
else
Refresh();
}
void SymbolListCtrl::ChangeListFilter(wxString filter)
{
m_list_filter = filter.MakeLower();
size_t visible_entries = m_data.size();
for (auto& [address, symbol] : m_data)
{
if (m_list_filter.IsEmpty())
symbol.visible = true;
else if (symbol.searchName.Contains(m_list_filter))
symbol.visible = true;
else
{
visible_entries--;
symbol.visible = false;
}
}
SetItemCount(visible_entries);
if (visible_entries > 0)
RefreshItems(GetTopItem(), std::min<long>(visible_entries - 1, GetTopItem() + GetCountPerPage() + 1));
RebuildVisibleItems();
}

View File

@ -10,22 +10,23 @@ public:
void ChangeListFilter(wxString filter);
private:
private:
struct SymbolItem {
SymbolItem() = default;
SymbolItem(wxString name, wxString libName, wxString searchName, bool visible) : name(name), libName(libName), searchName(searchName), visible(visible) {}
SymbolItem() = default;
SymbolItem(const wxString& name, const wxString& libName, const wxString& searchName) : name(name), libName(libName), searchName(searchName) {}
wxString name;
wxString libName;
wxString searchName;
bool visible;
};
using SymbolMap = std::map<MPTR, SymbolItem>;
std::map<MPTR, SymbolItem> m_data;
SymbolMap m_data;
wxString m_list_filter;
std::vector<SymbolMap::const_iterator> m_visible_items;
wxString OnGetItemText(long item, long column) const;
wxString OnGetItemText(long item, long column) const override;
void RebuildVisibleItems();
void OnLeftDClick(wxListEvent& event);
void OnRightClick(wxListEvent& event);
};

View File

@ -244,7 +244,7 @@ void TextureRelationViewerWindow::_setTextureRelationListItemTexture(wxListCtrl*
uiList->SetItem(rowIndex, columnIndex, texInfo->mipLevels == 1 ? "1 mip" : wxString::Format("%d mips", texInfo->mipLevels));
columnIndex++;
// last access
uiList->SetItem(rowIndex, columnIndex, wxString::Format("%lus", (GetTickCount() - texInfo->lastAccessTick + 499) / 1000));
uiList->SetItem(rowIndex, columnIndex, wxString::Format("%uus", (GetTickCount() - texInfo->lastAccessTick + 499) / 1000));
columnIndex++;
// overwrite resolution
wxString overwriteResLabel;

View File

@ -696,7 +696,7 @@ std::shared_ptr<VPADController> InputManager::get_vpad_controller(size_t index)
return {};
std::shared_lock lock(m_mutex);
return std::static_pointer_cast<VPADController>(m_vpad[index]);
return std::dynamic_pointer_cast<VPADController>(m_vpad[index]);
}
std::shared_ptr<WPADController> InputManager::get_wpad_controller(size_t index) const
@ -705,7 +705,7 @@ std::shared_ptr<WPADController> InputManager::get_wpad_controller(size_t index)
return {};
std::shared_lock lock(m_mutex);
return std::static_pointer_cast<WPADController>(m_wpad[index]);
return std::dynamic_pointer_cast<WPADController>(m_wpad[index]);
}
std::pair<size_t, size_t> InputManager::get_controller_count() const

View File

@ -263,6 +263,7 @@ void EmulatedController::clear_controllers()
float EmulatedController::get_axis_value(uint64 mapping) const
{
std::shared_lock lock(m_mutex);
const auto it = m_mappings.find(mapping);
if (it != m_mappings.cend())
{
@ -276,6 +277,7 @@ float EmulatedController::get_axis_value(uint64 mapping) const
bool EmulatedController::is_mapping_down(uint64 mapping) const
{
std::shared_lock lock(m_mutex);
const auto it = m_mappings.find(mapping);
if (it != m_mappings.cend())
{
@ -288,6 +290,7 @@ bool EmulatedController::is_mapping_down(uint64 mapping) const
std::string EmulatedController::get_mapping_name(uint64 mapping) const
{
std::shared_lock lock(m_mutex);
const auto it = m_mappings.find(mapping);
if (it != m_mappings.cend())
{
@ -301,6 +304,7 @@ std::string EmulatedController::get_mapping_name(uint64 mapping) const
std::shared_ptr<ControllerBase> EmulatedController::get_mapping_controller(uint64 mapping) const
{
std::shared_lock lock(m_mutex);
const auto it = m_mappings.find(mapping);
if (it != m_mappings.cend())
{
@ -314,17 +318,20 @@ std::shared_ptr<ControllerBase> EmulatedController::get_mapping_controller(uint6
void EmulatedController::delete_mapping(uint64 mapping)
{
std::scoped_lock lock(m_mutex);
m_mappings.erase(mapping);
}
void EmulatedController::clear_mappings()
{
std::scoped_lock lock(m_mutex);
m_mappings.clear();
}
void EmulatedController::set_mapping(uint64 mapping, const std::shared_ptr<ControllerBase>& controller,
uint64 button)
{
std::scoped_lock lock(m_mutex);
m_mappings[mapping] = { controller, button };
}

View File

@ -76,7 +76,7 @@
},
{
"name": "sdl3",
"version": "3.4.2"
"version": "3.4.10"
},
{
"name": "wxwidgets",