mirror of
https://github.com/izzy2lost/WeeU.git
synced 2026-07-06 00:19:59 -07:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ab53e98288 |
@@ -46,7 +46,6 @@ void decodeBC3Block_UNORM(uint8* inputData, float* imageRGBA);
|
||||
void decodeBC4Block_UNORM(uint8* blockStorage, float* rOutput);
|
||||
void decodeBC5Block_UNORM(uint8* blockStorage, float* rgOutput);
|
||||
void decodeBC5Block_SNORM(uint8* blockStorage, float* rgOutput);
|
||||
using decodingFn = void (uint8 *, float *);
|
||||
|
||||
inline void BC1_GetPixel(uint8* inputData, sint32 x, sint32 y, uint8 rgba[4])
|
||||
{
|
||||
@@ -1641,99 +1640,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class TextureDecoder_BC1_To_R8G8B8A8 : public TextureDecoder, public SingletonClass<TextureDecoder_BC1_To_R8G8B8A8>
|
||||
{
|
||||
public:
|
||||
|
||||
sint32 getBytesPerTexel(LatteTextureLoaderCtx* textureLoader) override
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
|
||||
void decode(LatteTextureLoaderCtx* textureLoader, uint8* outputData) override
|
||||
{
|
||||
for (sint32 y = 0; y < textureLoader->height; y += textureLoader->stepY)
|
||||
{
|
||||
for (sint32 x = 0; x < textureLoader->width; x += textureLoader->stepX)
|
||||
{
|
||||
uint8* blockData = LatteTextureLoader_GetInput(textureLoader, x, y);
|
||||
sint32 blockSizeX = (std::min)(4, textureLoader->width - x);
|
||||
sint32 blockSizeY = (std::min)(4, textureLoader->height - y);
|
||||
// decode 4x4 pixels at once
|
||||
float rgbaBlock[4 * 4 * 4];
|
||||
decodeBC1Block(blockData, rgbaBlock);
|
||||
for (sint32 py = 0; py < blockSizeY; py++)
|
||||
{
|
||||
sint32 yc = y + py;
|
||||
for (sint32 px = 0; px < blockSizeX; px++)
|
||||
{
|
||||
sint32 pixelOffset = (x + px + yc * textureLoader->width) * 4; // write to target buffer
|
||||
float red = rgbaBlock[(px + py * 4) * 4 + 0];
|
||||
float green = rgbaBlock[(px + py * 4) * 4 + 1];
|
||||
float blue = rgbaBlock[(px + py * 4) * 4 + 2];
|
||||
float alpha = rgbaBlock[(px + py * 4) * 4 + 3];
|
||||
*(outputData + pixelOffset + 0) = red * 255;
|
||||
*(outputData + pixelOffset + 1) = green * 255;
|
||||
*(outputData + pixelOffset + 2) = blue * 255;
|
||||
*(outputData + pixelOffset + 3) = alpha * 255;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void decodePixelToRGBA(uint8* blockData, uint8* outputPixel, uint8 blockOffsetX, uint8 blockOffsetY) override
|
||||
{
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
class TextureDecoder_BC2_To_R8G8B8A8 : public TextureDecoder, public SingletonClass<TextureDecoder_BC2_To_R8G8B8A8>
|
||||
{
|
||||
public:
|
||||
|
||||
sint32 getBytesPerTexel(LatteTextureLoaderCtx* textureLoader) override
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
|
||||
void decode(LatteTextureLoaderCtx* textureLoader, uint8* outputData) override
|
||||
{
|
||||
for (sint32 y = 0; y < textureLoader->height; y += textureLoader->stepY)
|
||||
{
|
||||
for (sint32 x = 0; x < textureLoader->width; x += textureLoader->stepX)
|
||||
{
|
||||
uint8* blockData = LatteTextureLoader_GetInput(textureLoader, x, y);
|
||||
sint32 blockSizeX = (std::min)(4, textureLoader->width - x);
|
||||
sint32 blockSizeY = (std::min)(4, textureLoader->height - y);
|
||||
// decode 4x4 pixels at once
|
||||
float rgbaBlock[4 * 4 * 4];
|
||||
decodeBC2Block_UNORM(blockData, rgbaBlock);
|
||||
for (sint32 py = 0; py < blockSizeY; py++)
|
||||
{
|
||||
sint32 yc = y + py;
|
||||
for (sint32 px = 0; px < blockSizeX; px++)
|
||||
{
|
||||
sint32 pixelOffset = (x + px + yc * textureLoader->width) * 4; // write to target buffer
|
||||
float red = rgbaBlock[(px + py * 4) * 4 + 0];
|
||||
float green = rgbaBlock[(px + py * 4) * 4 + 1];
|
||||
float blue = rgbaBlock[(px + py * 4) * 4 + 2];
|
||||
float alpha = rgbaBlock[(px + py * 4) * 4 + 3];
|
||||
*(outputData + pixelOffset + 0) = red * 255;
|
||||
*(outputData + pixelOffset + 1) = green * 255;
|
||||
*(outputData + pixelOffset + 2) = blue * 255;
|
||||
*(outputData + pixelOffset + 3) = alpha * 255;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void decodePixelToRGBA(uint8* blockData, uint8* outputPixel, uint8 blockOffsetX, uint8 blockOffsetY) override
|
||||
{
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
class TextureDecoder_BC2 : public TextureDecoder, public SingletonClass<TextureDecoder_BC2>
|
||||
{
|
||||
public:
|
||||
@@ -1943,53 +1849,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class TextureDecoder_BC3_To_R8G8B8A8 : public TextureDecoder, public SingletonClass<TextureDecoder_BC3_To_R8G8B8A8>
|
||||
{
|
||||
public:
|
||||
|
||||
sint32 getBytesPerTexel(LatteTextureLoaderCtx* textureLoader) override
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
|
||||
void decode(LatteTextureLoaderCtx* textureLoader, uint8* outputData) override
|
||||
{
|
||||
for (sint32 y = 0; y < textureLoader->height; y += textureLoader->stepY)
|
||||
{
|
||||
for (sint32 x = 0; x < textureLoader->width; x += textureLoader->stepX)
|
||||
{
|
||||
uint8* blockData = LatteTextureLoader_GetInput(textureLoader, x, y);
|
||||
sint32 blockSizeX = (std::min)(4, textureLoader->width - x);
|
||||
sint32 blockSizeY = (std::min)(4, textureLoader->height - y);
|
||||
// decode 4x4 pixels at once
|
||||
float rgbaBlock[4 * 4 * 4];
|
||||
decodeBC3Block_UNORM(blockData, rgbaBlock);
|
||||
for (sint32 py = 0; py < blockSizeY; py++)
|
||||
{
|
||||
sint32 yc = y + py;
|
||||
for (sint32 px = 0; px < blockSizeX; px++)
|
||||
{
|
||||
sint32 pixelOffset = (x + px + yc * textureLoader->width) * 4; // write to target buffer
|
||||
float red = rgbaBlock[(px + py * 4) * 4 + 0];
|
||||
float green = rgbaBlock[(px + py * 4) * 4 + 1];
|
||||
float blue = rgbaBlock[(px + py * 4) * 4 + 2];
|
||||
float alpha = rgbaBlock[(px + py * 4) * 4 + 3];
|
||||
*(outputData + pixelOffset + 0) = (uint8)(red * 255);
|
||||
*(outputData + pixelOffset + 1) = (uint8)(green * 255);
|
||||
*(outputData + pixelOffset + 2) = (uint8)(blue * 255);
|
||||
*(outputData + pixelOffset + 3) = (uint8)(alpha * 255);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void decodePixelToRGBA(uint8* blockData, uint8* outputPixel, uint8 blockOffsetX, uint8 blockOffsetY) override
|
||||
{
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
class TextureDecoder_BC3_UNORM_uncompress : public TextureDecoder_BC3_uncompress_generic, public SingletonClass<TextureDecoder_BC3_UNORM_uncompress>
|
||||
{
|
||||
// reuse TextureDecoder_BC3_uncompress_generic
|
||||
@@ -2088,55 +1947,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class TextureDecoder_BC4_To_R8 : public TextureDecoder, public SingletonClass<TextureDecoder_BC4_To_R8>
|
||||
{
|
||||
public:
|
||||
|
||||
sint32 getBytesPerTexel(LatteTextureLoaderCtx* textureLoader) override
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
void decode(LatteTextureLoaderCtx* textureLoader, uint8* outputData) override
|
||||
{
|
||||
for (sint32 y = 0; y < textureLoader->height; y += textureLoader->stepY)
|
||||
{
|
||||
for (sint32 x = 0; x < textureLoader->width; x += textureLoader->stepX)
|
||||
{
|
||||
uint8* blockData = LatteTextureLoader_GetInput(textureLoader, x, y);
|
||||
sint32 blockSizeX = (std::min)(4, textureLoader->width - x);
|
||||
sint32 blockSizeY = (std::min)(4, textureLoader->height - y);
|
||||
// decode 4x4 pixels at once
|
||||
float rBlock[4 * 4 * 1];
|
||||
decodeBC4Block_UNORM(blockData, rBlock);
|
||||
|
||||
for (sint32 py = 0; py < blockSizeY; py++)
|
||||
{
|
||||
sint32 yc = y + py;
|
||||
for (sint32 px = 0; px < blockSizeX; px++)
|
||||
{
|
||||
sint32 pixelOffset = (x + px + yc * textureLoader->width); // write to target buffer
|
||||
float red = rBlock[(px + py * 4) * 1 + 0];
|
||||
*(outputData + pixelOffset + 0) = (uint8)(red * 255);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void decodePixelToRGBA(uint8* blockData, uint8* outputPixel, uint8 blockOffsetX, uint8 blockOffsetY) override
|
||||
{
|
||||
float rBlock[4 * 4 * 1];
|
||||
decodeBC4Block_UNORM(blockData, rBlock);
|
||||
float red = rBlock[(blockOffsetX + blockOffsetY * 4) * 1 + 0];
|
||||
*(outputPixel + 0) = (uint8)(red * 255.0f);
|
||||
*(outputPixel + 1) = 0;
|
||||
*(outputPixel + 2) = 0;
|
||||
*(outputPixel + 3) = 255;
|
||||
}
|
||||
};
|
||||
|
||||
class TextureDecoder_BC4 : public TextureDecoder, public SingletonClass<TextureDecoder_BC4>
|
||||
{
|
||||
public:
|
||||
@@ -2172,57 +1982,6 @@ public:
|
||||
*(outputPixel + 3) = 255;
|
||||
}
|
||||
};
|
||||
template<decodingFn fn>
|
||||
class TextureDecoder_BC5_To_R8G8 : public TextureDecoder, public SingletonClass<TextureDecoder_BC5_To_R8G8<fn>>
|
||||
{
|
||||
public:
|
||||
|
||||
sint32 getBytesPerTexel(LatteTextureLoaderCtx* textureLoader) override
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
void decode(LatteTextureLoaderCtx* textureLoader, uint8* outputData) override
|
||||
{
|
||||
for (sint32 y = 0; y < textureLoader->height; y += textureLoader->stepY)
|
||||
{
|
||||
for (sint32 x = 0; x < textureLoader->width; x += textureLoader->stepX)
|
||||
{
|
||||
uint8* blockData = LatteTextureLoader_GetInput(textureLoader, x, y);
|
||||
sint32 blockSizeX = (std::min)(4, textureLoader->width - x);
|
||||
sint32 blockSizeY = (std::min)(4, textureLoader->height - y);
|
||||
// decode 4x4 pixels at once
|
||||
float rgBlock[4 * 4 * 2];
|
||||
fn(blockData, rgBlock);
|
||||
|
||||
for (sint32 py = 0; py < blockSizeY; py++)
|
||||
{
|
||||
sint32 yc = y + py;
|
||||
for (sint32 px = 0; px < blockSizeX; px++)
|
||||
{
|
||||
sint32 pixelOffset = (x + px + yc * textureLoader->width) * 2; // write to target buffer
|
||||
float red = rgBlock[(px + py * 4) * 2 + 0];
|
||||
float green = rgBlock[(px + py * 4) * 2 + 1];
|
||||
*(outputData + pixelOffset + 0) = (uint8)(red * 255);
|
||||
*(outputData + pixelOffset + 1) = (uint8)(green * 255);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void decodePixelToRGBA(uint8* blockData, uint8* outputPixel, uint8 blockOffsetX, uint8 blockOffsetY) override
|
||||
{
|
||||
float rgBlock[4 * 4 * 2];
|
||||
decodeBC5Block_UNORM(blockData, rgBlock);
|
||||
float red = rgBlock[(blockOffsetX + blockOffsetY * 4) * 2 + 0];
|
||||
float green = rgBlock[(blockOffsetX + blockOffsetY * 4) * 2 + 1];
|
||||
*(outputPixel + 0) = (uint8)(red * 255.0f);
|
||||
*(outputPixel + 1) = (uint8)(green * 255.0f);
|
||||
*(outputPixel + 2) = 0;
|
||||
*(outputPixel + 3) = 255;
|
||||
}
|
||||
};
|
||||
|
||||
class TextureDecoder_BC5_UNORM_uncompress : public TextureDecoder, public SingletonClass<TextureDecoder_BC5_UNORM_uncompress>
|
||||
{
|
||||
|
||||
@@ -323,12 +323,12 @@ VkSurfaceFormatKHR SwapchainInfoVk::ChooseSurfaceFormat(const std::vector<VkSurf
|
||||
|
||||
if (useSRGB)
|
||||
{
|
||||
if ((format.format == VK_FORMAT_B8G8R8A8_SRGB || format.format == VK_FORMAT_R8G8B8A8_SRGB) && format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR)
|
||||
if (format.format == VK_FORMAT_B8G8R8A8_SRGB && format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR)
|
||||
return format;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((format.format == VK_FORMAT_B8G8R8A8_UNORM || format.format == VK_FORMAT_R8G8B8A8_UNORM) && format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR)
|
||||
if (format.format == VK_FORMAT_B8G8R8A8_UNORM && format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR)
|
||||
return format;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -276,10 +276,7 @@ void VulkanRenderer::GetDeviceFeatures()
|
||||
cemuLog_log(LogType::Force, "Vulkan: present_wait extension: {}", (pwf.presentWait && pidf.presentId) ? "supported" : "unsupported");
|
||||
m_featureControl.deviceFeatures.geometry_shader = physicalDeviceFeatures2.features.geometryShader;
|
||||
m_featureControl.deviceFeatures.logic_op = physicalDeviceFeatures2.features.logicOp;
|
||||
m_featureControl.deviceFeatures.sampler_anisotropy = physicalDeviceFeatures2.features.samplerAnisotropy;
|
||||
m_featureControl.deviceFeatures.occlusion_query_precise = physicalDeviceFeatures2.features.occlusionQueryPrecise;
|
||||
m_featureControl.deviceFeatures.depth_clamp = physicalDeviceFeatures2.features.depthClamp;
|
||||
m_featureControl.deviceFeatures.vertex_pipeline_stores_and_atomics = physicalDeviceFeatures2.features.vertexPipelineStoresAndAtomics;
|
||||
|
||||
/* Get Vulkan device properties and limits */
|
||||
VkPhysicalDeviceFloatControlsPropertiesKHR pfcp{};
|
||||
prevStruct = nullptr;
|
||||
@@ -468,12 +465,12 @@ VulkanRenderer::VulkanRenderer()
|
||||
VkPhysicalDeviceFeatures deviceFeatures = {};
|
||||
|
||||
deviceFeatures.independentBlend = VK_TRUE;
|
||||
deviceFeatures.samplerAnisotropy = m_featureControl.deviceFeatures.sampler_anisotropy;
|
||||
deviceFeatures.samplerAnisotropy = VK_TRUE;
|
||||
deviceFeatures.imageCubeArray = VK_TRUE;
|
||||
deviceFeatures.geometryShader = m_featureControl.deviceFeatures.geometry_shader;
|
||||
deviceFeatures.logicOp = m_featureControl.deviceFeatures.logic_op;
|
||||
deviceFeatures.occlusionQueryPrecise = m_featureControl.deviceFeatures.occlusion_query_precise;
|
||||
deviceFeatures.depthClamp = m_featureControl.deviceFeatures.depth_clamp;
|
||||
deviceFeatures.occlusionQueryPrecise = VK_TRUE;
|
||||
deviceFeatures.depthClamp = VK_TRUE;
|
||||
deviceFeatures.depthBiasClamp = VK_TRUE;
|
||||
if (m_vendor == GfxVendor::AMD)
|
||||
{
|
||||
@@ -482,7 +479,7 @@ VulkanRenderer::VulkanRenderer()
|
||||
}
|
||||
if (m_featureControl.mode.useTFEmulationViaSSBO)
|
||||
{
|
||||
m_featureControl.mode.useTFEmulationViaSSBO = deviceFeatures.vertexPipelineStoresAndAtomics = m_featureControl.deviceFeatures.vertex_pipeline_stores_and_atomics;
|
||||
deviceFeatures.vertexPipelineStoresAndAtomics = true;
|
||||
}
|
||||
|
||||
void* deviceExtensionFeatures = nullptr;
|
||||
@@ -1735,16 +1732,6 @@ void VulkanRenderer::QueryMemoryInfo()
|
||||
|
||||
void VulkanRenderer::QueryAvailableFormats()
|
||||
{
|
||||
auto isFormatOptimal = [this](VkFormat format) -> bool {
|
||||
VkFormatProperties fmtProp{};
|
||||
vkGetPhysicalDeviceFormatProperties(m_physicalDevice, format, &fmtProp);
|
||||
return fmtProp.optimalTilingFeatures != 0;
|
||||
};
|
||||
m_supportedFormatInfo.fmt_bc1 = isFormatOptimal(VK_FORMAT_BC1_RGBA_SRGB_BLOCK) && isFormatOptimal(VK_FORMAT_BC1_RGBA_UNORM_BLOCK);
|
||||
m_supportedFormatInfo.fmt_bc2 = isFormatOptimal(VK_FORMAT_BC2_UNORM_BLOCK) && isFormatOptimal(VK_FORMAT_BC2_SRGB_BLOCK);
|
||||
m_supportedFormatInfo.fmt_bc3 = isFormatOptimal(VK_FORMAT_BC3_UNORM_BLOCK) && isFormatOptimal(VK_FORMAT_BC3_SRGB_BLOCK);
|
||||
m_supportedFormatInfo.fmt_bc4 = isFormatOptimal(VK_FORMAT_BC4_UNORM_BLOCK) && isFormatOptimal(VK_FORMAT_BC4_SNORM_BLOCK);
|
||||
m_supportedFormatInfo.fmt_bc5 = isFormatOptimal(VK_FORMAT_BC5_UNORM_BLOCK) && isFormatOptimal(VK_FORMAT_BC5_SNORM_BLOCK);
|
||||
VkFormatProperties fmtProp{};
|
||||
vkGetPhysicalDeviceFormatProperties(m_physicalDevice, VK_FORMAT_D24_UNORM_S8_UINT, &fmtProp);
|
||||
// D24S8
|
||||
@@ -2475,124 +2462,44 @@ void VulkanRenderer::GetTextureFormatInfoVK(Latte::E_GX2SURFFMT format, bool isD
|
||||
break;
|
||||
// compressed formats
|
||||
case Latte::E_GX2SURFFMT::BC1_SRGB:
|
||||
if (m_supportedFormatInfo.fmt_bc1)
|
||||
{
|
||||
formatInfoOut->vkImageFormat = VK_FORMAT_BC1_RGBA_SRGB_BLOCK; // todo - verify
|
||||
formatInfoOut->decoder = TextureDecoder_BC1::getInstance();
|
||||
}
|
||||
else
|
||||
{
|
||||
formatInfoOut->vkImageFormat = VK_FORMAT_R8G8B8A8_SRGB;
|
||||
formatInfoOut->decoder = TextureDecoder_BC1_To_R8G8B8A8::getInstance();
|
||||
}
|
||||
formatInfoOut->vkImageFormat = VK_FORMAT_BC1_RGBA_SRGB_BLOCK; // todo - verify
|
||||
formatInfoOut->decoder = TextureDecoder_BC1::getInstance();
|
||||
break;
|
||||
case Latte::E_GX2SURFFMT::BC1_UNORM:
|
||||
if (m_supportedFormatInfo.fmt_bc1)
|
||||
{
|
||||
formatInfoOut->vkImageFormat = VK_FORMAT_BC1_RGBA_UNORM_BLOCK; // todo - verify
|
||||
formatInfoOut->decoder = TextureDecoder_BC1::getInstance();
|
||||
}
|
||||
else
|
||||
{
|
||||
formatInfoOut->vkImageFormat = VK_FORMAT_R8G8B8A8_UNORM;
|
||||
formatInfoOut->decoder = TextureDecoder_BC1_To_R8G8B8A8::getInstance();
|
||||
}
|
||||
formatInfoOut->vkImageFormat = VK_FORMAT_BC1_RGBA_UNORM_BLOCK; // todo - verify
|
||||
formatInfoOut->decoder = TextureDecoder_BC1::getInstance();
|
||||
break;
|
||||
case Latte::E_GX2SURFFMT::BC2_UNORM:
|
||||
if (m_supportedFormatInfo.fmt_bc2)
|
||||
{
|
||||
formatInfoOut->vkImageFormat = VK_FORMAT_BC2_UNORM_BLOCK; // todo - verify
|
||||
formatInfoOut->decoder = TextureDecoder_BC2::getInstance();
|
||||
}
|
||||
else
|
||||
{
|
||||
formatInfoOut->vkImageFormat = VK_FORMAT_R8G8B8A8_UNORM;
|
||||
formatInfoOut->decoder = TextureDecoder_BC2_To_R8G8B8A8::getInstance();
|
||||
}
|
||||
formatInfoOut->vkImageFormat = VK_FORMAT_BC2_UNORM_BLOCK; // todo - verify
|
||||
formatInfoOut->decoder = TextureDecoder_BC2::getInstance();
|
||||
break;
|
||||
case Latte::E_GX2SURFFMT::BC2_SRGB:
|
||||
if (m_supportedFormatInfo.fmt_bc2)
|
||||
{
|
||||
formatInfoOut->vkImageFormat = VK_FORMAT_BC2_SRGB_BLOCK; // todo - verify
|
||||
formatInfoOut->decoder = TextureDecoder_BC2::getInstance();
|
||||
}
|
||||
else
|
||||
{
|
||||
formatInfoOut->vkImageFormat = VK_FORMAT_R8G8B8A8_SRGB;
|
||||
formatInfoOut->decoder = TextureDecoder_BC2_To_R8G8B8A8::getInstance();
|
||||
}
|
||||
formatInfoOut->vkImageFormat = VK_FORMAT_BC2_SRGB_BLOCK; // todo - verify
|
||||
formatInfoOut->decoder = TextureDecoder_BC2::getInstance();
|
||||
break;
|
||||
case Latte::E_GX2SURFFMT::BC3_UNORM:
|
||||
if (m_supportedFormatInfo.fmt_bc3)
|
||||
{
|
||||
formatInfoOut->vkImageFormat = VK_FORMAT_BC3_UNORM_BLOCK;
|
||||
formatInfoOut->decoder = TextureDecoder_BC3::getInstance();
|
||||
}
|
||||
else
|
||||
{
|
||||
formatInfoOut->vkImageFormat = VK_FORMAT_R8G8B8A8_UNORM;
|
||||
formatInfoOut->decoder = TextureDecoder_BC3_To_R8G8B8A8::getInstance();
|
||||
}
|
||||
formatInfoOut->vkImageFormat = VK_FORMAT_BC3_UNORM_BLOCK;
|
||||
formatInfoOut->decoder = TextureDecoder_BC3::getInstance();
|
||||
break;
|
||||
case Latte::E_GX2SURFFMT::BC3_SRGB:
|
||||
if (m_supportedFormatInfo.fmt_bc3)
|
||||
{
|
||||
formatInfoOut->vkImageFormat = VK_FORMAT_BC3_SRGB_BLOCK;
|
||||
formatInfoOut->decoder = TextureDecoder_BC3::getInstance();
|
||||
}
|
||||
else
|
||||
{
|
||||
formatInfoOut->vkImageFormat = VK_FORMAT_R8G8B8A8_SRGB;
|
||||
formatInfoOut->decoder = TextureDecoder_BC3_To_R8G8B8A8::getInstance();
|
||||
}
|
||||
formatInfoOut->vkImageFormat = VK_FORMAT_BC3_SRGB_BLOCK;
|
||||
formatInfoOut->decoder = TextureDecoder_BC3::getInstance();
|
||||
break;
|
||||
case Latte::E_GX2SURFFMT::BC4_UNORM:
|
||||
if (m_supportedFormatInfo.fmt_bc4)
|
||||
{
|
||||
formatInfoOut->vkImageFormat = VK_FORMAT_BC4_UNORM_BLOCK;
|
||||
formatInfoOut->decoder = TextureDecoder_BC4::getInstance();
|
||||
}
|
||||
else
|
||||
{
|
||||
formatInfoOut->vkImageFormat = VK_FORMAT_R8_UNORM;
|
||||
formatInfoOut->decoder = TextureDecoder_BC4_To_R8::getInstance();
|
||||
}
|
||||
formatInfoOut->vkImageFormat = VK_FORMAT_BC4_UNORM_BLOCK;
|
||||
formatInfoOut->decoder = TextureDecoder_BC4::getInstance();
|
||||
break;
|
||||
case Latte::E_GX2SURFFMT::BC4_SNORM:
|
||||
if (m_supportedFormatInfo.fmt_bc4)
|
||||
{
|
||||
formatInfoOut->vkImageFormat = VK_FORMAT_BC4_SNORM_BLOCK;
|
||||
formatInfoOut->decoder = TextureDecoder_BC4::getInstance();
|
||||
}
|
||||
else
|
||||
{
|
||||
formatInfoOut->vkImageFormat = VK_FORMAT_R8_SNORM;
|
||||
formatInfoOut->decoder = TextureDecoder_BC4_To_R8::getInstance();
|
||||
}
|
||||
formatInfoOut->vkImageFormat = VK_FORMAT_BC4_SNORM_BLOCK;
|
||||
formatInfoOut->decoder = TextureDecoder_BC4::getInstance();
|
||||
break;
|
||||
case Latte::E_GX2SURFFMT::BC5_UNORM:
|
||||
if (m_supportedFormatInfo.fmt_bc5)
|
||||
{
|
||||
formatInfoOut->vkImageFormat = VK_FORMAT_BC5_UNORM_BLOCK;
|
||||
formatInfoOut->decoder = TextureDecoder_BC5::getInstance();
|
||||
}
|
||||
else
|
||||
{
|
||||
formatInfoOut->vkImageFormat = VK_FORMAT_R8G8_UNORM;
|
||||
formatInfoOut->decoder = TextureDecoder_BC5_To_R8G8<decodeBC5Block_UNORM>::getInstance();
|
||||
}
|
||||
formatInfoOut->vkImageFormat = VK_FORMAT_BC5_UNORM_BLOCK;
|
||||
formatInfoOut->decoder = TextureDecoder_BC5::getInstance();
|
||||
break;
|
||||
case Latte::E_GX2SURFFMT::BC5_SNORM:
|
||||
if (m_supportedFormatInfo.fmt_bc5)
|
||||
{
|
||||
formatInfoOut->vkImageFormat = VK_FORMAT_BC5_SNORM_BLOCK;
|
||||
formatInfoOut->decoder = TextureDecoder_BC5::getInstance();
|
||||
}
|
||||
else
|
||||
{
|
||||
formatInfoOut->vkImageFormat = VK_FORMAT_R8G8_SNORM;
|
||||
formatInfoOut->decoder = TextureDecoder_BC5_To_R8G8<decodeBC5Block_SNORM>::getInstance();
|
||||
}
|
||||
formatInfoOut->vkImageFormat = VK_FORMAT_BC5_SNORM_BLOCK;
|
||||
formatInfoOut->decoder = TextureDecoder_BC5::getInstance();
|
||||
break;
|
||||
case Latte::E_GX2SURFFMT::R24_X8_UNORM:
|
||||
formatInfoOut->vkImageFormat = VK_FORMAT_R32_SFLOAT;
|
||||
|
||||
@@ -21,11 +21,6 @@ struct VkSupportedFormatInfo_t
|
||||
bool fmt_r5g6b5_unorm_pack{};
|
||||
bool fmt_r4g4b4a4_unorm_pack{};
|
||||
bool fmt_a1r5g5b5_unorm_pack{};
|
||||
bool fmt_bc1{};
|
||||
bool fmt_bc2{};
|
||||
bool fmt_bc3{};
|
||||
bool fmt_bc4{};
|
||||
bool fmt_bc5{};
|
||||
};
|
||||
|
||||
struct VkDescriptorSetInfo
|
||||
@@ -467,10 +462,6 @@ private:
|
||||
{
|
||||
bool geometry_shader;
|
||||
bool logic_op;
|
||||
bool sampler_anisotropy;
|
||||
bool occlusion_query_precise;
|
||||
bool depth_clamp;
|
||||
bool vertex_pipeline_stores_and_atomics;
|
||||
} deviceFeatures;
|
||||
|
||||
struct
|
||||
|
||||
@@ -80,24 +80,23 @@ android {
|
||||
}
|
||||
}
|
||||
buildFeatures {
|
||||
buildConfig true
|
||||
dataBinding true
|
||||
viewBinding true
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "androidx.datastore:datastore-rxjava3:1.1.1"
|
||||
implementation "androidx.datastore:datastore-preferences:1.1.1"
|
||||
implementation "androidx.datastore:datastore:1.1.1"
|
||||
implementation "com.squareup.okhttp3:okhttp:4.12.0"
|
||||
implementation 'androidx.appcompat:appcompat:1.7.0'
|
||||
implementation 'com.google.android.material:material:1.12.0'
|
||||
implementation "androidx.datastore:datastore-rxjava3:1.1.0"
|
||||
implementation "androidx.datastore:datastore-preferences:1.1.0"
|
||||
implementation "androidx.datastore:datastore:1.1.0"
|
||||
implementation("com.squareup.okhttp3:okhttp:4.12.0")
|
||||
implementation 'androidx.appcompat:appcompat:1.6.1'
|
||||
implementation 'com.google.android.material:material:1.9.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
||||
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
|
||||
implementation 'androidx.navigation:navigation-fragment:2.8.1'
|
||||
implementation 'androidx.navigation:navigation-ui:2.8.1'
|
||||
implementation 'androidx.navigation:navigation-fragment:2.5.3'
|
||||
implementation 'androidx.navigation:navigation-ui:2.5.3'
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.2.1'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
|
||||
}
|
||||
@@ -65,16 +65,5 @@
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<provider
|
||||
android:name=".features.DocumentsProvider"
|
||||
android:authorities="${applicationId}.provider"
|
||||
android:exported="true"
|
||||
android:grantUriPermissions="true"
|
||||
android:permission="android.permission.MANAGE_DOCUMENTS">
|
||||
<intent-filter>
|
||||
<action android:name="android.content.action.DOCUMENTS_PROVIDER" />
|
||||
</intent-filter>
|
||||
</provider>
|
||||
</application>
|
||||
</manifest>
|
||||
@@ -27,7 +27,7 @@ class AndroidFilesystemCallbacks : public FilesystemAndroid::FilesystemCallbacks
|
||||
AndroidFilesystemCallbacks()
|
||||
{
|
||||
JNIUtils::ScopedJNIENV env;
|
||||
m_fileUtilClass = JNIUtils::Scopedjclass("info/cemu/Cemu/nativeinterface/FileCallbacks");
|
||||
m_fileUtilClass = JNIUtils::Scopedjclass("info/cemu/Cemu/utils/FileUtil");
|
||||
m_openContentUriMid = env->GetStaticMethodID(*m_fileUtilClass, "openContentUri", "(Ljava/lang/String;)I");
|
||||
m_listFilesMid = env->GetStaticMethodID(*m_fileUtilClass, "listFiles", "(Ljava/lang/String;)[Ljava/lang/String;");
|
||||
m_isDirectoryMid = env->GetStaticMethodID(*m_fileUtilClass, "isDirectory", "(Ljava/lang/String;)Z");
|
||||
|
||||
@@ -1,16 +1,24 @@
|
||||
add_library(CemuAndroid SHARED
|
||||
AndroidAudio.cpp
|
||||
AndroidAudio.h
|
||||
AndroidEmulatedController.cpp
|
||||
AndroidEmulatedController.h
|
||||
AndroidFilesystemCallbacks.h
|
||||
AndroidGameTitleLoadedCallback.h
|
||||
CMakeLists.txt
|
||||
CafeSystemUtils.cpp
|
||||
CafeSystemUtils.h
|
||||
EmulationState.h
|
||||
GameTitleLoader.cpp
|
||||
GameTitleLoader.h
|
||||
Image.cpp
|
||||
Image.h
|
||||
JNIUtils.cpp
|
||||
NativeEmulation.cpp
|
||||
NativeGameTitles.cpp
|
||||
NativeGraphicPacks.cpp
|
||||
NativeInput.cpp
|
||||
NativeLib.cpp
|
||||
NativeSettings.cpp
|
||||
JNIUtils.h
|
||||
Utils.cpp
|
||||
Utils.h
|
||||
native-lib.cpp
|
||||
stb_image.h
|
||||
)
|
||||
|
||||
target_link_libraries(CemuAndroid PRIVATE
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
#include "CafeSystemUtils.h"
|
||||
|
||||
#include "Cafe/CafeSystem.h"
|
||||
#include "Cafe/TitleList/TitleList.h"
|
||||
|
||||
namespace CafeSystemUtils
|
||||
{
|
||||
void startGame(const fs::path& launchPath)
|
||||
{
|
||||
TitleInfo launchTitle{launchPath};
|
||||
if (launchTitle.IsValid())
|
||||
{
|
||||
// the title might not be in the TitleList, so we add it as a temporary entry
|
||||
CafeTitleList::AddTitleFromPath(launchPath);
|
||||
// title is valid, launch from TitleId
|
||||
TitleId baseTitleId;
|
||||
if (!CafeTitleList::FindBaseTitleId(launchTitle.GetAppTitleId(), baseTitleId))
|
||||
{
|
||||
throw GameBaseFilesNotFoundException();
|
||||
}
|
||||
CafeSystem::STATUS_CODE r = CafeSystem::PrepareForegroundTitle(baseTitleId);
|
||||
if (r != CafeSystem::STATUS_CODE::SUCCESS)
|
||||
{
|
||||
throw UnknownGameFilesException();
|
||||
}
|
||||
}
|
||||
else // if (launchTitle.GetFormat() == TitleInfo::TitleDataFormat::INVALID_STRUCTURE )
|
||||
{
|
||||
// title is invalid, if it's an RPX/ELF we can launch it directly
|
||||
// otherwise it's an error
|
||||
CafeTitleFileType fileType = DetermineCafeSystemFileType(launchPath);
|
||||
if (fileType == CafeTitleFileType::RPX || fileType == CafeTitleFileType::ELF)
|
||||
{
|
||||
CafeSystem::STATUS_CODE r = CafeSystem::PrepareForegroundTitleFromStandaloneRPX(launchPath);
|
||||
if (r != CafeSystem::STATUS_CODE::SUCCESS)
|
||||
{
|
||||
throw UnknownGameFilesException();
|
||||
}
|
||||
}
|
||||
else if (launchTitle.GetInvalidReason() == TitleInfo::InvalidReason::NO_DISC_KEY)
|
||||
{
|
||||
throw NoDiscKeyException();
|
||||
}
|
||||
else if (launchTitle.GetInvalidReason() == TitleInfo::InvalidReason::NO_TITLE_TIK)
|
||||
{
|
||||
throw NoTitleTikException();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw UnknownGameFilesException();
|
||||
}
|
||||
}
|
||||
CafeSystem::LaunchForegroundTitle();
|
||||
}
|
||||
}; // namespace CafeSystemUtils
|
||||
@@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
|
||||
#include "Cafe/TitleList/TitleId.h"
|
||||
|
||||
namespace CafeSystemUtils
|
||||
{
|
||||
class GameFilesException : public std::exception
|
||||
{
|
||||
public:
|
||||
explicit GameFilesException(const std::string& message)
|
||||
: m_message(message) {}
|
||||
|
||||
const char* what() const noexcept override
|
||||
{
|
||||
return m_message.c_str();
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_message;
|
||||
};
|
||||
|
||||
class GameBaseFilesNotFoundException : public GameFilesException
|
||||
{
|
||||
public:
|
||||
GameBaseFilesNotFoundException()
|
||||
: GameFilesException("Game base files not found.") {}
|
||||
};
|
||||
|
||||
class NoDiscKeyException : public GameFilesException
|
||||
{
|
||||
public:
|
||||
NoDiscKeyException()
|
||||
: GameFilesException("No disc key found.") {}
|
||||
};
|
||||
|
||||
class NoTitleTikException : public GameFilesException
|
||||
{
|
||||
public:
|
||||
NoTitleTikException()
|
||||
: GameFilesException("No title ticket found.") {}
|
||||
};
|
||||
|
||||
class UnknownGameFilesException : public GameFilesException
|
||||
{
|
||||
public:
|
||||
UnknownGameFilesException()
|
||||
: GameFilesException("Unknown error occurred during game launch.") {}
|
||||
};
|
||||
|
||||
void startGame(const fs::path& launchPath);
|
||||
}; // namespace CafeSystemUtils
|
||||
@@ -0,0 +1,360 @@
|
||||
#pragma once
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include "AndroidAudio.h"
|
||||
#include "AndroidEmulatedController.h"
|
||||
#include "AndroidFilesystemCallbacks.h"
|
||||
#include "Cafe/HW/Latte/Core/LatteOverlay.h"
|
||||
#include "Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h"
|
||||
#include "Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h"
|
||||
#include "CafeSystemUtils.h"
|
||||
#include "Cafe/CafeSystem.h"
|
||||
#include "Cemu/GuiSystem/GuiSystem.h"
|
||||
#include "GameTitleLoader.h"
|
||||
#include "Utils.h"
|
||||
#include "input/ControllerFactory.h"
|
||||
#include "input/InputManager.h"
|
||||
#include "input/api/Android/AndroidController.h"
|
||||
#include "input/api/Android/AndroidControllerProvider.h"
|
||||
|
||||
void CemuCommonInit();
|
||||
|
||||
class EmulationState
|
||||
{
|
||||
GameTitleLoader m_gameTitleLoader;
|
||||
std::unordered_map<int64_t, GraphicPackPtr> m_graphicPacks;
|
||||
void fillGraphicPacks()
|
||||
{
|
||||
m_graphicPacks.clear();
|
||||
auto graphicPacks = GraphicPack2::GetGraphicPacks();
|
||||
for (auto&& graphicPack : graphicPacks)
|
||||
{
|
||||
m_graphicPacks[reinterpret_cast<int64_t>(graphicPack.get())] = graphicPack;
|
||||
}
|
||||
}
|
||||
void onTouchEvent(sint32 x, sint32 y, bool isTV, std::optional<bool> status = {})
|
||||
{
|
||||
auto& instance = InputManager::instance();
|
||||
auto& touchInfo = isTV ? instance.m_main_mouse : instance.m_pad_mouse;
|
||||
std::scoped_lock lock(touchInfo.m_mutex);
|
||||
touchInfo.position = {x, y};
|
||||
if (status.has_value())
|
||||
touchInfo.left_down = touchInfo.left_down_toggle = status.value();
|
||||
}
|
||||
WiiUMotionHandler m_wiiUMotionHandler{};
|
||||
long m_lastMotionTimestamp;
|
||||
|
||||
public:
|
||||
void initializeEmulation()
|
||||
{
|
||||
g_config.SetFilename(ActiveSettings::GetConfigPath("settings.xml").generic_wstring());
|
||||
g_config.Load();
|
||||
FilesystemAndroid::setFilesystemCallbacks(std::make_shared<AndroidFilesystemCallbacks>());
|
||||
NetworkConfig::LoadOnce();
|
||||
InputManager::instance().load();
|
||||
auto& instance = InputManager::instance();
|
||||
InitializeGlobalVulkan();
|
||||
createCemuDirectories();
|
||||
LatteOverlay_init();
|
||||
CemuCommonInit();
|
||||
fillGraphicPacks();
|
||||
}
|
||||
|
||||
void initializeActiveSettings(const fs::path& dataPath, const fs::path& cachePath)
|
||||
{
|
||||
std::set<fs::path> failedWriteAccess;
|
||||
ActiveSettings::SetPaths(false, {}, dataPath, dataPath, cachePath, dataPath, failedWriteAccess);
|
||||
}
|
||||
|
||||
void clearSurface(bool isMainCanvas)
|
||||
{
|
||||
if (!isMainCanvas)
|
||||
{
|
||||
auto renderer = static_cast<VulkanRenderer*>(g_renderer.get());
|
||||
if (renderer)
|
||||
renderer->StopUsingPadAndWait();
|
||||
}
|
||||
}
|
||||
|
||||
void notifySurfaceChanged(bool isMainCanvas)
|
||||
{
|
||||
}
|
||||
|
||||
void setSurface(JNIEnv* env, jobject surface, bool isMainCanvas)
|
||||
{
|
||||
cemu_assert_debug(surface != nullptr);
|
||||
auto& windowHandleInfo = isMainCanvas ? GuiSystem::getWindowInfo().canvas_main : GuiSystem::getWindowInfo().canvas_pad;
|
||||
if (windowHandleInfo.surface)
|
||||
{
|
||||
ANativeWindow_release(static_cast<ANativeWindow*>(windowHandleInfo.surface));
|
||||
windowHandleInfo.surface = nullptr;
|
||||
}
|
||||
windowHandleInfo.surface = ANativeWindow_fromSurface(env, surface);
|
||||
int width, height;
|
||||
if (isMainCanvas)
|
||||
GuiSystem::getWindowPhysSize(width, height);
|
||||
else
|
||||
GuiSystem::getPadWindowPhysSize(width, height);
|
||||
VulkanRenderer::GetInstance()->InitializeSurface({width, height}, isMainCanvas);
|
||||
}
|
||||
|
||||
void setSurfaceSize(int width, int height, bool isMainCanvas)
|
||||
{
|
||||
auto& windowInfo = GuiSystem::getWindowInfo();
|
||||
if (isMainCanvas)
|
||||
{
|
||||
windowInfo.width = windowInfo.phys_width = width;
|
||||
windowInfo.height = windowInfo.phys_height = height;
|
||||
}
|
||||
else
|
||||
{
|
||||
windowInfo.pad_width = windowInfo.phys_pad_width = width;
|
||||
windowInfo.pad_height = windowInfo.phys_pad_height = height;
|
||||
}
|
||||
}
|
||||
|
||||
void onKeyEvent(const std::string& deviceDescriptor, const std::string& deviceName, int keyCode, bool isPressed)
|
||||
{
|
||||
auto apiProvider = InputManager::instance().get_api_provider(InputAPI::Android);
|
||||
auto androidControllerProvider = dynamic_cast<AndroidControllerProvider*>(apiProvider.get());
|
||||
androidControllerProvider->on_key_event(deviceDescriptor, deviceName, keyCode, isPressed);
|
||||
}
|
||||
|
||||
void onAxisEvent(const std::string& deviceDescriptor, const std::string& deviceName, int axisCode, float value)
|
||||
{
|
||||
auto apiProvider = InputManager::instance().get_api_provider(InputAPI::Android);
|
||||
auto androidControllerProvider = dynamic_cast<AndroidControllerProvider*>(apiProvider.get());
|
||||
androidControllerProvider->on_axis_event(deviceDescriptor, deviceName, axisCode, value);
|
||||
}
|
||||
|
||||
std::optional<std::string> getEmulatedControllerMapping(size_t index, uint64 mappingId)
|
||||
{
|
||||
return AndroidEmulatedController::getAndroidEmulatedController(index).getMapping(mappingId);
|
||||
}
|
||||
|
||||
AndroidEmulatedController& getEmulatedController(size_t index)
|
||||
{
|
||||
return AndroidEmulatedController::getAndroidEmulatedController(index);
|
||||
}
|
||||
|
||||
int getVPADControllersCount()
|
||||
{
|
||||
int vpadCount = 0;
|
||||
for (int i = 0; i < InputManager::kMaxController; i++)
|
||||
{
|
||||
auto emulatedController = AndroidEmulatedController::getAndroidEmulatedController(i).getEmulatedController();
|
||||
if (!emulatedController)
|
||||
continue;
|
||||
if (emulatedController->type() == EmulatedController::Type::VPAD)
|
||||
++vpadCount;
|
||||
}
|
||||
return vpadCount;
|
||||
}
|
||||
|
||||
int getWPADControllersCount()
|
||||
{
|
||||
int wpadCount = 0;
|
||||
for (int i = 0; i < InputManager::kMaxController; i++)
|
||||
{
|
||||
auto emulatedController = AndroidEmulatedController::getAndroidEmulatedController(
|
||||
i)
|
||||
.getEmulatedController();
|
||||
if (!emulatedController)
|
||||
continue;
|
||||
if (emulatedController->type() != EmulatedController::Type::VPAD)
|
||||
++wpadCount;
|
||||
}
|
||||
return wpadCount;
|
||||
}
|
||||
|
||||
EmulatedController::Type getEmulatedControllerType(size_t index)
|
||||
{
|
||||
auto emulatedController = AndroidEmulatedController::getAndroidEmulatedController(index).getEmulatedController();
|
||||
if (emulatedController)
|
||||
return emulatedController->type();
|
||||
throw std::runtime_error(fmt::format("can't get type for emulated controller {}", index));
|
||||
}
|
||||
|
||||
void clearEmulatedControllerMapping(size_t index, uint64 mapping)
|
||||
{
|
||||
AndroidEmulatedController::getAndroidEmulatedController(index).clearMapping(mapping);
|
||||
}
|
||||
|
||||
void setEmulatedControllerType(size_t index, EmulatedController::Type type)
|
||||
{
|
||||
auto& androidEmulatedController = AndroidEmulatedController::getAndroidEmulatedController(index);
|
||||
if (EmulatedController::Type::VPAD <= type && type < EmulatedController::Type::MAX)
|
||||
androidEmulatedController.setType(type);
|
||||
else
|
||||
androidEmulatedController.setDisabled();
|
||||
}
|
||||
|
||||
void initializeRenderer(JNIEnv* env, jobject testSurface)
|
||||
{
|
||||
cemu_assert_debug(testSurface != nullptr);
|
||||
// TODO: cleanup surface
|
||||
GuiSystem::getWindowInfo().window_main.surface = ANativeWindow_fromSurface(env, testSurface);
|
||||
g_renderer = std::make_unique<VulkanRenderer>();
|
||||
}
|
||||
void setReplaceTVWithPadView(bool showDRC)
|
||||
{
|
||||
// Emulate pressing the TAB key for showing DRC instead of TV
|
||||
GuiSystem::getWindowInfo().set_keystate(GuiSystem::PlatformKeyCodes::TAB, showDRC);
|
||||
}
|
||||
void setDPI(float dpi)
|
||||
{
|
||||
auto& windowInfo = GuiSystem::getWindowInfo();
|
||||
windowInfo.dpi_scale = windowInfo.pad_dpi_scale = dpi;
|
||||
}
|
||||
|
||||
bool isEmulatedControllerDisabled(size_t index)
|
||||
{
|
||||
return AndroidEmulatedController::getAndroidEmulatedController(index).getEmulatedController() == nullptr;
|
||||
}
|
||||
|
||||
std::map<uint64, std::string> getEmulatedControllerMappings(size_t index)
|
||||
{
|
||||
return AndroidEmulatedController::getAndroidEmulatedController(index).getMappings();
|
||||
}
|
||||
|
||||
void setControllerMapping(const std::string& deviceDescriptor, const std::string& deviceName, size_t index, uint64 mappingId, uint64 buttonId)
|
||||
{
|
||||
auto apiProvider = InputManager::instance().get_api_provider(InputAPI::Android);
|
||||
auto controller = ControllerFactory::CreateController(InputAPI::Android, deviceDescriptor, deviceName);
|
||||
AndroidEmulatedController::getAndroidEmulatedController(index).setMapping(mappingId, controller, buttonId);
|
||||
}
|
||||
void initializeAudioDevices()
|
||||
{
|
||||
auto& config = g_config.data();
|
||||
if (!config.tv_device.empty())
|
||||
AndroidAudio::createAudioDevice(IAudioAPI::AudioAPI::Cubeb, config.tv_channels, config.tv_volume, true);
|
||||
|
||||
if (!config.pad_device.empty())
|
||||
AndroidAudio::createAudioDevice(IAudioAPI::AudioAPI::Cubeb, config.pad_channels, config.pad_volume, false);
|
||||
}
|
||||
|
||||
void setOnGameTitleLoaded(const std::shared_ptr<GameTitleLoadedCallback>& onGameTitleLoaded)
|
||||
{
|
||||
m_gameTitleLoader.setOnTitleLoaded(onGameTitleLoaded);
|
||||
}
|
||||
|
||||
void addGamesPath(const std::string& gamePath)
|
||||
{
|
||||
auto& gamePaths = g_config.data().game_paths;
|
||||
if (std::any_of(gamePaths.begin(), gamePaths.end(), [&](auto path) { return path == gamePath; }))
|
||||
return;
|
||||
gamePaths.push_back(gamePath);
|
||||
g_config.Save();
|
||||
CafeTitleList::ClearScanPaths();
|
||||
for (auto& it : gamePaths)
|
||||
CafeTitleList::AddScanPath(it);
|
||||
CafeTitleList::Refresh();
|
||||
}
|
||||
|
||||
void removeGamesPath(const std::string& gamePath)
|
||||
{
|
||||
auto& gamePaths = g_config.data().game_paths;
|
||||
std::erase_if(gamePaths, [&](auto path) { return path == gamePath; });
|
||||
g_config.Save();
|
||||
CafeTitleList::ClearScanPaths();
|
||||
for (auto& it : gamePaths)
|
||||
CafeTitleList::AddScanPath(it);
|
||||
CafeTitleList::Refresh();
|
||||
}
|
||||
|
||||
void reloadGameTitles()
|
||||
{
|
||||
m_gameTitleLoader.reloadGameTitles();
|
||||
}
|
||||
|
||||
void startGame(const fs::path& gamePath)
|
||||
{
|
||||
GuiSystem::getWindowInfo().set_keystates_up();
|
||||
initializeAudioDevices();
|
||||
CafeSystemUtils::startGame(gamePath);
|
||||
}
|
||||
|
||||
void refreshGraphicPacks()
|
||||
{
|
||||
if (!CafeSystem::IsTitleRunning())
|
||||
{
|
||||
GraphicPack2::ClearGraphicPacks();
|
||||
GraphicPack2::LoadAll();
|
||||
fillGraphicPacks();
|
||||
}
|
||||
}
|
||||
|
||||
const std::unordered_map<int64_t, GraphicPackPtr>& getGraphicPacks() const
|
||||
{
|
||||
return m_graphicPacks;
|
||||
}
|
||||
|
||||
void setEnabledStateForGraphicPack(int64_t id, bool state)
|
||||
{
|
||||
auto graphicPack = m_graphicPacks.at(id);
|
||||
graphicPack->SetEnabled(state);
|
||||
saveGraphicPackStateToConfig(graphicPack);
|
||||
}
|
||||
|
||||
GraphicPackPtr getGraphicPack(int64_t id) const
|
||||
{
|
||||
return m_graphicPacks.at(id);
|
||||
}
|
||||
void setGraphicPackActivePreset(int64_t id, const std::string& presetCategory, const std::string& preset) const
|
||||
{
|
||||
auto graphicPack = m_graphicPacks.at(id);
|
||||
graphicPack->SetActivePreset(presetCategory, preset);
|
||||
saveGraphicPackStateToConfig(graphicPack);
|
||||
}
|
||||
void saveGraphicPackStateToConfig(GraphicPackPtr graphicPack) const
|
||||
{
|
||||
auto& data = g_config.data();
|
||||
auto filename = _utf8ToPath(graphicPack->GetNormalizedPathString());
|
||||
if (data.graphic_pack_entries.contains(filename))
|
||||
data.graphic_pack_entries.erase(filename);
|
||||
if (graphicPack->IsEnabled())
|
||||
{
|
||||
data.graphic_pack_entries.try_emplace(filename);
|
||||
auto& it = data.graphic_pack_entries[filename];
|
||||
// otherwise store all selected presets
|
||||
for (const auto& preset : graphicPack->GetActivePresets())
|
||||
it.try_emplace(preset->category, preset->name);
|
||||
}
|
||||
else if (graphicPack->IsDefaultEnabled())
|
||||
{
|
||||
// save that its disabled
|
||||
data.graphic_pack_entries.try_emplace(filename);
|
||||
auto& it = data.graphic_pack_entries[filename];
|
||||
it.try_emplace("_disabled", "false");
|
||||
}
|
||||
g_config.Save();
|
||||
}
|
||||
void onTouchMove(sint32 x, sint32 y, bool isTV)
|
||||
{
|
||||
onTouchEvent(x, y, isTV);
|
||||
}
|
||||
void onTouchUp(sint32 x, sint32 y, bool isTV)
|
||||
{
|
||||
onTouchEvent(x, y, isTV, false);
|
||||
}
|
||||
void onTouchDown(sint32 x, sint32 y, bool isTV)
|
||||
{
|
||||
onTouchEvent(x, y, isTV, true);
|
||||
}
|
||||
void onMotion(long timestamp, float gyroX, float gyroY, float gyroZ, float accelX, float accelY, float accelZ)
|
||||
{
|
||||
float deltaTime = (timestamp - m_lastMotionTimestamp) * 1e-9f;
|
||||
m_wiiUMotionHandler.processMotionSample(deltaTime, gyroX, gyroY, gyroZ, accelX * 0.098066f, -accelY * 0.098066f, -accelZ * 0.098066f);
|
||||
m_lastMotionTimestamp = timestamp;
|
||||
auto& deviceMotion = InputManager::instance().m_device_motion;
|
||||
std::scoped_lock lock{deviceMotion.m_mutex};
|
||||
deviceMotion.m_motion_sample = m_wiiUMotionHandler.getMotionSample();
|
||||
}
|
||||
void setMotionEnabled(bool enabled)
|
||||
{
|
||||
auto& deviceMotion = InputManager::instance().m_device_motion;
|
||||
std::scoped_lock lock{deviceMotion.m_mutex};
|
||||
deviceMotion.m_device_motion_enabled = enabled;
|
||||
}
|
||||
};
|
||||
@@ -1,261 +0,0 @@
|
||||
#include "JNIUtils.h"
|
||||
#include "AndroidAudio.h"
|
||||
#include "AndroidEmulatedController.h"
|
||||
#include "AndroidFilesystemCallbacks.h"
|
||||
#include "Cafe/HW/Latte/Core/LatteOverlay.h"
|
||||
#include "Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h"
|
||||
#include "Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h"
|
||||
#include "Cafe/CafeSystem.h"
|
||||
#include "Cemu/GuiSystem/GuiSystem.h"
|
||||
#include "GameTitleLoader.h"
|
||||
#include "input/ControllerFactory.h"
|
||||
#include "input/InputManager.h"
|
||||
#include "input/api/Android/AndroidController.h"
|
||||
#include "input/api/Android/AndroidControllerProvider.h"
|
||||
#include "config/ActiveSettings.h"
|
||||
#include "Cemu/ncrypto/ncrypto.h"
|
||||
|
||||
// forward declaration from main.cpp
|
||||
void CemuCommonInit();
|
||||
|
||||
namespace NativeEmulation
|
||||
{
|
||||
void initializeAudioDevices()
|
||||
{
|
||||
auto& config = g_config.data();
|
||||
if (!config.tv_device.empty())
|
||||
AndroidAudio::createAudioDevice(IAudioAPI::AudioAPI::Cubeb, config.tv_channels, config.tv_volume, true);
|
||||
|
||||
if (!config.pad_device.empty())
|
||||
AndroidAudio::createAudioDevice(IAudioAPI::AudioAPI::Cubeb, config.pad_channels, config.pad_volume, false);
|
||||
}
|
||||
|
||||
void createCemuDirectories()
|
||||
{
|
||||
std::wstring mlc = ActiveSettings::GetMlcPath().generic_wstring();
|
||||
|
||||
// create sys/usr folder in mlc01
|
||||
const auto sysFolder = fs::path(mlc).append(L"sys");
|
||||
fs::create_directories(sysFolder);
|
||||
|
||||
const auto usrFolder = fs::path(mlc).append(L"usr");
|
||||
fs::create_directories(usrFolder);
|
||||
fs::create_directories(fs::path(usrFolder).append("title/00050000")); // base
|
||||
fs::create_directories(fs::path(usrFolder).append("title/0005000c")); // dlc
|
||||
fs::create_directories(fs::path(usrFolder).append("title/0005000e")); // update
|
||||
|
||||
// Mii Maker save folders {0x500101004A000, 0x500101004A100, 0x500101004A200},
|
||||
fs::create_directories(fs::path(mlc).append(L"usr/save/00050010/1004a000/user/common/db"));
|
||||
fs::create_directories(fs::path(mlc).append(L"usr/save/00050010/1004a100/user/common/db"));
|
||||
fs::create_directories(fs::path(mlc).append(L"usr/save/00050010/1004a200/user/common/db"));
|
||||
|
||||
// lang files
|
||||
auto langDir = fs::path(mlc).append(L"sys/title/0005001b/1005c000/content");
|
||||
fs::create_directories(langDir);
|
||||
|
||||
auto langFile = fs::path(langDir).append("language.txt");
|
||||
if (!fs::exists(langFile))
|
||||
{
|
||||
std::ofstream file(langFile);
|
||||
if (file.is_open())
|
||||
{
|
||||
const char* langStrings[] = {"ja", "en", "fr", "de", "it", "es", "zh", "ko", "nl", "pt", "ru", "zh"};
|
||||
for (const char* lang : langStrings)
|
||||
file << fmt::format(R"("{}",)", lang) << std::endl;
|
||||
|
||||
file.flush();
|
||||
file.close();
|
||||
}
|
||||
}
|
||||
|
||||
auto countryFile = fs::path(langDir).append("country.txt");
|
||||
if (!fs::exists(countryFile))
|
||||
{
|
||||
std::ofstream file(countryFile);
|
||||
for (sint32 i = 0; i < 201; i++)
|
||||
{
|
||||
const char* countryCode = NCrypto::GetCountryAsString(i);
|
||||
if (boost::iequals(countryCode, "NN"))
|
||||
file << "NULL," << std::endl;
|
||||
else
|
||||
file << fmt::format(R"("{}",)", countryCode) << std::endl;
|
||||
}
|
||||
file.flush();
|
||||
file.close();
|
||||
}
|
||||
|
||||
// cemu directories
|
||||
const auto controllerProfileFolder = ActiveSettings::GetConfigPath(L"controllerProfiles").generic_wstring();
|
||||
if (!fs::exists(controllerProfileFolder))
|
||||
fs::create_directories(controllerProfileFolder);
|
||||
|
||||
const auto memorySearcherFolder = ActiveSettings::GetUserDataPath(L"memorySearcher").generic_wstring();
|
||||
if (!fs::exists(memorySearcherFolder))
|
||||
fs::create_directories(memorySearcherFolder);
|
||||
}
|
||||
enum StartGameResult : sint32
|
||||
{
|
||||
SUCCESSFUL = 0,
|
||||
ERROR_GAME_BASE_FILES_NOT_FOUND = 1,
|
||||
ERROR_NO_DISC_KEY = 2,
|
||||
ERROR_NO_TITLE_TIK = 3,
|
||||
ERROR_UNKNOWN = 4,
|
||||
};
|
||||
StartGameResult startGame(const fs::path& launchPath)
|
||||
{
|
||||
TitleInfo launchTitle{launchPath};
|
||||
if (launchTitle.IsValid())
|
||||
{
|
||||
// the title might not be in the TitleList, so we add it as a temporary entry
|
||||
CafeTitleList::AddTitleFromPath(launchPath);
|
||||
// title is valid, launch from TitleId
|
||||
TitleId baseTitleId;
|
||||
if (!CafeTitleList::FindBaseTitleId(launchTitle.GetAppTitleId(), baseTitleId))
|
||||
{
|
||||
return ERROR_GAME_BASE_FILES_NOT_FOUND;
|
||||
}
|
||||
CafeSystem::STATUS_CODE r = CafeSystem::PrepareForegroundTitle(baseTitleId);
|
||||
if (r != CafeSystem::STATUS_CODE::SUCCESS)
|
||||
{
|
||||
return ERROR_UNKNOWN;
|
||||
}
|
||||
}
|
||||
else // if (launchTitle.GetFormat() == TitleInfo::TitleDataFormat::INVALID_STRUCTURE )
|
||||
{
|
||||
// title is invalid, if it's an RPX/ELF we can launch it directly
|
||||
// otherwise it's an error
|
||||
CafeTitleFileType fileType = DetermineCafeSystemFileType(launchPath);
|
||||
if (fileType == CafeTitleFileType::RPX || fileType == CafeTitleFileType::ELF)
|
||||
{
|
||||
CafeSystem::STATUS_CODE r = CafeSystem::PrepareForegroundTitleFromStandaloneRPX(launchPath);
|
||||
if (r != CafeSystem::STATUS_CODE::SUCCESS)
|
||||
{
|
||||
return ERROR_UNKNOWN;
|
||||
}
|
||||
}
|
||||
else if (launchTitle.GetInvalidReason() == TitleInfo::InvalidReason::NO_DISC_KEY)
|
||||
{
|
||||
return ERROR_NO_DISC_KEY;
|
||||
}
|
||||
else if (launchTitle.GetInvalidReason() == TitleInfo::InvalidReason::NO_TITLE_TIK)
|
||||
{
|
||||
return ERROR_NO_TITLE_TIK;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ERROR_UNKNOWN;
|
||||
}
|
||||
}
|
||||
CafeSystem::LaunchForegroundTitle();
|
||||
return SUCCESSFUL;
|
||||
}
|
||||
} // namespace NativeEmulation
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeEmulation_setReplaceTVWithPadView([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jboolean swapped)
|
||||
{
|
||||
// Emulate pressing the TAB key for showing DRC instead of TV
|
||||
GuiSystem::getWindowInfo().set_keystate(GuiSystem::PlatformKeyCodes::TAB, swapped);
|
||||
}
|
||||
|
||||
extern "C" [[maybe_unused]] JNIEXPORT void JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeEmulation_initializeActiveSettings(JNIEnv* env, [[maybe_unused]] jclass clazz, jstring data_path, jstring cache_path)
|
||||
{
|
||||
std::string dataPath = JNIUtils::JStringToString(env, data_path);
|
||||
std::string cachePath = JNIUtils::JStringToString(env, cache_path);
|
||||
std::set<fs::path> failedWriteAccess;
|
||||
ActiveSettings::SetPaths(false, {}, dataPath, dataPath, cachePath, dataPath, failedWriteAccess);
|
||||
}
|
||||
|
||||
extern "C" [[maybe_unused]] JNIEXPORT void JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeEmulation_initializeEmulation([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz)
|
||||
{
|
||||
FilesystemAndroid::setFilesystemCallbacks(std::make_shared<AndroidFilesystemCallbacks>());
|
||||
g_config.SetFilename(ActiveSettings::GetConfigPath("settings.xml").generic_wstring());
|
||||
NativeEmulation::createCemuDirectories();
|
||||
NetworkConfig::LoadOnce();
|
||||
ActiveSettings::Init();
|
||||
LatteOverlay_init();
|
||||
CemuCommonInit();
|
||||
InitializeGlobalVulkan();
|
||||
// TODO: move this
|
||||
// fillGraphicPacks();
|
||||
}
|
||||
extern "C" [[maybe_unused]] JNIEXPORT void JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeEmulation_initializerRenderer(JNIEnv* env, [[maybe_unused]] jclass clazz, jobject testSurface)
|
||||
{
|
||||
JNIUtils::handleNativeException(env, [&]() {
|
||||
cemu_assert_debug(testSurface != nullptr);
|
||||
// TODO: cleanup surface
|
||||
GuiSystem::getWindowInfo().window_main.surface = ANativeWindow_fromSurface(env, testSurface);
|
||||
g_renderer = std::make_unique<VulkanRenderer>();
|
||||
});
|
||||
}
|
||||
|
||||
extern "C" [[maybe_unused]] JNIEXPORT void JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeEmulation_setDPI([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jfloat dpi)
|
||||
{
|
||||
auto& windowInfo = GuiSystem::getWindowInfo();
|
||||
windowInfo.dpi_scale = windowInfo.pad_dpi_scale = dpi;
|
||||
}
|
||||
|
||||
extern "C" [[maybe_unused]] JNIEXPORT void JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeEmulation_clearSurface([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jboolean is_main_canvas)
|
||||
{
|
||||
if (!is_main_canvas)
|
||||
{
|
||||
auto renderer = static_cast<VulkanRenderer*>(g_renderer.get());
|
||||
if (renderer)
|
||||
renderer->StopUsingPadAndWait();
|
||||
}
|
||||
}
|
||||
extern "C" [[maybe_unused]] JNIEXPORT void JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeEmulation_recreateRenderSurface([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jboolean is_main_canvas)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
extern "C" [[maybe_unused]] JNIEXPORT void JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeEmulation_setSurface(JNIEnv* env, [[maybe_unused]] jclass clazz, jobject surface, jboolean is_main_canvas)
|
||||
{
|
||||
JNIUtils::handleNativeException(env, [&]() {
|
||||
cemu_assert_debug(surface != nullptr);
|
||||
auto& windowHandleInfo = is_main_canvas ? GuiSystem::getWindowInfo().canvas_main : GuiSystem::getWindowInfo().canvas_pad;
|
||||
if (windowHandleInfo.surface)
|
||||
{
|
||||
ANativeWindow_release(static_cast<ANativeWindow*>(windowHandleInfo.surface));
|
||||
windowHandleInfo.surface = nullptr;
|
||||
}
|
||||
windowHandleInfo.surface = ANativeWindow_fromSurface(env, surface);
|
||||
int width, height;
|
||||
if (is_main_canvas)
|
||||
GuiSystem::getWindowPhysSize(width, height);
|
||||
else
|
||||
GuiSystem::getPadWindowPhysSize(width, height);
|
||||
VulkanRenderer::GetInstance()->InitializeSurface({width, height}, is_main_canvas);
|
||||
});
|
||||
}
|
||||
|
||||
extern "C" [[maybe_unused]] JNIEXPORT void JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeEmulation_setSurfaceSize([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jint width, jint height, jboolean is_main_canvas)
|
||||
{
|
||||
auto& windowInfo = GuiSystem::getWindowInfo();
|
||||
if (is_main_canvas)
|
||||
{
|
||||
windowInfo.width = windowInfo.phys_width = width;
|
||||
windowInfo.height = windowInfo.phys_height = height;
|
||||
}
|
||||
else
|
||||
{
|
||||
windowInfo.pad_width = windowInfo.phys_pad_width = width;
|
||||
windowInfo.pad_height = windowInfo.phys_pad_height = height;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" [[maybe_unused]] JNIEXPORT jint JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeEmulation_startGame([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jstring launchPath)
|
||||
{
|
||||
GuiSystem::getWindowInfo().set_keystates_up();
|
||||
NativeEmulation::initializeAudioDevices();
|
||||
return NativeEmulation::startGame(JNIUtils::JStringToString(env, launchPath));
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
#include "JNIUtils.h"
|
||||
#include "GameTitleLoader.h"
|
||||
#include "AndroidGameTitleLoadedCallback.h"
|
||||
|
||||
namespace NativeGameTitles
|
||||
{
|
||||
GameTitleLoader s_gameTitleLoader;
|
||||
}
|
||||
|
||||
extern "C" [[maybe_unused]] JNIEXPORT void JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeGameTitles_setGameTitleLoadedCallback(JNIEnv* env, [[maybe_unused]] jclass clazz, jobject game_title_loaded_callback)
|
||||
{
|
||||
if (game_title_loaded_callback == nullptr)
|
||||
{
|
||||
NativeGameTitles::s_gameTitleLoader.setOnTitleLoaded(nullptr);
|
||||
return;
|
||||
}
|
||||
jclass gameTitleLoadedCallbackClass = env->GetObjectClass(game_title_loaded_callback);
|
||||
jmethodID onGameTitleLoadedMID = env->GetMethodID(gameTitleLoadedCallbackClass, "onGameTitleLoaded", "(Ljava/lang/String;Ljava/lang/String;[III)V");
|
||||
env->DeleteLocalRef(gameTitleLoadedCallbackClass);
|
||||
NativeGameTitles::s_gameTitleLoader.setOnTitleLoaded(std::make_shared<AndroidGameTitleLoadedCallback>(onGameTitleLoadedMID, game_title_loaded_callback));
|
||||
}
|
||||
|
||||
extern "C" [[maybe_unused]] JNIEXPORT void JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeGameTitles_reloadGameTitles([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz)
|
||||
{
|
||||
NativeGameTitles::s_gameTitleLoader.reloadGameTitles();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jobject JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeGameTitles_getInstalledGamesTitleIds(JNIEnv* env, [[maybe_unused]] jclass clazz)
|
||||
{
|
||||
return JNIUtils::createJavaLongArrayList(env, CafeTitleList::GetAllTitleIds());
|
||||
}
|
||||
@@ -1,157 +0,0 @@
|
||||
#include "Cafe/CafeSystem.h"
|
||||
#include "config/CemuConfig.h"
|
||||
#include "Cafe/GraphicPack/GraphicPack2.h"
|
||||
#include "JNIUtils.h"
|
||||
|
||||
namespace NativeGraphicPacks
|
||||
{
|
||||
std::unordered_map<sint64, GraphicPackPtr> s_graphicPacks;
|
||||
|
||||
void fillGraphicPacks()
|
||||
{
|
||||
s_graphicPacks.clear();
|
||||
auto graphicPacks = GraphicPack2::GetGraphicPacks();
|
||||
for (auto&& graphicPack : graphicPacks)
|
||||
{
|
||||
s_graphicPacks[reinterpret_cast<sint64>(graphicPack.get())] = graphicPack;
|
||||
}
|
||||
}
|
||||
|
||||
void saveGraphicPackStateToConfig(GraphicPackPtr graphicPack)
|
||||
{
|
||||
auto& data = g_config.data();
|
||||
auto filename = _utf8ToPath(graphicPack->GetNormalizedPathString());
|
||||
if (data.graphic_pack_entries.contains(filename))
|
||||
data.graphic_pack_entries.erase(filename);
|
||||
if (graphicPack->IsEnabled())
|
||||
{
|
||||
data.graphic_pack_entries.try_emplace(filename);
|
||||
auto& it = data.graphic_pack_entries[filename];
|
||||
// otherwise store all selected presets
|
||||
for (const auto& preset : graphicPack->GetActivePresets())
|
||||
it.try_emplace(preset->category, preset->name);
|
||||
}
|
||||
else if (graphicPack->IsDefaultEnabled())
|
||||
{
|
||||
// save that its disabled
|
||||
data.graphic_pack_entries.try_emplace(filename);
|
||||
auto& it = data.graphic_pack_entries[filename];
|
||||
it.try_emplace("_disabled", "false");
|
||||
}
|
||||
g_config.Save();
|
||||
}
|
||||
|
||||
jobject getGraphicPresets(JNIEnv* env, GraphicPackPtr graphicPack, sint64 id)
|
||||
{
|
||||
auto graphicPackPresetClass = env->FindClass("info/cemu/Cemu/nativeinterface/NativeGraphicPacks$GraphicPackPreset");
|
||||
auto graphicPackPresetCtorId = env->GetMethodID(graphicPackPresetClass, "<init>", "(JLjava/lang/String;Ljava/util/ArrayList;Ljava/lang/String;)V");
|
||||
|
||||
std::vector<std::string> order;
|
||||
auto presets = graphicPack->GetCategorizedPresets(order);
|
||||
|
||||
std::vector<jobject> presetsJobjects;
|
||||
for (const auto& category : order)
|
||||
{
|
||||
const auto& entry = presets[category];
|
||||
// test if any preset is visible and update its status
|
||||
if (std::none_of(entry.cbegin(), entry.cend(), [graphicPack](const auto& p) { return p->visible; }))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
jstring categoryJStr = category.empty() ? nullptr : env->NewStringUTF(category.c_str());
|
||||
std::vector<std::string> presetSelections;
|
||||
std::optional<std::string> activePreset;
|
||||
for (auto& pentry : entry)
|
||||
{
|
||||
if (!pentry->visible)
|
||||
continue;
|
||||
|
||||
presetSelections.push_back(pentry->name);
|
||||
|
||||
if (pentry->active)
|
||||
activePreset = pentry->name;
|
||||
}
|
||||
|
||||
jstring activePresetJstr = nullptr;
|
||||
if (activePreset)
|
||||
activePresetJstr = env->NewStringUTF(activePreset->c_str());
|
||||
else if (!presetSelections.empty())
|
||||
activePresetJstr = env->NewStringUTF(presetSelections.front().c_str());
|
||||
auto presetJObject = env->NewObject(graphicPackPresetClass, graphicPackPresetCtorId, id, categoryJStr, JNIUtils::createJavaStringArrayList(env, presetSelections), activePresetJstr);
|
||||
presetsJobjects.push_back(presetJObject);
|
||||
}
|
||||
return JNIUtils::createArrayList(env, presetsJobjects);
|
||||
}
|
||||
} // namespace NativeGraphicPacks
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeGraphicPacks_refreshGraphicPacks([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz)
|
||||
{
|
||||
if (!CafeSystem::IsTitleRunning())
|
||||
{
|
||||
GraphicPack2::ClearGraphicPacks();
|
||||
GraphicPack2::LoadAll();
|
||||
NativeGraphicPacks::fillGraphicPacks();
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jobject JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeGraphicPacks_getGraphicPackBasicInfos(JNIEnv* env, [[maybe_unused]] jclass clazz)
|
||||
{
|
||||
auto graphicPackInfoClass = env->FindClass("info/cemu/Cemu/nativeinterface/NativeGraphicPacks$GraphicPackBasicInfo");
|
||||
auto graphicPackInfoCtorId = env->GetMethodID(graphicPackInfoClass, "<init>", "(JLjava/lang/String;Ljava/util/ArrayList;)V");
|
||||
|
||||
std::vector<jobject> graphicPackInfoJObjects;
|
||||
for (auto&& graphicPack : NativeGraphicPacks::s_graphicPacks)
|
||||
{
|
||||
jstring virtualPath = env->NewStringUTF(graphicPack.second->GetVirtualPath().c_str());
|
||||
jlong id = graphicPack.first;
|
||||
jobject titleIds = JNIUtils::createJavaLongArrayList(env, graphicPack.second->GetTitleIds());
|
||||
jobject jGraphicPack = env->NewObject(graphicPackInfoClass, graphicPackInfoCtorId, id, virtualPath, titleIds);
|
||||
graphicPackInfoJObjects.push_back(jGraphicPack);
|
||||
}
|
||||
return JNIUtils::createArrayList(env, graphicPackInfoJObjects);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jobject JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeGraphicPacks_getGraphicPack(JNIEnv* env, [[maybe_unused]] jclass clazz, jlong id)
|
||||
{
|
||||
auto graphicPackClass = env->FindClass("info/cemu/Cemu/nativeinterface/NativeGraphicPacks$GraphicPack");
|
||||
auto graphicPackCtorId = env->GetMethodID(graphicPackClass, "<init>", "(JZLjava/lang/String;Ljava/lang/String;Ljava/util/ArrayList;)V");
|
||||
auto graphicPack = NativeGraphicPacks::s_graphicPacks.at(id);
|
||||
|
||||
jstring graphicPackName = env->NewStringUTF(graphicPack->GetName().c_str());
|
||||
jstring graphicPackDescription = env->NewStringUTF(graphicPack->GetDescription().c_str());
|
||||
return env->NewObject(
|
||||
graphicPackClass,
|
||||
graphicPackCtorId,
|
||||
id,
|
||||
graphicPack->IsEnabled(),
|
||||
graphicPackName,
|
||||
graphicPackDescription,
|
||||
NativeGraphicPacks::getGraphicPresets(env, graphicPack, id));
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeGraphicPacks_setGraphicPackActive([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jlong id, jboolean active)
|
||||
{
|
||||
auto graphicPack = NativeGraphicPacks::s_graphicPacks.at(id);
|
||||
graphicPack->SetEnabled(active);
|
||||
NativeGraphicPacks::saveGraphicPackStateToConfig(graphicPack);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeGraphicPacks_setGraphicPackActivePreset([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jlong id, jstring category, jstring preset)
|
||||
{
|
||||
std::string presetCategory = category == nullptr ? "" : JNIUtils::JStringToString(env, category);
|
||||
auto graphicPack = NativeGraphicPacks::s_graphicPacks.at(id);
|
||||
graphicPack->SetActivePreset(presetCategory, JNIUtils::JStringToString(env, preset));
|
||||
NativeGraphicPacks::saveGraphicPackStateToConfig(graphicPack);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jobject JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeGraphicPacks_getGraphicPackPresets(JNIEnv* env, [[maybe_unused]] jclass clazz, jlong id)
|
||||
{
|
||||
return NativeGraphicPacks::getGraphicPresets(env, NativeGraphicPacks::s_graphicPacks.at(id), id);
|
||||
}
|
||||
@@ -1,190 +0,0 @@
|
||||
#include "JNIUtils.h"
|
||||
#include "input/ControllerFactory.h"
|
||||
#include "input/InputManager.h"
|
||||
#include "input/api/Android/AndroidController.h"
|
||||
#include "input/api/Android/AndroidControllerProvider.h"
|
||||
#include "AndroidEmulatedController.h"
|
||||
|
||||
namespace NativeInput
|
||||
{
|
||||
WiiUMotionHandler s_wiiUMotionHandler{};
|
||||
long s_lastMotionTimestamp = 0;
|
||||
|
||||
void onTouchEvent(sint32 x, sint32 y, bool isTV, std::optional<bool> status = {})
|
||||
{
|
||||
auto& instance = InputManager::instance();
|
||||
auto& touchInfo = isTV ? instance.m_main_mouse : instance.m_pad_mouse;
|
||||
std::scoped_lock lock(touchInfo.m_mutex);
|
||||
touchInfo.position = {x, y};
|
||||
if (status.has_value())
|
||||
touchInfo.left_down = touchInfo.left_down_toggle = status.value();
|
||||
}
|
||||
} // namespace NativeInput
|
||||
|
||||
extern "C" [[maybe_unused]] JNIEXPORT void JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeInput_onNativeKey(JNIEnv* env, [[maybe_unused]] jclass clazz, jstring device_descriptor, jstring device_name, jint key, jboolean is_pressed)
|
||||
{
|
||||
auto deviceDescriptor = JNIUtils::JStringToString(env, device_descriptor);
|
||||
auto deviceName = JNIUtils::JStringToString(env, device_name);
|
||||
auto apiProvider = InputManager::instance().get_api_provider(InputAPI::Android);
|
||||
auto androidControllerProvider = dynamic_cast<AndroidControllerProvider*>(apiProvider.get());
|
||||
androidControllerProvider->on_key_event(deviceDescriptor, deviceName, key, is_pressed);
|
||||
}
|
||||
|
||||
extern "C" [[maybe_unused]] JNIEXPORT void JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeInput_onNativeAxis(JNIEnv* env, [[maybe_unused]] jclass clazz, jstring device_descriptor, jstring device_name, jint axis, jfloat value)
|
||||
{
|
||||
auto deviceDescriptor = JNIUtils::JStringToString(env, device_descriptor);
|
||||
auto deviceName = JNIUtils::JStringToString(env, device_name);
|
||||
auto apiProvider = InputManager::instance().get_api_provider(InputAPI::Android);
|
||||
auto androidControllerProvider = dynamic_cast<AndroidControllerProvider*>(apiProvider.get());
|
||||
androidControllerProvider->on_axis_event(deviceDescriptor, deviceName, axis, value);
|
||||
}
|
||||
|
||||
extern "C" [[maybe_unused]] JNIEXPORT void JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeInput_setControllerType([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jint index, jint emulated_controller_type)
|
||||
{
|
||||
auto type = static_cast<EmulatedController::Type>(emulated_controller_type);
|
||||
auto& androidEmulatedController = AndroidEmulatedController::getAndroidEmulatedController(index);
|
||||
if (EmulatedController::Type::VPAD <= type && type < EmulatedController::Type::MAX)
|
||||
androidEmulatedController.setType(type);
|
||||
else
|
||||
androidEmulatedController.setDisabled();
|
||||
}
|
||||
|
||||
extern "C" [[maybe_unused]] JNIEXPORT jint JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeInput_getControllerType([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jint index)
|
||||
{
|
||||
auto emulatedController = AndroidEmulatedController::getAndroidEmulatedController(index).getEmulatedController();
|
||||
if (emulatedController)
|
||||
return emulatedController->type();
|
||||
throw std::runtime_error(fmt::format("can't get type for emulated controller {}", index));
|
||||
}
|
||||
|
||||
extern "C" [[maybe_unused]] JNIEXPORT jint JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeInput_getWPADControllersCount([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz)
|
||||
{
|
||||
int wpadCount = 0;
|
||||
for (size_t i = 0; i < InputManager::kMaxController; i++)
|
||||
{
|
||||
auto emulatedController = AndroidEmulatedController::getAndroidEmulatedController(i).getEmulatedController();
|
||||
if (!emulatedController)
|
||||
continue;
|
||||
if (emulatedController->type() != EmulatedController::Type::VPAD)
|
||||
++wpadCount;
|
||||
}
|
||||
return wpadCount;
|
||||
}
|
||||
|
||||
extern "C" [[maybe_unused]] JNIEXPORT jint JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeInput_getVPADControllersCount([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz)
|
||||
{
|
||||
int vpadCount = 0;
|
||||
for (size_t i = 0; i < InputManager::kMaxController; i++)
|
||||
{
|
||||
auto emulatedController = AndroidEmulatedController::getAndroidEmulatedController(i).getEmulatedController();
|
||||
if (!emulatedController)
|
||||
continue;
|
||||
if (emulatedController->type() == EmulatedController::Type::VPAD)
|
||||
++vpadCount;
|
||||
}
|
||||
return vpadCount;
|
||||
}
|
||||
|
||||
extern "C" [[maybe_unused]] JNIEXPORT jboolean JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeInput_isControllerDisabled([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jint index)
|
||||
{
|
||||
return AndroidEmulatedController::getAndroidEmulatedController(index).getEmulatedController() == nullptr;
|
||||
}
|
||||
|
||||
extern "C" [[maybe_unused]] JNIEXPORT void JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeInput_setControllerMapping(JNIEnv* env, [[maybe_unused]] jclass clazz, jstring device_descriptor, jstring device_name, jint index, jint mapping_id, jint button_id)
|
||||
{
|
||||
auto deviceName = JNIUtils::JStringToString(env, device_name);
|
||||
auto deviceDescriptor = JNIUtils::JStringToString(env, device_descriptor);
|
||||
auto apiProvider = InputManager::instance().get_api_provider(InputAPI::Android);
|
||||
auto controller = ControllerFactory::CreateController(InputAPI::Android, deviceDescriptor, deviceName);
|
||||
AndroidEmulatedController::getAndroidEmulatedController(index).setMapping(mapping_id, controller, button_id);
|
||||
}
|
||||
|
||||
extern "C" [[maybe_unused]] JNIEXPORT jstring JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeInput_getControllerMapping(JNIEnv* env, [[maybe_unused]] jclass clazz, jint index, jint mapping_id)
|
||||
{
|
||||
auto mapping = AndroidEmulatedController::getAndroidEmulatedController(index).getMapping(mapping_id);
|
||||
return env->NewStringUTF(mapping.value_or("").c_str());
|
||||
}
|
||||
|
||||
extern "C" [[maybe_unused]] JNIEXPORT void JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeInput_clearControllerMapping([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jint index, jint mapping_id)
|
||||
{
|
||||
AndroidEmulatedController::getAndroidEmulatedController(index).clearMapping(mapping_id);
|
||||
}
|
||||
|
||||
extern "C" [[maybe_unused]] JNIEXPORT jobject JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeInput_getControllerMappings(JNIEnv* env, [[maybe_unused]] jclass clazz, jint index)
|
||||
{
|
||||
jclass hashMapClass = env->FindClass("java/util/HashMap");
|
||||
jmethodID hashMapConstructor = env->GetMethodID(hashMapClass, "<init>", "()V");
|
||||
jmethodID hashMapPut = env->GetMethodID(hashMapClass, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
|
||||
jclass integerClass = env->FindClass("java/lang/Integer");
|
||||
jmethodID integerConstructor = env->GetMethodID(integerClass, "<init>", "(I)V");
|
||||
jobject hashMapObj = env->NewObject(hashMapClass, hashMapConstructor);
|
||||
auto mappings = AndroidEmulatedController::getAndroidEmulatedController(index).getMappings();
|
||||
for (const auto& pair : mappings)
|
||||
{
|
||||
jint key = pair.first;
|
||||
jstring buttonName = env->NewStringUTF(pair.second.c_str());
|
||||
jobject mappingId = env->NewObject(integerClass, integerConstructor, key);
|
||||
env->CallObjectMethod(hashMapObj, hashMapPut, mappingId, buttonName);
|
||||
}
|
||||
return hashMapObj;
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeInput_onTouchDown([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jint x, jint y, jboolean isTV)
|
||||
{
|
||||
NativeInput::onTouchEvent(x, y, isTV, true);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeInput_onTouchUp([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jint x, jint y, jboolean isTV)
|
||||
{
|
||||
NativeInput::onTouchEvent(x, y, isTV, false);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeInput_onTouchMove([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jint x, jint y, jboolean isTV)
|
||||
{
|
||||
NativeInput::onTouchEvent(x, y, isTV);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeInput_onMotion([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jlong timestamp, jfloat gyroX, jfloat gyroY, jfloat gyroZ, jfloat accelX, jfloat accelY, jfloat accelZ)
|
||||
{
|
||||
float deltaTime = (timestamp - NativeInput::s_lastMotionTimestamp) * 1e-9f;
|
||||
NativeInput::s_wiiUMotionHandler.processMotionSample(deltaTime, gyroX, gyroY, gyroZ, accelX * 0.098066f, -accelY * 0.098066f, -accelZ * 0.098066f);
|
||||
NativeInput::s_lastMotionTimestamp = timestamp;
|
||||
auto& deviceMotion = InputManager::instance().m_device_motion;
|
||||
std::scoped_lock lock{deviceMotion.m_mutex};
|
||||
deviceMotion.m_motion_sample = NativeInput::s_wiiUMotionHandler.getMotionSample();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeInput_setMotionEnabled([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jboolean motionEnabled)
|
||||
{
|
||||
auto& deviceMotion = InputManager::instance().m_device_motion;
|
||||
std::scoped_lock lock{deviceMotion.m_mutex};
|
||||
deviceMotion.m_device_motion_enabled = motionEnabled;
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeInput_onOverlayButton([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jint controllerIndex, jint mappingId, jboolean state)
|
||||
{
|
||||
AndroidEmulatedController::getAndroidEmulatedController(controllerIndex).setButtonValue(mappingId, state);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeInput_onOverlayAxis([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jint controllerIndex, jint mappingId, jfloat value)
|
||||
{
|
||||
AndroidEmulatedController::getAndroidEmulatedController(controllerIndex).setAxisValue(mappingId, value);
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
#include "JNIUtils.h"
|
||||
|
||||
extern "C" [[maybe_unused]] JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, [[maybe_unused]] void* reserved)
|
||||
{
|
||||
JNIUtils::g_jvm = vm;
|
||||
return JNI_VERSION_1_6;
|
||||
}
|
||||
@@ -1,336 +0,0 @@
|
||||
#include "JNIUtils.h"
|
||||
#include "config/CemuConfig.h"
|
||||
|
||||
extern "C" JNIEXPORT jint JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeSettings_getOverlayPosition([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz)
|
||||
{
|
||||
return static_cast<jint>(g_config.data().overlay.position);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeSettings_setOverlayPosition([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jint position)
|
||||
{
|
||||
g_config.data().overlay.position = static_cast<ScreenPosition>(position);
|
||||
g_config.Save();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jint JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeSettings_getOverlayTextScalePercentage([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz)
|
||||
{
|
||||
return g_config.data().overlay.text_scale;
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeSettings_setOverlayTextScalePercentage([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jint scalePercentage)
|
||||
{
|
||||
g_config.data().overlay.text_scale = scalePercentage;
|
||||
g_config.Save();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jboolean JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeSettings_isOverlayFPSEnabled([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz)
|
||||
{
|
||||
return g_config.data().overlay.fps;
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeSettings_setOverlayFPSEnabled([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jboolean enabled)
|
||||
{
|
||||
g_config.data().overlay.fps = enabled;
|
||||
g_config.Save();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jboolean JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeSettings_isOverlayDrawCallsPerFrameEnabled([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz)
|
||||
{
|
||||
return g_config.data().overlay.drawcalls;
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeSettings_setOverlayDrawCallsPerFrameEnabled([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jboolean enabled)
|
||||
{
|
||||
g_config.data().overlay.drawcalls = enabled;
|
||||
g_config.Save();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jboolean JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeSettings_isOverlayCPUUsageEnabled([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz)
|
||||
{
|
||||
return g_config.data().overlay.cpu_usage;
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeSettings_setOverlayCPUUsageEnabled([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jboolean enabled)
|
||||
{
|
||||
g_config.data().overlay.cpu_usage = enabled;
|
||||
g_config.Save();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jboolean JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeSettings_isOverlayCPUPerCoreUsageEnabled([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz)
|
||||
{
|
||||
return g_config.data().overlay.cpu_per_core_usage;
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeSettings_setOverlayCPUPerCoreUsageEnabled([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jboolean enabled)
|
||||
{
|
||||
g_config.data().overlay.cpu_per_core_usage = enabled;
|
||||
g_config.Save();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jboolean JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeSettings_isOverlayRAMUsageEnabled([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz)
|
||||
{
|
||||
return g_config.data().overlay.ram_usage;
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeSettings_setOverlayRAMUsageEnabled([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jboolean enabled)
|
||||
{
|
||||
g_config.data().overlay.ram_usage = enabled;
|
||||
g_config.Save();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jboolean JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeSettings_isOverlayVRAMUsageEnabled([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz)
|
||||
{
|
||||
return g_config.data().overlay.vram_usage;
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeSettings_setOverlayVRAMUsageEnabled([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jboolean enabled)
|
||||
{
|
||||
g_config.data().overlay.vram_usage = enabled;
|
||||
g_config.Save();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jboolean JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeSettings_isOverlayDebugEnabled([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz)
|
||||
{
|
||||
return g_config.data().overlay.debug;
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeSettings_setOverlayDebugEnabled([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jboolean enabled)
|
||||
{
|
||||
g_config.data().overlay.debug = enabled;
|
||||
g_config.Save();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jint JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeSettings_getNotificationsPosition([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz)
|
||||
{
|
||||
return static_cast<jint>(g_config.data().notification.position);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeSettings_setNotificationsPosition([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jint position)
|
||||
{
|
||||
g_config.data().notification.position = static_cast<ScreenPosition>(position);
|
||||
g_config.Save();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jint JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeSettings_getNotificationsTextScalePercentage([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz)
|
||||
{
|
||||
return g_config.data().notification.text_scale;
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeSettings_setNotificationsTextScalePercentage([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jint scalePercentage)
|
||||
{
|
||||
g_config.data().notification.text_scale = scalePercentage;
|
||||
g_config.Save();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jboolean JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeSettings_isNotificationControllerProfilesEnabled([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz)
|
||||
{
|
||||
return g_config.data().notification.controller_profiles;
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeSettings_setNotificationControllerProfilesEnabled([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jboolean enabled)
|
||||
{
|
||||
g_config.data().notification.controller_profiles = enabled;
|
||||
g_config.Save();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jboolean JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeSettings_isNotificationShaderCompilerEnabled([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz)
|
||||
{
|
||||
return g_config.data().notification.shader_compiling;
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeSettings_setNotificationShaderCompilerEnabled([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jboolean enabled)
|
||||
{
|
||||
g_config.data().notification.shader_compiling = enabled;
|
||||
g_config.Save();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jboolean JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeSettings_isNotificationFriendListEnabled([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz)
|
||||
{
|
||||
return g_config.data().notification.friends;
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeSettings_setNotificationFriendListEnabled([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jboolean enabled)
|
||||
{
|
||||
g_config.data().notification.friends = enabled;
|
||||
g_config.Save();
|
||||
}
|
||||
|
||||
extern "C" [[maybe_unused]] JNIEXPORT void JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeSettings_addGamesPath(JNIEnv* env, [[maybe_unused]] jclass clazz, jstring uri)
|
||||
{
|
||||
auto& gamePaths = g_config.data().game_paths;
|
||||
auto gamePath = JNIUtils::JStringToString(env, uri);
|
||||
if (std::any_of(gamePaths.begin(), gamePaths.end(), [&](auto path) { return path == gamePath; }))
|
||||
return;
|
||||
gamePaths.push_back(gamePath);
|
||||
g_config.Save();
|
||||
}
|
||||
|
||||
extern "C" [[maybe_unused]] JNIEXPORT void JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeSettings_removeGamesPath(JNIEnv* env, [[maybe_unused]] jclass clazz, jstring uri)
|
||||
{
|
||||
auto gamePath = JNIUtils::JStringToString(env, uri);
|
||||
auto& gamePaths = g_config.data().game_paths;
|
||||
std::erase_if(gamePaths, [&](auto path) { return path == gamePath; });
|
||||
g_config.Save();
|
||||
}
|
||||
|
||||
extern "C" [[maybe_unused]] JNIEXPORT jobject JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeSettings_getGamesPaths(JNIEnv* env, [[maybe_unused]] jclass clazz)
|
||||
{
|
||||
return JNIUtils::createJavaStringArrayList(env, g_config.data().game_paths);
|
||||
}
|
||||
|
||||
extern "C" [[maybe_unused]] JNIEXPORT jboolean JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeSettings_getAsyncShaderCompile([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz)
|
||||
{
|
||||
return g_config.data().async_compile;
|
||||
}
|
||||
|
||||
extern "C" [[maybe_unused]] JNIEXPORT void JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeSettings_setAsyncShaderCompile([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jboolean enabled)
|
||||
{
|
||||
g_config.data().async_compile = enabled;
|
||||
g_config.Save();
|
||||
}
|
||||
|
||||
extern "C" [[maybe_unused]] JNIEXPORT jint JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeSettings_getVSyncMode([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz)
|
||||
{
|
||||
return g_config.data().vsync;
|
||||
}
|
||||
|
||||
extern "C" [[maybe_unused]] JNIEXPORT void JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeSettings_setVSyncMode([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jint vsync_mode)
|
||||
{
|
||||
g_config.data().vsync = vsync_mode;
|
||||
g_config.Save();
|
||||
}
|
||||
|
||||
extern "C" [[maybe_unused]] JNIEXPORT jboolean JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeSettings_getAccurateBarriers([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz)
|
||||
{
|
||||
return g_config.data().vk_accurate_barriers;
|
||||
}
|
||||
|
||||
extern "C" [[maybe_unused]] JNIEXPORT void JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeSettings_setUpscalingFilter([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jint upscaling_filter)
|
||||
{
|
||||
g_config.data().upscale_filter = upscaling_filter;
|
||||
g_config.Save();
|
||||
}
|
||||
|
||||
extern "C" [[maybe_unused]] JNIEXPORT jboolean JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeSettings_getUpscalingFilter([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz)
|
||||
{
|
||||
return g_config.data().upscale_filter;
|
||||
}
|
||||
|
||||
extern "C" [[maybe_unused]] JNIEXPORT void JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeSettings_setDownscalingFilter([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jint downscaling_filter)
|
||||
{
|
||||
g_config.data().downscale_filter = downscaling_filter;
|
||||
g_config.Save();
|
||||
}
|
||||
|
||||
extern "C" [[maybe_unused]] JNIEXPORT jboolean JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeSettings_getDownscalingFilter([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz)
|
||||
{
|
||||
return g_config.data().downscale_filter;
|
||||
}
|
||||
|
||||
extern "C" [[maybe_unused]] JNIEXPORT void JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeSettings_setFullscreenScaling([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jint fullscreen_scaling)
|
||||
{
|
||||
g_config.data().fullscreen_scaling = fullscreen_scaling;
|
||||
g_config.Save();
|
||||
}
|
||||
|
||||
extern "C" [[maybe_unused]] JNIEXPORT jboolean JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeSettings_getFullscreenScaling([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz)
|
||||
{
|
||||
return g_config.data().fullscreen_scaling;
|
||||
}
|
||||
|
||||
extern "C" [[maybe_unused]] JNIEXPORT void JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeSettings_setAccurateBarriers([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jboolean enabled)
|
||||
{
|
||||
g_config.data().vk_accurate_barriers = enabled;
|
||||
g_config.Save();
|
||||
}
|
||||
|
||||
extern "C" [[maybe_unused]] JNIEXPORT jboolean JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeSettings_getAudioDeviceEnabled([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jboolean tv)
|
||||
{
|
||||
const auto& device = tv ? g_config.data().tv_device : g_config.data().pad_device;
|
||||
return !device.empty();
|
||||
}
|
||||
|
||||
extern "C" [[maybe_unused]] JNIEXPORT void JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeSettings_setAudioDeviceEnabled([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jboolean enabled, jboolean tv)
|
||||
{
|
||||
auto& device = tv ? g_config.data().tv_device : g_config.data().pad_device;
|
||||
if (enabled)
|
||||
device = L"Default";
|
||||
else
|
||||
device.clear();
|
||||
g_config.Save();
|
||||
}
|
||||
|
||||
extern "C" [[maybe_unused]] JNIEXPORT jint JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeSettings_getAudioDeviceChannels([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jboolean tv)
|
||||
{
|
||||
const auto& deviceChannels = tv ? g_config.data().tv_channels : g_config.data().pad_channels;
|
||||
return deviceChannels;
|
||||
}
|
||||
|
||||
extern "C" [[maybe_unused]] JNIEXPORT void JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeSettings_setAudioDeviceChannels([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jint channels, jboolean tv)
|
||||
{
|
||||
auto& deviceChannels = tv ? g_config.data().tv_channels : g_config.data().pad_channels;
|
||||
deviceChannels = static_cast<AudioChannels>(channels);
|
||||
g_config.Save();
|
||||
}
|
||||
|
||||
extern "C" [[maybe_unused]] JNIEXPORT jint JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeSettings_getAudioDeviceVolume([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jboolean tv)
|
||||
{
|
||||
const auto& deviceVolume = tv ? g_config.data().tv_volume : g_config.data().pad_volume;
|
||||
return deviceVolume;
|
||||
}
|
||||
|
||||
extern "C" [[maybe_unused]] JNIEXPORT void JNICALL
|
||||
Java_info_cemu_Cemu_nativeinterface_NativeSettings_setAudioDeviceVolume([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jint volume, jboolean tv)
|
||||
{
|
||||
auto& deviceVolume = tv ? g_config.data().tv_volume : g_config.data().pad_volume;
|
||||
deviceVolume = volume;
|
||||
g_config.Save();
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
#include "Utils.h"
|
||||
|
||||
#include "Cemu/ncrypto/ncrypto.h"
|
||||
#include "config/ActiveSettings.h"
|
||||
|
||||
void createCemuDirectories()
|
||||
{
|
||||
std::wstring mlc = ActiveSettings::GetMlcPath().generic_wstring();
|
||||
|
||||
// create sys/usr folder in mlc01
|
||||
try
|
||||
{
|
||||
const auto sysFolder = fs::path(mlc).append(L"sys");
|
||||
fs::create_directories(sysFolder);
|
||||
|
||||
const auto usrFolder = fs::path(mlc).append(L"usr");
|
||||
fs::create_directories(usrFolder);
|
||||
fs::create_directories(fs::path(usrFolder).append("title/00050000")); // base
|
||||
fs::create_directories(fs::path(usrFolder).append("title/0005000c")); // dlc
|
||||
fs::create_directories(fs::path(usrFolder).append("title/0005000e")); // update
|
||||
|
||||
// Mii Maker save folders {0x500101004A000, 0x500101004A100, 0x500101004A200},
|
||||
fs::create_directories(fs::path(mlc).append(L"usr/save/00050010/1004a000/user/common/db"));
|
||||
fs::create_directories(fs::path(mlc).append(L"usr/save/00050010/1004a100/user/common/db"));
|
||||
fs::create_directories(fs::path(mlc).append(L"usr/save/00050010/1004a200/user/common/db"));
|
||||
|
||||
// lang files
|
||||
auto langDir = fs::path(mlc).append(L"sys/title/0005001b/1005c000/content");
|
||||
fs::create_directories(langDir);
|
||||
|
||||
auto langFile = fs::path(langDir).append("language.txt");
|
||||
if (!fs::exists(langFile))
|
||||
{
|
||||
std::ofstream file(langFile);
|
||||
if (file.is_open())
|
||||
{
|
||||
const char* langStrings[] = {"ja", "en", "fr", "de", "it", "es", "zh", "ko", "nl", "pt", "ru", "zh"};
|
||||
for (const char* lang : langStrings)
|
||||
file << fmt::format(R"("{}",)", lang) << std::endl;
|
||||
|
||||
file.flush();
|
||||
file.close();
|
||||
}
|
||||
}
|
||||
|
||||
auto countryFile = fs::path(langDir).append("country.txt");
|
||||
if (!fs::exists(countryFile))
|
||||
{
|
||||
std::ofstream file(countryFile);
|
||||
for (sint32 i = 0; i < 201; i++)
|
||||
{
|
||||
const char* countryCode = NCrypto::GetCountryAsString(i);
|
||||
if (boost::iequals(countryCode, "NN"))
|
||||
file << "NULL," << std::endl;
|
||||
else
|
||||
file << fmt::format(R"("{}",)", countryCode) << std::endl;
|
||||
}
|
||||
file.flush();
|
||||
file.close();
|
||||
}
|
||||
} catch (const std::exception& ex)
|
||||
{
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// cemu directories
|
||||
try
|
||||
{
|
||||
const auto controllerProfileFolder = ActiveSettings::GetConfigPath(L"controllerProfiles").generic_wstring();
|
||||
if (!fs::exists(controllerProfileFolder))
|
||||
fs::create_directories(controllerProfileFolder);
|
||||
|
||||
const auto memorySearcherFolder = ActiveSettings::GetUserDataPath(L"memorySearcher").generic_wstring();
|
||||
if (!fs::exists(memorySearcherFolder))
|
||||
fs::create_directories(memorySearcherFolder);
|
||||
} catch (const std::exception& ex)
|
||||
{
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "config/ActiveSettings.h"
|
||||
#include "Cemu/ncrypto/ncrypto.h"
|
||||
|
||||
void createCemuDirectories();
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user