using System; using System.ComponentModel; using System.Linq; using NLua; using BizHawk.Emulation.Common; using BizHawk.Emulation.Cores.Nintendo.NES; using BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES; namespace BizHawk.Client.Common { [Description("Functions related specifically to Nes Cores")] public sealed class NesLuaLibrary : LuaLibraryBase { // TODO: // perhaps with the new core config system, one could // automatically bring out all of the settings to a lua table, with names. that // would be completely arbitrary and would remove the whole requirement for this mess public NesLuaLibrary(Lua lua) : base(lua) { } [OptionalService] private NES Neshawk { get; set; } [OptionalService] private QuickNES Quicknes { get; set; } [OptionalService] private IMemoryDomains MemoryDomains { get; set; } private bool NESAvailable => Neshawk != null || Quicknes != null; private bool HasMemoryDOmains => MemoryDomains != null; public NesLuaLibrary(Lua lua, Action logOutputCallback) : base(lua, logOutputCallback) { } public override string Name => "nes"; [LuaMethodExample("nes.addgamegenie( \"GXXZZLVI\" );")] [LuaMethod("addgamegenie", "Adds the specified game genie code. If an NES game is not currently loaded or the code is not a valid game genie code, this will have no effect")] public void AddGameGenie(string code) { if (NESAvailable && HasMemoryDOmains) { var decoder = new NESGameGenieDecoder(code); var watch = Watch.GenerateWatch( MemoryDomains["System Bus"], decoder.Address, WatchSize.Byte, DisplayType.Hex, false, code); Global.CheatList.Add(new Cheat( watch, decoder.Value, decoder.Compare)); } } [LuaMethodExample("if ( nes.getallowmorethaneightsprites( ) ) then\r\n\tconsole.log( \"Gets the NES setting 'Allow more than 8 sprites per scanline' value\" );\r\nend;")] [LuaMethod("getallowmorethaneightsprites", "Gets the NES setting 'Allow more than 8 sprites per scanline' value")] public bool GetAllowMoreThanEightSprites() { if (Quicknes != null) { return Quicknes.GetSettings().NumSprites != 8; } if (Neshawk != null) { return Neshawk.GetSettings().AllowMoreThanEightSprites; } throw new InvalidOperationException(); } [LuaMethodExample("local innesget = nes.getbottomscanline( false );")] [LuaMethod("getbottomscanline", "Gets the current value for the bottom scanline value")] public int GetBottomScanline(bool pal = false) { if (Quicknes != null) { return Quicknes.GetSettings().ClipTopAndBottom ? 231 : 239; } if (Neshawk != null) { return pal ? Neshawk.GetSettings().PAL_BottomLine : Neshawk.GetSettings().NTSC_BottomLine; } throw new InvalidOperationException(); } [LuaMethodExample("if ( nes.getclipleftandright( ) ) then\r\n\tconsole.log( \"Gets the current value for the Clip Left and Right sides option\" );\r\nend;")] [LuaMethod("getclipleftandright", "Gets the current value for the Clip Left and Right sides option")] public bool GetClipLeftAndRight() { if (Quicknes != null) { return Quicknes.GetSettings().ClipLeftAndRight; } if (Neshawk != null) { return Neshawk.GetSettings().ClipLeftAndRight; } throw new InvalidOperationException(); } [LuaMethodExample("if ( nes.getdispbackground( ) ) then\r\n\tconsole.log( \"Indicates whether or not the bg layer is being displayed\" );\r\nend;")] [LuaMethod("getdispbackground", "Indicates whether or not the bg layer is being displayed")] public bool GetDisplayBackground() { if (Quicknes != null) { return true; } if (Neshawk != null) { return Neshawk.GetSettings().DispBackground; } throw new InvalidOperationException(); } [LuaMethodExample("if ( nes.getdispsprites( ) ) then\r\n\tconsole.log( \"Indicates whether or not sprites are being displayed\" );\r\nend;")] [LuaMethod("getdispsprites", "Indicates whether or not sprites are being displayed")] public bool GetDisplaySprites() { if (Quicknes != null) { return Quicknes.GetSettings().NumSprites > 0; } if (Neshawk != null) { return Neshawk.GetSettings().DispSprites; } throw new InvalidOperationException(); } [LuaMethodExample("local innesget = nes.gettopscanline(false);")] [LuaMethod("gettopscanline", "Gets the current value for the top scanline value")] public int GetTopScanline(bool pal = false) { if (Quicknes != null) { return Quicknes.GetSettings().ClipTopAndBottom ? 8 : 0; } if (Neshawk != null) { return pal ? Neshawk.GetSettings().PAL_TopLine : Neshawk.GetSettings().NTSC_TopLine; } throw new InvalidOperationException(); } [LuaMethodExample("nes.removegamegenie( \"GXXZZLVI\" );")] [LuaMethod("removegamegenie", "Removes the specified game genie code. If an NES game is not currently loaded or the code is not a valid game genie code, this will have no effect")] public void RemoveGameGenie(string code) { if (NESAvailable) { var decoder = new NESGameGenieDecoder(code); Global.CheatList.RemoveRange( Global.CheatList.Where(c => c.Address == decoder.Address)); } } [LuaMethodExample("nes.setallowmorethaneightsprites( true );")] [LuaMethod("setallowmorethaneightsprites", "Sets the NES setting 'Allow more than 8 sprites per scanline'")] public void SetAllowMoreThanEightSprites(bool allow) { if (Neshawk != null) { var s = Neshawk.GetSettings(); s.AllowMoreThanEightSprites = allow; Neshawk.PutSettings(s); } else if (Quicknes != null) { var s = Quicknes.GetSettings(); s.NumSprites = allow ? 64 : 8; Quicknes.PutSettings(s); } } [LuaMethodExample("nes.setclipleftandright( true );")] [LuaMethod("setclipleftandright", "Sets the Clip Left and Right sides option")] public void SetClipLeftAndRight(bool leftandright) { if (Neshawk != null) { var s = Neshawk.GetSettings(); s.ClipLeftAndRight = leftandright; Neshawk.PutSettings(s); } else if (Quicknes != null) { var s = Quicknes.GetSettings(); s.ClipLeftAndRight = leftandright; Quicknes.PutSettings(s); } } [LuaMethodExample("nes.setdispbackground( true );")] [LuaMethod("setdispbackground", "Sets whether or not the background layer will be displayed")] public void SetDisplayBackground(bool show) { if (Neshawk != null) { var s = Neshawk.GetSettings(); s.DispBackground = show; Neshawk.PutSettings(s); } } [LuaMethodExample("nes.setdispsprites( true );")] [LuaMethod("setdispsprites", "Sets whether or not sprites will be displayed")] public void SetDisplaySprites(bool show) { if (Neshawk != null) { var s = Neshawk.GetSettings(); s.DispSprites = show; Neshawk.PutSettings(s); } else if (Quicknes != null) { var s = Quicknes.GetSettings(); s.NumSprites = show ? 8 : 0; Quicknes.PutSettings(s); } } [LuaMethodExample("nes.setscanlines( 10, 20, false );")] [LuaMethod("setscanlines", "sets the top and bottom scanlines to be drawn (same values as in the graphics options dialog). Top must be in the range of 0 to 127, bottom must be between 128 and 239. Not supported in the Quick Nes core")] public void SetScanlines(int top, int bottom, bool pal = false) { if (Neshawk != null) { if (top > 127) { top = 127; } else if (top < 0) { top = 0; } if (bottom > 239) { bottom = 239; } else if (bottom < 128) { bottom = 128; } var s = Neshawk.GetSettings(); if (pal) { s.PAL_TopLine = top; s.PAL_BottomLine = bottom; } else { s.NTSC_TopLine = top; s.NTSC_BottomLine = bottom; } Neshawk.PutSettings(s); } } } }