Merge branch 'master' into open-bp-cpp
315
.github/gles.patch
vendored
|
@ -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); }
|
||||
|
|
20
.github/mac-bundle-qt.sh
vendored
|
@ -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
|
@ -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
|
||||
|
|
14
.github/workflows/Android_Build.yml
vendored
|
@ -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: |
|
||||
|
|
4
.github/workflows/HTTP_Build.yml
vendored
|
@ -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
|
||||
|
||||
|
|
24
.github/workflows/Hydra_Build.yml
vendored
|
@ -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
|
||||
|
|
18
.github/workflows/Linux_AppImage_Build.yml
vendored
|
@ -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'
|
||||
|
|
8
.github/workflows/Linux_Build.yml
vendored
|
@ -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'
|
||||
|
|
58
.github/workflows/MacOS_Build.yml
vendored
|
@ -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
|
||||
|
|
91
.github/workflows/Qt_Build.yml
vendored
|
@ -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'
|
||||
|
|
4
.github/workflows/Windows_Build.yml
vendored
|
@ -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
|
@ -64,5 +64,9 @@ fb.bat
|
|||
*.elf
|
||||
*.smdh
|
||||
|
||||
# Compiled Metal shader files
|
||||
*.ir
|
||||
*.metallib
|
||||
|
||||
config.toml
|
||||
CMakeSettings.json
|
||||
|
|
131
.gitlab-ci.yml
Normal 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
|
@ -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
|
||||
|
|
385
CMakeLists.txt
|
@ -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
|
@ -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)
|
BIN
docs/3ds/accelerometer_readings/readings_flat_1.png
Normal file
After Width: | Height: | Size: 148 KiB |
BIN
docs/3ds/accelerometer_readings/readings_flat_2.png
Normal file
After Width: | Height: | Size: 54 KiB |
BIN
docs/3ds/accelerometer_readings/readings_shaking_1.png
Normal file
After Width: | Height: | Size: 212 KiB |
BIN
docs/3ds/accelerometer_readings/readings_shaking_2.png
Normal file
After Width: | Height: | Size: 65 KiB |
79
docs/3ds/lighting.md
Normal 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
After Width: | Height: | Size: 567 KiB |
BIN
docs/img/battery_icon.png
Normal file
After Width: | Height: | Size: 4.3 KiB |
BIN
docs/img/display_icon.png
Normal file
After Width: | Height: | Size: 166 B |
Before Width: | Height: | Size: 2 MiB After Width: | Height: | Size: 2 MiB |
BIN
docs/img/rcow_icon.png
Normal file
After Width: | Height: | Size: 32 KiB |
BIN
docs/img/rnap_icon.png
Normal file
After Width: | Height: | Size: 27 KiB |
BIN
docs/img/rsyn_icon.png
Normal file
After Width: | Height: | Size: 30 KiB |
BIN
docs/img/sdcard_icon.png
Normal file
After Width: | Height: | Size: 4.3 KiB |
BIN
docs/img/settings_icon.png
Normal file
After Width: | Height: | Size: 697 B |
BIN
docs/img/skyemu_icon.png
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
docs/img/sparkling_icon.png
Normal file
After Width: | Height: | Size: 549 B |
BIN
docs/img/speaker_icon.png
Normal file
After Width: | Height: | Size: 507 B |
BIN
docs/img/windows_alt_icon.ico
Normal file
After Width: | Height: | Size: 54 KiB |
BIN
docs/img/windows_icon.ico
Normal file
After Width: | Height: | Size: 54 KiB |
1
docs/img/windows_icon.rc
Normal file
|
@ -0,0 +1 @@
|
|||
AlberIcon ICON "windows_icon.ico"
|
|
@ -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
|
@ -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've agreed to be listed here, in no particular order.<br>If you think you should be listed here too, please inform us<br><br>- Peach (wheremyfoodat)<br>- noumidev<br>- liuk707<br>- Wunk<br>- marysaka<br>- Sky<br>- merryhime<br>- TGP17<br>- Shadow<br></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'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'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
|
@ -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've agreed to be listed here, in no particular order.<br>If you think you should be listed here too, please inform us<br><br>- Peach (wheremyfoodat)<br>- noumidev<br>- liuk707<br>- Wunk<br>- marysaka<br>- Sky<br>- merryhime<br>- TGP17<br>- Shadow<br></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'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'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
|
@ -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've agreed to be listed here, in no particular order.<br>If you think you should be listed here too, please inform us<br><br>- Peach (wheremyfoodat)<br>- noumidev<br>- liuk707<br>- Wunk<br>- marysaka<br>- Sky<br>- merryhime<br>- TGP17<br>- Shadow<br></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.<br>Si piensas que deberías ser listado, por favor infórmanos<br><br>- Peach (wheremyfoodat)<br>- noumidev<br>- liuk707<br>- Wunk<br>- marysaka<br>- Sky<br>- merryhime<br>- TGP17<br>- Shadow<br></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'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'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
|
@ -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've agreed to be listed here, in no particular order.<br>If you think you should be listed here too, please inform us<br><br>- Peach (wheremyfoodat)<br>- noumidev<br>- liuk707<br>- Wunk<br>- marysaka<br>- Sky<br>- merryhime<br>- TGP17<br>- Shadow<br></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.<br>Als jij vindt dat je op deze lijst zou moeten staan, laat het ons dan weten<br><br>- Peach (wheremyfoodat)<br>- noumidev<br>- liuk707<br>- Wunk<br>- marysaka<br>- Sky<br>- merryhime<br>- TGP17<br>- Shadow<br></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'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'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
|
@ -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've agreed to be listed here, in no particular order.<br>If you think you should be listed here too, please inform us<br><br>- Peach (wheremyfoodat)<br>- noumidev<br>- liuk707<br>- Wunk<br>- marysaka<br>- Sky<br>- merryhime<br>- TGP17<br>- Shadow<br></source>
|
||||
<translation>Panda3DS é desenvolvido por voluntários em seu tempo livre. Abaixo a lista de alguns volutário<br>(Lista sem nenhuma ordem específica)<br>Se acha que seu nome deveria estar listado aqui por favor informe-nos<br><br>- Peach (wheremyfoodat)<br>- noumidev<br>- liuk707<br>- Wunk<br>- marysaka<br>- Sky<br>- merryhime<br>- TGP17<br>- Shadow<br></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'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'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>
|
||||
|
45
include/PICA/draw_acceleration.hpp
Normal 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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)); }
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
257
include/PICA/pica_frag_config.hpp
Normal 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], ®s[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)); }
|
||||
};
|
47
include/PICA/pica_frag_uniforms.hpp
Normal 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
|
@ -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
|
57
include/PICA/pica_vert_config.hpp
Normal 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)); }
|
||||
};
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
);
|
131
include/PICA/shader_decompiler.hpp
Normal 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
|
43
include/PICA/shader_gen.hpp
Normal 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
|
9
include/PICA/shader_gen_types.hpp
Normal 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
|
|
@ -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
|
@ -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
|
|
@ -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;
|
||||
};
|
||||
};
|
||||
|
||||
|
|
25
include/audio/aac_decoder.hpp
Normal 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
|
58
include/audio/audio_interpolation.hpp
Normal 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
|
|
@ -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
|
|
@ -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);
|
||||
|
|
78
include/audio/dsp_simd.hpp
Normal 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
|
|
@ -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
|
||||
|
|
50
include/audio/hle_mixer.hpp
Normal 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
|
|
@ -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; }
|
||||
};
|
|
@ -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(); }
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
};
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
|
|
34
include/frontend_settings.hpp
Normal 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);
|
||||
};
|
|
@ -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 {
|
||||
|
|
30
include/fs/archive_card_spi.hpp
Normal 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 {};
|
||||
};
|
||||
};
|
30
include/fs/archive_twl_photo.hpp
Normal 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 {};
|
||||
};
|
||||
};
|
30
include/fs/archive_twl_sound.hpp
Normal 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 {};
|
||||
};
|
||||
};
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -18,6 +18,8 @@ class CPU;
|
|||
struct Scheduler;
|
||||
|
||||
class Kernel {
|
||||
using Handle = HorizonHandle;
|
||||
|
||||
std::span<u32, 16> regs;
|
||||
CPU& cpu;
|
||||
Memory& mem;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
};
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
@ -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
|
|
@ -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; }
|
||||
};
|
||||
|
|
13
include/renderer_gl/gl_driver.hpp
Normal 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
|
|
@ -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!");
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
};
|
|
@ -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;
|
||||
|
|