Camhack support (#1725)
for the camhack to work we have to save a state, hack memory, advance twice to see the changes, then load the state to prevent desync. since we can omit the framebuffer in savestates, loading them can happen without updating the screen, so the hacked camera remains visible. advancing 2 frames automatically is done like tastudio does it when it seeks to a frame, only from lua now. and the most questionable part is "invisible emulation", which is how Gens calls this IIRC, when everything that can distract or slow us down is skipped: sound, video, tools updates. new lua functions: - client.invisibleemulation() - client.seekframe() * for a test, mGBA core uses fake video and audio buffers and renders to them when we want to "skip" rendering. proper setup would involve actually skipping rendering those inside the core. * allow disabling video and audio updates for gpgx too (proper approach, no fake buffers involved) * add the script for Sonic Advance
This commit is contained in:
parent
825f10d52c
commit
5ca08b6c29
|
@ -0,0 +1,32 @@
|
|||
-- feos, 2019
|
||||
|
||||
local offX, offY, camX, camY
|
||||
local addr_offX = 0x5B96
|
||||
local addr_offY = 0x5B98
|
||||
local addr_camX = 0x59D0
|
||||
local addr_camY = 0x59D2
|
||||
|
||||
while true do
|
||||
client.invisibleemulation(true)
|
||||
local memorystate = memorysavestate.savecorestate()
|
||||
|
||||
offX = mainmemory.read_u16_le(addr_offX)
|
||||
offY = mainmemory.read_u16_le(addr_offY)
|
||||
camX = mainmemory.read_u16_le(addr_camX)
|
||||
camY = mainmemory.read_u16_le(addr_camY)
|
||||
|
||||
Xval = camX + offX - 128
|
||||
Yval = camY + offY - 80
|
||||
|
||||
mainmemory.write_u16_le(addr_camX, Xval)
|
||||
mainmemory.write_u16_le(addr_camY, Yval)
|
||||
|
||||
client.seekframe(emu.framecount()+1)
|
||||
client.invisibleemulation(false)
|
||||
client.seekframe(emu.framecount()+1)
|
||||
client.invisibleemulation(true)
|
||||
memorysavestate.loadcorestate(memorystate)
|
||||
memorysavestate.removestate(memorystate)
|
||||
-- client.invisibleemulation(false)
|
||||
emu.frameadvance()
|
||||
end
|
|
@ -571,6 +571,28 @@ namespace BizHawk.Client.EmuHawk
|
|||
public bool PressRewind { get; set; } // necessary for tastudio < button
|
||||
public bool FastForward { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Disables updates for video/audio, and enters "turbo" mode.
|
||||
/// Can be used to replicate Gens-rr's "latency compensation" that involves:
|
||||
/// <list type="bullet">
|
||||
/// <item><description>Saving a no-framebuffer state that is stored in RAM</description></item>
|
||||
/// <item><description>Emulating forth for some frames with updates disabled</description></item>
|
||||
/// <item><list type="bullet">
|
||||
/// <item><description>Optionally hacking in-game memory
|
||||
/// (like camera position, to show off-screen areas)</description></item>
|
||||
/// </list></item>
|
||||
/// <item><description>Updating the screen</description></item>
|
||||
/// <item><description>Loading the no-framebuffer state from RAM</description></item>
|
||||
/// </list>
|
||||
/// The most common usecase is CamHack for Sonic games.
|
||||
/// Accessing this from Lua allows to keep internal code hacks to minimum.
|
||||
/// <list type="bullet">
|
||||
/// <item><description><see cref="EmuHawkLuaLibrary.InvisibleEmulation(bool)"/></description></item>
|
||||
/// <item><description><see cref="EmuHawkLuaLibrary.SeekFrame(int)"/></description></item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
public bool InvisibleEmulation { get; set; }
|
||||
|
||||
// runloop won't exec lua
|
||||
public bool SuppressLua { get; set; }
|
||||
|
||||
|
@ -2809,7 +2831,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
|
||||
if (runFrame || force)
|
||||
{
|
||||
var isFastForwarding = Global.ClientControls["Fast Forward"] || IsTurboing;
|
||||
var isFastForwarding = Global.ClientControls["Fast Forward"] || IsTurboing || InvisibleEmulation;
|
||||
var isFastForwardingOrRewinding = isFastForwarding || isRewinding || _unthrottled;
|
||||
|
||||
if (isFastForwardingOrRewinding != _lastFastForwardingOrRewinding)
|
||||
|
@ -2842,10 +2864,13 @@ namespace BizHawk.Client.EmuHawk
|
|||
GlobalWin.Tools.UpdateToolsBefore();
|
||||
}
|
||||
|
||||
CaptureRewind(isRewinding);
|
||||
if (!InvisibleEmulation)
|
||||
{
|
||||
CaptureRewind(isRewinding);
|
||||
}
|
||||
|
||||
// Set volume, if enabled
|
||||
if (Global.Config.SoundEnabledNormal)
|
||||
if (Global.Config.SoundEnabledNormal && !InvisibleEmulation)
|
||||
{
|
||||
atten = Global.Config.SoundVolume / 100.0f;
|
||||
|
||||
|
@ -2878,13 +2903,14 @@ namespace BizHawk.Client.EmuHawk
|
|||
}
|
||||
}
|
||||
// why not skip audio if the user doesn't want sound
|
||||
bool renderSound = (Global.Config.SoundEnabled && !IsTurboing) || (_currAviWriter?.UsesAudio ?? false);
|
||||
bool renderSound = (Global.Config.SoundEnabled && !IsTurboing)
|
||||
|| (_currAviWriter?.UsesAudio ?? false);
|
||||
if (!renderSound)
|
||||
{
|
||||
atten = 0;
|
||||
}
|
||||
|
||||
bool render = !_throttle.skipNextFrame || (_currAviWriter?.UsesVideo ?? false);
|
||||
bool render = !InvisibleEmulation && (!_throttle.skipNextFrame || (_currAviWriter?.UsesVideo ?? false));
|
||||
bool new_frame = Emulator.FrameAdvance(Global.ControllerOutput, render, renderSound);
|
||||
|
||||
Global.MovieSession.HandleMovieAfterFrameLoop();
|
||||
|
@ -2924,7 +2950,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
UpdateToolsAfter(SuppressLua);
|
||||
}
|
||||
|
||||
if (!PauseAvi && new_frame)
|
||||
if (!PauseAvi && new_frame && !InvisibleEmulation)
|
||||
{
|
||||
AvFrameAdvance();
|
||||
}
|
||||
|
|
|
@ -120,6 +120,42 @@ namespace BizHawk.Client.EmuHawk
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use with <see cref="SeekFrame(int)"/> for CamHack.
|
||||
/// Refer to <see cref="MainForm.InvisibleEmulation"/> for the workflow details.
|
||||
/// </summary>
|
||||
[LuaMethodExample("client.invisibleemulation( true );")]
|
||||
[LuaMethod("invisibleemulation", "Disables and enables emulator updates")]
|
||||
public void InvisibleEmulation(bool invisible)
|
||||
{
|
||||
GlobalWin.MainForm.InvisibleEmulation = invisible;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use with <see cref="InvisibleEmulation(bool)"/> for CamHack.
|
||||
/// Refer to <see cref="MainForm.InvisibleEmulation"/> for the workflow details.
|
||||
/// </summary>
|
||||
[LuaMethodExample("client.seekframe( 100 );")]
|
||||
[LuaMethod("seekframe", "Makes the emulator seek to the frame specified")]
|
||||
public void SeekFrame(int frame)
|
||||
{
|
||||
bool wasPaused = GlobalWin.MainForm.EmulatorPaused;
|
||||
|
||||
// can't re-enter lua while doing this
|
||||
GlobalWin.MainForm.SuppressLua = true;
|
||||
while (Emulator.Frame != frame)
|
||||
{
|
||||
GlobalWin.MainForm.SeekFrameAdvance();
|
||||
}
|
||||
|
||||
GlobalWin.MainForm.SuppressLua = false;
|
||||
|
||||
if (!wasPaused)
|
||||
{
|
||||
GlobalWin.MainForm.UnpauseEmulator();
|
||||
}
|
||||
}
|
||||
|
||||
[LuaMethodExample("local incliget = client.gettargetscanlineintensity( );")]
|
||||
[LuaMethod("gettargetscanlineintensity", "Gets the current scanline intensity setting, used for the scanline display filter")]
|
||||
public static int GetTargetScanlineIntensity()
|
||||
|
|
|
@ -6,6 +6,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA
|
|||
public partial class MGBAHawk : ISoundProvider
|
||||
{
|
||||
private readonly short[] _soundbuff = new short[2048];
|
||||
private readonly short[] _dummysoundbuff = new short[2048];
|
||||
private int _nsamp;
|
||||
|
||||
public bool CanProvideAsync => false;
|
||||
|
|
|
@ -23,5 +23,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA
|
|||
public int VsyncDenominator => 4389;
|
||||
|
||||
private readonly int[] _videobuff = new int[240 * 160];
|
||||
|
||||
private readonly int[] _dummyvideobuff = new int[240 * 160];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -81,9 +81,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA
|
|||
IsLagFrame = LibmGBA.BizAdvance(
|
||||
_core,
|
||||
VBANext.GetButtons(controller),
|
||||
_videobuff,
|
||||
render ? _videobuff : _dummyvideobuff,
|
||||
ref _nsamp,
|
||||
_soundbuff,
|
||||
rendersound ? _soundbuff : _dummysoundbuff,
|
||||
RTCTime(),
|
||||
(short)controller.GetFloat("Tilt X"),
|
||||
(short)controller.GetFloat("Tilt Y"),
|
||||
|
|
|
@ -58,8 +58,12 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
|
|||
_drivelight = false;
|
||||
|
||||
Core.gpgx_advance();
|
||||
UpdateVideo();
|
||||
update_audio();
|
||||
|
||||
if (render)
|
||||
UpdateVideo();
|
||||
|
||||
if (rendersound)
|
||||
update_audio();
|
||||
|
||||
if (IsLagFrame)
|
||||
LagCount++;
|
||||
|
|
Loading…
Reference in New Issue