Merge pull request #20 from dolphin-emu/master

[pull] master from dolphin-emu:master
This commit is contained in:
Nayla 2023-08-23 19:14:16 -04:00 committed by GitHub
commit 872ece4c2f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
92 changed files with 18567 additions and 2615 deletions

View file

@ -0,0 +1,307 @@
{
"meta":
{
"title": "Dragon Ball Z: Budokai Tenkaichi 3 Definitions",
"author": "Seedonator"
},
"groups":
[
{
"name": "Bloom",
"targets": [
{
"type": "efb",
"texture_filename": "efb1_n000014_128x128_4"
}
]
},
{
"name": "HUD",
"targets": [
{
"type": "draw_started",
"prettyname": "HUD Backdrop",
"texture_filename": "tex1_256x64_50e9493ab5ecd6e9_5efd0133152917bc_9"
},
{
"type": "draw_started",
"prettyname": "Max Power Lightning",
"texture_filename": "tex1_256x128_76ee22f289405f4f_0a3363fc16e5200b_9"
},
{
"type": "draw_started",
"prettyname": "Timer Backdrop",
"texture_filename": "tex1_64x64_1b0bd43920520089_894789000b300d35_9"
},
{
"type": "draw_started",
"prettyname": "Teammate Backdrop",
"texture_filename": "tex1_64x64_76189a0850bac928_d5586441a9651de1_9"
},
{
"type": "draw_started",
"prettyname": "HUD Backdrop 2",
"texture_filename": "tex1_256x64_4751a5dd9b515483_6ef5db463bf9b5fe_9"
},
{
"type": "draw_started",
"prettyname": "Meters Colored",
"texture_filename": "tex1_256x64_974aae7fb39dd3fe_dc826162c7781cf3_9"
},
{
"type": "draw_started",
"prettyname": "Meters Colored 2",
"texture_filename": "tex1_256x64_974aae7fb39dd3fe_c0adc9c4480c91c1_9"
},
{
"type": "draw_started",
"prettyname": "Meters Red",
"texture_filename": "tex1_256x64_974aae7fb39dd3fe_65f3d25263258092_9"
},
{
"type": "draw_started",
"prettyname": "Meters Overlay",
"texture_filename": "tex1_256x64_974aae7fb39dd3fe_37c76c0d16ef044d_9"
},
{
"type": "draw_started",
"prettyname": "Meter Glow",
"texture_filename": "tex1_64x64_1c31b3c28a7aef47_1c42ae23afd2f40f_9"
},
{
"type": "draw_started",
"prettyname": "Meter Teammate",
"texture_filename": "tex1_128x32_277ce02a6f60e609_30b338ab0d636ab2_9"
},
{
"type": "draw_started",
"prettyname": "Stat Bonus Ki Damage",
"texture_filename": "tex1_32x32_20bc4452577d1f49_85422f750ab42532_8"
},
{
"type": "draw_started",
"prettyname": "Stat Bonus Charge Speed",
"texture_filename": "tex1_32x32_af9478cb6ebc0b7a_5ba8f3ea380bfdcf_8"
},
{
"type": "draw_started",
"prettyname": "Stat Bonus Defense",
"texture_filename": "tex1_32x32_c7cf9020318a2959_5d41872b1fcd6199_8"
},
{
"type": "draw_started",
"prettyname": "Stat Bonus Melee Damage",
"texture_filename": "tex1_32x32_e4c8e81a7318fa25_6e21f60b0c990c98_8"
},
{
"type": "draw_started",
"prettyname": "Blast Stock Number",
"texture_filename": "tex1_128x64_00ba91db7d176946_0ecdf6905c577ddb_9"
},
{
"type": "draw_started",
"prettyname": "Blast Stock Number Glow",
"texture_filename": "tex1_128x64_53d69bea13e150ab_24a4df43c24b0515_9"
},
{
"type": "draw_started",
"prettyname": "Timer Value",
"texture_filename": "tex1_128x128_6976151439efad50_501b3bc09a6958d7_8"
},
{
"type": "draw_started",
"prettyname": "Hit Text",
"texture_filename": "tex1_128x64_9366938c3344b862_83be965a88f28a40_9"
},
{
"type": "draw_started",
"prettyname": "Hit Counter",
"texture_filename": "tex1_128x128_273f16293f4d7a40_8a29b5887317abf8_8"
},
{
"type": "draw_started",
"prettyname": "Damage Counter",
"texture_filename": "tex1_128x128_1588ef89e137fc24_1cce6b863d92c9a0_8"
},
{
"type": "draw_started",
"prettyname": "Announcer Max Power",
"texture_filename": "tex1_128x64_482f78c5dfc14eab_0e7513b77d82c9ae_9"
},
{
"type": "draw_started",
"prettyname": "Announcer First Attack",
"texture_filename": "tex1_128x64_b92d24567ae12b3a_14d61fafa74e6334_9"
},
{
"type": "draw_started",
"prettyname": "Announcer Counter",
"texture_filename": "tex1_128x64_bbdfacdc36d171ec_85047913fd8df9aa_9"
},
{
"type": "draw_started",
"prettyname": "Announcer Lock On",
"texture_filename": "tex1_128x64_3ba4b1f803afd256_93ba5384bd764946_9"
},
{
"type": "draw_started",
"prettyname": "Announcer Boost",
"texture_filename": "tex1_128x64_09d5b89f7dbf0590_2abc7134cbb2138b_9"
},
{
"type": "draw_started",
"prettyname": "Buttom Prompt Y",
"texture_filename": "tex1_32x32_15f350b2b7f46481_66860824280a89bf_8"
},
{
"type": "draw_started",
"prettyname": "Button Prompt Y Hold",
"texture_filename": "tex1_32x32_00d2fb316a25d017_a889bebad6535a02_8"
},
{
"type": "draw_started",
"prettyname": "Button Prompt Y Mash",
"texture_filename": "tex1_64x64_8934f812b54f87c4_66860824280a89bf_8"
},
{
"type": "draw_started",
"prettyname": "Buttom Prompt B",
"texture_filename": "tex1_32x32_ce62786fc1170192_70b585e07941e91c_8"
},
{
"type": "draw_started",
"prettyname": "Button Prompt B Hold",
"texture_filename": "tex1_32x32_2841b917a7c23834_bdbd0425eb2f4b52_8"
},
{
"type": "draw_started",
"prettyname": "Button Prompt B Mash",
"texture_filename": "tex1_64x64_23b176984035b44c_8204be59ecb7c469_8"
},
{
"type": "draw_started",
"prettyname": "Buttom Prompt X",
"texture_filename": "tex1_32x32_b52817e68be0e2d7_d1b283ce04ce1c7c_8"
},
{
"type": "draw_started",
"prettyname": "Button Prompt X Mash",
"texture_filename": "tex1_64x64_cb8aa5843b3cc31c_ccbcbdf9c811ed5d_8"
},
{
"type": "draw_started",
"prettyname": "Buttom Prompt A",
"texture_filename": "tex1_32x32_630cfa888a9d005a_d95570935377e345_8"
},
{
"type": "draw_started",
"prettyname": "Button Prompt A Mash",
"texture_filename": "tex1_64x64_eef97dc696b0edd6_758bf7c8a0397add_8"
},
{
"type": "draw_started",
"prettyname": "Button Prompt Hold Hand",
"texture_filename": "tex1_32x32_80eec4ab54c12b14_2fbe88721f145bc3_8"
},
{
"type": "draw_started",
"prettyname": "Button Prompt LStick Up",
"texture_filename": "tex1_32x32_56eb18736158692a_831cb2dcaee7d9c7_8"
},
{
"type": "draw_started",
"prettyname": "Button Prompt LStick Down",
"texture_filename": "tex1_32x32_4f6911885cbfd6b7_b01602f981cf7194_8"
},
{
"type": "draw_started",
"prettyname": "Button Prompt LStick Left",
"texture_filename": "tex1_32x32_de66fb7b17f39fb2_723568783ce8af39_8"
},
{
"type": "draw_started",
"prettyname": "Button Prompt LStick Right",
"texture_filename": "tex1_32x32_4a3ce686c4d7d216_d9a6c961c9883e00_8"
},
{
"type": "draw_started",
"prettyname": "Button Prompt LStick Clash N",
"texture_filename": "tex1_64x64_0e45133195ab2db7_8061698fc3e81ec4_8"
},
{
"type": "draw_started",
"prettyname": "Button Prompt LStick Clash NE",
"texture_filename": "tex1_64x64_23a7ebdcad6f294c_961413a3996f1955_8"
},
{
"type": "draw_started",
"prettyname": "Button Prompt LStick Clash E",
"texture_filename": "tex1_64x64_fc736187474b6d87_5db58dd7fedde1e4_8"
},
{
"type": "draw_started",
"prettyname": "Button Prompt LStick Clash SE",
"texture_filename": "tex1_64x64_847d2a027ba61a6d_da6f9db59fa7a52b_8"
},
{
"type": "draw_started",
"prettyname": "Button Prompt LStick Clash S",
"texture_filename": "tex1_64x64_2fd46476d6eeb2a5_8cf71f59f3f4f61d_8"
},
{
"type": "draw_started",
"prettyname": "Button Prompt LStick Clash Neutral",
"texture_filename": "tex1_64x64_e63e59943072d5ba_13e4b338982e9dbc_8"
},
{
"type": "draw_started",
"prettyname": "Button Prompt Plus",
"texture_filename": "tex1_32x32_214dbb89f41af76c_ee10bcb01a356553_8"
},
{
"type": "draw_started",
"prettyname": "Button Prompt Mash Backdrop",
"texture_filename": "tex1_64x64_c7be948af15370ea_104c0c86a865a442_9"
},
{
"type": "draw_started",
"prettyname": "Text Font Main",
"texture_filename": "tex1_512x128_b38c0db36dfc6ac3_71a440118950d6cd_8"
},
{
"type": "draw_started",
"prettyname": "Text Font Special",
"texture_filename": "tex1_512x128_b38c0db36dfc6ac3_561056e6b4e21980_8"
}
]
}
]
}

View file

@ -0,0 +1,64 @@
// Based on https://github.com/Filoppi/PumboAutoHDR
/*
[configuration]
[OptionRangeFloat]
GUIName = HDR Display Max Nits
OptionName = HDR_DISPLAY_MAX_NITS
MinValue = 80
MaxValue = 2000
StepAmount = 1
DefaultValue = 400
[OptionRangeFloat]
GUIName = Shoulder Start Alpha
OptionName = AUTO_HDR_SHOULDER_START_ALPHA
MinValue = 0
MaxValue = 1
StepAmount = 0.01
DefaultValue = 0
[OptionRangeFloat]
GUIName = Shoulder Pow
OptionName = AUTO_HDR_SHOULDER_POW
MinValue = 1
MaxValue = 10
StepAmount = 0.05
DefaultValue = 2.5
[/configuration]
*/
void main()
{
float4 color = Sample();
// Nothing to do here, we are in SDR
if (!OptionEnabled(hdr_output) || !OptionEnabled(linear_space_output))
{
SetOutput(color);
return;
}
const float hdr_paper_white = hdr_paper_white_nits / hdr_sdr_white_nits;
// Restore the original SDR (0-1) brightness (we might or might not restore it later)
color.rgb /= hdr_paper_white;
// Find the color average
float sdr_ratio = (color.r + color.g + color.b) / 3.0;
const float auto_hdr_max_white = max(HDR_DISPLAY_MAX_NITS / (hdr_paper_white_nits / hdr_sdr_white_nits), hdr_sdr_white_nits) / hdr_sdr_white_nits;
if (sdr_ratio > AUTO_HDR_SHOULDER_START_ALPHA && AUTO_HDR_SHOULDER_START_ALPHA < 1.0)
{
const float auto_hdr_shoulder_ratio = 1.0 - (max(1.0 - sdr_ratio, 0.0) / (1.0 - AUTO_HDR_SHOULDER_START_ALPHA));
const float auto_hdr_extra_ratio = pow(auto_hdr_shoulder_ratio, AUTO_HDR_SHOULDER_POW) * (auto_hdr_max_white - 1.0);
const float auto_hdr_total_ratio = sdr_ratio + auto_hdr_extra_ratio;
color.rgb *= auto_hdr_total_ratio / sdr_ratio;
}
color.rgb *= hdr_paper_white;
SetOutput(color);
}

View file

@ -1,4 +1,6 @@
// References:
/***** COLOR CORRECTION *****/
// Color Space references:
// https://www.unravel.com.au/understanding-color-spaces
// SMPTE 170M - BT.601 (NTSC-M) -> BT.709
@ -21,7 +23,7 @@ mat3 from_PAL = transpose(mat3(
float3 LinearTosRGBGamma(float3 color)
{
float a = 0.055;
const float a = 0.055;
for (int i = 0; i < 3; ++i)
{
@ -36,17 +38,336 @@ float3 LinearTosRGBGamma(float3 color)
return color;
}
/***** COLOR SAMPLING *****/
// Non filtered gamma corrected sample (nearest neighbor)
float4 QuickSample(float3 uvw, float gamma)
{
#if 0 // Test sampling range
const float threshold = 0.00000001;
float2 xy = uvw.xy * GetResolution();
// Sampling outside the valid range, draw in yellow
if (xy.x < (0.0 - threshold) || xy.x > (GetResolution().x + threshold) || xy.y < (0.0 - threshold) || xy.y > (GetResolution().y + threshold))
return float4(1.0, 1.0, 0.0, 1);
// Sampling at the edges, draw in purple
if (xy.x < 1.0 || xy.x > (GetResolution().x - 1.0) || xy.y < 1.0 || xy.y > (GetResolution().y - 1.0))
return float4(0.5, 0, 0.5, 1);
#endif
float4 color = texture(samp1, uvw);
color.rgb = pow(color.rgb, float3(gamma));
return color;
}
float4 QuickSample(float2 uv, float w, float gamma)
{
return QuickSample(float3(uv, w), gamma);
}
float4 QuickSampleByPixel(float2 xy, float w, float gamma)
{
float3 uvw = float3(xy * GetInvResolution(), w);
return QuickSample(uvw, gamma);
}
/***** Bilinear Interpolation *****/
float4 BilinearSample(float3 uvw, float gamma)
{
// This emulates the (bi)linear filtering done directly from GPUs HW.
// Note that GPUs might natively filter red green and blue differently, but we don't do it.
// They might also use different filtering between upscaling and downscaling.
float2 source_size = GetResolution();
float2 pixel = (uvw.xy * source_size) - 0.5; // Try to find the matching pixel top left corner
// Find the integer and floating point parts
float2 int_pixel = floor(pixel);
float2 frac_pixel = fract(pixel);
// Take 4 samples around the original uvw
float4 c11 = QuickSampleByPixel(int_pixel + float2(0.5, 0.5), uvw.z, gamma);
float4 c21 = QuickSampleByPixel(int_pixel + float2(1.5, 0.5), uvw.z, gamma);
float4 c12 = QuickSampleByPixel(int_pixel + float2(0.5, 1.5), uvw.z, gamma);
float4 c22 = QuickSampleByPixel(int_pixel + float2(1.5, 1.5), uvw.z, gamma);
// Blend the 4 samples by their weight
return lerp(lerp(c11, c21, frac_pixel.x), lerp(c12, c22, frac_pixel.x), frac_pixel.y);
}
/***** Bicubic Interpolation *****/
// Formula derived from:
// https://en.wikipedia.org/wiki/Mitchell%E2%80%93Netravali_filters#Definition
// Values from:
// https://guideencodemoe-mkdocs.readthedocs.io/encoding/resampling/#mitchell-netravali-bicubic
// Other references:
// https://www.codeproject.com/Articles/236394/Bi-Cubic-and-Bi-Linear-Interpolation-with-GLSL
// https://github.com/ValveSoftware/gamescope/pull/740
// https://stackoverflow.com/questions/13501081/efficient-bicubic-filtering-code-in-glsl
#define CUBIC_COEFF_GEN(B, C) \
(mat4(/* t^0 */ ((B) / 6.0), (-(B) / 3.0 + 1.0), ((B) / 6.0), (0.0), \
/* t^1 */ (-(B) / 2.0 - (C)), (0.0), ((B) / 2.0 + (C)), (0.0), \
/* t^2 */ ((B) / 2.0 + 2.0 * (C)), (2.0 * (B) + (C)-3.0), \
(-5.0 * (B) / 2.0 - 2.0 * (C) + 3.0), (-(C)), \
/* t^3 */ (-(B) / 6.0 - (C)), (-3.0 * (B) / 2.0 - (C) + 2.0), \
(3.0 * (B) / 2.0 + (C)-2.0), ((B) / 6.0 + (C))))
float4 CubicCoeffs(float t, mat4 coeffs)
{
return coeffs * float4(1.0, t, t * t, t * t * t);
}
float4 CubicMix(float4 c0, float4 c1, float4 c2, float4 c3, float4 coeffs)
{
return c0 * coeffs[0] + c1 * coeffs[1] + c2 * coeffs[2] + c3 * coeffs[3];
}
// By Sam Belliveau. Public Domain license.
// Simple 16 tap, gamma correct, implementation of bicubic filtering.
float4 BicubicSample(float3 uvw, float gamma, mat4 coeffs)
{
float2 pixel = (uvw.xy * GetResolution()) - 0.5;
float2 int_pixel = floor(pixel);
float2 frac_pixel = fract(pixel);
float4 c00 = QuickSampleByPixel(int_pixel + float2(-0.5, -0.5), uvw.z, gamma);
float4 c10 = QuickSampleByPixel(int_pixel + float2(+0.5, -0.5), uvw.z, gamma);
float4 c20 = QuickSampleByPixel(int_pixel + float2(+1.5, -0.5), uvw.z, gamma);
float4 c30 = QuickSampleByPixel(int_pixel + float2(+2.5, -0.5), uvw.z, gamma);
float4 c01 = QuickSampleByPixel(int_pixel + float2(-0.5, +0.5), uvw.z, gamma);
float4 c11 = QuickSampleByPixel(int_pixel + float2(+0.5, +0.5), uvw.z, gamma);
float4 c21 = QuickSampleByPixel(int_pixel + float2(+1.5, +0.5), uvw.z, gamma);
float4 c31 = QuickSampleByPixel(int_pixel + float2(+2.5, +0.5), uvw.z, gamma);
float4 c02 = QuickSampleByPixel(int_pixel + float2(-0.5, +1.5), uvw.z, gamma);
float4 c12 = QuickSampleByPixel(int_pixel + float2(+0.5, +1.5), uvw.z, gamma);
float4 c22 = QuickSampleByPixel(int_pixel + float2(+1.5, +1.5), uvw.z, gamma);
float4 c32 = QuickSampleByPixel(int_pixel + float2(+2.5, +1.5), uvw.z, gamma);
float4 c03 = QuickSampleByPixel(int_pixel + float2(-0.5, +2.5), uvw.z, gamma);
float4 c13 = QuickSampleByPixel(int_pixel + float2(+0.5, +2.5), uvw.z, gamma);
float4 c23 = QuickSampleByPixel(int_pixel + float2(+1.5, +2.5), uvw.z, gamma);
float4 c33 = QuickSampleByPixel(int_pixel + float2(+2.5, +2.5), uvw.z, gamma);
float4 cx = CubicCoeffs(frac_pixel.x, coeffs);
float4 cy = CubicCoeffs(frac_pixel.y, coeffs);
float4 x0 = CubicMix(c00, c10, c20, c30, cx);
float4 x1 = CubicMix(c01, c11, c21, c31, cx);
float4 x2 = CubicMix(c02, c12, c22, c32, cx);
float4 x3 = CubicMix(c03, c13, c23, c33, cx);
return CubicMix(x0, x1, x2, x3, cy);
}
/***** Sharp Bilinear Filtering *****/
// Based on https://github.com/libretro/slang-shaders/blob/master/interpolation/shaders/sharp-bilinear.slang
// by Themaister, Public Domain license
// Does a bilinear stretch, with a preapplied Nx nearest-neighbor scale,
// giving a sharper image than plain bilinear.
float4 SharpBilinearSample(float3 uvw, float gamma)
{
float2 source_size = GetResolution();
float2 inverted_source_size = GetInvResolution();
float2 target_size = GetWindowResolution();
float2 texel = uvw.xy * source_size;
float2 texel_floored = floor(texel);
float2 s = fract(texel);
float scale = ceil(max(target_size.x * inverted_source_size.x, target_size.y * inverted_source_size.y));
float region_range = 0.5 - (0.5 / scale);
// Figure out where in the texel to sample to get correct pre-scaled bilinear.
float2 center_dist = s - 0.5;
float2 f = ((center_dist - clamp(center_dist, -region_range, region_range)) * scale) + 0.5;
float2 mod_texel = texel_floored + f;
uvw.xy = mod_texel * inverted_source_size;
return BilinearSample(uvw, gamma);
}
/***** Area Sampling *****/
// By Sam Belliveau and Filippo Tarpini. Public Domain license.
// Effectively a more accurate sharp bilinear filter when upscaling,
// that also works as a mathematically perfect downscale filter.
// https://entropymine.com/imageworsener/pixelmixing/
// https://github.com/obsproject/obs-studio/pull/1715
// https://legacy.imagemagick.org/Usage/filter/
float4 AreaSampling(float3 uvw, float gamma)
{
// Determine the sizes of the source and target images.
float2 source_size = GetResolution();
float2 inverted_target_size = GetInvWindowResolution();
// Determine the range of the source image that the target pixel will cover.
// Workaround: shift the resolution by 1/4 pixel to align the results with other sampling algorithms,
// otherwise the results would be offsetted, and we'd be sampling from coordinates outside the valid range.
float2 adjusted_source_size = source_size - 0.25;
float2 range = adjusted_source_size * inverted_target_size;
float2 beg = (uvw.xy * adjusted_source_size) - (range * 0.5);
float2 end = beg + range;
// Compute the top-left and bottom-right corners of the pixel box.
float2 f_beg = floor(beg);
float2 f_end = floor(end);
// Compute how much of the start and end pixels are covered horizontally & vertically.
float area_w = 1.0 - fract(beg.x);
float area_n = 1.0 - fract(beg.y);
float area_e = fract(end.x);
float area_s = fract(end.y);
// Compute the areas of the corner pixels in the pixel box.
float area_nw = area_n * area_w;
float area_ne = area_n * area_e;
float area_sw = area_s * area_w;
float area_se = area_s * area_e;
// Initialize the color accumulator.
float4 avg_color = float4(0.0, 0.0, 0.0, 0.0);
// Prevents rounding errors due to the coordinates flooring above
const float2 offset = float2(0.5, 0.5);
// Accumulate corner pixels.
avg_color += area_nw * QuickSampleByPixel(float2(f_beg.x, f_beg.y) + offset, uvw.z, gamma);
avg_color += area_ne * QuickSampleByPixel(float2(f_end.x, f_beg.y) + offset, uvw.z, gamma);
avg_color += area_sw * QuickSampleByPixel(float2(f_beg.x, f_end.y) + offset, uvw.z, gamma);
avg_color += area_se * QuickSampleByPixel(float2(f_end.x, f_end.y) + offset, uvw.z, gamma);
// Determine the size of the pixel box.
int x_range = int(f_end.x - f_beg.x + 0.5);
int y_range = int(f_end.y - f_beg.y + 0.5);
// Workaround to compile the shader with DX11/12.
// If this isn't done, it will complain that the loop could have too many iterations.
// This number should be enough to guarantee downscaling from very high to very small resolutions.
// Note that this number might be referenced in the UI.
const int max_iterations = 16;
// Fix up the average calculations in case we reached the upper limit
x_range = min(x_range, max_iterations);
y_range = min(y_range, max_iterations);
// Accumulate top and bottom edge pixels.
for (int ix = 0; ix < max_iterations; ++ix)
{
if (ix < x_range)
{
float x = f_beg.x + 1.0 + float(ix);
avg_color += area_n * QuickSampleByPixel(float2(x, f_beg.y) + offset, uvw.z, gamma);
avg_color += area_s * QuickSampleByPixel(float2(x, f_end.y) + offset, uvw.z, gamma);
}
}
// Accumulate left and right edge pixels and all the pixels in between.
for (int iy = 0; iy < max_iterations; ++iy)
{
if (iy < y_range)
{
float y = f_beg.y + 1.0 + float(iy);
avg_color += area_w * QuickSampleByPixel(float2(f_beg.x, y) + offset, uvw.z, gamma);
avg_color += area_e * QuickSampleByPixel(float2(f_end.x, y) + offset, uvw.z, gamma);
for (int ix = 0; ix < max_iterations; ++ix)
{
if (ix < x_range)
{
float x = f_beg.x + 1.0 + float(ix);
avg_color += QuickSampleByPixel(float2(x, y) + offset, uvw.z, gamma);
}
}
}
}
// Compute the area of the pixel box that was sampled.
float area_corners = area_nw + area_ne + area_sw + area_se;
float area_edges = float(x_range) * (area_n + area_s) + float(y_range) * (area_w + area_e);
float area_center = float(x_range) * float(y_range);
// Return the normalized average color.
return avg_color / (area_corners + area_edges + area_center);
}
/***** Main Functions *****/
// Returns an accurate (gamma corrected) sample of a gamma space space texture.
// Outputs in linear space for simplicity.
float4 LinearGammaCorrectedSample(float gamma)
{
float3 uvw = v_tex0;
float4 color = float4(0, 0, 0, 1);
if (resampling_method <= 1) // Bilinear
{
color = BilinearSample(uvw, gamma);
}
else if (resampling_method == 2) // Bicubic: B-Spline
{
color = BicubicSample(uvw, gamma, CUBIC_COEFF_GEN(1.0, 0.0));
}
else if (resampling_method == 3) // Bicubic: Mitchell-Netravali
{
color = BicubicSample(uvw, gamma, CUBIC_COEFF_GEN(1.0 / 3.0, 1.0 / 3.0));
}
else if (resampling_method == 4) // Bicubic: Catmull-Rom
{
color = BicubicSample(uvw, gamma, CUBIC_COEFF_GEN(0.0, 0.5));
}
else if (resampling_method == 5) // Sharp Bilinear
{
color = SharpBilinearSample(uvw, gamma);
}
else if (resampling_method == 6) // Area Sampling
{
color = AreaSampling(uvw, gamma);
}
else if (resampling_method == 7) // Nearest Neighbor
{
color = QuickSample(uvw, gamma);
}
else if (resampling_method == 8) // Bicubic: Hermite
{
color = BicubicSample(uvw, gamma, CUBIC_COEFF_GEN(0.0, 0.0));
}
return color;
}
void main()
{
// Note: sampling in gamma space is "wrong" if the source
// and target resolution don't match exactly.
// Fortunately at the moment here they always should but to do this correctly,
// we'd need to sample from 4 pixels, de-apply the gamma from each of these,
// and then do linear sampling on their corrected value.
float4 color = Sample();
// This tries to fall back on GPU HW sampling if it can (it won't be gamma corrected).
bool raw_resampling = resampling_method <= 0;
bool needs_rescaling = GetResolution() != GetWindowResolution();
// Convert to linear space to do any other kind of operation
color.rgb = pow(color.rgb, float3(game_gamma));
bool needs_resampling = needs_rescaling && (OptionEnabled(hdr_output) || OptionEnabled(correct_gamma) || !raw_resampling);
float4 color;
if (needs_resampling)
{
// Doing linear sampling in "gamma space" on linear texture formats isn't correct.
// If the source and target resolutions don't match, the GPU will return a color
// that is the average of 4 gamma space colors, but gamma space colors can't be blended together,
// gamma neeeds to be de-applied first. This makes a big difference if colors change
// drastically between two pixels.
color = LinearGammaCorrectedSample(game_gamma);
}
else
{
// Default GPU HW sampling. Bilinear is identical to Nearest Neighbor if the input and output resolutions match.
if (needs_rescaling)
color = texture(samp0, v_tex0);
else
color = texture(samp1, v_tex0);
// Convert to linear before doing any other of follow up operations.
color.rgb = pow(color.rgb, float3(game_gamma));
}
if (OptionEnabled(correct_color_space))
{

View file

@ -1,47 +0,0 @@
// Based on https://github.com/libretro/slang-shaders/blob/master/interpolation/shaders/sharp-bilinear.slang
// by Themaister, Public Domain license
// Does a bilinear stretch, with a preapplied Nx nearest-neighbor scale,
// giving a sharper image than plain bilinear.
/*
[configuration]
[OptionRangeFloat]
GUIName = Prescale Factor (set to 0 for automatic)
OptionName = PRESCALE_FACTOR
MinValue = 0.0
MaxValue = 16.0
StepAmount = 1.0
DefaultValue = 0.0
[/configuration]
*/
float CalculatePrescale(float config_scale) {
if (config_scale == 0.0) {
float2 source_size = GetResolution();
float2 window_size = GetWindowResolution();
return ceil(max(window_size.x / source_size.x, window_size.y / source_size.y));
} else {
return config_scale;
}
}
void main()
{
float2 source_size = GetResolution();
float2 texel = GetCoordinates() * source_size;
float2 texel_floored = floor(texel);
float2 s = fract(texel);
float config_scale = GetOption(PRESCALE_FACTOR);
float scale = CalculatePrescale(config_scale);
float region_range = 0.5 - 0.5 / scale;
// Figure out where in the texel to sample to get correct pre-scaled bilinear.
// Uses the hardware bilinear interpolator to avoid having to sample 4 times manually.
float2 center_dist = s - 0.5;
float2 f = (center_dist - clamp(center_dist, -region_range, region_range)) * scale + 0.5;
float2 mod_texel = texel_floored + f;
SetOutput(SampleLocation(mod_texel / source_size));
}

View file

@ -1,12 +1,11 @@
TITLES = https://www.gametdb.com (type: Wii language: DE_unique version: 20191106234221)
TITLES = https://www.gametdb.com (type: Wii language: DE_unique version: 20230727194133)
R22J01 = FlingSmash
R23P52 = Barbie und Die Drei Musketiere
R25PWR = LEGO Harry Potter: Die Jahre 1-4
R29P52 = Millennium Championship Paintball 2009
R2AP7D = Ice Age 2 - Jetzt taut's
R2AX7D = Ice Age 2 - Jetzt taut's
R2AX7D = Ice Age 2: Jetzt taut's
R2FP70 = Fritzi Fisch und der verschwundene Schatz
R2GP99 = Fragile Dreams - Farewell Ruins of the Moon
R2YP54 = Alles gute zum Geburtstag!
R3YP70 = Sam & Max: Season Two: All-Zeit Bereit
R42P69 = Die Sims 2: Gestrandet
@ -19,6 +18,7 @@ R4LXUG = Schweine Party
R4PP69 = Die Sims 2: Haustiere
R4ZJ01 = Fatal Frame 4: Mask of the Lunar Eclipse
R55P41 = Wer wird Millionär
R59D4Q = Club Penguin: Game Day!
R5AP8P = Der Goldene Kompass
R5AX8P = Der Goldene Kompass
R5FP41 = Academy of Champions: Fussball
@ -30,10 +30,11 @@ R5QPGT = Zirkus
R5UP41 = CSI: Tödliche Absichten
R5VP41 = James Cameron's Avatar: Das Spiel
R5VX41 = James Cameron's Avatar: Das Spiel
R5XJ13 = MySims Agents
R6APPU = Mein Baby und ich
R6MPML = America´s Next Top Model
R6WP68 = AMF Bowling: World Lanes
R6XP69 = Hasbro - Spiel Mal Wieder!
R6XP69 = Hasbro: Spiel mal wieder!
R7AP69 = SimAnimals Afrika
R7BP7J = Jelly Belly - Ballistic Beans
R7SP5G = Escape the Museum
@ -55,7 +56,7 @@ R9EPNP = Brico Party: Werde Heimwerker-König
R9GPWR = Die Legende der Wächter
R9LP41 = Girls Life: Pyjama-Party
R9RPNG = Dance Party - Pop Hits
RB2PGT = Balloon Pop!
RAAE01 = Disco de Startup Wii
RB7P54 = Bully: Die Ehrenrunde
RBEP52 = Bee Movie: Das Game
RBEX52 = Bee Movie: Das Game
@ -73,7 +74,7 @@ RC3X41 = Petz: Katzenfreunde
RC8P7D = Crash: Herrscher Der Mutanten
RC9PGN = CID : The Dummy
RCCPGT = Cooking Mama 1
RCGP54 = Carnival - Die Jahrmarkt-Party
RCGP54 = Carnival: Die Jahrmarkt-Party
RCIP41 = CSI: Eindeutige Beweise
RCKPGN = Klaus Gronewalds Sports Challenge
RCLP4Q = Himmel und Huhn: Ace in Action
@ -102,19 +103,18 @@ RG5PWR = Guinness World Records: Das Videospiel
RG6P69 = Boogie Superstar
RG8P41 = Petz Sports: Wilder Hunde-Spaß
RGAP8P = Planet 51: Das Spiel
RGFP69 = Der Pate: Blackhand Edition
RGHP52 = Guitar Hero III: Legends Of Rock
RGJP7U = George Der aus dem Dschungel kam
RGQE70 = Ghostbusters: Das Videospiel
RGQP70 = Ghostbusters: Das Videospiel
RGZP70 = Godzilla Unle고질라 봉인 해제ashed
RH5PKM = Horse Life: Freunde für immer
RH6P69 = Harry Potter und der Halbblutprinz
RHKP18 = Job Island
RHNP70 = My Horse & Me - Mein Pferd und Ich
RHNP70 = My Horse & Me: Mein Pferd und ich
RHQP4Q = Hannah Montana: Welttournee im Rampenlicht
RHQX4Q = Hannah Montana: Welttournee im Rampenlicht
RHQY4Q = Hannah Montana: Welttournee im Rampenlicht
RHVPFR = Moorhuhn - Das verbotene Schloss
RHVPFR = Moorhuhn: Das verbotene Schloss
RHZP41 = Abenteuer auf dem Reiterhof: Die Pferdeflüsterin
RI2P4Q = High School Musical: Sing It!
RI3P5D = Lucas der Ameisenschreck
@ -123,9 +123,8 @@ RIBPKM = Igor: Das Spiel
RIHP8P = Der unglaubliche Hulk
RIJP69 = G.I. Joe: Geheimauftrag Cobra
RIOPSU = Schauderhafte Geschichten: Skrupellose Römer
RIPPAF = One Piece Unlimited Cruise 1 - Der Schatz unter den Wellen
RIRP8P = Iron Man: The Video Game
RIUPAF = One Piece Unlimited Cruise 2: Das Erwachen eines Helden
RIUPAF = One Piece: Unlimited Cruise 2: Das Erwachen eines Helden
RJ2P52 = 007: Ein Quantum Trost
RJ8P64 = Indiana Jones und der Stab der Könige
RJ9PFR = Think Logic Trainer - Training für Körper und Geist
@ -144,21 +143,20 @@ RLADMR = Deal Or No Deal: Der Banker Schlägt Zurück
RLAPMR = Deal Or No Deal: Der Banker Schlägt Zurück
RLBPWR = LEGO Batman: Das Videospiel
RLFP64 = Star Wars The Clone Wars: Lichtschwertduelle
RLGP64 = LEGO Star Wars: Die Komplette Saga
RLGP64 = LEGO Star Wars: Die komplette Saga
RLIP64 = LEGO Indiana Jones: Die legendären Abenteuer
RLLP70 = Go West!: Ein Abenteuer mit Lucky Luke
RLLP70 = Go West! Ein Abenteuer mit Lucky Luke
RLUX4Q = Bolt: Ein Hund für alle Fälle!
RLVP78 = Avatar - Der Herr der Elemente
RM5P7D = Die Mumie: Das Grabmal des Drachenkaisers
RM9PGM = Mushroom Men: Der Sporenkrieg
RMNDFR = Meine Tierpension
RMNPFR = Meine Tierpension
RMXP78 = MX vs ATV: Untamed
RN3P78 = SpongeBob und seine Freunde: Angriff der Spielzeugroboter
RN3X78 = SpongeBob und seine Freunde: Angriff der Spielzeugroboter
RN4P41 = ANNO: Erschaffe eine neue Welt
RNIPGT = Mind.Body.Soul: Ernährung ist Wichtig
RNNP4Q = Die Chroniken Von Narnia: Prinz Kaspian Von Narnia
RNNP4Q = Die Chroniken von Narnia: Prinz Kaspian von Narnia
RNNX4Q = Die Chroniken von Narnia: Prinz Kaspian
RNNY4Q = Die Chroniken von Narnia: Prinz Kaspian
RNNZ4Q = Die Chroniken von Narnia: Prinz Kaspian
@ -169,11 +167,11 @@ ROJP52 = Rapala: Angel-Spaß
ROLP8P = Mario & Sonic bei den Olympischen Winterspielen
ROPP41 = Jagdfieber
ROTP7J = Twin Strike: Operation Thunderstorm
ROUPAF = One Piece Unlimited Cruise 1: Der Schatz unter den Wellen
ROUPAF = One Piece: Unlimited Cruise 1: Der Schatz unter den Wellen
ROVPHM = Playmobil Circus: Manege frei!
ROYP41 = Wolkig Mit Aussicht Auf Fleischbällchen: Das Videospiel
ROYX41 = Wolkig mit Aussicht auf Fleischbällchen
RP2P69 = Smarty Pants - Das Besserwisserspiel
RP2P69 = Smarty Pants: Das Besserwisserspiel
RP6P41 = Petz: Die Affen sind los
RP7P52 = Piraten: Die Jagd nach Blackbeards Schatz
RP9PRS = Space Chimps: Affen Im All
@ -182,7 +180,7 @@ RPFP52 = Pitfall: Das große Abenteuer
RPSP4Q = Disney Prinzessinnen: Märchenhafte Reise
RPTD52 = Tierbabys - Mein kleiner Welpe
RPVPKM = Mein erstes Katzenbaby
RPWX41 = Prince of Persia: Die Vergessene Zeit
RPWX41 = Prince of Persia: Die vergessene Zeit
RPYP9B = Pangya! Golf with Style
RQ4P78 = SpongeBob Schwammkopf: Die Kreatur aus der Krossen Krabbe
RQ8P08 = Moto GP
@ -192,7 +190,6 @@ RQKP41 = Kirmes Party
RQNPWR = Scooby-Doo! Geheimnisvolle Abenteuer
RQOP69 = Spore Helden
RQTP6V = Agatha Christie: Und dann gabs keines mehr
RQWPG9 = Puzzle Quest - Challenge of the Warlords
RQXP70 = Asterix bei den Olympischen Spielen
RRCP52 = Barbie Pferdeabenteuer: Im Reitercamp
RRGP52 = Madagascar 2
@ -214,7 +211,7 @@ RSND69 = Die Simpsons: Das Spiel
RSNP69 = Die Simpsons: Das Spiel
RSNX69 = Die Simpsons: Das Spiel
RSRP8P = Sonic und die Geheimen Ringe
RT3P54 = Rockstar Games präsentiert Tischtennis
RT3P54 = Rockstar Games präsentiert: Tischtennis
RT6PKM = Das Zauberkarussell
RTEHMR = Tierliebe Groß Geschrieben
RTEPFR = Meine Tierarztpraxis
@ -230,7 +227,7 @@ RU5X4Q = Küss den Frosch
RU7X5G = Nachts im Museum 2: Das Spiel
RUBP7N = Die ultimative Brettspiele-Sammlung
RUCXRT = RTL Winter Sports 2008
RUEP4Q = G-Force - Agenten mit Biss
RUEP4Q = G-Force: Agenten mit Biss
RUEX4Q = G-Force: Agenten mit Biss
RUEY4Q = G-Force: Agenten mit Biss
RUFP99 = Rune Factory Frontier
@ -262,7 +259,7 @@ RWAZ78 = WALL•E: Der Letzte räumt die Erde auf
RWFH41 = Mein Wortschatz-Coach: Verbessere dein Ausdrucksvermögen
RWMP78 = Worms: Odyssee im Wurmraum
RWSP8P = Mario & Sonic bei den Olympischen Spielen
RX2P70 = My Horse & Me 2: Mein Pferd und Ich
RX2P70 = My Horse & Me 2: Mein Pferd und ich
RX4PMT = Casper's Schreckensschule: Der gruselige Sporttag
RXDD4Q = Disney Th!nk - Das Schnelldenker-Quiz
RXDX4Q = Disney Th!nk: Schnelldenkerquiz
@ -292,7 +289,7 @@ S2ZP52 = Zhu Zhu Pets: Lustige Waldtiere
S3EP78 = Barbie: Fashionista Inc.
S3MP69 = Die Sims 3
S3PP4Q = Disney Princess: Mein Märchenhaftes Abenteuer
S5BPKM = Zurück in die Zukunft - Das Spiel
S5BPKM = Zurück in die Zukunft: Das Spiel
S5MPVZ = Monster High: Aller Anfang ist schwer
S6BP4Q = Merida - Legende der Highlands
S6IP78 = Disney Prinzessin: Bezaubernde Geschichten
@ -301,7 +298,8 @@ S7BP69 = Trivial Pursuit: Wetten & Gewinnen
S7FPGT = Zumba Kids: Die ultimate Zumba tanz-party
S7SP41 = Die Schlümpfe Party Pack
SA3P5G = Alvin und Die Chipmunks 3: Chip Brunch
SALP4Q = Alice Im Wunderland
SA3XGT = Alvin und Die Chipmunks 3: Chip Brunch
SALP4Q = Alice im Wunderland
SAOP78 = Monster High: Monsterkrasse Highschool-Klasse!
SAOXVZ = Monster High: Monsterkrasse Highschool-Klasse!
SAVX5G = Alvin und die Chipmunks 2: Ohren zu und durch
@ -317,7 +315,7 @@ SBVP78 = SpongeBob Schwammkopf: Volle Kanne Vollgas
SCIP41 = CSI: Tödliche Verschwörung
SCJP4Q = LEGO Pirates of the Caribbean: Das Videospiel
SCRPJH = Chicken Riot: Die wilde Hühnerjagd
SCUPFR = Moorhuhn - Jahrmarkt-Party
SCUPFR = Moorhuhn: Jahrmarkt-Party
SCWP41 = Mein Fitness-Coach: Dance Workout
SCYP4Q = Cars 2: Das Videospiel
SCYY4Q = Cars 2: Das Videospiel
@ -327,7 +325,7 @@ SDFP4Q = Disney Sing It: Filmhits
SDGP4Q = Disney Channel: All Star Party Games
SDIP4Q = Disney Sing It: Pop Party
SDLP78 = Doods großes Abenteuer
SDMPAF = Ich - Einfach Unverbesserlich
SDMPAF = Ich - Einfach unverbesserlich
SDPP54 = Dora - Das große Geburtstagsabenteuer
SDUP41 = Die Schlümpfe: Dance Party
SDWP18 = Der Schattenläufer und die Rätsel des dunklen Turms
@ -342,6 +340,7 @@ SFIP01 = Mystery Case Files: Der Fall Malgrave
SFPPFR = Fussball Fan Party
SFRDRV = Fit for Fun
SFRPXT = Fit for Fun
SFWP69 = FIFA Fußball-Weltmeisterschaft Südafrika 2010
SFWX69 = FIFA Fussball-Weltmeisterschaft Südafrika 2010
SFXPKM = X Factor
SFYPAF = Family Party 90
@ -360,7 +359,7 @@ SHDP52 = Drachenzähmen leicht gemacht
SHHP69 = Harry Potter und die Heiligtümer des Todes - Teil 1
SHMPLR = Henry der Schreckliche
SHOXKR = Hugo - Zauberei im Trollwald
SIAP52 = Ice Age 4: Voll Verschoben - Die Arktischen Spiele
SIAP52 = Ice Age 4: Voll verschoben - Die arktischen Spiele
SIIP8P = Mario & Sonic bei den Olympischen Spielen London 2012
SIJP52 = iCarly 2: Ab in die Klicke!
SJ2PWR = Scooby-Doo! und der Spuk im Sumpf
@ -376,7 +375,7 @@ SMFP4Q = Phineas und Ferb: Quer durch die 2. Dimension
SMGP78 = Megamind: Das Bündnis von Team Mega
SMJP52 = Monster Jam: Pfad der Zerstörung
SNYPVZ = Monster High: 13 Wünsche
SOBD7K = Bibi Blocksberg - Das große Hexenbesen-Rennen 2
SOBD7K = Bibi Blocksberg: Das große Hexenbesen-Rennen 2
SONDMR = Meine ersten Mitsing-Lieder
SONFMR = Meine ersten Mitsing-Lieder
SOSPAF = Turbo: Die Super-Stunt-Gang
@ -385,9 +384,11 @@ SP8P78 = Die Pinguine aus Madagaskar: Dr. Seltsam kehrt zurück
SPBPGT = Richtig Buchstabieren Party
SPDP52 = Spider-Man: Dimensions
SPXP41 = Prince of Persia: Die Vergessene Zeit
SQFPGT = Phineas und Ferb: Suche nach Super-Sachen
SQQPVZ = Disney Planes 2: Immer im Einsatz
SQTPML = Das Duell - Männer vs. Frauen: Partyspaß Total!
SR4P41 = Raving Rabbids: Die verrückte Zeitreise
SR5P41 = Raving Rabbids: Party Collection
SRPP4Q = Disney Rapunzel: Neu verföhnt
SRXP52 = Generator Rex: Providence Agent
SS8P78 = SpongeBob Schwammkopf: verflixt und zugemalt
@ -397,7 +398,6 @@ SSCFWR = Scene It? Ganz großes Kino!
SSCIWR = Scene It? Ganz großes Kino!
SSCPWR = Scene It? Ganz großes Kino!
SSCSWR = Scene It? Ganz großes Kino!
SSEDNG = We Sing Vol. 2
SSEPNG = We Sing Vol. 2
SSHPHH = Sherlock Holmes: Das Geheimnis des silbernen Ohrrings
ST5P52 = Transformers: Mission auf Cybertron
@ -412,43 +412,159 @@ SU5PVZ = Monster High: Labyrinth-Skaten
SU7PAF = Die Hüter des Lichts
SV3PAF = Madagascar 3: Flucht durch Europa
SV7PVZ = Die Pinguine aus Madagascar
SVDP52 = Spongebob Schwammkopf: Planktons Fiese Robo-Rache
SVDP52 = SpongeBob Schwammkopf: Planktons Fiese Robo-Rache
SVMP01 = Super Mario All-Stars: 25 Jahre Jubiläumsedition
SVQEVZ = Barbie und ihre Schwestern: Die Rettung der Welpen
SVQPVZ = Barbie und ihre Schwestern: Die Rettung der Welpen
SVVPAF = Die Croods: Steinzeit Party!
SW3PKM = Eurosport Winter Stars
CS4P00 = SingItStar NRJ Music Tour
RMCPCA = Mario Kart Wii (Katalanische Übersetzung)
SDNP01 = New SUPER DODO BROS wii
SIS1OH = SingItStar Custom: Volume 1
SISACD = SingItStar AC/DC
SISPOH = SingItStar Custom: Party
W2CP = Gehirntraining
W2FP = Physiofun - Balance Training
W2GD = Phoenix Wright Ace Attorney: Justice for All (Deutsche Version)
W2GP = Phoenix Wright Ace Attorney: Justice for All
W2MP = Blaster Master: Overdrive
W2PP = Physiofun - Beckenboden Training
W3GD = Phoenix Wright Ace Attorney 3: Trials And Tribulations
W3KP = ThruSpace: High Velocity 3D Puzzle
W3MP = Die Drei Musketiere Einer für alle!
W4AP = Arcade Sports: Air Hockey, Bowling, Pool, Snooker
W6BP = Eco-Shooter: Plant 530
W72P = Successfully Learning German Year 3
W73P = Lernerfolg Grundschule Deutsch Klasse 4
W74P = Lernerfolg Grundschule Deutsch Klasse 5
W7IP = Lernerfolg Grundschule Deutsch Klasse 2
W8CP = Bit.Trip Core
W8WP = Happy Holidays Halloween
W9BP = Big Town Shoot
W9RP = Happy Holidays Christmas
WA4P = WarioWare: Do It Yourself - Showcase
WA7P = Toribash Violence Perfected
WA8P = Art Style: Penta Tentacles
WAEP = Around the world
WAFP = Airport Mania: First Flight
WAHP = Trenches: Generals
WALP = Art Style: light trax
WAOP = The Very Hungry Caterpillar´s ABC
WB2P = Strong Bad Episode 4: Dangeresque 3
WB3P = Strong Bad Episode 5: 8-bit is Enough
WBEP = Beer Pong: Frat Party Games
WBFP = Bit.Trip Fate
WBGP = Bang Attack
WBPP = PLÄTTCHEN - twist 'n' paint
WBRP = Pirates: The Key of Dreams
WBXP = Strong Bad Episode 1: Homestar Ruiner
WBYP = Strong Bad Episode 2: Strong Badia - The Free
WBZP = Strong Bad Episode 3: Baddest of the Bands
WCHP = Chess Challenge
WCJP = Cocoto: Platform Jumper
WCKP = chick chick BOOM
WCSP = CueSports: Snooker vs Billiards
WD9P = Castlevania: The Adventure ReBirth
WDEP = Magic Destiny Astrological Games
WDFP = Defend your Castle
WDHP = Art Style: ROTOHEX
WDMP = Dr. Mario & Bazillenjagd
WDPP = Dr. Mario & Germ Buster (Friend Battle Demo)
WEMP = Aha! I Got It! Escape Game
WETP = Bilderbuch-Spiele: Ein Abenteuer zum Aufklappen
WF2P = FFCC: My Life as a Darklord
WF4P = Final Fantasy IV: The After Years
WFCP = FFCC: My Life as a King
WFQP = Frogger: Hyper Arcade Edition
WFTP = Fish'em All!
WFVP = Football Up
WFWP = Flowerworks: Follie's Adventure
WFYP = Family Games Pen & Paper Edition
WGDP = Gradius Rebirth
WGFP = Girlfriends Forever Magic Skate
WGGP = Gabrielle's Ghostly Groove: Monster Mix
WGPP = Zenquaria Virtuelles Aquarium
WGSD = Phoenix Wright: Ace Attorney (Deutsche Version)
WGSF = Phoenix Wright: Ace Attorney (French Version)
WGSP = Phoenix Wright: Ace Attorney
WHEE = Heracles: Chariot Racing
WHEP = Heracles: Chariot Racing
WHFP = Heavy Fire: Special Operations
WHRP = Heron: Steam Machine
WHUP = Geisterhaus-Partyschreck
WHWP = HoopWorld: BasketBrawl
WICP = NyxQuest: Kindred Spirits
WIDP = Dracula: Undead Awakening
WIEP = Tales of Monkey Island Chapter 3: Lair of the Leviathan
WILP = Tales of Monkey Island Chapter 1: Launch of the Screaming Narwhal
WINP = Das Verrückte Labyrinth
WIRP = Tales of Monkey Island Chapter 5: Rise Of The Pirate God
WISP = Tales of Monkey Island Chapter 2: The Siege of Spinner Cay
WITP = Aha! I Found It! Hidden Object Game
WIYP = Tales of Monkey Island Chapter 4: The Trial and Execution of Guybrush Threepwood
WJKP = Jewel Keepers: Easter Island
WKBP = You, Me and the Cubes
WKFP = Kung Fu Funk - Everybody Is Kung Fu Fighting
WKKP = Pop-Up Pirates!
WKRP = Karate Phants: Gloves of Glory
WKWP = Adventure on LOST ISLAND: Hidden Object Game
WLEE = Lernen mit den PooYoos: Teil 1
WLEP = Lernen mit den PooYoos: Teil 1
WLNE = Lernen mit den PooYoos: Teil 2
WLNP = Lernen mit den PooYoos: Teil 2
WLOP = LostWinds: Winter of the Melodias
WLZP = lilt line
WM7P = Anima Ark of Sinners
WMBP = MaBoShi: Drei-Formen-Action
WMCP = Monsteca Corral - Monsters Vs. Robots
WMJP = Dive - The Medes Islands Secret
WMSP = Enjoy your massage!
WN9E = Military Madness: Nectaris
WN9P = Military Madness: Nectaris
WNEE = Penguins & Friends Hey! That's My Fish!
WNEP = Penguins & Friends Hey! Thats my Fish!
WNVP = Neves Plus: Phantheon of Tangrams
WOBP = Art Style: ORBIENT
WOTP = Overturn: Mecha Wars
WP3P = Pearl Harbor Trilogy 1941: Red Sun Rising
WP4P = Learning with the PooYoos: Episode 3
WPJP = Pucca Mission: Küsse
WPVP = The Tales of Bearsworth Manor - Chaotic Conflicts
WZZP = The Tales of Bearsworth Manor - Puzzling Pages
WPKP = Texas Hold'Em Poker
WPQP = Protöthea
WPRP = Art Style: CUBELLO
WPVE = The Tales of Bearsworth Manor: Chaotic Conflicts
WREP = Racers Islands Crazy Arenas
WRIP = Rainbow Islands: Towering Adventure!
WRJP = Racers Islands - Crazy Racers
WRLP = FAST Racing League
WRRP = Robin Hood: The Return Of Richard
WRUP = Bit.Trip Runner
WSGP = Pop Them, Drop Them SAMEGAME
WSNP = Sonic The Hedgehog 4 Episode I
WSUP = Shootanto: Evolutionary Mayhem
WTEP = Tales of Elastic Boy Mission 1
WTFP = Bit.Trip Flux
WTMP = Adventure Island: The Beginning
WTRP = Bit.Trip Beat
WTWP = Fenimore Fillmore: The Westerner
WTXP = Texas Holdem Tournament
WU2P = Successfully Learning Mathematics Year 3
WU3P = Successfully Learning Mathematics Year 4
WU4P = Successfully Learning Mathematics Year 5
WUIP = Successfully Learning Mathematics Year 2
WVBP = Bit.Trip Void
WVOP = Rock'n Roll Climber
WVSP = Gods Vs Humans
WVUP = Mr Bumblebee Racing Champion
WW2P = Where's Wally? Fantastic Journey 2
WW3P = Where's Wally? Fantastic Journey 3
WWIP = Where's Wally? Fantastic Journey 1
WWRP = Excitebike: World Challenge
WWXP = Paper Wars Cannon Fodder
WXBP = Ben 10: Alien Force - The Rise of Hex
WYIP = escapeVektor: Chapter 1
WYSP = Yard Sale Hidden Treasures Sunnyville
WZIP = Rubik's Puzzle Galaxy: RUSH
XIBP = Fish em All Demo
XICP = Gods vs Humans Demo
XIDP = Racers Islands Crazy Racers Demo
@ -464,8 +580,121 @@ XIUP = Soccer Bashi Demo
XIVP = Mix Superstar Demo
XIZP = 3D Pixel Racing Demo
XJEP = Aya and the Cubes of Light Demo
FA9P = Zelda II: The Adventure of Link
FB2L = Super Mario Bros.: The Lost Levels
FBKP = Teenage Mutant Ninja Turles
FC8P = Castlevania II: Simon's Quest
FCSP = Probotector II: Return of the Evil Forces
FDGP = Ghosts'n Goblins
FDRP = Skate or Die
FEML = Bio Miracle Bokutte UPA
FEQP = Castlevania III Dracula's Curse
FERM = Startropics II: Zoda's Revenge
FF5P = Double Dragon II: The Revenge
FFEP = A Boy and His Blob: Trouble on Blobolonia
FFPP = Ufouria: THE SAGA
FFUP = Adventure Island 2
FFVM = S.C.A.T.: Special Cybernetic Attack Team
JA4P = Super Ghouls'n Ghosts
JABL = Marios Super Picross
JADD = The Legend of Zelda: A Link to the Past
JADE = The Legend of Zelda: A Link to the Past
JADF = The Legend of Zelda: A Link to the Past
JAFD = SimCity
JAHP = R-TYPE III: The Third Lightning
JAJP = Street Fighter II: The World Warrior
JALP = Super Probotector: Alien Rebels
JAZD = The Legend of the Mystical Ninja
JBBP = Super Street Fighter II: The New Challengers
JBDD = Donkey Kong Country 2: Diddy's Kong-Quest
JBDE = Donkey Kong Country 2: Diddy's Kong Quest
JBIP = Street Fighter II Turbo: Hyper Fighting
JBPP = Donkey Kong Country 3: Dixie Kongs Double Trouble
JCAL = DoReMi Fantasy - Milons DokiDoki Adventure
JCBM = Super Mario RPG: Legend of the Seven Stars
JCCP = Kirbys Fun Pak
JCDM = Kirbys Dream Land 3
JCJP = Super Punch Out!!
JCKP = Space Invaders -The Original Game-
JCTM = Ogre Battle: The March of the Black Queen
JD3P = SUPER E.D.F.: Earth Defense Force
JD8E = Super Adventure Island II
JDJP = Super Star Wars: The Empire Strikes Back
JDLP = Super Star Wars: Return of the Jedi
JDWP = Aero The Acrobat
JDZD = Mystic Quest Legend
NACE = The Legend of Zelda: Ocarina of Time
NACP = The Legend of Zelda: Ocarina of Time
NADE = Lylat wars
NAJN = Sin and Punishment
NAKS = Pokémon Snap
NAME = Kirby 64: The Crystal Shards
NAMP = Kirby 64: The Crystal Shards
NAND = Pokémon Puzzle League
NAOE = 1080° Snowboarding
NAOP = 1080°: TenEighty Snowboarding
NARE = The Legend of Zelda: Majora's Mask
NARP = The Legend of Zelda: Majora's Mask
NAYE = Ogre Battle 64: Person of Lordly Caliber
NAYM = Ogre Battle 64: Person of Lordly Caliber
LALP = Fantasy Zone II
LANP = Alex Kidd: The Lost Stars
LAPP = Wonder Boy III: The Dragon's Trap
MA8P = Ecco: The Tides of Time
MAHP = Sonic the Hedgehog
MALP = Bonanza Bros.
MAOP = Bio-Hazard Battle
MAVP = Wonder Boy In Monster World
MAXP = Alex Kidd In The Enchanted Castle
MB6P = Shining Force II
MBBP = Sonic the Hedgehog 2
MBFP = Shinobi III: Return of the Ninja master
MBIP = Landstalker: Die Schätze von König Nolo
MBJP = Ghouls'n Ghosts
MBLP = ESWAT City Under Siege
MBMP = Sonic the Hedgehog 3
MBUP = Sonic 3D: Flickies' Island
MBWM = Columns III: Revenge of Columns
MC3P = Super Street Fighter II: The New Challengers
MCCP = Phantasy Star III: Generations of Doom
MCHM = MUSHA
MCLP = Street Fighter II: Special Champion Edition
MCQP = Boogerman - A Pick and Flick Adventure
MCRP = Wolf of the Battlefield: MERCS
MCSP = Wonder Boy III: Monster Lair
MCVP = Pitfall: The Mayan Adventure
MCZP = Shanghai II Dragon's Eye
PAAP = Bomberman'93
PAGL = Bomberman'94
PARL = Detana Twin Bee
PAWP = Galaga'90
PBEP = Motoroader
PBIP = Bonk III: Bonk's Big Adventure
PBSP = Chew Man Fu
PBWP = Air 'Zonk'
PCSL = Digital Champ: Battle Boxing
PDJL = Street Fighter II': Champion Edition
QA3P = SimEarth: The Living Planet
QAAP = Super Air Zonk
QABP = Ys Book I & II
QADL = Gradius II: Gofer no Yabou
QAPL = Castlevania: Rondo of Blood
EA5P = Fatal Fury 3: Road To The Final Victory
EA7P = Samurai Shodown IV: Amakusa's Revenge
EA8M = Iron Clad
EAIP = Top Hunter
EBDP = Magical Drop 3
EBFP = Spin master
EBSP = The Path of the Warrior: Art of Fighting 3
ECAP = Real Bout Fatal Fury 2: The Newcomers
ECGP = Shock Troopers: 2nd Squad
E54P = GHOSTS'N GOBLINS
E55P = Commando
E57P = SonSon
E6PP = NINJA GAIDEN
C93P = The Last Ninja 2
C96P = Summer Games 2
C9IP = Cybernoid
HAAA = Fotokanal
HABA = Wii-Shop-Kanal
HACA = Mii-Kanal
@ -473,10 +702,25 @@ HACK = Mii-Kanal
HADE = Internet Kanal
HADP = Internet-Kanal
HAFP = Wetterkanal
HAGA = Nachrichtenkanal
HAGE = Nachrichtenkanal
HAGJ = Nachrichtenkanal
HAGP = Nachrichtenkanal
HAJP = Meinungskanal
HAPP = Mii-Wettbewerbskanal
HATP = Nintendo-Kanal
HAVP = Glückstagskanal
HAWP = Metroid Prime 3 Preview
HAYA = Fotokanal
HCAP = Jam with the Band Live
HCFE = Wii Speak-Kanal
HCFP = Wii Speak-Kanal
HCMP = Kirby TV-Kanal
HCRE = The Legend of Zelda: Skyward Sword - Speicherdaten-Update-Kanal für
HCRP = The Legend of Zelda: Skyward Sword - Speicherdaten-Update-Kanal für
RMCP = Mario Kart-Kanal
OHBC = Homebrew-Kanal
G2FD78 = Tak 2: Der Stab der Träume
G3AD69 = Der Herr der Ringe: Das dritte Zeitalter
G3DX6L = Carmen Sandiego: Das Geheimnis der gestohlenen Trommeln
G3MP41 = Der Anschlag
@ -484,34 +728,35 @@ G4CP54 = Charlie und die Schokoladen-fabrik
G4MP69 = Die Sims: Brechen Aus
G4OP69 = Die Sims 2: Haustiere
G4ZP69 = Die Sims 2
G6FD69 = FIFA Fussball-Weltmeisterschaft 2006
G8MP01 = Paper Mario: Die Legende Vom Äonentor
G6FD69 = FIFA Fußball-Weltmeisterschaft Deutschland 2006
G8MP01 = Paper Mario: Die Legende vom Äonentor
G9TD52 = Grosse Haie: Kleine Fische
GAZD69 = Harry Potter und der Gefangene von Askaban
GC3D78 = Scooby-Doo!: Fluch der Folianten
GCBP7D = Crash Bandicoot: Der Zorn Des Cortex
GC3D78 = Scooby-Doo! Fluch der Folianten
GCBP7D = Crash Bandicoot: Der Zorn des Cortex
GCGP41 = 3 Engel Für Charlie: Volle Power
GCIP69 = Die Sims
GCQD7D = Buffy im Bann der Dämonen: Chaos Bleeds
GDKPA4 = Disney´s Sports Fußball
GDOP41 = Disney's Donald Duck Phantomias: Platyrhynchos Kineticus
GEND69 = James Bond 007: Alles Oder Nichts
GDIX7D = Die Hard - Stirb Langsam: Vendetta
GDKPA4 = Disney Sports: Fußball
GDOP41 = Disneys Donald Duck: Phantomias - Platyrhynchos Kineticus
GEND69 = James Bond 007: Alles oder Nichts
GENP69 = 007: Alles Oder Nichts
GF4D52 = Die Fantastischen Vier
GFAD69 = FIFA Football 2003
GFSD69 = FIFA Fussball Weltmeisterschaft 2002
GGVD78 = Der SpongBob Schwammkopf Film
GFHP6V = Böse Nachbarn
GFSD69 = FIFA Fußball-Weltmeisterschaft 2002
GGVD78 = SpongBob Schwammkopf: Der Film
GH4D69 = Harry Potter und der Feuerkelch
GH5D52 = Ab Durch Die Hecke
GHBP7D = Der Hobbit
GHCD4Q = Himmel und Huhn
GHLY69 = Harry Potter und der Stein der Weisen
GHSY69 = Harry Potter Und Die Kammer Des Schreckens
GHSY69 = Harry Potter und die Kammer des Schreckens
GHVP08 = Disney's Tricky Micky
GIAP7D = Ice Age 2 : Jetzt Taut's
GIAP7D = Ice Age 2: Jetzt Taut's
GICD78 = Die Unglaublichen
GIHD78 = Scooby-Doo! Nacht der 100 Schrecken
GIQY78 = Die Unglaublichen: Der Angriff Des Tunnelgräbers
GIQY78 = Die Unglaublichen: Der Angriff des Tunnelgräbers
GJND78 = Jimmy Neutron: Der mutige Erfinder
GJUD78 = Tak und die Macht des Juju
GKBPAF = Baten Kaitos: Die Schwingen der Ewigkeit und der verlorene Ozean
@ -521,8 +766,9 @@ GL5X4F = Lego Star Wars : Das Videospiel
GL7P64 = LEGO Star Wars II: Die klassische Trilogie
GLCD52 = Lemony Snicket: Rätselhafte Ereignisse
GLHPG9 = Flutsch und weg
GLOD69 = Der Herr Der Ringe: Die Zwei Türme
GLOD69 = Der Herr der Ringe: Die zwei Türme
GLVD4Q = Die Chroniken von Narnia: Der König von Narnia
GLVP4Q = Die Chroniken von Narnia: Der König von Narnia
GLZD69 = 007: Liebesgrüsse aus Moskau
GMNP78 = Die Monster AG : Monster Ball
GNED78 = Findet Nemo
@ -533,8 +779,12 @@ GPHD52 = Pitfall: Die verlorene Expedition
GPLD9G = Disney's Ferkels Grosses Abenteuer - Spiel
GPQP6L = The Powerpuff Girls: Kampf den Gurkenschurken
GPXP01 = Pokémon Box: Rubin & Saphir
GQ4D78 = SpongeBob Schwammkopf: Die Kreatur aus der krossen Krabbe
GQQD78 = Nickelodeon SpongeBob Schwammkopf: Film ab!
GQ4D78 = SpongeBob Schwammkopf: Die Kreatur aus der Krossen Krabbe
GQQD78 = SpongeBob Schwammkopf: Film ab!
GQQE78 = SpongeBob Schwammkopf: Film ab!
GQQF78 = SpongeBob Schwammkopf: Film ab!
GQQH78 = SpongeBob Schwammkopf: Film ab!
GQQP78 = SpongeBob Schwammkopf: Film ab!
GQWX69 = Harry Potter: Quidditch - Weltmeisterschaft
GR9P6L = Die Herrschaft Des Feuers
GSKP7D = The Scorpion King : Aufstieg des Akkadiers

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,4 +1,4 @@
TITLES = https://www.gametdb.com (type: Wii language: FR_unique version: 20191106234238)
TITLES = https://www.gametdb.com (type: Wii language: FR_unique version: 20230727194148)
R22J01 = FlingSmash
R23P52 = Barbie et les Trois Mousquetaires
R25PWR = LEGO Harry Potter : Années 1 à 4
@ -28,13 +28,11 @@ R3YP70 = Sam & Max : Saison 2 : Au-Delà du Temps et de l'Espace
R42P69 = Les Sims 2 : Naufragés
R43P69 = EA Sports Active
R48P7D = Les Chroniques De Spiderwick
R49P01 = Donkey Kong : Jungle Beat
R4BPGT = Baby Foot
R4EP01 = Endless Ocean 2 : Aventuriers des Fonds Marins
R4PP69 = Les Sims 2 : Animaux & Cie
R4ZJ01 = Project Zero 4 - Mask of the Lunar Eclipse
R54FMR = Des Chiffres & des Lettres
R55F41 = Qui Veut Gagner Des Millions : 1ère Edition
R55P41 = Qui Veut Gagner Des Millions : 1ère Edition
R57FMR = Questions pour un champion : Le jeu officiel
R5AP8P = À la Croisée des Mondes : La Boussole d'Or
@ -48,6 +46,7 @@ R5TP69 = Grand Chelem Tennis
R5UP41 = Les Experts: Préméditation
R5VP41 = James Cameron's Avatar : The Game
R5VX41 = James Cameron's Avatar : The Game
R5XJ13 = MySims Agents
R5YD78 = All Star Pom Pom Girl 2
R5YP78 = All Star Pom Pom Girl 2
R62P4Q = Disney Sing It : Pop Hits
@ -81,7 +80,6 @@ R8RP41 = Arthur et la Vengeance de Maltazard
R8SP41 = Vacances Sports Party
R8UXMR = Adibou et les saisons magiques
R8VP41 = Planète Nature : Au Secours des Animaux Sauvages
R92P01 = Nouvelle Façon de Jouer ! Pikmin 2
R94PMR = Total Dérapage
R94XMR = Total Dérapage : Prêts pour le Grand Frisson ?
R9BPMT = Bob le bricoleur : On s'amuse comme des fous
@ -93,6 +91,7 @@ R9IP01 = Nouvelle Façon de Jouer ! Pikmin
R9JP69 = The Beatles : Rock Band
R9LP41 = Girls Life: Pyjama Party
R9SPPL = Sudoku Ball : Detective
RAAE01 = Disque Startup du Wii
RB4P08 = Resident Evil 4 : Wii Edition
RB4X08 = Resident Evil 4 : Wii Edition
RB5P41 = Brothers in Arms : Earned in Blood
@ -199,7 +198,6 @@ RH3P4Q = High School Musical 3 Dance! Nos Années Lycée
RH5PKM = Horse Life : Amis pour la vie
RH6P69 = Harry Potter et le Prince de Sang-Mêlé
RH8P4F = Tomb Raider : Underworld
RH8X4F = Tomb Raider : Underworld
RHCP52 = The History Channel : Battle for the Pacific
RHGP6Z = Agent Hugo : Lemoon Twist
RHKP18 = Job Island
@ -226,9 +224,8 @@ RIHP8P = L'Incroyable Hulk
RIJP69 = G.I. JOE : Le réveil du Cobra
RINP08 = Dead Rising : Chop Till You Drop
RIOPSU = Horribles Histoires : les Redoutables Romains
RIPPAF = One Piece Unlimited Cruise 1 : Le Trésor sous les Flots
RITFMR = Intervilles : Le Jeu Officiel
RIUPAF = One Piece - Unlimited Cruise 2 : L'Eveil d'un Héros
RIUPAF = One Piece: Unlimited Cruise 2: L'Eveil d'un Héros
RJ2P52 = 007 : Quantum of Solace
RJ4PRM = Naissance de Rome : Sur les Traces de César
RJ7FWP = Télé 7 Jeux : Mots Fléchés
@ -270,7 +267,6 @@ RLNFMR = Koh-Lanta
RLNHMR = Koh-Lanta
RLNIMR = Koh-Lanta
RLRP4F = Tomb Raider : Anniversary
RLTPNR = London Taxi : Rush Hour
RLTXUG = London Taxi : Rush Hour
RLUP4Q = Volt : Star Malgré Lui
RLUX4Q = Volt : Star Malgré Lui
@ -306,7 +302,6 @@ RNPP69 = Need for Speed : ProStreet
RNPX69 = Need for Speed : ProStreet
RNSF69 = Need for Speed : Carbon
RNSP69 = Need for Speed : Carbon
RNSX69 = Need for Speed : Carbon
RNXPDA = Naruto : Clash of Ninja Revolution European Version
RO7P7D = The Legend of Spyro : The Eternal Night
RO8P7D = La Légende de Spyro : La Naissance d'un Dragon
@ -319,7 +314,7 @@ ROLP8P = Mario & Sonic aux Jeux Olympiques d'Hiver
RONPG9 = Onechanbara : Bikini Zombie Slayers
ROPP41 = Les Rebelles de la Fôret
ROTP7J = Twin Strike : Operation Thunder
ROUPAF = One Piece - Unlimited Cruise 1 : Le Trésor sous les Flots
ROUPAF = One Piece: Unlimited Cruise 1: Le Trésor sous les Flots
ROVE6U = Playmobil Circus : Tous en Piste
ROVPHM = Playmobil Circus: Tous en Piste
ROYP41 = Tempête de boulettes géantes : Le Jeu Vidéo
@ -410,7 +405,7 @@ RTNP41 = Tenchu : Shadow Assassins
RTUFKM = Secret Files : Tunguska
RTUPKM = Secret Files : Tunguska
RTVP64 = Thrillville : Le parc en folie
RTYP01 = Wii Echecs
RTYP01 = Wii Échecs
RTZE08 = Zack & Wiki : Le Trésor de Barbaros
RTZJ08 = Zack & Wiki : Le Trésor de Barbaros
RTZP08 = Zack & Wiki : Le Trésor de Barbaros
@ -501,7 +496,6 @@ RY2P41 = Rayman Contre les Lapins Encore plus Crétins
RY3P41 = Rayman Prod' présente : The Lapins Crétins Show
RYBP69 = Boom Blox : Smash Party
RYDP6V = Pet Pals: Vétérinaire
RYGP99 = Rygar : The Battle of Argus
RYJPTV = Princesse Lillifee : La fée magique
RYKEAF = Family Ski & Snowboard
RYWP01 = Cérébrale Académie
@ -534,12 +528,14 @@ S6BP4Q = Rebelle
S6BX4Q = Rebelle
S6IP78 = Disney Princesse: Livres Enchantés
S6RP52 = Les Mondes de Ralph
S72J01 = Hoshi no Kirby: 20th Anniversary Edition
S7APWR = LEGO Batman 2 : DC Super Heroes
S7BP69 = Trivial Pursuit Casual
S7EP52 = Transformers : Édition Ultime Combat
S7FPGT = Zumba Kids : La fête ultime pour les enfants
S7SP41 = Les schtroumpfs Party Pack
SA3P5G = Alvin et Les Chipmunks 3
SA3XGT = Alvin et Les Chipmunks 3
SALE4Q = Alice au Pays des Merveilles
SALP4Q = Alice au Pays des Merveilles
SAOP78 = Monster High: Lycée d'Enfer
@ -580,6 +576,7 @@ SF5P41 = Mon Coach Personnel : Club Fitness
SFGP69 = Hasbro : Best of des Jeux en Famille Vol. 4
SFIP01 = Mystery Case Files : L'Affaire Malgrave
SFQP8P = Captain America : Super Soldier
SFWP69 = Coupe du Monde de la FIFA, Afrique du Sud 2010
SFWX69 = Coupe du Monde de la FIFA, Afrique du Sud 2010
SFWY69 = Coupe du Monde de la FIFA
SG8PAF = Yogi l'Ours: Le Jeu Vidéo
@ -596,6 +593,7 @@ SHMPLR = Lucas la Cata
SHVP78 = Hot Wheels
SIAP52 = L'Âge de glace 4 : La dérive des continents - Jeux de l'Arctique
SIIP8P = Mario & Sonic aux Jeux Olympiques de Londres 2012
SILP78 = Worms : Battle Islands
SINPNG = We Sing : Robbie Williams
SJ2PWR = Scooby-Doo! Panique dans la Marmite
SJ9P41 = Just Dance 2 : Extra Songs
@ -624,7 +622,6 @@ SNBP41 = NCIS: Adapté de la série TV
SNHP69 = Need for Speed
SNUPJW = Happy Neuron Academy - Testez Votre Q.I.
SNYPVZ = Monster High : 13 souhaits
SOMP01 = Beat the Beat : Rhythm Paradise
SONFMR = Mon Premier Karaoké
SOSPAF = Turbo : Équipe de Cascadeurs
SOUP01 = The Legend of Zelda : Skyward Sword
@ -676,6 +673,7 @@ SVDP52 = Bob l'éponge : La vengeance robotique de Plankton
SVHP69 = FIFA 14 - Édition Essentielle
SVHX69 = FIFA 14 - Édition Essentielle
SVMP01 = Super Mario All-Stars
SVQEVZ = Barbie et ses soeurs : La grande aventure des chiots
SVQPVZ = Barbie et ses soeurs : La grande aventure des chiots
SVVPAF = Les Croods : Fête Préhistorique !
SXAP52 = Guitar Hero : World Tour
@ -683,56 +681,105 @@ SXBP52 = Guitar Hero : Metallica
SXCP52 = Guitar Hero : Greatest Hits
SXDP52 = Guitar Hero : Van Halen
SXIP52 = Guitar Hero : Warriors of Rock
GMSE02 = Super Mario Sunshine Multijoueur
R4ZP01 = Project Zero 4 - Mask of the Lunar Eclipse
RMCE88 = Le Mario Kare Deluxa por jatras
RMCJ91 = Wiimms Mario Kart Fun 2021-09 Reservé
RMCK91 = Wiimms Mario Kart Fun 2021-09 Reservé
RMCP91 = Wiimms Mario Kart Fun 2021-09 Réservé
RMCPCA = Mario Kart Wii (traduction en catalan)
SBOD3Q = StarSing : Chansons Magiques de Disney v1.1
SILP4Q = SingItStar Latino
SNBE66 = Nouveau Super Mario Bros. Wii Apocalypse
W2CP = Cérébral Challenge
W2FP = Entrainement d'équilibre Physiofun
W2GD = Phoenix Wright Ace Attorney: Justice for All (Deutsche Version)
W2GF = Phoenix Wright: Ace Attorney: Justice for All
W2GP = Phoenix Wright Ace Attorney : Justice for All
W2MP = Blaster Master Overdrive
W2PP = Programme de rééducation du périnée Physiofun
W3GD = Phoenix Wright Ace Attorney 3: Trials And Tribulations
W3GF = Phoenix Wright: Ace Attorney: Trials and Tribulations
W3GP = Phoenix Wright Ace Attorney : Trials and Tribulations
W3KE = Thruspace
W3KP = Thruspace
W3MP = Les Trois Mousquetaires : Tous pour un!
W44P = Stop Stress : A Day of Fury
W4AP = Arcade Sports : Air Hockey, Bowling, Pool, Snooker
W6BP = Eco-Shooter: Plant 530
W72P = Successfully Learning German Year 3
W73P = Successfully Learning German Year 4
W74P = Successfully Learning German Year 5
W7IP = Successfully Learning German Year 2
W8CP = Bit.Trip Core
W8WP = Happy Holidays Halloween
W9BP = Big Town Shoot
W9RP = Happy Holidays Christmas
WA4P = WarioWare : Do It Yourself - Showcase
WA7P = Toribash - La violence perfectionnée
WA8P = ArtStyle: Penta Tentacles
WAEP = Around the world
WAFP = Airport Mania: First Flight
WAHP = Trenches: Generals
WALP = Art Style : light trax
WAOP = The Very Hungry Caterpillar´s ABC
WB2P = Strong Bad Episode 4 : Dangeresque 3
WB3P = Strong Bad Episode 5 : 8-bit is Enough
WBEP = Beer Pong : Frat Party Games
WBFP = Bit.Trip Fate
WBGP = Bang Attack
WBPP = PLÄTTCHEN - twist 'n' paint
WBRP = Pirates : The Key of Dreams
WBXP = Strong Bad Episode 1: Homestar Ruiner
WBYP = Strong Bad Episode 2 : Strong Badia - The Free
WBZP = Strong Bad Episode 3 : Baddest of the Bands
WCHP = Chess Challenge
WCJP = Cocoto : Platform Jumper
WCKP = chick chick BOOM
WCSP = CueSports : Snooker vs Billiards
WD9P = Castlevania : The Adventure ReBirth
WDEP = Magic Destiny Astrological Games
WDFP = Defend your Castle
WDHP = ArtStyle : ROTOHEX
WDMP = Dr. Mario & Bactericide
WDPP = Dr. Mario & Germ Buster (Friend Battle Demo)
WEMP = Aha! I Got It! Escape Game
WETP = Jeux de plateau en images : La chasse au trésor
WF2P = Final Fantasy Crystal Chronicles : My Life as a Darklord
WF3E = Family Games - Pen & Paper Edition
WF4P = Final Fantasy IV : Les Années Suivantes
WFCP = Final Fantasy Crystal Chronicles : My Life as a King
WFQP = Frogger: Hyper Arcade Edition
WFTP = Fish'em All!
WFVP = Football Up
WFWP = Flowerworks : Follie's Adventure
WFXP = Feu de Bois Chaleureux
WFYP = Family Games Pen & Paper Edition
WGDP = Gradius Rebirth
WGFP = Girlfriends Forever Magic Skate
WGGP = Gabrielle's Ghostly Groove: Monster Mix
WGPP = Zenquaria L'aquarium Virtuel
WGSE = Phoenix Wright: Ace Attorney
WGSF = Phoenix Wright: Ace Attorney (French Version)
WGSP = Phoenix Wright: Ace Attorney
WHEE = Heracles : Chariot Racing
WHEP = Heracles : Chariot Racing
WHFP = Heavy Fire: Special Operations
WHRP = Heron : Steam Machine
WHUP = Ghost Party
WHWP = HoopWorld : BasketBrawl
WICP = NyxQuest : Kindred Spirits
WIDP = Dracula : Undead Awakening
WIEP = Tales of Monkey Island Chapter 3 : Lair of the Leviathan
WILP = Tales of Monkey Island Chapter 1: Launch of the Screaming Narwhal
WIRP = Tales of Monkey Island Chapter 5: Rise Of The Pirate God
WISP = Tales of Monkey Island Chapter 2 : The Siege of Spinner Cay
WITP = Aha! I Found It! Hidden Object Game
WIYP = Tales of Monkey Island Chapter 4 : The Trial and Execution of Guybrush Threepwood
WJKP = Les Gardiens du Joyau : l'Île de l'Est
WKBP = You, Me and the Cubes
WKFP = Kung Fu Funk: Everybody Is Kung Fu Fighting
WKKP = Pop-Up Pirates!
WKRP = Karate Phants : Gloves of Glory
WKWP = Les aventures de l'ile perdue jeu d'objets cachés
WLEE = Apprends avec les PooYoos: Episode 1
@ -740,38 +787,76 @@ WLEP = Apprends avec les PooYoos: Episode 1
WLNE = Apprends avec les PooYoos: Episode 2
WLNP = Apprends avec les PooYoos: Episode 2
WLOP = LostWinds : Winter of the Melodias
WLZP = lilt line
WM7E = Anima Ark of Sinners
WM7P = Anima Ark of Sinners
WMBP = MaBoShi : l'arcade des trois formes
WMCP = Monsteca Corral: Monsters Vs. Robots
WMJP = Dive: The Medes Islands Secret
WMSP = Enjoy your massage!
WMWP = Le monde de Miffy
WMWX = Le monde de Miffy
WN9E = Military Madness: Nectaris
WN9P = Military Madness : Nectaris
WNEE = Penguins & Friends Hey! That's My Fish!
WNEP = Penguins & Friends Hey! Thats my Fish!
WNVP = Neves Plus
WOBE = Art Style: ORBIENT
WOBP = Art Style : ORBIENT
WOTP = Overturn : Mecha Wars
WP3P = Pearl Harbor Trilogy 1941: Red Sun Rising
WP4E = Apprends avec les Pooyoos Episode 3
WP4P = Apprends avec les Pooyoos Episode 3
WPJP = Pucca Mission Bisous
WPKP = Texas Hold'Em Poker
WPQP = Protöthea
WPRE = Art Style: Cubello
WPRP = Art Style : CUBELLO
WPVP = The Tales of Bearsworth Manor - Chaotic Conflicts
WPVE = The Tales of Bearsworth Manor: Chaotic Conflicts
WR2E = Labo Lapins Crétins
WR2P = Labo Lapins Crétins
WR9P = Megaman 9
WRDP = Mot Cache
WREP = Racers Islands Crazy Arenas
WRFE = Reel Fishing Challenge 2
WRFP = Reel Fishing Challenge 2
WRIP = Rainbow Islands : Towering Adventure!
WRJP = Racers Islands - Crazy Racers
WRLP = FAST Racing League
WRRP = Robin Hood : Le Retour de Richard
WRUP = Bit.Trip Runner
WRXP = Megaman 10
WSGP = Pop Them, Drop Them SAMEGAME
WSJP = Trouvez les Differences!
WSNP = Sonic The Hedgehog 4 Episode I
WSUE = Shootanto: Evolutionary Mayhem
WSUP = Shootanto : Evolutionary Mayhem
WTEP = Tales of Elastic Boy Mission 1
WTFP = Bit.Trip Flux
WTMP = Adventure Island The Beginning
WTRE = Bit.Trip Beat
WTRP = Bit.Trip : Beat
WTWP = Fenimore Fillmore The Westerner
WTXP = Texas Holdem Tournament
WU2P = Successfully Learning Mathematics Year 3
WU3P = Successfully Learning Mathematics Year 4
WU4P = Successfully Learning Mathematics Year 5
WUIP = Succès au primaire : Power maths
WVBP = Bit.Trip Void
WVOP = Rock'n Roll Climber
WVSP = Gods Vs Humans
WVUP = Mr Bumblebee Racing Champion
WW2P = Where's Wally? Fantastic Journey 2
WW3P = Where's Wally? Fantastic Journey 3
WWIP = Où est Charlie ? Voyage Fantastique 1
WWRP = Excitebike: World Challenge
WWXP = Paper Wars Cannon Fodder
WXBE = Ben 10 Alien Force The Rise of Hex
WXBP = Ben 10 Alien Force The Rise of Hex
WYIP = escapeVektor: Chapter 1
WYSP = Yard Sale Hidden Treasures Sunnyville
WZIP = Rubik's Puzzle Galaxy : RUSH
WZZP = The Tales of Bearsworth Manor - Puzzling Pages
WZZE = The Tales of Bearsworth Manor: Puzzling Pages
XIBP = Fish em All Demo
XICP = Gods vs Humans Demo
XIDP = Racers Islands Crazy Racers Demo
@ -790,60 +875,163 @@ XIZP = 3D Pixel Racing Demo
XJEP = Aya and the Cubes of Light Demo
FA9P = Zelda II : The Adventure of Link
FB2L = Super Mario Bros. : The Lost Levels
FBKP = Teenage Mutant Ninja Turles
FC8P = Castlevania II : Simon's Quest
FCSP = Probotector II : Return of the Evil Forces
FCYP = Yoshis Cookie
FDGP = Ghosts'n Goblins
FDRP = Skate or Die
FEML = Bio Miracle Bokutte UPA
FEQP = Castlevania III Dracula's Curse
FERM = Startropics II: Zoda's Revenge
FF5E = Double Dragon II: The Revenge
FF5P = Double Dragon II: The Revenge
FFEE = A Boy and His Blob: Trouble on Blobolonia
FFEP = A Boy and His Blob : Trouble on Blobolonia
FFPB = Ufouria: The Saga
FFPP = Ufouria: THE SAGA
FFUP = Adventure Island 2
FFVM = S.C.A.T.: Special Cybernetic Attack Team
JA4P = Super Ghouls'n Ghosts
JABL = Marios Super Picross
JADD = The Legend of Zelda : A Link to the Past
JADE = The Legend of Zelda : A Link to the Past
JADF = The Legend of Zelda : A Link to the Past
JADP = The Legend of Zelda : A Link to the Past
JAFF = SimCity
JAFP = SimCity
JAHP = R-TYPE III : The Third Lightning
JAJP = Street Fighter II : The World Warrior
JALP = Super Probotector : Alien Rebels
JAZP = The Legend of the Mystical Ninja
JBBP = Super Street Fighter II : The New Challengers
JBDP = Donkey Kong Country 2 : Diddy's Kong-Quest
JBIP = Street Fighter II Turbo : Hyper Fighting
JBPP = Donkey Kong Country 3 : Dixie Kongs Double Trouble
JCAL = DoReMi Fantasy - Milons DokiDoki Adventure
JCBM = Super Mario RPG : Legend of the Seven Stars
JCCP = Kirbys Fun Pak
JCDM = Kirbys Dream Land 3
JCJP = Super Punch Out!!
JCKP = Space Invaders -The Original Game-
JCTM = Ogre Battle : The March of the Black Queen
JD3P = SUPER E.D.F. : Earth Defense Force
JDJP = Super Star Wars : The Empire Strikes Back
JDLP = Super Star Wars : Return of the Jedi
JDWE = Aero the Acrobat
JDWP = Aero The Acrobat
JDZF = Mystic Quest Legend
NACP = The Legend of Zelda : Ocarina of Time
NAJN = Sin and Punishment
NAKS = Pokémon Snap
NAME = Kirby 64 : The Crystal Shards
NAMP = Kirby 64 : The Crystal Shards
NAND = Pokémon Puzzle League
NAOP = 1080°: TenEighty Snowboarding
NARP = The Legend of Zelda : Majora's Mask
NAYE = Ogre Battle 64: Person of Lordly Caliber
NAYM = Ogre Battle 64: Person of Lordly Caliber
LALP = Fantasy Zone II
LANP = Alex Kidd: The Lost Stars
LAPP = Wonder Boy III: The Dragon's Trap
MA8P = Ecco: The Tides of Time
MAHE = Sonic the Hedgehog
MAHP = Sonic the Hedgehog
MAKP = Shadow Dancer : The Secret of Shinobi
MALP = Bonanza Bros.
MAOP = Bio-Hazard Battle
MARP = La Légende de Thor
MB6P = Shining Force II
MAVP = Wonder Boy In Monster World
MAXP = Alex Kidd In The Enchanted Castle
MBBP = Sonic the Hedgehog 2
MBFP = Shinobi III: Return of the Ninja master
MBIP = Landstalker : Le Trésor du Roi Nole
MBJE = Ghouls'n Ghosts
MBJP = Ghouls'n Ghosts
MBLP = ESWAT City Under Siege
MBMP = Sonic the Hedgehog 3
MBUP = Sonic 3D: Flickies' Island
MBWM = Columns III: Revenge of Columns
MC3P = Super Street Fighter II: The New Challengers
MCCP = Phantasy Star III: Generations of Doom
MCHM = MUSHA : Metallic Uniframe Super Hybrid Armor
MCLP = Street Fighter II: Special Champion Edition
MCQP = Boogerman - A Pick and Flick Adventure
MCRP = Wolf of the Battlefield: MERCS
MCSP = Wonder Boy III: Monster Lair
MCVP = Pitfall: The Mayan Adventure
MCZP = Shanghai II Dragon's Eye
PAAP = Bomberman'93
PAGL = Bomberman'94
PARL = Detana Twin Bee
PAWP = Galaga'88
PB3P = Devil Crash
PBEP = Motoroader
PBIP = Bonk III : Bonk's Big Adventure
PBSP = Chew Man Fu
PBWP = Air 'Zonk'
PC2P = Taito Chase H.Q.
PCSL = Digital Champ : Battle Boxing
PDJL = Street Fighter II': Champion Edition
QA3P = SimEarth : The Living Planet
QAAP = Super Air Zonk
QABP = Ys Book I & II
QADL = Gradius II : Gofer no Yabou
QAPL = Castlevania: Rondo of Blood
QAPN = Castlevania Rondo of Blood
QAPP = Castlevania : Rondo of Blood
EA5E = Fatal Fury 3: Road to the Final Victory
EA5P = Fatal Fury 3 : Road To The Final Victory
EA7P = Samurai Shodown IV: Amakusa's Revenge
EA8M = Iron Clad
EAIE = Top Hunter
EAIP = Top Hunter
EASE = Samurai Shodown 2
EBDP = Magical Drop 3
EBFP = Spin master
EBSP = The Path of the Warrior: Art of Fighting 3
ECAP = Real Bout Fatal Fury 2: The Newcomers
ECGP = Shock Troopers: 2nd Squad
E54P = GHOSTS'N GOBLINS
E55P = Commando
E57P = SonSon
E6PP = NINJA GAIDEN
C93P = The Last Ninja 2
C96P = Summer Games 2
C9IP = Cybernoid
HAAA = Chaîne Photos
HABA = Chaîne Boutique
HABA = Chaîne boutique Wii
HACA = Chaîne Mii
HACK = Chaîne Mii
HADE = Chaîne Internet
HADP = Chaîne Internet
HAFP = Chaîne Météo
HAFA = Chaîne météo
HAFP = Chaîne météo
HAGA = Chaîne infos
HAGE = Chaîne infos
HAGP = Chaîne Infos
HAJP = Chaîne Votes
HAPP = Chaîne Concours Mii
HAGJ = Chaîne infos
HAGP = Chaîne infos
HAJP = Chaîne votes
HAPE = Chaîne Regardez-Mii
HAPP = Chaîne concours Mii
HATP = Chaîne Nintendo
HAYA = Chaîne Photos
HAVP = Chaîne jour de chance
HAWP = Metroid Prime 3 Preview
HAYA = Chaîne photos
HAYK = Chaîne Photo
HCAP = Jam with the Band Live
HCFE = Chaîne Wii Speak
HCFP = Chaîne Wii Speak
HCMP = Chaîne Kirby TV
HCRE = The Legend of Zelda: Skyward Sword - Chaîne mise à jour des données
HCRP = The Legend of Zelda: Skyward Sword - Chaîne mise à jour des données
RMCE = Chaîne Mario Kart
RMCP = Chaîne Mario Kart
DNUA = Donut Wii
OHBC = Chaîne Homebrew
RMCX = Chaîne Mario Kart Wii CTGP Revolution
G2FF78 = Tak 2: Le Sceptre des Rêves
G3AF69 = Le Seigneur des Anneaux : Le Tiers Âge
G3AF69 = Le Seigneur des Anneaux: Le Tiers Âge
G3DP6L = Carmen Sandiego : Le Secret des Tam-Tams Volés
G3MP41 = La Somme de toutes les Peurs
G3XP52 = X-Men: Le Jeu Officiel
@ -854,19 +1042,21 @@ G4ZP69 = Les Sims 2
G5DP78 = Scooby-Doo! : Démasqué
G6FF69 = Coupe du Monde de la FIFA 2006
G8MP01 = Paper Mario: La Porte Millénaire
G9TF52 = Gang de Requins
GAVY78 = Avatar : Le Dernier Maître de l'Air
GAZF69 = Harry Potter et le Prisonnier d'Azkaban
GC3F78 = Scooby-Doo! : Le Livre des Ténèbres
GCBP7D = Crash Bandicoot : La Vengeance de Cortex
GC3F78 = Scooby-Doo! Le Livre des Ténèbres
GCBP7D = Crash Bandicoot: La Vengeance de Cortex
GCGP41 = Charlie's Angels: Les Anges se Déchaînent
GCIP69 = Les Sims
GCOF52 = Call of Duty : Le Jour De Gloire
GCQF7D = Buffy contre les Vampires : Chaos Bleeds
GDDP41 = Donald Cou@k Att@k?*!
GDOP41 = Disney's Donald Qui est PK ?
GDOP41 = Disney's Donald Qui est PK?
GENF69 = James Bond 007: Quitte ou Double
GF4F52 = Les 4 Fantastiques
GFAF69 = FIFA Football 2003
GFHP6V = Un Voisin d'Enfer!
GFSF69 = Coupe du Monde FIFA 2002
GGVX78 = Bob l'Eponge : Le Film
GH2P69 = Need for Speed : Poursuite Infernale 2
GH4F69 = Harry Potter et la Coupe de Feu
@ -877,27 +1067,28 @@ GHSY69 = Harry Potter et la Chambre des Secrets
GHVP08 = Disney Cache-Cache Furtif
GIAP7D = L'Age de Glace 2
GICF78 = Les Indestructibles
GIHF78 = Scooby-Doo! La Nuit des 100 Frissons
GIHP78 = Scooby-Doo! : La Nuit des 100 Frissons
GIQX78 = Les Indestructibles: La Terrible Attaque du Démolisseur
GJUF78 = Tak et le Pouvoir de Juju
GJUF78 = Tak & Le Pouvoir de Juju
GKBPAF = Baten Kaitos : Les Ailes éternelles et l'Océan perdu
GKJF78 = Cars: Quatre Roues
GKLF69 = Le Seigneur des Anneaux : Le Retour du Roi
GKLF69 = Le Seigneur des Anneaux: Le Retour du Roi
GKMP41 = Prince of Persia: Les Deux Royaumes
GL5X4F = Lego Star Wars : Le Jeu Vidéo
GL7P64 = Lego Star Wars II: La Trilogie Originale
GLCF52 = Les Desastreuses Aventures des Orphelins Baudelaire
GLCF52 = Les Désastreuses Aventures des Orphelins Baudelaire: D'Apres Lemony Snicket
GLGP41 = Largo Winch : Aller Simple pour les Balkans
GLHPG9 = Souris City
GLNP69 = Les Looney Tunes Passent à l'Action
GLOF69 = Le Seigneur des Anneaux : Les Deux Tours
GLOF69 = Le Seigneur des Anneaux: Les Deux Tours
GLVF4Q = Le Monde de Narnia: Le Lion, la Sorcière et l'Armoire Magique
GLZF69 = 007 : Bons Baisers de Russie
GMFF69 = Medal of Honor : En Première Ligne
GMNP78 = Monstres & Cie : Crazy Balls
GMWF52 = Minority Report: Le futur vous rattrape
GNEF78 = Le Monde De Némo
GONF69 = Medal of Honor : Les Faucons de Guerre
GMWF52 = Minority Report: Le Futur vous Rattrape
GNEF78 = Le Monde de Némo
GONF69 = Medal of Honor: Les Faucons de Guerre
GOSP41 = Les Rebelles de la Forêt
GOYF69 = GoldenEye : Au service du Mal
GP3P78 = Le Pole Express
@ -909,20 +1100,25 @@ GQ4F78 = Bob L'éponge: La Créature du Crabe Croustillant
GQFFFK = Franklin: Un anniversaire surprise
GQLP54 = Dora l'Exploratrice: Voyage sur la Planète Violette
GQPP78 = Bob l'Eponge: Bataille pour Bikini Bottom
GQQF78 = Bob L'éponge : Silence on tourne!
GQQD78 = Bob L'éponge: Silence on tourne!
GQQE78 = Bob L'éponge: Silence on tourne!
GQQF78 = Bob L'éponge: Silence on tourne!
GQQH78 = Bob L'éponge: Silence on tourne!
GQQP78 = Bob L'éponge: Silence on tourne!
GQWX69 = Harry Potter: Coupe du Monde de Quidditch
GR2P52 = Les Royaumes Perdus II
GR8F69 = Medal of Honor : Soleil Levant
GR8F69 = Medal of Honor: Soleil Levant
GR9P6L = Le Règne Du Feu
GRNP52 = Les Royaumes Perdus
GRUF78 = Power Rangers : Dino Tonnerre
GRRF78 = Les Razmoket: La Rançon Royale
GRUF78 = Power Rangers: Dino Tonnerre
GSKP7D = Le Roi Scorpion : L'Ascension de l'Akkadien
GTYP69 = Ty: Le Tigre de Tasmanie
GU2F78 = 2 Games in 1: Les Indestructibles / Le Monde de Nemo
GU3X78 = 2 Games in 1: Bob L'éponge le Film / Tak 2 Le Sceptre des Rêves
GUBP69 = Les Urbz: Les Sims in the City
GVLF69 = Marvel Nemesis : L'Avenement des Imparfaits
GW7F69 = James bond 007 dans Espion pour Cible
GVLF69 = Marvel Nemesis: L'Avenement des Imparfaits
GW7F69 = James Bond 007 dans Espion pour Cible
GWHP41 = Winnie l'Ourson: à la Recherche des Souvenirs Oubliés
GWLX6L = Wallace & Gromit dans Le Project Zoo
GWVX52 = X-Men 2 : La Vengeance de Wolverine

View file

@ -1,4 +1,4 @@
TITLES = https://www.gametdb.com (type: Wii language: IT_unique version: 20191106234245)
TITLES = https://www.gametdb.com (type: Wii language: IT_unique version: 20230727194156)
R23P52 = Barbie e le Tre Moschettiere
R25PWR = LEGO Harry Potter: Anni 1-4
R2AP7D = L'Era Glaciale 2: Il Disgelo
@ -91,8 +91,7 @@ RIHP8P = L'Incredibile Hulk
RIJP69 = G.I. Joe: La nascita dei Cobra
RINP08 = Dead Rising: Salme di Fine Stagione
RIOPSU = Brutte Storie: I Rivoltanti Romani
RIPPAF = One Piece Unlimited Cruise 1 - The Treasure Beneath the Waves
RIUPAF = One Piece Unlimited Cruise 2: Il Risveglio di un Eroe
RIUPAF = One Piece: Unlimited Cruise 2: Il Risveglio di un Eroe
RJ8E64 = Indiana Jones e il Bastone dei Re
RJ8P64 = Indiana Jones e il Bastone dei Re
RJAX52 = Call of Duty: Modern Warfare - Edizione Reflex
@ -107,7 +106,7 @@ RLBPWR = LEGO Batman: Il Videogioco
RLFP64 = Star Wars The Clone Wars: L'era dei duelli
RLGP64 = LEGO Star Wars: La Saga Completa
RLIP64 = LEGO Indiana Jones: Le Avventure Originali
RLLP70 = Go West!: Un'Avventura di Lucky Luke
RLLP70 = Go West! Un'Avventura di Lucky Luke
RLNFMR = L'Isola dei Famosi
RLNHMR = L'Isola dei Famosi
RLNIMR = L'Isola dei Famosi
@ -133,7 +132,7 @@ RO8X7D = The Legend of Spyro: L'Alba del Drago
ROEPGT = Hotel Bau
ROLP8P = Mario & Sonic ai Giochi Olimpici Invernali
ROPP41 = Boog & Elliot a Caccia Di Amici
ROUPAF = One Piece Unlimited Cruise 1: Il Tesoro Sommerso
ROUPAF = One Piece: Unlimited Cruise 1: Il Tesoro Sommerso
ROYP41 = Piovono Polpette: Il Videogioco
ROYX41 = Piovono Polpette
RP2P69 = Le So Tutte!
@ -253,6 +252,7 @@ S75P69 = Monopoly
S7FPGT = Zumba Kids: Che la festa abbia inizio, con Zumba!
S7SP41 = I Puffi Party Pack
SA3P5G = Alvin Superstar 3: Si Salvi Chi Può
SA3XGT = Alvin Superstar 3: Si Salvi Chi Può
SALP4Q = Alice nel Paese delle Meraviglie
SAOP78 = Monster High: Scuola da Paura
SAOXVZ = Monster High: Scuola da Paura
@ -342,8 +342,9 @@ SU7PAF = Le 5 Leggende
SUUP78 = uDraw Studio: E sei subito artista!
SV3PAF = Madagascar 3: Ricercati in Europa
SV7PVZ = I Pinguini di Madagascar
SVDP52 = SpongeBob: La Vendetta Robotica de Plankton
SVDP52 = SpongeBob: La Vendetta Robotica di Plankton
SVMP01 = Super Mario All-Stars: Edizione per il 25° anniversario
SVQEVZ = Barbie e le sue sorelle: Salvataggio Cuccioli
SVQPVZ = Barbie e le sue sorelle: Salvataggio Cuccioli
SVVPAF = I Croods: Festa Preistorica!
SXAP52 = Guitar Hero 4: World Tour
@ -351,29 +352,155 @@ SXIP52 = Guitar Hero 6: Warriors of Rock
CGIE52 = Guitar Hero III Custom: Iron Maiden
CLAPSI = SingItStar Clásicos
DMSP4Q = Sing It Star e la Magia Disney
G01E01 = Super Smash Bros. Melee: Remix SD
MILPSI = SingItStar Miliki
R15POH = SingItStar Radio 105
RGGE52 = Guitar Hero III Custom: Rock The Games
RMCPCA = Mario Kart Wii (traduzione in catalano)
RSJESD = Guitar Hero III Custom: System Of A Down
S02PES = SingItStar 90's
SILP4Q = Sing It: Latino
SP9P4Q = SingItStar POP 2009
WFFF4I = Fatal Frame 4: La Maschera dell'eclissi lunare
W2CP = Brain Challenge L'Allena-Mente
W2FP = Physiofun - Balance Training
W2GD = Phoenix Wright Ace Attorney: Justice for All (Deutsche Version)
W2GI = Phoenix Wright: Ace Attorney: Justice for All
W2GP = Phoenix Wright Ace Attorney: Justice for All
W2MP = Blaster Master: Overdrive
W2PP = Physiofun: Pelvic Floor Training
W3GI = Phoenix Wright: Ace Attorney: Trials and Tribulations
W3KP = ThruSpace: High Velocity 3D Puzzle
W3MP = I Tre Moschettieri Uno per tutti!
W44P = Stop Stress: A Day of Fury
W4AP = Arcade Sports: Air Hockey, Bowling, Pool, Snooker
W6BP = 530 ECO SHOOTER
W72P = Successfully Learning German Year 3
W73P = Successfully Learning German Year 4
W74P = Successfully Learning German Year 5
W7IP = Successfully Learning German Year 2
W8CP = Bit.Trip Core
W8WP = Happy Holidays Halloween
W9BP = Big Town Shoot
W9RP = Happy Holidays Christmas
WA4P = WarioWare: Do It Yourself - Showcase
WA7P = Toribash Violence Perfected
WA8P = Art Style: Penta Tentacles
WAEP = Around the world
WAFP = Airport Mania: First Flight
WAHP = Trenches: Generals
WALP = Art Style: light trax
WAOP = The Very Hungry Caterpillar´s ABC
WB2P = Strong Bad Episode 4: Dangeresque 3
WB3P = Strong Bad Episode 5: 8-bit is Enough
WBEP = Beer Pong: Frat Party Games
WBFP = Bit.Trip Fate
WBGP = Bang Attack
WBPP = PLÄTTCHEN - twist 'n' paint
WBRP = Pirates: The Key of Dreams
WBXP = Strong Bad Episode 1: Homestar Ruiner
WBYP = Strong Bad Episode 2: Strong Badia - The Free
WBZP = Strong Bad Episode 3: Baddest of the Bands
WCHP = Chess Challenge
WCJP = Cocoto: Platform Jumper
WCKP = chick chick BOOM
WCSP = CueSports: Snooker vs Billiards
WDEP = Magic Destiny Astrological Games
WDFP = Defend your Castle
WDHP = Art Style: ROTOHEX
WDMP = Dr. Mario & Sterminavirus
WDPP = Dr. Mario & Germ Buster (Friend Battle Demo)
WEMP = Aha! I Got It! Escape Game
WETP = Giochi da tavolo animati: Un'avventura pop-up!
WF2P = Final Fantasy Crystal Chronicles: My Life as a Darklord
WF4P = Final Fantasy IV: The After Years
WFCP = Final Fantasy Crystal Chronicles: My Life as a King
WFQP = Frogger: Hyper Arcade Edition
WFTP = Fish'em All!
WFVP = Football Up
WFWP = Flowerworks: Follie's Adventure
WGFP = Girlfriends Forever: Magic Skate
WGGP = Gabrielle's Ghostly Groove: Monster Mix
WGPP = Zenquaria™: Acquario virtuale
WGSF = Phoenix Wright: Ace Attorney (French Version)
WGSI = Phoenix Wright: Ace Attorney
WGSP = Phoenix Wright: Ace Attorney
WHEP = Heracles: Chariot Racing
WHFP = Heavy Fire: Special Operations
WHRP = Heron: Steam Machine
WHUP = Fantasma Party
WHWP = HoopWorld: BasketBrawl
WICP = NyxQuest: Kindred Spirits
WIDP = Dracula: Undead Awakening
WIEP = Tales of Monkey Island Chapter 3: Lair of the Leviathan
WILP = Tales of Monkey Island Chapter 1: Launch of the Screaming Narwhal
WIRP = Tales of Monkey Island Chapter 5: Rise Of The Pirate God
WISP = Tales of Monkey Island Chapter 2: The Siege of Spinner Cay
WITP = Aha! I Found It! Hidden Object Game
WIYP = Tales of Monkey Island Chapter 4: The Trial and Execution of Guybrush Threepwood
WJKP = Jewel Keepers: Easter Island
WKBP = You, Me and the Cubes
WKFP = Kung Fu Funk: Everybody Is Kung Fu Fighting
WKKP = Pop-Up Pirates!
WKRP = Karate Phants: Gloves of Glory
WKWP = Adventure on LOST ISLAND: Hidden Object Game
WLEE = Impara con i PooYoo: Episodio 1
WLEP = Impara con i PooYoo: Episodio 1
WLNE = Impara con i PooYoo: Episodio 2
WLNP = Impara con i PooYoo: Episodio 2
WLOP = LostWinds: Winter of the Melodias
WLZP = lilt line
WM7P = Anima Ark of Sinners
WMBP = MaBoShi: L'arcade delle tre forme
WMCP = Monsteca Corral: Monsters Vs. Robots
WMJP = Dive: The Medes Islands Secret
WMSP = Enjoy your massage!
WN9E = Military Madness: Nectaris
WN9P = Military Madness: Nectaris
WNEE = Penguins & Friends Hey! That's My Fish!
WNEP = Penguins & Friends Hey! Thats my Fish!
WNVP = Neves Plus: Phantheon of Tangrams
WOBP = Art Style: ORBIENT
WOTP = Overturn: Mecha Wars
WP3P = Pearl Harbor Trilogy 1941: Red Sun Rising
WP4P = Learning with the PooYoos: Episode 3
WPKP = Texas Hold'Em Poker
WPQP = Protöthea
WPRP = Art Style: CUBELLO
WPVP = The Tales of Bearsworth Manor: Chaotic Conflicts
WREP = Racers Islands Crazy Arenas
WRIP = Rainbow Islands: Towering Adventure!
WRJP = Racers Islands - Crazy Racers
WRLP = FAST Racing League
WRRP = Robin Hood: The Return Of Richard
WRUP = Bit.Trip Runner
WSGP = Pop Them, Drop Them SAMEGAME
WSNP = Sonic The Hedgehog 4 Episode I
WSUP = Shootanto: Evolutionary Mayhem
WTEP = Tales of Elastic Boy Mission 1
WTFP = Bit.Trip Flux
WTMP = ADVENTURE ISLAND The Beginning
WTRP = Bit.Trip Beat
WTWP = Fenimore Fillmore: The Westerner
WTXP = Texas Holdem Tournament
WU2P = Successfully Learning Mathematics Year 3
WU3P = Successfully Learning Mathematics Year 4
WU4P = Successfully Learning Mathematics Year 5
WUIP = Successfully Learning Mathematics Year 2
WVBP = Bit.Trip Void
WVOP = Rock'n Roll Climber
WVSP = Gods Vs Humans
WVUP = Mr Bumblebee Racing Champion
WW2P = Where's Wally? Fantastic Journey 2
WW3P = Where's Wally? Fantastic Journey 3
WWIP = Where's Wally? Fantastic Journey 1
WWRP = Excitebike: World Challenge
WWXP = Paper Wars Cannon Fodder
WXBP = Ben 10: Alien Force - The Rise of Hex
WYIP = escapeVektor: Chapter 1
WYSP = Yard Sale Hidden Treasures Sunnyville
WZIP = Rubik's Puzzle Galaxy: RUSH
WZZP = The Tales of Bearsworth Manor: Puzzling Pages
XIBP = Fish em All Demo
XICP = Gods vs Humans Demo
XIDP = Racers Islands Crazy Racers Demo
@ -390,35 +517,147 @@ XIUP = Soccer Bashi Demo
XIVP = Mix Superstar Demo
XIZP = 3D Pixel Racing Demo
XJEP = Aya and the Cubes of Light Demo
FA9P = Zelda II: The Adventure of Link
FB2L = Super Mario Bros.: The Lost Levels
FBKP = Teenage Mutant Ninja Turles
FC8P = Castlevania II: Simon's Quest
FCSP = Probotector II: Return of the Evil Forces
FDGP = Ghosts'n Goblins
FDRP = Skate or Die
FEML = Bio Miracle Bokutte UPA
FEQP = Castlevania III Dracula's Curse
FERM = Zoda's Revenge: StarTropics II
FFVM = S.C.A.T.
FF5P = Double Dragon II: The Revenge
FFEP = A Boy and His Blob: Trouble on Blobolonia
FFPP = Ufouria: THE SAGA
FFUP = Adventure Island 2
FFWP = Donkey Kong: Edizione Originale
FFXP = Super Mario Brothers: Edizione Esclusiva per il 25° Anniversario
JCCP = Kirby's Fun Pak
JA4P = Super Ghouls'n Ghosts
JABL = Marios Super Picross
JADP = The Legend of Zelda: A Link to the Past
JAFP = SimCity
JAHP = R-TYPE III: The Third Lightning
JAJP = Street Fighter II: The World Warrior
JALP = Super Probotector: Alien Rebels
JAZP = The Legend of the Mystical Ninja
JBBP = Super Street Fighter II: The New Challengers
JBDP = Donkey Kong Country 2: Diddy's Kong-Quest
JBIP = Street Fighter II Turbo: Hyper Fighting
JBPP = Donkey Kong Country 3: Dixie Kongs Double Trouble
JCAL = DoReMi Fantasy - Milons DokiDoki Adventure
JCBM = Super Mario RPG: Legend of the Seven Stars
JCDM = Kirbys Dream Land 3
JCJP = Super Punch Out!!
JCKP = Space Invaders -The Original Game-
JCTM = Ogre Battle: The March of the Black Queen
JD3P = SUPER E.D.F.: Earth Defense Force
JDJP = Super Star Wars: The Empire Strikes Back
JDLP = Super Star Wars: Return of the Jedi
JDWP = Aero The Acrobat
JDZP = Mystic Quest Legend
JECM = CHRONO TRIGGER
NACP = The Legend of Zelda: Ocarina of Time
NAKS = Pokémon Snap
NAMP = Kirby 64: The Crystal Shards
NAOP = 1080°: TenEighty Snowboarding
NARP = The Legend of Zelda: Majora's Mask
NAYM = Ogre Battle 64: Person of Lordly Caliber
LALP = Fantasy Zone II
LANP = Alex Kidd: The Lost Stars
LAPP = Wonder Boy III: The Dragon's Trap
MA8P = Ecco: The Tides of Time
MAHP = Sonic the Hedgehog
MAKP = Shadow Dancer: The Secret of Shinobi
MALP = Bonanza Bros.
MAOP = Bio-Hazard Battle
MAVP = Wonder Boy In Monster World
MAXP = Alex Kidd In The Enchanted Castle
MB6P = Shining Force II
MBBP = Sonic the Hedgehog 2
MBFP = Shinobi III: Return of the Ninja master
MBIP = Landstalker: The Treasures of King Nole
MBJP = Ghouls'n Ghosts
MBLP = ESWAT City Under Siege
MBMP = Sonic the Hedgehog 3
MBUP = Sonic 3D: Flickies' Island
MBWM = Columns III: Revenge of Columns
MC3P = Super Street Fighter II: The New Challengers
MCCP = Phantasy Star III: Generations of Doom
MCHM = MUSHA
MCLP = Street Fighter II: Special Champion Edition
MCQP = Boogerman - A Pick and Flick Adventure
MCRP = Wolf of the Battlefield: MERCS
MCSP = Wonder Boy III: Monster Lair
MCVP = Pitfall: The Mayan Adventure
MCZP = Shanghai II Dragon's Eye
PAAP = Bomberman'93
PAGL = Bomberman'94
PARL = Detana Twin Bee
PAWP = Galaga'90
PBEP = Motoroader
PBIP = Bonk III: Bonk's Big Adventure
PBSP = Chew Man Fu
PBWP = Air 'Zonk'
PCSL = DIGITAL CHAMP Battle Boxing
PDJL = Street Fighter II': Champion Edition
QA3P = SimEarth: The Living Planet
QAAP = Super Air Zonk
QABP = Ys Book I & II
QADL = Gradius II: Gofer no Yabou
QAPL = CASTLEVANIA RONDO OF BLOOD
QAPP = CASTLEVANIA RONDO OF BLOOD
SP4V = CASTLEVANIA RONDO OF BLOOD
EA5P = Fatal Fury 3: Road To The Final Victory
EA7P = Samurai Shodown IV: Amakusa's Revenge
EA8M = Iron Clad
EAIP = Top Hunter
EBDP = Magical Drop 3
EBFP = Spin master
EBSP = The Path of the Warrior: Art of Fighting 3
ECAP = Real Bout Fatal Fury 2: The Newcomers
ECGP = Shock Troopers: 2nd Squad
E54P = GHOSTS'N GOBLINS
E55P = Wolf of the Battlefield: Commando
E57P = SonSon
E6PP = NINJA GAIDEN
C93P = The Last Ninja 2
C96P = Summer Games 2
C9IP = Cybernoid
HAAA = Canale Foto
HABA = Canale Wii Shop
HACA = Canale Mii
HADE = Canale Internet
HADP = Canale Internet
HAFP = Canale Meteo
HAGA = Canale Notizie
HAGE = Canale Notizie
HAGJ = Canale Notizie
HAGP = Canale Notizie
HAJP = Canale Vota Anche Tu
HAPP = Canale Concorsi Mii
HATP = Canale Nintendo
HAVP = Canal La fortuna ti sorride
HAWP = Metroid Prime 3 Preview
HAYA = Canale Foto
HCFE = Canale Wii Speak
HCFP = Canale Wii Speak
HCMP = Canale TV Kirby
HCRE = The Legend of Zelda: Skyward Sword - Canale Aggrioornamento dati di
HCRP = The Legend of Zelda: Skyward Sword - Canale Aggrioornamento dati di
RMCP = Canale Mario Kart
JODI = Canale Homebrew
LULZ = Canale Homebrew
OHBC = Canale Homebrew
G01E01 = Super Smash Bros. Melee: Remix SD
G4MP69 = The Sims: Fuori Tutti
G8MP01 = Paper Mario: Il Portale Millenario
GAZI69 = Harry Potter e il prigioniero di Azkaban
GAZI69 = Harry Potter e il Prigioniero di Azkaban
GCBP7D = Crash Bandicoot: L'ira di Cortex
GCOP52 = Call of Duty: L'Ora Degli Eroi
GDDP41 = Paperino: Oper@zione P@pero ?*!
GDOP41 = Disney's Donald Chi è PK?
GF4I52 = I fantastici 4
GF4I52 = I Fantastici 4
GFSP69 = Mondiali FIFA 2002
GH4I69 = Harry Potter e il Calice di Fuoco
GHBP7D = Lo Hobbit
GHLZ69 = Harry Potter e la Pietra Filosofale
@ -430,11 +669,17 @@ GKMP41 = Prince of Persia: I Due Troni
GLCP52 = Lemony Snicket Una serie di sfortunati eventi
GLHPG9 = Giù Per il Tubo
GLOI69 = Il Signore degli Anelli: Le Due Torri
GNEP78 = Alla ricerca di Nemo
GOSX41 = Boog & Elliot
GPXP01 = Pokémon Box: Rubino e Zaffiro
GQQD78 = SpongeBob: Ciak si gira!
GQQE78 = SpongeBob: Ciak si gira!
GQQF78 = SpongeBob: Ciak si gira!
GQQH78 = SpongeBob: Ciak si gira!
GQQP78 = SpongeBob: Ciak si gira!
GQWX69 = Harry Potter: La Coppa del Mundo di Quidditch
GR9P6L = Il Regno del Fuoco
GSXI64 = Star Wars - La Guerra dei Cloni
GSXI64 = Star Wars: La Guerra dei Cloni
GTYP69 = Ty la Tigre della Tasmania
GVLP69 = Marvel Nemesis: L'Ascesa degli Esseri Imperfetti
GWHP41 = Winnie the Pooh e le Pance Brontolanti

View file

@ -1,6 +1,6 @@
TITLES = https://www.gametdb.com (type: Wii language: JA_unique version: 20191106234252)
TITLES = https://www.gametdb.com (type: Wii language: JA_unique version: 20230727194203)
D2AJAF = みんなで冒険!ファミリートレーナー 体験版
DCHJAF = WE CHEER
DCHJAF = WE CHEER: おはスタプロデュース! 限定コラボゲームディスク
DHHJ8J = 平野綾 Premiumムービーディスク from 涼宮ハルヒの激動
DK6J18 = コロリンパ2 -アンソニーと黃金のひまわりのタネ-
DQAJK2 = アクエリアスベースボール 〜限界の、その先へ〜
@ -14,7 +14,7 @@ R2JJAF = 太鼓の達人Wii
R2LJMS = Hula Wii フラで始める 美と健康!!
R2PJ9B = スイングゴルフ パンヤ 2ndショット!
R2QJC0 = クッキングママ2 たいへん!ママは おおいそがし!!
R2SJ18 = デカスポルタ2
R2SJ18 = Deca Sporta 2: Wiiでスポーツ"10"種目!
R2UJ8P = レッツタップ
R2VJ01 = 罪と罰 宇宙の後継者
R2WJA4 = ウイニングイレブン プレーメーカー 2009
@ -28,7 +28,7 @@ R3OJ01 = メトロイド アザーエム
R3PJ52 = スピード・レーサー
R3TJG9 = トップスピン3
R3UJGD = おやこであそぼ ミッフィーのおもちゃばこ
R43J13 = パーソナルトレーナーWii30日生活改善プログラム
R43J13 = EA Sports アクティブ パーソナルトレーナー: Wii30日生活改善プログラム
R44J8P = 涼宮ハルヒの並列
R46JKB = ファントム・ブレイブWii
R49J01 = Wiiであそぶ ドンキーコングジャングルビート
@ -69,7 +69,7 @@ R8DJA4 = 遊戯王ファイブディーズ デュエルトランサー
R8EJQC = アースシーカー
R8FJHA = 匠レストランは大繁盛!
R8GJC8 = ジーワンジョッキー Wii 2008
R8NJG0 = バッティング レボリューション
R8NJG0 = 日本野球機構承認 - バッティング レボリューション
R8PJ01 = スーパーペーパーマリオ
R92J01 = Wiiであそぶ ピクミン2
R96JAF = 風のクロノア -door to phantomile-
@ -95,7 +95,7 @@ RC5JDQ = お掃除戦隊くりーんきーぱー
RCAJ78 = カーズ
RCCJC0 = クッキングママ みんなといっしょにお料理大会
RCDE52 = コール オブ デューティ3
RCHJAF = WE CHEER ~ウィーチア~
RCHJAF = WE CHEER
RCOJ99 = 名探偵コナン -追憶の幻想-
RCPJ18 = コロリンパ
RCQJDA = チョロQ Wii
@ -107,7 +107,7 @@ RDBJAF = ドラゴンボールZ Sparking! NEO
RDDJA4 = ダンスダンスレボリューション ホッテストパーティー
RDEJ0A = 全国デコトラ祭り
RDGJA4 = 悪魔城ドラキュラ ジャッジメント
RDIJG2 = THE DOG ISLAND -ひとつの花の物語-
RDIJG2 = Artlist Collection: The Dog Island ~ひとつの花の物語~
RDKJ01 = ドンキーコング たるジェットレース
RDMJ8N = Go!Go!ミノン
RDOJ41 = わんこと魔法のぼうし
@ -117,7 +117,7 @@ RDSJAF = ドラゴンボールZ Sparking! METEOR
RDTJAF = たまごっちのピカピカだいとーりょー!
RDUJDQ = スゴロクロニクル 〜右手に剣を左手にサイコロを〜
RDWJG9 = ドラゴンブレイド
RDXJ18 = デカスポルタ
RDXJ18 = Deca Sporta: Wiiでスポーツ"10"種目!
RDZJ01 = ディザスター デイ オブ クライシス
RE4J08 = バイオハザード
RE8J99 = 家庭教師ヒットマンREBORN! 禁斷の闇のデルタ
@ -151,13 +151,13 @@ RFUJA4 = 麻雀格闘倶楽部Wii Wi-Fi対応
RG2JJF = ギルティギア イグゼクス アクセントコア
RG4JC0 = 電車でGO! 新幹線EX 山陽新幹線編
RGCJJF = プチコプターWii アドベンチャーフライト
RGEJJ9 = ザ ワールド オブ ゴールデンエッグス
RGEJJ9 = ザ ワールド オブ ゴールデンエッグス: ノリノリリズム系
RGGJAF = ゲゲゲの鬼太郎 妖怪大運動会
RGHJ52 = ギターヒーロー3 レジェンド オブ ロック
RGIJC8 = ジーワンジョッキーWii
RGNJAF = 銀魂 万事屋ちゅ〜ぶ ツッコマブル動画
RGOJJ9 = ザ・ワールド・オブ・ゴールデンエッグス日産NOTE版
RGPJAF = パチスロ「機動戦士ガンダムII 〜哀・戦士編〜」
RGOJJ9 = ザ ワールド オブ ゴールデンエッグス: ノリノリリズム系 - Nissan Note オリジナルバージョン
RGPJAF = アニメスロットレボリューション パチスロ機動戦士ガンダムII ~哀・戦士編~
RGSJ8P = ゴースト・スカッド
RGTJBL = ジーティー プロ シリーズ
RGVJ52 = ギターヒーロー エアロスミス
@ -278,6 +278,7 @@ RQRJAF = スカイ・クロラ イノセン・テイセス
RR3JA4 = ファミリーチャレンジWii
RRBJ41 = ラビッツ・パーティー
RRSJ4Q = ルイスと未来泥棒 ウィルバーの危険な時間旅行
RRTE52 = 블록 파티! 20 게임들
RRUJJF = WINTER SPORTS 2009 - THE NEXT CHALLENGE
RRWJAF = スーパーロボット大戦NEO
RS3J52 = スパイダーマン3
@ -307,7 +308,7 @@ RT3JEL = Rockstar Games presents Table Tennis
RT4JAF = テイルズ オブ シンフォニア ラタトスクの騎士
RTDJES = 新・中華大仙 マイケルとメイメイの冒険
RTFJ52 = トランスフォーマー THE GAME
RTGJ18 = Wi-Fi対応 厳選テーブルゲームWii
RTGJ18 = 厳選テーブルゲーム Wii
RTIJ8P = 珍スポーツ
RTKJDQ = しゃるうぃ〜☆たころん
RTLJ18 = めざせ!!釣りマスター -世界にチャレンジ編-
@ -315,6 +316,7 @@ RTNJCQ = 天誅4
RTOJ8P = 428 ~封鎖された渋谷で~
RTRJ18 = めざせ!!釣りマスター
RTTJAF = たまごっちのフリフリ歌劇団
RTYP01 = 通信対局 ワールドチェス
RTZJ08 = 宝島Z バルバロスの秘宝
RUFJ99 = ルーンファクトリー フロンティア
RUNJ0Q = NEW 右脳キッズWii
@ -365,7 +367,7 @@ RZ8JG9 = SIMPLE 2000シリーズWiiVol.1 THEテーブルゲーム
RZ9JG9 = SIMPLE 2000シリーズWii Vol.2 THEパーティーゲーム
RZDJ01 = ゼルダの伝説 トワイライトプリンセス
RZJJ13 = デッドスペース エクストラクション
RZNJ01 = 斬撃のREGINLEIV
RZNJ01 = 斬撃のレギンレイヴ
RZPJ01 = リンクのボウガントレーニング
RZTJ01 = Wiiスポーツ リゾート
RZTW01 = Wiiスポーツ リゾート
@ -376,7 +378,7 @@ S2AJAF = みんなで冒険!ファミリートレーナー
S2LJ01 = ポケパーク2 ビヨンド・ザ・ワールド
S2PJA4 = ウイニングイレブン プレーメーカー 2012
S2TJAF = 太鼓の達人Wii ドドーンと2代目!
S3DJ18 = デカスポルタ3
S3DJ18 = Deca Sporta 3: Wiiでスポーツ
S3HJ08 = 戦国BASARA3 宴
S3RJMS = トウィンクルクイーン
S3SJ18 = カラオケJOYSOUND Wii SUPER DX
@ -384,7 +386,7 @@ S3TJAF = 太鼓の達人Wii みんなでパーティ☆3代目
S4MJGD = ドラゴンクエストX 目覚めし五つの種族 オンライン
S4SJGD = ドラゴンクエストX 眠れる勇者と導きの盟友 オンライン
S59JC8 = 戦国無双3
S5KJAF = 太鼓の達人WII 超ごうか版
S5KJAF = 太鼓の達人Wii 超ごうか版
S5QJC8 = 戦国無双3 猛将伝
S5SJHF = イナズマイレブンGO ストライカーズ 2013
S6TJGD = ドラゴンクエストX オールインワンパッケージ
@ -409,7 +411,7 @@ SD9JAF = SDガンダム ガシャポンウォーズ
SDJJAF = SDガンダム Gジェネレーション ワールド
SDQJGD = ドラゴンクエストX いにしえの竜の伝承 オンライン
SDWJ18 = 影の塔
SEAJ13 = パーソナルトレーナーWii6週間ひきしめプログラム
SEAJ13 = EA Sports アクティブ パーソナルトレーナー: Wii 6週間集中ひきしめプログラム
SEKJ99 = イケニエノヨル
SEMJ01 = ディズニー エピックミッキー ~ミッキーマウスと魔法の筆~
SEPE41 = ブラック・アイド・ピーズ・エクスペリエンス スペシャル・エディション
@ -498,69 +500,507 @@ SVMJ01 = スーパーマリオコレクション
SW4JA4 = ウイニングイレブン プレーメーカー 2011
SX3J01 = パンドラの塔 君のもとへ帰るまで
SX4J01 = ゼノブレイド
SX6JAF = プリキュア オールスターズ ぜんいんしゅうごう☆レッツダンス!
SX6JAF = プリキュア オールスターズ: ぜんいんしゅうごう☆レッツダンス!
DBSBT3 = Dragon ball z SparkingMeteor
DQAJSC = アクエリアスベースボール 限界の、その先へ
G2MK01 = メトロイドプライム2 ダークエコーズ
G4NEDA = カンタン操作で、超爽快忍者アクションが楽しめる!
G4SK01 = ゼルダの伝説 4つの剣+
GBIK08 = バイオハザード
GCDK08 = バイオハザード コード:ベロニカ完全版
GEAK8P = エターナルアルカディア レジェンド
GFEK01 = ファイアーエムブレム 蒼炎の軌跡
GGPE01 = マリオカート アーケードグランプリ
GGPE02 = マリオカート アーケードグランプリ2
GLMK01 = ルイージマンション
GM8K01 = メトロイドプライム
GMSE02 = Multiplayer Sunshine Super Mario
GMSK01 = スーパーマリオサンシャイン
GVS32J = バーチャストライカー3 Ver.2002 (トライフォース)
GVS45J = バーチャストライカー4
GZ2K01 = ゼルダの伝説 トワイライトプリンセス
GZBEB2 = 金色のガッシュベル!! ゴー!ゴー!魔物ファイト!!
GZLK01 = ゼルダの伝説 風のタクト
R24E01 = Wiiであそぶ ちびロボ!
R7CE01 = キャプテン★レインボー
R8EEQC = アースシーカー
R8FJSC = 匠レストランは大繁盛!
RMCEYP = ヨッシーレーシングリゾートプラス
RMCJ12 = マリオカートWii カスタム(2011-11 Wiimm)
RMCJ86 = マリオカートクリス3,500CT
RMCJBR = マリオカート Brown
RMCJYP = ヨッシーレーシングリゾートプラス
RMCKYP = ヨッシーレーシングリゾートプラス
RMCPCA = マリオカートWiiカタルーニャ語版
RMCPYP = ヨッシーレーシングリゾートプラス
ROSE01 = タクトオブマジック
RYAJSC = ヤッターマンWii ビックリドッキリマシンで猛レースだコロン
RZNE01 = 斬撃のレギンレイヴ
SEKE99 = Ikenie no Yoru
SMNEXE = 強化されたスーパーマリオブラザーズ.Wiiデラックス
SNBE66 = マグマスーパーマリオブラザーズWii黙示録
W2OJ = Blue Oasis: 미지의 심해
W2TJ = オニトレ~教官は鬼軍曹~
W34J = @SIMPLEシリーズ Vol.4 THE 密室からの脱出
W3DJ = 3° C
W42J = F・O・R・T・U・N・E フォーチュン~星のふりそそぐ丘~
W4KJ = Shikagari
W4OJ = Shikakui Atama wo Marukusuru Challenge
W82J = Jintori Action Taikokenchi Karakuri Shiro no Nazo
W8CJ = BIT.TRIP CORE: Rhythm Seijin no Gyakushuu
W8DJ = Mebius Drive
W8IJ = Hachi-One Diver Wii
W8PJ = Ouchi de Mugen Puchi Puchi Wii
W9IJ = 危険空域
WA2J = みんなでパズループ
WAQJ = 役満 井出洋介 の 健康 麻将
WBTJ = ファンタジックタンバリン FANTASIC TAMBOURINE
WA4J = WarioWare: D.I.Y. Showcase
WA8J = Art Style: Penta Tentacles
WALJ = Art Style: Lightstream
WAQJ = 役満 ii 井出洋介 の 健康 麻将
WARJ = Tsuushin Taikyoku Igo Doujou 2700 Mon
WASJ = Tsuushin Taikyoku: Hayazashi Syogi Sandan
WAUJ = Tsūshin Taikyoku: World Chess
WBJJ = Bokujou Monogatari Series: Makiba no Omise
WBMJ = Minna no Pokémon Bokujou
WCKJ = chick chick BOOM
WCSJ = Cue Sports: Wi-Fi Taisen Billiards
WCUJ = Atsui 12-Game: FuriFuri Party!
WD2J = Simple Wii Series Vol. 2: The Number Puzzle Neo
WD9J = ドラキュラ伝説 ReBirth
WDBJ = ダービードッグ
WDHJ = Art Style: Dialhex
WDIJ = Simple Wii Series Vol. 1: The Block Kuzushi Neo
WDMJ = Dr.MARIO&細菌撲滅
WDNJ = Discipline Teikoku no Tanjyou
WDPJ = Dr. Mario Online Rx (Friend Battle Demo)
WE6J = Sea Farm: Iruka to Watashi no Showtime
WEMJ = 1 Nuke! Dasshutsu Game * My Home Hen
WERJ = Blue Oasis: The Healing Space of Fish
WETJ = Asoberu Ehon: Tobida Sugoroku
WF2J = Final Fantasy Crystal Chronicles: Hikari to Yami no Himegimi to Sekai Seifuku no Tou
WF4J = Final Fantasy IV: The After Years - Tsuki no Kikan
WF5J = Okiraku Daifugou Wii
WFBJ = Beach e Oki o Tsukuccha Wow!
WFCJ = Final Fantasy Crystal Chronicles: Chiisana Ousama to Yakusoku no Kuni
WFPJ = ひらめきカードバトル メクルカ
WFSJ = みんなのシアターWii
WG2J = Sugar Bunnies Wii: Youkoso * Bunnies Field e
WGDJ = Gradius Rebirth: Updated
WGGJ = Ushimitsu Monstruo Puchi: Fushigi na Oshiro no Dance Party
WGMJ = Game SoundStation
WGOJ = World of Goo
WGPJ = Aqua Living: TV de Nagameru Uotachi
WGSJ = Gyakuten Saiban: Yomigaeru Gyakuten
WHHJ = Let's 全力ヒッチハイク!!!!!!!!!
WIKJ = Ivy the Kiwi? Mini
WINJ = Chokkan! Balance * Labyrinth
WJ2J = Jinsei Game: Happy Step
WK2J = Kappa-kun to Asobou: Kappa-kun to Ota no Shimikai
WK3J = Kappa-kun to Asobou: Kappa-kun to Mori no Nakamatachi
WK9J = Minna de Asobou Koinu de Kururin
WKEJ = RakuRaku Kinen Apori Wii: Kinenka no Isha ga Osheru Nanoka de yameru Houhou
WKKJ = Kurohige Kiki Ippatsu
WKNJ = Kanken Minna de Waiwai Kanji Nou
WKPJ = Kappa Kun to Mori no Nakama Tachi
WKQJ = Kentei! TV Wii Minna de Gotouchi Quiz Battle
WKWJ = Item Sagashi * Yousei to Fushigi no Shima
WLDJ = Boku mo Sekai o Sukuitai: Battle Tournament
WLEJ = Pooyoo to Asobou Episode 1
WLJJ = Boku mo Sekai o Sukuitai: Battle Tournament
WLKJ = リカちゃんおしゃれハウス
WLMJ = La Mulana
WLOJ = LostWinds: Winter of the Melodias
WM8J = Wi-Fi 8-Nin Battle Bomberman
WM9J = たたいて!モグポン
WMBJ = Katachi no Game: Marubou Shikaku
WMLJ = Major League Eating: The Game
WMOJ = Antaga Mawashite Sukuu Puzzle: Mochimochi Q
WMPJ = ことばのパズル もじぴったんWii
WMXJ = Max & the Magic Marker
WN9J = NECTARISネクタリス
WNPJ = すぐスロDUO「ニューパルサーRV」
WNVJ = ハメコミ  LUCKY PUZZLE Wii リターン
WNWJ = Hamekomi Lucky Puzzle Wii Return
WOBJ = Art Style: Orbital
WODJ = 王だぁ!
WOKJ = カラオケJOYSOUND Wii
WOXJ = Osu! Exercise Dojo
WOYJ = Bit Man
WOZJ = Kodomo Kyouiku Telebi Wii: Aiue-Oumuzu
WP5J = ポコスカれーしんぐ
WP6J = Boku wa Plarail Untenshi: Shinkansen Joukikikansha-Hen
WP9J = Po-Ka-Zu Wii
WPAJ = ポケモン不思議のダンジョン いくぞ!嵐の冒険団
WPDJ = 珍道中!!ポールの大冒険
WPFJ = ポケモン不思議のダンジョン すすめ!炎の冒険団
WPGJ = Penguin Life
WPHJ = ポケモン不思議のダンジョン めざせ!光の冒険団
WPIJ = Pit Crew Panic
WPNJ = Ponjan
WPPJ = Okiraku Ping Pong
WPRJ = Art Style: Cubeleo
WPSJ = Ransen Pokémon Scramble
WPTJ = FANTASIC CUBE ファンタジックキューブ
WPVJ = Kumanage Battle-Hen: Kiina no Kirai na Aoi Hoseki
WPXJ = Minna de Tobikome Penguin Diving Hooper Looper
WQ4J = Kentoushi FuriFuri Boxing
WR9J = ロックマン9 野望の復活!!
WRIJ = Rainbow Islands: Towering Adventure!
WRNJ = BIT.TRIP RUNNER
WRXJ = 宇宙からの脅威!!
WS8J = Minna de Taisen Puzzle Shanghai
WSAJ = MadSecta
WSCJ = Out of Galaxy: Gin no Koshika
WSGJ = さめがめ Wii
WSLJ = Shadow Walker
WSNJ = Sonic the Hedgehog 4: Episode I
WSUJ = シュータント過去編
WT8J = はじいて! ブロック ラッシュ
WTBJ = ファンタジックタンバリン FANTASIC TAMBOURINE
WTDJ = Tomica Drive
WTRJ = BIT.TRIP BEAT
WUHJ = Wiiでウルトラハンド
WUKJ = Unou Kids Okigaru Unou Training
WUNJ = Uno
WVBJ = BIT.TRIP VOID
WVDJ = Kodomo Kyouiku TV Wii: Aiue-O-Chan
WVOJ = Rock n' Roll Climber
WWRJ = Excitebike: World Race
WYKJ = Yomi Kiku Asobi
WZHJ = Animal Life: Doubutsu Fureai Seikatsu
WZJJ = @ SIMPLEシリーズ Vol.5 THE 柔道
WZMJ = Simple Wii Series Vol. 3: The Mahjong
WZPJ = ゾンビ イン ワンダーランド
WZZJ = Kumanage Puzzle-Hen: Piina no Suki na Akai Candy
FA5J = Fire Emblem: Ankoku Ryu to Hikari no Tsurugi
FA6J = Donkey Kong Jr. no Sansuu Asobi
FA8J = Hoshi no Kirby: Yume no Izumi no Monogatari
FA9J = The Legend of Zelda 2: Link no Bouken
FAQJ = Ninja Jajamaru-kun
FB2J = スーパーマリオブラザーズ2
FB3J = Valkyrie no Bouken: Toki no Kagi Densetsu
FBDJ = Hikari Shinwa: Palutena no Kagami
FBNJ = Ninja Ryuukenden
FBOJ = Gradius II
FBSJ = Meikyuu Kumikyoku: Milon no Daibouken
FC5J = Ganbare Goemon Karakuki Douchuu
FC8J = Dracula II: Noroi no Fuuin
FCFJ = Yie Ar Kung Fu
FCIJ = Volguard 2
FCJJ = SD Gundam World: Gachapon Senshi 2 - Capsule Senki
FCRJ = Takahashi Meijin no Boukenjima
FCTJ = RockMan
FCZJ = Kings Knight
FD3J = Nekketsu Koukou Dodgeball-bu Soccer-hen
FD7J = RockMan 3: Dr. Wily no Saigo?!
FDBJ = Famicom Mukashi Banashi: Shin Onigashima - Kouhen
FDGJ = Makai-Mura
FDNJ = RockMan 2
FDSJ = Famicom Tantei Club: Kieta Koukeisha (Kouhen)
FDTJ = Renegade
FDUJ = Nekketsu Koukou Dodge Ball Bu
FDWJ = Downtown Special: Kunio-kun no Jidaigeki Dayo Zenin Shuugou!
FDXJ = Famicom Tantei Club Part II: Ushiro ni Tatsu Shoujo (Kouhen)
FDZJ = Downtown Nekketsu Koushinkyoku
FE5J = Toukaidou Gojuusan Tsugi
FE6J = Ninja kun Majyou no Bouken
FE7J = Ninja kun Ashura no Shou
FE9J = Ike Ike! Nekketsu Hockey-bu: Subette Koronde Dairantou
FEEJ = Tantei Jingūji Saburō: Shinjuku Chūō Kōen Satsujin Jiken
FEFJ = Detective Saburo Jinguji 2: Yokohama-Ko Renzoku Satsujin Jiken
FEHJ = Tantei Jinguuji Saburo: Toki no Sugiyuku Mama ni
FEJJ = Nazo no Murasame Jou
FELJ = Transformers: Convoy no Nazo
FEMJ = Bio-Miracle Bokutte Upa
FEOJ = Zoids: Mokushiroku
FEQJ = Akumajo Densetsu
FESJ = Clu Clu Land
FEXJ = Wagyan Land
FF2J = Sugoro Quest: Dice no Senshi Tachi
FF5J = Double Dragon 2: The Revenge
FF7J = Ganbare Goemon Gaiden: Kieta Ougon Kiseru
FFGJ = SD Gundam World: Gachapon Senshi - Scramble Wars
FFLJ = Salad no Kuni no Tomato Hime
FFMJ = Chou-Wakusei Senki MetaFight
FFNJ = RockMan 4: Aratanaru Yabou!!
FFOJ = Moero TwinBee: Cinnamon Hakase o Sukue!
FFPJ = Furu Furu Park
FFUJ = Takahashi Meijin no Bouken Jima II
FFXJ = 25th スーパーマリオブラザーズ
FFYJ = RockMan 5: Blues no Wana!?
JA4J = Chou-Makai-Mura
JA5J = Heracles no Eikou III: Kamigami no Chinmoku
JA7J = Actraiser
JADJ = Zelda no Densetsu: Kamigami no Triforce
JAFJ = SimCity
JAGJ = Fire Emblem: Monshou no Nazo
JAHJ = R-Type III: The Third Lightning
JAJJ = Street Fighter II
JAPJ = Fire Emblem: Seisen no Keifu
JAUJ = Famicom Bunko: Hajimari no Mori
JAWJ = San Goku Shi IV
JAZJ = Ganbare Goemon: Yukihime Kyuushutsu Emaki
JB7J = Front Mission: Gun Hazard
JBBJ = Super Street Fighter II: The New Challengers
JBDJ = Super Donkey Kong 2: Dixie & Diddy
JBGJ = Mystery Dungeon: Shiren the Wanderer
JBHJ = Heracles no Eikou IV: Kamigami-kara no Okurimono
JBIJ = Street Fighter II Turbo: Hyper Fighting
JBKJ = Breath of Fire II: Shimei no Ko
JBPJ = Super Donkey Kong 3: Nazo no Krems Shima
JBVJ = Der Langrisser
JBWJ = Assault Suits Valken
JCAJ = DoReMi Fantasy: Milon no DokiDoki Daibouken
JCEJ = Fire Emblem: Thracia 776
JCGJ = Ganbare Goemon 2: Kiteretsu Shougun Magginesu
JCHJ = Ganbare Goemon 3
JCIJ = Famicom Tantei Club Part II: Ushiro ni Tatsu Shoujo
JCKJ = Space Invaders: The Original Game
JCMJ = Super Wagyan Land
JCTJ = Densetsu no Ogre Battle: The March of the Black Queen
JCUJ = Tactics Ogre: Let Us Cling Together
JCXJ = Super Nobunaga no Yabou: Zengokuban
JCZJ = Super Aoki Ookami to Shiroki Meshika: Genchou Hishi
JD3J = Super E.D.F. Earth Defense Force
JD5J = Rushing Beat Ran: Fukusei Toshi
JD9J = Chou-Genjin
JDOJ = Heisei Shin Onigashima: Zenpen
JDPJ = Heisei Shin Onigashima: Kouhen
JDQJ = Romancing Sa-Ga 3
JDTJ = Shin Megami Tensei if...
JDYJ = Rudra no Hihou
JDZJ = Final Fantasy USA: Mystic Quest
JEBJ = RockMan X
JEDJ = Kunio-Kun no Dodge Ball Dayo Zenin Shuugo!
JEFJ = Majin Tensei II: Spiral Nemesis
JEGJ = Gouketuji Ichizoku
JEHJ = RockMan X2
JEKJ = Kyūyaku Megami Tensei: Megami Tensei I・II
NA3J = Bomberman Hero
NACJ = Zelda no Densetsu: Toki no Ocarina
NAIJ = Wave Race 64: Kawasaki Jet Ski
NAJJ = Tsumi to Batsu
NALJ = Nintendo All-Star Dairantou Smash Brothers
NAOJ = 1080° Snowboarding
NARJ = Zelda no Densetsu: Majora no Kamen
NAYJ = Ogre Battle 64: Person of Lordly Caliber
LAEJ = Alex Kidd in Miracle World
LAGJ = Sonic the Hedgehog
LAJJ = Sonic the Hedgehog 2
LAKJ = Super Wonder Boy: Monster World
LALJ = Fantasy Zone II: The Tears of Opa-Opa
LANJ = Alex Kidd: The Lost Stars
MA3J = Puyo Puyo Tsuu
MA6J = Bare Knuckle II: Shitou no Chinkon Uta
MA7J = Shining and the Darkness
MAAJ = Juuouki
MAEJ = Golden Axe
MAHJ = Sonic the Hedgehog
MAKJ = Shadow Dancer: The Secret of Shinobi
MALJ = Bonanza Bros.
MAMJ = Vermilion
MAOJ = Crying: Aseimei Sensou
MAQJ = Bare Knuckle: Ikari no Tekken
MARJ = The Story of Thor: Hikari o Tsugu Mono
MAUJ = Puzzle & Action: Tant-R
MAVJ = Wonder Boy V: Monster World III
MAXJ = Alex Kidd: Tenkuu Mashiro
MB5J = Rangurissah II
MB6J = Shining Force II: Inishie no Fuuin
MB8J = Phantasy Star II: Kaerazaru Toki no Owari ni
MB9J = Pepenga Pengo
MBBJ = Sonic the Hedgehog 2
MBDJ = Golden Axe II
MBEJ = Shining Force: Kamigami no Isan
MBFJ = The Super Soldier II
MBHJ = Puzzle & Action: Ichidant-R
MBIJ = ランドストーカー 皇帝の財宝
MBJJ = Dai Makai-Mura
MBLJ = ESWAT: Cyber Police
MBMJ = Sonic the Hedgehog 3
MBOJ = Golden Axe III
MBQJ = Bare Knuckle III
MBVJ = Jusu Kihei Leynos
MBWJ = Columns III: Taiketsu! Columns World
MC3J = Super Street Fighter 2: The New Challengers
MCCJ = Toki no Keishousha: Phantasy Star III
MCFJ = フェリオス(メガドライブ版)
MCHJ = Musha Aleste
MCJJ = Splatterhouse Part 2
MCKJ = Phantasy Star: Sennenki no Owari ni
MCLJ = Street Fighter II' Plus: Champion Edition
MCMJ = Lord Monarch: Tokoton Sentou Densetsu
MCNJ = Hokuto no Ken: Shin Seikimatu Kyuseisyu Densetsu
MCRJ = Senjou no Ookami II
MCSJ = Wonder Boy III: Monster Lair
MCUJ = Dragon Slayer: The Legend of Heroes
MCVJ = Pitfall: The Mayan Adventure
PA2J = Nekketsu Koukou Dodgeball Bu: CD Soccer Hen
PA6J = Narazumono Sentou Butai: Bloody Wolf
PABJ = PC Genjin
PADJ = R-Type I
PAFJ = Victory Run: Eikou no 13,000KM
PAJJ = Joshoken Necromancer
PAMJ = Neutopia: Frey no Shou
PARJ = Detana TwinBee
PATJ = Kiki Kaikai
PAUJ = Kaizou Chounin Shubibinman
PBFJ = Fire ProWrestling: Combination Tag
PBHJ = PC Genjin 2
PBIJ = PC Genjin 3
PBJJ = Genpei Toumaden: Kannoni
PBNJ = Saigou no Nindou: Ninja Spirit
PBUJ = Chouzetsu Rinjin Bravoman
PBWJ = PC Denjin: Punkic Cyborgs
PBXJ = Kaizou Chounin Shubibinman 2: Atanaru Teki
PC4J = Space Invaders: Fukkatsu no Hi
PCBJ = Dead Moon: Tsuki Sekai no Akumu
PCMC = Gokuraku! Chuuka Taisen
PCSJ = Digital Champ: Battle Boxing
PDAJ = 熱血高校ドッジボール部® PC番外編
PDEJ = S.C.I.: Special Criminal Investigation
PDGJ = Fire ProWrestling 3: Legend Bout
PDIJ = チャンピオンレスラー®
PDJJ = Street Fighter II': Champion Edition
QA2J = Nekketsu Koukou Dodge Ball-Bu: CD Soccer-hen
QA4J = Super Darius II
QA5J = Space Invaders: The Original Game
QA7J = Legend of Xanadu: Kaze no Densetsu Xanadu
QA9J = Kaze no Densetsu: Xanadu II
QAAJ = CD Denjin: Rockabilly Tengoku
QABJ = Ys I + II
QADJ = Gradius II: Gofer no Yabou
QAEJ = A.III: A-Ressha de Ikou III
QAFJ = Chou Eiyuu Densetsu: Dynastic Hero
QAKJ = Ai Chou Aniki
QALJ = The Atlas: Renaissance Voyager
QAPJ = Akumajou Dracula X: Chi no Rondo
QAQJ = Dragon Slayer: Eiyuu Densetsu
QAUJ = Wonder Boy III: Monster Lair
QAVJ = Bomberman: Panic Bomber
QAZJ = Eikan wa Kimi ni: Kōkō Yakyū Zenkoku Taikai
QBAJ = L Dis
EA3J = Galaxy Fight: Universal Warriors
EA4J = Samurai Spirits Kibeniro Musouken
EA5J = Garou Densetsu 3: Road to the Final Victory
EA7J = Samurai Spirits: Amakusa Kourin
EA8J = Ironclad: Chotetsu Brikinger
EAAJ = Garou Densetsu: Shukumei no Takatai
EAEJ = Shin Samurai Spirits: Haohmaru Jigokuhen
EAIJ = Top Hunter
EANJ = Garou Densetsu 2: Aratanaru Tatakai
EASJ = Shin Samurai Spirits Haoumaru Jigokuhen
EAYJ = King of The Monsters 2
EB4J = ASO II: Last Guardian
EB5J = The Last Blade
EBNJ = Fu'un Mokushiroku: Kakutou Sousei
EBSJ = Art of Fighting: Ryuuko no Ken Gaiden
ECAJ = Real Bout Garou Densetsu 2: The Newcomers
ECDJ = Stakes Winner: G1 Kanzen Seihahe no Michi
ECEJ = Bakumatsu Rouman Dai Ni Maku: Gekka no Kenshi
ECGJ = Shock Troopers: 2nd Squad
ECHJ = The King of Fighters '98: Dream Match Never Ends
ECMJ = Tokuten Oh: Honoo no Libero
E55J = Senji no Ookami
E56J = Exed Eyes
E5VJ = Renegade
E6JJ = Bakutotsu Kijuutei: BaRaDuKe II
E6LJ = Marchen Maze
E6NJ = Solomon no Kagi
E6OJ = Bomb Jack Arcade
E6PJ = Ninja Ryukenden Arcade
E6QJ = Argos no Senshi
E6XJ = 獣王記(アーケード版)
E72J = Starblade
E74J = Cosmo Gang the Puzzle
E77J = The Return of Ishtar
E7LJ = Cosmo Gang the Video
E7XJ = Youkai Douchuki
C9SP = 불가능한 임무 II
XADJ = Yie Ar Kung-Fu 2
XAHJ = Penguin Adventure: Yume Tairiku Adventure
XAKJ = Parodius - Tako wa Chikyuu wo Sukuu
XAMJ = Knightmare: Majou Densetsu
XAOJ = Gofer no Yabou: Episode II
XAPJ = Metal Gear 2: Solid Snake
HAAA = 写真チヤンネル
HABA = Wiiショッピングチャンネル
HADE = インターネットチャンネル
HADJ = インターネットチャンネル
HAFJ = お天気チャンネル
HAFP = お天気チャンネル
HAGA = ニュースチャンネル
HAGE = ニュースチャンネル
HAGJ = ニュースチャンネル
HAGP = ニュースチャンネル
HAPP = Miiコンテストチャンネル
HATJ = Nintendo Channel
HATP = みんなのニンテンドーチャンネル
HAVJ = Today and Tomorrow Fortune Teller
HAVP = きょうとあしたの占いラッキーチャンネル
HBNJ = テレビの友チャンネル Gガイド for Wii
HC3J = USB乄モリ一修復プ囗グラム
HCAJ = バンブラDXスピーカーチャンネル
HCCJ = 住所情報
HCDJ = デジカメプリントチャンネル
HCFP = Wiiスピークチャンネル
HCGJ = インターネットにつなぐとできること
HCHJ = 出前チャンネル
HCIJ = Wiiの間
HCRE = ゼルダの伝説スカイウォードソード データ修復チャンネル
HCRJ = The Legend of Zelda Skyward Sword Update
HCRP = ゼルダの伝説スカイウォードソード データ修復チャンネル
HCYJ = Wii U本体更新 修復プログラム
HFNJ = Wii Fit からだチェックチャンネル
RMCJ = マリオカートチャンネル
DSYA = 시스체크
D29J01 = 月刊任天堂店頭デモ 5月号
D32J01 = 月刊任天堂店頭デモ 6月号
D33J01 = 月刊任天堂店頭デモ 7月号
D34J01 = 月刊任天堂店頭デモ 7月増刊号
D38J01 = 月刊任天堂店頭デモ 9月号
D39J01 = 月刊任天堂店頭デモ 10月号
D42J01 = 月刊任天堂店頭デモ 11月号
D43J01 = ゼルダの伝説 時のオカリナ GC
D44J01 = 月刊任天堂店頭デモ 12月号
D46J01 = 月刊任天堂店頭デモ 1月号
D47J01 = 月刊任天堂店頭デモ 2月号
D48J01 = 月刊任天堂店頭デモ 3月号
D49J01 = 月刊任天堂店頭デモ 4月号
D52J01 = 月刊任天堂店頭デモ 5月号
D53J01 = ニンテンドーゲームキューブ ソフトeカタログ2003・春
D54J01 = 月刊任天堂店頭デモ 6月号
D59J01 = 月刊任天堂店頭デモ 7月号
D62J01 = 月刊任天堂店頭デモ 8月号
D64J01 = 月刊任天堂店頭デモ 10月号
D65J01 = 月刊任天堂店頭デモ 11月号
D67J01 = 月刊任天堂店頭デモ 12月号
D68J01 = 月刊任天堂店頭デモ 1月号
D73J01 = 月刊任天堂店頭デモ 2月号
D75J01 = 月刊任天堂店頭デモ 3月号
D77J01 = 月刊任天堂店頭デモ 4月号
D78J01 = 月刊任天堂店頭デモ 5月号
D79J01 = 月刊任天堂店頭デモ 6月号
D82J01 = 月刊任天堂店頭デモ 7月号
D84J01 = 月刊任天堂店頭デモ 8月号
D85J01 = 月刊任天堂店頭デモ 9月号
D86J01 = 月刊任天堂店頭デモ 10月号
D87J01 = 月刊任天堂店頭デモ 11月号
D88J01 = 月刊任天堂店頭デモ 12月号
D89J01 = クラブニンテンドー オリジナルeカタログ 2004
D92J01 = 月刊任天堂店頭デモ 2004‐2005年末年始号
D93J01 = 月刊任天堂店頭デモ 1月号
D94J01 = 月刊任天堂店頭デモ 2月号
D97J01 = 月刊任天堂店頭デモ 5月号
E23J01 = 月刊任天堂店頭デモ 9月号
E24J01 = 月刊任天堂店頭デモ 10月号
E25J01 = 月刊任天堂店頭デモ 11月号
E27J01 = 月刊任天堂店頭デモ 1月号
E32J01 = 月刊任天堂店頭デモ 4月号
E34J01 = 月刊任天堂店頭デモ 6月号
E35J01 = 月刊任天堂店頭デモ 7月号
E36J01 = 月刊任天堂店頭デモ 8月号
E37J01 = 月刊任天堂店頭デモ 9月号
G2DJB2 = デジモンバトルクロニクル
G2GJB2 = 機動戦士ガンダム ガンダムvs.Zガンダム
G2MJ01 = メトロイドプライム2 ダークエコーズ
@ -569,7 +1009,7 @@ G2SJGE = 式神の城II
G2VJ08 = ビューティフル ジョー 2 ブラックフィルムの謎
G2XJ8P = ソニックジェムズコレクション
G3AJ13 = ロード・オブ・ザ・リング 中つ国第三紀
G3EJ51 = エクストリームG3
G3EJB0 = エクストリームG3
G3NJDA = NARUTOナルト激闘忍者大戦3
G3SJC0 = スーパーパズルボブル オールスターズ
G3TJ8P = ダビつく3 ダービー馬をつくろう!
@ -584,6 +1024,7 @@ G4SJ01 = ゼルダの伝説 4つの剣+
G4WJ99 = 牧場物語 しあわせの詩 for ワールド
G8FJ8P = バーチャファイター サイバージェネレーション
G8MJ01 = ペーパーマリオRPG
G8MK01 = ペーパーマリオRPG
G8OJ18 = ボボボーボ・ボーボボ 脱出!!ハジケ・ロワイヤル
G8SJAF = バトルスタジアムD.O.N
G8WJ01 = 突撃!!ファミコンウォーズ
@ -693,6 +1134,7 @@ GKBJAF = バテン・カイトス 終わらない翼と失われた海
GKDJ01 = 巨人のドシン
GKEJA4 = 実況パワフルプロ野球12決定版
GKFJ8P = カオスフィールド エクスパンデッド
GKFJMS = カオスフィールド エクスパンデッド
GKGJ01 = ドンキーコンガ
GKJJ78 = カーズ
GKLJ13 = ロード・オブ・ザ・リング/王の帰還
@ -771,7 +1213,7 @@ GSMJ08 = スパイダーマン
GSOJ8P = ソニック メガコレクション
GSSJ8P = セガ サッカースラム
GSTJ13 = SSX トリッキー
GSWJ64 = スター・ウォーズ ローグ スコードロン2
GSWJ13 = スター・ウォーズ ローグ スコードロン2
GSXJ13 = スター・ウォーズ クローン戦争
GT2J18 = 天外魔境II MANJIMARU
GT3J52 = トニーホークのプロスケーター3
@ -797,7 +1239,7 @@ GVWJDQ = 学園都市 ヴァラノワールローゼス
GW3JG2 = レッスルマニアX8
GW6JEM = ワールドサッカー ウイニングイレブン6 ファイナルエヴォリューション
GW9JG2 = レッスルマニアXIX
GWEJ51 = 18 Wheeler
GWEJB0 = 18 Wheeler
GWGJ4F = わいわいゴルフ
GWPJG2 = WWE デイ・オブ・レコニング
GWRJ01 = ウェーブレース ブルーストーム

File diff suppressed because it is too large Load diff

View file

@ -1,4 +1,4 @@
TITLES = https://www.gametdb.com (type: Wii language: NL_unique version: 20191106234309)
TITLES = https://www.gametdb.com (type: Wii language: NL_unique version: 20230727194218)
R23P52 = Barbie en De Drie Musketiers
R25PWR = LEGO Harry Potter: Jaren 1-4
R27X54 = Dora redt het Land van Kristal
@ -12,11 +12,11 @@ R42P69 = De Sims 2: Op een Onbewoond Eiland
R4CP69 = Simcity Creator
R4EP01 = Endless Ocean 2: Een zee vol avontuur
R4PP69 = De Sims 2: Huisdieren
R55F41 = Weekend Miljonairs
R55P41 = Weekend Miljonairs 1e Editie
R5FP41 = Academy of Champions
R5PP69 = Harry Potter en de Orde van de Feniks
R5PX69 = Harry Potter en de Orde van de Feniks
R5XJ13 = MySims Agents
R6XP69 = Hasbro: Familie Spellen Avond 2
R72P5G = Cake Mania: In The Mix!
R7YFMR = Peking Express
@ -65,7 +65,6 @@ RHNP70 = My Horse and Me
RHZP41 = Horsez: Plezier op de manege
RI2P4Q = High School Musical: Sing It!
RIGP54 = Go, Diego, Go! Het Grote Dinosaurus Avontuur
RIPPAF = One Piece Unlimited Cruise 1 - The Treasure Beneath the Waves
RIQPUJ = Dansen op het ijs
RJ9HMN = Think: Train je Brein
RJDPKM = Mijn Dierenkliniek
@ -76,7 +75,7 @@ RL2HMN = Paard & Pony: Mijn Paardenstal
RL2PFR = Paard & Pony: Mijn Paardenstal
RL7P69 = Littlest Pet Shop: Vrienden
RLHP52 = Little League World Series Baseball
RLLP70 = Go West!: Een Lucky Luke Avontuur!
RLLP70 = Go West! Een Lucky Luke Avontuur!
RLNFMR = Expeditie Robinson
RLNHMR = Expeditie Robinson
RLNIMR = Expeditie Robinson
@ -89,7 +88,6 @@ RNNP4Q = De Kronieken van Narnia: Prins Caspian
RNNX4Q = De Kronieken van Narnia: Prins Caspian
RNNY4Q = De Kronieken van Narnia: Prins Caspian
RNOP01 = Another Code: R - A Journey Into Lost Memories
RNSP69 = Need for Speed: Carbon
RO7P7D = De Legende van Spyro: De Eeuwige Nacht
RO8P7D = De Legende van Spyro: De Opkomst van een Draak
ROEPGT = Honden Hotel
@ -107,7 +105,6 @@ RQPP52 = cabela's big game hunter 2009
RQWPG9 = Puzzle Quest - Challenge of the Warlords
RQXP70 = Asterix en de Olympische Spelen
RRCP52 = Barbie Paardenavonturen: Het Paardrijkamp
RRHXUJ = In Mary Kings Riding School 2
RRMP69 = Hasbro Familie Spellen Avond
RRMX69 = Hasbro: Familie Spellen Avond
RRQP52 = Shrek - Crazy Party Games
@ -198,12 +195,143 @@ STOP4Q = Cars Toon: Takel's Sterke Verhalen
STYP52 = Tony Hawk : Shred
SVDP52 = SpongeBob Squarepants: Plankton's Robotic Revenge
SVMP01 = Super Mario All-Stars : 25th Anniversary Edition
SVQEVZ = Barbie En Haar Zusjes In Het Grote Puppy Avontuur
SVQPVZ = Barbie En Haar Zusjes In Het Grote Puppy Avontuur
SVZPVZ = Hoe Tem Je Een Draak 2
CG1P52 = Guitar Hero III Custom : Guitar Hero I
RMCPCA = Mario Kart Wii (Catalaanse vertaling)
W2FP = Physiofun - Balance Training
W2GP = Phoenix Wright Ace Attorney: Justice for All
W2MP = Blaster Master: Overdrive
W2PP = Physiofun: Pelvic Floor Training
W3GP = Phoenix Wright Ace Attorney: Trials and Tribulations
W3KP = ThruSpace: High Velocity 3D Puzzle
W3MP = The Three Musketeers: One for all
W44P = Stop Stress: A Day of Fury
W4AP = Arcade Sports: Air Hockey, Bowling, Pool, Snooker
W6BP = Eco-Shooter: Plant 530
W72P = Successfully Learning German Year 3
W73P = Successfully Learning German Year 4
W74P = Successfully Learning German Year 5
W7IP = Successfully Learning German Year 2
W8CP = Bit.Trip Core
W8WP = Happy Holidays: Halloween
W9BP = Big Town Shoot
W9RP = Happy Holidays: Christmas
WA4P = WarioWare: Do It Yourself - Showcase
WA7P = Toribash Violence Perfected
WA8P = Art Style: Penta Tentacles
WAEP = Around the world
WAFP = Airport Mania: First Flight
WAHP = Trenches: Generals
WALP = Art Style: light trax
WAOP = The Very Hungry Caterpillar´s ABC
WB2P = Strong Bad Episode 4: Dangeresque 3
WB3P = Strong Bad Episode 5: 8-bit is Enough
WBEP = Beer Pong: Frat Party Games
WBFP = Bit.Trip Fate
WBGP = Bang Attack
WBPP = PLÄTTCHEN - twist 'n' paint
WBRP = Pirates: The Key of Dreams
WBXP = Strong Bad Episode 1: Homestar Ruiner
WBYP = Strong Bad Episode 2: Strong Badia - The Free
WBZP = Strong Bad Episode 3: Baddest of the Bands
WCHP = Chess Challenge
WCJP = Cocoto: Platform Jumper
WCKP = chick chick BOOM
WCSP = CueSports: Snooker vs Billiards
WD9P = Castlevania: The Adventure ReBirth
WDEP = Magic Destiny Astrological Games
WDFP = Defend your Castle
WDHP = Art Style: ROTOHEX
WDPP = Dr. Mario & Germ Buster (Friend Battle Demo)
WEMP = Aha! I Got It! Escape Game
WETP = PictureBook Games: A Pop-Up Adventure
WF2P = Final Fantasy Crystal Chronicles: My Life as a Darklord
WF4P = Final Fantasy IV: The After Years
WFCP = Final Fantasy Crystal Chronicles: My Life as a King
WFQP = Frogger: Hyper Arcade Edition
WFTP = Fish'em All!
WFVP = Football Up
WFWP = Flowerworks: Follie's Adventure
WFYP = Family Games Pen & Paper Edition
WGDP = Gradius Rebirth
WGFP = Girlfriends Forever: Magic Skate
WGGP = Gabrielle's Ghostly Groove: Monster Mix
WGPP = Zenquaria: Virtual Aquarium
WGSP = Phoenix Wright: Ace Attorney
WHEP = Heracles: Chariot Racing
WHFP = Heavy Fire: Special Operations
WHRP = Heron: Steam Machine
WHWP = HoopWorld: BasketBrawl
WICP = NyxQuest: Kindred Spirits
WIDP = Dracula: Undead Awakening
WIEP = Tales of Monkey Island Chapter 3: Lair of the Leviathan
WILP = Tales of Monkey Island Chapter 1: Launch of the Screaming Narwhal
WIRP = Tales of Monkey Island Chapter 5: Rise Of The Pirate God
WISP = Tales of Monkey Island Chapter 2: The Siege of Spinner Cay
WITP = Aha! I Found It! Hidden Object Game
WIYP = Tales of Monkey Island Chapter 4: The Trial and Execution of Guybrush Threepwood
WJKP = Jewel Keepers: Easter Island
WKBP = You, Me and the Cubes
WKFP = Kung Fu Funk: Everybody Is Kung Fu Fighting
WKKP = Pop-Up Pirates!
WKRP = Karate Phants: Gloves of Glory
WKWP = Adventure on LOST ISLAND: Hidden Object Game
WLEE = Leren met de PooYoo's: Aflevering 1
WLEP = Leren met de PooYoo's: Aflevering 1
WLNP = Leren met de PooYoo's: Aflevering 2
WLOP = LostWinds: Winter of the Melodias
WLZP = lilt line
WM7P = Anima Ark of Sinners
WMBP = MaBoShi: The Three Shape Arcade
WMCP = Monsteca Corral: Monsters Vs. Robots
WMJP = Dive: The Medes Islands Secret
WMSP = Enjoy your massage!
WN9P = Military Madness: Nectaris
WNEP = Penguins & Friends Hey! Thats my Fish!
WNVP = Neves Plus: Phantheon of Tangrams
WOBP = Art Style: ORBIENT
WOTP = Overturn: Mecha Wars
WP3P = Pearl Harbor Trilogy 1941: Red Sun Rising
WP4P = Learning with the PooYoos: Episode 3
WPKP = Texas Hold'Em Poker
WPQP = Protöthea
WPRP = Art Style: CUBELLO
WPVP = The Tales of Bearsworth Manor: Chaotic Conflicts
WREP = Racers Islands Crazy Arenas
WRIP = Rainbow Islands: Towering Adventure!
WRJP = Racers Islands - Crazy Racers
WRLP = FAST Racing League
WRRP = Robin Hood: The Return Of Richard
WRUP = Bit.Trip Runner
WSGP = Pop Them, Drop Them SAMEGAME
WSNP = Sonic The Hedgehog 4 Episode I
WSUP = Shootanto: Evolutionary Mayhem
WTEP = Tales of Elastic Boy Mission 1
WTFP = Bit.Trip Flux
WTMP = Adventure Island: The Beginning
WTRP = Bit.Trip Beat
WTWP = Fenimore Fillmore: The Westerner
WTXP = Texas Holdem Tournament
WU2P = Successfully Learning Mathematics Year 3
WU3P = Successfully Learning Mathematics Year 4
WU4P = Successfully Learning Mathematics Year 5
WUIP = Successfully Learning Mathematics Year 2
WVBP = Bit.Trip Void
WVOP = Rock'n Roll Climber
WVSP = Gods Vs Humans
WVUP = Mr Bumblebee Racing Champion
WW2P = Where's Wally? Fantastic Journey 2
WW3P = Where's Wally? Fantastic Journey 3
WWIP = Where's Wally? Fantastic Journey 1
WWRP = Excitebike: World Challenge
WWXP = Paper Wars Cannon Fodder
WXBP = Ben 10: Alien Force - The Rise of Hex
WYIP = escapeVektor: Chapter 1
WYSP = Yard Sale Hidden Treasures Sunnyville
WZIP = Rubik's Puzzle Galaxy: RUSH
WZZP = The Tales of Bearsworth Manor: Puzzling Pages
XIBP = Fish em All Demo
XICP = Gods vs Humans Demo
XIDP = Racers Islands Crazy Racers Demo
@ -220,19 +348,143 @@ XIUP = Soccer Bashi Demo
XIVP = Mix Superstar Demo
XIZP = 3D Pixel Racing Demo
XJEP = Aya and the Cubes of Light Demo
FA9P = Zelda II: The Adventure of Link
FB2L = Super Mario Bros.: The Lost Levels
FBKP = Teenage Mutant Ninja Turles
FC8P = Castlevania II: Simon's Quest
FCSP = Probotector II: Return of the Evil Forces
FDGP = Ghosts'n Goblins
FDRP = Skate or Die
FEML = Bio Miracle Bokutte UPA
FEQP = Castlevania III Dracula's Curse
FERM = Startropics II: Zoda's Revenge
FF5P = Double Dragon II: The Revenge
FFEP = A Boy and His Blob: Trouble on Blobolonia
FFPP = Ufouria: THE SAGA
FFUP = Adventure Island 2
FFVM = S.C.A.T.: Special Cybernetic Attack Team
JA4P = Super Ghouls'n Ghosts
JABL = Marios Super Picross
JADP = The Legend of Zelda: A Link to the Past
JAFP = SimCity
JAHP = R-TYPE III: The Third Lightning
JAJP = Street Fighter II: The World Warrior
JALP = Super Probotector: Alien Rebels
JAZP = The Legend of the Mystical Ninja
JBBP = Super Street Fighter II: The New Challengers
JBDP = Donkey Kong Country 2: Diddy's Kong-Quest
JBIP = Street Fighter II Turbo: Hyper Fighting
JBPP = Donkey Kong Country 3: Dixie Kongs Double Trouble
JCAL = DoReMi Fantasy - Milons DokiDoki Adventure
JCBM = Super Mario RPG: Legend of the Seven Stars
JCCP = Kirbys Fun Pak
JCDM = Kirbys Dream Land 3
JCJP = Super Punch Out!!
JCKP = Space Invaders -The Original Game-
JCTM = Ogre Battle: The March of the Black Queen
JD3P = SUPER E.D.F.: Earth Defense Force
JDJP = Super Star Wars: The Empire Strikes Back
JDLP = Super Star Wars: Return of the Jedi
JDWP = Aero The Acrobat
JDZP = Mystic Quest Legend
NACP = The Legend of Zelda: Ocarina of Time
NAMP = Kirby 64: The Crystal Shards
NAOP = 1080°: TenEighty Snowboarding
NARP = The Legend of Zelda: Majora's Mask
NAYM = Ogre Battle 64: Person of Lordly Caliber
LALP = Fantasy Zone II
LANP = Alex Kidd: The Lost Stars
LAPP = Wonder Boy III: The Dragon's Trap
MA8P = Ecco: The Tides of Time
MAHP = Sonic the Hedgehog
MAKP = Shadow Dancer: The Secret of Shinobi
MALP = Bonanza Bros.
MAOP = Bio-Hazard Battle
MAVP = Wonder Boy In Monster World
MAXP = Alex Kidd In The Enchanted Castle
MB6P = Shining Force II
MBBP = Sonic the Hedgehog 2
MBFP = Shinobi III: Return of the Ninja master
MBIP = Landstalker: The Treasures of King Nole
MBJP = Ghouls'n Ghosts
MBLP = ESWAT City Under Siege
MBMP = Sonic the Hedgehog 3
MBUP = Sonic 3D: Flickies' Island
MBWM = Columns III: Revenge of Columns
MC3P = Super Street Fighter II: The New Challengers
MCCP = Phantasy Star III: Generations of Doom
MCHM = MUSHA
MCLP = Street Fighter II: Special Champion Edition
MCQP = Boogerman - A Pick and Flick Adventure
MCRP = Wolf of the Battlefield: MERCS
MCSP = Wonder Boy III: Monster Lair
MCVP = Pitfall: The Mayan Adventure
MCZP = Shanghai II Dragon's Eye
PAAP = Bomberman'93
PAGL = Bomberman'94
PARL = Detana Twin Bee
PAWP = Galaga'90
PBEP = Motoroader
PBIP = Bonk III: Bonk's Big Adventure
PBSP = Chew Man Fu
PBWP = Air 'Zonk'
PCSL = Digital Champ: Battle Boxing
PDJL = Street Fighter II': Champion Edition
QA3P = SimEarth: The Living Planet
QAAP = Super Air Zonk
QABP = Ys Book I & II
QADL = Gradius II: Gofer no Yabou
QAPL = Castlevania: Rondo of Blood
EA5P = Fatal Fury 3: Road To The Final Victory
EA7P = Samurai Shodown IV: Amakusa's Revenge
EA8M = Iron Clad
EAIP = Top Hunter
EBDP = Magical Drop 3
EBFP = Spin master
EBSP = The Path of the Warrior: Art of Fighting 3
ECAP = Real Bout Fatal Fury 2: The Newcomers
ECGP = Shock Troopers: 2nd Squad
E54P = GHOSTS'N GOBLINS
E55P = Commando
E57P = SonSon
E6PP = NINJA GAIDEN
C93P = The Last Ninja 2
C96P = Summer Games 2
C9IP = Cybernoid
HAAA = Fotokanaal
HABA = Wii-winkelkanaal
HACA = Mii-personagekanaal
HADE = Internetkanaal
HADP = Internetkanaal
HAFP = Weerkanaal
HAGA = Nachrichtenkanaal
HAGP = Nieuwskanaal
HAJP = Enquêtekanaal
HAPP = Mii-wedstrijdkanaal
HATP = Nintendo-kanaal
HAVP = Geluksdagkanaal
HAWP = Metroid Prime 3 Preview
HAYA = Fotokanaal
HCAP = Jam with the Band Live
HCFE = Wii Speak-Kanaal
HCFP = Wii Speak-Kanaal
OHBC = Homebrew-Kanaal
G4BP08 = Resident Evil 4: Wii Edition
G4CP54 = Sjakie en chocolade
G4MP69 = De Sims: Erop uit!
G4OP69 = De Sims 2: Huisdieren
G4ZP69 = De Sims 2
GAZH69 = Harry Potter en de gevangene van Azkaban
GF4H52 = Fantastic Four™ and ©2005
GH4H69 = Harry Potter en de Vuurbeker
GHSP69 = Harry Potter en de Geheime Kamer
GIQX78 = The Incredibles: De opkomst van De Ondermijner
GQQH78 = Nickelodeon SpongeBob Squarepants: Licht uit, Camera aan!
GQLP54 = Dora the Explorer: Reis naar de Paarse Planeet
GQQD78 = SpongeBob SquarePants: Licht uit, camera aan!
GQQE78 = SpongeBob SquarePants: Licht uit, camera aan!
GQQF78 = SpongeBob SquarePants: Licht uit, camera aan!
GQQH78 = SpongeBob SquarePants: Licht uit, camera aan!
GQQP78 = SpongeBob SquarePants: Licht uit, camera aan!
GQWP69 = Harry Potter: WK Zwerkbal
GUBP69 = De Urbz: Sims in the City
GWHP41 = Winnie de Poeh en het Knaagje in zijn Maagje

View file

@ -1,4 +1,4 @@
TITLES = https://www.gametdb.com (type: Wii language: PT_unique version: 20191106234316)
TITLES = https://www.gametdb.com (type: Wii language: PT_unique version: 20230727194225)
R42P69 = Os SIMS 2: Naufragos
R43P69 = EA Sports Active
R4PP69 = Os SIMS 2: Animais de Estimação
@ -6,7 +6,9 @@ R5AP8P = A Bússola Dourada
R5AX8P = A Bússola Dourada
R5PP69 = Harry Potter e a Ordem da Fénix
R5PX69 = Harry Potter e a Ordem da Fénix
R5XJ13 = MySims Agents
R6XP69 = Hasbro Family Game Night 2
RBYJ78 = Barnyard
RBYP78 = Balbúrdia Na Quinta
RCAP78 = Carros
RCAX78 = Carros
@ -15,6 +17,7 @@ RG6P69 = Boogie Superstar
RGQP70 = Os Caça-Fantasmas: O Vídeo Jogo
RH6P69 = Harry Potter e o Príncipe Misterioso
RHNP70 = O meu Cavalo e eu
RJ2JGD = 007: Quantum of Solace
RL7P69 = Littlest Pet Shop: Novos Amigos
RLWP78 = Ratatui
RLWX78 = Ratatui
@ -32,7 +35,61 @@ RZRPGT = Destiny Of Zorro
SERF4Q = Disney Epic Mickey 2: O Regresso dos Heróis
SERP4Q = Disney Epic Mickey 2: O Regresso dos Heróis
SHDP52 = Como Treinares o teu Dragão
SIIP8P = Mario & Sonic nos Jogos Olímpicos de Londres 2012
GFEK01 = Fire Emblem: Path of Radiance
GMSE02 = Super Mario Sunshine Multijogador
PT2PSI = SingIt Portugal Hits Festa de Verão
RMCPCA = Mario Kart Wii (tradução catalã)
WA4E = WarioWare: D.I.Y. Showcase
WA4P = _D.I.Y. Showcase
WAQJ = Yakuman Wii: Ide Yousuke no Kenkou Mahjong
WCSE = CueSports: Pool Revolution
WCSP = CueSports: Snooker vs Billiards
WL2E = Target Toss Pro: Lawn Darts
WN9E = Military Madness: Nectaris
WN9J = Military Madness: Nectaris
WN9P = Military Madness: Nectaris
WPKE = Texas Hold'Em Poker
WPKP = Texas Hold'Em Poker
WRIE = Rainbow Islands: Towering Adventure!
WRIP = Rainbow Islands: Towering Adventure!
WRXJ = Mega Man 10
WSNE = Sonic The Hedgehog 4 Episode I
WSNP = Sonic The Hedgehog 4 Episode I
WTXE = Texas Holdem Tournament
WTXP = Texas Holdem Tournament
WZIE = Rubik's Puzzle Galaxy RUSH
WZIP = Rubik's Puzzle Galaxy RUSH
MC3E = Super Street Fighter II: The New Challengers
MC3P = Super Street Fighter II: The New Challengers
HAAA = Canal Photo
HAYA = Canal Photo
G6TE5G = Os Jovens Titãs
GAXE5D = Lucas: Um Intruso no Formigueiro
GAZD69 = Harry Potter e o Prisoneiro de Azkaban
GAZE69 = Harry Potter e o Prisoneiro de Azkaban
GAZF69 = Harry Potter e o Prisoneiro de Azkaban
GAZH69 = Harry Potter e o Prisoneiro de Azkaban
GAZI69 = Harry Potter e o Prisoneiro de Azkaban
GAZJ13 = Harry Potter e o Prisoneiro de Azkaban
GAZM69 = Harry Potter e o Prisoneiro de Azkaban
GAZP69 = Harry Potter e o Prisoneiro de Azkaban
GAZS69 = Harry Potter e o Prisoneiro de Azkaban
GB4E51 = Burnout 2: Ponto de Impacto
GB4P51 = Burnout 2: Ponto de Impacto
GEDJ01 = Eternal Darkness: Sanity's Requiem
GENP69 = 007: Everything Or Nothing
GFEJ01 = Fire Emblem: Path of Radiance
GHSX69 = Harry Potter e a Câmara dos Segredos
GP3E78 = O Expresso Polar
GPQE6L = As Meninas Super-Poderosas: Relish Rampage
GR9E6L = Reino de Fogo
GT6E70 = O Exterminador do Futuro 3: A Redenção
GUBE69 = Os Urbz: Sims na Cidade
GW7E69 = 007: Agent Under Fire
GW7F69 = 007: Agent Under Fire
GW7P69 = 007: Agent Under Fire
GWKE41 = Peter Jackson's King Kong: O Jogo Oficial do Filme
GXFF69 = FIFA Footeball 2004
GXFI69 = FIFA Footeball 2004
GXFP69 = FIFA Football
GZQE7D = Robôs

View file

@ -1,10 +1,14 @@
TITLES = https://www.gametdb.com (type: Wii language: RU_unique version: 20191106234325)
TITLES = https://www.gametdb.com (type: Wii language: RU_unique version: 20230727194232)
R5IR4Q = История игрушек: Парк развлечений
RN4P41 = Anno: Create A New World
RWAR78 = Валл-И
RXDR4Q = Disney Отвечай Не Зевай
RY2R41 = Возвращение бешеных кролегов
RY2R41 = Возвращение бешеных кроликов
RYBP69 = BOOM BLOX Bash Party
SFDPAF = 家庭训练机 梦幻主题乐园(欧)
SKSE54 = NBA 2K13(美)
SP5E70 = 恶徒 来自地底的侵略者(美)
STNP41 = Приключения Тинтина: Тайна Единорога
GMSE02 = Супер Марио Саншайн Мультиплеер
SOMR01 = Ритм небес
G3EP51 = XGIII: 익스트림 G 레이싱

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -14,6 +14,7 @@ import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.core.content.ContextCompat;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.preference.PreferenceManager;
@ -73,8 +74,12 @@ public final class DirectoryInitialization
if (!setDolphinUserDirectory(context))
{
Toast.makeText(context, R.string.external_storage_not_mounted, Toast.LENGTH_LONG).show();
System.exit(1);
ContextCompat.getMainExecutor(context).execute(() ->
{
Toast.makeText(context, R.string.external_storage_not_mounted, Toast.LENGTH_LONG).show();
System.exit(1);
});
return;
}
extractSysDirectory(context);

View file

@ -119,6 +119,8 @@ const Info<std::string> GFX_DRIVER_LIB_NAME{{System::GFX, "Settings", "DriverLib
const Info<TextureFilteringMode> GFX_ENHANCE_FORCE_TEXTURE_FILTERING{
{System::GFX, "Enhancements", "ForceTextureFiltering"}, TextureFilteringMode::Default};
const Info<int> GFX_ENHANCE_MAX_ANISOTROPY{{System::GFX, "Enhancements", "MaxAnisotropy"}, 0};
const Info<OutputResamplingMode> GFX_ENHANCE_OUTPUT_RESAMPLING{
{System::GFX, "Enhancements", "OutputResampling"}, OutputResamplingMode::Default};
const Info<std::string> GFX_ENHANCE_POST_SHADER{
{System::GFX, "Enhancements", "PostProcessingShader"}, ""};
const Info<bool> GFX_ENHANCE_FORCE_TRUE_COLOR{{System::GFX, "Enhancements", "ForceTrueColor"},

View file

@ -11,6 +11,7 @@ enum class AspectMode : int;
enum class ShaderCompilationMode : int;
enum class StereoMode : int;
enum class TextureFilteringMode : int;
enum class OutputResamplingMode : int;
enum class ColorCorrectionRegion : int;
enum class TriState : int;
@ -102,6 +103,7 @@ extern const Info<bool> GFX_MODS_ENABLE;
extern const Info<TextureFilteringMode> GFX_ENHANCE_FORCE_TEXTURE_FILTERING;
extern const Info<int> GFX_ENHANCE_MAX_ANISOTROPY; // NOTE - this is x in (1 << x)
extern const Info<OutputResamplingMode> GFX_ENHANCE_OUTPUT_RESAMPLING;
extern const Info<std::string> GFX_ENHANCE_POST_SHADER;
extern const Info<bool> GFX_ENHANCE_FORCE_TRUE_COLOR;
extern const Info<bool> GFX_ENHANCE_DISABLE_COPY_FILTER;
@ -118,7 +120,7 @@ static constexpr float GFX_CC_DISPLAY_GAMMA_MIN = 2.2f;
static constexpr float GFX_CC_DISPLAY_GAMMA_MAX = 2.4f;
static constexpr float GFX_CC_HDR_PAPER_WHITE_NITS_MIN = 80.f;
static constexpr float GFX_CC_HDR_PAPER_WHITE_NITS_MAX = 400.f;
static constexpr float GFX_CC_HDR_PAPER_WHITE_NITS_MAX = 500.f;
extern const Info<bool> GFX_CC_CORRECT_COLOR_SPACE;
extern const Info<ColorCorrectionRegion> GFX_CC_GAME_COLOR_SPACE;

View file

@ -818,8 +818,6 @@ static bool PauseAndLock(Core::System& system, bool do_lock, bool unpause_on_unl
was_unpaused = system.GetCPU().PauseAndLock(true);
}
system.GetExpansionInterface().PauseAndLock(do_lock, false);
// audio has to come after CPU, because CPU thread can wait for audio thread (m_throttle).
system.GetDSP().GetDSPEmulator()->PauseAndLock(do_lock);

View file

@ -3,9 +3,15 @@
#include "Core/HLE/HLE_OS.h"
#include <cinttypes>
#include <memory>
#include <string>
#include <string_view>
#include <fmt/format.h>
#include <fmt/printf.h>
#include "Common/BitUtils.h"
#include "Common/CommonTypes.h"
#include "Common/Logging/Log.h"
#include "Common/MsgHandler.h"
@ -204,82 +210,365 @@ void HLE_LogVFPrint(const Core::CPUThreadGuard& guard)
HLE_LogFPrint(guard, ParameterType::VariableArgumentList);
}
namespace
{
class HLEPrintArgsVAList final : public HLEPrintArgs
{
public:
HLEPrintArgsVAList(const Core::CPUThreadGuard& guard, HLE::SystemVABI::VAList* va_list)
: m_guard(guard), m_va_list(va_list)
{
}
u32 GetU32() override { return m_va_list->GetArgT<u32>(); }
u64 GetU64() override { return m_va_list->GetArgT<u64>(); }
double GetF64() override { return m_va_list->GetArgT<double>(); }
std::string GetString(std::optional<u32> max_length) override
{
return max_length == 0u ? std::string() :
PowerPC::MMU::HostGetString(m_guard, m_va_list->GetArgT<u32>(),
max_length.value_or(0u));
}
std::u16string GetU16String(std::optional<u32> max_length) override
{
return max_length == 0u ? std::u16string() :
PowerPC::MMU::HostGetU16String(m_guard, m_va_list->GetArgT<u32>(),
max_length.value_or(0u));
}
private:
const Core::CPUThreadGuard& m_guard;
HLE::SystemVABI::VAList* m_va_list;
};
} // namespace
std::string GetStringVA(Core::System& system, const Core::CPUThreadGuard& guard, u32 str_reg,
ParameterType parameter_type)
{
auto& ppc_state = system.GetPPCState();
std::string ArgumentBuffer;
std::string result;
std::string string = PowerPC::MMU::HostGetString(guard, ppc_state.gpr[str_reg]);
auto ap =
std::unique_ptr<HLE::SystemVABI::VAList> ap =
parameter_type == ParameterType::VariableArgumentList ?
std::make_unique<HLE::SystemVABI::VAListStruct>(system, guard,
ppc_state.gpr[str_reg + 1]) :
std::make_unique<HLE::SystemVABI::VAList>(system, ppc_state.gpr[1] + 0x8, str_reg + 1);
std::make_unique<HLE::SystemVABI::VAListStruct>(guard, ppc_state.gpr[str_reg + 1]) :
std::make_unique<HLE::SystemVABI::VAList>(guard, ppc_state.gpr[1] + 0x8, str_reg + 1);
for (size_t i = 0; i < string.size(); i++)
HLEPrintArgsVAList args(guard, ap.get());
return GetStringVA(&args, string);
}
std::string GetStringVA(HLEPrintArgs* args, std::string_view string)
{
std::string result;
for (size_t i = 0; i < string.size(); ++i)
{
if (string[i] == '%')
{
ArgumentBuffer = '%';
i++;
if (string[i] == '%')
{
result += '%';
continue;
}
while (i < string.size() &&
(string[i] < 'A' || string[i] > 'z' || string[i] == 'l' || string[i] == '-'))
{
ArgumentBuffer += string[i++];
}
if (i >= string.size())
break;
ArgumentBuffer += string[i];
switch (string[i])
{
case 's':
result +=
StringFromFormat(ArgumentBuffer.c_str(),
PowerPC::MMU::HostGetString(guard, ap->GetArgT<u32>(guard)).c_str());
break;
case 'a':
case 'A':
case 'e':
case 'E':
case 'f':
case 'F':
case 'g':
case 'G':
result += StringFromFormat(ArgumentBuffer.c_str(), ap->GetArgT<double>(guard));
break;
case 'p':
// Override, so 64bit Dolphin prints 32bit pointers, since the ppc is 32bit :)
result += StringFromFormat("%x", ap->GetArgT<u32>(guard));
break;
case 'n':
// %n doesn't output anything, so the result variable is untouched
// the actual PPC function will take care of the memory write
break;
default:
if (string[i - 1] == 'l' && string[i - 2] == 'l')
result += StringFromFormat(ArgumentBuffer.c_str(), ap->GetArgT<u64>(guard));
else
result += StringFromFormat(ArgumentBuffer.c_str(), ap->GetArgT<u32>(guard));
break;
}
}
else
if (string[i] != '%')
{
result += string[i];
continue;
}
const size_t formatting_start_position = i;
++i;
if (i < string.size() && string[i] == '%')
{
result += '%';
continue;
}
bool left_justified_flag = false;
bool sign_prepended_flag = false;
bool space_prepended_flag = false;
bool alternative_form_flag = false;
bool padding_zeroes_flag = false;
while (i < string.size())
{
if (string[i] == '-')
left_justified_flag = true;
else if (string[i] == '+')
sign_prepended_flag = true;
else if (string[i] == ' ')
space_prepended_flag = true;
else if (string[i] == '#')
alternative_form_flag = true;
else if (string[i] == '0')
padding_zeroes_flag = true;
else
break;
++i;
}
const auto take_field_or_precision = [&](bool* left_justified_flag_ptr) -> std::optional<u32> {
if (i >= string.size())
return std::nullopt;
if (string[i] == '*')
{
++i;
const s32 result = Common::BitCast<s32>(args->GetU32());
if (result >= 0)
return static_cast<u32>(result);
if (left_justified_flag_ptr)
{
// field width; this results in positive field width and left_justified flag set
*left_justified_flag_ptr = true;
return static_cast<u32>(-static_cast<s64>(result));
}
// precision; this is ignored
return std::nullopt;
}
size_t start = i;
while (i < string.size() && string[i] >= '0' && string[i] <= '9')
++i;
if (start != i)
{
while (start < i && string[start] == '0')
++start;
if (start == i)
return 0;
u32 result = 0;
const std::string tmp(string, start, i - start);
if (TryParse(tmp, &result, 10))
return result;
}
return std::nullopt;
};
const std::optional<u32> field_width = take_field_or_precision(&left_justified_flag);
std::optional<u32> precision = std::nullopt;
if (i < string.size() && string[i] == '.')
{
++i;
precision = take_field_or_precision(nullptr).value_or(0);
}
enum class LengthModifier
{
None,
hh,
h,
l,
ll,
L,
};
auto length_modifier = LengthModifier::None;
if (i < string.size() && (string[i] == 'h' || string[i] == 'l' || string[i] == 'L'))
{
++i;
if (i < string.size() && string[i - 1] == 'h' && string[i] == 'h')
{
++i;
length_modifier = LengthModifier::hh;
}
else if (i < string.size() && string[i - 1] == 'l' && string[i] == 'l')
{
++i;
length_modifier = LengthModifier::ll;
}
else if (string[i - 1] == 'h')
{
length_modifier = LengthModifier::h;
}
else if (string[i - 1] == 'l')
{
length_modifier = LengthModifier::l;
}
else if (string[i - 1] == 'L')
{
length_modifier = LengthModifier::L;
}
}
if (i >= string.size())
{
// not a valid formatting string, print the formatting string as-is
result += string.substr(formatting_start_position);
break;
}
const char format_specifier = string[i];
switch (format_specifier)
{
case 's':
{
if (length_modifier == LengthModifier::l)
{
// This is a bit of a mess... wchar_t could be 16 bits or 32 bits per character depending
// on the software. Retail software seems usually to use 16 bits and homebrew 32 bits, but
// that's really just a guess. Ideally we can figure out a way to autodetect this, but if
// not we should probably just expose a setting for it in the debugger somewhere. For now
// we just assume 16 bits.
fmt::format_to(
std::back_inserter(result), fmt::runtime(left_justified_flag ? "{0:<{1}}" : "{0:>{1}}"),
UTF8ToSHIFTJIS(UTF16ToUTF8(args->GetU16String(precision))), field_width.value_or(0));
}
else
{
fmt::format_to(std::back_inserter(result),
fmt::runtime(left_justified_flag ? "{0:<{1}}" : "{0:>{1}}"),
args->GetString(precision), field_width.value_or(0));
}
break;
}
case 'c':
{
const s32 value = Common::BitCast<s32>(args->GetU32());
if (length_modifier == LengthModifier::l)
{
// Same problem as with wide strings here.
const char16_t wide_char = static_cast<char16_t>(value);
fmt::format_to(std::back_inserter(result),
fmt::runtime(left_justified_flag ? "{0:<{1}}" : "{0:>{1}}"),
UTF8ToSHIFTJIS(UTF16ToUTF8(std::u16string_view(&wide_char, 1))),
field_width.value_or(0));
}
else
{
fmt::format_to(std::back_inserter(result),
fmt::runtime(left_justified_flag ? "{0:<{1}}" : "{0:>{1}}"),
static_cast<char>(value), field_width.value_or(0));
}
break;
}
case 'd':
case 'i':
{
const auto options = fmt::format(
"{}{}{}{}{}{}", left_justified_flag ? "-" : "", sign_prepended_flag ? "+" : "",
space_prepended_flag ? " " : "", padding_zeroes_flag ? "0" : "",
field_width ? fmt::format("{}", *field_width) : "",
precision ? fmt::format(".{}", *precision) : "");
if (length_modifier == LengthModifier::ll)
{
const s64 value = Common::BitCast<s64>(args->GetU64());
result += fmt::sprintf(fmt::format("%{}" PRId64, options).c_str(), value);
}
else
{
s32 value = Common::BitCast<s32>(args->GetU32());
if (length_modifier == LengthModifier::h)
value = static_cast<s16>(value);
else if (length_modifier == LengthModifier::hh)
value = static_cast<s8>(value);
result += fmt::sprintf(fmt::format("%{}" PRId32, options).c_str(), value);
}
break;
}
case 'o':
{
const auto options = fmt::format(
"{}{}{}{}{}{}{}", left_justified_flag ? "-" : "", sign_prepended_flag ? "+" : "",
space_prepended_flag ? " " : "", alternative_form_flag ? "#" : "",
padding_zeroes_flag ? "0" : "", field_width ? fmt::format("{}", *field_width) : "",
precision ? fmt::format(".{}", *precision) : "");
if (length_modifier == LengthModifier::ll)
{
const u64 value = args->GetU64();
result += fmt::sprintf(fmt::format("%{}" PRIo64, options).c_str(), value);
}
else
{
u32 value = args->GetU32();
if (length_modifier == LengthModifier::h)
value = static_cast<u16>(value);
else if (length_modifier == LengthModifier::hh)
value = static_cast<u8>(value);
result += fmt::sprintf(fmt::format("%{}" PRIo32, options).c_str(), value);
}
break;
}
case 'x':
case 'X':
{
const auto options = fmt::format(
"{}{}{}{}{}{}{}", left_justified_flag ? "-" : "", sign_prepended_flag ? "+" : "",
space_prepended_flag ? " " : "", alternative_form_flag ? "#" : "",
padding_zeroes_flag ? "0" : "", field_width ? fmt::format("{}", *field_width) : "",
precision ? fmt::format(".{}", *precision) : "");
if (length_modifier == LengthModifier::ll)
{
const u64 value = args->GetU64();
result += fmt::sprintf(
fmt::format("%{}{}", options, format_specifier == 'x' ? PRIx64 : PRIX64).c_str(),
value);
}
else
{
u32 value = args->GetU32();
if (length_modifier == LengthModifier::h)
value = static_cast<u16>(value);
else if (length_modifier == LengthModifier::hh)
value = static_cast<u8>(value);
result += fmt::sprintf(
fmt::format("%{}{}", options, format_specifier == 'x' ? PRIx32 : PRIX32).c_str(),
value);
}
break;
}
case 'u':
{
const auto options = fmt::format(
"{}{}{}{}{}{}", left_justified_flag ? "-" : "", sign_prepended_flag ? "+" : "",
space_prepended_flag ? " " : "", padding_zeroes_flag ? "0" : "",
field_width ? fmt::format("{}", *field_width) : "",
precision ? fmt::format(".{}", *precision) : "");
if (length_modifier == LengthModifier::ll)
{
const u64 value = args->GetU64();
result += fmt::sprintf(fmt::format("%{}" PRIu64, options).c_str(), value);
}
else
{
u32 value = args->GetU32();
if (length_modifier == LengthModifier::h)
value = static_cast<u16>(value);
else if (length_modifier == LengthModifier::hh)
value = static_cast<u8>(value);
result += fmt::sprintf(fmt::format("%{}" PRIu32, options).c_str(), value);
}
break;
}
case 'f':
case 'F':
case 'e':
case 'E':
case 'a':
case 'A':
case 'g':
case 'G':
{
const auto options = fmt::format(
"{}{}{}{}{}{}{}", left_justified_flag ? "-" : "", sign_prepended_flag ? "+" : "",
space_prepended_flag ? " " : "", alternative_form_flag ? "#" : "",
padding_zeroes_flag ? "0" : "", field_width ? fmt::format("{}", *field_width) : "",
precision ? fmt::format(".{}", *precision) : "");
double value = args->GetF64();
result += fmt::sprintf(fmt::format("%{}{}", options, format_specifier).c_str(), value);
break;
}
case 'n':
// %n doesn't output anything, so the result variable is untouched
// the actual PPC function will take care of the memory write
break;
case 'p':
{
const auto options =
fmt::format("{}{}{}{}{}", left_justified_flag ? "-" : "", sign_prepended_flag ? "+" : "",
space_prepended_flag ? " " : "", padding_zeroes_flag ? "0" : "",
field_width ? fmt::format("{}", *field_width) : "");
const u32 value = args->GetU32();
result += fmt::sprintf(fmt::format("%{}" PRIx32, options).c_str(), value);
break;
}
default:
// invalid conversion specifier, print the formatting string as-is
result += string.substr(formatting_start_position, formatting_start_position - i + 1);
break;
}
}

View file

@ -3,6 +3,11 @@
#pragma once
#include <optional>
#include <string>
#include "Common/CommonTypes.h"
namespace Core
{
class CPUThreadGuard;
@ -10,6 +15,18 @@ class CPUThreadGuard;
namespace HLE_OS
{
class HLEPrintArgs
{
public:
virtual u32 GetU32() = 0;
virtual u64 GetU64() = 0;
virtual double GetF64() = 0;
virtual std::string GetString(std::optional<u32> max_length) = 0;
virtual std::u16string GetU16String(std::optional<u32> max_length) = 0;
};
std::string GetStringVA(HLEPrintArgs* args, std::string_view string);
void HLE_GeneralDebugPrint(const Core::CPUThreadGuard& guard);
void HLE_GeneralDebugVPrint(const Core::CPUThreadGuard& guard);
void HLE_write_console(const Core::CPUThreadGuard& guard);

View file

@ -2,29 +2,29 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "Core/HLE/HLE_VarArgs.h"
#include "Core/Core.h"
#include "Core/System.h"
#include "Common/Logging/Log.h"
HLE::SystemVABI::VAList::~VAList() = default;
u32 HLE::SystemVABI::VAList::GetGPR(const Core::CPUThreadGuard&, u32 gpr) const
u32 HLE::SystemVABI::VAList::GetGPR(u32 gpr) const
{
return m_system.GetPPCState().gpr[gpr];
return m_guard.GetSystem().GetPPCState().gpr[gpr];
}
double HLE::SystemVABI::VAList::GetFPR(const Core::CPUThreadGuard&, u32 fpr) const
double HLE::SystemVABI::VAList::GetFPR(u32 fpr) const
{
return m_system.GetPPCState().ps[fpr].PS0AsDouble();
return m_guard.GetSystem().GetPPCState().ps[fpr].PS0AsDouble();
}
HLE::SystemVABI::VAListStruct::VAListStruct(Core::System& system, const Core::CPUThreadGuard& guard,
u32 address)
: VAList(system, 0), m_va_list{PowerPC::MMU::HostRead_U8(guard, address),
PowerPC::MMU::HostRead_U8(guard, address + 1),
PowerPC::MMU::HostRead_U32(guard, address + 4),
PowerPC::MMU::HostRead_U32(guard, address + 8)},
m_address(address), m_has_fpr_area(system.GetPPCState().cr.GetBit(6) == 1)
HLE::SystemVABI::VAListStruct::VAListStruct(const Core::CPUThreadGuard& guard, u32 address)
: VAList(guard, 0), m_va_list{PowerPC::MMU::HostRead_U8(guard, address),
PowerPC::MMU::HostRead_U8(guard, address + 1),
PowerPC::MMU::HostRead_U32(guard, address + 4),
PowerPC::MMU::HostRead_U32(guard, address + 8)},
m_address(address), m_has_fpr_area(guard.GetSystem().GetPPCState().cr.GetBit(6) == 1)
{
m_stack = m_va_list.overflow_arg_area;
m_gpr += m_va_list.gpr;
@ -41,7 +41,7 @@ u32 HLE::SystemVABI::VAListStruct::GetFPRArea() const
return GetGPRArea() + 4 * 8;
}
u32 HLE::SystemVABI::VAListStruct::GetGPR(const Core::CPUThreadGuard& guard, u32 gpr) const
u32 HLE::SystemVABI::VAListStruct::GetGPR(u32 gpr) const
{
if (gpr < 3 || gpr > 10)
{
@ -49,10 +49,10 @@ u32 HLE::SystemVABI::VAListStruct::GetGPR(const Core::CPUThreadGuard& guard, u32
return 0;
}
const u32 gpr_address = Common::AlignUp(GetGPRArea() + 4 * (gpr - 3), 4);
return PowerPC::MMU::HostRead_U32(guard, gpr_address);
return PowerPC::MMU::HostRead_U32(m_guard, gpr_address);
}
double HLE::SystemVABI::VAListStruct::GetFPR(const Core::CPUThreadGuard& guard, u32 fpr) const
double HLE::SystemVABI::VAListStruct::GetFPR(u32 fpr) const
{
if (!m_has_fpr_area || fpr < 1 || fpr > 8)
{
@ -60,5 +60,5 @@ double HLE::SystemVABI::VAListStruct::GetFPR(const Core::CPUThreadGuard& guard,
return 0.0;
}
const u32 fpr_address = Common::AlignUp(GetFPRArea() + 8 * (fpr - 1), 8);
return PowerPC::MMU::HostRead_F64(guard, fpr_address);
return PowerPC::MMU::HostRead_F64(m_guard, fpr_address);
}

View file

@ -3,14 +3,14 @@
#pragma once
#include <type_traits>
#include "Common/Align.h"
#include "Common/CommonTypes.h"
#include "Core/PowerPC/MMU.h"
#include "Core/PowerPC/PowerPC.h"
#include <type_traits>
namespace Core
{
class CPUThreadGuard;
@ -38,9 +38,9 @@ constexpr bool IS_ARG_REAL = std::is_floating_point<T>();
class VAList
{
public:
explicit VAList(Core::System& system, u32 stack, u32 gpr = 3, u32 fpr = 1, u32 gpr_max = 10,
u32 fpr_max = 8)
: m_system(system), m_gpr(gpr), m_fpr(fpr), m_gpr_max(gpr_max), m_fpr_max(fpr_max),
explicit VAList(const Core::CPUThreadGuard& guard, u32 stack, u32 gpr = 3, u32 fpr = 1,
u32 gpr_max = 10, u32 fpr_max = 8)
: m_guard(guard), m_gpr(gpr), m_fpr(fpr), m_gpr_max(gpr_max), m_fpr_max(fpr_max),
m_stack(stack)
{
}
@ -48,14 +48,14 @@ public:
// 0 - arg_ARGPOINTER
template <typename T, typename std::enable_if_t<IS_ARG_POINTER<T>>* = nullptr>
T GetArg(const Core::CPUThreadGuard& guard)
T GetArg()
{
T obj;
u32 addr = GetArg<u32>(guard);
u32 addr = GetArg<u32>();
for (size_t i = 0; i < sizeof(T); i += 1, addr += 1)
{
reinterpret_cast<u8*>(&obj)[i] = PowerPC::MMU::HostRead_U8(guard, addr);
reinterpret_cast<u8*>(&obj)[i] = PowerPC::MMU::HostRead_U8(m_guard, addr);
}
return obj;
@ -63,20 +63,20 @@ public:
// 1 - arg_WORD
template <typename T, typename std::enable_if_t<IS_WORD<T>>* = nullptr>
T GetArg(const Core::CPUThreadGuard& guard)
T GetArg()
{
static_assert(!std::is_pointer<T>(), "VAList doesn't support pointers");
u64 value;
if (m_gpr <= m_gpr_max)
{
value = GetGPR(guard, m_gpr);
value = GetGPR(m_gpr);
m_gpr += 1;
}
else
{
m_stack = Common::AlignUp(m_stack, 4);
value = PowerPC::MMU::HostRead_U32(guard, m_stack);
value = PowerPC::MMU::HostRead_U32(m_guard, m_stack);
m_stack += 4;
}
@ -85,7 +85,7 @@ public:
// 2 - arg_DOUBLEWORD
template <typename T, typename std::enable_if_t<IS_DOUBLE_WORD<T>>* = nullptr>
T GetArg(const Core::CPUThreadGuard& guard)
T GetArg()
{
u64 value;
@ -93,13 +93,13 @@ public:
m_gpr += 1;
if (m_gpr < m_gpr_max)
{
value = static_cast<u64>(GetGPR(guard, m_gpr)) << 32 | GetGPR(guard, m_gpr + 1);
value = static_cast<u64>(GetGPR(m_gpr)) << 32 | GetGPR(m_gpr + 1);
m_gpr += 2;
}
else
{
m_stack = Common::AlignUp(m_stack, 8);
value = PowerPC::MMU::HostRead_U64(guard, m_stack);
value = PowerPC::MMU::HostRead_U64(m_guard, m_stack);
m_stack += 8;
}
@ -108,19 +108,19 @@ public:
// 3 - arg_ARGREAL
template <typename T, typename std::enable_if_t<IS_ARG_REAL<T>>* = nullptr>
T GetArg(const Core::CPUThreadGuard& guard)
T GetArg()
{
double value;
if (m_fpr <= m_fpr_max)
{
value = GetFPR(guard, m_fpr);
value = GetFPR(m_fpr);
m_fpr += 1;
}
else
{
m_stack = Common::AlignUp(m_stack, 8);
value = PowerPC::MMU::HostRead_F64(guard, m_stack);
value = PowerPC::MMU::HostRead_F64(m_guard, m_stack);
m_stack += 8;
}
@ -129,13 +129,13 @@ public:
// Helper
template <typename T>
T GetArgT(const Core::CPUThreadGuard& guard)
T GetArgT()
{
return static_cast<T>(GetArg<T>(guard));
return static_cast<T>(GetArg<T>());
}
protected:
Core::System& m_system;
const Core::CPUThreadGuard& m_guard;
u32 m_gpr = 3;
u32 m_fpr = 1;
const u32 m_gpr_max = 10;
@ -143,8 +143,8 @@ protected:
u32 m_stack;
private:
virtual u32 GetGPR(const Core::CPUThreadGuard& guard, u32 gpr) const;
virtual double GetFPR(const Core::CPUThreadGuard& guard, u32 fpr) const;
virtual u32 GetGPR(u32 gpr) const;
virtual double GetFPR(u32 fpr) const;
};
// See System V ABI (SVR4) for more details
@ -156,7 +156,7 @@ private:
class VAListStruct : public VAList
{
public:
explicit VAListStruct(Core::System& system, const Core::CPUThreadGuard& guard, u32 address);
explicit VAListStruct(const Core::CPUThreadGuard& guard, u32 address);
~VAListStruct() = default;
private:
@ -174,8 +174,8 @@ private:
u32 GetGPRArea() const;
u32 GetFPRArea() const;
u32 GetGPR(const Core::CPUThreadGuard& guard, u32 gpr) const override;
double GetFPR(const Core::CPUThreadGuard& guard, u32 fpr) const override;
u32 GetGPR(u32 gpr) const override;
double GetFPR(u32 fpr) const override;
};
} // namespace HLE::SystemVABI

View file

@ -169,12 +169,6 @@ void ExpansionInterfaceManager::DoState(PointerWrap& p)
channel->DoState(p);
}
void ExpansionInterfaceManager::PauseAndLock(bool doLock, bool unpauseOnUnlock)
{
for (auto& channel : m_channels)
channel->PauseAndLock(doLock, unpauseOnUnlock);
}
void ExpansionInterfaceManager::RegisterMMIO(MMIO::Mapping* mmio, u32 base)
{
for (int i = 0; i < MAX_EXI_CHANNELS; ++i)

View file

@ -72,7 +72,6 @@ public:
void Init(const Sram* override_sram);
void Shutdown();
void DoState(PointerWrap& p);
void PauseAndLock(bool doLock, bool unpauseOnUnlock);
void RegisterMMIO(MMIO::Mapping* mmio, u32 base);

View file

@ -286,12 +286,6 @@ void CEXIChannel::DoState(PointerWrap& p)
}
}
void CEXIChannel::PauseAndLock(bool do_lock, bool resume_on_unlock)
{
for (auto& device : m_devices)
device->PauseAndLock(do_lock, resume_on_unlock);
}
void CEXIChannel::SetEXIINT(bool exiint)
{
m_status.EXIINT = !!exiint;

View file

@ -45,7 +45,6 @@ public:
bool IsCausingInterrupt();
void DoState(PointerWrap& p);
void PauseAndLock(bool do_lock, bool resume_on_unlock);
// This should only be used to transition interrupts from SP1 to Channel 2
void SetEXIINT(bool exiint);

View file

@ -89,10 +89,6 @@ void IEXIDevice::DoState(PointerWrap& p)
{
}
void IEXIDevice::PauseAndLock(bool do_lock, bool resume_on_unlock)
{
}
bool IEXIDevice::IsInterruptSet()
{
return false;

View file

@ -64,7 +64,6 @@ public:
virtual bool IsPresent() const;
virtual void SetCS(int cs);
virtual void DoState(PointerWrap& p);
virtual void PauseAndLock(bool do_lock, bool resume_on_unlock = true);
// Is generating interrupt ?
virtual bool IsInterruptSet();

View file

@ -169,7 +169,7 @@ NetPlayServer::NetPlayServer(const u16 port, const bool forward_port, NetPlayUI*
m_chunked_data_thread = std::thread(&NetPlayServer::ChunkedDataThreadFunc, this);
#ifdef USE_UPNP
if (forward_port)
if (forward_port && !traversal_config.use_traversal)
Common::UPnP::TryPortmapping(port);
#endif
}

View file

@ -490,6 +490,21 @@ void Jit64::FakeBLCall(u32 after)
SetJumpTarget(skip_exit);
}
void Jit64::EmitUpdateMembase()
{
MOV(64, R(RMEM), PPCSTATE(mem_ptr));
}
void Jit64::EmitStoreMembase(const OpArg& msr, X64Reg scratch_reg)
{
auto& memory = m_system.GetMemory();
MOV(64, R(RMEM), ImmPtr(memory.GetLogicalBase()));
MOV(64, R(scratch_reg), ImmPtr(memory.GetPhysicalBase()));
TEST(32, msr, Imm32(1 << (31 - 27)));
CMOVcc(64, RMEM, R(scratch_reg), CC_Z);
MOV(64, PPCSTATE(mem_ptr), R(RMEM));
}
void Jit64::WriteExit(u32 destination, bool bl, u32 after)
{
if (!m_enable_blr_optimization)
@ -599,6 +614,7 @@ void Jit64::WriteRfiExitDestInRSCRATCH()
ABI_PushRegistersAndAdjustStack({}, 0);
ABI_CallFunctionP(PowerPC::CheckExceptionsFromJIT, &m_system.GetPowerPC());
ABI_PopRegistersAndAdjustStack({}, 0);
EmitUpdateMembase();
SUB(32, PPCSTATE(downcount), Imm32(js.downcountAmount));
JMP(asm_routines.dispatcher, Jump::Near);
}
@ -620,6 +636,7 @@ void Jit64::WriteExceptionExit()
ABI_PushRegistersAndAdjustStack({}, 0);
ABI_CallFunctionP(PowerPC::CheckExceptionsFromJIT, &m_system.GetPowerPC());
ABI_PopRegistersAndAdjustStack({}, 0);
EmitUpdateMembase();
SUB(32, PPCSTATE(downcount), Imm32(js.downcountAmount));
JMP(asm_routines.dispatcher, Jump::Near);
}
@ -632,6 +649,7 @@ void Jit64::WriteExternalExceptionExit()
ABI_PushRegistersAndAdjustStack({}, 0);
ABI_CallFunctionP(PowerPC::CheckExternalExceptionsFromJIT, &m_system.GetPowerPC());
ABI_PopRegistersAndAdjustStack({}, 0);
EmitUpdateMembase();
SUB(32, PPCSTATE(downcount), Imm32(js.downcountAmount));
JMP(asm_routines.dispatcher, Jump::Near);
}
@ -639,6 +657,7 @@ void Jit64::WriteExternalExceptionExit()
void Jit64::Run()
{
ProtectStack();
m_system.GetJitInterface().UpdateMembase();
CompiledCode pExecAddr = (CompiledCode)asm_routines.enter_code;
pExecAddr();
@ -649,6 +668,7 @@ void Jit64::Run()
void Jit64::SingleStep()
{
ProtectStack();
m_system.GetJitInterface().UpdateMembase();
CompiledCode pExecAddr = (CompiledCode)asm_routines.enter_code;
pExecAddr();
@ -745,6 +765,7 @@ void Jit64::Jit(u32 em_address, bool clear_cache_and_retry_on_failure)
m_ppc_state.npc = nextPC;
m_ppc_state.Exceptions |= EXCEPTION_ISI;
m_system.GetPowerPC().CheckExceptions();
m_system.GetJitInterface().UpdateMembase();
WARN_LOG_FMT(POWERPC, "ISI exception at {:#010x}", nextPC);
return;
}

View file

@ -87,6 +87,8 @@ public:
// Utilities for use by opcodes
void EmitUpdateMembase();
void EmitStoreMembase(const Gen::OpArg& msr, Gen::X64Reg scratch_reg);
void FakeBLCall(u32 after);
void WriteExit(u32 destination, bool bl = false, u32 after = 0);
void JustWriteExit(u32 destination, bool bl, u32 after);

View file

@ -65,6 +65,11 @@ void Jit64AsmRoutineManager::Generate()
ABI_CallFunction(CoreTiming::GlobalAdvance);
ABI_PopRegistersAndAdjustStack({}, 0);
// When we've just entered the jit we need to update the membase
// GlobalAdvance also checks exceptions after which we need to
// update the membase so it makes sense to do this here.
MOV(64, R(RMEM), PPCSTATE(mem_ptr));
// skip the sync and compare first time
FixupBranch skipToRealDispatch = J(enable_debugging ? Jump::Near : Jump::Short);
@ -104,8 +109,6 @@ void Jit64AsmRoutineManager::Generate()
dispatcher_no_check = GetCodePtr();
auto& memory = system.GetMemory();
// The following is a translation of JitBaseBlockCache::Dispatch into assembly.
const bool assembly_dispatcher = true;
if (assembly_dispatcher)
@ -165,13 +168,6 @@ void Jit64AsmRoutineManager::Generate()
FixupBranch state_mismatch = J_CC(CC_NE);
// Success; branch to the block we found.
// Switch to the correct memory base, in case MSR.DR has changed.
TEST(32, PPCSTATE(msr), Imm32(1 << (31 - 27)));
FixupBranch physmem = J_CC(CC_Z);
MOV(64, R(RMEM), ImmPtr(memory.GetLogicalBase()));
JMPptr(MDisp(RSCRATCH, static_cast<s32>(offsetof(JitBlockData, normalEntry))));
SetJumpTarget(physmem);
MOV(64, R(RMEM), ImmPtr(memory.GetPhysicalBase()));
JMPptr(MDisp(RSCRATCH, static_cast<s32>(offsetof(JitBlockData, normalEntry))));
SetJumpTarget(not_found);
@ -189,13 +185,7 @@ void Jit64AsmRoutineManager::Generate()
TEST(64, R(ABI_RETURN), R(ABI_RETURN));
FixupBranch no_block_available = J_CC(CC_Z);
// Switch to the correct memory base, in case MSR.DR has changed.
TEST(32, PPCSTATE(msr), Imm32(1 << (31 - 27)));
FixupBranch physmem = J_CC(CC_Z);
MOV(64, R(RMEM), ImmPtr(memory.GetLogicalBase()));
JMPptr(R(ABI_RETURN));
SetJumpTarget(physmem);
MOV(64, R(RMEM), ImmPtr(memory.GetPhysicalBase()));
// Jump to the block
JMPptr(R(ABI_RETURN));
SetJumpTarget(no_block_available);

View file

@ -12,6 +12,7 @@
#include "Core/PowerPC/Jit64Common/Jit64PowerPCState.h"
#include "Core/PowerPC/PPCAnalyst.h"
#include "Core/PowerPC/PowerPC.h"
#include "Core/System.h"
// The branches are known good, or at least reasonably good.
// No need for a disable-mechanism.
@ -54,6 +55,9 @@ void Jit64::rfi(UGeckoInstruction inst)
MOV(32, R(RSCRATCH), PPCSTATE_SRR1);
AND(32, R(RSCRATCH), Imm32(mask & clearMSR13));
OR(32, PPCSTATE(msr), R(RSCRATCH));
EmitStoreMembase(R(RSCRATCH), RSCRATCH2);
// NPC = SRR0;
MOV(32, R(RSCRATCH), PPCSTATE_SRR0);
WriteRfiExitDestInRSCRATCH();

View file

@ -438,7 +438,10 @@ void Jit64::mtmsr(UGeckoInstruction inst)
RCOpArg Rs = gpr.BindOrImm(inst.RS, RCMode::Read);
RegCache::Realize(Rs);
MOV(32, PPCSTATE(msr), Rs);
EmitStoreMembase(PPCSTATE(msr), RSCRATCH2);
}
gpr.Flush();
fpr.Flush();

View file

@ -127,8 +127,11 @@ bool JitArm64::HandleFault(uintptr_t access_address, SContext* ctx)
{
ERROR_LOG_FMT(DYNA_REC,
"JitArm64 address calculation overflowed. This should never happen! "
"PC {:#018x}, access address {:#018x}, memory base {:#018x}, MSR.DR {}",
ctx->CTX_PC, access_address, memory_base, m_ppc_state.msr.DR);
"PC {:#018x}, access address {:#018x}, memory base {:#018x}, MSR.DR {}, "
"mem_ptr {}, pbase {}, lbase {}",
ctx->CTX_PC, access_address, memory_base, m_ppc_state.msr.DR,
fmt::ptr(m_ppc_state.mem_ptr), fmt::ptr(memory.GetPhysicalBase()),
fmt::ptr(memory.GetLogicalBase()));
}
else
{
@ -353,6 +356,24 @@ void JitArm64::IntializeSpeculativeConstants()
}
}
void JitArm64::EmitUpdateMembase()
{
LDR(IndexType::Unsigned, MEM_REG, PPC_REG, PPCSTATE_OFF(mem_ptr));
}
void JitArm64::EmitStoreMembase(const ARM64Reg& msr)
{
auto& memory = m_system.GetMemory();
ARM64Reg WD = gpr.GetReg();
ARM64Reg XD = EncodeRegTo64(WD);
MOVP2R(MEM_REG, jo.fastmem_arena ? memory.GetLogicalBase() : memory.GetLogicalPageMappingsBase());
MOVP2R(XD, jo.fastmem_arena ? memory.GetPhysicalBase() : memory.GetPhysicalPageMappingsBase());
TST(msr, LogicalImm(1 << (31 - 27), 32));
CSEL(MEM_REG, MEM_REG, XD, CCFlags::CC_NEQ);
STR(IndexType::Unsigned, MEM_REG, PPC_REG, PPCSTATE_OFF(mem_ptr));
gpr.Unlock(WD);
}
void JitArm64::WriteExit(u32 destination, bool LK, u32 exit_address_after_return)
{
Cleanup();
@ -523,6 +544,7 @@ void JitArm64::WriteExceptionExit(ARM64Reg dest, bool only_external, bool always
else
MOVP2R(EncodeRegTo64(DISPATCHER_PC), &PowerPC::CheckExceptionsFromJIT);
BLR(EncodeRegTo64(DISPATCHER_PC));
EmitUpdateMembase();
LDR(IndexType::Unsigned, DISPATCHER_PC, PPC_REG, PPCSTATE_OFF(npc));
@ -636,6 +658,7 @@ void JitArm64::EndTimeProfile(JitBlock* b)
void JitArm64::Run()
{
ProtectStack();
m_system.GetJitInterface().UpdateMembase();
CompiledCode pExecAddr = (CompiledCode)enter_code;
pExecAddr();
@ -646,6 +669,7 @@ void JitArm64::Run()
void JitArm64::SingleStep()
{
ProtectStack();
m_system.GetJitInterface().UpdateMembase();
CompiledCode pExecAddr = (CompiledCode)enter_code;
pExecAddr();
@ -747,6 +771,7 @@ void JitArm64::Jit(u32 em_address, bool clear_cache_and_retry_on_failure)
m_ppc_state.npc = nextPC;
m_ppc_state.Exceptions |= EXCEPTION_ISI;
m_system.GetPowerPC().CheckExceptions();
m_system.GetJitInterface().UpdateMembase();
WARN_LOG_FMT(POWERPC, "ISI exception at {:#010x}", nextPC);
return;
}

View file

@ -311,6 +311,9 @@ protected:
void BeginTimeProfile(JitBlock* b);
void EndTimeProfile(JitBlock* b);
void EmitUpdateMembase();
void EmitStoreMembase(const Arm64Gen::ARM64Reg& msr);
// Exits
void WriteExit(u32 destination, bool LK = false, u32 exit_address_after_return = 0);
void WriteExit(Arm64Gen::ARM64Reg dest, bool LK = false, u32 exit_address_after_return = 0);

View file

@ -11,6 +11,7 @@
#include "Core/PowerPC/JitArm64/JitArm64_RegCache.h"
#include "Core/PowerPC/PPCTables.h"
#include "Core/PowerPC/PowerPC.h"
#include "Core/System.h"
using namespace Arm64Gen;
@ -64,6 +65,8 @@ void JitArm64::rfi(UGeckoInstruction inst)
STR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(msr)); // STR rB in to rA
EmitStoreMembase(WA);
LDR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF_SPR(SPR_SRR0));
gpr.Unlock(WB, WC);

View file

@ -94,6 +94,8 @@ void JitArm64::mtmsr(UGeckoInstruction inst)
gpr.BindToRegister(inst.RS, true);
STR(IndexType::Unsigned, gpr.R(inst.RS), PPC_REG, PPCSTATE_OFF(msr));
EmitStoreMembase(gpr.R(inst.RS));
gpr.Flush(FlushMode::All, ARM64Reg::INVALID_REG);
fpr.Flush(FlushMode::All, ARM64Reg::INVALID_REG);

View file

@ -95,26 +95,13 @@ void JitArm64::GenerateAsm()
bool assembly_dispatcher = true;
auto& memory = m_system.GetMemory();
if (assembly_dispatcher)
{
// set the mem_base based on MSR flags
LDR(IndexType::Unsigned, ARM64Reg::W28, PPC_REG, PPCSTATE_OFF(msr));
FixupBranch physmem = TBNZ(ARM64Reg::W28, 31 - 27);
MOVP2R(MEM_REG,
jo.fastmem_arena ? memory.GetPhysicalBase() : memory.GetPhysicalPageMappingsBase());
FixupBranch membaseend = B();
SetJumpTarget(physmem);
MOVP2R(MEM_REG,
jo.fastmem_arena ? memory.GetLogicalBase() : memory.GetLogicalPageMappingsBase());
SetJumpTarget(membaseend);
if (GetBlockCache()->GetFastBlockMap())
{
// Check if there is a block
ARM64Reg pc_masked = ARM64Reg::X25;
ARM64Reg cache_base = ARM64Reg::X27;
ARM64Reg cache_base = ARM64Reg::X24;
ARM64Reg block = ARM64Reg::X30;
LSL(pc_masked, DISPATCHER_PC, 1);
MOVP2R(cache_base, GetBlockCache()->GetFastBlockMap());
@ -122,7 +109,7 @@ void JitArm64::GenerateAsm()
FixupBranch not_found = CBZ(block);
// b.msrBits != msr
ARM64Reg msr = ARM64Reg::W25;
ARM64Reg msr = ARM64Reg::W27;
ARM64Reg msr2 = ARM64Reg::W24;
LDR(IndexType::Unsigned, msr, PPC_REG, PPCSTATE_OFF(msr));
AND(msr, msr, LogicalImm(JitBaseBlockCache::JIT_CACHE_MSR_MASK, 32));
@ -181,14 +168,6 @@ void JitArm64::GenerateAsm()
FixupBranch no_block_available = CBZ(ARM64Reg::X0);
// set the mem_base based on MSR flags and jump to next block.
LDR(IndexType::Unsigned, ARM64Reg::W28, PPC_REG, PPCSTATE_OFF(msr));
FixupBranch physmem = TBNZ(ARM64Reg::W28, 31 - 27);
MOVP2R(MEM_REG,
jo.fastmem_arena ? memory.GetPhysicalBase() : memory.GetPhysicalPageMappingsBase());
BR(ARM64Reg::X0);
SetJumpTarget(physmem);
MOVP2R(MEM_REG, jo.fastmem_arena ? memory.GetLogicalBase() : memory.GetLogicalPageMappingsBase());
BR(ARM64Reg::X0);
// Call JIT
@ -217,6 +196,11 @@ void JitArm64::GenerateAsm()
MOVP2R(ARM64Reg::X8, &CoreTiming::GlobalAdvance);
BLR(ARM64Reg::X8);
// When we've just entered the jit we need to update the membase
// GlobalAdvance also checks exceptions after which we need to
// update the membase so it makes sense to do this here.
EmitUpdateMembase();
// Load the PC back into DISPATCHER_PC (the exception handler might have changed it)
LDR(IndexType::Unsigned, DISPATCHER_PC, PPC_REG, PPCSTATE_OFF(pc));

View file

@ -98,6 +98,25 @@ void JitInterface::SetProfilingState(ProfilingState state)
m_jit->jo.profile_blocks = state == ProfilingState::Enabled;
}
void JitInterface::UpdateMembase()
{
if (!m_jit)
return;
auto& ppc_state = m_system.GetPPCState();
auto& memory = m_system.GetMemory();
if (ppc_state.msr.DR)
{
ppc_state.mem_ptr =
m_jit->jo.fastmem_arena ? memory.GetLogicalBase() : memory.GetLogicalPageMappingsBase();
}
else
{
ppc_state.mem_ptr =
m_jit->jo.fastmem_arena ? memory.GetPhysicalBase() : memory.GetPhysicalPageMappingsBase();
}
}
void JitInterface::WriteProfileResults(const std::string& filename) const
{
Profiler::ProfileStats prof_stats;

View file

@ -61,6 +61,7 @@ public:
u32 entry_address;
};
void UpdateMembase();
void SetProfilingState(ProfilingState state);
void WriteProfileResults(const std::string& filename) const;
void GetProfileResults(Profiler::ProfileStats* prof_stats) const;

View file

@ -867,6 +867,22 @@ std::string MMU::HostGetString(const Core::CPUThreadGuard& guard, u32 address, s
return s;
}
std::u16string MMU::HostGetU16String(const Core::CPUThreadGuard& guard, u32 address, size_t size)
{
std::u16string s;
do
{
if (!HostIsRAMAddress(guard, address) || !HostIsRAMAddress(guard, address + 1))
break;
const u16 res = HostRead_U16(guard, address);
if (!res)
break;
s += static_cast<char16_t>(res);
address += 2;
} while (size == 0 || s.length() < size);
return s;
}
std::optional<ReadResult<std::string>> MMU::HostTryReadString(const Core::CPUThreadGuard& guard,
u32 address, size_t size,
RequestedAddressSpace space)

View file

@ -132,6 +132,8 @@ public:
static double HostRead_F64(const Core::CPUThreadGuard& guard, u32 address);
static u32 HostRead_Instruction(const Core::CPUThreadGuard& guard, u32 address);
static std::string HostGetString(const Core::CPUThreadGuard& guard, u32 address, size_t size = 0);
static std::u16string HostGetU16String(const Core::CPUThreadGuard& guard, u32 address,
size_t size = 0);
// Try to read a value from emulated memory at the given address in the given memory space.
// If the read succeeds, the returned value will be present and the ReadResult contains the read

View file

@ -236,9 +236,9 @@ void PowerPCManager::InitializeCPUCore(CPUCore cpu_core)
m_mode = m_cpu_core_base == &interpreter ? CoreMode::Interpreter : CoreMode::JIT;
}
const std::vector<CPUCore>& AvailableCPUCores()
std::span<const CPUCore> AvailableCPUCores()
{
static const std::vector<CPUCore> cpu_cores = {
static constexpr auto cpu_cores = {
#ifdef _M_X86_64
CPUCore::JIT64,
#elif defined(_M_ARM_64)
@ -569,7 +569,10 @@ void PowerPCManager::CheckExceptions()
else
{
CheckExternalExceptions();
return;
}
m_system.GetJitInterface().UpdateMembase();
}
void PowerPCManager::CheckExternalExceptions()
@ -623,6 +626,8 @@ void PowerPCManager::CheckExternalExceptions()
exceptions);
}
}
m_system.GetJitInterface().UpdateMembase();
}
void PowerPCManager::CheckBreakPoints()

View file

@ -6,6 +6,7 @@
#include <array>
#include <cstddef>
#include <iosfwd>
#include <span>
#include <tuple>
#include <type_traits>
#include <vector>
@ -169,6 +170,7 @@ struct PowerPCState
// Storage for the stack pointer of the BLR optimization.
u8* stored_stack_pointer = nullptr;
u8* mem_ptr = nullptr;
std::array<std::array<TLBEntry, TLB_SIZE / TLB_WAYS>, NUM_TLBS> tlb;
@ -238,7 +240,7 @@ static_assert(offsetof(PowerPC::PowerPCState, above_fits_in_first_0x100) <= 0x10
#endif
#endif
const std::vector<CPUCore>& AvailableCPUCores();
std::span<const CPUCore> AvailableCPUCores();
CPUCore DefaultCPUCore();
class PowerPCManager

View file

@ -663,15 +663,18 @@
<ClInclude Include="VideoCommon\GeometryShaderGen.h" />
<ClInclude Include="VideoCommon\GeometryShaderManager.h" />
<ClInclude Include="VideoCommon\GraphicsModSystem\Config\GraphicsMod.h" />
<ClInclude Include="VideoCommon\GraphicsModSystem\Config\GraphicsModAsset.h" />
<ClInclude Include="VideoCommon\GraphicsModSystem\Config\GraphicsModFeature.h" />
<ClInclude Include="VideoCommon\GraphicsModSystem\Config\GraphicsModGroup.h" />
<ClInclude Include="VideoCommon\GraphicsModSystem\Config\GraphicsTarget.h" />
<ClInclude Include="VideoCommon\GraphicsModSystem\Config\GraphicsTargetGroup.h" />
<ClInclude Include="VideoCommon\GraphicsModSystem\Constants.h" />
<ClInclude Include="VideoCommon\GraphicsModSystem\Runtime\Actions\CustomPipelineAction.h" />
<ClInclude Include="VideoCommon\GraphicsModSystem\Runtime\Actions\MoveAction.h" />
<ClInclude Include="VideoCommon\GraphicsModSystem\Runtime\Actions\PrintAction.h" />
<ClInclude Include="VideoCommon\GraphicsModSystem\Runtime\Actions\ScaleAction.h" />
<ClInclude Include="VideoCommon\GraphicsModSystem\Runtime\Actions\SkipAction.h" />
<ClInclude Include="VideoCommon\GraphicsModSystem\Runtime\CustomShaderCache.h" />
<ClInclude Include="VideoCommon\GraphicsModSystem\Runtime\FBInfo.h" />
<ClInclude Include="VideoCommon\GraphicsModSystem\Runtime\GraphicsModAction.h" />
<ClInclude Include="VideoCommon\GraphicsModSystem\Runtime\GraphicsModActionData.h" />
@ -1276,14 +1279,17 @@
<ClCompile Include="VideoCommon\GeometryShaderGen.cpp" />
<ClCompile Include="VideoCommon\GeometryShaderManager.cpp" />
<ClCompile Include="VideoCommon\GraphicsModSystem\Config\GraphicsMod.cpp" />
<ClCompile Include="VideoCommon\GraphicsModSystem\Config\GraphicsModAsset.cpp" />
<ClCompile Include="VideoCommon\GraphicsModSystem\Config\GraphicsModFeature.cpp" />
<ClCompile Include="VideoCommon\GraphicsModSystem\Config\GraphicsModGroup.cpp" />
<ClCompile Include="VideoCommon\GraphicsModSystem\Config\GraphicsTarget.cpp" />
<ClCompile Include="VideoCommon\GraphicsModSystem\Config\GraphicsTargetGroup.cpp" />
<ClCompile Include="VideoCommon\GraphicsModSystem\Runtime\Actions\CustomPipelineAction.cpp" />
<ClCompile Include="VideoCommon\GraphicsModSystem\Runtime\Actions\MoveAction.cpp" />
<ClCompile Include="VideoCommon\GraphicsModSystem\Runtime\Actions\PrintAction.cpp" />
<ClCompile Include="VideoCommon\GraphicsModSystem\Runtime\Actions\ScaleAction.cpp" />
<ClCompile Include="VideoCommon\GraphicsModSystem\Runtime\Actions\SkipAction.cpp" />
<ClCompile Include="VideoCommon\GraphicsModSystem\Runtime\CustomShaderCache.cpp" />
<ClCompile Include="VideoCommon\GraphicsModSystem\Runtime\FBInfo.cpp" />
<ClCompile Include="VideoCommon\GraphicsModSystem\Runtime\GraphicsModActionFactory.cpp" />
<ClCompile Include="VideoCommon\GraphicsModSystem\Runtime\GraphicsModManager.cpp" />

View file

@ -52,8 +52,8 @@ void AchievementSettingsWidget::CreateLayout()
m_common_password_label = new QLabel(tr("Password"));
m_common_password_input = new QLineEdit(QStringLiteral(""));
m_common_password_input->setEchoMode(QLineEdit::Password);
m_common_login_button = new QPushButton(tr("Login"));
m_common_logout_button = new QPushButton(tr("Logout"));
m_common_login_button = new QPushButton(tr("Log In"));
m_common_logout_button = new QPushButton(tr("Log Out"));
m_common_login_failed = new QLabel(tr("Login Failed"));
m_common_login_failed->setStyleSheet(QStringLiteral("QLabel { color : red; }"));
m_common_login_failed->setVisible(false);

View file

@ -21,6 +21,7 @@
#include "DolphinQt/Config/Graphics/ColorCorrectionConfigWindow.h"
#include "DolphinQt/Config/Graphics/GraphicsWindow.h"
#include "DolphinQt/Config/Graphics/PostProcessingConfigWindow.h"
#include "DolphinQt/Config/ToolTipControls/ToolTipPushButton.h"
#include "DolphinQt/QtUtils/NonDefaultQPushButton.h"
#include "DolphinQt/QtUtils/SetWindowDecorations.h"
#include "DolphinQt/Settings.h"
@ -104,7 +105,23 @@ void EnhancementsWidget::CreateWidgets()
m_texture_filtering_combo->addItem(tr("Force Linear and 16x Anisotropic"),
TEXTURE_FILTERING_FORCE_LINEAR_ANISO_16X);
m_configure_color_correction = new NonDefaultQPushButton(tr("Configure"));
m_output_resampling_combo = new ToolTipComboBox();
m_output_resampling_combo->addItem(tr("Default"),
static_cast<int>(OutputResamplingMode::Default));
m_output_resampling_combo->addItem(tr("Bilinear"),
static_cast<int>(OutputResamplingMode::Bilinear));
m_output_resampling_combo->addItem(tr("Bicubic: B-Spline"),
static_cast<int>(OutputResamplingMode::BSpline));
m_output_resampling_combo->addItem(tr("Bicubic: Mitchell-Netravali"),
static_cast<int>(OutputResamplingMode::MitchellNetravali));
m_output_resampling_combo->addItem(tr("Bicubic: Catmull-Rom"),
static_cast<int>(OutputResamplingMode::CatmullRom));
m_output_resampling_combo->addItem(tr("Sharp Bilinear"),
static_cast<int>(OutputResamplingMode::SharpBilinear));
m_output_resampling_combo->addItem(tr("Area Sampling"),
static_cast<int>(OutputResamplingMode::AreaSampling));
m_configure_color_correction = new ToolTipPushButton(tr("Configure"));
m_pp_effect = new ToolTipComboBox();
m_configure_pp_effect = new NonDefaultQPushButton(tr("Configure"));
@ -135,6 +152,10 @@ void EnhancementsWidget::CreateWidgets()
enhancements_layout->addWidget(m_texture_filtering_combo, row, 1, 1, -1);
++row;
enhancements_layout->addWidget(new QLabel(tr("Output Resampling:")), row, 0);
enhancements_layout->addWidget(m_output_resampling_combo, row, 1, 1, -1);
++row;
enhancements_layout->addWidget(new QLabel(tr("Color Correction:")), row, 0);
enhancements_layout->addWidget(m_configure_color_correction, row, 1, 1, -1);
++row;
@ -194,6 +215,8 @@ void EnhancementsWidget::ConnectWidgets()
[this](int) { SaveSettings(); });
connect(m_texture_filtering_combo, qOverload<int>(&QComboBox::currentIndexChanged),
[this](int) { SaveSettings(); });
connect(m_output_resampling_combo, qOverload<int>(&QComboBox::currentIndexChanged),
[this](int) { SaveSettings(); });
connect(m_pp_effect, qOverload<int>(&QComboBox::currentIndexChanged),
[this](int) { SaveSettings(); });
connect(m_3d_mode, qOverload<int>(&QComboBox::currentIndexChanged), [this] {
@ -324,6 +347,14 @@ void EnhancementsWidget::LoadSettings()
break;
}
// Resampling
const OutputResamplingMode output_resampling_mode =
Config::Get(Config::GFX_ENHANCE_OUTPUT_RESAMPLING);
m_output_resampling_combo->setCurrentIndex(static_cast<int>(output_resampling_mode));
m_output_resampling_combo->setEnabled(g_Config.backend_info.bSupportsPostProcessing);
// Color Correction
m_configure_color_correction->setEnabled(g_Config.backend_info.bSupportsPostProcessing);
// Post Processing Shader
@ -412,6 +443,10 @@ void EnhancementsWidget::SaveSettings()
break;
}
const int output_resampling_selection = m_output_resampling_combo->currentData().toInt();
Config::SetBaseOrCurrent(Config::GFX_ENHANCE_OUTPUT_RESAMPLING,
static_cast<OutputResamplingMode>(output_resampling_selection));
const bool anaglyph = g_Config.stereo_mode == StereoMode::Anaglyph;
const bool passive = g_Config.stereo_mode == StereoMode::Passive;
Config::SetBaseOrCurrent(Config::GFX_ENHANCE_POST_SHADER,
@ -454,9 +489,40 @@ void EnhancementsWidget::AddDescriptions()
"scaling filter selected by the game.<br><br>Any option except 'Default' will alter the look "
"of the game's textures and might cause issues in a small number of "
"games.<br><br><dolphin_emphasis>If unsure, select 'Default'.</dolphin_emphasis>");
static const char TR_OUTPUT_RESAMPLING_DESCRIPTION[] =
QT_TR_NOOP("Affects how the game output is scaled to the window resolution."
"<br>The performance mostly depends on the number of samples each method uses."
"<br>Compared to SSAA, resampling is useful in case the output window"
"<br>resolution isn't a multiplier of the native emulation resolution."
"<br><br><b>Default</b> - [fastest]"
"<br>Internal GPU bilinear sampler which is not gamma corrected."
"<br>This setting might be ignored if gamma correction is forced on."
"<br><br><b>Bilinear</b> - [4 samples]"
"<br>Gamma corrected linear interpolation between pixels."
"<br><br><b>Bicubic</b> - [16 samples]"
"<br>Gamma corrected cubic interpolation between pixels."
"<br>Good when rescaling between close resolutions. i.e 1080p and 1440p."
"<br>Comes in various flavors:"
"<br><b>B-Spline</b>: Blurry, but avoids all lobing artifacts"
"<br><b>Mitchell-Netravali</b>: Good middle ground between blurry and lobing"
"<br><b>Catmull-Rom</b>: Sharper, but can cause lobing artifacts"
"<br><br><b>Sharp Bilinear</b> - [1-4 samples]"
"<br>Similarly to \"Nearest Neighbor\", it maintains a sharp look,"
"<br>but also does some blending to avoid shimmering."
"<br>Works best with 2D games at low resolutions."
"<br><br><b>Area Sampling</b> - [up to 324 samples]"
"<br>Weights pixels by the percentage of area they occupy. Gamma corrected."
"<br>Best for down scaling by more than 2x."
"<br><br><dolphin_emphasis>If unsure, select 'Default'.</dolphin_emphasis>");
static const char TR_COLOR_CORRECTION_DESCRIPTION[] =
QT_TR_NOOP("A group of features to make the colors more accurate,"
" matching the color space Wii and GC games were meant for.");
QT_TR_NOOP("A group of features to make the colors more accurate, matching the color space "
"Wii and GC games were meant for.");
static const char TR_POSTPROCESSING_DESCRIPTION[] =
QT_TR_NOOP("Applies a post-processing effect after rendering a frame.<br><br "
"/><dolphin_emphasis>If unsure, select (off).</dolphin_emphasis>");
@ -536,7 +602,11 @@ void EnhancementsWidget::AddDescriptions()
m_texture_filtering_combo->SetTitle(tr("Texture Filtering"));
m_texture_filtering_combo->SetDescription(tr(TR_FORCE_TEXTURE_FILTERING_DESCRIPTION));
m_configure_color_correction->setToolTip(tr(TR_COLOR_CORRECTION_DESCRIPTION));
m_output_resampling_combo->SetTitle(tr("Output Resampling"));
m_output_resampling_combo->SetDescription(tr(TR_OUTPUT_RESAMPLING_DESCRIPTION));
m_configure_color_correction->SetTitle(tr("Color Correction"));
m_configure_color_correction->SetDescription(tr(TR_COLOR_CORRECTION_DESCRIPTION));
m_pp_effect->SetTitle(tr("Post-Processing Effect"));
m_pp_effect->SetDescription(tr(TR_POSTPROCESSING_DESCRIPTION));

View file

@ -16,6 +16,7 @@ class QComboBox;
class QPushButton;
class QSlider;
class ToolTipComboBox;
class ToolTipPushButton;
class EnhancementsWidget final : public QWidget
{
@ -38,8 +39,9 @@ private:
ConfigChoice* m_ir_combo;
ToolTipComboBox* m_aa_combo;
ToolTipComboBox* m_texture_filtering_combo;
ToolTipComboBox* m_output_resampling_combo;
ToolTipComboBox* m_pp_effect;
QPushButton* m_configure_color_correction;
ToolTipPushButton* m_configure_color_correction;
QPushButton* m_configure_pp_effect;
ConfigBool* m_scaled_efb_copy;
ConfigBool* m_per_pixel_lighting;

View file

@ -192,7 +192,7 @@ void GraphicsModListWidget::OnModChanged(std::optional<std::string> absolute_pat
if (!absolute_path)
{
m_selected_mod_name->setText(QStringLiteral("No graphics mod selected"));
m_selected_mod_name->setText(tr("No graphics mod selected"));
m_selected_mod_name->setAlignment(Qt::AlignCenter);
return;
}

View file

@ -152,10 +152,8 @@ void LogWidget::CreateWidgets()
m_log_text->setUndoRedoEnabled(false);
m_log_text->setMaximumBlockCount(MAX_LOG_LINES);
QPalette palette = m_log_text->palette();
palette.setColor(QPalette::Base, Qt::black);
palette.setColor(QPalette::Text, Qt::white);
m_log_text->setPalette(palette);
m_log_text->setStyleSheet(
QStringLiteral("QPlainTextEdit { background-color: black; color: white; }"));
}
void LogWidget::ConnectWidgets()

View file

@ -22,6 +22,8 @@
#include "Core/HW/SystemTimers.h"
#include "Core/PowerPC/PowerPC.h"
#include "DolphinQt/Config/ConfigControls/ConfigBool.h"
#include "DolphinQt/QtUtils/SignalBlocking.h"
#include "DolphinQt/Settings.h"
static const std::map<PowerPC::CPUCore, const char*> CPU_CORE_NAMES = {
@ -63,21 +65,25 @@ void AdvancedPane::CreateLayout()
m_cpu_emulation_engine_combobox->addItem(tr(CPU_CORE_NAMES.at(cpu_core)));
}
m_enable_mmu_checkbox = new QCheckBox(tr("Enable MMU"));
m_enable_mmu_checkbox->setToolTip(tr(
"Enables the Memory Management Unit, needed for some games. (ON = Compatible, OFF = Fast)"));
m_enable_mmu_checkbox = new ConfigBool(tr("Enable MMU"), Config::MAIN_MMU);
m_enable_mmu_checkbox->SetDescription(
tr("Enables the Memory Management Unit, needed for some games. (ON = Compatible, OFF = "
"Fast)<br><br><dolphin_emphasis>If unsure, leave this unchecked.</dolphin_emphasis>"));
cpu_options_group_layout->addWidget(m_enable_mmu_checkbox);
m_pause_on_panic_checkbox = new QCheckBox(tr("Pause on Panic"));
m_pause_on_panic_checkbox->setToolTip(
tr("Pauses the emulation if a Read/Write or Unknown Instruction panic occurs.\nEnabling will "
"affect performance.\nThe performance impact is the same as having Enable MMU on."));
m_pause_on_panic_checkbox = new ConfigBool(tr("Pause on Panic"), Config::MAIN_PAUSE_ON_PANIC);
m_pause_on_panic_checkbox->SetDescription(
tr("Pauses the emulation if a Read/Write or Unknown Instruction panic occurs.<br>Enabling "
"will affect performance.<br>The performance impact is the same as having Enable MMU "
"on.<br><br><dolphin_emphasis>If unsure, leave this unchecked.</dolphin_emphasis>"));
cpu_options_group_layout->addWidget(m_pause_on_panic_checkbox);
m_accurate_cpu_cache_checkbox = new QCheckBox(tr("Enable Write-Back Cache (slow)"));
m_accurate_cpu_cache_checkbox->setToolTip(
tr("Enables emulation of the CPU write-back cache.\nEnabling will have a significant impact "
"on performance.\nThis should be left disabled unless absolutely needed."));
m_accurate_cpu_cache_checkbox =
new ConfigBool(tr("Enable Write-Back Cache (slow)"), Config::MAIN_ACCURATE_CPU_CACHE);
m_accurate_cpu_cache_checkbox->SetDescription(
tr("Enables emulation of the CPU write-back cache.<br>Enabling will have a significant "
"impact on performance.<br>This should be left disabled unless absolutely "
"needed.<br><br><dolphin_emphasis>If unsure, leave this unchecked.</dolphin_emphasis>"));
cpu_options_group_layout->addWidget(m_accurate_cpu_cache_checkbox);
auto* clock_override = new QGroupBox(tr("Clock Override"));
@ -184,21 +190,13 @@ void AdvancedPane::CreateLayout()
void AdvancedPane::ConnectLayout()
{
connect(m_cpu_emulation_engine_combobox,
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), [](int index) {
Config::SetBaseOrCurrent(Config::MAIN_CPU_CORE, PowerPC::AvailableCPUCores()[index]);
connect(m_cpu_emulation_engine_combobox, qOverload<int>(&QComboBox::currentIndexChanged),
[](int index) {
const auto cpu_cores = PowerPC::AvailableCPUCores();
if (index >= 0 && static_cast<size_t>(index) < cpu_cores.size())
Config::SetBaseOrCurrent(Config::MAIN_CPU_CORE, cpu_cores[index]);
});
connect(m_enable_mmu_checkbox, &QCheckBox::toggled, this,
[](bool checked) { Config::SetBaseOrCurrent(Config::MAIN_MMU, checked); });
connect(m_pause_on_panic_checkbox, &QCheckBox::toggled, this,
[](bool checked) { Config::SetBaseOrCurrent(Config::MAIN_PAUSE_ON_PANIC, checked); });
connect(m_accurate_cpu_cache_checkbox, &QCheckBox::toggled, this,
[](bool checked) { Config::SetBaseOrCurrent(Config::MAIN_ACCURATE_CPU_CACHE, checked); });
m_cpu_clock_override_checkbox->setChecked(Config::Get(Config::MAIN_OVERCLOCK_ENABLE));
connect(m_cpu_clock_override_checkbox, &QCheckBox::toggled, [this](bool enable_clock_override) {
Config::SetBaseOrCurrent(Config::MAIN_OVERCLOCK_ENABLE, enable_clock_override);
Update();
@ -211,7 +209,6 @@ void AdvancedPane::ConnectLayout()
Update();
});
m_ram_override_checkbox->setChecked(Config::Get(Config::MAIN_RAM_OVERRIDE_ENABLE));
connect(m_ram_override_checkbox, &QCheckBox::toggled, [this](bool enable_ram_override) {
Config::SetBaseOrCurrent(Config::MAIN_RAM_OVERRIDE_ENABLE, enable_ram_override);
Update();
@ -229,15 +226,11 @@ void AdvancedPane::ConnectLayout()
Update();
});
m_custom_rtc_checkbox->setChecked(Config::Get(Config::MAIN_CUSTOM_RTC_ENABLE));
connect(m_custom_rtc_checkbox, &QCheckBox::toggled, [this](bool enable_custom_rtc) {
Config::SetBaseOrCurrent(Config::MAIN_CUSTOM_RTC_ENABLE, enable_custom_rtc);
Update();
});
QDateTime initial_date_time;
initial_date_time.setSecsSinceEpoch(Config::Get(Config::MAIN_CUSTOM_RTC_VALUE));
m_custom_rtc_datetime->setDateTime(initial_date_time);
connect(m_custom_rtc_datetime, &QDateTimeEdit::dateTimeChanged, [this](QDateTime date_time) {
Config::SetBaseOrCurrent(Config::MAIN_CUSTOM_RTC_VALUE,
static_cast<u32>(date_time.toSecsSinceEpoch()));
@ -252,7 +245,7 @@ void AdvancedPane::Update()
const bool enable_ram_override_widgets = Config::Get(Config::MAIN_RAM_OVERRIDE_ENABLE);
const bool enable_custom_rtc_widgets = Config::Get(Config::MAIN_CUSTOM_RTC_ENABLE) && !running;
const std::vector<PowerPC::CPUCore>& available_cpu_cores = PowerPC::AvailableCPUCores();
const auto available_cpu_cores = PowerPC::AvailableCPUCores();
const auto cpu_core = Config::Get(Config::MAIN_CPU_CORE);
for (size_t i = 0; i < available_cpu_cores.size(); ++i)
{
@ -260,21 +253,19 @@ void AdvancedPane::Update()
m_cpu_emulation_engine_combobox->setCurrentIndex(int(i));
}
m_cpu_emulation_engine_combobox->setEnabled(!running);
m_enable_mmu_checkbox->setChecked(Config::Get(Config::MAIN_MMU));
m_enable_mmu_checkbox->setEnabled(!running);
m_pause_on_panic_checkbox->setChecked(Config::Get(Config::MAIN_PAUSE_ON_PANIC));
m_pause_on_panic_checkbox->setEnabled(!running);
m_accurate_cpu_cache_checkbox->setChecked(Config::Get(Config::MAIN_ACCURATE_CPU_CACHE));
m_accurate_cpu_cache_checkbox->setEnabled(!running);
QFont bf = font();
bf.setBold(Config::GetActiveLayerForConfig(Config::MAIN_OVERCLOCK_ENABLE) !=
Config::LayerType::Base);
m_cpu_clock_override_checkbox->setFont(bf);
m_cpu_clock_override_checkbox->setChecked(enable_cpu_clock_override_widgets);
{
QFont bf = font();
bf.setBold(Config::GetActiveLayerForConfig(Config::MAIN_OVERCLOCK_ENABLE) !=
Config::LayerType::Base);
const QSignalBlocker blocker(m_cpu_clock_override_checkbox);
m_cpu_clock_override_checkbox->setFont(bf);
m_cpu_clock_override_checkbox->setChecked(enable_cpu_clock_override_widgets);
}
m_cpu_clock_override_slider->setEnabled(enable_cpu_clock_override_widgets);
m_cpu_clock_override_slider_label->setEnabled(enable_cpu_clock_override_widgets);
@ -293,6 +284,7 @@ void AdvancedPane::Update()
}());
m_ram_override_checkbox->setEnabled(!running);
SignalBlocking(m_ram_override_checkbox)->setChecked(enable_ram_override_widgets);
m_mem1_override_slider->setEnabled(enable_ram_override_widgets && !running);
m_mem1_override_slider_label->setEnabled(enable_ram_override_widgets && !running);
@ -323,5 +315,10 @@ void AdvancedPane::Update()
}());
m_custom_rtc_checkbox->setEnabled(!running);
SignalBlocking(m_custom_rtc_checkbox)->setChecked(Config::Get(Config::MAIN_CUSTOM_RTC_ENABLE));
QDateTime initial_date_time;
initial_date_time.setSecsSinceEpoch(Config::Get(Config::MAIN_CUSTOM_RTC_VALUE));
m_custom_rtc_datetime->setEnabled(enable_custom_rtc_widgets);
SignalBlocking(m_custom_rtc_datetime)->setDateTime(initial_date_time);
}

View file

@ -7,6 +7,7 @@
#include <QWidget>
class ConfigBool;
class QCheckBox;
class QComboBox;
class QLabel;
@ -31,9 +32,9 @@ private:
void Update();
QComboBox* m_cpu_emulation_engine_combobox;
QCheckBox* m_enable_mmu_checkbox;
QCheckBox* m_pause_on_panic_checkbox;
QCheckBox* m_accurate_cpu_cache_checkbox;
ConfigBool* m_enable_mmu_checkbox;
ConfigBool* m_pause_on_panic_checkbox;
ConfigBool* m_accurate_cpu_cache_checkbox;
QCheckBox* m_cpu_clock_override_checkbox;
QSlider* m_cpu_clock_override_slider;
QLabel* m_cpu_clock_override_slider_label;

View file

@ -128,14 +128,16 @@ void USBDeviceAddToWhitelistDialog::AddUSBDeviceToWhitelist()
const std::string pid_string(StripWhitespace(device_pid_textbox->text().toStdString()));
if (!IsValidUSBIDString(vid_string))
{
// i18n: Here, VID means Vendor ID (for a USB device).
ModalMessageBox::critical(this, tr("USB Whitelist Error"), tr("The entered VID is invalid."));
ModalMessageBox::critical(this, tr("USB Whitelist Error"),
// i18n: Here, VID means Vendor ID (for a USB device).
tr("The entered VID is invalid."));
return;
}
if (!IsValidUSBIDString(pid_string))
{
// i18n: Here, PID means Product ID (for a USB device).
ModalMessageBox::critical(this, tr("USB Whitelist Error"), tr("The entered PID is invalid."));
ModalMessageBox::critical(this, tr("USB Whitelist Error"),
// i18n: Here, PID means Product ID (for a USB device).
tr("The entered PID is invalid."));
return;
}

View file

@ -50,7 +50,7 @@ TASInputWindow::TASInputWindow(QWidget* parent) : QDialog(parent)
QGridLayout* settings_layout = new QGridLayout;
m_use_controller = new QCheckBox(QStringLiteral("Enable Controller Inpu&t"));
m_use_controller = new QCheckBox(tr("Enable Controller Inpu&t"));
m_use_controller->setToolTip(tr("Warning: Analog inputs may reset to controller values at "
"random. In some cases this can be fixed by adding a deadzone."));
settings_layout->addWidget(m_use_controller, 0, 0, 1, 2);

View file

@ -203,11 +203,14 @@ bool InputConfig::IsControllerControlledByGamepadDevice(int index) const
const auto& controller = m_controllers.at(index).get()->GetDefaultDevice();
// By default on Android, no device is selected
if (controller.source == "")
return false;
// Filter out anything which obviously not a gamepad
return !((controller.source == "Quartz") // OSX Quartz Keyboard/Mouse
|| (controller.source == "XInput2") // Linux and BSD Keyboard/Mouse
|| (controller.source == "Android" &&
controller.name == "Touchscreen") // Android Touchscreen
|| (controller.source == "Android" && controller.cid <= 0) // Android non-gamepad device
|| (controller.source == "DInput" &&
controller.name == "Keyboard Mouse")); // Windows Keyboard/Mouse
}

View file

@ -20,14 +20,7 @@ struct PixelShaderData;
class CustomAssetLibrary
{
public:
// TODO: this should be std::chrono::system_clock::time_point to
// support any type of loader where the time isn't from the filesystem
// but there's no way to convert filesystem times to system times
// without 'clock_cast', once our builders catch up
// to support 'clock_cast' we should update this
// For now, it's fine as a filesystem library is all that is
// available
using TimeType = std::filesystem::file_time_type;
using TimeType = std::chrono::system_clock::time_point;
// The AssetID is a unique identifier for a particular asset
using AssetID = std::string;

View file

@ -17,6 +17,20 @@ namespace VideoCommon
{
namespace
{
std::chrono::system_clock::time_point FileTimeToSysTime(std::filesystem::file_time_type file_time)
{
#ifdef _WIN32
return std::chrono::clock_cast<std::chrono::system_clock>(file_time);
#else
// Note: all compilers should switch to chrono::clock_cast
// once it is available for use
const auto system_time_now = std::chrono::system_clock::now();
const auto file_time_now = decltype(file_time)::clock::now();
return std::chrono::time_point_cast<std::chrono::system_clock::duration>(
file_time - file_time_now + system_time_now);
#endif
}
std::size_t GetAssetSize(const CustomTextureData& data)
{
std::size_t total = 0;
@ -42,8 +56,9 @@ DirectFilesystemAssetLibrary::GetLastAssetWriteTime(const AssetID& asset_id) con
const auto tp = std::filesystem::last_write_time(value, ec);
if (ec)
continue;
if (tp > max_entry)
max_entry = tp;
auto tp_sys = FileTimeToSysTime(tp);
if (tp_sys > max_entry)
max_entry = tp_sys;
}
return max_entry;
}
@ -235,7 +250,7 @@ CustomAssetLibrary::LoadInfo DirectFilesystemAssetLibrary::LoadTexture(const Ass
if (!LoadMips(asset_path, data))
return {};
return LoadInfo{GetAssetSize(*data), last_loaded_time};
return LoadInfo{GetAssetSize(*data), FileTimeToSysTime(last_loaded_time)};
}
else if (ext == ".png")
{
@ -252,7 +267,7 @@ CustomAssetLibrary::LoadInfo DirectFilesystemAssetLibrary::LoadTexture(const Ass
if (!LoadMips(asset_path, data))
return {};
return LoadInfo{GetAssetSize(*data), last_loaded_time};
return LoadInfo{GetAssetSize(*data), FileTimeToSysTime(last_loaded_time)};
}
ERROR_LOG_FMT(VIDEO, "Asset '{}' error - extension '{}' unknown!", asset_id, ext);

View file

@ -64,6 +64,8 @@ add_library(videocommon
GeometryShaderManager.h
GraphicsModSystem/Config/GraphicsMod.cpp
GraphicsModSystem/Config/GraphicsMod.h
GraphicsModSystem/Config/GraphicsModAsset.cpp
GraphicsModSystem/Config/GraphicsModAsset.h
GraphicsModSystem/Config/GraphicsModFeature.cpp
GraphicsModSystem/Config/GraphicsModFeature.h
GraphicsModSystem/Config/GraphicsModGroup.cpp
@ -73,6 +75,8 @@ add_library(videocommon
GraphicsModSystem/Config/GraphicsTargetGroup.cpp
GraphicsModSystem/Config/GraphicsTargetGroup.h
GraphicsModSystem/Constants.h
GraphicsModSystem/Runtime/Actions/CustomPipelineAction.cpp
GraphicsModSystem/Runtime/Actions/CustomPipelineAction.h
GraphicsModSystem/Runtime/Actions/MoveAction.cpp
GraphicsModSystem/Runtime/Actions/MoveAction.h
GraphicsModSystem/Runtime/Actions/PrintAction.cpp
@ -81,6 +85,8 @@ add_library(videocommon
GraphicsModSystem/Runtime/Actions/ScaleAction.h
GraphicsModSystem/Runtime/Actions/SkipAction.cpp
GraphicsModSystem/Runtime/Actions/SkipAction.h
GraphicsModSystem/Runtime/CustomShaderCache.cpp
GraphicsModSystem/Runtime/CustomShaderCache.h
GraphicsModSystem/Runtime/FBInfo.cpp
GraphicsModSystem/Runtime/FBInfo.h
GraphicsModSystem/Runtime/GraphicsModAction.h

View file

@ -58,6 +58,8 @@ struct alignas(16) PixelShaderConstants
// For shader_framebuffer_fetch logic ops:
u32 logic_op_enable; // bool
LogicOp logic_op_mode;
// For custom shaders...
u32 time_ms;
};
struct alignas(16) VertexShaderConstants

View file

@ -178,6 +178,27 @@ bool GraphicsModConfig::DeserializeFromConfig(const picojson::value& value)
}
}
const auto& assets = value.get("assets");
if (assets.is<picojson::array>())
{
for (const auto& asset_val : assets.get<picojson::array>())
{
if (!asset_val.is<picojson::object>())
{
ERROR_LOG_FMT(
VIDEO, "Failed to load mod configuration file, specified asset is not a json object");
return false;
}
GraphicsModAssetConfig asset;
if (!asset.DeserializeFromConfig(asset_val.get<picojson::object>()))
{
return false;
}
m_assets.push_back(std::move(asset));
}
}
return true;
}

View file

@ -9,6 +9,7 @@
#include <picojson.h>
#include "VideoCommon/GraphicsModSystem/Config/GraphicsModAsset.h"
#include "VideoCommon/GraphicsModSystem/Config/GraphicsModFeature.h"
#include "VideoCommon/GraphicsModSystem/Config/GraphicsTargetGroup.h"
@ -30,6 +31,7 @@ struct GraphicsModConfig
std::vector<GraphicsTargetGroupConfig> m_groups;
std::vector<GraphicsModFeatureConfig> m_features;
std::vector<GraphicsModAssetConfig> m_assets;
static std::optional<GraphicsModConfig> Create(const std::string& file, Source source);
static std::optional<GraphicsModConfig> Create(const picojson::object* obj);

View file

@ -0,0 +1,53 @@
// Copyright 2023 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "VideoCommon/GraphicsModSystem/Config/GraphicsModAsset.h"
#include "Common/Logging/Log.h"
bool GraphicsModAssetConfig::DeserializeFromConfig(const picojson::object& obj)
{
auto name_iter = obj.find("name");
if (name_iter == obj.end())
{
ERROR_LOG_FMT(VIDEO, "Failed to load mod configuration file, specified asset has no name");
return false;
}
if (!name_iter->second.is<std::string>())
{
ERROR_LOG_FMT(VIDEO, "Failed to load mod configuration file, specified asset has a name "
"that is not a string");
return false;
}
m_name = name_iter->second.to_str();
auto data_iter = obj.find("data");
if (data_iter == obj.end())
{
ERROR_LOG_FMT(VIDEO, "Failed to load mod configuration file, specified asset '{}' has no data",
m_name);
return false;
}
if (!data_iter->second.is<picojson::object>())
{
ERROR_LOG_FMT(VIDEO,
"Failed to load mod configuration file, specified asset '{}' has data "
"that is not an object",
m_name);
return false;
}
for (const auto& [key, value] : data_iter->second.get<picojson::object>())
{
if (!value.is<std::string>())
{
ERROR_LOG_FMT(VIDEO,
"Failed to load mod configuration file, specified asset '{}' has data "
"with a value for key '{}' that is not a string",
m_name, key);
return false;
}
m_map[key] = value.to_str();
}
return true;
}

View file

@ -0,0 +1,18 @@
// Copyright 2023 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <string>
#include <picojson.h>
#include "VideoCommon/Assets/DirectFilesystemAssetLibrary.h"
struct GraphicsModAssetConfig
{
std::string m_name;
VideoCommon::DirectFilesystemAssetLibrary::AssetMap m_map;
bool DeserializeFromConfig(const picojson::object& obj);
};

View file

@ -0,0 +1,449 @@
// Copyright 2022 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "VideoCommon/GraphicsModSystem/Runtime/Actions/CustomPipelineAction.h"
#include <algorithm>
#include <array>
#include <fmt/format.h>
#include "Common/FileUtil.h"
#include "Common/Logging/Log.h"
#include "Common/StringUtil.h"
#include "Core/System.h"
#include "VideoCommon/AbstractGfx.h"
#include "VideoCommon/Assets/CustomAssetLoader.h"
#include "VideoCommon/Assets/DirectFilesystemAssetLibrary.h"
#include "VideoCommon/ShaderGenCommon.h"
#include "VideoCommon/TextureCacheBase.h"
namespace
{
bool IsQualifier(std::string_view value)
{
static std::array<std::string_view, 7> qualifiers = {"attribute", "const", "highp", "lowp",
"mediump", "uniform", "varying"};
return std::find(qualifiers.begin(), qualifiers.end(), value) != qualifiers.end();
}
bool IsBuiltInMacro(std::string_view value)
{
static std::array<std::string_view, 5> built_in = {"__LINE__", "__FILE__", "__VERSION__",
"GL_core_profile", "GL_compatibility_profile"};
return std::find(built_in.begin(), built_in.end(), value) != built_in.end();
}
std::vector<std::string> GlobalConflicts(std::string_view source)
{
std::string_view last_identifier = "";
std::vector<std::string> global_result;
u32 scope = 0;
for (u32 i = 0; i < source.size(); i++)
{
// If we're out of global scope, we don't care
// about any of the details
if (scope > 0)
{
if (source[i] == '{')
{
scope++;
}
else if (source[i] == '}')
{
scope--;
}
continue;
}
const auto parse_identifier = [&]() {
const u32 start = i;
for (; i < source.size(); i++)
{
if (!Common::IsAlpha(source[i]) && source[i] != '_' && !std::isdigit(source[i]))
break;
}
u32 end = i;
i--; // unwind
return source.substr(start, end - start);
};
if (Common::IsAlpha(source[i]) || source[i] == '_')
{
const std::string_view identifier = parse_identifier();
if (IsQualifier(identifier))
continue;
if (IsBuiltInMacro(identifier))
continue;
last_identifier = identifier;
}
else if (source[i] == '#')
{
const auto parse_until_end_of_preprocessor = [&]() {
bool continue_until_next_newline = false;
for (; i < source.size(); i++)
{
if (source[i] == '\n')
{
if (continue_until_next_newline)
continue_until_next_newline = false;
else
break;
}
else if (source[i] == '\\')
{
continue_until_next_newline = true;
}
}
};
i++;
const std::string_view identifier = parse_identifier();
if (identifier == "define")
{
i++;
// skip whitespace
while (source[i] == ' ')
{
i++;
}
global_result.push_back(std::string{parse_identifier()});
parse_until_end_of_preprocessor();
}
else
{
parse_until_end_of_preprocessor();
}
}
else if (source[i] == '{')
{
scope++;
}
else if (source[i] == '(')
{
// Unlikely the user will be using layouts but...
if (last_identifier == "layout")
continue;
// Since we handle equality, we can assume the identifier
// before '(' is a function definition
global_result.push_back(std::string{last_identifier});
}
else if (source[i] == '=')
{
global_result.push_back(std::string{last_identifier});
i++;
for (; i < source.size(); i++)
{
if (source[i] == ';')
break;
}
}
else if (source[i] == '/')
{
if ((i + 1) >= source.size())
continue;
if (source[i + 1] == '/')
{
// Go to end of line...
for (; i < source.size(); i++)
{
if (source[i] == '\n')
break;
}
}
else if (source[i + 1] == '*')
{
// Multiline, look for first '*/'
for (; i < source.size(); i++)
{
if (source[i] == '/' && source[i - 1] == '*')
break;
}
}
}
}
// Sort the conflicts from largest to smallest string
// this way we can ensure smaller strings that are a substring
// of the larger string are able to be replaced appropriately
std::sort(global_result.begin(), global_result.end(),
[](const std::string& first, const std::string& second) {
return first.size() > second.size();
});
return global_result;
}
void WriteDefines(ShaderCode* out, const std::vector<std::string>& texture_code_names,
u32 texture_unit)
{
for (std::size_t i = 0; i < texture_code_names.size(); i++)
{
const auto& code_name = texture_code_names[i];
out->Write("#define {}_UNIT_{{0}} {}\n", code_name, texture_unit);
out->Write(
"#define {0}_COORD_{{0}} float3(data.texcoord[data.texmap_to_texcoord_index[{1}]].xy, "
"{2})\n",
code_name, texture_unit, i + 1);
}
}
} // namespace
std::unique_ptr<CustomPipelineAction>
CustomPipelineAction::Create(const picojson::value& json_data,
std::shared_ptr<VideoCommon::CustomAssetLibrary> library)
{
std::vector<CustomPipelineAction::PipelinePassPassDescription> pipeline_passes;
const auto& passes_json = json_data.get("passes");
if (passes_json.is<picojson::array>())
{
for (const auto& passes_json_val : passes_json.get<picojson::array>())
{
CustomPipelineAction::PipelinePassPassDescription pipeline_pass;
if (!passes_json_val.is<picojson::object>())
{
ERROR_LOG_FMT(VIDEO,
"Failed to load custom pipeline action, 'passes' has an array value that "
"is not an object!");
return nullptr;
}
auto pass = passes_json_val.get<picojson::object>();
if (!pass.contains("pixel_material_asset"))
{
ERROR_LOG_FMT(VIDEO,
"Failed to load custom pipeline action, 'passes' value missing required "
"field 'pixel_material_asset'");
return nullptr;
}
auto pixel_material_asset_json = pass["pixel_material_asset"];
if (!pixel_material_asset_json.is<std::string>())
{
ERROR_LOG_FMT(VIDEO, "Failed to load custom pipeline action, 'passes' field "
"'pixel_material_asset' is not a string!");
return nullptr;
}
pipeline_pass.m_pixel_material_asset = pixel_material_asset_json.to_str();
pipeline_passes.push_back(std::move(pipeline_pass));
}
}
if (pipeline_passes.empty())
{
ERROR_LOG_FMT(VIDEO, "Failed to load custom pipeline action, must specify at least one pass");
return nullptr;
}
if (pipeline_passes.size() > 1)
{
ERROR_LOG_FMT(
VIDEO,
"Failed to load custom pipeline action, multiple passes are not currently supported");
return nullptr;
}
return std::make_unique<CustomPipelineAction>(std::move(library), std::move(pipeline_passes));
}
CustomPipelineAction::CustomPipelineAction(
std::shared_ptr<VideoCommon::CustomAssetLibrary> library,
std::vector<PipelinePassPassDescription> pass_descriptions)
: m_library(std::move(library)), m_passes_config(std::move(pass_descriptions))
{
m_passes.resize(m_passes_config.size());
}
CustomPipelineAction::~CustomPipelineAction() = default;
void CustomPipelineAction::OnDrawStarted(GraphicsModActionData::DrawStarted* draw_started)
{
if (!draw_started) [[unlikely]]
return;
if (!draw_started->custom_pixel_shader) [[unlikely]]
return;
if (!m_valid)
return;
if (m_passes.empty()) [[unlikely]]
return;
// For now assume a single pass
auto& pass = m_passes[0];
if (!pass.m_pixel_shader.m_asset) [[unlikely]]
return;
const auto shader_data = pass.m_pixel_shader.m_asset->GetData();
if (shader_data)
{
if (pass.m_pixel_shader.m_asset->GetLastLoadedTime() > pass.m_pixel_shader.m_cached_write_time)
{
const auto material = pass.m_pixel_material.m_asset->GetData();
if (!material)
return;
pass.m_pixel_shader.m_cached_write_time = pass.m_pixel_shader.m_asset->GetLastLoadedTime();
for (const auto& prop : material->properties)
{
if (!shader_data->m_properties.contains(prop.m_code_name))
{
ERROR_LOG_FMT(VIDEO,
"Custom pipeline has material asset '{}' that has property '{}'"
"that is not on shader asset '{}'",
pass.m_pixel_material.m_asset->GetAssetId(), prop.m_code_name,
pass.m_pixel_shader.m_asset->GetAssetId());
return;
}
}
// Calculate shader details
std::string color_shader_data =
ReplaceAll(shader_data->m_shader_source, "custom_main", CUSTOM_PIXELSHADER_COLOR_FUNC);
const auto global_conflicts = GlobalConflicts(color_shader_data);
color_shader_data = ReplaceAll(color_shader_data, "\r\n", "\n");
color_shader_data = ReplaceAll(color_shader_data, "{", "{{");
color_shader_data = ReplaceAll(color_shader_data, "}", "}}");
// First replace global conflicts with dummy strings
// This avoids the problem where a shorter word
// is in a longer word, ex two functions: 'execute' and 'execute_fast'
for (std::size_t i = 0; i < global_conflicts.size(); i++)
{
const std::string& identifier = global_conflicts[i];
color_shader_data =
ReplaceAll(color_shader_data, identifier, fmt::format("_{0}_DOLPHIN_TEMP_{0}_", i));
}
// Now replace the temporaries with the actual value
for (std::size_t i = 0; i < global_conflicts.size(); i++)
{
const std::string& identifier = global_conflicts[i];
color_shader_data = ReplaceAll(color_shader_data, fmt::format("_{0}_DOLPHIN_TEMP_{0}_", i),
fmt::format("{}_{{0}}", identifier));
}
for (const auto& texture_code_name : m_texture_code_names)
{
color_shader_data =
ReplaceAll(color_shader_data, fmt::format("{}_COORD", texture_code_name),
fmt::format("{}_COORD_{{0}}", texture_code_name));
color_shader_data = ReplaceAll(color_shader_data, fmt::format("{}_UNIT", texture_code_name),
fmt::format("{}_UNIT_{{0}}", texture_code_name));
}
m_last_generated_shader_code = ShaderCode{};
WriteDefines(&m_last_generated_shader_code, m_texture_code_names, draw_started->texture_unit);
m_last_generated_shader_code.Write("{}", color_shader_data);
}
CustomPixelShader custom_pixel_shader;
custom_pixel_shader.custom_shader = m_last_generated_shader_code.GetBuffer();
*draw_started->custom_pixel_shader = custom_pixel_shader;
}
}
void CustomPipelineAction::OnTextureCreate(GraphicsModActionData::TextureCreate* create)
{
if (!create->custom_textures) [[unlikely]]
return;
if (!create->additional_dependencies) [[unlikely]]
return;
if (m_passes_config.empty()) [[unlikely]]
return;
if (m_passes.empty()) [[unlikely]]
return;
m_valid = true;
auto& loader = Core::System::GetInstance().GetCustomAssetLoader();
// For now assume a single pass
const auto& pass_config = m_passes_config[0];
auto& pass = m_passes[0];
if (!pass.m_pixel_material.m_asset)
{
pass.m_pixel_material.m_asset =
loader.LoadMaterial(pass_config.m_pixel_material_asset, m_library);
pass.m_pixel_material.m_cached_write_time = pass.m_pixel_material.m_asset->GetLastLoadedTime();
}
create->additional_dependencies->push_back(VideoCommon::CachedAsset<VideoCommon::CustomAsset>{
pass.m_pixel_material.m_asset, pass.m_pixel_material.m_asset->GetLastLoadedTime()});
const auto material_data = pass.m_pixel_material.m_asset->GetData();
if (!material_data)
return;
if (!pass.m_pixel_shader.m_asset || pass.m_pixel_material.m_asset->GetLastLoadedTime() >
pass.m_pixel_material.m_cached_write_time)
{
pass.m_pixel_shader.m_asset = loader.LoadPixelShader(material_data->shader_asset, m_library);
// Note: the asset timestamp will be updated in the draw command
}
create->additional_dependencies->push_back(VideoCommon::CachedAsset<VideoCommon::CustomAsset>{
pass.m_pixel_shader.m_asset, pass.m_pixel_shader.m_asset->GetLastLoadedTime()});
m_texture_code_names.clear();
std::vector<VideoCommon::CachedAsset<VideoCommon::GameTextureAsset>> game_assets;
for (const auto& property : material_data->properties)
{
if (property.m_type == VideoCommon::MaterialProperty::Type::Type_TextureAsset)
{
if (property.m_value)
{
if (auto* value = std::get_if<std::string>(&*property.m_value))
{
auto asset = loader.LoadGameTexture(*value, m_library);
if (asset)
{
const auto loaded_time = asset->GetLastLoadedTime();
game_assets.push_back(VideoCommon::CachedAsset<VideoCommon::GameTextureAsset>{
std::move(asset), loaded_time});
m_texture_code_names.push_back(property.m_code_name);
}
}
}
}
}
// Note: we swap here instead of doing a clear + append of the member
// variable so that any loaded assets from previous iterations
// won't be let go
std::swap(pass.m_game_textures, game_assets);
for (auto& game_texture : pass.m_game_textures)
{
if (game_texture.m_asset)
{
auto data = game_texture.m_asset->GetData();
if (data)
{
if (create->texture_width != data->m_levels[0].width ||
create->texture_height != data->m_levels[0].height)
{
ERROR_LOG_FMT(VIDEO,
"Custom pipeline for texture '{}' has asset '{}' that does not match "
"the width/height of the texture loaded. Texture {}x{} vs asset {}x{}",
create->texture_name, game_texture.m_asset->GetAssetId(),
create->texture_width, create->texture_height, data->m_levels[0].width,
data->m_levels[0].height);
m_valid = false;
}
}
else
{
m_valid = false;
}
}
}
// TODO: compare game textures and shader requirements
create->custom_textures->insert(create->custom_textures->end(), pass.m_game_textures.begin(),
pass.m_game_textures.end());
}

View file

@ -0,0 +1,54 @@
// Copyright 2022 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <memory>
#include <string>
#include <string_view>
#include <vector>
#include <picojson.h>
#include "VideoCommon/AbstractTexture.h"
#include "VideoCommon/Assets/CustomAssetLibrary.h"
#include "VideoCommon/Assets/MaterialAsset.h"
#include "VideoCommon/Assets/ShaderAsset.h"
#include "VideoCommon/Assets/TextureAsset.h"
#include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModAction.h"
#include "VideoCommon/ShaderGenCommon.h"
class CustomPipelineAction final : public GraphicsModAction
{
public:
struct PipelinePassPassDescription
{
std::string m_pixel_material_asset;
};
static std::unique_ptr<CustomPipelineAction>
Create(const picojson::value& json_data,
std::shared_ptr<VideoCommon::CustomAssetLibrary> library);
CustomPipelineAction(std::shared_ptr<VideoCommon::CustomAssetLibrary> library,
std::vector<PipelinePassPassDescription> pass_descriptions);
~CustomPipelineAction();
void OnDrawStarted(GraphicsModActionData::DrawStarted*) override;
void OnTextureCreate(GraphicsModActionData::TextureCreate*) override;
private:
std::shared_ptr<VideoCommon::CustomAssetLibrary> m_library;
std::vector<PipelinePassPassDescription> m_passes_config;
struct PipelinePass
{
VideoCommon::CachedAsset<VideoCommon::MaterialAsset> m_pixel_material;
VideoCommon::CachedAsset<VideoCommon::PixelShaderAsset> m_pixel_shader;
std::vector<VideoCommon::CachedAsset<VideoCommon::GameTextureAsset>> m_game_textures;
};
std::vector<PipelinePass> m_passes;
ShaderCode m_last_generated_shader_code;
bool m_valid = true;
std::vector<std::string> m_texture_code_names;
};

View file

@ -0,0 +1,376 @@
// Copyright 2022 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "VideoCommon/GraphicsModSystem/Runtime/CustomShaderCache.h"
#include "VideoCommon/AbstractGfx.h"
#include "VideoCommon/VideoConfig.h"
CustomShaderCache::CustomShaderCache()
{
m_api_type = g_ActiveConfig.backend_info.api_type;
m_host_config.bits = ShaderHostConfig::GetCurrent().bits;
m_async_shader_compiler = g_gfx->CreateAsyncShaderCompiler();
m_async_shader_compiler->StartWorkerThreads(1); // TODO
m_async_uber_shader_compiler = g_gfx->CreateAsyncShaderCompiler();
m_async_uber_shader_compiler->StartWorkerThreads(1); // TODO
m_frame_end_handler =
AfterFrameEvent::Register([this] { RetrieveAsyncShaders(); }, "RetreiveAsyncShaders");
}
CustomShaderCache::~CustomShaderCache()
{
if (m_async_shader_compiler)
m_async_shader_compiler->StopWorkerThreads();
if (m_async_uber_shader_compiler)
m_async_uber_shader_compiler->StopWorkerThreads();
}
void CustomShaderCache::RetrieveAsyncShaders()
{
m_async_shader_compiler->RetrieveWorkItems();
m_async_uber_shader_compiler->RetrieveWorkItems();
}
void CustomShaderCache::Reload()
{
while (m_async_shader_compiler->HasPendingWork() || m_async_shader_compiler->HasCompletedWork())
{
m_async_shader_compiler->RetrieveWorkItems();
}
while (m_async_uber_shader_compiler->HasPendingWork() ||
m_async_uber_shader_compiler->HasCompletedWork())
{
m_async_uber_shader_compiler->RetrieveWorkItems();
}
m_ps_cache = {};
m_uber_ps_cache = {};
m_pipeline_cache = {};
m_uber_pipeline_cache = {};
}
std::optional<const AbstractPipeline*>
CustomShaderCache::GetPipelineAsync(const VideoCommon::GXPipelineUid& uid,
const CustomShaderInstance& custom_shaders,
const AbstractPipelineConfig& pipeline_config)
{
if (auto holder = m_pipeline_cache.GetHolder(uid, custom_shaders))
{
if (holder->pending)
return std::nullopt;
return holder->value.get();
}
AsyncCreatePipeline(uid, custom_shaders, pipeline_config);
return std::nullopt;
}
std::optional<const AbstractPipeline*>
CustomShaderCache::GetPipelineAsync(const VideoCommon::GXUberPipelineUid& uid,
const CustomShaderInstance& custom_shaders,
const AbstractPipelineConfig& pipeline_config)
{
if (auto holder = m_uber_pipeline_cache.GetHolder(uid, custom_shaders))
{
if (holder->pending)
return std::nullopt;
return holder->value.get();
}
AsyncCreatePipeline(uid, custom_shaders, pipeline_config);
return std::nullopt;
}
void CustomShaderCache::AsyncCreatePipeline(const VideoCommon::GXPipelineUid& uid,
const CustomShaderInstance& custom_shaders,
const AbstractPipelineConfig& pipeline_config)
{
class PipelineWorkItem final : public VideoCommon::AsyncShaderCompiler::WorkItem
{
public:
PipelineWorkItem(CustomShaderCache* shader_cache, const VideoCommon::GXPipelineUid& uid,
const CustomShaderInstance& custom_shaders, PipelineIterator iterator,
const AbstractPipelineConfig& pipeline_config)
: m_shader_cache(shader_cache), m_uid(uid), m_iterator(iterator),
m_custom_shaders(custom_shaders), m_config(pipeline_config)
{
SetStagesReady();
}
void SetStagesReady()
{
m_stages_ready = true;
PixelShaderUid ps_uid = m_uid.ps_uid;
ClearUnusedPixelShaderUidBits(m_shader_cache->m_api_type, m_shader_cache->m_host_config,
&ps_uid);
if (auto holder = m_shader_cache->m_ps_cache.GetHolder(ps_uid, m_custom_shaders))
{
// If the pixel shader is no longer pending compilation
// and the shader compilation succeeded, set
// the pipeline to use the new pixel shader.
// Otherwise, use the existing shader.
if (!holder->pending && holder->value.get())
{
m_config.pixel_shader = holder->value.get();
}
m_stages_ready &= !holder->pending;
}
else
{
m_stages_ready &= false;
m_shader_cache->QueuePixelShaderCompile(ps_uid, m_custom_shaders);
}
}
bool Compile() override
{
if (m_stages_ready)
{
m_pipeline = g_gfx->CreatePipeline(m_config);
}
return true;
}
void Retrieve() override
{
if (m_stages_ready)
{
m_shader_cache->NotifyPipelineFinished(m_iterator, std::move(m_pipeline));
}
else
{
// Re-queue for next frame.
auto wi = m_shader_cache->m_async_shader_compiler->CreateWorkItem<PipelineWorkItem>(
m_shader_cache, m_uid, m_custom_shaders, m_iterator, m_config);
m_shader_cache->m_async_shader_compiler->QueueWorkItem(std::move(wi), 0);
}
}
private:
CustomShaderCache* m_shader_cache;
std::unique_ptr<AbstractPipeline> m_pipeline;
VideoCommon::GXPipelineUid m_uid;
PipelineIterator m_iterator;
AbstractPipelineConfig m_config;
CustomShaderInstance m_custom_shaders;
bool m_stages_ready;
};
auto list_iter = m_pipeline_cache.InsertElement(uid, custom_shaders);
auto work_item = m_async_shader_compiler->CreateWorkItem<PipelineWorkItem>(
this, uid, custom_shaders, list_iter, pipeline_config);
m_async_shader_compiler->QueueWorkItem(std::move(work_item), 0);
}
void CustomShaderCache::AsyncCreatePipeline(const VideoCommon::GXUberPipelineUid& uid,
const CustomShaderInstance& custom_shaders,
const AbstractPipelineConfig& pipeline_config)
{
class PipelineWorkItem final : public VideoCommon::AsyncShaderCompiler::WorkItem
{
public:
PipelineWorkItem(CustomShaderCache* shader_cache, const VideoCommon::GXUberPipelineUid& uid,
const CustomShaderInstance& custom_shaders, UberPipelineIterator iterator,
const AbstractPipelineConfig& pipeline_config)
: m_shader_cache(shader_cache), m_uid(uid), m_iterator(iterator),
m_custom_shaders(custom_shaders), m_config(pipeline_config)
{
SetStagesReady();
}
void SetStagesReady()
{
m_stages_ready = true;
UberShader::PixelShaderUid ps_uid = m_uid.ps_uid;
ClearUnusedPixelShaderUidBits(m_shader_cache->m_api_type, m_shader_cache->m_host_config,
&ps_uid);
if (auto holder = m_shader_cache->m_uber_ps_cache.GetHolder(ps_uid, m_custom_shaders))
{
if (!holder->pending && holder->value.get())
{
m_config.pixel_shader = holder->value.get();
}
m_stages_ready &= !holder->pending;
}
else
{
m_stages_ready &= false;
m_shader_cache->QueuePixelShaderCompile(ps_uid, m_custom_shaders);
}
}
bool Compile() override
{
if (m_stages_ready)
{
if (m_config.pixel_shader == nullptr || m_config.vertex_shader == nullptr)
return false;
m_pipeline = g_gfx->CreatePipeline(m_config);
}
return true;
}
void Retrieve() override
{
if (m_stages_ready)
{
m_shader_cache->NotifyPipelineFinished(m_iterator, std::move(m_pipeline));
}
else
{
// Re-queue for next frame.
auto wi = m_shader_cache->m_async_uber_shader_compiler->CreateWorkItem<PipelineWorkItem>(
m_shader_cache, m_uid, m_custom_shaders, m_iterator, m_config);
m_shader_cache->m_async_uber_shader_compiler->QueueWorkItem(std::move(wi), 0);
}
}
private:
CustomShaderCache* m_shader_cache;
std::unique_ptr<AbstractPipeline> m_pipeline;
VideoCommon::GXUberPipelineUid m_uid;
UberPipelineIterator m_iterator;
AbstractPipelineConfig m_config;
CustomShaderInstance m_custom_shaders;
bool m_stages_ready;
};
auto list_iter = m_uber_pipeline_cache.InsertElement(uid, custom_shaders);
auto work_item = m_async_uber_shader_compiler->CreateWorkItem<PipelineWorkItem>(
this, uid, custom_shaders, list_iter, pipeline_config);
m_async_uber_shader_compiler->QueueWorkItem(std::move(work_item), 0);
}
void CustomShaderCache::NotifyPipelineFinished(PipelineIterator iterator,
std::unique_ptr<AbstractPipeline> pipeline)
{
iterator->second.pending = false;
iterator->second.value = std::move(pipeline);
}
void CustomShaderCache::NotifyPipelineFinished(UberPipelineIterator iterator,
std::unique_ptr<AbstractPipeline> pipeline)
{
iterator->second.pending = false;
iterator->second.value = std::move(pipeline);
}
void CustomShaderCache::QueuePixelShaderCompile(const PixelShaderUid& uid,
const CustomShaderInstance& custom_shaders)
{
class PixelShaderWorkItem final : public VideoCommon::AsyncShaderCompiler::WorkItem
{
public:
PixelShaderWorkItem(CustomShaderCache* shader_cache, const PixelShaderUid& uid,
const CustomShaderInstance& custom_shaders, PixelShaderIterator iter)
: m_shader_cache(shader_cache), m_uid(uid), m_custom_shaders(custom_shaders), m_iter(iter)
{
}
bool Compile() override
{
m_shader = m_shader_cache->CompilePixelShader(m_uid, m_custom_shaders);
return true;
}
void Retrieve() override
{
m_shader_cache->NotifyPixelShaderFinished(m_iter, std::move(m_shader));
}
private:
CustomShaderCache* m_shader_cache;
std::unique_ptr<AbstractShader> m_shader;
PixelShaderUid m_uid;
CustomShaderInstance m_custom_shaders;
PixelShaderIterator m_iter;
};
auto list_iter = m_ps_cache.InsertElement(uid, custom_shaders);
auto work_item = m_async_shader_compiler->CreateWorkItem<PixelShaderWorkItem>(
this, uid, custom_shaders, list_iter);
m_async_shader_compiler->QueueWorkItem(std::move(work_item), 0);
}
void CustomShaderCache::QueuePixelShaderCompile(const UberShader::PixelShaderUid& uid,
const CustomShaderInstance& custom_shaders)
{
class PixelShaderWorkItem final : public VideoCommon::AsyncShaderCompiler::WorkItem
{
public:
PixelShaderWorkItem(CustomShaderCache* shader_cache, const UberShader::PixelShaderUid& uid,
const CustomShaderInstance& custom_shaders, UberPixelShaderIterator iter)
: m_shader_cache(shader_cache), m_uid(uid), m_custom_shaders(custom_shaders), m_iter(iter)
{
}
bool Compile() override
{
m_shader = m_shader_cache->CompilePixelShader(m_uid, m_custom_shaders);
return true;
}
void Retrieve() override
{
m_shader_cache->NotifyPixelShaderFinished(m_iter, std::move(m_shader));
}
private:
CustomShaderCache* m_shader_cache;
std::unique_ptr<AbstractShader> m_shader;
UberShader::PixelShaderUid m_uid;
CustomShaderInstance m_custom_shaders;
UberPixelShaderIterator m_iter;
};
auto list_iter = m_uber_ps_cache.InsertElement(uid, custom_shaders);
auto work_item = m_async_uber_shader_compiler->CreateWorkItem<PixelShaderWorkItem>(
this, uid, custom_shaders, list_iter);
m_async_uber_shader_compiler->QueueWorkItem(std::move(work_item), 0);
}
std::unique_ptr<AbstractShader>
CustomShaderCache::CompilePixelShader(const PixelShaderUid& uid,
const CustomShaderInstance& custom_shaders) const
{
const ShaderCode source_code = GeneratePixelShaderCode(
m_api_type, m_host_config, uid.GetUidData(), custom_shaders.pixel_contents);
return g_gfx->CreateShaderFromSource(ShaderStage::Pixel, source_code.GetBuffer(),
"Custom Pixel Shader");
}
std::unique_ptr<AbstractShader>
CustomShaderCache::CompilePixelShader(const UberShader::PixelShaderUid& uid,
const CustomShaderInstance& custom_shaders) const
{
const ShaderCode source_code =
GenPixelShader(m_api_type, m_host_config, uid.GetUidData(), custom_shaders.pixel_contents);
return g_gfx->CreateShaderFromSource(ShaderStage::Pixel, source_code.GetBuffer(),
"Custom Uber Pixel Shader");
}
void CustomShaderCache::NotifyPixelShaderFinished(PixelShaderIterator iterator,
std::unique_ptr<AbstractShader> shader)
{
iterator->second.pending = false;
iterator->second.value = std::move(shader);
}
void CustomShaderCache::NotifyPixelShaderFinished(UberPixelShaderIterator iterator,
std::unique_ptr<AbstractShader> shader)
{
iterator->second.pending = false;
iterator->second.value = std::move(shader);
}

View file

@ -0,0 +1,144 @@
// Copyright 2022 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <list>
#include <map>
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include "VideoCommon/AbstractPipeline.h"
#include "VideoCommon/AbstractShader.h"
#include "VideoCommon/AsyncShaderCompiler.h"
#include "VideoCommon/GXPipelineTypes.h"
#include "VideoCommon/PixelShaderGen.h"
#include "VideoCommon/ShaderGenCommon.h"
#include "VideoCommon/UberShaderPixel.h"
#include "VideoCommon/VideoEvents.h"
struct CustomShaderInstance
{
CustomPixelShaderContents pixel_contents;
bool operator==(const CustomShaderInstance& other) const = default;
};
class CustomShaderCache
{
public:
CustomShaderCache();
~CustomShaderCache();
CustomShaderCache(const CustomShaderCache&) = delete;
CustomShaderCache(CustomShaderCache&&) = delete;
CustomShaderCache& operator=(const CustomShaderCache&) = delete;
CustomShaderCache& operator=(CustomShaderCache&&) = delete;
// Changes the shader host config. Shaders should be reloaded afterwards.
void SetHostConfig(const ShaderHostConfig& host_config) { m_host_config.bits = host_config.bits; }
// Retrieves all pending shaders/pipelines from the async compiler.
void RetrieveAsyncShaders();
// Reloads/recreates all shaders and pipelines.
void Reload();
// The optional will be empty if this pipeline is now background compiling.
std::optional<const AbstractPipeline*>
GetPipelineAsync(const VideoCommon::GXPipelineUid& uid,
const CustomShaderInstance& custom_shaders,
const AbstractPipelineConfig& pipeline_config);
std::optional<const AbstractPipeline*>
GetPipelineAsync(const VideoCommon::GXUberPipelineUid& uid,
const CustomShaderInstance& custom_shaders,
const AbstractPipelineConfig& pipeline_config);
private:
// Configuration bits.
APIType m_api_type = APIType::Nothing;
ShaderHostConfig m_host_config = {};
std::unique_ptr<VideoCommon::AsyncShaderCompiler> m_async_shader_compiler;
std::unique_ptr<VideoCommon::AsyncShaderCompiler> m_async_uber_shader_compiler;
void AsyncCreatePipeline(const VideoCommon::GXPipelineUid& uid,
const CustomShaderInstance& custom_shaders,
const AbstractPipelineConfig& pipeline_config);
void AsyncCreatePipeline(const VideoCommon::GXUberPipelineUid& uid,
const CustomShaderInstance& custom_shaders,
const AbstractPipelineConfig& pipeline_config);
// Shader/Pipeline cache helper
template <typename Uid, typename ValueType>
struct Cache
{
struct CacheHolder
{
std::unique_ptr<ValueType> value = nullptr;
bool pending = true;
};
using CacheElement = std::pair<CustomShaderInstance, CacheHolder>;
using CacheList = std::list<CacheElement>;
std::map<Uid, CacheList> uid_to_cachelist;
const CacheHolder* GetHolder(const Uid& uid, const CustomShaderInstance& custom_shaders) const
{
if (auto uuid_it = uid_to_cachelist.find(uid); uuid_it != uid_to_cachelist.end())
{
for (const auto& [custom_shader_val, holder] : uuid_it->second)
{
if (custom_shaders == custom_shader_val)
{
return &holder;
}
}
}
return nullptr;
}
typename CacheList::iterator InsertElement(const Uid& uid,
const CustomShaderInstance& custom_shaders)
{
CacheList& cachelist = uid_to_cachelist[uid];
CacheElement e{custom_shaders, CacheHolder{}};
return cachelist.emplace(cachelist.begin(), std::move(e));
}
};
Cache<PixelShaderUid, AbstractShader> m_ps_cache;
Cache<UberShader::PixelShaderUid, AbstractShader> m_uber_ps_cache;
Cache<VideoCommon::GXPipelineUid, AbstractPipeline> m_pipeline_cache;
Cache<VideoCommon::GXUberPipelineUid, AbstractPipeline> m_uber_pipeline_cache;
using PipelineIterator = Cache<VideoCommon::GXPipelineUid, AbstractPipeline>::CacheList::iterator;
using UberPipelineIterator =
Cache<VideoCommon::GXUberPipelineUid, AbstractPipeline>::CacheList::iterator;
using PixelShaderIterator = Cache<PixelShaderUid, AbstractShader>::CacheList::iterator;
using UberPixelShaderIterator =
Cache<UberShader::PixelShaderUid, AbstractShader>::CacheList::iterator;
void NotifyPipelineFinished(PipelineIterator iterator,
std::unique_ptr<AbstractPipeline> pipeline);
void NotifyPipelineFinished(UberPipelineIterator iterator,
std::unique_ptr<AbstractPipeline> pipeline);
std::unique_ptr<AbstractShader>
CompilePixelShader(const PixelShaderUid& uid, const CustomShaderInstance& custom_shaders) const;
void NotifyPixelShaderFinished(PixelShaderIterator iterator,
std::unique_ptr<AbstractShader> shader);
std::unique_ptr<AbstractShader>
CompilePixelShader(const UberShader::PixelShaderUid& uid,
const CustomShaderInstance& custom_shaders) const;
void NotifyPixelShaderFinished(UberPixelShaderIterator iterator,
std::unique_ptr<AbstractShader> shader);
void QueuePixelShaderCompile(const PixelShaderUid& uid,
const CustomShaderInstance& custom_shaders);
void QueuePixelShaderCompile(const UberShader::PixelShaderUid& uid,
const CustomShaderInstance& custom_shaders);
Common::EventHook m_frame_end_handler;
};

View file

@ -3,18 +3,23 @@
#pragma once
#include <array>
#include <optional>
#include <string_view>
#include <vector>
#include "Common/CommonTypes.h"
#include "Common/Matrix.h"
#include "VideoCommon/Assets/TextureAsset.h"
#include "VideoCommon/PixelShaderGen.h"
namespace GraphicsModActionData
{
struct DrawStarted
{
u32 texture_unit;
bool* skip;
std::optional<CustomPixelShader>* custom_pixel_shader;
};
struct EFB

View file

@ -3,6 +3,7 @@
#include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModActionFactory.h"
#include "VideoCommon/GraphicsModSystem/Runtime/Actions/CustomPipelineAction.h"
#include "VideoCommon/GraphicsModSystem/Runtime/Actions/MoveAction.h"
#include "VideoCommon/GraphicsModSystem/Runtime/Actions/PrintAction.h"
#include "VideoCommon/GraphicsModSystem/Runtime/Actions/ScaleAction.h"
@ -11,7 +12,7 @@
namespace GraphicsModActionFactory
{
std::unique_ptr<GraphicsModAction> Create(std::string_view name, const picojson::value& json_data,
std::string_view path)
std::shared_ptr<VideoCommon::CustomAssetLibrary> library)
{
if (name == "print")
{
@ -29,6 +30,10 @@ std::unique_ptr<GraphicsModAction> Create(std::string_view name, const picojson:
{
return ScaleAction::Create(json_data);
}
else if (name == "custom_pipeline")
{
return CustomPipelineAction::Create(json_data, std::move(library));
}
return nullptr;
}

View file

@ -8,10 +8,11 @@
#include <picojson.h>
#include "VideoCommon/Assets/CustomAssetLibrary.h"
#include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModAction.h"
namespace GraphicsModActionFactory
{
std::unique_ptr<GraphicsModAction> Create(std::string_view name, const picojson::value& json_data,
std::string_view path);
std::shared_ptr<VideoCommon::CustomAssetLibrary> library);
}

View file

@ -13,7 +13,9 @@
#include "Core/ConfigManager.h"
#include "VideoCommon/Assets/DirectFilesystemAssetLibrary.h"
#include "VideoCommon/GraphicsModSystem/Config/GraphicsMod.h"
#include "VideoCommon/GraphicsModSystem/Config/GraphicsModAsset.h"
#include "VideoCommon/GraphicsModSystem/Config/GraphicsModGroup.h"
#include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModActionFactory.h"
#include "VideoCommon/TextureInfo.h"
@ -187,6 +189,8 @@ void GraphicsModManager::Load(const GraphicsModGroupConfig& config)
const auto& mods = config.GetMods();
auto filesystem_library = std::make_shared<VideoCommon::DirectFilesystemAssetLibrary>();
std::map<std::string, std::vector<GraphicsTargetConfig>> group_to_targets;
for (const auto& mod : mods)
{
@ -208,6 +212,29 @@ void GraphicsModManager::Load(const GraphicsModGroupConfig& config)
group_to_targets[internal_group].push_back(target);
}
}
std::string base_path;
SplitPath(mod.GetAbsolutePath(), &base_path, nullptr, nullptr);
for (const GraphicsModAssetConfig& asset : mod.m_assets)
{
auto asset_map = asset.m_map;
for (auto& [k, v] : asset_map)
{
if (v.is_absolute())
{
WARN_LOG_FMT(VIDEO,
"Specified graphics mod asset '{}' for mod '{}' has an absolute path, you "
"shouldn't release this to users.",
asset.m_name, mod.m_title);
}
else
{
v = std::filesystem::path{base_path} / v;
}
}
filesystem_library->SetAssetIDMapData(asset.m_name, std::move(asset_map));
}
}
for (const auto& mod : mods)
@ -215,12 +242,11 @@ void GraphicsModManager::Load(const GraphicsModGroupConfig& config)
for (const GraphicsModFeatureConfig& feature : mod.m_features)
{
const auto create_action =
[](const std::string_view& action_name, const picojson::value& json_data,
GraphicsModConfig mod_config) -> std::unique_ptr<GraphicsModAction> {
std::string base_path;
SplitPath(mod_config.GetAbsolutePath(), &base_path, nullptr, nullptr);
auto action = GraphicsModActionFactory::Create(action_name, json_data, base_path);
[filesystem_library](const std::string_view& action_name,
const picojson::value& json_data,
GraphicsModConfig mod_config) -> std::unique_ptr<GraphicsModAction> {
auto action =
GraphicsModActionFactory::Create(action_name, json_data, std::move(filesystem_library));
if (action == nullptr)
{
return nullptr;

View file

@ -175,3 +175,180 @@ void GetLightingShaderUid(LightingUidData& uid_data)
}
}
}
void GenerateCustomLightingHeaderDetails(ShaderCode* out, u32 enablelighting, u32 light_mask)
{
u32 light_count = 0;
for (u32 j = 0; j < NUM_XF_COLOR_CHANNELS; j++)
{
if ((enablelighting & (1 << j)) != 0) // Color lights
{
for (int i = 0; i < 8; ++i)
{
if ((light_mask & (1 << (i + 8 * j))) != 0)
{
light_count++;
}
}
}
if ((enablelighting & (1 << (j + 2))) != 0) // Alpha lights
{
for (int i = 0; i < 8; ++i)
{
if ((light_mask & (1 << (i + 8 * (j + 2)))) != 0)
{
light_count++;
}
}
}
}
if (light_count > 0)
{
out->Write("\tCustomShaderLightData[{}] light;\n", light_count);
}
else
{
// Cheat so shaders compile
out->Write("\tCustomShaderLightData[1] light;\n", light_count);
}
out->Write("\tint light_count;\n");
}
void GenerateCustomLightingImplementation(ShaderCode* out, const LightingUidData& uid_data,
std::string_view in_color_name)
{
auto generate_lighting = [](ShaderCode* out, const LightingUidData& uid_data, int index,
int litchan_index, u32 channel_index, u32 custom_light_index,
bool alpha) {
const auto attnfunc =
static_cast<AttenuationFunc>((uid_data.attnfunc >> (2 * litchan_index)) & 0x3);
const std::string_view light_type = alpha ? "alpha" : "color";
const std::string name = fmt::format("lights_chan{}_{}", channel_index, light_type);
out->Write("\t{{\n");
out->Write("\t\tcustom_data.{}[{}].direction = " LIGHT_DIR ".xyz;\n", name, custom_light_index,
LIGHT_DIR_PARAMS(index));
out->Write("\t\tcustom_data.{}[{}].position = " LIGHT_POS ".xyz;\n", name, custom_light_index,
LIGHT_POS_PARAMS(index));
out->Write("\t\tcustom_data.{}[{}].cosatt = " LIGHT_COSATT ";\n", name, custom_light_index,
LIGHT_COSATT_PARAMS(index));
out->Write("\t\tcustom_data.{}[{}].distatt = " LIGHT_DISTATT ";\n", name, custom_light_index,
LIGHT_DISTATT_PARAMS(index));
out->Write("\t\tcustom_data.{}[{}].attenuation_type = {};\n", name, custom_light_index,
static_cast<u32>(attnfunc));
if (alpha)
{
out->Write("\t\tcustom_data.{}[{}].color = float3(" LIGHT_COL
") / float3(255.0, 255.0, 255.0);\n",
name, custom_light_index, LIGHT_COL_PARAMS(index, alpha ? "a" : "rgb"));
}
else
{
out->Write("\t\tcustom_data.{}[{}].color = " LIGHT_COL " / float3(255.0, 255.0, 255.0);\n",
name, custom_light_index, LIGHT_COL_PARAMS(index, alpha ? "a" : "rgb"));
}
out->Write("\t}}\n");
};
for (u32 i = 0; i < 8; i++)
{
for (u32 channel_index = 0; channel_index < NUM_XF_COLOR_CHANNELS; channel_index++)
{
out->Write("\tcustom_data.lights_chan{}_color[{}].direction = float3(0, 0, 0);\n",
channel_index, i);
out->Write("\tcustom_data.lights_chan{}_color[{}].position = float3(0, 0, 0);\n",
channel_index, i);
out->Write("\tcustom_data.lights_chan{}_color[{}].color = float3(0, 0, 0);\n", channel_index,
i);
out->Write("\tcustom_data.lights_chan{}_color[{}].cosatt = float4(0, 0, 0, 0);\n",
channel_index, i);
out->Write("\tcustom_data.lights_chan{}_color[{}].distatt = float4(0, 0, 0, 0);\n",
channel_index, i);
out->Write("\tcustom_data.lights_chan{}_color[{}].attenuation_type = 0;\n", channel_index, i);
out->Write("\tcustom_data.lights_chan{}_alpha[{}].direction = float3(0, 0, 0);\n",
channel_index, i);
out->Write("\tcustom_data.lights_chan{}_alpha[{}].position = float3(0, 0, 0);\n",
channel_index, i);
out->Write("\tcustom_data.lights_chan{}_alpha[{}].color = float3(0, 0, 0);\n", channel_index,
i);
out->Write("\tcustom_data.lights_chan{}_alpha[{}].cosatt = float4(0, 0, 0, 0);\n",
channel_index, i);
out->Write("\tcustom_data.lights_chan{}_alpha[{}].distatt = float4(0, 0, 0, 0);\n",
channel_index, i);
out->Write("\tcustom_data.lights_chan{}_alpha[{}].attenuation_type = 0;\n", channel_index, i);
}
}
for (u32 j = 0; j < NUM_XF_COLOR_CHANNELS; j++)
{
const bool colormatsource = !!(uid_data.matsource & (1 << j));
if (colormatsource) // from vertex
out->Write("custom_data.base_material[{}] = {}{};\n", j, in_color_name, j);
else // from color
out->Write("custom_data.base_material[{}] = {}[{}] / 255.0;\n", j, I_MATERIALS, j + 2);
if ((uid_data.enablelighting & (1 << j)) != 0)
{
if ((uid_data.ambsource & (1 << j)) != 0) // from vertex
out->Write("custom_data.ambient_lighting[{}] = {}{};\n", j, in_color_name, j);
else // from color
out->Write("custom_data.ambient_lighting[{}] = {}[{}] / 255.0;\n", j, I_MATERIALS, j);
}
else
{
out->Write("custom_data.ambient_lighting[{}] = float4(1, 1, 1, 1);\n", j);
}
// check if alpha is different
const bool alphamatsource = !!(uid_data.matsource & (1 << (j + 2)));
if (alphamatsource != colormatsource)
{
if (alphamatsource) // from vertex
out->Write("custom_data.base_material[{}].w = {}{}.w;\n", j, in_color_name, j);
else // from color
out->Write("custom_data.base_material[{}].w = {}[{}].w / 255.0;\n", j, I_MATERIALS, j + 2);
}
if ((uid_data.enablelighting & (1 << (j + 2))) != 0)
{
if ((uid_data.ambsource & (1 << (j + 2))) != 0) // from vertex
out->Write("custom_data.ambient_lighting[{}].w = {}{}.w;\n", j, in_color_name, j);
else // from color
out->Write("custom_data.ambient_lighting[{}].w = {}[{}].w / 255.0;\n", j, I_MATERIALS, j);
}
else
{
out->Write("custom_data.ambient_lighting[{}].w = 1;\n", j);
}
u32 light_count = 0;
if ((uid_data.enablelighting & (1 << j)) != 0) // Color lights
{
for (int i = 0; i < 8; ++i)
{
if ((uid_data.light_mask & (1 << (i + 8 * j))) != 0)
{
generate_lighting(out, uid_data, i, j, j, light_count, false);
light_count++;
}
}
}
out->Write("\tcustom_data.light_chan{}_color_count = {};\n", j, light_count);
light_count = 0;
if ((uid_data.enablelighting & (1 << (j + 2))) != 0) // Alpha lights
{
for (int i = 0; i < 8; ++i)
{
if ((uid_data.light_mask & (1 << (i + 8 * (j + 2)))) != 0)
{
generate_lighting(out, uid_data, i, j + 2, j, light_count, true);
light_count++;
}
}
}
out->Write("\tcustom_data.light_chan{}_alpha_count = {};\n", j, light_count);
}
}

View file

@ -47,3 +47,7 @@ constexpr char s_lighting_struct[] = "struct Light {\n"
void GenerateLightingShaderCode(ShaderCode& object, const LightingUidData& uid_data,
std::string_view in_color_name, std::string_view dest);
void GetLightingShaderUid(LightingUidData& uid_data);
void GenerateCustomLightingHeaderDetails(ShaderCode* out, u32 enablelighting, u32 light_mask);
void GenerateCustomLightingImplementation(ShaderCode* out, const LightingUidData& uid_data,
std::string_view in_color_name);

View file

@ -3,6 +3,7 @@
#include "VideoCommon/PixelShaderGen.h"
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <fmt/format.h>
@ -130,6 +131,17 @@ constexpr Common::EnumMap<const char*, TevColorArg::Zero> tev_c_input_table{
"int3(0,0,0)", // ZERO
};
constexpr Common::EnumMap<const char*, TevColorArg::Zero> tev_c_input_type{
"CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_PREV", "CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_PREV",
"CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_COLOR", "CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_COLOR",
"CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_COLOR", "CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_COLOR",
"CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_COLOR", "CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_COLOR",
"CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_TEX", "CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_TEX",
"CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_RAS", "CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_RAS",
"CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_NUMERIC", "CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_NUMERIC",
"CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_KONST", "CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_NUMERIC",
};
constexpr Common::EnumMap<const char*, TevAlphaArg::Zero> tev_a_input_table{
"prev.a", // APREV,
"c0.a", // A0,
@ -141,6 +153,13 @@ constexpr Common::EnumMap<const char*, TevAlphaArg::Zero> tev_a_input_table{
"0", // ZERO
};
constexpr Common::EnumMap<const char*, TevAlphaArg::Zero> tev_a_input_type{
"CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_PREV", "CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_COLOR",
"CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_COLOR", "CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_COLOR",
"CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_TEX", "CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_RAS",
"CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_KONST", "CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_NUMERIC",
};
constexpr Common::EnumMap<const char*, RasColorChan::Zero> tev_ras_table{
"iround(col0 * 255.0)",
"iround(col1 * 255.0)",
@ -387,6 +406,7 @@ void WritePixelShaderCommonHeader(ShaderCode& out, APIType api_type,
"\tbool blend_subtract_alpha;\n"
"\tbool logic_op_enable;\n"
"\tuint logic_op_mode;\n"
"\tuint time_ms;\n"
"}};\n\n");
out.Write("#define bpmem_combiners(i) (bpmem_pack1[(i)].xy)\n"
"#define bpmem_tevind(i) (bpmem_pack1[(i)].z)\n"
@ -732,8 +752,131 @@ uint WrapCoord(int coord, uint wrap, int size) {{
}
}
void WriteCustomShaderStructImpl(ShaderCode* out, u32 num_stages, bool per_pixel_lighting,
const pixel_shader_uid_data* uid_data)
{
out->Write("\tCustomShaderData custom_data;\n");
if (per_pixel_lighting)
{
out->Write("\tcustom_data.position = WorldPos;\n");
out->Write("\tcustom_data.normal = Normal;\n");
}
else
{
out->Write("\tcustom_data.position = float3(0, 0, 0);\n");
out->Write("\tcustom_data.normal = float3(0, 0, 0);\n");
}
if (uid_data->genMode_numtexgens == 0) [[unlikely]]
{
out->Write("\tcustom_data.texcoord[0] = float3(0, 0, 0);\n");
}
else
{
for (u32 i = 0; i < uid_data->genMode_numtexgens; ++i)
{
out->Write("\tif (tex{0}.z == 0.0)\n", i);
out->Write("\t{{\n");
out->Write("\t\tcustom_data.texcoord[{0}] = tex{0};\n", i);
out->Write("\t}}\n");
out->Write("\telse {{\n");
out->Write("\t\tcustom_data.texcoord[{0}] = float3(tex{0}.xy / tex{0}.z, 0);\n", i);
out->Write("\t}}\n");
}
}
for (u32 i = 0; i < 8; i++)
{
// Shader compilation complains if every index isn't initialized
out->Write("\tcustom_data.texmap_to_texcoord_index[{0}] = 0;\n", i);
}
for (u32 i = 0; i < uid_data->genMode_numindstages; ++i)
{
if ((uid_data->nIndirectStagesUsed & (1U << i)) != 0)
{
u32 texcoord = uid_data->GetTevindirefCoord(i);
const u32 texmap = uid_data->GetTevindirefMap(i);
// Quirk: when the tex coord is not less than the number of tex gens (i.e. the tex coord does
// not exist), then tex coord 0 is used (though sometimes glitchy effects happen on console).
// This affects the Mario portrait in Luigi's Mansion, where the developers forgot to set
// the number of tex gens to 2 (bug 11462).
if (texcoord >= uid_data->genMode_numtexgens)
texcoord = 0;
out->Write("\tcustom_data.texmap_to_texcoord_index[{}] = {};\n", texmap, texcoord);
}
}
out->Write("\tcustom_data.texcoord_count = {};\n", uid_data->genMode_numtexgens);
// Try and do a best guess on what the texcoord index is
// Note: one issue with this would be textures that are used
// multiple times in the same draw but with different texture coordinates.
// In that scenario, only the last texture coordinate would be defined.
// This issue can be seen in how Rogue Squadron 2 does bump mapping
for (u32 i = 0; i < num_stages; i++)
{
auto& tevstage = uid_data->stagehash[i];
// Quirk: when the tex coord is not less than the number of tex gens (i.e. the tex coord does
// not exist), then tex coord 0 is used (though sometimes glitchy effects happen on console).
u32 texcoord = tevstage.tevorders_texcoord;
const bool has_tex_coord = texcoord < uid_data->genMode_numtexgens;
if (!has_tex_coord)
texcoord = 0;
out->Write("\tcustom_data.texmap_to_texcoord_index[{}] = {};\n", tevstage.tevorders_texmap,
texcoord);
}
GenerateCustomLightingImplementation(out, uid_data->lighting, "colors_");
for (u32 i = 0; i < 16; i++)
{
// Shader compilation complains if every struct isn't initialized
// Color Input
for (u32 j = 0; j < 4; j++)
{
out->Write("\tcustom_data.tev_stages[{}].input_color[{}].input_type = "
"CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_UNUSED;\n",
i, j);
out->Write("\tcustom_data.tev_stages[{}].input_color[{}].value = "
"float3(0, 0, 0);\n",
i, j);
}
// Alpha Input
for (u32 j = 0; j < 4; j++)
{
out->Write("\tcustom_data.tev_stages[{}].input_alpha[{}].input_type = "
"CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_UNUSED;\n",
i, j);
out->Write("\tcustom_data.tev_stages[{}].input_alpha[{}].value = "
"float(0);\n",
i, j);
}
// Texmap
out->Write("\tcustom_data.tev_stages[{}].texmap = 0u;\n", i);
// Output
out->Write("\tcustom_data.tev_stages[{}].output_color = "
"float4(0, 0, 0, 0);\n",
i);
}
// Actual data will be filled out in the tev stage code, just set the
// stage count for now
out->Write("\tcustom_data.tev_stage_count = {};\n", num_stages);
// Time
out->Write("\tcustom_data.time_ms = time_ms;\n");
}
static void WriteStage(ShaderCode& out, const pixel_shader_uid_data* uid_data, int n,
APIType api_type, bool stereo);
APIType api_type, bool stereo, bool has_custom_shaders);
static void WriteTevRegular(ShaderCode& out, std::string_view components, TevBias bias, TevOp op,
bool clamp, TevScale scale);
static void WriteAlphaTest(ShaderCode& out, const pixel_shader_uid_data* uid_data, APIType api_type,
@ -746,7 +889,8 @@ static void WriteColor(ShaderCode& out, APIType api_type, const pixel_shader_uid
static void WriteBlend(ShaderCode& out, const pixel_shader_uid_data* uid_data);
ShaderCode GeneratePixelShaderCode(APIType api_type, const ShaderHostConfig& host_config,
const pixel_shader_uid_data* uid_data)
const pixel_shader_uid_data* uid_data,
const CustomPixelShaderContents& custom_details)
{
ShaderCode out;
@ -762,8 +906,17 @@ ShaderCode GeneratePixelShaderCode(APIType api_type, const ShaderHostConfig& hos
// Stuff that is shared between ubershaders and pixelgen.
WriteBitfieldExtractHeader(out, api_type, host_config);
WritePixelShaderCommonHeader(out, api_type, host_config, uid_data->bounding_box);
// Custom shader details
WriteCustomShaderStructDef(&out, uid_data->genMode_numtexgens);
for (std::size_t i = 0; i < custom_details.shaders.size(); i++)
{
const auto& shader_details = custom_details.shaders[i];
out.Write(fmt::runtime(shader_details.custom_shader), i);
}
out.Write("\n#define sampleTextureWrapper(texmap, uv, layer) "
"sampleTexture(texmap, samp[texmap], uv, layer)\n");
@ -892,6 +1045,14 @@ ShaderCode GeneratePixelShaderCode(APIType api_type, const ShaderHostConfig& hos
out.Write("void main()\n{{\n");
out.Write("\tfloat4 rawpos = gl_FragCoord;\n");
bool has_custom_shaders = false;
if (std::any_of(custom_details.shaders.begin(), custom_details.shaders.end(),
[](const std::optional<CustomPixelShader>& ps) { return ps.has_value(); }))
{
WriteCustomShaderStructImpl(&out, numStages, per_pixel_lighting, uid_data);
has_custom_shaders = true;
}
if (use_framebuffer_fetch)
{
// Store off a copy of the initial framebuffer value.
@ -1013,7 +1174,7 @@ ShaderCode GeneratePixelShaderCode(APIType api_type, const ShaderHostConfig& hos
for (u32 i = 0; i < numStages; i++)
{
// Build the equation for this stage
WriteStage(out, uid_data, i, api_type, stereo);
WriteStage(out, uid_data, i, api_type, stereo, has_custom_shaders);
}
{
@ -1146,7 +1307,21 @@ ShaderCode GeneratePixelShaderCode(APIType api_type, const ShaderHostConfig& hos
// Write the color and alpha values to the framebuffer
// If using shader blend, we still use the separate alpha
WriteColor(out, api_type, uid_data, !uid_data->no_dual_src || uid_data->blend_enable);
const bool use_dual_source = !uid_data->no_dual_src || uid_data->blend_enable;
WriteColor(out, api_type, uid_data, use_dual_source);
for (std::size_t i = 0; i < custom_details.shaders.size(); i++)
{
const auto& shader_details = custom_details.shaders[i];
if (!shader_details.custom_shader.empty())
{
out.Write("\t{{\n");
out.Write("\t\tcustom_data.final_color = ocol0;\n");
out.Write("\t\tocol0.xyz = {}_{}(custom_data).xyz;\n", CUSTOM_PIXELSHADER_COLOR_FUNC, i);
out.Write("\t}}\n\n");
}
}
if (uid_data->blend_enable)
WriteBlend(out, uid_data);
@ -1162,7 +1337,7 @@ ShaderCode GeneratePixelShaderCode(APIType api_type, const ShaderHostConfig& hos
}
static void WriteStage(ShaderCode& out, const pixel_shader_uid_data* uid_data, int n,
APIType api_type, bool stereo)
APIType api_type, bool stereo, bool has_custom_shaders)
{
using Common::EnumMap;
@ -1556,6 +1731,58 @@ static void WriteStage(ShaderCode& out, const pixel_shader_uid_data* uid_data, i
out.Write(", -1024, 1023)");
out.Write(";\n");
if (has_custom_shaders)
{
// Color input
out.Write(
"\tcustom_data.tev_stages[{}].input_color[0].value = {} / float3(255.0, 255.0, 255.0);\n",
n, tev_c_input_table[cc.a]);
out.Write("\tcustom_data.tev_stages[{}].input_color[0].input_type = {};\n", n,
tev_c_input_type[cc.a]);
out.Write(
"\tcustom_data.tev_stages[{}].input_color[1].value = {} / float3(255.0, 255.0, 255.0);\n",
n, tev_c_input_table[cc.b]);
out.Write("\tcustom_data.tev_stages[{}].input_color[1].input_type = {};\n", n,
tev_c_input_type[cc.b]);
out.Write(
"\tcustom_data.tev_stages[{}].input_color[2].value = {} / float3(255.0, 255.0, 255.0);\n",
n, tev_c_input_table[cc.c]);
out.Write("\tcustom_data.tev_stages[{}].input_color[2].input_type = {};\n", n,
tev_c_input_type[cc.c]);
out.Write(
"\tcustom_data.tev_stages[{}].input_color[3].value = {} / float3(255.0, 255.0, 255.0);\n",
n, tev_c_input_table[cc.d]);
out.Write("\tcustom_data.tev_stages[{}].input_color[3].input_type = {};\n", n,
tev_c_input_type[cc.d]);
// Alpha input
out.Write("\tcustom_data.tev_stages[{}].input_alpha[0].value = {} / float(255.0);\n", n,
tev_a_input_table[ac.a]);
out.Write("\tcustom_data.tev_stages[{}].input_alpha[0].input_type = {};\n", n,
tev_a_input_type[ac.a]);
out.Write("\tcustom_data.tev_stages[{}].input_alpha[1].value = {} / float(255.0);\n", n,
tev_a_input_table[ac.b]);
out.Write("\tcustom_data.tev_stages[{}].input_alpha[1].input_type = {};\n", n,
tev_a_input_type[ac.b]);
out.Write("\tcustom_data.tev_stages[{}].input_alpha[2].value = {} / float(255.0);\n", n,
tev_a_input_table[ac.c]);
out.Write("\tcustom_data.tev_stages[{}].input_alpha[2].input_type = {};\n", n,
tev_a_input_type[ac.c]);
out.Write("\tcustom_data.tev_stages[{}].input_alpha[3].value = {} / float(255.0);\n", n,
tev_a_input_table[ac.d]);
out.Write("\tcustom_data.tev_stages[{}].input_alpha[3].input_type = {};\n", n,
tev_a_input_type[ac.d]);
// Texmap
out.Write("\tcustom_data.tev_stages[{}].texmap = {}u;\n", n, stage.tevorders_texmap);
// Output
out.Write("\tcustom_data.tev_stages[{}].output_color.rgb = {} / float3(255.0, 255.0, 255.0);\n",
n, tev_c_output_table[cc.dest]);
out.Write("\tcustom_data.tev_stages[{}].output_color.a = {} / float(255.0);\n", n,
tev_a_output_table[ac.dest]);
}
}
static void WriteTevRegular(ShaderCode& out, std::string_view components, TevBias bias, TevOp op,

View file

@ -158,8 +158,12 @@ struct pixel_shader_uid_data
using PixelShaderUid = ShaderUid<pixel_shader_uid_data>;
void WriteCustomShaderStructImpl(ShaderCode* out, u32 num_stages, bool per_pixel_lighting,
const pixel_shader_uid_data* uid_data);
ShaderCode GeneratePixelShaderCode(APIType api_type, const ShaderHostConfig& host_config,
const pixel_shader_uid_data* uid_data);
const pixel_shader_uid_data* uid_data,
const CustomPixelShaderContents& custom_details);
void WritePixelShaderCommonHeader(ShaderCode& out, APIType api_type,
const ShaderHostConfig& host_config, bool bounding_box);
void ClearUnusedPixelShaderUidBits(APIType api_type, const ShaderHostConfig& host_config,

View file

@ -419,9 +419,9 @@ std::vector<std::string> PostProcessing::GetPassiveShaderList()
bool PostProcessing::Initialize(AbstractTextureFormat format)
{
m_framebuffer_format = format;
// CompilePixelShader must be run first if configuration options are used.
// CompilePixelShader() must be run first if configuration options are used.
// Otherwise the UBO has a different member list between vertex and pixel
// shaders, which is a link error.
// shaders, which is a link error on some backends.
if (!CompilePixelShader() || !CompileVertexShader() || !CompilePipeline())
return false;
@ -486,23 +486,29 @@ void PostProcessing::BlitFromTexture(const MathUtil::Rectangle<int>& dst,
MathUtil::Rectangle<int> src_rect = src;
g_gfx->SetSamplerState(0, RenderState::GetLinearSamplerState());
g_gfx->SetSamplerState(1, RenderState::GetPointSamplerState());
g_gfx->SetTexture(0, src_tex);
g_gfx->SetTexture(1, src_tex);
const bool is_color_correction_active = IsColorCorrectionActive();
const bool needs_color_correction = IsColorCorrectionActive();
// Rely on the default (bi)linear sampler with the default mode
// (it might not be gamma corrected).
const bool needs_resampling =
g_ActiveConfig.output_resampling_mode > OutputResamplingMode::Default;
const bool needs_intermediary_buffer = NeedsIntermediaryBuffer();
const bool needs_default_pipeline = needs_color_correction || needs_resampling;
const AbstractPipeline* final_pipeline = m_pipeline.get();
std::vector<u8>* uniform_staging_buffer = &m_default_uniform_staging_buffer;
bool default_uniform_staging_buffer = true;
const MathUtil::Rectangle<int> present_rect = g_presenter->GetTargetRectangle();
// Intermediary pass.
// We draw to a high quality intermediary texture for two reasons:
// We draw to a high quality intermediary texture for a couple reasons:
// -Consistently do high quality gamma corrected resampling (upscaling/downscaling)
// -Keep quality for gamma and gamut conversions, and HDR output
// (low bit depths lose too much quality with gamma conversions)
// -We make a texture of the exact same res as the source one,
// because all the post process shaders we already had assume that
// the source texture size (EFB) is different from the swap chain
// texture size (which matches the window size).
if (m_default_pipeline && is_color_correction_active && needs_intermediary_buffer)
// -Keep the post process phase in linear space, to better operate with colors
if (m_default_pipeline && needs_default_pipeline && needs_intermediary_buffer)
{
AbstractFramebuffer* const previous_framebuffer = g_gfx->GetCurrentFramebuffer();
@ -512,13 +518,18 @@ void PostProcessing::BlitFromTexture(const MathUtil::Rectangle<int>& dst,
// so it would be a waste to allocate two layers (see "bUsesExplictQuadBuffering").
const u32 target_layers = copy_all_layers ? src_tex->GetLayers() : 1;
const u32 target_width =
needs_resampling ? present_rect.GetWidth() : static_cast<u32>(src_rect.GetWidth());
const u32 target_height =
needs_resampling ? present_rect.GetHeight() : static_cast<u32>(src_rect.GetHeight());
if (!m_intermediary_frame_buffer || !m_intermediary_color_texture ||
m_intermediary_color_texture.get()->GetWidth() != static_cast<u32>(src_rect.GetWidth()) ||
m_intermediary_color_texture.get()->GetHeight() != static_cast<u32>(src_rect.GetHeight()) ||
m_intermediary_color_texture.get()->GetWidth() != target_width ||
m_intermediary_color_texture.get()->GetHeight() != target_height ||
m_intermediary_color_texture.get()->GetLayers() != target_layers)
{
const TextureConfig intermediary_color_texture_config(
src_rect.GetWidth(), src_rect.GetHeight(), 1, target_layers, src_tex->GetSamples(),
target_width, target_height, 1, target_layers, src_tex->GetSamples(),
s_intermediary_buffer_format, AbstractTextureFlag_RenderTarget);
m_intermediary_color_texture = g_gfx->CreateTexture(intermediary_color_texture_config,
"Intermediary post process texture");
@ -530,8 +541,8 @@ void PostProcessing::BlitFromTexture(const MathUtil::Rectangle<int>& dst,
g_gfx->SetFramebuffer(m_intermediary_frame_buffer.get());
FillUniformBuffer(src_rect, src_tex, src_layer, g_gfx->GetCurrentFramebuffer()->GetRect(),
g_presenter->GetTargetRectangle(), uniform_staging_buffer->data(),
!default_uniform_staging_buffer);
present_rect, uniform_staging_buffer->data(), !default_uniform_staging_buffer,
true);
g_vertex_manager->UploadUtilityUniforms(uniform_staging_buffer->data(),
static_cast<u32>(uniform_staging_buffer->size()));
@ -544,6 +555,7 @@ void PostProcessing::BlitFromTexture(const MathUtil::Rectangle<int>& dst,
src_rect = m_intermediary_color_texture->GetRect();
src_tex = m_intermediary_color_texture.get();
g_gfx->SetTexture(0, src_tex);
g_gfx->SetTexture(1, src_tex);
// The "m_intermediary_color_texture" has already copied
// from the specified source layer onto its first one.
// If we query for a layer that the source texture doesn't have,
@ -557,7 +569,7 @@ void PostProcessing::BlitFromTexture(const MathUtil::Rectangle<int>& dst,
// If we have no custom user shader selected, and color correction
// is active, directly run the fixed pipeline shader instead of
// doing two passes, with the second one doing nothing useful.
if (m_default_pipeline && is_color_correction_active)
if (m_default_pipeline && needs_default_pipeline)
{
final_pipeline = m_default_pipeline.get();
}
@ -580,8 +592,8 @@ void PostProcessing::BlitFromTexture(const MathUtil::Rectangle<int>& dst,
if (final_pipeline)
{
FillUniformBuffer(src_rect, src_tex, src_layer, g_gfx->GetCurrentFramebuffer()->GetRect(),
g_presenter->GetTargetRectangle(), uniform_staging_buffer->data(),
!default_uniform_staging_buffer);
present_rect, uniform_staging_buffer->data(), !default_uniform_staging_buffer,
false);
g_vertex_manager->UploadUtilityUniforms(uniform_staging_buffer->data(),
static_cast<u32>(uniform_staging_buffer->size()));
@ -609,7 +621,11 @@ std::string PostProcessing::GetUniformBufferHeader(bool user_post_process) const
// The first (but not necessarily only) source layer we target
ss << " int src_layer;\n";
ss << " uint time;\n";
ss << " int graphics_api;\n";
// If true, it's an intermediary buffer (including the first), if false, it's the final one
ss << " int intermediary_buffer;\n";
ss << " int resampling_method;\n";
ss << " int correct_color_space;\n";
ss << " int game_color_space;\n";
ss << " int correct_gamma;\n";
@ -742,6 +758,7 @@ void SetOutput(float4 color)
#define GetOption(x) (x)
#define OptionEnabled(x) ((x) != 0)
#define OptionDisabled(x) ((x) == 0)
)";
return ss.str();
@ -752,13 +769,9 @@ std::string PostProcessing::GetFooter() const
return {};
}
bool PostProcessing::CompileVertexShader()
std::string GetVertexShaderBody()
{
std::ostringstream ss;
// We never need the user selected post process custom uniforms in the vertex shader
const bool user_post_process = false;
ss << GetUniformBufferHeader(user_post_process);
if (g_ActiveConfig.backend_info.bSupportsGeometryShaders)
{
ss << "VARYING_LOCATION(0) out VertexData {\n";
@ -779,21 +792,34 @@ bool PostProcessing::CompileVertexShader()
// Vulkan Y needs to be inverted on every pass
if (g_ActiveConfig.backend_info.api_type == APIType::Vulkan)
{
ss << " opos.y = -opos.y;\n";
std::string s2 = ss.str();
s2 += "}\n";
m_default_vertex_shader = g_gfx->CreateShaderFromSource(ShaderStage::Vertex, s2,
"Default post-processing vertex shader");
// OpenGL Y needs to be inverted once only (in the last pass)
if (g_ActiveConfig.backend_info.api_type == APIType::OpenGL)
ss << " opos.y = -opos.y;\n";
}
// OpenGL Y needs to be inverted in all passes except the last one
else if (g_ActiveConfig.backend_info.api_type == APIType::OpenGL)
{
ss << " if (intermediary_buffer != 0)\n";
ss << " opos.y = -opos.y;\n";
}
ss << "}\n";
return ss.str();
}
bool PostProcessing::CompileVertexShader()
{
std::ostringstream ss_default;
ss_default << GetUniformBufferHeader(false);
ss_default << GetVertexShaderBody();
m_default_vertex_shader = g_gfx->CreateShaderFromSource(ShaderStage::Vertex, ss_default.str(),
"Default post-processing vertex shader");
std::ostringstream ss;
ss << GetUniformBufferHeader(true);
ss << GetVertexShaderBody();
m_vertex_shader =
g_gfx->CreateShaderFromSource(ShaderStage::Vertex, ss.str(), "Post-processing vertex shader");
if (!m_default_vertex_shader || !m_vertex_shader)
{
PanicAlertFmt("Failed to compile post-processing vertex shader");
@ -816,6 +842,9 @@ struct BuiltinUniforms
std::array<float, 4> src_rect;
s32 src_layer;
u32 time;
s32 graphics_api;
s32 intermediary_buffer;
s32 resampling_method;
s32 correct_color_space;
s32 game_color_space;
s32 correct_gamma;
@ -839,7 +868,7 @@ void PostProcessing::FillUniformBuffer(const MathUtil::Rectangle<int>& src,
const AbstractTexture* src_tex, int src_layer,
const MathUtil::Rectangle<int>& dst,
const MathUtil::Rectangle<int>& wnd, u8* buffer,
bool user_post_process)
bool user_post_process, bool intermediary_buffer)
{
const float rcp_src_width = 1.0f / src_tex->GetWidth();
const float rcp_src_height = 1.0f / src_tex->GetHeight();
@ -860,7 +889,10 @@ void PostProcessing::FillUniformBuffer(const MathUtil::Rectangle<int>& src,
static_cast<float>(src.GetHeight()) * rcp_src_height};
builtin_uniforms.src_layer = static_cast<s32>(src_layer);
builtin_uniforms.time = static_cast<u32>(m_timer.ElapsedMs());
builtin_uniforms.graphics_api = static_cast<s32>(g_ActiveConfig.backend_info.api_type);
builtin_uniforms.intermediary_buffer = static_cast<s32>(intermediary_buffer);
builtin_uniforms.resampling_method = static_cast<s32>(g_ActiveConfig.output_resampling_mode);
// Color correction related uniforms.
// These are mainly used by the "m_default_pixel_shader",
// but should also be accessible to all other shaders.
@ -883,6 +915,8 @@ void PostProcessing::FillUniformBuffer(const MathUtil::Rectangle<int>& src,
std::memcpy(buffer, &builtin_uniforms, sizeof(builtin_uniforms));
buffer += sizeof(builtin_uniforms);
// Don't include the custom pp shader options if they are not necessary,
// having mismatching uniforms between different shaders can cause issues on some backends
if (!user_post_process)
return;
@ -1000,8 +1034,7 @@ bool PostProcessing::CompilePipeline()
const bool needs_intermediary_buffer = NeedsIntermediaryBuffer();
AbstractPipelineConfig config = {};
config.vertex_shader =
needs_intermediary_buffer ? m_vertex_shader.get() : m_default_vertex_shader.get();
config.vertex_shader = m_default_vertex_shader.get();
// This geometry shader will take care of reading both layer 0 and 1 on the source texture,
// and writing to both layer 0 and 1 on the render target.
config.geometry_shader = UseGeometryShaderForPostProcess(needs_intermediary_buffer) ?
@ -1018,7 +1051,7 @@ bool PostProcessing::CompilePipeline()
if (config.pixel_shader)
m_default_pipeline = g_gfx->CreatePipeline(config);
config.vertex_shader = m_default_vertex_shader.get();
config.vertex_shader = m_vertex_shader.get();
config.geometry_shader = UseGeometryShaderForPostProcess(false) ?
g_shader_cache->GetTexcoordGeometryShader() :
nullptr;

View file

@ -124,7 +124,8 @@ protected:
size_t CalculateUniformsSize(bool user_post_process) const;
void FillUniformBuffer(const MathUtil::Rectangle<int>& src, const AbstractTexture* src_tex,
int src_layer, const MathUtil::Rectangle<int>& dst,
const MathUtil::Rectangle<int>& wnd, u8* buffer, bool user_post_process);
const MathUtil::Rectangle<int>& wnd, u8* buffer, bool user_post_process,
bool intermediary_buffer);
// Timer for determining our time value
Common::Timer m_timer;

View file

@ -449,7 +449,7 @@ ShaderCache::CompileVertexUberShader(const UberShader::VertexShaderUid& uid) con
std::unique_ptr<AbstractShader> ShaderCache::CompilePixelShader(const PixelShaderUid& uid) const
{
const ShaderCode source_code =
GeneratePixelShaderCode(m_api_type, m_host_config, uid.GetUidData());
GeneratePixelShaderCode(m_api_type, m_host_config, uid.GetUidData(), {});
return g_gfx->CreateShaderFromSource(ShaderStage::Pixel, source_code.GetBuffer());
}
@ -457,7 +457,7 @@ std::unique_ptr<AbstractShader>
ShaderCache::CompilePixelUberShader(const UberShader::PixelShaderUid& uid) const
{
const ShaderCode source_code =
UberShader::GenPixelShader(m_api_type, m_host_config, uid.GetUidData());
UberShader::GenPixelShader(m_api_type, m_host_config, uid.GetUidData(), {});
return g_gfx->CreateShaderFromSource(ShaderStage::Pixel, source_code.GetBuffer(),
fmt::to_string(*uid.GetUidData()));
}

View file

@ -10,6 +10,7 @@
#include "Core/ConfigManager.h"
#include "VideoCommon/VideoCommon.h"
#include "VideoCommon/VideoConfig.h"
#include "VideoCommon/XFMemory.h"
ShaderHostConfig ShaderHostConfig::GetCurrent()
{
@ -362,3 +363,90 @@ const char* GetInterpolationQualifier(bool msaa, bool ssaa, bool in_glsl_interfa
return "sample";
}
}
void WriteCustomShaderStructDef(ShaderCode* out, u32 numtexgens)
{
// Bump this when there are breaking changes to the API
out->Write("#define CUSTOM_SHADER_API_VERSION 1;\n");
// CUSTOM_SHADER_LIGHTING_ATTENUATION_TYPE "enum" values
out->Write("const uint CUSTOM_SHADER_LIGHTING_ATTENUATION_TYPE_NONE = {}u;\n",
static_cast<u32>(AttenuationFunc::None));
out->Write("const uint CUSTOM_SHADER_LIGHTING_ATTENUATION_TYPE_POINT = {}u;\n",
static_cast<u32>(AttenuationFunc::Spec));
out->Write("const uint CUSTOM_SHADER_LIGHTING_ATTENUATION_TYPE_DIR = {}u;\n",
static_cast<u32>(AttenuationFunc::Dir));
out->Write("const uint CUSTOM_SHADER_LIGHTING_ATTENUATION_TYPE_SPOT = {}u;\n",
static_cast<u32>(AttenuationFunc::Spot));
out->Write("struct CustomShaderLightData\n");
out->Write("{{\n");
out->Write("\tfloat3 position;\n");
out->Write("\tfloat3 direction;\n");
out->Write("\tfloat3 color;\n");
out->Write("\tuint attenuation_type;\n");
out->Write("\tfloat4 cosatt;\n");
out->Write("\tfloat4 distatt;\n");
out->Write("}};\n\n");
// CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE "enum" values
out->Write("const uint CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_PREV = 0u;\n");
out->Write("const uint CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_COLOR = 1u;\n");
out->Write("const uint CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_TEX = 2u;\n");
out->Write("const uint CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_RAS = 3u;\n");
out->Write("const uint CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_KONST = 4u;\n");
out->Write("const uint CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_NUMERIC = 5u;\n");
out->Write("const uint CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_UNUSED = 6u;\n");
out->Write("struct CustomShaderTevStageInputColor\n");
out->Write("{{\n");
out->Write("\tuint input_type;\n");
out->Write("\tfloat3 value;\n");
out->Write("}};\n\n");
out->Write("struct CustomShaderTevStageInputAlpha\n");
out->Write("{{\n");
out->Write("\tuint input_type;\n");
out->Write("\tfloat value;\n");
out->Write("}};\n\n");
out->Write("struct CustomShaderTevStage\n");
out->Write("{{\n");
out->Write("\tCustomShaderTevStageInputColor[4] input_color;\n");
out->Write("\tCustomShaderTevStageInputAlpha[4] input_alpha;\n");
out->Write("\tuint texmap;\n");
out->Write("\tfloat4 output_color;\n");
out->Write("}};\n\n");
// Custom structure for data we pass to custom shader hooks
out->Write("struct CustomShaderData\n");
out->Write("{{\n");
out->Write("\tfloat3 position;\n");
out->Write("\tfloat3 normal;\n");
if (numtexgens == 0)
{
// Cheat so shaders compile
out->Write("\tfloat3[1] texcoord;\n");
}
else
{
out->Write("\tfloat3[{}] texcoord;\n", numtexgens);
}
out->Write("\tuint texcoord_count;\n");
out->Write("\tuint[8] texmap_to_texcoord_index;\n");
out->Write("\tCustomShaderLightData[8] lights_chan0_color;\n");
out->Write("\tCustomShaderLightData[8] lights_chan0_alpha;\n");
out->Write("\tCustomShaderLightData[8] lights_chan1_color;\n");
out->Write("\tCustomShaderLightData[8] lights_chan1_alpha;\n");
out->Write("\tfloat4[2] ambient_lighting;\n");
out->Write("\tfloat4[2] base_material;\n");
out->Write("\tuint light_chan0_color_count;\n");
out->Write("\tuint light_chan0_alpha_count;\n");
out->Write("\tuint light_chan1_color_count;\n");
out->Write("\tuint light_chan1_alpha_count;\n");
out->Write("\tCustomShaderTevStage[16] tev_stages;\n");
out->Write("\tuint tev_stage_count;\n");
out->Write("\tfloat4 final_color;\n");
out->Write("\tuint time_ms;\n");
out->Write("}};\n\n");
}

View file

@ -7,6 +7,7 @@
#include <functional>
#include <iterator>
#include <string>
#include <string_view>
#include <type_traits>
#include <vector>
@ -327,3 +328,21 @@ static const char s_geometry_shader_uniforms[] = "\tfloat4 " I_STEREOPARAMS ";\n
"\tfloat4 " I_LINEPTPARAMS ";\n"
"\tint4 " I_TEXOFFSET ";\n"
"\tuint vs_expand;\n";
constexpr std::string_view CUSTOM_PIXELSHADER_COLOR_FUNC = "customShaderColor";
struct CustomPixelShader
{
std::string custom_shader;
bool operator==(const CustomPixelShader& other) const = default;
};
struct CustomPixelShaderContents
{
std::vector<CustomPixelShader> shaders;
bool operator==(const CustomPixelShaderContents& other) const = default;
};
void WriteCustomShaderStructDef(ShaderCode* out, u32 numtexgens);

View file

@ -17,6 +17,260 @@
namespace UberShader
{
namespace
{
void WriteCustomShaderStructImpl(ShaderCode* out, u32 num_texgen, bool per_pixel_lighting)
{
out->Write("\tCustomShaderData custom_data;\n");
if (per_pixel_lighting)
{
out->Write("\tcustom_data.position = WorldPos;\n");
out->Write("\tcustom_data.normal = Normal;\n");
}
else
{
out->Write("\tcustom_data.position = float3(0, 0, 0);\n");
out->Write("\tcustom_data.normal = float3(0, 0, 0);\n");
}
if (num_texgen == 0) [[unlikely]]
{
out->Write("\tcustom_data.texcoord[0] = float3(0, 0, 0);\n");
}
else
{
for (u32 i = 0; i < num_texgen; ++i)
{
out->Write("\tif (tex{0}.z == 0.0)\n", i);
out->Write("\t{{\n");
out->Write("\t\tcustom_data.texcoord[{0}] = tex{0};\n", i);
out->Write("\t}}\n");
out->Write("\telse {{\n");
out->Write("\t\tcustom_data.texcoord[{0}] = float3(tex{0}.xy / tex{0}.z, 0);\n", i);
out->Write("\t}}\n");
}
}
out->Write("\tcustom_data.texcoord_count = {};\n", num_texgen);
for (u32 i = 0; i < 8; i++)
{
// Shader compilation complains if every index isn't initialized
out->Write("\tcustom_data.texmap_to_texcoord_index[{0}] = {0};\n", i);
}
for (u32 i = 0; i < NUM_XF_COLOR_CHANNELS; i++)
{
out->Write("\tcustom_data.base_material[{}] = vec4(0, 0, 0, 1);\n", i);
out->Write("\tcustom_data.ambient_lighting[{}] = vec4(0, 0, 0, 1);\n", i);
// Shader compilation errors can throw if not everything is initialized
for (u32 light_count_index = 0; light_count_index < 8; light_count_index++)
{
// Color
out->Write("\tcustom_data.lights_chan{}_color[{}].direction = float3(0, 0, 0);\n", i,
light_count_index);
out->Write("\tcustom_data.lights_chan{}_color[{}].position = float3(0, 0, 0);\n", i,
light_count_index);
out->Write("\tcustom_data.lights_chan{}_color[{}].color = float3(0, 0, 0);\n", i,
light_count_index);
out->Write("\tcustom_data.lights_chan{}_color[{}].cosatt = float4(0, 0, 0, 0);\n", i,
light_count_index);
out->Write("\tcustom_data.lights_chan{}_color[{}].distatt = float4(0, 0, 0, 0);\n", i,
light_count_index);
out->Write("\tcustom_data.lights_chan{}_color[{}].attenuation_type = 0;\n", i,
light_count_index);
// Alpha
out->Write("\tcustom_data.lights_chan{}_alpha[{}].direction = float3(0, 0, 0);\n", i,
light_count_index);
out->Write("\tcustom_data.lights_chan{}_alpha[{}].position = float3(0, 0, 0);\n", i,
light_count_index);
out->Write("\tcustom_data.lights_chan{}_alpha[{}].color = float3(0, 0, 0);\n", i,
light_count_index);
out->Write("\tcustom_data.lights_chan{}_alpha[{}].cosatt = float4(0, 0, 0, 0);\n", i,
light_count_index);
out->Write("\tcustom_data.lights_chan{}_alpha[{}].distatt = float4(0, 0, 0, 0);\n", i,
light_count_index);
out->Write("\tcustom_data.lights_chan{}_alpha[{}].attenuation_type = 0;\n", i,
light_count_index);
}
out->Write("\tcustom_data.light_chan{}_color_count = 0;\n", i);
out->Write("\tcustom_data.light_chan{}_alpha_count = 0;\n", i);
}
if (num_texgen > 0) [[likely]]
{
out->Write("\n");
out->Write("\tfor(uint stage = 0u; stage <= num_stages; stage++)\n");
out->Write("\t{{\n");
out->Write("\t\tStageState ss;\n");
out->Write("\t\tss.order = bpmem_tevorder(stage>>1);\n");
out->Write("\t\tif ((stage & 1u) == 1u)\n");
out->Write("\t\t\tss.order = ss.order >> {};\n\n",
int(TwoTevStageOrders().enable_tex_odd.StartBit() -
TwoTevStageOrders().enable_tex_even.StartBit()));
out->Write("\t\tuint texmap = {};\n",
BitfieldExtract<&TwoTevStageOrders::texcoord_even>("ss.order"));
// Shader compilation is weird, shader arrays can't use indexing by variable
// to set values unless the variable is an index in a for loop.
// So instead we have to do this if check nonsense
for (u32 i = 0; i < 8; i++)
{
out->Write("\t\tif (texmap == {})\n", i);
out->Write("\t\t{{\n");
out->Write("\t\t\tcustom_data.texmap_to_texcoord_index[{}] = selectTexCoordIndex(texmap);\n",
i);
out->Write("\t\t}}\n");
}
out->Write("\t}}\n");
}
out->Write("\tuint light_count = 0;\n");
out->Write("\tfor (uint chan = 0u; chan < {}u; chan++)\n", NUM_XF_COLOR_CHANNELS);
out->Write("\t{{\n");
out->Write("\t\tuint colorreg = xfmem_color(chan);\n");
out->Write("\t\tuint alphareg = xfmem_alpha(chan);\n");
for (const auto& color_type : std::array<std::string_view, 2>{"colorreg", "alphareg"})
{
if (color_type == "colorreg")
{
out->Write("\t\tcustom_data.base_material[0] = " I_MATERIALS "[2u] / 255.0; \n");
out->Write("\t\tif ({} != 0u)\n", BitfieldExtract<&LitChannel::enablelighting>(color_type));
out->Write("\t\t\tcustom_data.base_material[0] = colors_0; \n");
}
else
{
out->Write("custom_data.base_material[1].w = " I_MATERIALS "[3u].w / 255.0; \n");
out->Write("\t\tif ({} != 0u)\n", BitfieldExtract<&LitChannel::enablelighting>(color_type));
out->Write("\t\t\tcustom_data.base_material[1].w = colors_1.w; \n");
}
out->Write("\t\tif ({} != 0u)\n", BitfieldExtract<&LitChannel::enablelighting>(color_type));
out->Write("\t\t{{\n");
out->Write("\t\t\tuint light_mask = {} | ({} << 4u);\n",
BitfieldExtract<&LitChannel::lightMask0_3>(color_type),
BitfieldExtract<&LitChannel::lightMask4_7>(color_type));
out->Write("\t\t\tuint attnfunc = {};\n", BitfieldExtract<&LitChannel::attnfunc>(color_type));
out->Write("\t\t\tfor (uint light_index = 0u; light_index < 8u; light_index++)\n");
out->Write("\t\t\t{{\n");
out->Write("\t\t\t\tif ((light_mask & (1u << light_index)) != 0u)\n");
out->Write("\t\t\t\t{{\n");
// Shader compilation is weird, shader arrays can't use indexing by variable
// to set values unless the variable is an index in a for loop.
// So instead we have to do this if check nonsense
for (u32 light_count_index = 0; light_count_index < 8; light_count_index++)
{
out->Write("\t\t\t\t\tif (light_index == {})\n", light_count_index);
out->Write("\t\t\t\t\t{{\n");
if (color_type == "colorreg")
{
for (u32 channel_index = 0; channel_index < NUM_XF_COLOR_CHANNELS; channel_index++)
{
out->Write("\t\t\t\t\t\tif (chan == {})\n", channel_index);
out->Write("\t\t\t\t\t\t{{\n");
out->Write("\t\t\t\t\t\t\tcustom_data.lights_chan{}_color[{}].direction = " I_LIGHTS
"[light_index].dir.xyz;\n",
channel_index, light_count_index);
out->Write("\t\t\t\t\t\t\tcustom_data.lights_chan{}_color[{}].position = " I_LIGHTS
"[light_index].pos.xyz;\n",
channel_index, light_count_index);
out->Write("\t\t\t\t\t\t\tcustom_data.lights_chan{}_color[{}].cosatt = " I_LIGHTS
"[light_index].cosatt;\n",
channel_index, light_count_index);
out->Write("\t\t\t\t\t\t\tcustom_data.lights_chan{}_color[{}].distatt = " I_LIGHTS
"[light_index].distatt;\n",
channel_index, light_count_index);
out->Write(
"\t\t\t\t\t\t\tcustom_data.lights_chan{}_color[{}].attenuation_type = attnfunc;\n",
channel_index, light_count_index);
out->Write("\t\t\t\t\t\t\tcustom_data.lights_chan{}_color[{}].color = " I_LIGHTS
"[light_index].color.rgb / float3(255.0, 255.0, 255.0);\n",
channel_index, light_count_index);
out->Write("\t\t\t\t\t\t\tcustom_data.light_chan{}_color_count += 1;\n", channel_index);
out->Write("\t\t\t\t\t\t}}\n");
}
}
else
{
for (u32 channel_index = 0; channel_index < NUM_XF_COLOR_CHANNELS; channel_index++)
{
out->Write("\t\t\t\t\t\tif (chan == {})\n", channel_index);
out->Write("\t\t\t\t\t\t{{\n");
out->Write("\t\t\t\t\t\t\tcustom_data.lights_chan{}_alpha[{}].direction = " I_LIGHTS
"[light_index].dir.xyz;\n",
channel_index, light_count_index);
out->Write("\t\t\t\t\t\t\tcustom_data.lights_chan{}_alpha[{}].position = " I_LIGHTS
"[light_index].pos.xyz;\n",
channel_index, light_count_index);
out->Write("\t\t\t\t\t\t\tcustom_data.lights_chan{}_alpha[{}].cosatt = " I_LIGHTS
"[light_index].cosatt;\n",
channel_index, light_count_index);
out->Write("\t\t\t\t\t\t\tcustom_data.lights_chan{}_alpha[{}].distatt = " I_LIGHTS
"[light_index].distatt;\n",
channel_index, light_count_index);
out->Write(
"\t\t\t\t\t\t\tcustom_data.lights_chan{}_alpha[{}].attenuation_type = attnfunc;\n",
channel_index, light_count_index);
out->Write("\t\t\t\t\t\t\tcustom_data.lights_chan{}_alpha[{}].color = float3(" I_LIGHTS
"[light_index].color.a) / float3(255.0, 255.0, 255.0);\n",
channel_index, light_count_index);
out->Write("\t\t\t\t\t\t\tcustom_data.light_chan{}_alpha_count += 1;\n", channel_index);
out->Write("\t\t\t\t\t\t}}\n");
}
}
out->Write("\t\t\t\t\t}}\n");
}
out->Write("\t\t\t\t}}\n");
out->Write("\t\t\t}}\n");
out->Write("\t\t}}\n");
}
out->Write("\t}}\n");
for (u32 i = 0; i < 16; i++)
{
// Shader compilation complains if every struct isn't initialized
// Color Input
for (u32 j = 0; j < 4; j++)
{
out->Write("\tcustom_data.tev_stages[{}].input_color[{}].input_type = "
"CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_UNUSED;\n",
i, j);
out->Write("\tcustom_data.tev_stages[{}].input_color[{}].value = "
"float3(0, 0, 0);\n",
i, j);
}
// Alpha Input
for (u32 j = 0; j < 4; j++)
{
out->Write("\tcustom_data.tev_stages[{}].input_alpha[{}].input_type = "
"CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_UNUSED;\n",
i, j);
out->Write("\tcustom_data.tev_stages[{}].input_alpha[{}].value = "
"float(0);\n",
i, j);
}
// Texmap
out->Write("\tcustom_data.tev_stages[{}].texmap = 0u;\n", i);
// Output
out->Write("\tcustom_data.tev_stages[{}].output_color = "
"float4(0, 0, 0, 0);\n",
i);
}
// Actual data will be filled out in the tev stage code, just set the
// stage count for now
out->Write("\tcustom_data.tev_stage_count = num_stages;\n");
// Time
out->Write("\tcustom_data.time_ms = time_ms;\n");
}
} // namespace
PixelShaderUid GetPixelShaderUid()
{
PixelShaderUid out;
@ -56,7 +310,8 @@ void ClearUnusedPixelShaderUidBits(APIType api_type, const ShaderHostConfig& hos
}
ShaderCode GenPixelShader(APIType api_type, const ShaderHostConfig& host_config,
const pixel_ubershader_uid_data* uid_data)
const pixel_ubershader_uid_data* uid_data,
const CustomPixelShaderContents& custom_details)
{
const bool per_pixel_lighting = host_config.per_pixel_lighting;
const bool msaa = host_config.msaa;
@ -76,6 +331,12 @@ ShaderCode GenPixelShader(APIType api_type, const ShaderHostConfig& host_config,
out.Write("// {}\n", *uid_data);
WriteBitfieldExtractHeader(out, api_type, host_config);
WritePixelShaderCommonHeader(out, api_type, host_config, bounding_box);
WriteCustomShaderStructDef(&out, numTexgen);
for (std::size_t i = 0; i < custom_details.shaders.size(); i++)
{
const auto& shader_details = custom_details.shaders[i];
out.Write(fmt::runtime(shader_details.custom_shader), i);
}
if (per_pixel_lighting)
WriteLightingFunction(out);
@ -228,6 +489,68 @@ ShaderCode GenPixelShader(APIType api_type, const ShaderHostConfig& host_config,
}
out.Write("}}\n\n");
out.Write("uint selectTexCoordIndex(uint texmap)");
out.Write("{{\n");
if (api_type == APIType::D3D)
{
out.Write(" switch (texmap) {{\n");
for (u32 i = 0; i < numTexgen; i++)
{
out.Write(" case {}u:\n"
" return {};\n",
i, i);
}
out.Write(" default:\n"
" return 0;\n"
" }}\n");
}
else
{
out.Write(" if (texmap >= {}u) {{\n", numTexgen);
out.Write(" return 0;\n"
" }}\n");
if (numTexgen > 4)
out.Write(" if (texmap < 4u) {{\n");
if (numTexgen > 2)
out.Write(" if (texmap < 2u) {{\n");
if (numTexgen > 1)
out.Write(" return (texmap == 0u) ? 0 : 1;\n");
else
out.Write(" return 0;\n");
if (numTexgen > 2)
{
out.Write(" }} else {{\n"); // >= 2 < min(4, numTexgen)
if (numTexgen > 3)
out.Write(" return (texmap == 2u) ? 2 : 3;\n");
else
out.Write(" return 2;\n");
out.Write(" }}\n");
}
if (numTexgen > 4)
{
out.Write(" }} else {{\n"); // >= 4 < min(8, numTexgen)
if (numTexgen > 6)
out.Write(" if (texmap < 6u) {{\n");
if (numTexgen > 5)
out.Write(" return (texmap == 4u) ? 4 : 5;\n");
else
out.Write(" return 4;\n");
if (numTexgen > 6)
{
out.Write(" }} else {{\n"); // >= 6 < min(8, numTexgen)
if (numTexgen > 7)
out.Write(" return (texmap == 6u) ? 6 : 7;\n");
else
out.Write(" return 6;\n");
out.Write(" }}\n");
}
out.Write(" }}\n");
}
}
out.Write("}}\n\n");
}
// =====================
@ -316,43 +639,43 @@ ShaderCode GenPixelShader(APIType api_type, const ShaderHostConfig& host_config,
// TEV's Special Lerp
// ======================
const auto WriteTevLerp = [&out](std::string_view components) {
out.Write(
"// TEV's Linear Interpolate, plus bias, add/subtract and scale\n"
"int{0} tevLerp{0}(int{0} A, int{0} B, int{0} C, int{0} D, uint bias, bool op, "
"uint scale) {{\n"
" // Scale C from 0..255 to 0..256\n"
" C += C >> 7;\n"
"\n"
" // Add bias to D\n"
" if (bias == 1u) D += 128;\n"
" else if (bias == 2u) D -= 128;\n"
"\n"
" int{0} lerp = (A << 8) + (B - A)*C;\n"
" if (scale != 3u) {{\n"
" lerp = lerp << scale;\n"
" D = D << scale;\n"
" }}\n"
"\n"
" // TODO: Is this rounding bias still added when the scale is divide by 2? Currently we "
"do not apply it.\n"
" if (scale != 3u)\n"
" lerp = lerp + (op ? 127 : 128);\n"
"\n"
" int{0} result = lerp >> 8;\n"
"\n"
" // Add/Subtract D\n"
" if (op) // Subtract\n"
" result = D - result;\n"
" else // Add\n"
" result = D + result;\n"
"\n"
" // Most of the Scale was moved inside the lerp for improved precision\n"
" // But we still do the divide by 2 here\n"
" if (scale == 3u)\n"
" result = result >> 1;\n"
" return result;\n"
"}}\n\n",
components);
out.Write("// TEV's Linear Interpolate, plus bias, add/subtract and scale\n"
"int{0} tevLerp{0}(int{0} A, int{0} B, int{0} C, int{0} D, uint bias, bool op, "
"uint scale) {{\n"
" // Scale C from 0..255 to 0..256\n"
" C += C >> 7;\n"
"\n"
" // Add bias to D\n"
" if (bias == 1u) D += 128;\n"
" else if (bias == 2u) D -= 128;\n"
"\n"
" int{0} lerp = (A << 8) + (B - A)*C;\n"
" if (scale != 3u) {{\n"
" lerp = lerp << scale;\n"
" D = D << scale;\n"
" }}\n"
"\n"
" // TODO: Is this rounding bias still added when the scale is divide by 2? "
"Currently we "
"do not apply it.\n"
" if (scale != 3u)\n"
" lerp = lerp + (op ? 127 : 128);\n"
"\n"
" int{0} result = lerp >> 8;\n"
"\n"
" // Add/Subtract D\n"
" if (op) // Subtract\n"
" result = D - result;\n"
" else // Add\n"
" result = D + result;\n"
"\n"
" // Most of the Scale was moved inside the lerp for improved precision\n"
" // But we still do the divide by 2 here\n"
" if (scale == 3u)\n"
" result = result >> 1;\n"
" return result;\n"
"}}\n\n",
components);
};
WriteTevLerp(""); // int
WriteTevLerp("3"); // int3
@ -437,6 +760,25 @@ ShaderCode GenPixelShader(APIType api_type, const ShaderHostConfig& host_config,
"return int3(0, 0, 0);", // ZERO
};
static constexpr Common::EnumMap<std::string_view, TevColorArg::Zero> tev_c_input_type{
"return CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_PREV;",
"return CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_PREV;",
"return CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_COLOR;",
"return CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_COLOR;",
"return CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_COLOR;",
"return CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_COLOR;",
"return CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_COLOR;",
"return CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_COLOR;",
"return CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_TEX;",
"return CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_TEX;",
"return CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_RAS;",
"return CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_RAS;",
"return CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_NUMERIC;",
"return CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_NUMERIC;",
"return CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_KONST;",
"return CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_NUMERIC;",
};
static constexpr Common::EnumMap<std::string_view, TevAlphaArg::Zero> tev_a_input_table{
"return s.Reg[0].a;", // APREV,
"return s.Reg[1].a;", // A0,
@ -448,6 +790,17 @@ ShaderCode GenPixelShader(APIType api_type, const ShaderHostConfig& host_config,
"return 0;", // ZERO
};
static constexpr Common::EnumMap<std::string_view, TevAlphaArg::Zero> tev_a_input_type{
"return CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_PREV;",
"return CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_COLOR;",
"return CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_COLOR;",
"return CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_COLOR;",
"return CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_TEX;",
"return CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_RAS;",
"return CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_KONST;",
"return CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_NUMERIC;",
};
static constexpr Common::EnumMap<std::string_view, TevOutput::Color2> tev_regs_lookup_table{
"return s.Reg[0];",
"return s.Reg[1];",
@ -489,6 +842,16 @@ ShaderCode GenPixelShader(APIType api_type, const ShaderHostConfig& host_config,
out.Write("}}\n"
"\n");
out.Write("// Helper function for Custom Shader Input Type\n"
"uint getColorInputType(uint index) {{\n");
WriteSwitch(out, api_type, "index", tev_c_input_type, 2, false);
out.Write("}}\n"
"\n"
"uint getAlphaInputType(uint index) {{\n");
WriteSwitch(out, api_type, "index", tev_a_input_type, 2, false);
out.Write("}}\n"
"\n");
// Since the fixed-point texture coodinate variables aren't global, we need to pass
// them to the select function. This applies to all backends.
if (numTexgen > 0)
@ -505,6 +868,17 @@ ShaderCode GenPixelShader(APIType api_type, const ShaderHostConfig& host_config,
out.Write("void main()\n{{\n");
out.Write(" float4 rawpos = gl_FragCoord;\n");
out.Write(" uint num_stages = {};\n\n",
BitfieldExtract<&GenMode::numtevstages>("bpmem_genmode"));
bool has_custom_shader_details = false;
if (std::any_of(custom_details.shaders.begin(), custom_details.shaders.end(),
[](const std::optional<CustomPixelShader>& ps) { return ps.has_value(); }))
{
WriteCustomShaderStructImpl(&out, numTexgen, per_pixel_lighting);
has_custom_shader_details = true;
}
if (use_framebuffer_fetch)
{
// Store off a copy of the initial framebuffer value.
@ -563,9 +937,6 @@ ShaderCode GenPixelShader(APIType api_type, const ShaderHostConfig& host_config,
" // o.colors_1 = float4(0.0, 0.0, 0.0, 0.0);\n");
}
out.Write(" uint num_stages = {};\n\n",
BitfieldExtract<&GenMode::numtevstages>("bpmem_genmode"));
out.Write(" // Main tev loop\n");
out.Write(" for(uint stage = 0u; stage <= num_stages; stage++)\n"
@ -618,9 +989,9 @@ ShaderCode GenPixelShader(APIType api_type, const ShaderHostConfig& host_config,
// indirect texture stage is enabled). If the matrix is off, the result doesn't matter; if the
// indirect texture stage is disabled, the result is undefined (and produces a glitchy pattern
// on hardware, different from this).
// For the undefined case, we just skip applying the indirect operation, which is close enough.
// Viewtiful Joe hits the undefined case (bug 12525).
// Wrapping and add to previous still apply in this case (and when the stage is disabled).
// For the undefined case, we just skip applying the indirect operation, which is close
// enough. Viewtiful Joe hits the undefined case (bug 12525). Wrapping and add to previous
// still apply in this case (and when the stage is disabled).
out.Write(" if (bpmem_iref(bt) != 0u) {{\n");
out.Write(" int3 indcoord;\n");
LookupIndirectTexture("indcoord", "bt");
@ -826,7 +1197,8 @@ ShaderCode GenPixelShader(APIType api_type, const ShaderHostConfig& host_config,
" alpha_B = selectAlphaInput(s, ss, {0}colors_0, {0}colors_1, alpha_b) & 255;\n"
" }};\n"
" int alpha_C = selectAlphaInput(s, ss, {0}colors_0, {0}colors_1, alpha_c) & 255;\n"
" int alpha_D = selectAlphaInput(s, ss, {0}colors_0, {0}colors_1, alpha_d); // 10 bits "
" int alpha_D = selectAlphaInput(s, ss, {0}colors_0, {0}colors_1, alpha_d); // 10 "
"bits "
"+ sign\n"
"\n", // TODO: do we need to sign extend?
color_input_prefix);
@ -857,9 +1229,81 @@ ShaderCode GenPixelShader(APIType api_type, const ShaderHostConfig& host_config,
"\n"
" // Write result to the correct input register of the next stage\n");
WriteSwitch(out, api_type, "alpha_dest", tev_a_set_table, 6, true);
out.Write(" }}\n"
" }} // Main TEV loop\n"
"\n");
if (has_custom_shader_details)
{
for (u32 stage_index = 0; stage_index < 16; stage_index++)
{
out.Write("\tif (stage == {}u) {{\n", stage_index);
// Color input
out.Write("\t\tcustom_data.tev_stages[{}].input_color[0].value = color_A / float3(255.0, "
"255.0, 255.0);\n",
stage_index);
out.Write("\t\tcustom_data.tev_stages[{}].input_color[0].input_type = "
"getColorInputType(color_a);\n",
stage_index);
out.Write("\t\tcustom_data.tev_stages[{}].input_color[1].value = color_B / float3(255.0, "
"255.0, 255.0);\n",
stage_index);
out.Write("\t\tcustom_data.tev_stages[{}].input_color[1].input_type = "
"getColorInputType(color_b);\n",
stage_index);
out.Write("\t\tcustom_data.tev_stages[{}].input_color[2].value = color_C / float3(255.0, "
"255.0, 255.0);\n",
stage_index);
out.Write("\t\tcustom_data.tev_stages[{}].input_color[2].input_type = "
"getColorInputType(color_c);\n",
stage_index);
out.Write("\t\tcustom_data.tev_stages[{}].input_color[3].value = color_D / float3(255.0, "
"255.0, 255.0);\n",
stage_index);
out.Write("\t\tcustom_data.tev_stages[{}].input_color[3].input_type = "
"getColorInputType(color_c);\n",
stage_index);
// Alpha input
out.Write("\t\tcustom_data.tev_stages[{}].input_alpha[0].value = alpha_A / float(255.0);\n",
stage_index);
out.Write("\t\tcustom_data.tev_stages[{}].input_alpha[0].input_type = "
"getAlphaInputType(alpha_a);\n",
stage_index);
out.Write("\t\tcustom_data.tev_stages[{}].input_alpha[1].value = alpha_B / float(255.0);\n",
stage_index);
out.Write("\t\tcustom_data.tev_stages[{}].input_alpha[1].input_type = "
"getAlphaInputType(alpha_b);\n",
stage_index);
out.Write("\t\tcustom_data.tev_stages[{}].input_alpha[2].value = alpha_C / float(255.0);\n",
stage_index);
out.Write("\t\tcustom_data.tev_stages[{}].input_alpha[2].input_type = "
"getAlphaInputType(alpha_c);\n",
stage_index);
out.Write("\t\tcustom_data.tev_stages[{}].input_alpha[3].value = alpha_D / float(255.0);\n",
stage_index);
out.Write("\t\tcustom_data.tev_stages[{}].input_alpha[3].input_type = "
"getAlphaInputType(alpha_d);\n",
stage_index);
if (numTexgen != 0)
{
// Texmap
out.Write("\t\tif (texture_enabled) {{\n");
out.Write("\t\t\tuint sampler_num = {};\n",
BitfieldExtract<&TwoTevStageOrders::texmap_even>("ss.order"));
out.Write("\t\tcustom_data.tev_stages[{}].texmap = sampler_num;\n", stage_index);
out.Write("\t\t}}\n");
}
// Output
out.Write("\t\tcustom_data.tev_stages[{}].output_color.rgb = color / float3(255.0, 255.0, "
"255.0);\n",
stage_index);
out.Write("\t\tcustom_data.tev_stages[{}].output_color.a = alpha / float(255.0);\n",
stage_index);
out.Write("\t}}\n");
}
}
out.Write(" }}\n");
out.Write(" }} // Main TEV loop\n");
out.Write("\n");
// Select the output color and alpha registers from the last stage.
out.Write(" int4 TevResult;\n");
@ -942,8 +1386,8 @@ ShaderCode GenPixelShader(APIType api_type, const ShaderHostConfig& host_config,
{
// Instead of using discard, fetch the framebuffer's color value and use it as the output
// for this fragment.
out.Write(
" #define discard_fragment {{ real_ocol0 = float4(initial_ocol0.xyz, 1.0); return; }}\n");
out.Write(" #define discard_fragment {{ real_ocol0 = float4(initial_ocol0.xyz, 1.0); "
"return; }}\n");
}
else
{
@ -1109,8 +1553,8 @@ ShaderCode GenPixelShader(APIType api_type, const ShaderHostConfig& host_config,
" }}\n");
}
// Some backends require that the shader outputs be uint when writing to a uint render target for
// logic op.
// Some backends require that the shader outputs be uint when writing to a uint render target
// for logic op.
if (uid_data->uint_output)
{
out.Write(" if (bpmem_rgba6_format)\n"
@ -1142,6 +1586,19 @@ ShaderCode GenPixelShader(APIType api_type, const ShaderHostConfig& host_config,
}
}
for (std::size_t i = 0; i < custom_details.shaders.size(); i++)
{
const auto& shader_details = custom_details.shaders[i];
if (!shader_details.custom_shader.empty())
{
out.Write("\t{{\n");
out.Write("\t\tcustom_data.final_color = ocol0;\n");
out.Write("\t\tocol0.xyz = {}_{}(custom_data).xyz;\n", CUSTOM_PIXELSHADER_COLOR_FUNC, i);
out.Write("\t}}\n\n");
}
}
if (bounding_box)
{
out.Write(" if (bpmem_bounding_box) {{\n"
@ -1209,13 +1666,13 @@ ShaderCode GenPixelShader(APIType api_type, const ShaderHostConfig& host_config,
WriteSwitch(out, api_type, "blend_dst_factor", blendDstFactor, 4, true);
WriteSwitch(out, api_type, "blend_dst_factor_alpha", blendDstFactorAlpha, 4, true);
out.Write(
" float4 blend_result;\n"
" if (blend_subtract)\n"
" blend_result.rgb = initial_ocol0.rgb * blend_dst.rgb - ocol0.rgb * blend_src.rgb;\n"
" else\n"
" blend_result.rgb = initial_ocol0.rgb * blend_dst.rgb + ocol0.rgb * "
"blend_src.rgb;\n");
out.Write(" float4 blend_result;\n"
" if (blend_subtract)\n"
" blend_result.rgb = initial_ocol0.rgb * blend_dst.rgb - ocol0.rgb * "
"blend_src.rgb;\n"
" else\n"
" blend_result.rgb = initial_ocol0.rgb * blend_dst.rgb + ocol0.rgb * "
"blend_src.rgb;\n");
out.Write(" if (blend_subtract_alpha)\n"
" blend_result.a = initial_ocol0.a * blend_dst.a - ocol0.a * blend_src.a;\n"

View file

@ -29,7 +29,8 @@ using PixelShaderUid = ShaderUid<pixel_ubershader_uid_data>;
PixelShaderUid GetPixelShaderUid();
ShaderCode GenPixelShader(APIType api_type, const ShaderHostConfig& host_config,
const pixel_ubershader_uid_data* uid_data);
const pixel_ubershader_uid_data* uid_data,
const CustomPixelShaderContents& custom_details);
void EnumeratePixelShaderUids(const std::function<void(const PixelShaderUid&)>& callback);
void ClearUnusedPixelShaderUidBits(APIType api_type, const ShaderHostConfig& host_config,

View file

@ -15,6 +15,7 @@
#include "Core/ConfigManager.h"
#include "Core/DolphinAnalytics.h"
#include "Core/HW/SystemTimers.h"
#include "Core/System.h"
#include "VideoCommon/AbstractGfx.h"
@ -23,12 +24,14 @@
#include "VideoCommon/DataReader.h"
#include "VideoCommon/FramebufferManager.h"
#include "VideoCommon/GeometryShaderManager.h"
#include "VideoCommon/GraphicsModSystem/Runtime/CustomShaderCache.h"
#include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModActionData.h"
#include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.h"
#include "VideoCommon/IndexGenerator.h"
#include "VideoCommon/NativeVertexFormat.h"
#include "VideoCommon/OpcodeDecoding.h"
#include "VideoCommon/PerfQueryBase.h"
#include "VideoCommon/PixelShaderGen.h"
#include "VideoCommon/PixelShaderManager.h"
#include "VideoCommon/Statistics.h"
#include "VideoCommon/TextureCacheBase.h"
@ -105,7 +108,10 @@ VertexManagerBase::~VertexManagerBase() = default;
bool VertexManagerBase::Initialize()
{
m_frame_end_event = AfterFrameEvent::Register([this] { OnEndFrame(); }, "VertexManagerBase");
m_after_present_event = AfterPresentEvent::Register(
[this](PresentInfo& pi) { m_ticks_elapsed = pi.emulated_timestamp; }, "VertexManagerBase");
m_index_generator.Init();
m_custom_shader_cache = std::make_unique<CustomShaderCache>();
m_cpu_cull.Init();
return true;
}
@ -523,10 +529,18 @@ void VertexManagerBase::Flush()
auto& geometry_shader_manager = system.GetGeometryShaderManager();
auto& vertex_shader_manager = system.GetVertexShaderManager();
if (g_ActiveConfig.bGraphicMods)
{
const double seconds_elapsed =
static_cast<double>(m_ticks_elapsed) / SystemTimers::GetTicksPerSecond();
pixel_shader_manager.constants.time_ms = seconds_elapsed * 1000;
}
CalculateBinormals(VertexLoaderManager::GetCurrentVertexFormat());
// Calculate ZSlope for zfreeze
const auto used_textures = UsedTextures();
std::vector<std::string> texture_names;
std::vector<u32> texture_units;
if (!m_cull_all)
{
if (!g_ActiveConfig.bGraphicMods)
@ -543,7 +557,12 @@ void VertexManagerBase::Flush()
const auto cache_entry = g_texture_cache->Load(TextureInfo::FromStage(i));
if (cache_entry)
{
texture_names.push_back(cache_entry->texture_info_name);
if (std::find(texture_names.begin(), texture_names.end(),
cache_entry->texture_info_name) == texture_names.end())
{
texture_names.push_back(cache_entry->texture_info_name);
texture_units.push_back(i);
}
}
}
}
@ -562,13 +581,24 @@ void VertexManagerBase::Flush()
if (!m_cull_all)
{
for (const auto& texture_name : texture_names)
CustomPixelShaderContents custom_pixel_shader_contents;
std::optional<CustomPixelShader> custom_pixel_shader;
std::vector<std::string> custom_pixel_texture_names;
for (int i = 0; i < texture_names.size(); i++)
{
const std::string& texture_name = texture_names[i];
const u32 texture_unit = texture_units[i];
bool skip = false;
GraphicsModActionData::DrawStarted draw_started{&skip};
GraphicsModActionData::DrawStarted draw_started{texture_unit, &skip, &custom_pixel_shader};
for (const auto& action : g_graphics_mod_manager->GetDrawStartedActions(texture_name))
{
action->OnDrawStarted(&draw_started);
if (custom_pixel_shader)
{
custom_pixel_shader_contents.shaders.push_back(*custom_pixel_shader);
custom_pixel_texture_names.push_back(texture_name);
}
custom_pixel_shader = std::nullopt;
}
if (skip == true)
return;
@ -610,7 +640,65 @@ void VertexManagerBase::Flush()
UpdatePipelineObject();
if (m_current_pipeline_object)
{
g_gfx->SetPipeline(m_current_pipeline_object);
const AbstractPipeline* current_pipeline = m_current_pipeline_object;
if (!custom_pixel_shader_contents.shaders.empty())
{
CustomShaderInstance custom_shaders;
custom_shaders.pixel_contents = std::move(custom_pixel_shader_contents);
switch (g_ActiveConfig.iShaderCompilationMode)
{
case ShaderCompilationMode::Synchronous:
case ShaderCompilationMode::AsynchronousSkipRendering:
{
if (auto pipeline = m_custom_shader_cache->GetPipelineAsync(
m_current_pipeline_config, custom_shaders, m_current_pipeline_object->m_config))
{
current_pipeline = *pipeline;
}
}
break;
case ShaderCompilationMode::SynchronousUberShaders:
{
// D3D has issues compiling large custom ubershaders
// use specialized shaders instead
if (g_ActiveConfig.backend_info.api_type == APIType::D3D)
{
if (auto pipeline = m_custom_shader_cache->GetPipelineAsync(
m_current_pipeline_config, custom_shaders, m_current_pipeline_object->m_config))
{
current_pipeline = *pipeline;
}
}
else
{
if (auto pipeline = m_custom_shader_cache->GetPipelineAsync(
m_current_uber_pipeline_config, custom_shaders,
m_current_pipeline_object->m_config))
{
current_pipeline = *pipeline;
}
}
}
break;
case ShaderCompilationMode::AsynchronousUberShaders:
{
if (auto pipeline = m_custom_shader_cache->GetPipelineAsync(
m_current_pipeline_config, custom_shaders, m_current_pipeline_object->m_config))
{
current_pipeline = *pipeline;
}
else if (auto uber_pipeline = m_custom_shader_cache->GetPipelineAsync(
m_current_uber_pipeline_config, custom_shaders,
m_current_pipeline_object->m_config))
{
current_pipeline = *uber_pipeline;
}
}
break;
};
}
g_gfx->SetPipeline(current_pipeline);
if (PerfQueryBase::ShouldEmulate())
g_perf_query->EnableQuery(bpmem.zcontrol.early_ztest ? PQG_ZCOMP_ZCOMPLOC : PQG_ZCOMP);
@ -1006,3 +1094,9 @@ void VertexManagerBase::OnEndFrame()
// state changes the specialized shader will not take over.
InvalidatePipelineObject();
}
void VertexManagerBase::NotifyCustomShaderCacheOfHostChange(const ShaderHostConfig& host_config)
{
m_custom_shader_cache->SetHostConfig(host_config);
m_custom_shader_cache->Reload();
}

View file

@ -15,6 +15,7 @@
#include "VideoCommon/ShaderCache.h"
#include "VideoCommon/VideoEvents.h"
class CustomShaderCache;
class DataReader;
class NativeVertexFormat;
class PointerWrap;
@ -128,6 +129,7 @@ public:
m_current_pipeline_object = nullptr;
m_pipeline_config_changed = true;
}
void NotifyCustomShaderCacheOfHostChange(const ShaderHostConfig& host_config);
// Utility pipeline drawing (e.g. EFB copies, post-processing, UI).
virtual void UploadUtilityUniforms(const void* uniforms, u32 uniforms_size);
@ -230,7 +232,11 @@ private:
std::vector<u32> m_scheduled_command_buffer_kicks;
bool m_allow_background_execution = true;
std::unique_ptr<CustomShaderCache> m_custom_shader_cache;
u64 m_ticks_elapsed;
Common::EventHook m_frame_end_event;
Common::EventHook m_after_present_event;
};
extern std::unique_ptr<VertexManagerBase> g_vertex_manager;

View file

@ -134,6 +134,7 @@ void VideoConfig::Refresh()
texture_filtering_mode = Config::Get(Config::GFX_ENHANCE_FORCE_TEXTURE_FILTERING);
iMaxAnisotropy = Config::Get(Config::GFX_ENHANCE_MAX_ANISOTROPY);
output_resampling_mode = Config::Get(Config::GFX_ENHANCE_OUTPUT_RESAMPLING);
sPostProcessingShader = Config::Get(Config::GFX_ENHANCE_POST_SHADER);
bForceTrueColor = Config::Get(Config::GFX_ENHANCE_FORCE_TRUE_COLOR);
bDisableCopyFilter = Config::Get(Config::GFX_ENHANCE_DISABLE_COPY_FILTER);
@ -353,6 +354,7 @@ void CheckForConfigChanges()
{
OSD::AddMessage("Video config changed, reloading shaders.", OSD::Duration::NORMAL);
g_vertex_manager->InvalidatePipelineObject();
g_vertex_manager->NotifyCustomShaderCacheOfHostChange(new_host_config);
g_shader_cache->SetHostConfig(new_host_config);
g_shader_cache->Reload();
g_framebuffer_manager->RecompileShaders();

View file

@ -52,6 +52,17 @@ enum class TextureFilteringMode : int
Linear,
};
enum class OutputResamplingMode : int
{
Default,
Bilinear,
BSpline,
MitchellNetravali,
CatmullRom,
SharpBilinear,
AreaSampling,
};
enum class ColorCorrectionRegion : int
{
SMPTE_NTSCM,
@ -103,6 +114,7 @@ struct VideoConfig final
bool bSSAA = false;
int iEFBScale = 0;
TextureFilteringMode texture_filtering_mode = TextureFilteringMode::Default;
OutputResamplingMode output_resampling_mode = OutputResamplingMode::Default;
int iMaxAnisotropy = 0;
std::string sPostProcessingShader;
bool bForceTrueColor = false;

View file

@ -128,6 +128,9 @@ protected:
}
void TearDown() override { cpu_info = CPUInfo(); }
void ResetCodeBuffer() { emitter->SetCodePtr(code_buffer, code_buffer_end); }
void ExpectDisassembly(const std::string& expected)
{
std::string disasmed;
@ -186,8 +189,16 @@ protected:
EXPECT_EQ(expected_norm, disasmed_norm);
// Reset code buffer afterwards.
emitter->SetCodePtr(code_buffer, code_buffer_end);
ResetCodeBuffer();
}
void ExpectBytes(const std::vector<u8> expected_bytes)
{
const std::vector<u8> code_bytes(code_buffer, emitter->GetWritableCodePtr());
EXPECT_EQ(expected_bytes, code_bytes);
ResetCodeBuffer();
}
std::unique_ptr<X64CodeBlock> emitter;
@ -286,15 +297,13 @@ TEST_F(x64EmitterTest, POP_Register)
TEST_F(x64EmitterTest, JMP)
{
emitter->NOP(6);
emitter->JMP(code_buffer);
ExpectDisassembly("multibyte nop "
"jmp .-8");
emitter->NOP(1);
emitter->JMP(code_buffer, XEmitter::Jump::Short);
ExpectBytes({/* nop */ 0x90, /* short jmp */ 0xeb, /* offset -3 */ 0xfd});
emitter->NOP(6);
emitter->NOP(1);
emitter->JMP(code_buffer, XEmitter::Jump::Near);
ExpectDisassembly("multibyte nop "
"jmp .-11");
ExpectBytes({/* nop */ 0x90, /* near jmp */ 0xe9, /* offset -6 */ 0xfa, 0xff, 0xff, 0xff});
}
TEST_F(x64EmitterTest, JMPptr_Register)
@ -306,11 +315,93 @@ TEST_F(x64EmitterTest, JMPptr_Register)
}
}
// TODO: J/SetJumpTarget
TEST_F(x64EmitterTest, J)
{
FixupBranch jump = emitter->J(XEmitter::Jump::Short);
emitter->NOP(1);
emitter->SetJumpTarget(jump);
ExpectBytes({/* short jmp */ 0xeb, /* offset 1 */ 0x1, /* nop */ 0x90});
// TODO: CALL
jump = emitter->J(XEmitter::Jump::Near);
emitter->NOP(1);
emitter->SetJumpTarget(jump);
ExpectBytes({/* near jmp */ 0xe9, /* offset 1 */ 0x1, 0x0, 0x0, 0x0, /* nop */ 0x90});
}
// TODO: J_CC
TEST_F(x64EmitterTest, CALL)
{
FixupBranch call = emitter->CALL();
emitter->NOP(6);
emitter->SetJumpTarget(call);
ExpectDisassembly("call .+6 "
"multibyte nop");
const u8* const code_start = emitter->GetCodePtr();
emitter->CALL(code_start + 5);
ExpectDisassembly("call .+0");
emitter->NOP(6);
emitter->CALL(code_start);
ExpectDisassembly("multibyte nop "
"call .-11");
}
TEST_F(x64EmitterTest, J_CC)
{
for (const auto& [condition_code, condition_name] : ccnames)
{
FixupBranch fixup = emitter->J_CC(condition_code, XEmitter::Jump::Short);
emitter->NOP(1);
emitter->SetJumpTarget(fixup);
const u8 short_jump_condition_opcode = 0x70 + condition_code;
ExpectBytes({short_jump_condition_opcode, /* offset 1 */ 0x1, /* nop */ 0x90});
fixup = emitter->J_CC(condition_code, XEmitter::Jump::Near);
emitter->NOP(1);
emitter->SetJumpTarget(fixup);
const u8 near_jump_condition_opcode = 0x80 + condition_code;
ExpectBytes({/* two byte opcode */ 0x0f, near_jump_condition_opcode, /* offset 1 */ 0x1, 0x0,
0x0, 0x0, /* nop */ 0x90});
}
// Verify a short jump is used when possible and a near jump when needed.
//
// A short jump to a particular address and a near jump to that same address will have different
// offsets. This is because short jumps are 2 bytes and near jumps are 6 bytes, and the offset to
// the target is calculated from the address of the next instruction.
const u8* const code_start = emitter->GetCodePtr();
constexpr int short_jump_bytes = 2;
const u8* const next_byte_after_short_jump_instruction = code_start + short_jump_bytes;
constexpr int longest_backward_short_jump = 0x80;
const u8* const furthest_byte_reachable_with_backward_short_jump =
next_byte_after_short_jump_instruction - longest_backward_short_jump;
emitter->J_CC(CC_O, furthest_byte_reachable_with_backward_short_jump);
ExpectBytes({/* JO opcode */ 0x70, /* offset -128 */ 0x80});
const u8* const closest_byte_requiring_backward_near_jump =
furthest_byte_reachable_with_backward_short_jump - 1;
emitter->J_CC(CC_O, closest_byte_requiring_backward_near_jump);
// This offset is 5 less than the offset for the furthest backward short jump. -1 because this
// target is 1 byte before the short target, and -4 because the address of the next instruction is
// 4 bytes further away from the jump target than it would be with a short jump.
ExpectBytes({/* two byte JO opcode */ 0x0f, 0x80, /* offset -133 */ 0x7b, 0xff, 0xff, 0xff});
constexpr int longest_forward_short_jump = 0x7f;
const u8* const furthest_byte_reachable_with_forward_short_jump =
next_byte_after_short_jump_instruction + longest_forward_short_jump;
emitter->J_CC(CC_O, furthest_byte_reachable_with_forward_short_jump);
ExpectBytes({/* JO opcode */ 0x70, /* offset 127 */ 0x7f});
const u8* const closest_byte_requiring_forward_near_jump =
furthest_byte_reachable_with_forward_short_jump + 1;
emitter->J_CC(CC_O, closest_byte_requiring_forward_near_jump);
// This offset is 3 less than the offset for the furthest forward short jump. +1 because this
// target is 1 byte after the short target, and -4 because the address of the next instruction is
// 4 bytes closer to the jump target than it would be with a short jump.
ExpectBytes({/* two byte JO opcode */ 0x0f, 0x80, /* offset 124 */ 0x7c, 0x0, 0x0, 0x0});
}
TEST_F(x64EmitterTest, SETcc)
{

View file

@ -0,0 +1,251 @@
# Dolphin Custom Pipeline Specification
Dolphin provides content creators a way to overwrite its internal graphics pipeline data using graphics mods. At the moment, this supports modifying only the pixel shader. This document will describe the specification and give some examples.
## Graphics mod metadata format
This feature is powered by graphics mods. This document assumes the user is familiar with them and will only detail the action specific data needed to trigger this capability.
The action type for this feature is `custom_pipeline`. This action has the following data:
|Identifier |Required | Since |
|-------------------------|---------|-------|
|``passes`` | **Yes** | v1 |
`passes` is an array of pass blobs. Note that at the moment, Dolphin only supports a single pass. Each pass can have the following data:
|Identifier |Required | Since |
|-------------------------|---------|-------|
|``pixel_material_asset`` | **Yes** | v1 |
Here `pixel_material_asset` is the name of a material asset.
A full example is given below:
```json
{
"assets": [
{
"name": "material_replace_normal",
"data":
{
"": "normal.material.json"
}
},
{
"name": "shader_replace_normal",
"data":
{
"metadata": "replace_normal.shader.json",
"shader": "replace_normal.glsl"
}
},
{
"name": "normal_texture",
"data":
{
"": "normal_texture.png"
}
}
],
"features": [
{
"action": "custom_pipeline",
"action_data": {
"passes": [
{
"pixel_material_asset": "material_replace_normal"
}
]
},
"group": "PipelineTarget"
}
],
"groups": [
{
"name": "PipelineTarget",
"targets": [
{
"texture_filename": "tex1_512x512_m_afdbe7efg332229e_14",
"type": "draw_started"
},
{
"texture_filename": "tex1_512x512_m_afdbe7efg332229e_14",
"type": "create_texture"
}
]
}
]
}
```
## The shader format
The shaders are written in GLSL and converted to the target shader that the backend uses internally. The user is expected to provide an entrypoint with the following signature:
```
vec4 custom_main( in CustomShaderData data )
```
`CustomShaderData` encompasses all the data that Dolphin will pass to the user (in addition to the `samp` variable outlined above which is how textures are accessed). It has the following structure:
|Name | Type | Since | Description |
|-----------------------------|-------------------------|-------|-----------------------------------------------------------------------------------------------|
|``position`` | vec3 | v1 | The position of this pixel in _view space_ |
|``normal`` | vec3 | v1 | The normal of this pixel in _view space_ |
|``texcoord`` | vec3[] | v1 | An array of texture coordinates, the amount available is specified by ``texcoord_count`` |
|``texcoord_count`` | uint | v1 | The count of texture coordinates |
|``texmap_to_texcoord_index`` | uint[] | v1 | An array of texture units to texture coordinate values |
|``lights_chan0_color`` | CustomShaderLightData[] | v1 | An array of color lights for channel 0, the amount is specified by ``light_chan0_color_count``|
|``lights_chan0_alpha`` | CustomShaderLightData[] | v1 | An array of alpha lights for channel 0, the amount is specified by ``light_chan0_alpha_count``|
|``lights_chan1_color`` | CustomShaderLightData[] | v1 | An array of color lights for channel 1, the amount is specified by ``light_chan1_color_count``|
|``lights_chan1_alpha`` | CustomShaderLightData[] | v1 | An array of alpha lights for channel 1, the amount is specified by ``light_chan1_alpha_count``|
|``ambient_lighting`` | vec4[] | v1 | An array of ambient lighting values. Count is two, one for each color channel |
|``base_material`` | vec4[] | v1 | An array of the base material values. Count is two, one for each color channel |
|``tev_stages`` | CustomShaderTevStage[] | v1 | An array of TEV stages, the amount is specified by ``tev_stage_count`` |
|``tev_stage_count`` | uint | v1 | The count of TEV stages |
|``final_color`` | vec4 | v1 | The final color generated by Dolphin after all TEV stages are executed |
|``time_ms`` | uint | v1 | The time that has passed in milliseconds, since the game was started. Useful for animating |
`CustomShaderLightData` is used to denote lighting data the game is applying when rendering the specific draw call. It has the following structure:
|Name | Type | Since | Description |
|-------------------------|-------------------------|-------|-------------------------------------------------------------------------------------------------|
|``position`` | vec3 | v1 | The position of the light in _view space_ |
|``direction`` | vec3 | v1 | The direction in _view space_ the light is pointing (only applicable for point and spot lights) |
|``color`` | vec3 | v1 | The color of the light |
|``attenuation_type`` | uint | v1 | The attentuation type of the light. See details below |
|``cosatt`` | vec4 | v1 | The cos attenuation values used |
|``distatt`` | vec4 | v1 | The distance attenuation values used |
The `attenuation_type` is defined as a `uint` but is effecitvely an enumeration. It has the following values:
|Name | Since | Description |
|--------------------------------------------------|-------|-------------------------------------------------------------------------|
|``CUSTOM_SHADER_LIGHTING_ATTENUATION_TYPE_POINT`` | v1 | This value denotes the lighting attentuation is for a point light |
|``CUSTOM_SHADER_LIGHTING_ATTENUATION_TYPE_DIR`` | v1 | This value denotes the lighting attentuation is for a directional light |
|``CUSTOM_SHADER_LIGHTING_ATTENUATION_TYPE_SPOT`` | v1 | This value denotes the lighting attentuation is for a directional light |
`CustomShaderTevStage` is used to denote the various TEV operations. Each operation describes a graphical operation that the game is applying when rendering the specific draw call. It has the following structure:
|Name | Type | Since | Description |
|-------------------------|----------------------------------|-------|-------------------------------------------------------------------------------|
|``input_color`` | CustomShaderTevStageInputColor[] | v1 | The four color inputs that are used to produce the final output of this stage |
|``input_alpha`` | CustomShaderTevStageInputAlpha[] | v1 | The four alpha inputs that are used to produce the final output of this stage |
|``texmap`` | uint | v1 | The texture unit for this stage |
|``output_color`` | vec4 | v1 | The final output color this stage produces |
`CustomShaderTevStageInputColor` is a single input TEV operation for a color value. It has the following structure:
|Name | Type | Since | Description |
|-------------------------|------|-------|-------------------------------------------------|
|``input_type`` | uint | v1 | The input type of the input. See details below |
|``value`` | vec3 | v1 | The value of input |
The `input_type` is defined as a `uint` but is effectively an enumeration. it has the following values:
|Name | Since | Description |
|--------------------------------------------------|-------|---------------------------------------------------------------------------|
|``CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_PREV`` | v1 | The value is provided by the last stage |
|``CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_COLOR`` | v1 | The value is provided by the color data |
|``CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_TEX`` | v1 | The value is provided by a texture |
|``CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_RAS`` | v1 | |
|``CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_KONST`` | v1 | The value is a constant value defined by the software |
|``CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_NUMERIC`` | v1 | The value is a constant numeric value like vec3(0, 0, 0) or vec3(1, 1, 1) |
`CustomShaderTevStageInputAlpha` is a single input TEV operation for an alpha value. It has the following structure:
|Name | Type | Since | Description |
|-------------------------|------|-------|-------------------------------------------------------------------------------|
|``input_type`` | uint | v1 | The input type of the input. See `input_type` for color input stages |
|``value`` | uint | v1 | The value of input |
## Examples
Below are a handful of examples.
### Single color
The following shader displays the color red on the screen:
```glsl
vec4 custom_main( in CustomShaderData data )
{
return vec4(1.0, 0.0, 0.0, 1.0);
}
```
### Normal
The following shader displays the normal on the screen:
```glsl
vec4 custom_main( in CustomShaderData data )
{
return vec4(data.normal * 0.5 + 0.5, 1);
}
```
### Reading a texture
The following shader displays the contents of the texture denoted in the shader asset as `MY_TEX`:
```glsl
vec4 custom_main( in CustomShaderData data )
{
return texture(samp[MY_TEX_UNIT], MY_TEX_COORD);
}
```
### Capturing the first texture the game renders with
The following shader would display the contents of the first texture the game uses, ignoring any other operations. If no stages are available or none exist with a texture it would use the final color of all the staging operations:
```glsl
vec4 custom_main( in CustomShaderData data )
{
vec4 final_color = data.final_color;
uint texture_set = 0;
for (uint i = 0; i < data.tev_stage_count; i++)
{
// There are 4 color inputs
for (uint j = 0; j < 4; j++)
{
if (data.tev_stages[i].input_color[j].input_type == CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_TEX && texture_set == 0)
{
final_color = vec4(data.tev_stages[i].input_color[j].value, 1.0);
texture_set = 1;
}
}
}
return final_color;
}
```
### Applying lighting with a point type attenuation
The following shader would apply the lighting for any point lights used during the draw for channel 0's color lights, using blue as a base color:
```glsl
vec4 custom_main( in CustomShaderData data )
{
float total_diffuse = 0;
for (int i = 0; i < data.light_chan0_color_count; i++)
{
if (data.lights_chan0_color[i].attenuation_type == CUSTOM_SHADER_LIGHTING_ATTENUATION_TYPE_POINT)
{
vec3 light_dir = normalize(data.lights_chan0_color[i].position - data.position.xyz);
float attn = (dot(normal, light_dir) >= 0.0) ? max(0.0, dot(normal, data.lights_chan0_color[i].direction.xyz)) : 0.0;
vec3 cosAttn = data.lights_chan0_color[i].cosatt.xyz;
vec3 distAttn = data.lights_chan0_color[i].distatt.xyz;
attn = max(0.0, dot(cosAttn, vec3(1.0, attn, attn*attn))) / dot(distAttn, vec3(1.0, attn, attn * attn));
total_diffuse += attn * max(0.0, dot(normal, light_dir));
}
}
return vec4(total_diffuse * vec3(0, 0, 1), 1);
}
```