using System; using System.Diagnostics; using System.Globalization; using System.IO; using System.Xml.Linq; using AxibugEmuOnline.Client.ClientCore; using UnityEditor; using UnityEngine; using VirtualNes.Core; using VirtualNes.Core.Debug; using Debug = System.Diagnostics.Debug; namespace AxibugEmuOnline.Client { public class NesEmulator : MonoBehaviour, IEmuCore { public VideoProvider VideoProvider; public AudioProvider AudioProvider; //模拟器核心实例化对象 public NES NesCore { get; private set; } /// 是否暂停 public bool IsPause { get; private set; } private void Start() { //关闭垂直同步 QualitySettings.vSyncCount = 0; //设为60帧 Application.targetFrameRate = 60; VideoProvider.NesEmu = this; AudioProvider.NesEmu = this; } /// /// Unity的逐帧驱动 /// private unsafe void Update() { if (IsPause) return; if (NesCore != null) { PushEmulatorFrame(); if (InGameUI.Instance.IsNetPlay) FixEmulatorFrame(); var screenBuffer = NesCore.ppu.GetScreenPtr(); VideoProvider.SetDrawData(screenBuffer); } } public EnumPlatform Platform => EnumPlatform.NES; /// /// 指定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 Pause() { IsPause = true; } public void Resume() { IsPause = 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) { var st = new State(); st.FromByte(data); NesCore.LoadState(st); } public uint Frame => NesCore.FrameCount; /// /// 停止游戏 /// public void StopGame() { NesCore?.Dispose(); NesCore = null; } #if UNITY_EDITOR private ControllerState m_lastState; #endif //是否跳帧,单机无效 private void FixEmulatorFrame() { var skipFrameCount = App.roomMgr.netReplay.GetSkipFrameCount(); if (skipFrameCount > 0) App.log.Debug($"SKIP FRAME : {skipFrameCount}"); for (var i = 0; i < skipFrameCount; i++) if (!PushEmulatorFrame()) break; } //推进帧 private bool PushEmulatorFrame() { Supporter.SampleInput(NesCore.FrameCount); var controlState = Supporter.GetControllerState(); //如果未收到Input数据,核心帧不推进 if (!controlState.valid) return false; #if UNITY_EDITOR if (controlState != m_lastState) App.log.Info($"[LOCALDEBUG]{NesCore.FrameCount}-->{controlState}"); m_lastState = controlState; #endif NesCore.pad.Sync(controlState); NesCore.EmulateFrame(true); return true; } #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"); Debug.Assert(games != null, nameof(games) + " != null"); foreach (var game in games) { var crcStr = game.Element("rom")?.Attribute("crc32")?.Value; var crc = uint.Parse($"{crcStr}", 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 }); } EditorUtility.SetDirty(db); AssetDatabase.SaveAssets(); } #endif } }