From 580de2d245cbc46505098f0ed9f93e7f35372f64 Mon Sep 17 00:00:00 2001 From: "ALIENJACK\\alien" Date: Tue, 6 Aug 2024 18:09:32 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E8=BE=93=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Resources/NES/ControlSettings.asset | 126 ++++++++++++++++++ .../Resources/NES/ControlSettings.asset.meta | 8 ++ .../Script/NesEmulator/AudioProvider.cs | 2 - .../Script/NesEmulator/CoreSupporter.cs | 6 + .../Script/NesEmulator/NesControllerMapper.cs | 84 ++++++++++++ .../NesEmulator/NesControllerMapper.cs.meta | 11 ++ .../Assets/Script/NesEmulator/NesEmulator.cs | 2 + .../Assets/VirtualNes.Core/PAD.cs | 114 ++++++++++++++++ .../Supporter/ControllerState.cs | 51 +++++++ .../Supporter/ControllerState.cs.meta | 11 ++ .../Supporter/EmulatorConfig/CfgController.cs | 23 +++- .../EmulatorConfig/EmulatorConfig.cs | 4 +- .../VirtualNes.Core/Supporter/Supporter.cs | 6 + References/virtuanessrc097-master/NES/PAD.cpp | Bin 36724 -> 36272 bytes 14 files changed, 444 insertions(+), 4 deletions(-) create mode 100644 AxibugEmuOnline.Client/Assets/Resources/NES/ControlSettings.asset create mode 100644 AxibugEmuOnline.Client/Assets/Resources/NES/ControlSettings.asset.meta create mode 100644 AxibugEmuOnline.Client/Assets/Script/NesEmulator/NesControllerMapper.cs create mode 100644 AxibugEmuOnline.Client/Assets/Script/NesEmulator/NesControllerMapper.cs.meta create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/ControllerState.cs create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/ControllerState.cs.meta diff --git a/AxibugEmuOnline.Client/Assets/Resources/NES/ControlSettings.asset b/AxibugEmuOnline.Client/Assets/Resources/NES/ControlSettings.asset new file mode 100644 index 0000000..d2b65d7 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Resources/NES/ControlSettings.asset @@ -0,0 +1,126 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 66fc8233a79cd254f8d005452dcd4ac0, type: 3} + m_Name: ControlSettings + m_EditorClassIdentifier: + Player1: + UP: + buttonType: 1 + keyCode: 119 + DOWN: + buttonType: 2 + keyCode: 115 + LEFT: + buttonType: 4 + keyCode: 97 + RIGHT: + buttonType: 8 + keyCode: 100 + A: + buttonType: 16 + keyCode: 107 + B: + buttonType: 32 + keyCode: 106 + SELECT: + buttonType: 64 + keyCode: 118 + START: + buttonType: 128 + keyCode: 98 + MIC: + buttonType: 256 + keyCode: 0 + Player2: + UP: + buttonType: 1 + keyCode: 0 + DOWN: + buttonType: 2 + keyCode: 0 + LEFT: + buttonType: 4 + keyCode: 0 + RIGHT: + buttonType: 8 + keyCode: 0 + A: + buttonType: 16 + keyCode: 0 + B: + buttonType: 32 + keyCode: 0 + SELECT: + buttonType: 64 + keyCode: 0 + START: + buttonType: 128 + keyCode: 0 + MIC: + buttonType: 256 + keyCode: 0 + Player3: + UP: + buttonType: 1 + keyCode: 0 + DOWN: + buttonType: 2 + keyCode: 0 + LEFT: + buttonType: 4 + keyCode: 0 + RIGHT: + buttonType: 8 + keyCode: 0 + A: + buttonType: 16 + keyCode: 0 + B: + buttonType: 32 + keyCode: 0 + SELECT: + buttonType: 64 + keyCode: 0 + START: + buttonType: 128 + keyCode: 0 + MIC: + buttonType: 256 + keyCode: 0 + Player4: + UP: + buttonType: 1 + keyCode: 0 + DOWN: + buttonType: 2 + keyCode: 0 + LEFT: + buttonType: 4 + keyCode: 0 + RIGHT: + buttonType: 8 + keyCode: 0 + A: + buttonType: 16 + keyCode: 0 + B: + buttonType: 32 + keyCode: 0 + SELECT: + buttonType: 64 + keyCode: 0 + START: + buttonType: 128 + keyCode: 0 + MIC: + buttonType: 256 + keyCode: 0 diff --git a/AxibugEmuOnline.Client/Assets/Resources/NES/ControlSettings.asset.meta b/AxibugEmuOnline.Client/Assets/Resources/NES/ControlSettings.asset.meta new file mode 100644 index 0000000..60582ad --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Resources/NES/ControlSettings.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: fa3a6bfd9566da84eb494ff280abe34c +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Script/NesEmulator/AudioProvider.cs b/AxibugEmuOnline.Client/Assets/Script/NesEmulator/AudioProvider.cs index f014cf5..f4335b7 100644 --- a/AxibugEmuOnline.Client/Assets/Script/NesEmulator/AudioProvider.cs +++ b/AxibugEmuOnline.Client/Assets/Script/NesEmulator/AudioProvider.cs @@ -3,7 +3,6 @@ using VirtualNes.Core; namespace AxibugEmuOnline.Client { - public class AudioProvider : MonoBehaviour { public NesEmulator NesEmu; @@ -40,7 +39,6 @@ namespace AxibugEmuOnline.Client data[i] = rawFloat; for (int fill = 1; fill < step; fill++) data[i + fill] = rawFloat; - } } diff --git a/AxibugEmuOnline.Client/Assets/Script/NesEmulator/CoreSupporter.cs b/AxibugEmuOnline.Client/Assets/Script/NesEmulator/CoreSupporter.cs index ae72035..68e7e8c 100644 --- a/AxibugEmuOnline.Client/Assets/Script/NesEmulator/CoreSupporter.cs +++ b/AxibugEmuOnline.Client/Assets/Script/NesEmulator/CoreSupporter.cs @@ -98,5 +98,11 @@ namespace AxibugEmuOnline.Client var db = Resources.Load("NES/ROMDB"); return db.GetMapperNo(rom.GetPROM_CRC(), out mapperNo); } + + public ControllerState GetControllerState() + { + var mapper = Resources.Load("NES/ControlSettings"); + return mapper.CreateState(); + } } } diff --git a/AxibugEmuOnline.Client/Assets/Script/NesEmulator/NesControllerMapper.cs b/AxibugEmuOnline.Client/Assets/Script/NesEmulator/NesControllerMapper.cs new file mode 100644 index 0000000..82ebec0 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Script/NesEmulator/NesControllerMapper.cs @@ -0,0 +1,84 @@ +using System; +using UnityEngine; +using VirtualNes.Core; + +namespace AxibugEmuOnline.Client +{ + public class NesControllerMapper : ScriptableObject + { + public MapperSetter Player1 = new MapperSetter(); + public MapperSetter Player2 = new MapperSetter(); + public MapperSetter Player3 = new MapperSetter(); + public MapperSetter Player4 = new MapperSetter(); + + public ControllerState CreateState() + { + var state1 = Player1.GetButtons(); + var state2 = Player2.GetButtons(); + var state3 = Player3.GetButtons(); + var state4 = Player4.GetButtons(); + + return new ControllerState(state1, state2, state3, state4); + } + + [Serializable] + public class Mapper + { + public EnumButtonType buttonType; + public KeyCode keyCode; + + public Mapper(EnumButtonType buttonType) + { + this.buttonType = buttonType; + } + } + + [Serializable] + public class MapperSetter + { + public Mapper UP = new Mapper(EnumButtonType.UP); + public Mapper DOWN = new Mapper(EnumButtonType.DOWN); + public Mapper LEFT = new Mapper(EnumButtonType.LEFT); + public Mapper RIGHT = new Mapper(EnumButtonType.RIGHT); + public Mapper A = new Mapper(EnumButtonType.A); + public Mapper B = new Mapper(EnumButtonType.B); + public Mapper SELECT = new Mapper(EnumButtonType.SELECT); + public Mapper START = new Mapper(EnumButtonType.START); + public Mapper MIC = new Mapper(EnumButtonType.MIC); + + public EnumButtonType GetButtons() + { + EnumButtonType res = 0; + + if (Input.GetKey(UP.keyCode)) + res |= EnumButtonType.UP; + + if (Input.GetKey(DOWN.keyCode)) + res |= EnumButtonType.DOWN; + + if (Input.GetKey(LEFT.keyCode)) + res |= EnumButtonType.LEFT; + + if (Input.GetKey(RIGHT.keyCode)) + res |= EnumButtonType.RIGHT; + + if (Input.GetKey(A.keyCode)) + res |= EnumButtonType.A; + + if (Input.GetKey(B.keyCode)) + res |= EnumButtonType.B; + + if (Input.GetKey(SELECT.keyCode)) + res |= EnumButtonType.SELECT; + + if (Input.GetKey(START.keyCode)) + res |= EnumButtonType.START; + + if (Input.GetKey(MIC.keyCode)) + res |= EnumButtonType.MIC; + + return res; + } + } + } +} diff --git a/AxibugEmuOnline.Client/Assets/Script/NesEmulator/NesControllerMapper.cs.meta b/AxibugEmuOnline.Client/Assets/Script/NesEmulator/NesControllerMapper.cs.meta new file mode 100644 index 0000000..2350d8d --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Script/NesEmulator/NesControllerMapper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 66fc8233a79cd254f8d005452dcd4ac0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Script/NesEmulator/NesEmulator.cs b/AxibugEmuOnline.Client/Assets/Script/NesEmulator/NesEmulator.cs index e2e390c..1468598 100644 --- a/AxibugEmuOnline.Client/Assets/Script/NesEmulator/NesEmulator.cs +++ b/AxibugEmuOnline.Client/Assets/Script/NesEmulator/NesEmulator.cs @@ -57,6 +57,8 @@ namespace AxibugEmuOnline.Client { if (NesCore != null) { + var controlState = Supporter.GetControllerState(); + NesCore.pad.Sync(controlState); NesCore.EmulateFrame(true); var screenBuffer = NesCore.ppu.GetScreenPtr(); diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PAD.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PAD.cs index 01f5df1..f9b6ccf 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PAD.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PAD.cs @@ -1,5 +1,6 @@ using Codice.CM.Client.Differences; using System; +using System.Collections.Generic; namespace VirtualNes.Core { @@ -25,6 +26,19 @@ namespace VirtualNes.Core public uint pad1bit, pad2bit, pad3bit, pad4bit; + private static int[] ren10fps = new int[6] { 1, 1, 1, 0, 0, 0 }; + private static int[] ren15fps = new int[4] { 1, 1, 0, 0 }; + private static int[] ren20fps = new int[3] { 1, 1, 0 }; + private static int[] ren30fps = new int[2] { 1, 0 }; + private static int[] renmask = new int[4] { 6, 4, 3, 2 }; + public static Dictionary rentbl = new Dictionary() + { + {0,ren10fps }, + {1,ren15fps }, + {2,ren20fps }, + {3,ren30fps }, + }; + public PAD(NES parent) { nes = parent; @@ -405,6 +419,106 @@ namespace VirtualNes.Core } } + public void Sync(ControllerState state) + { + padbit[0] = SyncSub(0, state); + padbit[1] = SyncSub(1, state); + padbit[2] = SyncSub(2, state); + padbit[3] = SyncSub(3, state); + + // Mic + micbit = 0; + if (state.HasButton(1, EnumButtonType.MIC)) micbit |= 4; + + // For Excontroller + if (expad != null) + { + expad.Sync(); + } + } + + + + private byte SyncSub(int no, ControllerState state) + { + ushort bit = 0; + + // Up + if (state.HasButton(no, EnumButtonType.UP)) + bit |= 1 << 4; + // Down + if (state.HasButton(no, EnumButtonType.DOWN)) + bit |= 1 << 5; + // Left + if (state.HasButton(no, EnumButtonType.LEFT)) + bit |= 1 << 6; + // Right + if (state.HasButton(no, EnumButtonType.RIGHT)) + bit |= 1 << 7; + + // 同時入力を禁止する + // if( (bit&((1<<4)|(1<<5))) == ((1<<4)|(1<<5)) ) + // bit &= ~((1<<4)|(1<<5)); + if ((bit & ((1 << 6) | (1 << 7))) == ((1 << 6) | (1 << 7))) + bit = (byte)(bit & ~((1 << 6) | (1 << 7))); + + // A + if (state.HasButton(no, EnumButtonType.A)) bit |= 1 << 0; + // B + if (state.HasButton(no, EnumButtonType.B)) bit |= 1 << 1; + + // Select + if (state.HasButton(no, EnumButtonType.SELECT)) bit |= 1 << 2; + // Start + if (state.HasButton(no, EnumButtonType.START)) bit |= 1 << 3; + + // A rapid setup + if ((bit & (1 << 8)) != 0) + { + int spd = Supporter.Config.controller.nRapid[no][0]; + if (spd >= 3) spd = 3; + + int[] tbl = rentbl[spd]; + + if (padcnt[no][0] >= renmask[spd]) + padcnt[no][0] = 0; + + if ((tbl[padcnt[no][0]]) != 0) + bit |= (1 << 0); + else + bit = (byte)(bit & ~(1 << 0)); + + padcnt[no][0]++; + } + else + { + padcnt[no][0] = 0; + } + // B rapid setup + if ((bit & (1 << 9)) != 0) + { + int spd = Supporter.Config.controller.nRapid[no][1]; + if (spd >= 3) spd = 3; + int[] tbl = rentbl[spd]; + + if (padcnt[no][1] >= renmask[spd]) + padcnt[no][1] = 0; + + if ((tbl[padcnt[no][1]]) != 0) + bit |= (1 << 1); + else + bit = (byte)(bit & ~(1 << 1)); + + padcnt[no][1]++; + } + else + { + padcnt[no][1] = 0; + } + + return (byte)(bit & 0xFF); + } + internal bool IsZapperMode() { return bZapperMode; diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/ControllerState.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/ControllerState.cs new file mode 100644 index 0000000..418e35c --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/ControllerState.cs @@ -0,0 +1,51 @@ +using System; + +namespace VirtualNes.Core +{ + public struct ControllerState + { + private uint raw0; + private uint raw1; + private uint raw2; + private uint raw3; + + public ControllerState( + EnumButtonType player0_buttons, + EnumButtonType player1_buttons, + EnumButtonType player2_buttons, + EnumButtonType player3_buttons) + { + raw0 = (uint)player0_buttons; + raw1 = (uint)player1_buttons; + raw2 = (uint)player2_buttons; + raw3 = (uint)player3_buttons; + } + + public bool HasButton(int player, EnumButtonType button) + { + uint raw = 0; + switch (player) + { + case 0: raw = raw0; break; + case 1: raw = raw1; break; + case 2: raw = raw2; break; + case 3: raw = raw3; break; + } + return (raw & (uint)button) == (uint)button; + } + } + + [Flags] + public enum EnumButtonType + { + UP = 1, + DOWN = 2, + LEFT = 4, + RIGHT = 8, + A = 16, + B = 32, + SELECT = 64, + START = 128, + MIC = 256 + } +} diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/ControllerState.cs.meta b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/ControllerState.cs.meta new file mode 100644 index 0000000..aa3411a --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/ControllerState.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 88eb13b75812fc040ad7eb146af2bb80 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgController.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgController.cs index 9650980..ee4bf00 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgController.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgController.cs @@ -1,6 +1,27 @@ -namespace VirtualNes.Core +using Codice.CM.Client.Differences; + +namespace VirtualNes.Core { public class CfgController { + public ushort[][] nButton = new ushort[4][] + { + new ushort[64],new ushort[64], new ushort[64], new ushort[64], + }; + public ushort[][] nRapid = new ushort[4][] + { + new ushort[2],new ushort[2],new ushort[2],new ushort[2], + }; + + // 0:Crazy Climber + // 1:Famly Trainer + // 2:Exciting Boxing + // 3:Mahjang + public ushort[][] nExButton = new ushort[4][] + { + new ushort[64],new ushort[64], new ushort[64], new ushort[64], + }; + + public ushort[] nVSUnisystem = new ushort[64]; } } \ No newline at end of file diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/EmulatorConfig.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/EmulatorConfig.cs index b24afe0..d552bef 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/EmulatorConfig.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/EmulatorConfig.cs @@ -1,4 +1,6 @@ -namespace VirtualNes.Core +using System; + +namespace VirtualNes.Core { public class EmulatorConfig { diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/Supporter.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/Supporter.cs index 98f7b97..eb5a91f 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/Supporter.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/Supporter.cs @@ -54,6 +54,11 @@ namespace VirtualNes.Core return s_support.TryGetMapperNo(rom, out mapperNo); } + public static ControllerState GetControllerState() + { + return s_support.GetControllerState(); + } + public static EmulatorConfig Config => s_support.Config; } @@ -70,5 +75,6 @@ namespace VirtualNes.Core void SaveFile(byte[] fileData, string directPath, string fileName); Stream OpenFile(string directPath, string fileName); bool TryGetMapperNo(ROM rom, out int mapperNo); + ControllerState GetControllerState(); } } diff --git a/References/virtuanessrc097-master/NES/PAD.cpp b/References/virtuanessrc097-master/NES/PAD.cpp index 4712b1cd7ab16028a1ececd77cbdd5197273719f..b778ba2df04c44b73b407aea01b4bc41fb0f8525 100644 GIT binary patch delta 6235 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