forked from sin365/AxibugEmuOnline
核心.net standard2.0
This commit is contained in:
parent
7ed77bb499
commit
dc9d42de5c
@ -51,9 +51,25 @@ namespace VirtualNes.Core
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
//return CombineHashCode(raw0, raw1, raw2, raw3, valid);
|
||||
return HashCode.Combine(raw0, raw1, raw2, raw3, valid);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// .NetStandard2.0编译备用函数
|
||||
/// </summary>
|
||||
/// <param name="raw0"></param>
|
||||
/// <param name="raw1"></param>
|
||||
/// <param name="raw2"></param>
|
||||
/// <param name="raw3"></param>
|
||||
/// <param name="valid"></param>
|
||||
/// <returns></returns>
|
||||
static int CombineHashCode(uint raw0, uint raw1, uint raw2, uint raw3, bool valid)
|
||||
{
|
||||
uint validUInt = valid ? 1u : 0u;
|
||||
uint combinedHash = (raw0 * 31 + raw1) * 31 + raw2 * 31 + raw3 * 31 + validUInt;
|
||||
return (int)combinedHash;
|
||||
}
|
||||
public static bool operator ==(ControllerState left, ControllerState right)
|
||||
{
|
||||
return
|
||||
|
@ -7,7 +7,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AxibugEmuOnline.Web", "Axib
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AxibugEmuOnline.Server", "AxibugEmuOnline.Server\AxibugEmuOnline.Server.csproj", "{9F509DB4-CDB4-4CDB-9C10-805C5E644EEF}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VirtualNes.Core", "VirtualNes.Core\VirtualNes.Core.csproj", "{8A4771D6-74B9-453C-9932-6B774280E325}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VirtualNes.Core", "Core\VirtualNes.Core\VirtualNes.Core.csproj", "{637EB35A-4D1A-41EA-9A7B-459E17E93981}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
@ -23,10 +23,10 @@ Global
|
||||
{9F509DB4-CDB4-4CDB-9C10-805C5E644EEF}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9F509DB4-CDB4-4CDB-9C10-805C5E644EEF}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{9F509DB4-CDB4-4CDB-9C10-805C5E644EEF}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{8A4771D6-74B9-453C-9932-6B774280E325}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{8A4771D6-74B9-453C-9932-6B774280E325}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{8A4771D6-74B9-453C-9932-6B774280E325}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{8A4771D6-74B9-453C-9932-6B774280E325}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{637EB35A-4D1A-41EA-9A7B-459E17E93981}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{637EB35A-4D1A-41EA-9A7B-459E17E93981}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{637EB35A-4D1A-41EA-9A7B-459E17E93981}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{637EB35A-4D1A-41EA-9A7B-459E17E93981}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -73,7 +73,7 @@ namespace VirtualNes.Core
|
||||
|
||||
public void Process(ISoundDataBuffer lpBuffer, uint dwSize)
|
||||
{
|
||||
int nBits = Supporter.Config.sound.nBits;
|
||||
int nBits = Supporter.S.Config.sound.nBits;
|
||||
uint dwLength = (uint)(dwSize / (nBits / 8));
|
||||
int output;
|
||||
QUEUEDATA q = new QUEUEDATA();
|
||||
@ -82,11 +82,11 @@ namespace VirtualNes.Core
|
||||
var pSoundBuf = m_SoundBuffer;
|
||||
int nCcount = 0;
|
||||
|
||||
int nFilterType = Supporter.Config.sound.nFilterType;
|
||||
int nFilterType = Supporter.S.Config.sound.nFilterType;
|
||||
|
||||
if (!Supporter.Config.sound.bEnable)
|
||||
if (!Supporter.S.Config.sound.bEnable)
|
||||
{
|
||||
byte empty = (byte)(Supporter.Config.sound.nRate == 8 ? 128 : 0);
|
||||
byte empty = (byte)(Supporter.S.Config.sound.nRate == 8 ? 128 : 0);
|
||||
for (int i = 0; i < dwSize; i++)
|
||||
lpBuffer.WriteByte(empty);
|
||||
return;
|
||||
@ -108,7 +108,7 @@ namespace VirtualNes.Core
|
||||
MemoryUtility.ZEROMEMORY(vol, vol.Length);
|
||||
|
||||
var bMute = m_bMute;
|
||||
var nVolume = Supporter.Config.sound.nVolume;
|
||||
var nVolume = Supporter.S.Config.sound.nVolume;
|
||||
|
||||
int nMasterVolume = bMute[0] ? nVolume[0] : 0;
|
||||
|
||||
@ -151,7 +151,7 @@ namespace VirtualNes.Core
|
||||
vol[23] = (int)(bMute[8] ? (FME7_VOL * nVolume[11] * nMasterVolume) / (100 * 100) : 0);
|
||||
|
||||
// double cycle_rate = ((double)FRAME_CYCLES*60.0/12.0)/(double)Config.sound.nRate;
|
||||
double cycle_rate = (nes.nescfg.FrameCycles * 60.0 / 12.0) / Supporter.Config.sound.nRate;
|
||||
double cycle_rate = (nes.nescfg.FrameCycles * 60.0 / 12.0) / Supporter.S.Config.sound.nRate;
|
||||
|
||||
// CPUサイクル数がループしてしまった時の対策処理
|
||||
if (elapsed_time > nes.cpu.GetTotalCycles())
|
||||
@ -253,7 +253,7 @@ namespace VirtualNes.Core
|
||||
// DC成分のカット(HPF TEST)
|
||||
{
|
||||
// static double cutoff = (2.0*3.141592653579*40.0/44100.0);
|
||||
double cutoff = cutofftemp / Supporter.Config.sound.nRate;
|
||||
double cutoff = cutofftemp / Supporter.S.Config.sound.nRate;
|
||||
double @in, @out;
|
||||
|
||||
@in = output;
|
||||
@ -436,7 +436,7 @@ namespace VirtualNes.Core
|
||||
public void SoundSetup()
|
||||
{
|
||||
float fClock = nes.nescfg.CpuClock;
|
||||
int nRate = Supporter.Config.sound.nRate;
|
||||
int nRate = Supporter.S.Config.sound.nRate;
|
||||
|
||||
@internal.Setup(fClock, nRate);
|
||||
vrc6.Setup(fClock, nRate);
|
||||
@ -460,7 +460,7 @@ namespace VirtualNes.Core
|
||||
elapsed_time = 0;
|
||||
|
||||
float fClock = nes.nescfg.CpuClock;
|
||||
int nRate = Supporter.Config.sound.nRate;
|
||||
int nRate = Supporter.S.Config.sound.nRate;
|
||||
|
||||
@internal.Reset(fClock, nRate);
|
||||
vrc6.Reset(fClock, nRate);
|
||||
|
@ -1,4 +1,4 @@
|
||||
|
||||
|
||||
using System;
|
||||
|
||||
namespace VirtualNes.Core
|
||||
@ -184,15 +184,15 @@ namespace VirtualNes.Core
|
||||
|
||||
Setup(fClock, nRate);
|
||||
|
||||
// $4011反場ヽ趙仄卅中
|
||||
// $4011は初期化しない
|
||||
ushort addr;
|
||||
for (addr = 0x4000; addr <= 0x4010; addr++)
|
||||
{
|
||||
Write(addr, 0x00);
|
||||
SyncWrite(addr, 0x00);
|
||||
}
|
||||
// Write( 0x4001, 0x08 ); // Reset媆反inc乒奈玉卞卅月?
|
||||
// Write( 0x4005, 0x08 ); // Reset媆反inc乒奈玉卞卅月?
|
||||
// Write( 0x4001, 0x08 ); // Reset時はincモードになる?
|
||||
// Write( 0x4005, 0x08 ); // Reset時はincモードになる?
|
||||
Write(0x4012, 0x00);
|
||||
Write(0x4013, 0x00);
|
||||
Write(0x4015, 0x00);
|
||||
@ -200,7 +200,7 @@ namespace VirtualNes.Core
|
||||
SyncWrite(0x4013, 0x00);
|
||||
SyncWrite(0x4015, 0x00);
|
||||
|
||||
// $4017反𤩸五煋心匹場ヽ趙仄卅中(場ヽ乒奈玉互0匹丐月及毛ヽ渾仄凶末白玄互丐月鮋)
|
||||
// $4017は書き込みで初期化しない(初期モードが0であるのを期待したソフトがある為)
|
||||
FrameIRQ = 0xC0;
|
||||
FrameCycle = 0;
|
||||
FrameIRQoccur = 0;
|
||||
@ -301,7 +301,7 @@ namespace VirtualNes.Core
|
||||
case 0x4017:
|
||||
break;
|
||||
|
||||
// VirtuaNES嘐衄禾奈玄
|
||||
// VirtuaNES固有ポート
|
||||
case 0x4018:
|
||||
UpdateRectangle(ch0, data);
|
||||
UpdateRectangle(ch1, data);
|
||||
@ -635,7 +635,7 @@ namespace VirtualNes.Core
|
||||
}
|
||||
}
|
||||
|
||||
// 奶件民平堪中皿民用奶朮市永玄(TEST)
|
||||
// インチキ臭いプチノイズカット(TEST)
|
||||
ch4.dpcm_output_real = ((ch4.reg[1] & 0x01) + ch4.dpcm_value * 2) - 0x40;
|
||||
if (Math.Abs(ch4.dpcm_output_real - ch4.dpcm_output_fake) <= 8)
|
||||
{
|
||||
@ -712,7 +712,7 @@ namespace VirtualNes.Core
|
||||
private int RenderTriangle()
|
||||
{
|
||||
int vol;
|
||||
if (Supporter.Config.sound.bDisableVolumeEffect)
|
||||
if (Supporter.S.Config.sound.bDisableVolumeEffect)
|
||||
{
|
||||
vol = 256;
|
||||
}
|
||||
@ -731,7 +731,7 @@ namespace VirtualNes.Core
|
||||
return ch2.nowvolume * vol / 256;
|
||||
}
|
||||
|
||||
if (!(Supporter.Config.sound.bChangeTone && ChannelTone[2, 0] != 0))
|
||||
if (!(Supporter.S.Config.sound.bChangeTone && ChannelTone[2, 0] != 0))
|
||||
{
|
||||
ch2.phaseacc -= cycle_rate;
|
||||
if (ch2.phaseacc >= 0)
|
||||
@ -756,7 +756,7 @@ namespace VirtualNes.Core
|
||||
return ch2.nowvolume * vol / 256;
|
||||
}
|
||||
|
||||
// 樓笭ⅸ歙
|
||||
// 加重平均
|
||||
int num_times, total;
|
||||
num_times = total = 0;
|
||||
while (ch2.phaseacc < 0)
|
||||
@ -799,7 +799,7 @@ namespace VirtualNes.Core
|
||||
return ch2.nowvolume * vol / 256;
|
||||
}
|
||||
|
||||
// 樓笭ⅸ歙
|
||||
// 加重平均
|
||||
int num_times, total;
|
||||
num_times = total = 0;
|
||||
while (ch2.phaseacc < 0)
|
||||
@ -832,9 +832,9 @@ namespace VirtualNes.Core
|
||||
}
|
||||
int volume = ch.nowvolume;
|
||||
|
||||
if (!(Supporter.Config.sound.bChangeTone && (ChannelTone[(ch.complement == 0) ? 0 : 1, ch.reg[0] >> 6]) != 0))
|
||||
if (!(Supporter.S.Config.sound.bChangeTone && (ChannelTone[(ch.complement == 0) ? 0 : 1, ch.reg[0] >> 6]) != 0))
|
||||
{
|
||||
// 娗嶲<EFBFBD>燴
|
||||
// 補間処理
|
||||
double total;
|
||||
double sample_weight = ch.phaseacc;
|
||||
if (sample_weight > cycle_rate)
|
||||
@ -864,7 +864,7 @@ namespace VirtualNes.Core
|
||||
int x = ChannelTone[(ch.complement == 0) ? 0 : 1, ch.reg[0] >> 6] - 1;
|
||||
int pTone = 0;
|
||||
|
||||
// 載陔剠仄
|
||||
// 更新無し
|
||||
ch.phaseacc -= cycle_rate * 2;
|
||||
if (ch.phaseacc >= 0)
|
||||
{
|
||||
@ -872,7 +872,7 @@ namespace VirtualNes.Core
|
||||
return temp * volume / ((1 << RECTANGLE_VOL_SHIFT) / 2);
|
||||
}
|
||||
|
||||
// 1旦氾永皿分仃載陔
|
||||
// 1ステップだけ更新
|
||||
int freq = INT2FIX(ch.freq + 1);
|
||||
if (freq > cycle_rate * 2)
|
||||
{
|
||||
@ -882,7 +882,7 @@ namespace VirtualNes.Core
|
||||
return temp * volume / ((1 << RECTANGLE_VOL_SHIFT) / 2);
|
||||
}
|
||||
|
||||
// 樓笭ⅸ歙
|
||||
// 加重平均
|
||||
int num_times, total;
|
||||
num_times = total = 0;
|
||||
while (ch.phaseacc < 0)
|
||||
@ -1015,7 +1015,7 @@ namespace VirtualNes.Core
|
||||
SyncWrite4017(data);
|
||||
break;
|
||||
|
||||
// VirtuaNES娚葲<EFBFBD><EFBFBD><EFBFBD>
|
||||
// VirtuaNES屌桳億乕僩
|
||||
case 0x4018:
|
||||
SyncUpdateRectangle(ch0, data);
|
||||
SyncUpdateRectangle(ch1, data);
|
||||
|
@ -1110,7 +1110,7 @@ namespace VirtualNes.Core
|
||||
case 0xD2: /* JAM */
|
||||
case 0xF2: /* JAM */
|
||||
default:
|
||||
if (!Supporter.Config.emulator.bIllegalOp)
|
||||
if (!Supporter.S.Config.emulator.bIllegalOp)
|
||||
{
|
||||
throw new Exception("IllegalOp");
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ namespace VirtualNes.Core
|
||||
Debuger.Log($"SOUND CODE:{addr & 0x1F:X2}");
|
||||
|
||||
// OSDにするべきか…
|
||||
if (Supporter.Config.sound.bExtraSoundEnable)
|
||||
if (Supporter.S.Config.sound.bExtraSoundEnable)
|
||||
{
|
||||
//TODO : 似乎VirtuaNES有直接播放某个音频文件的功能
|
||||
//DirectSound.EsfAllStop();
|
||||
|
@ -43,7 +43,7 @@ namespace VirtualNes.Core
|
||||
}
|
||||
|
||||
// OSDにするべきか…
|
||||
if (Supporter.Config.sound.bExtraSoundEnable)
|
||||
if (Supporter.S.Config.sound.bExtraSoundEnable)
|
||||
{
|
||||
//TODO : 似乎VirtuaNES有直接播放某个音频文件的功能
|
||||
//DirectSound.EsfAllStop();
|
||||
|
@ -57,7 +57,7 @@ namespace VirtualNes.Core
|
||||
9,10, 8,11,13,12,14,15 };
|
||||
|
||||
// OSDにするべきか…
|
||||
if (Supporter.Config.sound.bExtraSoundEnable)
|
||||
if (Supporter.S.Config.sound.bExtraSoundEnable)
|
||||
{
|
||||
//TODO : 似乎VirtuaNES有直接播放某个音频文件的功能
|
||||
Debuger.Log($"CODE {data:X2}");
|
||||
|
@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
@ -249,7 +249,7 @@ namespace VirtualNes.Core
|
||||
LoadDISK();
|
||||
|
||||
{
|
||||
// Pad・ッ・鬣ケトレ、タ、ネウ<EFBFBD>レサッ・ソ・、・゚・<EFBFBD>ー、ャ゚W、、、ホ、ヌ、ウ、ウ、ヌ
|
||||
// Padクラス内だと初期化タイミングが遅いのでここで
|
||||
uint crc = rom.GetPROM_CRC();
|
||||
if (
|
||||
crc == 0xe792de94 // Best Play - Pro Yakyuu (New) (J)
|
||||
@ -275,7 +275,7 @@ namespace VirtualNes.Core
|
||||
|
||||
LoadTurboFile();
|
||||
|
||||
// VS-Unisystem、ホ・ヌ・ユ・ゥ・<EFBFBD>ネヤOカィ
|
||||
// VS-Unisystemのデフォルト設定
|
||||
if (rom.IsVSUNISYSTEM())
|
||||
{
|
||||
uint crc = rom.GetPROM_CRC();
|
||||
@ -286,13 +286,13 @@ namespace VirtualNes.Core
|
||||
|
||||
Reset();
|
||||
|
||||
// ・イゥ`・犹フモミ、ホ・ヌ・ユ・ゥ・<EFBDA9>ネ・ェ・ラ・キ・逾<EFBDA5><E980BE>Oカィ(ヤOカィ諾、ケ瓶、ヒハケ、ヲ樣)
|
||||
// ゲーム固有のデフォルトオプションを設定(設定戻す時に使う為)
|
||||
GameOption.defRenderMethod = (int)GetRenderMethod();
|
||||
GameOption.defIRQtype = GetIrqType();
|
||||
GameOption.defFrameIRQ = GetFrameIRQmode();
|
||||
GameOption.defVideoMode = GetVideoMode();
|
||||
|
||||
// ヤOカィ、<EFBFBD>愰`・ノ、キ、ニヤOカィ、ケ、<EFBDB9>(・ィ・<EFBDA8>ネ・熙ャ殪、ア、<EFBDB1>ミ・ヌ・ユ・ゥ・<EFBDA9>ネ、ャネ<EFBDAC><EFBE88>)
|
||||
// 設定をロードして設定する(エントリが無ければデフォルトが入る)
|
||||
if (rom.GetMapperNo() != 20)
|
||||
{
|
||||
GameOption.Load(rom.GetPROM_CRC());
|
||||
@ -326,17 +326,17 @@ namespace VirtualNes.Core
|
||||
if (pad.GetExController() != (int)EXCONTROLLER.EXCONTROLLER_TURBOFILE)
|
||||
return;
|
||||
|
||||
var fp = Supporter.OpenFile(Supporter.Config.path.szSavePath, "TurboFile.vtf");
|
||||
var fp = Supporter.S.OpenFile(Supporter.S.Config.path.szSavePath, "TurboFile.vtf");
|
||||
try
|
||||
{
|
||||
if (fp == null)
|
||||
{
|
||||
// xxx ・ユ・。・、・<EFBFBD><EFBFBD>_、ア、゙、サ、<EFBFBD>
|
||||
// xxx ファイルを開けません
|
||||
throw new Exception($"Can Not Open File [TurboFile.vtf]");
|
||||
}
|
||||
|
||||
long size = fp.Length;
|
||||
// ・ユ・。・、・<EFBFBD>オ・、・コネ。オテ
|
||||
// ファイルサイズ取得
|
||||
if (size > 32 * 1024)
|
||||
{
|
||||
size = 32 * 1024;
|
||||
@ -354,7 +354,7 @@ namespace VirtualNes.Core
|
||||
|
||||
private void LoadDISK()
|
||||
{
|
||||
//todo : エナオ忞巐チネ。ヨァウヨ
|
||||
//todo : 磁碟机读取支持
|
||||
}
|
||||
|
||||
private void LoadSRAM()
|
||||
@ -367,10 +367,10 @@ namespace VirtualNes.Core
|
||||
if (!rom.IsSAVERAM())
|
||||
return;
|
||||
|
||||
var saveFileDir = Supporter.Config.path.szSavePath;
|
||||
var saveFileDir = Supporter.S.Config.path.szSavePath;
|
||||
var saveFileName = $"{rom.GetRomName()}.sav";
|
||||
|
||||
var fp = Supporter.OpenFile(saveFileDir, saveFileName);
|
||||
var fp = Supporter.S.OpenFile(saveFileDir, saveFileName);
|
||||
|
||||
try
|
||||
{
|
||||
@ -400,7 +400,7 @@ namespace VirtualNes.Core
|
||||
{
|
||||
int i;
|
||||
|
||||
// メモリクリア
|
||||
// 儊儌儕僋儕傾
|
||||
MemoryUtility.ZEROMEMORY(MMU.RAM, MMU.RAM.Length);
|
||||
MemoryUtility.ZEROMEMORY(MMU.WRAM, MMU.WRAM.Length);
|
||||
MemoryUtility.ZEROMEMORY(MMU.DRAM, MMU.DRAM.Length);
|
||||
@ -420,11 +420,11 @@ namespace VirtualNes.Core
|
||||
|
||||
MMU.PROM = MMU.VROM = null;
|
||||
|
||||
// 0 除算防止対策
|
||||
// 0 彍嶼杊巭懳嶔
|
||||
MMU.PROM_8K_SIZE = MMU.PROM_16K_SIZE = MMU.PROM_32K_SIZE = 1;
|
||||
MMU.VROM_1K_SIZE = MMU.VROM_2K_SIZE = MMU.VROM_4K_SIZE = MMU.VROM_8K_SIZE = 1;
|
||||
|
||||
// デフォルトバンク設定
|
||||
// 僨僼僅儖僩僶儞僋愝掕
|
||||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
MMU.CPU_MEM_BANK[i] = null;
|
||||
@ -432,11 +432,11 @@ namespace VirtualNes.Core
|
||||
MMU.CPU_MEM_PAGE[i] = 0;
|
||||
}
|
||||
|
||||
// 内臓RAM/WRAM
|
||||
// 撪憻RAM/WRAM
|
||||
MMU.SetPROM_Bank(0, MMU.RAM, MMU.BANKTYPE_RAM);
|
||||
MMU.SetPROM_Bank(3, MMU.WRAM, MMU.BANKTYPE_RAM);
|
||||
|
||||
// ダミー
|
||||
// 僟儈乕
|
||||
MMU.SetPROM_Bank(1, MMU.XRAM, MMU.BANKTYPE_ROM);
|
||||
MMU.SetPROM_Bank(2, MMU.XRAM, MMU.BANKTYPE_ROM);
|
||||
|
||||
@ -502,13 +502,13 @@ namespace VirtualNes.Core
|
||||
EmulationCPU(nescfg.ScanlineCycles);
|
||||
if (bDraw)
|
||||
{
|
||||
ppu.Scanline(scanline, Supporter.Config.graphics.bAllSprite, Supporter.Config.graphics.bLeftClip);
|
||||
ppu.Scanline(scanline, Supporter.S.Config.graphics.bAllSprite, Supporter.S.Config.graphics.bLeftClip);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pad.IsZapperMode() && scanline == ZapperY)
|
||||
{
|
||||
ppu.Scanline(scanline, Supporter.Config.graphics.bAllSprite, Supporter.Config.graphics.bLeftClip);
|
||||
ppu.Scanline(scanline, Supporter.S.Config.graphics.bAllSprite, Supporter.S.Config.graphics.bLeftClip);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -518,11 +518,11 @@ namespace VirtualNes.Core
|
||||
}
|
||||
else
|
||||
{
|
||||
ppu.Scanline(scanline, Supporter.Config.graphics.bAllSprite, Supporter.Config.graphics.bLeftClip);
|
||||
ppu.Scanline(scanline, Supporter.S.Config.graphics.bAllSprite, Supporter.S.Config.graphics.bLeftClip);
|
||||
}
|
||||
}
|
||||
}
|
||||
ppu.ScanlineNext(); // これの位置でラスター系は画面が違う
|
||||
ppu.ScanlineNext(); // 偙傟偺埵抲偱儔僗僞乕宯偼夋柺偑堘偆
|
||||
if (RenderMethod == EnumRenderMethod.PRE_ALL_RENDER)
|
||||
EmulationCPU(nescfg.ScanlineCycles);
|
||||
|
||||
@ -535,13 +535,13 @@ namespace VirtualNes.Core
|
||||
EmulationCPU(nescfg.HDrawCycles);
|
||||
if (bDraw)
|
||||
{
|
||||
ppu.Scanline(scanline, Supporter.Config.graphics.bAllSprite, Supporter.Config.graphics.bLeftClip);
|
||||
ppu.Scanline(scanline, Supporter.S.Config.graphics.bAllSprite, Supporter.S.Config.graphics.bLeftClip);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pad.IsZapperMode() && scanline == ZapperY)
|
||||
{
|
||||
ppu.Scanline(scanline, Supporter.Config.graphics.bAllSprite, Supporter.Config.graphics.bLeftClip);
|
||||
ppu.Scanline(scanline, Supporter.S.Config.graphics.bAllSprite, Supporter.S.Config.graphics.bLeftClip);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -551,7 +551,7 @@ namespace VirtualNes.Core
|
||||
}
|
||||
else
|
||||
{
|
||||
ppu.Scanline(scanline, Supporter.Config.graphics.bAllSprite, Supporter.Config.graphics.bLeftClip);
|
||||
ppu.Scanline(scanline, Supporter.S.Config.graphics.bAllSprite, Supporter.S.Config.graphics.bLeftClip);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -583,7 +583,7 @@ namespace VirtualNes.Core
|
||||
{
|
||||
pad.VSync();
|
||||
|
||||
// VBLANK期間
|
||||
// VBLANK婜娫
|
||||
if (scanline == nescfg.TotalScanlines - 1)
|
||||
{
|
||||
ppu.VBlankEnd();
|
||||
@ -642,7 +642,7 @@ namespace VirtualNes.Core
|
||||
|
||||
if (scanline == 0)
|
||||
{
|
||||
// ダミースキャンライン
|
||||
// 僟儈乕僗僉儍儞儔僀儞
|
||||
// H-Draw (4fetches*32)
|
||||
EmulationCPU(FETCH_CYCLES * 128);
|
||||
ppu.FrameStart();
|
||||
@ -655,10 +655,10 @@ namespace VirtualNes.Core
|
||||
}
|
||||
else if (scanline < 240)
|
||||
{
|
||||
// スクリーン描画(Scanline 1~239)
|
||||
// 僗僋儕乕儞昤夋(Scanline 1乣239)
|
||||
if (bDraw)
|
||||
{
|
||||
ppu.Scanline(scanline, Supporter.Config.graphics.bAllSprite, Supporter.Config.graphics.bLeftClip);
|
||||
ppu.Scanline(scanline, Supporter.S.Config.graphics.bAllSprite, Supporter.S.Config.graphics.bLeftClip);
|
||||
ppu.ScanlineNext();
|
||||
EmulationCPU(FETCH_CYCLES * 10);
|
||||
mapper.HSync(scanline);
|
||||
@ -670,7 +670,7 @@ namespace VirtualNes.Core
|
||||
{
|
||||
if (pad.IsZapperMode() && scanline == ZapperY)
|
||||
{
|
||||
ppu.Scanline(scanline, Supporter.Config.graphics.bAllSprite, Supporter.Config.graphics.bLeftClip);
|
||||
ppu.Scanline(scanline, Supporter.S.Config.graphics.bAllSprite, Supporter.S.Config.graphics.bLeftClip);
|
||||
ppu.ScanlineNext();
|
||||
EmulationCPU(FETCH_CYCLES * 10);
|
||||
mapper.HSync(scanline);
|
||||
@ -694,7 +694,7 @@ namespace VirtualNes.Core
|
||||
}
|
||||
else
|
||||
{
|
||||
ppu.Scanline(scanline, Supporter.Config.graphics.bAllSprite, Supporter.Config.graphics.bLeftClip);
|
||||
ppu.Scanline(scanline, Supporter.S.Config.graphics.bAllSprite, Supporter.S.Config.graphics.bLeftClip);
|
||||
ppu.ScanlineNext();
|
||||
EmulationCPU(FETCH_CYCLES * 10);
|
||||
mapper.HSync(scanline);
|
||||
@ -707,7 +707,7 @@ namespace VirtualNes.Core
|
||||
}
|
||||
else if (scanline == 240)
|
||||
{
|
||||
// ダミースキャンライン (Scanline 240)
|
||||
// 僟儈乕僗僉儍儞儔僀儞 (Scanline 240)
|
||||
mapper.VSync();
|
||||
|
||||
EmulationCPU(nescfg.HDrawCycles);
|
||||
@ -720,7 +720,7 @@ namespace VirtualNes.Core
|
||||
{
|
||||
pad.VSync();
|
||||
|
||||
// VBLANK期間
|
||||
// VBLANK婜娫
|
||||
if (scanline == nescfg.TotalScanlines - 1)
|
||||
{
|
||||
ppu.VBlankEnd();
|
||||
@ -864,7 +864,7 @@ namespace VirtualNes.Core
|
||||
MMU.VROM_4K_SIZE = rom.GetVROM_SIZE() * 2;
|
||||
MMU.VROM_8K_SIZE = rom.GetVROM_SIZE();
|
||||
|
||||
// デフォルトバンク
|
||||
// 僨僼僅儖僩僶儞僋
|
||||
if (MMU.VROM_8K_SIZE != 0)
|
||||
{
|
||||
MMU.SetVROM_8K_Bank(0);
|
||||
@ -874,7 +874,7 @@ namespace VirtualNes.Core
|
||||
MMU.SetCRAM_8K_Bank(0);
|
||||
}
|
||||
|
||||
// ミラー
|
||||
// 儈儔乕
|
||||
if (rom.Is4SCREEN())
|
||||
{
|
||||
MMU.SetVRAM_Mirror(MMU.VRAM_MIRROR4);
|
||||
@ -976,13 +976,13 @@ namespace VirtualNes.Core
|
||||
reg.S = 0xFF;
|
||||
reg.P = CPU.Z_FLAG | CPU.R_FLAG | CPU.I_FLAG;
|
||||
|
||||
// 安全対策を兼ねてあえてループに(1秒分)
|
||||
// 埨慡懳嶔傪寭偹偰偁偊偰儖乕僾偵(1昩暘)
|
||||
for (int i = 0; i < nescfg.TotalScanlines * 60; i++)
|
||||
{
|
||||
EmulationCPU(nescfg.ScanlineCycles);
|
||||
cpu.GetContext(ref reg);
|
||||
|
||||
// 無限ループに入ったことを確認したら抜ける
|
||||
// 柍尷儖乕僾偵擖偭偨偙偲傪妋擣偟偨傜敳偗傞
|
||||
if (reg.PC == 0x4700)
|
||||
{
|
||||
break;
|
||||
@ -993,7 +993,7 @@ namespace VirtualNes.Core
|
||||
}
|
||||
|
||||
cpu.GetContext(ref reg);
|
||||
// 無限ループに入っていたら再設定する
|
||||
// 柍尷儖乕僾偵擖偭偰偄偨傜嵞愝掕偡傞
|
||||
if (reg.PC == 0x4700)
|
||||
{
|
||||
reg.PC = 0x4720; // Play Address
|
||||
@ -1009,7 +1009,7 @@ namespace VirtualNes.Core
|
||||
else
|
||||
{
|
||||
cpu.GetContext(ref reg);
|
||||
reg.PC = 0x4700; // 無限ループ
|
||||
reg.PC = 0x4700; // 柍尷儖乕僾
|
||||
reg.S = 0xFF;
|
||||
|
||||
EmulationCPU(nescfg.ScanlineCycles * nescfg.TotalScanlines);
|
||||
@ -1095,7 +1095,7 @@ namespace VirtualNes.Core
|
||||
|
||||
Debuger.Log($"Saving SAVERAM...[{romName}]");
|
||||
|
||||
Supporter.SaveSRAMToFile(MMU.WRAM, romName);
|
||||
Supporter.S.SaveSRAMToFile(MMU.WRAM, romName);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1143,7 +1143,7 @@ namespace VirtualNes.Core
|
||||
}
|
||||
}
|
||||
|
||||
Supporter.SaveDISKToFile(contents.ToArray(), rom.GetRomName());
|
||||
Supporter.S.SaveDISKToFile(contents.ToArray(), rom.GetRomName());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -1168,7 +1168,7 @@ namespace VirtualNes.Core
|
||||
{
|
||||
Debuger.Log("Saving TURBOFILE...");
|
||||
|
||||
Supporter.SaveFile(MMU.ERAM, Supporter.Config.path.szSavePath, "TurboFile.vtf");
|
||||
Supporter.S.SaveFile(MMU.ERAM, Supporter.S.Config.path.szSavePath, "TurboFile.vtf");
|
||||
}
|
||||
}
|
||||
|
||||
@ -1186,7 +1186,7 @@ namespace VirtualNes.Core
|
||||
if (m_BarcodeCycles > 1000)
|
||||
{
|
||||
m_BarcodeCycles = 0;
|
||||
// 停止?
|
||||
// 掆巭丠
|
||||
if (m_BarcodeData[m_BarcodePtr] != 0xFF)
|
||||
{
|
||||
m_BarcodeOut = m_BarcodeData[m_BarcodePtr++];
|
||||
@ -1227,7 +1227,7 @@ namespace VirtualNes.Core
|
||||
return;
|
||||
|
||||
m_TapeCycles += (nescfg.CpuClock / 32000.0);
|
||||
// m_TapeCycles += (nescfg.CpuClock / 22050.0); // 遅すぎてダメっぽい
|
||||
// m_TapeCycles += (nescfg.CpuClock / 22050.0); // 抶偡偓偰僟儊偭傐偄
|
||||
|
||||
if (m_bTapePlay)
|
||||
{
|
||||
@ -1293,7 +1293,7 @@ namespace VirtualNes.Core
|
||||
return MMU.CPU_MEM_BANK[addr >> 13][addr & 0x1FFF];
|
||||
}
|
||||
|
||||
return 0x00; // Warning予防
|
||||
return 0x00; // Warning梊杊
|
||||
}
|
||||
|
||||
private byte ReadReg(ushort addr)
|
||||
@ -1504,7 +1504,7 @@ namespace VirtualNes.Core
|
||||
pad.Write(addr, data);
|
||||
apu.Write(addr, data);
|
||||
break;
|
||||
// VirtuaNES固有ポート
|
||||
// VirtuaNES屌桳億乕僩
|
||||
case 0x18:
|
||||
apu.Write(addr, data);
|
||||
break;
|
||||
@ -1596,7 +1596,7 @@ namespace VirtualNes.Core
|
||||
ref state.reg.cpureg.FrameIRQ_type,
|
||||
ref state.reg.cpureg.FrameIRQ,
|
||||
ref state.reg.cpureg.FrameIRQ_occur);
|
||||
state.reg.cpureg.FrameIRQ_cycles = cycles; // イホユユ、ャINT、ハ樣」ィ、ゥ
|
||||
state.reg.cpureg.FrameIRQ_cycles = cycles; // 参照がINTな為(ぉ
|
||||
|
||||
state.reg.cpureg.DMA_cycles = cpu.GetDmaCycles();
|
||||
state.reg.cpureg.emul_cycles = emul_cycles;
|
||||
@ -1625,7 +1625,7 @@ namespace VirtualNes.Core
|
||||
MemoryUtility.memcpy(state.ram.SPPAL, MMU.SPPAL, state.ram.SPPAL.Length);
|
||||
MemoryUtility.memcpy(state.ram.SPRAM, MMU.SPRAM, state.ram.SPRAM.Length);
|
||||
|
||||
// S-RAM STATE(ハケモテ/ホエハケモテ、ヒ騅、<E9A885>鬢コエ贇レ、ケ、<EFBDB9>ミ・サゥ`・ヨ、ケ、<EFBDB9>)
|
||||
// S-RAM STATE(使用/未使用に関わらず存在すればセーブする)
|
||||
if (rom.IsSAVERAM())
|
||||
{
|
||||
size = (uint)SAVERAM_SIZE;
|
||||
@ -1649,9 +1649,9 @@ namespace VirtualNes.Core
|
||||
uint size = 0;
|
||||
|
||||
// SAVE CPU MEMORY BANK DATA
|
||||
// BANK0,1,2、マ・ミ・<EFBFBD>ッ・サゥ`・ヨ、ヒ騅4、ハ、キ
|
||||
// VirtuaNES0.30、ォ、<EFBFBD>
|
||||
// ・ミ・<EFBFBD>ッ」ウ、マSRAMハケモテ、ヒ騅、<EFBFBD>鬢コ・サゥ`・ヨ
|
||||
// BANK0,1,2はバンクセーブに関係なし
|
||||
// VirtuaNES0.30から
|
||||
// バンク3はSRAM使用に関わらずセーブ
|
||||
for (int i = 3; i < 8; i++)
|
||||
{
|
||||
state.mmu.CPU_MEM_TYPE[i] = MMU.CPU_MEM_TYPE[i];
|
||||
@ -1696,7 +1696,7 @@ namespace VirtualNes.Core
|
||||
}
|
||||
}
|
||||
|
||||
// WRITE VRAM MEMORY(ウ」、ヒ4Kキヨ、ケ、ル、ニ抱、ュ゙z、<EFBFBD>)
|
||||
// WRITE VRAM MEMORY(常に4K分すべて書き込む)
|
||||
state.VRAM = new byte[4 * 1024];
|
||||
Array.Copy(MMU.VRAM, state.VRAM, state.VRAM.Length);
|
||||
|
||||
@ -1764,7 +1764,7 @@ namespace VirtualNes.Core
|
||||
int DiskSize = 16 + 65500 * rom.GetDiskNo();
|
||||
|
||||
|
||||
// マ珀`ハ<>、<EFBFBD>ォ・ヲ・<EFBDA6>ネ
|
||||
// 相違数をカウント
|
||||
for (int i = 16; i < DiskSize; i++)
|
||||
{
|
||||
if (lpWrite[i] != 0)
|
||||
@ -1874,9 +1874,9 @@ namespace VirtualNes.Core
|
||||
//BANK STATE
|
||||
{
|
||||
// SAVE CPU MEMORY BANK DATA
|
||||
// BANK0,1,2、マ・ミ・<EFBFBD>ッ・サゥ`・ヨ、ヒ騅4、ハ、キ
|
||||
// VirtuaNES0.30、ォ、<EFBFBD>
|
||||
// ・ミ・<EFBFBD>ッ」ウ、マSRAMハケモテ、ヒ騅、<EFBFBD>鬢コ・サゥ`・ヨ
|
||||
// BANK0,1,2はバンクセーブに関係なし
|
||||
// VirtuaNES0.30から
|
||||
// バンク3はSRAM使用に関わらずセーブ
|
||||
for (byte i = 3; i < 8; i++)
|
||||
{
|
||||
MMU.CPU_MEM_TYPE[i] = state.mmu.CPU_MEM_TYPE[i];
|
||||
|
@ -181,7 +181,7 @@ namespace VirtualNes.Core
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Supporter.Config.emulator.bFourPlayer)
|
||||
if (Supporter.S.Config.emulator.bFourPlayer)
|
||||
{
|
||||
// NES type
|
||||
pad1bit = padbitsync[0] | ((uint)padbitsync[2] << 8) | 0x00080000;
|
||||
|
@ -50,7 +50,7 @@ namespace VirtualNes.Core
|
||||
|
||||
try
|
||||
{
|
||||
fp = Supporter.OpenRom(fname);
|
||||
fp = Supporter.S.OpenRom(fname);
|
||||
if (fp == null)
|
||||
{
|
||||
throw new System.Exception($"Open Rom Failed:[{fname}]");
|
||||
@ -167,7 +167,7 @@ namespace VirtualNes.Core
|
||||
lpPRG[3] = 0x1A;
|
||||
lpPRG[4] = (byte)diskno;
|
||||
|
||||
fp = Supporter.OpenFile_DISKSYS();
|
||||
fp = Supporter.S.OpenFile_DISKSYS();
|
||||
if (fp == null)
|
||||
{
|
||||
throw new Exception($"Not found DISKSYS.ROM for [{fname}]");
|
||||
@ -217,7 +217,7 @@ namespace VirtualNes.Core
|
||||
throw new Exception($"Unsupport format:[{fname}]");
|
||||
}
|
||||
|
||||
Supporter.GetFilePathInfo(fname, out fullpath, out path);
|
||||
Supporter.S.GetRomPathInfo(fname, out fullpath, out path);
|
||||
name = Path.GetFileNameWithoutExtension(fullpath);
|
||||
if (!bNSF)
|
||||
{
|
||||
@ -244,7 +244,7 @@ namespace VirtualNes.Core
|
||||
|
||||
FileNameCheck(fname);
|
||||
|
||||
if (Supporter.TryGetMapperNo(this, out int mapperNo))
|
||||
if (Supporter.S.TryGetMapperNo(this, out int mapperNo))
|
||||
{
|
||||
Debuger.Log($"ROMDB Set Mapper #{mapper:000} to #{mapperNo:000}");
|
||||
mapper = mapperNo;
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public struct ControllerState
|
||||
public struct ControllerState : IEquatable<ControllerState>
|
||||
{
|
||||
public uint raw0;
|
||||
public uint raw1;
|
||||
@ -11,19 +11,57 @@ namespace VirtualNes.Core
|
||||
|
||||
public bool valid;
|
||||
|
||||
public ControllerState(
|
||||
EnumButtonType player0_buttons,
|
||||
EnumButtonType player1_buttons,
|
||||
EnumButtonType player2_buttons,
|
||||
EnumButtonType player3_buttons)
|
||||
public ControllerState(EnumButtonType[] states)
|
||||
{
|
||||
raw0 = (uint)player0_buttons;
|
||||
raw1 = (uint)player1_buttons;
|
||||
raw2 = (uint)player2_buttons;
|
||||
raw3 = (uint)player3_buttons;
|
||||
raw0 = (uint)states[0];
|
||||
raw1 = (uint)states[1];
|
||||
raw2 = (uint)states[2];
|
||||
raw3 = (uint)states[3];
|
||||
valid = true;
|
||||
}
|
||||
|
||||
public bool HasButton(int player, EnumButtonType button)
|
||||
{
|
||||
uint raw = player switch
|
||||
{
|
||||
0 => raw0,
|
||||
1 => raw1,
|
||||
2 => raw2,
|
||||
3 => raw3,
|
||||
_ => 0
|
||||
};
|
||||
return (raw & (uint)button) == (uint)button;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{raw0}|{raw1}|{raw2}|{raw3}";
|
||||
}
|
||||
|
||||
#region Impl_Equals
|
||||
public bool Equals(ControllerState other)
|
||||
{
|
||||
return raw0 == other.raw0 && raw1 == other.raw1 && raw2 == other.raw2 && raw3 == other.raw3 && valid == other.valid;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is ControllerState other && Equals(other);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return CombineHashCode(raw0, raw1, raw2, raw3, valid);
|
||||
//return HashCode.Combine(raw0, raw1, raw2, raw3, valid);
|
||||
}
|
||||
|
||||
static int CombineHashCode(uint raw0, uint raw1, uint raw2, uint raw3, bool valid)
|
||||
{
|
||||
uint validUInt = valid ? 1u : 0u;
|
||||
uint combinedHash = (raw0 * 31 + raw1) * 31 + raw2 * 31 + raw3 * 31 + validUInt;
|
||||
return (int)combinedHash;
|
||||
}
|
||||
|
||||
public static bool operator ==(ControllerState left, ControllerState right)
|
||||
{
|
||||
return
|
||||
@ -37,24 +75,7 @@ namespace VirtualNes.Core
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{raw0}|{raw1}|{raw2}|{raw3}";
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
[Flags]
|
||||
|
@ -1,70 +1,16 @@
|
||||
using System.IO;
|
||||
using System.IO;
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public static class Supporter
|
||||
{
|
||||
private static ISupporterImpl s_support;
|
||||
internal static ISupporterImpl S => s_support;
|
||||
|
||||
public static void Setup(ISupporterImpl supporter)
|
||||
{
|
||||
s_support = supporter;
|
||||
}
|
||||
|
||||
public static Stream OpenRom(string fname)
|
||||
{
|
||||
return s_support.OpenRom(fname);
|
||||
}
|
||||
|
||||
public static void GetFilePathInfo(string fname, out string fullPath, out string directPath)
|
||||
{
|
||||
s_support.GetRomPathInfo(fname, out fullPath, out directPath);
|
||||
}
|
||||
|
||||
public static Stream OpenFile_DISKSYS()
|
||||
{
|
||||
return s_support.OpenFile_DISKSYS();
|
||||
}
|
||||
|
||||
public static void SaveSRAMToFile(byte[] sramContent, string romName)
|
||||
{
|
||||
s_support.SaveSRAMToFile(sramContent, romName);
|
||||
}
|
||||
|
||||
public static void SaveDISKToFile(byte[] diskFileContent, string romName)
|
||||
{
|
||||
s_support.SaveDISKToFile(diskFileContent, romName);
|
||||
}
|
||||
|
||||
public static void PrepareDirectory(string directPath)
|
||||
{
|
||||
s_support.PrepareDirectory(directPath);
|
||||
}
|
||||
|
||||
public static void SaveFile(byte[] fileData, string directPath, string fileName)
|
||||
{
|
||||
s_support.SaveFile(fileData, directPath, fileName);
|
||||
}
|
||||
public static Stream OpenFile(string directPath, string fileName)
|
||||
{
|
||||
return s_support.OpenFile(directPath, fileName);
|
||||
}
|
||||
|
||||
public static bool TryGetMapperNo(ROM rom, out int mapperNo)
|
||||
{
|
||||
return s_support.TryGetMapperNo(rom, out mapperNo);
|
||||
}
|
||||
|
||||
public static ControllerState GetControllerState()
|
||||
{
|
||||
return s_support.GetControllerState();
|
||||
}
|
||||
|
||||
public static void SampleInput(uint frameCount)
|
||||
{
|
||||
s_support.SampleInput(frameCount);
|
||||
}
|
||||
|
||||
public static EmulatorConfig Config => s_support.Config;
|
||||
}
|
||||
|
||||
public interface ISupporterImpl
|
||||
@ -75,7 +21,6 @@ namespace VirtualNes.Core
|
||||
void SaveSRAMToFile(byte[] sramContent, string romName);
|
||||
void SaveDISKToFile(byte[] diskFileContent, string romName);
|
||||
EmulatorConfig Config { get; }
|
||||
|
||||
void PrepareDirectory(string directPath);
|
||||
void SaveFile(byte[] fileData, string directPath, string fileName);
|
||||
Stream OpenFile(string directPath, string fileName);
|
||||
|
Loading…
Reference in New Issue
Block a user