mirror of https://github.com/mgba-emu/mgba.git
Merge branch 'master' into feature/sio-dolphin
This commit is contained in:
commit
962e91135a
74
CHANGES
74
CHANGES
|
@ -1,4 +1,10 @@
|
|||
0.4.0: (Future)
|
||||
0.5.0: (Future)
|
||||
Bugfixes:
|
||||
- VFS: Fix reading 7z archives without rewinding first
|
||||
Misc:
|
||||
- GBA: Slightly optimize GBAProcessEvents
|
||||
|
||||
0.4.0: (2016-02-02)
|
||||
Features:
|
||||
- Officially supported ports for the Nintendo 3DS, Wii, and PlayStation Vita
|
||||
- I/O viewer
|
||||
|
@ -15,45 +21,59 @@ Features:
|
|||
- Libretro: Settings for using BIOS and skipping intro
|
||||
- Libretro: Customizable idle loop removal
|
||||
- Implemented cycle counting for sprite rendering
|
||||
- Cleaner, unified settings window
|
||||
- Added a setting for pausing when the emulator is not in focus
|
||||
- Customizable paths for save games, save states, screenshots and patches
|
||||
- Controller hotplugging
|
||||
- Ability to store save games and active cheats within savestates
|
||||
Bugfixes:
|
||||
- Util: Fix PowerPC PNG read/write pixel order
|
||||
- VFS: Fix VFileReadline and remove _vfdReadline
|
||||
- GBA Memory: Fix DMA register writing behavior
|
||||
- GBA BIOS: Fix misaligned CpuSet
|
||||
- ARM7: Fix sign of unaligned LDRSH
|
||||
- ARM7: Fix decoding of some ARM ALU instructions with shifters
|
||||
- Debugger: Fix watchpoints in gdb
|
||||
- GBA: Fix warnings when creating and loading savestates
|
||||
- GBA Memory: Fix DMAs triggering two cycles early
|
||||
- GBA Hardware: Fix GPIO on big endian
|
||||
- Util: Fix excessive memory allocation when decoding a PNG
|
||||
- GBA: Fix Iridion II savetype
|
||||
- GBA BIOS: Fix misaligned CpuSet
|
||||
- GBA Cheats: Fix cheats setting the Action Replay version
|
||||
- GBA Hardware: Fix GPIO on big endian
|
||||
- GBA Memory: Fix DMA register writing behavior
|
||||
- GBA Memory: Fix DMAs triggering two cycles early
|
||||
- Libretro: Fix aspect ratio
|
||||
- Qt: Fix some potential crashes with the gamepad mapping
|
||||
- Qt: Fix keys being mapped incorrectly when loading configuration file
|
||||
- Util: Fix PowerPC PNG read/write pixel order
|
||||
- Util: Fix excessive memory allocation when decoding a PNG
|
||||
- VFS: Fix VFileReadline and remove _vfdReadline
|
||||
Misc:
|
||||
- Qt: Window size command line options are now supported
|
||||
- Qt: Increase usability of key mapper
|
||||
- GBA Memory: Use a dynamically sized mask for ROM memory
|
||||
- ARM7: Combine shifter-immediate and shifter-register functions to reduce binary size
|
||||
- SDL: Support fullscreen in SDL 1.2
|
||||
- Libretro: Use anonymous memory mappers for large blocks of memory
|
||||
- Qt: Add 'Apply' button to settings window
|
||||
- GBA Video: Remove lastHblank, as it is implied
|
||||
- GBA Config: Add "override" layer for better one-time configuration
|
||||
- SDL: Allow GBASDLAudio to be used without a thread context
|
||||
- All: Improved PowerPC support
|
||||
- All: Fix some undefined behavior warnings
|
||||
- Util: Use VFile for configuration
|
||||
- GBA Memory: Implement several unimplemented memory access types
|
||||
- ARM7: Combine shifter-immediate and shifter-register functions to reduce binary size
|
||||
- Debugger: Convert breakpoints and watchpoints from linked-lists to vectors
|
||||
- GBA: Implement bad I/O register loading
|
||||
- GBA: Allow jumping to OAM and palette RAM
|
||||
- GBA BIOS: Finish implementing RegisterRamReset
|
||||
- GBA Config: Add "override" layer for better one-time configuration
|
||||
- GBA Input: Consolidate GBA_KEY_NONE and GBA_NO_MAPPING
|
||||
- GBA Memory: Use a dynamically sized mask for ROM memory
|
||||
- GBA Memory: Implement several unimplemented memory access types
|
||||
- GBA Memory: Add GBAView* functions for viewing memory directly without bus issues
|
||||
- Util: Add MutexTryLock
|
||||
- GBA RR: Starting from savestate now embeds the savegame
|
||||
- GBA RR: Add preliminary SRAM support for VBM loading
|
||||
- GBA RR: Add support for resets in movies
|
||||
- GBA Video: Remove lastHblank, as it is implied
|
||||
- Libretro: Use anonymous memory mappers for large blocks of memory
|
||||
- Libretro: Add install target for libretro core
|
||||
- Qt: Window size command line options are now supported
|
||||
- Qt: Increase usability of key mapper
|
||||
- Qt: Add 'Apply' button to settings window
|
||||
- Qt: Gray out "Skip BIOS intro" while "Use BIOS file" is unchecked
|
||||
- Qt: Allow use of modifier keys as input
|
||||
- Qt: Optimize log viewer
|
||||
- GBA RR: Starting from savestate now embeds the savegame
|
||||
- Libretro: Add install target for libretro core
|
||||
- 3DS: Update to new ctrulib API
|
||||
- GBA RR: Add preliminary SRAM support for VBM loading
|
||||
- GBA RR: Add support for resets in movies
|
||||
- GBA Input: Consolidate GBA_KEY_NONE and GBA_NO_MAPPING
|
||||
- Qt: Added button for breaking into the GDB debugger
|
||||
- Qt: Add box for showing duration of rewind
|
||||
- SDL: Support fullscreen in SDL 1.2
|
||||
- SDL: Allow GBASDLAudio to be used without a thread context
|
||||
- Util: Use VFile for configuration
|
||||
- Util: Add MutexTryLock
|
||||
|
||||
0.3.2: (2015-12-16)
|
||||
Bugfixes:
|
||||
|
|
|
@ -125,7 +125,6 @@ Footnotes
|
|||
|
||||
- OBJ window for modes 3, 4 and 5 ([Bug #5](http://mgba.io/b/5))
|
||||
- Mosaic for transformed OBJs ([Bug #9](http://mgba.io/b/9))
|
||||
- BIOS call RegisterRamReset is partially stubbed out ([Bug #141](http://mgba.io/b/141))
|
||||
|
||||
<a name="flashdetect">[2]</a> Flash memory size detection does not work in some cases. These can be configured at runtime, but filing a bug is recommended if such a case is encountered.
|
||||
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
[shader]
|
||||
name=Pixelate
|
||||
author=endrift
|
||||
description=Only scale up the screen at an integer ratio
|
||||
passes=1
|
||||
|
||||
[pass.0]
|
||||
blend=1
|
||||
integerScaling=1
|
|
@ -0,0 +1,38 @@
|
|||
[shader]
|
||||
name=xBR
|
||||
author=Hyllian
|
||||
description=xBR upsampling filter
|
||||
passes=1
|
||||
|
||||
[pass.0]
|
||||
integerScaling=1
|
||||
vertexShader=xbr.vs
|
||||
fragmentShader=xbr.fs
|
||||
|
||||
[pass.0.uniform.XBR_Y_WEIGHT]
|
||||
type=float
|
||||
default=48
|
||||
readableName=Y Weight
|
||||
min=0
|
||||
max=100
|
||||
|
||||
[pass.0.uniform.XBR_EQ_THRESHOLD]
|
||||
type=float
|
||||
readableName=Eq Threshold
|
||||
default=10.0
|
||||
min=0.0
|
||||
max=50.0
|
||||
|
||||
[pass.0.uniform.XBR_EQ_THRESHOLD2]
|
||||
type=float
|
||||
readableName=Eq Threshold2
|
||||
default=2.0
|
||||
min=0.0
|
||||
max=4.0
|
||||
|
||||
[pass.0.uniform.XBR_LV2_COEFFICIENT]
|
||||
type=float
|
||||
readableName=Lv2 Coefficient
|
||||
default=2.0
|
||||
min=1.0
|
||||
max=3.0
|
|
@ -0,0 +1,251 @@
|
|||
/*
|
||||
Hyllian's xBR-lv3 Shader
|
||||
|
||||
Copyright (C) 2011-2015 Hyllian - sergiogdb@gmail.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
|
||||
Incorporates some of the ideas from SABR shader. Thanks to Joshua Street.
|
||||
*/
|
||||
|
||||
uniform float XBR_Y_WEIGHT;
|
||||
uniform float XBR_EQ_THRESHOLD;
|
||||
uniform float XBR_EQ_THRESHOLD2;
|
||||
uniform float XBR_LV2_COEFFICIENT;
|
||||
|
||||
const mat3 yuv = mat3(0.299, 0.587, 0.114, -0.169, -0.331, 0.499, 0.499, -0.418, -0.0813);
|
||||
const vec4 delta = vec4(0.4, 0.4, 0.4, 0.4);
|
||||
|
||||
vec4 df(vec4 A, vec4 B)
|
||||
{
|
||||
return vec4(abs(A-B));
|
||||
}
|
||||
|
||||
float c_df(vec3 c1, vec3 c2) {
|
||||
vec3 df = abs(c1 - c2);
|
||||
return df.r + df.g + df.b;
|
||||
}
|
||||
|
||||
bvec4 eq(vec4 A, vec4 B)
|
||||
{
|
||||
return lessThan(df(A, B), vec4(XBR_EQ_THRESHOLD));
|
||||
}
|
||||
|
||||
bvec4 eq2(vec4 A, vec4 B)
|
||||
{
|
||||
return lessThan(df(A, B), vec4(XBR_EQ_THRESHOLD2));
|
||||
}
|
||||
|
||||
bvec4 and(bvec4 A, bvec4 B)
|
||||
{
|
||||
return bvec4(A.x && B.x, A.y && B.y, A.z && B.z, A.w && B.w);
|
||||
}
|
||||
|
||||
bvec4 or(bvec4 A, bvec4 B)
|
||||
{
|
||||
return bvec4(A.x || B.x, A.y || B.y, A.z || B.z, A.w || B.w);
|
||||
}
|
||||
|
||||
vec4 weighted_distance(vec4 a, vec4 b, vec4 c, vec4 d, vec4 e, vec4 f, vec4 g, vec4 h)
|
||||
{
|
||||
return (df(a,b) + df(a,c) + df(d,e) + df(d,f) + 4.0*df(g,h));
|
||||
}
|
||||
|
||||
// GLSL shader autogenerated by cg2glsl.py.
|
||||
#if __VERSION__ >= 130
|
||||
#define varying in
|
||||
#define COMPAT_TEXTURE texture
|
||||
out vec4 FragColor;
|
||||
#else
|
||||
#define FragColor gl_FragColor
|
||||
#define COMPAT_TEXTURE texture2D
|
||||
#endif
|
||||
|
||||
#ifdef GL_ES
|
||||
#ifdef GL_FRAGMENT_PRECISION_HIGH
|
||||
precision highp float;
|
||||
#else
|
||||
precision mediump float;
|
||||
#endif
|
||||
#define COMPAT_PRECISION mediump
|
||||
#else
|
||||
#define COMPAT_PRECISION
|
||||
#endif
|
||||
uniform sampler2D tex;
|
||||
varying vec2 texCoord;
|
||||
varying vec4 TEX1;
|
||||
varying vec4 TEX2;
|
||||
varying vec4 TEX3;
|
||||
varying vec4 TEX4;
|
||||
varying vec4 TEX5;
|
||||
varying vec4 TEX6;
|
||||
varying vec4 TEX7;
|
||||
|
||||
const vec2 TextureSize = vec2(240.0, 160.0);
|
||||
|
||||
void main()
|
||||
{
|
||||
bvec4 edr, edr_left, edr_up, edr3_left, edr3_up, px; // px = pixel, edr = edge detection rule
|
||||
bvec4 interp_restriction_lv1, interp_restriction_lv2_left, interp_restriction_lv2_up;
|
||||
bvec4 interp_restriction_lv3_left, interp_restriction_lv3_up;
|
||||
bvec4 nc, nc30, nc60, nc45, nc15, nc75; // new_color
|
||||
vec4 fx, fx_left, fx_up, finalfx, fx3_left, fx3_up; // inequations of straight lines.
|
||||
vec3 res1, res2, pix1, pix2;
|
||||
float blend1, blend2;
|
||||
|
||||
vec2 fp = fract(texCoord * TextureSize);
|
||||
|
||||
vec3 A1 = COMPAT_TEXTURE(tex, TEX1.xw).rgb;
|
||||
vec3 B1 = COMPAT_TEXTURE(tex, TEX1.yw).rgb;
|
||||
vec3 C1 = COMPAT_TEXTURE(tex, TEX1.zw).rgb;
|
||||
|
||||
vec3 A = COMPAT_TEXTURE(tex, TEX2.xw).rgb;
|
||||
vec3 B = COMPAT_TEXTURE(tex, TEX2.yw).rgb;
|
||||
vec3 C = COMPAT_TEXTURE(tex, TEX2.zw).rgb;
|
||||
|
||||
vec3 D = COMPAT_TEXTURE(tex, TEX3.xw).rgb;
|
||||
vec3 E = COMPAT_TEXTURE(tex, TEX3.yw).rgb;
|
||||
vec3 F = COMPAT_TEXTURE(tex, TEX3.zw).rgb;
|
||||
|
||||
vec3 G = COMPAT_TEXTURE(tex, TEX4.xw).rgb;
|
||||
vec3 H = COMPAT_TEXTURE(tex, TEX4.yw).rgb;
|
||||
vec3 I = COMPAT_TEXTURE(tex, TEX4.zw).rgb;
|
||||
|
||||
vec3 G5 = COMPAT_TEXTURE(tex, TEX5.xw).rgb;
|
||||
vec3 H5 = COMPAT_TEXTURE(tex, TEX5.yw).rgb;
|
||||
vec3 I5 = COMPAT_TEXTURE(tex, TEX5.zw).rgb;
|
||||
|
||||
vec3 A0 = COMPAT_TEXTURE(tex, TEX6.xy).rgb;
|
||||
vec3 D0 = COMPAT_TEXTURE(tex, TEX6.xz).rgb;
|
||||
vec3 G0 = COMPAT_TEXTURE(tex, TEX6.xw).rgb;
|
||||
|
||||
vec3 C4 = COMPAT_TEXTURE(tex, TEX7.xy).rgb;
|
||||
vec3 F4 = COMPAT_TEXTURE(tex, TEX7.xz).rgb;
|
||||
vec3 I4 = COMPAT_TEXTURE(tex, TEX7.xw).rgb;
|
||||
|
||||
vec4 b = transpose(mat4x3(B, D, H, F)) * (XBR_Y_WEIGHT * yuv[0]);
|
||||
vec4 c = transpose(mat4x3(C, A, G, I)) * (XBR_Y_WEIGHT * yuv[0]);
|
||||
vec4 e = transpose(mat4x3(E, E, E, E)) * (XBR_Y_WEIGHT * yuv[0]);
|
||||
vec4 d = b.yzwx;
|
||||
vec4 f = b.wxyz;
|
||||
vec4 g = c.zwxy;
|
||||
vec4 h = b.zwxy;
|
||||
vec4 i = c.wxyz;
|
||||
|
||||
vec4 i4 = transpose(mat4x3(I4, C1, A0, G5)) * (XBR_Y_WEIGHT*yuv[0]);
|
||||
vec4 i5 = transpose(mat4x3(I5, C4, A1, G0)) * (XBR_Y_WEIGHT*yuv[0]);
|
||||
vec4 h5 = transpose(mat4x3(H5, F4, B1, D0)) * (XBR_Y_WEIGHT*yuv[0]);
|
||||
vec4 f4 = h5.yzwx;
|
||||
|
||||
vec4 c1 = i4.yzwx;
|
||||
vec4 g0 = i5.wxyz;
|
||||
vec4 b1 = h5.zwxy;
|
||||
vec4 d0 = h5.wxyz;
|
||||
|
||||
vec4 Ao = vec4( 1.0, -1.0, -1.0, 1.0 );
|
||||
vec4 Bo = vec4( 1.0, 1.0, -1.0,-1.0 );
|
||||
vec4 Co = vec4( 1.5, 0.5, -0.5, 0.5 );
|
||||
vec4 Ax = vec4( 1.0, -1.0, -1.0, 1.0 );
|
||||
vec4 Bx = vec4( 0.5, 2.0, -0.5,-2.0 );
|
||||
vec4 Cx = vec4( 1.0, 1.0, -0.5, 0.0 );
|
||||
vec4 Ay = vec4( 1.0, -1.0, -1.0, 1.0 );
|
||||
vec4 By = vec4( 2.0, 0.5, -2.0,-0.5 );
|
||||
vec4 Cy = vec4( 2.0, 0.0, -1.0, 0.5 );
|
||||
|
||||
vec4 Az = vec4( 6.0, -2.0, -6.0, 2.0 );
|
||||
vec4 Bz = vec4( 2.0, 6.0, -2.0, -6.0 );
|
||||
vec4 Cz = vec4( 5.0, 3.0, -3.0, -1.0 );
|
||||
vec4 Aw = vec4( 2.0, -6.0, -2.0, 6.0 );
|
||||
vec4 Bw = vec4( 6.0, 2.0, -6.0,-2.0 );
|
||||
vec4 Cw = vec4( 5.0, -1.0, -3.0, 3.0 );
|
||||
|
||||
fx = (Ao*fp.y+Bo*fp.x);
|
||||
fx_left = (Ax*fp.y+Bx*fp.x);
|
||||
fx_up = (Ay*fp.y+By*fp.x);
|
||||
fx3_left= (Az*fp.y+Bz*fp.x);
|
||||
fx3_up = (Aw*fp.y+Bw*fp.x);
|
||||
|
||||
// It uses CORNER_C if none of the others are defined.
|
||||
#ifdef CORNER_A
|
||||
interp_restriction_lv1 = and(notEqual(e, f), notEqual(e, h));
|
||||
#elif defined(CORNER_B)
|
||||
interp_restriction_lv1 = ((e!=f) && (e!=h) && ( !eq(f,b) && !eq(h,d) || eq(e,i) && !eq(f,i4) && !eq(h,i5) || eq(e,g) || eq(e,c) ) );
|
||||
#elif defined(CORNER_D)
|
||||
interp_restriction_lv1 = ((e!=f) && (e!=h) && ( !eq(f,b) && !eq(h,d) || eq(e,i) && !eq(f,i4) && !eq(h,i5) || eq(e,g) || eq(e,c) ) && (f!=f4 && f!=i || h!=h5 && h!=i || h!=g || f!=c || eq(b,c1) && eq(d,g0)));
|
||||
#else
|
||||
interp_restriction_lv1 = and(and(notEqual(e, f), notEqual(e, h)),
|
||||
or(or(and(not(eq(f,b)), not(eq(f,c))),
|
||||
and(not(eq(h,d)), not(eq(h,g)))),
|
||||
or(and(eq(e,i), or(and(not(eq(f,f4)), not(eq(f,i4))),
|
||||
and(not(eq(h,h5)), not(eq(h,i5))))),
|
||||
or(eq(e,g), eq(e,c)))));
|
||||
#endif
|
||||
|
||||
interp_restriction_lv2_left = and(notEqual(e, g), notEqual(d, g));
|
||||
interp_restriction_lv2_up = and(notEqual(e, c), notEqual(b, c));
|
||||
interp_restriction_lv3_left = and(eq2(g,g0), not(eq2(d0,g0)));
|
||||
interp_restriction_lv3_up = and(eq2(c,c1), not(eq2(b1,c1)));
|
||||
|
||||
vec4 fx45 = smoothstep(Co - delta, Co + delta, fx);
|
||||
vec4 fx30 = smoothstep(Cx - delta, Cx + delta, fx_left);
|
||||
vec4 fx60 = smoothstep(Cy - delta, Cy + delta, fx_up);
|
||||
vec4 fx15 = smoothstep(Cz - delta, Cz + delta, fx3_left);
|
||||
vec4 fx75 = smoothstep(Cw - delta, Cw + delta, fx3_up);
|
||||
|
||||
edr = and(lessThan(weighted_distance( e, c, g, i, h5, f4, h, f), weighted_distance( h, d, i5, f, i4, b, e, i)), interp_restriction_lv1);
|
||||
edr_left = and(lessThanEqual((XBR_LV2_COEFFICIENT*df(f,g)), df(h,c)), interp_restriction_lv2_left);
|
||||
edr_up = and(greaterThanEqual(df(f,g), (XBR_LV2_COEFFICIENT*df(h,c))), interp_restriction_lv2_up);
|
||||
edr3_left = interp_restriction_lv3_left;
|
||||
edr3_up = interp_restriction_lv3_up;
|
||||
|
||||
nc45 = and(edr, bvec4(fx45));
|
||||
nc30 = and(edr, and(edr_left, bvec4(fx30)));
|
||||
nc60 = and(edr, and(edr_up, bvec4(fx60)));
|
||||
nc15 = and(and(edr, edr_left), and(edr3_left, bvec4(fx15)));
|
||||
nc75 = and(and(edr, edr_up), and(edr3_up, bvec4(fx75)));
|
||||
|
||||
px = lessThanEqual(df(e, f), df(e, h));
|
||||
|
||||
nc = bvec4(nc75.x || nc15.x || nc30.x || nc60.x || nc45.x, nc75.y || nc15.y || nc30.y || nc60.y || nc45.y, nc75.z || nc15.z || nc30.z || nc60.z || nc45.z, nc75.w || nc15.w || nc30.w || nc60.w || nc45.w);
|
||||
|
||||
vec4 final45 = vec4(nc45) * fx45;
|
||||
vec4 final30 = vec4(nc30) * fx30;
|
||||
vec4 final60 = vec4(nc60) * fx60;
|
||||
vec4 final15 = vec4(nc15) * fx15;
|
||||
vec4 final75 = vec4(nc75) * fx75;
|
||||
|
||||
vec4 maximo = max(max(max(final15, final75),max(final30, final60)), final45);
|
||||
|
||||
if (nc.x) {pix1 = px.x ? F : H; blend1 = maximo.x;}
|
||||
else if (nc.y) {pix1 = px.y ? B : F; blend1 = maximo.y;}
|
||||
else if (nc.z) {pix1 = px.z ? D : B; blend1 = maximo.z;}
|
||||
else if (nc.w) {pix1 = px.w ? H : D; blend1 = maximo.w;}
|
||||
|
||||
if (nc.w) {pix2 = px.w ? H : D; blend2 = maximo.w;}
|
||||
else if (nc.z) {pix2 = px.z ? D : B; blend2 = maximo.z;}
|
||||
else if (nc.y) {pix2 = px.y ? B : F; blend2 = maximo.y;}
|
||||
else if (nc.x) {pix2 = px.x ? F : H; blend2 = maximo.x;}
|
||||
|
||||
res1 = mix(E, pix1, blend1);
|
||||
res2 = mix(E, pix2, blend2);
|
||||
vec3 res = mix(res1, res2, step(c_df(E, res1), c_df(E, res2)));
|
||||
|
||||
FragColor = vec4(res, 1.0);
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
Hyllian's xBR-lv3 Shader
|
||||
|
||||
Copyright (C) 2011-2015 Hyllian - sergiogdb@gmail.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
Incorporates some of the ideas from SABR shader. Thanks to Joshua Street.
|
||||
*/
|
||||
|
||||
varying vec2 texCoord;
|
||||
varying vec4 TEX1;
|
||||
varying vec4 TEX2;
|
||||
varying vec4 TEX3;
|
||||
varying vec4 TEX4;
|
||||
varying vec4 TEX5;
|
||||
varying vec4 TEX6;
|
||||
varying vec4 TEX7;
|
||||
attribute vec4 position;
|
||||
|
||||
/* VERTEX_SHADER */
|
||||
void main()
|
||||
{
|
||||
gl_Position = position;
|
||||
|
||||
vec2 ps = vec2(1.0/240.0, 1.0/160.0);
|
||||
float dx = ps.x;
|
||||
float dy = ps.y;
|
||||
|
||||
// A1 B1 C1
|
||||
// A0 A B C C4
|
||||
// D0 D E F F4
|
||||
// G0 G H I I4
|
||||
// G5 H5 I5
|
||||
|
||||
texCoord = (position.st + vec2(1.0, 1.0)) * vec2(0.5, 0.5);
|
||||
TEX1 = texCoord.xxxy + vec4( -dx, 0, dx,-2.0*dy); // A1 B1 C1
|
||||
TEX2 = texCoord.xxxy + vec4( -dx, 0, dx, -dy); // A B C
|
||||
TEX3 = texCoord.xxxy + vec4( -dx, 0, dx, 0); // D E F
|
||||
TEX4 = texCoord.xxxy + vec4( -dx, 0, dx, dy); // G H I
|
||||
TEX5 = texCoord.xxxy + vec4( -dx, 0, dx, 2.0*dy); // G5 H5 I5
|
||||
TEX6 = texCoord.xyyy + vec4(-2.0*dx,-dy, 0, dy); // A0 D0 G0
|
||||
TEX7 = texCoord.xyyy + vec4( 2.0*dx,-dy, 0, dy); // C4 F4 I4
|
||||
}
|
|
@ -11,9 +11,9 @@
|
|||
|
||||
#define ADDR_MODE_1_SHIFT(OP) \
|
||||
info->op3.reg = opcode & 0x0000000F; \
|
||||
info->op3.shifterOp = ARM_SHIFT_ ## OP; \
|
||||
info->operandFormat |= ARM_OPERAND_REGISTER_3; \
|
||||
if (opcode & 0x00000010) { \
|
||||
info->op3.shifterOp = ARM_SHIFT_ ## OP; \
|
||||
info->op3.shifterReg = (opcode >> 8) & 0xF; \
|
||||
++info->iCycles; \
|
||||
info->operandFormat |= ARM_OPERAND_SHIFT_REGISTER_3; \
|
||||
|
@ -101,11 +101,13 @@
|
|||
info->affectsCPSR = S; \
|
||||
SHIFTER; \
|
||||
if (SKIPPED == 1) { \
|
||||
info->operandFormat >>= 8; \
|
||||
info->op1 = info->op2; \
|
||||
info->op2 = info->op3; \
|
||||
info->operandFormat >>= 8; \
|
||||
} else if (SKIPPED == 2) { \
|
||||
info->operandFormat &= ~ARM_OPERAND_2; \
|
||||
info->op2 = info->op3; \
|
||||
info->operandFormat |= info->operandFormat >> 8; \
|
||||
info->operandFormat &= ~ARM_OPERAND_3; \
|
||||
} \
|
||||
if (info->op1.reg == ARM_PC) { \
|
||||
info->branchType = ARM_BRANCH_INDIRECT; \
|
||||
|
|
|
@ -536,7 +536,7 @@ static void _setWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector*
|
|||
return;
|
||||
}
|
||||
uint32_t address = dv->intValue;
|
||||
ARMDebuggerSetWatchpoint(&debugger->d, address);
|
||||
ARMDebuggerSetWatchpoint(&debugger->d, address, WATCHPOINT_RW); // TODO: ro/wo
|
||||
}
|
||||
|
||||
static void _breakIntoDefault(int signal) {
|
||||
|
@ -802,7 +802,11 @@ static void _reportEntry(struct ARMDebugger* debugger, enum DebuggerEntryReason
|
|||
break;
|
||||
case DEBUGGER_ENTER_WATCHPOINT:
|
||||
if (info) {
|
||||
printf("Hit watchpoint at 0x%08X: (old value = 0x%08X)\n", info->address, info->oldValue);
|
||||
if (info->accessType & WATCHPOINT_WRITE) {
|
||||
printf("Hit watchpoint at 0x%08X: (new value = 0x%08x, old value = 0x%08X)\n", info->address, info->newValue, info->oldValue);
|
||||
} else {
|
||||
printf("Hit watchpoint at 0x%08X: (value = 0x%08x)\n", info->address, info->oldValue);
|
||||
}
|
||||
} else {
|
||||
printf("Hit watchpoint\n");
|
||||
}
|
||||
|
|
|
@ -12,10 +12,14 @@
|
|||
|
||||
const uint32_t ARM_DEBUGGER_ID = 0xDEADBEEF;
|
||||
|
||||
static struct DebugBreakpoint* _lookupBreakpoint(struct DebugBreakpoint* breakpoints, uint32_t address) {
|
||||
for (; breakpoints; breakpoints = breakpoints->next) {
|
||||
if (breakpoints->address == address) {
|
||||
return breakpoints;
|
||||
DEFINE_VECTOR(DebugBreakpointList, struct DebugBreakpoint);
|
||||
DEFINE_VECTOR(DebugWatchpointList, struct DebugWatchpoint);
|
||||
|
||||
static struct DebugBreakpoint* _lookupBreakpoint(struct DebugBreakpointList* breakpoints, uint32_t address) {
|
||||
size_t i;
|
||||
for (i = 0; i < DebugBreakpointListSize(breakpoints); ++i) {
|
||||
if (DebugBreakpointListGetPointer(breakpoints, i)->address == address) {
|
||||
return DebugBreakpointListGetPointer(breakpoints, i);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
@ -29,7 +33,7 @@ static void _checkBreakpoints(struct ARMDebugger* debugger) {
|
|||
} else {
|
||||
instructionLength = WORD_SIZE_THUMB;
|
||||
}
|
||||
struct DebugBreakpoint* breakpoint = _lookupBreakpoint(debugger->breakpoints, debugger->cpu->gprs[ARM_PC] - instructionLength);
|
||||
struct DebugBreakpoint* breakpoint = _lookupBreakpoint(&debugger->breakpoints, debugger->cpu->gprs[ARM_PC] - instructionLength);
|
||||
if (!breakpoint) {
|
||||
return;
|
||||
}
|
||||
|
@ -52,11 +56,11 @@ void ARMDebuggerInit(struct ARMCore* cpu, struct ARMComponent* component) {
|
|||
struct ARMDebugger* debugger = (struct ARMDebugger*) component;
|
||||
debugger->cpu = cpu;
|
||||
debugger->state = DEBUGGER_RUNNING;
|
||||
debugger->breakpoints = 0;
|
||||
debugger->swBreakpoints = 0;
|
||||
debugger->originalMemory = cpu->memory;
|
||||
debugger->watchpoints = 0;
|
||||
debugger->currentBreakpoint = 0;
|
||||
DebugBreakpointListInit(&debugger->breakpoints, 0);
|
||||
DebugBreakpointListInit(&debugger->swBreakpoints, 0);
|
||||
DebugWatchpointListInit(&debugger->watchpoints, 0);
|
||||
if (debugger->init) {
|
||||
debugger->init(debugger);
|
||||
}
|
||||
|
@ -65,12 +69,15 @@ void ARMDebuggerInit(struct ARMCore* cpu, struct ARMComponent* component) {
|
|||
void ARMDebuggerDeinit(struct ARMComponent* component) {
|
||||
struct ARMDebugger* debugger = (struct ARMDebugger*) component;
|
||||
debugger->deinit(debugger);
|
||||
DebugBreakpointListDeinit(&debugger->breakpoints);
|
||||
DebugBreakpointListDeinit(&debugger->swBreakpoints);
|
||||
DebugWatchpointListDeinit(&debugger->watchpoints);
|
||||
}
|
||||
|
||||
void ARMDebuggerRun(struct ARMDebugger* debugger) {
|
||||
switch (debugger->state) {
|
||||
case DEBUGGER_RUNNING:
|
||||
if (!debugger->breakpoints && !debugger->watchpoints) {
|
||||
if (!DebugBreakpointListSize(&debugger->breakpoints) && !DebugWatchpointListSize(&debugger->watchpoints)) {
|
||||
ARMRunLoop(debugger->cpu);
|
||||
} else {
|
||||
ARMRun(debugger->cpu);
|
||||
|
@ -105,7 +112,7 @@ void ARMDebuggerEnter(struct ARMDebugger* debugger, enum DebuggerEntryReason rea
|
|||
struct ARMCore* cpu = debugger->cpu;
|
||||
cpu->nextEvent = cpu->cycles;
|
||||
if (reason == DEBUGGER_ENTER_BREAKPOINT) {
|
||||
struct DebugBreakpoint* breakpoint = _lookupBreakpoint(debugger->swBreakpoints, _ARMPCAddress(cpu));
|
||||
struct DebugBreakpoint* breakpoint = _lookupBreakpoint(&debugger->swBreakpoints, _ARMPCAddress(cpu));
|
||||
debugger->currentBreakpoint = breakpoint;
|
||||
if (breakpoint && breakpoint->isSw) {
|
||||
info->address = breakpoint->address;
|
||||
|
@ -122,11 +129,9 @@ void ARMDebuggerEnter(struct ARMDebugger* debugger, enum DebuggerEntryReason rea
|
|||
}
|
||||
|
||||
void ARMDebuggerSetBreakpoint(struct ARMDebugger* debugger, uint32_t address) {
|
||||
struct DebugBreakpoint* breakpoint = malloc(sizeof(struct DebugBreakpoint));
|
||||
struct DebugBreakpoint* breakpoint = DebugBreakpointListAppend(&debugger->breakpoints);
|
||||
breakpoint->address = address;
|
||||
breakpoint->next = debugger->breakpoints;
|
||||
breakpoint->isSw = false;
|
||||
debugger->breakpoints = breakpoint;
|
||||
}
|
||||
|
||||
bool ARMDebuggerSetSoftwareBreakpoint(struct ARMDebugger* debugger, uint32_t address, enum ExecutionMode mode) {
|
||||
|
@ -135,56 +140,44 @@ bool ARMDebuggerSetSoftwareBreakpoint(struct ARMDebugger* debugger, uint32_t add
|
|||
return false;
|
||||
}
|
||||
|
||||
struct DebugBreakpoint* breakpoint = malloc(sizeof(struct DebugBreakpoint));
|
||||
struct DebugBreakpoint* breakpoint = DebugBreakpointListAppend(&debugger->swBreakpoints);
|
||||
breakpoint->address = address;
|
||||
breakpoint->next = debugger->swBreakpoints;
|
||||
breakpoint->isSw = true;
|
||||
breakpoint->sw.opcode = opcode;
|
||||
breakpoint->sw.mode = mode;
|
||||
debugger->swBreakpoints = breakpoint;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ARMDebuggerClearBreakpoint(struct ARMDebugger* debugger, uint32_t address) {
|
||||
struct DebugBreakpoint** previous = &debugger->breakpoints;
|
||||
struct DebugBreakpoint* breakpoint;
|
||||
struct DebugBreakpoint** next;
|
||||
while ((breakpoint = *previous)) {
|
||||
next = &breakpoint->next;
|
||||
if (breakpoint->address == address) {
|
||||
*previous = *next;
|
||||
free(breakpoint);
|
||||
continue;
|
||||
struct DebugBreakpointList* breakpoints = &debugger->breakpoints;
|
||||
size_t i;
|
||||
for (i = 0; i < DebugBreakpointListSize(breakpoints); ++i) {
|
||||
if (DebugBreakpointListGetPointer(breakpoints, i)->address == address) {
|
||||
DebugBreakpointListShift(breakpoints, i, 1);
|
||||
}
|
||||
previous = next;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ARMDebuggerSetWatchpoint(struct ARMDebugger* debugger, uint32_t address) {
|
||||
if (!debugger->watchpoints) {
|
||||
void ARMDebuggerSetWatchpoint(struct ARMDebugger* debugger, uint32_t address, enum WatchpointType type) {
|
||||
if (!DebugWatchpointListSize(&debugger->watchpoints)) {
|
||||
ARMDebuggerInstallMemoryShim(debugger);
|
||||
}
|
||||
struct DebugWatchpoint* watchpoint = malloc(sizeof(struct DebugWatchpoint));
|
||||
struct DebugWatchpoint* watchpoint = DebugWatchpointListAppend(&debugger->watchpoints);
|
||||
watchpoint->address = address;
|
||||
watchpoint->next = debugger->watchpoints;
|
||||
debugger->watchpoints = watchpoint;
|
||||
watchpoint->type = type;
|
||||
}
|
||||
|
||||
void ARMDebuggerClearWatchpoint(struct ARMDebugger* debugger, uint32_t address) {
|
||||
struct DebugWatchpoint** previous = &debugger->watchpoints;
|
||||
struct DebugWatchpoint* watchpoint;
|
||||
struct DebugWatchpoint** next;
|
||||
while ((watchpoint = *previous)) {
|
||||
next = &watchpoint->next;
|
||||
if (watchpoint->address == address) {
|
||||
*previous = *next;
|
||||
free(watchpoint);
|
||||
continue;
|
||||
struct DebugWatchpointList* watchpoints = &debugger->watchpoints;
|
||||
size_t i;
|
||||
for (i = 0; i < DebugWatchpointListSize(watchpoints); ++i) {
|
||||
if (DebugWatchpointListGetPointer(watchpoints, i)->address == address) {
|
||||
DebugWatchpointListShift(watchpoints, i, 1);
|
||||
}
|
||||
previous = next;
|
||||
}
|
||||
if (!debugger->watchpoints) {
|
||||
if (!DebugWatchpointListSize(&debugger->watchpoints)) {
|
||||
ARMDebuggerRemoveMemoryShim(debugger);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,8 @@
|
|||
|
||||
#include "util/common.h"
|
||||
|
||||
#include "arm.h"
|
||||
#include "arm/arm.h"
|
||||
#include "util/vector.h"
|
||||
|
||||
extern const uint32_t ARM_DEBUGGER_ID;
|
||||
|
||||
|
@ -20,7 +21,6 @@ enum DebuggerState {
|
|||
};
|
||||
|
||||
struct DebugBreakpoint {
|
||||
struct DebugBreakpoint* next;
|
||||
uint32_t address;
|
||||
bool isSw;
|
||||
struct {
|
||||
|
@ -32,15 +32,17 @@ struct DebugBreakpoint {
|
|||
enum WatchpointType {
|
||||
WATCHPOINT_WRITE = 1,
|
||||
WATCHPOINT_READ = 2,
|
||||
WATCHPOINT_RW = 3
|
||||
WATCHPOINT_RW = WATCHPOINT_WRITE | WATCHPOINT_READ
|
||||
};
|
||||
|
||||
struct DebugWatchpoint {
|
||||
struct DebugWatchpoint* next;
|
||||
uint32_t address;
|
||||
enum WatchpointType type;
|
||||
};
|
||||
|
||||
DECLARE_VECTOR(DebugBreakpointList, struct DebugBreakpoint);
|
||||
DECLARE_VECTOR(DebugWatchpointList, struct DebugWatchpoint);
|
||||
|
||||
enum DebuggerEntryReason {
|
||||
DEBUGGER_ENTER_MANUAL,
|
||||
DEBUGGER_ENTER_ATTACHED,
|
||||
|
@ -54,7 +56,9 @@ struct DebuggerEntryInfo {
|
|||
union {
|
||||
struct {
|
||||
uint32_t oldValue;
|
||||
uint32_t newValue;
|
||||
enum WatchpointType watchType;
|
||||
enum WatchpointType accessType;
|
||||
};
|
||||
|
||||
struct {
|
||||
|
@ -75,9 +79,9 @@ struct ARMDebugger {
|
|||
enum DebuggerState state;
|
||||
struct ARMCore* cpu;
|
||||
|
||||
struct DebugBreakpoint* breakpoints;
|
||||
struct DebugBreakpoint* swBreakpoints;
|
||||
struct DebugWatchpoint* watchpoints;
|
||||
struct DebugBreakpointList breakpoints;
|
||||
struct DebugBreakpointList swBreakpoints;
|
||||
struct DebugWatchpointList watchpoints;
|
||||
struct ARMMemory originalMemory;
|
||||
|
||||
struct DebugBreakpoint* currentBreakpoint;
|
||||
|
@ -101,7 +105,7 @@ void ARMDebuggerEnter(struct ARMDebugger*, enum DebuggerEntryReason, struct Debu
|
|||
void ARMDebuggerSetBreakpoint(struct ARMDebugger* debugger, uint32_t address);
|
||||
bool ARMDebuggerSetSoftwareBreakpoint(struct ARMDebugger* debugger, uint32_t address, enum ExecutionMode mode);
|
||||
void ARMDebuggerClearBreakpoint(struct ARMDebugger* debugger, uint32_t address);
|
||||
void ARMDebuggerSetWatchpoint(struct ARMDebugger* debugger, uint32_t address);
|
||||
void ARMDebuggerSetWatchpoint(struct ARMDebugger* debugger, uint32_t address, enum WatchpointType type);
|
||||
void ARMDebuggerClearWatchpoint(struct ARMDebugger* debugger, uint32_t address);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -40,13 +40,19 @@ static void _gdbStubEntered(struct ARMDebugger* debugger, enum DebuggerEntryReas
|
|||
snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "S%02x", SIGINT);
|
||||
break;
|
||||
case DEBUGGER_ENTER_BREAKPOINT:
|
||||
snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "S%02x", SIGTRAP);
|
||||
snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "S%02x", SIGTRAP); // TODO: Use hwbreak/swbreak if gdb supports it
|
||||
break;
|
||||
case DEBUGGER_ENTER_WATCHPOINT: // TODO: Make watchpoints raise with address
|
||||
case DEBUGGER_ENTER_WATCHPOINT:
|
||||
if (info) {
|
||||
const char* type = 0;
|
||||
switch (info->watchType) {
|
||||
case WATCHPOINT_WRITE:
|
||||
if (info->newValue == info->oldValue) {
|
||||
if (stub->d.state == DEBUGGER_PAUSED) {
|
||||
stub->d.state = DEBUGGER_RUNNING;
|
||||
}
|
||||
return;
|
||||
}
|
||||
type = "watch";
|
||||
break;
|
||||
case WATCHPOINT_READ:
|
||||
|
@ -56,7 +62,7 @@ static void _gdbStubEntered(struct ARMDebugger* debugger, enum DebuggerEntryReas
|
|||
type = "awatch";
|
||||
break;
|
||||
}
|
||||
snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "T%02x%s:%08X", SIGTRAP, type, info->address);
|
||||
snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "T%02x%s:%08x;", SIGTRAP, type, info->address);
|
||||
} else {
|
||||
snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "S%02x", SIGTRAP);
|
||||
}
|
||||
|
@ -317,9 +323,17 @@ static void _setBreakpoint(struct GDBStub* stub, const char* message) {
|
|||
_sendMessage(stub);
|
||||
break;
|
||||
case '2':
|
||||
ARMDebuggerSetWatchpoint(&stub->d, address, WATCHPOINT_WRITE);
|
||||
strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
|
||||
_sendMessage(stub);
|
||||
break;
|
||||
case '3':
|
||||
ARMDebuggerSetWatchpoint(&stub->d, address, WATCHPOINT_READ);
|
||||
strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
|
||||
_sendMessage(stub);
|
||||
break;
|
||||
case '4':
|
||||
ARMDebuggerSetWatchpoint(&stub->d, address);
|
||||
ARMDebuggerSetWatchpoint(&stub->d, address, WATCHPOINT_RW);
|
||||
strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
|
||||
_sendMessage(stub);
|
||||
break;
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
|
||||
#include <string.h>
|
||||
|
||||
static bool _checkWatchpoints(struct ARMDebugger* debugger, uint32_t address, struct DebuggerEntryInfo* info, int width);
|
||||
static bool _checkWatchpoints(struct ARMDebugger* debugger, uint32_t address, struct DebuggerEntryInfo* info, enum WatchpointType type, uint32_t newValue, int width);
|
||||
|
||||
#define FIND_DEBUGGER(DEBUGGER, CPU) \
|
||||
{ \
|
||||
|
@ -32,18 +32,29 @@ static bool _checkWatchpoints(struct ARMDebugger* debugger, uint32_t address, st
|
|||
return debugger->originalMemory.NAME(cpu, __VA_ARGS__); \
|
||||
}
|
||||
|
||||
#define CREATE_WATCHPOINT_SHIM(NAME, WIDTH, RETURN, TYPES, ...) \
|
||||
#define CREATE_WATCHPOINT_READ_SHIM(NAME, WIDTH, RETURN, TYPES, ...) \
|
||||
static RETURN ARMDebuggerShim_ ## NAME TYPES { \
|
||||
struct ARMDebugger* debugger; \
|
||||
FIND_DEBUGGER(debugger, cpu); \
|
||||
struct DebuggerEntryInfo info; \
|
||||
if (_checkWatchpoints(debugger, address, &info, WIDTH)) { \
|
||||
if (_checkWatchpoints(debugger, address, &info, WATCHPOINT_READ, 0, WIDTH)) { \
|
||||
ARMDebuggerEnter(debugger, DEBUGGER_ENTER_WATCHPOINT, &info); \
|
||||
} \
|
||||
return debugger->originalMemory.NAME(cpu, __VA_ARGS__); \
|
||||
}
|
||||
|
||||
#define CREATE_MULTIPLE_WATCHPOINT_SHIM(NAME) \
|
||||
#define CREATE_WATCHPOINT_WRITE_SHIM(NAME, WIDTH, RETURN, TYPES, ...) \
|
||||
static RETURN ARMDebuggerShim_ ## NAME TYPES { \
|
||||
struct ARMDebugger* debugger; \
|
||||
FIND_DEBUGGER(debugger, cpu); \
|
||||
struct DebuggerEntryInfo info; \
|
||||
if (_checkWatchpoints(debugger, address, &info, WATCHPOINT_WRITE, value, WIDTH)) { \
|
||||
ARMDebuggerEnter(debugger, DEBUGGER_ENTER_WATCHPOINT, &info); \
|
||||
} \
|
||||
return debugger->originalMemory.NAME(cpu, __VA_ARGS__); \
|
||||
}
|
||||
|
||||
#define CREATE_MULTIPLE_WATCHPOINT_SHIM(NAME, ACCESS_TYPE) \
|
||||
static uint32_t ARMDebuggerShim_ ## NAME (struct ARMCore* cpu, uint32_t address, int mask, enum LSMDirection direction, int* cycleCounter) { \
|
||||
struct ARMDebugger* debugger; \
|
||||
FIND_DEBUGGER(debugger, cpu); \
|
||||
|
@ -60,28 +71,30 @@ static bool _checkWatchpoints(struct ARMDebugger* debugger, uint32_t address, st
|
|||
unsigned i; \
|
||||
for (i = 0; i < popcount; ++i) { \
|
||||
struct DebuggerEntryInfo info; \
|
||||
if (_checkWatchpoints(debugger, base + 4 * i, &info, 4)) { \
|
||||
if (_checkWatchpoints(debugger, base + 4 * i, &info, ACCESS_TYPE, 0, 4)) { \
|
||||
ARMDebuggerEnter(debugger, DEBUGGER_ENTER_WATCHPOINT, &info); \
|
||||
} \
|
||||
} \
|
||||
return debugger->originalMemory.NAME(cpu, address, mask, direction, cycleCounter); \
|
||||
}
|
||||
|
||||
CREATE_WATCHPOINT_SHIM(load32, 4, uint32_t, (struct ARMCore* cpu, uint32_t address, int* cycleCounter), address, cycleCounter)
|
||||
CREATE_WATCHPOINT_SHIM(load16, 2, uint32_t, (struct ARMCore* cpu, uint32_t address, int* cycleCounter), address, cycleCounter)
|
||||
CREATE_WATCHPOINT_SHIM(load8, 1, uint32_t, (struct ARMCore* cpu, uint32_t address, int* cycleCounter), address, cycleCounter)
|
||||
CREATE_WATCHPOINT_SHIM(store32, 4, void, (struct ARMCore* cpu, uint32_t address, int32_t value, int* cycleCounter), address, value, cycleCounter)
|
||||
CREATE_WATCHPOINT_SHIM(store16, 2, void, (struct ARMCore* cpu, uint32_t address, int16_t value, int* cycleCounter), address, value, cycleCounter)
|
||||
CREATE_WATCHPOINT_SHIM(store8, 1, void, (struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCounter), address, value, cycleCounter)
|
||||
CREATE_MULTIPLE_WATCHPOINT_SHIM(loadMultiple)
|
||||
CREATE_MULTIPLE_WATCHPOINT_SHIM(storeMultiple)
|
||||
CREATE_WATCHPOINT_READ_SHIM(load32, 4, uint32_t, (struct ARMCore* cpu, uint32_t address, int* cycleCounter), address, cycleCounter)
|
||||
CREATE_WATCHPOINT_READ_SHIM(load16, 2, uint32_t, (struct ARMCore* cpu, uint32_t address, int* cycleCounter), address, cycleCounter)
|
||||
CREATE_WATCHPOINT_READ_SHIM(load8, 1, uint32_t, (struct ARMCore* cpu, uint32_t address, int* cycleCounter), address, cycleCounter)
|
||||
CREATE_WATCHPOINT_WRITE_SHIM(store32, 4, void, (struct ARMCore* cpu, uint32_t address, int32_t value, int* cycleCounter), address, value, cycleCounter)
|
||||
CREATE_WATCHPOINT_WRITE_SHIM(store16, 2, void, (struct ARMCore* cpu, uint32_t address, int16_t value, int* cycleCounter), address, value, cycleCounter)
|
||||
CREATE_WATCHPOINT_WRITE_SHIM(store8, 1, void, (struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCounter), address, value, cycleCounter)
|
||||
CREATE_MULTIPLE_WATCHPOINT_SHIM(loadMultiple, WATCHPOINT_READ)
|
||||
CREATE_MULTIPLE_WATCHPOINT_SHIM(storeMultiple, WATCHPOINT_WRITE)
|
||||
CREATE_SHIM(setActiveRegion, void, (struct ARMCore* cpu, uint32_t address), address)
|
||||
|
||||
static bool _checkWatchpoints(struct ARMDebugger* debugger, uint32_t address, struct DebuggerEntryInfo* info, int width) {
|
||||
static bool _checkWatchpoints(struct ARMDebugger* debugger, uint32_t address, struct DebuggerEntryInfo* info, enum WatchpointType type, uint32_t newValue, int width) {
|
||||
--width;
|
||||
struct DebugWatchpoint* watchpoints;
|
||||
for (watchpoints = debugger->watchpoints; watchpoints; watchpoints = watchpoints->next) {
|
||||
if (!((watchpoints->address ^ address) & ~width)) {
|
||||
struct DebugWatchpoint* watchpoint;
|
||||
size_t i;
|
||||
for (i = 0; i < DebugWatchpointListSize(&debugger->watchpoints); ++i) {
|
||||
watchpoint = DebugWatchpointListGetPointer(&debugger->watchpoints, i);
|
||||
if (!((watchpoint->address ^ address) & ~width) && watchpoint->type & type) {
|
||||
switch (width + 1) {
|
||||
case 1:
|
||||
info->oldValue = debugger->originalMemory.load8(debugger->cpu, address, 0);
|
||||
|
@ -93,8 +106,10 @@ static bool _checkWatchpoints(struct ARMDebugger* debugger, uint32_t address, st
|
|||
info->oldValue = debugger->originalMemory.load32(debugger->cpu, address, 0);
|
||||
break;
|
||||
}
|
||||
info->newValue = newValue;
|
||||
info->address = address;
|
||||
info->watchType = watchpoints->type;
|
||||
info->watchType = watchpoint->type;
|
||||
info->accessType = type;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -71,10 +71,96 @@ static void _RegisterRamReset(struct GBA* gba) {
|
|||
cpu->memory.store32(cpu, BASE_IO | REG_JOY_TRANS_LO, 0, 0);
|
||||
}
|
||||
if (registers & 0x40) {
|
||||
GBALog(gba, GBA_LOG_STUB, "RegisterRamReset on Audio unimplemented");
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_SOUND1CNT_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_SOUND1CNT_HI, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_SOUND1CNT_X, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_SOUND2CNT_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_SOUND2CNT_HI, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_SOUND3CNT_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_SOUND3CNT_HI, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_SOUND3CNT_X, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_SOUND4CNT_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_SOUND4CNT_HI, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_SOUNDCNT_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_SOUNDCNT_HI, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_SOUNDCNT_X, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_SOUNDBIAS, 0x200, 0);
|
||||
memset(gba->audio.ch3.wavedata, 0, sizeof(gba->audio.ch3.wavedata));
|
||||
}
|
||||
if (registers & 0x80) {
|
||||
GBALog(gba, GBA_LOG_STUB, "RegisterRamReset on IO unimplemented");
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x00, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x04, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x06, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x08, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x0A, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x0C, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x0E, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x10, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x12, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x14, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x16, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x18, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x1A, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x1C, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x1E, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_BG2PA, 0x100, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_BG2PB, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_BG2PC, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_BG2PD, 0x100, 0);
|
||||
cpu->memory.store32(cpu, BASE_IO | 0x28, 0, 0);
|
||||
cpu->memory.store32(cpu, BASE_IO | 0x2C, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_BG3PA, 0x100, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_BG3PB, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_BG3PC, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_BG3PD, 0x100, 0);
|
||||
cpu->memory.store32(cpu, BASE_IO | 0x38, 0, 0);
|
||||
cpu->memory.store32(cpu, BASE_IO | 0x3C, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x40, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x42, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x44, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x46, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x48, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x4A, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x4C, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x50, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x52, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x54, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0xB0, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0xB2, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0xB4, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0xB6, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0xB8, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0xBA, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0xBC, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0xBE, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0xC0, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0xC2, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0xC4, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0xC6, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0xC8, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0xCA, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0xCC, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0xCE, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0xD0, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0xD2, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0xD4, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0xD6, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0xD8, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0xDA, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0xDC, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0xDE, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x100, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x102, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x104, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x106, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x108, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x10A, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x10C, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x10E, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x200, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x202, 0xFFFF, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x204, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x208, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -110,13 +110,18 @@ void GBACheatDeviceCreate(struct GBACheatDevice* device) {
|
|||
}
|
||||
|
||||
void GBACheatDeviceDestroy(struct GBACheatDevice* device) {
|
||||
GBACheatDeviceClear(device);
|
||||
GBACheatSetsDeinit(&device->cheats);
|
||||
}
|
||||
|
||||
void GBACheatDeviceClear(struct GBACheatDevice* device) {
|
||||
size_t i;
|
||||
for (i = 0; i < GBACheatSetsSize(&device->cheats); ++i) {
|
||||
struct GBACheatSet* set = *GBACheatSetsGetPointer(&device->cheats, i);
|
||||
GBACheatSetDeinit(set);
|
||||
free(set);
|
||||
}
|
||||
GBACheatSetsDeinit(&device->cheats);
|
||||
GBACheatSetsClear(&device->cheats);
|
||||
}
|
||||
|
||||
void GBACheatSetInit(struct GBACheatSet* set, const char* name) {
|
||||
|
@ -260,6 +265,7 @@ bool GBACheatParseFile(struct GBACheatDevice* device, struct VFile* vf) {
|
|||
do {
|
||||
++i;
|
||||
} while (isspace((int) cheat[i]));
|
||||
cheat[strlen(cheat) - 1] = '\0'; // Remove trailing newline
|
||||
newSet = malloc(sizeof(*set));
|
||||
GBACheatSetInit(newSet, &cheat[i]);
|
||||
newSet->enabled = !nextDisabled;
|
||||
|
|
|
@ -192,6 +192,7 @@ struct VFile;
|
|||
|
||||
void GBACheatDeviceCreate(struct GBACheatDevice*);
|
||||
void GBACheatDeviceDestroy(struct GBACheatDevice*);
|
||||
void GBACheatDeviceClear(struct GBACheatDevice*);
|
||||
|
||||
void GBACheatSetInit(struct GBACheatSet*, const char* name);
|
||||
void GBACheatSetDeinit(struct GBACheatSet*);
|
||||
|
|
|
@ -75,12 +75,14 @@ void GBACheatReseedGameShark(uint32_t* seeds, uint16_t params, const uint8_t* t1
|
|||
}
|
||||
|
||||
void GBACheatSetGameSharkVersion(struct GBACheatSet* cheats, int version) {
|
||||
cheats->gsaVersion = 1;
|
||||
cheats->gsaVersion = version;
|
||||
switch (version) {
|
||||
case 1:
|
||||
case 2:
|
||||
memcpy(cheats->gsaSeeds, GBACheatGameSharkSeeds, 4 * sizeof(uint32_t));
|
||||
break;
|
||||
case 3:
|
||||
case 4:
|
||||
memcpy(cheats->gsaSeeds, GBACheatProActionReplaySeeds, 4 * sizeof(uint32_t));
|
||||
break;
|
||||
}
|
||||
|
@ -198,9 +200,11 @@ bool GBACheatAddGameShark(struct GBACheatSet* set, uint32_t op1, uint32_t op2) {
|
|||
switch (set->gsaVersion) {
|
||||
case 0:
|
||||
case 3:
|
||||
case 4:
|
||||
GBACheatSetGameSharkVersion(set, 1);
|
||||
// Fall through
|
||||
case 1:
|
||||
case 2:
|
||||
GBACheatDecryptGameShark(&o1, &o2, set->gsaSeeds);
|
||||
return GBACheatAddGameSharkRaw(set, o1, o2);
|
||||
}
|
||||
|
|
|
@ -298,9 +298,11 @@ bool GBACheatAddProActionReplay(struct GBACheatSet* set, uint32_t op1, uint32_t
|
|||
switch (set->gsaVersion) {
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
GBACheatSetGameSharkVersion(set, 3);
|
||||
// Fall through
|
||||
case 3:
|
||||
case 4:
|
||||
GBACheatDecryptGameShark(&o1, &o2, set->gsaSeeds);
|
||||
return GBACheatAddProActionReplayRaw(set, o1, o2);
|
||||
}
|
||||
|
|
|
@ -347,6 +347,11 @@ void GBAConfigMap(const struct GBAConfig* config, struct GBAOptions* opts) {
|
|||
_lookupIntValue(config, "width", &opts->width);
|
||||
_lookupIntValue(config, "height", &opts->height);
|
||||
|
||||
_lookupCharValue(config, "savegamePath", &opts->savegamePath);
|
||||
_lookupCharValue(config, "savestatePath", &opts->savestatePath);
|
||||
_lookupCharValue(config, "screenshotPath", &opts->screenshotPath);
|
||||
_lookupCharValue(config, "patchPath", &opts->patchPath);
|
||||
|
||||
char* idleOptimization = 0;
|
||||
if (_lookupCharValue(config, "idleOptimization", &idleOptimization)) {
|
||||
if (strcasecmp(idleOptimization, "ignore") == 0) {
|
||||
|
@ -409,6 +414,14 @@ struct Configuration* GBAConfigGetOverrides(struct GBAConfig* config) {
|
|||
void GBAConfigFreeOpts(struct GBAOptions* opts) {
|
||||
free(opts->bios);
|
||||
free(opts->shader);
|
||||
free(opts->savegamePath);
|
||||
free(opts->savestatePath);
|
||||
free(opts->screenshotPath);
|
||||
free(opts->patchPath);
|
||||
opts->bios = 0;
|
||||
opts->shader = 0;
|
||||
opts->savegamePath = 0;
|
||||
opts->savestatePath = 0;
|
||||
opts->screenshotPath = 0;
|
||||
opts->patchPath = 0;
|
||||
}
|
||||
|
|
|
@ -40,6 +40,11 @@ struct GBAOptions {
|
|||
bool suspendScreensaver;
|
||||
char* shader;
|
||||
|
||||
char* savegamePath;
|
||||
char* savestatePath;
|
||||
char* screenshotPath;
|
||||
char* patchPath;
|
||||
|
||||
int volume;
|
||||
bool mute;
|
||||
|
||||
|
|
|
@ -21,7 +21,9 @@ bool GBAContextInit(struct GBAContext* context, const char* port) {
|
|||
context->fname = 0;
|
||||
context->save = 0;
|
||||
context->renderer = 0;
|
||||
#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
|
||||
GBADirectorySetInit(&context->dirs);
|
||||
#endif
|
||||
memset(context->components, 0, sizeof(context->components));
|
||||
|
||||
if (!context->gba || !context->cpu) {
|
||||
|
@ -79,11 +81,17 @@ void GBAContextDeinit(struct GBAContext* context) {
|
|||
mappedMemoryFree(context->gba, 0);
|
||||
mappedMemoryFree(context->cpu, 0);
|
||||
GBAConfigDeinit(&context->config);
|
||||
#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
|
||||
GBADirectorySetDeinit(&context->dirs);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool GBAContextLoadROM(struct GBAContext* context, const char* path, bool autoloadSave) {
|
||||
#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
|
||||
context->rom = GBADirectorySetOpenPath(&context->dirs, path, GBAIsROM);
|
||||
#else
|
||||
context->rom = VFileOpen(path, O_RDONLY);
|
||||
#endif
|
||||
if (!context->rom) {
|
||||
return false;
|
||||
}
|
||||
|
@ -106,7 +114,9 @@ bool GBAContextLoadROM(struct GBAContext* context, const char* path, bool autolo
|
|||
|
||||
void GBAContextUnloadROM(struct GBAContext* context) {
|
||||
GBAUnloadROM(context->gba);
|
||||
#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
|
||||
GBADirectorySetDetachBase(&context->dirs);
|
||||
#endif
|
||||
if (context->rom) {
|
||||
context->rom->close(context->rom);
|
||||
context->rom = 0;
|
||||
|
|
|
@ -21,7 +21,9 @@ struct GBAContext {
|
|||
const char* fname;
|
||||
struct VFile* save;
|
||||
struct VFile* bios;
|
||||
#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
|
||||
struct GBADirectorySet dirs;
|
||||
#endif
|
||||
struct ARMComponent* components[GBA_COMPONENT_MAX];
|
||||
struct GBAConfig config;
|
||||
struct GBAOptions opts;
|
||||
|
|
|
@ -5,8 +5,10 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#include "directories.h"
|
||||
|
||||
#include "gba/context/config.h"
|
||||
#include "util/vfs.h"
|
||||
|
||||
#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
|
||||
void GBADirectorySetInit(struct GBADirectorySet* dirs) {
|
||||
dirs->base = 0;
|
||||
dirs->archive = 0;
|
||||
|
@ -99,3 +101,46 @@ struct VFile* GBADirectorySetOpenPath(struct GBADirectorySet* dirs, const char*
|
|||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
void GBADirectorySetMapOptions(struct GBADirectorySet* dirs, const struct GBAOptions* opts) {
|
||||
if (opts->savegamePath) {
|
||||
struct VDir* dir = VDirOpen(opts->savegamePath);
|
||||
if (dir) {
|
||||
if (dirs->save && dirs->save != dirs->base) {
|
||||
dirs->save->close(dirs->save);
|
||||
}
|
||||
dirs->save = dir;
|
||||
}
|
||||
}
|
||||
|
||||
if (opts->savestatePath) {
|
||||
struct VDir* dir = VDirOpen(opts->savestatePath);
|
||||
if (dir) {
|
||||
if (dirs->state && dirs->state != dirs->base) {
|
||||
dirs->state->close(dirs->state);
|
||||
}
|
||||
dirs->state = dir;
|
||||
}
|
||||
}
|
||||
|
||||
if (opts->screenshotPath) {
|
||||
struct VDir* dir = VDirOpen(opts->screenshotPath);
|
||||
if (dir) {
|
||||
if (dirs->screenshot && dirs->screenshot != dirs->base) {
|
||||
dirs->screenshot->close(dirs->screenshot);
|
||||
}
|
||||
dirs->screenshot = dir;
|
||||
}
|
||||
}
|
||||
|
||||
if (opts->patchPath) {
|
||||
struct VDir* dir = VDirOpen(opts->patchPath);
|
||||
if (dir) {
|
||||
if (dirs->patch && dirs->patch != dirs->base) {
|
||||
dirs->patch->close(dirs->patch);
|
||||
}
|
||||
dirs->patch = dir;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include "util/common.h"
|
||||
|
||||
#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
|
||||
struct VDir;
|
||||
|
||||
struct GBADirectorySet {
|
||||
|
@ -27,4 +28,8 @@ void GBADirectorySetDetachBase(struct GBADirectorySet* dirs);
|
|||
|
||||
struct VFile* GBADirectorySetOpenPath(struct GBADirectorySet* dirs, const char* path, bool (*filter)(struct VFile*));
|
||||
|
||||
struct GBAOptions;
|
||||
void GBADirectorySetMapOptions(struct GBADirectorySet* dirs, const struct GBAOptions* opts);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
@ -205,8 +205,19 @@ void GBASkipBIOS(struct GBA* gba) {
|
|||
}
|
||||
|
||||
static void GBAProcessEvents(struct ARMCore* cpu) {
|
||||
struct GBA* gba = (struct GBA*) cpu->master;
|
||||
|
||||
gba->bus = cpu->prefetch[1];
|
||||
if (cpu->executionMode == MODE_THUMB) {
|
||||
gba->bus |= cpu->prefetch[1] << 16;
|
||||
}
|
||||
|
||||
if (gba->springIRQ) {
|
||||
ARMRaiseIRQ(cpu);
|
||||
gba->springIRQ = 0;
|
||||
}
|
||||
|
||||
do {
|
||||
struct GBA* gba = (struct GBA*) cpu->master;
|
||||
int32_t cycles = cpu->nextEvent;
|
||||
int32_t nextEvent = INT_MAX;
|
||||
int32_t testEvent;
|
||||
|
@ -216,16 +227,6 @@ static void GBAProcessEvents(struct ARMCore* cpu) {
|
|||
}
|
||||
#endif
|
||||
|
||||
gba->bus = cpu->prefetch[1];
|
||||
if (cpu->executionMode == MODE_THUMB) {
|
||||
gba->bus |= cpu->prefetch[1] << 16;
|
||||
}
|
||||
|
||||
if (gba->springIRQ) {
|
||||
ARMRaiseIRQ(cpu);
|
||||
gba->springIRQ = 0;
|
||||
}
|
||||
|
||||
testEvent = GBAVideoProcessEvents(&gba->video, cycles);
|
||||
if (testEvent < nextEvent) {
|
||||
nextEvent = testEvent;
|
||||
|
|
|
@ -15,6 +15,10 @@
|
|||
#include "util/png-io.h"
|
||||
#include "util/vfs.h"
|
||||
|
||||
#ifdef _3DS
|
||||
#include <3ds.h>
|
||||
#endif
|
||||
|
||||
#include <sys/time.h>
|
||||
|
||||
#define FPS_GRANULARITY 120
|
||||
|
@ -237,6 +241,12 @@ void GBAGUIRun(struct GBAGUIRunner* runner, const char* path) {
|
|||
runner->lastFpsCheck = 1000000LL * tv.tv_sec + tv.tv_usec;
|
||||
|
||||
while (true) {
|
||||
#ifdef _3DS
|
||||
running = aptMainLoop();
|
||||
if (!running) {
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
uint32_t guiKeys;
|
||||
GUIPollInput(&runner->params, &guiKeys, 0);
|
||||
if (guiKeys & (1 << GUI_INPUT_CANCEL)) {
|
||||
|
|
|
@ -263,10 +263,18 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) {
|
|||
cpu->memory.activeRegion = memory->iwram;
|
||||
cpu->memory.activeMask = SIZE_WORKING_IRAM - 1;
|
||||
break;
|
||||
case REGION_PALETTE_RAM:
|
||||
cpu->memory.activeRegion = (uint32_t*) gba->video.palette;
|
||||
cpu->memory.activeMask = SIZE_PALETTE_RAM - 1;
|
||||
break;
|
||||
case REGION_VRAM:
|
||||
cpu->memory.activeRegion = (uint32_t*) gba->video.renderer->vram;
|
||||
cpu->memory.activeMask = 0x0000FFFF;
|
||||
break;
|
||||
case REGION_OAM:
|
||||
cpu->memory.activeRegion = (uint32_t*) gba->video.oam.raw;
|
||||
cpu->memory.activeMask = SIZE_OAM - 1;
|
||||
break;
|
||||
case REGION_CART0:
|
||||
case REGION_CART0_EX:
|
||||
case REGION_CART1:
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "serialize.h"
|
||||
|
||||
#include "gba/audio.h"
|
||||
#include "gba/cheats.h"
|
||||
#include "gba/io.h"
|
||||
#include "gba/rr/rr.h"
|
||||
#include "gba/supervisor/thread.h"
|
||||
|
@ -205,6 +206,14 @@ struct VFile* GBAGetState(struct GBA* gba, struct VDir* dir, int slot, bool writ
|
|||
return dir->openFile(dir, path, write ? (O_CREAT | O_TRUNC | O_RDWR) : O_RDONLY);
|
||||
}
|
||||
|
||||
void GBADeleteState(struct GBA* gba, struct VDir* dir, int slot) {
|
||||
char basename[PATH_MAX];
|
||||
separatePath(gba->activeFile, 0, basename, 0);
|
||||
char path[PATH_MAX];
|
||||
snprintf(path, sizeof(path), "%s.ss%i", basename, slot);
|
||||
dir->deleteFile(dir, path);
|
||||
}
|
||||
|
||||
#ifdef USE_PNG
|
||||
static bool _savePNGState(struct GBA* gba, struct VFile* vf, struct GBAExtdata* extdata) {
|
||||
unsigned stride;
|
||||
|
@ -426,6 +435,20 @@ bool GBASaveStateNamed(struct GBA* gba, struct VFile* vf, int flags) {
|
|||
}
|
||||
svf->close(svf);
|
||||
}
|
||||
struct VFile* cheatVf = 0;
|
||||
if (flags & SAVESTATE_CHEATS && gba->cpu->components && gba->cpu->components[GBA_COMPONENT_CHEAT_DEVICE]) {
|
||||
struct GBACheatDevice* device = (struct GBACheatDevice*) gba->cpu->components[GBA_COMPONENT_CHEAT_DEVICE];
|
||||
cheatVf = VFileMemChunk(0, 0);
|
||||
if (cheatVf) {
|
||||
GBACheatSaveFile(device, cheatVf);
|
||||
struct GBAExtdataItem item = {
|
||||
.size = cheatVf->size(cheatVf),
|
||||
.data = cheatVf->map(cheatVf, cheatVf->size(cheatVf), MAP_READ),
|
||||
.clean = 0
|
||||
};
|
||||
GBAExtdataPut(&extdata, EXTDATA_CHEATS, &item);
|
||||
}
|
||||
};
|
||||
#ifdef USE_PNG
|
||||
if (!(flags & SAVESTATE_SCREENSHOT)) {
|
||||
#else
|
||||
|
@ -435,6 +458,9 @@ bool GBASaveStateNamed(struct GBA* gba, struct VFile* vf, int flags) {
|
|||
struct GBASerializedState* state = vf->map(vf, sizeof(struct GBASerializedState), MAP_WRITE);
|
||||
if (!state) {
|
||||
GBAExtdataDeinit(&extdata);
|
||||
if (cheatVf) {
|
||||
cheatVf->close(cheatVf);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
GBASerialize(gba, state);
|
||||
|
@ -442,6 +468,9 @@ bool GBASaveStateNamed(struct GBA* gba, struct VFile* vf, int flags) {
|
|||
vf->seek(vf, sizeof(struct GBASerializedState), SEEK_SET);
|
||||
GBAExtdataSerialize(&extdata, vf);
|
||||
GBAExtdataDeinit(&extdata);
|
||||
if (cheatVf) {
|
||||
cheatVf->close(cheatVf);
|
||||
}
|
||||
return true;
|
||||
#ifdef USE_PNG
|
||||
}
|
||||
|
@ -461,6 +490,7 @@ struct GBASerializedState* GBAExtractState(struct VFile* vf, struct GBAExtdata*
|
|||
return _loadPNGState(vf, extdata);
|
||||
}
|
||||
#endif
|
||||
vf->seek(vf, 0, SEEK_SET);
|
||||
if (vf->size(vf) < (ssize_t) sizeof(struct GBASerializedState)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -501,6 +531,17 @@ bool GBALoadStateNamed(struct GBA* gba, struct VFile* vf, int flags) {
|
|||
svf->close(svf);
|
||||
}
|
||||
}
|
||||
if (flags & SAVESTATE_CHEATS && gba->cpu->components && gba->cpu->components[GBA_COMPONENT_CHEAT_DEVICE] && GBAExtdataGet(&extdata, EXTDATA_CHEATS, &item)) {
|
||||
if (item.size) {
|
||||
struct GBACheatDevice* device = (struct GBACheatDevice*) gba->cpu->components[GBA_COMPONENT_CHEAT_DEVICE];
|
||||
struct VFile* svf = VFileFromMemory(item.data, item.size);
|
||||
if (svf) {
|
||||
GBACheatDeviceClear(device);
|
||||
GBACheatParseFile(device, svf);
|
||||
svf->close(svf);
|
||||
}
|
||||
}
|
||||
}
|
||||
GBAExtdataDeinit(&extdata);
|
||||
return success;
|
||||
}
|
||||
|
|
|
@ -339,11 +339,13 @@ enum GBAExtdataTag {
|
|||
EXTDATA_NONE = 0,
|
||||
EXTDATA_SCREENSHOT = 1,
|
||||
EXTDATA_SAVEDATA = 2,
|
||||
EXTDATA_CHEATS = 3,
|
||||
EXTDATA_MAX
|
||||
};
|
||||
|
||||
#define SAVESTATE_SCREENSHOT 1
|
||||
#define SAVESTATE_SAVEDATA 2
|
||||
#define SAVESTATE_CHEATS 4
|
||||
|
||||
struct GBAExtdataItem {
|
||||
int32_t size;
|
||||
|
@ -364,6 +366,7 @@ bool GBADeserialize(struct GBA* gba, const struct GBASerializedState* state);
|
|||
bool GBASaveState(struct GBAThread* thread, struct VDir* dir, int slot, int flags);
|
||||
bool GBALoadState(struct GBAThread* thread, struct VDir* dir, int slot, int flags);
|
||||
struct VFile* GBAGetState(struct GBA* gba, struct VDir* dir, int slot, bool write);
|
||||
void GBADeleteState(struct GBA* thread, struct VDir* dir, int slot);
|
||||
|
||||
bool GBASaveStateNamed(struct GBA* gba, struct VFile* vf, int flags);
|
||||
bool GBALoadStateNamed(struct GBA* gba, struct VFile* vf, int flags);
|
||||
|
|
|
@ -401,6 +401,8 @@ void GBAMapOptionsToContext(const struct GBAOptions* opts, struct GBAThread* thr
|
|||
}
|
||||
|
||||
threadContext->idleOptimization = opts->idleOptimization;
|
||||
|
||||
GBADirectorySetMapOptions(&threadContext->dirs, opts);
|
||||
}
|
||||
|
||||
void GBAMapArgumentsToContext(const struct GBAArguments* args, struct GBAThread* threadContext) {
|
||||
|
@ -441,7 +443,6 @@ bool GBAThreadStart(struct GBAThread* threadContext) {
|
|||
return false;
|
||||
}
|
||||
|
||||
GBADirectorySetInit(&threadContext->dirs);
|
||||
_reloadDirectories(threadContext);
|
||||
|
||||
MutexInit(&threadContext->stateMutex);
|
||||
|
@ -571,8 +572,6 @@ void GBAThreadJoin(struct GBAThread* threadContext) {
|
|||
threadContext->patch->close(threadContext->patch);
|
||||
threadContext->patch = 0;
|
||||
}
|
||||
|
||||
GBADirectorySetDeinit(&threadContext->dirs);
|
||||
}
|
||||
|
||||
bool GBAThreadIsActive(struct GBAThread* threadContext) {
|
||||
|
|
|
@ -48,7 +48,9 @@ struct GBAThread {
|
|||
struct GBAVideoRenderer* renderer;
|
||||
struct GBASIODriverSet sioDrivers;
|
||||
struct ARMDebugger* debugger;
|
||||
#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
|
||||
struct GBADirectorySet dirs;
|
||||
#endif
|
||||
struct VFile* rom;
|
||||
struct VFile* save;
|
||||
struct VFile* bios;
|
||||
|
|
|
@ -45,6 +45,7 @@ static void _vd3dRewind(struct VDir* vd);
|
|||
static struct VDirEntry* _vd3dListNext(struct VDir* vd);
|
||||
static struct VFile* _vd3dOpenFile(struct VDir* vd, const char* path, int mode);
|
||||
static struct VDir* _vd3dOpenDir(struct VDir* vd, const char* path);
|
||||
static bool _vd3dDeleteFile(struct VDir* vd, const char* path);
|
||||
|
||||
static const char* _vd3deName(struct VDirEntry* vde);
|
||||
static enum VFSType _vd3deType(struct VDirEntry* vde);
|
||||
|
@ -191,6 +192,7 @@ struct VDir* VDirOpen(const char* path) {
|
|||
vd3d->d.listNext = _vd3dListNext;
|
||||
vd3d->d.openFile = _vd3dOpenFile;
|
||||
vd3d->d.openDir = _vd3dOpenDir;
|
||||
vd3d->d.deleteFile = _vd3dDeleteFile;
|
||||
|
||||
vd3d->vde.d.name = _vd3deName;
|
||||
vd3d->vde.d.type = _vd3deType;
|
||||
|
@ -257,6 +259,22 @@ static struct VDir* _vd3dOpenDir(struct VDir* vd, const char* path) {
|
|||
return vd2;
|
||||
}
|
||||
|
||||
static bool _vd3dDeleteFile(struct VDir* vd, const char* path) {
|
||||
struct VDir3DS* vd3d = (struct VDir3DS*) vd;
|
||||
if (!path) {
|
||||
return 0;
|
||||
}
|
||||
const char* dir = vd3d->path;
|
||||
char* combined = malloc(sizeof(char) * (strlen(path) + strlen(dir) + 2));
|
||||
sprintf(combined, "%s/%s", dir, path);
|
||||
|
||||
// TODO: Use UTF-16
|
||||
FS_Path newPath = fsMakePath(PATH_ASCII, combined);
|
||||
bool ret = !FSUSER_DeleteFile(sdmcArchive, newPath);
|
||||
free(combined);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const char* _vd3deName(struct VDirEntry* vde) {
|
||||
struct VDirEntry3DS* vd3de = (struct VDirEntry3DS*) vde;
|
||||
if (!vd3de->utf8Name[0]) {
|
||||
|
|
|
@ -92,6 +92,7 @@ AccessControlInfo:
|
|||
|
||||
MaxCpu : 0x9E # Default
|
||||
CpuSpeed : 804mhz
|
||||
EnableL2Cache : true
|
||||
|
||||
DisableDebug : true
|
||||
EnableForceDebug : false
|
||||
|
|
|
@ -70,6 +70,25 @@ unsigned GUIFontGlyphWidth(const struct GUIFont* font, uint32_t glyph) {
|
|||
return defaultFontMetrics[glyph].width;
|
||||
}
|
||||
|
||||
void GUIFontIconMetrics(const struct GUIFont* font, enum GUIIcon icon, unsigned* w, unsigned* h) {
|
||||
UNUSED(font);
|
||||
if (icon >= GUI_ICON_MAX) {
|
||||
if (w) {
|
||||
*w = 0;
|
||||
}
|
||||
if (h) {
|
||||
*h = 0;
|
||||
}
|
||||
} else {
|
||||
if (w) {
|
||||
*w = defaultIconMetrics[icon].width;
|
||||
}
|
||||
if (h) {
|
||||
*h = defaultIconMetrics[icon].height;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GUIFontDrawGlyph(const struct GUIFont* font, int glyph_x, int glyph_y, uint32_t color, uint32_t glyph) {
|
||||
ctrActivateTexture(&font->texture);
|
||||
|
||||
|
@ -124,3 +143,14 @@ void GUIFontDrawIcon(const struct GUIFont* font, int x, int y, enum GUIAlignment
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void GUIFontDrawIconSize(const struct GUIFont* font, int x, int y, int w, int h, uint32_t color, enum GUIIcon icon) {
|
||||
ctrActivateTexture(&font->icons);
|
||||
|
||||
if (icon >= GUI_ICON_MAX) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct GUIIconMetric metric = defaultIconMetrics[icon];
|
||||
ctrAddRectScaled(color, x, y, w ? w : metric.width, h ? h : metric.height, metric.x, metric.y, metric.width, metric.height);
|
||||
}
|
||||
|
|
|
@ -32,8 +32,9 @@ static enum ScreenMode {
|
|||
|
||||
#define _3DS_INPUT 0x3344534B
|
||||
|
||||
#define AUDIO_SAMPLES 0x80
|
||||
#define AUDIO_SAMPLE_BUFFER (AUDIO_SAMPLES * 24)
|
||||
#define AUDIO_SAMPLES 384
|
||||
#define AUDIO_SAMPLE_BUFFER (AUDIO_SAMPLES * 16)
|
||||
#define DSP_BUFFERS 4
|
||||
|
||||
FS_Archive sdmcArchive;
|
||||
|
||||
|
@ -43,7 +44,12 @@ static struct GBA3DSRotationSource {
|
|||
angularRate gyro;
|
||||
} rotation;
|
||||
|
||||
static bool hasSound;
|
||||
static enum {
|
||||
NO_SOUND,
|
||||
DSP_SUPPORTED,
|
||||
CSND_SUPPORTED
|
||||
} hasSound;
|
||||
|
||||
// TODO: Move into context
|
||||
static struct GBAVideoSoftwareRenderer renderer;
|
||||
static struct GBAAVStream stream;
|
||||
|
@ -53,6 +59,10 @@ static size_t audioPos = 0;
|
|||
static struct ctrTexture gbaOutputTexture;
|
||||
static int guiDrawn;
|
||||
static int screenCleanup;
|
||||
static ndspWaveBuf dspBuffer[DSP_BUFFERS];
|
||||
static int bufferId = 0;
|
||||
|
||||
static aptHookCookie cookie;
|
||||
|
||||
enum {
|
||||
GUI_ACTIVE = 1,
|
||||
|
@ -70,12 +80,65 @@ enum {
|
|||
|
||||
extern bool allocateRomBuffer(void);
|
||||
|
||||
static void _cleanup(void) {
|
||||
if (renderer.outputBuffer) {
|
||||
linearFree(renderer.outputBuffer);
|
||||
}
|
||||
|
||||
if (gbaOutputTexture.data) {
|
||||
ctrDeinitGpu();
|
||||
vramFree(gbaOutputTexture.data);
|
||||
}
|
||||
|
||||
gfxExit();
|
||||
|
||||
if (hasSound != NO_SOUND) {
|
||||
linearFree(audioLeft);
|
||||
}
|
||||
|
||||
if (hasSound == CSND_SUPPORTED) {
|
||||
linearFree(audioRight);
|
||||
csndExit();
|
||||
}
|
||||
|
||||
if (hasSound == DSP_SUPPORTED) {
|
||||
ndspExit();
|
||||
}
|
||||
|
||||
csndExit();
|
||||
ptmuExit();
|
||||
}
|
||||
|
||||
static void _aptHook(APT_HookType hook, void* user) {
|
||||
UNUSED(user);
|
||||
switch (hook) {
|
||||
case APTHOOK_ONSUSPEND:
|
||||
case APTHOOK_ONSLEEP:
|
||||
if (hasSound == CSND_SUPPORTED) {
|
||||
CSND_SetPlayState(8, 0);
|
||||
CSND_SetPlayState(9, 0);
|
||||
csndExecCmds(false);
|
||||
}
|
||||
break;
|
||||
case APTHOOK_ONEXIT:
|
||||
if (hasSound == CSND_SUPPORTED) {
|
||||
CSND_SetPlayState(8, 0);
|
||||
CSND_SetPlayState(9, 0);
|
||||
csndExecCmds(false);
|
||||
}
|
||||
_cleanup();
|
||||
exit(0);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void _map3DSKey(struct GBAInputMap* map, int ctrKey, enum GBAKey key) {
|
||||
GBAInputBindKey(map, _3DS_INPUT, __builtin_ctz(ctrKey), key);
|
||||
}
|
||||
|
||||
static void _csndPlaySound(u32 flags, u32 sampleRate, float vol, void* left, void* right, u32 size)
|
||||
{
|
||||
static void _csndPlaySound(u32 flags, u32 sampleRate, float vol, void* left, void* right, u32 size) {
|
||||
u32 pleft = 0, pright = 0;
|
||||
|
||||
int loopMode = (flags >> 10) & 3;
|
||||
|
@ -187,7 +250,7 @@ static void _guiFinish(void) {
|
|||
|
||||
static void _setup(struct GBAGUIRunner* runner) {
|
||||
runner->context.gba->rotationSource = &rotation.d;
|
||||
if (hasSound) {
|
||||
if (hasSound != NO_SOUND) {
|
||||
runner->context.gba->stream = &stream;
|
||||
}
|
||||
|
||||
|
@ -229,12 +292,16 @@ static void _gameLoaded(struct GBAGUIRunner* runner) {
|
|||
blip_set_rates(runner->context.gba->audio.left, GBA_ARM7TDMI_FREQUENCY, 32768 * ratio);
|
||||
blip_set_rates(runner->context.gba->audio.right, GBA_ARM7TDMI_FREQUENCY, 32768 * ratio);
|
||||
#endif
|
||||
if (hasSound) {
|
||||
if (hasSound != NO_SOUND) {
|
||||
audioPos = 0;
|
||||
}
|
||||
if (hasSound == CSND_SUPPORTED) {
|
||||
memset(audioLeft, 0, AUDIO_SAMPLE_BUFFER * sizeof(int16_t));
|
||||
memset(audioRight, 0, AUDIO_SAMPLE_BUFFER * sizeof(int16_t));
|
||||
audioPos = 0;
|
||||
_csndPlaySound(SOUND_REPEAT | SOUND_FORMAT_16BIT, 32768, 1.0, audioLeft, audioRight, AUDIO_SAMPLE_BUFFER * sizeof(int16_t));
|
||||
csndExecCmds(false);
|
||||
} else if (hasSound == DSP_SUPPORTED) {
|
||||
memset(audioLeft, 0, AUDIO_SAMPLE_BUFFER * 2 * sizeof(int16_t));
|
||||
}
|
||||
unsigned mode;
|
||||
if (GBAConfigGetUIntValue(&runner->context.config, "screenMode", &mode) && mode != screenMode) {
|
||||
|
@ -244,7 +311,7 @@ static void _gameLoaded(struct GBAGUIRunner* runner) {
|
|||
}
|
||||
|
||||
static void _gameUnloaded(struct GBAGUIRunner* runner) {
|
||||
if (hasSound) {
|
||||
if (hasSound == CSND_SUPPORTED) {
|
||||
CSND_SetPlayState(8, 0);
|
||||
CSND_SetPlayState(9, 0);
|
||||
csndExecCmds(false);
|
||||
|
@ -311,7 +378,7 @@ static void _drawFrame(struct GBAGUIRunner* runner, bool faded) {
|
|||
GX_TRANSFER_OUT_TILED(1) | GX_TRANSFER_FLIP_VERT(1));
|
||||
|
||||
#if RESAMPLE_LIBRARY == RESAMPLE_BLIP_BUF
|
||||
if (!hasSound) {
|
||||
if (hasSound == NO_SOUND) {
|
||||
blip_clear(runner->context.gba->audio.left);
|
||||
blip_clear(runner->context.gba->audio.right);
|
||||
}
|
||||
|
@ -446,23 +513,45 @@ static int32_t _readGyroZ(struct GBARotationSource* source) {
|
|||
|
||||
static void _postAudioBuffer(struct GBAAVStream* stream, struct GBAAudio* audio) {
|
||||
UNUSED(stream);
|
||||
if (hasSound == CSND_SUPPORTED) {
|
||||
#if RESAMPLE_LIBRARY == RESAMPLE_BLIP_BUF
|
||||
blip_read_samples(audio->left, &audioLeft[audioPos], AUDIO_SAMPLES, false);
|
||||
blip_read_samples(audio->right, &audioRight[audioPos], AUDIO_SAMPLES, false);
|
||||
blip_read_samples(audio->left, &audioLeft[audioPos], AUDIO_SAMPLES, false);
|
||||
blip_read_samples(audio->right, &audioRight[audioPos], AUDIO_SAMPLES, false);
|
||||
#elif RESAMPLE_LIBRARY == RESAMPLE_NN
|
||||
GBAAudioCopy(audio, &audioLeft[audioPos], &audioRight[audioPos], AUDIO_SAMPLES);
|
||||
GBAAudioCopy(audio, &audioLeft[audioPos], &audioRight[audioPos], AUDIO_SAMPLES);
|
||||
#endif
|
||||
GSPGPU_FlushDataCache(&audioLeft[audioPos], AUDIO_SAMPLES * sizeof(int16_t));
|
||||
GSPGPU_FlushDataCache(&audioRight[audioPos], AUDIO_SAMPLES * sizeof(int16_t));
|
||||
audioPos = (audioPos + AUDIO_SAMPLES) % AUDIO_SAMPLE_BUFFER;
|
||||
if (audioPos == AUDIO_SAMPLES * 3) {
|
||||
u8 playing = 0;
|
||||
csndIsPlaying(0x8, &playing);
|
||||
if (!playing) {
|
||||
CSND_SetPlayState(0x8, 1);
|
||||
CSND_SetPlayState(0x9, 1);
|
||||
csndExecCmds(false);
|
||||
GSPGPU_FlushDataCache(&audioLeft[audioPos], AUDIO_SAMPLES * sizeof(int16_t));
|
||||
GSPGPU_FlushDataCache(&audioRight[audioPos], AUDIO_SAMPLES * sizeof(int16_t));
|
||||
audioPos = (audioPos + AUDIO_SAMPLES) % AUDIO_SAMPLE_BUFFER;
|
||||
if (audioPos == AUDIO_SAMPLES * 3) {
|
||||
u8 playing = 0;
|
||||
csndIsPlaying(0x8, &playing);
|
||||
if (!playing) {
|
||||
CSND_SetPlayState(0x8, 1);
|
||||
CSND_SetPlayState(0x9, 1);
|
||||
csndExecCmds(false);
|
||||
}
|
||||
}
|
||||
} else if (hasSound == DSP_SUPPORTED) {
|
||||
int startId = bufferId;
|
||||
while (dspBuffer[bufferId].status == NDSP_WBUF_QUEUED || dspBuffer[bufferId].status == NDSP_WBUF_PLAYING) {
|
||||
bufferId = (bufferId + 1) & (DSP_BUFFERS - 1);
|
||||
if (bufferId == startId) {
|
||||
blip_clear(audio->left);
|
||||
blip_clear(audio->right);
|
||||
return;
|
||||
}
|
||||
}
|
||||
void* tmpBuf = dspBuffer[bufferId].data_pcm16;
|
||||
memset(&dspBuffer[bufferId], 0, sizeof(dspBuffer[bufferId]));
|
||||
dspBuffer[bufferId].data_pcm16 = tmpBuf;
|
||||
dspBuffer[bufferId].nsamples = AUDIO_SAMPLES;
|
||||
#if RESAMPLE_LIBRARY == RESAMPLE_BLIP_BUF
|
||||
blip_read_samples(audio->left, dspBuffer[bufferId].data_pcm16, AUDIO_SAMPLES, true);
|
||||
blip_read_samples(audio->right, dspBuffer[bufferId].data_pcm16 + 1, AUDIO_SAMPLES, true);
|
||||
#endif
|
||||
DSP_FlushDataCache(dspBuffer[bufferId].data_pcm16, AUDIO_SAMPLES * 2 * sizeof(int16_t));
|
||||
ndspChnWaveBufAdd(0, &dspBuffer[bufferId]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -480,10 +569,30 @@ int main() {
|
|||
return 1;
|
||||
}
|
||||
|
||||
ptmuInit();
|
||||
hasSound = !csndInit();
|
||||
aptHook(&cookie, _aptHook, 0);
|
||||
|
||||
if (hasSound) {
|
||||
ptmuInit();
|
||||
hasSound = NO_SOUND;
|
||||
if (!ndspInit()) {
|
||||
hasSound = DSP_SUPPORTED;
|
||||
ndspSetOutputMode(NDSP_OUTPUT_STEREO);
|
||||
ndspSetOutputCount(1);
|
||||
ndspChnReset(0);
|
||||
ndspChnSetFormat(0, NDSP_FORMAT_STEREO_PCM16);
|
||||
ndspChnSetInterp(0, NDSP_INTERP_NONE);
|
||||
ndspChnSetRate(0, 0x8000);
|
||||
ndspChnWaveBufClear(0);
|
||||
audioLeft = linearMemAlign(AUDIO_SAMPLES * DSP_BUFFERS * 2 * sizeof(int16_t), 0x80);
|
||||
memset(dspBuffer, 0, sizeof(dspBuffer));
|
||||
int i;
|
||||
for (i = 0; i < DSP_BUFFERS; ++i) {
|
||||
dspBuffer[i].data_pcm16 = &audioLeft[AUDIO_SAMPLES * i * 2];
|
||||
dspBuffer[i].nsamples = AUDIO_SAMPLES;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasSound == NO_SOUND && !csndInit()) {
|
||||
hasSound = CSND_SUPPORTED;
|
||||
audioLeft = linearMemAlign(AUDIO_SAMPLE_BUFFER * sizeof(int16_t), 0x80);
|
||||
audioRight = linearMemAlign(AUDIO_SAMPLE_BUFFER * sizeof(int16_t), 0x80);
|
||||
}
|
||||
|
@ -492,7 +601,8 @@ int main() {
|
|||
|
||||
if (ctrInitGpu() < 0) {
|
||||
gbaOutputTexture.data = 0;
|
||||
goto cleanup;
|
||||
_cleanup();
|
||||
return 1;
|
||||
}
|
||||
|
||||
ctrTexture_Init(&gbaOutputTexture);
|
||||
|
@ -504,7 +614,8 @@ int main() {
|
|||
void* outputTextureEnd = (u8*)gbaOutputTexture.data + 256 * 256 * 2;
|
||||
|
||||
if (!gbaOutputTexture.data) {
|
||||
goto cleanup;
|
||||
_cleanup();
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Zero texture data to make sure no garbage around the border interferes with filtering
|
||||
|
@ -523,7 +634,8 @@ int main() {
|
|||
struct GUIFont* font = GUIFontCreate();
|
||||
|
||||
if (!font) {
|
||||
goto cleanup;
|
||||
_cleanup();
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct GBAGUIRunner runner = {
|
||||
|
@ -610,24 +722,6 @@ int main() {
|
|||
GBAGUIRunloop(&runner);
|
||||
GBAGUIDeinit(&runner);
|
||||
|
||||
cleanup:
|
||||
if (renderer.outputBuffer) {
|
||||
linearFree(renderer.outputBuffer);
|
||||
}
|
||||
|
||||
if (gbaOutputTexture.data) {
|
||||
ctrDeinitGpu();
|
||||
vramFree(gbaOutputTexture.data);
|
||||
}
|
||||
|
||||
gfxExit();
|
||||
|
||||
if (hasSound) {
|
||||
linearFree(audioLeft);
|
||||
linearFree(audioRight);
|
||||
}
|
||||
|
||||
csndExit();
|
||||
ptmuExit();
|
||||
_cleanup();
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -36,7 +36,6 @@
|
|||
static const struct option _options[] = {
|
||||
{ "bios", required_argument, 0, 'b' },
|
||||
{ "cheats", required_argument, 0, 'c' },
|
||||
{ "dirmode", required_argument, 0, 'D' },
|
||||
{ "frameskip", required_argument, 0, 's' },
|
||||
#ifdef USE_CLI_DEBUGGER
|
||||
{ "debug", no_argument, 0, 'd' },
|
||||
|
@ -56,7 +55,7 @@ static bool _parseGraphicsArg(struct SubParser* parser, struct GBAConfig* config
|
|||
bool parseArguments(struct GBAArguments* opts, struct GBAConfig* config, int argc, char* const* argv, struct SubParser* subparser) {
|
||||
int ch;
|
||||
char options[64] =
|
||||
"b:c:Dhl:p:s:v:"
|
||||
"b:c:hl:p:s:v:"
|
||||
#ifdef USE_CLI_DEBUGGER
|
||||
"d"
|
||||
#endif
|
||||
|
@ -86,9 +85,6 @@ bool parseArguments(struct GBAArguments* opts, struct GBAConfig* config, int arg
|
|||
case 'c':
|
||||
opts->cheatsFile = strdup(optarg);
|
||||
break;
|
||||
case 'D':
|
||||
opts->dirmode = true;
|
||||
break;
|
||||
#ifdef USE_CLI_DEBUGGER
|
||||
case 'd':
|
||||
if (opts->debuggerType != DEBUGGER_NONE) {
|
||||
|
|
|
@ -25,7 +25,6 @@ struct GBAArguments {
|
|||
char* fname;
|
||||
char* patch;
|
||||
char* cheatsFile;
|
||||
bool dirmode;
|
||||
char* movie;
|
||||
|
||||
enum DebuggerType debuggerType;
|
||||
|
|
|
@ -32,6 +32,8 @@
|
|||
<integer>0</integer>
|
||||
<key>OEGameCoreSupportsRewinding</key>
|
||||
<true/>
|
||||
<key>OEGameCoreSupportsCheatCode</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>OEGameCorePlayerCount</key>
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "util/common.h"
|
||||
|
||||
#include "gba/cheats.h"
|
||||
#include "gba/cheats/gameshark.h"
|
||||
#include "gba/renderers/video-software.h"
|
||||
#include "gba/serialize.h"
|
||||
#include "gba/context/context.h"
|
||||
|
@ -45,7 +46,7 @@
|
|||
struct GBAContext context;
|
||||
struct GBAVideoSoftwareRenderer renderer;
|
||||
struct GBACheatDevice cheats;
|
||||
struct GBACheatSet cheatSet;
|
||||
NSMutableDictionary *cheatSets;
|
||||
uint16_t keys;
|
||||
}
|
||||
@end
|
||||
|
@ -70,8 +71,7 @@
|
|||
GBAAudioResizeBuffer(&context.gba->audio, SAMPLES);
|
||||
GBACheatDeviceCreate(&cheats);
|
||||
GBACheatAttachDevice(context.gba, &cheats);
|
||||
GBACheatSetInit(&cheatSet, "openemu");
|
||||
GBACheatAddSet(&cheats, &cheatSet);
|
||||
cheatSets = [[NSMutableDictionary alloc] init];
|
||||
keys = 0;
|
||||
}
|
||||
|
||||
|
@ -81,9 +81,18 @@
|
|||
- (void)dealloc
|
||||
{
|
||||
GBAContextDeinit(&context);
|
||||
GBACheatRemoveSet(&cheats, &cheatSet);
|
||||
[cheatSets enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
|
||||
UNUSED(key);
|
||||
UNUSED(stop);
|
||||
GBACheatRemoveSet(&cheats, [obj pointerValue]);
|
||||
}];
|
||||
GBACheatDeviceDestroy(&cheats);
|
||||
GBACheatSetDeinit(&cheatSet);
|
||||
[cheatSets enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
|
||||
UNUSED(key);
|
||||
UNUSED(stop);
|
||||
GBACheatSetDeinit([obj pointerValue]);
|
||||
}];
|
||||
[cheatSets release];
|
||||
free(renderer.outputBuffer);
|
||||
|
||||
[super dealloc];
|
||||
|
@ -275,5 +284,33 @@ const int GBAMap[] = {
|
|||
keys &= ~(1 << GBAMap[button]);
|
||||
}
|
||||
|
||||
#pragma mark - Cheats
|
||||
|
||||
- (void)setCheat:(NSString *)code setType:(NSString *)type setEnabled:(BOOL)enabled
|
||||
{
|
||||
code = [code stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
|
||||
code = [code stringByReplacingOccurrencesOfString:@" " withString:@""];
|
||||
|
||||
NSString *codeId = [code stringByAppendingFormat:@"/%@", type];
|
||||
struct GBACheatSet* cheatSet = [[cheatSets objectForKey:codeId] pointerValue];
|
||||
if (cheatSet) {
|
||||
cheatSet->enabled = enabled;
|
||||
return;
|
||||
}
|
||||
cheatSet = malloc(sizeof(*cheatSet));
|
||||
GBACheatSetInit(cheatSet, [codeId UTF8String]);
|
||||
if ([type isEqual:@"GameShark"]) {
|
||||
GBACheatSetGameSharkVersion(cheatSet, 1);
|
||||
} else if ([type isEqual:@"Action Replay"]) {
|
||||
GBACheatSetGameSharkVersion(cheatSet, 3);
|
||||
}
|
||||
NSArray *codeSet = [code componentsSeparatedByString:@"+"];
|
||||
for (id c in codeSet) {
|
||||
GBACheatAddLine(cheatSet, [c UTF8String]);
|
||||
}
|
||||
cheatSet->enabled = enabled;
|
||||
[cheatSets setObject:[NSValue valueWithPointer:cheatSet] forKey:codeId];
|
||||
GBACheatAddSet(&cheats, cheatSet);
|
||||
}
|
||||
@end
|
||||
|
||||
|
|
|
@ -99,12 +99,12 @@ void GBAGLContextPostFrame(struct VideoBackend* v, const void* frame) {
|
|||
glBindTexture(GL_TEXTURE_2D, context->tex);
|
||||
#ifdef COLOR_16_BIT
|
||||
#ifdef COLOR_5_6_5
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, frame);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, frame);
|
||||
#else
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, frame);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, frame);
|
||||
#endif
|
||||
#else
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, frame);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, GL_RGBA, GL_UNSIGNED_BYTE, frame);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,13 @@
|
|||
|
||||
#define MAX_PASSES 8
|
||||
|
||||
static const GLchar* const _gles2Header =
|
||||
"#version 100\n"
|
||||
"precision mediump float;\n";
|
||||
|
||||
static const GLchar* const _gl3Header =
|
||||
"#version 120\n";
|
||||
|
||||
static const char* const _vertexShader =
|
||||
"attribute vec4 position;\n"
|
||||
"varying vec2 texCoord;\n"
|
||||
|
@ -35,12 +42,15 @@ static const char* const _fragmentShader =
|
|||
"varying vec2 texCoord;\n"
|
||||
"uniform sampler2D tex;\n"
|
||||
"uniform float gamma;\n"
|
||||
"uniform vec3 desaturation;\n"
|
||||
"uniform vec3 scale;\n"
|
||||
"uniform vec3 bias;\n"
|
||||
|
||||
"void main() {\n"
|
||||
" vec4 color = texture2D(tex, texCoord);\n"
|
||||
" color.a = 1.;\n"
|
||||
" float average = dot(color.rgb, vec3(1.)) / 3.;\n"
|
||||
" color.rgb = mix(color.rgb, vec3(average), desaturation);\n"
|
||||
" color.rgb = scale * pow(color.rgb, vec3(gamma, gamma, gamma)) + bias;\n"
|
||||
" gl_FragColor = color;\n"
|
||||
"}";
|
||||
|
@ -82,7 +92,7 @@ static void GBAGLES2ContextInit(struct VideoBackend* v, WHandle handle) {
|
|||
|
||||
glClearColor(0.f, 0.f, 0.f, 1.f);
|
||||
|
||||
struct GBAGLES2Uniform* uniforms = malloc(sizeof(struct GBAGLES2Uniform) * 3);
|
||||
struct GBAGLES2Uniform* uniforms = malloc(sizeof(struct GBAGLES2Uniform) * 4);
|
||||
uniforms[0].name = "gamma";
|
||||
uniforms[0].readableName = "Gamma";
|
||||
uniforms[0].type = GL_FLOAT;
|
||||
|
@ -113,8 +123,20 @@ static void GBAGLES2ContextInit(struct VideoBackend* v, WHandle handle) {
|
|||
uniforms[2].max.fvec3[0] = 1.0f;
|
||||
uniforms[2].max.fvec3[1] = 1.0f;
|
||||
uniforms[2].max.fvec3[2] = 1.0f;
|
||||
GBAGLES2ShaderInit(&context->initialShader, _vertexShader, _fragmentShader, -1, -1, uniforms, 3);
|
||||
GBAGLES2ShaderInit(&context->finalShader, 0, 0, 0, 0, 0, 0);
|
||||
uniforms[3].name = "desaturation";
|
||||
uniforms[3].readableName = "Desaturation";
|
||||
uniforms[3].type = GL_FLOAT_VEC3;
|
||||
uniforms[3].value.fvec3[0] = 0.0f;
|
||||
uniforms[3].value.fvec3[1] = 0.0f;
|
||||
uniforms[3].value.fvec3[2] = 0.0f;
|
||||
uniforms[3].min.fvec3[0] = 0.0f;
|
||||
uniforms[3].min.fvec3[1] = 0.0f;
|
||||
uniforms[3].min.fvec3[2] = 0.0f;
|
||||
uniforms[3].max.fvec3[0] = 1.0f;
|
||||
uniforms[3].max.fvec3[1] = 1.0f;
|
||||
uniforms[3].max.fvec3[2] = 1.0f;
|
||||
GBAGLES2ShaderInit(&context->initialShader, _vertexShader, _fragmentShader, -1, -1, false, uniforms, 4);
|
||||
GBAGLES2ShaderInit(&context->finalShader, 0, 0, 0, 0, false, 0, 0);
|
||||
glDeleteFramebuffers(1, &context->finalShader.fbo);
|
||||
context->finalShader.fbo = 0;
|
||||
}
|
||||
|
@ -173,6 +195,12 @@ void _drawShader(struct GBAGLES2Shader* shader) {
|
|||
drawH = viewport[3];
|
||||
padH = viewport[1];
|
||||
}
|
||||
if (shader->integerScaling) {
|
||||
padW = 0;
|
||||
padH = 0;
|
||||
drawW -= drawW % VIDEO_HORIZONTAL_PIXELS;
|
||||
drawH -= drawH % VIDEO_VERTICAL_PIXELS;
|
||||
}
|
||||
glViewport(padW, padH, drawW, drawH);
|
||||
if (!shader->width || !shader->height) {
|
||||
GLint oldTex;
|
||||
|
@ -182,7 +210,7 @@ void _drawShader(struct GBAGLES2Shader* shader) {
|
|||
glBindTexture(GL_TEXTURE_2D, oldTex);
|
||||
}
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, shader->filter ? GL_LINEAR : GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, shader->filter ? GL_LINEAR : GL_NEAREST);
|
||||
glUseProgram(shader->program);
|
||||
glUniform1i(shader->texLocation, 0);
|
||||
|
@ -263,7 +291,6 @@ void GBAGLES2ContextDrawFrame(struct VideoBackend* v) {
|
|||
void GBAGLES2ContextPostFrame(struct VideoBackend* v, const void* frame) {
|
||||
struct GBAGLES2Context* context = (struct GBAGLES2Context*) v;
|
||||
glBindTexture(GL_TEXTURE_2D, context->tex);
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, 256);
|
||||
#ifdef COLOR_16_BIT
|
||||
#ifdef COLOR_5_6_5
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, frame);
|
||||
|
@ -273,7 +300,6 @@ void GBAGLES2ContextPostFrame(struct VideoBackend* v, const void* frame) {
|
|||
#else
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, 0, GL_RGBA, GL_UNSIGNED_BYTE, frame);
|
||||
#endif
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||
}
|
||||
|
||||
void GBAGLES2ContextCreate(struct GBAGLES2Context* context) {
|
||||
|
@ -290,9 +316,10 @@ void GBAGLES2ContextCreate(struct GBAGLES2Context* context) {
|
|||
context->nShaders = 0;
|
||||
}
|
||||
|
||||
void GBAGLES2ShaderInit(struct GBAGLES2Shader* shader, const char* vs, const char* fs, int width, int height, struct GBAGLES2Uniform* uniforms, size_t nUniforms) {
|
||||
void GBAGLES2ShaderInit(struct GBAGLES2Shader* shader, const char* vs, const char* fs, int width, int height, bool integerScaling, struct GBAGLES2Uniform* uniforms, size_t nUniforms) {
|
||||
shader->width = width >= 0 ? width : VIDEO_HORIZONTAL_PIXELS;
|
||||
shader->height = height >= 0 ? height : VIDEO_VERTICAL_PIXELS;
|
||||
shader->integerScaling = integerScaling;
|
||||
shader->filter = false;
|
||||
shader->blend = false;
|
||||
shader->uniforms = uniforms;
|
||||
|
@ -314,16 +341,27 @@ void GBAGLES2ShaderInit(struct GBAGLES2Shader* shader, const char* vs, const cha
|
|||
shader->program = glCreateProgram();
|
||||
shader->vertexShader = glCreateShader(GL_VERTEX_SHADER);
|
||||
shader->fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
|
||||
const GLchar* shaderBuffer[2];
|
||||
const GLubyte* version = glGetString(GL_VERSION);
|
||||
if (strncmp((const char*) version, "OpenGL ES ", strlen("OpenGL ES "))) {
|
||||
shaderBuffer[0] = _gl3Header;
|
||||
} else {
|
||||
shaderBuffer[0] = _gles2Header;
|
||||
}
|
||||
if (vs) {
|
||||
glShaderSource(shader->vertexShader, 1, (const GLchar**) &vs, 0);
|
||||
shaderBuffer[1] = vs;
|
||||
} else {
|
||||
glShaderSource(shader->vertexShader, 1, (const GLchar**) &_nullVertexShader, 0);
|
||||
shaderBuffer[1] = _nullVertexShader;
|
||||
}
|
||||
glShaderSource(shader->vertexShader, 2, shaderBuffer, 0);
|
||||
|
||||
if (fs) {
|
||||
glShaderSource(shader->fragmentShader, 1, (const GLchar**) &fs, 0);
|
||||
shaderBuffer[1] = fs;
|
||||
} else {
|
||||
glShaderSource(shader->fragmentShader, 1, (const GLchar**) &_nullFragmentShader, 0);
|
||||
shaderBuffer[1] = _nullFragmentShader;
|
||||
}
|
||||
glShaderSource(shader->fragmentShader, 2, shaderBuffer, 0);
|
||||
|
||||
glAttachShader(shader->program, shader->vertexShader);
|
||||
glAttachShader(shader->program, shader->fragmentShader);
|
||||
char log[1024];
|
||||
|
@ -771,8 +809,10 @@ bool GBAGLES2ShaderLoad(struct VideoShader* shader, struct VDir* dir) {
|
|||
}
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
int scaling = 0;
|
||||
_lookupIntValue(&description, passName, "width", &width);
|
||||
_lookupIntValue(&description, passName, "height", &height);
|
||||
_lookupIntValue(&description, passName, "integerScaling", &scaling);
|
||||
|
||||
struct GBAGLES2UniformList uniformVector;
|
||||
GBAGLES2UniformListInit(&uniformVector, 0);
|
||||
|
@ -790,7 +830,7 @@ bool GBAGLES2ShaderLoad(struct VideoShader* shader, struct VDir* dir) {
|
|||
memcpy(uniformBlock, GBAGLES2UniformListGetPointer(&uniformVector, 0), sizeof(*uniformBlock) * u);
|
||||
GBAGLES2UniformListDeinit(&uniformVector);
|
||||
|
||||
GBAGLES2ShaderInit(&shaderBlock[n], vssrc, fssrc, width, height, uniformBlock, u);
|
||||
GBAGLES2ShaderInit(&shaderBlock[n], vssrc, fssrc, width, height, scaling, uniformBlock, u);
|
||||
int b = 0;
|
||||
_lookupIntValue(&description, passName, "blend", &b);
|
||||
if (b) {
|
||||
|
|
|
@ -53,6 +53,7 @@ struct GBAGLES2Uniform {
|
|||
struct GBAGLES2Shader {
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
bool integerScaling;
|
||||
bool filter;
|
||||
bool blend;
|
||||
GLuint tex;
|
||||
|
@ -83,7 +84,7 @@ struct GBAGLES2Context {
|
|||
|
||||
void GBAGLES2ContextCreate(struct GBAGLES2Context*);
|
||||
|
||||
void GBAGLES2ShaderInit(struct GBAGLES2Shader*, const char* vs, const char* fs, int width, int height, struct GBAGLES2Uniform* uniforms, size_t nUniforms);
|
||||
void GBAGLES2ShaderInit(struct GBAGLES2Shader*, const char* vs, const char* fs, int width, int height, bool integerScaling, struct GBAGLES2Uniform* uniforms, size_t nUniforms);
|
||||
void GBAGLES2ShaderDeinit(struct GBAGLES2Shader*);
|
||||
void GBAGLES2ShaderAttach(struct GBAGLES2Context*, struct GBAGLES2Shader*, size_t nShaders);
|
||||
void GBAGLES2ShaderDetach(struct GBAGLES2Context*);
|
||||
|
|
|
@ -49,17 +49,36 @@ unsigned GUIFontGlyphWidth(const struct GUIFont* font, uint32_t glyph) {
|
|||
return defaultFontMetrics[glyph].width * 2;
|
||||
}
|
||||
|
||||
void GUIFontIconMetrics(const struct GUIFont* font, enum GUIIcon icon, unsigned* w, unsigned* h) {
|
||||
UNUSED(font);
|
||||
if (icon >= GUI_ICON_MAX) {
|
||||
if (w) {
|
||||
*w = 0;
|
||||
}
|
||||
if (h) {
|
||||
*h = 0;
|
||||
}
|
||||
} else {
|
||||
if (w) {
|
||||
*w = defaultIconMetrics[icon].width * 2;
|
||||
}
|
||||
if (h) {
|
||||
*h = defaultIconMetrics[icon].height * 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GUIFontDrawGlyph(const struct GUIFont* font, int x, int y, uint32_t color, uint32_t glyph) {
|
||||
if (glyph > 0x7F) {
|
||||
glyph = '?';
|
||||
}
|
||||
struct GUIFontGlyphMetric metric = defaultFontMetrics[glyph];
|
||||
vita2d_draw_texture_tint_part_scale(font->tex, x, y - GLYPH_HEIGHT + metric.padding.top * 2,
|
||||
vita2d_draw_texture_tint_part(font->tex, x, y - GLYPH_HEIGHT + metric.padding.top * 2,
|
||||
(glyph & 15) * CELL_WIDTH + metric.padding.left * 2,
|
||||
(glyph >> 4) * CELL_HEIGHT + metric.padding.top * 2,
|
||||
CELL_WIDTH - (metric.padding.left + metric.padding.right) * 2,
|
||||
CELL_HEIGHT - (metric.padding.top + metric.padding.bottom) * 2,
|
||||
1, 1, color);
|
||||
color);
|
||||
}
|
||||
|
||||
void GUIFontDrawIcon(const struct GUIFont* font, int x, int y, enum GUIAlignment align, enum GUIOrientation orient, uint32_t color, enum GUIIcon icon) {
|
||||
|
@ -86,13 +105,13 @@ void GUIFontDrawIcon(const struct GUIFont* font, int x, int y, enum GUIAlignment
|
|||
|
||||
switch (orient) {
|
||||
case GUI_ORIENT_HMIRROR:
|
||||
vita2d_draw_texture_tint_part_scale(font->icons, x, y,
|
||||
vita2d_draw_texture_tint_part_scale(font->icons, x + metric.width * 2, y,
|
||||
metric.x * 2, metric.y * 2,
|
||||
metric.width * 2, metric.height * 2,
|
||||
-1, 1, color);
|
||||
return;
|
||||
case GUI_ORIENT_VMIRROR:
|
||||
vita2d_draw_texture_tint_part_scale(font->icons, x, y,
|
||||
vita2d_draw_texture_tint_part_scale(font->icons, x, y + metric.height * 2,
|
||||
metric.x * 2, metric.y * 2,
|
||||
metric.width * 2, metric.height * 2,
|
||||
1, -1, color);
|
||||
|
@ -107,3 +126,14 @@ void GUIFontDrawIcon(const struct GUIFont* font, int x, int y, enum GUIAlignment
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void GUIFontDrawIconSize(const struct GUIFont* font, int x, int y, int w, int h, uint32_t color, enum GUIIcon icon) {
|
||||
if (icon >= GUI_ICON_MAX) {
|
||||
return;
|
||||
}
|
||||
struct GUIIconMetric metric = defaultIconMetrics[icon];
|
||||
vita2d_draw_texture_tint_part_scale(font->icons, x, y,
|
||||
metric.x * 2, metric.y * 2,
|
||||
metric.width * 2, metric.height * 2,
|
||||
w ? (w / (float) metric.width) : 1, h ? (h / (float) metric.height) : 1, color);
|
||||
}
|
||||
|
|
|
@ -22,8 +22,6 @@
|
|||
|
||||
#include <vita2d.h>
|
||||
|
||||
PSP2_MODULE_INFO(0, 0, "mGBA");
|
||||
|
||||
static void _drawStart(void) {
|
||||
vita2d_set_vblank_wait(false);
|
||||
vita2d_start_drawing();
|
||||
|
@ -43,29 +41,29 @@ static uint32_t _pollInput(void) {
|
|||
SceCtrlData pad;
|
||||
sceCtrlPeekBufferPositive(0, &pad, 1);
|
||||
int input = 0;
|
||||
if (pad.buttons & PSP2_CTRL_TRIANGLE) {
|
||||
if (pad.buttons & SCE_CTRL_TRIANGLE) {
|
||||
input |= 1 << GUI_INPUT_CANCEL;
|
||||
}
|
||||
if (pad.buttons & PSP2_CTRL_SQUARE) {
|
||||
if (pad.buttons & SCE_CTRL_SQUARE) {
|
||||
input |= 1 << GBA_GUI_INPUT_SCREEN_MODE;
|
||||
}
|
||||
if (pad.buttons & PSP2_CTRL_CIRCLE) {
|
||||
if (pad.buttons & SCE_CTRL_CIRCLE) {
|
||||
input |= 1 << GUI_INPUT_BACK;
|
||||
}
|
||||
if (pad.buttons & PSP2_CTRL_CROSS) {
|
||||
if (pad.buttons & SCE_CTRL_CROSS) {
|
||||
input |= 1 << GUI_INPUT_SELECT;
|
||||
}
|
||||
|
||||
if (pad.buttons & PSP2_CTRL_UP || pad.ly < 64) {
|
||||
if (pad.buttons & SCE_CTRL_UP || pad.ly < 64) {
|
||||
input |= 1 << GUI_INPUT_UP;
|
||||
}
|
||||
if (pad.buttons & PSP2_CTRL_DOWN || pad.ly >= 192) {
|
||||
if (pad.buttons & SCE_CTRL_DOWN || pad.ly >= 192) {
|
||||
input |= 1 << GUI_INPUT_DOWN;
|
||||
}
|
||||
if (pad.buttons & PSP2_CTRL_LEFT || pad.lx < 64) {
|
||||
if (pad.buttons & SCE_CTRL_LEFT || pad.lx < 64) {
|
||||
input |= 1 << GUI_INPUT_LEFT;
|
||||
}
|
||||
if (pad.buttons & PSP2_CTRL_RIGHT || pad.lx >= 192) {
|
||||
if (pad.buttons & SCE_CTRL_RIGHT || pad.lx >= 192) {
|
||||
input |= 1 << GUI_INPUT_RIGHT;
|
||||
}
|
||||
|
||||
|
@ -108,7 +106,7 @@ int main() {
|
|||
GUI_PARAMS_TRAIL
|
||||
},
|
||||
.configExtra = (struct GUIMenuItem[]) {
|
||||
{
|
||||
{
|
||||
.title = "Screen mode",
|
||||
.data = "screenMode",
|
||||
.submenu = 0,
|
||||
|
@ -138,10 +136,10 @@ int main() {
|
|||
"R",
|
||||
0, // L2?
|
||||
0, // R2?
|
||||
"Triangle",
|
||||
"Circle",
|
||||
"Cross",
|
||||
"Square"
|
||||
"\1\xC",
|
||||
"\1\xA",
|
||||
"\1\xB",
|
||||
"\1\xD"
|
||||
},
|
||||
.nKeys = 16
|
||||
},
|
||||
|
|
|
@ -65,7 +65,7 @@ static void _mapVitaKey(struct GBAInputMap* map, int pspKey, enum GBAKey key) {
|
|||
static THREAD_ENTRY _audioThread(void* context) {
|
||||
struct GBAPSP2AudioContext* audio = (struct GBAPSP2AudioContext*) context;
|
||||
struct GBAStereoSample buffer[PSP2_SAMPLES];
|
||||
int audioPort = sceAudioOutOpenPort(PSP2_AUDIO_OUT_PORT_TYPE_MAIN, PSP2_SAMPLES, 48000, PSP2_AUDIO_OUT_MODE_STEREO);
|
||||
int audioPort = sceAudioOutOpenPort(SCE_AUDIO_OUT_PORT_TYPE_MAIN, PSP2_SAMPLES, 48000, SCE_AUDIO_OUT_MODE_STEREO);
|
||||
while (audio->running) {
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
MutexLock(&audio->mutex);
|
||||
|
@ -75,7 +75,7 @@ static THREAD_ENTRY _audioThread(void* context) {
|
|||
len = PSP2_SAMPLES;
|
||||
}
|
||||
if (len > 0) {
|
||||
len &= ~(PSP2_AUDIO_MIN_LEN - 1);
|
||||
len &= ~(SCE_AUDIO_MIN_LEN - 1);
|
||||
CircleBufferRead(&audio->buffer, buffer, len * sizeof(buffer[0]));
|
||||
MutexUnlock(&audio->mutex);
|
||||
sceAudioOutOutput(audioPort, buffer);
|
||||
|
@ -137,16 +137,16 @@ uint16_t GBAPSP2PollInput(struct GBAGUIRunner* runner) {
|
|||
|
||||
void GBAPSP2Setup(struct GBAGUIRunner* runner) {
|
||||
scePowerSetArmClockFrequency(80);
|
||||
_mapVitaKey(&runner->context.inputMap, PSP2_CTRL_CROSS, GBA_KEY_A);
|
||||
_mapVitaKey(&runner->context.inputMap, PSP2_CTRL_CIRCLE, GBA_KEY_B);
|
||||
_mapVitaKey(&runner->context.inputMap, PSP2_CTRL_START, GBA_KEY_START);
|
||||
_mapVitaKey(&runner->context.inputMap, PSP2_CTRL_SELECT, GBA_KEY_SELECT);
|
||||
_mapVitaKey(&runner->context.inputMap, PSP2_CTRL_UP, GBA_KEY_UP);
|
||||
_mapVitaKey(&runner->context.inputMap, PSP2_CTRL_DOWN, GBA_KEY_DOWN);
|
||||
_mapVitaKey(&runner->context.inputMap, PSP2_CTRL_LEFT, GBA_KEY_LEFT);
|
||||
_mapVitaKey(&runner->context.inputMap, PSP2_CTRL_RIGHT, GBA_KEY_RIGHT);
|
||||
_mapVitaKey(&runner->context.inputMap, PSP2_CTRL_LTRIGGER, GBA_KEY_L);
|
||||
_mapVitaKey(&runner->context.inputMap, PSP2_CTRL_RTRIGGER, GBA_KEY_R);
|
||||
_mapVitaKey(&runner->context.inputMap, SCE_CTRL_CROSS, GBA_KEY_A);
|
||||
_mapVitaKey(&runner->context.inputMap, SCE_CTRL_CIRCLE, GBA_KEY_B);
|
||||
_mapVitaKey(&runner->context.inputMap, SCE_CTRL_START, GBA_KEY_START);
|
||||
_mapVitaKey(&runner->context.inputMap, SCE_CTRL_SELECT, GBA_KEY_SELECT);
|
||||
_mapVitaKey(&runner->context.inputMap, SCE_CTRL_UP, GBA_KEY_UP);
|
||||
_mapVitaKey(&runner->context.inputMap, SCE_CTRL_DOWN, GBA_KEY_DOWN);
|
||||
_mapVitaKey(&runner->context.inputMap, SCE_CTRL_LEFT, GBA_KEY_LEFT);
|
||||
_mapVitaKey(&runner->context.inputMap, SCE_CTRL_RIGHT, GBA_KEY_RIGHT);
|
||||
_mapVitaKey(&runner->context.inputMap, SCE_CTRL_LTRIGGER, GBA_KEY_L);
|
||||
_mapVitaKey(&runner->context.inputMap, SCE_CTRL_RTRIGGER, GBA_KEY_R);
|
||||
|
||||
struct GBAAxis desc = { GBA_KEY_DOWN, GBA_KEY_UP, 192, 64 };
|
||||
GBAInputBindAxis(&runner->context.inputMap, PSP2_INPUT, 0, &desc);
|
||||
|
|
|
@ -43,6 +43,7 @@ static void _vdsceRewind(struct VDir* vd);
|
|||
static struct VDirEntry* _vdsceListNext(struct VDir* vd);
|
||||
static struct VFile* _vdsceOpenFile(struct VDir* vd, const char* path, int mode);
|
||||
static struct VDir* _vdsceOpenDir(struct VDir* vd, const char* path);
|
||||
static bool _vdsceDeleteFile(struct VDir* vd, const char* path);
|
||||
|
||||
static const char* _vdesceName(struct VDirEntry* vde);
|
||||
static enum VFSType _vdesceType(struct VDirEntry* vde);
|
||||
|
@ -152,6 +153,7 @@ struct VDir* VDirOpen(const char* path) {
|
|||
vd->d.listNext = _vdsceListNext;
|
||||
vd->d.openFile = _vdsceOpenFile;
|
||||
vd->d.openDir = _vdsceOpenDir;
|
||||
vd->d.deleteFile = _vdsceDeleteFile;
|
||||
vd->path = strdup(path);
|
||||
|
||||
vd->de.d.name = _vdesceName;
|
||||
|
@ -215,6 +217,20 @@ struct VDir* _vdsceOpenDir(struct VDir* vd, const char* path) {
|
|||
return vd2;
|
||||
}
|
||||
|
||||
bool _vdsceDeleteFile(struct VDir* vd, const char* path) {
|
||||
struct VDirSce* vdsce = (struct VDirSce*) vd;
|
||||
if (!path) {
|
||||
return 0;
|
||||
}
|
||||
const char* dir = vdsce->path;
|
||||
char* combined = malloc(sizeof(char) * (strlen(path) + strlen(dir) + strlen(PATH_SEP) + 1));
|
||||
sprintf(combined, "%s%s%s", dir, PATH_SEP, path);
|
||||
|
||||
bool ret = sceIoRemove(combined) >= 0;
|
||||
free(combined);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const char* _vdesceName(struct VDirEntry* vde) {
|
||||
struct VDirEntrySce* vdesce = (struct VDirEntrySce*) vde;
|
||||
return vdesce->ent.d_name;
|
||||
|
@ -222,7 +238,7 @@ static const char* _vdesceName(struct VDirEntry* vde) {
|
|||
|
||||
static enum VFSType _vdesceType(struct VDirEntry* vde) {
|
||||
struct VDirEntrySce* vdesce = (struct VDirEntrySce*) vde;
|
||||
if (PSP2_S_ISDIR(vdesce->ent.d_stat.st_mode)) {
|
||||
if (SCE_S_ISDIR(vdesce->ent.d_stat.st_mode)) {
|
||||
return VFS_DIRECTORY;
|
||||
}
|
||||
return VFS_FILE;
|
||||
|
|
|
@ -31,6 +31,7 @@ CheatsView::CheatsView(GameController* controller, QWidget* parent)
|
|||
connect(m_ui.addSet, SIGNAL(clicked()), this, SLOT(addSet()));
|
||||
connect(m_ui.remove, SIGNAL(clicked()), this, SLOT(removeSet()));
|
||||
connect(controller, SIGNAL(gameStopped(GBAThread*)), &m_model, SLOT(invalidated()));
|
||||
connect(controller, SIGNAL(stateLoaded(GBAThread*)), &m_model, SLOT(invalidated()));
|
||||
|
||||
connect(m_ui.add, &QPushButton::clicked, [this]() {
|
||||
enterCheat(GBACheatAddLine);
|
||||
|
|
|
@ -128,6 +128,7 @@ ConfigController::~ConfigController() {
|
|||
|
||||
bool ConfigController::parseArguments(GBAArguments* args, int argc, char* argv[], SubParser* subparser) {
|
||||
if (::parseArguments(args, &m_config, argc, argv, subparser)) {
|
||||
GBAConfigFreeOpts(&m_opts);
|
||||
GBAConfigMap(&m_config, &m_opts);
|
||||
return true;
|
||||
}
|
||||
|
@ -262,6 +263,9 @@ void ConfigController::setMRU(const QList<QString>& mru) {
|
|||
void ConfigController::write() {
|
||||
GBAConfigSave(&m_config);
|
||||
m_settings->sync();
|
||||
|
||||
GBAConfigFreeOpts(&m_opts);
|
||||
GBAConfigMap(&m_config, &m_opts);
|
||||
}
|
||||
|
||||
void ConfigController::makePortable() {
|
||||
|
|
|
@ -14,30 +14,34 @@ extern "C" {
|
|||
|
||||
using namespace QGBA;
|
||||
|
||||
#ifdef BUILD_GL
|
||||
#if defined(BUILD_GL) || defined(BUILD_GLES2) || defined(USE_EPOXY)
|
||||
Display::Driver Display::s_driver = Display::Driver::OPENGL;
|
||||
#else
|
||||
Display::Driver Display::s_driver = Display::Driver::QT;
|
||||
#endif
|
||||
|
||||
Display* Display::create(QWidget* parent) {
|
||||
#ifdef BUILD_GL
|
||||
#if defined(BUILD_GL) || defined(BUILD_GLES2) || defined(USE_EPOXY)
|
||||
QGLFormat format(QGLFormat(QGL::Rgba | QGL::DoubleBuffer));
|
||||
format.setSwapInterval(1);
|
||||
#endif
|
||||
|
||||
switch (s_driver) {
|
||||
#ifdef BUILD_GL
|
||||
#if defined(BUILD_GL) || defined(BUILD_GLES2) || defined(USE_EPOXY)
|
||||
case Driver::OPENGL:
|
||||
return new DisplayGL(format, parent);
|
||||
return new DisplayGL(format, false, parent);
|
||||
#endif
|
||||
#ifdef BUILD_GL
|
||||
case Driver::OPENGL1:
|
||||
return new DisplayGL(format, true, parent);
|
||||
#endif
|
||||
|
||||
case Driver::QT:
|
||||
return new DisplayQt(parent);
|
||||
|
||||
default:
|
||||
#ifdef BUILD_GL
|
||||
return new DisplayGL(format, parent);
|
||||
#if defined(BUILD_GL) || defined(BUILD_GLES2) || defined(USE_EPOXY)
|
||||
return new DisplayGL(format, false, parent);
|
||||
#else
|
||||
return new DisplayQt(parent);
|
||||
#endif
|
||||
|
|
|
@ -22,8 +22,11 @@ Q_OBJECT
|
|||
public:
|
||||
enum class Driver {
|
||||
QT = 0,
|
||||
#ifdef BUILD_GL
|
||||
#if defined(BUILD_GL) || defined(BUILD_GLES2) || defined(USE_EPOXY)
|
||||
OPENGL = 1,
|
||||
#endif
|
||||
#ifdef BUILD_GL
|
||||
OPENGL1 = 2,
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
@ -24,14 +24,22 @@ extern "C" {
|
|||
|
||||
using namespace QGBA;
|
||||
|
||||
DisplayGL::DisplayGL(const QGLFormat& format, QWidget* parent)
|
||||
DisplayGL::DisplayGL(const QGLFormat& format, bool force1, QWidget* parent)
|
||||
: Display(parent)
|
||||
, m_isDrawing(false)
|
||||
, m_gl(new EmptyGLWidget(format, this))
|
||||
, m_drawThread(nullptr)
|
||||
, m_context(nullptr)
|
||||
{
|
||||
m_painter = new PainterGL(m_gl, QGLFormat::openGLVersionFlags());
|
||||
QGLFormat::OpenGLVersionFlags versions = QGLFormat::openGLVersionFlags();
|
||||
if (force1) {
|
||||
versions &= QGLFormat::OpenGL_Version_1_1 |
|
||||
QGLFormat::OpenGL_Version_1_2 |
|
||||
QGLFormat::OpenGL_Version_1_3 |
|
||||
QGLFormat::OpenGL_Version_1_4 |
|
||||
QGLFormat::OpenGL_Version_1_5;
|
||||
}
|
||||
m_painter = new PainterGL(m_gl, versions);
|
||||
m_gl->setMouseTracking(true);
|
||||
m_gl->setAttribute(Qt::WA_TransparentForMouseEvents); // This doesn't seem to work?
|
||||
}
|
||||
|
@ -188,7 +196,7 @@ PainterGL::PainterGL(QGLWidget* parent, QGLFormat::OpenGLVersionFlags glVersion)
|
|||
#endif
|
||||
|
||||
#if !defined(_WIN32) || defined(USE_EPOXY)
|
||||
if (glVersion & QGLFormat::OpenGL_Version_3_0) {
|
||||
if (glVersion & (QGLFormat::OpenGL_Version_3_0 | QGLFormat::OpenGL_ES_Version_2_0)) {
|
||||
gl2Backend = new GBAGLES2Context;
|
||||
GBAGLES2ContextCreate(gl2Backend);
|
||||
m_backend = &gl2Backend->d;
|
||||
|
@ -366,7 +374,7 @@ void PainterGL::enqueue(const uint32_t* backing) {
|
|||
} else {
|
||||
buffer = m_free.takeLast();
|
||||
}
|
||||
memcpy(buffer, backing, 256 * VIDEO_VERTICAL_PIXELS * BYTES_PER_PIXEL);
|
||||
memcpy(buffer, backing, VIDEO_HORIZONTAL_PIXELS * VIDEO_VERTICAL_PIXELS * BYTES_PER_PIXEL);
|
||||
m_queue.enqueue(buffer);
|
||||
m_mutex.unlock();
|
||||
}
|
||||
|
|
|
@ -10,6 +10,9 @@
|
|||
|
||||
#ifdef USE_EPOXY
|
||||
#include <epoxy/gl.h>
|
||||
#ifndef GLdouble
|
||||
#define GLdouble GLdouble
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <QGLWidget>
|
||||
|
@ -42,7 +45,7 @@ class DisplayGL : public Display {
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
DisplayGL(const QGLFormat& format, QWidget* parent = nullptr);
|
||||
DisplayGL(const QGLFormat& format, bool force1 = false, QWidget* parent = nullptr);
|
||||
~DisplayGL();
|
||||
|
||||
bool isDrawing() const override { return m_isDrawing; }
|
||||
|
|
|
@ -41,12 +41,12 @@ void DisplayQt::framePosted(const uint32_t* buffer) {
|
|||
}
|
||||
#ifdef COLOR_16_BIT
|
||||
#ifdef COLOR_5_6_5
|
||||
m_backing = QImage(reinterpret_cast<const uchar*>(buffer), 256, 256, QImage::Format_RGB16);
|
||||
m_backing = QImage(reinterpret_cast<const uchar*>(buffer), VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, QImage::Format_RGB16);
|
||||
#else
|
||||
m_backing = QImage(reinterpret_cast<const uchar*>(buffer), 256, 256, QImage::Format_RGB555);
|
||||
m_backing = QImage(reinterpret_cast<const uchar*>(buffer), VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, QImage::Format_RGB555);
|
||||
#endif
|
||||
#else
|
||||
m_backing = QImage(reinterpret_cast<const uchar*>(buffer), 256, 256, QImage::Format_RGB32);
|
||||
m_backing = QImage(reinterpret_cast<const uchar*>(buffer), VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, QImage::Format_RGB32);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -124,28 +124,27 @@ GBAApp* GBAApp::app() {
|
|||
return g_app;
|
||||
}
|
||||
|
||||
void GBAApp::interruptAll() {
|
||||
void GBAApp::pauseAll(QList<int>* paused) {
|
||||
for (int i = 0; i < MAX_GBAS; ++i) {
|
||||
if (!m_windows[i] || !m_windows[i]->controller()->isLoaded()) {
|
||||
if (!m_windows[i] || !m_windows[i]->controller()->isLoaded() || m_windows[i]->controller()->isPaused()) {
|
||||
continue;
|
||||
}
|
||||
m_windows[i]->controller()->threadInterrupt();
|
||||
m_windows[i]->controller()->setPaused(true);
|
||||
paused->append(i);
|
||||
}
|
||||
}
|
||||
|
||||
void GBAApp::continueAll() {
|
||||
for (int i = 0; i < MAX_GBAS; ++i) {
|
||||
if (!m_windows[i] || !m_windows[i]->controller()->isLoaded()) {
|
||||
continue;
|
||||
}
|
||||
m_windows[i]->controller()->threadContinue();
|
||||
void GBAApp::continueAll(const QList<int>* paused) {
|
||||
for (int i : *paused) {
|
||||
m_windows[i]->controller()->setPaused(false);
|
||||
}
|
||||
}
|
||||
|
||||
QString GBAApp::getOpenFileName(QWidget* owner, const QString& title, const QString& filter) {
|
||||
interruptAll();
|
||||
QList<int> paused;
|
||||
pauseAll(&paused);
|
||||
QString filename = QFileDialog::getOpenFileName(owner, title, m_configController.getQtOption("lastDirectory").toString(), filter);
|
||||
continueAll();
|
||||
continueAll(&paused);
|
||||
if (!filename.isEmpty()) {
|
||||
m_configController.setQtOption("lastDirectory", QFileInfo(filename).dir().path());
|
||||
}
|
||||
|
@ -153,9 +152,21 @@ QString GBAApp::getOpenFileName(QWidget* owner, const QString& title, const QStr
|
|||
}
|
||||
|
||||
QString GBAApp::getSaveFileName(QWidget* owner, const QString& title, const QString& filter) {
|
||||
interruptAll();
|
||||
QList<int> paused;
|
||||
pauseAll(&paused);
|
||||
QString filename = QFileDialog::getSaveFileName(owner, title, m_configController.getQtOption("lastDirectory").toString(), filter);
|
||||
continueAll();
|
||||
continueAll(&paused);
|
||||
if (!filename.isEmpty()) {
|
||||
m_configController.setQtOption("lastDirectory", QFileInfo(filename).dir().path());
|
||||
}
|
||||
return filename;
|
||||
}
|
||||
|
||||
QString GBAApp::getOpenDirectoryName(QWidget* owner, const QString& title) {
|
||||
QList<int> paused;
|
||||
pauseAll(&paused);
|
||||
QString filename = QFileDialog::getExistingDirectory(owner, title, m_configController.getQtOption("lastDirectory").toString());
|
||||
continueAll(&paused);
|
||||
if (!filename.isEmpty()) {
|
||||
m_configController.setQtOption("lastDirectory", QFileInfo(filename).dir().path());
|
||||
}
|
||||
|
@ -210,12 +221,13 @@ GBAApp::FileDialog::FileDialog(GBAApp* app, QWidget* parent, const QString& capt
|
|||
}
|
||||
|
||||
int GBAApp::FileDialog::exec() {
|
||||
m_app->interruptAll();
|
||||
QList<int> paused;
|
||||
m_app->pauseAll(&paused);
|
||||
bool didAccept = QFileDialog::exec() == QDialog::Accepted;
|
||||
QStringList filenames = selectedFiles();
|
||||
if (!filenames.isEmpty()) {
|
||||
m_app->m_configController.setQtOption("lastDirectory", QFileInfo(filenames[0]).dir().path());
|
||||
}
|
||||
m_app->continueAll();
|
||||
m_app->continueAll(&paused);
|
||||
return didAccept;
|
||||
}
|
||||
|
|
|
@ -36,6 +36,7 @@ public:
|
|||
|
||||
QString getOpenFileName(QWidget* owner, const QString& title, const QString& filter = QString());
|
||||
QString getSaveFileName(QWidget* owner, const QString& title, const QString& filter = QString());
|
||||
QString getOpenDirectoryName(QWidget* owner, const QString& title);
|
||||
|
||||
QFileDialog* getOpenFileDialog(QWidget* owner, const QString& title, const QString& filter = QString());
|
||||
QFileDialog* getSaveFileDialog(QWidget* owner, const QString& title, const QString& filter = QString());
|
||||
|
@ -43,10 +44,6 @@ public:
|
|||
const NoIntroDB* gameDB() const { return m_db; }
|
||||
bool reloadGameDB();
|
||||
|
||||
public slots:
|
||||
void interruptAll();
|
||||
void continueAll();
|
||||
|
||||
protected:
|
||||
bool event(QEvent*);
|
||||
|
||||
|
@ -63,6 +60,9 @@ private:
|
|||
|
||||
Window* newWindowInternal();
|
||||
|
||||
void pauseAll(QList<int>* paused);
|
||||
void continueAll(const QList<int>* paused);
|
||||
|
||||
ConfigController m_configController;
|
||||
Window* m_windows[MAX_GBAS];
|
||||
MultiplayerController m_multiplayer;
|
||||
|
|
|
@ -15,6 +15,12 @@
|
|||
#include "InputController.h"
|
||||
#include "KeyEditor.h"
|
||||
|
||||
#ifdef BUILD_SDL
|
||||
extern "C" {
|
||||
#include "platform/sdl/sdl-events.h"
|
||||
}
|
||||
#endif
|
||||
|
||||
using namespace QGBA;
|
||||
|
||||
const qreal GBAKeyEditor::DPAD_CENTER_X = 0.247;
|
||||
|
@ -51,22 +57,19 @@ GBAKeyEditor::GBAKeyEditor(InputController* controller, int type, const QString&
|
|||
|
||||
#ifdef BUILD_SDL
|
||||
if (type == SDL_BINDING_BUTTON) {
|
||||
controller->updateJoysticks();
|
||||
controller->recalibrateAxes();
|
||||
lookupAxes(map);
|
||||
|
||||
m_profileSelect = new QComboBox(this);
|
||||
m_profileSelect->addItems(controller->connectedGamepads(type));
|
||||
int activeGamepad = controller->gamepad(type);
|
||||
selectGamepad(activeGamepad);
|
||||
if (activeGamepad > 0) {
|
||||
m_profileSelect->setCurrentIndex(activeGamepad);
|
||||
}
|
||||
|
||||
connect(m_profileSelect, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), [this] (int i) {
|
||||
m_controller->setGamepad(m_type, i);
|
||||
m_profile = m_profileSelect->currentText();
|
||||
m_controller->loadProfile(m_type, m_profile);
|
||||
refresh();
|
||||
});
|
||||
connect(m_profileSelect, SIGNAL(currentIndexChanged(int)), this, SLOT(selectGamepad(int)));
|
||||
|
||||
m_clear = new QWidget(this);
|
||||
QHBoxLayout* layout = new QHBoxLayout;
|
||||
|
@ -105,9 +108,6 @@ GBAKeyEditor::GBAKeyEditor(InputController* controller, int type, const QString&
|
|||
connect(setAll, SIGNAL(pressed()), this, SLOT(setAll()));
|
||||
layout->addWidget(setAll);
|
||||
|
||||
QPushButton* save = new QPushButton(tr("Save"));
|
||||
connect(save, SIGNAL(pressed()), this, SLOT(save()));
|
||||
layout->addWidget(save);
|
||||
layout->setSpacing(6);
|
||||
|
||||
m_keyOrder = QList<KeyEditor*>{
|
||||
|
@ -136,6 +136,10 @@ GBAKeyEditor::GBAKeyEditor(InputController* controller, int type, const QString&
|
|||
setAll->setFocus();
|
||||
}
|
||||
|
||||
GBAKeyEditor::~GBAKeyEditor() {
|
||||
m_controller->releaseFocus(this);
|
||||
}
|
||||
|
||||
void GBAKeyEditor::setAll() {
|
||||
m_currentKey = m_keyOrder.begin();
|
||||
(*m_currentKey)->setFocus();
|
||||
|
@ -174,9 +178,10 @@ void GBAKeyEditor::closeEvent(QCloseEvent*) {
|
|||
}
|
||||
|
||||
bool GBAKeyEditor::event(QEvent* event) {
|
||||
if (event->type() == QEvent::WindowActivate) {
|
||||
QEvent::Type type = event->type();
|
||||
if (type == QEvent::WindowActivate || type == QEvent::Show) {
|
||||
m_controller->stealFocus(this);
|
||||
} else if (event->type() == QEvent::WindowDeactivate) {
|
||||
} else if (type == QEvent::WindowDeactivate || type == QEvent::Hide) {
|
||||
m_controller->releaseFocus(this);
|
||||
}
|
||||
return QWidget::event(event);
|
||||
|
@ -280,7 +285,7 @@ void GBAKeyEditor::lookupAxes(const GBAInputMap* map) {
|
|||
|
||||
void GBAKeyEditor::bindKey(const KeyEditor* keyEditor, GBAKey key) {
|
||||
#ifdef BUILD_SDL
|
||||
if (m_type == SDL_BINDING_BUTTON) {
|
||||
if (m_type == SDL_BINDING_BUTTON && keyEditor->axis() >= 0) {
|
||||
m_controller->bindAxis(m_type, keyEditor->axis(), keyEditor->direction(), key);
|
||||
}
|
||||
#endif
|
||||
|
@ -309,6 +314,13 @@ void GBAKeyEditor::setAxisValue(int axis, int32_t value) {
|
|||
KeyEditor* focused = *m_currentKey;
|
||||
focused->setValueAxis(axis, value);
|
||||
}
|
||||
|
||||
void GBAKeyEditor::selectGamepad(int index) {
|
||||
m_controller->setGamepad(m_type, index);
|
||||
m_profile = m_profileSelect->currentText();
|
||||
m_controller->loadProfile(m_type, m_profile);
|
||||
refresh();
|
||||
}
|
||||
#endif
|
||||
|
||||
KeyEditor* GBAKeyEditor::keyById(GBAKey key) {
|
||||
|
|
|
@ -28,6 +28,7 @@ Q_OBJECT
|
|||
|
||||
public:
|
||||
GBAKeyEditor(InputController* controller, int type, const QString& profile = QString(), QWidget* parent = nullptr);
|
||||
virtual ~GBAKeyEditor();
|
||||
|
||||
public slots:
|
||||
void setAll();
|
||||
|
@ -45,6 +46,7 @@ private slots:
|
|||
void refresh();
|
||||
#ifdef BUILD_SDL
|
||||
void setAxisValue(int axis, int32_t value);
|
||||
void selectGamepad(int index);
|
||||
#endif
|
||||
|
||||
private:
|
||||
|
|
|
@ -75,3 +75,12 @@ void GDBController::listen() {
|
|||
}
|
||||
m_gameController->threadContinue();
|
||||
}
|
||||
|
||||
void GDBController::breakInto() {
|
||||
if (!isAttached()) {
|
||||
return;
|
||||
}
|
||||
m_gameController->threadInterrupt();
|
||||
ARMDebuggerEnter(&m_gdbStub.d, DEBUGGER_ENTER_MANUAL, 0);
|
||||
m_gameController->threadContinue();
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ public slots:
|
|||
void attach();
|
||||
void detach();
|
||||
void listen();
|
||||
void breakInto();
|
||||
|
||||
signals:
|
||||
void listening();
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <QLineEdit>
|
||||
#include <QMessageBox>
|
||||
#include <QPushButton>
|
||||
#include <QHBoxLayout>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "GDBController.h"
|
||||
|
@ -45,10 +46,20 @@ GDBWindow::GDBWindow(GDBController* controller, QWidget* parent)
|
|||
connect(m_bindAddressEdit, SIGNAL(textChanged(const QString&)), this, SLOT(bindAddressChanged(const QString&)));
|
||||
settingsGrid->addWidget(m_bindAddressEdit, 1, 1, Qt::AlignLeft);
|
||||
|
||||
QHBoxLayout* buttons = new QHBoxLayout;
|
||||
|
||||
m_startStopButton = new QPushButton;
|
||||
mainSegment->addWidget(m_startStopButton);
|
||||
buttons->addWidget(m_startStopButton);
|
||||
|
||||
m_breakButton = new QPushButton;
|
||||
m_breakButton->setText(tr("Break"));
|
||||
buttons->addWidget(m_breakButton);
|
||||
|
||||
mainSegment->addLayout(buttons);
|
||||
|
||||
connect(m_gdbController, SIGNAL(listening()), this, SLOT(started()));
|
||||
connect(m_gdbController, SIGNAL(listenFailed()), this, SLOT(failed()));
|
||||
connect(m_breakButton, SIGNAL(clicked()), controller, SLOT(breakInto()));
|
||||
|
||||
if (m_gdbController->isAttached()) {
|
||||
started();
|
||||
|
@ -91,6 +102,7 @@ void GDBWindow::started() {
|
|||
m_portEdit->setEnabled(false);
|
||||
m_bindAddressEdit->setEnabled(false);
|
||||
m_startStopButton->setText(tr("Stop"));
|
||||
m_breakButton->setEnabled(true);
|
||||
disconnect(m_startStopButton, SIGNAL(clicked()), m_gdbController, SLOT(listen()));
|
||||
connect(m_startStopButton, SIGNAL(clicked()), m_gdbController, SLOT(detach()));
|
||||
connect(m_startStopButton, SIGNAL(clicked()), this, SLOT(stopped()));
|
||||
|
@ -100,6 +112,7 @@ void GDBWindow::stopped() {
|
|||
m_portEdit->setEnabled(true);
|
||||
m_bindAddressEdit->setEnabled(true);
|
||||
m_startStopButton->setText(tr("Start"));
|
||||
m_breakButton->setEnabled(false);
|
||||
disconnect(m_startStopButton, SIGNAL(clicked()), m_gdbController, SLOT(detach()));
|
||||
disconnect(m_startStopButton, SIGNAL(clicked()), this, SLOT(stopped()));
|
||||
connect(m_startStopButton, SIGNAL(clicked()), m_gdbController, SLOT(listen()));
|
||||
|
|
|
@ -36,6 +36,7 @@ private:
|
|||
QLineEdit* m_portEdit;
|
||||
QLineEdit* m_bindAddressEdit;
|
||||
QPushButton* m_startStopButton;
|
||||
QPushButton* m_breakButton;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
extern "C" {
|
||||
#include "gba/audio.h"
|
||||
#include "gba/context/config.h"
|
||||
#include "gba/context/directories.h"
|
||||
#include "gba/gba.h"
|
||||
#include "gba/serialize.h"
|
||||
#include "gba/sharkport.h"
|
||||
|
@ -31,8 +32,8 @@ using namespace std;
|
|||
|
||||
GameController::GameController(QObject* parent)
|
||||
: QObject(parent)
|
||||
, m_drawContext(new uint32_t[256 * VIDEO_HORIZONTAL_PIXELS])
|
||||
, m_frontBuffer(new uint32_t[256 * 256])
|
||||
, m_drawContext(new uint32_t[VIDEO_VERTICAL_PIXELS * VIDEO_HORIZONTAL_PIXELS])
|
||||
, m_frontBuffer(new uint32_t[VIDEO_VERTICAL_PIXELS * VIDEO_HORIZONTAL_PIXELS])
|
||||
, m_threadContext()
|
||||
, m_activeKeys(0)
|
||||
, m_inactiveKeys(0)
|
||||
|
@ -57,11 +58,13 @@ GameController::GameController(QObject* parent)
|
|||
, m_stateSlot(1)
|
||||
, m_backupLoadState(nullptr)
|
||||
, m_backupSaveState(nullptr)
|
||||
, m_saveStateFlags(SAVESTATE_SCREENSHOT | SAVESTATE_SAVEDATA | SAVESTATE_CHEATS)
|
||||
, m_loadStateFlags(SAVESTATE_SCREENSHOT)
|
||||
{
|
||||
m_renderer = new GBAVideoSoftwareRenderer;
|
||||
GBAVideoSoftwareRendererCreate(m_renderer);
|
||||
m_renderer->outputBuffer = (color_t*) m_drawContext;
|
||||
m_renderer->outputBufferStride = 256;
|
||||
m_renderer->outputBufferStride = VIDEO_HORIZONTAL_PIXELS;
|
||||
|
||||
GBACheatDeviceCreate(&m_cheatDevice);
|
||||
GBASIODolphinCreate(&m_dolphin);
|
||||
|
@ -75,6 +78,7 @@ GameController::GameController(QObject* parent)
|
|||
m_threadContext.rewindBufferCapacity = 0;
|
||||
m_threadContext.cheats = &m_cheatDevice;
|
||||
m_threadContext.logLevel = GBA_LOG_ALL;
|
||||
GBADirectorySetInit(&m_threadContext.dirs);
|
||||
|
||||
m_lux.p = this;
|
||||
m_lux.sample = [](GBALuminanceSource* context) {
|
||||
|
@ -111,11 +115,8 @@ GameController::GameController(QObject* parent)
|
|||
context->gba->video.renderer->disableOBJ = !controller->m_videoLayers[4];
|
||||
controller->m_fpsTarget = context->fpsTarget;
|
||||
|
||||
if (GBALoadState(context, context->dirs.state, 0, SAVESTATE_SCREENSHOT)) {
|
||||
VFile* vf = GBAGetState(context->gba, context->dirs.state, 0, true);
|
||||
if (vf) {
|
||||
vf->truncate(vf, 0);
|
||||
}
|
||||
if (context->dirs.state && GBALoadState(context, context->dirs.state, 0, controller->m_loadStateFlags)) {
|
||||
GBADeleteState(context->gba, context->dirs.state, 0);
|
||||
}
|
||||
QMetaObject::invokeMethod(controller, "gameStarted", Q_ARG(GBAThread*, context));
|
||||
};
|
||||
|
@ -127,7 +128,7 @@ GameController::GameController(QObject* parent)
|
|||
|
||||
m_threadContext.frameCallback = [](GBAThread* context) {
|
||||
GameController* controller = static_cast<GameController*>(context->userData);
|
||||
memcpy(controller->m_frontBuffer, controller->m_drawContext, 256 * VIDEO_HORIZONTAL_PIXELS * BYTES_PER_PIXEL);
|
||||
memcpy(controller->m_frontBuffer, controller->m_drawContext, VIDEO_VERTICAL_PIXELS * VIDEO_HORIZONTAL_PIXELS * BYTES_PER_PIXEL);
|
||||
QMetaObject::invokeMethod(controller, "frameAvailable", Q_ARG(const uint32_t*, controller->m_frontBuffer));
|
||||
if (controller->m_pauseAfterFrame.testAndSetAcquire(true, false)) {
|
||||
GBAThreadPauseFromThread(context);
|
||||
|
@ -140,7 +141,7 @@ GameController::GameController(QObject* parent)
|
|||
return false;
|
||||
}
|
||||
GameController* controller = static_cast<GameController*>(context->userData);
|
||||
if (!GBASaveState(context, context->dirs.state, 0, true)) {
|
||||
if (!GBASaveState(context, context->dirs.state, 0, controller->m_saveStateFlags)) {
|
||||
return false;
|
||||
}
|
||||
QMetaObject::invokeMethod(controller, "closeGame");
|
||||
|
@ -218,6 +219,7 @@ GameController::~GameController() {
|
|||
detachDolphin();
|
||||
closeGame();
|
||||
GBACheatDeviceDestroy(&m_cheatDevice);
|
||||
GBADirectorySetDeinit(&m_threadContext.dirs);
|
||||
delete m_renderer;
|
||||
delete[] m_drawContext;
|
||||
delete[] m_frontBuffer;
|
||||
|
@ -279,6 +281,7 @@ void GameController::setOptions(const GBAOptions* opts) {
|
|||
setMute(opts->mute);
|
||||
|
||||
threadInterrupt();
|
||||
GBADirectorySetMapOptions(&m_threadContext.dirs, opts);
|
||||
m_threadContext.idleOptimization = opts->idleOptimization;
|
||||
threadContinue();
|
||||
}
|
||||
|
@ -301,26 +304,22 @@ void GameController::setDebugger(ARMDebugger* debugger) {
|
|||
}
|
||||
#endif
|
||||
|
||||
void GameController::loadGame(const QString& path, bool dirmode) {
|
||||
void GameController::loadGame(const QString& path) {
|
||||
closeGame();
|
||||
if (!dirmode) {
|
||||
QFile file(path);
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
postLog(GBA_LOG_ERROR, tr("Failed to open game file: %1").arg(path));
|
||||
return;
|
||||
}
|
||||
file.close();
|
||||
QFile file(path);
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
postLog(GBA_LOG_ERROR, tr("Failed to open game file: %1").arg(path));
|
||||
return;
|
||||
}
|
||||
file.close();
|
||||
|
||||
m_fname = path;
|
||||
m_dirmode = dirmode;
|
||||
openGame();
|
||||
}
|
||||
|
||||
void GameController::bootBIOS() {
|
||||
closeGame();
|
||||
m_fname = QString();
|
||||
m_dirmode = false;
|
||||
openGame(true);
|
||||
}
|
||||
|
||||
|
@ -360,7 +359,7 @@ void GameController::openGame(bool biosOnly) {
|
|||
}
|
||||
|
||||
m_inputController->recalibrateAxes();
|
||||
memset(m_drawContext, 0xF8, 1024 * VIDEO_HORIZONTAL_PIXELS);
|
||||
memset(m_drawContext, 0xF8, VIDEO_VERTICAL_PIXELS * VIDEO_HORIZONTAL_PIXELS * 4);
|
||||
|
||||
if (!GBAThreadStart(&m_threadContext)) {
|
||||
m_gameOpen = false;
|
||||
|
@ -720,6 +719,10 @@ void GameController::setUseBIOS(bool use) {
|
|||
}
|
||||
|
||||
void GameController::loadState(int slot) {
|
||||
if (!m_threadContext.fname) {
|
||||
// We're in the BIOS
|
||||
return;
|
||||
}
|
||||
if (slot > 0 && slot != m_stateSlot) {
|
||||
m_stateSlot = slot;
|
||||
m_backupSaveState.clear();
|
||||
|
@ -730,7 +733,7 @@ void GameController::loadState(int slot) {
|
|||
controller->m_backupLoadState = new GBASerializedState;
|
||||
}
|
||||
GBASerialize(context->gba, controller->m_backupLoadState);
|
||||
if (GBALoadState(context, context->dirs.state, controller->m_stateSlot, SAVESTATE_SCREENSHOT)) {
|
||||
if (GBALoadState(context, context->dirs.state, controller->m_stateSlot, controller->m_loadStateFlags)) {
|
||||
controller->frameAvailable(controller->m_drawContext);
|
||||
controller->stateLoaded(context);
|
||||
}
|
||||
|
@ -738,6 +741,10 @@ void GameController::loadState(int slot) {
|
|||
}
|
||||
|
||||
void GameController::saveState(int slot) {
|
||||
if (!m_threadContext.fname) {
|
||||
// We're in the BIOS
|
||||
return;
|
||||
}
|
||||
if (slot > 0) {
|
||||
m_stateSlot = slot;
|
||||
}
|
||||
|
@ -749,7 +756,7 @@ void GameController::saveState(int slot) {
|
|||
vf->read(vf, controller->m_backupSaveState.data(), controller->m_backupSaveState.size());
|
||||
vf->close(vf);
|
||||
}
|
||||
GBASaveState(context, context->dirs.state, controller->m_stateSlot, SAVESTATE_SCREENSHOT | EXTDATA_SAVEDATA);
|
||||
GBASaveState(context, context->dirs.state, controller->m_stateSlot, controller->m_saveStateFlags);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -921,6 +928,14 @@ void GameController::reloadAudioDriver() {
|
|||
}
|
||||
}
|
||||
|
||||
void GameController::setSaveStateExtdata(int flags) {
|
||||
m_saveStateFlags = flags;
|
||||
}
|
||||
|
||||
void GameController::setLoadStateExtdata(int flags) {
|
||||
m_loadStateFlags = flags;
|
||||
}
|
||||
|
||||
void GameController::setLuminanceValue(uint8_t value) {
|
||||
m_luxValue = value;
|
||||
value = std::max<int>(value - 0x16, 0);
|
||||
|
|
|
@ -101,7 +101,7 @@ signals:
|
|||
void postLog(int level, const QString& log);
|
||||
|
||||
public slots:
|
||||
void loadGame(const QString& path, bool dirmode = false);
|
||||
void loadGame(const QString& path);
|
||||
void loadBIOS(const QString& path);
|
||||
void yankPak();
|
||||
void replaceGame(const QString& path);
|
||||
|
@ -142,6 +142,8 @@ public slots:
|
|||
void setAVStream(GBAAVStream*);
|
||||
void clearAVStream();
|
||||
void reloadAudioDriver();
|
||||
void setSaveStateExtdata(int flags);
|
||||
void setLoadStateExtdata(int flags);
|
||||
|
||||
#ifdef USE_PNG
|
||||
void screenshot();
|
||||
|
@ -186,7 +188,6 @@ private:
|
|||
GBASIODolphin m_dolphin;
|
||||
|
||||
bool m_gameOpen;
|
||||
bool m_dirmode;
|
||||
|
||||
QString m_fname;
|
||||
QString m_bios;
|
||||
|
@ -216,6 +217,8 @@ private:
|
|||
int m_stateSlot;
|
||||
GBASerializedState* m_backupLoadState;
|
||||
QByteArray m_backupSaveState;
|
||||
int m_saveStateFlags;
|
||||
int m_loadStateFlags;
|
||||
|
||||
InputController* m_inputController;
|
||||
MultiplayerController* m_multiplayer;
|
||||
|
|
|
@ -46,6 +46,7 @@ InputController::InputController(int playerId, QWidget* topLevel, QObject* paren
|
|||
++s_sdlInited;
|
||||
m_sdlPlayer.bindings = &m_inputMap;
|
||||
GBASDLInitBindings(&m_inputMap);
|
||||
updateJoysticks();
|
||||
#endif
|
||||
|
||||
m_gamepadTimer = new QTimer(this);
|
||||
|
@ -146,9 +147,9 @@ const char* InputController::profileForType(uint32_t type) {
|
|||
#ifdef BUILD_SDL
|
||||
if (type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
return SDL_JoystickName(m_sdlPlayer.joystick);
|
||||
return SDL_JoystickName(m_sdlPlayer.joystick->joystick);
|
||||
#else
|
||||
return SDL_JoystickName(SDL_JoystickIndex(m_sdlPlayer.joystick));
|
||||
return SDL_JoystickName(SDL_JoystickIndex(m_sdlPlayer.joystick->joystick));
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
@ -161,12 +162,12 @@ QStringList InputController::connectedGamepads(uint32_t type) const {
|
|||
#ifdef BUILD_SDL
|
||||
if (type == SDL_BINDING_BUTTON) {
|
||||
QStringList pads;
|
||||
for (size_t i = 0; i < s_sdlEvents.nJoysticks; ++i) {
|
||||
for (size_t i = 0; i < SDL_JoystickListSize(&s_sdlEvents.joysticks); ++i) {
|
||||
const char* name;
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
name = SDL_JoystickName(s_sdlEvents.joysticks[i]);
|
||||
name = SDL_JoystickName(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, i)->joystick);
|
||||
#else
|
||||
name = SDL_JoystickName(SDL_JoystickIndex(s_sdlEvents.joysticks[i]));
|
||||
name = SDL_JoystickName(SDL_JoystickIndex(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, i)->joystick));
|
||||
#endif
|
||||
if (name) {
|
||||
pads.append(QString(name));
|
||||
|
@ -184,7 +185,7 @@ QStringList InputController::connectedGamepads(uint32_t type) const {
|
|||
int InputController::gamepad(uint32_t type) const {
|
||||
#ifdef BUILD_SDL
|
||||
if (type == SDL_BINDING_BUTTON) {
|
||||
return m_sdlPlayer.joystickIndex;
|
||||
return m_sdlPlayer.joystick ? m_sdlPlayer.joystick->index : 0;
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
|
@ -282,11 +283,17 @@ void InputController::bindKey(uint32_t type, int key, GBAKey gbaKey) {
|
|||
return GBAInputBindKey(&m_inputMap, type, key, gbaKey);
|
||||
}
|
||||
|
||||
void InputController::updateJoysticks() {
|
||||
#ifdef BUILD_SDL
|
||||
GBASDLUpdateJoysticks(&s_sdlEvents);
|
||||
#endif
|
||||
}
|
||||
|
||||
int InputController::pollEvents() {
|
||||
int activeButtons = 0;
|
||||
#ifdef BUILD_SDL
|
||||
if (m_playerAttached) {
|
||||
SDL_Joystick* joystick = m_sdlPlayer.joystick;
|
||||
if (m_playerAttached && m_sdlPlayer.joystick) {
|
||||
SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
|
||||
SDL_JoystickUpdate();
|
||||
int numButtons = SDL_JoystickNumButtons(joystick);
|
||||
int i;
|
||||
|
@ -336,8 +343,8 @@ int InputController::pollEvents() {
|
|||
QSet<int> InputController::activeGamepadButtons(int type) {
|
||||
QSet<int> activeButtons;
|
||||
#ifdef BUILD_SDL
|
||||
if (m_playerAttached && type == SDL_BINDING_BUTTON) {
|
||||
SDL_Joystick* joystick = m_sdlPlayer.joystick;
|
||||
if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
|
||||
SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
|
||||
SDL_JoystickUpdate();
|
||||
int numButtons = SDL_JoystickNumButtons(joystick);
|
||||
int i;
|
||||
|
@ -353,8 +360,8 @@ QSet<int> InputController::activeGamepadButtons(int type) {
|
|||
|
||||
void InputController::recalibrateAxes() {
|
||||
#ifdef BUILD_SDL
|
||||
if (m_playerAttached) {
|
||||
SDL_Joystick* joystick = m_sdlPlayer.joystick;
|
||||
if (m_playerAttached && m_sdlPlayer.joystick) {
|
||||
SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
|
||||
SDL_JoystickUpdate();
|
||||
int numAxes = SDL_JoystickNumAxes(joystick);
|
||||
if (numAxes < 1) {
|
||||
|
@ -372,8 +379,8 @@ void InputController::recalibrateAxes() {
|
|||
QSet<QPair<int, GamepadAxisEvent::Direction>> InputController::activeGamepadAxes(int type) {
|
||||
QSet<QPair<int, GamepadAxisEvent::Direction>> activeAxes;
|
||||
#ifdef BUILD_SDL
|
||||
if (m_playerAttached && type == SDL_BINDING_BUTTON) {
|
||||
SDL_Joystick* joystick = m_sdlPlayer.joystick;
|
||||
if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
|
||||
SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
|
||||
SDL_JoystickUpdate();
|
||||
int numAxes = SDL_JoystickNumAxes(joystick);
|
||||
if (numAxes < 1) {
|
||||
|
@ -399,14 +406,19 @@ void InputController::bindAxis(uint32_t type, int axis, GamepadAxisEvent::Direct
|
|||
if (old) {
|
||||
description = *old;
|
||||
}
|
||||
int deadzone = 0;
|
||||
if (m_deadzones.size() > axis) {
|
||||
deadzone = m_deadzones[axis];
|
||||
}
|
||||
switch (direction) {
|
||||
case GamepadAxisEvent::NEGATIVE:
|
||||
description.lowDirection = key;
|
||||
description.deadLow = m_deadzones[axis] - AXIS_THRESHOLD;
|
||||
|
||||
description.deadLow = deadzone - AXIS_THRESHOLD;
|
||||
break;
|
||||
case GamepadAxisEvent::POSITIVE:
|
||||
description.highDirection = key;
|
||||
description.deadHigh = m_deadzones[axis] + AXIS_THRESHOLD;
|
||||
description.deadHigh = deadzone + AXIS_THRESHOLD;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
|
|
|
@ -52,6 +52,7 @@ public:
|
|||
|
||||
const GBAInputMap* map() const { return &m_inputMap; }
|
||||
|
||||
void updateJoysticks();
|
||||
int pollEvents();
|
||||
|
||||
static const int32_t AXIS_THRESHOLD = 0x3000;
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "GamepadButtonEvent.h"
|
||||
#include "ShortcutController.h"
|
||||
|
||||
#include <QFontMetrics>
|
||||
#include <QKeyEvent>
|
||||
|
||||
using namespace QGBA;
|
||||
|
@ -72,7 +73,8 @@ void KeyEditor::clearAxis() {
|
|||
|
||||
QSize KeyEditor::sizeHint() const {
|
||||
QSize hint = QLineEdit::sizeHint();
|
||||
hint.setWidth(40);
|
||||
QFontMetrics fm(font());
|
||||
hint.setWidth(fm.height() * 3);
|
||||
return hint;
|
||||
}
|
||||
|
||||
|
|
|
@ -70,15 +70,13 @@ LogView::LogView(LogController* log, QWidget* parent)
|
|||
|
||||
void LogView::postLog(int level, const QString& log) {
|
||||
QString line = QString("%1:\t%2").arg(LogController::toString(level)).arg(log);
|
||||
if (isVisible()) {
|
||||
m_ui.view->appendPlainText(line);
|
||||
} else {
|
||||
m_pendingLines.enqueue(line);
|
||||
}
|
||||
// TODO: Log to file
|
||||
m_pendingLines.enqueue(line);
|
||||
++m_lines;
|
||||
if (m_lines > m_lineLimit) {
|
||||
clearLine();
|
||||
}
|
||||
update();
|
||||
}
|
||||
|
||||
void LogView::clear() {
|
||||
|
@ -145,10 +143,11 @@ void LogView::setMaxLines(int limit) {
|
|||
}
|
||||
}
|
||||
|
||||
void LogView::showEvent(QShowEvent*) {
|
||||
void LogView::paintEvent(QPaintEvent* event) {
|
||||
while (!m_pendingLines.isEmpty()) {
|
||||
m_ui.view->appendPlainText(m_pendingLines.dequeue());
|
||||
}
|
||||
QWidget::paintEvent(event);
|
||||
}
|
||||
|
||||
void LogView::clearLine() {
|
||||
|
|
|
@ -38,7 +38,7 @@ private slots:
|
|||
void setMaxLines(int);
|
||||
|
||||
protected:
|
||||
virtual void showEvent(QShowEvent*) override;
|
||||
virtual void paintEvent(QPaintEvent*) override;
|
||||
|
||||
private:
|
||||
static const int DEFAULT_LINE_LIMIT = 1000;
|
||||
|
|
|
@ -14,7 +14,9 @@ extern "C" {
|
|||
|
||||
using namespace QGBA;
|
||||
|
||||
ROMInfo::ROMInfo(GameController* controller, QWidget* parent) {
|
||||
ROMInfo::ROMInfo(GameController* controller, QWidget* parent)
|
||||
: QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint)
|
||||
{
|
||||
m_ui.setupUi(this);
|
||||
|
||||
if (!controller->isLoaded()) {
|
||||
|
|
|
@ -81,9 +81,10 @@ void SensorView::jiggerer(QAbstractButton* button, void (InputController::*sette
|
|||
}
|
||||
|
||||
bool SensorView::event(QEvent* event) {
|
||||
if (event->type() == QEvent::WindowActivate) {
|
||||
QEvent::Type type = event->type();
|
||||
if (type == QEvent::WindowActivate || type == QEvent::Show) {
|
||||
m_input->stealFocus(this);
|
||||
} else if (event->type() == QEvent::WindowDeactivate) {
|
||||
} else if (type == QEvent::WindowDeactivate || type == QEvent::Hide) {
|
||||
m_input->releaseFocus(this);
|
||||
}
|
||||
return QWidget::event(event);
|
||||
|
|
|
@ -9,56 +9,89 @@
|
|||
#include "ConfigController.h"
|
||||
#include "Display.h"
|
||||
#include "GBAApp.h"
|
||||
#include "GBAKeyEditor.h"
|
||||
#include "InputController.h"
|
||||
#include "ShortcutView.h"
|
||||
|
||||
extern "C" {
|
||||
#include "gba/serialize.h"
|
||||
}
|
||||
|
||||
using namespace QGBA;
|
||||
|
||||
SettingsView::SettingsView(ConfigController* controller, QWidget* parent)
|
||||
SettingsView::SettingsView(ConfigController* controller, InputController* inputController, ShortcutController* shortcutController, QWidget* parent)
|
||||
: QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint)
|
||||
, m_controller(controller)
|
||||
{
|
||||
m_ui.setupUi(this);
|
||||
|
||||
loadSetting("bios", m_ui.bios);
|
||||
loadSetting("useBios", m_ui.useBios);
|
||||
loadSetting("skipBios", m_ui.skipBios);
|
||||
loadSetting("audioBuffers", m_ui.audioBufferSize);
|
||||
loadSetting("sampleRate", m_ui.sampleRate);
|
||||
loadSetting("videoSync", m_ui.videoSync);
|
||||
loadSetting("audioSync", m_ui.audioSync);
|
||||
loadSetting("frameskip", m_ui.frameskip);
|
||||
loadSetting("fpsTarget", m_ui.fpsTarget);
|
||||
loadSetting("lockAspectRatio", m_ui.lockAspectRatio);
|
||||
loadSetting("volume", m_ui.volume);
|
||||
loadSetting("mute", m_ui.mute);
|
||||
loadSetting("rewindEnable", m_ui.rewind);
|
||||
loadSetting("rewindBufferInterval", m_ui.rewindInterval);
|
||||
loadSetting("rewindBufferCapacity", m_ui.rewindCapacity);
|
||||
loadSetting("resampleVideo", m_ui.resampleVideo);
|
||||
loadSetting("allowOpposingDirections", m_ui.allowOpposingDirections);
|
||||
loadSetting("suspendScreensaver", m_ui.suspendScreensaver);
|
||||
reloadConfig();
|
||||
|
||||
double fastForwardRatio = loadSetting("fastForwardRatio").toDouble();
|
||||
if (fastForwardRatio <= 0) {
|
||||
m_ui.fastForwardUnbounded->setChecked(true);
|
||||
m_ui.fastForwardRatio->setEnabled(false);
|
||||
} else {
|
||||
m_ui.fastForwardUnbounded->setChecked(false);
|
||||
m_ui.fastForwardRatio->setEnabled(true);
|
||||
m_ui.fastForwardRatio->setValue(fastForwardRatio);
|
||||
if (m_ui.savegamePath->text().isEmpty()) {
|
||||
m_ui.savegameSameDir->setChecked(true);
|
||||
}
|
||||
connect(m_ui.fastForwardUnbounded, &QAbstractButton::toggled, [this](bool checked) {
|
||||
m_ui.fastForwardRatio->setEnabled(!checked);
|
||||
connect(m_ui.savegameSameDir, &QAbstractButton::toggled, [this] (bool e) {
|
||||
if (e) {
|
||||
m_ui.savegamePath->clear();
|
||||
}
|
||||
});
|
||||
connect(m_ui.savegameBrowse, &QAbstractButton::pressed, [this] () {
|
||||
QString path = GBAApp::app()->getOpenDirectoryName(this, "Select directory");
|
||||
if (!path.isNull()) {
|
||||
m_ui.savegameSameDir->setChecked(false);
|
||||
m_ui.savegamePath->setText(path);
|
||||
}
|
||||
});
|
||||
|
||||
QString idleOptimization = loadSetting("idleOptimization");
|
||||
if (idleOptimization == "ignore") {
|
||||
m_ui.idleOptimization->setCurrentIndex(0);
|
||||
} else if (idleOptimization == "remove") {
|
||||
m_ui.idleOptimization->setCurrentIndex(1);
|
||||
} else if (idleOptimization == "detect") {
|
||||
m_ui.idleOptimization->setCurrentIndex(2);
|
||||
if (m_ui.savestatePath->text().isEmpty()) {
|
||||
m_ui.savestateSameDir->setChecked(true);
|
||||
}
|
||||
connect(m_ui.savestateSameDir, &QAbstractButton::toggled, [this] (bool e) {
|
||||
if (e) {
|
||||
m_ui.savestatePath->clear();
|
||||
}
|
||||
});
|
||||
connect(m_ui.savestateBrowse, &QAbstractButton::pressed, [this] () {
|
||||
QString path = GBAApp::app()->getOpenDirectoryName(this, "Select directory");
|
||||
if (!path.isNull()) {
|
||||
m_ui.savestateSameDir->setChecked(false);
|
||||
m_ui.savestatePath->setText(path);
|
||||
}
|
||||
});
|
||||
|
||||
if (m_ui.screenshotPath->text().isEmpty()) {
|
||||
m_ui.screenshotSameDir->setChecked(true);
|
||||
}
|
||||
connect(m_ui.screenshotSameDir, &QAbstractButton::toggled, [this] (bool e) {
|
||||
if (e) {
|
||||
m_ui.screenshotPath->clear();
|
||||
}
|
||||
});
|
||||
connect(m_ui.screenshotBrowse, &QAbstractButton::pressed, [this] () {
|
||||
QString path = GBAApp::app()->getOpenDirectoryName(this, "Select directory");
|
||||
if (!path.isNull()) {
|
||||
m_ui.screenshotSameDir->setChecked(false);
|
||||
m_ui.screenshotPath->setText(path);
|
||||
}
|
||||
});
|
||||
|
||||
if (m_ui.patchPath->text().isEmpty()) {
|
||||
m_ui.patchSameDir->setChecked(true);
|
||||
}
|
||||
connect(m_ui.patchSameDir, &QAbstractButton::toggled, [this] (bool e) {
|
||||
if (e) {
|
||||
m_ui.patchPath->clear();
|
||||
}
|
||||
});
|
||||
connect(m_ui.patchBrowse, &QAbstractButton::pressed, [this] () {
|
||||
QString path = GBAApp::app()->getOpenDirectoryName(this, "Select directory");
|
||||
if (!path.isNull()) {
|
||||
m_ui.patchSameDir->setChecked(false);
|
||||
m_ui.patchPath->setText(path);
|
||||
}
|
||||
});
|
||||
|
||||
// TODO: Move to reloadConfig()
|
||||
QVariant audioDriver = m_controller->getQtOption("audioDriver");
|
||||
#ifdef BUILD_QT_MULTIMEDIA
|
||||
m_ui.audioDriver->addItem(tr("Qt Multimedia"), static_cast<int>(AudioProcessor::Driver::QT_MULTIMEDIA));
|
||||
|
@ -74,19 +107,27 @@ SettingsView::SettingsView(ConfigController* controller, QWidget* parent)
|
|||
}
|
||||
#endif
|
||||
|
||||
// TODO: Move to reloadConfig()
|
||||
QVariant displayDriver = m_controller->getQtOption("displayDriver");
|
||||
m_ui.displayDriver->addItem(tr("Software (Qt)"), static_cast<int>(Display::Driver::QT));
|
||||
if (!displayDriver.isNull() && displayDriver.toInt() == static_cast<int>(Display::Driver::QT)) {
|
||||
m_ui.displayDriver->setCurrentIndex(m_ui.displayDriver->count() - 1);
|
||||
}
|
||||
|
||||
#ifdef BUILD_GL
|
||||
#if defined(BUILD_GL) || defined(BUILD_GLES2) || defined(USE_EPOXY)
|
||||
m_ui.displayDriver->addItem(tr("OpenGL"), static_cast<int>(Display::Driver::OPENGL));
|
||||
if (displayDriver.isNull() || displayDriver.toInt() == static_cast<int>(Display::Driver::OPENGL)) {
|
||||
m_ui.displayDriver->setCurrentIndex(m_ui.displayDriver->count() - 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef BUILD_GL
|
||||
m_ui.displayDriver->addItem(tr("OpenGL (force version 1.x)"), static_cast<int>(Display::Driver::OPENGL1));
|
||||
if (displayDriver.isNull() || displayDriver.toInt() == static_cast<int>(Display::Driver::OPENGL1)) {
|
||||
m_ui.displayDriver->setCurrentIndex(m_ui.displayDriver->count() - 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
connect(m_ui.biosBrowse, SIGNAL(clicked()), this, SLOT(selectBios()));
|
||||
connect(m_ui.buttonBox, SIGNAL(accepted()), this, SLOT(updateConfig()));
|
||||
connect(m_ui.buttonBox, &QDialogButtonBox::clicked, [this](QAbstractButton* button) {
|
||||
|
@ -94,6 +135,26 @@ SettingsView::SettingsView(ConfigController* controller, QWidget* parent)
|
|||
updateConfig();
|
||||
}
|
||||
});
|
||||
|
||||
GBAKeyEditor* editor = new GBAKeyEditor(inputController, InputController::KEYBOARD, QString(), this);
|
||||
m_ui.stackedWidget->addWidget(editor);
|
||||
m_ui.tabs->addItem("Keyboard");
|
||||
connect(m_ui.buttonBox, SIGNAL(accepted()), editor, SLOT(save()));
|
||||
|
||||
#ifdef BUILD_SDL
|
||||
inputController->recalibrateAxes();
|
||||
const char* profile = inputController->profileForType(SDL_BINDING_BUTTON);
|
||||
editor = new GBAKeyEditor(inputController, SDL_BINDING_BUTTON, profile);
|
||||
m_ui.stackedWidget->addWidget(editor);
|
||||
m_ui.tabs->addItem("Controllers");
|
||||
connect(m_ui.buttonBox, SIGNAL(accepted()), editor, SLOT(save()));
|
||||
#endif
|
||||
|
||||
ShortcutView* shortcutView = new ShortcutView();
|
||||
shortcutView->setController(shortcutController);
|
||||
shortcutView->setInputController(inputController);
|
||||
m_ui.stackedWidget->addWidget(shortcutView);
|
||||
m_ui.tabs->addItem("Shortcuts");
|
||||
}
|
||||
|
||||
void SettingsView::selectBios() {
|
||||
|
@ -103,6 +164,14 @@ void SettingsView::selectBios() {
|
|||
}
|
||||
}
|
||||
|
||||
void SettingsView::recalculateRewind() {
|
||||
int interval = m_ui.rewindInterval->value();
|
||||
int capacity = m_ui.rewindCapacity->value();
|
||||
double duration = m_ui.fpsTarget->value();
|
||||
m_ui.rewindDuration->setValue(interval * capacity / duration);
|
||||
|
||||
}
|
||||
|
||||
void SettingsView::updateConfig() {
|
||||
saveSetting("bios", m_ui.bios);
|
||||
saveSetting("useBios", m_ui.useBios);
|
||||
|
@ -122,6 +191,11 @@ void SettingsView::updateConfig() {
|
|||
saveSetting("resampleVideo", m_ui.resampleVideo);
|
||||
saveSetting("allowOpposingDirections", m_ui.allowOpposingDirections);
|
||||
saveSetting("suspendScreensaver", m_ui.suspendScreensaver);
|
||||
saveSetting("pauseOnFocusLost", m_ui.pauseOnFocusLost);
|
||||
saveSetting("savegamePath", m_ui.savegamePath);
|
||||
saveSetting("savestatePath", m_ui.savestatePath);
|
||||
saveSetting("screenshotPath", m_ui.screenshotPath);
|
||||
saveSetting("patchPath", m_ui.patchPath);
|
||||
|
||||
if (m_ui.fastForwardUnbounded->isChecked()) {
|
||||
saveSetting("fastForwardRatio", "-1");
|
||||
|
@ -141,6 +215,18 @@ void SettingsView::updateConfig() {
|
|||
break;
|
||||
}
|
||||
|
||||
int loadState = 0;
|
||||
loadState |= m_ui.loadStateScreenshot->isChecked() ? SAVESTATE_SCREENSHOT : 0;
|
||||
loadState |= m_ui.loadStateSave->isChecked() ? SAVESTATE_SAVEDATA : 0;
|
||||
loadState |= m_ui.loadStateCheats->isChecked() ? SAVESTATE_CHEATS : 0;
|
||||
saveSetting("loadStateExtdata", loadState);
|
||||
|
||||
int saveState = 0;
|
||||
saveState |= m_ui.saveStateScreenshot->isChecked() ? SAVESTATE_SCREENSHOT : 0;
|
||||
saveState |= m_ui.saveStateSave->isChecked() ? SAVESTATE_SAVEDATA : 0;
|
||||
saveState |= m_ui.saveStateCheats->isChecked() ? SAVESTATE_CHEATS : 0;
|
||||
saveSetting("saveStateExtdata", saveState);
|
||||
|
||||
QVariant audioDriver = m_ui.audioDriver->itemData(m_ui.audioDriver->currentIndex());
|
||||
if (audioDriver != m_controller->getQtOption("audioDriver")) {
|
||||
m_controller->setQtOption("audioDriver", audioDriver);
|
||||
|
@ -157,9 +243,76 @@ void SettingsView::updateConfig() {
|
|||
|
||||
m_controller->write();
|
||||
|
||||
emit pathsChanged();
|
||||
emit biosLoaded(m_ui.bios->text());
|
||||
}
|
||||
|
||||
void SettingsView::reloadConfig() {
|
||||
loadSetting("bios", m_ui.bios);
|
||||
loadSetting("useBios", m_ui.useBios);
|
||||
loadSetting("skipBios", m_ui.skipBios);
|
||||
loadSetting("audioBuffers", m_ui.audioBufferSize);
|
||||
loadSetting("sampleRate", m_ui.sampleRate);
|
||||
loadSetting("videoSync", m_ui.videoSync);
|
||||
loadSetting("audioSync", m_ui.audioSync);
|
||||
loadSetting("frameskip", m_ui.frameskip);
|
||||
loadSetting("fpsTarget", m_ui.fpsTarget);
|
||||
loadSetting("lockAspectRatio", m_ui.lockAspectRatio);
|
||||
loadSetting("volume", m_ui.volume);
|
||||
loadSetting("mute", m_ui.mute);
|
||||
loadSetting("rewindEnable", m_ui.rewind);
|
||||
loadSetting("rewindBufferInterval", m_ui.rewindInterval);
|
||||
loadSetting("rewindBufferCapacity", m_ui.rewindCapacity);
|
||||
loadSetting("resampleVideo", m_ui.resampleVideo);
|
||||
loadSetting("allowOpposingDirections", m_ui.allowOpposingDirections);
|
||||
loadSetting("suspendScreensaver", m_ui.suspendScreensaver);
|
||||
loadSetting("pauseOnFocusLost", m_ui.pauseOnFocusLost);
|
||||
loadSetting("savegamePath", m_ui.savegamePath);
|
||||
loadSetting("savestatePath", m_ui.savestatePath);
|
||||
loadSetting("screenshotPath", m_ui.screenshotPath);
|
||||
loadSetting("patchPath", m_ui.patchPath);
|
||||
|
||||
double fastForwardRatio = loadSetting("fastForwardRatio").toDouble();
|
||||
if (fastForwardRatio <= 0) {
|
||||
m_ui.fastForwardUnbounded->setChecked(true);
|
||||
m_ui.fastForwardRatio->setEnabled(false);
|
||||
} else {
|
||||
m_ui.fastForwardUnbounded->setChecked(false);
|
||||
m_ui.fastForwardRatio->setEnabled(true);
|
||||
m_ui.fastForwardRatio->setValue(fastForwardRatio);
|
||||
}
|
||||
|
||||
connect(m_ui.rewindInterval, SIGNAL(valueChanged(int)), this, SLOT(recalculateRewind()));
|
||||
connect(m_ui.rewindCapacity, SIGNAL(valueChanged(int)), this, SLOT(recalculateRewind()));
|
||||
connect(m_ui.fpsTarget, SIGNAL(valueChanged(double)), this, SLOT(recalculateRewind()));
|
||||
|
||||
QString idleOptimization = loadSetting("idleOptimization");
|
||||
if (idleOptimization == "ignore") {
|
||||
m_ui.idleOptimization->setCurrentIndex(0);
|
||||
} else if (idleOptimization == "remove") {
|
||||
m_ui.idleOptimization->setCurrentIndex(1);
|
||||
} else if (idleOptimization == "detect") {
|
||||
m_ui.idleOptimization->setCurrentIndex(2);
|
||||
}
|
||||
|
||||
bool ok;
|
||||
int loadState = loadSetting("loadStateExtdata").toInt(&ok);
|
||||
if (!ok) {
|
||||
loadState = SAVESTATE_SCREENSHOT;
|
||||
}
|
||||
m_ui.loadStateScreenshot->setChecked(loadState & SAVESTATE_SCREENSHOT);
|
||||
m_ui.loadStateSave->setChecked(loadState & SAVESTATE_SAVEDATA);
|
||||
m_ui.loadStateCheats->setChecked(loadState & SAVESTATE_CHEATS);
|
||||
|
||||
int saveState = loadSetting("saveStateExtdata").toInt(&ok);
|
||||
if (!ok) {
|
||||
saveState = SAVESTATE_SCREENSHOT | SAVESTATE_SAVEDATA | SAVESTATE_CHEATS;
|
||||
}
|
||||
m_ui.saveStateScreenshot->setChecked(saveState & SAVESTATE_SCREENSHOT);
|
||||
m_ui.saveStateSave->setChecked(saveState & SAVESTATE_SAVEDATA);
|
||||
m_ui.saveStateCheats->setChecked(saveState & SAVESTATE_CHEATS);
|
||||
}
|
||||
|
||||
void SettingsView::saveSetting(const char* key, const QAbstractButton* field) {
|
||||
m_controller->setOption(key, field->isChecked());
|
||||
m_controller->updateOption(key);
|
||||
|
|
|
@ -13,26 +13,32 @@
|
|||
namespace QGBA {
|
||||
|
||||
class ConfigController;
|
||||
class InputController;
|
||||
class ShortcutController;
|
||||
|
||||
class SettingsView : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
SettingsView(ConfigController* controller, QWidget* parent = nullptr);
|
||||
SettingsView(ConfigController* controller, InputController* inputController, ShortcutController* shortcutController, QWidget* parent = nullptr);
|
||||
|
||||
signals:
|
||||
void biosLoaded(const QString&);
|
||||
void audioDriverChanged();
|
||||
void displayDriverChanged();
|
||||
void pathsChanged();
|
||||
|
||||
private slots:
|
||||
void selectBios();
|
||||
void recalculateRewind();
|
||||
void updateConfig();
|
||||
void reloadConfig();
|
||||
|
||||
private:
|
||||
Ui::SettingsView m_ui;
|
||||
|
||||
ConfigController* m_controller;
|
||||
InputController* m_input;
|
||||
|
||||
void saveSetting(const char* key, const QAbstractButton*);
|
||||
void saveSetting(const char* key, const QComboBox*);
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>707</width>
|
||||
<height>420</height>
|
||||
<width>544</width>
|
||||
<height>425</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
|
@ -19,16 +19,65 @@
|
|||
<property name="windowTitle">
|
||||
<string>Settings</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetFixedSize</enum>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item row="2" column="0" colspan="2">
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QListWidget" name="tabs">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>140</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="currentRow">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Audio/Video</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Emulation</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Savestates</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Paths</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QStackedWidget" name="stackedWidget">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="stackedWidgetPage1">
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<property name="fieldGrowthPolicy">
|
||||
<enum>QFormLayout::ExpandingFieldsGrow</enum>
|
||||
<enum>QFormLayout::FieldsStayAtSizeHint</enum>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_14">
|
||||
|
@ -120,6 +169,50 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_14">
|
||||
<item>
|
||||
<widget class="QComboBox" name="sampleRate">
|
||||
<property name="editable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="currentText" stdset="0">
|
||||
<string>44100</string>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>22050</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>32000</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>44100</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>48000</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_20">
|
||||
<property name="text">
|
||||
<string>Hz</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_17">
|
||||
<property name="text">
|
||||
|
@ -131,6 +224,12 @@
|
|||
<layout class="QHBoxLayout" name="horizontalLayout_6">
|
||||
<item>
|
||||
<widget class="QSlider" name="volume">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>128</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>256</number>
|
||||
</property>
|
||||
|
@ -155,7 +254,7 @@
|
|||
</layout>
|
||||
</item>
|
||||
<item row="4" column="0" colspan="2">
|
||||
<widget class="Line" name="line">
|
||||
<widget class="Line" name="line_4">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
|
@ -238,7 +337,7 @@
|
|||
</layout>
|
||||
</item>
|
||||
<item row="8" column="0" colspan="2">
|
||||
<widget class="Line" name="line_4">
|
||||
<widget class="Line" name="line_5">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
|
@ -283,61 +382,13 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_14">
|
||||
<item>
|
||||
<widget class="QComboBox" name="sampleRate">
|
||||
<property name="editable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="currentText" stdset="0">
|
||||
<string>44100</string>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>22050</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>32000</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>44100</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>48000</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_20">
|
||||
<property name="text">
|
||||
<string>Hz</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_5">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</widget>
|
||||
<widget class="QWidget" name="stackedWidgetPage2">
|
||||
<layout class="QFormLayout" name="formLayout_2">
|
||||
<property name="fieldGrowthPolicy">
|
||||
<enum>QFormLayout::FieldsStayAtSizeHint</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QFormLayout" name="formLayout_4">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
|
@ -366,6 +417,16 @@
|
|||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QCheckBox" name="useBios">
|
||||
<property name="text">
|
||||
<string>Use BIOS file if found</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QCheckBox" name="skipBios">
|
||||
<property name="text">
|
||||
|
@ -373,120 +434,21 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0" colspan="2">
|
||||
<widget class="Line" name="line_3">
|
||||
<item row="3" column="0" colspan="2">
|
||||
<widget class="Line" name="line">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<widget class="QCheckBox" name="rewind">
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_18">
|
||||
<property name="text">
|
||||
<string>Enable rewind</string>
|
||||
<string>Fast forward speed</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Create rewind state:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_12">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>Every</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="rewindInterval"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<string>frames</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<widget class="QLabel" name="label_8">
|
||||
<property name="text">
|
||||
<string>Rewind history:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_13">
|
||||
<item>
|
||||
<widget class="QSpinBox" name="rewindCapacity"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_7">
|
||||
<property name="text">
|
||||
<string>states</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="11" column="0" colspan="2">
|
||||
<widget class="Line" name="line_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="12" column="1">
|
||||
<widget class="QCheckBox" name="allowOpposingDirections">
|
||||
<property name="text">
|
||||
<string>Allow opposing input directions</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="13" column="1">
|
||||
<widget class="QCheckBox" name="suspendScreensaver">
|
||||
<property name="text">
|
||||
<string>Suspend screensaver</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="14" column="0">
|
||||
<widget class="QLabel" name="label_15">
|
||||
<property name="text">
|
||||
<string>Idle loops</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="14" column="1">
|
||||
<widget class="QComboBox" name="idleOptimization">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Run all</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Remove known</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Detect and remove</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="1">
|
||||
<item row="4" column="1">
|
||||
<widget class="QDoubleSpinBox" name="fastForwardRatio">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
|
@ -508,14 +470,7 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="0">
|
||||
<widget class="QLabel" name="label_18">
|
||||
<property name="text">
|
||||
<string>Fast forward speed</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="1">
|
||||
<item row="5" column="1">
|
||||
<widget class="QCheckBox" name="fastForwardUnbounded">
|
||||
<property name="text">
|
||||
<string>Unbounded</string>
|
||||
|
@ -525,32 +480,423 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="0" colspan="2">
|
||||
<widget class="Line" name="line_6">
|
||||
<item row="6" column="0" colspan="2">
|
||||
<widget class="Line" name="line_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QCheckBox" name="useBios">
|
||||
<item row="7" column="1">
|
||||
<widget class="QCheckBox" name="allowOpposingDirections">
|
||||
<property name="text">
|
||||
<string>Use BIOS file</string>
|
||||
<string>Allow opposing input directions</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="1">
|
||||
<widget class="QCheckBox" name="suspendScreensaver">
|
||||
<property name="text">
|
||||
<string>Suspend screensaver</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="1">
|
||||
<widget class="QCheckBox" name="pauseOnFocusLost">
|
||||
<property name="text">
|
||||
<string>Pause when inactive</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="0">
|
||||
<widget class="QLabel" name="label_15">
|
||||
<property name="text">
|
||||
<string>Idle loops</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="1">
|
||||
<widget class="QComboBox" name="idleOptimization">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Run all</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Remove known</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Detect and remove</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QWidget" name="page_2">
|
||||
<layout class="QFormLayout" name="formLayout_4">
|
||||
<property name="fieldGrowthPolicy">
|
||||
<enum>QFormLayout::FieldsStayAtSizeHint</enum>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_24">
|
||||
<property name="text">
|
||||
<string>Save extra data</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QCheckBox" name="saveStateScreenshot">
|
||||
<property name="text">
|
||||
<string>Screenshot</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QCheckBox" name="saveStateSave">
|
||||
<property name="text">
|
||||
<string>Save data</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QCheckBox" name="saveStateCheats">
|
||||
<property name="text">
|
||||
<string>Cheat codes</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0" colspan="2">
|
||||
<widget class="Line" name="line_8">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="label_25">
|
||||
<property name="text">
|
||||
<string>Load extra data</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<widget class="QCheckBox" name="loadStateScreenshot">
|
||||
<property name="text">
|
||||
<string>Screenshot</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="1">
|
||||
<widget class="QCheckBox" name="loadStateSave">
|
||||
<property name="text">
|
||||
<string>Save data</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="1">
|
||||
<widget class="QCheckBox" name="loadStateCheats">
|
||||
<property name="text">
|
||||
<string>Cheat codes</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="0" colspan="2">
|
||||
<widget class="Line" name="line_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="1">
|
||||
<widget class="QCheckBox" name="rewind">
|
||||
<property name="text">
|
||||
<string>Enable rewind</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Create rewind state:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_12">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>Every</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="rewindInterval"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<string>frames</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="11" column="0">
|
||||
<widget class="QLabel" name="label_8">
|
||||
<property name="text">
|
||||
<string>Rewind history:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="11" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_13">
|
||||
<item>
|
||||
<widget class="QSpinBox" name="rewindCapacity"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_7">
|
||||
<property name="text">
|
||||
<string>states</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="12" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_5">
|
||||
<item>
|
||||
<widget class="QDoubleSpinBox" name="rewindDuration">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>999.990000000000009</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_26">
|
||||
<property name="text">
|
||||
<string>seconds</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="page">
|
||||
<layout class="QFormLayout" name="formLayout_3">
|
||||
<property name="fieldGrowthPolicy">
|
||||
<enum>QFormLayout::FieldsStayAtSizeHint</enum>
|
||||
</property>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_21">
|
||||
<property name="text">
|
||||
<string>Save games</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="savegamePath">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>170</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="savegameBrowse">
|
||||
<property name="text">
|
||||
<string>Browse</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QCheckBox" name="savegameSameDir">
|
||||
<property name="text">
|
||||
<string>Same directory as the ROM</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="2">
|
||||
<widget class="Line" name="line_7">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_22">
|
||||
<property name="text">
|
||||
<string>Save states</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="savestatePath">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>170</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="savestateBrowse">
|
||||
<property name="text">
|
||||
<string>Browse</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<widget class="QCheckBox" name="savestateSameDir">
|
||||
<property name="text">
|
||||
<string>Same directory as the ROM</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0" colspan="2">
|
||||
<widget class="Line" name="line_6">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<widget class="QLabel" name="label_23">
|
||||
<property name="text">
|
||||
<string>Screenshots</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_7">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="screenshotPath">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>170</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="screenshotBrowse">
|
||||
<property name="text">
|
||||
<string>Browse</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="8" column="1">
|
||||
<widget class="QCheckBox" name="screenshotSameDir">
|
||||
<property name="text">
|
||||
<string>Same directory as the ROM</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="0" colspan="2">
|
||||
<widget class="Line" name="line_15">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="0">
|
||||
<widget class="QLabel" name="label_47">
|
||||
<property name="text">
|
||||
<string>Patches</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_26">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="patchPath">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>170</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="patchBrowse">
|
||||
<property name="text">
|
||||
<string>Browse</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="11" column="1">
|
||||
<widget class="QCheckBox" name="patchSameDir">
|
||||
<property name="text">
|
||||
<string>Same directory as the ROM</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
|
@ -590,18 +936,98 @@
|
|||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>useBios</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>skipBios</receiver>
|
||||
<slot>setEnabled(bool)</slot>
|
||||
<sender>tabs</sender>
|
||||
<signal>currentRowChanged(int)</signal>
|
||||
<receiver>stackedWidget</receiver>
|
||||
<slot>setCurrentIndex(int)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>520</x>
|
||||
<y>62</y>
|
||||
<x>61</x>
|
||||
<y>209</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>525</x>
|
||||
<y>83</y>
|
||||
<x>315</x>
|
||||
<y>209</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>savegameSameDir</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>savegamePath</receiver>
|
||||
<slot>setDisabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>392</x>
|
||||
<y>82</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>366</x>
|
||||
<y>48</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>savestateSameDir</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>savestatePath</receiver>
|
||||
<slot>setDisabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>392</x>
|
||||
<y>161</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>366</x>
|
||||
<y>127</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>screenshotSameDir</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>screenshotPath</receiver>
|
||||
<slot>setDisabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>392</x>
|
||||
<y>240</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>366</x>
|
||||
<y>206</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>patchSameDir</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>patchPath</receiver>
|
||||
<slot>setDisabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>345</x>
|
||||
<y>319</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>340</x>
|
||||
<y>285</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>fastForwardUnbounded</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>fastForwardRatio</receiver>
|
||||
<slot>setDisabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>338</x>
|
||||
<y>163</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>327</x>
|
||||
<y>135</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
|
|
|
@ -28,7 +28,7 @@ extern "C" {
|
|||
using namespace QGBA;
|
||||
|
||||
ShaderSelector::ShaderSelector(Display* display, ConfigController* config, QWidget* parent)
|
||||
: QDialog(parent)
|
||||
: QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint)
|
||||
, m_display(display)
|
||||
, m_config(config)
|
||||
, m_shaderPath("")
|
||||
|
|
|
@ -137,10 +137,13 @@ void ShortcutController::addFunctions(QMenu* menu, std::function<void()> press,
|
|||
smenu->addFunctions(qMakePair(press, release), shortcut, visibleName, name);
|
||||
endInsertRows();
|
||||
ShortcutItem* item = &smenu->items().last();
|
||||
bool loadedShortcut = false;
|
||||
if (m_config) {
|
||||
loadShortcuts(item);
|
||||
loadedShortcut = loadShortcuts(item);
|
||||
}
|
||||
if (!loadedShortcut && !m_heldKeys.contains(shortcut)) {
|
||||
m_heldKeys[shortcut] = item;
|
||||
}
|
||||
m_heldKeys[shortcut] = item;
|
||||
emit dataChanged(createIndex(smenu->items().count() - 1, 0, item),
|
||||
createIndex(smenu->items().count() - 1, 2, item));
|
||||
}
|
||||
|
@ -387,10 +390,11 @@ bool ShortcutController::eventFilter(QObject*, QEvent* event) {
|
|||
return false;
|
||||
}
|
||||
|
||||
void ShortcutController::loadShortcuts(ShortcutItem* item) {
|
||||
bool ShortcutController::loadShortcuts(ShortcutItem* item) {
|
||||
if (item->name().isNull()) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
loadGamepadShortcuts(item);
|
||||
QVariant shortcut = m_config->getQtOption(item->name(), KEY_SECTION);
|
||||
if (!shortcut.isNull()) {
|
||||
if (shortcut.toString().endsWith("+")) {
|
||||
|
@ -398,8 +402,9 @@ void ShortcutController::loadShortcuts(ShortcutItem* item) {
|
|||
} else {
|
||||
updateKey(item, QKeySequence(shortcut.toString())[0]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
loadGamepadShortcuts(item);
|
||||
return false;
|
||||
}
|
||||
|
||||
void ShortcutController::loadGamepadShortcuts(ShortcutItem* item) {
|
||||
|
|
|
@ -128,7 +128,7 @@ protected:
|
|||
private:
|
||||
ShortcutItem* itemAt(const QModelIndex& index);
|
||||
const ShortcutItem* itemAt(const QModelIndex& index) const;
|
||||
void loadShortcuts(ShortcutItem*);
|
||||
bool loadShortcuts(ShortcutItem*);
|
||||
void loadGamepadShortcuts(ShortcutItem*);
|
||||
void onSubitems(ShortcutItem*, std::function<void(ShortcutItem*)> func);
|
||||
void updateKey(ShortcutItem* item, int keySequence);
|
||||
|
|
|
@ -37,6 +37,10 @@ ShortcutView::ShortcutView(QWidget* parent)
|
|||
connect(m_ui.clearButton, SIGNAL(clicked()), this, SLOT(clear()));
|
||||
}
|
||||
|
||||
ShortcutView::~ShortcutView() {
|
||||
m_input->releaseFocus(this);
|
||||
}
|
||||
|
||||
void ShortcutView::setController(ShortcutController* controller) {
|
||||
m_controller = controller;
|
||||
m_ui.shortcutTable->setModel(controller);
|
||||
|
@ -117,9 +121,10 @@ void ShortcutView::closeEvent(QCloseEvent*) {
|
|||
|
||||
bool ShortcutView::event(QEvent* event) {
|
||||
if (m_input) {
|
||||
if (event->type() == QEvent::WindowActivate) {
|
||||
QEvent::Type type = event->type();
|
||||
if (type == QEvent::WindowActivate || type == QEvent::Show) {
|
||||
m_input->stealFocus(this);
|
||||
} else if (event->type() == QEvent::WindowDeactivate) {
|
||||
} else if (type == QEvent::WindowDeactivate || type == QEvent::Hide) {
|
||||
m_input->releaseFocus(this);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ Q_OBJECT
|
|||
|
||||
public:
|
||||
ShortcutView(QWidget* parent = nullptr);
|
||||
~ShortcutView();
|
||||
|
||||
void setController(ShortcutController* controller);
|
||||
void setInputController(InputController* input);
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
#include "Display.h"
|
||||
#include "GameController.h"
|
||||
#include "GBAApp.h"
|
||||
#include "GBAKeyEditor.h"
|
||||
#include "GDBController.h"
|
||||
#include "GDBWindow.h"
|
||||
#include "GIFView.h"
|
||||
|
@ -36,7 +35,6 @@
|
|||
#include "SettingsView.h"
|
||||
#include "ShaderSelector.h"
|
||||
#include "ShortcutController.h"
|
||||
#include "ShortcutView.h"
|
||||
#include "VideoView.h"
|
||||
|
||||
extern "C" {
|
||||
|
@ -74,6 +72,7 @@ Window::Window(ConfigController* config, int playerId, QWidget* parent)
|
|||
, m_shortcutController(new ShortcutController(this))
|
||||
, m_playerId(playerId)
|
||||
, m_fullscreenOnStart(false)
|
||||
, m_autoresume(false)
|
||||
{
|
||||
setFocusPolicy(Qt::StrongFocus);
|
||||
setAcceptDrops(true);
|
||||
|
@ -105,7 +104,7 @@ Window::Window(ConfigController* config, int playerId, QWidget* parent)
|
|||
connect(m_controller, SIGNAL(rewound(GBAThread*)), m_display, SLOT(forceDraw()));
|
||||
connect(m_controller, &GameController::gamePaused, [this]() {
|
||||
QImage currentImage(reinterpret_cast<const uchar*>(m_controller->drawContext()), VIDEO_HORIZONTAL_PIXELS,
|
||||
VIDEO_VERTICAL_PIXELS, 1024, QImage::Format_RGBX8888);
|
||||
VIDEO_VERTICAL_PIXELS, VIDEO_HORIZONTAL_PIXELS * BYTES_PER_PIXEL, QImage::Format_RGBX8888);
|
||||
QPixmap pixmap;
|
||||
pixmap.convertFromImage(currentImage);
|
||||
m_screenWidget->setPixmap(pixmap);
|
||||
|
@ -142,6 +141,7 @@ Window::Window(ConfigController* config, int playerId, QWidget* parent)
|
|||
connect(this, SIGNAL(sampleRateChanged(unsigned)), m_controller, SLOT(setAudioSampleRate(unsigned)));
|
||||
connect(this, SIGNAL(fpsTargetChanged(float)), m_controller, SLOT(setFPSTarget(float)));
|
||||
connect(&m_fpsTimer, SIGNAL(timeout()), this, SLOT(showFPS()));
|
||||
connect(&m_focusCheck, SIGNAL(timeout()), this, SLOT(focusCheck()));
|
||||
connect(m_display, &Display::hideCursor, [this]() {
|
||||
if (static_cast<QStackedLayout*>(m_screenWidget->layout())->currentWidget() == m_display) {
|
||||
m_screenWidget->setCursor(Qt::BlankCursor);
|
||||
|
@ -154,6 +154,7 @@ Window::Window(ConfigController* config, int playerId, QWidget* parent)
|
|||
|
||||
m_log.setLevels(GBA_LOG_WARN | GBA_LOG_ERROR | GBA_LOG_FATAL | GBA_LOG_STATUS);
|
||||
m_fpsTimer.setInterval(FPS_TIMER_INTERVAL);
|
||||
m_focusCheck.setInterval(200);
|
||||
|
||||
m_shortcutController->setConfigController(m_config);
|
||||
setupMenu(menuBar());
|
||||
|
@ -179,7 +180,7 @@ void Window::argumentsPassed(GBAArguments* args) {
|
|||
}
|
||||
|
||||
if (args->fname) {
|
||||
m_controller->loadGame(args->fname, args->dirmode);
|
||||
m_controller->loadGame(args->fname);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -197,16 +198,7 @@ void Window::setConfig(ConfigController* config) {
|
|||
|
||||
void Window::loadConfig() {
|
||||
const GBAOptions* opts = m_config->options();
|
||||
|
||||
m_log.setLevels(opts->logLevel);
|
||||
|
||||
m_controller->setOptions(opts);
|
||||
m_display->lockAspectRatio(opts->lockAspectRatio);
|
||||
m_display->filter(opts->resampleVideo);
|
||||
|
||||
if (opts->bios) {
|
||||
m_controller->loadBIOS(opts->bios);
|
||||
}
|
||||
reloadConfig();
|
||||
|
||||
// TODO: Move these to ConfigController
|
||||
if (opts->fpsTarget) {
|
||||
|
@ -238,14 +230,41 @@ void Window::loadConfig() {
|
|||
}
|
||||
}
|
||||
|
||||
m_inputController.setScreensaverSuspendable(opts->suspendScreensaver);
|
||||
|
||||
m_mruFiles = m_config->getMRU();
|
||||
updateMRU();
|
||||
|
||||
m_inputController.setConfiguration(m_config);
|
||||
}
|
||||
|
||||
void Window::reloadConfig() {
|
||||
const GBAOptions* opts = m_config->options();
|
||||
|
||||
m_log.setLevels(opts->logLevel);
|
||||
|
||||
QString saveStateExtdata = m_config->getOption("saveStateExtdata");
|
||||
bool ok;
|
||||
int flags = saveStateExtdata.toInt(&ok);
|
||||
if (ok) {
|
||||
m_controller->setSaveStateExtdata(flags);
|
||||
}
|
||||
|
||||
QString loadStateExtdata = m_config->getOption("loadStateExtdata");
|
||||
flags = loadStateExtdata.toInt(&ok);
|
||||
if (ok) {
|
||||
m_controller->setLoadStateExtdata(flags);
|
||||
}
|
||||
|
||||
m_controller->setOptions(opts);
|
||||
m_display->lockAspectRatio(opts->lockAspectRatio);
|
||||
m_display->filter(opts->resampleVideo);
|
||||
|
||||
if (opts->bios) {
|
||||
m_controller->loadBIOS(opts->bios);
|
||||
}
|
||||
|
||||
m_inputController.setScreensaverSuspendable(opts->suspendScreensaver);
|
||||
}
|
||||
|
||||
void Window::saveConfig() {
|
||||
m_inputController.saveConfiguration();
|
||||
m_config->write();
|
||||
|
@ -254,7 +273,7 @@ void Window::saveConfig() {
|
|||
void Window::selectROM() {
|
||||
QStringList formats{
|
||||
"*.gba",
|
||||
#ifdef USE_LIBZIP
|
||||
#if defined(USE_LIBZIP) || defined(USE_ZLIB)
|
||||
"*.zip",
|
||||
#endif
|
||||
#ifdef USE_LZMA
|
||||
|
@ -274,7 +293,7 @@ void Window::selectROM() {
|
|||
void Window::replaceROM() {
|
||||
QStringList formats{
|
||||
"*.gba",
|
||||
#ifdef USE_LIBZIP
|
||||
#if defined(USE_LIBZIP) || defined(USE_ZLIB)
|
||||
"*.zip",
|
||||
#endif
|
||||
#ifdef USE_LZMA
|
||||
|
@ -344,29 +363,15 @@ void Window::exportSharkport() {
|
|||
}
|
||||
}
|
||||
|
||||
void Window::openKeymapWindow() {
|
||||
GBAKeyEditor* keyEditor = new GBAKeyEditor(&m_inputController, InputController::KEYBOARD);
|
||||
openView(keyEditor);
|
||||
}
|
||||
|
||||
void Window::openSettingsWindow() {
|
||||
SettingsView* settingsWindow = new SettingsView(m_config);
|
||||
SettingsView* settingsWindow = new SettingsView(m_config, &m_inputController, m_shortcutController);
|
||||
connect(settingsWindow, SIGNAL(biosLoaded(const QString&)), m_controller, SLOT(loadBIOS(const QString&)));
|
||||
connect(settingsWindow, SIGNAL(audioDriverChanged()), m_controller, SLOT(reloadAudioDriver()));
|
||||
connect(settingsWindow, SIGNAL(displayDriverChanged()), this, SLOT(mustRestart()));
|
||||
connect(settingsWindow, SIGNAL(pathsChanged()), this, SLOT(reloadConfig()));
|
||||
openView(settingsWindow);
|
||||
}
|
||||
|
||||
void Window::openShortcutWindow() {
|
||||
#ifdef BUILD_SDL
|
||||
m_inputController.recalibrateAxes();
|
||||
#endif
|
||||
ShortcutView* shortcutView = new ShortcutView();
|
||||
shortcutView->setController(m_shortcutController);
|
||||
shortcutView->setInputController(&m_inputController);
|
||||
openView(shortcutView);
|
||||
}
|
||||
|
||||
void Window::openOverrideWindow() {
|
||||
OverrideView* overrideWindow = new OverrideView(m_controller, m_config);
|
||||
openView(overrideWindow);
|
||||
|
@ -407,14 +412,6 @@ void Window::openROMInfo() {
|
|||
openView(romInfo);
|
||||
}
|
||||
|
||||
#ifdef BUILD_SDL
|
||||
void Window::openGamepadWindow() {
|
||||
const char* profile = m_inputController.profileForType(SDL_BINDING_BUTTON);
|
||||
GBAKeyEditor* keyEditor = new GBAKeyEditor(&m_inputController, SDL_BINDING_BUTTON, profile);
|
||||
openView(keyEditor);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_FFMPEG
|
||||
void Window::openVideoWindow() {
|
||||
if (!m_videoView) {
|
||||
|
@ -629,6 +626,7 @@ void Window::gameStarted(GBAThread* context) {
|
|||
|
||||
m_hitUnimplementedBiosCall = false;
|
||||
m_fpsTimer.start();
|
||||
m_focusCheck.start();
|
||||
}
|
||||
|
||||
void Window::gameStopped() {
|
||||
|
@ -643,6 +641,7 @@ void Window::gameStopped() {
|
|||
m_screenWidget->unsetCursor();
|
||||
|
||||
m_fpsTimer.stop();
|
||||
m_focusCheck.stop();
|
||||
}
|
||||
|
||||
void Window::gameCrashed(const QString& errorMessage) {
|
||||
|
@ -1196,18 +1195,6 @@ void Window::setupMenu(QMenuBar* menubar) {
|
|||
toolsMenu->addSeparator();
|
||||
addControlledAction(toolsMenu, toolsMenu->addAction(tr("Settings..."), this, SLOT(openSettingsWindow())),
|
||||
"settings");
|
||||
addControlledAction(toolsMenu, toolsMenu->addAction(tr("Edit shortcuts..."), this, SLOT(openShortcutWindow())),
|
||||
"shortcuts");
|
||||
|
||||
QAction* keymap = new QAction(tr("Remap keyboard..."), toolsMenu);
|
||||
connect(keymap, SIGNAL(triggered()), this, SLOT(openKeymapWindow()));
|
||||
addControlledAction(toolsMenu, keymap, "remapKeyboard");
|
||||
|
||||
#ifdef BUILD_SDL
|
||||
QAction* gamepad = new QAction(tr("Remap gamepad..."), toolsMenu);
|
||||
connect(gamepad, SIGNAL(triggered()), this, SLOT(openGamepadWindow()));
|
||||
addControlledAction(toolsMenu, gamepad, "remapGamepad");
|
||||
#endif
|
||||
|
||||
toolsMenu->addSeparator();
|
||||
|
||||
|
@ -1271,6 +1258,16 @@ void Window::setupMenu(QMenuBar* menubar) {
|
|||
m_inputController.setAllowOpposing(value.toBool());
|
||||
}, this);
|
||||
|
||||
ConfigOption* saveStateExtdata = m_config->addOption("saveStateExtdata");
|
||||
saveStateExtdata->connect([this](const QVariant& value) {
|
||||
m_controller->setSaveStateExtdata(value.toInt());
|
||||
}, this);
|
||||
|
||||
ConfigOption* loadStateExtdata = m_config->addOption("loadStateExtdata");
|
||||
loadStateExtdata->connect([this](const QVariant& value) {
|
||||
m_controller->setLoadStateExtdata(value.toInt());
|
||||
}, this);
|
||||
|
||||
QAction* exitFullScreen = new QAction(tr("Exit fullscreen"), frameMenu);
|
||||
connect(exitFullScreen, SIGNAL(triggered()), this, SLOT(exitFullScreen()));
|
||||
exitFullScreen->setShortcut(QKeySequence("Esc"));
|
||||
|
@ -1397,6 +1394,18 @@ QAction* Window::addHiddenAction(QMenu* menu, QAction* action, const QString& na
|
|||
return action;
|
||||
}
|
||||
|
||||
void Window::focusCheck() {
|
||||
if (!m_config->getOption("pauseOnFocusLost").toInt()) {
|
||||
return;
|
||||
}
|
||||
if (QGuiApplication::focusWindow() && m_autoresume) {
|
||||
m_controller->setPaused(false);
|
||||
} else if (!QGuiApplication::focusWindow() && !m_controller->isPaused()) {
|
||||
m_autoresume = true;
|
||||
m_controller->setPaused(true);
|
||||
}
|
||||
}
|
||||
|
||||
WindowBackground::WindowBackground(QWidget* parent)
|
||||
: QLabel(parent)
|
||||
{
|
||||
|
|
|
@ -66,6 +66,7 @@ public slots:
|
|||
void exitFullScreen();
|
||||
void toggleFullScreen();
|
||||
void loadConfig();
|
||||
void reloadConfig();
|
||||
void saveConfig();
|
||||
|
||||
void replaceROM();
|
||||
|
@ -75,10 +76,7 @@ public slots:
|
|||
void importSharkport();
|
||||
void exportSharkport();
|
||||
|
||||
void openKeymapWindow();
|
||||
void openSettingsWindow();
|
||||
void openShortcutWindow();
|
||||
|
||||
void openOverrideWindow();
|
||||
void openSensorWindow();
|
||||
void openCheatsWindow();
|
||||
|
@ -90,10 +88,6 @@ public slots:
|
|||
void openAboutScreen();
|
||||
void openROMInfo();
|
||||
|
||||
#ifdef BUILD_SDL
|
||||
void openGamepadWindow();
|
||||
#endif
|
||||
|
||||
#ifdef USE_FFMPEG
|
||||
void openVideoWindow();
|
||||
#endif
|
||||
|
@ -130,6 +124,7 @@ private slots:
|
|||
|
||||
void recordFrame();
|
||||
void showFPS();
|
||||
void focusCheck();
|
||||
|
||||
private:
|
||||
static const int FPS_TIMER_INTERVAL = 2000;
|
||||
|
@ -171,6 +166,8 @@ private:
|
|||
ShaderSelector* m_shaderView;
|
||||
int m_playerId;
|
||||
bool m_fullscreenOnStart;
|
||||
QTimer m_focusCheck;
|
||||
bool m_autoresume;
|
||||
|
||||
bool m_hitUnimplementedBiosCall;
|
||||
|
||||
|
|
|
@ -30,8 +30,8 @@ void GBASDLGLCreate(struct SDLSoftwareRenderer* renderer) {
|
|||
bool GBASDLGLInit(struct SDLSoftwareRenderer* renderer) {
|
||||
GBASDLGLCommonInit(renderer);
|
||||
|
||||
renderer->d.outputBuffer = malloc(256 * 256 * BYTES_PER_PIXEL);
|
||||
renderer->d.outputBufferStride = 256;
|
||||
renderer->d.outputBuffer = malloc(VIDEO_HORIZONTAL_PIXELS * VIDEO_VERTICAL_PIXELS * BYTES_PER_PIXEL);
|
||||
renderer->d.outputBufferStride = VIDEO_HORIZONTAL_PIXELS;
|
||||
|
||||
GBAGLContextCreate(&renderer->gl);
|
||||
renderer->gl.d.user = renderer;
|
||||
|
|
|
@ -93,8 +93,8 @@ bool GBASDLGLES2Init(struct SDLSoftwareRenderer* renderer) {
|
|||
GBASDLGLCommonInit(renderer);
|
||||
#endif
|
||||
|
||||
renderer->d.outputBuffer = memalign(16, 256 * 256 * 4);
|
||||
renderer->d.outputBufferStride = 256;
|
||||
renderer->d.outputBuffer = memalign(16, VIDEO_HORIZONTAL_PIXELS * VIDEO_VERTICAL_PIXELS * 4);
|
||||
renderer->d.outputBufferStride = VIDEO_HORIZONTAL_PIXELS;
|
||||
|
||||
GBAGLES2ContextCreate(&renderer->gl2);
|
||||
renderer->gl2.d.user = renderer;
|
||||
|
|
|
@ -95,7 +95,7 @@ int main(int argc, char** argv) {
|
|||
|
||||
#ifdef BUILD_GL
|
||||
GBASDLGLCreate(&renderer);
|
||||
#elif defined(BUILD_GLES2)
|
||||
#elif defined(BUILD_GLES2) || defined(USE_EPOXY)
|
||||
GBASDLGLES2Create(&renderer);
|
||||
#else
|
||||
GBASDLSWCreate(&renderer);
|
||||
|
@ -164,6 +164,7 @@ int main(int argc, char** argv) {
|
|||
GBAConfigFreeOpts(&opts);
|
||||
GBAConfigDeinit(&config);
|
||||
free(context.debugger);
|
||||
GBADirectorySetDeinit(&context.dirs);
|
||||
GBASDLDetachPlayer(&renderer.events, &renderer.player);
|
||||
GBAInputMapDeinit(&inputMap);
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
#ifdef BUILD_GLES2
|
||||
#if defined(BUILD_GLES2) || defined(USE_EPOXY)
|
||||
#include "platform/opengl/gles2.h"
|
||||
#endif
|
||||
|
||||
|
@ -63,7 +63,7 @@ struct SDLSoftwareRenderer {
|
|||
#ifdef BUILD_GL
|
||||
struct GBAGLContext gl;
|
||||
#endif
|
||||
#ifdef BUILD_GLES2
|
||||
#if defined(BUILD_GLES2) || defined(USE_EPOXY)
|
||||
struct GBAGLES2Context gl2;
|
||||
#endif
|
||||
|
||||
|
@ -92,7 +92,7 @@ void GBASDLSWCreate(struct SDLSoftwareRenderer* renderer);
|
|||
void GBASDLGLCreate(struct SDLSoftwareRenderer* renderer);
|
||||
#endif
|
||||
|
||||
#ifdef BUILD_GLES2
|
||||
#if defined(BUILD_GLES2) || defined(USE_EPOXY)
|
||||
void GBASDLGLES2Create(struct SDLSoftwareRenderer* renderer);
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -24,6 +24,8 @@
|
|||
#define GYRO_STEPS 100
|
||||
#define RUMBLE_PWM 20
|
||||
|
||||
DEFINE_VECTOR(SDL_JoystickList, struct SDL_JoystickCombo);
|
||||
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
static void _GBASDLSetRumble(struct GBARumble* rumble, int enable);
|
||||
#endif
|
||||
|
@ -52,22 +54,9 @@ bool GBASDLInitEvents(struct GBASDLEvents* context) {
|
|||
|
||||
SDL_JoystickEventState(SDL_ENABLE);
|
||||
int nJoysticks = SDL_NumJoysticks();
|
||||
SDL_JoystickListInit(&context->joysticks, nJoysticks);
|
||||
if (nJoysticks > 0) {
|
||||
context->nJoysticks = nJoysticks;
|
||||
context->joysticks = calloc(context->nJoysticks, sizeof(SDL_Joystick*));
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
context->haptic = calloc(context->nJoysticks, sizeof(SDL_Haptic*));
|
||||
#endif
|
||||
size_t i;
|
||||
for (i = 0; i < context->nJoysticks; ++i) {
|
||||
context->joysticks[i] = SDL_JoystickOpen(i);
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
context->haptic[i] = SDL_HapticOpenFromJoystick(context->joysticks[i]);
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
context->nJoysticks = 0;
|
||||
context->joysticks = 0;
|
||||
GBASDLUpdateJoysticks(context);
|
||||
}
|
||||
|
||||
context->playersAttached = 0;
|
||||
|
@ -75,7 +64,6 @@ bool GBASDLInitEvents(struct GBASDLEvents* context) {
|
|||
size_t i;
|
||||
for (i = 0; i < MAX_PLAYERS; ++i) {
|
||||
context->preferredJoysticks[i] = 0;
|
||||
context->joysticksClaimed[i] = SIZE_MAX;
|
||||
}
|
||||
|
||||
#if !SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
|
@ -88,13 +76,14 @@ bool GBASDLInitEvents(struct GBASDLEvents* context) {
|
|||
|
||||
void GBASDLDeinitEvents(struct GBASDLEvents* context) {
|
||||
size_t i;
|
||||
for (i = 0; i < context->nJoysticks; ++i) {
|
||||
for (i = 0; i < SDL_JoystickListSize(&context->joysticks); ++i) {
|
||||
struct SDL_JoystickCombo* joystick = SDL_JoystickListGetPointer(&context->joysticks, i);
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
SDL_HapticClose(context->haptic[i]);
|
||||
SDL_HapticClose(joystick->haptic);
|
||||
#endif
|
||||
SDL_JoystickClose(context->joysticks[i]);
|
||||
SDL_JoystickClose(joystick->joystick);
|
||||
}
|
||||
|
||||
SDL_JoystickListDeinit(&context->joysticks);
|
||||
SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
|
||||
}
|
||||
|
||||
|
@ -160,7 +149,6 @@ void GBASDLInitBindings(struct GBAInputMap* inputMap) {
|
|||
|
||||
bool GBASDLAttachPlayer(struct GBASDLEvents* events, struct GBASDLPlayer* player) {
|
||||
player->joystick = 0;
|
||||
player->joystickIndex = SIZE_MAX;
|
||||
|
||||
if (events->playersAttached >= MAX_PLAYERS) {
|
||||
return false;
|
||||
|
@ -187,15 +175,17 @@ bool GBASDLAttachPlayer(struct GBASDLEvents* events, struct GBASDLPlayer* player
|
|||
player->rotation.p = player;
|
||||
|
||||
player->playerId = events->playersAttached;
|
||||
events->players[player->playerId] = player;
|
||||
size_t firstUnclaimed = SIZE_MAX;
|
||||
size_t index = SIZE_MAX;
|
||||
|
||||
size_t i;
|
||||
for (i = 0; i < events->nJoysticks; ++i) {
|
||||
for (i = 0; i < SDL_JoystickListSize(&events->joysticks); ++i) {
|
||||
bool claimed = false;
|
||||
|
||||
int p;
|
||||
for (p = 0; p < events->playersAttached; ++p) {
|
||||
if (events->joysticksClaimed[p] == i) {
|
||||
if (events->players[p]->joystick == SDL_JoystickListGetPointer(&events->joysticks, i)) {
|
||||
claimed = true;
|
||||
break;
|
||||
}
|
||||
|
@ -210,28 +200,26 @@ bool GBASDLAttachPlayer(struct GBASDLEvents* events, struct GBASDLPlayer* player
|
|||
|
||||
const char* joystickName;
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
joystickName = SDL_JoystickName(events->joysticks[i]);
|
||||
joystickName = SDL_JoystickName(SDL_JoystickListGetPointer(&events->joysticks, i)->joystick);
|
||||
#else
|
||||
joystickName = SDL_JoystickName(SDL_JoystickIndex(events->joysticks[i]));
|
||||
joystickName = SDL_JoystickName(SDL_JoystickIndex(SDL_JoystickListGetPointer(&events->joysticks, i)->joystick));
|
||||
#endif
|
||||
if (events->preferredJoysticks[player->playerId] && strcmp(events->preferredJoysticks[player->playerId], joystickName) == 0) {
|
||||
player->joystickIndex = i;
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (player->joystickIndex == SIZE_MAX && firstUnclaimed != SIZE_MAX) {
|
||||
player->joystickIndex = firstUnclaimed;
|
||||
if (index == SIZE_MAX && firstUnclaimed != SIZE_MAX) {
|
||||
index = firstUnclaimed;
|
||||
}
|
||||
|
||||
if (player->joystickIndex != SIZE_MAX) {
|
||||
player->joystick = events->joysticks[player->joystickIndex];
|
||||
events->joysticksClaimed[player->playerId] = player->joystickIndex;
|
||||
if (index != SIZE_MAX) {
|
||||
player->joystick = SDL_JoystickListGetPointer(&events->joysticks, index);
|
||||
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
player->haptic = events->haptic[player->joystickIndex];
|
||||
if (player->haptic) {
|
||||
SDL_HapticRumbleInit(player->haptic);
|
||||
if (player->joystick->haptic) {
|
||||
SDL_HapticRumbleInit(player->joystick->haptic);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -241,7 +229,19 @@ bool GBASDLAttachPlayer(struct GBASDLEvents* events, struct GBASDLPlayer* player
|
|||
}
|
||||
|
||||
void GBASDLDetachPlayer(struct GBASDLEvents* events, struct GBASDLPlayer* player) {
|
||||
events->joysticksClaimed[player->playerId] = SIZE_MAX;
|
||||
if (player != events->players[player->playerId]) {
|
||||
return;
|
||||
}
|
||||
int i;
|
||||
for (i = player->playerId; i < events->playersAttached; ++i) {
|
||||
if (i + 1 < MAX_PLAYERS) {
|
||||
events->players[i] = events->players[i + 1];
|
||||
}
|
||||
if (i < events->playersAttached - 1) {
|
||||
events->players[i]->playerId = i;
|
||||
}
|
||||
}
|
||||
--events->playersAttached;
|
||||
CircleBufferDeinit(&player->rotation.zHistory);
|
||||
}
|
||||
|
||||
|
@ -250,15 +250,15 @@ void GBASDLPlayerLoadConfig(struct GBASDLPlayer* context, const struct Configura
|
|||
if (context->joystick) {
|
||||
GBAInputMapLoad(context->bindings, SDL_BINDING_BUTTON, config);
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
const char* name = SDL_JoystickName(context->joystick);
|
||||
const char* name = SDL_JoystickName(context->joystick->joystick);
|
||||
#else
|
||||
const char* name = SDL_JoystickName(SDL_JoystickIndex(context->joystick));
|
||||
const char* name = SDL_JoystickName(SDL_JoystickIndex(context->joystick->joystick));
|
||||
#endif
|
||||
GBAInputProfileLoad(context->bindings, SDL_BINDING_BUTTON, config, name);
|
||||
|
||||
const char* value;
|
||||
char* end;
|
||||
int numAxes = SDL_JoystickNumAxes(context->joystick);
|
||||
int numAxes = SDL_JoystickNumAxes(context->joystick->joystick);
|
||||
int axis;
|
||||
value = GBAInputGetCustomValue(config, SDL_BINDING_BUTTON, "tiltAxisX", name);
|
||||
if (value) {
|
||||
|
@ -301,9 +301,9 @@ void GBASDLPlayerLoadConfig(struct GBASDLPlayer* context, const struct Configura
|
|||
void GBASDLPlayerSaveConfig(const struct GBASDLPlayer* context, struct Configuration* config) {
|
||||
if (context->joystick) {
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
const char* name = SDL_JoystickName(context->joystick);
|
||||
const char* name = SDL_JoystickName(context->joystick->joystick);
|
||||
#else
|
||||
const char* name = SDL_JoystickName(SDL_JoystickIndex(context->joystick));
|
||||
const char* name = SDL_JoystickName(SDL_JoystickIndex(context->joystick->joystick));
|
||||
#endif
|
||||
char value[12];
|
||||
snprintf(value, sizeof(value), "%i", context->rotation.axisX);
|
||||
|
@ -320,14 +320,54 @@ void GBASDLPlayerSaveConfig(const struct GBASDLPlayer* context, struct Configura
|
|||
}
|
||||
|
||||
void GBASDLPlayerChangeJoystick(struct GBASDLEvents* events, struct GBASDLPlayer* player, size_t index) {
|
||||
if (player->playerId >= MAX_PLAYERS || index >= events->nJoysticks) {
|
||||
if (player->playerId >= MAX_PLAYERS || index >= SDL_JoystickListSize(&events->joysticks)) {
|
||||
return;
|
||||
}
|
||||
events->joysticksClaimed[player->playerId] = index;
|
||||
player->joystickIndex = index;
|
||||
player->joystick = events->joysticks[index];
|
||||
player->joystick = SDL_JoystickListGetPointer(&events->joysticks, index);
|
||||
}
|
||||
|
||||
void GBASDLUpdateJoysticks(struct GBASDLEvents* events) {
|
||||
// Pump SDL joystick events without eating the rest of the events
|
||||
SDL_JoystickUpdate();
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
player->haptic = events->haptic[index];
|
||||
SDL_Event event;
|
||||
while (SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_JOYDEVICEADDED, SDL_JOYDEVICEREMOVED) > 0) {
|
||||
if (event.type == SDL_JOYDEVICEADDED) {
|
||||
struct SDL_JoystickCombo* joystick = SDL_JoystickListAppend(&events->joysticks);
|
||||
joystick->joystick = SDL_JoystickOpen(event.jdevice.which);
|
||||
joystick->id = SDL_JoystickInstanceID(joystick->joystick);
|
||||
joystick->index = SDL_JoystickListSize(&events->joysticks) - 1;
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
joystick->haptic = SDL_HapticOpenFromJoystick(joystick->joystick);
|
||||
#endif
|
||||
} else if (event.type == SDL_JOYDEVICEREMOVED) {
|
||||
SDL_JoystickID ids[MAX_PLAYERS];
|
||||
size_t i;
|
||||
for (i = 0; (int) i < events->playersAttached; ++i) {
|
||||
if (events->players[i]->joystick) {
|
||||
ids[i] = events->players[i]->joystick->id;
|
||||
events->players[i]->joystick = 0;
|
||||
} else {
|
||||
ids[i] = -1;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < SDL_JoystickListSize(&events->joysticks);) {
|
||||
struct SDL_JoystickCombo* joystick = SDL_JoystickListGetPointer(&events->joysticks, i);
|
||||
if (joystick->id == event.jdevice.which) {
|
||||
SDL_JoystickListShift(&events->joysticks, i, 1);
|
||||
continue;
|
||||
}
|
||||
SDL_JoystickListGetPointer(&events->joysticks, i)->index = i;
|
||||
int p;
|
||||
for (p = 0; p < events->playersAttached; ++p) {
|
||||
if (joystick->id == ids[p]) {
|
||||
events->players[p]->joystick = SDL_JoystickListGetPointer(&events->joysticks, i);
|
||||
}
|
||||
}
|
||||
++i;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -540,7 +580,7 @@ void GBASDLHandleEvent(struct GBAThread* context, struct GBASDLPlayer* sdlContex
|
|||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
static void _GBASDLSetRumble(struct GBARumble* rumble, int enable) {
|
||||
struct GBASDLRumble* sdlRumble = (struct GBASDLRumble*) rumble;
|
||||
if (!sdlRumble->p->haptic || !SDL_HapticRumbleSupported(sdlRumble->p->haptic)) {
|
||||
if (!sdlRumble->p->joystick->haptic || !SDL_HapticRumbleSupported(sdlRumble->p->joystick->haptic)) {
|
||||
return;
|
||||
}
|
||||
sdlRumble->level += enable;
|
||||
|
@ -551,15 +591,18 @@ static void _GBASDLSetRumble(struct GBARumble* rumble, int enable) {
|
|||
}
|
||||
CircleBufferWrite8(&sdlRumble->history, enable);
|
||||
if (sdlRumble->level) {
|
||||
SDL_HapticRumblePlay(sdlRumble->p->haptic, sdlRumble->level / (float) RUMBLE_PWM, 20);
|
||||
SDL_HapticRumblePlay(sdlRumble->p->joystick->haptic, sdlRumble->level / (float) RUMBLE_PWM, 20);
|
||||
} else {
|
||||
SDL_HapticRumbleStop(sdlRumble->p->haptic);
|
||||
SDL_HapticRumbleStop(sdlRumble->p->joystick->haptic);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static int32_t _readTilt(struct GBASDLPlayer* player, int axis) {
|
||||
return SDL_JoystickGetAxis(player->joystick, axis) * 0x3800;
|
||||
if (!player->joystick) {
|
||||
return 0;
|
||||
}
|
||||
return SDL_JoystickGetAxis(player->joystick->joystick, axis) * 0x3800;
|
||||
}
|
||||
|
||||
static int32_t _GBASDLReadTiltX(struct GBARotationSource* source) {
|
||||
|
@ -581,9 +624,12 @@ static int32_t _GBASDLReadGyroZ(struct GBARotationSource* source) {
|
|||
static void _GBASDLRotationSample(struct GBARotationSource* source) {
|
||||
struct GBASDLRotation* rotation = (struct GBASDLRotation*) source;
|
||||
SDL_JoystickUpdate();
|
||||
if (!rotation->p->joystick) {
|
||||
return;
|
||||
}
|
||||
|
||||
int x = SDL_JoystickGetAxis(rotation->p->joystick, rotation->gyroX);
|
||||
int y = SDL_JoystickGetAxis(rotation->p->joystick, rotation->gyroY);
|
||||
int x = SDL_JoystickGetAxis(rotation->p->joystick->joystick, rotation->gyroX);
|
||||
int y = SDL_JoystickGetAxis(rotation->p->joystick->joystick, rotation->gyroY);
|
||||
union {
|
||||
float f;
|
||||
int32_t i;
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include "util/common.h"
|
||||
#include "util/circle-buffer.h"
|
||||
#include "util/vector.h"
|
||||
|
||||
#include "gba/supervisor/thread.h"
|
||||
|
||||
|
@ -21,14 +22,25 @@
|
|||
struct GBAVideoSoftwareRenderer;
|
||||
struct Configuration;
|
||||
|
||||
struct SDL_JoystickCombo {
|
||||
SDL_JoystickID id;
|
||||
size_t index;
|
||||
SDL_Joystick* joystick;
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
SDL_Haptic* haptic;
|
||||
#endif
|
||||
};
|
||||
|
||||
DECLARE_VECTOR(SDL_JoystickList, struct SDL_JoystickCombo);
|
||||
|
||||
struct GBASDLPlayer;
|
||||
|
||||
struct GBASDLEvents {
|
||||
SDL_Joystick** joysticks;
|
||||
size_t nJoysticks;
|
||||
struct SDL_JoystickList joysticks;
|
||||
const char* preferredJoysticks[MAX_PLAYERS];
|
||||
int playersAttached;
|
||||
size_t joysticksClaimed[MAX_PLAYERS];
|
||||
struct GBASDLPlayer* players[MAX_PLAYERS];
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
SDL_Haptic** haptic;
|
||||
int screensaverSuspendDepth;
|
||||
bool screensaverSuspendable;
|
||||
#endif
|
||||
|
@ -37,13 +49,11 @@ struct GBASDLEvents {
|
|||
struct GBASDLPlayer {
|
||||
size_t playerId;
|
||||
struct GBAInputMap* bindings;
|
||||
SDL_Joystick* joystick;
|
||||
size_t joystickIndex;
|
||||
struct SDL_JoystickCombo* joystick;
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
SDL_Window* window;
|
||||
int fullscreen;
|
||||
int windowUpdated;
|
||||
SDL_Haptic* haptic;
|
||||
|
||||
struct GBASDLRumble {
|
||||
struct GBARumble d;
|
||||
|
@ -80,6 +90,7 @@ bool GBASDLAttachPlayer(struct GBASDLEvents*, struct GBASDLPlayer*);
|
|||
void GBASDLDetachPlayer(struct GBASDLEvents*, struct GBASDLPlayer*);
|
||||
void GBASDLEventsLoadConfig(struct GBASDLEvents*, const struct Configuration*);
|
||||
void GBASDLPlayerChangeJoystick(struct GBASDLEvents*, struct GBASDLPlayer*, size_t index);
|
||||
void GBASDLUpdateJoysticks(struct GBASDLEvents* events);
|
||||
|
||||
void GBASDLInitBindings(struct GBAInputMap* inputMap);
|
||||
void GBASDLPlayerLoadConfig(struct GBASDLPlayer*, const struct Configuration*);
|
||||
|
|
|
@ -65,6 +65,25 @@ unsigned GUIFontGlyphWidth(const struct GUIFont* font, uint32_t glyph) {
|
|||
return defaultFontMetrics[glyph].width * 2;
|
||||
}
|
||||
|
||||
void GUIFontIconMetrics(const struct GUIFont* font, enum GUIIcon icon, unsigned* w, unsigned* h) {
|
||||
UNUSED(font);
|
||||
if (icon >= GUI_ICON_MAX) {
|
||||
if (w) {
|
||||
*w = 0;
|
||||
}
|
||||
if (h) {
|
||||
*h = 0;
|
||||
}
|
||||
} else {
|
||||
if (w) {
|
||||
*w = defaultIconMetrics[icon].width * 2;
|
||||
}
|
||||
if (h) {
|
||||
*h = defaultIconMetrics[icon].height * 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GUIFontDrawGlyph(const struct GUIFont* font, int x, int y, uint32_t color, uint32_t glyph) {
|
||||
color = (color >> 24) | (color << 8);
|
||||
GXTexObj tex;
|
||||
|
@ -180,3 +199,55 @@ void GUIFontDrawIcon(const struct GUIFont* font, int x, int y, enum GUIAlignment
|
|||
GX_TexCoord2f32(u[3], v[3]);
|
||||
GX_End();
|
||||
}
|
||||
|
||||
void GUIFontDrawIconSize(const struct GUIFont* font, int x, int y, int w, int h, uint32_t color, enum GUIIcon icon) {
|
||||
if (icon >= GUI_ICON_MAX) {
|
||||
return;
|
||||
}
|
||||
|
||||
color = (color >> 24) | (color << 8);
|
||||
GXTexObj tex;
|
||||
|
||||
struct GUIFont* ncfont = font;
|
||||
TPL_GetTexture(&ncfont->iconsTdf, 0, &tex);
|
||||
GX_LoadTexObj(&tex, GX_TEXMAP0);
|
||||
|
||||
GX_SetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_NOOP);
|
||||
GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_F32, 0);
|
||||
GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0);
|
||||
|
||||
struct GUIIconMetric metric = defaultIconMetrics[icon];
|
||||
|
||||
float u[4];
|
||||
float v[4];
|
||||
|
||||
if (!h) {
|
||||
h = metric.height * 2;
|
||||
}
|
||||
if (!w) {
|
||||
w = metric.width * 2;
|
||||
}
|
||||
|
||||
u[0] = u[3] = metric.x / 256.f;
|
||||
u[1] = u[2] = (metric.x + metric.width) / 256.f;
|
||||
v[0] = v[1] = (metric.y + metric.height) / 64.f;
|
||||
v[2] = v[3] = metric.y / 64.f;
|
||||
|
||||
GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
|
||||
GX_Position2s16(x, y + h);
|
||||
GX_Color1u32(color);
|
||||
GX_TexCoord2f32(u[0], v[0]);
|
||||
|
||||
GX_Position2s16(x + w, y + h);
|
||||
GX_Color1u32(color);
|
||||
GX_TexCoord2f32(u[1], v[1]);
|
||||
|
||||
GX_Position2s16(x + w, y);
|
||||
GX_Color1u32(color);
|
||||
GX_TexCoord2f32(u[2], v[2]);
|
||||
|
||||
GX_Position2s16(x, y);
|
||||
GX_Color1u32(color);
|
||||
GX_TexCoord2f32(u[3], v[3]);
|
||||
GX_End();
|
||||
}
|
||||
|
|
|
@ -264,15 +264,15 @@ int main(int argc, char* argv[]) {
|
|||
"1",
|
||||
"B",
|
||||
"A",
|
||||
"Minus",
|
||||
"-",
|
||||
0,
|
||||
0,
|
||||
"Home",
|
||||
"\1\xE",
|
||||
"Left",
|
||||
"Right",
|
||||
"Down",
|
||||
"Up",
|
||||
"Plus",
|
||||
"+",
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
|
@ -311,9 +311,9 @@ int main(int argc, char* argv[]) {
|
|||
"ZL",
|
||||
0,
|
||||
"R",
|
||||
"Plus",
|
||||
"Home",
|
||||
"Minus",
|
||||
"+",
|
||||
"\1\xE",
|
||||
"-",
|
||||
"L",
|
||||
"Down",
|
||||
"Right",
|
||||
|
@ -426,18 +426,18 @@ static uint32_t _pollInput(void) {
|
|||
int keys = 0;
|
||||
int x = PAD_StickX(0);
|
||||
int y = PAD_StickY(0);
|
||||
int w_x = WPAD_StickX(0,0);
|
||||
int w_y = WPAD_StickY(0,0);
|
||||
if (x < -0x40 || w_x < -0x40) {
|
||||
int w_x = WPAD_StickX(0, 0);
|
||||
int w_y = WPAD_StickY(0, 0);
|
||||
if (x < -0x20 || w_x < -0x20) {
|
||||
keys |= 1 << GUI_INPUT_LEFT;
|
||||
}
|
||||
if (x > 0x40 || w_x > 0x40) {
|
||||
if (x > 0x20 || w_x > 0x20) {
|
||||
keys |= 1 << GUI_INPUT_RIGHT;
|
||||
}
|
||||
if (y < -0x40 || w_y <- 0x40) {
|
||||
if (y < -0x20 || w_y <- 0x20) {
|
||||
keys |= 1 << GUI_INPUT_DOWN;
|
||||
}
|
||||
if (y > 0x40 || w_y > 0x40) {
|
||||
if (y > 0x20 || w_y > 0x20) {
|
||||
keys |= 1 << GUI_INPUT_UP;
|
||||
}
|
||||
if ((padkeys & PAD_BUTTON_A) || (wiiPad & WPAD_BUTTON_2) ||
|
||||
|
@ -552,10 +552,10 @@ void _setup(struct GBAGUIRunner* runner) {
|
|||
_mapKey(&runner->context.inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_FULL_L, GBA_KEY_L);
|
||||
_mapKey(&runner->context.inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_FULL_R, GBA_KEY_R);
|
||||
|
||||
struct GBAAxis desc = { GBA_KEY_RIGHT, GBA_KEY_LEFT, 0x40, -0x40 };
|
||||
struct GBAAxis desc = { GBA_KEY_RIGHT, GBA_KEY_LEFT, 0x20, -0x20 };
|
||||
GBAInputBindAxis(&runner->context.inputMap, GCN1_INPUT, 0, &desc);
|
||||
GBAInputBindAxis(&runner->context.inputMap, CLASSIC_INPUT, 0, &desc);
|
||||
desc = (struct GBAAxis) { GBA_KEY_UP, GBA_KEY_DOWN, 0x40, -0x40 };
|
||||
desc = (struct GBAAxis) { GBA_KEY_UP, GBA_KEY_DOWN, 0x20, -0x20 };
|
||||
GBAInputBindAxis(&runner->context.inputMap, GCN1_INPUT, 1, &desc);
|
||||
GBAInputBindAxis(&runner->context.inputMap, CLASSIC_INPUT, 1, &desc);
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ static inline int MutexLock(Mutex* mutex) {
|
|||
|
||||
static inline int MutexTryLock(Mutex* mutex) {
|
||||
if (TryEnterCriticalSection(mutex)) {
|
||||
return GetLastError();
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
|
|
@ -151,5 +151,5 @@ struct GUIIconMetric defaultIconMetrics[] = {
|
|||
[GUI_ICON_BUTTON_CROSS] = { 18, 34, 12, 11 },
|
||||
[GUI_ICON_BUTTON_TRIANGLE] = { 34, 34, 12, 11 },
|
||||
[GUI_ICON_BUTTON_SQUARE] = { 50, 34, 12, 11 },
|
||||
[GUI_ICON_BUTTON_HOME] = { 66, 34, 16, 16 },
|
||||
[GUI_ICON_BUTTON_HOME] = { 66, 34, 12, 11 },
|
||||
};
|
||||
|
|
|
@ -5,12 +5,23 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#include "util/gui/font.h"
|
||||
|
||||
#include "util/string.h"
|
||||
|
||||
unsigned GUIFontSpanWidth(const struct GUIFont* font, const char* text) {
|
||||
unsigned width = 0;
|
||||
size_t i;
|
||||
for (i = 0; text[i]; ++i) {
|
||||
char c = text[i];
|
||||
width += GUIFontGlyphWidth(font, c);
|
||||
size_t len = strlen(text);
|
||||
while (len) {
|
||||
uint32_t c = utf8Char(&text, &len);
|
||||
if (c == '\1') {
|
||||
c = utf8Char(&text, &len);
|
||||
if (c < GUI_ICON_MAX) {
|
||||
unsigned w;
|
||||
GUIFontIconMetrics(font, c, &w, 0);
|
||||
width += w;
|
||||
}
|
||||
} else {
|
||||
width += GUIFontGlyphWidth(font, c);
|
||||
}
|
||||
}
|
||||
return width;
|
||||
}
|
||||
|
@ -29,8 +40,18 @@ void GUIFontPrint(const struct GUIFont* font, int x, int y, enum GUIAlignment al
|
|||
size_t len = strlen(text);
|
||||
while (len) {
|
||||
uint32_t c = utf8Char(&text, &len);
|
||||
GUIFontDrawGlyph(font, x, y, color, c);
|
||||
x += GUIFontGlyphWidth(font, c);
|
||||
if (c == '\1') {
|
||||
c = utf8Char(&text, &len);
|
||||
if (c < GUI_ICON_MAX) {
|
||||
GUIFontDrawIcon(font, x, y, GUI_ALIGN_BOTTOM, GUI_ORIENT_0, color, c);
|
||||
unsigned w;
|
||||
GUIFontIconMetrics(font, c, &w, 0);
|
||||
x += w;
|
||||
}
|
||||
} else {
|
||||
GUIFontDrawGlyph(font, x, y, color, c);
|
||||
x += GUIFontGlyphWidth(font, c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -75,11 +75,13 @@ struct GUIIconMetric {
|
|||
unsigned GUIFontHeight(const struct GUIFont*);
|
||||
unsigned GUIFontGlyphWidth(const struct GUIFont*, uint32_t glyph);
|
||||
unsigned GUIFontSpanWidth(const struct GUIFont*, const char* text);
|
||||
void GUIFontIconMetrics(const struct GUIFont*, enum GUIIcon icon, unsigned* w, unsigned* h);
|
||||
|
||||
ATTRIBUTE_FORMAT(printf, 6, 7)
|
||||
void GUIFontPrintf(const struct GUIFont*, int x, int y, enum GUIAlignment, uint32_t color, const char* text, ...);
|
||||
void GUIFontPrint(const struct GUIFont*, int x, int y, enum GUIAlignment, uint32_t color, const char* text);
|
||||
void GUIFontDrawGlyph(const struct GUIFont*, int x, int y, uint32_t color, uint32_t glyph);
|
||||
void GUIFontDrawIcon(const struct GUIFont*, int x, int y, enum GUIAlignment, enum GUIOrientation, uint32_t color, enum GUIIcon);
|
||||
void GUIFontDrawIconSize(const struct GUIFont* font, int x, int y, int w, int h, uint32_t color, enum GUIIcon icon);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -8,6 +8,10 @@
|
|||
#include "util/gui.h"
|
||||
#include "util/gui/font.h"
|
||||
|
||||
#ifdef _3DS
|
||||
#include <3ds.h>
|
||||
#endif
|
||||
|
||||
DEFINE_VECTOR(GUIMenuItemList, struct GUIMenuItem);
|
||||
|
||||
enum GUIMenuExitReason GUIShowMenu(struct GUIParams* params, struct GUIMenu* menu, struct GUIMenuItem** item) {
|
||||
|
@ -23,6 +27,11 @@ enum GUIMenuExitReason GUIShowMenu(struct GUIParams* params, struct GUIMenu* men
|
|||
|
||||
GUIInvalidateKeys(params);
|
||||
while (true) {
|
||||
#ifdef _3DS
|
||||
if (!aptMainLoop()) {
|
||||
return GUI_MENU_EXIT_CANCEL;
|
||||
}
|
||||
#endif
|
||||
uint32_t newInput = 0;
|
||||
GUIPollInput(params, &newInput, 0);
|
||||
unsigned cx, cy;
|
||||
|
@ -88,7 +97,7 @@ enum GUIMenuExitReason GUIShowMenu(struct GUIParams* params, struct GUIMenu* men
|
|||
++menu->index;
|
||||
} else if (cy <= params->height - lineHeight && cy > 2 * lineHeight) {
|
||||
size_t location = cy - 2 * lineHeight;
|
||||
location *= GUIMenuItemListSize(&menu->items);
|
||||
location *= GUIMenuItemListSize(&menu->items) - 1;
|
||||
menu->index = location / (params->height - 3 * lineHeight);
|
||||
}
|
||||
}
|
||||
|
@ -140,10 +149,10 @@ enum GUIMenuExitReason GUIShowMenu(struct GUIParams* params, struct GUIMenu* men
|
|||
int color = 0xE0A0A0A0;
|
||||
if (i == menu->index) {
|
||||
color = 0xFFFFFFFF;
|
||||
GUIFontDrawIcon(params->font, 2, y, GUI_ALIGN_BOTTOM | GUI_ALIGN_LEFT, GUI_ORIENT_0, 0xFFFFFFFF, GUI_ICON_POINTER);
|
||||
GUIFontDrawIcon(params->font, lineHeight * 0.8f, y, GUI_ALIGN_BOTTOM | GUI_ALIGN_RIGHT, GUI_ORIENT_0, 0xFFFFFFFF, GUI_ICON_POINTER);
|
||||
}
|
||||
struct GUIMenuItem* item = GUIMenuItemListGetPointer(&menu->items, i);
|
||||
GUIFontPrintf(params->font, 0, y, GUI_ALIGN_LEFT, color, " %s", item->title);
|
||||
GUIFontPrint(params->font, lineHeight, y, GUI_ALIGN_LEFT, color, item->title);
|
||||
if (item->validStates && item->validStates[item->state]) {
|
||||
GUIFontPrintf(params->font, params->width, y, GUI_ALIGN_RIGHT, color, "%s ", item->validStates[item->state]);
|
||||
}
|
||||
|
@ -154,15 +163,18 @@ enum GUIMenuExitReason GUIShowMenu(struct GUIParams* params, struct GUIMenu* men
|
|||
}
|
||||
|
||||
if (itemsPerScreen < GUIMenuItemListSize(&menu->items)) {
|
||||
y = 2 * lineHeight;
|
||||
GUIFontDrawIcon(params->font, params->width - 8, y, GUI_ALIGN_HCENTER | GUI_ALIGN_BOTTOM, GUI_ORIENT_VMIRROR, 0xFFFFFFFF, GUI_ICON_SCROLLBAR_BUTTON);
|
||||
for (; y < params->height - 16; y += 16) {
|
||||
GUIFontDrawIcon(params->font, params->width - 8, y, GUI_ALIGN_HCENTER | GUI_ALIGN_TOP, GUI_ORIENT_0, 0xFFFFFFFF, GUI_ICON_SCROLLBAR_TRACK);
|
||||
}
|
||||
GUIFontDrawIcon(params->font, params->width - 8, y, GUI_ALIGN_HCENTER | GUI_ALIGN_TOP, GUI_ORIENT_0, 0xFFFFFFFF, GUI_ICON_SCROLLBAR_BUTTON);
|
||||
|
||||
size_t top = 2 * lineHeight;
|
||||
y = menu->index * (y - top - 16) / GUIMenuItemListSize(&menu->items);
|
||||
size_t bottom = params->height - 8;
|
||||
unsigned w;
|
||||
unsigned right;
|
||||
GUIFontIconMetrics(params->font, GUI_ICON_SCROLLBAR_BUTTON, &right, 0);
|
||||
GUIFontIconMetrics(params->font, GUI_ICON_SCROLLBAR_TRACK, &w, 0);
|
||||
right = (right - w) / 2;
|
||||
GUIFontDrawIcon(params->font, params->width - 8, top, GUI_ALIGN_HCENTER | GUI_ALIGN_BOTTOM, GUI_ORIENT_VMIRROR, 0xFFFFFFFF, GUI_ICON_SCROLLBAR_BUTTON);
|
||||
GUIFontDrawIconSize(params->font, params->width - right - 8, top, 0, bottom - top, 0xFFFFFFFF, GUI_ICON_SCROLLBAR_TRACK);
|
||||
GUIFontDrawIcon(params->font, params->width - 8, bottom, GUI_ALIGN_HCENTER | GUI_ALIGN_TOP, GUI_ORIENT_0, 0xFFFFFFFF, GUI_ICON_SCROLLBAR_BUTTON);
|
||||
|
||||
y = menu->index * (bottom - top - 16) / GUIMenuItemListSize(&menu->items);
|
||||
GUIFontDrawIcon(params->font, params->width - 8, top + y, GUI_ALIGN_HCENTER | GUI_ALIGN_TOP, GUI_ORIENT_0, 0xFFFFFFFF, GUI_ICON_SCROLLBAR_THUMB);
|
||||
}
|
||||
|
||||
|
|
|
@ -40,27 +40,27 @@ struct VFile* VFileOpen(const char* path, int flags) {
|
|||
}
|
||||
return VFileFOpen(path, chflags);
|
||||
#elif defined(PSP2)
|
||||
int sceFlags = PSP2_O_RDONLY;
|
||||
int sceFlags = SCE_O_RDONLY;
|
||||
switch (flags & O_ACCMODE) {
|
||||
case O_WRONLY:
|
||||
sceFlags = PSP2_O_WRONLY;
|
||||
sceFlags = SCE_O_WRONLY;
|
||||
break;
|
||||
case O_RDWR:
|
||||
sceFlags = PSP2_O_RDWR;
|
||||
sceFlags = SCE_O_RDWR;
|
||||
break;
|
||||
case O_RDONLY:
|
||||
sceFlags = PSP2_O_RDONLY;
|
||||
sceFlags = SCE_O_RDONLY;
|
||||
break;
|
||||
}
|
||||
|
||||
if (flags & O_APPEND) {
|
||||
sceFlags |= PSP2_O_APPEND;
|
||||
sceFlags |= SCE_O_APPEND;
|
||||
}
|
||||
if (flags & O_TRUNC) {
|
||||
sceFlags |= PSP2_O_TRUNC;
|
||||
sceFlags |= SCE_O_TRUNC;
|
||||
}
|
||||
if (flags & O_CREAT) {
|
||||
sceFlags |= PSP2_O_CREAT;
|
||||
sceFlags |= SCE_O_CREAT;
|
||||
}
|
||||
return VFileOpenSce(path, sceFlags, 0666);
|
||||
#elif defined(USE_VFS_3DS)
|
||||
|
|
|
@ -59,6 +59,7 @@ struct VDir {
|
|||
struct VDirEntry* (*listNext)(struct VDir* vd);
|
||||
struct VFile* (*openFile)(struct VDir* vd, const char* name, int mode);
|
||||
struct VDir* (*openDir)(struct VDir* vd, const char* name);
|
||||
bool (*deleteFile)(struct VDir* vd, const char* name);
|
||||
};
|
||||
|
||||
struct VFile* VFileOpen(const char* path, int flags);
|
||||
|
|
|
@ -12,6 +12,7 @@ static void _vdlRewind(struct VDir* vd);
|
|||
static struct VDirEntry* _vdlListNext(struct VDir* vd);
|
||||
static struct VFile* _vdlOpenFile(struct VDir* vd, const char* path, int mode);
|
||||
static struct VDir* _vdlOpenDir(struct VDir* vd, const char* path);
|
||||
static bool _vdlDeleteFile(struct VDir* vd, const char* path);
|
||||
|
||||
static const char* _vdleName(struct VDirEntry* vde);
|
||||
static enum VFSType _vdleType(struct VDirEntry* vde);
|
||||
|
@ -38,6 +39,7 @@ struct VDir* VDeviceList() {
|
|||
vd->d.listNext = _vdlListNext;
|
||||
vd->d.openFile = _vdlOpenFile;
|
||||
vd->d.openDir = _vdlOpenDir;
|
||||
vd->d.deleteFile = _vdlDeleteFile;
|
||||
|
||||
vd->vde.d.name = _vdleName;
|
||||
vd->vde.d.type = _vdleType;
|
||||
|
@ -93,6 +95,12 @@ static struct VDir* _vdlOpenDir(struct VDir* vd, const char* path) {
|
|||
return VDirOpen(path);
|
||||
}
|
||||
|
||||
static bool _vdlDeleteFile(struct VDir* vd, const char* path) {
|
||||
UNUSED(vd);
|
||||
UNUSED(path);
|
||||
return false;
|
||||
}
|
||||
|
||||
static const char* _vdleName(struct VDirEntry* vde) {
|
||||
struct VDirEntryDevList* vdle = (struct VDirEntryDevList*) vde;
|
||||
return vdle->name;
|
||||
|
|
|
@ -15,6 +15,7 @@ static void _vdRewind(struct VDir* vd);
|
|||
static struct VDirEntry* _vdListNext(struct VDir* vd);
|
||||
static struct VFile* _vdOpenFile(struct VDir* vd, const char* path, int mode);
|
||||
static struct VDir* _vdOpenDir(struct VDir* vd, const char* path);
|
||||
static bool _vdDeleteFile(struct VDir* vd, const char* path);
|
||||
|
||||
static const char* _vdeName(struct VDirEntry* vde);
|
||||
static enum VFSType _vdeType(struct VDirEntry* vde);
|
||||
|
@ -55,6 +56,7 @@ struct VDir* VDirOpen(const char* path) {
|
|||
vd->d.listNext = _vdListNext;
|
||||
vd->d.openFile = _vdOpenFile;
|
||||
vd->d.openDir = _vdOpenDir;
|
||||
vd->d.deleteFile = _vdDeleteFile;
|
||||
vd->path = strdup(path);
|
||||
vd->de = de;
|
||||
|
||||
|
@ -121,6 +123,20 @@ struct VDir* _vdOpenDir(struct VDir* vd, const char* path) {
|
|||
return vd2;
|
||||
}
|
||||
|
||||
bool _vdDeleteFile(struct VDir* vd, const char* path) {
|
||||
struct VDirDE* vdde = (struct VDirDE*) vd;
|
||||
if (!path) {
|
||||
return false;
|
||||
}
|
||||
const char* dir = vdde->path;
|
||||
char* combined = malloc(sizeof(char) * (strlen(path) + strlen(dir) + 2));
|
||||
sprintf(combined, "%s%s%s", dir, PATH_SEP, path);
|
||||
|
||||
bool ret = !unlink(combined);
|
||||
free(combined);
|
||||
return ret;
|
||||
}
|
||||
|
||||
const char* _vdeName(struct VDirEntry* vde) {
|
||||
struct VDirEntryDE* vdede = (struct VDirEntryDE*) vde;
|
||||
if (vdede->ent) {
|
||||
|
|
|
@ -63,6 +63,7 @@ static void _vd7zRewind(struct VDir* vd);
|
|||
static struct VDirEntry* _vd7zListNext(struct VDir* vd);
|
||||
static struct VFile* _vd7zOpenFile(struct VDir* vd, const char* path, int mode);
|
||||
static struct VDir* _vd7zOpenDir(struct VDir* vd, const char* path);
|
||||
static bool _vd7zDeleteFile(struct VDir* vd, const char* path);
|
||||
|
||||
static const char* _vde7zName(struct VDirEntry* vde);
|
||||
static enum VFSType _vde7zType(struct VDirEntry* vde);
|
||||
|
@ -102,7 +103,7 @@ struct VDir* VDirOpen7z(const char* path, int flags) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
vd->dirent.index = 0;
|
||||
vd->dirent.index = -1;
|
||||
vd->dirent.utf8 = 0;
|
||||
vd->dirent.vd = vd;
|
||||
vd->dirent.d.name = _vde7zName;
|
||||
|
@ -113,6 +114,7 @@ struct VDir* VDirOpen7z(const char* path, int flags) {
|
|||
vd->d.listNext = _vd7zListNext;
|
||||
vd->d.openFile = _vd7zOpenFile;
|
||||
vd->d.openDir = _vd7zOpenDir;
|
||||
vd->d.deleteFile = _vd7zDeleteFile;
|
||||
|
||||
return &vd->d;
|
||||
}
|
||||
|
@ -309,6 +311,13 @@ struct VDir* _vd7zOpenDir(struct VDir* vd, const char* path) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
bool _vd7zDeleteFile(struct VDir* vd, const char* path) {
|
||||
UNUSED(vd);
|
||||
UNUSED(path);
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
|
||||
bool _vf7zSync(struct VFile* vf, const void* memory, size_t size) {
|
||||
UNUSED(vf);
|
||||
UNUSED(memory);
|
||||
|
|
|
@ -74,6 +74,7 @@ static void _vdzRewind(struct VDir* vd);
|
|||
static struct VDirEntry* _vdzListNext(struct VDir* vd);
|
||||
static struct VFile* _vdzOpenFile(struct VDir* vd, const char* path, int mode);
|
||||
static struct VDir* _vdzOpenDir(struct VDir* vd, const char* path);
|
||||
static bool _vdzDeleteFile(struct VDir* vd, const char* path);
|
||||
|
||||
static const char* _vdezName(struct VDirEntry* vde);
|
||||
static enum VFSType _vdezType(struct VDirEntry* vde);
|
||||
|
@ -172,6 +173,7 @@ struct VDir* VDirOpenZip(const char* path, int flags) {
|
|||
vd->d.listNext = _vdzListNext;
|
||||
vd->d.openFile = _vdzOpenFile;
|
||||
vd->d.openDir = _vdzOpenDir;
|
||||
vd->d.deleteFile = _vdzDeleteFile;
|
||||
vd->z = z;
|
||||
|
||||
#ifndef USE_LIBZIP
|
||||
|
@ -410,6 +412,13 @@ struct VDir* _vdzOpenDir(struct VDir* vd, const char* path) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
bool _vdzDeleteFile(struct VDir* vd, const char* path) {
|
||||
UNUSED(vd);
|
||||
UNUSED(path);
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
|
||||
bool _vfzSync(struct VFile* vf, const void* memory, size_t size) {
|
||||
UNUSED(vf);
|
||||
UNUSED(memory);
|
||||
|
@ -624,6 +633,13 @@ struct VDir* _vdzOpenDir(struct VDir* vd, const char* path) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
bool _vdzDeleteFile(struct VDir* vd, const char* path) {
|
||||
UNUSED(vd);
|
||||
UNUSED(path);
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
|
||||
bool _vfzSync(struct VFile* vf, const void* memory, size_t size) {
|
||||
UNUSED(vf);
|
||||
UNUSED(memory);
|
||||
|
|
|
@ -2,9 +2,9 @@ if(NOT PROJECT_NAME)
|
|||
set(PROJECT_NAME "mGBA")
|
||||
endif()
|
||||
set(LIB_VERSION_MAJOR 0)
|
||||
set(LIB_VERSION_MINOR 4)
|
||||
set(LIB_VERSION_MINOR 5)
|
||||
set(LIB_VERSION_PATCH 0)
|
||||
set(LIB_VERSION_ABI 0.4)
|
||||
set(LIB_VERSION_ABI 0.5)
|
||||
set(LIB_VERSION_STRING ${LIB_VERSION_MAJOR}.${LIB_VERSION_MINOR}.${LIB_VERSION_PATCH})
|
||||
set(SUMMARY "${PROJECT_NAME} Game Boy Advance Emulator")
|
||||
|
||||
|
|
Loading…
Reference in New Issue