2024-08-05 18:42:58 +08:00
|
|
|
|
using AxibugEmuOnline.Client.ClientCore;
|
2024-07-30 11:57:09 +08:00
|
|
|
|
using System;
|
2024-09-23 18:15:34 +08:00
|
|
|
|
using System.Diagnostics;
|
2024-08-07 11:30:18 +08:00
|
|
|
|
using System.IO;
|
2024-08-05 18:42:58 +08:00
|
|
|
|
using System.Xml.Linq;
|
2024-07-24 14:27:10 +08:00
|
|
|
|
using UnityEngine;
|
|
|
|
|
using VirtualNes.Core;
|
|
|
|
|
using VirtualNes.Core.Debug;
|
|
|
|
|
|
|
|
|
|
namespace AxibugEmuOnline.Client
|
|
|
|
|
{
|
2024-09-23 18:15:34 +08:00
|
|
|
|
public class NesEmulator : MonoBehaviour, IEmuCore
|
2024-09-14 17:22:01 +08:00
|
|
|
|
{
|
2024-08-06 16:03:17 +08:00
|
|
|
|
public NES NesCore { get; private set; }
|
2024-07-25 11:03:58 +08:00
|
|
|
|
|
2024-07-30 11:57:09 +08:00
|
|
|
|
public VideoProvider VideoProvider;
|
2024-08-02 16:50:16 +08:00
|
|
|
|
public AudioProvider AudioProvider;
|
2024-09-14 17:22:01 +08:00
|
|
|
|
public bool m_bPause;
|
2024-07-30 11:57:09 +08:00
|
|
|
|
|
2024-07-24 14:27:10 +08:00
|
|
|
|
private void Start()
|
|
|
|
|
{
|
2024-08-02 10:58:04 +08:00
|
|
|
|
Application.targetFrameRate = 60;
|
2024-08-06 16:03:17 +08:00
|
|
|
|
VideoProvider.NesEmu = this;
|
|
|
|
|
AudioProvider.NesEmu = this;
|
2024-07-24 14:27:10 +08:00
|
|
|
|
}
|
|
|
|
|
|
2024-08-14 13:09:22 +08:00
|
|
|
|
public void StartGame(RomFile rom)
|
2024-07-24 14:27:10 +08:00
|
|
|
|
{
|
2024-07-25 11:03:58 +08:00
|
|
|
|
StopGame();
|
|
|
|
|
|
2024-07-24 14:27:10 +08:00
|
|
|
|
Supporter.Setup(new CoreSupporter());
|
|
|
|
|
Debuger.Setup(new CoreDebuger());
|
2024-07-30 11:57:09 +08:00
|
|
|
|
|
2024-11-08 13:48:53 +08:00
|
|
|
|
App.nesRomLib.AddRomFile(rom);
|
|
|
|
|
|
2024-07-30 11:57:09 +08:00
|
|
|
|
try
|
|
|
|
|
{
|
2024-08-14 13:09:22 +08:00
|
|
|
|
NesCore = new NES(rom.FileName);
|
2024-07-30 11:57:09 +08:00
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
2024-08-06 16:03:17 +08:00
|
|
|
|
NesCore = null;
|
2024-09-23 18:15:34 +08:00
|
|
|
|
App.log.Error(ex.ToString());
|
2024-07-30 11:57:09 +08:00
|
|
|
|
}
|
2024-07-25 11:03:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void StopGame()
|
|
|
|
|
{
|
2024-08-06 16:03:17 +08:00
|
|
|
|
NesCore?.Dispose();
|
|
|
|
|
NesCore = null;
|
2024-07-25 11:03:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void Update()
|
|
|
|
|
{
|
2024-09-14 17:22:01 +08:00
|
|
|
|
if (m_bPause) return;
|
|
|
|
|
|
2024-08-06 16:03:17 +08:00
|
|
|
|
if (NesCore != null)
|
2024-07-30 11:57:09 +08:00
|
|
|
|
{
|
2024-11-11 20:09:54 +08:00
|
|
|
|
PushEmulatorFrame();
|
|
|
|
|
if (InGameUI.Instance.IsNetPlay)
|
|
|
|
|
FixEmulatorFrame();
|
2024-07-30 18:53:36 +08:00
|
|
|
|
|
2024-08-06 16:03:17 +08:00
|
|
|
|
var screenBuffer = NesCore.ppu.GetScreenPtr();
|
|
|
|
|
var lineColorMode = NesCore.ppu.GetLineColorMode();
|
2024-07-30 18:53:36 +08:00
|
|
|
|
VideoProvider.SetDrawData(screenBuffer, lineColorMode, 256, 240);
|
2024-07-30 11:57:09 +08:00
|
|
|
|
}
|
2024-07-24 14:27:10 +08:00
|
|
|
|
}
|
2024-08-05 18:42:58 +08:00
|
|
|
|
|
2024-11-11 20:09:54 +08:00
|
|
|
|
private void FixEmulatorFrame()
|
|
|
|
|
{
|
|
|
|
|
int skipFrameCount = 0;
|
|
|
|
|
var frameGap = App.roomMgr.netReplay.mDiffFrameCount;
|
|
|
|
|
if (frameGap > 10000) return;
|
|
|
|
|
|
2024-11-12 14:47:57 +08:00
|
|
|
|
if (frameGap <= 2) skipFrameCount = 0;
|
|
|
|
|
if (frameGap > 2 && frameGap < 6) skipFrameCount = 1 + 1;
|
|
|
|
|
else if (frameGap > 7 && frameGap < 12) skipFrameCount = 2 + 1;
|
|
|
|
|
else if (frameGap > 13 && frameGap < 20) skipFrameCount = 3 + 1;
|
2024-11-11 20:19:50 +08:00
|
|
|
|
else skipFrameCount = frameGap - 2;
|
2024-11-11 20:09:54 +08:00
|
|
|
|
|
|
|
|
|
if (skipFrameCount > 0) App.log.Debug($"SKIP FRAME : {skipFrameCount}");
|
|
|
|
|
for (int i = 0; i < skipFrameCount; i++)
|
|
|
|
|
{
|
|
|
|
|
if (!PushEmulatorFrame()) break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-12 09:58:30 +08:00
|
|
|
|
ControllerState lastState;
|
2024-11-11 20:09:54 +08:00
|
|
|
|
private bool PushEmulatorFrame()
|
|
|
|
|
{
|
2024-11-12 09:58:30 +08:00
|
|
|
|
Supporter.SampleInput(NesCore.FrameCount);
|
2024-11-11 20:09:54 +08:00
|
|
|
|
var controlState = Supporter.GetControllerState();
|
|
|
|
|
|
|
|
|
|
//<2F><><EFBFBD><EFBFBD>δ<EFBFBD>յ<EFBFBD>Input<75><74><EFBFBD><EFBFBD>,<2C><><EFBFBD><EFBFBD>֡<EFBFBD><D6A1><EFBFBD>ƽ<EFBFBD>
|
|
|
|
|
if (!controlState.valid) return false;
|
|
|
|
|
|
2024-11-12 12:49:58 +08:00
|
|
|
|
#if UNITY_EDITOR
|
2024-11-12 09:58:30 +08:00
|
|
|
|
if (controlState != lastState)
|
|
|
|
|
{
|
|
|
|
|
App.log.Info($"[LOCALDEBUG]{NesCore.FrameCount}-->{controlState}");
|
|
|
|
|
}
|
2024-11-12 12:49:58 +08:00
|
|
|
|
#endif
|
2024-11-12 09:58:30 +08:00
|
|
|
|
|
2024-11-11 20:09:54 +08:00
|
|
|
|
NesCore.pad.Sync(controlState);
|
|
|
|
|
NesCore.EmulateFrame(true);
|
|
|
|
|
|
2024-11-12 09:58:30 +08:00
|
|
|
|
lastState = controlState;
|
|
|
|
|
|
2024-11-11 20:09:54 +08:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-14 17:22:01 +08:00
|
|
|
|
public void Pause()
|
|
|
|
|
{
|
|
|
|
|
m_bPause = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Resume()
|
|
|
|
|
{
|
|
|
|
|
m_bPause = false;
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-23 18:15:34 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[Conditional("UNITY_EDITOR")]
|
2024-08-29 18:31:36 +08:00
|
|
|
|
[ContextMenu("ImportNesDB")]
|
|
|
|
|
public void ImportNesDB()
|
2024-08-05 18:42:58 +08:00
|
|
|
|
{
|
|
|
|
|
var db = Resources.Load<RomDB>("NES/ROMDB");
|
|
|
|
|
db.Clear();
|
|
|
|
|
|
2024-08-07 11:30:18 +08:00
|
|
|
|
var xmlStr = File.ReadAllText("nes20db.xml");
|
|
|
|
|
var xml = XDocument.Parse(xmlStr);
|
2024-08-05 18:42:58 +08:00
|
|
|
|
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}");
|
|
|
|
|
|
2024-08-07 11:30:18 +08:00
|
|
|
|
if (mapper > 255) continue;
|
2024-08-05 18:42:58 +08:00
|
|
|
|
db.AddInfo(new RomDB.RomInfo { CRC = crc, Mapper = mapper });
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-07 11:30:18 +08:00
|
|
|
|
UnityEditor.EditorUtility.SetDirty(db);
|
2024-08-05 18:42:58 +08:00
|
|
|
|
UnityEditor.AssetDatabase.SaveAssets();
|
|
|
|
|
}
|
2024-09-23 18:15:34 +08:00
|
|
|
|
|
|
|
|
|
public void SetupScheme()
|
|
|
|
|
{
|
|
|
|
|
ControlScheme.Current = ControlSchemeSetts.NES;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
2024-07-24 14:27:10 +08:00
|
|
|
|
}
|
|
|
|
|
}
|