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:
|
Features:
|
||||||
- Officially supported ports for the Nintendo 3DS, Wii, and PlayStation Vita
|
- Officially supported ports for the Nintendo 3DS, Wii, and PlayStation Vita
|
||||||
- I/O viewer
|
- I/O viewer
|
||||||
|
@ -15,45 +21,59 @@ Features:
|
||||||
- Libretro: Settings for using BIOS and skipping intro
|
- Libretro: Settings for using BIOS and skipping intro
|
||||||
- Libretro: Customizable idle loop removal
|
- Libretro: Customizable idle loop removal
|
||||||
- Implemented cycle counting for sprite rendering
|
- 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:
|
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 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: 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: 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
|
- 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:
|
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: Improved PowerPC support
|
||||||
- All: Fix some undefined behavior warnings
|
- All: Fix some undefined behavior warnings
|
||||||
- Util: Use VFile for configuration
|
- ARM7: Combine shifter-immediate and shifter-register functions to reduce binary size
|
||||||
- GBA Memory: Implement several unimplemented memory access types
|
- Debugger: Convert breakpoints and watchpoints from linked-lists to vectors
|
||||||
- GBA: Implement bad I/O register loading
|
- 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
|
- 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: Gray out "Skip BIOS intro" while "Use BIOS file" is unchecked
|
||||||
- Qt: Allow use of modifier keys as input
|
- Qt: Allow use of modifier keys as input
|
||||||
- Qt: Optimize log viewer
|
- Qt: Optimize log viewer
|
||||||
- GBA RR: Starting from savestate now embeds the savegame
|
- Qt: Added button for breaking into the GDB debugger
|
||||||
- Libretro: Add install target for libretro core
|
- Qt: Add box for showing duration of rewind
|
||||||
- 3DS: Update to new ctrulib API
|
- SDL: Support fullscreen in SDL 1.2
|
||||||
- GBA RR: Add preliminary SRAM support for VBM loading
|
- SDL: Allow GBASDLAudio to be used without a thread context
|
||||||
- GBA RR: Add support for resets in movies
|
- Util: Use VFile for configuration
|
||||||
- GBA Input: Consolidate GBA_KEY_NONE and GBA_NO_MAPPING
|
- Util: Add MutexTryLock
|
||||||
|
|
||||||
0.3.2: (2015-12-16)
|
0.3.2: (2015-12-16)
|
||||||
Bugfixes:
|
Bugfixes:
|
||||||
|
|
|
@ -125,7 +125,6 @@ Footnotes
|
||||||
|
|
||||||
- OBJ window for modes 3, 4 and 5 ([Bug #5](http://mgba.io/b/5))
|
- 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))
|
- 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.
|
<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) \
|
#define ADDR_MODE_1_SHIFT(OP) \
|
||||||
info->op3.reg = opcode & 0x0000000F; \
|
info->op3.reg = opcode & 0x0000000F; \
|
||||||
|
info->op3.shifterOp = ARM_SHIFT_ ## OP; \
|
||||||
info->operandFormat |= ARM_OPERAND_REGISTER_3; \
|
info->operandFormat |= ARM_OPERAND_REGISTER_3; \
|
||||||
if (opcode & 0x00000010) { \
|
if (opcode & 0x00000010) { \
|
||||||
info->op3.shifterOp = ARM_SHIFT_ ## OP; \
|
|
||||||
info->op3.shifterReg = (opcode >> 8) & 0xF; \
|
info->op3.shifterReg = (opcode >> 8) & 0xF; \
|
||||||
++info->iCycles; \
|
++info->iCycles; \
|
||||||
info->operandFormat |= ARM_OPERAND_SHIFT_REGISTER_3; \
|
info->operandFormat |= ARM_OPERAND_SHIFT_REGISTER_3; \
|
||||||
|
@ -101,11 +101,13 @@
|
||||||
info->affectsCPSR = S; \
|
info->affectsCPSR = S; \
|
||||||
SHIFTER; \
|
SHIFTER; \
|
||||||
if (SKIPPED == 1) { \
|
if (SKIPPED == 1) { \
|
||||||
info->operandFormat >>= 8; \
|
|
||||||
info->op1 = info->op2; \
|
info->op1 = info->op2; \
|
||||||
info->op2 = info->op3; \
|
info->op2 = info->op3; \
|
||||||
|
info->operandFormat >>= 8; \
|
||||||
} else if (SKIPPED == 2) { \
|
} 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) { \
|
if (info->op1.reg == ARM_PC) { \
|
||||||
info->branchType = ARM_BRANCH_INDIRECT; \
|
info->branchType = ARM_BRANCH_INDIRECT; \
|
||||||
|
|
|
@ -536,7 +536,7 @@ static void _setWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector*
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
uint32_t address = dv->intValue;
|
uint32_t address = dv->intValue;
|
||||||
ARMDebuggerSetWatchpoint(&debugger->d, address);
|
ARMDebuggerSetWatchpoint(&debugger->d, address, WATCHPOINT_RW); // TODO: ro/wo
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _breakIntoDefault(int signal) {
|
static void _breakIntoDefault(int signal) {
|
||||||
|
@ -802,7 +802,11 @@ static void _reportEntry(struct ARMDebugger* debugger, enum DebuggerEntryReason
|
||||||
break;
|
break;
|
||||||
case DEBUGGER_ENTER_WATCHPOINT:
|
case DEBUGGER_ENTER_WATCHPOINT:
|
||||||
if (info) {
|
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 {
|
} else {
|
||||||
printf("Hit watchpoint\n");
|
printf("Hit watchpoint\n");
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,10 +12,14 @@
|
||||||
|
|
||||||
const uint32_t ARM_DEBUGGER_ID = 0xDEADBEEF;
|
const uint32_t ARM_DEBUGGER_ID = 0xDEADBEEF;
|
||||||
|
|
||||||
static struct DebugBreakpoint* _lookupBreakpoint(struct DebugBreakpoint* breakpoints, uint32_t address) {
|
DEFINE_VECTOR(DebugBreakpointList, struct DebugBreakpoint);
|
||||||
for (; breakpoints; breakpoints = breakpoints->next) {
|
DEFINE_VECTOR(DebugWatchpointList, struct DebugWatchpoint);
|
||||||
if (breakpoints->address == address) {
|
|
||||||
return breakpoints;
|
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;
|
return 0;
|
||||||
|
@ -29,7 +33,7 @@ static void _checkBreakpoints(struct ARMDebugger* debugger) {
|
||||||
} else {
|
} else {
|
||||||
instructionLength = WORD_SIZE_THUMB;
|
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) {
|
if (!breakpoint) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -52,11 +56,11 @@ void ARMDebuggerInit(struct ARMCore* cpu, struct ARMComponent* component) {
|
||||||
struct ARMDebugger* debugger = (struct ARMDebugger*) component;
|
struct ARMDebugger* debugger = (struct ARMDebugger*) component;
|
||||||
debugger->cpu = cpu;
|
debugger->cpu = cpu;
|
||||||
debugger->state = DEBUGGER_RUNNING;
|
debugger->state = DEBUGGER_RUNNING;
|
||||||
debugger->breakpoints = 0;
|
|
||||||
debugger->swBreakpoints = 0;
|
|
||||||
debugger->originalMemory = cpu->memory;
|
debugger->originalMemory = cpu->memory;
|
||||||
debugger->watchpoints = 0;
|
|
||||||
debugger->currentBreakpoint = 0;
|
debugger->currentBreakpoint = 0;
|
||||||
|
DebugBreakpointListInit(&debugger->breakpoints, 0);
|
||||||
|
DebugBreakpointListInit(&debugger->swBreakpoints, 0);
|
||||||
|
DebugWatchpointListInit(&debugger->watchpoints, 0);
|
||||||
if (debugger->init) {
|
if (debugger->init) {
|
||||||
debugger->init(debugger);
|
debugger->init(debugger);
|
||||||
}
|
}
|
||||||
|
@ -65,12 +69,15 @@ void ARMDebuggerInit(struct ARMCore* cpu, struct ARMComponent* component) {
|
||||||
void ARMDebuggerDeinit(struct ARMComponent* component) {
|
void ARMDebuggerDeinit(struct ARMComponent* component) {
|
||||||
struct ARMDebugger* debugger = (struct ARMDebugger*) component;
|
struct ARMDebugger* debugger = (struct ARMDebugger*) component;
|
||||||
debugger->deinit(debugger);
|
debugger->deinit(debugger);
|
||||||
|
DebugBreakpointListDeinit(&debugger->breakpoints);
|
||||||
|
DebugBreakpointListDeinit(&debugger->swBreakpoints);
|
||||||
|
DebugWatchpointListDeinit(&debugger->watchpoints);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ARMDebuggerRun(struct ARMDebugger* debugger) {
|
void ARMDebuggerRun(struct ARMDebugger* debugger) {
|
||||||
switch (debugger->state) {
|
switch (debugger->state) {
|
||||||
case DEBUGGER_RUNNING:
|
case DEBUGGER_RUNNING:
|
||||||
if (!debugger->breakpoints && !debugger->watchpoints) {
|
if (!DebugBreakpointListSize(&debugger->breakpoints) && !DebugWatchpointListSize(&debugger->watchpoints)) {
|
||||||
ARMRunLoop(debugger->cpu);
|
ARMRunLoop(debugger->cpu);
|
||||||
} else {
|
} else {
|
||||||
ARMRun(debugger->cpu);
|
ARMRun(debugger->cpu);
|
||||||
|
@ -105,7 +112,7 @@ void ARMDebuggerEnter(struct ARMDebugger* debugger, enum DebuggerEntryReason rea
|
||||||
struct ARMCore* cpu = debugger->cpu;
|
struct ARMCore* cpu = debugger->cpu;
|
||||||
cpu->nextEvent = cpu->cycles;
|
cpu->nextEvent = cpu->cycles;
|
||||||
if (reason == DEBUGGER_ENTER_BREAKPOINT) {
|
if (reason == DEBUGGER_ENTER_BREAKPOINT) {
|
||||||
struct DebugBreakpoint* breakpoint = _lookupBreakpoint(debugger->swBreakpoints, _ARMPCAddress(cpu));
|
struct DebugBreakpoint* breakpoint = _lookupBreakpoint(&debugger->swBreakpoints, _ARMPCAddress(cpu));
|
||||||
debugger->currentBreakpoint = breakpoint;
|
debugger->currentBreakpoint = breakpoint;
|
||||||
if (breakpoint && breakpoint->isSw) {
|
if (breakpoint && breakpoint->isSw) {
|
||||||
info->address = breakpoint->address;
|
info->address = breakpoint->address;
|
||||||
|
@ -122,11 +129,9 @@ void ARMDebuggerEnter(struct ARMDebugger* debugger, enum DebuggerEntryReason rea
|
||||||
}
|
}
|
||||||
|
|
||||||
void ARMDebuggerSetBreakpoint(struct ARMDebugger* debugger, uint32_t address) {
|
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->address = address;
|
||||||
breakpoint->next = debugger->breakpoints;
|
|
||||||
breakpoint->isSw = false;
|
breakpoint->isSw = false;
|
||||||
debugger->breakpoints = breakpoint;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ARMDebuggerSetSoftwareBreakpoint(struct ARMDebugger* debugger, uint32_t address, enum ExecutionMode mode) {
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct DebugBreakpoint* breakpoint = malloc(sizeof(struct DebugBreakpoint));
|
struct DebugBreakpoint* breakpoint = DebugBreakpointListAppend(&debugger->swBreakpoints);
|
||||||
breakpoint->address = address;
|
breakpoint->address = address;
|
||||||
breakpoint->next = debugger->swBreakpoints;
|
|
||||||
breakpoint->isSw = true;
|
breakpoint->isSw = true;
|
||||||
breakpoint->sw.opcode = opcode;
|
breakpoint->sw.opcode = opcode;
|
||||||
breakpoint->sw.mode = mode;
|
breakpoint->sw.mode = mode;
|
||||||
debugger->swBreakpoints = breakpoint;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ARMDebuggerClearBreakpoint(struct ARMDebugger* debugger, uint32_t address) {
|
void ARMDebuggerClearBreakpoint(struct ARMDebugger* debugger, uint32_t address) {
|
||||||
struct DebugBreakpoint** previous = &debugger->breakpoints;
|
struct DebugBreakpointList* breakpoints = &debugger->breakpoints;
|
||||||
struct DebugBreakpoint* breakpoint;
|
size_t i;
|
||||||
struct DebugBreakpoint** next;
|
for (i = 0; i < DebugBreakpointListSize(breakpoints); ++i) {
|
||||||
while ((breakpoint = *previous)) {
|
if (DebugBreakpointListGetPointer(breakpoints, i)->address == address) {
|
||||||
next = &breakpoint->next;
|
DebugBreakpointListShift(breakpoints, i, 1);
|
||||||
if (breakpoint->address == address) {
|
|
||||||
*previous = *next;
|
|
||||||
free(breakpoint);
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
previous = next;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ARMDebuggerSetWatchpoint(struct ARMDebugger* debugger, uint32_t address) {
|
void ARMDebuggerSetWatchpoint(struct ARMDebugger* debugger, uint32_t address, enum WatchpointType type) {
|
||||||
if (!debugger->watchpoints) {
|
if (!DebugWatchpointListSize(&debugger->watchpoints)) {
|
||||||
ARMDebuggerInstallMemoryShim(debugger);
|
ARMDebuggerInstallMemoryShim(debugger);
|
||||||
}
|
}
|
||||||
struct DebugWatchpoint* watchpoint = malloc(sizeof(struct DebugWatchpoint));
|
struct DebugWatchpoint* watchpoint = DebugWatchpointListAppend(&debugger->watchpoints);
|
||||||
watchpoint->address = address;
|
watchpoint->address = address;
|
||||||
watchpoint->next = debugger->watchpoints;
|
watchpoint->type = type;
|
||||||
debugger->watchpoints = watchpoint;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ARMDebuggerClearWatchpoint(struct ARMDebugger* debugger, uint32_t address) {
|
void ARMDebuggerClearWatchpoint(struct ARMDebugger* debugger, uint32_t address) {
|
||||||
struct DebugWatchpoint** previous = &debugger->watchpoints;
|
struct DebugWatchpointList* watchpoints = &debugger->watchpoints;
|
||||||
struct DebugWatchpoint* watchpoint;
|
size_t i;
|
||||||
struct DebugWatchpoint** next;
|
for (i = 0; i < DebugWatchpointListSize(watchpoints); ++i) {
|
||||||
while ((watchpoint = *previous)) {
|
if (DebugWatchpointListGetPointer(watchpoints, i)->address == address) {
|
||||||
next = &watchpoint->next;
|
DebugWatchpointListShift(watchpoints, i, 1);
|
||||||
if (watchpoint->address == address) {
|
|
||||||
*previous = *next;
|
|
||||||
free(watchpoint);
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
previous = next;
|
|
||||||
}
|
}
|
||||||
if (!debugger->watchpoints) {
|
if (!DebugWatchpointListSize(&debugger->watchpoints)) {
|
||||||
ARMDebuggerRemoveMemoryShim(debugger);
|
ARMDebuggerRemoveMemoryShim(debugger);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,8 @@
|
||||||
|
|
||||||
#include "util/common.h"
|
#include "util/common.h"
|
||||||
|
|
||||||
#include "arm.h"
|
#include "arm/arm.h"
|
||||||
|
#include "util/vector.h"
|
||||||
|
|
||||||
extern const uint32_t ARM_DEBUGGER_ID;
|
extern const uint32_t ARM_DEBUGGER_ID;
|
||||||
|
|
||||||
|
@ -20,7 +21,6 @@ enum DebuggerState {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct DebugBreakpoint {
|
struct DebugBreakpoint {
|
||||||
struct DebugBreakpoint* next;
|
|
||||||
uint32_t address;
|
uint32_t address;
|
||||||
bool isSw;
|
bool isSw;
|
||||||
struct {
|
struct {
|
||||||
|
@ -32,15 +32,17 @@ struct DebugBreakpoint {
|
||||||
enum WatchpointType {
|
enum WatchpointType {
|
||||||
WATCHPOINT_WRITE = 1,
|
WATCHPOINT_WRITE = 1,
|
||||||
WATCHPOINT_READ = 2,
|
WATCHPOINT_READ = 2,
|
||||||
WATCHPOINT_RW = 3
|
WATCHPOINT_RW = WATCHPOINT_WRITE | WATCHPOINT_READ
|
||||||
};
|
};
|
||||||
|
|
||||||
struct DebugWatchpoint {
|
struct DebugWatchpoint {
|
||||||
struct DebugWatchpoint* next;
|
|
||||||
uint32_t address;
|
uint32_t address;
|
||||||
enum WatchpointType type;
|
enum WatchpointType type;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
DECLARE_VECTOR(DebugBreakpointList, struct DebugBreakpoint);
|
||||||
|
DECLARE_VECTOR(DebugWatchpointList, struct DebugWatchpoint);
|
||||||
|
|
||||||
enum DebuggerEntryReason {
|
enum DebuggerEntryReason {
|
||||||
DEBUGGER_ENTER_MANUAL,
|
DEBUGGER_ENTER_MANUAL,
|
||||||
DEBUGGER_ENTER_ATTACHED,
|
DEBUGGER_ENTER_ATTACHED,
|
||||||
|
@ -54,7 +56,9 @@ struct DebuggerEntryInfo {
|
||||||
union {
|
union {
|
||||||
struct {
|
struct {
|
||||||
uint32_t oldValue;
|
uint32_t oldValue;
|
||||||
|
uint32_t newValue;
|
||||||
enum WatchpointType watchType;
|
enum WatchpointType watchType;
|
||||||
|
enum WatchpointType accessType;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
|
@ -75,9 +79,9 @@ struct ARMDebugger {
|
||||||
enum DebuggerState state;
|
enum DebuggerState state;
|
||||||
struct ARMCore* cpu;
|
struct ARMCore* cpu;
|
||||||
|
|
||||||
struct DebugBreakpoint* breakpoints;
|
struct DebugBreakpointList breakpoints;
|
||||||
struct DebugBreakpoint* swBreakpoints;
|
struct DebugBreakpointList swBreakpoints;
|
||||||
struct DebugWatchpoint* watchpoints;
|
struct DebugWatchpointList watchpoints;
|
||||||
struct ARMMemory originalMemory;
|
struct ARMMemory originalMemory;
|
||||||
|
|
||||||
struct DebugBreakpoint* currentBreakpoint;
|
struct DebugBreakpoint* currentBreakpoint;
|
||||||
|
@ -101,7 +105,7 @@ void ARMDebuggerEnter(struct ARMDebugger*, enum DebuggerEntryReason, struct Debu
|
||||||
void ARMDebuggerSetBreakpoint(struct ARMDebugger* debugger, uint32_t address);
|
void ARMDebuggerSetBreakpoint(struct ARMDebugger* debugger, uint32_t address);
|
||||||
bool ARMDebuggerSetSoftwareBreakpoint(struct ARMDebugger* debugger, uint32_t address, enum ExecutionMode mode);
|
bool ARMDebuggerSetSoftwareBreakpoint(struct ARMDebugger* debugger, uint32_t address, enum ExecutionMode mode);
|
||||||
void ARMDebuggerClearBreakpoint(struct ARMDebugger* debugger, uint32_t address);
|
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);
|
void ARMDebuggerClearWatchpoint(struct ARMDebugger* debugger, uint32_t address);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -40,13 +40,19 @@ static void _gdbStubEntered(struct ARMDebugger* debugger, enum DebuggerEntryReas
|
||||||
snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "S%02x", SIGINT);
|
snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "S%02x", SIGINT);
|
||||||
break;
|
break;
|
||||||
case DEBUGGER_ENTER_BREAKPOINT:
|
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;
|
break;
|
||||||
case DEBUGGER_ENTER_WATCHPOINT: // TODO: Make watchpoints raise with address
|
case DEBUGGER_ENTER_WATCHPOINT:
|
||||||
if (info) {
|
if (info) {
|
||||||
const char* type = 0;
|
const char* type = 0;
|
||||||
switch (info->watchType) {
|
switch (info->watchType) {
|
||||||
case WATCHPOINT_WRITE:
|
case WATCHPOINT_WRITE:
|
||||||
|
if (info->newValue == info->oldValue) {
|
||||||
|
if (stub->d.state == DEBUGGER_PAUSED) {
|
||||||
|
stub->d.state = DEBUGGER_RUNNING;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
type = "watch";
|
type = "watch";
|
||||||
break;
|
break;
|
||||||
case WATCHPOINT_READ:
|
case WATCHPOINT_READ:
|
||||||
|
@ -56,7 +62,7 @@ static void _gdbStubEntered(struct ARMDebugger* debugger, enum DebuggerEntryReas
|
||||||
type = "awatch";
|
type = "awatch";
|
||||||
break;
|
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 {
|
} else {
|
||||||
snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "S%02x", SIGTRAP);
|
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);
|
_sendMessage(stub);
|
||||||
break;
|
break;
|
||||||
case '2':
|
case '2':
|
||||||
|
ARMDebuggerSetWatchpoint(&stub->d, address, WATCHPOINT_WRITE);
|
||||||
|
strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
|
||||||
|
_sendMessage(stub);
|
||||||
|
break;
|
||||||
case '3':
|
case '3':
|
||||||
|
ARMDebuggerSetWatchpoint(&stub->d, address, WATCHPOINT_READ);
|
||||||
|
strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
|
||||||
|
_sendMessage(stub);
|
||||||
|
break;
|
||||||
case '4':
|
case '4':
|
||||||
ARMDebuggerSetWatchpoint(&stub->d, address);
|
ARMDebuggerSetWatchpoint(&stub->d, address, WATCHPOINT_RW);
|
||||||
strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
|
strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
|
||||||
_sendMessage(stub);
|
_sendMessage(stub);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
|
|
||||||
#include <string.h>
|
#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) \
|
#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__); \
|
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 { \
|
static RETURN ARMDebuggerShim_ ## NAME TYPES { \
|
||||||
struct ARMDebugger* debugger; \
|
struct ARMDebugger* debugger; \
|
||||||
FIND_DEBUGGER(debugger, cpu); \
|
FIND_DEBUGGER(debugger, cpu); \
|
||||||
struct DebuggerEntryInfo info; \
|
struct DebuggerEntryInfo info; \
|
||||||
if (_checkWatchpoints(debugger, address, &info, WIDTH)) { \
|
if (_checkWatchpoints(debugger, address, &info, WATCHPOINT_READ, 0, WIDTH)) { \
|
||||||
ARMDebuggerEnter(debugger, DEBUGGER_ENTER_WATCHPOINT, &info); \
|
ARMDebuggerEnter(debugger, DEBUGGER_ENTER_WATCHPOINT, &info); \
|
||||||
} \
|
} \
|
||||||
return debugger->originalMemory.NAME(cpu, __VA_ARGS__); \
|
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) { \
|
static uint32_t ARMDebuggerShim_ ## NAME (struct ARMCore* cpu, uint32_t address, int mask, enum LSMDirection direction, int* cycleCounter) { \
|
||||||
struct ARMDebugger* debugger; \
|
struct ARMDebugger* debugger; \
|
||||||
FIND_DEBUGGER(debugger, cpu); \
|
FIND_DEBUGGER(debugger, cpu); \
|
||||||
|
@ -60,28 +71,30 @@ static bool _checkWatchpoints(struct ARMDebugger* debugger, uint32_t address, st
|
||||||
unsigned i; \
|
unsigned i; \
|
||||||
for (i = 0; i < popcount; ++i) { \
|
for (i = 0; i < popcount; ++i) { \
|
||||||
struct DebuggerEntryInfo info; \
|
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); \
|
ARMDebuggerEnter(debugger, DEBUGGER_ENTER_WATCHPOINT, &info); \
|
||||||
} \
|
} \
|
||||||
} \
|
} \
|
||||||
return debugger->originalMemory.NAME(cpu, address, mask, direction, cycleCounter); \
|
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_READ_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_READ_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_READ_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_WRITE_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_WRITE_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_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)
|
CREATE_MULTIPLE_WATCHPOINT_SHIM(loadMultiple, WATCHPOINT_READ)
|
||||||
CREATE_MULTIPLE_WATCHPOINT_SHIM(storeMultiple)
|
CREATE_MULTIPLE_WATCHPOINT_SHIM(storeMultiple, WATCHPOINT_WRITE)
|
||||||
CREATE_SHIM(setActiveRegion, void, (struct ARMCore* cpu, uint32_t address), address)
|
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;
|
--width;
|
||||||
struct DebugWatchpoint* watchpoints;
|
struct DebugWatchpoint* watchpoint;
|
||||||
for (watchpoints = debugger->watchpoints; watchpoints; watchpoints = watchpoints->next) {
|
size_t i;
|
||||||
if (!((watchpoints->address ^ address) & ~width)) {
|
for (i = 0; i < DebugWatchpointListSize(&debugger->watchpoints); ++i) {
|
||||||
|
watchpoint = DebugWatchpointListGetPointer(&debugger->watchpoints, i);
|
||||||
|
if (!((watchpoint->address ^ address) & ~width) && watchpoint->type & type) {
|
||||||
switch (width + 1) {
|
switch (width + 1) {
|
||||||
case 1:
|
case 1:
|
||||||
info->oldValue = debugger->originalMemory.load8(debugger->cpu, address, 0);
|
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);
|
info->oldValue = debugger->originalMemory.load32(debugger->cpu, address, 0);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
info->newValue = newValue;
|
||||||
info->address = address;
|
info->address = address;
|
||||||
info->watchType = watchpoints->type;
|
info->watchType = watchpoint->type;
|
||||||
|
info->accessType = type;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,10 +71,96 @@ static void _RegisterRamReset(struct GBA* gba) {
|
||||||
cpu->memory.store32(cpu, BASE_IO | REG_JOY_TRANS_LO, 0, 0);
|
cpu->memory.store32(cpu, BASE_IO | REG_JOY_TRANS_LO, 0, 0);
|
||||||
}
|
}
|
||||||
if (registers & 0x40) {
|
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) {
|
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) {
|
void GBACheatDeviceDestroy(struct GBACheatDevice* device) {
|
||||||
|
GBACheatDeviceClear(device);
|
||||||
|
GBACheatSetsDeinit(&device->cheats);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GBACheatDeviceClear(struct GBACheatDevice* device) {
|
||||||
size_t i;
|
size_t i;
|
||||||
for (i = 0; i < GBACheatSetsSize(&device->cheats); ++i) {
|
for (i = 0; i < GBACheatSetsSize(&device->cheats); ++i) {
|
||||||
struct GBACheatSet* set = *GBACheatSetsGetPointer(&device->cheats, i);
|
struct GBACheatSet* set = *GBACheatSetsGetPointer(&device->cheats, i);
|
||||||
GBACheatSetDeinit(set);
|
GBACheatSetDeinit(set);
|
||||||
free(set);
|
free(set);
|
||||||
}
|
}
|
||||||
GBACheatSetsDeinit(&device->cheats);
|
GBACheatSetsClear(&device->cheats);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBACheatSetInit(struct GBACheatSet* set, const char* name) {
|
void GBACheatSetInit(struct GBACheatSet* set, const char* name) {
|
||||||
|
@ -260,6 +265,7 @@ bool GBACheatParseFile(struct GBACheatDevice* device, struct VFile* vf) {
|
||||||
do {
|
do {
|
||||||
++i;
|
++i;
|
||||||
} while (isspace((int) cheat[i]));
|
} while (isspace((int) cheat[i]));
|
||||||
|
cheat[strlen(cheat) - 1] = '\0'; // Remove trailing newline
|
||||||
newSet = malloc(sizeof(*set));
|
newSet = malloc(sizeof(*set));
|
||||||
GBACheatSetInit(newSet, &cheat[i]);
|
GBACheatSetInit(newSet, &cheat[i]);
|
||||||
newSet->enabled = !nextDisabled;
|
newSet->enabled = !nextDisabled;
|
||||||
|
|
|
@ -192,6 +192,7 @@ struct VFile;
|
||||||
|
|
||||||
void GBACheatDeviceCreate(struct GBACheatDevice*);
|
void GBACheatDeviceCreate(struct GBACheatDevice*);
|
||||||
void GBACheatDeviceDestroy(struct GBACheatDevice*);
|
void GBACheatDeviceDestroy(struct GBACheatDevice*);
|
||||||
|
void GBACheatDeviceClear(struct GBACheatDevice*);
|
||||||
|
|
||||||
void GBACheatSetInit(struct GBACheatSet*, const char* name);
|
void GBACheatSetInit(struct GBACheatSet*, const char* name);
|
||||||
void GBACheatSetDeinit(struct GBACheatSet*);
|
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) {
|
void GBACheatSetGameSharkVersion(struct GBACheatSet* cheats, int version) {
|
||||||
cheats->gsaVersion = 1;
|
cheats->gsaVersion = version;
|
||||||
switch (version) {
|
switch (version) {
|
||||||
case 1:
|
case 1:
|
||||||
|
case 2:
|
||||||
memcpy(cheats->gsaSeeds, GBACheatGameSharkSeeds, 4 * sizeof(uint32_t));
|
memcpy(cheats->gsaSeeds, GBACheatGameSharkSeeds, 4 * sizeof(uint32_t));
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
|
case 4:
|
||||||
memcpy(cheats->gsaSeeds, GBACheatProActionReplaySeeds, 4 * sizeof(uint32_t));
|
memcpy(cheats->gsaSeeds, GBACheatProActionReplaySeeds, 4 * sizeof(uint32_t));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -198,9 +200,11 @@ bool GBACheatAddGameShark(struct GBACheatSet* set, uint32_t op1, uint32_t op2) {
|
||||||
switch (set->gsaVersion) {
|
switch (set->gsaVersion) {
|
||||||
case 0:
|
case 0:
|
||||||
case 3:
|
case 3:
|
||||||
|
case 4:
|
||||||
GBACheatSetGameSharkVersion(set, 1);
|
GBACheatSetGameSharkVersion(set, 1);
|
||||||
// Fall through
|
// Fall through
|
||||||
case 1:
|
case 1:
|
||||||
|
case 2:
|
||||||
GBACheatDecryptGameShark(&o1, &o2, set->gsaSeeds);
|
GBACheatDecryptGameShark(&o1, &o2, set->gsaSeeds);
|
||||||
return GBACheatAddGameSharkRaw(set, o1, o2);
|
return GBACheatAddGameSharkRaw(set, o1, o2);
|
||||||
}
|
}
|
||||||
|
|
|
@ -298,9 +298,11 @@ bool GBACheatAddProActionReplay(struct GBACheatSet* set, uint32_t op1, uint32_t
|
||||||
switch (set->gsaVersion) {
|
switch (set->gsaVersion) {
|
||||||
case 0:
|
case 0:
|
||||||
case 1:
|
case 1:
|
||||||
|
case 2:
|
||||||
GBACheatSetGameSharkVersion(set, 3);
|
GBACheatSetGameSharkVersion(set, 3);
|
||||||
// Fall through
|
// Fall through
|
||||||
case 3:
|
case 3:
|
||||||
|
case 4:
|
||||||
GBACheatDecryptGameShark(&o1, &o2, set->gsaSeeds);
|
GBACheatDecryptGameShark(&o1, &o2, set->gsaSeeds);
|
||||||
return GBACheatAddProActionReplayRaw(set, o1, o2);
|
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, "width", &opts->width);
|
||||||
_lookupIntValue(config, "height", &opts->height);
|
_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;
|
char* idleOptimization = 0;
|
||||||
if (_lookupCharValue(config, "idleOptimization", &idleOptimization)) {
|
if (_lookupCharValue(config, "idleOptimization", &idleOptimization)) {
|
||||||
if (strcasecmp(idleOptimization, "ignore") == 0) {
|
if (strcasecmp(idleOptimization, "ignore") == 0) {
|
||||||
|
@ -409,6 +414,14 @@ struct Configuration* GBAConfigGetOverrides(struct GBAConfig* config) {
|
||||||
void GBAConfigFreeOpts(struct GBAOptions* opts) {
|
void GBAConfigFreeOpts(struct GBAOptions* opts) {
|
||||||
free(opts->bios);
|
free(opts->bios);
|
||||||
free(opts->shader);
|
free(opts->shader);
|
||||||
|
free(opts->savegamePath);
|
||||||
|
free(opts->savestatePath);
|
||||||
|
free(opts->screenshotPath);
|
||||||
|
free(opts->patchPath);
|
||||||
opts->bios = 0;
|
opts->bios = 0;
|
||||||
opts->shader = 0;
|
opts->shader = 0;
|
||||||
|
opts->savegamePath = 0;
|
||||||
|
opts->savestatePath = 0;
|
||||||
|
opts->screenshotPath = 0;
|
||||||
|
opts->patchPath = 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,6 +40,11 @@ struct GBAOptions {
|
||||||
bool suspendScreensaver;
|
bool suspendScreensaver;
|
||||||
char* shader;
|
char* shader;
|
||||||
|
|
||||||
|
char* savegamePath;
|
||||||
|
char* savestatePath;
|
||||||
|
char* screenshotPath;
|
||||||
|
char* patchPath;
|
||||||
|
|
||||||
int volume;
|
int volume;
|
||||||
bool mute;
|
bool mute;
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,9 @@ bool GBAContextInit(struct GBAContext* context, const char* port) {
|
||||||
context->fname = 0;
|
context->fname = 0;
|
||||||
context->save = 0;
|
context->save = 0;
|
||||||
context->renderer = 0;
|
context->renderer = 0;
|
||||||
|
#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
|
||||||
GBADirectorySetInit(&context->dirs);
|
GBADirectorySetInit(&context->dirs);
|
||||||
|
#endif
|
||||||
memset(context->components, 0, sizeof(context->components));
|
memset(context->components, 0, sizeof(context->components));
|
||||||
|
|
||||||
if (!context->gba || !context->cpu) {
|
if (!context->gba || !context->cpu) {
|
||||||
|
@ -79,11 +81,17 @@ void GBAContextDeinit(struct GBAContext* context) {
|
||||||
mappedMemoryFree(context->gba, 0);
|
mappedMemoryFree(context->gba, 0);
|
||||||
mappedMemoryFree(context->cpu, 0);
|
mappedMemoryFree(context->cpu, 0);
|
||||||
GBAConfigDeinit(&context->config);
|
GBAConfigDeinit(&context->config);
|
||||||
|
#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
|
||||||
GBADirectorySetDeinit(&context->dirs);
|
GBADirectorySetDeinit(&context->dirs);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GBAContextLoadROM(struct GBAContext* context, const char* path, bool autoloadSave) {
|
bool GBAContextLoadROM(struct GBAContext* context, const char* path, bool autoloadSave) {
|
||||||
|
#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
|
||||||
context->rom = GBADirectorySetOpenPath(&context->dirs, path, GBAIsROM);
|
context->rom = GBADirectorySetOpenPath(&context->dirs, path, GBAIsROM);
|
||||||
|
#else
|
||||||
|
context->rom = VFileOpen(path, O_RDONLY);
|
||||||
|
#endif
|
||||||
if (!context->rom) {
|
if (!context->rom) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -106,7 +114,9 @@ bool GBAContextLoadROM(struct GBAContext* context, const char* path, bool autolo
|
||||||
|
|
||||||
void GBAContextUnloadROM(struct GBAContext* context) {
|
void GBAContextUnloadROM(struct GBAContext* context) {
|
||||||
GBAUnloadROM(context->gba);
|
GBAUnloadROM(context->gba);
|
||||||
|
#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
|
||||||
GBADirectorySetDetachBase(&context->dirs);
|
GBADirectorySetDetachBase(&context->dirs);
|
||||||
|
#endif
|
||||||
if (context->rom) {
|
if (context->rom) {
|
||||||
context->rom->close(context->rom);
|
context->rom->close(context->rom);
|
||||||
context->rom = 0;
|
context->rom = 0;
|
||||||
|
|
|
@ -21,7 +21,9 @@ struct GBAContext {
|
||||||
const char* fname;
|
const char* fname;
|
||||||
struct VFile* save;
|
struct VFile* save;
|
||||||
struct VFile* bios;
|
struct VFile* bios;
|
||||||
|
#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
|
||||||
struct GBADirectorySet dirs;
|
struct GBADirectorySet dirs;
|
||||||
|
#endif
|
||||||
struct ARMComponent* components[GBA_COMPONENT_MAX];
|
struct ARMComponent* components[GBA_COMPONENT_MAX];
|
||||||
struct GBAConfig config;
|
struct GBAConfig config;
|
||||||
struct GBAOptions opts;
|
struct GBAOptions opts;
|
||||||
|
|
|
@ -5,8 +5,10 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
#include "directories.h"
|
#include "directories.h"
|
||||||
|
|
||||||
|
#include "gba/context/config.h"
|
||||||
#include "util/vfs.h"
|
#include "util/vfs.h"
|
||||||
|
|
||||||
|
#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
|
||||||
void GBADirectorySetInit(struct GBADirectorySet* dirs) {
|
void GBADirectorySetInit(struct GBADirectorySet* dirs) {
|
||||||
dirs->base = 0;
|
dirs->base = 0;
|
||||||
dirs->archive = 0;
|
dirs->archive = 0;
|
||||||
|
@ -99,3 +101,46 @@ struct VFile* GBADirectorySetOpenPath(struct GBADirectorySet* dirs, const char*
|
||||||
}
|
}
|
||||||
return file;
|
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"
|
#include "util/common.h"
|
||||||
|
|
||||||
|
#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
|
||||||
struct VDir;
|
struct VDir;
|
||||||
|
|
||||||
struct GBADirectorySet {
|
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 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
|
#endif
|
||||||
|
|
|
@ -205,8 +205,19 @@ void GBASkipBIOS(struct GBA* gba) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void GBAProcessEvents(struct ARMCore* cpu) {
|
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 {
|
do {
|
||||||
struct GBA* gba = (struct GBA*) cpu->master;
|
|
||||||
int32_t cycles = cpu->nextEvent;
|
int32_t cycles = cpu->nextEvent;
|
||||||
int32_t nextEvent = INT_MAX;
|
int32_t nextEvent = INT_MAX;
|
||||||
int32_t testEvent;
|
int32_t testEvent;
|
||||||
|
@ -216,16 +227,6 @@ static void GBAProcessEvents(struct ARMCore* cpu) {
|
||||||
}
|
}
|
||||||
#endif
|
#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);
|
testEvent = GBAVideoProcessEvents(&gba->video, cycles);
|
||||||
if (testEvent < nextEvent) {
|
if (testEvent < nextEvent) {
|
||||||
nextEvent = testEvent;
|
nextEvent = testEvent;
|
||||||
|
|
|
@ -15,6 +15,10 @@
|
||||||
#include "util/png-io.h"
|
#include "util/png-io.h"
|
||||||
#include "util/vfs.h"
|
#include "util/vfs.h"
|
||||||
|
|
||||||
|
#ifdef _3DS
|
||||||
|
#include <3ds.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
|
|
||||||
#define FPS_GRANULARITY 120
|
#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;
|
runner->lastFpsCheck = 1000000LL * tv.tv_sec + tv.tv_usec;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
|
#ifdef _3DS
|
||||||
|
running = aptMainLoop();
|
||||||
|
if (!running) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
uint32_t guiKeys;
|
uint32_t guiKeys;
|
||||||
GUIPollInput(&runner->params, &guiKeys, 0);
|
GUIPollInput(&runner->params, &guiKeys, 0);
|
||||||
if (guiKeys & (1 << GUI_INPUT_CANCEL)) {
|
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.activeRegion = memory->iwram;
|
||||||
cpu->memory.activeMask = SIZE_WORKING_IRAM - 1;
|
cpu->memory.activeMask = SIZE_WORKING_IRAM - 1;
|
||||||
break;
|
break;
|
||||||
|
case REGION_PALETTE_RAM:
|
||||||
|
cpu->memory.activeRegion = (uint32_t*) gba->video.palette;
|
||||||
|
cpu->memory.activeMask = SIZE_PALETTE_RAM - 1;
|
||||||
|
break;
|
||||||
case REGION_VRAM:
|
case REGION_VRAM:
|
||||||
cpu->memory.activeRegion = (uint32_t*) gba->video.renderer->vram;
|
cpu->memory.activeRegion = (uint32_t*) gba->video.renderer->vram;
|
||||||
cpu->memory.activeMask = 0x0000FFFF;
|
cpu->memory.activeMask = 0x0000FFFF;
|
||||||
break;
|
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:
|
||||||
case REGION_CART0_EX:
|
case REGION_CART0_EX:
|
||||||
case REGION_CART1:
|
case REGION_CART1:
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include "serialize.h"
|
#include "serialize.h"
|
||||||
|
|
||||||
#include "gba/audio.h"
|
#include "gba/audio.h"
|
||||||
|
#include "gba/cheats.h"
|
||||||
#include "gba/io.h"
|
#include "gba/io.h"
|
||||||
#include "gba/rr/rr.h"
|
#include "gba/rr/rr.h"
|
||||||
#include "gba/supervisor/thread.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);
|
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
|
#ifdef USE_PNG
|
||||||
static bool _savePNGState(struct GBA* gba, struct VFile* vf, struct GBAExtdata* extdata) {
|
static bool _savePNGState(struct GBA* gba, struct VFile* vf, struct GBAExtdata* extdata) {
|
||||||
unsigned stride;
|
unsigned stride;
|
||||||
|
@ -426,6 +435,20 @@ bool GBASaveStateNamed(struct GBA* gba, struct VFile* vf, int flags) {
|
||||||
}
|
}
|
||||||
svf->close(svf);
|
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
|
#ifdef USE_PNG
|
||||||
if (!(flags & SAVESTATE_SCREENSHOT)) {
|
if (!(flags & SAVESTATE_SCREENSHOT)) {
|
||||||
#else
|
#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);
|
struct GBASerializedState* state = vf->map(vf, sizeof(struct GBASerializedState), MAP_WRITE);
|
||||||
if (!state) {
|
if (!state) {
|
||||||
GBAExtdataDeinit(&extdata);
|
GBAExtdataDeinit(&extdata);
|
||||||
|
if (cheatVf) {
|
||||||
|
cheatVf->close(cheatVf);
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
GBASerialize(gba, state);
|
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);
|
vf->seek(vf, sizeof(struct GBASerializedState), SEEK_SET);
|
||||||
GBAExtdataSerialize(&extdata, vf);
|
GBAExtdataSerialize(&extdata, vf);
|
||||||
GBAExtdataDeinit(&extdata);
|
GBAExtdataDeinit(&extdata);
|
||||||
|
if (cheatVf) {
|
||||||
|
cheatVf->close(cheatVf);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
#ifdef USE_PNG
|
#ifdef USE_PNG
|
||||||
}
|
}
|
||||||
|
@ -461,6 +490,7 @@ struct GBASerializedState* GBAExtractState(struct VFile* vf, struct GBAExtdata*
|
||||||
return _loadPNGState(vf, extdata);
|
return _loadPNGState(vf, extdata);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
vf->seek(vf, 0, SEEK_SET);
|
||||||
if (vf->size(vf) < (ssize_t) sizeof(struct GBASerializedState)) {
|
if (vf->size(vf) < (ssize_t) sizeof(struct GBASerializedState)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -501,6 +531,17 @@ bool GBALoadStateNamed(struct GBA* gba, struct VFile* vf, int flags) {
|
||||||
svf->close(svf);
|
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);
|
GBAExtdataDeinit(&extdata);
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
|
@ -339,11 +339,13 @@ enum GBAExtdataTag {
|
||||||
EXTDATA_NONE = 0,
|
EXTDATA_NONE = 0,
|
||||||
EXTDATA_SCREENSHOT = 1,
|
EXTDATA_SCREENSHOT = 1,
|
||||||
EXTDATA_SAVEDATA = 2,
|
EXTDATA_SAVEDATA = 2,
|
||||||
|
EXTDATA_CHEATS = 3,
|
||||||
EXTDATA_MAX
|
EXTDATA_MAX
|
||||||
};
|
};
|
||||||
|
|
||||||
#define SAVESTATE_SCREENSHOT 1
|
#define SAVESTATE_SCREENSHOT 1
|
||||||
#define SAVESTATE_SAVEDATA 2
|
#define SAVESTATE_SAVEDATA 2
|
||||||
|
#define SAVESTATE_CHEATS 4
|
||||||
|
|
||||||
struct GBAExtdataItem {
|
struct GBAExtdataItem {
|
||||||
int32_t size;
|
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 GBASaveState(struct GBAThread* thread, struct VDir* dir, int slot, int flags);
|
||||||
bool GBALoadState(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);
|
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 GBASaveStateNamed(struct GBA* gba, struct VFile* vf, int flags);
|
||||||
bool GBALoadStateNamed(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;
|
threadContext->idleOptimization = opts->idleOptimization;
|
||||||
|
|
||||||
|
GBADirectorySetMapOptions(&threadContext->dirs, opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBAMapArgumentsToContext(const struct GBAArguments* args, struct GBAThread* threadContext) {
|
void GBAMapArgumentsToContext(const struct GBAArguments* args, struct GBAThread* threadContext) {
|
||||||
|
@ -441,7 +443,6 @@ bool GBAThreadStart(struct GBAThread* threadContext) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
GBADirectorySetInit(&threadContext->dirs);
|
|
||||||
_reloadDirectories(threadContext);
|
_reloadDirectories(threadContext);
|
||||||
|
|
||||||
MutexInit(&threadContext->stateMutex);
|
MutexInit(&threadContext->stateMutex);
|
||||||
|
@ -571,8 +572,6 @@ void GBAThreadJoin(struct GBAThread* threadContext) {
|
||||||
threadContext->patch->close(threadContext->patch);
|
threadContext->patch->close(threadContext->patch);
|
||||||
threadContext->patch = 0;
|
threadContext->patch = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
GBADirectorySetDeinit(&threadContext->dirs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GBAThreadIsActive(struct GBAThread* threadContext) {
|
bool GBAThreadIsActive(struct GBAThread* threadContext) {
|
||||||
|
|
|
@ -48,7 +48,9 @@ struct GBAThread {
|
||||||
struct GBAVideoRenderer* renderer;
|
struct GBAVideoRenderer* renderer;
|
||||||
struct GBASIODriverSet sioDrivers;
|
struct GBASIODriverSet sioDrivers;
|
||||||
struct ARMDebugger* debugger;
|
struct ARMDebugger* debugger;
|
||||||
|
#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
|
||||||
struct GBADirectorySet dirs;
|
struct GBADirectorySet dirs;
|
||||||
|
#endif
|
||||||
struct VFile* rom;
|
struct VFile* rom;
|
||||||
struct VFile* save;
|
struct VFile* save;
|
||||||
struct VFile* bios;
|
struct VFile* bios;
|
||||||
|
|
|
@ -45,6 +45,7 @@ static void _vd3dRewind(struct VDir* vd);
|
||||||
static struct VDirEntry* _vd3dListNext(struct VDir* vd);
|
static struct VDirEntry* _vd3dListNext(struct VDir* vd);
|
||||||
static struct VFile* _vd3dOpenFile(struct VDir* vd, const char* path, int mode);
|
static struct VFile* _vd3dOpenFile(struct VDir* vd, const char* path, int mode);
|
||||||
static struct VDir* _vd3dOpenDir(struct VDir* vd, const char* path);
|
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 const char* _vd3deName(struct VDirEntry* vde);
|
||||||
static enum VFSType _vd3deType(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.listNext = _vd3dListNext;
|
||||||
vd3d->d.openFile = _vd3dOpenFile;
|
vd3d->d.openFile = _vd3dOpenFile;
|
||||||
vd3d->d.openDir = _vd3dOpenDir;
|
vd3d->d.openDir = _vd3dOpenDir;
|
||||||
|
vd3d->d.deleteFile = _vd3dDeleteFile;
|
||||||
|
|
||||||
vd3d->vde.d.name = _vd3deName;
|
vd3d->vde.d.name = _vd3deName;
|
||||||
vd3d->vde.d.type = _vd3deType;
|
vd3d->vde.d.type = _vd3deType;
|
||||||
|
@ -257,6 +259,22 @@ static struct VDir* _vd3dOpenDir(struct VDir* vd, const char* path) {
|
||||||
return vd2;
|
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) {
|
static const char* _vd3deName(struct VDirEntry* vde) {
|
||||||
struct VDirEntry3DS* vd3de = (struct VDirEntry3DS*) vde;
|
struct VDirEntry3DS* vd3de = (struct VDirEntry3DS*) vde;
|
||||||
if (!vd3de->utf8Name[0]) {
|
if (!vd3de->utf8Name[0]) {
|
||||||
|
|
|
@ -92,6 +92,7 @@ AccessControlInfo:
|
||||||
|
|
||||||
MaxCpu : 0x9E # Default
|
MaxCpu : 0x9E # Default
|
||||||
CpuSpeed : 804mhz
|
CpuSpeed : 804mhz
|
||||||
|
EnableL2Cache : true
|
||||||
|
|
||||||
DisableDebug : true
|
DisableDebug : true
|
||||||
EnableForceDebug : false
|
EnableForceDebug : false
|
||||||
|
|
|
@ -70,6 +70,25 @@ unsigned GUIFontGlyphWidth(const struct GUIFont* font, uint32_t glyph) {
|
||||||
return defaultFontMetrics[glyph].width;
|
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) {
|
void GUIFontDrawGlyph(const struct GUIFont* font, int glyph_x, int glyph_y, uint32_t color, uint32_t glyph) {
|
||||||
ctrActivateTexture(&font->texture);
|
ctrActivateTexture(&font->texture);
|
||||||
|
|
||||||
|
@ -124,3 +143,14 @@ void GUIFontDrawIcon(const struct GUIFont* font, int x, int y, enum GUIAlignment
|
||||||
break;
|
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 _3DS_INPUT 0x3344534B
|
||||||
|
|
||||||
#define AUDIO_SAMPLES 0x80
|
#define AUDIO_SAMPLES 384
|
||||||
#define AUDIO_SAMPLE_BUFFER (AUDIO_SAMPLES * 24)
|
#define AUDIO_SAMPLE_BUFFER (AUDIO_SAMPLES * 16)
|
||||||
|
#define DSP_BUFFERS 4
|
||||||
|
|
||||||
FS_Archive sdmcArchive;
|
FS_Archive sdmcArchive;
|
||||||
|
|
||||||
|
@ -43,7 +44,12 @@ static struct GBA3DSRotationSource {
|
||||||
angularRate gyro;
|
angularRate gyro;
|
||||||
} rotation;
|
} rotation;
|
||||||
|
|
||||||
static bool hasSound;
|
static enum {
|
||||||
|
NO_SOUND,
|
||||||
|
DSP_SUPPORTED,
|
||||||
|
CSND_SUPPORTED
|
||||||
|
} hasSound;
|
||||||
|
|
||||||
// TODO: Move into context
|
// TODO: Move into context
|
||||||
static struct GBAVideoSoftwareRenderer renderer;
|
static struct GBAVideoSoftwareRenderer renderer;
|
||||||
static struct GBAAVStream stream;
|
static struct GBAAVStream stream;
|
||||||
|
@ -53,6 +59,10 @@ static size_t audioPos = 0;
|
||||||
static struct ctrTexture gbaOutputTexture;
|
static struct ctrTexture gbaOutputTexture;
|
||||||
static int guiDrawn;
|
static int guiDrawn;
|
||||||
static int screenCleanup;
|
static int screenCleanup;
|
||||||
|
static ndspWaveBuf dspBuffer[DSP_BUFFERS];
|
||||||
|
static int bufferId = 0;
|
||||||
|
|
||||||
|
static aptHookCookie cookie;
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
GUI_ACTIVE = 1,
|
GUI_ACTIVE = 1,
|
||||||
|
@ -70,12 +80,65 @@ enum {
|
||||||
|
|
||||||
extern bool allocateRomBuffer(void);
|
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) {
|
static void _map3DSKey(struct GBAInputMap* map, int ctrKey, enum GBAKey key) {
|
||||||
GBAInputBindKey(map, _3DS_INPUT, __builtin_ctz(ctrKey), 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;
|
u32 pleft = 0, pright = 0;
|
||||||
|
|
||||||
int loopMode = (flags >> 10) & 3;
|
int loopMode = (flags >> 10) & 3;
|
||||||
|
@ -187,7 +250,7 @@ static void _guiFinish(void) {
|
||||||
|
|
||||||
static void _setup(struct GBAGUIRunner* runner) {
|
static void _setup(struct GBAGUIRunner* runner) {
|
||||||
runner->context.gba->rotationSource = &rotation.d;
|
runner->context.gba->rotationSource = &rotation.d;
|
||||||
if (hasSound) {
|
if (hasSound != NO_SOUND) {
|
||||||
runner->context.gba->stream = &stream;
|
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.left, GBA_ARM7TDMI_FREQUENCY, 32768 * ratio);
|
||||||
blip_set_rates(runner->context.gba->audio.right, GBA_ARM7TDMI_FREQUENCY, 32768 * ratio);
|
blip_set_rates(runner->context.gba->audio.right, GBA_ARM7TDMI_FREQUENCY, 32768 * ratio);
|
||||||
#endif
|
#endif
|
||||||
if (hasSound) {
|
if (hasSound != NO_SOUND) {
|
||||||
|
audioPos = 0;
|
||||||
|
}
|
||||||
|
if (hasSound == CSND_SUPPORTED) {
|
||||||
memset(audioLeft, 0, AUDIO_SAMPLE_BUFFER * sizeof(int16_t));
|
memset(audioLeft, 0, AUDIO_SAMPLE_BUFFER * sizeof(int16_t));
|
||||||
memset(audioRight, 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));
|
_csndPlaySound(SOUND_REPEAT | SOUND_FORMAT_16BIT, 32768, 1.0, audioLeft, audioRight, AUDIO_SAMPLE_BUFFER * sizeof(int16_t));
|
||||||
csndExecCmds(false);
|
csndExecCmds(false);
|
||||||
|
} else if (hasSound == DSP_SUPPORTED) {
|
||||||
|
memset(audioLeft, 0, AUDIO_SAMPLE_BUFFER * 2 * sizeof(int16_t));
|
||||||
}
|
}
|
||||||
unsigned mode;
|
unsigned mode;
|
||||||
if (GBAConfigGetUIntValue(&runner->context.config, "screenMode", &mode) && mode != screenMode) {
|
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) {
|
static void _gameUnloaded(struct GBAGUIRunner* runner) {
|
||||||
if (hasSound) {
|
if (hasSound == CSND_SUPPORTED) {
|
||||||
CSND_SetPlayState(8, 0);
|
CSND_SetPlayState(8, 0);
|
||||||
CSND_SetPlayState(9, 0);
|
CSND_SetPlayState(9, 0);
|
||||||
csndExecCmds(false);
|
csndExecCmds(false);
|
||||||
|
@ -311,7 +378,7 @@ static void _drawFrame(struct GBAGUIRunner* runner, bool faded) {
|
||||||
GX_TRANSFER_OUT_TILED(1) | GX_TRANSFER_FLIP_VERT(1));
|
GX_TRANSFER_OUT_TILED(1) | GX_TRANSFER_FLIP_VERT(1));
|
||||||
|
|
||||||
#if RESAMPLE_LIBRARY == RESAMPLE_BLIP_BUF
|
#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.left);
|
||||||
blip_clear(runner->context.gba->audio.right);
|
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) {
|
static void _postAudioBuffer(struct GBAAVStream* stream, struct GBAAudio* audio) {
|
||||||
UNUSED(stream);
|
UNUSED(stream);
|
||||||
|
if (hasSound == CSND_SUPPORTED) {
|
||||||
#if RESAMPLE_LIBRARY == RESAMPLE_BLIP_BUF
|
#if RESAMPLE_LIBRARY == RESAMPLE_BLIP_BUF
|
||||||
blip_read_samples(audio->left, &audioLeft[audioPos], AUDIO_SAMPLES, false);
|
blip_read_samples(audio->left, &audioLeft[audioPos], AUDIO_SAMPLES, false);
|
||||||
blip_read_samples(audio->right, &audioRight[audioPos], AUDIO_SAMPLES, false);
|
blip_read_samples(audio->right, &audioRight[audioPos], AUDIO_SAMPLES, false);
|
||||||
#elif RESAMPLE_LIBRARY == RESAMPLE_NN
|
#elif RESAMPLE_LIBRARY == RESAMPLE_NN
|
||||||
GBAAudioCopy(audio, &audioLeft[audioPos], &audioRight[audioPos], AUDIO_SAMPLES);
|
GBAAudioCopy(audio, &audioLeft[audioPos], &audioRight[audioPos], AUDIO_SAMPLES);
|
||||||
#endif
|
#endif
|
||||||
GSPGPU_FlushDataCache(&audioLeft[audioPos], AUDIO_SAMPLES * sizeof(int16_t));
|
GSPGPU_FlushDataCache(&audioLeft[audioPos], AUDIO_SAMPLES * sizeof(int16_t));
|
||||||
GSPGPU_FlushDataCache(&audioRight[audioPos], AUDIO_SAMPLES * sizeof(int16_t));
|
GSPGPU_FlushDataCache(&audioRight[audioPos], AUDIO_SAMPLES * sizeof(int16_t));
|
||||||
audioPos = (audioPos + AUDIO_SAMPLES) % AUDIO_SAMPLE_BUFFER;
|
audioPos = (audioPos + AUDIO_SAMPLES) % AUDIO_SAMPLE_BUFFER;
|
||||||
if (audioPos == AUDIO_SAMPLES * 3) {
|
if (audioPos == AUDIO_SAMPLES * 3) {
|
||||||
u8 playing = 0;
|
u8 playing = 0;
|
||||||
csndIsPlaying(0x8, &playing);
|
csndIsPlaying(0x8, &playing);
|
||||||
if (!playing) {
|
if (!playing) {
|
||||||
CSND_SetPlayState(0x8, 1);
|
CSND_SetPlayState(0x8, 1);
|
||||||
CSND_SetPlayState(0x9, 1);
|
CSND_SetPlayState(0x9, 1);
|
||||||
csndExecCmds(false);
|
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;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ptmuInit();
|
aptHook(&cookie, _aptHook, 0);
|
||||||
hasSound = !csndInit();
|
|
||||||
|
|
||||||
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);
|
audioLeft = linearMemAlign(AUDIO_SAMPLE_BUFFER * sizeof(int16_t), 0x80);
|
||||||
audioRight = 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) {
|
if (ctrInitGpu() < 0) {
|
||||||
gbaOutputTexture.data = 0;
|
gbaOutputTexture.data = 0;
|
||||||
goto cleanup;
|
_cleanup();
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ctrTexture_Init(&gbaOutputTexture);
|
ctrTexture_Init(&gbaOutputTexture);
|
||||||
|
@ -504,7 +614,8 @@ int main() {
|
||||||
void* outputTextureEnd = (u8*)gbaOutputTexture.data + 256 * 256 * 2;
|
void* outputTextureEnd = (u8*)gbaOutputTexture.data + 256 * 256 * 2;
|
||||||
|
|
||||||
if (!gbaOutputTexture.data) {
|
if (!gbaOutputTexture.data) {
|
||||||
goto cleanup;
|
_cleanup();
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Zero texture data to make sure no garbage around the border interferes with filtering
|
// Zero texture data to make sure no garbage around the border interferes with filtering
|
||||||
|
@ -523,7 +634,8 @@ int main() {
|
||||||
struct GUIFont* font = GUIFontCreate();
|
struct GUIFont* font = GUIFontCreate();
|
||||||
|
|
||||||
if (!font) {
|
if (!font) {
|
||||||
goto cleanup;
|
_cleanup();
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct GBAGUIRunner runner = {
|
struct GBAGUIRunner runner = {
|
||||||
|
@ -610,24 +722,6 @@ int main() {
|
||||||
GBAGUIRunloop(&runner);
|
GBAGUIRunloop(&runner);
|
||||||
GBAGUIDeinit(&runner);
|
GBAGUIDeinit(&runner);
|
||||||
|
|
||||||
cleanup:
|
_cleanup();
|
||||||
if (renderer.outputBuffer) {
|
|
||||||
linearFree(renderer.outputBuffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gbaOutputTexture.data) {
|
|
||||||
ctrDeinitGpu();
|
|
||||||
vramFree(gbaOutputTexture.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
gfxExit();
|
|
||||||
|
|
||||||
if (hasSound) {
|
|
||||||
linearFree(audioLeft);
|
|
||||||
linearFree(audioRight);
|
|
||||||
}
|
|
||||||
|
|
||||||
csndExit();
|
|
||||||
ptmuExit();
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,6 @@
|
||||||
static const struct option _options[] = {
|
static const struct option _options[] = {
|
||||||
{ "bios", required_argument, 0, 'b' },
|
{ "bios", required_argument, 0, 'b' },
|
||||||
{ "cheats", required_argument, 0, 'c' },
|
{ "cheats", required_argument, 0, 'c' },
|
||||||
{ "dirmode", required_argument, 0, 'D' },
|
|
||||||
{ "frameskip", required_argument, 0, 's' },
|
{ "frameskip", required_argument, 0, 's' },
|
||||||
#ifdef USE_CLI_DEBUGGER
|
#ifdef USE_CLI_DEBUGGER
|
||||||
{ "debug", no_argument, 0, 'd' },
|
{ "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) {
|
bool parseArguments(struct GBAArguments* opts, struct GBAConfig* config, int argc, char* const* argv, struct SubParser* subparser) {
|
||||||
int ch;
|
int ch;
|
||||||
char options[64] =
|
char options[64] =
|
||||||
"b:c:Dhl:p:s:v:"
|
"b:c:hl:p:s:v:"
|
||||||
#ifdef USE_CLI_DEBUGGER
|
#ifdef USE_CLI_DEBUGGER
|
||||||
"d"
|
"d"
|
||||||
#endif
|
#endif
|
||||||
|
@ -86,9 +85,6 @@ bool parseArguments(struct GBAArguments* opts, struct GBAConfig* config, int arg
|
||||||
case 'c':
|
case 'c':
|
||||||
opts->cheatsFile = strdup(optarg);
|
opts->cheatsFile = strdup(optarg);
|
||||||
break;
|
break;
|
||||||
case 'D':
|
|
||||||
opts->dirmode = true;
|
|
||||||
break;
|
|
||||||
#ifdef USE_CLI_DEBUGGER
|
#ifdef USE_CLI_DEBUGGER
|
||||||
case 'd':
|
case 'd':
|
||||||
if (opts->debuggerType != DEBUGGER_NONE) {
|
if (opts->debuggerType != DEBUGGER_NONE) {
|
||||||
|
|
|
@ -25,7 +25,6 @@ struct GBAArguments {
|
||||||
char* fname;
|
char* fname;
|
||||||
char* patch;
|
char* patch;
|
||||||
char* cheatsFile;
|
char* cheatsFile;
|
||||||
bool dirmode;
|
|
||||||
char* movie;
|
char* movie;
|
||||||
|
|
||||||
enum DebuggerType debuggerType;
|
enum DebuggerType debuggerType;
|
||||||
|
|
|
@ -32,6 +32,8 @@
|
||||||
<integer>0</integer>
|
<integer>0</integer>
|
||||||
<key>OEGameCoreSupportsRewinding</key>
|
<key>OEGameCoreSupportsRewinding</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
<key>OEGameCoreSupportsCheatCode</key>
|
||||||
|
<true/>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
<key>OEGameCorePlayerCount</key>
|
<key>OEGameCorePlayerCount</key>
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#include "util/common.h"
|
#include "util/common.h"
|
||||||
|
|
||||||
#include "gba/cheats.h"
|
#include "gba/cheats.h"
|
||||||
|
#include "gba/cheats/gameshark.h"
|
||||||
#include "gba/renderers/video-software.h"
|
#include "gba/renderers/video-software.h"
|
||||||
#include "gba/serialize.h"
|
#include "gba/serialize.h"
|
||||||
#include "gba/context/context.h"
|
#include "gba/context/context.h"
|
||||||
|
@ -45,7 +46,7 @@
|
||||||
struct GBAContext context;
|
struct GBAContext context;
|
||||||
struct GBAVideoSoftwareRenderer renderer;
|
struct GBAVideoSoftwareRenderer renderer;
|
||||||
struct GBACheatDevice cheats;
|
struct GBACheatDevice cheats;
|
||||||
struct GBACheatSet cheatSet;
|
NSMutableDictionary *cheatSets;
|
||||||
uint16_t keys;
|
uint16_t keys;
|
||||||
}
|
}
|
||||||
@end
|
@end
|
||||||
|
@ -70,8 +71,7 @@
|
||||||
GBAAudioResizeBuffer(&context.gba->audio, SAMPLES);
|
GBAAudioResizeBuffer(&context.gba->audio, SAMPLES);
|
||||||
GBACheatDeviceCreate(&cheats);
|
GBACheatDeviceCreate(&cheats);
|
||||||
GBACheatAttachDevice(context.gba, &cheats);
|
GBACheatAttachDevice(context.gba, &cheats);
|
||||||
GBACheatSetInit(&cheatSet, "openemu");
|
cheatSets = [[NSMutableDictionary alloc] init];
|
||||||
GBACheatAddSet(&cheats, &cheatSet);
|
|
||||||
keys = 0;
|
keys = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,9 +81,18 @@
|
||||||
- (void)dealloc
|
- (void)dealloc
|
||||||
{
|
{
|
||||||
GBAContextDeinit(&context);
|
GBAContextDeinit(&context);
|
||||||
GBACheatRemoveSet(&cheats, &cheatSet);
|
[cheatSets enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
|
||||||
|
UNUSED(key);
|
||||||
|
UNUSED(stop);
|
||||||
|
GBACheatRemoveSet(&cheats, [obj pointerValue]);
|
||||||
|
}];
|
||||||
GBACheatDeviceDestroy(&cheats);
|
GBACheatDeviceDestroy(&cheats);
|
||||||
GBACheatSetDeinit(&cheatSet);
|
[cheatSets enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
|
||||||
|
UNUSED(key);
|
||||||
|
UNUSED(stop);
|
||||||
|
GBACheatSetDeinit([obj pointerValue]);
|
||||||
|
}];
|
||||||
|
[cheatSets release];
|
||||||
free(renderer.outputBuffer);
|
free(renderer.outputBuffer);
|
||||||
|
|
||||||
[super dealloc];
|
[super dealloc];
|
||||||
|
@ -275,5 +284,33 @@ const int GBAMap[] = {
|
||||||
keys &= ~(1 << GBAMap[button]);
|
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
|
@end
|
||||||
|
|
||||||
|
|
|
@ -99,12 +99,12 @@ void GBAGLContextPostFrame(struct VideoBackend* v, const void* frame) {
|
||||||
glBindTexture(GL_TEXTURE_2D, context->tex);
|
glBindTexture(GL_TEXTURE_2D, context->tex);
|
||||||
#ifdef COLOR_16_BIT
|
#ifdef COLOR_16_BIT
|
||||||
#ifdef COLOR_5_6_5
|
#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
|
#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
|
#endif
|
||||||
#else
|
#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
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,13 @@
|
||||||
|
|
||||||
#define MAX_PASSES 8
|
#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 =
|
static const char* const _vertexShader =
|
||||||
"attribute vec4 position;\n"
|
"attribute vec4 position;\n"
|
||||||
"varying vec2 texCoord;\n"
|
"varying vec2 texCoord;\n"
|
||||||
|
@ -35,12 +42,15 @@ static const char* const _fragmentShader =
|
||||||
"varying vec2 texCoord;\n"
|
"varying vec2 texCoord;\n"
|
||||||
"uniform sampler2D tex;\n"
|
"uniform sampler2D tex;\n"
|
||||||
"uniform float gamma;\n"
|
"uniform float gamma;\n"
|
||||||
|
"uniform vec3 desaturation;\n"
|
||||||
"uniform vec3 scale;\n"
|
"uniform vec3 scale;\n"
|
||||||
"uniform vec3 bias;\n"
|
"uniform vec3 bias;\n"
|
||||||
|
|
||||||
"void main() {\n"
|
"void main() {\n"
|
||||||
" vec4 color = texture2D(tex, texCoord);\n"
|
" vec4 color = texture2D(tex, texCoord);\n"
|
||||||
" color.a = 1.;\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"
|
" color.rgb = scale * pow(color.rgb, vec3(gamma, gamma, gamma)) + bias;\n"
|
||||||
" gl_FragColor = color;\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);
|
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].name = "gamma";
|
||||||
uniforms[0].readableName = "Gamma";
|
uniforms[0].readableName = "Gamma";
|
||||||
uniforms[0].type = GL_FLOAT;
|
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[0] = 1.0f;
|
||||||
uniforms[2].max.fvec3[1] = 1.0f;
|
uniforms[2].max.fvec3[1] = 1.0f;
|
||||||
uniforms[2].max.fvec3[2] = 1.0f;
|
uniforms[2].max.fvec3[2] = 1.0f;
|
||||||
GBAGLES2ShaderInit(&context->initialShader, _vertexShader, _fragmentShader, -1, -1, uniforms, 3);
|
uniforms[3].name = "desaturation";
|
||||||
GBAGLES2ShaderInit(&context->finalShader, 0, 0, 0, 0, 0, 0);
|
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);
|
glDeleteFramebuffers(1, &context->finalShader.fbo);
|
||||||
context->finalShader.fbo = 0;
|
context->finalShader.fbo = 0;
|
||||||
}
|
}
|
||||||
|
@ -173,6 +195,12 @@ void _drawShader(struct GBAGLES2Shader* shader) {
|
||||||
drawH = viewport[3];
|
drawH = viewport[3];
|
||||||
padH = viewport[1];
|
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);
|
glViewport(padW, padH, drawW, drawH);
|
||||||
if (!shader->width || !shader->height) {
|
if (!shader->width || !shader->height) {
|
||||||
GLint oldTex;
|
GLint oldTex;
|
||||||
|
@ -182,7 +210,7 @@ void _drawShader(struct GBAGLES2Shader* shader) {
|
||||||
glBindTexture(GL_TEXTURE_2D, oldTex);
|
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);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, shader->filter ? GL_LINEAR : GL_NEAREST);
|
||||||
glUseProgram(shader->program);
|
glUseProgram(shader->program);
|
||||||
glUniform1i(shader->texLocation, 0);
|
glUniform1i(shader->texLocation, 0);
|
||||||
|
@ -263,7 +291,6 @@ void GBAGLES2ContextDrawFrame(struct VideoBackend* v) {
|
||||||
void GBAGLES2ContextPostFrame(struct VideoBackend* v, const void* frame) {
|
void GBAGLES2ContextPostFrame(struct VideoBackend* v, const void* frame) {
|
||||||
struct GBAGLES2Context* context = (struct GBAGLES2Context*) v;
|
struct GBAGLES2Context* context = (struct GBAGLES2Context*) v;
|
||||||
glBindTexture(GL_TEXTURE_2D, context->tex);
|
glBindTexture(GL_TEXTURE_2D, context->tex);
|
||||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, 256);
|
|
||||||
#ifdef COLOR_16_BIT
|
#ifdef COLOR_16_BIT
|
||||||
#ifdef COLOR_5_6_5
|
#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);
|
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
|
#else
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, 0, GL_RGBA, GL_UNSIGNED_BYTE, frame);
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, 0, GL_RGBA, GL_UNSIGNED_BYTE, frame);
|
||||||
#endif
|
#endif
|
||||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBAGLES2ContextCreate(struct GBAGLES2Context* context) {
|
void GBAGLES2ContextCreate(struct GBAGLES2Context* context) {
|
||||||
|
@ -290,9 +316,10 @@ void GBAGLES2ContextCreate(struct GBAGLES2Context* context) {
|
||||||
context->nShaders = 0;
|
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->width = width >= 0 ? width : VIDEO_HORIZONTAL_PIXELS;
|
||||||
shader->height = height >= 0 ? height : VIDEO_VERTICAL_PIXELS;
|
shader->height = height >= 0 ? height : VIDEO_VERTICAL_PIXELS;
|
||||||
|
shader->integerScaling = integerScaling;
|
||||||
shader->filter = false;
|
shader->filter = false;
|
||||||
shader->blend = false;
|
shader->blend = false;
|
||||||
shader->uniforms = uniforms;
|
shader->uniforms = uniforms;
|
||||||
|
@ -314,16 +341,27 @@ void GBAGLES2ShaderInit(struct GBAGLES2Shader* shader, const char* vs, const cha
|
||||||
shader->program = glCreateProgram();
|
shader->program = glCreateProgram();
|
||||||
shader->vertexShader = glCreateShader(GL_VERTEX_SHADER);
|
shader->vertexShader = glCreateShader(GL_VERTEX_SHADER);
|
||||||
shader->fragmentShader = glCreateShader(GL_FRAGMENT_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) {
|
if (vs) {
|
||||||
glShaderSource(shader->vertexShader, 1, (const GLchar**) &vs, 0);
|
shaderBuffer[1] = vs;
|
||||||
} else {
|
} else {
|
||||||
glShaderSource(shader->vertexShader, 1, (const GLchar**) &_nullVertexShader, 0);
|
shaderBuffer[1] = _nullVertexShader;
|
||||||
}
|
}
|
||||||
|
glShaderSource(shader->vertexShader, 2, shaderBuffer, 0);
|
||||||
|
|
||||||
if (fs) {
|
if (fs) {
|
||||||
glShaderSource(shader->fragmentShader, 1, (const GLchar**) &fs, 0);
|
shaderBuffer[1] = fs;
|
||||||
} else {
|
} 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->vertexShader);
|
||||||
glAttachShader(shader->program, shader->fragmentShader);
|
glAttachShader(shader->program, shader->fragmentShader);
|
||||||
char log[1024];
|
char log[1024];
|
||||||
|
@ -771,8 +809,10 @@ bool GBAGLES2ShaderLoad(struct VideoShader* shader, struct VDir* dir) {
|
||||||
}
|
}
|
||||||
int width = 0;
|
int width = 0;
|
||||||
int height = 0;
|
int height = 0;
|
||||||
|
int scaling = 0;
|
||||||
_lookupIntValue(&description, passName, "width", &width);
|
_lookupIntValue(&description, passName, "width", &width);
|
||||||
_lookupIntValue(&description, passName, "height", &height);
|
_lookupIntValue(&description, passName, "height", &height);
|
||||||
|
_lookupIntValue(&description, passName, "integerScaling", &scaling);
|
||||||
|
|
||||||
struct GBAGLES2UniformList uniformVector;
|
struct GBAGLES2UniformList uniformVector;
|
||||||
GBAGLES2UniformListInit(&uniformVector, 0);
|
GBAGLES2UniformListInit(&uniformVector, 0);
|
||||||
|
@ -790,7 +830,7 @@ bool GBAGLES2ShaderLoad(struct VideoShader* shader, struct VDir* dir) {
|
||||||
memcpy(uniformBlock, GBAGLES2UniformListGetPointer(&uniformVector, 0), sizeof(*uniformBlock) * u);
|
memcpy(uniformBlock, GBAGLES2UniformListGetPointer(&uniformVector, 0), sizeof(*uniformBlock) * u);
|
||||||
GBAGLES2UniformListDeinit(&uniformVector);
|
GBAGLES2UniformListDeinit(&uniformVector);
|
||||||
|
|
||||||
GBAGLES2ShaderInit(&shaderBlock[n], vssrc, fssrc, width, height, uniformBlock, u);
|
GBAGLES2ShaderInit(&shaderBlock[n], vssrc, fssrc, width, height, scaling, uniformBlock, u);
|
||||||
int b = 0;
|
int b = 0;
|
||||||
_lookupIntValue(&description, passName, "blend", &b);
|
_lookupIntValue(&description, passName, "blend", &b);
|
||||||
if (b) {
|
if (b) {
|
||||||
|
|
|
@ -53,6 +53,7 @@ struct GBAGLES2Uniform {
|
||||||
struct GBAGLES2Shader {
|
struct GBAGLES2Shader {
|
||||||
unsigned width;
|
unsigned width;
|
||||||
unsigned height;
|
unsigned height;
|
||||||
|
bool integerScaling;
|
||||||
bool filter;
|
bool filter;
|
||||||
bool blend;
|
bool blend;
|
||||||
GLuint tex;
|
GLuint tex;
|
||||||
|
@ -83,7 +84,7 @@ struct GBAGLES2Context {
|
||||||
|
|
||||||
void GBAGLES2ContextCreate(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 GBAGLES2ShaderDeinit(struct GBAGLES2Shader*);
|
||||||
void GBAGLES2ShaderAttach(struct GBAGLES2Context*, struct GBAGLES2Shader*, size_t nShaders);
|
void GBAGLES2ShaderAttach(struct GBAGLES2Context*, struct GBAGLES2Shader*, size_t nShaders);
|
||||||
void GBAGLES2ShaderDetach(struct GBAGLES2Context*);
|
void GBAGLES2ShaderDetach(struct GBAGLES2Context*);
|
||||||
|
|
|
@ -49,17 +49,36 @@ unsigned GUIFontGlyphWidth(const struct GUIFont* font, uint32_t glyph) {
|
||||||
return defaultFontMetrics[glyph].width * 2;
|
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) {
|
void GUIFontDrawGlyph(const struct GUIFont* font, int x, int y, uint32_t color, uint32_t glyph) {
|
||||||
if (glyph > 0x7F) {
|
if (glyph > 0x7F) {
|
||||||
glyph = '?';
|
glyph = '?';
|
||||||
}
|
}
|
||||||
struct GUIFontGlyphMetric metric = defaultFontMetrics[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 & 15) * CELL_WIDTH + metric.padding.left * 2,
|
||||||
(glyph >> 4) * CELL_HEIGHT + metric.padding.top * 2,
|
(glyph >> 4) * CELL_HEIGHT + metric.padding.top * 2,
|
||||||
CELL_WIDTH - (metric.padding.left + metric.padding.right) * 2,
|
CELL_WIDTH - (metric.padding.left + metric.padding.right) * 2,
|
||||||
CELL_HEIGHT - (metric.padding.top + metric.padding.bottom) * 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) {
|
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) {
|
switch (orient) {
|
||||||
case GUI_ORIENT_HMIRROR:
|
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.x * 2, metric.y * 2,
|
||||||
metric.width * 2, metric.height * 2,
|
metric.width * 2, metric.height * 2,
|
||||||
-1, 1, color);
|
-1, 1, color);
|
||||||
return;
|
return;
|
||||||
case GUI_ORIENT_VMIRROR:
|
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.x * 2, metric.y * 2,
|
||||||
metric.width * 2, metric.height * 2,
|
metric.width * 2, metric.height * 2,
|
||||||
1, -1, color);
|
1, -1, color);
|
||||||
|
@ -107,3 +126,14 @@ void GUIFontDrawIcon(const struct GUIFont* font, int x, int y, enum GUIAlignment
|
||||||
break;
|
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>
|
#include <vita2d.h>
|
||||||
|
|
||||||
PSP2_MODULE_INFO(0, 0, "mGBA");
|
|
||||||
|
|
||||||
static void _drawStart(void) {
|
static void _drawStart(void) {
|
||||||
vita2d_set_vblank_wait(false);
|
vita2d_set_vblank_wait(false);
|
||||||
vita2d_start_drawing();
|
vita2d_start_drawing();
|
||||||
|
@ -43,29 +41,29 @@ static uint32_t _pollInput(void) {
|
||||||
SceCtrlData pad;
|
SceCtrlData pad;
|
||||||
sceCtrlPeekBufferPositive(0, &pad, 1);
|
sceCtrlPeekBufferPositive(0, &pad, 1);
|
||||||
int input = 0;
|
int input = 0;
|
||||||
if (pad.buttons & PSP2_CTRL_TRIANGLE) {
|
if (pad.buttons & SCE_CTRL_TRIANGLE) {
|
||||||
input |= 1 << GUI_INPUT_CANCEL;
|
input |= 1 << GUI_INPUT_CANCEL;
|
||||||
}
|
}
|
||||||
if (pad.buttons & PSP2_CTRL_SQUARE) {
|
if (pad.buttons & SCE_CTRL_SQUARE) {
|
||||||
input |= 1 << GBA_GUI_INPUT_SCREEN_MODE;
|
input |= 1 << GBA_GUI_INPUT_SCREEN_MODE;
|
||||||
}
|
}
|
||||||
if (pad.buttons & PSP2_CTRL_CIRCLE) {
|
if (pad.buttons & SCE_CTRL_CIRCLE) {
|
||||||
input |= 1 << GUI_INPUT_BACK;
|
input |= 1 << GUI_INPUT_BACK;
|
||||||
}
|
}
|
||||||
if (pad.buttons & PSP2_CTRL_CROSS) {
|
if (pad.buttons & SCE_CTRL_CROSS) {
|
||||||
input |= 1 << GUI_INPUT_SELECT;
|
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;
|
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;
|
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;
|
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;
|
input |= 1 << GUI_INPUT_RIGHT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,7 +106,7 @@ int main() {
|
||||||
GUI_PARAMS_TRAIL
|
GUI_PARAMS_TRAIL
|
||||||
},
|
},
|
||||||
.configExtra = (struct GUIMenuItem[]) {
|
.configExtra = (struct GUIMenuItem[]) {
|
||||||
{
|
{
|
||||||
.title = "Screen mode",
|
.title = "Screen mode",
|
||||||
.data = "screenMode",
|
.data = "screenMode",
|
||||||
.submenu = 0,
|
.submenu = 0,
|
||||||
|
@ -138,10 +136,10 @@ int main() {
|
||||||
"R",
|
"R",
|
||||||
0, // L2?
|
0, // L2?
|
||||||
0, // R2?
|
0, // R2?
|
||||||
"Triangle",
|
"\1\xC",
|
||||||
"Circle",
|
"\1\xA",
|
||||||
"Cross",
|
"\1\xB",
|
||||||
"Square"
|
"\1\xD"
|
||||||
},
|
},
|
||||||
.nKeys = 16
|
.nKeys = 16
|
||||||
},
|
},
|
||||||
|
|
|
@ -65,7 +65,7 @@ static void _mapVitaKey(struct GBAInputMap* map, int pspKey, enum GBAKey key) {
|
||||||
static THREAD_ENTRY _audioThread(void* context) {
|
static THREAD_ENTRY _audioThread(void* context) {
|
||||||
struct GBAPSP2AudioContext* audio = (struct GBAPSP2AudioContext*) context;
|
struct GBAPSP2AudioContext* audio = (struct GBAPSP2AudioContext*) context;
|
||||||
struct GBAStereoSample buffer[PSP2_SAMPLES];
|
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) {
|
while (audio->running) {
|
||||||
memset(buffer, 0, sizeof(buffer));
|
memset(buffer, 0, sizeof(buffer));
|
||||||
MutexLock(&audio->mutex);
|
MutexLock(&audio->mutex);
|
||||||
|
@ -75,7 +75,7 @@ static THREAD_ENTRY _audioThread(void* context) {
|
||||||
len = PSP2_SAMPLES;
|
len = PSP2_SAMPLES;
|
||||||
}
|
}
|
||||||
if (len > 0) {
|
if (len > 0) {
|
||||||
len &= ~(PSP2_AUDIO_MIN_LEN - 1);
|
len &= ~(SCE_AUDIO_MIN_LEN - 1);
|
||||||
CircleBufferRead(&audio->buffer, buffer, len * sizeof(buffer[0]));
|
CircleBufferRead(&audio->buffer, buffer, len * sizeof(buffer[0]));
|
||||||
MutexUnlock(&audio->mutex);
|
MutexUnlock(&audio->mutex);
|
||||||
sceAudioOutOutput(audioPort, buffer);
|
sceAudioOutOutput(audioPort, buffer);
|
||||||
|
@ -137,16 +137,16 @@ uint16_t GBAPSP2PollInput(struct GBAGUIRunner* runner) {
|
||||||
|
|
||||||
void GBAPSP2Setup(struct GBAGUIRunner* runner) {
|
void GBAPSP2Setup(struct GBAGUIRunner* runner) {
|
||||||
scePowerSetArmClockFrequency(80);
|
scePowerSetArmClockFrequency(80);
|
||||||
_mapVitaKey(&runner->context.inputMap, PSP2_CTRL_CROSS, GBA_KEY_A);
|
_mapVitaKey(&runner->context.inputMap, SCE_CTRL_CROSS, GBA_KEY_A);
|
||||||
_mapVitaKey(&runner->context.inputMap, PSP2_CTRL_CIRCLE, GBA_KEY_B);
|
_mapVitaKey(&runner->context.inputMap, SCE_CTRL_CIRCLE, GBA_KEY_B);
|
||||||
_mapVitaKey(&runner->context.inputMap, PSP2_CTRL_START, GBA_KEY_START);
|
_mapVitaKey(&runner->context.inputMap, SCE_CTRL_START, GBA_KEY_START);
|
||||||
_mapVitaKey(&runner->context.inputMap, PSP2_CTRL_SELECT, GBA_KEY_SELECT);
|
_mapVitaKey(&runner->context.inputMap, SCE_CTRL_SELECT, GBA_KEY_SELECT);
|
||||||
_mapVitaKey(&runner->context.inputMap, PSP2_CTRL_UP, GBA_KEY_UP);
|
_mapVitaKey(&runner->context.inputMap, SCE_CTRL_UP, GBA_KEY_UP);
|
||||||
_mapVitaKey(&runner->context.inputMap, PSP2_CTRL_DOWN, GBA_KEY_DOWN);
|
_mapVitaKey(&runner->context.inputMap, SCE_CTRL_DOWN, GBA_KEY_DOWN);
|
||||||
_mapVitaKey(&runner->context.inputMap, PSP2_CTRL_LEFT, GBA_KEY_LEFT);
|
_mapVitaKey(&runner->context.inputMap, SCE_CTRL_LEFT, GBA_KEY_LEFT);
|
||||||
_mapVitaKey(&runner->context.inputMap, PSP2_CTRL_RIGHT, GBA_KEY_RIGHT);
|
_mapVitaKey(&runner->context.inputMap, SCE_CTRL_RIGHT, GBA_KEY_RIGHT);
|
||||||
_mapVitaKey(&runner->context.inputMap, PSP2_CTRL_LTRIGGER, GBA_KEY_L);
|
_mapVitaKey(&runner->context.inputMap, SCE_CTRL_LTRIGGER, GBA_KEY_L);
|
||||||
_mapVitaKey(&runner->context.inputMap, PSP2_CTRL_RTRIGGER, GBA_KEY_R);
|
_mapVitaKey(&runner->context.inputMap, SCE_CTRL_RTRIGGER, GBA_KEY_R);
|
||||||
|
|
||||||
struct GBAAxis desc = { GBA_KEY_DOWN, GBA_KEY_UP, 192, 64 };
|
struct GBAAxis desc = { GBA_KEY_DOWN, GBA_KEY_UP, 192, 64 };
|
||||||
GBAInputBindAxis(&runner->context.inputMap, PSP2_INPUT, 0, &desc);
|
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 VDirEntry* _vdsceListNext(struct VDir* vd);
|
||||||
static struct VFile* _vdsceOpenFile(struct VDir* vd, const char* path, int mode);
|
static struct VFile* _vdsceOpenFile(struct VDir* vd, const char* path, int mode);
|
||||||
static struct VDir* _vdsceOpenDir(struct VDir* vd, const char* path);
|
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 const char* _vdesceName(struct VDirEntry* vde);
|
||||||
static enum VFSType _vdesceType(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.listNext = _vdsceListNext;
|
||||||
vd->d.openFile = _vdsceOpenFile;
|
vd->d.openFile = _vdsceOpenFile;
|
||||||
vd->d.openDir = _vdsceOpenDir;
|
vd->d.openDir = _vdsceOpenDir;
|
||||||
|
vd->d.deleteFile = _vdsceDeleteFile;
|
||||||
vd->path = strdup(path);
|
vd->path = strdup(path);
|
||||||
|
|
||||||
vd->de.d.name = _vdesceName;
|
vd->de.d.name = _vdesceName;
|
||||||
|
@ -215,6 +217,20 @@ struct VDir* _vdsceOpenDir(struct VDir* vd, const char* path) {
|
||||||
return vd2;
|
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) {
|
static const char* _vdesceName(struct VDirEntry* vde) {
|
||||||
struct VDirEntrySce* vdesce = (struct VDirEntrySce*) vde;
|
struct VDirEntrySce* vdesce = (struct VDirEntrySce*) vde;
|
||||||
return vdesce->ent.d_name;
|
return vdesce->ent.d_name;
|
||||||
|
@ -222,7 +238,7 @@ static const char* _vdesceName(struct VDirEntry* vde) {
|
||||||
|
|
||||||
static enum VFSType _vdesceType(struct VDirEntry* vde) {
|
static enum VFSType _vdesceType(struct VDirEntry* vde) {
|
||||||
struct VDirEntrySce* vdesce = (struct VDirEntrySce*) 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_DIRECTORY;
|
||||||
}
|
}
|
||||||
return VFS_FILE;
|
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.addSet, SIGNAL(clicked()), this, SLOT(addSet()));
|
||||||
connect(m_ui.remove, SIGNAL(clicked()), this, SLOT(removeSet()));
|
connect(m_ui.remove, SIGNAL(clicked()), this, SLOT(removeSet()));
|
||||||
connect(controller, SIGNAL(gameStopped(GBAThread*)), &m_model, SLOT(invalidated()));
|
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]() {
|
connect(m_ui.add, &QPushButton::clicked, [this]() {
|
||||||
enterCheat(GBACheatAddLine);
|
enterCheat(GBACheatAddLine);
|
||||||
|
|
|
@ -128,6 +128,7 @@ ConfigController::~ConfigController() {
|
||||||
|
|
||||||
bool ConfigController::parseArguments(GBAArguments* args, int argc, char* argv[], SubParser* subparser) {
|
bool ConfigController::parseArguments(GBAArguments* args, int argc, char* argv[], SubParser* subparser) {
|
||||||
if (::parseArguments(args, &m_config, argc, argv, subparser)) {
|
if (::parseArguments(args, &m_config, argc, argv, subparser)) {
|
||||||
|
GBAConfigFreeOpts(&m_opts);
|
||||||
GBAConfigMap(&m_config, &m_opts);
|
GBAConfigMap(&m_config, &m_opts);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -262,6 +263,9 @@ void ConfigController::setMRU(const QList<QString>& mru) {
|
||||||
void ConfigController::write() {
|
void ConfigController::write() {
|
||||||
GBAConfigSave(&m_config);
|
GBAConfigSave(&m_config);
|
||||||
m_settings->sync();
|
m_settings->sync();
|
||||||
|
|
||||||
|
GBAConfigFreeOpts(&m_opts);
|
||||||
|
GBAConfigMap(&m_config, &m_opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigController::makePortable() {
|
void ConfigController::makePortable() {
|
||||||
|
|
|
@ -14,30 +14,34 @@ extern "C" {
|
||||||
|
|
||||||
using namespace QGBA;
|
using namespace QGBA;
|
||||||
|
|
||||||
#ifdef BUILD_GL
|
#if defined(BUILD_GL) || defined(BUILD_GLES2) || defined(USE_EPOXY)
|
||||||
Display::Driver Display::s_driver = Display::Driver::OPENGL;
|
Display::Driver Display::s_driver = Display::Driver::OPENGL;
|
||||||
#else
|
#else
|
||||||
Display::Driver Display::s_driver = Display::Driver::QT;
|
Display::Driver Display::s_driver = Display::Driver::QT;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Display* Display::create(QWidget* parent) {
|
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));
|
QGLFormat format(QGLFormat(QGL::Rgba | QGL::DoubleBuffer));
|
||||||
format.setSwapInterval(1);
|
format.setSwapInterval(1);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
switch (s_driver) {
|
switch (s_driver) {
|
||||||
#ifdef BUILD_GL
|
#if defined(BUILD_GL) || defined(BUILD_GLES2) || defined(USE_EPOXY)
|
||||||
case Driver::OPENGL:
|
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
|
#endif
|
||||||
|
|
||||||
case Driver::QT:
|
case Driver::QT:
|
||||||
return new DisplayQt(parent);
|
return new DisplayQt(parent);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
#ifdef BUILD_GL
|
#if defined(BUILD_GL) || defined(BUILD_GLES2) || defined(USE_EPOXY)
|
||||||
return new DisplayGL(format, parent);
|
return new DisplayGL(format, false, parent);
|
||||||
#else
|
#else
|
||||||
return new DisplayQt(parent);
|
return new DisplayQt(parent);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -22,8 +22,11 @@ Q_OBJECT
|
||||||
public:
|
public:
|
||||||
enum class Driver {
|
enum class Driver {
|
||||||
QT = 0,
|
QT = 0,
|
||||||
#ifdef BUILD_GL
|
#if defined(BUILD_GL) || defined(BUILD_GLES2) || defined(USE_EPOXY)
|
||||||
OPENGL = 1,
|
OPENGL = 1,
|
||||||
|
#endif
|
||||||
|
#ifdef BUILD_GL
|
||||||
|
OPENGL1 = 2,
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -24,14 +24,22 @@ extern "C" {
|
||||||
|
|
||||||
using namespace QGBA;
|
using namespace QGBA;
|
||||||
|
|
||||||
DisplayGL::DisplayGL(const QGLFormat& format, QWidget* parent)
|
DisplayGL::DisplayGL(const QGLFormat& format, bool force1, QWidget* parent)
|
||||||
: Display(parent)
|
: Display(parent)
|
||||||
, m_isDrawing(false)
|
, m_isDrawing(false)
|
||||||
, m_gl(new EmptyGLWidget(format, this))
|
, m_gl(new EmptyGLWidget(format, this))
|
||||||
, m_drawThread(nullptr)
|
, m_drawThread(nullptr)
|
||||||
, m_context(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->setMouseTracking(true);
|
||||||
m_gl->setAttribute(Qt::WA_TransparentForMouseEvents); // This doesn't seem to work?
|
m_gl->setAttribute(Qt::WA_TransparentForMouseEvents); // This doesn't seem to work?
|
||||||
}
|
}
|
||||||
|
@ -188,7 +196,7 @@ PainterGL::PainterGL(QGLWidget* parent, QGLFormat::OpenGLVersionFlags glVersion)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !defined(_WIN32) || defined(USE_EPOXY)
|
#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;
|
gl2Backend = new GBAGLES2Context;
|
||||||
GBAGLES2ContextCreate(gl2Backend);
|
GBAGLES2ContextCreate(gl2Backend);
|
||||||
m_backend = &gl2Backend->d;
|
m_backend = &gl2Backend->d;
|
||||||
|
@ -366,7 +374,7 @@ void PainterGL::enqueue(const uint32_t* backing) {
|
||||||
} else {
|
} else {
|
||||||
buffer = m_free.takeLast();
|
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_queue.enqueue(buffer);
|
||||||
m_mutex.unlock();
|
m_mutex.unlock();
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,9 @@
|
||||||
|
|
||||||
#ifdef USE_EPOXY
|
#ifdef USE_EPOXY
|
||||||
#include <epoxy/gl.h>
|
#include <epoxy/gl.h>
|
||||||
|
#ifndef GLdouble
|
||||||
|
#define GLdouble GLdouble
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <QGLWidget>
|
#include <QGLWidget>
|
||||||
|
@ -42,7 +45,7 @@ class DisplayGL : public Display {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
DisplayGL(const QGLFormat& format, QWidget* parent = nullptr);
|
DisplayGL(const QGLFormat& format, bool force1 = false, QWidget* parent = nullptr);
|
||||||
~DisplayGL();
|
~DisplayGL();
|
||||||
|
|
||||||
bool isDrawing() const override { return m_isDrawing; }
|
bool isDrawing() const override { return m_isDrawing; }
|
||||||
|
|
|
@ -41,12 +41,12 @@ void DisplayQt::framePosted(const uint32_t* buffer) {
|
||||||
}
|
}
|
||||||
#ifdef COLOR_16_BIT
|
#ifdef COLOR_16_BIT
|
||||||
#ifdef COLOR_5_6_5
|
#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
|
#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
|
#endif
|
||||||
#else
|
#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
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -124,28 +124,27 @@ GBAApp* GBAApp::app() {
|
||||||
return g_app;
|
return g_app;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBAApp::interruptAll() {
|
void GBAApp::pauseAll(QList<int>* paused) {
|
||||||
for (int i = 0; i < MAX_GBAS; ++i) {
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
m_windows[i]->controller()->threadInterrupt();
|
m_windows[i]->controller()->setPaused(true);
|
||||||
|
paused->append(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBAApp::continueAll() {
|
void GBAApp::continueAll(const QList<int>* paused) {
|
||||||
for (int i = 0; i < MAX_GBAS; ++i) {
|
for (int i : *paused) {
|
||||||
if (!m_windows[i] || !m_windows[i]->controller()->isLoaded()) {
|
m_windows[i]->controller()->setPaused(false);
|
||||||
continue;
|
|
||||||
}
|
|
||||||
m_windows[i]->controller()->threadContinue();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString GBAApp::getOpenFileName(QWidget* owner, const QString& title, const QString& filter) {
|
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);
|
QString filename = QFileDialog::getOpenFileName(owner, title, m_configController.getQtOption("lastDirectory").toString(), filter);
|
||||||
continueAll();
|
continueAll(&paused);
|
||||||
if (!filename.isEmpty()) {
|
if (!filename.isEmpty()) {
|
||||||
m_configController.setQtOption("lastDirectory", QFileInfo(filename).dir().path());
|
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) {
|
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);
|
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()) {
|
if (!filename.isEmpty()) {
|
||||||
m_configController.setQtOption("lastDirectory", QFileInfo(filename).dir().path());
|
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() {
|
int GBAApp::FileDialog::exec() {
|
||||||
m_app->interruptAll();
|
QList<int> paused;
|
||||||
|
m_app->pauseAll(&paused);
|
||||||
bool didAccept = QFileDialog::exec() == QDialog::Accepted;
|
bool didAccept = QFileDialog::exec() == QDialog::Accepted;
|
||||||
QStringList filenames = selectedFiles();
|
QStringList filenames = selectedFiles();
|
||||||
if (!filenames.isEmpty()) {
|
if (!filenames.isEmpty()) {
|
||||||
m_app->m_configController.setQtOption("lastDirectory", QFileInfo(filenames[0]).dir().path());
|
m_app->m_configController.setQtOption("lastDirectory", QFileInfo(filenames[0]).dir().path());
|
||||||
}
|
}
|
||||||
m_app->continueAll();
|
m_app->continueAll(&paused);
|
||||||
return didAccept;
|
return didAccept;
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,7 @@ public:
|
||||||
|
|
||||||
QString getOpenFileName(QWidget* owner, const QString& title, const QString& filter = QString());
|
QString getOpenFileName(QWidget* owner, const QString& title, const QString& filter = QString());
|
||||||
QString getSaveFileName(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* getOpenFileDialog(QWidget* owner, const QString& title, const QString& filter = QString());
|
||||||
QFileDialog* getSaveFileDialog(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; }
|
const NoIntroDB* gameDB() const { return m_db; }
|
||||||
bool reloadGameDB();
|
bool reloadGameDB();
|
||||||
|
|
||||||
public slots:
|
|
||||||
void interruptAll();
|
|
||||||
void continueAll();
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool event(QEvent*);
|
bool event(QEvent*);
|
||||||
|
|
||||||
|
@ -63,6 +60,9 @@ private:
|
||||||
|
|
||||||
Window* newWindowInternal();
|
Window* newWindowInternal();
|
||||||
|
|
||||||
|
void pauseAll(QList<int>* paused);
|
||||||
|
void continueAll(const QList<int>* paused);
|
||||||
|
|
||||||
ConfigController m_configController;
|
ConfigController m_configController;
|
||||||
Window* m_windows[MAX_GBAS];
|
Window* m_windows[MAX_GBAS];
|
||||||
MultiplayerController m_multiplayer;
|
MultiplayerController m_multiplayer;
|
||||||
|
|
|
@ -15,6 +15,12 @@
|
||||||
#include "InputController.h"
|
#include "InputController.h"
|
||||||
#include "KeyEditor.h"
|
#include "KeyEditor.h"
|
||||||
|
|
||||||
|
#ifdef BUILD_SDL
|
||||||
|
extern "C" {
|
||||||
|
#include "platform/sdl/sdl-events.h"
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
using namespace QGBA;
|
using namespace QGBA;
|
||||||
|
|
||||||
const qreal GBAKeyEditor::DPAD_CENTER_X = 0.247;
|
const qreal GBAKeyEditor::DPAD_CENTER_X = 0.247;
|
||||||
|
@ -51,22 +57,19 @@ GBAKeyEditor::GBAKeyEditor(InputController* controller, int type, const QString&
|
||||||
|
|
||||||
#ifdef BUILD_SDL
|
#ifdef BUILD_SDL
|
||||||
if (type == SDL_BINDING_BUTTON) {
|
if (type == SDL_BINDING_BUTTON) {
|
||||||
|
controller->updateJoysticks();
|
||||||
controller->recalibrateAxes();
|
controller->recalibrateAxes();
|
||||||
lookupAxes(map);
|
lookupAxes(map);
|
||||||
|
|
||||||
m_profileSelect = new QComboBox(this);
|
m_profileSelect = new QComboBox(this);
|
||||||
m_profileSelect->addItems(controller->connectedGamepads(type));
|
m_profileSelect->addItems(controller->connectedGamepads(type));
|
||||||
int activeGamepad = controller->gamepad(type);
|
int activeGamepad = controller->gamepad(type);
|
||||||
|
selectGamepad(activeGamepad);
|
||||||
if (activeGamepad > 0) {
|
if (activeGamepad > 0) {
|
||||||
m_profileSelect->setCurrentIndex(activeGamepad);
|
m_profileSelect->setCurrentIndex(activeGamepad);
|
||||||
}
|
}
|
||||||
|
|
||||||
connect(m_profileSelect, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), [this] (int i) {
|
connect(m_profileSelect, SIGNAL(currentIndexChanged(int)), this, SLOT(selectGamepad(int)));
|
||||||
m_controller->setGamepad(m_type, i);
|
|
||||||
m_profile = m_profileSelect->currentText();
|
|
||||||
m_controller->loadProfile(m_type, m_profile);
|
|
||||||
refresh();
|
|
||||||
});
|
|
||||||
|
|
||||||
m_clear = new QWidget(this);
|
m_clear = new QWidget(this);
|
||||||
QHBoxLayout* layout = new QHBoxLayout;
|
QHBoxLayout* layout = new QHBoxLayout;
|
||||||
|
@ -105,9 +108,6 @@ GBAKeyEditor::GBAKeyEditor(InputController* controller, int type, const QString&
|
||||||
connect(setAll, SIGNAL(pressed()), this, SLOT(setAll()));
|
connect(setAll, SIGNAL(pressed()), this, SLOT(setAll()));
|
||||||
layout->addWidget(setAll);
|
layout->addWidget(setAll);
|
||||||
|
|
||||||
QPushButton* save = new QPushButton(tr("Save"));
|
|
||||||
connect(save, SIGNAL(pressed()), this, SLOT(save()));
|
|
||||||
layout->addWidget(save);
|
|
||||||
layout->setSpacing(6);
|
layout->setSpacing(6);
|
||||||
|
|
||||||
m_keyOrder = QList<KeyEditor*>{
|
m_keyOrder = QList<KeyEditor*>{
|
||||||
|
@ -136,6 +136,10 @@ GBAKeyEditor::GBAKeyEditor(InputController* controller, int type, const QString&
|
||||||
setAll->setFocus();
|
setAll->setFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GBAKeyEditor::~GBAKeyEditor() {
|
||||||
|
m_controller->releaseFocus(this);
|
||||||
|
}
|
||||||
|
|
||||||
void GBAKeyEditor::setAll() {
|
void GBAKeyEditor::setAll() {
|
||||||
m_currentKey = m_keyOrder.begin();
|
m_currentKey = m_keyOrder.begin();
|
||||||
(*m_currentKey)->setFocus();
|
(*m_currentKey)->setFocus();
|
||||||
|
@ -174,9 +178,10 @@ void GBAKeyEditor::closeEvent(QCloseEvent*) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GBAKeyEditor::event(QEvent* event) {
|
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);
|
m_controller->stealFocus(this);
|
||||||
} else if (event->type() == QEvent::WindowDeactivate) {
|
} else if (type == QEvent::WindowDeactivate || type == QEvent::Hide) {
|
||||||
m_controller->releaseFocus(this);
|
m_controller->releaseFocus(this);
|
||||||
}
|
}
|
||||||
return QWidget::event(event);
|
return QWidget::event(event);
|
||||||
|
@ -280,7 +285,7 @@ void GBAKeyEditor::lookupAxes(const GBAInputMap* map) {
|
||||||
|
|
||||||
void GBAKeyEditor::bindKey(const KeyEditor* keyEditor, GBAKey key) {
|
void GBAKeyEditor::bindKey(const KeyEditor* keyEditor, GBAKey key) {
|
||||||
#ifdef BUILD_SDL
|
#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);
|
m_controller->bindAxis(m_type, keyEditor->axis(), keyEditor->direction(), key);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -309,6 +314,13 @@ void GBAKeyEditor::setAxisValue(int axis, int32_t value) {
|
||||||
KeyEditor* focused = *m_currentKey;
|
KeyEditor* focused = *m_currentKey;
|
||||||
focused->setValueAxis(axis, value);
|
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
|
#endif
|
||||||
|
|
||||||
KeyEditor* GBAKeyEditor::keyById(GBAKey key) {
|
KeyEditor* GBAKeyEditor::keyById(GBAKey key) {
|
||||||
|
|
|
@ -28,6 +28,7 @@ Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
GBAKeyEditor(InputController* controller, int type, const QString& profile = QString(), QWidget* parent = nullptr);
|
GBAKeyEditor(InputController* controller, int type, const QString& profile = QString(), QWidget* parent = nullptr);
|
||||||
|
virtual ~GBAKeyEditor();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void setAll();
|
void setAll();
|
||||||
|
@ -45,6 +46,7 @@ private slots:
|
||||||
void refresh();
|
void refresh();
|
||||||
#ifdef BUILD_SDL
|
#ifdef BUILD_SDL
|
||||||
void setAxisValue(int axis, int32_t value);
|
void setAxisValue(int axis, int32_t value);
|
||||||
|
void selectGamepad(int index);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -75,3 +75,12 @@ void GDBController::listen() {
|
||||||
}
|
}
|
||||||
m_gameController->threadContinue();
|
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 attach();
|
||||||
void detach();
|
void detach();
|
||||||
void listen();
|
void listen();
|
||||||
|
void breakInto();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void listening();
|
void listening();
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include <QLineEdit>
|
#include <QLineEdit>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
|
#include <QHBoxLayout>
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
#include "GDBController.h"
|
#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&)));
|
connect(m_bindAddressEdit, SIGNAL(textChanged(const QString&)), this, SLOT(bindAddressChanged(const QString&)));
|
||||||
settingsGrid->addWidget(m_bindAddressEdit, 1, 1, Qt::AlignLeft);
|
settingsGrid->addWidget(m_bindAddressEdit, 1, 1, Qt::AlignLeft);
|
||||||
|
|
||||||
|
QHBoxLayout* buttons = new QHBoxLayout;
|
||||||
|
|
||||||
m_startStopButton = new QPushButton;
|
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(listening()), this, SLOT(started()));
|
||||||
connect(m_gdbController, SIGNAL(listenFailed()), this, SLOT(failed()));
|
connect(m_gdbController, SIGNAL(listenFailed()), this, SLOT(failed()));
|
||||||
|
connect(m_breakButton, SIGNAL(clicked()), controller, SLOT(breakInto()));
|
||||||
|
|
||||||
if (m_gdbController->isAttached()) {
|
if (m_gdbController->isAttached()) {
|
||||||
started();
|
started();
|
||||||
|
@ -91,6 +102,7 @@ void GDBWindow::started() {
|
||||||
m_portEdit->setEnabled(false);
|
m_portEdit->setEnabled(false);
|
||||||
m_bindAddressEdit->setEnabled(false);
|
m_bindAddressEdit->setEnabled(false);
|
||||||
m_startStopButton->setText(tr("Stop"));
|
m_startStopButton->setText(tr("Stop"));
|
||||||
|
m_breakButton->setEnabled(true);
|
||||||
disconnect(m_startStopButton, SIGNAL(clicked()), m_gdbController, SLOT(listen()));
|
disconnect(m_startStopButton, SIGNAL(clicked()), m_gdbController, SLOT(listen()));
|
||||||
connect(m_startStopButton, SIGNAL(clicked()), m_gdbController, SLOT(detach()));
|
connect(m_startStopButton, SIGNAL(clicked()), m_gdbController, SLOT(detach()));
|
||||||
connect(m_startStopButton, SIGNAL(clicked()), this, SLOT(stopped()));
|
connect(m_startStopButton, SIGNAL(clicked()), this, SLOT(stopped()));
|
||||||
|
@ -100,6 +112,7 @@ void GDBWindow::stopped() {
|
||||||
m_portEdit->setEnabled(true);
|
m_portEdit->setEnabled(true);
|
||||||
m_bindAddressEdit->setEnabled(true);
|
m_bindAddressEdit->setEnabled(true);
|
||||||
m_startStopButton->setText(tr("Start"));
|
m_startStopButton->setText(tr("Start"));
|
||||||
|
m_breakButton->setEnabled(false);
|
||||||
disconnect(m_startStopButton, SIGNAL(clicked()), m_gdbController, SLOT(detach()));
|
disconnect(m_startStopButton, SIGNAL(clicked()), m_gdbController, SLOT(detach()));
|
||||||
disconnect(m_startStopButton, SIGNAL(clicked()), this, SLOT(stopped()));
|
disconnect(m_startStopButton, SIGNAL(clicked()), this, SLOT(stopped()));
|
||||||
connect(m_startStopButton, SIGNAL(clicked()), m_gdbController, SLOT(listen()));
|
connect(m_startStopButton, SIGNAL(clicked()), m_gdbController, SLOT(listen()));
|
||||||
|
|
|
@ -36,6 +36,7 @@ private:
|
||||||
QLineEdit* m_portEdit;
|
QLineEdit* m_portEdit;
|
||||||
QLineEdit* m_bindAddressEdit;
|
QLineEdit* m_bindAddressEdit;
|
||||||
QPushButton* m_startStopButton;
|
QPushButton* m_startStopButton;
|
||||||
|
QPushButton* m_breakButton;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include "gba/audio.h"
|
#include "gba/audio.h"
|
||||||
#include "gba/context/config.h"
|
#include "gba/context/config.h"
|
||||||
|
#include "gba/context/directories.h"
|
||||||
#include "gba/gba.h"
|
#include "gba/gba.h"
|
||||||
#include "gba/serialize.h"
|
#include "gba/serialize.h"
|
||||||
#include "gba/sharkport.h"
|
#include "gba/sharkport.h"
|
||||||
|
@ -31,8 +32,8 @@ using namespace std;
|
||||||
|
|
||||||
GameController::GameController(QObject* parent)
|
GameController::GameController(QObject* parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
, m_drawContext(new uint32_t[256 * VIDEO_HORIZONTAL_PIXELS])
|
, m_drawContext(new uint32_t[VIDEO_VERTICAL_PIXELS * VIDEO_HORIZONTAL_PIXELS])
|
||||||
, m_frontBuffer(new uint32_t[256 * 256])
|
, m_frontBuffer(new uint32_t[VIDEO_VERTICAL_PIXELS * VIDEO_HORIZONTAL_PIXELS])
|
||||||
, m_threadContext()
|
, m_threadContext()
|
||||||
, m_activeKeys(0)
|
, m_activeKeys(0)
|
||||||
, m_inactiveKeys(0)
|
, m_inactiveKeys(0)
|
||||||
|
@ -57,11 +58,13 @@ GameController::GameController(QObject* parent)
|
||||||
, m_stateSlot(1)
|
, m_stateSlot(1)
|
||||||
, m_backupLoadState(nullptr)
|
, m_backupLoadState(nullptr)
|
||||||
, m_backupSaveState(nullptr)
|
, m_backupSaveState(nullptr)
|
||||||
|
, m_saveStateFlags(SAVESTATE_SCREENSHOT | SAVESTATE_SAVEDATA | SAVESTATE_CHEATS)
|
||||||
|
, m_loadStateFlags(SAVESTATE_SCREENSHOT)
|
||||||
{
|
{
|
||||||
m_renderer = new GBAVideoSoftwareRenderer;
|
m_renderer = new GBAVideoSoftwareRenderer;
|
||||||
GBAVideoSoftwareRendererCreate(m_renderer);
|
GBAVideoSoftwareRendererCreate(m_renderer);
|
||||||
m_renderer->outputBuffer = (color_t*) m_drawContext;
|
m_renderer->outputBuffer = (color_t*) m_drawContext;
|
||||||
m_renderer->outputBufferStride = 256;
|
m_renderer->outputBufferStride = VIDEO_HORIZONTAL_PIXELS;
|
||||||
|
|
||||||
GBACheatDeviceCreate(&m_cheatDevice);
|
GBACheatDeviceCreate(&m_cheatDevice);
|
||||||
GBASIODolphinCreate(&m_dolphin);
|
GBASIODolphinCreate(&m_dolphin);
|
||||||
|
@ -75,6 +78,7 @@ GameController::GameController(QObject* parent)
|
||||||
m_threadContext.rewindBufferCapacity = 0;
|
m_threadContext.rewindBufferCapacity = 0;
|
||||||
m_threadContext.cheats = &m_cheatDevice;
|
m_threadContext.cheats = &m_cheatDevice;
|
||||||
m_threadContext.logLevel = GBA_LOG_ALL;
|
m_threadContext.logLevel = GBA_LOG_ALL;
|
||||||
|
GBADirectorySetInit(&m_threadContext.dirs);
|
||||||
|
|
||||||
m_lux.p = this;
|
m_lux.p = this;
|
||||||
m_lux.sample = [](GBALuminanceSource* context) {
|
m_lux.sample = [](GBALuminanceSource* context) {
|
||||||
|
@ -111,11 +115,8 @@ GameController::GameController(QObject* parent)
|
||||||
context->gba->video.renderer->disableOBJ = !controller->m_videoLayers[4];
|
context->gba->video.renderer->disableOBJ = !controller->m_videoLayers[4];
|
||||||
controller->m_fpsTarget = context->fpsTarget;
|
controller->m_fpsTarget = context->fpsTarget;
|
||||||
|
|
||||||
if (GBALoadState(context, context->dirs.state, 0, SAVESTATE_SCREENSHOT)) {
|
if (context->dirs.state && GBALoadState(context, context->dirs.state, 0, controller->m_loadStateFlags)) {
|
||||||
VFile* vf = GBAGetState(context->gba, context->dirs.state, 0, true);
|
GBADeleteState(context->gba, context->dirs.state, 0);
|
||||||
if (vf) {
|
|
||||||
vf->truncate(vf, 0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
QMetaObject::invokeMethod(controller, "gameStarted", Q_ARG(GBAThread*, context));
|
QMetaObject::invokeMethod(controller, "gameStarted", Q_ARG(GBAThread*, context));
|
||||||
};
|
};
|
||||||
|
@ -127,7 +128,7 @@ GameController::GameController(QObject* parent)
|
||||||
|
|
||||||
m_threadContext.frameCallback = [](GBAThread* context) {
|
m_threadContext.frameCallback = [](GBAThread* context) {
|
||||||
GameController* controller = static_cast<GameController*>(context->userData);
|
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));
|
QMetaObject::invokeMethod(controller, "frameAvailable", Q_ARG(const uint32_t*, controller->m_frontBuffer));
|
||||||
if (controller->m_pauseAfterFrame.testAndSetAcquire(true, false)) {
|
if (controller->m_pauseAfterFrame.testAndSetAcquire(true, false)) {
|
||||||
GBAThreadPauseFromThread(context);
|
GBAThreadPauseFromThread(context);
|
||||||
|
@ -140,7 +141,7 @@ GameController::GameController(QObject* parent)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
GameController* controller = static_cast<GameController*>(context->userData);
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
QMetaObject::invokeMethod(controller, "closeGame");
|
QMetaObject::invokeMethod(controller, "closeGame");
|
||||||
|
@ -218,6 +219,7 @@ GameController::~GameController() {
|
||||||
detachDolphin();
|
detachDolphin();
|
||||||
closeGame();
|
closeGame();
|
||||||
GBACheatDeviceDestroy(&m_cheatDevice);
|
GBACheatDeviceDestroy(&m_cheatDevice);
|
||||||
|
GBADirectorySetDeinit(&m_threadContext.dirs);
|
||||||
delete m_renderer;
|
delete m_renderer;
|
||||||
delete[] m_drawContext;
|
delete[] m_drawContext;
|
||||||
delete[] m_frontBuffer;
|
delete[] m_frontBuffer;
|
||||||
|
@ -279,6 +281,7 @@ void GameController::setOptions(const GBAOptions* opts) {
|
||||||
setMute(opts->mute);
|
setMute(opts->mute);
|
||||||
|
|
||||||
threadInterrupt();
|
threadInterrupt();
|
||||||
|
GBADirectorySetMapOptions(&m_threadContext.dirs, opts);
|
||||||
m_threadContext.idleOptimization = opts->idleOptimization;
|
m_threadContext.idleOptimization = opts->idleOptimization;
|
||||||
threadContinue();
|
threadContinue();
|
||||||
}
|
}
|
||||||
|
@ -301,26 +304,22 @@ void GameController::setDebugger(ARMDebugger* debugger) {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void GameController::loadGame(const QString& path, bool dirmode) {
|
void GameController::loadGame(const QString& path) {
|
||||||
closeGame();
|
closeGame();
|
||||||
if (!dirmode) {
|
QFile file(path);
|
||||||
QFile file(path);
|
if (!file.open(QIODevice::ReadOnly)) {
|
||||||
if (!file.open(QIODevice::ReadOnly)) {
|
postLog(GBA_LOG_ERROR, tr("Failed to open game file: %1").arg(path));
|
||||||
postLog(GBA_LOG_ERROR, tr("Failed to open game file: %1").arg(path));
|
return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
file.close();
|
|
||||||
}
|
}
|
||||||
|
file.close();
|
||||||
|
|
||||||
m_fname = path;
|
m_fname = path;
|
||||||
m_dirmode = dirmode;
|
|
||||||
openGame();
|
openGame();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameController::bootBIOS() {
|
void GameController::bootBIOS() {
|
||||||
closeGame();
|
closeGame();
|
||||||
m_fname = QString();
|
m_fname = QString();
|
||||||
m_dirmode = false;
|
|
||||||
openGame(true);
|
openGame(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -360,7 +359,7 @@ void GameController::openGame(bool biosOnly) {
|
||||||
}
|
}
|
||||||
|
|
||||||
m_inputController->recalibrateAxes();
|
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)) {
|
if (!GBAThreadStart(&m_threadContext)) {
|
||||||
m_gameOpen = false;
|
m_gameOpen = false;
|
||||||
|
@ -720,6 +719,10 @@ void GameController::setUseBIOS(bool use) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameController::loadState(int slot) {
|
void GameController::loadState(int slot) {
|
||||||
|
if (!m_threadContext.fname) {
|
||||||
|
// We're in the BIOS
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (slot > 0 && slot != m_stateSlot) {
|
if (slot > 0 && slot != m_stateSlot) {
|
||||||
m_stateSlot = slot;
|
m_stateSlot = slot;
|
||||||
m_backupSaveState.clear();
|
m_backupSaveState.clear();
|
||||||
|
@ -730,7 +733,7 @@ void GameController::loadState(int slot) {
|
||||||
controller->m_backupLoadState = new GBASerializedState;
|
controller->m_backupLoadState = new GBASerializedState;
|
||||||
}
|
}
|
||||||
GBASerialize(context->gba, controller->m_backupLoadState);
|
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->frameAvailable(controller->m_drawContext);
|
||||||
controller->stateLoaded(context);
|
controller->stateLoaded(context);
|
||||||
}
|
}
|
||||||
|
@ -738,6 +741,10 @@ void GameController::loadState(int slot) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameController::saveState(int slot) {
|
void GameController::saveState(int slot) {
|
||||||
|
if (!m_threadContext.fname) {
|
||||||
|
// We're in the BIOS
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (slot > 0) {
|
if (slot > 0) {
|
||||||
m_stateSlot = slot;
|
m_stateSlot = slot;
|
||||||
}
|
}
|
||||||
|
@ -749,7 +756,7 @@ void GameController::saveState(int slot) {
|
||||||
vf->read(vf, controller->m_backupSaveState.data(), controller->m_backupSaveState.size());
|
vf->read(vf, controller->m_backupSaveState.data(), controller->m_backupSaveState.size());
|
||||||
vf->close(vf);
|
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) {
|
void GameController::setLuminanceValue(uint8_t value) {
|
||||||
m_luxValue = value;
|
m_luxValue = value;
|
||||||
value = std::max<int>(value - 0x16, 0);
|
value = std::max<int>(value - 0x16, 0);
|
||||||
|
|
|
@ -101,7 +101,7 @@ signals:
|
||||||
void postLog(int level, const QString& log);
|
void postLog(int level, const QString& log);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void loadGame(const QString& path, bool dirmode = false);
|
void loadGame(const QString& path);
|
||||||
void loadBIOS(const QString& path);
|
void loadBIOS(const QString& path);
|
||||||
void yankPak();
|
void yankPak();
|
||||||
void replaceGame(const QString& path);
|
void replaceGame(const QString& path);
|
||||||
|
@ -142,6 +142,8 @@ public slots:
|
||||||
void setAVStream(GBAAVStream*);
|
void setAVStream(GBAAVStream*);
|
||||||
void clearAVStream();
|
void clearAVStream();
|
||||||
void reloadAudioDriver();
|
void reloadAudioDriver();
|
||||||
|
void setSaveStateExtdata(int flags);
|
||||||
|
void setLoadStateExtdata(int flags);
|
||||||
|
|
||||||
#ifdef USE_PNG
|
#ifdef USE_PNG
|
||||||
void screenshot();
|
void screenshot();
|
||||||
|
@ -186,7 +188,6 @@ private:
|
||||||
GBASIODolphin m_dolphin;
|
GBASIODolphin m_dolphin;
|
||||||
|
|
||||||
bool m_gameOpen;
|
bool m_gameOpen;
|
||||||
bool m_dirmode;
|
|
||||||
|
|
||||||
QString m_fname;
|
QString m_fname;
|
||||||
QString m_bios;
|
QString m_bios;
|
||||||
|
@ -216,6 +217,8 @@ private:
|
||||||
int m_stateSlot;
|
int m_stateSlot;
|
||||||
GBASerializedState* m_backupLoadState;
|
GBASerializedState* m_backupLoadState;
|
||||||
QByteArray m_backupSaveState;
|
QByteArray m_backupSaveState;
|
||||||
|
int m_saveStateFlags;
|
||||||
|
int m_loadStateFlags;
|
||||||
|
|
||||||
InputController* m_inputController;
|
InputController* m_inputController;
|
||||||
MultiplayerController* m_multiplayer;
|
MultiplayerController* m_multiplayer;
|
||||||
|
|
|
@ -46,6 +46,7 @@ InputController::InputController(int playerId, QWidget* topLevel, QObject* paren
|
||||||
++s_sdlInited;
|
++s_sdlInited;
|
||||||
m_sdlPlayer.bindings = &m_inputMap;
|
m_sdlPlayer.bindings = &m_inputMap;
|
||||||
GBASDLInitBindings(&m_inputMap);
|
GBASDLInitBindings(&m_inputMap);
|
||||||
|
updateJoysticks();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
m_gamepadTimer = new QTimer(this);
|
m_gamepadTimer = new QTimer(this);
|
||||||
|
@ -146,9 +147,9 @@ const char* InputController::profileForType(uint32_t type) {
|
||||||
#ifdef BUILD_SDL
|
#ifdef BUILD_SDL
|
||||||
if (type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
|
if (type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
|
||||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||||
return SDL_JoystickName(m_sdlPlayer.joystick);
|
return SDL_JoystickName(m_sdlPlayer.joystick->joystick);
|
||||||
#else
|
#else
|
||||||
return SDL_JoystickName(SDL_JoystickIndex(m_sdlPlayer.joystick));
|
return SDL_JoystickName(SDL_JoystickIndex(m_sdlPlayer.joystick->joystick));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -161,12 +162,12 @@ QStringList InputController::connectedGamepads(uint32_t type) const {
|
||||||
#ifdef BUILD_SDL
|
#ifdef BUILD_SDL
|
||||||
if (type == SDL_BINDING_BUTTON) {
|
if (type == SDL_BINDING_BUTTON) {
|
||||||
QStringList pads;
|
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;
|
const char* name;
|
||||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
#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
|
#else
|
||||||
name = SDL_JoystickName(SDL_JoystickIndex(s_sdlEvents.joysticks[i]));
|
name = SDL_JoystickName(SDL_JoystickIndex(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, i)->joystick));
|
||||||
#endif
|
#endif
|
||||||
if (name) {
|
if (name) {
|
||||||
pads.append(QString(name));
|
pads.append(QString(name));
|
||||||
|
@ -184,7 +185,7 @@ QStringList InputController::connectedGamepads(uint32_t type) const {
|
||||||
int InputController::gamepad(uint32_t type) const {
|
int InputController::gamepad(uint32_t type) const {
|
||||||
#ifdef BUILD_SDL
|
#ifdef BUILD_SDL
|
||||||
if (type == SDL_BINDING_BUTTON) {
|
if (type == SDL_BINDING_BUTTON) {
|
||||||
return m_sdlPlayer.joystickIndex;
|
return m_sdlPlayer.joystick ? m_sdlPlayer.joystick->index : 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -282,11 +283,17 @@ void InputController::bindKey(uint32_t type, int key, GBAKey gbaKey) {
|
||||||
return GBAInputBindKey(&m_inputMap, type, key, gbaKey);
|
return GBAInputBindKey(&m_inputMap, type, key, gbaKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InputController::updateJoysticks() {
|
||||||
|
#ifdef BUILD_SDL
|
||||||
|
GBASDLUpdateJoysticks(&s_sdlEvents);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
int InputController::pollEvents() {
|
int InputController::pollEvents() {
|
||||||
int activeButtons = 0;
|
int activeButtons = 0;
|
||||||
#ifdef BUILD_SDL
|
#ifdef BUILD_SDL
|
||||||
if (m_playerAttached) {
|
if (m_playerAttached && m_sdlPlayer.joystick) {
|
||||||
SDL_Joystick* joystick = m_sdlPlayer.joystick;
|
SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
|
||||||
SDL_JoystickUpdate();
|
SDL_JoystickUpdate();
|
||||||
int numButtons = SDL_JoystickNumButtons(joystick);
|
int numButtons = SDL_JoystickNumButtons(joystick);
|
||||||
int i;
|
int i;
|
||||||
|
@ -336,8 +343,8 @@ int InputController::pollEvents() {
|
||||||
QSet<int> InputController::activeGamepadButtons(int type) {
|
QSet<int> InputController::activeGamepadButtons(int type) {
|
||||||
QSet<int> activeButtons;
|
QSet<int> activeButtons;
|
||||||
#ifdef BUILD_SDL
|
#ifdef BUILD_SDL
|
||||||
if (m_playerAttached && type == SDL_BINDING_BUTTON) {
|
if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
|
||||||
SDL_Joystick* joystick = m_sdlPlayer.joystick;
|
SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
|
||||||
SDL_JoystickUpdate();
|
SDL_JoystickUpdate();
|
||||||
int numButtons = SDL_JoystickNumButtons(joystick);
|
int numButtons = SDL_JoystickNumButtons(joystick);
|
||||||
int i;
|
int i;
|
||||||
|
@ -353,8 +360,8 @@ QSet<int> InputController::activeGamepadButtons(int type) {
|
||||||
|
|
||||||
void InputController::recalibrateAxes() {
|
void InputController::recalibrateAxes() {
|
||||||
#ifdef BUILD_SDL
|
#ifdef BUILD_SDL
|
||||||
if (m_playerAttached) {
|
if (m_playerAttached && m_sdlPlayer.joystick) {
|
||||||
SDL_Joystick* joystick = m_sdlPlayer.joystick;
|
SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
|
||||||
SDL_JoystickUpdate();
|
SDL_JoystickUpdate();
|
||||||
int numAxes = SDL_JoystickNumAxes(joystick);
|
int numAxes = SDL_JoystickNumAxes(joystick);
|
||||||
if (numAxes < 1) {
|
if (numAxes < 1) {
|
||||||
|
@ -372,8 +379,8 @@ void InputController::recalibrateAxes() {
|
||||||
QSet<QPair<int, GamepadAxisEvent::Direction>> InputController::activeGamepadAxes(int type) {
|
QSet<QPair<int, GamepadAxisEvent::Direction>> InputController::activeGamepadAxes(int type) {
|
||||||
QSet<QPair<int, GamepadAxisEvent::Direction>> activeAxes;
|
QSet<QPair<int, GamepadAxisEvent::Direction>> activeAxes;
|
||||||
#ifdef BUILD_SDL
|
#ifdef BUILD_SDL
|
||||||
if (m_playerAttached && type == SDL_BINDING_BUTTON) {
|
if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
|
||||||
SDL_Joystick* joystick = m_sdlPlayer.joystick;
|
SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
|
||||||
SDL_JoystickUpdate();
|
SDL_JoystickUpdate();
|
||||||
int numAxes = SDL_JoystickNumAxes(joystick);
|
int numAxes = SDL_JoystickNumAxes(joystick);
|
||||||
if (numAxes < 1) {
|
if (numAxes < 1) {
|
||||||
|
@ -399,14 +406,19 @@ void InputController::bindAxis(uint32_t type, int axis, GamepadAxisEvent::Direct
|
||||||
if (old) {
|
if (old) {
|
||||||
description = *old;
|
description = *old;
|
||||||
}
|
}
|
||||||
|
int deadzone = 0;
|
||||||
|
if (m_deadzones.size() > axis) {
|
||||||
|
deadzone = m_deadzones[axis];
|
||||||
|
}
|
||||||
switch (direction) {
|
switch (direction) {
|
||||||
case GamepadAxisEvent::NEGATIVE:
|
case GamepadAxisEvent::NEGATIVE:
|
||||||
description.lowDirection = key;
|
description.lowDirection = key;
|
||||||
description.deadLow = m_deadzones[axis] - AXIS_THRESHOLD;
|
|
||||||
|
description.deadLow = deadzone - AXIS_THRESHOLD;
|
||||||
break;
|
break;
|
||||||
case GamepadAxisEvent::POSITIVE:
|
case GamepadAxisEvent::POSITIVE:
|
||||||
description.highDirection = key;
|
description.highDirection = key;
|
||||||
description.deadHigh = m_deadzones[axis] + AXIS_THRESHOLD;
|
description.deadHigh = deadzone + AXIS_THRESHOLD;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -52,6 +52,7 @@ public:
|
||||||
|
|
||||||
const GBAInputMap* map() const { return &m_inputMap; }
|
const GBAInputMap* map() const { return &m_inputMap; }
|
||||||
|
|
||||||
|
void updateJoysticks();
|
||||||
int pollEvents();
|
int pollEvents();
|
||||||
|
|
||||||
static const int32_t AXIS_THRESHOLD = 0x3000;
|
static const int32_t AXIS_THRESHOLD = 0x3000;
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include "GamepadButtonEvent.h"
|
#include "GamepadButtonEvent.h"
|
||||||
#include "ShortcutController.h"
|
#include "ShortcutController.h"
|
||||||
|
|
||||||
|
#include <QFontMetrics>
|
||||||
#include <QKeyEvent>
|
#include <QKeyEvent>
|
||||||
|
|
||||||
using namespace QGBA;
|
using namespace QGBA;
|
||||||
|
@ -72,7 +73,8 @@ void KeyEditor::clearAxis() {
|
||||||
|
|
||||||
QSize KeyEditor::sizeHint() const {
|
QSize KeyEditor::sizeHint() const {
|
||||||
QSize hint = QLineEdit::sizeHint();
|
QSize hint = QLineEdit::sizeHint();
|
||||||
hint.setWidth(40);
|
QFontMetrics fm(font());
|
||||||
|
hint.setWidth(fm.height() * 3);
|
||||||
return hint;
|
return hint;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -70,15 +70,13 @@ LogView::LogView(LogController* log, QWidget* parent)
|
||||||
|
|
||||||
void LogView::postLog(int level, const QString& log) {
|
void LogView::postLog(int level, const QString& log) {
|
||||||
QString line = QString("%1:\t%2").arg(LogController::toString(level)).arg(log);
|
QString line = QString("%1:\t%2").arg(LogController::toString(level)).arg(log);
|
||||||
if (isVisible()) {
|
// TODO: Log to file
|
||||||
m_ui.view->appendPlainText(line);
|
m_pendingLines.enqueue(line);
|
||||||
} else {
|
|
||||||
m_pendingLines.enqueue(line);
|
|
||||||
}
|
|
||||||
++m_lines;
|
++m_lines;
|
||||||
if (m_lines > m_lineLimit) {
|
if (m_lines > m_lineLimit) {
|
||||||
clearLine();
|
clearLine();
|
||||||
}
|
}
|
||||||
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
void LogView::clear() {
|
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()) {
|
while (!m_pendingLines.isEmpty()) {
|
||||||
m_ui.view->appendPlainText(m_pendingLines.dequeue());
|
m_ui.view->appendPlainText(m_pendingLines.dequeue());
|
||||||
}
|
}
|
||||||
|
QWidget::paintEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LogView::clearLine() {
|
void LogView::clearLine() {
|
||||||
|
|
|
@ -38,7 +38,7 @@ private slots:
|
||||||
void setMaxLines(int);
|
void setMaxLines(int);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void showEvent(QShowEvent*) override;
|
virtual void paintEvent(QPaintEvent*) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static const int DEFAULT_LINE_LIMIT = 1000;
|
static const int DEFAULT_LINE_LIMIT = 1000;
|
||||||
|
|
|
@ -14,7 +14,9 @@ extern "C" {
|
||||||
|
|
||||||
using namespace QGBA;
|
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);
|
m_ui.setupUi(this);
|
||||||
|
|
||||||
if (!controller->isLoaded()) {
|
if (!controller->isLoaded()) {
|
||||||
|
|
|
@ -81,9 +81,10 @@ void SensorView::jiggerer(QAbstractButton* button, void (InputController::*sette
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SensorView::event(QEvent* event) {
|
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);
|
m_input->stealFocus(this);
|
||||||
} else if (event->type() == QEvent::WindowDeactivate) {
|
} else if (type == QEvent::WindowDeactivate || type == QEvent::Hide) {
|
||||||
m_input->releaseFocus(this);
|
m_input->releaseFocus(this);
|
||||||
}
|
}
|
||||||
return QWidget::event(event);
|
return QWidget::event(event);
|
||||||
|
|
|
@ -9,56 +9,89 @@
|
||||||
#include "ConfigController.h"
|
#include "ConfigController.h"
|
||||||
#include "Display.h"
|
#include "Display.h"
|
||||||
#include "GBAApp.h"
|
#include "GBAApp.h"
|
||||||
|
#include "GBAKeyEditor.h"
|
||||||
|
#include "InputController.h"
|
||||||
|
#include "ShortcutView.h"
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include "gba/serialize.h"
|
||||||
|
}
|
||||||
|
|
||||||
using namespace QGBA;
|
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)
|
: QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint)
|
||||||
, m_controller(controller)
|
, m_controller(controller)
|
||||||
{
|
{
|
||||||
m_ui.setupUi(this);
|
m_ui.setupUi(this);
|
||||||
|
|
||||||
loadSetting("bios", m_ui.bios);
|
reloadConfig();
|
||||||
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);
|
|
||||||
|
|
||||||
double fastForwardRatio = loadSetting("fastForwardRatio").toDouble();
|
if (m_ui.savegamePath->text().isEmpty()) {
|
||||||
if (fastForwardRatio <= 0) {
|
m_ui.savegameSameDir->setChecked(true);
|
||||||
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.fastForwardUnbounded, &QAbstractButton::toggled, [this](bool checked) {
|
connect(m_ui.savegameSameDir, &QAbstractButton::toggled, [this] (bool e) {
|
||||||
m_ui.fastForwardRatio->setEnabled(!checked);
|
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 (m_ui.savestatePath->text().isEmpty()) {
|
||||||
if (idleOptimization == "ignore") {
|
m_ui.savestateSameDir->setChecked(true);
|
||||||
m_ui.idleOptimization->setCurrentIndex(0);
|
|
||||||
} else if (idleOptimization == "remove") {
|
|
||||||
m_ui.idleOptimization->setCurrentIndex(1);
|
|
||||||
} else if (idleOptimization == "detect") {
|
|
||||||
m_ui.idleOptimization->setCurrentIndex(2);
|
|
||||||
}
|
}
|
||||||
|
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");
|
QVariant audioDriver = m_controller->getQtOption("audioDriver");
|
||||||
#ifdef BUILD_QT_MULTIMEDIA
|
#ifdef BUILD_QT_MULTIMEDIA
|
||||||
m_ui.audioDriver->addItem(tr("Qt Multimedia"), static_cast<int>(AudioProcessor::Driver::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
|
#endif
|
||||||
|
|
||||||
|
// TODO: Move to reloadConfig()
|
||||||
QVariant displayDriver = m_controller->getQtOption("displayDriver");
|
QVariant displayDriver = m_controller->getQtOption("displayDriver");
|
||||||
m_ui.displayDriver->addItem(tr("Software (Qt)"), static_cast<int>(Display::Driver::QT));
|
m_ui.displayDriver->addItem(tr("Software (Qt)"), static_cast<int>(Display::Driver::QT));
|
||||||
if (!displayDriver.isNull() && displayDriver.toInt() == 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);
|
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));
|
m_ui.displayDriver->addItem(tr("OpenGL"), static_cast<int>(Display::Driver::OPENGL));
|
||||||
if (displayDriver.isNull() || displayDriver.toInt() == 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);
|
m_ui.displayDriver->setCurrentIndex(m_ui.displayDriver->count() - 1);
|
||||||
}
|
}
|
||||||
#endif
|
#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.biosBrowse, SIGNAL(clicked()), this, SLOT(selectBios()));
|
||||||
connect(m_ui.buttonBox, SIGNAL(accepted()), this, SLOT(updateConfig()));
|
connect(m_ui.buttonBox, SIGNAL(accepted()), this, SLOT(updateConfig()));
|
||||||
connect(m_ui.buttonBox, &QDialogButtonBox::clicked, [this](QAbstractButton* button) {
|
connect(m_ui.buttonBox, &QDialogButtonBox::clicked, [this](QAbstractButton* button) {
|
||||||
|
@ -94,6 +135,26 @@ SettingsView::SettingsView(ConfigController* controller, QWidget* parent)
|
||||||
updateConfig();
|
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() {
|
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() {
|
void SettingsView::updateConfig() {
|
||||||
saveSetting("bios", m_ui.bios);
|
saveSetting("bios", m_ui.bios);
|
||||||
saveSetting("useBios", m_ui.useBios);
|
saveSetting("useBios", m_ui.useBios);
|
||||||
|
@ -122,6 +191,11 @@ void SettingsView::updateConfig() {
|
||||||
saveSetting("resampleVideo", m_ui.resampleVideo);
|
saveSetting("resampleVideo", m_ui.resampleVideo);
|
||||||
saveSetting("allowOpposingDirections", m_ui.allowOpposingDirections);
|
saveSetting("allowOpposingDirections", m_ui.allowOpposingDirections);
|
||||||
saveSetting("suspendScreensaver", m_ui.suspendScreensaver);
|
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()) {
|
if (m_ui.fastForwardUnbounded->isChecked()) {
|
||||||
saveSetting("fastForwardRatio", "-1");
|
saveSetting("fastForwardRatio", "-1");
|
||||||
|
@ -141,6 +215,18 @@ void SettingsView::updateConfig() {
|
||||||
break;
|
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());
|
QVariant audioDriver = m_ui.audioDriver->itemData(m_ui.audioDriver->currentIndex());
|
||||||
if (audioDriver != m_controller->getQtOption("audioDriver")) {
|
if (audioDriver != m_controller->getQtOption("audioDriver")) {
|
||||||
m_controller->setQtOption("audioDriver", audioDriver);
|
m_controller->setQtOption("audioDriver", audioDriver);
|
||||||
|
@ -157,9 +243,76 @@ void SettingsView::updateConfig() {
|
||||||
|
|
||||||
m_controller->write();
|
m_controller->write();
|
||||||
|
|
||||||
|
emit pathsChanged();
|
||||||
emit biosLoaded(m_ui.bios->text());
|
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) {
|
void SettingsView::saveSetting(const char* key, const QAbstractButton* field) {
|
||||||
m_controller->setOption(key, field->isChecked());
|
m_controller->setOption(key, field->isChecked());
|
||||||
m_controller->updateOption(key);
|
m_controller->updateOption(key);
|
||||||
|
|
|
@ -13,26 +13,32 @@
|
||||||
namespace QGBA {
|
namespace QGBA {
|
||||||
|
|
||||||
class ConfigController;
|
class ConfigController;
|
||||||
|
class InputController;
|
||||||
|
class ShortcutController;
|
||||||
|
|
||||||
class SettingsView : public QDialog {
|
class SettingsView : public QDialog {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SettingsView(ConfigController* controller, QWidget* parent = nullptr);
|
SettingsView(ConfigController* controller, InputController* inputController, ShortcutController* shortcutController, QWidget* parent = nullptr);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void biosLoaded(const QString&);
|
void biosLoaded(const QString&);
|
||||||
void audioDriverChanged();
|
void audioDriverChanged();
|
||||||
void displayDriverChanged();
|
void displayDriverChanged();
|
||||||
|
void pathsChanged();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void selectBios();
|
void selectBios();
|
||||||
|
void recalculateRewind();
|
||||||
void updateConfig();
|
void updateConfig();
|
||||||
|
void reloadConfig();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Ui::SettingsView m_ui;
|
Ui::SettingsView m_ui;
|
||||||
|
|
||||||
ConfigController* m_controller;
|
ConfigController* m_controller;
|
||||||
|
InputController* m_input;
|
||||||
|
|
||||||
void saveSetting(const char* key, const QAbstractButton*);
|
void saveSetting(const char* key, const QAbstractButton*);
|
||||||
void saveSetting(const char* key, const QComboBox*);
|
void saveSetting(const char* key, const QComboBox*);
|
||||||
|
|
|
@ -6,8 +6,8 @@
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>707</width>
|
<width>544</width>
|
||||||
<height>420</height>
|
<height>425</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
|
@ -19,16 +19,65 @@
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
<string>Settings</string>
|
<string>Settings</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
<property name="sizeConstraint">
|
<property name="sizeConstraint">
|
||||||
<enum>QLayout::SetFixedSize</enum>
|
<enum>QLayout::SetFixedSize</enum>
|
||||||
</property>
|
</property>
|
||||||
<item>
|
<item row="2" column="0" colspan="2">
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
<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>
|
<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">
|
<layout class="QFormLayout" name="formLayout">
|
||||||
<property name="fieldGrowthPolicy">
|
<property name="fieldGrowthPolicy">
|
||||||
<enum>QFormLayout::ExpandingFieldsGrow</enum>
|
<enum>QFormLayout::FieldsStayAtSizeHint</enum>
|
||||||
</property>
|
</property>
|
||||||
<item row="0" column="0">
|
<item row="0" column="0">
|
||||||
<widget class="QLabel" name="label_14">
|
<widget class="QLabel" name="label_14">
|
||||||
|
@ -120,6 +169,50 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</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">
|
<item row="3" column="0">
|
||||||
<widget class="QLabel" name="label_17">
|
<widget class="QLabel" name="label_17">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
|
@ -131,6 +224,12 @@
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_6">
|
<layout class="QHBoxLayout" name="horizontalLayout_6">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QSlider" name="volume">
|
<widget class="QSlider" name="volume">
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>128</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
<property name="maximum">
|
<property name="maximum">
|
||||||
<number>256</number>
|
<number>256</number>
|
||||||
</property>
|
</property>
|
||||||
|
@ -155,7 +254,7 @@
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item row="4" column="0" colspan="2">
|
<item row="4" column="0" colspan="2">
|
||||||
<widget class="Line" name="line">
|
<widget class="Line" name="line_4">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Horizontal</enum>
|
<enum>Qt::Horizontal</enum>
|
||||||
</property>
|
</property>
|
||||||
|
@ -238,7 +337,7 @@
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item row="8" column="0" colspan="2">
|
<item row="8" column="0" colspan="2">
|
||||||
<widget class="Line" name="line_4">
|
<widget class="Line" name="line_5">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Horizontal</enum>
|
<enum>Qt::Horizontal</enum>
|
||||||
</property>
|
</property>
|
||||||
|
@ -283,61 +382,13 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</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>
|
</layout>
|
||||||
</item>
|
</widget>
|
||||||
<item>
|
<widget class="QWidget" name="stackedWidgetPage2">
|
||||||
<widget class="Line" name="line_5">
|
<layout class="QFormLayout" name="formLayout_2">
|
||||||
<property name="orientation">
|
<property name="fieldGrowthPolicy">
|
||||||
<enum>Qt::Vertical</enum>
|
<enum>QFormLayout::FieldsStayAtSizeHint</enum>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<layout class="QFormLayout" name="formLayout_4">
|
|
||||||
<item row="0" column="0">
|
<item row="0" column="0">
|
||||||
<widget class="QLabel" name="label">
|
<widget class="QLabel" name="label">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
|
@ -366,6 +417,16 @@
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</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">
|
<item row="2" column="1">
|
||||||
<widget class="QCheckBox" name="skipBios">
|
<widget class="QCheckBox" name="skipBios">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
|
@ -373,120 +434,21 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="4" column="0" colspan="2">
|
<item row="3" column="0" colspan="2">
|
||||||
<widget class="Line" name="line_3">
|
<widget class="Line" name="line">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Horizontal</enum>
|
<enum>Qt::Horizontal</enum>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="5" column="1">
|
<item row="4" column="0">
|
||||||
<widget class="QCheckBox" name="rewind">
|
<widget class="QLabel" name="label_18">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Enable rewind</string>
|
<string>Fast forward speed</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="6" column="0">
|
<item row="4" column="1">
|
||||||
<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">
|
|
||||||
<widget class="QDoubleSpinBox" name="fastForwardRatio">
|
<widget class="QDoubleSpinBox" name="fastForwardRatio">
|
||||||
<property name="enabled">
|
<property name="enabled">
|
||||||
<bool>false</bool>
|
<bool>false</bool>
|
||||||
|
@ -508,14 +470,7 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="9" column="0">
|
<item row="5" column="1">
|
||||||
<widget class="QLabel" name="label_18">
|
|
||||||
<property name="text">
|
|
||||||
<string>Fast forward speed</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="10" column="1">
|
|
||||||
<widget class="QCheckBox" name="fastForwardUnbounded">
|
<widget class="QCheckBox" name="fastForwardUnbounded">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Unbounded</string>
|
<string>Unbounded</string>
|
||||||
|
@ -525,32 +480,423 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="8" column="0" colspan="2">
|
<item row="6" column="0" colspan="2">
|
||||||
<widget class="Line" name="line_6">
|
<widget class="Line" name="line_3">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Horizontal</enum>
|
<enum>Qt::Horizontal</enum>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="1">
|
<item row="7" column="1">
|
||||||
<widget class="QCheckBox" name="useBios">
|
<widget class="QCheckBox" name="allowOpposingDirections">
|
||||||
<property name="text">
|
<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>
|
||||||
<property name="checked">
|
<property name="checked">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</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>
|
</layout>
|
||||||
</item>
|
</widget>
|
||||||
</layout>
|
<widget class="QWidget" name="page_2">
|
||||||
</item>
|
<layout class="QFormLayout" name="formLayout_4">
|
||||||
<item>
|
<property name="fieldGrowthPolicy">
|
||||||
<widget class="QDialogButtonBox" name="buttonBox">
|
<enum>QFormLayout::FieldsStayAtSizeHint</enum>
|
||||||
<property name="standardButtons">
|
</property>
|
||||||
<set>QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
<item row="0" column="0">
|
||||||
</property>
|
<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>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
|
@ -590,18 +936,98 @@
|
||||||
</hints>
|
</hints>
|
||||||
</connection>
|
</connection>
|
||||||
<connection>
|
<connection>
|
||||||
<sender>useBios</sender>
|
<sender>tabs</sender>
|
||||||
<signal>toggled(bool)</signal>
|
<signal>currentRowChanged(int)</signal>
|
||||||
<receiver>skipBios</receiver>
|
<receiver>stackedWidget</receiver>
|
||||||
<slot>setEnabled(bool)</slot>
|
<slot>setCurrentIndex(int)</slot>
|
||||||
<hints>
|
<hints>
|
||||||
<hint type="sourcelabel">
|
<hint type="sourcelabel">
|
||||||
<x>520</x>
|
<x>61</x>
|
||||||
<y>62</y>
|
<y>209</y>
|
||||||
</hint>
|
</hint>
|
||||||
<hint type="destinationlabel">
|
<hint type="destinationlabel">
|
||||||
<x>525</x>
|
<x>315</x>
|
||||||
<y>83</y>
|
<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>
|
</hint>
|
||||||
</hints>
|
</hints>
|
||||||
</connection>
|
</connection>
|
||||||
|
|
|
@ -28,7 +28,7 @@ extern "C" {
|
||||||
using namespace QGBA;
|
using namespace QGBA;
|
||||||
|
|
||||||
ShaderSelector::ShaderSelector(Display* display, ConfigController* config, QWidget* parent)
|
ShaderSelector::ShaderSelector(Display* display, ConfigController* config, QWidget* parent)
|
||||||
: QDialog(parent)
|
: QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint)
|
||||||
, m_display(display)
|
, m_display(display)
|
||||||
, m_config(config)
|
, m_config(config)
|
||||||
, m_shaderPath("")
|
, m_shaderPath("")
|
||||||
|
|
|
@ -137,10 +137,13 @@ void ShortcutController::addFunctions(QMenu* menu, std::function<void()> press,
|
||||||
smenu->addFunctions(qMakePair(press, release), shortcut, visibleName, name);
|
smenu->addFunctions(qMakePair(press, release), shortcut, visibleName, name);
|
||||||
endInsertRows();
|
endInsertRows();
|
||||||
ShortcutItem* item = &smenu->items().last();
|
ShortcutItem* item = &smenu->items().last();
|
||||||
|
bool loadedShortcut = false;
|
||||||
if (m_config) {
|
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),
|
emit dataChanged(createIndex(smenu->items().count() - 1, 0, item),
|
||||||
createIndex(smenu->items().count() - 1, 2, item));
|
createIndex(smenu->items().count() - 1, 2, item));
|
||||||
}
|
}
|
||||||
|
@ -387,10 +390,11 @@ bool ShortcutController::eventFilter(QObject*, QEvent* event) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShortcutController::loadShortcuts(ShortcutItem* item) {
|
bool ShortcutController::loadShortcuts(ShortcutItem* item) {
|
||||||
if (item->name().isNull()) {
|
if (item->name().isNull()) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
loadGamepadShortcuts(item);
|
||||||
QVariant shortcut = m_config->getQtOption(item->name(), KEY_SECTION);
|
QVariant shortcut = m_config->getQtOption(item->name(), KEY_SECTION);
|
||||||
if (!shortcut.isNull()) {
|
if (!shortcut.isNull()) {
|
||||||
if (shortcut.toString().endsWith("+")) {
|
if (shortcut.toString().endsWith("+")) {
|
||||||
|
@ -398,8 +402,9 @@ void ShortcutController::loadShortcuts(ShortcutItem* item) {
|
||||||
} else {
|
} else {
|
||||||
updateKey(item, QKeySequence(shortcut.toString())[0]);
|
updateKey(item, QKeySequence(shortcut.toString())[0]);
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
loadGamepadShortcuts(item);
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShortcutController::loadGamepadShortcuts(ShortcutItem* item) {
|
void ShortcutController::loadGamepadShortcuts(ShortcutItem* item) {
|
||||||
|
|
|
@ -128,7 +128,7 @@ protected:
|
||||||
private:
|
private:
|
||||||
ShortcutItem* itemAt(const QModelIndex& index);
|
ShortcutItem* itemAt(const QModelIndex& index);
|
||||||
const ShortcutItem* itemAt(const QModelIndex& index) const;
|
const ShortcutItem* itemAt(const QModelIndex& index) const;
|
||||||
void loadShortcuts(ShortcutItem*);
|
bool loadShortcuts(ShortcutItem*);
|
||||||
void loadGamepadShortcuts(ShortcutItem*);
|
void loadGamepadShortcuts(ShortcutItem*);
|
||||||
void onSubitems(ShortcutItem*, std::function<void(ShortcutItem*)> func);
|
void onSubitems(ShortcutItem*, std::function<void(ShortcutItem*)> func);
|
||||||
void updateKey(ShortcutItem* item, int keySequence);
|
void updateKey(ShortcutItem* item, int keySequence);
|
||||||
|
|
|
@ -37,6 +37,10 @@ ShortcutView::ShortcutView(QWidget* parent)
|
||||||
connect(m_ui.clearButton, SIGNAL(clicked()), this, SLOT(clear()));
|
connect(m_ui.clearButton, SIGNAL(clicked()), this, SLOT(clear()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ShortcutView::~ShortcutView() {
|
||||||
|
m_input->releaseFocus(this);
|
||||||
|
}
|
||||||
|
|
||||||
void ShortcutView::setController(ShortcutController* controller) {
|
void ShortcutView::setController(ShortcutController* controller) {
|
||||||
m_controller = controller;
|
m_controller = controller;
|
||||||
m_ui.shortcutTable->setModel(controller);
|
m_ui.shortcutTable->setModel(controller);
|
||||||
|
@ -117,9 +121,10 @@ void ShortcutView::closeEvent(QCloseEvent*) {
|
||||||
|
|
||||||
bool ShortcutView::event(QEvent* event) {
|
bool ShortcutView::event(QEvent* event) {
|
||||||
if (m_input) {
|
if (m_input) {
|
||||||
if (event->type() == QEvent::WindowActivate) {
|
QEvent::Type type = event->type();
|
||||||
|
if (type == QEvent::WindowActivate || type == QEvent::Show) {
|
||||||
m_input->stealFocus(this);
|
m_input->stealFocus(this);
|
||||||
} else if (event->type() == QEvent::WindowDeactivate) {
|
} else if (type == QEvent::WindowDeactivate || type == QEvent::Hide) {
|
||||||
m_input->releaseFocus(this);
|
m_input->releaseFocus(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ShortcutView(QWidget* parent = nullptr);
|
ShortcutView(QWidget* parent = nullptr);
|
||||||
|
~ShortcutView();
|
||||||
|
|
||||||
void setController(ShortcutController* controller);
|
void setController(ShortcutController* controller);
|
||||||
void setInputController(InputController* input);
|
void setInputController(InputController* input);
|
||||||
|
|
|
@ -20,7 +20,6 @@
|
||||||
#include "Display.h"
|
#include "Display.h"
|
||||||
#include "GameController.h"
|
#include "GameController.h"
|
||||||
#include "GBAApp.h"
|
#include "GBAApp.h"
|
||||||
#include "GBAKeyEditor.h"
|
|
||||||
#include "GDBController.h"
|
#include "GDBController.h"
|
||||||
#include "GDBWindow.h"
|
#include "GDBWindow.h"
|
||||||
#include "GIFView.h"
|
#include "GIFView.h"
|
||||||
|
@ -36,7 +35,6 @@
|
||||||
#include "SettingsView.h"
|
#include "SettingsView.h"
|
||||||
#include "ShaderSelector.h"
|
#include "ShaderSelector.h"
|
||||||
#include "ShortcutController.h"
|
#include "ShortcutController.h"
|
||||||
#include "ShortcutView.h"
|
|
||||||
#include "VideoView.h"
|
#include "VideoView.h"
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
@ -74,6 +72,7 @@ Window::Window(ConfigController* config, int playerId, QWidget* parent)
|
||||||
, m_shortcutController(new ShortcutController(this))
|
, m_shortcutController(new ShortcutController(this))
|
||||||
, m_playerId(playerId)
|
, m_playerId(playerId)
|
||||||
, m_fullscreenOnStart(false)
|
, m_fullscreenOnStart(false)
|
||||||
|
, m_autoresume(false)
|
||||||
{
|
{
|
||||||
setFocusPolicy(Qt::StrongFocus);
|
setFocusPolicy(Qt::StrongFocus);
|
||||||
setAcceptDrops(true);
|
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, SIGNAL(rewound(GBAThread*)), m_display, SLOT(forceDraw()));
|
||||||
connect(m_controller, &GameController::gamePaused, [this]() {
|
connect(m_controller, &GameController::gamePaused, [this]() {
|
||||||
QImage currentImage(reinterpret_cast<const uchar*>(m_controller->drawContext()), VIDEO_HORIZONTAL_PIXELS,
|
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;
|
QPixmap pixmap;
|
||||||
pixmap.convertFromImage(currentImage);
|
pixmap.convertFromImage(currentImage);
|
||||||
m_screenWidget->setPixmap(pixmap);
|
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(sampleRateChanged(unsigned)), m_controller, SLOT(setAudioSampleRate(unsigned)));
|
||||||
connect(this, SIGNAL(fpsTargetChanged(float)), m_controller, SLOT(setFPSTarget(float)));
|
connect(this, SIGNAL(fpsTargetChanged(float)), m_controller, SLOT(setFPSTarget(float)));
|
||||||
connect(&m_fpsTimer, SIGNAL(timeout()), this, SLOT(showFPS()));
|
connect(&m_fpsTimer, SIGNAL(timeout()), this, SLOT(showFPS()));
|
||||||
|
connect(&m_focusCheck, SIGNAL(timeout()), this, SLOT(focusCheck()));
|
||||||
connect(m_display, &Display::hideCursor, [this]() {
|
connect(m_display, &Display::hideCursor, [this]() {
|
||||||
if (static_cast<QStackedLayout*>(m_screenWidget->layout())->currentWidget() == m_display) {
|
if (static_cast<QStackedLayout*>(m_screenWidget->layout())->currentWidget() == m_display) {
|
||||||
m_screenWidget->setCursor(Qt::BlankCursor);
|
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_log.setLevels(GBA_LOG_WARN | GBA_LOG_ERROR | GBA_LOG_FATAL | GBA_LOG_STATUS);
|
||||||
m_fpsTimer.setInterval(FPS_TIMER_INTERVAL);
|
m_fpsTimer.setInterval(FPS_TIMER_INTERVAL);
|
||||||
|
m_focusCheck.setInterval(200);
|
||||||
|
|
||||||
m_shortcutController->setConfigController(m_config);
|
m_shortcutController->setConfigController(m_config);
|
||||||
setupMenu(menuBar());
|
setupMenu(menuBar());
|
||||||
|
@ -179,7 +180,7 @@ void Window::argumentsPassed(GBAArguments* args) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (args->fname) {
|
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() {
|
void Window::loadConfig() {
|
||||||
const GBAOptions* opts = m_config->options();
|
const GBAOptions* opts = m_config->options();
|
||||||
|
reloadConfig();
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Move these to ConfigController
|
// TODO: Move these to ConfigController
|
||||||
if (opts->fpsTarget) {
|
if (opts->fpsTarget) {
|
||||||
|
@ -238,14 +230,41 @@ void Window::loadConfig() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_inputController.setScreensaverSuspendable(opts->suspendScreensaver);
|
|
||||||
|
|
||||||
m_mruFiles = m_config->getMRU();
|
m_mruFiles = m_config->getMRU();
|
||||||
updateMRU();
|
updateMRU();
|
||||||
|
|
||||||
m_inputController.setConfiguration(m_config);
|
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() {
|
void Window::saveConfig() {
|
||||||
m_inputController.saveConfiguration();
|
m_inputController.saveConfiguration();
|
||||||
m_config->write();
|
m_config->write();
|
||||||
|
@ -254,7 +273,7 @@ void Window::saveConfig() {
|
||||||
void Window::selectROM() {
|
void Window::selectROM() {
|
||||||
QStringList formats{
|
QStringList formats{
|
||||||
"*.gba",
|
"*.gba",
|
||||||
#ifdef USE_LIBZIP
|
#if defined(USE_LIBZIP) || defined(USE_ZLIB)
|
||||||
"*.zip",
|
"*.zip",
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_LZMA
|
#ifdef USE_LZMA
|
||||||
|
@ -274,7 +293,7 @@ void Window::selectROM() {
|
||||||
void Window::replaceROM() {
|
void Window::replaceROM() {
|
||||||
QStringList formats{
|
QStringList formats{
|
||||||
"*.gba",
|
"*.gba",
|
||||||
#ifdef USE_LIBZIP
|
#if defined(USE_LIBZIP) || defined(USE_ZLIB)
|
||||||
"*.zip",
|
"*.zip",
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_LZMA
|
#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() {
|
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(biosLoaded(const QString&)), m_controller, SLOT(loadBIOS(const QString&)));
|
||||||
connect(settingsWindow, SIGNAL(audioDriverChanged()), m_controller, SLOT(reloadAudioDriver()));
|
connect(settingsWindow, SIGNAL(audioDriverChanged()), m_controller, SLOT(reloadAudioDriver()));
|
||||||
connect(settingsWindow, SIGNAL(displayDriverChanged()), this, SLOT(mustRestart()));
|
connect(settingsWindow, SIGNAL(displayDriverChanged()), this, SLOT(mustRestart()));
|
||||||
|
connect(settingsWindow, SIGNAL(pathsChanged()), this, SLOT(reloadConfig()));
|
||||||
openView(settingsWindow);
|
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() {
|
void Window::openOverrideWindow() {
|
||||||
OverrideView* overrideWindow = new OverrideView(m_controller, m_config);
|
OverrideView* overrideWindow = new OverrideView(m_controller, m_config);
|
||||||
openView(overrideWindow);
|
openView(overrideWindow);
|
||||||
|
@ -407,14 +412,6 @@ void Window::openROMInfo() {
|
||||||
openView(romInfo);
|
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
|
#ifdef USE_FFMPEG
|
||||||
void Window::openVideoWindow() {
|
void Window::openVideoWindow() {
|
||||||
if (!m_videoView) {
|
if (!m_videoView) {
|
||||||
|
@ -629,6 +626,7 @@ void Window::gameStarted(GBAThread* context) {
|
||||||
|
|
||||||
m_hitUnimplementedBiosCall = false;
|
m_hitUnimplementedBiosCall = false;
|
||||||
m_fpsTimer.start();
|
m_fpsTimer.start();
|
||||||
|
m_focusCheck.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Window::gameStopped() {
|
void Window::gameStopped() {
|
||||||
|
@ -643,6 +641,7 @@ void Window::gameStopped() {
|
||||||
m_screenWidget->unsetCursor();
|
m_screenWidget->unsetCursor();
|
||||||
|
|
||||||
m_fpsTimer.stop();
|
m_fpsTimer.stop();
|
||||||
|
m_focusCheck.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Window::gameCrashed(const QString& errorMessage) {
|
void Window::gameCrashed(const QString& errorMessage) {
|
||||||
|
@ -1196,18 +1195,6 @@ void Window::setupMenu(QMenuBar* menubar) {
|
||||||
toolsMenu->addSeparator();
|
toolsMenu->addSeparator();
|
||||||
addControlledAction(toolsMenu, toolsMenu->addAction(tr("Settings..."), this, SLOT(openSettingsWindow())),
|
addControlledAction(toolsMenu, toolsMenu->addAction(tr("Settings..."), this, SLOT(openSettingsWindow())),
|
||||||
"settings");
|
"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();
|
toolsMenu->addSeparator();
|
||||||
|
|
||||||
|
@ -1271,6 +1258,16 @@ void Window::setupMenu(QMenuBar* menubar) {
|
||||||
m_inputController.setAllowOpposing(value.toBool());
|
m_inputController.setAllowOpposing(value.toBool());
|
||||||
}, this);
|
}, 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);
|
QAction* exitFullScreen = new QAction(tr("Exit fullscreen"), frameMenu);
|
||||||
connect(exitFullScreen, SIGNAL(triggered()), this, SLOT(exitFullScreen()));
|
connect(exitFullScreen, SIGNAL(triggered()), this, SLOT(exitFullScreen()));
|
||||||
exitFullScreen->setShortcut(QKeySequence("Esc"));
|
exitFullScreen->setShortcut(QKeySequence("Esc"));
|
||||||
|
@ -1397,6 +1394,18 @@ QAction* Window::addHiddenAction(QMenu* menu, QAction* action, const QString& na
|
||||||
return action;
|
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)
|
WindowBackground::WindowBackground(QWidget* parent)
|
||||||
: QLabel(parent)
|
: QLabel(parent)
|
||||||
{
|
{
|
||||||
|
|
|
@ -66,6 +66,7 @@ public slots:
|
||||||
void exitFullScreen();
|
void exitFullScreen();
|
||||||
void toggleFullScreen();
|
void toggleFullScreen();
|
||||||
void loadConfig();
|
void loadConfig();
|
||||||
|
void reloadConfig();
|
||||||
void saveConfig();
|
void saveConfig();
|
||||||
|
|
||||||
void replaceROM();
|
void replaceROM();
|
||||||
|
@ -75,10 +76,7 @@ public slots:
|
||||||
void importSharkport();
|
void importSharkport();
|
||||||
void exportSharkport();
|
void exportSharkport();
|
||||||
|
|
||||||
void openKeymapWindow();
|
|
||||||
void openSettingsWindow();
|
void openSettingsWindow();
|
||||||
void openShortcutWindow();
|
|
||||||
|
|
||||||
void openOverrideWindow();
|
void openOverrideWindow();
|
||||||
void openSensorWindow();
|
void openSensorWindow();
|
||||||
void openCheatsWindow();
|
void openCheatsWindow();
|
||||||
|
@ -90,10 +88,6 @@ public slots:
|
||||||
void openAboutScreen();
|
void openAboutScreen();
|
||||||
void openROMInfo();
|
void openROMInfo();
|
||||||
|
|
||||||
#ifdef BUILD_SDL
|
|
||||||
void openGamepadWindow();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef USE_FFMPEG
|
#ifdef USE_FFMPEG
|
||||||
void openVideoWindow();
|
void openVideoWindow();
|
||||||
#endif
|
#endif
|
||||||
|
@ -130,6 +124,7 @@ private slots:
|
||||||
|
|
||||||
void recordFrame();
|
void recordFrame();
|
||||||
void showFPS();
|
void showFPS();
|
||||||
|
void focusCheck();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static const int FPS_TIMER_INTERVAL = 2000;
|
static const int FPS_TIMER_INTERVAL = 2000;
|
||||||
|
@ -171,6 +166,8 @@ private:
|
||||||
ShaderSelector* m_shaderView;
|
ShaderSelector* m_shaderView;
|
||||||
int m_playerId;
|
int m_playerId;
|
||||||
bool m_fullscreenOnStart;
|
bool m_fullscreenOnStart;
|
||||||
|
QTimer m_focusCheck;
|
||||||
|
bool m_autoresume;
|
||||||
|
|
||||||
bool m_hitUnimplementedBiosCall;
|
bool m_hitUnimplementedBiosCall;
|
||||||
|
|
||||||
|
|
|
@ -30,8 +30,8 @@ void GBASDLGLCreate(struct SDLSoftwareRenderer* renderer) {
|
||||||
bool GBASDLGLInit(struct SDLSoftwareRenderer* renderer) {
|
bool GBASDLGLInit(struct SDLSoftwareRenderer* renderer) {
|
||||||
GBASDLGLCommonInit(renderer);
|
GBASDLGLCommonInit(renderer);
|
||||||
|
|
||||||
renderer->d.outputBuffer = malloc(256 * 256 * BYTES_PER_PIXEL);
|
renderer->d.outputBuffer = malloc(VIDEO_HORIZONTAL_PIXELS * VIDEO_VERTICAL_PIXELS * BYTES_PER_PIXEL);
|
||||||
renderer->d.outputBufferStride = 256;
|
renderer->d.outputBufferStride = VIDEO_HORIZONTAL_PIXELS;
|
||||||
|
|
||||||
GBAGLContextCreate(&renderer->gl);
|
GBAGLContextCreate(&renderer->gl);
|
||||||
renderer->gl.d.user = renderer;
|
renderer->gl.d.user = renderer;
|
||||||
|
|
|
@ -93,8 +93,8 @@ bool GBASDLGLES2Init(struct SDLSoftwareRenderer* renderer) {
|
||||||
GBASDLGLCommonInit(renderer);
|
GBASDLGLCommonInit(renderer);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
renderer->d.outputBuffer = memalign(16, 256 * 256 * 4);
|
renderer->d.outputBuffer = memalign(16, VIDEO_HORIZONTAL_PIXELS * VIDEO_VERTICAL_PIXELS * 4);
|
||||||
renderer->d.outputBufferStride = 256;
|
renderer->d.outputBufferStride = VIDEO_HORIZONTAL_PIXELS;
|
||||||
|
|
||||||
GBAGLES2ContextCreate(&renderer->gl2);
|
GBAGLES2ContextCreate(&renderer->gl2);
|
||||||
renderer->gl2.d.user = renderer;
|
renderer->gl2.d.user = renderer;
|
||||||
|
|
|
@ -95,7 +95,7 @@ int main(int argc, char** argv) {
|
||||||
|
|
||||||
#ifdef BUILD_GL
|
#ifdef BUILD_GL
|
||||||
GBASDLGLCreate(&renderer);
|
GBASDLGLCreate(&renderer);
|
||||||
#elif defined(BUILD_GLES2)
|
#elif defined(BUILD_GLES2) || defined(USE_EPOXY)
|
||||||
GBASDLGLES2Create(&renderer);
|
GBASDLGLES2Create(&renderer);
|
||||||
#else
|
#else
|
||||||
GBASDLSWCreate(&renderer);
|
GBASDLSWCreate(&renderer);
|
||||||
|
@ -164,6 +164,7 @@ int main(int argc, char** argv) {
|
||||||
GBAConfigFreeOpts(&opts);
|
GBAConfigFreeOpts(&opts);
|
||||||
GBAConfigDeinit(&config);
|
GBAConfigDeinit(&config);
|
||||||
free(context.debugger);
|
free(context.debugger);
|
||||||
|
GBADirectorySetDeinit(&context.dirs);
|
||||||
GBASDLDetachPlayer(&renderer.events, &renderer.player);
|
GBASDLDetachPlayer(&renderer.events, &renderer.player);
|
||||||
GBAInputMapDeinit(&inputMap);
|
GBAInputMapDeinit(&inputMap);
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
#pragma GCC diagnostic pop
|
#pragma GCC diagnostic pop
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef BUILD_GLES2
|
#if defined(BUILD_GLES2) || defined(USE_EPOXY)
|
||||||
#include "platform/opengl/gles2.h"
|
#include "platform/opengl/gles2.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -63,7 +63,7 @@ struct SDLSoftwareRenderer {
|
||||||
#ifdef BUILD_GL
|
#ifdef BUILD_GL
|
||||||
struct GBAGLContext gl;
|
struct GBAGLContext gl;
|
||||||
#endif
|
#endif
|
||||||
#ifdef BUILD_GLES2
|
#if defined(BUILD_GLES2) || defined(USE_EPOXY)
|
||||||
struct GBAGLES2Context gl2;
|
struct GBAGLES2Context gl2;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -92,7 +92,7 @@ void GBASDLSWCreate(struct SDLSoftwareRenderer* renderer);
|
||||||
void GBASDLGLCreate(struct SDLSoftwareRenderer* renderer);
|
void GBASDLGLCreate(struct SDLSoftwareRenderer* renderer);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef BUILD_GLES2
|
#if defined(BUILD_GLES2) || defined(USE_EPOXY)
|
||||||
void GBASDLGLES2Create(struct SDLSoftwareRenderer* renderer);
|
void GBASDLGLES2Create(struct SDLSoftwareRenderer* renderer);
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -24,6 +24,8 @@
|
||||||
#define GYRO_STEPS 100
|
#define GYRO_STEPS 100
|
||||||
#define RUMBLE_PWM 20
|
#define RUMBLE_PWM 20
|
||||||
|
|
||||||
|
DEFINE_VECTOR(SDL_JoystickList, struct SDL_JoystickCombo);
|
||||||
|
|
||||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||||
static void _GBASDLSetRumble(struct GBARumble* rumble, int enable);
|
static void _GBASDLSetRumble(struct GBARumble* rumble, int enable);
|
||||||
#endif
|
#endif
|
||||||
|
@ -52,22 +54,9 @@ bool GBASDLInitEvents(struct GBASDLEvents* context) {
|
||||||
|
|
||||||
SDL_JoystickEventState(SDL_ENABLE);
|
SDL_JoystickEventState(SDL_ENABLE);
|
||||||
int nJoysticks = SDL_NumJoysticks();
|
int nJoysticks = SDL_NumJoysticks();
|
||||||
|
SDL_JoystickListInit(&context->joysticks, nJoysticks);
|
||||||
if (nJoysticks > 0) {
|
if (nJoysticks > 0) {
|
||||||
context->nJoysticks = nJoysticks;
|
GBASDLUpdateJoysticks(context);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
context->playersAttached = 0;
|
context->playersAttached = 0;
|
||||||
|
@ -75,7 +64,6 @@ bool GBASDLInitEvents(struct GBASDLEvents* context) {
|
||||||
size_t i;
|
size_t i;
|
||||||
for (i = 0; i < MAX_PLAYERS; ++i) {
|
for (i = 0; i < MAX_PLAYERS; ++i) {
|
||||||
context->preferredJoysticks[i] = 0;
|
context->preferredJoysticks[i] = 0;
|
||||||
context->joysticksClaimed[i] = SIZE_MAX;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !SDL_VERSION_ATLEAST(2, 0, 0)
|
#if !SDL_VERSION_ATLEAST(2, 0, 0)
|
||||||
|
@ -88,13 +76,14 @@ bool GBASDLInitEvents(struct GBASDLEvents* context) {
|
||||||
|
|
||||||
void GBASDLDeinitEvents(struct GBASDLEvents* context) {
|
void GBASDLDeinitEvents(struct GBASDLEvents* context) {
|
||||||
size_t i;
|
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)
|
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||||
SDL_HapticClose(context->haptic[i]);
|
SDL_HapticClose(joystick->haptic);
|
||||||
#endif
|
#endif
|
||||||
SDL_JoystickClose(context->joysticks[i]);
|
SDL_JoystickClose(joystick->joystick);
|
||||||
}
|
}
|
||||||
|
SDL_JoystickListDeinit(&context->joysticks);
|
||||||
SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
|
SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -160,7 +149,6 @@ void GBASDLInitBindings(struct GBAInputMap* inputMap) {
|
||||||
|
|
||||||
bool GBASDLAttachPlayer(struct GBASDLEvents* events, struct GBASDLPlayer* player) {
|
bool GBASDLAttachPlayer(struct GBASDLEvents* events, struct GBASDLPlayer* player) {
|
||||||
player->joystick = 0;
|
player->joystick = 0;
|
||||||
player->joystickIndex = SIZE_MAX;
|
|
||||||
|
|
||||||
if (events->playersAttached >= MAX_PLAYERS) {
|
if (events->playersAttached >= MAX_PLAYERS) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -187,15 +175,17 @@ bool GBASDLAttachPlayer(struct GBASDLEvents* events, struct GBASDLPlayer* player
|
||||||
player->rotation.p = player;
|
player->rotation.p = player;
|
||||||
|
|
||||||
player->playerId = events->playersAttached;
|
player->playerId = events->playersAttached;
|
||||||
|
events->players[player->playerId] = player;
|
||||||
size_t firstUnclaimed = SIZE_MAX;
|
size_t firstUnclaimed = SIZE_MAX;
|
||||||
|
size_t index = SIZE_MAX;
|
||||||
|
|
||||||
size_t i;
|
size_t i;
|
||||||
for (i = 0; i < events->nJoysticks; ++i) {
|
for (i = 0; i < SDL_JoystickListSize(&events->joysticks); ++i) {
|
||||||
bool claimed = false;
|
bool claimed = false;
|
||||||
|
|
||||||
int p;
|
int p;
|
||||||
for (p = 0; p < events->playersAttached; ++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;
|
claimed = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -210,28 +200,26 @@ bool GBASDLAttachPlayer(struct GBASDLEvents* events, struct GBASDLPlayer* player
|
||||||
|
|
||||||
const char* joystickName;
|
const char* joystickName;
|
||||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||||
joystickName = SDL_JoystickName(events->joysticks[i]);
|
joystickName = SDL_JoystickName(SDL_JoystickListGetPointer(&events->joysticks, i)->joystick);
|
||||||
#else
|
#else
|
||||||
joystickName = SDL_JoystickName(SDL_JoystickIndex(events->joysticks[i]));
|
joystickName = SDL_JoystickName(SDL_JoystickIndex(SDL_JoystickListGetPointer(&events->joysticks, i)->joystick));
|
||||||
#endif
|
#endif
|
||||||
if (events->preferredJoysticks[player->playerId] && strcmp(events->preferredJoysticks[player->playerId], joystickName) == 0) {
|
if (events->preferredJoysticks[player->playerId] && strcmp(events->preferredJoysticks[player->playerId], joystickName) == 0) {
|
||||||
player->joystickIndex = i;
|
index = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (player->joystickIndex == SIZE_MAX && firstUnclaimed != SIZE_MAX) {
|
if (index == SIZE_MAX && firstUnclaimed != SIZE_MAX) {
|
||||||
player->joystickIndex = firstUnclaimed;
|
index = firstUnclaimed;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (player->joystickIndex != SIZE_MAX) {
|
if (index != SIZE_MAX) {
|
||||||
player->joystick = events->joysticks[player->joystickIndex];
|
player->joystick = SDL_JoystickListGetPointer(&events->joysticks, index);
|
||||||
events->joysticksClaimed[player->playerId] = player->joystickIndex;
|
|
||||||
|
|
||||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||||
player->haptic = events->haptic[player->joystickIndex];
|
if (player->joystick->haptic) {
|
||||||
if (player->haptic) {
|
SDL_HapticRumbleInit(player->joystick->haptic);
|
||||||
SDL_HapticRumbleInit(player->haptic);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -241,7 +229,19 @@ bool GBASDLAttachPlayer(struct GBASDLEvents* events, struct GBASDLPlayer* player
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBASDLDetachPlayer(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);
|
CircleBufferDeinit(&player->rotation.zHistory);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -250,15 +250,15 @@ void GBASDLPlayerLoadConfig(struct GBASDLPlayer* context, const struct Configura
|
||||||
if (context->joystick) {
|
if (context->joystick) {
|
||||||
GBAInputMapLoad(context->bindings, SDL_BINDING_BUTTON, config);
|
GBAInputMapLoad(context->bindings, SDL_BINDING_BUTTON, config);
|
||||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||||
const char* name = SDL_JoystickName(context->joystick);
|
const char* name = SDL_JoystickName(context->joystick->joystick);
|
||||||
#else
|
#else
|
||||||
const char* name = SDL_JoystickName(SDL_JoystickIndex(context->joystick));
|
const char* name = SDL_JoystickName(SDL_JoystickIndex(context->joystick->joystick));
|
||||||
#endif
|
#endif
|
||||||
GBAInputProfileLoad(context->bindings, SDL_BINDING_BUTTON, config, name);
|
GBAInputProfileLoad(context->bindings, SDL_BINDING_BUTTON, config, name);
|
||||||
|
|
||||||
const char* value;
|
const char* value;
|
||||||
char* end;
|
char* end;
|
||||||
int numAxes = SDL_JoystickNumAxes(context->joystick);
|
int numAxes = SDL_JoystickNumAxes(context->joystick->joystick);
|
||||||
int axis;
|
int axis;
|
||||||
value = GBAInputGetCustomValue(config, SDL_BINDING_BUTTON, "tiltAxisX", name);
|
value = GBAInputGetCustomValue(config, SDL_BINDING_BUTTON, "tiltAxisX", name);
|
||||||
if (value) {
|
if (value) {
|
||||||
|
@ -301,9 +301,9 @@ void GBASDLPlayerLoadConfig(struct GBASDLPlayer* context, const struct Configura
|
||||||
void GBASDLPlayerSaveConfig(const struct GBASDLPlayer* context, struct Configuration* config) {
|
void GBASDLPlayerSaveConfig(const struct GBASDLPlayer* context, struct Configuration* config) {
|
||||||
if (context->joystick) {
|
if (context->joystick) {
|
||||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||||
const char* name = SDL_JoystickName(context->joystick);
|
const char* name = SDL_JoystickName(context->joystick->joystick);
|
||||||
#else
|
#else
|
||||||
const char* name = SDL_JoystickName(SDL_JoystickIndex(context->joystick));
|
const char* name = SDL_JoystickName(SDL_JoystickIndex(context->joystick->joystick));
|
||||||
#endif
|
#endif
|
||||||
char value[12];
|
char value[12];
|
||||||
snprintf(value, sizeof(value), "%i", context->rotation.axisX);
|
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) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
events->joysticksClaimed[player->playerId] = index;
|
player->joystick = SDL_JoystickListGetPointer(&events->joysticks, index);
|
||||||
player->joystickIndex = index;
|
}
|
||||||
player->joystick = 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)
|
#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
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -540,7 +580,7 @@ void GBASDLHandleEvent(struct GBAThread* context, struct GBASDLPlayer* sdlContex
|
||||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||||
static void _GBASDLSetRumble(struct GBARumble* rumble, int enable) {
|
static void _GBASDLSetRumble(struct GBARumble* rumble, int enable) {
|
||||||
struct GBASDLRumble* sdlRumble = (struct GBASDLRumble*) rumble;
|
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;
|
return;
|
||||||
}
|
}
|
||||||
sdlRumble->level += enable;
|
sdlRumble->level += enable;
|
||||||
|
@ -551,15 +591,18 @@ static void _GBASDLSetRumble(struct GBARumble* rumble, int enable) {
|
||||||
}
|
}
|
||||||
CircleBufferWrite8(&sdlRumble->history, enable);
|
CircleBufferWrite8(&sdlRumble->history, enable);
|
||||||
if (sdlRumble->level) {
|
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 {
|
} else {
|
||||||
SDL_HapticRumbleStop(sdlRumble->p->haptic);
|
SDL_HapticRumbleStop(sdlRumble->p->joystick->haptic);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static int32_t _readTilt(struct GBASDLPlayer* player, int axis) {
|
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) {
|
static int32_t _GBASDLReadTiltX(struct GBARotationSource* source) {
|
||||||
|
@ -581,9 +624,12 @@ static int32_t _GBASDLReadGyroZ(struct GBARotationSource* source) {
|
||||||
static void _GBASDLRotationSample(struct GBARotationSource* source) {
|
static void _GBASDLRotationSample(struct GBARotationSource* source) {
|
||||||
struct GBASDLRotation* rotation = (struct GBASDLRotation*) source;
|
struct GBASDLRotation* rotation = (struct GBASDLRotation*) source;
|
||||||
SDL_JoystickUpdate();
|
SDL_JoystickUpdate();
|
||||||
|
if (!rotation->p->joystick) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
int x = SDL_JoystickGetAxis(rotation->p->joystick, rotation->gyroX);
|
int x = SDL_JoystickGetAxis(rotation->p->joystick->joystick, rotation->gyroX);
|
||||||
int y = SDL_JoystickGetAxis(rotation->p->joystick, rotation->gyroY);
|
int y = SDL_JoystickGetAxis(rotation->p->joystick->joystick, rotation->gyroY);
|
||||||
union {
|
union {
|
||||||
float f;
|
float f;
|
||||||
int32_t i;
|
int32_t i;
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
#include "util/common.h"
|
#include "util/common.h"
|
||||||
#include "util/circle-buffer.h"
|
#include "util/circle-buffer.h"
|
||||||
|
#include "util/vector.h"
|
||||||
|
|
||||||
#include "gba/supervisor/thread.h"
|
#include "gba/supervisor/thread.h"
|
||||||
|
|
||||||
|
@ -21,14 +22,25 @@
|
||||||
struct GBAVideoSoftwareRenderer;
|
struct GBAVideoSoftwareRenderer;
|
||||||
struct Configuration;
|
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 {
|
struct GBASDLEvents {
|
||||||
SDL_Joystick** joysticks;
|
struct SDL_JoystickList joysticks;
|
||||||
size_t nJoysticks;
|
|
||||||
const char* preferredJoysticks[MAX_PLAYERS];
|
const char* preferredJoysticks[MAX_PLAYERS];
|
||||||
int playersAttached;
|
int playersAttached;
|
||||||
size_t joysticksClaimed[MAX_PLAYERS];
|
struct GBASDLPlayer* players[MAX_PLAYERS];
|
||||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||||
SDL_Haptic** haptic;
|
|
||||||
int screensaverSuspendDepth;
|
int screensaverSuspendDepth;
|
||||||
bool screensaverSuspendable;
|
bool screensaverSuspendable;
|
||||||
#endif
|
#endif
|
||||||
|
@ -37,13 +49,11 @@ struct GBASDLEvents {
|
||||||
struct GBASDLPlayer {
|
struct GBASDLPlayer {
|
||||||
size_t playerId;
|
size_t playerId;
|
||||||
struct GBAInputMap* bindings;
|
struct GBAInputMap* bindings;
|
||||||
SDL_Joystick* joystick;
|
struct SDL_JoystickCombo* joystick;
|
||||||
size_t joystickIndex;
|
|
||||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||||
SDL_Window* window;
|
SDL_Window* window;
|
||||||
int fullscreen;
|
int fullscreen;
|
||||||
int windowUpdated;
|
int windowUpdated;
|
||||||
SDL_Haptic* haptic;
|
|
||||||
|
|
||||||
struct GBASDLRumble {
|
struct GBASDLRumble {
|
||||||
struct GBARumble d;
|
struct GBARumble d;
|
||||||
|
@ -80,6 +90,7 @@ bool GBASDLAttachPlayer(struct GBASDLEvents*, struct GBASDLPlayer*);
|
||||||
void GBASDLDetachPlayer(struct GBASDLEvents*, struct GBASDLPlayer*);
|
void GBASDLDetachPlayer(struct GBASDLEvents*, struct GBASDLPlayer*);
|
||||||
void GBASDLEventsLoadConfig(struct GBASDLEvents*, const struct Configuration*);
|
void GBASDLEventsLoadConfig(struct GBASDLEvents*, const struct Configuration*);
|
||||||
void GBASDLPlayerChangeJoystick(struct GBASDLEvents*, struct GBASDLPlayer*, size_t index);
|
void GBASDLPlayerChangeJoystick(struct GBASDLEvents*, struct GBASDLPlayer*, size_t index);
|
||||||
|
void GBASDLUpdateJoysticks(struct GBASDLEvents* events);
|
||||||
|
|
||||||
void GBASDLInitBindings(struct GBAInputMap* inputMap);
|
void GBASDLInitBindings(struct GBAInputMap* inputMap);
|
||||||
void GBASDLPlayerLoadConfig(struct GBASDLPlayer*, const struct Configuration*);
|
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;
|
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) {
|
void GUIFontDrawGlyph(const struct GUIFont* font, int x, int y, uint32_t color, uint32_t glyph) {
|
||||||
color = (color >> 24) | (color << 8);
|
color = (color >> 24) | (color << 8);
|
||||||
GXTexObj tex;
|
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_TexCoord2f32(u[3], v[3]);
|
||||||
GX_End();
|
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",
|
"1",
|
||||||
"B",
|
"B",
|
||||||
"A",
|
"A",
|
||||||
"Minus",
|
"-",
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
"Home",
|
"\1\xE",
|
||||||
"Left",
|
"Left",
|
||||||
"Right",
|
"Right",
|
||||||
"Down",
|
"Down",
|
||||||
"Up",
|
"Up",
|
||||||
"Plus",
|
"+",
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
|
@ -311,9 +311,9 @@ int main(int argc, char* argv[]) {
|
||||||
"ZL",
|
"ZL",
|
||||||
0,
|
0,
|
||||||
"R",
|
"R",
|
||||||
"Plus",
|
"+",
|
||||||
"Home",
|
"\1\xE",
|
||||||
"Minus",
|
"-",
|
||||||
"L",
|
"L",
|
||||||
"Down",
|
"Down",
|
||||||
"Right",
|
"Right",
|
||||||
|
@ -426,18 +426,18 @@ static uint32_t _pollInput(void) {
|
||||||
int keys = 0;
|
int keys = 0;
|
||||||
int x = PAD_StickX(0);
|
int x = PAD_StickX(0);
|
||||||
int y = PAD_StickY(0);
|
int y = PAD_StickY(0);
|
||||||
int w_x = WPAD_StickX(0,0);
|
int w_x = WPAD_StickX(0, 0);
|
||||||
int w_y = WPAD_StickY(0,0);
|
int w_y = WPAD_StickY(0, 0);
|
||||||
if (x < -0x40 || w_x < -0x40) {
|
if (x < -0x20 || w_x < -0x20) {
|
||||||
keys |= 1 << GUI_INPUT_LEFT;
|
keys |= 1 << GUI_INPUT_LEFT;
|
||||||
}
|
}
|
||||||
if (x > 0x40 || w_x > 0x40) {
|
if (x > 0x20 || w_x > 0x20) {
|
||||||
keys |= 1 << GUI_INPUT_RIGHT;
|
keys |= 1 << GUI_INPUT_RIGHT;
|
||||||
}
|
}
|
||||||
if (y < -0x40 || w_y <- 0x40) {
|
if (y < -0x20 || w_y <- 0x20) {
|
||||||
keys |= 1 << GUI_INPUT_DOWN;
|
keys |= 1 << GUI_INPUT_DOWN;
|
||||||
}
|
}
|
||||||
if (y > 0x40 || w_y > 0x40) {
|
if (y > 0x20 || w_y > 0x20) {
|
||||||
keys |= 1 << GUI_INPUT_UP;
|
keys |= 1 << GUI_INPUT_UP;
|
||||||
}
|
}
|
||||||
if ((padkeys & PAD_BUTTON_A) || (wiiPad & WPAD_BUTTON_2) ||
|
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_L, GBA_KEY_L);
|
||||||
_mapKey(&runner->context.inputMap, CLASSIC_INPUT, WPAD_CLASSIC_BUTTON_FULL_R, GBA_KEY_R);
|
_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, GCN1_INPUT, 0, &desc);
|
||||||
GBAInputBindAxis(&runner->context.inputMap, CLASSIC_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, GCN1_INPUT, 1, &desc);
|
||||||
GBAInputBindAxis(&runner->context.inputMap, CLASSIC_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) {
|
static inline int MutexTryLock(Mutex* mutex) {
|
||||||
if (TryEnterCriticalSection(mutex)) {
|
if (TryEnterCriticalSection(mutex)) {
|
||||||
return GetLastError();
|
return 0;
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -151,5 +151,5 @@ struct GUIIconMetric defaultIconMetrics[] = {
|
||||||
[GUI_ICON_BUTTON_CROSS] = { 18, 34, 12, 11 },
|
[GUI_ICON_BUTTON_CROSS] = { 18, 34, 12, 11 },
|
||||||
[GUI_ICON_BUTTON_TRIANGLE] = { 34, 34, 12, 11 },
|
[GUI_ICON_BUTTON_TRIANGLE] = { 34, 34, 12, 11 },
|
||||||
[GUI_ICON_BUTTON_SQUARE] = { 50, 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/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
#include "util/gui/font.h"
|
#include "util/gui/font.h"
|
||||||
|
|
||||||
|
#include "util/string.h"
|
||||||
|
|
||||||
unsigned GUIFontSpanWidth(const struct GUIFont* font, const char* text) {
|
unsigned GUIFontSpanWidth(const struct GUIFont* font, const char* text) {
|
||||||
unsigned width = 0;
|
unsigned width = 0;
|
||||||
size_t i;
|
size_t len = strlen(text);
|
||||||
for (i = 0; text[i]; ++i) {
|
while (len) {
|
||||||
char c = text[i];
|
uint32_t c = utf8Char(&text, &len);
|
||||||
width += GUIFontGlyphWidth(font, c);
|
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;
|
return width;
|
||||||
}
|
}
|
||||||
|
@ -29,8 +40,18 @@ void GUIFontPrint(const struct GUIFont* font, int x, int y, enum GUIAlignment al
|
||||||
size_t len = strlen(text);
|
size_t len = strlen(text);
|
||||||
while (len) {
|
while (len) {
|
||||||
uint32_t c = utf8Char(&text, &len);
|
uint32_t c = utf8Char(&text, &len);
|
||||||
GUIFontDrawGlyph(font, x, y, color, c);
|
if (c == '\1') {
|
||||||
x += GUIFontGlyphWidth(font, c);
|
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 GUIFontHeight(const struct GUIFont*);
|
||||||
unsigned GUIFontGlyphWidth(const struct GUIFont*, uint32_t glyph);
|
unsigned GUIFontGlyphWidth(const struct GUIFont*, uint32_t glyph);
|
||||||
unsigned GUIFontSpanWidth(const struct GUIFont*, const char* text);
|
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)
|
ATTRIBUTE_FORMAT(printf, 6, 7)
|
||||||
void GUIFontPrintf(const struct GUIFont*, int x, int y, enum GUIAlignment, uint32_t color, const char* text, ...);
|
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 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 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 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
|
#endif
|
||||||
|
|
|
@ -8,6 +8,10 @@
|
||||||
#include "util/gui.h"
|
#include "util/gui.h"
|
||||||
#include "util/gui/font.h"
|
#include "util/gui/font.h"
|
||||||
|
|
||||||
|
#ifdef _3DS
|
||||||
|
#include <3ds.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
DEFINE_VECTOR(GUIMenuItemList, struct GUIMenuItem);
|
DEFINE_VECTOR(GUIMenuItemList, struct GUIMenuItem);
|
||||||
|
|
||||||
enum GUIMenuExitReason GUIShowMenu(struct GUIParams* params, struct GUIMenu* menu, struct GUIMenuItem** item) {
|
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);
|
GUIInvalidateKeys(params);
|
||||||
while (true) {
|
while (true) {
|
||||||
|
#ifdef _3DS
|
||||||
|
if (!aptMainLoop()) {
|
||||||
|
return GUI_MENU_EXIT_CANCEL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
uint32_t newInput = 0;
|
uint32_t newInput = 0;
|
||||||
GUIPollInput(params, &newInput, 0);
|
GUIPollInput(params, &newInput, 0);
|
||||||
unsigned cx, cy;
|
unsigned cx, cy;
|
||||||
|
@ -88,7 +97,7 @@ enum GUIMenuExitReason GUIShowMenu(struct GUIParams* params, struct GUIMenu* men
|
||||||
++menu->index;
|
++menu->index;
|
||||||
} else if (cy <= params->height - lineHeight && cy > 2 * lineHeight) {
|
} else if (cy <= params->height - lineHeight && cy > 2 * lineHeight) {
|
||||||
size_t location = 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);
|
menu->index = location / (params->height - 3 * lineHeight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -140,10 +149,10 @@ enum GUIMenuExitReason GUIShowMenu(struct GUIParams* params, struct GUIMenu* men
|
||||||
int color = 0xE0A0A0A0;
|
int color = 0xE0A0A0A0;
|
||||||
if (i == menu->index) {
|
if (i == menu->index) {
|
||||||
color = 0xFFFFFFFF;
|
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);
|
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]) {
|
if (item->validStates && item->validStates[item->state]) {
|
||||||
GUIFontPrintf(params->font, params->width, y, GUI_ALIGN_RIGHT, color, "%s ", 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)) {
|
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;
|
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);
|
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);
|
return VFileFOpen(path, chflags);
|
||||||
#elif defined(PSP2)
|
#elif defined(PSP2)
|
||||||
int sceFlags = PSP2_O_RDONLY;
|
int sceFlags = SCE_O_RDONLY;
|
||||||
switch (flags & O_ACCMODE) {
|
switch (flags & O_ACCMODE) {
|
||||||
case O_WRONLY:
|
case O_WRONLY:
|
||||||
sceFlags = PSP2_O_WRONLY;
|
sceFlags = SCE_O_WRONLY;
|
||||||
break;
|
break;
|
||||||
case O_RDWR:
|
case O_RDWR:
|
||||||
sceFlags = PSP2_O_RDWR;
|
sceFlags = SCE_O_RDWR;
|
||||||
break;
|
break;
|
||||||
case O_RDONLY:
|
case O_RDONLY:
|
||||||
sceFlags = PSP2_O_RDONLY;
|
sceFlags = SCE_O_RDONLY;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flags & O_APPEND) {
|
if (flags & O_APPEND) {
|
||||||
sceFlags |= PSP2_O_APPEND;
|
sceFlags |= SCE_O_APPEND;
|
||||||
}
|
}
|
||||||
if (flags & O_TRUNC) {
|
if (flags & O_TRUNC) {
|
||||||
sceFlags |= PSP2_O_TRUNC;
|
sceFlags |= SCE_O_TRUNC;
|
||||||
}
|
}
|
||||||
if (flags & O_CREAT) {
|
if (flags & O_CREAT) {
|
||||||
sceFlags |= PSP2_O_CREAT;
|
sceFlags |= SCE_O_CREAT;
|
||||||
}
|
}
|
||||||
return VFileOpenSce(path, sceFlags, 0666);
|
return VFileOpenSce(path, sceFlags, 0666);
|
||||||
#elif defined(USE_VFS_3DS)
|
#elif defined(USE_VFS_3DS)
|
||||||
|
|
|
@ -59,6 +59,7 @@ struct VDir {
|
||||||
struct VDirEntry* (*listNext)(struct VDir* vd);
|
struct VDirEntry* (*listNext)(struct VDir* vd);
|
||||||
struct VFile* (*openFile)(struct VDir* vd, const char* name, int mode);
|
struct VFile* (*openFile)(struct VDir* vd, const char* name, int mode);
|
||||||
struct VDir* (*openDir)(struct VDir* vd, const char* name);
|
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);
|
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 VDirEntry* _vdlListNext(struct VDir* vd);
|
||||||
static struct VFile* _vdlOpenFile(struct VDir* vd, const char* path, int mode);
|
static struct VFile* _vdlOpenFile(struct VDir* vd, const char* path, int mode);
|
||||||
static struct VDir* _vdlOpenDir(struct VDir* vd, const char* path);
|
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 const char* _vdleName(struct VDirEntry* vde);
|
||||||
static enum VFSType _vdleType(struct VDirEntry* vde);
|
static enum VFSType _vdleType(struct VDirEntry* vde);
|
||||||
|
@ -38,6 +39,7 @@ struct VDir* VDeviceList() {
|
||||||
vd->d.listNext = _vdlListNext;
|
vd->d.listNext = _vdlListNext;
|
||||||
vd->d.openFile = _vdlOpenFile;
|
vd->d.openFile = _vdlOpenFile;
|
||||||
vd->d.openDir = _vdlOpenDir;
|
vd->d.openDir = _vdlOpenDir;
|
||||||
|
vd->d.deleteFile = _vdlDeleteFile;
|
||||||
|
|
||||||
vd->vde.d.name = _vdleName;
|
vd->vde.d.name = _vdleName;
|
||||||
vd->vde.d.type = _vdleType;
|
vd->vde.d.type = _vdleType;
|
||||||
|
@ -93,6 +95,12 @@ static struct VDir* _vdlOpenDir(struct VDir* vd, const char* path) {
|
||||||
return VDirOpen(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) {
|
static const char* _vdleName(struct VDirEntry* vde) {
|
||||||
struct VDirEntryDevList* vdle = (struct VDirEntryDevList*) vde;
|
struct VDirEntryDevList* vdle = (struct VDirEntryDevList*) vde;
|
||||||
return vdle->name;
|
return vdle->name;
|
||||||
|
|
|
@ -15,6 +15,7 @@ static void _vdRewind(struct VDir* vd);
|
||||||
static struct VDirEntry* _vdListNext(struct VDir* vd);
|
static struct VDirEntry* _vdListNext(struct VDir* vd);
|
||||||
static struct VFile* _vdOpenFile(struct VDir* vd, const char* path, int mode);
|
static struct VFile* _vdOpenFile(struct VDir* vd, const char* path, int mode);
|
||||||
static struct VDir* _vdOpenDir(struct VDir* vd, const char* path);
|
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 const char* _vdeName(struct VDirEntry* vde);
|
||||||
static enum VFSType _vdeType(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.listNext = _vdListNext;
|
||||||
vd->d.openFile = _vdOpenFile;
|
vd->d.openFile = _vdOpenFile;
|
||||||
vd->d.openDir = _vdOpenDir;
|
vd->d.openDir = _vdOpenDir;
|
||||||
|
vd->d.deleteFile = _vdDeleteFile;
|
||||||
vd->path = strdup(path);
|
vd->path = strdup(path);
|
||||||
vd->de = de;
|
vd->de = de;
|
||||||
|
|
||||||
|
@ -121,6 +123,20 @@ struct VDir* _vdOpenDir(struct VDir* vd, const char* path) {
|
||||||
return vd2;
|
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) {
|
const char* _vdeName(struct VDirEntry* vde) {
|
||||||
struct VDirEntryDE* vdede = (struct VDirEntryDE*) vde;
|
struct VDirEntryDE* vdede = (struct VDirEntryDE*) vde;
|
||||||
if (vdede->ent) {
|
if (vdede->ent) {
|
||||||
|
|
|
@ -63,6 +63,7 @@ static void _vd7zRewind(struct VDir* vd);
|
||||||
static struct VDirEntry* _vd7zListNext(struct VDir* vd);
|
static struct VDirEntry* _vd7zListNext(struct VDir* vd);
|
||||||
static struct VFile* _vd7zOpenFile(struct VDir* vd, const char* path, int mode);
|
static struct VFile* _vd7zOpenFile(struct VDir* vd, const char* path, int mode);
|
||||||
static struct VDir* _vd7zOpenDir(struct VDir* vd, const char* path);
|
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 const char* _vde7zName(struct VDirEntry* vde);
|
||||||
static enum VFSType _vde7zType(struct VDirEntry* vde);
|
static enum VFSType _vde7zType(struct VDirEntry* vde);
|
||||||
|
@ -102,7 +103,7 @@ struct VDir* VDirOpen7z(const char* path, int flags) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
vd->dirent.index = 0;
|
vd->dirent.index = -1;
|
||||||
vd->dirent.utf8 = 0;
|
vd->dirent.utf8 = 0;
|
||||||
vd->dirent.vd = vd;
|
vd->dirent.vd = vd;
|
||||||
vd->dirent.d.name = _vde7zName;
|
vd->dirent.d.name = _vde7zName;
|
||||||
|
@ -113,6 +114,7 @@ struct VDir* VDirOpen7z(const char* path, int flags) {
|
||||||
vd->d.listNext = _vd7zListNext;
|
vd->d.listNext = _vd7zListNext;
|
||||||
vd->d.openFile = _vd7zOpenFile;
|
vd->d.openFile = _vd7zOpenFile;
|
||||||
vd->d.openDir = _vd7zOpenDir;
|
vd->d.openDir = _vd7zOpenDir;
|
||||||
|
vd->d.deleteFile = _vd7zDeleteFile;
|
||||||
|
|
||||||
return &vd->d;
|
return &vd->d;
|
||||||
}
|
}
|
||||||
|
@ -309,6 +311,13 @@ struct VDir* _vd7zOpenDir(struct VDir* vd, const char* path) {
|
||||||
return 0;
|
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) {
|
bool _vf7zSync(struct VFile* vf, const void* memory, size_t size) {
|
||||||
UNUSED(vf);
|
UNUSED(vf);
|
||||||
UNUSED(memory);
|
UNUSED(memory);
|
||||||
|
|
|
@ -74,6 +74,7 @@ static void _vdzRewind(struct VDir* vd);
|
||||||
static struct VDirEntry* _vdzListNext(struct VDir* vd);
|
static struct VDirEntry* _vdzListNext(struct VDir* vd);
|
||||||
static struct VFile* _vdzOpenFile(struct VDir* vd, const char* path, int mode);
|
static struct VFile* _vdzOpenFile(struct VDir* vd, const char* path, int mode);
|
||||||
static struct VDir* _vdzOpenDir(struct VDir* vd, const char* path);
|
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 const char* _vdezName(struct VDirEntry* vde);
|
||||||
static enum VFSType _vdezType(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.listNext = _vdzListNext;
|
||||||
vd->d.openFile = _vdzOpenFile;
|
vd->d.openFile = _vdzOpenFile;
|
||||||
vd->d.openDir = _vdzOpenDir;
|
vd->d.openDir = _vdzOpenDir;
|
||||||
|
vd->d.deleteFile = _vdzDeleteFile;
|
||||||
vd->z = z;
|
vd->z = z;
|
||||||
|
|
||||||
#ifndef USE_LIBZIP
|
#ifndef USE_LIBZIP
|
||||||
|
@ -410,6 +412,13 @@ struct VDir* _vdzOpenDir(struct VDir* vd, const char* path) {
|
||||||
return 0;
|
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) {
|
bool _vfzSync(struct VFile* vf, const void* memory, size_t size) {
|
||||||
UNUSED(vf);
|
UNUSED(vf);
|
||||||
UNUSED(memory);
|
UNUSED(memory);
|
||||||
|
@ -624,6 +633,13 @@ struct VDir* _vdzOpenDir(struct VDir* vd, const char* path) {
|
||||||
return 0;
|
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) {
|
bool _vfzSync(struct VFile* vf, const void* memory, size_t size) {
|
||||||
UNUSED(vf);
|
UNUSED(vf);
|
||||||
UNUSED(memory);
|
UNUSED(memory);
|
||||||
|
|
|
@ -2,9 +2,9 @@ if(NOT PROJECT_NAME)
|
||||||
set(PROJECT_NAME "mGBA")
|
set(PROJECT_NAME "mGBA")
|
||||||
endif()
|
endif()
|
||||||
set(LIB_VERSION_MAJOR 0)
|
set(LIB_VERSION_MAJOR 0)
|
||||||
set(LIB_VERSION_MINOR 4)
|
set(LIB_VERSION_MINOR 5)
|
||||||
set(LIB_VERSION_PATCH 0)
|
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(LIB_VERSION_STRING ${LIB_VERSION_MAJOR}.${LIB_VERSION_MINOR}.${LIB_VERSION_PATCH})
|
||||||
set(SUMMARY "${PROJECT_NAME} Game Boy Advance Emulator")
|
set(SUMMARY "${PROJECT_NAME} Game Boy Advance Emulator")
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue