using System; using System.Windows.Forms; using System.IO; using BizHawk.Client.ApiHawk; using BizHawk.Client.Common; using BizHawk.Emulation.Common; using DisplayType = BizHawk.Client.Common.DisplayType; namespace HelloWorld { /// All of this is example code, but it's at least a little more substantiative than a simple "hello world". [ExternalTool("HelloWorld", Description = "An example of how to interact with EmuHawk")] // [ExternalToolApplicability.SingleRom(CoreSystem.NES, "EA343F4E445A9050D4B4FBAC2C77D0693B1D0922")] // example of limiting tool usage (this is SMB1) [ExternalToolEmbeddedIcon("HelloWorld.icon_Hello.ico")] public partial class CustomMainForm : Form, IExternalToolForm { /// RequiredServices are populated by EmuHawk at runtime. [RequiredService] private IEmulator? _emu { get; set; } [RequiredService] private IMemoryDomains? _memoryDomains { get; set; } private WatchList? _watches; private WatchList Watches { get { WatchList CreateWatches() { var w = new WatchList(_memoryDomains, _emu?.SystemId ?? string.Empty); w.AddRange(new[] { Watch.GenerateWatch(_memoryDomains?.MainMemory, 0x40, WatchSize.Byte, DisplayType.Hex, true), Watch.GenerateWatch(_memoryDomains?.MainMemory, 0x50, WatchSize.Word, DisplayType.Unsigned, true), Watch.GenerateWatch(_memoryDomains?.MainMemory, 0x60, WatchSize.DWord, DisplayType.Hex, true) }); return w; } _watches ??= CreateWatches(); return _watches; } } public CustomMainForm() { InitializeComponent(); label_GameHash.Click += label_GameHash_Click; ClientApi.BeforeQuickSave += (sender, e) => { if (e.Slot != 0) return; // only take effect on slot 0 var basePath = Path.Combine(PathManager.GetSaveStatePath(Global.Game), "Test"); if (!Directory.Exists(basePath)) Directory.CreateDirectory(basePath); ClientApi.SaveState(Path.Combine(basePath, e.Name)); e.Handled = true; }; ClientApi.BeforeQuickLoad += (sender, e) => { if (e.Slot != 0) return; // only take effect on slot 0 var basePath = Path.Combine(PathManager.GetSaveStatePath(Global.Game), "Test"); ClientApi.LoadState(Path.Combine(basePath, e.Name)); e.Handled = true; }; } /// We want to be called before rendering. public bool UpdateBefore => true; public bool AskSaveChanges() => true; /// This is called instead of the usual when EmuHawk is turboing. public void FastUpdate() {} public void NewUpdate(ToolFormUpdateType type) {} /// This is called once when the form is opened, and every time a new movie session starts. public void Restart() { #if false ClientApi.SetExtraPadding(50, 50); #endif if (Global.Game.Name != "Null") { Watches.RefreshDomains(_memoryDomains); label_Game.Text = $"You're playing {Global.Game.Name}"; label_GameHash.Text = $"Hash: {Global.Game.Hash}"; } else { label_Game.Text = "You're playing... nothing"; label_GameHash.Text = string.Empty; } } /// Called just before every video frame. public void UpdateValues() { if (Global.Game.Name == "Null" || Watches.Count < 3) return; Watches.UpdateValues(); label_Watch1.Text = $"First watch ({Watches[0].AddressString}) current value: {Watches[0].ValueString}"; label_Watch2.Text = $"Second watch ({Watches[1].AddressString}) current value: {Watches[1].ValueString}"; label_Watch3.Text = $"Third watch ({Watches[2].AddressString}) current value: {Watches[2].ValueString}"; } private void button1_Click(object sender, EventArgs e) => ClientApi.DoFrameAdvance(); private void button2_Click(object sender, EventArgs e) => ClientApi.GetInput(1); private void button3_Click(object sender, EventArgs e) { for (var i = 0; i < 600; i++) { if (i % 60 == 0) { var j1 = ClientApi.GetInput(1); j1.AddInput(JoypadButton.A); ClientApi.SetInput(1, j1); ClientApi.DoFrameAdvance(); j1.RemoveInput(JoypadButton.A); ClientApi.SetInput(1, j1); ClientApi.DoFrameAdvance(); } ClientApi.DoFrameAdvance(); } var j = ClientApi.GetInput(1); j.ClearInputs(); ClientApi.SetInput(1, j); } private void label_GameHash_Click(object sender, EventArgs e) => Clipboard.SetText(Global.Game.Hash); private void loadstate_Click(object sender, EventArgs e) { if (string.IsNullOrWhiteSpace(savestateName.Text)) return; ClientApi.LoadState(savestateName.Text); #if false static void Test(BinaryReader r) { var b = new System.Drawing.Bitmap(r.BaseStream); } BinaryStateLoader.LoadAndDetect($"{savestateName.Text}.State").GetLump(BinaryStateLump.Framebuffer, false, Test); #endif } private void saveState_Click(object sender, EventArgs e) { if (!string.IsNullOrWhiteSpace(savestateName.Text)) ClientApi.SaveState(savestateName.Text); } } }