Merge branch 'master' into open-bp-cpp

This commit is contained in:
sylvieee-iot 2024-12-31 16:28:40 +02:00 committed by GitHub
commit 606158fb27
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
289 changed files with 18600 additions and 1763 deletions

315
.github/gles.patch vendored
View file

@ -1,76 +1,5 @@
diff --git a/src/core/renderer_gl/renderer_gl.cpp b/src/core/renderer_gl/renderer_gl.cpp
index a11a6ffa..77486a09 100644
--- a/src/core/renderer_gl/renderer_gl.cpp
+++ b/src/core/renderer_gl/renderer_gl.cpp
@@ -357,27 +357,27 @@ void RendererGL::bindTexturesToSlots() {
}
glActiveTexture(GL_TEXTURE0 + 3);
- glBindTexture(GL_TEXTURE_1D_ARRAY, lightLUTTextureArray);
+ // glBindTexture(GL_TEXTURE_1D_ARRAY, lightLUTTextureArray);
glActiveTexture(GL_TEXTURE0);
}
void RendererGL::updateLightingLUT() {
- gpu.lightingLUTDirty = false;
- std::array<u16, GPU::LightingLutSize> u16_lightinglut;
-
- for (int i = 0; i < gpu.lightingLUT.size(); i++) {
- uint64_t value = gpu.lightingLUT[i] & ((1 << 12) - 1);
- u16_lightinglut[i] = value * 65535 / 4095;
- }
-
- glActiveTexture(GL_TEXTURE0 + 3);
- glBindTexture(GL_TEXTURE_1D_ARRAY, lightLUTTextureArray);
- glTexImage2D(GL_TEXTURE_1D_ARRAY, 0, GL_R16, 256, Lights::LUT_Count, 0, GL_RED, GL_UNSIGNED_SHORT, u16_lightinglut.data());
- glTexParameteri(GL_TEXTURE_1D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_1D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_1D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_1D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- glActiveTexture(GL_TEXTURE0);
+ // gpu.lightingLUTDirty = false;
+ // std::array<u16, GPU::LightingLutSize> u16_lightinglut;
+
+ // for (int i = 0; i < gpu.lightingLUT.size(); i++) {
+ // uint64_t value = gpu.lightingLUT[i] & ((1 << 12) - 1);
+ // u16_lightinglut[i] = value * 65535 / 4095;
+ // }
+
+ // glActiveTexture(GL_TEXTURE0 + 3);
+ // glBindTexture(GL_TEXTURE_1D_ARRAY, lightLUTTextureArray);
+ // glTexImage2D(GL_TEXTURE_1D_ARRAY, 0, GL_R16, 256, Lights::LUT_Count, 0, GL_RED, GL_UNSIGNED_SHORT, u16_lightinglut.data());
+ // glTexParameteri(GL_TEXTURE_1D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ // glTexParameteri(GL_TEXTURE_1D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ // glTexParameteri(GL_TEXTURE_1D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ // glTexParameteri(GL_TEXTURE_1D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ // glActiveTexture(GL_TEXTURE0);
}
void RendererGL::drawVertices(PICA::PrimType primType, std::span<const Vertex> vertices) {
diff --git a/src/host_shaders/opengl_display.frag b/src/host_shaders/opengl_display.frag
index 612671c8..1937f711 100644
--- a/src/host_shaders/opengl_display.frag
+++ b/src/host_shaders/opengl_display.frag
@@ -1,4 +1,5 @@
-#version 410 core
+#version 300 es
+precision mediump float;
in vec2 UV;
out vec4 FragColor;
diff --git a/src/host_shaders/opengl_display.vert b/src/host_shaders/opengl_display.vert
index 990e2f80..2e7842ac 100644
--- a/src/host_shaders/opengl_display.vert
+++ b/src/host_shaders/opengl_display.vert
@@ -1,4 +1,5 @@
-#version 410 core
+#version 300 es
+precision mediump float;
out vec2 UV;
void main() {
diff --git a/src/host_shaders/opengl_fragment_shader.frag b/src/host_shaders/opengl_fragment_shader.frag
index f6fa6c55..bb88e278 100644
index 9f07df0b..96a35afa 100644
--- a/src/host_shaders/opengl_fragment_shader.frag
+++ b/src/host_shaders/opengl_fragment_shader.frag
@@ -1,4 +1,5 @@
@ -78,36 +7,29 @@ index f6fa6c55..bb88e278 100644
+#version 300 es
+precision mediump float;
in vec3 v_tangent;
in vec3 v_normal;
@@ -27,7 +28,7 @@ uniform bool u_depthmapEnable;
uniform sampler2D u_tex0;
uniform sampler2D u_tex1;
uniform sampler2D u_tex2;
-uniform sampler1DArray u_tex_lighting_lut;
+// uniform sampler1DArray u_tex_lighting_lut;
in vec4 v_quaternion;
in vec4 v_colour;
@@ -41,8 +42,8 @@ vec3 normal;
const uint samplerEnabledBitfields[2] = uint[2](0x7170e645u, 0x7f013fefu);
uniform uint u_picaRegs[0x200 - 0x48];
bool isSamplerEnabled(uint environment_id, uint lut_id) {
- uint index = 7 * environment_id + lut_id;
- uint arrayIndex = (index >> 5);
+ uint index = 7u * environment_id + lut_id;
+ uint arrayIndex = (index >> 5u);
return (samplerEnabledBitfields[arrayIndex] & (1u << (index & 31u))) != 0u;
}
@@ -145,16 +146,23 @@ vec4 tevCalculateCombiner(int tev_id) {
#define RR_LUT 6u
@@ -166,11 +167,17 @@ float lutLookup(uint lut, int index) {
return texelFetch(u_tex_luts, ivec2(index, int(lut)), 0).r;
}
float lutLookup(uint lut, uint light, float value) {
- if (lut >= FR_LUT && lut <= RR_LUT) lut -= 1;
- if (lut == SP_LUT) lut = light + 8;
- return texture(u_tex_lighting_lut, vec2(value, lut)).r;
+ // if (lut >= FR_LUT && lut <= RR_LUT) lut -= 1;
+ // if (lut == SP_LUT) lut = light + 8;
+ // return texture(u_tex_lighting_lut, vec2(value, lut)).r;
+ return 0.0;
+}
+
+// some gles versions have bitfieldExtract and complain if you redefine it, some don't and compile error, using this instead
+// some gles versions have bitfieldExtractCompat and complain if you redefine it, some don't and compile error, using this instead
+uint bitfieldExtractCompat(uint val, int off, int size) {
+ uint mask = uint((1 << size) - 1);
+ return uint(val >> off) & mask;
}
+}
+
vec3 regToColor(uint reg) {
// Normalization scale to convert from [0...255] to [0.0...1.0]
const float scale = 1.0 / 255.0;
@ -117,89 +39,115 @@ index f6fa6c55..bb88e278 100644
}
// Convert an arbitrary-width floating point literal to an f32
@@ -189,7 +197,7 @@ void calcLighting(out vec4 primary_color, out vec4 secondary_color) {
vec3 view = normalize(v_view);
@@ -201,7 +208,7 @@ float lightLutLookup(uint environment_id, uint lut_id, uint light_id, vec3 light
// These are the spotlight attenuation LUTs
bit_in_config1 = 8 + int(light_id & 7u);
lut_index = 8u + light_id;
- } else if (lut_id <= 6) {
+ } else if (lut_id <= 6u) {
bit_in_config1 = 16 + int(lut_id);
lut_index = lut_id;
} else {
@@ -210,16 +217,16 @@ float lightLutLookup(uint environment_id, uint lut_id, uint light_id, vec3 light
bool current_sampler_enabled = isSamplerEnabled(environment_id, lut_id); // 7 luts per environment
- if (!current_sampler_enabled || (bitfieldExtract(GPUREG_LIGHTING_CONFIG1, bit_in_config1, 1) != 0u)) {
+ if (!current_sampler_enabled || (bitfieldExtractCompat(GPUREG_LIGHTING_CONFIG1, bit_in_config1, 1) != 0u)) {
return 1.0;
}
- uint scale_id = bitfieldExtract(GPUREG_LIGHTING_LUTINPUT_SCALE, int(lut_id) << 2, 3);
+ uint scale_id = bitfieldExtractCompat(GPUREG_LIGHTING_LUTINPUT_SCALE, int(lut_id) << 2, 3);
float scale = float(1u << scale_id);
if (scale_id >= 6u) scale /= 256.0;
float delta = 1.0;
- uint input_id = bitfieldExtract(GPUREG_LIGHTING_LUTINPUT_SELECT, int(lut_id) << 2, 3);
+ uint input_id = bitfieldExtractCompat(GPUREG_LIGHTING_LUTINPUT_SELECT, int(lut_id) << 2, 3);
switch (input_id) {
case 0u: {
delta = dot(normal, normalize(half_vector));
@@ -243,9 +250,9 @@ float lightLutLookup(uint environment_id, uint lut_id, uint light_id, vec3 light
// Sign extend them. Normally bitfieldExtract would do that but it's missing on some versions
// of GLSL so we do it manually
- int se_x = bitfieldExtract(GPUREG_LIGHTi_SPOTDIR_LOW, 0, 13);
- int se_y = bitfieldExtract(GPUREG_LIGHTi_SPOTDIR_LOW, 16, 13);
- int se_z = bitfieldExtract(GPUREG_LIGHTi_SPOTDIR_HIGH, 0, 13);
+ int se_x = int(bitfieldExtractCompat(uint(GPUREG_LIGHTi_SPOTDIR_LOW), 0, 13));
+ int se_y = int(bitfieldExtractCompat(uint(GPUREG_LIGHTi_SPOTDIR_LOW), 16, 13));
+ int se_z = int(bitfieldExtractCompat(uint(GPUREG_LIGHTi_SPOTDIR_HIGH), 0, 13));
if ((se_x & 0x1000) == 0x1000) se_x |= 0xffffe000;
if ((se_y & 0x1000) == 0x1000) se_y |= 0xffffe000;
@@ -272,9 +279,9 @@ float lightLutLookup(uint environment_id, uint lut_id, uint light_id, vec3 light
}
// 0 = enabled
- if (bitfieldExtract(GPUREG_LIGHTING_LUTINPUT_ABS, 1 + (int(lut_id) << 2), 1) == 0u) {
+ if (bitfieldExtractCompat(GPUREG_LIGHTING_LUTINPUT_ABS, 1 + (int(lut_id) << 2), 1) == 0u) {
// Two sided diffuse
- if (bitfieldExtract(GPUREG_LIGHTi_CONFIG, 1, 1) == 0u) {
+ if (bitfieldExtractCompat(GPUREG_LIGHTi_CONFIG, 1, 1) == 0u) {
delta = max(delta, 0.0);
} else {
delta = abs(delta);
@@ -298,7 +305,7 @@ vec3 rotateVec3ByQuaternion(vec3 v, vec4 q) {
// Implements the following algorthm: https://mathb.in/26766
void calcLighting(out vec4 primary_color, out vec4 secondary_color) {
uint GPUREG_LIGHTING_ENABLE = readPicaReg(0x008Fu);
- if (bitfieldExtract(GPUREG_LIGHTING_ENABLE, 0, 1) == 0u) {
+ if (bitfieldExtractCompat(GPUREG_LIGHTING_ENABLE, 0, 1) == 0u) {
primary_color = secondary_color = vec4(1.0);
primary_color = secondary_color = vec4(0.0);
return;
}
@@ -213,7 +221,7 @@ void calcLighting(out vec4 primary_color, out vec4 secondary_color) {
bool error_unimpl = false;
@@ -315,7 +322,7 @@ void calcLighting(out vec4 primary_color, out vec4 secondary_color) {
GPUREG_LIGHTING_LUTINPUT_ABS = readPicaReg(0x01D0u);
GPUREG_LIGHTING_LUTINPUT_SELECT = readPicaReg(0x01D1u);
- uint bump_mode = bitfieldExtract(GPUREG_LIGHTING_CONFIG0, 28, 2);
+ uint bump_mode = bitfieldExtractCompat(GPUREG_LIGHTING_CONFIG0, 28, 2);
// Bump mode is ignored for now because it breaks some games ie. Toad Treasure Tracker
switch (bump_mode) {
@@ -328,15 +335,15 @@ void calcLighting(out vec4 primary_color, out vec4 secondary_color) {
vec4 diffuse_sum = vec4(0.0, 0.0, 0.0, 1.0);
vec4 specular_sum = vec4(0.0, 0.0, 0.0, 1.0);
- uint environment_id = bitfieldExtract(GPUREG_LIGHTING_CONFIG0, 4, 4);
- bool clamp_highlights = bitfieldExtract(GPUREG_LIGHTING_CONFIG0, 27, 1) == 1u;
+ uint environment_id = bitfieldExtractCompat(GPUREG_LIGHTING_CONFIG0, 4, 4);
+ bool clamp_highlights = bitfieldExtractCompat(GPUREG_LIGHTING_CONFIG0, 27, 1) == 1u;
uint light_id;
vec3 light_vector;
vec3 half_vector;
for (uint i = 0u; i < GPUREG_LIGHTING_NUM_LIGHTS; i++) {
- uint light_id = bitfieldExtract(GPUREG_LIGHTING_LIGHT_PERMUTATION, int(i * 3u), 3);
+ uint light_id = bitfieldExtractCompat(GPUREG_LIGHTING_LIGHT_PERMUTATION, int(i * 3u), 3);
- light_id = bitfieldExtract(GPUREG_LIGHTING_LIGHT_PERMUTATION, int(i) << 2, 3);
+ light_id = bitfieldExtractCompat(GPUREG_LIGHTING_LIGHT_PERMUTATION, int(i) << 2, 3);
uint GPUREG_LIGHTi_SPECULAR0 = readPicaReg(0x0140u + 0x10u * light_id);
uint GPUREG_LIGHTi_SPECULAR1 = readPicaReg(0x0141u + 0x10u * light_id);
@@ -224,14 +232,14 @@ void calcLighting(out vec4 primary_color, out vec4 secondary_color) {
uint GPUREG_LIGHTi_CONFIG = readPicaReg(0x0149u + 0x10u * light_id);
uint GPUREG_LIGHTi_SPECULAR0 = readPicaReg(0x0140u + (light_id << 4u));
uint GPUREG_LIGHTi_SPECULAR1 = readPicaReg(0x0141u + (light_id << 4u));
@@ -348,12 +355,12 @@ void calcLighting(out vec4 primary_color, out vec4 secondary_color) {
vec3 light_vector = normalize(vec3(
float light_distance;
vec3 light_position = vec3(
- decodeFP(bitfieldExtract(GPUREG_LIGHTi_VECTOR_LOW, 0, 16), 5u, 10u), decodeFP(bitfieldExtract(GPUREG_LIGHTi_VECTOR_LOW, 16, 16), 5u, 10u),
- decodeFP(bitfieldExtract(GPUREG_LIGHTi_VECTOR_HIGH, 0, 16), 5u, 10u)
+ decodeFP(bitfieldExtractCompat(GPUREG_LIGHTi_VECTOR_LOW, 0, 16), 5u, 10u), decodeFP(bitfieldExtractCompat(GPUREG_LIGHTi_VECTOR_LOW, 16, 16), 5u, 10u),
+ decodeFP(bitfieldExtractCompat(GPUREG_LIGHTi_VECTOR_HIGH, 0, 16), 5u, 10u)
));
vec3 half_vector;
);
// Positional Light
- if (bitfieldExtract(GPUREG_LIGHTi_CONFIG, 0, 1) == 0u) {
+ if (bitfieldExtractCompat(GPUREG_LIGHTi_CONFIG, 0, 1) == 0u) {
// error_unimpl = true;
half_vector = normalize(normalize(light_vector + v_view) + view);
}
@@ -242,12 +250,12 @@ void calcLighting(out vec4 primary_color, out vec4 secondary_color) {
light_vector = light_position + v_view;
}
for (int c = 0; c < 7; c++) {
- if (bitfieldExtract(GPUREG_LIGHTING_CONFIG1, 16 + c, 1) == 0u) {
- uint scale_id = bitfieldExtract(GPUREG_LIGHTING_LUTINPUT_SCALE, c * 4, 3);
+ if (bitfieldExtractCompat(GPUREG_LIGHTING_CONFIG1, 16 + c, 1) == 0u) {
+ uint scale_id = bitfieldExtractCompat(GPUREG_LIGHTING_LUTINPUT_SCALE, c * 4, 3);
float scale = float(1u << scale_id);
if (scale_id >= 6u) scale /= 256.0;
- uint input_id = bitfieldExtract(GPUREG_LIGHTING_LUTINPUT_SELECT, c * 4, 3);
+ uint input_id = bitfieldExtractCompat(GPUREG_LIGHTING_LUTINPUT_SELECT, c * 4, 3);
if (input_id == 0u)
d[c] = dot(normal, half_vector);
else if (input_id == 1u)
@@ -260,9 +268,9 @@ void calcLighting(out vec4 primary_color, out vec4 secondary_color) {
uint GPUREG_LIGHTi_SPOTDIR_LOW = readPicaReg(0x0146u + 0x10u * light_id);
uint GPUREG_LIGHTi_SPOTDIR_HIGH = readPicaReg(0x0147u + 0x10u * light_id);
vec3 spot_light_vector = normalize(vec3(
- decodeFP(bitfieldExtract(GPUREG_LIGHTi_SPOTDIR_LOW, 0, 16), 1u, 11u),
- decodeFP(bitfieldExtract(GPUREG_LIGHTi_SPOTDIR_LOW, 16, 16), 1u, 11u),
- decodeFP(bitfieldExtract(GPUREG_LIGHTi_SPOTDIR_HIGH, 0, 16), 1u, 11u)
+ decodeFP(bitfieldExtractCompat(GPUREG_LIGHTi_SPOTDIR_LOW, 0, 16), 1u, 11u),
+ decodeFP(bitfieldExtractCompat(GPUREG_LIGHTi_SPOTDIR_LOW, 16, 16), 1u, 11u),
+ decodeFP(bitfieldExtractCompat(GPUREG_LIGHTi_SPOTDIR_HIGH, 0, 16), 1u, 11u)
));
d[c] = dot(-light_vector, spot_light_vector); // -L dot P (aka Spotlight aka SP);
} else if (input_id == 5u) {
@@ -273,13 +281,13 @@ void calcLighting(out vec4 primary_color, out vec4 secondary_color) {
}
d[c] = lutLookup(uint(c), light_id, d[c] * 0.5 + 0.5) * scale;
- if (bitfieldExtract(GPUREG_LIGHTING_LUTINPUT_ABS, 2 * c, 1) != 0u) d[c] = abs(d[c]);
+ if (bitfieldExtractCompat(GPUREG_LIGHTING_LUTINPUT_ABS, 2 * c, 1) != 0u) d[c] = abs(d[c]);
} else {
d[c] = 1.0;
}
}
- uint lookup_config = bitfieldExtract(GPUREG_LIGHTi_CONFIG, 4, 4);
+ uint lookup_config = bitfieldExtractCompat(GPUREG_LIGHTi_CONFIG, 4, 4);
if (lookup_config == 0u) {
d[D1_LUT] = 0.0;
d[FR_LUT] = 0.0;
@@ -310,7 +318,7 @@ void calcLighting(out vec4 primary_color, out vec4 secondary_color) {
float NdotL = dot(normal, light_vector); // Li dot N
@@ -369,23 +376,23 @@ void calcLighting(out vec4 primary_color, out vec4 secondary_color) {
float NdotL = dot(normal, light_vector); // N dot Li
// Two sided diffuse
- if (bitfieldExtract(GPUREG_LIGHTi_CONFIG, 1, 1) == 0u)
@ -207,19 +155,40 @@ index f6fa6c55..bb88e278 100644
NdotL = max(0.0, NdotL);
else
NdotL = abs(NdotL);
@@ -321,8 +329,8 @@ void calcLighting(out vec4 primary_color, out vec4 secondary_color) {
secondary_color.rgb += light_factor * (regToColor(GPUREG_LIGHTi_SPECULAR0) * d[D0_LUT] +
regToColor(GPUREG_LIGHTi_SPECULAR1) * d[D1_LUT] * vec3(d[RR_LUT], d[RG_LUT], d[RB_LUT]));
float geometric_factor;
- bool use_geo_0 = bitfieldExtract(GPUREG_LIGHTi_CONFIG, 2, 1) == 1u;
- bool use_geo_1 = bitfieldExtract(GPUREG_LIGHTi_CONFIG, 3, 1) == 1u;
+ bool use_geo_0 = bitfieldExtractCompat(GPUREG_LIGHTi_CONFIG, 2, 1) == 1u;
+ bool use_geo_1 = bitfieldExtractCompat(GPUREG_LIGHTi_CONFIG, 3, 1) == 1u;
if (use_geo_0 || use_geo_1) {
geometric_factor = dot(half_vector, half_vector);
geometric_factor = geometric_factor == 0.0 ? 0.0 : min(NdotL / geometric_factor, 1.0);
}
float distance_attenuation = 1.0;
- if (bitfieldExtract(GPUREG_LIGHTING_CONFIG1, 24 + int(light_id), 1) == 0u) {
- uint GPUREG_LIGHTi_ATTENUATION_BIAS = bitfieldExtract(readPicaReg(0x014Au + (light_id << 4u)), 0, 20);
- uint GPUREG_LIGHTi_ATTENUATION_SCALE = bitfieldExtract(readPicaReg(0x014Bu + (light_id << 4u)), 0, 20);
+ if (bitfieldExtractCompat(GPUREG_LIGHTING_CONFIG1, 24 + int(light_id), 1) == 0u) {
+ uint GPUREG_LIGHTi_ATTENUATION_BIAS = bitfieldExtractCompat(readPicaReg(0x014Au + (light_id << 4u)), 0, 20);
+ uint GPUREG_LIGHTi_ATTENUATION_SCALE = bitfieldExtractCompat(readPicaReg(0x014Bu + (light_id << 4u)), 0, 20);
float distance_attenuation_bias = decodeFP(GPUREG_LIGHTi_ATTENUATION_BIAS, 7u, 12u);
float distance_attenuation_scale = decodeFP(GPUREG_LIGHTi_ATTENUATION_SCALE, 7u, 12u);
@@ -430,8 +437,8 @@ void calcLighting(out vec4 primary_color, out vec4 secondary_color) {
specular_sum.rgb += light_factor * clamp_factor * (specular0 + specular1);
}
- uint fresnel_output1 = bitfieldExtract(GPUREG_LIGHTING_CONFIG0, 2, 1);
- uint fresnel_output2 = bitfieldExtract(GPUREG_LIGHTING_CONFIG0, 3, 1);
+ uint fresnel_output1 = bitfieldExtractCompat(GPUREG_LIGHTING_CONFIG0, 2, 1);
+ uint fresnel_output2 = bitfieldExtractCompat(GPUREG_LIGHTING_CONFIG0, 3, 1);
if (fresnel_output1 == 1u) primary_color.a = d[FR_LUT];
if (fresnel_output2 == 1u) secondary_color.a = d[FR_LUT];
// Uses parameters from the last light as Fresnel is only applied to the last light
float fresnel_factor;
diff --git a/src/host_shaders/opengl_vertex_shader.vert b/src/host_shaders/opengl_vertex_shader.vert
index a25d7a6d..7cf40398 100644
index 057f9a88..dc735ced 100644
--- a/src/host_shaders/opengl_vertex_shader.vert
+++ b/src/host_shaders/opengl_vertex_shader.vert
@@ -1,4 +1,6 @@
@ -230,7 +199,7 @@ index a25d7a6d..7cf40398 100644
layout(location = 0) in vec4 a_coords;
layout(location = 1) in vec4 a_quaternion;
@@ -20,7 +22,7 @@ out vec2 v_texcoord2;
@@ -18,7 +20,7 @@ out vec2 v_texcoord2;
flat out vec4 v_textureEnvColor[6];
flat out vec4 v_textureEnvBufferColor;
@ -239,7 +208,7 @@ index a25d7a6d..7cf40398 100644
// TEV uniforms
uniform uint u_textureEnvColor[6];
@@ -93,6 +95,6 @@ void main() {
@@ -81,8 +83,8 @@ void main() {
);
// There's also another, always-on clipping plane based on vertex z
@ -247,16 +216,20 @@ index a25d7a6d..7cf40398 100644
- gl_ClipDistance[1] = dot(clipData, a_coords);
+ // gl_ClipDistance[0] = -a_coords.z;
+ // gl_ClipDistance[1] = dot(clipData, a_coords);
v_quaternion = a_quaternion;
}
diff --git a/third_party/opengl/opengl.hpp b/third_party/opengl/opengl.hpp
index f368f573..5ead7f63 100644
index 607815fa..cbfcc096 100644
--- a/third_party/opengl/opengl.hpp
+++ b/third_party/opengl/opengl.hpp
@@ -520,21 +520,21 @@ namespace OpenGL {
@@ -602,22 +602,22 @@ namespace OpenGL {
static void disableScissor() { glDisable(GL_SCISSOR_TEST); }
static void enableBlend() { glEnable(GL_BLEND); }
static void disableBlend() { glDisable(GL_BLEND); }
static void enableLogicOp() { glEnable(GL_COLOR_LOGIC_OP); }
- static void enableLogicOp() { glEnable(GL_COLOR_LOGIC_OP); }
- static void disableLogicOp() { glDisable(GL_COLOR_LOGIC_OP); }
+ static void enableLogicOp() { /* glEnable(GL_COLOR_LOGIC_OP); */ }
+ static void disableLogicOp() { /* glDisable(GL_COLOR_LOGIC_OP); */ }
static void enableDepth() { glEnable(GL_DEPTH_TEST); }
static void disableDepth() { glDisable(GL_DEPTH_TEST); }

View file

@ -5,16 +5,16 @@ PATH="$PATH:/usr/libexec"
# Construct the app iconset.
mkdir alber.iconset
convert docs/img/alber-icon.ico -alpha on -background none -units PixelsPerInch -density 72 -resize 16x16 alber.iconset/icon_16x16.png
convert docs/img/alber-icon.ico -alpha on -background none -units PixelsPerInch -density 144 -resize 32x32 alber.iconset/icon_16x16@2x.png
convert docs/img/alber-icon.ico -alpha on -background none -units PixelsPerInch -density 72 -resize 32x32 alber.iconset/icon_32x32.png
convert docs/img/alber-icon.ico -alpha on -background none -units PixelsPerInch -density 144 -resize 64x64 alber.iconset/icon_32x32@2x.png
convert docs/img/alber-icon.ico -alpha on -background none -units PixelsPerInch -density 72 -resize 128x128 alber.iconset/icon_128x128.png
convert docs/img/alber-icon.ico -alpha on -background none -units PixelsPerInch -density 144 -resize 256x256 alber.iconset/icon_128x128@2x.png
convert docs/img/alber-icon.ico -alpha on -background none -units PixelsPerInch -density 72 -resize 256x256 alber.iconset/icon_256x256.png
convert docs/img/alber-icon.ico -alpha on -background none -units PixelsPerInch -density 144 -resize 512x512 alber.iconset/icon_256x256@2x.png
convert docs/img/alber-icon.ico -alpha on -background none -units PixelsPerInch -density 72 -resize 512x512 alber.iconset/icon_512x512.png
convert docs/img/alber-icon.ico -alpha on -background none -units PixelsPerInch -density 144 -resize 1024x1024 alber.iconset/icon_512x512@2x.png
convert docs/img/mac_icon.ico -alpha on -background none -units PixelsPerInch -density 72 -resize 16x16 alber.iconset/icon_16x16.png
convert docs/img/mac_icon.ico -alpha on -background none -units PixelsPerInch -density 144 -resize 32x32 alber.iconset/icon_16x16@2x.png
convert docs/img/mac_icon.ico -alpha on -background none -units PixelsPerInch -density 72 -resize 32x32 alber.iconset/icon_32x32.png
convert docs/img/mac_icon.ico -alpha on -background none -units PixelsPerInch -density 144 -resize 64x64 alber.iconset/icon_32x32@2x.png
convert docs/img/mac_icon.ico -alpha on -background none -units PixelsPerInch -density 72 -resize 128x128 alber.iconset/icon_128x128.png
convert docs/img/mac_icon.ico -alpha on -background none -units PixelsPerInch -density 144 -resize 256x256 alber.iconset/icon_128x128@2x.png
convert docs/img/mac_icon.ico -alpha on -background none -units PixelsPerInch -density 72 -resize 256x256 alber.iconset/icon_256x256.png
convert docs/img/mac_icon.ico -alpha on -background none -units PixelsPerInch -density 144 -resize 512x512 alber.iconset/icon_256x256@2x.png
convert docs/img/mac_icon.ico -alpha on -background none -units PixelsPerInch -density 72 -resize 512x512 alber.iconset/icon_512x512.png
convert docs/img/mac_icon.ico -alpha on -background none -units PixelsPerInch -density 144 -resize 1024x1024 alber.iconset/icon_512x512@2x.png
iconutil --convert icns alber.iconset
# Set up the .app directory

20
.github/mac-bundle.sh vendored
View file

@ -5,16 +5,16 @@ PATH="$PATH:/usr/libexec"
# Construct the app iconset.
mkdir alber.iconset
convert docs/img/alber-icon.ico -alpha on -background none -units PixelsPerInch -density 72 -resize 16x16 alber.iconset/icon_16x16.png
convert docs/img/alber-icon.ico -alpha on -background none -units PixelsPerInch -density 144 -resize 32x32 alber.iconset/icon_16x16@2x.png
convert docs/img/alber-icon.ico -alpha on -background none -units PixelsPerInch -density 72 -resize 32x32 alber.iconset/icon_32x32.png
convert docs/img/alber-icon.ico -alpha on -background none -units PixelsPerInch -density 144 -resize 64x64 alber.iconset/icon_32x32@2x.png
convert docs/img/alber-icon.ico -alpha on -background none -units PixelsPerInch -density 72 -resize 128x128 alber.iconset/icon_128x128.png
convert docs/img/alber-icon.ico -alpha on -background none -units PixelsPerInch -density 144 -resize 256x256 alber.iconset/icon_128x128@2x.png
convert docs/img/alber-icon.ico -alpha on -background none -units PixelsPerInch -density 72 -resize 256x256 alber.iconset/icon_256x256.png
convert docs/img/alber-icon.ico -alpha on -background none -units PixelsPerInch -density 144 -resize 512x512 alber.iconset/icon_256x256@2x.png
convert docs/img/alber-icon.ico -alpha on -background none -units PixelsPerInch -density 72 -resize 512x512 alber.iconset/icon_512x512.png
convert docs/img/alber-icon.ico -alpha on -background none -units PixelsPerInch -density 144 -resize 1024x1024 alber.iconset/icon_512x512@2x.png
convert docs/img/mac_icon.ico -alpha on -background none -units PixelsPerInch -density 72 -resize 16x16 alber.iconset/icon_16x16.png
convert docs/img/mac_icon.ico -alpha on -background none -units PixelsPerInch -density 144 -resize 32x32 alber.iconset/icon_16x16@2x.png
convert docs/img/mac_icon.ico -alpha on -background none -units PixelsPerInch -density 72 -resize 32x32 alber.iconset/icon_32x32.png
convert docs/img/mac_icon.ico -alpha on -background none -units PixelsPerInch -density 144 -resize 64x64 alber.iconset/icon_32x32@2x.png
convert docs/img/mac_icon.ico -alpha on -background none -units PixelsPerInch -density 72 -resize 128x128 alber.iconset/icon_128x128.png
convert docs/img/mac_icon.ico -alpha on -background none -units PixelsPerInch -density 144 -resize 256x256 alber.iconset/icon_128x128@2x.png
convert docs/img/mac_icon.ico -alpha on -background none -units PixelsPerInch -density 72 -resize 256x256 alber.iconset/icon_256x256.png
convert docs/img/mac_icon.ico -alpha on -background none -units PixelsPerInch -density 144 -resize 512x512 alber.iconset/icon_256x256@2x.png
convert docs/img/mac_icon.ico -alpha on -background none -units PixelsPerInch -density 72 -resize 512x512 alber.iconset/icon_512x512.png
convert docs/img/mac_icon.ico -alpha on -background none -units PixelsPerInch -density 144 -resize 1024x1024 alber.iconset/icon_512x512@2x.png
iconutil --convert icns alber.iconset
# Set up the .app directory

View file

@ -8,7 +8,7 @@ on:
jobs:
x64:
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
strategy:
matrix:
@ -23,6 +23,9 @@ jobs:
- name: Fetch submodules
run: git submodule update --init --recursive
- name: Setup CCache
uses: hendrikmuhs/ccache-action@v1.2
- name: Set up gradle caches
uses: actions/cache@v4
with:
@ -47,7 +50,7 @@ jobs:
java-version: '17'
- name: Configure CMake
run: cmake -B ${{github.workspace}}/build -DBUILD_HYDRA_CORE=1 -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_TOOLCHAIN_FILE=${ANDROID_NDK_ROOT}/build/cmake/android.toolchain.cmake -DANDROID_ABI=x86_64 -DENABLE_VULKAN=0 -DENABLE_USER_BUILD=ON
run: cmake -B ${{github.workspace}}/build -DBUILD_HYDRA_CORE=1 -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_TOOLCHAIN_FILE=${ANDROID_NDK_ROOT}/build/cmake/android.toolchain.cmake -DANDROID_ABI=x86_64 -DENABLE_VULKAN=0 -DENABLE_USER_BUILD=ON -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
- name: Build
run: |
@ -73,7 +76,7 @@ jobs:
./src/pandroid/app/build/outputs/apk/${{ env.BUILD_TYPE }}/app-${{ env.BUILD_TYPE }}.apk
arm64:
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
strategy:
matrix:
@ -88,6 +91,9 @@ jobs:
- name: Fetch submodules
run: git submodule update --init --recursive
- name: Setup CCache
uses: hendrikmuhs/ccache-action@v1.2
- name: Set up gradle caches
uses: actions/cache@v4
with:
@ -112,7 +118,7 @@ jobs:
java-version: '17'
- name: Configure CMake
run: cmake -B ${{github.workspace}}/build -DBUILD_HYDRA_CORE=1 -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_TOOLCHAIN_FILE=${ANDROID_NDK_ROOT}/build/cmake/android.toolchain.cmake -DANDROID_ABI=arm64-v8a -DENABLE_VULKAN=0 -DENABLE_USER_BUILD=ON -DCMAKE_CXX_FLAGS="-march=armv8-a+crypto"
run: cmake -B ${{github.workspace}}/build -DBUILD_HYDRA_CORE=1 -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_TOOLCHAIN_FILE=${ANDROID_NDK_ROOT}/build/cmake/android.toolchain.cmake -DANDROID_ABI=arm64-v8a -DENABLE_VULKAN=0 -DENABLE_USER_BUILD=ON -DCMAKE_CXX_FLAGS="-march=armv8-a+crypto" -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
- name: Build
run: |

View file

@ -16,10 +16,10 @@ jobs:
# well on Windows or Mac. You can convert this to a matrix build if you need
# cross-platform coverage.
# See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Fetch submodules
run: git submodule update --init --recursive

View file

@ -15,7 +15,7 @@ jobs:
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Fetch submodules
run: git submodule update --init --recursive
@ -51,14 +51,14 @@ jobs:
with:
name: Windows Libretro core
path: |
${{github.workspace}}/build/panda3ds_libretro.dll
${{github.workspace}}/build/${{ env.BUILD_TYPE }}/panda3ds_libretro.dll
${{github.workspace}}/docs/libretro/panda3ds_libretro.info
MacOS:
runs-on: macos-13
runs-on: macos-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Fetch submodules
run: git submodule update --init --recursive
@ -70,7 +70,7 @@ jobs:
vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang
- name: Configure CMake
run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DENABLE_USER_BUILD=ON -DBUILD_HYDRA_CORE=ON
run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DENABLE_USER_BUILD=ON -DBUILD_HYDRA_CORE=ON -DCMAKE_OSX_ARCHITECTURE=x86_64
- name: Build
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
@ -84,7 +84,7 @@ jobs:
- name: Configure CMake (Again)
run: |
rm -rf ${{github.workspace}}/build
cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DENABLE_USER_BUILD=ON -DBUILD_LIBRETRO_CORE=ON
cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DENABLE_USER_BUILD=ON -DBUILD_LIBRETRO_CORE=ON -DCMAKE_OSX_ARCHITECTURE=x86_64
- name: Build (Again)
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} && ls -R ${{github.workspace}}/build
@ -98,16 +98,16 @@ jobs:
${{github.workspace}}/docs/libretro/panda3ds_libretro.info
Linux:
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Fetch submodules
run: git submodule update --init --recursive
- name: Install misc packages
run: |
sudo apt-get update && sudo apt install libx11-dev libgl1-mesa-glx mesa-common-dev libfuse2 libwayland-dev
sudo apt-get update && sudo apt install libx11-dev libgl1 libglx-mesa0 mesa-common-dev libfuse2 libwayland-dev
- name: Install newer Clang
run: |
@ -151,16 +151,16 @@ jobs:
${{github.workspace}}/docs/libretro/panda3ds_libretro.info
Android-x64:
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Fetch submodules
run: git submodule update --init --recursive
- name: Install misc packages
run: |
sudo apt-get update && sudo apt install libx11-dev libgl1-mesa-glx mesa-common-dev libfuse2 libwayland-dev
sudo apt-get update && sudo apt install libx11-dev libgl1 libglx-mesa0 mesa-common-dev libfuse2 libwayland-dev
- name: Setup Vulkan SDK
uses: humbletim/setup-vulkan-sdk@v1.2.0

View file

@ -16,15 +16,15 @@ jobs:
# well on Windows or Mac. You can convert this to a matrix build if you need
# cross-platform coverage.
# See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix
runs-on: ubuntu-20.04
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Fetch submodules
run: git submodule update --init --recursive
- name: Install misc packages
run: sudo apt-get update && sudo apt install libx11-dev libgl1-mesa-glx mesa-common-dev libfuse2
run: sudo apt-get update && sudo apt install libx11-dev libgl1 libglx-mesa0 mesa-common-dev libfuse2 libwayland-dev
- name: Install newer Clang
run: |
@ -33,11 +33,11 @@ jobs:
sudo ./llvm.sh 17
- name: Setup Vulkan SDK
run: |
wget -qO - http://packages.lunarg.com/lunarg-signing-key-pub.asc | sudo apt-key add -
sudo wget -qO /etc/apt/sources.list.d/lunarg-vulkan-focal.list http://packages.lunarg.com/vulkan/lunarg-vulkan-focal.list
sudo apt update
sudo apt install vulkan-sdk
uses: humbletim/setup-vulkan-sdk@v1.2.0
with:
vulkan-query-version: latest
vulkan-use-cache: true
vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang
- name: Configure CMake
# Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.
@ -52,7 +52,7 @@ jobs:
run: ./.github/linux-appimage.sh
- name: Upload executable
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v4
with:
name: Linux executable
path: './Alber-x86_64.AppImage'

View file

@ -16,15 +16,15 @@ jobs:
# well on Windows or Mac. You can convert this to a matrix build if you need
# cross-platform coverage.
# See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Fetch submodules
run: git submodule update --init --recursive
- name: Install misc packages
run: sudo apt-get update && sudo apt install libx11-dev libgl1-mesa-glx mesa-common-dev
run: sudo apt-get update && sudo apt install libx11-dev libgl1 libglx-mesa0 mesa-common-dev libwayland-dev
- name: Install newer Clang
run: |
@ -49,7 +49,7 @@ jobs:
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
- name: Upload executable
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v4
with:
name: Linux executable
path: './build/Alber'

View file

@ -12,14 +12,15 @@ env:
jobs:
build:
# The CMake configure and build commands are platform agnostic and should work equally
# well on Windows or Mac. You can convert this to a matrix build if you need
# cross-platform coverage.
# See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix
runs-on: macos-13
strategy:
matrix:
arch: [x86_64, arm64]
name: MacOS-${{ matrix.arch }}
runs-on: macos-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Fetch submodules
run: git submodule update --init --recursive
@ -33,7 +34,7 @@ jobs:
- name: Configure CMake
# Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.
# See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type
run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DENABLE_USER_BUILD=ON
run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DENABLE_USER_BUILD=ON -DCMAKE_OSX_ARCHITECTURES=${{ matrix.arch }}
- name: Build
# Build your program with the given configuration
@ -49,10 +50,45 @@ jobs:
run: codesign --force -s - -vvvv Alber.app
- name: Zip it up
run: zip -r Alber Alber.app
run: zip -r Alber-${{ matrix.arch }} Alber.app
- name: Upload MacOS App
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v4
with:
name: MacOS Alber App Bundle
path: 'Alber.zip'
name: MacOS Alber App Bundle (${{ matrix.arch }})
path: Alber-${{ matrix.arch }}.zip
MacOS-Universal:
name: MacOS-Universal
needs: [build]
runs-on: macos-latest
steps:
- name: Download x86_64
uses: actions/download-artifact@v4
with:
name: MacOS Alber App Bundle (x86_64)
path: x86_64
- name: Download ARM64
uses: actions/download-artifact@v4
with:
name: MacOS Alber App Bundle (arm64)
path: arm64
- name: Combine app bundles
shell: bash
run: |
set -x
unzip x86_64/*.zip -d x86_64
unzip arm64/*.zip -d arm64
lipo {x86_64,arm64}/Alber.app/Contents/MacOS/Alber -create -output Alber
cp -v -a arm64/Alber.app Alber.app
cp -v Alber Alber.app/Contents/MacOS/Alber
# Mix in x86_64 files that do not appear in the ARM64 build (e.g. libvulkan)
cp -v -R -n x86_64/Alber.app/* Alber.app/ || true
codesign --force -s - -vvvv Alber.app
zip -r -y Alber-universal.zip Alber.app
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: MacOS Alber App Bundle (universal)
path: Alber-universal.zip

View file

@ -15,7 +15,7 @@ jobs:
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Fetch submodules
run: git submodule update --init --recursive
@ -45,16 +45,21 @@ jobs:
windeployqt --dir upload upload/Alber.exe
- name: Upload executable
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v4
with:
name: Windows executable
path: upload
MacOS:
runs-on: macos-13
strategy:
matrix:
arch: [x86_64, arm64]
name: MacOS-${{ matrix.arch }}
runs-on: macos-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Fetch submodules
run: git submodule update --init --recursive
@ -69,11 +74,17 @@ jobs:
run: |
brew install dylibbundler imagemagick
- name: Install qt
run: brew install qt && which macdeployqt
- name: Install Qt
uses: jurplel/install-qt-action@v3
with:
aqtversion: '==3.1.*'
version: '6.8.1'
host: 'mac'
target: 'desktop'
arch: 'clang_64'
- name: Configure CMake
run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DENABLE_USER_BUILD=ON -DENABLE_QT_GUI=ON
run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DENABLE_USER_BUILD=ON -DENABLE_QT_GUI=ON -DCMAKE_OSX_ARCHITECTURES=${{ matrix.arch }}
- name: Build
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
@ -87,28 +98,62 @@ jobs:
run: codesign --force -s - -vvvv Alber.app
- name: Zip it up
run: zip -r Alber Alber.app
run: zip -r Alber-${{ matrix.arch }} Alber.app
- name: Upload MacOS App
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v4
with:
name: MacOS Alber App Bundle
path: 'Alber.zip'
name: MacOS Alber App Bundle (${{ matrix.arch }})
path: Alber-${{ matrix.arch }}.zip
Linux:
runs-on: ubuntu-20.04
MacOS-Universal:
name: MacOS-Universal
needs: [MacOS]
runs-on: macos-latest
steps:
- uses: actions/checkout@v2
- name: Download x86_64
uses: actions/download-artifact@v4
with:
name: MacOS Alber App Bundle (x86_64)
path: x86_64
- name: Download ARM64
uses: actions/download-artifact@v4
with:
name: MacOS Alber App Bundle (arm64)
path: arm64
- name: Combine app bundles
shell: bash
run: |
set -x
unzip x86_64/*.zip -d x86_64
unzip arm64/*.zip -d arm64
lipo {x86_64,arm64}/Alber.app/Contents/MacOS/Alber -create -output Alber
cp -v -a arm64/Alber.app Alber.app
cp -v Alber Alber.app/Contents/MacOS/Alber
# Mix in x86_64 files that do not appear in the ARM64 build (e.g. libvulkan)
cp -v -R -n x86_64/Alber.app/* Alber.app/ || true
codesign --force -s - -vvvv Alber.app
zip -r -y Alber-universal.zip Alber.app
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: MacOS Alber App Bundle (universal)
path: Alber-universal.zip
Linux:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- name: Fetch submodules
run: git submodule update --init --recursive
- name: Install misc packages
run: |
sudo apt-get update && sudo apt install libx11-dev libgl1-mesa-glx mesa-common-dev libfuse2 libwayland-dev
sudo add-apt-repository -y ppa:savoury1/qt-6-2
sudo apt-get update && sudo apt install libx11-dev libgl1 libglx-mesa0 mesa-common-dev libfuse2 libwayland-dev libgl1-mesa-dev
sudo apt update
sudo apt install qt6-base-dev qt6-base-private-dev
sudo apt install qt6-base-dev qt6-base-private-dev qt6-tools-dev
- name: Install newer Clang
run: |
@ -117,11 +162,11 @@ jobs:
sudo ./llvm.sh 17
- name: Setup Vulkan SDK
run: |
wget -qO - http://packages.lunarg.com/lunarg-signing-key-pub.asc | sudo apt-key add -
sudo wget -qO /etc/apt/sources.list.d/lunarg-vulkan-focal.list http://packages.lunarg.com/vulkan/lunarg-vulkan-focal.list
sudo apt update
sudo apt install vulkan-sdk
uses: humbletim/setup-vulkan-sdk@v1.2.0
with:
vulkan-query-version: latest
vulkan-use-cache: true
vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang
- name: Configure CMake
run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_C_COMPILER=clang-17 -DCMAKE_CXX_COMPILER=clang++-17 -DENABLE_USER_BUILD=ON -DENABLE_QT_GUI=ON
@ -135,7 +180,7 @@ jobs:
./.github/linux-appimage-qt.sh
- name: Upload executable
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v4
with:
name: Linux executable
path: './Alber-x86_64.AppImage'

View file

@ -19,7 +19,7 @@ jobs:
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Fetch submodules
run: git submodule update --init --recursive
@ -40,7 +40,7 @@ jobs:
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
- name: Upload executable
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v4
with:
name: Windows executable
path: './build/${{ env.BUILD_TYPE }}/Alber.exe'

4
.gitignore vendored
View file

@ -64,5 +64,9 @@ fb.bat
*.elf
*.smdh
# Compiled Metal shader files
*.ir
*.metallib
config.toml
CMakeSettings.json

131
.gitlab-ci.yml Normal file
View file

@ -0,0 +1,131 @@
# DESCRIPTION: GitLab CI/CD for libRetro (NOT FOR GitLab-proper)
##############################################################################
################################# BOILERPLATE ################################
##############################################################################
# Core definitions
.core-defs:
variables:
GIT_SUBMODULE_STRATEGY: recursive
CORENAME: panda3ds
BASE_CORE_ARGS: -DBUILD_LIBRETRO_CORE=ON -DENABLE_USER_BUILD=ON -DENABLE_VULKAN=OFF -DENABLE_LUAJIT=OFF -DENABLE_DISCORD_RPC=OFF -DENABLE_METAL=OFF
CORE_ARGS: ${BASE_CORE_ARGS}
# Inclusion templates, required for the build to work
include:
################################## DESKTOPS ################################
# Linux
- project: 'libretro-infrastructure/ci-templates'
file: '/linux-cmake.yml'
# Windows
- project: 'libretro-infrastructure/ci-templates'
file: '/windows-cmake-mingw.yml'
# MacOS
- project: 'libretro-infrastructure/ci-templates'
file: 'osx-cmake-x86.yml'
# MacOS
- project: 'libretro-infrastructure/ci-templates'
file: 'osx-cmake-arm64.yml'
################################## CELLULAR ################################
# Android
- project: 'libretro-infrastructure/ci-templates'
file: '/android-cmake.yml'
# iOS
- project: 'libretro-infrastructure/ci-templates'
file: '/ios-cmake.yml'
# Stages for building
stages:
- build-prepare
- build-static
- build-shared
##############################################################################
#################################### STAGES ##################################
##############################################################################
#
################################### DESKTOPS #################################
# Linux 64-bit
libretro-build-linux-x64:
image: $CI_SERVER_HOST:5050/libretro-infrastructure/libretro-build-amd64-ubuntu:latest
before_script:
- export NUMPROC=$(($(nproc)/5))
- sudo apt-get update -qy
- sudo apt-get install -qy software-properties-common
- sudo add-apt-repository -y ppa:savoury1/build-tools
- sudo add-apt-repository -y ppa:savoury1/gcc-defaults-12
- sudo apt-get update -qy
- sudo apt-get install -qy cmake gcc-12 g++-12
variables:
CC: /usr/bin/gcc-12
CXX: /usr/bin/g++-12
extends:
- .libretro-linux-cmake-x86_64
- .core-defs
# Windows 64-bit
libretro-build-windows-x64:
extends:
- .libretro-windows-cmake-x86_64
- .core-defs
# MacOS 64-bit
libretro-build-osx-x64:
tags:
- mac-apple-silicon
variables:
CORE_ARGS: ${BASE_CORE_ARGS} -DCMAKE_OSX_ARCHITECTURES=x86_64 -DCRYPTOPP_AMD64=1
extends:
- .libretro-osx-cmake-x86
- .core-defs
# MacOS arm 64-bit
libretro-build-osx-arm64:
tags:
- mac-apple-silicon
extends:
- .libretro-osx-cmake-arm64
- .core-defs
################################### CELLULAR #################################
# Android ARMv7a
#android-armeabi-v7a:
# extends:
# - .libretro-android-cmake-armeabi-v7a
# - .core-defs
# Android ARMv8a
# android-arm64-v8a:
# extends:
# - .libretro-android-cmake-arm64-v8a
# - .core-defs
# Android 64-bit x86
# android-x86_64:
# extends:
# - .libretro-android-cmake-x86_64
# - .core-defs
# Android 32-bit x86
# android-x86:
# extends:
# - .libretro-android-cmake-x86
# - .core-defs
# iOS
# libretro-build-ios-arm64:
# extends:
# - .libretro-ios-cmake-arm64
# - .core-defs
# variables:
# CORE_ARGS: -DBUILD_LIBRETRO_CORE=ON -DBUILD_PLAY=OFF -DENABLE_AMAZON_S3=off -DBUILD_TESTS=OFF -DCMAKE_TOOLCHAIN_FILE=deps/Dependencies/cmake-ios/ios.cmake -DTARGET_IOS=ON
# LIBNAME: ${CORENAME}_libretro_ios.dylib
################################### CONSOLES #################################

20
.gitmodules vendored
View file

@ -40,9 +40,6 @@
[submodule "third_party/zep"]
path = third_party/zep
url = https://github.com/Panda3DS-emu/zep
[submodule "third_party/oaknut"]
path = third_party/oaknut
url = https://github.com/merryhime/oaknut
[submodule "third_party/luv"]
path = third_party/luv
url = https://github.com/luvit/luv
@ -75,4 +72,19 @@
url = https://github.com/machinezone/IXWebSocket
[submodule "third_party/hips"]
path = third_party/hips
url = https://github.com/wheremyfoodat/Hips
url = https://github.com/wheremyfoodat/Hips
[submodule "third_party/metal-cpp"]
path = third_party/metal-cpp
url = https://github.com/Panda3DS-emu/metal-cpp
[submodule "third_party/fmt"]
path = third_party/fmt
url = https://github.com/fmtlib/fmt
[submodule "third_party/fdk-aac"]
path = third_party/fdk-aac
url = https://github.com/Panda3DS-emu/fdk-aac/
[submodule "third_party/cryptoppwin"]
path = third_party/cryptoppwin
url = https://github.com/shadps4-emu/ext-cryptoppwin
[submodule "third_party/oaknut"]
path = third_party/oaknut
url = https://github.com/panda3ds-emu/oaknut

View file

@ -19,19 +19,38 @@ endif()
project(Alber)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
if(APPLE)
enable_language(OBJC)
endif()
# Enable RC support in order to use resource files for application icons
if(WIN32)
enable_language(RC)
set(APP_RESOURCES docs/img/windows_icon.rc)
endif()
if(NOT CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-format-nonliteral -Wno-format-security")
endif()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-format-nonliteral -Wno-format-security -Wno-invalid-offsetof")
endif()
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-interference-size")
endif()
if(ANDROID)
set(DEFAULT_OPENGL_PROFILE OpenGLES)
else()
set(DEFAULT_OPENGL_PROFILE OpenGL)
endif()
option(DISABLE_PANIC_DEV "Make a build with fewer and less intrusive asserts" ON)
option(GPU_DEBUG_INFO "Enable additional GPU debugging info" OFF)
option(ENABLE_OPENGL "Enable OpenGL rendering backend" ON)
option(ENABLE_VULKAN "Enable Vulkan rendering backend" ON)
option(ENABLE_METAL "Enable Metal rendering backend (if available)" ON)
option(ENABLE_WAYLAND "Enable Wayland support on Linux platforms" ON)
option(ENABLE_LTO "Enable link-time optimization" OFF)
option(ENABLE_TESTS "Compile unit-tests" OFF)
option(ENABLE_USER_BUILD "Make a user-facing build. These builds have various assertions disabled, LTO, and more" OFF)
@ -39,8 +58,20 @@ option(ENABLE_HTTP_SERVER "Enable HTTP server. Used for Discord bot support" OFF
option(ENABLE_DISCORD_RPC "Compile with Discord RPC support (disabled by default)" ON)
option(ENABLE_LUAJIT "Enable scripting with the Lua programming language" ON)
option(ENABLE_QT_GUI "Enable the Qt GUI. If not selected then the emulator uses a minimal SDL-based UI instead" OFF)
option(USE_SYSTEM_SDL2 "Use the system's SDL2 package" OFF)
option(ENABLE_GIT_VERSIONING "Enables querying git for the emulator version" ON)
option(BUILD_HYDRA_CORE "Build a Hydra core" OFF)
option(BUILD_LIBRETRO_CORE "Build a Libretro core" OFF)
option(ENABLE_RENDERDOC_API "Build with support for Renderdoc's capture API for graphics debugging" ON)
option(DISABLE_SSE4 "Build with SSE4 instructions disabled, may reduce performance" OFF)
set(OPENGL_PROFILE ${DEFAULT_OPENGL_PROFILE} CACHE STRING "OpenGL profile to use if OpenGL is enabled. Valid values are 'OpenGL' and 'OpenGLES'.")
set_property(CACHE OPENGL_PROFILE PROPERTY STRINGS OpenGL OpenGLES)
if(ENABLE_OPENGL AND (OPENGL_PROFILE STREQUAL "OpenGLES"))
message(STATUS "Building with OpenGLES support")
add_compile_definitions(USING_GLES)
endif()
if(BUILD_HYDRA_CORE)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
@ -51,6 +82,49 @@ if(BUILD_LIBRETRO_CORE)
add_compile_definitions(__LIBRETRO__)
endif()
if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC" AND ENABLE_USER_BUILD)
# Disable stack buffer overflow checks in user builds
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /GS-")
endif()
# Generate versioning files
find_package(Git)
set(PANDA3DS_VERSION "0.9")
if(NOT EXISTS ${CMAKE_BINARY_DIR}/include/version.hpp.in)
file(WRITE ${CMAKE_BINARY_DIR}/include/version.hpp.in "#define PANDA3DS_VERSION \"\${PANDA3DS_VERSION}\"")
endif()
if(GIT_FOUND AND ENABLE_GIT_VERSIONING)
execute_process(
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND ${GIT_EXECUTABLE} describe --tags --abbrev=0
OUTPUT_VARIABLE git_version_tag OUTPUT_STRIP_TRAILING_WHITESPACE
)
execute_process(
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND ${GIT_EXECUTABLE} rev-parse --short=7 HEAD
OUTPUT_VARIABLE git_version_rev OUTPUT_STRIP_TRAILING_WHITESPACE
)
if(NOT git_version_tag STREQUAL "")
set(PANDA3DS_VERSION "${git_version_tag}")
execute_process(
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND ${GIT_EXECUTABLE} describe --tags
OUTPUT_VARIABLE git_version_desc OUTPUT_STRIP_TRAILING_WHITESPACE
)
if(git_version_tag STREQUAL git_version_desc)
set(git_version_rev "")
endif()
unset(git_version_desc)
endif()
if(NOT git_version_rev STREQUAL "")
set(PANDA3DS_VERSION "${PANDA3DS_VERSION}.${git_version_rev}")
endif()
string(REGEX REPLACE "^v" "" PANDA3DS_VERSION "${PANDA3DS_VERSION}")
unset(git_version_tag)
unset(git_version_rev)
endif()
configure_file(${CMAKE_BINARY_DIR}/include/version.hpp.in ${CMAKE_BINARY_DIR}/include/version.hpp)
include_directories(${CMAKE_BINARY_DIR}/include/)
add_library(AlberCore STATIC)
include_directories(${PROJECT_SOURCE_DIR}/include/)
@ -61,7 +135,6 @@ include_directories(third_party/elfio/)
include_directories(third_party/hips/include/)
include_directories(third_party/imgui/)
include_directories(third_party/dynarmic/src)
include_directories(third_party/cryptopp/)
include_directories(third_party/cityhash/include)
include_directories(third_party/result/include)
include_directories(third_party/xxhash/include)
@ -75,24 +148,35 @@ add_compile_definitions(NOMINMAX) # Make windows.h not define min/ma
add_compile_definitions(WIN32_LEAN_AND_MEAN) # Make windows.h not include literally everything
add_compile_definitions(SDL_MAIN_HANDLED)
if(ENABLE_WAYLAND)
add_compile_definitions(WAYLAND_ENABLED)
endif()
if(ENABLE_DISCORD_RPC AND NOT ANDROID)
add_subdirectory(third_party/discord-rpc)
include_directories(third_party/discord-rpc/include)
endif()
set(SDL_STATIC ON CACHE BOOL "" FORCE)
set(SDL_SHARED OFF CACHE BOOL "" FORCE)
set(SDL_TEST OFF CACHE BOOL "" FORCE)
if (NOT ANDROID)
add_subdirectory(third_party/SDL2)
target_link_libraries(AlberCore PUBLIC SDL2-static)
if (USE_SYSTEM_SDL2)
find_package(SDL2 CONFIG REQUIRED)
target_link_libraries(AlberCore PUBLIC SDL2::SDL2)
else()
set(SDL_STATIC ON CACHE BOOL "" FORCE)
set(SDL_SHARED OFF CACHE BOOL "" FORCE)
set(SDL_TEST OFF CACHE BOOL "" FORCE)
add_subdirectory(third_party/SDL2)
target_link_libraries(AlberCore PUBLIC SDL2-static)
endif()
endif()
add_subdirectory(third_party/fmt)
add_subdirectory(third_party/toml11)
include_directories(${SDL2_INCLUDE_DIR})
include_directories(third_party/toml11)
include_directories(third_party/glm)
include_directories(third_party/renderdoc)
include_directories(third_party/duckstation)
add_subdirectory(third_party/cmrc)
@ -110,10 +194,26 @@ if(ANDROID)
target_link_libraries(AlberCore PRIVATE EGL log)
endif()
set(CRYPTOPP_BUILD_TESTING OFF)
add_subdirectory(third_party/cryptopp)
add_subdirectory(third_party/glad)
# Cryptopp doesn't support compiling under clang-cl, so we have to include it as a prebuilt MSVC static library
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND MSVC)
add_subdirectory(third_party/cryptoppwin)
include_directories(third_party/cryptoppwin/include)
target_link_libraries(AlberCore PRIVATE cryptoppwin)
# Also silence some of clang-cl's more... intrusive warnings
set(WARNING_FLAGS "/W1 -Wno-unused-function -Wno-unused-but-set-variable -Wno-reorder-ctor")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNING_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARNING_FLAGS}")
else()
set(CRYPTOPP_BUILD_TESTING OFF)
add_subdirectory(third_party/cryptopp)
include_directories(third_party/cryptopp)
target_link_libraries(AlberCore PRIVATE cryptopp)
endif()
if(ENABLE_LUAJIT)
add_subdirectory(third_party/LuaJIT luajit)
include_directories(third_party/LuaJIT/src ${CMAKE_BINARY_DIR}/luajit)
@ -129,26 +229,68 @@ if(ENABLE_LUAJIT)
target_link_libraries(AlberCore PRIVATE libluajit)
endif()
# Check for x64
if (CMAKE_SYSTEM_PROCESSOR STREQUAL "AMD64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "x86-64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64")
# Detect target architecture
if (NOT APPLE OR "${CMAKE_OSX_ARCHITECTURES}" STREQUAL "")
# Normal target detection
if (CMAKE_SYSTEM_PROCESSOR STREQUAL "AMD64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "x86-64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64")
set(HOST_X64 TRUE)
else()
set(HOST_X64 FALSE)
endif()
if (CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64")
set(HOST_ARM64 TRUE)
else()
set(HOST_ARM64 FALSE)
endif()
else()
# Apple target detection
if("x86_64" IN_LIST CMAKE_OSX_ARCHITECTURES)
set(HOST_X64 TRUE)
else()
set(HOST_X64 FALSE)
endif()
if("arm64" IN_LIST CMAKE_OSX_ARCHITECTURES)
set(HOST_ARM64 TRUE)
else()
set(HOST_ARM64 FALSE)
endif()
if (HOST_ARM64 AND HOST_X64)
message(FATAL_ERROR "Universal builds not supported like this! Please compile separately and stitch together")
endif()
endif()
if (HOST_X64)
add_subdirectory(third_party/xbyak) # Add xbyak submodule for x86 JITs
include_directories(third_party/xbyak)
add_compile_definitions(PANDA3DS_DYNAPICA_SUPPORTED)
add_compile_definitions(PANDA3DS_X64_HOST)
else()
set(HOST_X64 FALSE)
endif()
# Check for arm64
if (CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64")
set(HOST_ARM64 TRUE)
if (HOST_ARM64)
add_subdirectory(third_party/oaknut) # Add Oaknut submodule for arm64 JITs
include_directories(third_party/oaknut/include)
add_compile_definitions(PANDA3DS_DYNAPICA_SUPPORTED)
add_compile_definitions(PANDA3DS_ARM64_HOST)
else()
set(HOST_ARM64 FALSE)
endif()
# Enable SSE4.1 if it's not explicitly disabled
# Annoyingly, we can't easily do this if we're using MSVC cause there's no SSE4.1 flag, only SSE4.1
if(NOT MSVC OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND NOT DISABLE_SSE4 AND HOST_X64)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -msse4.1")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -msse4.1")
elseif(MSVC AND NOT DISABLE_SSE4)
# Tell our SIMD code to use SSE4.1 by defining the relevant macros.
# Clang defines these macros, MSVC does not.
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /D__SSE3__ /D__SSE4_1__")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /D__SSE3__ /D__SSE4_1__")
endif()
if(ENABLE_RENDERDOC_API)
find_package(RenderDoc 1.6.0 MODULE REQUIRED)
add_compile_definitions(PANDA3DS_ENABLE_RENDERDOC)
endif()
if(HOST_X64 OR HOST_ARM64)
@ -162,6 +304,7 @@ else()
endif()
add_subdirectory(third_party/teakra EXCLUDE_FROM_ALL)
add_subdirectory(third_party/fdk-aac)
set(CAPSTONE_ARCHITECTURE_DEFAULT OFF)
set(CAPSTONE_ARM_SUPPORT ON)
@ -173,7 +316,8 @@ set(SOURCE_FILES src/emulator.cpp src/io_file.cpp src/config.cpp
src/core/CPU/cpu_dynarmic.cpp src/core/CPU/dynarmic_cycles.cpp
src/core/memory.cpp src/renderer.cpp src/core/renderer_null/renderer_null.cpp
src/http_server.cpp src/stb_image_write.c src/core/cheats.cpp src/core/action_replay.cpp
src/discord_rpc.cpp src/lua.cpp src/memory_mapped_file.cpp src/miniaudio.cpp
src/discord_rpc.cpp src/lua.cpp src/memory_mapped_file.cpp src/miniaudio.cpp src/renderdoc.cpp
src/frontend_settings.cpp
)
set(CRYPTO_SOURCE_FILES src/core/crypto/aes_engine.cpp)
set(KERNEL_SOURCE_FILES src/core/kernel/kernel.cpp src/core/kernel/resource_limits.cpp
@ -193,25 +337,29 @@ set(SERVICE_SOURCE_FILES src/core/services/service_manager.cpp src/core/services
src/core/services/act.cpp src/core/services/nfc.cpp src/core/services/dlp_srvr.cpp
src/core/services/ir_user.cpp src/core/services/http.cpp src/core/services/soc.cpp
src/core/services/ssl.cpp src/core/services/news_u.cpp src/core/services/amiibo_device.cpp
src/core/services/csnd.cpp src/core/services/nwm_uds.cpp
src/core/services/csnd.cpp src/core/services/nwm_uds.cpp src/core/services/fonts.cpp
src/core/services/ns.cpp
)
set(PICA_SOURCE_FILES src/core/PICA/gpu.cpp src/core/PICA/regs.cpp src/core/PICA/shader_unit.cpp
src/core/PICA/shader_interpreter.cpp src/core/PICA/dynapica/shader_rec.cpp
src/core/PICA/dynapica/shader_rec_emitter_x64.cpp src/core/PICA/pica_hash.cpp
src/core/PICA/dynapica/shader_rec_emitter_arm64.cpp
src/core/PICA/dynapica/shader_rec_emitter_arm64.cpp src/core/PICA/shader_gen_glsl.cpp
src/core/PICA/shader_decompiler.cpp src/core/PICA/draw_acceleration.cpp
)
set(LOADER_SOURCE_FILES src/core/loader/elf.cpp src/core/loader/ncsd.cpp src/core/loader/ncch.cpp src/core/loader/3dsx.cpp src/core/loader/lz77.cpp)
set(FS_SOURCE_FILES src/core/fs/archive_self_ncch.cpp src/core/fs/archive_save_data.cpp src/core/fs/archive_sdmc.cpp
src/core/fs/archive_ext_save_data.cpp src/core/fs/archive_ncch.cpp src/core/fs/romfs.cpp
src/core/fs/ivfc.cpp src/core/fs/archive_user_save_data.cpp src/core/fs/archive_system_save_data.cpp
src/core/fs/archive_twl_photo.cpp src/core/fs/archive_twl_sound.cpp src/core/fs/archive_card_spi.cpp
)
set(APPLET_SOURCE_FILES src/core/applets/applet.cpp src/core/applets/mii_selector.cpp src/core/applets/software_keyboard.cpp src/core/applets/applet_manager.cpp
src/core/applets/error_applet.cpp
)
set(AUDIO_SOURCE_FILES src/core/audio/dsp_core.cpp src/core/audio/null_core.cpp src/core/audio/teakra_core.cpp
src/core/audio/miniaudio_device.cpp src/core/audio/hle_core.cpp
src/core/audio/miniaudio_device.cpp src/core/audio/hle_core.cpp src/core/audio/aac_decoder.cpp
src/core/audio/audio_interpolation.cpp
)
set(RENDERER_SW_SOURCE_FILES src/core/renderer_sw/renderer_sw.cpp)
@ -230,7 +378,7 @@ set(HEADER_FILES include/emulator.hpp include/helpers.hpp include/termcolor.hpp
include/services/mic.hpp include/services/cecd.hpp include/services/ac.hpp
include/services/am.hpp include/services/boss.hpp include/services/frd.hpp include/services/nim.hpp
include/fs/archive_ext_save_data.hpp include/fs/archive_ncch.hpp include/services/mcu/mcu_hwc.hpp
include/colour.hpp include/services/y2r.hpp include/services/cam.hpp include/services/ssl.hpp
include/colour.hpp include/services/y2r.hpp include/services/cam.hpp include/services/ssl.hpp
include/services/ldr_ro.hpp include/ipc.hpp include/services/act.hpp include/services/nfc.hpp
include/system_models.hpp include/services/dlp_srvr.hpp include/PICA/dynapica/pica_recs.hpp
include/PICA/dynapica/x64_regs.hpp include/PICA/dynapica/vertex_loader_rec.hpp include/PICA/dynapica/shader_rec.hpp
@ -241,21 +389,28 @@ set(HEADER_FILES include/emulator.hpp include/helpers.hpp include/termcolor.hpp
include/config.hpp include/services/ir_user.hpp include/http_server.hpp include/cheats.hpp
include/action_replay.hpp include/renderer_sw/renderer_sw.hpp include/compiler_builtins.hpp
include/fs/romfs.hpp include/fs/ivfc.hpp include/discord_rpc.hpp include/services/http.hpp include/result/result_cfg.hpp
include/applets/applet.hpp include/applets/mii_selector.hpp include/math_util.hpp include/services/soc.hpp
include/applets/applet.hpp include/applets/mii_selector.hpp include/math_util.hpp include/services/soc.hpp
include/services/news_u.hpp include/applets/software_keyboard.hpp include/applets/applet_manager.hpp include/fs/archive_user_save_data.hpp
include/services/amiibo_device.hpp include/services/nfc_types.hpp include/swap.hpp include/services/csnd.hpp include/services/nwm_uds.hpp
include/fs/archive_system_save_data.hpp include/lua_manager.hpp include/memory_mapped_file.hpp include/hydra_icon.hpp
include/PICA/dynapica/shader_rec_emitter_arm64.hpp include/scheduler.hpp include/applets/error_applet.hpp
include/PICA/dynapica/shader_rec_emitter_arm64.hpp include/scheduler.hpp include/applets/error_applet.hpp include/PICA/shader_gen.hpp
include/audio/dsp_core.hpp include/audio/null_core.hpp include/audio/teakra_core.hpp
include/audio/miniaudio_device.hpp include/ring_buffer.hpp include/bitfield.hpp include/audio/dsp_shared_mem.hpp
include/audio/hle_core.hpp include/capstone.hpp include/audio/aac.hpp include/external_haptics_manager.hpp
include/audio/hle_core.hpp include/capstone.hpp include/audio/aac.hpp include/PICA/pica_frag_config.hpp
include/PICA/pica_frag_uniforms.hpp include/PICA/shader_gen_types.hpp include/PICA/shader_decompiler.hpp
include/PICA/pica_vert_config.hpp include/sdl_sensors.hpp include/PICA/draw_acceleration.hpp include/renderdoc.hpp
include/align.hpp include/audio/aac_decoder.hpp include/PICA/pica_simd.hpp include/services/fonts.hpp
include/audio/audio_interpolation.hpp include/audio/hle_mixer.hpp include/audio/dsp_simd.hpp
include/services/dsp_firmware_db.hpp include/frontend_settings.hpp include/fs/archive_twl_photo.hpp
include/fs/archive_twl_sound.hpp include/fs/archive_card_spi.hpp include/services/ns.hpp
include/external_haptics_manager.hpp
)
cmrc_add_resource_library(
resources_console_fonts
NAMESPACE ConsoleFonts
WHENCE "src/core/services/fonts/"
"src/core/services/fonts/CitraSharedFontUSRelocated.bin"
"src/core/services/fonts/SharedFontReplacement.bin"
)
set(THIRD_PARTY_SOURCE_FILES third_party/imgui/imgui.cpp
@ -285,17 +440,22 @@ if(ENABLE_LUAJIT AND NOT ANDROID)
target_link_libraries(AlberCore PRIVATE buttplugCpp)
endif()
set(GL_CONTEXT_SOURCE_FILES "")
if(ENABLE_QT_GUI)
include_directories(third_party/duckstation)
set(THIRD_PARTY_SOURCE_FILES ${THIRD_PARTY_SOURCE_FILES} third_party/duckstation/window_info.cpp third_party/duckstation/gl/context.cpp)
set(GL_CONTEXT_SOURCE_FILES ${GL_CONTEXT_SOURCE_FILES} third_party/duckstation/window_info.cpp third_party/duckstation/gl/context.cpp)
if(APPLE)
set(THIRD_PARTY_SOURCE_FILES ${THIRD_PARTY_SOURCE_FILES} third_party/duckstation/gl/context_agl.mm)
set(GL_CONTEXT_SOURCE_FILES ${GL_CONTEXT_SOURCE_FILES} third_party/duckstation/gl/context_agl.mm)
elseif(WIN32)
set(THIRD_PARTY_SOURCE_FILES ${THIRD_PARTY_SOURCE_FILES} third_party/duckstation/gl/context_wgl.cpp)
set(GL_CONTEXT_SOURCE_FILES ${GL_CONTEXT_SOURCE_FILES} third_party/duckstation/gl/context_wgl.cpp)
else()
set(THIRD_PARTY_SOURCE_FILES ${THIRD_PARTY_SOURCE_FILES} third_party/duckstation/gl/context_egl.cpp third_party/duckstation/gl/context_egl_wayland.cpp
third_party/duckstation/gl/context_egl_x11.cpp third_party/duckstation/gl/context_glx.cpp third_party/duckstation/gl/x11_window.cpp)
set(GL_CONTEXT_SOURCE_FILES ${GL_CONTEXT_SOURCE_FILES} third_party/duckstation/gl/context_egl.cpp third_party/duckstation/gl/context_egl_x11.cpp
third_party/duckstation/gl/context_glx.cpp third_party/duckstation/gl/x11_window.cpp)
if(ENABLE_WAYLAND)
set(GL_CONTEXT_SOURCE_FILES ${GL_CONTEXT_SOURCE_FILES} third_party/duckstation/gl/context_egl_wayland.cpp)
endif()
endif()
endif()
@ -309,7 +469,7 @@ source_group("Source Files\\Core\\Applets" FILES ${APPLET_SOURCE_FILES})
source_group("Source Files\\Core\\PICA" FILES ${PICA_SOURCE_FILES})
source_group("Source Files\\Core\\Audio" FILES ${AUDIO_SOURCE_FILES})
source_group("Source Files\\Core\\Software Renderer" FILES ${RENDERER_SW_SOURCE_FILES})
source_group("Source Files\\Third Party" FILES ${THIRD_PARTY_SOURCE_FILES})
source_group("Source Files\\Third Party" FILES ${THIRD_PARTY_SOURCE_FILES} ${GL_CONTEXT_SOURCE_FILES})
set(RENDERER_GL_SOURCE_FILES "") # Empty by default unless we are compiling with the GL renderer
set(RENDERER_VK_SOURCE_FILES "") # Empty by default unless we are compiling with the VK renderer
@ -319,16 +479,19 @@ if(ENABLE_OPENGL)
set(RENDERER_GL_INCLUDE_FILES third_party/opengl/opengl.hpp
include/renderer_gl/renderer_gl.hpp include/renderer_gl/textures.hpp
include/renderer_gl/surfaces.hpp include/renderer_gl/surface_cache.hpp
include/renderer_gl/gl_state.hpp
include/renderer_gl/gl_state.hpp include/renderer_gl/gl_driver.hpp
)
set(RENDERER_GL_SOURCE_FILES src/core/renderer_gl/renderer_gl.cpp
src/core/renderer_gl/textures.cpp src/core/renderer_gl/etc1.cpp
src/core/renderer_gl/gl_state.cpp src/host_shaders/opengl_display.frag
src/host_shaders/opengl_display.vert src/host_shaders/opengl_vertex_shader.vert
src/core/renderer_gl/gl_state.cpp src/host_shaders/opengl_display.vert
src/host_shaders/opengl_display.frag src/host_shaders/opengl_es_display.vert
src/host_shaders/opengl_es_display.frag src/host_shaders/opengl_vertex_shader.vert
src/host_shaders/opengl_fragment_shader.frag
)
set(THIRD_PARTY_SOURCE_FILES ${THIRD_PARTY_SOURCE_FILES} third_party/duckstation/gl/stream_buffer.cpp)
set(HEADER_FILES ${HEADER_FILES} ${RENDERER_GL_INCLUDE_FILES})
source_group("Source Files\\Core\\OpenGL Renderer" FILES ${RENDERER_GL_SOURCE_FILES})
@ -336,8 +499,10 @@ if(ENABLE_OPENGL)
resources_renderer_gl
NAMESPACE RendererGL
WHENCE "src/host_shaders/"
"src/host_shaders/opengl_display.frag"
"src/host_shaders/opengl_display.vert"
"src/host_shaders/opengl_display.frag"
"src/host_shaders/opengl_es_display.vert"
"src/host_shaders/opengl_es_display.frag"
"src/host_shaders/opengl_vertex_shader.vert"
"src/host_shaders/opengl_fragment_shader.frag"
)
@ -411,14 +576,88 @@ if(ENABLE_VULKAN)
target_link_libraries(AlberCore PRIVATE Vulkan::Vulkan resources_renderer_vk)
endif()
if(ENABLE_METAL AND APPLE)
set(RENDERER_MTL_INCLUDE_FILES include/renderer_mtl/renderer_mtl.hpp
include/renderer_mtl/mtl_depth_stencil_cache.hpp
include/renderer_mtl/mtl_blit_pipeline_cache.hpp
include/renderer_mtl/mtl_draw_pipeline_cache.hpp
include/renderer_mtl/mtl_render_target.hpp
include/renderer_mtl/mtl_texture.hpp
include/renderer_mtl/mtl_vertex_buffer_cache.hpp
include/renderer_mtl/mtl_lut_texture.hpp
include/renderer_mtl/mtl_command_encoder.hpp
include/renderer_mtl/mtl_common.hpp
include/renderer_mtl/pica_to_mtl.hpp
include/renderer_mtl/objc_helper.hpp
)
set(RENDERER_MTL_SOURCE_FILES src/core/renderer_mtl/metal_cpp_impl.cpp
src/core/renderer_mtl/renderer_mtl.cpp
src/core/renderer_mtl/mtl_texture.cpp
src/core/renderer_mtl/mtl_etc1.cpp
src/core/renderer_mtl/mtl_lut_texture.cpp
src/core/renderer_mtl/objc_helper.mm
src/host_shaders/metal_shaders.metal
src/host_shaders/metal_blit.metal
#src/host_shaders/metal_copy_to_lut_texture.metal
)
set(HEADER_FILES ${HEADER_FILES} ${RENDERER_MTL_INCLUDE_FILES})
source_group("Source Files\\Core\\Metal Renderer" FILES ${RENDERER_MTL_SOURCE_FILES})
set(RENDERER_MTL_HOST_SHADERS_SOURCES)
function (add_metal_shader SHADER)
set(SHADER_SOURCE "${CMAKE_SOURCE_DIR}/src/host_shaders/${SHADER}.metal")
set(SHADER_IR "${CMAKE_SOURCE_DIR}/src/host_shaders/${SHADER}.ir")
set(SHADER_METALLIB "${CMAKE_SOURCE_DIR}/src/host_shaders/${SHADER}.metallib")
# TODO: only include sources in debug builds
add_custom_command(
OUTPUT ${SHADER_IR}
COMMAND xcrun -sdk macosx metal -gline-tables-only -frecord-sources -o ${SHADER_IR} -c ${SHADER_SOURCE}
DEPENDS ${SHADER_SOURCE}
VERBATIM)
add_custom_command(
OUTPUT ${SHADER_METALLIB}
COMMAND xcrun -sdk macosx metallib -o ${SHADER_METALLIB} ${SHADER_IR}
DEPENDS ${SHADER_IR}
VERBATIM)
set(RENDERER_MTL_HOST_SHADERS_SOURCES ${RENDERER_MTL_HOST_SHADERS_SOURCES} ${SHADER_METALLIB})
endfunction()
add_metal_shader(metal_shaders)
add_metal_shader(metal_blit)
#add_metal_shader(metal_copy_to_lut_texture)
add_custom_target(
compile_msl_shaders
DEPENDS ${RENDERER_MTL_HOST_SHADERS_SOURCES}
)
cmrc_add_resource_library(
resources_renderer_mtl
NAMESPACE RendererMTL
WHENCE "src/host_shaders/"
"src/host_shaders/metal_shaders.metallib"
"src/host_shaders/metal_blit.metallib"
#"src/host_shaders/metal_copy_to_lut_texture.metallib"
)
add_dependencies(resources_renderer_mtl compile_msl_shaders)
target_sources(AlberCore PRIVATE ${RENDERER_MTL_SOURCE_FILES})
target_compile_definitions(AlberCore PUBLIC "PANDA3DS_ENABLE_METAL=1")
target_include_directories(AlberCore PRIVATE third_party/metal-cpp)
# TODO: check if all of them are needed
target_link_libraries(AlberCore PRIVATE "-framework Metal" "-framework Foundation" "-framework QuartzCore" resources_renderer_mtl)
endif()
source_group("Header Files\\Core" FILES ${HEADER_FILES})
set(ALL_SOURCES ${SOURCE_FILES} ${FS_SOURCE_FILES} ${CRYPTO_SOURCE_FILES} ${KERNEL_SOURCE_FILES}
set(ALL_SOURCES ${SOURCE_FILES} ${FS_SOURCE_FILES} ${CRYPTO_SOURCE_FILES} ${KERNEL_SOURCE_FILES}
${LOADER_SOURCE_FILES} ${SERVICE_SOURCE_FILES} ${APPLET_SOURCE_FILES} ${RENDERER_SW_SOURCE_FILES} ${PICA_SOURCE_FILES} ${THIRD_PARTY_SOURCE_FILES}
${AUDIO_SOURCE_FILES} ${HEADER_FILES} ${FRONTEND_HEADER_FILES})
target_sources(AlberCore PRIVATE ${ALL_SOURCES})
target_link_libraries(AlberCore PRIVATE dynarmic cryptopp glad resources_console_fonts teakra)
target_link_libraries(AlberCore PUBLIC glad capstone)
target_link_libraries(AlberCore PRIVATE dynarmic glad resources_console_fonts teakra fdk-aac)
target_link_libraries(AlberCore PUBLIC glad capstone fmt::fmt)
if(ENABLE_DISCORD_RPC AND NOT ANDROID)
target_compile_definitions(AlberCore PUBLIC "PANDA3DS_ENABLE_DISCORD_RPC=1")
@ -453,14 +692,17 @@ if(NOT BUILD_HYDRA_CORE AND NOT BUILD_LIBRETRO_CORE)
add_executable(Alber)
if(ENABLE_QT_GUI)
find_package(Qt6 REQUIRED COMPONENTS Widgets)
find_package(Qt6 REQUIRED COMPONENTS Widgets LinguistTools)
if(NOT ENABLE_OPENGL)
message(FATAL_ERROR "Qt frontend requires OpenGL")
endif()
option(GENERATE_QT_TRANSLATION "Generate Qt translation file" OFF)
set(QT_LANGUAGES docs/translations)
set(FRONTEND_SOURCE_FILES src/panda_qt/main.cpp src/panda_qt/screen.cpp src/panda_qt/main_window.cpp src/panda_qt/about_window.cpp
src/panda_qt/config_window.cpp src/panda_qt/zep.cpp src/panda_qt/text_editor.cpp src/panda_qt/cheats_window.cpp src/panda_qt/mappings.cpp
src/panda_qt/patch_window.cpp src/panda_qt/elided_label.cpp src/panda_qt/shader_editor.cpp
src/panda_qt/patch_window.cpp src/panda_qt/elided_label.cpp src/panda_qt/shader_editor.cpp src/panda_qt/translations.cpp
)
set(FRONTEND_HEADER_FILES include/panda_qt/screen.hpp include/panda_qt/main_window.hpp include/panda_qt/about_window.hpp
include/panda_qt/config_window.hpp include/panda_qt/text_editor.hpp include/panda_qt/cheats_window.hpp
@ -494,18 +736,49 @@ if(NOT BUILD_HYDRA_CORE AND NOT BUILD_LIBRETRO_CORE)
endif()
endif()
# Generates an en.ts file for translations
# To update the file, use cmake --build --target Alber_lupdate
if(GENERATE_QT_TRANSLATION)
find_package(Qt6 REQUIRED COMPONENTS LinguistTools)
qt_add_lupdate(Alber TS_FILES ${QT_LANGUAGES}/en.ts
SOURCES ${FRONTEND_SOURCE_FILES}
INCLUDE_DIRECTORIES ${FRONTEND_HEADER_FILES}
NO_GLOBAL_TARGET
)
endif()
qt_add_resources(AlberCore "app_images"
PREFIX "/"
FILES
docs/img/rsob_icon.png docs/img/rstarstruck_icon.png docs/img/rpog_icon.png
docs/img/rsob_icon.png docs/img/rstarstruck_icon.png docs/img/rpog_icon.png docs/img/rsyn_icon.png
docs/img/settings_icon.png docs/img/display_icon.png docs/img/speaker_icon.png
docs/img/sparkling_icon.png docs/img/battery_icon.png docs/img/sdcard_icon.png
docs/img/rnap_icon.png docs/img/rcow_icon.png docs/img/skyemu_icon.png
)
# Translation files in Qt's .ts format. Will be converted into binary files and embedded into the executable
set(TRANSLATIONS_TS docs/translations/en.ts docs/translations/el.ts docs/translations/es.ts docs/translations/pt_br.ts docs/translations/nl.ts)
set_source_files_properties(${TRANSLATIONS_TS} PROPERTIES OUTPUT_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/translations")
qt_add_translation(TRANSLATIONS_QM ${TRANSLATIONS_TS})
set(TRANSLATIONS_QRC ${CMAKE_CURRENT_BINARY_DIR}/translations/translations.qrc)
file(WRITE ${TRANSLATIONS_QRC} "<RCC><qresource prefix=\"translations\">\n")
foreach (QM ${TRANSLATIONS_QM})
message("${QM}")
get_filename_component(QM_FILE ${QM} NAME)
file(APPEND ${TRANSLATIONS_QRC} "<file>${QM_FILE}</file>\n")
endforeach (QM)
file(APPEND ${TRANSLATIONS_QRC} "</qresource></RCC>")
qt_add_resources(TRANSLATIONS ${TRANSLATIONS_QRC})
set(APP_RESOURCES ${APP_RESOURCES} ${TRANSLATIONS})
else()
set(FRONTEND_SOURCE_FILES src/panda_sdl/main.cpp src/panda_sdl/frontend_sdl.cpp src/panda_sdl/mappings.cpp)
set(FRONTEND_HEADER_FILES "")
set(FRONTEND_HEADER_FILES "include/panda_sdl/frontend_sdl.hpp")
endif()
target_link_libraries(Alber PRIVATE AlberCore)
target_sources(Alber PRIVATE ${FRONTEND_SOURCE_FILES} ${FRONTEND_HEADER_FILES})
target_sources(Alber PRIVATE ${FRONTEND_SOURCE_FILES} ${FRONTEND_HEADER_FILES} ${GL_CONTEXT_SOURCE_FILES} ${APP_RESOURCES})
elseif(BUILD_HYDRA_CORE)
target_compile_definitions(AlberCore PRIVATE PANDA3DS_HYDRA_CORE=1)
include_directories(third_party/hydra_core/include)
@ -513,17 +786,17 @@ elseif(BUILD_HYDRA_CORE)
target_link_libraries(Alber PUBLIC AlberCore)
elseif(BUILD_LIBRETRO_CORE)
include_directories(third_party/libretro/include)
add_library(Alber SHARED src/libretro_core.cpp)
target_link_libraries(Alber PUBLIC AlberCore)
set_target_properties(Alber PROPERTIES
OUTPUT_NAME "panda3ds_libretro"
PREFIX ""
)
add_library(panda3ds_libretro SHARED src/libretro_core.cpp)
target_link_libraries(panda3ds_libretro PUBLIC AlberCore)
set_target_properties(panda3ds_libretro PROPERTIES PREFIX "")
endif()
if(ENABLE_LTO OR ENABLE_USER_BUILD)
set_target_properties(Alber PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE)
if (NOT BUILD_LIBRETRO_CORE)
set_target_properties(Alber PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE)
else()
set_target_properties(panda3ds_libretro PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE)
endif()
endif()
if(ENABLE_TESTS)

25
cmake/FindRenderDoc.cmake Normal file
View file

@ -0,0 +1,25 @@
# SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later
set(RENDERDOC_INCLUDE_DIR third_party/renderdoc)
if (RENDERDOC_INCLUDE_DIR AND EXISTS "${RENDERDOC_INCLUDE_DIR}/renderdoc_app.h")
file(STRINGS "${RENDERDOC_INCLUDE_DIR}/renderdoc_app.h" RENDERDOC_VERSION_LINE REGEX "typedef struct RENDERDOC_API")
string(REGEX REPLACE ".*typedef struct RENDERDOC_API_([0-9]+)_([0-9]+)_([0-9]+).*" "\\1.\\2.\\3" RENDERDOC_VERSION "${RENDERDOC_VERSION_LINE}")
unset(RENDERDOC_VERSION_LINE)
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(RenderDoc
REQUIRED_VARS RENDERDOC_INCLUDE_DIR
VERSION_VAR RENDERDOC_VERSION
)
if (RenderDoc_FOUND AND NOT TARGET RenderDoc::API)
add_library(RenderDoc::API INTERFACE IMPORTED)
set_target_properties(RenderDoc::API PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${RENDERDOC_INCLUDE_DIR}"
)
endif()
mark_as_advanced(RENDERDOC_INCLUDE_DIR)

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

79
docs/3ds/lighting.md Normal file
View file

@ -0,0 +1,79 @@
## Info on the lighting implementation
### Missing shadow attenuation
Shadow attenuation samples a texture unit, and that likely needs render to texture for most games so that they can construct
their shadow map. As such the colors are not multiplied by the shadow attenuation value, so there's no shadows.
### Missing bump mapping
Bump mapping also samples a texture unit, most likely doesn't need render to texture however may need better texture sampling
implementation (such as GPUREG_TEXUNITi_BORDER_COLOR, GPUREG_TEXUNITi_BORDER_PARAM). Bump mapping would work for some things,
namely the 3ds-examples bump mapping demo, but would break others such as Toad Treasure Tracker with a naive `texture` implementation.
Also the CP configuration is missing, because it needs a tangent map implementation. It is currently marked with error_unimpl.
### samplerEnabledBitfields
Holds the enabled state of the lighting samples for various PICA configurations
As explained in https://www.3dbrew.org/wiki/GPU/Internal_Registers#GPUREG_LIGHTING_CONFIG0
```c
const bool samplerEnabled[9 * 7] = bool[9 * 7](
// D0 D1 SP FR RB RG RR
true, false, true, false, false, false, true, // Configuration 0: D0, SP, RR
false, false, true, true, false, false, true, // Configuration 1: FR, SP, RR
true, true, false, false, false, false, true, // Configuration 2: D0, D1, RR
true, true, false, true, false, false, false, // Configuration 3: D0, D1, FR
true, true, true, false, true, true, true, // Configuration 4: All except for FR
true, false, true, true, true, true, true, // Configuration 5: All except for D1
true, true, true, true, false, false, true, // Configuration 6: All except for RB and RG
false, false, false, false, false, false, false, // Configuration 7: Unused
true, true, true, true, true, true, true // Configuration 8: All
);
```
The above has been condensed to two uints for performance reasons.
You can confirm they are the same by running the following:
```c
const uint samplerEnabledBitfields[2] = { 0x7170e645u, 0x7f013fefu };
for (int i = 0; i < 9 * 7; i++) {
unsigned arrayIndex = (i >> 5);
bool b = (samplerEnabledBitfields[arrayIndex] & (1u << (i & 31))) != 0u;
if (samplerEnabled[i] == b) {
printf("%d: happy\n", i);
} else {
printf("%d: unhappy\n", i);
}
}
```
### lightLutLookup
lut_id is one of these values
0 D0
1 D1
2 SP
3 FR
4 RB
5 RG
6 RR
lut_index on the other hand represents the actual index of the LUT in the texture
u_tex_luts has 24 LUTs for lighting and they are used like so:
0 D0
1 D1
2 is missing because SP uses LUTs 8-15
3 FR
4 RB
5 RG
6 RR
8-15 SP0-7
16-23 DA0-7, but this is not handled in this function as the lookup is a bit different
The light environment configuration controls which LUTs are available for use
If a LUT is not available in the selected configuration, its value will always read a constant 1.0 regardless of the enable state in GPUREG_LIGHTING_CONFIG1
If RR is enabled but not RG or RB, the output of RR is used for the three components; Red, Green and Blue.
### Distance attenuation
Distance attenuation is computed differently from the other factors, for example
it doesn't store its scale in GPUREG_LIGHTING_LUTINPUT_SCALE and it doesn't use
GPUREG_LIGHTING_LUTINPUT_SELECT. Instead, it uses the distance from the light to the
fragment and the distance attenuation scale and bias to calculate where in the LUT to look up.
See: https://www.3dbrew.org/wiki/GPU/Internal_Registers#GPUREG_LIGHTi_ATTENUATION_SCALE

BIN
docs/img/KirbyAndroid.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 567 KiB

BIN
docs/img/battery_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

BIN
docs/img/display_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 B

View file

Before

Width:  |  Height:  |  Size: 2 MiB

After

Width:  |  Height:  |  Size: 2 MiB

BIN
docs/img/rcow_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
docs/img/rnap_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

BIN
docs/img/rsyn_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

BIN
docs/img/sdcard_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

BIN
docs/img/settings_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 697 B

BIN
docs/img/skyemu_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
docs/img/sparkling_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 549 B

BIN
docs/img/speaker_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 507 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

BIN
docs/img/windows_icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

1
docs/img/windows_icon.rc Normal file
View file

@ -0,0 +1 @@
AlberIcon ICON "windows_icon.ico"

View file

@ -1,7 +1,7 @@
# Software Information
display_name = "Nintendo - 3DS (Panda3DS)"
authors = "Panda3DS Authors (tm)"
supported_extensions = "3ds|3dsx|elf|axf|cci|cxi|app"
supported_extensions = "3ds|3dsx|elf|axf|cci|cxi|app|ncch"
corename = "Panda3DS"
categories = "Emulator"
license = "GPLv3"

763
docs/translations/el.ts Normal file
View file

@ -0,0 +1,763 @@
<?xml version="1.0" ?><!DOCTYPE TS><TS version="2.1" language="el">
<context>
<name>AboutWindow</name>
<message>
<location filename="../../src/panda_qt/about_window.cpp" line="16"/>
<source>About Panda3DS</source>
<translation>Σχετικά με το Panda3DS</translation>
</message>
<message>
<location filename="../../src/panda_qt/about_window.cpp" line="35"/>
<source>Panda3DS is a free and open source Nintendo 3DS emulator, for Windows, MacOS and Linux</source>
<translation>Τo Panda3DS είναι ένας δωρεάν και open source εξομοιωτής του Nintendo 3DS, για Windows, MacOS και Linux</translation>
</message>
<message>
<location filename="../../src/panda_qt/about_window.cpp" line="36"/>
<source>Visit panda3ds.com for help with Panda3DS and links to our official support sites.</source>
<translation>Επισκεφτείται το panda3ds.com για βοήθεια με το Panda3DS και συνδέσμους στις επίσημες σελίδες υποστήριξης μας.</translation>
</message>
<message>
<location filename="../../src/panda_qt/about_window.cpp" line="38"/>
<source>Panda3DS is developed by volunteers in their spare time. Below is a list of some of these volunteers who&apos;ve agreed to be listed here, in no particular order.&lt;br&gt;If you think you should be listed here too, please inform us&lt;br&gt;&lt;br&gt;- Peach (wheremyfoodat)&lt;br&gt;- noumidev&lt;br&gt;- liuk707&lt;br&gt;- Wunk&lt;br&gt;- marysaka&lt;br&gt;- Sky&lt;br&gt;- merryhime&lt;br&gt;- TGP17&lt;br&gt;- Shadow&lt;br&gt;</source>
<translation type="unfinished"/>
</message>
</context>
<context>
<name>CheatEditDialog</name>
<message>
<location filename="../../src/panda_qt/cheats_window.cpp" line="72"/>
<source>Edit Cheat</source>
<translation>Επεξεργασία Κωδικού</translation>
</message>
<message>
<location filename="../../src/panda_qt/cheats_window.cpp" line="82"/>
<source>Cheat name</source>
<translation>Όνομα κωδικού</translation>
</message>
</context>
<context>
<name>CheatEntryWidget</name>
<message>
<location filename="../../src/panda_qt/cheats_window.cpp" line="34"/>
<source>Edit</source>
<translation>Επεξεργασία</translation>
</message>
</context>
<context>
<name>CheatsWindow</name>
<message>
<location filename="../../src/panda_qt/cheats_window.cpp" line="164"/>
<source>Cheats</source>
<translation>Κωδικοί</translation>
</message>
<message>
<location filename="../../src/panda_qt/cheats_window.cpp" line="177"/>
<source>Add</source>
<translation>Προσθήκη</translation>
</message>
<message>
<location filename="../../src/panda_qt/cheats_window.cpp" line="178"/>
<source>Remove</source>
<translation>Αφαίρεση</translation>
</message>
</context>
<context>
<name>ConfigWindow</name>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="7"/>
<source>Configuration</source>
<translation>Ρυθμίσεις</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="63"/>
<source>Interface Settings</source>
<translation>Ρυθμίσεις Διεπαφής</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="69"/>
<source>System</source>
<translation>Σύστημα</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="70"/>
<source>Light</source>
<translation>Φωτεινό</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="71"/>
<source>Dark</source>
<translation>Σκοτεινό</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="72"/>
<source>Greetings Cat</source>
<translation>Γεια Σου Γάτα</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="73"/>
<source>Cream</source>
<translation>Κρέμα</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="81"/>
<source>Color theme</source>
<translation>Χρώματα</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="84"/>
<source>Happy panda</source>
<translation>Χαρούμενο Πάντα</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="85"/>
<source>Happy panda (colourful)</source>
<translation>Χαρούμενο Πάντα (χρωματιστό)</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="86"/>
<source>Sleepy panda</source>
<translation>Πάντα που νυστάζει</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="87"/>
<source>Cow panda</source>
<translation>Αγελάδα πάντα</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="88"/>
<source>The penguin from SkyEmu</source>
<translation>Ο πιγκουίνος από το SkyEmu</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="97"/>
<source>Window icon</source>
<translation>Εικονίδιο Παραθύρου</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="100"/>
<source>Language</source>
<translation>Γλώσσα</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="102"/>
<source>Show version on window title</source>
<translation>Εμφάνιση έκδοσης στον τίτλο του παραθύρου</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="109"/>
<source>Alber v%1</source>
<translation>Αλβέρτος v%1</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="109"/>
<source>Alber</source>
<translation>Αλβέρτος</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="114"/>
<source>Remember window position</source>
<translation>Αποθήκευση θέσης παραθύρου</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="119"/>
<source>General Settings</source>
<translation>Γενικές Ρυθμίσεις</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="130"/>
<source>Browse...</source>
<translation>Περιήγηση</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="134"/>
<source>Select Directory</source>
<translation>Επιλογή φακέλου</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="146"/>
<source>Default ROMs path</source>
<translation>Προεπιλεγμένος φάκελος ROM</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="148"/>
<source>Enable Discord RPC</source>
<translation>Ενεργοποίηση Discord RPC</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="152"/>
<source>Use portable build</source>
<translation>Ενεργοποίηση φορητής εγκατάστασης</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="156"/>
<source>Print version in console output</source>
<translation>Εκτύπωση έκδοσης στην κονσόλα</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="161"/>
<source>Graphics Settings</source>
<translation>Ρυθμίσεις Γραφικών</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="167"/>
<location filename="../../src/panda_qt/config_window.cpp" line="221"/>
<source>Null</source>
<translation>Κανένα</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="168"/>
<source>OpenGL</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="169"/>
<source>Vulkan</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="175"/>
<source>GPU renderer</source>
<translation>Πυρήνας GPU</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="177"/>
<source>Enable Renderdoc</source>
<translation>Ενεργοποίηση Renderdoc</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="181"/>
<source>Enable shader JIT</source>
<translation>Ενεργοποίηση μεταγλωττιστή shaders</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="185"/>
<source>Enable VSync</source>
<translation>Ενεργοποίηση VSync</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="189"/>
<source>Use ubershaders (No stutter, maybe slower)</source>
<translation>Χρήση ubershaders (Χωρίς stutter, ίσως πιο αργό)</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="193"/>
<source>Accurate shader multiplication</source>
<translation>Ακριβής πολλαπλασιασμός στα shaders</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="197"/>
<source>Accelerate shaders</source>
<translation>Επιτάχυνση shaders</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="201"/>
<source>Force shadergen when rendering lights</source>
<translation>Εξαναγκασμός shadergen όταν υπάρχουν φώτα</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="212"/>
<source>Light threshold for forcing shadergen</source>
<translation>Αριθμός φωτών για εξαναγκασμό shadergen</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="215"/>
<source>Audio Settings</source>
<translation>Ρυθμίσεις Ήχου</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="222"/>
<source>LLE</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="223"/>
<source>HLE</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="229"/>
<source>DSP emulation</source>
<translation>Εξομοίωση DSP</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="231"/>
<source>Enable audio</source>
<translation>Ενεργοποίηση ήχου</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="235"/>
<source>Enable AAC audio</source>
<translation>Ενεργοποίηση ήχου AAC</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="239"/>
<source>Print DSP firmware</source>
<translation>Εκτύπωση λογισμικού DSP</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="243"/>
<source>Mute audio device</source>
<translation>Σίγαση συσκευής ήχου</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="248"/>
<source>Cubic</source>
<translation>Κυβική</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="249"/>
<source>Linear</source>
<translation>Γραμμική</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="255"/>
<source>Volume curve</source>
<translation>Κλίμακα ήχου</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="273"/>
<source>Audio device volume</source>
<translation>Ένταση ήχου</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="276"/>
<source>Battery Settings</source>
<translation>Ρυθμίσεις μπαταρίας</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="288"/>
<source>Battery percentage</source>
<translation>Ποσοστό μπαταρίας</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="290"/>
<source>Charger plugged</source>
<translation>Φορτιστής</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="295"/>
<source>SD Card Settings</source>
<translation>Ρυθμίσης κάρτας SD</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="300"/>
<source>Enable virtual SD card</source>
<translation>Ενεργοποίηση εικονικής SD</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="304"/>
<source>Write protect virtual SD card</source>
<translation>Προστασία της SD από εγγραφή</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="309"/>
<source>Interface</source>
<translation>Διεπαφή</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="309"/>
<source>User Interface settings</source>
<translation>Ρυθμίσεις διεπαφής</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="310"/>
<source>General</source>
<translation>Γενικά</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="310"/>
<source>General emulator settings</source>
<translation>Γενικές ρυθμίσεις εξομοιωτή</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="311"/>
<source>Graphics</source>
<translation>Γραφικά</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="311"/>
<source>Graphics emulation and output settings</source>
<translation>Ρυθμίσεις εξομοίωσης γραφικών</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="312"/>
<source>Audio</source>
<translation>Ήχος</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="312"/>
<source>Audio emulation and output settings</source>
<translation>Ρυθμίσεις εξομοίωσης ήχου</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="313"/>
<source>Battery</source>
<translation>Μπαταρία</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="313"/>
<source>Battery emulation settings</source>
<translation>Ρυθμίσεις εξομοίωσης μπαταρίας</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="314"/>
<source>SD Card</source>
<translation>Κάρτα SD</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="314"/>
<source>SD Card emulation settings</source>
<translation>Ρυθμίσεις εξομοίωσης κάρτας SD</translation>
</message>
<message>
<location filename="../../src/panda_qt/translations.cpp" line="75"/>
<source>Language change successful</source>
<translation>Επιτυχία αλλαγής γλώσσας</translation>
</message>
<message>
<location filename="../../src/panda_qt/translations.cpp" line="76"/>
<source>Restart Panda3DS for the new language to be used.</source>
<translation>Επανεκκινήστε το Panda3DS για να εφαρμοστεί η νέα γλώσσα.</translation>
</message>
<message>
<location filename="../../src/panda_qt/translations.cpp" line="82"/>
<source>Language change failed</source>
<translation>Αποτυχία αλλαγής γλώσσας</translation>
</message>
<message>
<location filename="../../src/panda_qt/translations.cpp" line="83"/>
<source>The language you selected is not included in Panda3DS. If you&apos;re seeing this, someone messed up the language UI code...</source>
<translation>Το Panda3DS δεν υποστηρίζει τον γλώσσα που επιλέξατε. Αν το βλέπετε αυτό, κάποιος έκανε λάθος στον κώδικα, κατηγορήστε τον Πάρη...</translation>
</message>
</context>
<context>
<name>MainWindow</name>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="20"/>
<source>Alber</source>
<translation>Αλβέρτος</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="38"/>
<source>File</source>
<translation>Αρχεία</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="39"/>
<source>Emulation</source>
<translation>Εξομοίωση</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="40"/>
<source>Tools</source>
<translation>Εργαλεία</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="41"/>
<source>About</source>
<translation>Σχετικά</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="44"/>
<source>Load game</source>
<translation>Φόρτωση παιχνιδιού</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="45"/>
<source>Load Lua script</source>
<translation>Φόρτωση αρχείου Lua</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="46"/>
<source>Open Panda3DS folder</source>
<translation>Άνοιγμα φακέλου Panda3DS</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="55"/>
<source>Pause</source>
<translation>Παύση</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="56"/>
<source>Resume</source>
<translation>Συνέχεια</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="57"/>
<source>Reset</source>
<translation>Επανέναρξη</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="58"/>
<source>Configure</source>
<translation>Ρύθμιση</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="64"/>
<source>Dump RomFS</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="65"/>
<source>Open Lua Editor</source>
<translation>Άνοιγμα Lua Editor</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="66"/>
<source>Open Cheats Editor</source>
<translation>Άνοιγμα Editor κωδικών</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="67"/>
<source>Open Patch Window</source>
<translation>Άνοιγμα παραθύρου για patching</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="68"/>
<source>Open Shader Editor</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="69"/>
<source>Dump loaded DSP firmware</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="78"/>
<source>About Panda3DS</source>
<translation>Σχετικά με το Panda3DS</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="195"/>
<source>Select 3DS ROM to load</source>
<translation>Επιλέξτε 3DS ROM για να φορτώσετε</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="196"/>
<source>Nintendo 3DS ROMs (*.3ds *.cci *.cxi *.app *.ncch *.3dsx *.elf *.axf)</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="209"/>
<source>Select Lua script to load</source>
<translation>Επιλέξτε αρχείο Lua για να φορτώσετε</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="209"/>
<source>Lua scripts (*.lua *.txt)</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="272"/>
<source>Select folder to dump RomFS files to</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="288"/>
<source>Invalid format for RomFS dumping</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="289"/>
<source>The currently loaded app is not in a format that supports RomFS</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="292"/>
<location filename="../../src/panda_qt/main_window.cpp" line="323"/>
<location filename="../../src/panda_qt/main_window.cpp" line="336"/>
<source>OK</source>
<translation>ΟΚ</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="299"/>
<source>No RomFS found</source>
<translation>Δεν βρέθηκε RomFS</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="299"/>
<source>No RomFS partition was found in the loaded app</source>
<translation>Δεν βρέθηκε RomFS στην εφαρμογή που έχει φορτωθεί</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="305"/>
<source>Select file</source>
<translation>Επιλέξτε αρχείο</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="305"/>
<source>DSP firmware file (*.cdc)</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="320"/>
<source>No DSP firmware loaded</source>
<translation>Δεν έχει φορτωθεί DSP firmware</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="320"/>
<source>The currently loaded app has not uploaded a firmware to the DSP</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="331"/>
<source>Failed to open output file</source>
<translation>Αποτυχία ανοίγματος του αρχείου εξόδου</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="332"/>
<source>The currently loaded DSP firmware could not be written to the selected file. Please make sure you have permission to access this file</source>
<translation type="unfinished"/>
</message>
</context>
<context>
<name>PatchWindow</name>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="15"/>
<source>ROM patcher</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="23"/>
<source>Select input file</source>
<translation>Επιλογή αρχείου εισόδου</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="24"/>
<location filename="../../src/panda_qt/patch_window.cpp" line="36"/>
<source>Select</source>
<translation>Επιλέξτε</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="35"/>
<location filename="../../src/panda_qt/patch_window.cpp" line="63"/>
<source>Select patch file</source>
<translation>Επιλογή αρχείου patch</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="47"/>
<source>Apply patch</source>
<translation>Εφαρμογή patch</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="56"/>
<source>Select file to patch</source>
<translation>Επιλέξτε αρχείο να κάνετε patch</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="56"/>
<location filename="../../src/panda_qt/patch_window.cpp" line="80"/>
<source>All files (*.*)</source>
<translation>Όλα τα αρχεία (*.*)</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="63"/>
<source>Patch files (*.ips *.ups *.bps)</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="71"/>
<source>Paths not provided correctly</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="71"/>
<source>Please provide paths for both the input file and the patch file</source>
<translation>Παρακαλούμε διαλέξτε και αρχείο εισόδου και αρχείο patch</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="80"/>
<source>Select file</source>
<translation>Επιλογή αρχείου</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="84"/>
<source>No output path</source>
<translation>Δεν επιλέχθηκε φάκελος εξόδου</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="84"/>
<source>No path was provided for the output file, no patching was done</source>
<translation>Δεν επιλέχθηκε αρχείο εξόδου. Δεν έγινε patching</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="99"/>
<source>Unknown patch format</source>
<translation>Άγνωστο είδος patch</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="99"/>
<source>Unknown format for patch file. Currently IPS, UPS and BPS are supported</source>
<translation>Άγνωστο είδος αρχείου patch. Υποστηρίζονται αρχεία IPS, UPS και BPS</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="108"/>
<source>Failed to open input files</source>
<translation>Αποτυχία ανοίγματος των αρχείων εισόδου</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="108"/>
<source>Make sure they&apos;re in a directory Panda3DS has access to</source>
<translation>Βεβαιωθείτε ότι είναι σε φάκελο που έχει πρόσβαση το Panda3DS</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="135"/>
<source>Patching Success</source>
<translation>Επιτυχής Εφαρμογή Patch</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="135"/>
<source>Your file was patched successfully.</source>
<translation>To αρχείο σας έγινε patch με επιτυχία</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="141"/>
<source>Checksum mismatch</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="142"/>
<source>Patch was applied successfully but a checksum mismatch was detected. The input or output files might not be correct</source>
<translation>Το patch εφαρμόστηκε με επιτυχία αλλά ανιχνεύτηκε σφάλμα στο checksum. Ενδέχεται τα αρχεία εισόδου η εξόδου να είναι λανθασμένα</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="146"/>
<source>Patching error</source>
<translation>Σφάλμα στο patching</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="146"/>
<source>An error occured while patching</source>
<translation>Προέκυψε σφάλμα στο patching</translation>
</message>
</context>
<context>
<name>PatchWindow::PatchWindow</name>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="153"/>
<source>OK</source>
<translation>ΟΚ</translation>
</message>
</context>
<context>
<name>ShaderEditorWindow</name>
<message>
<location filename="../../src/panda_qt/shader_editor.cpp" line="26"/>
<source>Reload shader</source>
<translation>Επαναφόρτωση shader</translation>
</message>
</context>
<context>
<name>TextEditorWindow</name>
<message>
<location filename="../../src/panda_qt/text_editor.cpp" line="12"/>
<source>Lua Editor</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../../src/panda_qt/text_editor.cpp" line="27"/>
<source>Load script</source>
<translation>Φόρτωση αρχείου</translation>
</message>
</context>
</TS>

766
docs/translations/en.ts Normal file
View file

@ -0,0 +1,766 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1">
<context>
<name>AboutWindow</name>
<message>
<location filename="../../src/panda_qt/about_window.cpp" line="16"/>
<source>About Panda3DS</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/about_window.cpp" line="35"/>
<source>Panda3DS is a free and open source Nintendo 3DS emulator, for Windows, MacOS and Linux</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/about_window.cpp" line="36"/>
<source>Visit panda3ds.com for help with Panda3DS and links to our official support sites.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/about_window.cpp" line="38"/>
<source>Panda3DS is developed by volunteers in their spare time. Below is a list of some of these volunteers who&apos;ve agreed to be listed here, in no particular order.&lt;br&gt;If you think you should be listed here too, please inform us&lt;br&gt;&lt;br&gt;- Peach (wheremyfoodat)&lt;br&gt;- noumidev&lt;br&gt;- liuk707&lt;br&gt;- Wunk&lt;br&gt;- marysaka&lt;br&gt;- Sky&lt;br&gt;- merryhime&lt;br&gt;- TGP17&lt;br&gt;- Shadow&lt;br&gt;</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>CheatEditDialog</name>
<message>
<location filename="../../src/panda_qt/cheats_window.cpp" line="72"/>
<source>Edit Cheat</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/cheats_window.cpp" line="82"/>
<source>Cheat name</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>CheatEntryWidget</name>
<message>
<location filename="../../src/panda_qt/cheats_window.cpp" line="34"/>
<source>Edit</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>CheatsWindow</name>
<message>
<location filename="../../src/panda_qt/cheats_window.cpp" line="164"/>
<source>Cheats</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/cheats_window.cpp" line="177"/>
<source>Add</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/cheats_window.cpp" line="178"/>
<source>Remove</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ConfigWindow</name>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="7"/>
<source>Configuration</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="63"/>
<source>Interface Settings</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="69"/>
<source>System</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="70"/>
<source>Light</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="71"/>
<source>Dark</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="72"/>
<source>Greetings Cat</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="73"/>
<source>Cream</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="81"/>
<source>Color theme</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="84"/>
<source>Happy panda</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="85"/>
<source>Happy panda (colourful)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="86"/>
<source>Sleepy panda</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="87"/>
<source>Cow panda</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="88"/>
<source>The penguin from SkyEmu</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="97"/>
<source>Window icon</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="100"/>
<source>Language</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="102"/>
<source>Show version on window title</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="15"/>
<location filename="../../src/panda_qt/config_window.cpp" line="109"/>
<source>Alber v%1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="109"/>
<source>Alber</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="114"/>
<source>Remember window position</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="119"/>
<source>General Settings</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="130"/>
<source>Browse...</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="134"/>
<source>Select Directory</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="146"/>
<source>Default ROMs path</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="148"/>
<source>Enable Discord RPC</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="152"/>
<source>Use portable build</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="156"/>
<source>Print version in console output</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="161"/>
<source>Graphics Settings</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="167"/>
<location filename="../../src/panda_qt/config_window.cpp" line="221"/>
<source>Null</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="168"/>
<source>OpenGL</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="169"/>
<source>Vulkan</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="175"/>
<source>GPU renderer</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="177"/>
<source>Enable Renderdoc</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="181"/>
<source>Enable shader JIT</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="185"/>
<source>Enable VSync</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="189"/>
<source>Use ubershaders (No stutter, maybe slower)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="193"/>
<source>Accurate shader multiplication</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="197"/>
<source>Accelerate shaders</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="201"/>
<source>Force shadergen when rendering lights</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="212"/>
<source>Light threshold for forcing shadergen</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="215"/>
<source>Audio Settings</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="222"/>
<source>LLE</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="223"/>
<source>HLE</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="229"/>
<source>DSP emulation</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="231"/>
<source>Enable audio</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="235"/>
<source>Enable AAC audio</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="239"/>
<source>Print DSP firmware</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="243"/>
<source>Mute audio device</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="248"/>
<source>Cubic</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="249"/>
<source>Linear</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="255"/>
<source>Volume curve</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="273"/>
<source>Audio device volume</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="276"/>
<source>Battery Settings</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="288"/>
<source>Battery percentage</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="290"/>
<source>Charger plugged</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="295"/>
<source>SD Card Settings</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="300"/>
<source>Enable virtual SD card</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="304"/>
<source>Write protect virtual SD card</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="309"/>
<source>Interface</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="309"/>
<source>User Interface settings</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="310"/>
<source>General</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="310"/>
<source>General emulator settings</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="311"/>
<source>Graphics</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="311"/>
<source>Graphics emulation and output settings</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="312"/>
<source>Audio</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="312"/>
<source>Audio emulation and output settings</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="313"/>
<source>Battery</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="313"/>
<source>Battery emulation settings</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="314"/>
<source>SD Card</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="314"/>
<source>SD Card emulation settings</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/translations.cpp" line="75"/>
<source>Language change successful</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/translations.cpp" line="76"/>
<source>Restart Panda3DS for the new language to be used.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/translations.cpp" line="82"/>
<source>Language change failed</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/translations.cpp" line="83"/>
<source>The language you selected is not included in Panda3DS. If you&apos;re seeing this, someone messed up the language UI code...</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>MainWindow</name>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="20"/>
<source>Alber</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="38"/>
<source>File</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="39"/>
<source>Emulation</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="40"/>
<source>Tools</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="41"/>
<source>About</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="44"/>
<source>Load game</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="45"/>
<source>Load Lua script</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="46"/>
<source>Open Panda3DS folder</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="55"/>
<source>Pause</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="56"/>
<source>Resume</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="57"/>
<source>Reset</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="58"/>
<source>Configure</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="64"/>
<source>Dump RomFS</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="65"/>
<source>Open Lua Editor</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="66"/>
<source>Open Cheats Editor</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="67"/>
<source>Open Patch Window</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="68"/>
<source>Open Shader Editor</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="69"/>
<source>Dump loaded DSP firmware</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="78"/>
<source>About Panda3DS</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="195"/>
<source>Select 3DS ROM to load</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="196"/>
<source>Nintendo 3DS ROMs (*.3ds *.cci *.cxi *.app *.ncch *.3dsx *.elf *.axf)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="209"/>
<source>Select Lua script to load</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="209"/>
<source>Lua scripts (*.lua *.txt)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="272"/>
<source>Select folder to dump RomFS files to</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="288"/>
<source>Invalid format for RomFS dumping</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="289"/>
<source>The currently loaded app is not in a format that supports RomFS</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="292"/>
<location filename="../../src/panda_qt/main_window.cpp" line="323"/>
<location filename="../../src/panda_qt/main_window.cpp" line="336"/>
<source>OK</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="299"/>
<source>No RomFS found</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="299"/>
<source>No RomFS partition was found in the loaded app</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="305"/>
<source>Select file</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="305"/>
<source>DSP firmware file (*.cdc)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="320"/>
<source>No DSP firmware loaded</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="320"/>
<source>The currently loaded app has not uploaded a firmware to the DSP</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="331"/>
<source>Failed to open output file</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="332"/>
<source>The currently loaded DSP firmware could not be written to the selected file. Please make sure you have permission to access this file</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PatchWindow</name>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="15"/>
<source>ROM patcher</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="23"/>
<source>Select input file</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="24"/>
<location filename="../../src/panda_qt/patch_window.cpp" line="36"/>
<source>Select</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="35"/>
<location filename="../../src/panda_qt/patch_window.cpp" line="63"/>
<source>Select patch file</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="47"/>
<source>Apply patch</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="56"/>
<source>Select file to patch</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="56"/>
<location filename="../../src/panda_qt/patch_window.cpp" line="80"/>
<source>All files (*.*)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="63"/>
<source>Patch files (*.ips *.ups *.bps)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="71"/>
<source>Paths not provided correctly</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="71"/>
<source>Please provide paths for both the input file and the patch file</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="80"/>
<source>Select file</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="84"/>
<source>No output path</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="84"/>
<source>No path was provided for the output file, no patching was done</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="99"/>
<source>Unknown patch format</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="99"/>
<source>Unknown format for patch file. Currently IPS, UPS and BPS are supported</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="108"/>
<source>Failed to open input files</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="108"/>
<source>Make sure they&apos;re in a directory Panda3DS has access to</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="135"/>
<source>Patching Success</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="135"/>
<source>Your file was patched successfully.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="141"/>
<source>Checksum mismatch</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="142"/>
<source>Patch was applied successfully but a checksum mismatch was detected. The input or output files might not be correct</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="146"/>
<source>Patching error</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="146"/>
<source>An error occured while patching</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PatchWindow::PatchWindow</name>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="153"/>
<source>OK</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ShaderEditorWindow</name>
<message>
<location filename="../../src/panda_qt/shader_editor.cpp" line="26"/>
<source>Reload shader</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>TextEditorWindow</name>
<message>
<location filename="../../src/panda_qt/text_editor.cpp" line="12"/>
<source>Lua Editor</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../src/panda_qt/text_editor.cpp" line="27"/>
<source>Load script</source>
<translation type="unfinished"></translation>
</message>
</context>
</TS>

763
docs/translations/es.ts Normal file
View file

@ -0,0 +1,763 @@
<?xml version="1.0" ?><!DOCTYPE TS><TS version="2.1" language="es">
<context>
<name>AboutWindow</name>
<message>
<location filename="../../src/panda_qt/about_window.cpp" line="16"/>
<source>About Panda3DS</source>
<translation>Acerca de Panda3DS</translation>
</message>
<message>
<location filename="../../src/panda_qt/about_window.cpp" line="35"/>
<source>Panda3DS is a free and open source Nintendo 3DS emulator, for Windows, MacOS and Linux</source>
<translation>Panda3DS es un emulador libre y de código abierto de Nintendo 3DS para Windows, MacOS y Linux</translation>
</message>
<message>
<location filename="../../src/panda_qt/about_window.cpp" line="36"/>
<source>Visit panda3ds.com for help with Panda3DS and links to our official support sites.</source>
<translation>Visita panda3ds.com para obtener ayuda con Panda3DS y los links a nuestras páginas oficiales de soporte.</translation>
</message>
<message>
<location filename="../../src/panda_qt/about_window.cpp" line="38"/>
<source>Panda3DS is developed by volunteers in their spare time. Below is a list of some of these volunteers who&apos;ve agreed to be listed here, in no particular order.&lt;br&gt;If you think you should be listed here too, please inform us&lt;br&gt;&lt;br&gt;- Peach (wheremyfoodat)&lt;br&gt;- noumidev&lt;br&gt;- liuk707&lt;br&gt;- Wunk&lt;br&gt;- marysaka&lt;br&gt;- Sky&lt;br&gt;- merryhime&lt;br&gt;- TGP17&lt;br&gt;- Shadow&lt;br&gt;</source>
<translation>Panda3DS es desarrollado por voluntarios en su tiempo libre. Debajo se encuentran los voluntarios que están de acuerdo con ser listados aquí, en ningún orden en particular.&lt;br&gt;Si piensas que deberías ser listado, por favor infórmanos&lt;br&gt;&lt;br&gt;- Peach (wheremyfoodat)&lt;br&gt;- noumidev&lt;br&gt;- liuk707&lt;br&gt;- Wunk&lt;br&gt;- marysaka&lt;br&gt;- Sky&lt;br&gt;- merryhime&lt;br&gt;- TGP17&lt;br&gt;- Shadow&lt;br&gt;</translation>
</message>
</context>
<context>
<name>CheatEditDialog</name>
<message>
<location filename="../../src/panda_qt/cheats_window.cpp" line="72"/>
<source>Edit Cheat</source>
<translation>Editar Truco</translation>
</message>
<message>
<location filename="../../src/panda_qt/cheats_window.cpp" line="82"/>
<source>Cheat name</source>
<translation>Nombre del truco</translation>
</message>
</context>
<context>
<name>CheatEntryWidget</name>
<message>
<location filename="../../src/panda_qt/cheats_window.cpp" line="34"/>
<source>Edit</source>
<translation>Editar</translation>
</message>
</context>
<context>
<name>CheatsWindow</name>
<message>
<location filename="../../src/panda_qt/cheats_window.cpp" line="164"/>
<source>Cheats</source>
<translation>Trucos</translation>
</message>
<message>
<location filename="../../src/panda_qt/cheats_window.cpp" line="177"/>
<source>Add</source>
<translation>Añadir</translation>
</message>
<message>
<location filename="../../src/panda_qt/cheats_window.cpp" line="178"/>
<source>Remove</source>
<translation>Quitar</translation>
</message>
</context>
<context>
<name>ConfigWindow</name>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="7"/>
<source>Configuration</source>
<translation>Configuración</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="63"/>
<source>Interface Settings</source>
<translation>Configuración de Interfaz</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="69"/>
<source>System</source>
<translation>Sistema</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="70"/>
<source>Light</source>
<translation>Claro</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="71"/>
<source>Dark</source>
<translation>Oscuro</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="72"/>
<source>Greetings Cat</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="73"/>
<source>Cream</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="81"/>
<source>Color theme</source>
<translation>Tema de color</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="84"/>
<source>Happy panda</source>
<translation>Panda feliz</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="85"/>
<source>Happy panda (colourful)</source>
<translation>Panda feliz (colorido)</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="86"/>
<source>Sleepy panda</source>
<translation>Panda somnoliento</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="87"/>
<source>Cow panda</source>
<translation>Panda vaca</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="88"/>
<source>The penguin from SkyEmu</source>
<translation>El pungüino de SkyEmu</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="97"/>
<source>Window icon</source>
<translation>Icono de ventana</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="100"/>
<source>Language</source>
<translation>Idioma</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="102"/>
<source>Show version on window title</source>
<translation>Mostrar versión en la barra de título de la ventana</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="109"/>
<source>Alber v%1</source>
<translation>Alber v%1</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="109"/>
<source>Alber</source>
<translation>Alber</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="114"/>
<source>Remember window position</source>
<translation>Recordar posición de la ventana</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="119"/>
<source>General Settings</source>
<translation>Configuración General</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="130"/>
<source>Browse...</source>
<translation>Examinar...</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="134"/>
<source>Select Directory</source>
<translation>Seleccionar Directorio</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="146"/>
<source>Default ROMs path</source>
<translation>Ruta predeterminada de ROMs</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="148"/>
<source>Enable Discord RPC</source>
<translation>Activar Discord RPC</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="152"/>
<source>Use portable build</source>
<translation>Usar build portable</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="156"/>
<source>Print version in console output</source>
<translation>Imprimir versión en consola</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="161"/>
<source>Graphics Settings</source>
<translation>Configuración de Gráficos</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="167"/>
<location filename="../../src/panda_qt/config_window.cpp" line="221"/>
<source>Null</source>
<translation>Nulo</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="168"/>
<source>OpenGL</source>
<translation>OpenGL</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="169"/>
<source>Vulkan</source>
<translation>Vulkan</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="175"/>
<source>GPU renderer</source>
<translation>Renderizador GPU</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="177"/>
<source>Enable Renderdoc</source>
<translation>Activar Renderdoc</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="181"/>
<source>Enable shader JIT</source>
<translation>Activar JIT de shaders</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="185"/>
<source>Enable VSync</source>
<translation>Activar VSync</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="189"/>
<source>Use ubershaders (No stutter, maybe slower)</source>
<translation>Usar ubershaders (No stuttering, puede ser más lento)</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="193"/>
<source>Accurate shader multiplication</source>
<translation>Multiplicación precisa de shaders</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="197"/>
<source>Accelerate shaders</source>
<translation>Acelerar shaders</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="201"/>
<source>Force shadergen when rendering lights</source>
<translation>Forzar shadergen al renderizar luces</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="212"/>
<source>Light threshold for forcing shadergen</source>
<translation>Umbral de luz para forzar shadergen</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="215"/>
<source>Audio Settings</source>
<translation>Configuración de Audio</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="222"/>
<source>LLE</source>
<translation>LLE</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="223"/>
<source>HLE</source>
<translation>HLE</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="229"/>
<source>DSP emulation</source>
<translation>Emulación de DSP</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="231"/>
<source>Enable audio</source>
<translation>Activar audio</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="235"/>
<source>Enable AAC audio</source>
<translation>Activar audio AAC</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="239"/>
<source>Print DSP firmware</source>
<translation>Imprimir firmware DSP</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="243"/>
<source>Mute audio device</source>
<translation>Silenciar dispositivo de audio</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="248"/>
<source>Cubic</source>
<translation>Cúbico</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="249"/>
<source>Linear</source>
<translation>Linear</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="255"/>
<source>Volume curve</source>
<translation>Curva del volumen</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="273"/>
<source>Audio device volume</source>
<translation>Volumen del dispositivo de audio</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="276"/>
<source>Battery Settings</source>
<translation>Configuración de Batería</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="288"/>
<source>Battery percentage</source>
<translation>Porcentaje de batería</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="290"/>
<source>Charger plugged</source>
<translation>Cargador conectado</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="295"/>
<source>SD Card Settings</source>
<translation>Configuración de Tarjeta SD</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="300"/>
<source>Enable virtual SD card</source>
<translation>Activar tarjeta SD virtual</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="304"/>
<source>Write protect virtual SD card</source>
<translation>Proteger tarjeta SD de escritura</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="309"/>
<source>Interface</source>
<translation>Interfaz</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="309"/>
<source>User Interface settings</source>
<translation>Configuración de Interfaz de Usuario</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="310"/>
<source>General</source>
<translation>General</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="310"/>
<source>General emulator settings</source>
<translation>Configuración general del emulador</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="311"/>
<source>Graphics</source>
<translation>Gráficos</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="311"/>
<source>Graphics emulation and output settings</source>
<translation>Configuración de emulación de gráficos y salida de vídeo</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="312"/>
<source>Audio</source>
<translation>Audio</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="312"/>
<source>Audio emulation and output settings</source>
<translation>Configuración de emulación y salida de audio</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="313"/>
<source>Battery</source>
<translation>Batería</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="313"/>
<source>Battery emulation settings</source>
<translation>Configuración de emulación de la batería</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="314"/>
<source>SD Card</source>
<translation>Tarjeta SD</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="314"/>
<source>SD Card emulation settings</source>
<translation>Configuración de emulación de la tarjeta SD</translation>
</message>
<message>
<location filename="../../src/panda_qt/translations.cpp" line="75"/>
<source>Language change successful</source>
<translation>Idioma cambiado correctamente</translation>
</message>
<message>
<location filename="../../src/panda_qt/translations.cpp" line="76"/>
<source>Restart Panda3DS for the new language to be used.</source>
<translation>Reinicie Panda3DS para utilizar el nuevo idioma.</translation>
</message>
<message>
<location filename="../../src/panda_qt/translations.cpp" line="82"/>
<source>Language change failed</source>
<translation>Cambio de idioma fallido</translation>
</message>
<message>
<location filename="../../src/panda_qt/translations.cpp" line="83"/>
<source>The language you selected is not included in Panda3DS. If you&apos;re seeing this, someone messed up the language UI code...</source>
<translation>El idioma que ha seleccionado no está incluido en Panda3DS. Si está viendo esto, alguien cometió un error en el código...</translation>
</message>
</context>
<context>
<name>MainWindow</name>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="20"/>
<source>Alber</source>
<translation>Alber</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="38"/>
<source>File</source>
<translation>Archivo</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="39"/>
<source>Emulation</source>
<translation>Emulación</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="40"/>
<source>Tools</source>
<translation>Herramientas</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="41"/>
<source>About</source>
<translation>Acerca de</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="44"/>
<source>Load game</source>
<translation>Cargar juego</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="45"/>
<source>Load Lua script</source>
<translation>Cargar script Lua</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="46"/>
<source>Open Panda3DS folder</source>
<translation>Abrir carpeta Panda3DS</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="55"/>
<source>Pause</source>
<translation>Pausar</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="56"/>
<source>Resume</source>
<translation>Reanudar</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="57"/>
<source>Reset</source>
<translation>Reiniciar</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="58"/>
<source>Configure</source>
<translation>Configurar</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="64"/>
<source>Dump RomFS</source>
<translation>Volcar RomFS</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="65"/>
<source>Open Lua Editor</source>
<translation>Abrir Editor Lua</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="66"/>
<source>Open Cheats Editor</source>
<translation>Abrir Editor de Trucos</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="67"/>
<source>Open Patch Window</source>
<translation>Abrir Ventana de Parches</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="68"/>
<source>Open Shader Editor</source>
<translation>Abrir Editor de Shaders</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="69"/>
<source>Dump loaded DSP firmware</source>
<translation>Volcar firmware DSP cargado</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="78"/>
<source>About Panda3DS</source>
<translation>Acerca de Panda3DS</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="195"/>
<source>Select 3DS ROM to load</source>
<translation>Seleccione el ROM de 3DS a cargar</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="196"/>
<source>Nintendo 3DS ROMs (*.3ds *.cci *.cxi *.app *.ncch *.3dsx *.elf *.axf)</source>
<translation>ROMs de Nintendo 3DS (*.3ds *.cci *.cxi *.app *.ncch *.3dsx *.elf *.axf)</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="209"/>
<source>Select Lua script to load</source>
<translation>Seleccione el script Lua a cargar</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="209"/>
<source>Lua scripts (*.lua *.txt)</source>
<translation>Scripts Lua (*.lua *.txt)</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="272"/>
<source>Select folder to dump RomFS files to</source>
<translation>Seleccione la carpeta donde volcar los archivos del RomFS</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="288"/>
<source>Invalid format for RomFS dumping</source>
<translation>Formato inváido para volcado de RomFS</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="289"/>
<source>The currently loaded app is not in a format that supports RomFS</source>
<translation>La aplicación cargada no tiene un formato que soporta RomFS</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="292"/>
<location filename="../../src/panda_qt/main_window.cpp" line="323"/>
<location filename="../../src/panda_qt/main_window.cpp" line="336"/>
<source>OK</source>
<translation>OK</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="299"/>
<source>No RomFS found</source>
<translation>RomFS no encontrado</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="299"/>
<source>No RomFS partition was found in the loaded app</source>
<translation>No se encontró una partición RomFS en la aplicación cargada</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="305"/>
<source>Select file</source>
<translation>Seleccionar archivo</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="305"/>
<source>DSP firmware file (*.cdc)</source>
<translation>Archivo de firmware DSP (*.cdc)</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="320"/>
<source>No DSP firmware loaded</source>
<translation>Firmware DSP no cargado</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="320"/>
<source>The currently loaded app has not uploaded a firmware to the DSP</source>
<translation>La aplicación cargada no ha subido un firmware al DSP</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="331"/>
<source>Failed to open output file</source>
<translation>Error al abrir el archivo de salida</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="332"/>
<source>The currently loaded DSP firmware could not be written to the selected file. Please make sure you have permission to access this file</source>
<translation>No se pudo escribir el firmware DSP cargado al archivo seleccionado. Por favor asegure que tiene los permisos necesarios para acceder a este archivo</translation>
</message>
</context>
<context>
<name>PatchWindow</name>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="15"/>
<source>ROM patcher</source>
<translation>Parcheador de ROM</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="23"/>
<source>Select input file</source>
<translation>Seleccione el archivo de entrada</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="24"/>
<location filename="../../src/panda_qt/patch_window.cpp" line="36"/>
<source>Select</source>
<translation>Seleccionar</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="35"/>
<location filename="../../src/panda_qt/patch_window.cpp" line="63"/>
<source>Select patch file</source>
<translation>Seleccione el archivo de parche</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="47"/>
<source>Apply patch</source>
<translation>Aplicar parche</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="56"/>
<source>Select file to patch</source>
<translation>Seleccione el archivo a parchear</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="56"/>
<location filename="../../src/panda_qt/patch_window.cpp" line="80"/>
<source>All files (*.*)</source>
<translation>Todos los archivos (*.*)</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="63"/>
<source>Patch files (*.ips *.ups *.bps)</source>
<translation>Archivos de parche (*.ips *.ups *.bps)</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="71"/>
<source>Paths not provided correctly</source>
<translation>Rutas no proporcionadas correctamente</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="71"/>
<source>Please provide paths for both the input file and the patch file</source>
<translation>Por favor proporcione rutas para el archivo de entrada y el parche</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="80"/>
<source>Select file</source>
<translation>Seleccionar archivo</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="84"/>
<source>No output path</source>
<translation>No hay archivo de salida</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="84"/>
<source>No path was provided for the output file, no patching was done</source>
<translation>No se ha proporcionado una ruta para el archivo de salida, no se ha aplicado el parche</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="99"/>
<source>Unknown patch format</source>
<translation>Formato del parche desconocido</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="99"/>
<source>Unknown format for patch file. Currently IPS, UPS and BPS are supported</source>
<translation>Formato desconocido del archivo de parche. Actualmente son soportados IPS, UPS y BPS</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="108"/>
<source>Failed to open input files</source>
<translation>Error al abrir archivos de entrada</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="108"/>
<source>Make sure they&apos;re in a directory Panda3DS has access to</source>
<translation>Asegure que estén en un directorio al que Panda3DS tenga acceso</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="135"/>
<source>Patching Success</source>
<translation>Parche exitoso</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="135"/>
<source>Your file was patched successfully.</source>
<translation>Su archivo fue parcheado con éxito.</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="141"/>
<source>Checksum mismatch</source>
<translation>Discrepancia en la suma de verificación</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="142"/>
<source>Patch was applied successfully but a checksum mismatch was detected. The input or output files might not be correct</source>
<translation>El parche se aplicó con éxito pero se detectó una discrepancia en la suma de verificación. Los archivos de entrada o salida pueden no ser correctos</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="146"/>
<source>Patching error</source>
<translation>Error de parcheo</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="146"/>
<source>An error occured while patching</source>
<translation>Ha ocurrido un error en el parcheo</translation>
</message>
</context>
<context>
<name>PatchWindow::PatchWindow</name>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="153"/>
<source>OK</source>
<translation>OK</translation>
</message>
</context>
<context>
<name>ShaderEditorWindow</name>
<message>
<location filename="../../src/panda_qt/shader_editor.cpp" line="26"/>
<source>Reload shader</source>
<translation>Recargar shader</translation>
</message>
</context>
<context>
<name>TextEditorWindow</name>
<message>
<location filename="../../src/panda_qt/text_editor.cpp" line="12"/>
<source>Lua Editor</source>
<translation>Editor Lua</translation>
</message>
<message>
<location filename="../../src/panda_qt/text_editor.cpp" line="27"/>
<source>Load script</source>
<translation>Cargar script</translation>
</message>
</context>
</TS>

763
docs/translations/nl.ts Normal file
View file

@ -0,0 +1,763 @@
<?xml version="1.0" ?><!DOCTYPE TS><TS version="2.1" language="nl">
<context>
<name>AboutWindow</name>
<message>
<location filename="../../src/panda_qt/about_window.cpp" line="16"/>
<source>About Panda3DS</source>
<translation>Over Panda3DS</translation>
</message>
<message>
<location filename="../../src/panda_qt/about_window.cpp" line="35"/>
<source>Panda3DS is a free and open source Nintendo 3DS emulator, for Windows, MacOS and Linux</source>
<translation>Panda3DS is een gratis, open source Nintendo 3DS-emulator voor Windows, MacOS en Linux</translation>
</message>
<message>
<location filename="../../src/panda_qt/about_window.cpp" line="36"/>
<source>Visit panda3ds.com for help with Panda3DS and links to our official support sites.</source>
<translation>Bezoek panda3ds.com voor ondersteuning van Panda3DS en links naar onze officiële ondersteuningskanalen.</translation>
</message>
<message>
<location filename="../../src/panda_qt/about_window.cpp" line="38"/>
<source>Panda3DS is developed by volunteers in their spare time. Below is a list of some of these volunteers who&apos;ve agreed to be listed here, in no particular order.&lt;br&gt;If you think you should be listed here too, please inform us&lt;br&gt;&lt;br&gt;- Peach (wheremyfoodat)&lt;br&gt;- noumidev&lt;br&gt;- liuk707&lt;br&gt;- Wunk&lt;br&gt;- marysaka&lt;br&gt;- Sky&lt;br&gt;- merryhime&lt;br&gt;- TGP17&lt;br&gt;- Shadow&lt;br&gt;</source>
<translation>Panda3DS wordt ontwikkeld door vrijwilligers in hun vrije tijd. Hieronder een lijst van sommige van deze vrijwilligers die akkoord zijn met een vermelding, in willekeurige volgorde.&lt;br&gt;Als jij vindt dat je op deze lijst zou moeten staan, laat het ons dan weten&lt;br&gt;&lt;br&gt;- Peach (wheremyfoodat)&lt;br&gt;- noumidev&lt;br&gt;- liuk707&lt;br&gt;- Wunk&lt;br&gt;- marysaka&lt;br&gt;- Sky&lt;br&gt;- merryhime&lt;br&gt;- TGP17&lt;br&gt;- Shadow&lt;br&gt;</translation>
</message>
</context>
<context>
<name>CheatEditDialog</name>
<message>
<location filename="../../src/panda_qt/cheats_window.cpp" line="72"/>
<source>Edit Cheat</source>
<translation>Cheat bewerken</translation>
</message>
<message>
<location filename="../../src/panda_qt/cheats_window.cpp" line="82"/>
<source>Cheat name</source>
<translation>Cheatnaam</translation>
</message>
</context>
<context>
<name>CheatEntryWidget</name>
<message>
<location filename="../../src/panda_qt/cheats_window.cpp" line="34"/>
<source>Edit</source>
<translation>Bewerken</translation>
</message>
</context>
<context>
<name>CheatsWindow</name>
<message>
<location filename="../../src/panda_qt/cheats_window.cpp" line="164"/>
<source>Cheats</source>
<translation>Cheats</translation>
</message>
<message>
<location filename="../../src/panda_qt/cheats_window.cpp" line="177"/>
<source>Add</source>
<translation>Toevoegen</translation>
</message>
<message>
<location filename="../../src/panda_qt/cheats_window.cpp" line="178"/>
<source>Remove</source>
<translation>Verwijderen</translation>
</message>
</context>
<context>
<name>ConfigWindow</name>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="7"/>
<source>Configuration</source>
<translation>Instellingen</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="63"/>
<source>Interface Settings</source>
<translation>Interfaceinstellingen</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="69"/>
<source>System</source>
<translation>Systeem</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="70"/>
<source>Light</source>
<translation>Licht</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="71"/>
<source>Dark</source>
<translation>Donker</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="72"/>
<source>Greetings Cat</source>
<translation>Begroetingskat</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="73"/>
<source>Cream</source>
<translation>Crème</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="81"/>
<source>Color theme</source>
<translation>Kleurenthema</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="84"/>
<source>Happy panda</source>
<translation>Blije panda</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="85"/>
<source>Happy panda (colourful)</source>
<translation>Blije panda (kleurrijk)</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="86"/>
<source>Sleepy panda</source>
<translation>Slaperige panda</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="87"/>
<source>Cow panda</source>
<translation>Koeienpanda</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="88"/>
<source>The penguin from SkyEmu</source>
<translation>De pinguïn van SkyEmu</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="97"/>
<source>Window icon</source>
<translation>Venstericoon</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="100"/>
<source>Language</source>
<translation>Taal</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="102"/>
<source>Show version on window title</source>
<translation>Toon versie in venstertitel</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="109"/>
<source>Alber v%1</source>
<translation>Alber v%1</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="109"/>
<source>Alber</source>
<translation>Alber</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="114"/>
<source>Remember window position</source>
<translation>Vensterpositie onthouden</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="119"/>
<source>General Settings</source>
<translation>Algemene instellingen</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="130"/>
<source>Browse...</source>
<translation>Bladeren...</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="134"/>
<source>Select Directory</source>
<translation>Kies map</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="146"/>
<source>Default ROMs path</source>
<translation>Standaard pad voor ROMs</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="148"/>
<source>Enable Discord RPC</source>
<translation>Discord RPC inschakelen</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="152"/>
<source>Use portable build</source>
<translation>Portable build gebruiken</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="156"/>
<source>Print version in console output</source>
<translation>Versie afdrukken in consoleuitvoer</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="161"/>
<source>Graphics Settings</source>
<translation>Grafische instellingen</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="167"/>
<location filename="../../src/panda_qt/config_window.cpp" line="221"/>
<source>Null</source>
<translation>Null</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="168"/>
<source>OpenGL</source>
<translation>OpenGL</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="169"/>
<source>Vulkan</source>
<translation>Vulkan</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="175"/>
<source>GPU renderer</source>
<translation>Renderen op videokaart</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="177"/>
<source>Enable Renderdoc</source>
<translation>Renderdoc inschakelen</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="181"/>
<source>Enable shader JIT</source>
<translation>Shader JIT inschakelen</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="185"/>
<source>Enable VSync</source>
<translation>VSync inschakelen</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="189"/>
<source>Use ubershaders (No stutter, maybe slower)</source>
<translation>Ubershaders gebruiken (geen haperingen, mogelijk langzamer)</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="193"/>
<source>Accurate shader multiplication</source>
<translation>Nauwkeurige vermenigvuldigen in shaders</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="197"/>
<source>Accelerate shaders</source>
<translation>Shaders versnellen</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="201"/>
<source>Force shadergen when rendering lights</source>
<translation>Shadergen afdwingen bij tekenen licht</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="212"/>
<source>Light threshold for forcing shadergen</source>
<translation>Lichtgrens voor afdwingen shadergen</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="215"/>
<source>Audio Settings</source>
<translation>Audioinstellingen</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="222"/>
<source>LLE</source>
<translation>LLE</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="223"/>
<source>HLE</source>
<translation>HLE</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="229"/>
<source>DSP emulation</source>
<translation>DSP-emulatie</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="231"/>
<source>Enable audio</source>
<translation>Audio inschakelen</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="235"/>
<source>Enable AAC audio</source>
<translation>AAC-audio inschakelen</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="239"/>
<source>Print DSP firmware</source>
<translation>DSP-firmware afdrukken</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="243"/>
<source>Mute audio device</source>
<translation>Audioapparaat dempen</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="248"/>
<source>Cubic</source>
<translation>Kubiek</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="249"/>
<source>Linear</source>
<translation>Lineair</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="255"/>
<source>Volume curve</source>
<translation>Volumecurve</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="273"/>
<source>Audio device volume</source>
<translation>Volume audioapparaat</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="276"/>
<source>Battery Settings</source>
<translation>Batterij-instellingen</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="288"/>
<source>Battery percentage</source>
<translation>Batterijpercentage</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="290"/>
<source>Charger plugged</source>
<translation>Oplader aangesloten</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="295"/>
<source>SD Card Settings</source>
<translation>Instellingen SD-kaart</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="300"/>
<source>Enable virtual SD card</source>
<translation>Virtuele SD-kaart inschakelen</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="304"/>
<source>Write protect virtual SD card</source>
<translation>Virtuele SD-kaart schrijfbeveiligen</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="309"/>
<source>Interface</source>
<translation>Interface</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="309"/>
<source>User Interface settings</source>
<translation>Instellingen gebruikersinterface</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="310"/>
<source>General</source>
<translation>Algemeen</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="310"/>
<source>General emulator settings</source>
<translation>Algemene emulatorinstellingen</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="311"/>
<source>Graphics</source>
<translation>Weergave</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="311"/>
<source>Graphics emulation and output settings</source>
<translation>Instellingen grafische emulatie en weergave </translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="312"/>
<source>Audio</source>
<translation>Audio</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="312"/>
<source>Audio emulation and output settings</source>
<translation>Instellingen audioemulatie en weergave</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="313"/>
<source>Battery</source>
<translation>Batterij</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="313"/>
<source>Battery emulation settings</source>
<translation>Instellingen batterijemulatie</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="314"/>
<source>SD Card</source>
<translation>SD-kaart</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="314"/>
<source>SD Card emulation settings</source>
<translation>Instellingen SD-kaart-emulatie</translation>
</message>
<message>
<location filename="../../src/panda_qt/translations.cpp" line="75"/>
<source>Language change successful</source>
<translation>Taal succesvol ingesteld</translation>
</message>
<message>
<location filename="../../src/panda_qt/translations.cpp" line="76"/>
<source>Restart Panda3DS for the new language to be used.</source>
<translation>Herstart Panda3DS om de nieuw gekozen taal te gebruiken.</translation>
</message>
<message>
<location filename="../../src/panda_qt/translations.cpp" line="82"/>
<source>Language change failed</source>
<translation>Wijzigen van taal mislukt</translation>
</message>
<message>
<location filename="../../src/panda_qt/translations.cpp" line="83"/>
<source>The language you selected is not included in Panda3DS. If you&apos;re seeing this, someone messed up the language UI code...</source>
<translation>De gekozen taal is niet beschikbaar in Panda3DS. Als je dit leest heeft iemand de taalcode verprutst...</translation>
</message>
</context>
<context>
<name>MainWindow</name>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="20"/>
<source>Alber</source>
<translation>Alber</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="38"/>
<source>File</source>
<translation>Bestand</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="39"/>
<source>Emulation</source>
<translation>Emulatie</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="40"/>
<source>Tools</source>
<translation>Hulpmiddelen</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="41"/>
<source>About</source>
<translation>Over</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="44"/>
<source>Load game</source>
<translation>Spel laden</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="45"/>
<source>Load Lua script</source>
<translation>LUA-script laden</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="46"/>
<source>Open Panda3DS folder</source>
<translation>Open Panda3DS-map</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="55"/>
<source>Pause</source>
<translation>Pauzeren</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="56"/>
<source>Resume</source>
<translation>Hervatten</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="57"/>
<source>Reset</source>
<translation>Reset</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="58"/>
<source>Configure</source>
<translation>Instellingen</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="64"/>
<source>Dump RomFS</source>
<translation>RomFS dumpen</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="65"/>
<source>Open Lua Editor</source>
<translation>Open LUA-editor</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="66"/>
<source>Open Cheats Editor</source>
<translation>Open cheats-editor</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="67"/>
<source>Open Patch Window</source>
<translation>Open patchvenster</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="68"/>
<source>Open Shader Editor</source>
<translation>Open shader-editor</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="69"/>
<source>Dump loaded DSP firmware</source>
<translation>Geladen DSP-firmware dumpen</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="78"/>
<source>About Panda3DS</source>
<translation>Over Panda3DS</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="195"/>
<source>Select 3DS ROM to load</source>
<translation>Kies 3DS ROM om te laden</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="196"/>
<source>Nintendo 3DS ROMs (*.3ds *.cci *.cxi *.app *.ncch *.3dsx *.elf *.axf)</source>
<translation>Nintendo 3DS ROMs (*.3ds *.cci *.cxi *.app *.ncch *.3dsx *.elf *.axf)</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="209"/>
<source>Select Lua script to load</source>
<translation>Kies LUA-script om te laden</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="209"/>
<source>Lua scripts (*.lua *.txt)</source>
<translation>LUA-scripts (*.lua *.txt)</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="272"/>
<source>Select folder to dump RomFS files to</source>
<translation>Kies map om RomFS-bestanden heen te dumpen</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="288"/>
<source>Invalid format for RomFS dumping</source>
<translation>Ongeldig formaat voor RomFS dump</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="289"/>
<source>The currently loaded app is not in a format that supports RomFS</source>
<translation>Het formaat van de momenteel geladen applicatie ondersteunt geen RomFS</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="292"/>
<location filename="../../src/panda_qt/main_window.cpp" line="323"/>
<location filename="../../src/panda_qt/main_window.cpp" line="336"/>
<source>OK</source>
<translation>OK</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="299"/>
<source>No RomFS found</source>
<translation>Geen RomFS gevonden</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="299"/>
<source>No RomFS partition was found in the loaded app</source>
<translation>Geen RomFS-partitie gevonden in de geladen applicatie</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="305"/>
<source>Select file</source>
<translation>Selecteer bestand</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="305"/>
<source>DSP firmware file (*.cdc)</source>
<translation>DSP-firmware-bestand (*.cdc)</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="320"/>
<source>No DSP firmware loaded</source>
<translation>Geen DSP-firmware geladen</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="320"/>
<source>The currently loaded app has not uploaded a firmware to the DSP</source>
<translation>De momenteel geladen applicatie heeft geen firmware geüpload naar de DSP</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="331"/>
<source>Failed to open output file</source>
<translation>Uitvoerbestand openen mislukt</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="332"/>
<source>The currently loaded DSP firmware could not be written to the selected file. Please make sure you have permission to access this file</source>
<translation>De momenteel geladen DSP-firmware kan niet worden geschreven naar het gekozen bestand. Controleer de permissies van het gekozen bestand</translation>
</message>
</context>
<context>
<name>PatchWindow</name>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="15"/>
<source>ROM patcher</source>
<translation>ROM-patcher</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="23"/>
<source>Select input file</source>
<translation>Kies invoerbestand</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="24"/>
<location filename="../../src/panda_qt/patch_window.cpp" line="36"/>
<source>Select</source>
<translation>Kies</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="35"/>
<location filename="../../src/panda_qt/patch_window.cpp" line="63"/>
<source>Select patch file</source>
<translation>Kies patchbestand</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="47"/>
<source>Apply patch</source>
<translation>Patch toepassen</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="56"/>
<source>Select file to patch</source>
<translation>Kies bestand om te patchen</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="56"/>
<location filename="../../src/panda_qt/patch_window.cpp" line="80"/>
<source>All files (*.*)</source>
<translation>Alle bestanden (*.*)</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="63"/>
<source>Patch files (*.ips *.ups *.bps)</source>
<translation>Patch-bestanden (*.ips *.ups *.bps)</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="71"/>
<source>Paths not provided correctly</source>
<translation>Paden incorrect meegegeven</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="71"/>
<source>Please provide paths for both the input file and the patch file</source>
<translation>Geef paden van invoerbestand en patchbestand op</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="80"/>
<source>Select file</source>
<translation>Kies bestand</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="84"/>
<source>No output path</source>
<translation>Geen uitvoerpad</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="84"/>
<source>No path was provided for the output file, no patching was done</source>
<translation>Geen pad opgegeven voor uitvoerbestand, patch niet toegepast</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="99"/>
<source>Unknown patch format</source>
<translation>Onbekend patchformaat</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="99"/>
<source>Unknown format for patch file. Currently IPS, UPS and BPS are supported</source>
<translation>Ongeldig formaat van patchbestand. Momenteel wordt IPS, UPS en BPS ondersteund</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="108"/>
<source>Failed to open input files</source>
<translation>Openen van invoerbestanden mislukt</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="108"/>
<source>Make sure they&apos;re in a directory Panda3DS has access to</source>
<translation>Zorg ervoor dat ze in een map staan waar Panda3DS toegang toe heeft</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="135"/>
<source>Patching Success</source>
<translation>Patch succesvol</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="135"/>
<source>Your file was patched successfully.</source>
<translation>Het bestand is succesvol gepatcht.</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="141"/>
<source>Checksum mismatch</source>
<translation>Checksum komt niet overeen</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="142"/>
<source>Patch was applied successfully but a checksum mismatch was detected. The input or output files might not be correct</source>
<translation>Patch is succesvol toegepast maar de checksum komt niet overeen. Invoer- of uitvoerbestand is mogelijk ongeldig</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="146"/>
<source>Patching error</source>
<translation>Fout tijdens patchen</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="146"/>
<source>An error occured while patching</source>
<translation>Er is bij het patchen een fout opgetreden</translation>
</message>
</context>
<context>
<name>PatchWindow::PatchWindow</name>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="153"/>
<source>OK</source>
<translation>OK</translation>
</message>
</context>
<context>
<name>ShaderEditorWindow</name>
<message>
<location filename="../../src/panda_qt/shader_editor.cpp" line="26"/>
<source>Reload shader</source>
<translation>Shader herladen</translation>
</message>
</context>
<context>
<name>TextEditorWindow</name>
<message>
<location filename="../../src/panda_qt/text_editor.cpp" line="12"/>
<source>Lua Editor</source>
<translation>LUA-editor</translation>
</message>
<message>
<location filename="../../src/panda_qt/text_editor.cpp" line="27"/>
<source>Load script</source>
<translation>Script laden</translation>
</message>
</context>
</TS>

764
docs/translations/pt_br.ts Normal file
View file

@ -0,0 +1,764 @@
<?xml version="1.0" ?><!DOCTYPE TS><TS version="2.1" language="es">
<context>
<name>AboutWindow</name>
<message>
<location filename="../../src/panda_qt/about_window.cpp" line="16"/>
<source>About Panda3DS</source>
<translation>Sobre o Panda3DS</translation>
</message>
<message>
<location filename="../../src/panda_qt/about_window.cpp" line="35"/>
<source>Panda3DS is a free and open source Nintendo 3DS emulator, for Windows, MacOS and Linux</source>
<translation>Panda3DS é um emulador gratuito e open-source para Windows, MacOS e Linux</translation>
</message>
<message>
<location filename="../../src/panda_qt/about_window.cpp" line="36"/>
<source>Visit panda3ds.com for help with Panda3DS and links to our official support sites.</source>
<translation>Visite panda3ds.com para obter ajuda e links de suporte oficial.</translation>
</message>
<message>
<location filename="../../src/panda_qt/about_window.cpp" line="38"/>
<source>Panda3DS is developed by volunteers in their spare time. Below is a list of some of these volunteers who&apos;ve agreed to be listed here, in no particular order.&lt;br&gt;If you think you should be listed here too, please inform us&lt;br&gt;&lt;br&gt;- Peach (wheremyfoodat)&lt;br&gt;- noumidev&lt;br&gt;- liuk707&lt;br&gt;- Wunk&lt;br&gt;- marysaka&lt;br&gt;- Sky&lt;br&gt;- merryhime&lt;br&gt;- TGP17&lt;br&gt;- Shadow&lt;br&gt;</source>
<translation>Panda3DS é desenvolvido por voluntários em seu tempo livre. Abaixo a lista de alguns volutário&lt;br&gt;(Lista sem nenhuma ordem específica)&lt;br&gt;Se acha que seu nome deveria estar listado aqui por favor informe-nos&lt;br&gt;&lt;br&gt;- Peach (wheremyfoodat)&lt;br&gt;- noumidev&lt;br&gt;- liuk707&lt;br&gt;- Wunk&lt;br&gt;- marysaka&lt;br&gt;- Sky&lt;br&gt;- merryhime&lt;br&gt;- TGP17&lt;br&gt;- Shadow&lt;br&gt;</translation>
</message>
</context>
<context>
<name>CheatEditDialog</name>
<message>
<location filename="../../src/panda_qt/cheats_window.cpp" line="72"/>
<source>Edit Cheat</source>
<translation>Editar Trapaças</translation>
</message>
<message>
<location filename="../../src/panda_qt/cheats_window.cpp" line="82"/>
<source>Cheat name</source>
<translation>Nome da trapaça</translation>
</message>
</context>
<context>
<name>CheatEntryWidget</name>
<message>
<location filename="../../src/panda_qt/cheats_window.cpp" line="34"/>
<source>Edit</source>
<translation>Editar</translation>
</message>
</context>
<context>
<name>CheatsWindow</name>
<message>
<location filename="../../src/panda_qt/cheats_window.cpp" line="164"/>
<source>Cheats</source>
<translation>Trapaça</translation>
</message>
<message>
<location filename="../../src/panda_qt/cheats_window.cpp" line="177"/>
<source>Add</source>
<translation>Adicionar</translation>
</message>
<message>
<location filename="../../src/panda_qt/cheats_window.cpp" line="178"/>
<source>Remove</source>
<translation>Sair</translation>
</message>
</context>
<context>
<name>ConfigWindow</name>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="7"/>
<source>Configuration</source>
<translation>Configurações</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="63"/>
<source>Interface Settings</source>
<translation>Configurações da interface</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="69"/>
<source>System</source>
<translation>Sistema</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="70"/>
<source>Light</source>
<translation>Claro</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="71"/>
<source>Dark</source>
<translation>Escuro</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="72"/>
<source>Greetings Cat</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="73"/>
<source>Cream</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="81"/>
<source>Color theme</source>
<translation>Tema de color</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="84"/>
<source>Happy panda</source>
<translation>Panda feliz</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="85"/>
<source>Happy panda (colourful)</source>
<translation>Panda feliz (colorido)</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="86"/>
<source>Sleepy panda</source>
<translation>Panda sonolento</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="87"/>
<source>Cow panda</source>
<translation>Panda vaca</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="88"/>
<source>The penguin from SkyEmu</source>
<translation>O pinguim do SkyEmu</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="97"/>
<source>Window icon</source>
<translation>Icone da janela</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="100"/>
<source>Language</source>
<translation>Idioma</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="102"/>
<source>Show version on window title</source>
<translation>Mostrar versão na barra de título</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="109"/>
<source>Alber v%1</source>
<translation>Alber v%1</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="109"/>
<source>Alber</source>
<translation>Alber</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="114"/>
<source>Remember window position</source>
<translation>Lembrar posição da janela</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="119"/>
<source>General Settings</source>
<translation>Configurações gerais</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="130"/>
<source>Browse...</source>
<translation>Navegar...</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="134"/>
<source>Select Directory</source>
<translation>Selecionar o diretório</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="146"/>
<source>Default ROMs path</source>
<translation>Diretório padrão das ROMs</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="148"/>
<source>Enable Discord RPC</source>
<translation>Ativar Discord RPC</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="152"/>
<source>Use portable build</source>
<translation>Usar build portatil</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="156"/>
<source>Print version in console output</source>
<translation>Imprimir versão no console</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="161"/>
<source>Graphics Settings</source>
<translation>Configurações gráficas</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="167"/>
<location filename="../../src/panda_qt/config_window.cpp" line="221"/>
<source>Null</source>
<translation>Nulo</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="168"/>
<source>OpenGL</source>
<translation>OpenGL</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="169"/>
<source>Vulkan</source>
<translation>Vulkan</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="175"/>
<source>GPU renderer</source>
<translation>Renderizador GPU</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="177"/>
<source>Enable Renderdoc</source>
<translation>Ativar Renderdoc</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="181"/>
<source>Enable shader JIT</source>
<translation>Ativar JIT de shaders</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="185"/>
<source>Enable VSync</source>
<translation>Ativar VSync</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="189"/>
<source>Use ubershaders (No stutter, maybe slower)</source>
<translation>Usar ubershaders (No stuttering, puede ser más lento)</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="193"/>
<source>Accurate shader multiplication</source>
<translation>Multiplicação precisa de shaders</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="197"/>
<source>Accelerate shaders</source>
<translation>Graficos acelerados</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="201"/>
<source>Force shadergen when rendering lights</source>
<translation>Forçar shadergen ao renderizar luzes.</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="212"/>
<source>Light threshold for forcing shadergen</source>
<translation>Limear de luzes para forçar shadergen</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="215"/>
<source>Audio Settings</source>
<translation>Configurações de audio</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="222"/>
<source>LLE</source>
<translation>LLE</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="223"/>
<source>HLE</source>
<translation>HLE</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="229"/>
<source>DSP emulation</source>
<translation>Emulação DSP</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="231"/>
<source>Enable audio</source>
<translation>Ativar audio</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="235"/>
<source>Enable AAC audio</source>
<translation>Ativar audio AAC</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="239"/>
<source>Print DSP firmware</source>
<translation>Imprimir firmware DSP</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="243"/>
<source>Mute audio device</source>
<translation>Silenciar dispositivo de audio</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="248"/>
<source>Cubic</source>
<translation>Cúbico</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="249"/>
<source>Linear</source>
<translation>Linear</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="255"/>
<source>Volume curve</source>
<translation>Curva de volume</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="273"/>
<source>Audio device volume</source>
<translation>Volume do dispositivo de audio</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="276"/>
<source>Battery Settings</source>
<translation>Configurações de bateria</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="288"/>
<source>Battery percentage</source>
<translation>Porcentagem da bateria</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="290"/>
<source>Charger plugged</source>
<translation>Carregador conectado</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="295"/>
<source>SD Card Settings</source>
<translation>Configurações do cartão de memoria</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="300"/>
<source>Enable virtual SD card</source>
<translation>Ativar cartão de memoria virtual</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="304"/>
<source>Write protect virtual SD card</source>
<translation>Proteger cartão de memoria virtual contra escrita</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="309"/>
<source>Interface</source>
<translation>Interface</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="309"/>
<source>User Interface settings</source>
<translation>Configurações da interface de usuario</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="310"/>
<source>General</source>
<translation>Geral</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="310"/>
<source>General emulator settings</source>
<translation>Configurações gerais do emulador</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="311"/>
<source>Graphics</source>
<translation>Gráficos</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="311"/>
<source>Graphics emulation and output settings</source>
<translation>Configurações da emulação e saida de video</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="312"/>
<source>Audio</source>
<translation>Audio</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="312"/>
<source>Audio emulation and output settings</source>
<translation>Configurações da emulação e saida de audio</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="313"/>
<source>Battery</source>
<translation>Bateria</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="313"/>
<source>Battery emulation settings</source>
<translation>Configuração da emulação da bateria</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="314"/>
<source>SD Card</source>
<translation>Cartão de memoria</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="314"/>
<source>SD Card emulation settings</source>
<translation>Configurar a emulação do cartão de memoria</translation>
</message>
<message>
<location filename="../../src/panda_qt/translations.cpp" line="75"/>
<source>Language change successful</source>
<translation>Idioma alterado com sucesso</translation>
</message>
<message>
<location filename="../../src/panda_qt/translations.cpp" line="76"/>
<source>Restart Panda3DS for the new language to be used.</source>
<translation>Para aplicar o novo idioma feche e abra o emulador.</translation>
</message>
<message>
<location filename="../../src/panda_qt/translations.cpp" line="82"/>
<source>Language change failed</source>
<translation>A mudaça de idioma falhou</translation>
</message>
<message>
<location filename="../../src/panda_qt/translations.cpp" line="83"/>
<source>The language you selected is not included in Panda3DS. If you&apos;re seeing this, someone messed up the language UI code...</source>
<translation>O idioma selecionado não existe no Panda3DS. Se você esta vendo esse erro existe um erro no codigo...</translation>
</message>
</context>
<context>
<name>MainWindow</name>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="20"/>
<source>Alber</source>
<translation>Alber</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="38"/>
<source>File</source>
<translation>Arquivo</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="39"/>
<source>Emulation</source>
<translation>Emulação</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="40"/>
<source>Tools</source>
<translation>Ferramentas</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="41"/>
<source>About</source>
<translation>Sobre</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="44"/>
<source>Load game</source>
<translation>Carregar jogo</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="45"/>
<source>Load Lua script</source>
<translation>Carregar Script Lua</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="46"/>
<source>Open Panda3DS folder</source>
<translation>Abrir pasta do Panda3DS</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="55"/>
<source>Pause</source>
<translation>Pausar</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="56"/>
<source>Resume</source>
<translation>Continuar</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="57"/>
<source>Reset</source>
<translation>Reiniciar</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="58"/>
<source>Configure</source>
<translation>Configurar</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="64"/>
<source>Dump RomFS</source>
<translation>Extrair RomFS</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="65"/>
<source>Open Lua Editor</source>
<translation>Abrir editor Lua</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="66"/>
<source>Open Cheats Editor</source>
<translation>Abrir editor de trapaças</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="67"/>
<source>Open Patch Window</source>
<translation>Abrir janela de trapaças</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="68"/>
<source>Open Shader Editor</source>
<translation>Abrir editor de shaders</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="69"/>
<source>Dump loaded DSP firmware</source>
<translation>Extrair firmware DSP carregado</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="78"/>
<source>About Panda3DS</source>
<translation>Sobre o Panda3DS</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="195"/>
<source>Select 3DS ROM to load</source>
<translation>Selecione uma ROM de 3DS para carregar</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="196"/>
<source>Nintendo 3DS ROMs (*.3ds *.cci *.cxi *.app *.ncch *.3dsx *.elf *.axf)</source>
<translation>ROMs de Nintendo 3DS (*.3ds *.cci *.cxi *.app *.ncch *.3dsx *.elf *.axf)</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="209"/>
<source>Select Lua script to load</source>
<translation>Selecione uma ROM de 3DS para carregar</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="209"/>
<source>Lua scripts (*.lua *.txt)</source>
<translation>Scripts Lua (*.lua *.txt)</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="272"/>
<source>Select folder to dump RomFS files to</source>
<translation>Selecione onde a RomFS será extraida</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="288"/>
<source>Invalid format for RomFS dumping</source>
<translation>Formato de RomFS inválido</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="289"/>
<source>The currently loaded app is not in a format that supports RomFS</source>
<translation>O aplicativo carregado não suporta RomFS</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="292"/>
<location filename="../../src/panda_qt/main_window.cpp" line="323"/>
<location filename="../../src/panda_qt/main_window.cpp" line="336"/>
<source>OK</source>
<translation>OK</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="299"/>
<source>No RomFS found</source>
<translation>RomFS no encontrado</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="299"/>
<source>No RomFS partition was found in the loaded app</source>
<translation>A partição RomFS não foi encontrada no aplicativo</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="305"/>
<source>Select file</source>
<translation>Selecionar arquivo</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="305"/>
<source>DSP firmware file (*.cdc)</source>
<translation>Arquivo do firmware DSP (*.cdc)</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="320"/>
<source>No DSP firmware loaded</source>
<translation>Nenhum firmware DSP carregado</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="320"/>
<source>The currently loaded app has not uploaded a firmware to the DSP</source>
<translation>A aplicação não carregou um firmware DSP</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="331"/>
<source>Failed to open output file</source>
<translation>Erro ao abrir arquvio de destino</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="332"/>
<source>The currently loaded DSP firmware could not be written to the selected file. Please make sure you have permission to access this file</source>
<translation>O firmware DSP carregado não pode escrever no arquivo selecionado. Porfavor veja se você tem permissão para modificalo-lo.</translation>
</message>
</context>
<context>
<name>PatchWindow</name>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="15"/>
<source>ROM patcher</source>
<translation>Editor de ROM</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="23"/>
<source>Select input file</source>
<translation>Selecione o arquivo de entrada</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="24"/>
<location filename="../../src/panda_qt/patch_window.cpp" line="36"/>
<source>Select</source>
<translation>Selecionar</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="35"/>
<location filename="../../src/panda_qt/patch_window.cpp" line="63"/>
<source>Select patch file</source>
<translation>Seleciona um arquivo de patch</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="47"/>
<source>Apply patch</source>
<translation>Aplicar patch</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="56"/>
<source>Select file to patch</source>
<translation>Selecione um arquivo para editar</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="56"/>
<location filename="../../src/panda_qt/patch_window.cpp" line="80"/>
<source>All files (*.*)</source>
<translation>Todos os arquivos (*.*)</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="63"/>
<source>Patch files (*.ips *.ups *.bps)</source>
<translation>Arquivos de patch (*.ips *.ups *.bps)</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="71"/>
<source>Paths not provided correctly</source>
<translation>Diretórios não fornecidos corretamente</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="71"/>
<source>Please provide paths for both the input file and the patch file</source>
<translation>Por favor selecione os diretórios tanto para o arquivo de origem como para o patch</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="80"/>
<source>Select file</source>
<translation>Selecionar arquivo</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="84"/>
<source>No output path</source>
<translation>Sem diretório de saida</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="84"/>
<source>No path was provided for the output file, no patching was done</source>
<translation>Nenhum diretorio de destino foi fornecido, patch não aplicado.</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="99"/>
<source>Unknown patch format</source>
<translation>Formato de patch desconhecido</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="99"/>
<source>Unknown format for patch file. Currently IPS, UPS and BPS are supported</source>
<translation>Arquivo de patch inválido. Atualmete são suportado patches nos formatos IPS, UPS e BPS</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="108"/>
<source>Failed to open input files</source>
<translation>Falha ao abrir os arquivos</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="108"/>
<source>Make sure they&apos;re in a directory Panda3DS has access to</source>
<translation>Certifique-se de que eles estejam em um diretório ao qual o Panda3DS tenha acesso</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="135"/>
<source>Patching Success</source>
<translation>Patch aplicado</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="135"/>
<source>Your file was patched successfully.</source>
<translation>O patch foi aplicado com sucesso ao arquivo</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="141"/>
<source>Checksum mismatch</source>
<translation>Checagem inválido</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="142"/>
<source>Patch was applied successfully but a checksum mismatch was detected. The input or output files might not be correct</source>
<translation>O patch foi aplicado com sucesso porem a checagem falhou. O arquivo de origem ou destino pode não estar correto.</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="146"/>
<source>Patching error</source>
<translation>Erro de patch</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="146"/>
<source>An error occured while patching</source>
<translation>Um erro ocorreu ao aplicar o patch</translation>
</message>
</context>
<context>
<name>PatchWindow::PatchWindow</name>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="153"/>
<source>OK</source>
<translation>OK</translation>
</message>
</context>
<context>
<name>ShaderEditorWindow</name>
<message>
<location filename="../../src/panda_qt/shader_editor.cpp" line="26"/>
<source>Reload shader</source>
<translation>Recargar shader</translation>
</message>
</context>
<context>
<name>TextEditorWindow</name>
<message>
<location filename="../../src/panda_qt/text_editor.cpp" line="12"/>
<source>Lua Editor</source>
<translation>Editor Lua</translation>
</message>
<message>
<location filename="../../src/panda_qt/text_editor.cpp" line="27"/>
<source>Load script</source>
<translation>Cargar script</translation>
</message>
</context>
</TS>

View file

@ -0,0 +1,45 @@
#pragma once
#include <array>
#include "helpers.hpp"
namespace PICA {
struct DrawAcceleration {
static constexpr u32 maxAttribCount = 16;
static constexpr u32 maxLoaderCount = 12;
struct AttributeInfo {
u32 offset;
u32 stride;
u8 type;
u8 componentCount;
std::array<float, 4> fixedValue; // For fixed attributes
};
struct Loader {
// Data to upload for this loader
u8* data;
usize size;
};
u8* indexBuffer;
// Minimum and maximum index in the index buffer for a draw call
u16 minimumIndex, maximumIndex;
u32 totalAttribCount;
u32 totalLoaderCount;
u32 enabledAttributeMask;
u32 fixedAttributes;
u32 vertexDataSize;
std::array<AttributeInfo, maxAttribCount> attributeInfo;
std::array<Loader, maxLoaderCount> loaders;
bool canBeAccelerated;
bool indexed;
bool useShortIndices;
};
} // namespace PICA

View file

@ -2,7 +2,7 @@
#include "helpers.hpp"
#include "vertex_loader_rec.hpp"
// Common file for our PICA JITs (From vertex config -> CPU assembly and from PICA shader -> CPU assembly)
// Common file for our PICA JITs (From PICA shader -> CPU assembly)
namespace Dynapica {
#ifdef PANDA3DS_DYNAPICA_SUPPORTED

View file

@ -22,8 +22,11 @@ class ShaderJIT {
ShaderCache cache;
#endif
bool accurateMul = false;
public:
void setAccurateMul(bool value) { accurateMul = value; }
#ifdef PANDA3DS_SHADER_JIT_SUPPORTED
// Call this before starting to process a batch of vertices
// This will read the PICA config (uploaded shader and shader operand descriptors) and search if we've already compiled this shader
@ -36,11 +39,11 @@ class ShaderJIT {
static constexpr bool isAvailable() { return true; }
#else
void prepare(PICAShader& shaderUnit) {
Helpers::panic("Vertex Loader JIT: Tried to run ShaderJIT::Prepare on platform that does not support shader jit");
Helpers::panic("Shader JIT: Tried to run ShaderJIT::Prepare on platform that does not support shader jit");
}
void run(PICAShader& shaderUnit) {
Helpers::panic("Vertex Loader JIT: Tried to run ShaderJIT::Run on platform that does not support shader jit");
Helpers::panic("Shader JIT: Tried to run ShaderJIT::Run on platform that does not support shader jit");
}
// Define dummy callback. This should never be called if the shader JIT is not supported

View file

@ -37,6 +37,8 @@ class ShaderEmitter : private oaknut::CodeBlock, public oaknut::CodeGenerator {
// Shows whether the loaded shader has any log2 and exp2 instructions
bool codeHasLog2 = false;
bool codeHasExp2 = false;
// Whether to compile this shader using accurate, safe, non-IEEE multiplication (slow) or faster but less accurate mul
bool useSafeMUL = false;
oaknut::Label log2Func, exp2Func;
oaknut::Label emitLog2Func();
@ -123,7 +125,7 @@ class ShaderEmitter : private oaknut::CodeBlock, public oaknut::CodeGenerator {
PrologueCallback prologueCb = nullptr;
// Initialize our emitter with "allocSize" bytes of memory allocated for the code buffer
ShaderEmitter() : oaknut::CodeBlock(allocSize), oaknut::CodeGenerator(oaknut::CodeBlock::ptr()) {}
ShaderEmitter(bool useSafeMUL) : oaknut::CodeBlock(allocSize), oaknut::CodeGenerator(oaknut::CodeBlock::ptr()), useSafeMUL(useSafeMUL) {}
// PC must be a valid entrypoint here. It doesn't have that much overhead in this case, so we use std::array<>::at() to assert it does
InstructionCallback getInstructionCallback(u32 pc) { return getLabelPointer<InstructionCallback>(instructionLabels.at(pc)); }

View file

@ -32,6 +32,8 @@ class ShaderEmitter : public Xbyak::CodeGenerator {
Label negateVector;
// Vector value of (1.0, 1.0, 1.0, 1.0) for SLT(i)/SGE(i)
Label onesVector;
// Vector value of (0xFF, 0xFF, 0xFF, 0) for setting the w component to 0 in DP3
Label dp3Vector;
u32 recompilerPC = 0; // PC the recompiler is currently recompiling @
u32 loopLevel = 0; // The current loop nesting level (0 = not in a loop)
@ -43,12 +45,17 @@ class ShaderEmitter : public Xbyak::CodeGenerator {
// Shows whether the loaded shader has any log2 and exp2 instructions
bool codeHasLog2 = false;
bool codeHasExp2 = false;
// Whether to compile this shader using accurate, safe, non-IEEE multiplication (slow) or faster but less accurate mul
bool useSafeMUL = false;
Xbyak::Label log2Func, exp2Func;
Xbyak::Label emitLog2Func();
Xbyak::Label emitExp2Func();
Xbyak::util::Cpu cpuCaps;
// Emit a PICA200-compliant multiplication that handles "0 * inf = 0"
void emitSafeMUL(Xbyak::Xmm src1, Xbyak::Xmm src2, Xbyak::Xmm scratch);
// Compile all instructions from [current recompiler PC, end)
void compileUntil(const PICAShader& shaderUnit, u32 endPC);
// Compile instruction "instr"
@ -125,7 +132,7 @@ class ShaderEmitter : public Xbyak::CodeGenerator {
PrologueCallback prologueCb = nullptr;
// Initialize our emitter with "allocSize" bytes of RWX memory
ShaderEmitter() : Xbyak::CodeGenerator(allocSize) {
ShaderEmitter(bool useSafeMUL) : Xbyak::CodeGenerator(allocSize), useSafeMUL(useSafeMUL) {
cpuCaps = Xbyak::util::Cpu();
haveSSE4_1 = cpuCaps.has(Xbyak::util::Cpu::tSSE41);

View file

@ -1,6 +1,7 @@
#pragma once
#include <array>
#include "PICA/draw_acceleration.hpp"
#include "PICA/dynapica/shader_rec.hpp"
#include "PICA/float_types.hpp"
#include "PICA/pica_vertex.hpp"
@ -13,6 +14,12 @@
#include "memory.hpp"
#include "renderer.hpp"
enum class ShaderExecMode {
Interpreter, // Interpret shaders on the CPU
JIT, // Recompile shaders to CPU machine code
Hardware, // Recompiler shaders to host shaders and run them on the GPU
};
class GPU {
static constexpr u32 regNum = 0x300;
static constexpr u32 extRegNum = 0x1000;
@ -45,7 +52,7 @@ class GPU {
uint immediateModeVertIndex;
uint immediateModeAttrIndex; // Index of the immediate mode attribute we're uploading
template <bool indexed, bool useShaderJIT>
template <bool indexed, ShaderExecMode mode>
void drawArrays();
// Silly method of avoiding linking problems. TODO: Change to something less silly
@ -81,6 +88,7 @@ class GPU {
std::unique_ptr<Renderer> renderer;
PICA::Vertex getImmediateModeVertex();
void getAcceleratedDrawInfo(PICA::DrawAcceleration& accel, bool indexed);
public:
// 256 entries per LUT with each LUT as its own row forming a 2D image 256 * LUT_COUNT
// Encoded in PICA native format
@ -92,6 +100,9 @@ class GPU {
// Set to false by the renderer when the lighting_lut is uploaded ot the GPU
bool lightingLUTDirty = false;
bool fogLUTDirty = false;
std::array<uint32_t, 128> fogLUT;
GPU(Memory& mem, EmulatorConfig& config);
void display() { renderer->display(); }
void screenshot(const std::string& name) { renderer->screenshot(name); }
@ -164,7 +175,8 @@ class GPU {
u32 index = paddr - PhysicalAddrs::VRAM;
return (T*)&vram[index];
} else [[unlikely]] {
Helpers::panic("[GPU] Tried to access unknown physical address: %08X", paddr);
Helpers::warn("[GPU] Tried to access unknown physical address: %08X", paddr);
return nullptr;
}
}

View file

@ -0,0 +1,257 @@
#pragma once
#include <array>
#include <cstring>
#include <type_traits>
#include <unordered_map>
#include "PICA/pica_hash.hpp"
#include "PICA/regs.hpp"
#include "bitfield.hpp"
#include "helpers.hpp"
namespace PICA {
struct OutputConfig {
union {
u32 raw{};
// Merge the enable + compare function into 1 field to avoid duplicate shaders
// enable == off means a CompareFunction of Always
BitField<0, 3, CompareFunction> alphaTestFunction;
BitField<3, 1, u32> depthMapEnable;
BitField<4, 4, LogicOpMode> logicOpMode;
};
};
struct TextureConfig {
u32 texUnitConfig;
u32 texEnvUpdateBuffer;
// There's 6 TEV stages, and each one is configured via 4 word-sized registers
// (+ the constant color register, which we don't include here, otherwise we'd generate too many shaders)
std::array<u32, 4 * 6> tevConfigs;
};
struct FogConfig {
union {
u32 raw{};
BitField<0, 3, FogMode> mode;
BitField<3, 1, u32> flipDepth;
};
};
struct Light {
union {
u16 raw;
BitField<0, 3, u16> num;
BitField<3, 1, u16> directional;
BitField<4, 1, u16> twoSidedDiffuse;
BitField<5, 1, u16> distanceAttenuationEnable;
BitField<6, 1, u16> spotAttenuationEnable;
BitField<7, 1, u16> geometricFactor0;
BitField<8, 1, u16> geometricFactor1;
BitField<9, 1, u16> shadowEnable;
};
};
struct LightingLUTConfig {
union {
u32 raw;
BitField<0, 1, u32> enable;
BitField<1, 1, u32> absInput;
BitField<2, 3, u32> type;
BitField<5, 3, u32> scale;
};
};
struct LightingConfig {
union {
u32 raw{};
BitField<0, 1, u32> enable;
BitField<1, 4, u32> lightNum;
BitField<5, 2, u32> bumpMode;
BitField<7, 2, u32> bumpSelector;
BitField<9, 1, u32> bumpRenorm;
BitField<10, 1, u32> clampHighlights;
BitField<11, 4, u32> config;
BitField<15, 1, u32> enablePrimaryAlpha;
BitField<16, 1, u32> enableSecondaryAlpha;
BitField<17, 1, u32> enableShadow;
BitField<18, 1, u32> shadowPrimary;
BitField<19, 1, u32> shadowSecondary;
BitField<20, 1, u32> shadowInvert;
BitField<21, 1, u32> shadowAlpha;
BitField<22, 2, u32> shadowSelector;
};
std::array<LightingLUTConfig, 7> luts{};
std::array<Light, 8> lights{};
LightingConfig(const std::array<u32, 0x300>& regs) {
// Ignore lighting registers if it's disabled
if ((regs[InternalRegs::LightingEnable] & 1) == 0) {
return;
}
const u32 config0 = regs[InternalRegs::LightConfig0];
const u32 config1 = regs[InternalRegs::LightConfig1];
const u32 totalLightCount = Helpers::getBits<0, 3>(regs[InternalRegs::LightNumber]) + 1;
enable = 1;
lightNum = totalLightCount;
enableShadow = Helpers::getBit<0>(config0);
if (enableShadow) [[unlikely]] {
shadowPrimary = Helpers::getBit<16>(config0);
shadowSecondary = Helpers::getBit<17>(config0);
shadowInvert = Helpers::getBit<18>(config0);
shadowAlpha = Helpers::getBit<19>(config0);
shadowSelector = Helpers::getBits<24, 2>(config0);
}
enablePrimaryAlpha = Helpers::getBit<2>(config0);
enableSecondaryAlpha = Helpers::getBit<3>(config0);
config = Helpers::getBits<4, 4>(config0);
bumpSelector = Helpers::getBits<22, 2>(config0);
clampHighlights = Helpers::getBit<27>(config0);
bumpMode = Helpers::getBits<28, 2>(config0);
bumpRenorm = Helpers::getBit<30>(config0) ^ 1; // 0 = enable so flip it with xor
for (int i = 0; i < totalLightCount; i++) {
auto& light = lights[i];
light.num = (regs[InternalRegs::LightPermutation] >> (i * 4)) & 0x7;
const u32 lightConfig = regs[InternalRegs::Light0Config + 0x10 * light.num];
light.directional = Helpers::getBit<0>(lightConfig);
light.twoSidedDiffuse = Helpers::getBit<1>(lightConfig);
light.geometricFactor0 = Helpers::getBit<2>(lightConfig);
light.geometricFactor1 = Helpers::getBit<3>(lightConfig);
light.shadowEnable = ((config1 >> light.num) & 1) ^ 1; // This also does 0 = enabled
light.spotAttenuationEnable = ((config1 >> (8 + light.num)) & 1) ^ 1; // Same here
light.distanceAttenuationEnable = ((config1 >> (24 + light.num)) & 1) ^ 1; // Of course same here
}
LightingLUTConfig& d0 = luts[Lights::LUT_D0];
LightingLUTConfig& d1 = luts[Lights::LUT_D1];
LightingLUTConfig& sp = luts[spotlightLutIndex];
LightingLUTConfig& fr = luts[Lights::LUT_FR];
LightingLUTConfig& rb = luts[Lights::LUT_RB];
LightingLUTConfig& rg = luts[Lights::LUT_RG];
LightingLUTConfig& rr = luts[Lights::LUT_RR];
d0.enable = Helpers::getBit<16>(config1) == 0;
d1.enable = Helpers::getBit<17>(config1) == 0;
fr.enable = Helpers::getBit<19>(config1) == 0;
rb.enable = Helpers::getBit<20>(config1) == 0;
rg.enable = Helpers::getBit<21>(config1) == 0;
rr.enable = Helpers::getBit<22>(config1) == 0;
sp.enable = 1;
const u32 lutAbs = regs[InternalRegs::LightLUTAbs];
const u32 lutSelect = regs[InternalRegs::LightLUTSelect];
const u32 lutScale = regs[InternalRegs::LightLUTScale];
if (d0.enable) {
d0.absInput = Helpers::getBit<1>(lutAbs) == 0;
d0.type = Helpers::getBits<0, 3>(lutSelect);
d0.scale = Helpers::getBits<0, 3>(lutScale);
}
if (d1.enable) {
d1.absInput = Helpers::getBit<5>(lutAbs) == 0;
d1.type = Helpers::getBits<4, 3>(lutSelect);
d1.scale = Helpers::getBits<4, 3>(lutScale);
}
sp.absInput = Helpers::getBit<9>(lutAbs) == 0;
sp.type = Helpers::getBits<8, 3>(lutSelect);
sp.scale = Helpers::getBits<8, 3>(lutScale);
if (fr.enable) {
fr.absInput = Helpers::getBit<13>(lutAbs) == 0;
fr.type = Helpers::getBits<12, 3>(lutSelect);
fr.scale = Helpers::getBits<12, 3>(lutScale);
}
if (rb.enable) {
rb.absInput = Helpers::getBit<17>(lutAbs) == 0;
rb.type = Helpers::getBits<16, 3>(lutSelect);
rb.scale = Helpers::getBits<16, 3>(lutScale);
}
if (rg.enable) {
rg.absInput = Helpers::getBit<21>(lutAbs) == 0;
rg.type = Helpers::getBits<20, 3>(lutSelect);
rg.scale = Helpers::getBits<20, 3>(lutScale);
}
if (rr.enable) {
rr.absInput = Helpers::getBit<25>(lutAbs) == 0;
rr.type = Helpers::getBits<24, 3>(lutSelect);
rr.scale = Helpers::getBits<24, 3>(lutScale);
}
}
};
// Config used for identifying unique fragment pipeline configurations
struct FragmentConfig {
OutputConfig outConfig;
TextureConfig texConfig;
FogConfig fogConfig;
LightingConfig lighting;
bool operator==(const FragmentConfig& config) const {
// Hash function and equality operator required by std::unordered_map
return std::memcmp(this, &config, sizeof(FragmentConfig)) == 0;
}
FragmentConfig(const std::array<u32, 0x300>& regs) : lighting(regs) {
auto alphaTestConfig = regs[InternalRegs::AlphaTestConfig];
auto alphaTestFunction = Helpers::getBits<4, 3>(alphaTestConfig);
outConfig.alphaTestFunction =
(alphaTestConfig & 1) ? static_cast<PICA::CompareFunction>(alphaTestFunction) : PICA::CompareFunction::Always;
outConfig.depthMapEnable = regs[InternalRegs::DepthmapEnable] & 1;
// Shows if blending is enabled. If it is not enabled, then logic ops are enabled instead
const bool blendingEnabled = (regs[InternalRegs::ColourOperation] & (1 << 8)) != 0;
outConfig.logicOpMode = blendingEnabled ? LogicOpMode::Copy : LogicOpMode(Helpers::getBits<0, 4>(regs[InternalRegs::LogicOp]));
texConfig.texUnitConfig = regs[InternalRegs::TexUnitCfg];
texConfig.texEnvUpdateBuffer = regs[InternalRegs::TexEnvUpdateBuffer];
// Set up TEV stages. Annoyingly we can't just memcpy as the TEV registers are arranged like
// {Source, Operand, Combiner, Color, Scale} and we want to skip the color register since it's uploaded via UBO
#define setupTevStage(stage) \
std::memcpy(&texConfig.tevConfigs[stage * 4], &regs[InternalRegs::TexEnv##stage##Source], 3 * sizeof(u32)); \
texConfig.tevConfigs[stage * 4 + 3] = regs[InternalRegs::TexEnv##stage##Source + 4];
setupTevStage(0);
setupTevStage(1);
setupTevStage(2);
setupTevStage(3);
setupTevStage(4);
setupTevStage(5);
#undef setupTevStage
fogConfig.mode = (FogMode)Helpers::getBits<0, 3>(regs[InternalRegs::TexEnvUpdateBuffer]);
if (fogConfig.mode == FogMode::Fog) {
fogConfig.flipDepth = Helpers::getBit<16>(regs[InternalRegs::TexEnvUpdateBuffer]);
}
}
};
static_assert(
std::has_unique_object_representations<OutputConfig>() && std::has_unique_object_representations<TextureConfig>() &&
std::has_unique_object_representations<FogConfig>() && std::has_unique_object_representations<Light>()
);
} // namespace PICA
// Override std::hash for our fragment config class
template <>
struct std::hash<PICA::FragmentConfig> {
std::size_t operator()(const PICA::FragmentConfig& config) const noexcept { return PICAHash::computeHash((const char*)&config, sizeof(config)); }
};

View file

@ -0,0 +1,47 @@
#pragma once
#include <array>
#include <cstddef>
#include <type_traits>
#include "helpers.hpp"
namespace PICA {
struct LightUniform {
using vec3 = std::array<float, 3>;
// std140 requires vec3s be aligned to 16 bytes
alignas(16) vec3 specular0;
alignas(16) vec3 specular1;
alignas(16) vec3 diffuse;
alignas(16) vec3 ambient;
alignas(16) vec3 position;
alignas(16) vec3 spotlightDirection;
float distanceAttenuationBias;
float distanceAttenuationScale;
};
struct FragmentUniforms {
using vec3 = std::array<float, 3>;
using vec4 = std::array<float, 4>;
static constexpr usize tevStageCount = 6;
s32 alphaReference;
float depthScale;
float depthOffset;
alignas(16) vec4 constantColors[tevStageCount];
alignas(16) vec4 tevBufferColor;
alignas(16) vec4 clipCoords;
// Note: We upload these as a u32 and decode on GPU.
// Particularly the fog colour since fog is really uncommon and it doesn't matter if we decode on GPU.
u32 globalAmbientLight;
u32 fogColor;
// NOTE: THIS MUST BE LAST so that if lighting is disabled we can potentially omit uploading it
LightUniform lightUniforms[8];
};
// Assert that lightUniforms is the last member of the structure
static_assert(offsetof(FragmentUniforms, lightUniforms) + 8 * sizeof(LightUniform) == sizeof(FragmentUniforms));
} // namespace PICA

275
include/PICA/pica_simd.hpp Normal file
View file

@ -0,0 +1,275 @@
#pragma once
#include <algorithm>
#include <limits>
#include <utility>
#include "compiler_builtins.hpp"
#include "helpers.hpp"
#if defined(_M_AMD64) || defined(__x86_64__)
#define PICA_SIMD_X64
#include <immintrin.h>
#elif defined(_M_ARM64) || defined(__aarch64__)
#define PICA_SIMD_ARM64
#include <arm_neon.h>
#endif
// Optimized functions for analyzing PICA index buffers (Finding minimum and maximum index values inside them)
namespace PICA::IndexBuffer {
// Non-SIMD, portable algorithm
template <bool useShortIndices>
std::pair<u16, u16> analyzePortable(u8* indexBuffer, u32 vertexCount) {
u16 minimumIndex = std::numeric_limits<u16>::max();
u16 maximumIndex = 0;
// Calculate the minimum and maximum indices used in the index buffer, so we'll only upload them
if constexpr (useShortIndices) {
u16* indexBuffer16 = reinterpret_cast<u16*>(indexBuffer);
for (u32 i = 0; i < vertexCount; i++) {
u16 index = indexBuffer16[i];
minimumIndex = std::min(minimumIndex, index);
maximumIndex = std::max(maximumIndex, index);
}
} else {
for (u32 i = 0; i < vertexCount; i++) {
u16 index = u16(indexBuffer[i]);
minimumIndex = std::min(minimumIndex, index);
maximumIndex = std::max(maximumIndex, index);
}
}
return {minimumIndex, maximumIndex};
}
#ifdef PICA_SIMD_ARM64
template <bool useShortIndices>
ALWAYS_INLINE std::pair<u16, u16> analyzeNEON(u8* indexBuffer, u32 vertexCount) {
// We process 16 bytes per iteration, which is 8 vertices if we're using u16 indices or 16 vertices if we're using u8 indices
constexpr u32 vertsPerLoop = (useShortIndices) ? 8 : 16;
if (vertexCount < vertsPerLoop) {
return analyzePortable<useShortIndices>(indexBuffer, vertexCount);
}
u16 minimumIndex, maximumIndex;
if constexpr (useShortIndices) {
// 16-bit indices
uint16x8_t minima = vdupq_n_u16(0xffff);
uint16x8_t maxima = vdupq_n_u16(0);
while (vertexCount >= vertsPerLoop) {
const uint16x8_t data = vld1q_u16(reinterpret_cast<u16*>(indexBuffer));
minima = vminq_u16(data, minima);
maxima = vmaxq_u16(data, maxima);
indexBuffer += 16;
vertexCount -= vertsPerLoop;
}
// Do horizontal min/max operations to get the actual minimum and maximum from all the vertices we processed with SIMD
// We want to gather the actual minimum and maximum in the line bottom lane of the minima/maxima vectors
// uint16x4_t foldedMinima1 = vmin_u16(vget_high_u16(minima), vget_low_u16(minima));
// uint16x4_t foldedMaxima1 = vmax_u16(vget_high_u16(maxima), vget_low_u16(maxima));
uint16x8_t foldedMinima1 = vpminq_u16(minima, minima);
uint16x8_t foldedMinima2 = vpminq_u16(foldedMinima1, foldedMinima1);
uint16x8_t foldedMinima3 = vpminq_u16(foldedMinima2, foldedMinima2);
uint16x8_t foldedMaxima1 = vpmaxq_u16(maxima, maxima);
uint16x8_t foldedMaxima2 = vpmaxq_u16(foldedMaxima1, foldedMaxima1);
uint16x8_t foldedMaxima3 = vpmaxq_u16(foldedMaxima2, foldedMaxima2);
minimumIndex = vgetq_lane_u16(foldedMinima3, 0);
maximumIndex = vgetq_lane_u16(foldedMaxima3, 0);
} else {
// 8-bit indices
uint8x16_t minima = vdupq_n_u8(0xff);
uint8x16_t maxima = vdupq_n_u8(0);
while (vertexCount >= vertsPerLoop) {
uint8x16_t data = vld1q_u8(indexBuffer);
minima = vminq_u8(data, minima);
maxima = vmaxq_u8(data, maxima);
indexBuffer += 16;
vertexCount -= vertsPerLoop;
}
// Do a similar horizontal min/max as in the u16 case, except now we're working uint8x16 instead of uint16x4 so we need 4 folds
uint8x16_t foldedMinima1 = vpminq_u8(minima, minima);
uint8x16_t foldedMinima2 = vpminq_u8(foldedMinima1, foldedMinima1);
uint8x16_t foldedMinima3 = vpminq_u8(foldedMinima2, foldedMinima2);
uint8x16_t foldedMinima4 = vpminq_u8(foldedMinima3, foldedMinima3);
uint8x16_t foldedMaxima1 = vpmaxq_u8(maxima, maxima);
uint8x16_t foldedMaxima2 = vpmaxq_u8(foldedMaxima1, foldedMaxima1);
uint8x16_t foldedMaxima3 = vpmaxq_u8(foldedMaxima2, foldedMaxima2);
uint8x16_t foldedMaxima4 = vpmaxq_u8(foldedMaxima3, foldedMaxima3);
minimumIndex = u16(vgetq_lane_u8(foldedMinima4, 0));
maximumIndex = u16(vgetq_lane_u8(foldedMaxima4, 0));
}
// If any indices could not be processed cause the buffer size is not 16-byte aligned, process them the naive way
// Calculate the minimum and maximum indices used in the index buffer, so we'll only upload them
while (vertexCount > 0) {
if constexpr (useShortIndices) {
u16 index = *reinterpret_cast<u16*>(indexBuffer);
minimumIndex = std::min(minimumIndex, index);
maximumIndex = std::max(maximumIndex, index);
indexBuffer += 2;
} else {
u16 index = u16(*indexBuffer++);
minimumIndex = std::min(minimumIndex, index);
maximumIndex = std::max(maximumIndex, index);
}
vertexCount -= 1;
}
return {minimumIndex, maximumIndex};
}
#endif
#if defined(PICA_SIMD_X64) && (defined(__SSE4_1__) || defined(__AVX__))
template <bool useShortIndices>
ALWAYS_INLINE std::pair<u16, u16> analyzeSSE4_1(u8* indexBuffer, u32 vertexCount) {
// We process 16 bytes per iteration, which is 8 vertices if we're using u16
// indices or 16 vertices if we're using u8 indices
constexpr u32 vertsPerLoop = (useShortIndices) ? 8 : 16;
if (vertexCount < vertsPerLoop) {
return analyzePortable<useShortIndices>(indexBuffer, vertexCount);
}
u16 minimumIndex, maximumIndex;
if constexpr (useShortIndices) {
// Calculate the horizontal minimum/maximum value across an SSE vector of 16-bit unsigned integers.
// Based on https://stackoverflow.com/a/22259607
auto horizontalMin16 = [](__m128i vector) -> u16 { return u16(_mm_cvtsi128_si32(_mm_minpos_epu16(vector))); };
auto horizontalMax16 = [](__m128i vector) -> u16 {
// We have an instruction to compute horizontal minimum but not maximum, so we use it.
// To use it, we have to subtract each value from 0xFFFF (which we do with an xor), then execute a horizontal minimum
__m128i flipped = _mm_xor_si128(vector, _mm_set_epi32(0xffffffffu, 0xffffffffu, 0xffffffffu, 0xffffffffu));
u16 min = u16(_mm_cvtsi128_si32(_mm_minpos_epu16(flipped)));
return u16(min ^ 0xffff);
};
// 16-bit indices
// Initialize the minima vector to all FFs (So 0xFFFF for each 16-bit lane)
// And the maxima vector to all 0s (0 for each 16-bit lane)
__m128i minima = _mm_set_epi32(0xffffffffu, 0xffffffffu, 0xffffffffu, 0xffffffffu);
__m128i maxima = _mm_set_epi32(0, 0, 0, 0);
while (vertexCount >= vertsPerLoop) {
const __m128i data = _mm_loadu_si128(reinterpret_cast<const __m128i*>(indexBuffer));
minima = _mm_min_epu16(data, minima);
maxima = _mm_max_epu16(data, maxima);
indexBuffer += 16;
vertexCount -= vertsPerLoop;
}
minimumIndex = u16(horizontalMin16(minima));
maximumIndex = u16(horizontalMax16(maxima));
} else {
// Calculate the horizontal minimum/maximum value across an SSE vector of 8-bit unsigned integers.
// Based on https://stackoverflow.com/a/22259607
auto horizontalMin8 = [](__m128i vector) -> u8 {
vector = _mm_min_epu8(vector, _mm_shuffle_epi32(vector, _MM_SHUFFLE(3, 2, 3, 2)));
vector = _mm_min_epu8(vector, _mm_shuffle_epi32(vector, _MM_SHUFFLE(1, 1, 1, 1)));
vector = _mm_min_epu8(vector, _mm_shufflelo_epi16(vector, _MM_SHUFFLE(1, 1, 1, 1)));
vector = _mm_min_epu8(vector, _mm_srli_epi16(vector, 8));
return u8(_mm_cvtsi128_si32(vector));
};
auto horizontalMax8 = [](__m128i vector) -> u8 {
vector = _mm_max_epu8(vector, _mm_shuffle_epi32(vector, _MM_SHUFFLE(3, 2, 3, 2)));
vector = _mm_max_epu8(vector, _mm_shuffle_epi32(vector, _MM_SHUFFLE(1, 1, 1, 1)));
vector = _mm_max_epu8(vector, _mm_shufflelo_epi16(vector, _MM_SHUFFLE(1, 1, 1, 1)));
vector = _mm_max_epu8(vector, _mm_srli_epi16(vector, 8));
return u8(_mm_cvtsi128_si32(vector));
};
// 8-bit indices
// Initialize the minima vector to all FFs (So 0xFF for each 8-bit lane)
// And the maxima vector to all 0s (0 for each 8-bit lane)
__m128i minima = _mm_set_epi32(0xffffffffu, 0xffffffffu, 0xffffffffu, 0xffffffffu);
__m128i maxima = _mm_set_epi32(0, 0, 0, 0);
while (vertexCount >= vertsPerLoop) {
const __m128i data = _mm_loadu_si128(reinterpret_cast<const __m128i*>(indexBuffer));
minima = _mm_min_epu8(data, minima);
maxima = _mm_max_epu8(data, maxima);
indexBuffer += 16;
vertexCount -= vertsPerLoop;
}
minimumIndex = u16(horizontalMin8(minima));
maximumIndex = u16(horizontalMax8(maxima));
}
// If any indices could not be processed cause the buffer size
// is not 16-byte aligned, process them the naive way
// Calculate the minimum and maximum indices used in the index
// buffer, so we'll only upload them
while (vertexCount > 0) {
if constexpr (useShortIndices) {
u16 index = *reinterpret_cast<u16*>(indexBuffer);
minimumIndex = std::min(minimumIndex, index);
maximumIndex = std::max(maximumIndex, index);
indexBuffer += 2;
} else {
u16 index = u16(*indexBuffer++);
minimumIndex = std::min(minimumIndex, index);
maximumIndex = std::max(maximumIndex, index);
}
vertexCount -= 1;
}
return {minimumIndex, maximumIndex};
}
#endif
// Analyzes a PICA index buffer to get the minimum and maximum indices in the
// buffer, and returns them in a pair in the form [min, max]. Takes a template
// parameter to decide whether the indices in the buffer are u8 or u16
template <bool useShortIndices>
std::pair<u16, u16> analyze(u8* indexBuffer, u32 vertexCount) {
#if defined(PICA_SIMD_ARM64)
return analyzeNEON<useShortIndices>(indexBuffer, vertexCount);
#elif defined(PICA_SIMD_X64) && (defined(__SSE4_1__) || defined(__AVX__))
// Annoyingly, MSVC refuses to define __SSE4_1__ even when we're building with AVX
return analyzeSSE4_1<useShortIndices>(indexBuffer, vertexCount);
#else
return analyzePortable<useShortIndices>(indexBuffer, vertexCount);
#endif
}
// In some really unfortunate scenarios (eg Android Studio emulator), we don't have access to glDrawRangeElementsBaseVertex
// So we need to subtract the base vertex index from every index in the index buffer ourselves
// This is not really common, so we do it without SIMD for the moment, just to be able to run on Android Studio
template <bool useShortIndices>
void subtractBaseIndex(u8* indexBuffer, u32 indexCount, u16 baseIndex) {
// Calculate the minimum and maximum indices used in the index buffer, so we'll only upload them
if constexpr (useShortIndices) {
u16* indexBuffer16 = reinterpret_cast<u16*>(indexBuffer);
for (u32 i = 0; i < indexCount; i++) {
indexBuffer16[i] -= baseIndex;
}
} else {
u8 baseIndex8 = u8(baseIndex);
for (u32 i = 0; i < indexCount; i++) {
indexBuffer[i] -= baseIndex8;
}
}
}
} // namespace PICA::IndexBuffer

View file

@ -0,0 +1,57 @@
#pragma once
#include <array>
#include <cassert>
#include <cstring>
#include <type_traits>
#include <unordered_map>
#include "PICA/pica_hash.hpp"
#include "PICA/regs.hpp"
#include "PICA/shader.hpp"
#include "bitfield.hpp"
#include "helpers.hpp"
namespace PICA {
// Configuration struct used
struct VertConfig {
PICAHash::HashType shaderHash;
PICAHash::HashType opdescHash;
u32 entrypoint;
// PICA registers for configuring shader output->fragment semantic mapping
std::array<u32, 7> outmaps{};
u16 outputMask;
u8 outputCount;
bool usingUbershader;
// Pad to 56 bytes so that the compiler won't insert unnecessary padding, which in turn will affect our unordered_map lookup
// As the padding will get hashed and memcmp'd...
u32 pad{};
bool operator==(const VertConfig& config) const {
// Hash function and equality operator required by std::unordered_map
return std::memcmp(this, &config, sizeof(VertConfig)) == 0;
}
VertConfig(PICAShader& shader, const std::array<u32, 0x300>& regs, bool usingUbershader) : usingUbershader(usingUbershader) {
shaderHash = shader.getCodeHash();
opdescHash = shader.getOpdescHash();
entrypoint = shader.entrypoint;
outputCount = regs[PICA::InternalRegs::ShaderOutputCount] & 7;
outputMask = regs[PICA::InternalRegs::VertexShaderOutputMask];
for (int i = 0; i < outputCount; i++) {
// Mask out unused bits
outmaps[i] = regs[PICA::InternalRegs::ShaderOutmap0 + i] & 0x1F1F1F1F;
}
}
};
} // namespace PICA
static_assert(sizeof(PICA::VertConfig) == 56);
// Override std::hash for our vertex config class
template <>
struct std::hash<PICA::VertConfig> {
std::size_t operator()(const PICA::VertConfig& config) const noexcept { return PICAHash::computeHash((const char*)&config, sizeof(config)); }
};

View file

@ -1,7 +1,8 @@
#pragma once
#include "PICA/float_types.hpp"
#include <array>
#include "PICA/float_types.hpp"
namespace PICA {
// A representation of the output vertex as it comes out of the vertex shader, with padding and all
struct Vertex {

View file

@ -51,6 +51,18 @@ namespace PICA {
#undef defineTexEnv
// clang-format on
// Fog registers
FogColor = 0xE1,
FogLUTIndex = 0xE6,
FogLUTData0 = 0xE8,
FogLUTData1 = 0xE9,
FogLUTData2 = 0xEA,
FogLUTData3 = 0xEB,
FogLUTData4 = 0xEC,
FogLUTData5 = 0xED,
FogLUTData6 = 0xEE,
FogLUTData7 = 0xEF,
// Framebuffer registers
ColourOperation = 0x100,
BlendFunc = 0x101,
@ -67,7 +79,29 @@ namespace PICA {
ColourBufferLoc = 0x11D,
FramebufferSize = 0x11E,
//LightingRegs
// Lighting registers
LightingEnable = 0x8F,
Light0Specular0 = 0x140,
Light0Specular1 = 0x141,
Light0Diffuse = 0x142,
Light0Ambient = 0x143,
Light0XY = 0x144,
Light0Z = 0x145,
Light0SpotlightXY = 0x146,
Light0SpotlightZ = 0x147,
Light0Config = 0x149,
Light0AttenuationBias = 0x14A,
Light0AttenuationScale = 0x14B,
LightGlobalAmbient = 0x1C0,
LightNumber = 0x1C2,
LightConfig0 = 0x1C3,
LightConfig1 = 0x1C4,
LightPermutation = 0x1D9,
LightLUTAbs = 0x1D0,
LightLUTSelect = 0x1D1,
LightLUTScale = 0x1D2,
LightingLUTIndex = 0x01C5,
LightingLUTData0 = 0x01C8,
LightingLUTData1 = 0x01C9,
@ -231,7 +265,8 @@ namespace PICA {
enum : u32 {
LUT_D0 = 0,
LUT_D1,
LUT_FR,
// LUT 2 is not used, the emulator internally uses it for referring to the current source's spotlight in shaders
LUT_FR = 0x3,
LUT_RB,
LUT_RG,
LUT_RR,
@ -255,6 +290,11 @@ namespace PICA {
};
}
// There's actually 8 different LUTs (SP0-SP7), one for each light with different indices (8-15)
// We use an unused LUT value for "this light source's spotlight" instead and figure out which light source to use in compileLutLookup
// This is particularly intuitive in several places, such as checking if a LUT is enabled
static constexpr int spotlightLutIndex = 2;
enum class TextureFmt : u32 {
RGBA8 = 0x0,
RGB8 = 0x1,
@ -345,4 +385,156 @@ namespace PICA {
GeometryPrimitive = 3,
};
enum class CompareFunction : u32 {
Never = 0,
Always = 1,
Equal = 2,
NotEqual = 3,
Less = 4,
LessOrEqual = 5,
Greater = 6,
GreaterOrEqual = 7,
};
enum class LogicOpMode : u32 {
Clear = 0,
And = 1,
ReverseAnd = 2,
Copy = 3,
Set = 4,
InvertedCopy = 5,
Nop = 6,
Invert = 7,
Nand = 8,
Or = 9,
Nor = 10,
Xor = 11,
Equiv = 12,
InvertedAnd = 13,
ReverseOr = 14,
InvertedOr = 15,
};
enum class FogMode : u32 {
Disabled = 0,
Fog = 5,
Gas = 7,
};
struct TexEnvConfig {
enum class Source : u8 {
PrimaryColor = 0x0,
PrimaryFragmentColor = 0x1,
SecondaryFragmentColor = 0x2,
Texture0 = 0x3,
Texture1 = 0x4,
Texture2 = 0x5,
Texture3 = 0x6,
// TODO: Inbetween values are unknown
PreviousBuffer = 0xD,
Constant = 0xE,
Previous = 0xF,
};
enum class ColorOperand : u8 {
SourceColor = 0x0,
OneMinusSourceColor = 0x1,
SourceAlpha = 0x2,
OneMinusSourceAlpha = 0x3,
SourceRed = 0x4,
OneMinusSourceRed = 0x5,
// TODO: Inbetween values are unknown
SourceGreen = 0x8,
OneMinusSourceGreen = 0x9,
// Inbetween values are unknown
SourceBlue = 0xC,
OneMinusSourceBlue = 0xD,
};
enum class AlphaOperand : u8 {
SourceAlpha = 0x0,
OneMinusSourceAlpha = 0x1,
SourceRed = 0x2,
OneMinusSourceRed = 0x3,
SourceGreen = 0x4,
OneMinusSourceGreen = 0x5,
SourceBlue = 0x6,
OneMinusSourceBlue = 0x7,
};
enum class Operation : u8 {
Replace = 0,
Modulate = 1,
Add = 2,
AddSigned = 3,
Lerp = 4,
Subtract = 5,
Dot3RGB = 6,
Dot3RGBA = 7,
MultiplyAdd = 8,
AddMultiply = 9,
};
// RGB sources
Source colorSource1, colorSource2, colorSource3;
// Alpha sources
Source alphaSource1, alphaSource2, alphaSource3;
// RGB operands
ColorOperand colorOperand1, colorOperand2, colorOperand3;
// Alpha operands
AlphaOperand alphaOperand1, alphaOperand2, alphaOperand3;
// Texture environment operations for this stage
Operation colorOp, alphaOp;
u32 constColor;
private:
// These are the only private members since their value doesn't actually reflect the scale
// So we make them public so we'll always use the appropriate member functions instead
u8 colorScale;
u8 alphaScale;
public:
// Create texture environment object from TEV registers
TexEnvConfig(u32 source, u32 operand, u32 combiner, u32 color, u32 scale) : constColor(color) {
colorSource1 = Helpers::getBits<0, 4, Source>(source);
colorSource2 = Helpers::getBits<4, 4, Source>(source);
colorSource3 = Helpers::getBits<8, 4, Source>(source);
alphaSource1 = Helpers::getBits<16, 4, Source>(source);
alphaSource2 = Helpers::getBits<20, 4, Source>(source);
alphaSource3 = Helpers::getBits<24, 4, Source>(source);
colorOperand1 = Helpers::getBits<0, 4, ColorOperand>(operand);
colorOperand2 = Helpers::getBits<4, 4, ColorOperand>(operand);
colorOperand3 = Helpers::getBits<8, 4, ColorOperand>(operand);
alphaOperand1 = Helpers::getBits<12, 3, AlphaOperand>(operand);
alphaOperand2 = Helpers::getBits<16, 3, AlphaOperand>(operand);
alphaOperand3 = Helpers::getBits<20, 3, AlphaOperand>(operand);
colorOp = Helpers::getBits<0, 4, Operation>(combiner);
alphaOp = Helpers::getBits<16, 4, Operation>(combiner);
colorScale = Helpers::getBits<0, 2>(scale);
alphaScale = Helpers::getBits<16, 2>(scale);
}
u32 getColorScale() { return (colorScale <= 2) ? (1 << colorScale) : 1; }
u32 getAlphaScale() { return (alphaScale <= 2) ? (1 << alphaScale) : 1; }
bool isPassthroughStage() {
// clang-format off
// Thank you to the Citra dev that wrote this out
return (
colorOp == Operation::Replace && alphaOp == Operation::Replace &&
colorSource1 == Source::Previous && alphaSource1 == Source::Previous &&
colorOperand1 == ColorOperand::SourceColor && alphaOperand1 == AlphaOperand::SourceAlpha &&
getColorScale() == 1 && getAlphaScale() == 1
);
// clang-format on
}
};
} // namespace PICA

View file

@ -1,6 +1,8 @@
#pragma once
#include <algorithm>
#include <array>
#include <cassert>
#include <cstddef>
#include <cstring>
#include "PICA/float_types.hpp"
@ -21,7 +23,7 @@ namespace ShaderOpcodes {
DST = 0x04,
EX2 = 0x05,
LG2 = 0x06,
LIT = 0x07,
LITP = 0x07,
MUL = 0x08,
SGE = 0x09,
SLT = 0x0A,
@ -56,6 +58,10 @@ namespace ShaderOpcodes {
};
}
namespace PICA::ShaderGen {
class ShaderDecompiler;
};
// Note: All PICA f24 vec4 registers must have the alignas(16) specifier to make them easier to access in SSE/NEON code in the JIT
class PICAShader {
using f24 = Floats::f24;
@ -90,14 +96,22 @@ class PICAShader {
public:
// These are placed close to the temp registers and co because it helps the JIT generate better code
u32 entrypoint = 0; // Initial shader PC
u32 boolUniform;
std::array<std::array<u8, 4>, 4> intUniforms;
// We want these registers in this order & with this alignment for uploading them directly to a UBO
// When emulating shaders on the GPU. Plus this alignment for float uniforms is necessary for doing SIMD in the shader->CPU recompilers.
alignas(16) std::array<vec4f, 96> floatUniforms;
alignas(16) std::array<std::array<u8, 4>, 4> intUniforms;
u32 boolUniform;
alignas(16) std::array<vec4f, 16> fixedAttributes; // Fixed vertex attributes
alignas(16) std::array<vec4f, 16> inputs; // Attributes passed to the shader
alignas(16) std::array<vec4f, 16> outputs;
alignas(16) vec4f dummy = vec4f({f24::zero(), f24::zero(), f24::zero(), f24::zero()}); // Dummy register used by the JIT
// We use a hashmap for matching 3DS shaders to their equivalent compiled code in our shader cache in the shader JIT
// We choose our hash type to be a 64-bit integer by default, as the collision chance is very tiny and generating it is decently optimal
// Ideally we want to be able to support multiple different types of hash depending on compilation settings, but let's get this working first
using Hash = PICAHash::HashType;
protected:
std::array<u32, 128> operandDescriptors;
@ -116,20 +130,20 @@ class PICAShader {
std::array<CallInfo, 4> callInfo;
ShaderType type;
// We use a hashmap for matching 3DS shaders to their equivalent compiled code in our shader cache in the shader JIT
// We choose our hash type to be a 64-bit integer by default, as the collision chance is very tiny and generating it is decently optimal
// Ideally we want to be able to support multiple different types of hash depending on compilation settings, but let's get this working first
using Hash = PICAHash::HashType;
Hash lastCodeHash = 0; // Last hash computed for the shader code (Used for the JIT caching mechanism)
Hash lastOpdescHash = 0; // Last hash computed for the operand descriptors (Also used for the JIT)
public:
bool uniformsDirty = false;
protected:
bool codeHashDirty = false;
bool opdescHashDirty = false;
// Add these as friend classes for the JIT so it has access to all important state
friend class ShaderJIT;
friend class ShaderEmitter;
friend class PICA::ShaderGen::ShaderDecompiler;
vec4f getSource(u32 source);
vec4f& getDest(u32 dest);
@ -151,6 +165,7 @@ class PICAShader {
void jmpc(u32 instruction);
void jmpu(u32 instruction);
void lg2(u32 instruction);
void litp(u32 instruction);
void loop(u32 instruction);
void mad(u32 instruction);
void madi(u32 instruction);
@ -220,13 +235,9 @@ class PICAShader {
public:
static constexpr size_t maxInstructionCount = 4096;
std::array<u32, maxInstructionCount> loadedShader; // Currently loaded & active shader
std::array<u32, maxInstructionCount> bufferedShader; // Shader to be transferred when the SH_CODETRANSFER_END reg gets written to
PICAShader(ShaderType type) : type(type) {}
// Theese functions are in the header to be inlined more easily, though with LTO I hope I'll be able to move them
void finalize() { std::memcpy(&loadedShader[0], &bufferedShader[0], 4096 * sizeof(u32)); }
void setBufferIndex(u32 index) { bufferIndex = index & 0xfff; }
void setOpDescriptorIndex(u32 index) { opDescriptorIndex = index & 0x7f; }
@ -235,7 +246,7 @@ class PICAShader {
Helpers::panic("o no, shader upload overflew");
}
bufferedShader[bufferIndex++] = word;
loadedShader[bufferIndex++] = word;
bufferIndex &= 0xfff;
codeHashDirty = true; // Signal the JIT if necessary that the program hash has potentially changed
@ -277,6 +288,7 @@ class PICAShader {
uniform[2] = f24::fromRaw(((floatUniformBuffer[0] & 0xff) << 16) | (floatUniformBuffer[1] >> 16));
uniform[3] = f24::fromRaw(floatUniformBuffer[0] >> 8);
}
uniformsDirty = true;
}
}
@ -288,6 +300,12 @@ class PICAShader {
u[1] = getBits<8, 8>(word);
u[2] = getBits<16, 8>(word);
u[3] = getBits<24, 8>(word);
uniformsDirty = true;
}
void uploadBoolUniform(u32 value) {
boolUniform = value;
uniformsDirty = true;
}
void run();
@ -295,4 +313,13 @@ class PICAShader {
Hash getCodeHash();
Hash getOpdescHash();
};
// Returns how big the PICA uniforms are combined. Used for hw accelerated shaders where we upload the uniforms to our GPU.
static constexpr usize totalUniformSize() { return sizeof(floatUniforms) + sizeof(intUniforms) + sizeof(boolUniform); }
void* getUniformPointer() { return static_cast<void*>(&floatUniforms); }
};
static_assert(
offsetof(PICAShader, intUniforms) == offsetof(PICAShader, floatUniforms) + 96 * sizeof(float) * 4 &&
offsetof(PICAShader, boolUniform) == offsetof(PICAShader, intUniforms) + 4 * sizeof(u8) * 4
);

View file

@ -0,0 +1,131 @@
#pragma once
#include <fmt/format.h>
#include <map>
#include <set>
#include <string>
#include <tuple>
#include <utility>
#include <vector>
#include "PICA/shader.hpp"
#include "PICA/shader_gen_types.hpp"
struct EmulatorConfig;
namespace PICA::ShaderGen {
// Control flow analysis is partially based on
// https://github.com/PabloMK7/citra/blob/d0179559466ff09731d74474322ee880fbb44b00/src/video_core/shader/generator/glsl_shader_decompiler.cpp#L33
struct ControlFlow {
// A continuous range of addresses
struct AddressRange {
u32 start, end;
AddressRange(u32 start, u32 end) : start(start), end(end) {}
// Use lexicographic comparison for functions in order to sort them in a set
bool operator<(const AddressRange& other) const { return std::tie(start, end) < std::tie(other.start, other.end); }
};
struct Function {
using Labels = std::set<u32>;
enum class ExitMode {
Unknown, // Can't guarantee whether we'll exit properly, fall back to CPU shaders (can happen with jmp shenanigans)
AlwaysReturn, // All paths reach the return point.
Conditional, // One or more code paths reach the return point or an END instruction conditionally.
AlwaysEnd, // All paths reach an END instruction.
};
u32 start; // Starting PC of the function
u32 end; // End PC of the function
Labels outLabels{}; // Labels this function can "goto" (jump) to
ExitMode exitMode = ExitMode::Unknown;
explicit Function(u32 start, u32 end) : start(start), end(end) {}
bool operator<(const Function& other) const { return AddressRange(start, end) < AddressRange(other.start, other.end); }
std::string getIdentifier() const { return fmt::format("fn_{}_{}", start, end); }
// To handle weird control flow, we have to return from each function a bool that indicates whether or not the shader reached an end
// instruction and should thus terminate. This is necessary for games like Rayman and Gravity Falls, which have "END" instructions called
// from within functions deep in the callstack
std::string getForwardDecl() const { return fmt::format("bool fn_{}_{}();\n", start, end); }
std::string getCallStatement() const { return fmt::format("fn_{}_{}()", start, end); }
};
std::set<Function> functions{};
std::map<AddressRange, Function::ExitMode> exitMap{};
// Tells us whether analysis of the shader we're trying to compile failed, in which case we'll need to fail back to shader emulation
// On the CPU
bool analysisFailed = false;
// This will recursively add all functions called by the function too, as analyzeFunction will call addFunction on control flow instructions
const Function* addFunction(const PICAShader& shader, u32 start, u32 end) {
auto searchIterator = functions.find(Function(start, end));
if (searchIterator != functions.end()) {
return &(*searchIterator);
}
// Add this function and analyze it if it doesn't already exist
Function function(start, end);
function.exitMode = analyzeFunction(shader, start, end, function.outLabels);
// This function could not be fully analyzed, report failure
if (function.exitMode == Function::ExitMode::Unknown) {
analysisFailed = true;
return nullptr;
}
// Add function to our function list
auto [it, added] = functions.insert(std::move(function));
return &(*it);
}
void analyze(const PICAShader& shader, u32 entrypoint);
Function::ExitMode analyzeFunction(const PICAShader& shader, u32 start, u32 end, Function::Labels& labels);
};
class ShaderDecompiler {
using AddressRange = ControlFlow::AddressRange;
using Function = ControlFlow::Function;
ControlFlow controlFlow{};
PICAShader& shader;
EmulatorConfig& config;
std::string decompiledShader;
u32 entrypoint;
API api;
Language language;
bool compilationError = false;
void compileInstruction(u32& pc, bool& finished);
// Compile range "range" and returns the end PC or if we're "finished" with the program (called an END instruction)
std::pair<u32, bool> compileRange(const AddressRange& range);
void callFunction(const Function& function);
const Function* findFunction(const AddressRange& range);
void writeAttributes();
std::string getSource(u32 source, u32 index) const;
std::string getDest(u32 dest) const;
std::string getSwizzlePattern(u32 swizzle) const;
std::string getDestSwizzle(u32 destinationMask) const;
const char* getCondition(u32 cond, u32 refX, u32 refY);
void setDest(u32 operandDescriptor, const std::string& dest, const std::string& value);
// Returns if the instruction uses the typical register encodings most instructions use
// With some exceptions like MAD/MADI, and the control flow instructions which are completely different
bool usesCommonEncoding(u32 instruction) const;
public:
ShaderDecompiler(PICAShader& shader, EmulatorConfig& config, u32 entrypoint, API api, Language language)
: shader(shader), entrypoint(entrypoint), config(config), api(api), language(language), decompiledShader("") {}
std::string decompile();
};
std::string decompileShader(PICAShader& shader, EmulatorConfig& config, u32 entrypoint, API api, Language language);
} // namespace PICA::ShaderGen

View file

@ -0,0 +1,43 @@
#pragma once
#include <string>
#include "PICA/gpu.hpp"
#include "PICA/pica_frag_config.hpp"
#include "PICA/pica_vert_config.hpp"
#include "PICA/regs.hpp"
#include "PICA/shader_gen_types.hpp"
#include "helpers.hpp"
namespace PICA::ShaderGen {
class FragmentGenerator {
API api;
Language language;
void compileTEV(std::string& shader, int stage, const PICA::FragmentConfig& config);
void getSource(std::string& shader, PICA::TexEnvConfig::Source source, int index, const PICA::FragmentConfig& config);
void getColorOperand(std::string& shader, PICA::TexEnvConfig::Source source, PICA::TexEnvConfig::ColorOperand color, int index, const PICA::FragmentConfig& config);
void getAlphaOperand(std::string& shader, PICA::TexEnvConfig::Source source, PICA::TexEnvConfig::AlphaOperand alpha, int index, const PICA::FragmentConfig& config);
void getColorOperation(std::string& shader, PICA::TexEnvConfig::Operation op);
void getAlphaOperation(std::string& shader, PICA::TexEnvConfig::Operation op);
void applyAlphaTest(std::string& shader, const PICA::FragmentConfig& config);
void compileLights(std::string& shader, const PICA::FragmentConfig& config);
void compileLUTLookup(std::string& shader, const PICA::FragmentConfig& config, u32 lightIndex, u32 lutID);
bool isSamplerEnabled(u32 environmentID, u32 lutID);
void compileFog(std::string& shader, const PICA::FragmentConfig& config);
void compileLogicOps(std::string& shader, const PICA::FragmentConfig& config);
public:
FragmentGenerator(API api, Language language) : api(api), language(language) {}
std::string generate(const PICA::FragmentConfig& config, void* driverInfo = nullptr);
std::string getDefaultVertexShader();
// For when PICA shader is acceleration is enabled. Turn the PICA shader source into a proper vertex shader
std::string getVertexShaderAccelerated(const std::string& picaSource, const PICA::VertConfig& vertConfig, bool usingUbershader);
void setTarget(API api, Language language) {
this->api = api;
this->language = language;
}
};
}; // namespace PICA::ShaderGen

View file

@ -0,0 +1,9 @@
#pragma once
namespace PICA::ShaderGen {
// Graphics API this shader is targetting
enum class API { GL, GLES, Vulkan };
// Shading language to use (Only GLSL for the time being)
enum class Language { GLSL };
} // namespace PICA::ShaderGen

View file

@ -2,10 +2,9 @@
#include "PICA/shader.hpp"
class ShaderUnit {
public:
PICAShader vs; // Vertex shader
PICAShader gs; // Geometry shader
public:
PICAShader vs; // Vertex shader
PICAShader gs; // Geometry shader
ShaderUnit() : vs(ShaderType::Vertex), gs(ShaderType::Geometry) {}
void reset();

100
include/align.hpp Normal file
View file

@ -0,0 +1,100 @@
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
#pragma once
#include <cstdlib>
#include "compiler_builtins.hpp"
#include "helpers.hpp"
#ifdef _WIN32
#include <malloc.h>
#endif
namespace Common {
template <typename T>
constexpr bool isAligned(T value, unsigned int alignment) {
return (value % static_cast<T>(alignment)) == 0;
}
template <typename T>
constexpr T alignUp(T value, unsigned int alignment) {
return (value + static_cast<T>(alignment - 1)) / static_cast<T>(alignment) * static_cast<T>(alignment);
}
template <typename T>
constexpr T alignDown(T value, unsigned int alignment) {
return value / static_cast<T>(alignment) * static_cast<T>(alignment);
}
template <typename T>
constexpr bool isAlignedPow2(T value, unsigned int alignment) {
return (value & static_cast<T>(alignment - 1)) == 0;
}
template <typename T>
constexpr T alignUpPow2(T value, unsigned int alignment) {
return (value + static_cast<T>(alignment - 1)) & static_cast<T>(~static_cast<T>(alignment - 1));
}
template <typename T>
constexpr T alignDownPow2(T value, unsigned int alignment) {
return value & static_cast<T>(~static_cast<T>(alignment - 1));
}
template <typename T>
constexpr bool isPow2(T value) {
return (value & (value - 1)) == 0;
}
template <typename T>
constexpr T previousPow2(T value) {
if (value == static_cast<T>(0)) return 0;
value |= (value >> 1);
value |= (value >> 2);
value |= (value >> 4);
if constexpr (sizeof(T) >= 16) value |= (value >> 8);
if constexpr (sizeof(T) >= 32) value |= (value >> 16);
if constexpr (sizeof(T) >= 64) value |= (value >> 32);
return value - (value >> 1);
}
template <typename T>
constexpr T nextPow2(T value) {
// https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
if (value == static_cast<T>(0)) return 0;
value--;
value |= (value >> 1);
value |= (value >> 2);
value |= (value >> 4);
if constexpr (sizeof(T) >= 16) value |= (value >> 8);
if constexpr (sizeof(T) >= 32) value |= (value >> 16);
if constexpr (sizeof(T) >= 64) value |= (value >> 32);
value++;
return value;
}
ALWAYS_INLINE static void* alignedMalloc(size_t size, size_t alignment) {
#ifdef _WIN32
return _aligned_malloc(size, alignment);
#else
// Unaligned sizes are slow on macOS.
#ifdef __APPLE__
if (isPow2(alignment)) size = (size + alignment - 1) & ~(alignment - 1);
#endif
void* ret = nullptr;
return (posix_memalign(&ret, alignment, size) == 0) ? ret : nullptr;
#endif
}
ALWAYS_INLINE static void alignedFree(void* ptr) {
#ifdef _MSC_VER
_aligned_free(ptr);
#else
free(ptr);
#endif
}
} // namespace Common

View file

@ -54,6 +54,15 @@ namespace Audio::AAC {
u32_le sampleCount;
};
struct DecodeRequest {
u32_le address; // Address of input AAC stream
u32_le size; // Size of input AAC stream
u32_le destAddrLeft; // Output address for left channel samples
u32_le destAddrRight; // Output address for right channel samples
u32_le unknown1;
u32_le unknown2;
};
struct Message {
u16_le mode = Mode::None; // Encode or decode AAC?
u16_le command = Command::Init;
@ -62,7 +71,9 @@ namespace Audio::AAC {
// Info on the AAC request
union {
std::array<u8, 24> commandData{};
DecodeResponse decodeResponse;
DecodeRequest decodeRequest;
};
};

View file

@ -0,0 +1,25 @@
#pragma once
#include <functional>
#include "audio/aac.hpp"
#include "helpers.hpp"
struct AAC_DECODER_INSTANCE;
namespace Audio::AAC {
class Decoder {
using DecoderHandle = AAC_DECODER_INSTANCE*;
using PaddrCallback = std::function<u8*(u32)>;
DecoderHandle decoderHandle = nullptr;
bool isInitialized() { return decoderHandle != nullptr; }
void initialize();
public:
// Decode function. Takes in a reference to the AAC response & request, and a callback for paddr -> pointer conversions
// We also allow for optionally muting the AAC output (setting all of it to 0) instead of properly decoding it, for debug/research purposes
void decode(AAC::Message& response, const AAC::Message& request, PaddrCallback paddrCallback, bool enableAudio = true);
~Decoder();
};
} // namespace Audio::AAC

View file

@ -0,0 +1,58 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <deque>
#include "audio/hle_mixer.hpp"
#include "helpers.hpp"
namespace Audio::Interpolation {
// A variable length buffer of signed PCM16 stereo samples.
using StereoBuffer16 = std::deque<std::array<s16, 2>>;
using StereoFrame16 = Audio::DSPMixer::StereoFrame<s16>;
struct State {
// Two historical samples.
std::array<s16, 2> xn1 = {}; //< x[n-1]
std::array<s16, 2> xn2 = {}; //< x[n-2]
// Current fractional position.
u64 fposition = 0;
};
/**
* No interpolation. This is equivalent to a zero-order hold. There is a two-sample predelay.
* @param state Interpolation state.
* @param input Input buffer.
* @param rate Stretch factor. Must be a positive non-zero value.
* rate > 1.0 performs decimation and rate < 1.0 performs upsampling.
* @param output The resampled audio buffer.
* @param outputi The index of output to start writing to.
*/
void none(State& state, StereoBuffer16& input, float rate, StereoFrame16& output, usize& outputi);
/**
* Linear interpolation. This is equivalent to a first-order hold. There is a two-sample predelay.
* @param state Interpolation state.
* @param input Input buffer.
* @param rate Stretch factor. Must be a positive non-zero value.
* rate > 1.0 performs decimation and rate < 1.0 performs upsampling.
* @param output The resampled audio buffer.
* @param outputi The index of output to start writing to.
*/
void linear(State& state, StereoBuffer16& input, float rate, StereoFrame16& output, usize& outputi);
/**
* Polyphase interpolation. This is currently stubbed to just perform linear interpolation
* @param state Interpolation state.
* @param input Input buffer.
* @param rate Stretch factor. Must be a positive non-zero value.
* rate > 1.0 performs decimation and rate < 1.0 performs upsampling.
* @param output The resampled audio buffer.
* @param outputi The index of output to start writing to.
*/
void polyphase(State& state, StereoBuffer16& input, float rate, StereoFrame16& output, usize& outputi);
} // namespace Audio::Interpolation

View file

@ -8,12 +8,13 @@
#include "helpers.hpp"
#include "logger.hpp"
#include "scheduler.hpp"
#include "ring_buffer.hpp"
#include "scheduler.hpp"
// The DSP core must have access to the DSP service to be able to trigger interrupts properly
class DSPService;
class Memory;
struct EmulatorConfig;
namespace Audio {
// There are 160 stereo samples in 1 audio frame, so 320 samples total
@ -24,12 +25,14 @@ namespace Audio {
static constexpr u64 lleSlice = 16384;
class DSPCore {
using Samples = Common::RingBuffer<s16, 1024>;
// 0x2000 stereo (= 2 channel) samples
using Samples = Common::RingBuffer<s16, 0x2000 * 2>;
protected:
Memory& mem;
Scheduler& scheduler;
DSPService& dspService;
EmulatorConfig& settings;
Samples sampleBuffer;
bool audioEnabled = false;
@ -38,12 +41,12 @@ namespace Audio {
public:
enum class Type { Null, Teakra, HLE };
DSPCore(Memory& mem, Scheduler& scheduler, DSPService& dspService)
: mem(mem), scheduler(scheduler), dspService(dspService) {}
DSPCore(Memory& mem, Scheduler& scheduler, DSPService& dspService, EmulatorConfig& settings)
: mem(mem), scheduler(scheduler), dspService(dspService), settings(settings) {}
virtual ~DSPCore() {}
virtual void reset() = 0;
virtual void runAudioFrame() = 0;
virtual void runAudioFrame(u64 eventTimestamp) = 0;
virtual u8* getDspMemory() = 0;
virtual u16 recvData(u32 regId) = 0;
@ -62,5 +65,5 @@ namespace Audio {
virtual void setAudioEnabled(bool enable) { audioEnabled = enable; }
};
std::unique_ptr<DSPCore> makeDSPCore(DSPCore::Type type, Memory& mem, Scheduler& scheduler, DSPService& dspService);
std::unique_ptr<DSPCore> makeDSPCore(EmulatorConfig& config, Memory& mem, Scheduler& scheduler, DSPService& dspService);
} // namespace Audio

View file

@ -324,8 +324,8 @@ namespace Audio::HLE {
BitField<15, 1, u32> outputBufferCountDirty;
BitField<16, 1, u32> masterVolumeDirty;
BitField<24, 1, u32> auxReturnVolume0Dirty;
BitField<25, 1, u32> auxReturnVolume1Dirty;
BitField<24, 1, u32> auxVolume0Dirty;
BitField<25, 1, u32> auxVolume1Dirty;
BitField<26, 1, u32> outputFormatDirty;
BitField<27, 1, u32> clippingModeDirty;
BitField<28, 1, u32> headphonesConnectedDirty;
@ -337,7 +337,7 @@ namespace Audio::HLE {
/// The DSP has three intermediate audio mixers. This controls the volume level (0.0-1.0) for
/// each at the final mixer.
float_le masterVolume;
std::array<float_le, 2> auxReturnVolume;
std::array<float_le, 2> auxVolumes;
u16_le outputBufferCount;
u16 pad1[2];
@ -422,7 +422,7 @@ namespace Audio::HLE {
struct DspStatus {
u16_le unknown;
u16_le dropped_frames;
u16_le droppedFrames;
u16 pad0[0xE];
};
ASSERT_DSP_STRUCT(DspStatus, 32);

View file

@ -0,0 +1,78 @@
#pragma once
#include "audio/hle_mixer.hpp"
#include "compiler_builtins.hpp"
#include "helpers.hpp"
#if defined(_M_AMD64) || defined(__x86_64__)
#define DSP_SIMD_X64
#include <immintrin.h>
#elif defined(_M_ARM64) || defined(__aarch64__)
#define DSP_SIMD_ARM64
#include <arm_neon.h>
#endif
// Optimized SIMD functions for mixing the stereo output of a DSP voice into a quadraphonic intermediate mix
namespace DSP::MixIntoQuad {
using IntermediateMix = Audio::DSPMixer::IntermediateMix;
using StereoFrame16 = Audio::DSPMixer::StereoFrame<s16>;
// Non-SIMD, portable algorithm
ALWAYS_INLINE static void mixPortable(IntermediateMix& mix, StereoFrame16& frame, const float* gains) {
for (usize sampleIndex = 0; sampleIndex < Audio::samplesInFrame; sampleIndex++) {
// Mono samples are in the format: (l, r)
// When converting to quad, gain0 and gain2 are applied to the left sample, gain1 and gain3 to the right one
mix[sampleIndex][0] += s32(frame[sampleIndex][0] * gains[0]);
mix[sampleIndex][1] += s32(frame[sampleIndex][1] * gains[1]);
mix[sampleIndex][2] += s32(frame[sampleIndex][0] * gains[2]);
mix[sampleIndex][3] += s32(frame[sampleIndex][1] * gains[3]);
}
}
#if defined(DSP_SIMD_X64) && (defined(__SSE4_1__) || defined(__AVX__))
ALWAYS_INLINE static void mixSSE4_1(IntermediateMix& mix, StereoFrame16& frame, const float* gains) {
__m128 gains_ = _mm_load_ps(gains);
for (usize sampleIndex = 0; sampleIndex < Audio::samplesInFrame; sampleIndex++) {
// The stereo samples, repeated every 4 bytes inside the vector register
__m128i stereoSamples = _mm_castps_si128(_mm_load1_ps((float*)&frame[sampleIndex][0]));
__m128 currentFrame = _mm_cvtepi32_ps(_mm_cvtepi16_epi32(stereoSamples));
__m128i offset = _mm_cvttps_epi32(_mm_mul_ps(currentFrame, gains_));
__m128i intermediateMixPrev = _mm_load_si128((__m128i*)&mix[sampleIndex][0]);
__m128i result = _mm_add_epi32(intermediateMixPrev, offset);
_mm_store_si128((__m128i*)&mix[sampleIndex][0], result);
}
}
#endif
#ifdef DSP_SIMD_ARM64
ALWAYS_INLINE static void mixNEON(IntermediateMix& mix, StereoFrame16& frame, const float* gains) {
float32x4_t gains_ = vld1q_f32(gains);
for (usize sampleIndex = 0; sampleIndex < Audio::samplesInFrame; sampleIndex++) {
// Load l and r samples and repeat them every 4 bytes
int32x4_t stereoSamples = vld1q_dup_s32((s32*)&frame[sampleIndex][0]);
// Expand the bottom 4 s16 samples into an int32x4 with sign extension, then convert them to float32x4
float32x4_t currentFrame = vcvtq_f32_s32(vmovl_s16(vget_low_s16(vreinterpretq_s16_s32(stereoSamples))));
// Multiply samples by their respective gains, truncate the result, and add it into the intermediate mix buffer
int32x4_t offset = vcvtq_s32_f32(vmulq_f32(currentFrame, gains_));
int32x4_t intermediateMixPrev = vld1q_s32((s32*)&mix[sampleIndex][0]);
int32x4_t result = vaddq_s32(intermediateMixPrev, offset);
vst1q_s32((s32*)&mix[sampleIndex][0], result);
}
}
#endif
// Mixes the stereo output of a DSP voice into a quadraphonic intermediate mix
static void mix(IntermediateMix& mix, StereoFrame16& frame, const float* gains) {
#if defined(DSP_SIMD_ARM64)
return mixNEON(mix, frame, gains);
#elif defined(DSP_SIMD_X64) && (defined(__SSE4_1__) || defined(__AVX__))
return mixSSE4_1(mix, frame, gains);
#else
return mixPortable(mix, frame, gains);
#endif
}
} // namespace DSP::MixIntoQuad

View file

@ -2,18 +2,19 @@
#include <array>
#include <cassert>
#include <deque>
#include <memory>
#include <queue>
#include <vector>
#include "audio/aac.hpp"
#include "audio/aac_decoder.hpp"
#include "audio/audio_interpolation.hpp"
#include "audio/dsp_core.hpp"
#include "audio/dsp_shared_mem.hpp"
#include "audio/hle_mixer.hpp"
#include "memory.hpp"
namespace Audio {
using SampleFormat = HLE::SourceConfiguration::Configuration::Format;
using SourceType = HLE::SourceConfiguration::Configuration::MonoOrStereo;
struct DSPSource {
// Audio buffer information
// https://www.3dbrew.org/wiki/DSP_Memory_Region
@ -33,8 +34,8 @@ namespace Audio {
SampleFormat format;
SourceType sourceType;
bool fromQueue = false; // Is this buffer from the buffer queue or an embedded buffer?
bool hasPlayedOnce = false; // Has the buffer been played at least once before?
bool fromQueue = false; // Is this buffer from the buffer queue or an embedded buffer?
bool hasPlayedOnce = false; // Has the buffer been played at least once before?
bool operator<(const Buffer& other) const {
// Lower ID = Higher priority
@ -42,17 +43,34 @@ namespace Audio {
return this->bufferID > other.bufferID;
}
};
// Buffer of decoded PCM16 samples. TODO: Are there better alternatives to use over deque?
using SampleBuffer = std::deque<std::array<s16, 2>>;
using BufferQueue = std::priority_queue<Buffer>;
using InterpolationMode = HLE::SourceConfiguration::Configuration::InterpolationMode;
using InterpolationState = Audio::Interpolation::State;
// The samples this voice output for this audio frame.
// Aligned to 4 for SIMD purposes.
alignas(4) DSPMixer::StereoFrame<s16> currentFrame;
BufferQueue buffers;
SampleFormat sampleFormat = SampleFormat::ADPCM;
SourceType sourceType = SourceType::Stereo;
InterpolationMode interpolationMode = InterpolationMode::Linear;
InterpolationState interpolationState;
// There's one gain configuration for each of the 3 intermediate mixing stages
// And each gain configuration is composed of 4 gain values, one for each sample in a quad-channel sample
// Aligned to 16 for SIMD purposes
alignas(16) std::array<std::array<float, 4>, 3> gains;
// Of the 3 intermediate mix stages, typically only the first one is actually enabled and the other ones do nothing
// Ie their gain is vec4(0.0). We track which stages are disabled (have a gain of all 0s) using this bitfield and skip them
// In order to save up on CPU time.
uint enabledMixStages = 0;
std::array<float, 3> gain0, gain1, gain2;
u32 samplePosition; // Sample number into the current audio buffer
float rateMultiplier;
u16 syncCount;
u16 currentBufferID;
u16 previousBufferID;
@ -95,22 +113,23 @@ namespace Audio {
// The audio frame types are public in case we want to use them for unit tests
public:
template <typename T, usize channelCount = 1>
using Sample = std::array<T, channelCount>;
using Sample = DSPMixer::Sample<T, channelCount>;
template <typename T, usize channelCount>
using Frame = std::array<Sample<T, channelCount>, 160>;
using Frame = DSPMixer::Frame<T, channelCount>;
template <typename T>
using MonoFrame = Frame<T, 1>;
using MonoFrame = DSPMixer::MonoFrame<T>;
template <typename T>
using StereoFrame = Frame<T, 2>;
using StereoFrame = DSPMixer::StereoFrame<T>;
template <typename T>
using QuadFrame = Frame<T, 4>;
using QuadFrame = DSPMixer::QuadFrame<T>;
using Source = Audio::DSPSource;
using SampleBuffer = Source::SampleBuffer;
using IntermediateMix = DSPMixer::IntermediateMix;
private:
enum class DSPState : u32 {
@ -127,6 +146,9 @@ namespace Audio {
std::array<Source, Audio::HLE::sourceCount> sources; // DSP voices
Audio::HLE::DspMemory dspRam;
Audio::DSPMixer mixer;
std::unique_ptr<Audio::AAC::Decoder> aacDecoder;
void resetAudioPipe();
bool loaded = false; // Have we loaded a component?
@ -142,7 +164,7 @@ namespace Audio {
} else if (counter1 == 0xffff && counter0 != 0xfffe) {
return 0;
} else {
return counter0 > counter1 ? 0 : 0;
return (counter0 > counter1) ? 0 : 1;
}
}
@ -169,9 +191,12 @@ namespace Audio {
void handleAACRequest(const AAC::Message& request);
void updateSourceConfig(Source& source, HLE::SourceConfiguration::Configuration& config, s16_le* adpcmCoefficients);
void updateMixerConfig(HLE::SharedMemory& sharedMem);
void generateFrame(StereoFrame<s16>& frame);
void generateFrame(DSPSource& source);
void outputFrame();
// Perform the final mix, mixing the quadraphonic samples from all voices into the output audio frame
void performMix(Audio::HLE::SharedMemory& readRegion, Audio::HLE::SharedMemory& writeRegion);
// Decode an entire buffer worth of audio
void decodeBuffer(DSPSource& source);
@ -181,11 +206,11 @@ namespace Audio {
SampleBuffer decodeADPCM(const u8* data, usize sampleCount, Source& source);
public:
HLE_DSP(Memory& mem, Scheduler& scheduler, DSPService& dspService);
HLE_DSP(Memory& mem, Scheduler& scheduler, DSPService& dspService, EmulatorConfig& config);
~HLE_DSP() override {}
void reset() override;
void runAudioFrame() override;
void runAudioFrame(u64 eventTimestamp) override;
u8* getDspMemory() override { return dspRam.rawMemory.data(); }
@ -199,5 +224,4 @@ namespace Audio {
void setSemaphore(u16 value) override {}
void setSemaphoreMask(u16 value) override {}
};
} // namespace Audio

View file

@ -0,0 +1,50 @@
#pragma once
#include <array>
#include "audio/dsp_shared_mem.hpp"
#include "helpers.hpp"
namespace Audio {
using SampleFormat = HLE::SourceConfiguration::Configuration::Format;
using SourceType = HLE::SourceConfiguration::Configuration::MonoOrStereo;
class DSPMixer {
public:
template <typename T, usize channelCount = 1>
using Sample = std::array<T, channelCount>;
template <typename T, usize channelCount>
using Frame = std::array<Sample<T, channelCount>, 160>;
template <typename T>
using MonoFrame = Frame<T, 1>;
template <typename T>
using StereoFrame = Frame<T, 2>;
template <typename T>
using QuadFrame = Frame<T, 4>;
// Internally the DSP uses four channels when mixing.
// Neatly, QuadFrame<s32> means that every sample is a uint32x4 value, which is particularly nice for SIMD mixing
using IntermediateMix = QuadFrame<s32>;
private:
using ChannelFormat = HLE::DspConfiguration::OutputFormat;
// The audio from each DSP voice is converted to quadraphonic and then fed into 3 intermediate mixing stages
// Two of these intermediate mixers (second and third) are used for effects, including custom effects done on the CPU
static constexpr usize mixerStageCount = 3;
public:
ChannelFormat channelFormat = ChannelFormat::Stereo;
std::array<float, mixerStageCount> volumes;
std::array<bool, 2> enableAuxStages;
void reset() {
channelFormat = ChannelFormat::Stereo;
volumes.fill(0.0);
enableAuxStages.fill(false);
}
};
} // namespace Audio

View file

@ -3,29 +3,39 @@
#include <string>
#include <vector>
#include "config.hpp"
#include "helpers.hpp"
#include "miniaudio.h"
#include "ring_buffer.hpp"
class MiniAudioDevice {
using Samples = Common::RingBuffer<ma_int16, 1024>;
using Samples = Common::RingBuffer<ma_int16, 0x2000 * 2>;
static constexpr ma_uint32 sampleRate = 32768; // 3DS sample rate
static constexpr ma_uint32 channelCount = 2; // Audio output is stereo
ma_device device;
ma_context context;
ma_device_config deviceConfig;
ma_device device;
ma_resampler resampler;
Samples* samples = nullptr;
const AudioDeviceConfig& audioSettings;
bool initialized = false;
bool running = false;
// Store the last stereo sample we output. We play this when underruning to avoid pops.
std::array<s16, 2> lastStereoSample;
std::vector<std::string> audioDevices;
public:
MiniAudioDevice();
MiniAudioDevice(const AudioDeviceConfig& audioSettings);
// If safe is on, we create a null audio device
void init(Samples& samples, bool safe = false);
void close();
void start();
void stop();
bool isInitialized() const { return initialized; }
};

View file

@ -20,14 +20,14 @@ namespace Audio {
std::array<u8, Memory::DSP_RAM_SIZE> dspRam;
void resetAudioPipe();
bool loaded = false; // Have we loaded a component?
bool loaded = false; // Have we loaded a component?
public:
NullDSP(Memory& mem, Scheduler& scheduler, DSPService& dspService) : DSPCore(mem, scheduler, dspService) {}
NullDSP(Memory& mem, Scheduler& scheduler, DSPService& dspService, EmulatorConfig& config) : DSPCore(mem, scheduler, dspService, config) {}
~NullDSP() override {}
void reset() override;
void runAudioFrame() override;
void runAudioFrame(u64 eventTimestamp) override;
u8* getDspMemory() override { return dspRam.data(); }

View file

@ -77,13 +77,13 @@ namespace Audio {
}
public:
TeakraDSP(Memory& mem, Scheduler& scheduler, DSPService& dspService);
TeakraDSP(Memory& mem, Scheduler& scheduler, DSPService& dspService, EmulatorConfig& config);
~TeakraDSP() override {}
void reset() override;
// Run 1 slice of DSP instructions and schedule the next audio frame
void runAudioFrame() override {
void runAudioFrame(u64 eventTimestamp) override {
runSlice();
scheduler.addEvent(Scheduler::EventType::RunDSP, scheduler.currentTimestamp + Audio::lleSlice * 2);
}

View file

@ -1,8 +1,35 @@
#pragma once
#include <filesystem>
#include <string>
#include "audio/dsp_core.hpp"
#include "frontend_settings.hpp"
#include "renderer.hpp"
#include "services/region_codes.hpp"
struct AudioDeviceConfig {
// Audio curve to use for volumes between 0-100
enum class VolumeCurve : int {
Cubic = 0, // Samples are scaled by volume ^ 3
Linear = 1, // Samples are scaled by volume
};
float volumeRaw = 1.0f;
VolumeCurve volumeCurve = VolumeCurve::Cubic;
bool muteAudio = false;
float getVolume() const {
if (muteAudio) {
return 0.0f;
}
return volumeRaw;
}
static VolumeCurve volumeCurveFromString(std::string inString);
static const char* volumeCurveToString(VolumeCurve curve);
};
// Remember to initialize every field here to its default value otherwise bad things will happen
struct EmulatorConfig {
@ -13,27 +40,80 @@ struct EmulatorConfig {
static constexpr bool shaderJitDefault = false;
#endif
// For now, use specialized shaders by default on MacOS as M1 drivers are buggy when using the ubershader, and on Android since mobile GPUs are
// horrible. On other platforms we default to ubershader + shadergen fallback for lights
#if defined(__ANDROID__) || defined(__APPLE__)
static constexpr bool ubershaderDefault = false;
#else
static constexpr bool ubershaderDefault = true;
#endif
static constexpr bool accelerateShadersDefault = true;
#if defined(__LIBRETRO__)
static constexpr bool audioEnabledDefault = true;
#else
static constexpr bool audioEnabledDefault = false;
#endif
bool shaderJitEnabled = shaderJitDefault;
bool useUbershaders = ubershaderDefault;
bool accelerateShaders = accelerateShadersDefault;
bool accurateShaderMul = false;
bool discordRpcEnabled = false;
// Toggles whether to force shadergen when there's more than N lights active and we're using the ubershader, for better performance
bool forceShadergenForLights = true;
int lightShadergenThreshold = 1;
RendererType rendererType = RendererType::OpenGL;
Audio::DSPCore::Type dspType = Audio::DSPCore::Type::Null;
Audio::DSPCore::Type dspType = Audio::DSPCore::Type::HLE;
bool sdCardInserted = true;
bool sdWriteProtected = false;
bool usePortableBuild = false;
bool audioEnabled = false;
bool audioEnabled = audioEnabledDefault;
bool vsyncEnabled = true;
bool aacEnabled = true; // Enable AAC audio?
bool enableRenderdoc = false;
bool printAppVersion = true;
bool printDSPFirmware = false;
bool chargerPlugged = true;
// Default to 3% battery to make users suffer
int batteryPercentage = 3;
LanguageCodes systemLanguage = LanguageCodes::English;
// Default ROM path to open in Qt and misc frontends
std::filesystem::path defaultRomPath = "";
std::filesystem::path filePath;
// Frontend window settings
struct WindowSettings {
static constexpr int defaultX = 200;
static constexpr int defaultY = 200;
static constexpr int defaultWidth = 800;
static constexpr int defaultHeight = 240 * 2;
bool rememberPosition = false; // Remember window position & size
bool showAppVersion = false;
int x = defaultX;
int y = defaultY;
int width = defaultHeight;
int height = defaultHeight;
};
WindowSettings windowSettings;
AudioDeviceConfig audioDeviceConfig;
FrontendSettings frontendSettings;
EmulatorConfig(const std::filesystem::path& path);
void load();
void save();
static LanguageCodes languageCodeFromString(std::string inString);
static const char* languageCodeToString(LanguageCodes code);
};

View file

@ -1,20 +1,29 @@
#pragma once
#include <array>
#include <cstring>
#include <cstdint>
#include <climits>
#include <cstdint>
#include <cstring>
#include <filesystem>
#include <optional>
#include <vector>
#include "helpers.hpp"
#include "io_file.hpp"
#include "swap.hpp"
namespace Crypto {
constexpr std::size_t AesKeySize = 0x10;
constexpr usize AesKeySize = 0x10;
using AESKey = std::array<u8, AesKeySize>;
template <std::size_t N>
static std::array<u8, N> rolArray(const std::array<u8, N>& value, std::size_t bits) {
struct Seed {
u64_le titleID;
AESKey seed;
std::array<u8, 8> pad;
};
template <usize N>
static std::array<u8, N> rolArray(const std::array<u8, N>& value, usize bits) {
const auto bitWidth = N * CHAR_BIT;
bits %= bitWidth;
@ -24,18 +33,18 @@ namespace Crypto {
std::array<u8, N> result;
for (std::size_t i = 0; i < N; i++) {
for (usize i = 0; i < N; i++) {
result[i] = ((value[(i + byteShift) % N] << bitShift) | (value[(i + byteShift + 1) % N] >> (CHAR_BIT - bitShift))) & UINT8_MAX;
}
return result;
}
template <std::size_t N>
template <usize N>
static std::array<u8, N> addArray(const std::array<u8, N>& a, const std::array<u8, N>& b) {
std::array<u8, N> result;
std::size_t sum = 0;
std::size_t carry = 0;
usize sum = 0;
usize carry = 0;
for (std::int64_t i = N - 1; i >= 0; i--) {
sum = a[i] + b[i] + carry;
@ -46,11 +55,11 @@ namespace Crypto {
return result;
}
template <std::size_t N>
template <usize N>
static std::array<u8, N> xorArray(const std::array<u8, N>& a, const std::array<u8, N>& b) {
std::array<u8, N> result;
for (std::size_t i = 0; i < N; i++) {
for (usize i = 0; i < N; i++) {
result[i] = a[i] ^ b[i];
}
@ -63,7 +72,7 @@ namespace Crypto {
}
AESKey rawKey;
for (std::size_t i = 0; i < rawKey.size(); i++) {
for (usize i = 0; i < rawKey.size(); i++) {
rawKey[i] = static_cast<u8>(std::stoi(hex.substr(i * 2, 2), 0, 16));
}
@ -76,7 +85,7 @@ namespace Crypto {
std::optional<AESKey> normalKey = std::nullopt;
};
enum KeySlotId : std::size_t {
enum KeySlotId : usize {
NCCHKey0 = 0x2C,
NCCHKey1 = 0x25,
NCCHKey2 = 0x18,
@ -84,14 +93,17 @@ namespace Crypto {
};
class AESEngine {
private:
constexpr static std::size_t AesKeySlotCount = 0x40;
private:
constexpr static usize AesKeySlotCount = 0x40;
std::optional<AESKey> m_generator = std::nullopt;
std::array<AESKeySlot, AesKeySlotCount> m_slots;
bool keysLoaded = false;
constexpr void updateNormalKey(std::size_t slotId) {
std::vector<Seed> seeds;
IOFile seedDatabase;
constexpr void updateNormalKey(usize slotId) {
if (m_generator.has_value() && hasKeyX(slotId) && hasKeyY(slotId)) {
auto& keySlot = m_slots.at(slotId);
AESKey keyX = keySlot.keyX.value();
@ -101,13 +113,17 @@ namespace Crypto {
}
}
public:
public:
AESEngine() {}
void loadKeys(const std::filesystem::path& path);
void setSeedPath(const std::filesystem::path& path);
// Returns true on success, false on failure
bool loadSeeds();
bool haveKeys() { return keysLoaded; }
bool haveGenerator() { return m_generator.has_value(); }
constexpr bool hasKeyX(std::size_t slotId) {
constexpr bool hasKeyX(usize slotId) {
if (slotId >= AesKeySlotCount) {
return false;
}
@ -115,18 +131,16 @@ namespace Crypto {
return m_slots.at(slotId).keyX.has_value();
}
constexpr AESKey getKeyX(std::size_t slotId) {
return m_slots.at(slotId).keyX.value_or(AESKey{});
}
constexpr AESKey getKeyX(usize slotId) { return m_slots.at(slotId).keyX.value_or(AESKey{}); }
constexpr void setKeyX(std::size_t slotId, const AESKey &key) {
constexpr void setKeyX(usize slotId, const AESKey& key) {
if (slotId < AesKeySlotCount) {
m_slots.at(slotId).keyX = key;
updateNormalKey(slotId);
}
}
constexpr bool hasKeyY(std::size_t slotId) {
constexpr bool hasKeyY(usize slotId) {
if (slotId >= AesKeySlotCount) {
return false;
}
@ -134,18 +148,16 @@ namespace Crypto {
return m_slots.at(slotId).keyY.has_value();
}
constexpr AESKey getKeyY(std::size_t slotId) {
return m_slots.at(slotId).keyY.value_or(AESKey{});
}
constexpr AESKey getKeyY(usize slotId) { return m_slots.at(slotId).keyY.value_or(AESKey{}); }
constexpr void setKeyY(std::size_t slotId, const AESKey &key) {
constexpr void setKeyY(usize slotId, const AESKey& key) {
if (slotId < AesKeySlotCount) {
m_slots.at(slotId).keyY = key;
updateNormalKey(slotId);
}
}
constexpr bool hasNormalKey(std::size_t slotId) {
constexpr bool hasNormalKey(usize slotId) {
if (slotId >= AesKeySlotCount) {
return false;
}
@ -153,14 +165,14 @@ namespace Crypto {
return m_slots.at(slotId).normalKey.has_value();
}
constexpr AESKey getNormalKey(std::size_t slotId) {
return m_slots.at(slotId).normalKey.value_or(AESKey{});
}
constexpr AESKey getNormalKey(usize slotId) { return m_slots.at(slotId).normalKey.value_or(AESKey{}); }
constexpr void setNormalKey(std::size_t slotId, const AESKey &key) {
constexpr void setNormalKey(usize slotId, const AESKey& key) {
if (slotId < AesKeySlotCount) {
m_slots.at(slotId).normalKey = key;
}
}
std::optional<AESKey> getSeedFromDB(u64 titleID);
};
}
} // namespace Crypto

View file

@ -17,6 +17,8 @@ namespace Discord {
void init();
void update(RPCStatus status, const std::string& title);
void stop();
bool running() const { return enabled; }
};
} // namespace Discord

View file

@ -66,7 +66,6 @@ class Emulator {
#ifdef PANDA3DS_ENABLE_DISCORD_RPC
Discord::RPC discordRpc;
#endif
void setAudioEnabled(bool enable);
void updateDiscord();
// Keep the handle for the ROM here to reload when necessary and to prevent deleting it
@ -90,7 +89,6 @@ class Emulator {
~Emulator();
void step();
void render();
void reset(ReloadOption reload);
void runFrame();
// Poll the scheduler for events
@ -99,6 +97,7 @@ class Emulator {
void resume(); // Resume the emulator
void pause(); // Pause the emulator
void togglePause();
void setAudioEnabled(bool enable);
bool loadAmiibo(const std::filesystem::path& path);
bool loadROM(const std::filesystem::path& path);
@ -118,6 +117,9 @@ class Emulator {
void setOutputSize(u32 width, u32 height) { gpu.setOutputSize(width, height); }
void deinitGraphicsContext() { gpu.deinitGraphicsContext(); }
// Reloads some settings that require special handling, such as audio enable
void reloadSettings();
EmulatorConfig& getConfig() { return config; }
Cheats& getCheats() { return cheats; }
ServiceManager& getServiceManager() { return kernel.getServiceManager(); }
@ -135,4 +137,7 @@ class Emulator {
std::filesystem::path getAppDataRoot();
std::span<u8> getSMDH();
private:
void loadRenderdoc();
};

View file

@ -0,0 +1,34 @@
#pragma once
#include <string>
// Some UI settings that aren't fully frontend-dependent. Note: Not all frontends will support the same settings.
// Note: Any enums should ideally be ordered in the same order we want to show them in UI dropdown menus, so that we can cast indices to enums
// directly.
struct FrontendSettings {
enum class Theme : int {
System = 0,
Light = 1,
Dark = 2,
GreetingsCat = 3,
Cream = 4,
};
// Different panda-themed window icons
enum class WindowIcon : int {
Rpog = 0,
Rsyn = 1,
Rnap = 2,
Rcow = 3,
SkyEmu = 4,
};
Theme theme = Theme::Dark;
WindowIcon icon = WindowIcon::Rpog;
std::string language = "en";
static Theme themeFromString(std::string inString);
static const char* themeToString(Theme theme);
static WindowIcon iconFromString(std::string inString);
static const char* iconToString(WindowIcon icon);
};

View file

@ -7,6 +7,7 @@
#include <string>
#include <type_traits>
#include <vector>
#include "helpers.hpp"
#include "memory.hpp"
#include "result.hpp"
@ -15,13 +16,13 @@
using Result::HorizonResult;
namespace PathType {
enum : u32 {
Invalid = 0,
Empty = 1,
Binary = 2,
ASCII = 3,
UTF16 = 4,
};
enum : u32 {
Invalid = 0,
Empty = 1,
Binary = 2,
ASCII = 3,
UTF16 = 4,
};
}
namespace ArchiveID {
@ -34,91 +35,103 @@ namespace ArchiveID {
SDMC = 9,
SDMCWriteOnly = 0xA,
CardSPI = 0x12345679,
SavedataAndNcch = 0x2345678A,
// 3DBrew: This is the same as the regular SaveData archive, except with this the savedata ID and mediatype is loaded from the input archive
// lowpath.
UserSaveData1 = 0x567890B2,
// 3DBrew: Similar to 0x567890B2 but can only access Accessible Save specified in exheader?
UserSaveData2 = 0x567890B4,
TwlPhoto = 0x567890AC,
TwlSound = 0x567890AD,
};
static std::string toString(u32 id) {
switch (id) {
case SelfNCCH: return "SelfNCCH";
case SaveData: return "SaveData";
case ExtSaveData: return "ExtSaveData";
case SharedExtSaveData: return "SharedExtSaveData";
case SystemSaveData: return "SystemSaveData";
case SDMC: return "SDMC";
case SDMCWriteOnly: return "SDMC (Write-only)";
case SavedataAndNcch: return "Savedata & NCCH (archive 0x2345678A)";
default: return "Unknown archive";
}
}
}
static std::string toString(u32 id) {
switch (id) {
case SelfNCCH: return "SelfNCCH";
case SaveData: return "SaveData";
case ExtSaveData: return "ExtSaveData";
case SharedExtSaveData: return "SharedExtSaveData";
case SystemSaveData: return "SystemSaveData";
case SDMC: return "SDMC";
case SDMCWriteOnly: return "SDMC (Write-only)";
case SavedataAndNcch: return "Savedata & NCCH (archive 0x2345678A)";
case TwlPhoto: return "TWL_PHOTO";
case TwlSound: return "TWL_SOUND";
default: return "Unknown archive";
}
}
} // namespace ArchiveID
struct FSPath {
u32 type = PathType::Invalid;
u32 type = PathType::Invalid;
std::vector<u8> binary; // Path data for binary paths
std::string string; // Path data for ASCII paths
std::u16string utf16_string;
std::vector<u8> binary; // Path data for binary paths
std::string string; // Path data for ASCII paths
std::u16string utf16_string;
FSPath() {}
FSPath() {}
FSPath(u32 type, const std::vector<u8>& vec) : type(type) {
switch (type) {
case PathType::Binary:
binary = std::move(vec);
break;
FSPath(u32 type, const std::vector<u8>& vec) : type(type) {
switch (type) {
case PathType::Binary: binary = std::move(vec); break;
case PathType::ASCII:
string.resize(vec.size() - 1); // -1 because of the null terminator
std::memcpy(string.data(), vec.data(), vec.size() - 1); // Copy string data
break;
case PathType::ASCII:
string.resize(vec.size() - 1); // -1 because of the null terminator
std::memcpy(string.data(), vec.data(), vec.size() - 1); // Copy string data
break;
case PathType::UTF16: {
const size_t size = vec.size() / sizeof(u16) - 1; // Character count. -1 because null terminator here too
utf16_string.resize(size);
std::memcpy(utf16_string.data(), vec.data(), size * sizeof(u16));
break;
}
; }
}
case PathType::UTF16: {
const size_t size = vec.size() / sizeof(u16) - 1; // Character count. -1 because null terminator here too
utf16_string.resize(size);
std::memcpy(utf16_string.data(), vec.data(), size * sizeof(u16));
break;
};
}
}
bool isUTF16() const { return type == PathType::UTF16; }
bool isASCII() const { return type == PathType::ASCII; }
bool isBinary() const { return type == PathType::Binary; }
// This is not called "isEmpty()" to make obvious that we're talking about an empty-type path, NOT an empty text path
bool isEmptyType() const { return type == PathType::Empty; }
bool isTextPath() const { return isUTF16() || isASCII(); }
};
struct FilePerms {
u32 raw;
u32 raw;
FilePerms(u32 val) : raw(val) {}
bool read() const { return (raw & 1) != 0; }
bool write() const { return (raw & 2) != 0; }
bool create() const { return (raw & 4) != 0; }
FilePerms(u32 val) : raw(val) {}
bool read() const { return (raw & 1) != 0; }
bool write() const { return (raw & 2) != 0; }
bool create() const { return (raw & 4) != 0; }
};
class ArchiveBase;
struct FileSession {
ArchiveBase* archive = nullptr;
FILE* fd = nullptr; // File descriptor for file sessions that require them.
FSPath path;
FSPath archivePath;
u32 priority = 0; // TODO: What does this even do
bool isOpen;
ArchiveBase* archive = nullptr;
FILE* fd = nullptr; // File descriptor for file sessions that require them.
FSPath path;
FSPath archivePath;
u32 priority = 0; // TODO: What does this even do
bool isOpen;
FileSession(ArchiveBase* archive, const FSPath& filePath, const FSPath& archivePath, FILE* fd, bool isOpen = true) :
archive(archive), path(filePath), archivePath(archivePath), fd(fd), isOpen(isOpen), priority(0) {}
FileSession(ArchiveBase* archive, const FSPath& filePath, const FSPath& archivePath, FILE* fd, bool isOpen = true)
: archive(archive), path(filePath), archivePath(archivePath), fd(fd), isOpen(isOpen), priority(0) {}
// For cloning a file session
FileSession(const FileSession& other) : archive(other.archive), path(other.path),
archivePath(other.archivePath), fd(other.fd), isOpen(other.isOpen), priority(other.priority) {}
// For cloning a file session
FileSession(const FileSession& other)
: archive(other.archive), path(other.path), archivePath(other.archivePath), fd(other.fd), isOpen(other.isOpen), priority(other.priority) {}
};
struct ArchiveSession {
ArchiveBase* archive = nullptr;
FSPath path;
bool isOpen;
ArchiveBase* archive = nullptr;
FSPath path;
bool isOpen;
ArchiveSession(ArchiveBase* archive, const FSPath& filePath, bool isOpen = true) : archive(archive), path(filePath), isOpen(isOpen) {}
ArchiveSession(ArchiveBase* archive, const FSPath& filePath, bool isOpen = true) : archive(archive), path(filePath), isOpen(isOpen) {}
};
struct DirectoryEntry {
@ -156,106 +169,125 @@ struct DirectorySession {
using FileDescriptor = std::optional<FILE*>;
class ArchiveBase {
public:
struct FormatInfo {
u32 size; // Archive size
u32 numOfDirectories; // Number of directories
u32 numOfFiles; // Number of files
bool duplicateData; // Whether to duplicate data or not
};
public:
struct FormatInfo {
u32 size; // Archive size
u32 numOfDirectories; // Number of directories
u32 numOfFiles; // Number of files
bool duplicateData; // Whether to duplicate data or not
};
protected:
using Handle = u32;
protected:
using Handle = u32;
static constexpr FileDescriptor NoFile = nullptr;
static constexpr FileDescriptor FileError = std::nullopt;
Memory& mem;
static constexpr FileDescriptor NoFile = nullptr;
static constexpr FileDescriptor FileError = std::nullopt;
Memory& mem;
// Returns if a specified 3DS path in UTF16 or ASCII format is safe or not
// A 3DS path is considered safe if its first character is '/' which means we're not trying to access anything outside the root of the fs
// And if it doesn't contain enough instances of ".." (Indicating "climb up a folder" in filesystems) to let the software climb up the directory tree
// And access files outside of the emulator's app data folder
template <u32 format>
bool isPathSafe(const FSPath& path) {
static_assert(format == PathType::ASCII || format == PathType::UTF16);
using String = typename std::conditional<format == PathType::UTF16, std::u16string, std::string>::type; // String type for the path
using Char = typename String::value_type; // Char type for the path
// Returns if a specified 3DS path in UTF16 or ASCII format is safe or not
// A 3DS path is considered safe if its first character is '/' which means we're not trying to access anything outside the root of the fs
// And if it doesn't contain enough instances of ".." (Indicating "climb up a folder" in filesystems) to let the software climb up the directory
// tree And access files outside of the emulator's app data folder
template <u32 format>
bool isPathSafe(const FSPath& path) {
static_assert(format == PathType::ASCII || format == PathType::UTF16);
using String = typename std::conditional<format == PathType::UTF16, std::u16string, std::string>::type; // String type for the path
using Char = typename String::value_type; // Char type for the path
String pathString, dots;
if constexpr (std::is_same<String, std::u16string>()) {
pathString = path.utf16_string;
dots = u"..";
} else {
pathString = path.string;
dots = "..";
}
String pathString, dots;
if constexpr (std::is_same<String, std::u16string>()) {
pathString = path.utf16_string;
dots = u"..";
} else {
pathString = path.string;
dots = "..";
}
// If the path string doesn't begin with / then that means it's accessing outside the FS root, which is invalid & unsafe
if (pathString[0] != Char('/')) return false;
// If the path string doesn't begin with / then that means it's accessing outside the FS root, which is invalid & unsafe
if (pathString[0] != Char('/')) return false;
// Counts how many folders sans the root our file is nested under.
// If it's < 0 at any point of parsing, then the path is unsafe and tries to crawl outside our file sandbox.
// If it's 0 then this is the FS root.
// If it's > 0 then we're in a subdirectory of the root.
int level = 0;
// Counts how many folders sans the root our file is nested under.
// If it's < 0 at any point of parsing, then the path is unsafe and tries to crawl outside our file sandbox.
// If it's 0 then this is the FS root.
// If it's > 0 then we're in a subdirectory of the root.
int level = 0;
// Split the string on / characters and see how many of the substrings are ".."
size_t pos = 0;
while ((pos = pathString.find(Char('/'))) != String::npos) {
String token = pathString.substr(0, pos);
pathString.erase(0, pos + 1);
// Split the string on / characters and see how many of the substrings are ".."
size_t pos = 0;
while ((pos = pathString.find(Char('/'))) != String::npos) {
String token = pathString.substr(0, pos);
pathString.erase(0, pos + 1);
if (token == dots) {
level--;
if (level < 0) return false;
} else {
level++;
}
}
if (token == dots) {
level--;
if (level < 0) return false;
} else {
level++;
}
}
return true;
}
return true;
}
public:
virtual std::string name() = 0;
virtual u64 getFreeBytes() = 0;
virtual HorizonResult createFile(const FSPath& path, u64 size) = 0;
virtual HorizonResult deleteFile(const FSPath& path) = 0;
public:
virtual std::string name() = 0;
virtual u64 getFreeBytes() = 0;
virtual HorizonResult createFile(const FSPath& path, u64 size) = 0;
virtual HorizonResult deleteFile(const FSPath& path) = 0;
virtual Rust::Result<FormatInfo, HorizonResult> getFormatInfo(const FSPath& path) {
Helpers::panic("Unimplemented GetFormatInfo for %s archive", name().c_str());
// Return a dummy struct just to avoid the UB of not returning anything, even if we panic
return Ok(FormatInfo{ .size = 0, .numOfDirectories = 0, .numOfFiles = 0, .duplicateData = false });
}
virtual Rust::Result<FormatInfo, HorizonResult> getFormatInfo(const FSPath& path) {
Helpers::panic("Unimplemented GetFormatInfo for %s archive", name().c_str());
// Return a dummy struct just to avoid the UB of not returning anything, even if we panic
return Ok(FormatInfo{.size = 0, .numOfDirectories = 0, .numOfFiles = 0, .duplicateData = false});
}
virtual HorizonResult createDirectory(const FSPath& path) {
Helpers::panic("Unimplemented CreateDirectory for %s archive", name().c_str());
return Result::FS::AlreadyExists;
}
virtual HorizonResult createDirectory(const FSPath& path) {
Helpers::panic("Unimplemented CreateDirectory for %s archive", name().c_str());
return Result::FS::AlreadyExists;
}
// Returns nullopt if opening the file failed, otherwise returns a file descriptor to it (nullptr if none is needed)
virtual FileDescriptor openFile(const FSPath& path, const FilePerms& perms) = 0;
virtual Rust::Result<ArchiveBase*, HorizonResult> openArchive(const FSPath& path) = 0;
// Returns nullopt if opening the file failed, otherwise returns a file descriptor to it (nullptr if none is needed)
virtual FileDescriptor openFile(const FSPath& path, const FilePerms& perms) = 0;
virtual Rust::Result<ArchiveBase*, HorizonResult> openArchive(const FSPath& path) = 0;
virtual Rust::Result<DirectorySession, HorizonResult> openDirectory(const FSPath& path) {
Helpers::panic("Unimplemented OpenDirectory for %s archive", name().c_str());
return Err(Result::FS::FileNotFoundAlt);
}
virtual Rust::Result<DirectorySession, HorizonResult> openDirectory(const FSPath& path) {
Helpers::panic("Unimplemented OpenDirectory for %s archive", name().c_str());
return Err(Result::FS::FileNotFoundAlt);
}
virtual void format(const FSPath& path, const FormatInfo& info) {
Helpers::panic("Unimplemented Format for %s archive", name().c_str());
}
virtual void format(const FSPath& path, const FormatInfo& info) { Helpers::panic("Unimplemented Format for %s archive", name().c_str()); }
virtual HorizonResult renameFile(const FSPath& oldPath, const FSPath& newPath) {
virtual HorizonResult renameFile(const FSPath& oldPath, const FSPath& newPath) {
Helpers::panic("Unimplemented RenameFile for %s archive", name().c_str());
return Result::Success;
}
}
// Read size bytes from a file starting at offset "offset" into a certain buffer in memory
// Returns the number of bytes read, or nullopt if the read failed
virtual std::optional<u32> readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) = 0;
// Read size bytes from a file starting at offset "offset" into a certain buffer in memory
// Returns the number of bytes read, or nullopt if the read failed
virtual std::optional<u32> readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) = 0;
ArchiveBase(Memory& mem) : mem(mem) {}
ArchiveBase(Memory& mem) : mem(mem) {}
bool isSafeTextPath(const FSPath& path) {
if (path.type == PathType::UTF16) {
return isPathSafe<PathType::UTF16>(path);
} else if (path.type == PathType::ASCII){
return isPathSafe<PathType::ASCII>(path);
}
Helpers::panic("ArchiveBase::IsSafeTextPath: Invalid path");
}
// Appends a 3DS path to an std::filesystem::path
void appendPath(std::filesystem::path& diskPath, const FSPath& guestPath) {
if (guestPath.type == PathType::UTF16) {
diskPath += std::filesystem::path(guestPath.utf16_string).make_preferred();
} else if (guestPath.type == PathType::ASCII) {
diskPath += std::filesystem::path(guestPath.string).make_preferred();
} else [[unlikely]] {
Helpers::panic("ArchiveBase::AppendPath: Invalid 3DS path");
}
}
};
struct ArchiveResource {

View file

@ -0,0 +1,30 @@
#pragma once
#include "archive_base.hpp"
#include "result/result.hpp"
using Result::HorizonResult;
class CardSPIArchive : public ArchiveBase {
public:
CardSPIArchive(Memory& mem) : ArchiveBase(mem) {}
std::string name() override { return "Card SPI"; }
u64 getFreeBytes() override {
Helpers::warn("Unimplemented GetFreeBytes for Card SPI archive");
return 0_MB;
}
HorizonResult createDirectory(const FSPath& path) override;
HorizonResult createFile(const FSPath& path, u64 size) override;
HorizonResult deleteFile(const FSPath& path) override;
Rust::Result<ArchiveBase*, HorizonResult> openArchive(const FSPath& path) override;
Rust::Result<DirectorySession, HorizonResult> openDirectory(const FSPath& path) override;
FileDescriptor openFile(const FSPath& path, const FilePerms& perms) override;
std::optional<u32> readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) override {
Helpers::panic("Unimplemented ReadFile for Card SPI archive");
return {};
};
};

View file

@ -0,0 +1,30 @@
#pragma once
#include "archive_base.hpp"
#include "result/result.hpp"
using Result::HorizonResult;
class TWLPhotoArchive : public ArchiveBase {
public:
TWLPhotoArchive(Memory& mem) : ArchiveBase(mem) {}
std::string name() override { return "TWL_PHOTO"; }
u64 getFreeBytes() override {
Helpers::warn("Unimplemented GetFreeBytes for TWLPhoto archive");
return 32_MB;
}
HorizonResult createDirectory(const FSPath& path) override;
HorizonResult createFile(const FSPath& path, u64 size) override;
HorizonResult deleteFile(const FSPath& path) override;
Rust::Result<ArchiveBase*, HorizonResult> openArchive(const FSPath& path) override;
Rust::Result<DirectorySession, HorizonResult> openDirectory(const FSPath& path) override;
FileDescriptor openFile(const FSPath& path, const FilePerms& perms) override;
std::optional<u32> readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) override {
Helpers::panic("Unimplemented ReadFile for TWL_PHOTO archive");
return {};
};
};

View file

@ -0,0 +1,30 @@
#pragma once
#include "archive_base.hpp"
#include "result/result.hpp"
using Result::HorizonResult;
class TWLSoundArchive : public ArchiveBase {
public:
TWLSoundArchive(Memory& mem) : ArchiveBase(mem) {}
std::string name() override { return "TWL_SOUND"; }
u64 getFreeBytes() override {
Helpers::warn("Unimplemented GetFreeBytes for TWLSound archive");
return 32_MB;
}
HorizonResult createDirectory(const FSPath& path) override;
HorizonResult createFile(const FSPath& path, u64 size) override;
HorizonResult deleteFile(const FSPath& path) override;
Rust::Result<ArchiveBase*, HorizonResult> openArchive(const FSPath& path) override;
Rust::Result<DirectorySession, HorizonResult> openDirectory(const FSPath& path) override;
FileDescriptor openFile(const FSPath& path, const FilePerms& perms) override;
std::optional<u32> readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) override {
Helpers::panic("Unimplemented ReadFile for TWL_SOUND archive");
return {};
};
};

View file

@ -4,7 +4,6 @@
#include <cstdint>
#include <iostream>
#include <iterator>
#include <sstream>
#include <string>
#include <vector>
#include <memory>
@ -162,19 +161,6 @@ namespace Helpers {
return std::bit_cast<To, From>(from);
}
#endif
static std::vector<std::string> split(const std::string& s, const char c) {
std::istringstream tmp(s);
std::vector<std::string> result(1);
while (std::getline(tmp, *result.rbegin(), c)) {
result.emplace_back();
}
// Remove temporary slot
result.pop_back();
return result;
}
}; // namespace Helpers
// UDLs for memory size values

View file

@ -2,8 +2,19 @@
#include <cstdint>
namespace IPC {
namespace BufferType {
enum : std::uint32_t {
Send = 1,
Receive = 2,
};
}
constexpr std::uint32_t responseHeader(std::uint32_t commandID, std::uint32_t normalResponses, std::uint32_t translateResponses) {
// TODO: Maybe validate the response count stuff fits in 6 bits
return (commandID << 16) | (normalResponses << 6) | translateResponses;
}
}
constexpr std::uint32_t pointerHeader(std::uint32_t index, std::uint32_t size, std::uint32_t type) {
return (size << 14) | (index << 10) | (type << 1);
}
} // namespace IPC

View file

@ -8,6 +8,7 @@ namespace ConfigMem {
KernelVersionMajor = 0x1FF80003,
SyscoreVer = 0x1FF80010,
EnvInfo = 0x1FF80014,
PrevFirm = 0x1FF80016,
AppMemAlloc = 0x1FF80040,
FirmUnknown = 0x1FF80060,
FirmRevision = 0x1FF80061,
@ -30,6 +31,11 @@ namespace ConfigMem {
// Shows what type of hardware we're running on
namespace HardwareCodes {
enum : u8 { Product = 1, Devboard = 2, Debugger = 3, Capture = 4 };
enum : u8 {
Product = 1,
Devboard = 2,
Debugger = 3,
Capture = 4,
};
}
} // namespace ConfigMem

View file

@ -1,7 +1,7 @@
#pragma once
#include "helpers.hpp"
using Handle = u32;
using HorizonHandle = u32;
namespace KernelHandles {
enum : u32 {
@ -20,6 +20,7 @@ namespace KernelHandles {
CFG_U, // CFG service (Console & region info)
CFG_I,
CFG_S, // Used by most system apps in lieu of cfg:u
CFG_NOR, // Used by system settings app
CSND, // Plays audio directly from PCM samples
DLP_SRVR, // Download Play: Server. Used for network play.
DSP, // DSP service (Used for audio decoding and output)
@ -38,11 +39,14 @@ namespace KernelHandles {
NIM, // Updates, DLC, etc
NDM, // ?????
NS_S, // Nintendo Shell service
NWM_EXT, // ?????
NWM_UDS, // Local multiplayer
NEWS_U, // This service literally has 1 command (AddNotification) and I don't even understand what it does
NEWS_S, // news:u on steroids
NEWS_U, // This service literally has 1 command (AddNotification)
PTM_U, // PTM service (Used for accessing various console info, such as battery, shell and pedometer state)
PTM_SYSM, // PTM system service
PTM_PLAY, // PTM Play service, used for retrieving play history
PTM_GETS, // PTM RTC service (GetSystemTime)
SOC, // Socket service
SSL, // SSL service (Totally didn't expect that)
Y2R, // Also does camera stuff
@ -61,17 +65,17 @@ namespace KernelHandles {
};
// Returns whether "handle" belongs to one of the OS services
static constexpr bool isServiceHandle(Handle handle) {
static constexpr bool isServiceHandle(HorizonHandle handle) {
return handle >= MinServiceHandle && handle <= MaxServiceHandle;
}
// Returns whether "handle" belongs to one of the OS services' shared memory areas
static constexpr bool isSharedMemHandle(Handle handle) {
static constexpr bool isSharedMemHandle(HorizonHandle handle) {
return handle >= MinSharedMemHandle && handle <= MaxSharedMemHandle;
}
// Returns the name of a handle as a string based on the given handle
static const char* getServiceName(Handle handle) {
static const char* getServiceName(HorizonHandle handle) {
switch (handle) {
case AC: return "AC";
case ACT: return "ACT";
@ -82,6 +86,8 @@ namespace KernelHandles {
case CECD: return "CECD";
case CFG_U: return "CFG:U";
case CFG_I: return "CFG:I";
case CFG_S: return "CFG:S";
case CFG_NOR: return "CFG:NOR";
case CSND: return "CSND";
case DSP: return "DSP";
case DLP_SRVR: return "DLP::SRVR";
@ -97,13 +103,16 @@ namespace KernelHandles {
case MCU_HWC: return "MCU::HWC";
case MIC: return "MIC";
case NDM: return "NDM";
case NEWS_S: return "NEWS_S";
case NEWS_U: return "NEWS_U";
case NWM_EXT: return "nwm::EXT";
case NWM_UDS: return "nwm::UDS";
case NFC: return "NFC";
case NIM: return "NIM";
case PTM_U: return "PTM:U";
case PTM_SYSM: return "PTM:SYSM";
case PTM_PLAY: return "PTM:PLAY";
case PTM_GETS: return "PTM:GETS";
case SOC: return "SOC";
case SSL: return "SSL";
case Y2R: return "Y2R";

View file

@ -18,6 +18,8 @@ class CPU;
struct Scheduler;
class Kernel {
using Handle = HorizonHandle;
std::span<u32, 16> regs;
CPU& cpu;
Memory& mem;

View file

@ -47,7 +47,7 @@ enum class ProcessorID : s32 {
struct AddressArbiter {};
struct ResourceLimits {
Handle handle;
HorizonHandle handle;
s32 currentCommit = 0;
};
@ -91,6 +91,8 @@ struct Port {
};
struct Session {
using Handle = HorizonHandle;
Handle portHandle; // The port this session is subscribed to
Session(Handle portHandle) : portHandle(portHandle) {}
};
@ -109,6 +111,8 @@ enum class ThreadStatus {
};
struct Thread {
using Handle = HorizonHandle;
u32 initialSP; // Initial r13 value
u32 entrypoint; // Initial r15 value
u32 priority;
@ -161,6 +165,8 @@ static const char* kernelObjectTypeToString(KernelObjectType t) {
}
struct Mutex {
using Handle = HorizonHandle;
u64 waitlist; // Refer to the getWaitlist function below for documentation
Handle ownerThread = 0; // Index of the thread that holds the mutex if it's locked
Handle handle; // Handle of the mutex itself
@ -203,6 +209,8 @@ struct MemoryBlock {
// Generic kernel object class
struct KernelObject {
using Handle = HorizonHandle;
Handle handle = 0; // A u32 the OS will use to identify objects
void* data = nullptr;
KernelObjectType type;

View file

@ -50,6 +50,7 @@ struct NCCH {
static constexpr u64 mediaUnit = 0x200;
u64 size = 0; // Size of NCCH converted to bytes
u64 saveDataSize = 0;
u32 stackSize = 0;
u32 bssSize = 0;
u32 exheaderSize = 0;
@ -64,8 +65,6 @@ struct NCCH {
// Contents of the .code file in the ExeFS
std::vector<u8> codeFile;
// Contains of the cart's save data
std::vector<u8> saveData;
// The cart region. Only the CXI's region matters to us. Necessary to get past region locking
std::optional<Regions> region = std::nullopt;
std::vector<u8> smdh;
@ -78,7 +77,7 @@ struct NCCH {
bool hasExeFS() { return exeFS.size != 0; }
bool hasRomFS() { return romFS.size != 0; }
bool hasCode() { return codeFile.size() != 0; }
bool hasSaveData() { return saveData.size() != 0; }
bool hasSaveData() { return saveDataSize != 0; }
// Parse SMDH for region info and such. Returns false on failure, true on success
bool parseSMDH(const std::vector<u8> &smdh);

View file

@ -65,6 +65,7 @@ namespace Log {
static Logger<false> nwmUdsLogger;
static Logger<false> nimLogger;
static Logger<false> ndmLogger;
static Logger<false> nsLogger;
static Logger<false> ptmLogger;
static Logger<false> socLogger;
static Logger<false> sslLogger;

View file

@ -102,6 +102,8 @@ namespace KernelMemoryTypes {
}
class Memory {
using Handle = HorizonHandle;
u8* fcram;
u8* dspRam; // Provided to us by Audio
u8* vram; // Provided to the memory class by the GPU class
@ -213,8 +215,14 @@ private:
}
enum class BatteryLevel {
Empty = 0, AlmostEmpty, OneBar, TwoBars, ThreeBars, FourBars
Empty = 0,
AlmostEmpty,
OneBar,
TwoBars,
ThreeBars,
FourBars,
};
u8 getBatteryState(bool adapterConnected, bool charging, BatteryLevel batteryLevel) {
u8 value = static_cast<u8>(batteryLevel) << 2; // Bits 2:4 are the battery level from 0 to 5
if (adapterConnected) value |= 1 << 0; // Bit 0 shows if the charger is connected
@ -290,5 +298,5 @@ private:
bool allocateMainThreadStack(u32 size);
Regions getConsoleRegion();
void copySharedFont(u8* ptr);
void copySharedFont(u8* ptr, u32 vaddr);
};

View file

@ -1,6 +1,13 @@
#pragma once
#include <QAction>
#include <QCheckBox>
#include <QDialog>
#include <QLabel>
#include <QLineEdit>
#include <QListWidget>
#include <QPushButton>
#include <QTextEdit>
#include <QWidget>
#include <filesystem>
#include <memory>
@ -24,3 +31,60 @@ class CheatsWindow final : public QWidget {
std::filesystem::path cheatPath;
Emulator* emu;
};
struct CheatMetadata {
u32 handle = Cheats::badCheatHandle;
std::string name = "New cheat";
std::string code;
bool enabled = true;
};
class CheatEntryWidget : public QWidget {
Q_OBJECT
public:
CheatEntryWidget(Emulator* emu, CheatMetadata metadata, QListWidget* parent);
void Update() {
name->setText(metadata.name.c_str());
enabled->setChecked(metadata.enabled);
update();
}
void Remove() {
emu->getCheats().removeCheat(metadata.handle);
cheatList->takeItem(cheatList->row(listItem));
deleteLater();
}
const CheatMetadata& getMetadata() { return metadata; }
void setMetadata(const CheatMetadata& metadata) { this->metadata = metadata; }
private:
void checkboxChanged(int state);
void editClicked();
Emulator* emu;
CheatMetadata metadata;
u32 handle;
QLabel* name;
QCheckBox* enabled;
QListWidget* cheatList;
QListWidgetItem* listItem;
};
class CheatEditDialog : public QDialog {
Q_OBJECT
public:
CheatEditDialog(Emulator* emu, CheatEntryWidget& cheatEntry);
void accepted();
void rejected();
private:
Emulator* emu;
CheatEntryWidget& cheatEntry;
QTextEdit* codeEdit;
QLineEdit* nameEdit;
};

View file

@ -1,30 +1,58 @@
#pragma once
#include <QApplication>
#include <QCheckBox>
#include <QComboBox>
#include <QDialog>
#include <QListWidget>
#include <QPalette>
#include <QStackedWidget>
#include <QTextEdit>
#include <QWidget>
#include <QtWidgets>
#include <array>
#include <functional>
#include <utility>
#include "emulator.hpp"
#include "frontend_settings.hpp"
class ConfigWindow : public QDialog {
Q_OBJECT
private:
enum class Theme : int {
System = 0,
Light = 1,
Dark = 2,
GreetingsCat = 3,
Cream = 4,
};
using ConfigCallback = std::function<void()>;
using MainWindowCallback = std::function<QWidget*()>;
Theme currentTheme;
QComboBox* themeSelect = nullptr;
using Theme = FrontendSettings::Theme;
using WindowIcon = FrontendSettings::WindowIcon;
void setTheme(Theme theme);
QTextEdit* helpText = nullptr;
QListWidget* widgetList = nullptr;
QStackedWidget* widgetContainer = nullptr;
static constexpr size_t settingWidgetCount = 6;
std::array<QString, settingWidgetCount> helpTexts;
// The config class holds a copy of the emulator config which it edits and sends
// over to the emulator in a thread-safe manner
EmulatorConfig config;
ConfigCallback updateConfig;
MainWindowCallback getMainWindow;
void addWidget(QWidget* widget, QString title, QString icon, QString helpText);
void setTheme(FrontendSettings::Theme theme);
void setIcon(FrontendSettings::WindowIcon icon);
QComboBox* createLanguageSelect();
public:
ConfigWindow(QWidget* parent = nullptr);
ConfigWindow(ConfigCallback configCallback, MainWindowCallback windowCallback, const EmulatorConfig& config, QWidget* parent = nullptr);
~ConfigWindow();
EmulatorConfig& getConfig() { return config; }
private:
Emulator* emu;
};

View file

@ -50,6 +50,8 @@ class MainWindow : public QMainWindow {
PressTouchscreen,
ReleaseTouchscreen,
ReloadUbershader,
SetScreenSize,
UpdateConfig,
};
// Tagged union representing our message queue messages
@ -81,6 +83,11 @@ class MainWindow : public QMainWindow {
u16 x;
u16 y;
} touchscreen;
struct {
u32 width;
u32 height;
} screenSize;
};
};
@ -95,7 +102,7 @@ class MainWindow : public QMainWindow {
QMenuBar* menuBar = nullptr;
InputMappings keyboardMappings;
ScreenWidget screen;
ScreenWidget* screen;
AboutWindow* aboutWindow;
ConfigWindow* configWindow;
CheatsWindow* cheatsEditor;
@ -116,12 +123,15 @@ class MainWindow : public QMainWindow {
void showAboutMenu();
void initControllers();
void pollControllers();
void setupControllerSensors(SDL_GameController* controller);
void sendMessage(const EmulatorMessage& message);
void dispatchMessage(const EmulatorMessage& message);
void loadTranslation();
// Tracks whether we are using an OpenGL-backed renderer or a Vulkan-backed renderer
bool usingGL = false;
bool usingVk = false;
bool usingMtl = false;
// Variables to keep track of whether the user is controlling the 3DS analog stick with their keyboard
// This is done so when a gamepad is connected, we won't automatically override the 3DS analog stick settings with the gamepad's state
@ -133,12 +143,18 @@ class MainWindow : public QMainWindow {
MainWindow(QApplication* app, QWidget* parent = nullptr);
~MainWindow();
void closeEvent(QCloseEvent* event) override;
void keyPressEvent(QKeyEvent* event) override;
void keyReleaseEvent(QKeyEvent* event) override;
void mousePressEvent(QMouseEvent* event) override;
void mouseReleaseEvent(QMouseEvent* event) override;
void mouseMoveEvent(QMouseEvent* event) override;
void loadLuaScript(const std::string& code);
void reloadShader(const std::string& shader);
void editCheat(u32 handle, const std::vector<uint8_t>& cheat, const std::function<void(u32)>& callback);
void handleScreenResize(u32 width, u32 height);
void handleTouchscreenPress(QMouseEvent* event);
};

View file

@ -1,5 +1,6 @@
#pragma once
#include <QWidget>
#include <functional>
#include <memory>
#include "gl/context.h"
@ -10,15 +11,28 @@ class ScreenWidget : public QWidget {
Q_OBJECT
public:
ScreenWidget(QWidget* parent = nullptr);
using ResizeCallback = std::function<void(u32, u32)>;
ScreenWidget(ResizeCallback resizeCallback, QWidget* parent = nullptr);
void resizeEvent(QResizeEvent* event) override;
// Called by the emulator thread for resizing the actual GL surface, since the emulator thread owns the GL context
void resizeSurface(u32 width, u32 height);
GL::Context* getGLContext() { return glContext.get(); }
// Dimensions of our output surface
u32 surfaceWidth = 0;
u32 surfaceHeight = 0;
WindowInfo windowInfo;
// Cached "previous" dimensions, used when resizing our window
u32 previousWidth = 0;
u32 previousHeight = 0;
private:
std::unique_ptr<GL::Context> glContext = nullptr;
ResizeCallback resizeCallback;
bool createGLContext();
qreal devicePixelRatioFromScreen() const;

View file

@ -23,6 +23,8 @@ class FrontendSDL {
SDL_GameController* gameController = nullptr;
InputMappings keyboardMappings;
u32 windowWidth = 400;
u32 windowHeight = 480;
int gameControllerID;
bool programRunning = true;
@ -35,4 +37,6 @@ class FrontendSDL {
// And so the user can still use the keyboard to control the analog
bool keyboardAnalogX = false;
bool keyboardAnalogY = false;
void setupControllerSensors(SDL_GameController* controller);
};

73
include/renderdoc.hpp Normal file
View file

@ -0,0 +1,73 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <string>
#include "helpers.hpp"
#ifdef PANDA3DS_ENABLE_RENDERDOC
namespace Renderdoc {
// Loads renderdoc dynamic library module.
void loadRenderdoc();
// Begins a capture if a renderdoc instance is attached.
void startCapture();
// Ends current renderdoc capture.
void endCapture();
// Triggers capturing process.
void triggerCapture();
// Sets output directory for captures
void setOutputDir(const std::string& path, const std::string& prefix);
// Returns whether Renderdoc has been loaded
bool isLoaded();
// Returns whether we've compiled with Renderdoc support
static constexpr bool isSupported() { return true; }
} // namespace Renderdoc
#else
namespace Renderdoc {
static void loadRenderdoc() {}
static void startCapture() { Helpers::panic("Tried to start a Renderdoc capture while support for renderdoc is disabled"); }
static void endCapture() { Helpers::panic("Tried to end a Renderdoc capture while support for renderdoc is disabled"); }
static void triggerCapture() { Helpers::panic("Tried to trigger a Renderdoc capture while support for renderdoc is disabled"); }
static void setOutputDir(const std::string& path, const std::string& prefix) {}
static constexpr bool isSupported() { return false; }
static constexpr bool isLoaded() { return false; }
} // namespace Renderdoc
#endif
namespace Renderdoc {
// RAII scope class that encloses a Renderdoc capture, as long as it's triggered by triggerCapture
struct Scope {
Scope() { Renderdoc::startCapture(); }
~Scope() { Renderdoc::endCapture(); }
Scope(const Scope&) = delete;
Scope& operator=(const Scope&) = delete;
Scope(Scope&&) = delete;
Scope& operator=(const Scope&&) = delete;
};
// RAII scope class that encloses a Renderdoc capture. Unlike regular Scope it doesn't wait for a trigger, it will always issue the capture
// trigger on its own and take a capture
struct InstantScope {
InstantScope() {
Renderdoc::triggerCapture();
Renderdoc::startCapture();
}
~InstantScope() { Renderdoc::endCapture(); }
InstantScope(const InstantScope&) = delete;
InstantScope& operator=(const InstantScope&) = delete;
InstantScope(InstantScope&&) = delete;
InstantScope& operator=(const InstantScope&&) = delete;
};
} // namespace Renderdoc

View file

@ -1,9 +1,10 @@
#pragma once
#include <array>
#include <optional>
#include <span>
#include <string>
#include <optional>
#include "PICA/draw_acceleration.hpp"
#include "PICA/pica_vertex.hpp"
#include "PICA/regs.hpp"
#include "helpers.hpp"
@ -17,12 +18,16 @@ enum class RendererType : s8 {
Null = 0,
OpenGL = 1,
Vulkan = 2,
Software = 3,
Metal = 3,
Software = 4,
};
class GPU;
struct EmulatorConfig;
struct SDL_Window;
class GPU;
class ShaderUnit;
class Renderer {
protected:
GPU& gpu;
@ -46,6 +51,8 @@ class Renderer {
u32 outputWindowWidth = 400;
u32 outputWindowHeight = 240 * 2;
EmulatorConfig* emulatorConfig = nullptr;
public:
Renderer(GPU& gpu, const std::array<u32, regNum>& internalRegs, const std::array<u32, extRegNum>& externalRegs);
virtual ~Renderer();
@ -74,6 +81,16 @@ class Renderer {
virtual std::string getUbershader() { return ""; }
virtual void setUbershader(const std::string& shader) {}
// Only relevant for OpenGL renderer and other OpenGL-based backends (eg software)
// Called to notify the core to use OpenGL ES and not desktop GL
virtual void setupGLES() {}
// This function is called on every draw call before parsing vertex data.
// It is responsible for things like looking up which vertex/fragment shaders to use, recompiling them if they don't exist, choosing between
// ubershaders and shadergen, and so on.
// Returns whether this draw is eligible for using hardware-accelerated shaders or if shaders should run on the CPU
virtual bool prepareForDraw(ShaderUnit& shaderUnit, PICA::DrawAcceleration* accel) { return false; }
// Functions for initializing the graphics context for the Qt frontend, where we don't have the convenience of SDL_Window
#ifdef PANDA3DS_FRONTEND_QT
virtual void initGraphicsContext(GL::Context* context) { Helpers::panic("Tried to initialize incompatible renderer with GL context"); }
@ -99,4 +116,6 @@ class Renderer {
outputWindowWidth = width;
outputWindowHeight = height;
}
void setConfig(EmulatorConfig* config) { emulatorConfig = config; }
};

View file

@ -0,0 +1,13 @@
#pragma once
// Information about our OpenGL/OpenGL ES driver that we should keep track of
// Stuff like whether specific extensions are supported, and potentially things like OpenGL context information
namespace OpenGL {
struct Driver {
bool usingGLES = false;
bool supportsExtFbFetch = false;
bool supportsArmFbFetch = false;
bool supportFbFetch() const { return supportsExtFbFetch || supportsArmFbFetch; }
};
} // namespace OpenGL

View file

@ -38,11 +38,14 @@ struct GLStateManager {
GLuint stencilMask;
GLuint boundVAO;
GLuint boundVBO;
GLuint currentProgram;
GLuint boundUBO;
GLenum depthFunc;
GLenum logicOp;
GLenum blendEquationRGB, blendEquationAlpha;
GLenum blendFuncSourceRGB, blendFuncSourceAlpha;
GLenum blendFuncDestRGB, blendFuncDestAlpha;
void reset();
void resetBlend();
@ -51,7 +54,7 @@ struct GLStateManager {
void resetColourMask();
void resetDepth();
void resetVAO();
void resetVBO();
void resetBuffers();
void resetProgram();
void resetScissor();
void resetStencil();
@ -169,13 +172,6 @@ struct GLStateManager {
}
}
void bindVBO(GLuint handle) {
if (boundVBO != handle) {
boundVBO = handle;
glBindBuffer(GL_ARRAY_BUFFER, handle);
}
}
void useProgram(GLuint handle) {
if (currentProgram != handle) {
currentProgram = handle;
@ -183,8 +179,14 @@ struct GLStateManager {
}
}
void bindUBO(GLuint handle) {
if (boundUBO != handle) {
boundUBO = handle;
glBindBuffer(GL_UNIFORM_BUFFER, boundUBO);
}
}
void bindVAO(const OpenGL::VertexArray& vao) { bindVAO(vao.handle()); }
void bindVBO(const OpenGL::VertexBuffer& vbo) { bindVBO(vbo.handle()); }
void useProgram(const OpenGL::Program& program) { useProgram(program.handle()); }
void setColourMask(bool r, bool g, bool b, bool a) {
@ -224,6 +226,41 @@ struct GLStateManager {
}
void setDepthFunc(OpenGL::DepthFunc func) { setDepthFunc(static_cast<GLenum>(func)); }
// Counterpart to glBlendEquationSeparate
void setBlendEquation(GLenum modeRGB, GLenum modeAlpha) {
if (blendEquationRGB != modeRGB || blendEquationAlpha != modeAlpha) {
blendEquationRGB = modeRGB;
blendEquationAlpha = modeAlpha;
glBlendEquationSeparate(modeRGB, modeAlpha);
}
}
// Counterpart to glBlendFuncSeparate
void setBlendFunc(GLenum sourceRGB, GLenum destRGB, GLenum sourceAlpha, GLenum destAlpha) {
if (blendFuncSourceRGB != sourceRGB || blendFuncDestRGB != destRGB || blendFuncSourceAlpha != sourceAlpha ||
blendFuncDestAlpha != destAlpha) {
blendFuncSourceRGB = sourceRGB;
blendFuncDestRGB = destRGB;
blendFuncSourceAlpha = sourceAlpha;
blendFuncDestAlpha = destAlpha;
glBlendFuncSeparate(sourceRGB, destRGB,sourceAlpha, destAlpha);
}
}
// Counterpart to regular glBlendEquation
void setBlendEquation(GLenum mode) { setBlendEquation(mode, mode); }
void setBlendEquation(OpenGL::BlendEquation modeRGB, OpenGL::BlendEquation modeAlpha) {
setBlendEquation(static_cast<GLenum>(modeRGB), static_cast<GLenum>(modeAlpha));
}
void setBlendEquation(OpenGL::BlendEquation mode) {
setBlendEquation(static_cast<GLenum>(mode));
}
};
static_assert(std::is_trivially_constructible<GLStateManager>(), "OpenGL State Manager class is not trivially constructible!");

View file

@ -1,11 +1,23 @@
#pragma once
#include <array>
#include <cstring>
#include <functional>
#include <memory>
#include <optional>
#include <span>
#include <unordered_map>
#include <utility>
#include "PICA/float_types.hpp"
#include "PICA/pica_frag_config.hpp"
#include "PICA/pica_hash.hpp"
#include "PICA/pica_vert_config.hpp"
#include "PICA/pica_vertex.hpp"
#include "PICA/regs.hpp"
#include "PICA/shader_gen.hpp"
#include "gl/stream_buffer.h"
#include "gl_driver.hpp"
#include "gl_state.hpp"
#include "helpers.hpp"
#include "logger.hpp"
@ -22,27 +34,48 @@ class RendererGL final : public Renderer {
OpenGL::Program triangleProgram;
OpenGL::Program displayProgram;
OpenGL::VertexArray vao;
// VAO for when not using accelerated vertex shaders. Contains attribute declarations matching to the PICA fixed function fragment attributes
OpenGL::VertexArray defaultVAO;
// VAO for when using accelerated vertex shaders. The PICA vertex shader inputs are passed as attributes without CPU processing.
OpenGL::VertexArray hwShaderVAO;
OpenGL::VertexBuffer vbo;
// TEV configuration uniform locations
GLint textureEnvSourceLoc = -1;
GLint textureEnvOperandLoc = -1;
GLint textureEnvCombinerLoc = -1;
GLint textureEnvColorLoc = -1;
GLint textureEnvScaleLoc = -1;
// Data
struct {
// TEV configuration uniform locations
GLint textureEnvSourceLoc = -1;
GLint textureEnvOperandLoc = -1;
GLint textureEnvCombinerLoc = -1;
GLint textureEnvColorLoc = -1;
GLint textureEnvScaleLoc = -1;
// Uniform of PICA registers
GLint picaRegLoc = -1;
// Uniform of PICA registers
GLint picaRegLoc = -1;
// Depth configuration uniform locations
GLint depthOffsetLoc = -1;
GLint depthScaleLoc = -1;
GLint depthmapEnableLoc = -1;
// Depth configuration uniform locations
GLint depthOffsetLoc = -1;
GLint depthScaleLoc = -1;
GLint depthmapEnableLoc = -1;
} ubershaderData;
float oldDepthScale = -1.0;
float oldDepthOffset = 0.0;
bool oldDepthmapEnable = false;
// Set by prepareForDraw, tells us whether the current draw is using hw-accelerated shader
bool usingAcceleratedShader = false;
bool performIndexedRender = false;
bool usingShortIndices = false;
// Set by prepareForDraw, metadata for indexed renders
GLuint minimumIndex = 0;
GLuint maximumIndex = 0;
void* hwIndexBufferOffset = nullptr;
// When doing hw shaders, we cache which attributes are enabled in our VAO to avoid having to enable/disable all attributes on each draw
u32 previousAttributeMask = 0;
// Cached pointer to the current vertex shader when using HW accelerated shaders
OpenGL::Shader* generatedVertexShader = nullptr;
SurfaceCache<DepthBuffer, 16, true> depthBufferCache;
SurfaceCache<ColourBuffer, 16, true> colourBufferCache;
@ -53,25 +86,82 @@ class RendererGL final : public Renderer {
OpenGL::VertexBuffer dummyVBO;
OpenGL::Texture screenTexture;
GLuint lightLUTTextureArray;
OpenGL::Texture LUTTexture;
OpenGL::Framebuffer screenFramebuffer;
OpenGL::Texture blankTexture;
// The "default" vertex shader to use when using specialized shaders but not PICA vertex shader -> GLSL recompilation
// We can compile this once and then link it with all other generated fragment shaders
OpenGL::Shader defaultShadergenVs;
GLuint shadergenFragmentUBO;
// UBO for uploading the PICA uniforms when using hw shaders
GLuint hwShaderUniformUBO;
using StreamBuffer = OpenGLStreamBuffer;
std::unique_ptr<StreamBuffer> hwVertexBuffer;
std::unique_ptr<StreamBuffer> hwIndexBuffer;
// Cache of fixed attribute values so that we don't do any duplicate updates
std::array<std::array<float, 4>, 16> fixedAttrValues;
// Cached recompiled fragment shader
struct CachedProgram {
OpenGL::Program program;
};
struct ShaderCache {
std::unordered_map<PICA::VertConfig, std::optional<OpenGL::Shader>> vertexShaderCache;
std::unordered_map<PICA::FragmentConfig, OpenGL::Shader> fragmentShaderCache;
// Program cache indexed by GLuints for the vertex and fragment shader to use
// Top 32 bits are the vertex shader GLuint, bottom 32 bits are the fs GLuint
std::unordered_map<u64, CachedProgram> programCache;
void clear() {
for (auto& it : programCache) {
CachedProgram& cachedProgram = it.second;
cachedProgram.program.free();
}
for (auto& it : vertexShaderCache) {
if (it.second.has_value()) {
it.second->free();
}
}
for (auto& it : fragmentShaderCache) {
it.second.free();
}
programCache.clear();
vertexShaderCache.clear();
fragmentShaderCache.clear();
}
};
ShaderCache shaderCache;
OpenGL::Framebuffer getColourFBO();
OpenGL::Texture getTexture(Texture& tex);
OpenGL::Program& getSpecializedShader();
PICA::ShaderGen::FragmentGenerator fragShaderGen;
OpenGL::Driver driverInfo;
MAKE_LOG_FUNCTION(log, rendererLogger)
void setupBlending();
void setupStencilTest(bool stencilEnable);
void bindDepthBuffer();
void setupTextureEnvState();
void setupUbershaderTexEnv();
void bindTexturesToSlots();
void updateLightingLUT();
void updateFogLUT();
void initGraphicsContextInternal();
void accelerateVertexUpload(ShaderUnit& shaderUnit, PICA::DrawAcceleration* accel);
void compileDisplayShader();
public:
RendererGL(GPU& gpu, const std::array<u32, regNum>& internalRegs, const std::array<u32, extRegNum>& externalRegs)
: Renderer(gpu, internalRegs, externalRegs) {}
: Renderer(gpu, internalRegs, externalRegs), fragShaderGen(PICA::ShaderGen::API::GL, PICA::ShaderGen::Language::GLSL) {}
~RendererGL() override;
void reset() override;
@ -80,12 +170,14 @@ class RendererGL final : public Renderer {
void clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control) override; // Clear a GPU buffer in VRAM
void displayTransfer(u32 inputAddr, u32 outputAddr, u32 inputSize, u32 outputSize, u32 flags) override; // Perform display transfer
void textureCopy(u32 inputAddr, u32 outputAddr, u32 totalBytes, u32 inputSize, u32 outputSize, u32 flags) override;
void drawVertices(PICA::PrimType primType, std::span<const PICA::Vertex> vertices) override; // Draw the given vertices
void drawVertices(PICA::PrimType primType, std::span<const PICA::Vertex> vertices) override; // Draw the given vertices
void deinitGraphicsContext() override;
virtual bool supportsShaderReload() override { return true; }
virtual std::string getUbershader() override;
virtual void setUbershader(const std::string& shader) override;
virtual bool prepareForDraw(ShaderUnit& shaderUnit, PICA::DrawAcceleration* accel) override;
virtual void setupGLES() override;
std::optional<ColourBuffer> getColourBuffer(u32 addr, PICA::ColorFmt format, u32 width, u32 height, bool createIfnotFound = true);
@ -100,4 +192,4 @@ class RendererGL final : public Renderer {
// Take a screenshot of the screen and store it in a file
void screenshot(const std::string& name) override;
};
};

View file

@ -19,8 +19,6 @@ template <typename SurfaceType, size_t capacity, bool evictOnOverflow = false>
class SurfaceCache {
// Vanilla std::optional can't hold actual references
using OptionalRef = std::optional<std::reference_wrapper<SurfaceType>>;
static_assert(std::is_same<SurfaceType, ColourBuffer>() || std::is_same<SurfaceType, DepthBuffer>() ||
std::is_same<SurfaceType, Texture>(), "Invalid surface type");
size_t size;
size_t evictionIndex;

Some files were not shown because too many files have changed in this diff Show more