using AxibugEmuOnline.Client.ClientCore; using System; using System.Diagnostics; using System.IO; using System.Xml.Linq; using UnityEngine; using VirtualNes.Core; using VirtualNes.Core.Debug; namespace AxibugEmuOnline.Client { public class NesEmulator : MonoBehaviour, IEmuCore { public EnumPlatform Platform => EnumPlatform.NES; //模拟器核心实例化对象 public NES NesCore { get; private set; } //视频驱动(这里是Unity接收模拟器画面数据的并渲染出来的实现) public VideoProvider VideoProvider; //音频驱动(这里是Unity接收模拟器音频数据的并播放出来的实现) public AudioProvider AudioProvider; //是否暂停 private bool m_bPause; /// 是否暂停 public bool IsPause => m_bPause; private void Start() { //关闭垂直同步 QualitySettings.vSyncCount = 0; //设为60帧 Application.targetFrameRate = 60; VideoProvider.NesEmu = this; AudioProvider.NesEmu = this; } /// /// 指定ROM开始游戏 /// /// public void StartGame(RomFile rom) { StopGame(); Supporter.Setup(new CoreSupporter()); Debuger.Setup(new CoreDebuger()); App.nesRomLib.AddRomFile(rom); try { NesCore = new NES(rom.FileName); } catch (Exception ex) { NesCore = null; App.log.Error(ex.ToString()); } } /// /// 停止游戏 /// public void StopGame() { NesCore?.Dispose(); NesCore = null; } /// /// Unity的逐帧驱动 /// private unsafe void Update() { if (m_bPause) return; if (NesCore != null) { PushEmulatorFrame(); if (InGameUI.Instance.IsNetPlay) FixEmulatorFrame(); var screenBuffer = NesCore.ppu.GetScreenPtr(); VideoProvider.SetDrawData(screenBuffer); } } //是否跳帧,单机无效 private void FixEmulatorFrame() { var skipFrameCount = App.roomMgr.netReplay.GetSkipFrameCount(); if (skipFrameCount > 0) App.log.Debug($"SKIP FRAME : {skipFrameCount}"); for (int i = 0; i < skipFrameCount; i++) { if (!PushEmulatorFrame()) break; } } ControllerState lastState; //推进帧 private bool PushEmulatorFrame() { Supporter.SampleInput(NesCore.FrameCount); var controlState = Supporter.GetControllerState(); //如果未收到Input数据,核心帧不推进 if (!controlState.valid) return false; #if UNITY_EDITOR if (controlState != lastState) { App.log.Info($"[LOCALDEBUG]{NesCore.FrameCount}-->{controlState}"); } #endif NesCore.pad.Sync(controlState); NesCore.EmulateFrame(true); lastState = controlState; return true; } public void Pause() { m_bPause = true; } public void Resume() { m_bPause = false; } public void DoReset() { NesCore.Reset(); } public void SetupScheme() { CommandDispatcher.Instance.Current = CommandDispatcher.Instance.Gaming; } public void LoadState(object state) { NesCore.LoadState((State)state); } public object GetState() { return NesCore.GetState(); } /// /// 获取即时存档 /// /// public byte[] GetStateBytes() { return NesCore.GetState().ToBytes(); } /// /// 加载即时存档 /// /// public void LoadStateFromBytes(byte[] data) { State st = new State(); st.FromByte(data); NesCore.LoadState(st); } public uint Frame => NesCore.FrameCount; #if UNITY_EDITOR /// /// 编辑器用 /// [Conditional("UNITY_EDITOR")] [ContextMenu("ImportNesDB")] public void ImportNesDB() { var db = Resources.Load("NES/ROMDB"); db.Clear(); var xmlStr = File.ReadAllText("nes20db.xml"); var xml = XDocument.Parse(xmlStr); var games = xml.Element("nes20db").Elements("game"); foreach (var game in games) { var crcStr = game.Element("rom").Attribute("crc32").Value; var crc = uint.Parse($"{crcStr}", System.Globalization.NumberStyles.HexNumber); var mapper = int.Parse($"{game.Element("pcb").Attribute("mapper").Value}"); if (mapper > 255) continue; db.AddInfo(new RomDB.RomInfo { CRC = crc, Mapper = mapper }); } UnityEditor.EditorUtility.SetDirty(db); UnityEditor.AssetDatabase.SaveAssets(); } #endif } }