1 Commits
Author SHA1 Message Date
SSimco ab53e98288 Fixed conversion from native paths 2024-09-22 07:18:29 +03:00
58 changed files with 2246 additions and 2808 deletions
-241
View File
@@ -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
+10 -11
View File
@@ -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");
+14 -6
View File
@@ -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();
}
+80
View File
@@ -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);
}
}
+6
View File
@@ -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