AxibugEmuOnline/AxibugEmuOnline.Client/Assets/Script/AppMain/NesEmulator/NesEmulator.cs
2024-12-19 20:36:04 +08:00

211 lines
5.4 KiB
C#

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; }
/// <summary> 是否暂停 </summary>
public bool IsPause { get; private set; }
private void Start()
{
//关闭垂直同步
QualitySettings.vSyncCount = 0;
//设为60帧
Application.targetFrameRate = 60;
VideoProvider.NesEmu = this;
AudioProvider.NesEmu = this;
}
/// <summary>
/// Unity的逐帧驱动
/// </summary>
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;
/// <summary>
/// 指定ROM开始游戏
/// </summary>
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();
}
/// <summary>
/// 获取即时存档
/// </summary>
/// <returns></returns>
public byte[] GetStateBytes()
{
return NesCore.GetState().ToBytes();
}
/// <summary>
/// 加载即时存档
/// </summary>
/// <param
/// name="data">
/// </param>
public void LoadStateFromBytes(byte[] data)
{
var st = new State();
st.FromByte(data);
NesCore.LoadState(st);
}
public uint Frame => NesCore.FrameCount;
/// <summary>
/// 停止游戏
/// </summary>
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
/// <summary>
/// 编辑器用
/// </summary>
[Conditional("UNITY_EDITOR")]
[ContextMenu("ImportNesDB")]
public void ImportNesDB()
{
var db = Resources.Load<RomDB>("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
}
}