using System; using System.Collections.Generic; using System.IO; using System.IO.Compression; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using Mesen.GUI.Config; using Mesen.GUI.Forms; namespace Mesen.GUI { public class InteropEmu { private const string DLLPath = "WinMesen.dll"; [DllImport(DLLPath)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool TestDll(); [DllImport(DLLPath, EntryPoint = "GetMesenVersion")] private static extern UInt32 GetMesenVersionWrapper(); [DllImport(DLLPath)] public static extern void InitializeEmu([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(UTF8Marshaler))]string homeFolder, IntPtr windowHandle, IntPtr dxViewerHandle, [MarshalAs(UnmanagedType.I1)]bool noAudio, [MarshalAs(UnmanagedType.I1)]bool noVideo, [MarshalAs(UnmanagedType.I1)]bool noInput); [DllImport(DLLPath)] public static extern void Release(); [DllImport(DLLPath)] public static extern void SetDisplayLanguage(Language lang); [DllImport(DLLPath)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool IsRunning(); [DllImport(DLLPath)] public static extern void LoadROM([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(UTF8Marshaler))]string filename, Int32 archiveFileIndex, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8Marshaler))]string ipsFile); [DllImport(DLLPath)] public static extern void AddKnownGameFolder([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(UTF8Marshaler))]string folder); [DllImport(DLLPath, EntryPoint = "GetArchiveRomList")] private static extern IntPtr GetArchiveRomListWrapper([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(UTF8Marshaler))]string filename); public static List GetArchiveRomList(string filename) { return new List(PtrToStringUtf8(InteropEmu.GetArchiveRomListWrapper(filename)).Split(new string[] { "[!|!]" }, StringSplitOptions.RemoveEmptyEntries)); } [DllImport(DLLPath)] public static extern void SetMousePosition(double x, double y); [DllImport(DLLPath)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool HasZapper(); [DllImport(DLLPath)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool HasArkanoidPaddle(); [DllImport(DLLPath)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool HasFourScore(); [DllImport(DLLPath)] public static extern void SetControllerType(int port, ControllerType type); [DllImport(DLLPath)] public static extern void SetControllerKeys(int port, KeyMappingSet mapping); [DllImport(DLLPath)] public static extern void SetExpansionDevice(ExpansionPortDevice device); [DllImport(DLLPath)] public static extern void SetConsoleType(ConsoleType type); [DllImport(DLLPath)] public static extern void SetEmulatorKeys(EmulatorKeyMappingSet mappings); [DllImport(DLLPath)] public static extern ControllerType GetControllerType(int port); [DllImport(DLLPath)] public static extern ExpansionPortDevice GetExpansionDevice(); [DllImport(DLLPath)] public static extern ConsoleType GetConsoleType(); [DllImport(DLLPath)] public static extern void UpdateInputDevices(); [DllImport(DLLPath)] public static extern UInt32 GetPressedKey(); [DllImport(DLLPath)] public static extern UInt32 GetKeyCode([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(UTF8Marshaler))]string keyName); [DllImport(DLLPath, EntryPoint="GetKeyName")] private static extern IntPtr GetKeyNameWrapper(UInt32 key); [DllImport(DLLPath)] public static extern void SetKeyState(Int32 scanCode, [MarshalAs(UnmanagedType.I1)]bool pressed); [DllImport(DLLPath)] public static extern void ResetKeyState(); [DllImport(DLLPath)] public static extern void Run(); [DllImport(DLLPath)] public static extern void Pause(); [DllImport(DLLPath)] public static extern void Resume(); [DllImport(DLLPath)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool IsPaused(); [DllImport(DLLPath)] public static extern void Stop(); [DllImport(DLLPath, EntryPoint = "GetRomInfo")] private static extern UInt32 GetRomInfoWrapper(ref InteropRomInfo romInfo, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8Marshaler))]string filename = "", Int32 archiveFileIndex = -1); [DllImport(DLLPath)] public static extern void Reset(); [DllImport(DLLPath)] public static extern void ResetLagCounter(); [DllImport(DLLPath)] public static extern void StartServer(UInt16 port, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8Marshaler))]string hostPlayerName); [DllImport(DLLPath)] public static extern void StopServer(); [DllImport(DLLPath)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool IsServerRunning(); [DllImport(DLLPath)] public static extern void Connect([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(UTF8Marshaler))]string host, UInt16 port, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(UTF8Marshaler))]string playerName, [MarshalAs(UnmanagedType.I1)]bool spectator); [DllImport(DLLPath)] public static extern void Disconnect(); [DllImport(DLLPath)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool IsConnected(); [DllImport(DLLPath)] public static extern Int32 NetPlayGetAvailableControllers(); [DllImport(DLLPath)] public static extern void NetPlaySelectController(Int32 controllerPort); [DllImport(DLLPath)] public static extern ControllerType NetPlayGetControllerType(Int32 controllerPort); [DllImport(DLLPath)] public static extern Int32 NetPlayGetControllerPort(); [DllImport(DLLPath)] public static extern void TakeScreenshot(); [DllImport(DLLPath)] public static extern IntPtr RegisterNotificationCallback(NotificationListener.NotificationCallback callback); [DllImport(DLLPath)] public static extern void UnregisterNotificationCallback(IntPtr notificationListener); [DllImport(DLLPath)] public static extern void DisplayMessage([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(UTF8Marshaler))]string title, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(UTF8Marshaler))]string message, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8Marshaler))]string param1 = null); [DllImport(DLLPath, EntryPoint= "GetLog")] private static extern IntPtr GetLogWrapper(); [DllImport(DLLPath)] public static extern void MoviePlay([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(UTF8Marshaler))]string filename); [DllImport(DLLPath)] public static extern void MovieRecord([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(UTF8Marshaler))]string filename, [MarshalAs(UnmanagedType.I1)]bool reset); [DllImport(DLLPath)] public static extern void MovieStop(); [DllImport(DLLPath)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool MoviePlaying(); [DllImport(DLLPath)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool MovieRecording(); [DllImport(DLLPath)] public static extern void WaveRecord([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(UTF8Marshaler))]string filename); [DllImport(DLLPath)] public static extern void WaveStop(); [DllImport(DLLPath)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool WaveIsRecording(); [DllImport(DLLPath)] public static extern Int32 RomTestRun([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(UTF8Marshaler))]string filename); [DllImport(DLLPath)] public static extern void RomTestRecord([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(UTF8Marshaler))]string filename, [MarshalAs(UnmanagedType.I1)]bool reset); [DllImport(DLLPath)] public static extern void RomTestRecordFromMovie([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(UTF8Marshaler))]string testFilename, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(UTF8Marshaler))]string movieFilename); [DllImport(DLLPath)] public static extern void RomTestRecordFromTest([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(UTF8Marshaler))]string newTestFilename, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(UTF8Marshaler))]string existingTestFilename); [DllImport(DLLPath)] public static extern void RomTestStop(); [DllImport(DLLPath)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool RomTestRecording(); [DllImport(DLLPath)] public static extern void SaveState(UInt32 stateIndex); [DllImport(DLLPath)] public static extern void LoadState(UInt32 stateIndex); [DllImport(DLLPath)] public static extern Int64 GetStateInfo(UInt32 stateIndex); [DllImport(DLLPath)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool IsNsf(); [DllImport(DLLPath)] public static extern void NsfSelectTrack(Byte trackNumber); [DllImport(DLLPath)] public static extern Int32 NsfGetCurrentTrack(); [DllImport(DLLPath, EntryPoint = "NsfGetHeader")] private static extern void NsfGetHeaderWrapper(out NsfHeader header); [DllImport(DLLPath)] public static extern void NsfSetNsfConfig(Int32 autoDetectSilenceDelay, Int32 moveToNextTrackTime, [MarshalAs(UnmanagedType.I1)]bool disableApuIrqs); [DllImport(DLLPath)] public static extern UInt32 FdsGetSideCount(); [DllImport(DLLPath)] public static extern void FdsEjectDisk(); [DllImport(DLLPath)] public static extern void FdsInsertDisk(UInt32 diskNumber); [DllImport(DLLPath)] public static extern void FdsSwitchDiskSide(); [DllImport(DLLPath)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool IsVsSystem(); [DllImport(DLLPath)] public static extern void VsInsertCoin(UInt32 port); [DllImport(DLLPath)] public static extern void VsSetGameConfig(PpuModel model, VsInputType inputType, byte dipSwitches); [DllImport(DLLPath)] public static extern void SetCheats([MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)]InteropCheatInfo[] cheats, UInt32 length); [DllImport(DLLPath)] private static extern void SetFlags(EmulationFlags flags); [DllImport(DLLPath)] private static extern void ClearFlags(EmulationFlags flags); [DllImport(DLLPath)] public static extern void SetRamPowerOnState(RamPowerOnState state); [DllImport(DLLPath)] public static extern void SetMasterVolume(double volume); [DllImport(DLLPath)] public static extern void SetChannelVolume(AudioChannel channel, double volume); [DllImport(DLLPath)] public static extern void SetSampleRate(UInt32 sampleRate); [DllImport(DLLPath)] public static extern void SetAudioLatency(UInt32 msLatency); [DllImport(DLLPath)] public static extern void SetStereoFilter(StereoFilter stereoFilter); [DllImport(DLLPath)] public static extern void SetStereoDelay(Int32 delay); [DllImport(DLLPath)] public static extern void SetStereoPanningAngle(double angle); [DllImport(DLLPath)] public static extern void SetReverbParameters(double strength, double delay); [DllImport(DLLPath)] public static extern void SetCrossFeedRatio(UInt32 ratio); [DllImport(DLLPath)] public static extern void SetNesModel(NesModel model); [DllImport(DLLPath)] public static extern void SetEmulationSpeed(UInt32 emulationSpeed); [DllImport(DLLPath)] public static extern void IncreaseEmulationSpeed(); [DllImport(DLLPath)] public static extern void DecreaseEmulationSpeed(); [DllImport(DLLPath)] public static extern UInt32 GetEmulationSpeed(); [DllImport(DLLPath)] public static extern void SetTurboSpeed(UInt32 turboSpeed); [DllImport(DLLPath)] public static extern void SetOverclockRate(UInt32 overclockRate, [MarshalAs(UnmanagedType.I1)]bool adjustApu); [DllImport(DLLPath)] public static extern void SetPpuNmiConfig(UInt32 extraScanlinesBeforeNmi, UInt32 extraScanlineAfterNmi); [DllImport(DLLPath)] public static extern void SetOverscanDimensions(UInt32 left, UInt32 right, UInt32 top, UInt32 bottom); [DllImport(DLLPath)] public static extern void SetVideoScale(double scale); [DllImport(DLLPath)] public static extern void SetVideoAspectRatio(VideoAspectRatio aspectRatio); [DllImport(DLLPath)] public static extern void SetVideoFilter(VideoFilterType filter); [DllImport(DLLPath)] public static extern void SetVideoResizeFilter(VideoResizeFilter filter); [DllImport(DLLPath)] public static extern void SetRgbPalette(byte[] palette); [DllImport(DLLPath)] public static extern void SetPictureSettings(double brightness, double contrast, double saturation, double hue, double scanlineIntensity); [DllImport(DLLPath)] public static extern void SetNtscFilterSettings(double artifacts, double bleed, double fringing, double gamma, double resolution, double sharpness, [MarshalAs(UnmanagedType.I1)]bool mergeFields); [DllImport(DLLPath)] public static extern void SetInputDisplaySettings(byte visiblePorts, InputDisplayPosition displayPosition, [MarshalAs(UnmanagedType.I1)]bool displayHorizontally); [DllImport(DLLPath)] public static extern void SetAutoSaveOptions(UInt32 delayInMinutes, [MarshalAs(UnmanagedType.I1)]bool showMessage); [DllImport(DLLPath, EntryPoint="GetRgbPalette")] private static extern void GetRgbPaletteWrapper(IntPtr paletteBuffer); [DllImport(DLLPath, EntryPoint="GetScreenSize")] private static extern void GetScreenSizeWrapper(out ScreenSize size, [MarshalAs(UnmanagedType.I1)]bool ignoreScale); [DllImport(DLLPath, EntryPoint= "GetAudioDevices")] private static extern IntPtr GetAudioDevicesWrapper(); [DllImport(DLLPath)] public static extern void SetAudioDevice(string audioDevice); [DllImport(DLLPath)] public static extern void DebugInitialize(); [DllImport(DLLPath)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool DebugIsDebuggerRunning(); [DllImport(DLLPath)] public static extern void DebugRelease(); [DllImport(DLLPath)] public static extern void DebugSetFlags(DebuggerFlags flags); [DllImport(DLLPath)] public static extern void DebugGetState(ref DebugState state); [DllImport(DLLPath)] public static extern void DebugSetState(DebugState state); [DllImport(DLLPath)] public static extern void DebugSetBreakpoints([MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)]InteropBreakpoint[] breakpoints, UInt32 length); [DllImport(DLLPath)] public static extern void DebugSetLabel(UInt32 address, AddressType addressType, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(UTF8Marshaler))]string label, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8Marshaler))]string comment); [DllImport(DLLPath)] public static extern void DebugStep(UInt32 count); [DllImport(DLLPath)] public static extern void DebugPpuStep(UInt32 count); [DllImport(DLLPath)] public static extern void DebugStepCycles(UInt32 count); [DllImport(DLLPath)] public static extern void DebugStepOut(); [DllImport(DLLPath)] public static extern void DebugStepOver(); [DllImport(DLLPath)] public static extern void DebugRun(); [DllImport(DLLPath)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool DebugIsExecutionStopped(); [DllImport(DLLPath)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool DebugIsCodeChanged(); [DllImport(DLLPath)] public static extern Byte DebugGetMemoryValue(UInt32 addr); [DllImport(DLLPath)] public static extern Int32 DebugGetRelativeAddress(UInt32 absoluteAddr, AddressType type); [DllImport(DLLPath)] public static extern Int32 DebugGetAbsoluteAddress(UInt32 relativeAddr); [DllImport(DLLPath)] public static extern Int32 DebugGetMemorySize(DebugMemoryType type); [DllImport(DLLPath)] public static extern void DebugGetAbsoluteAddressAndType(UInt32 relativeAddr, ref AddressTypeInfo addressTypeInfo); [DllImport(DLLPath)] public static extern void DebugSetPpuViewerScanlineCycle(Int32 scanline, Int32 cycle); [DllImport(DLLPath)] public static extern void DebugSetNextStatement(UInt16 addr); [DllImport(DLLPath)] public static extern Int32 DebugEvaluateExpression([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(UTF8Marshaler))]string expression, out EvalResultType resultType); [DllImport(DLLPath)] public static extern void DebugStartTraceLogger(TraceLoggerOptions options); [DllImport(DLLPath)] public static extern void DebugStopTraceLogger(); [DllImport(DLLPath)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool DebugLoadCdlFile([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(UTF8Marshaler))]string cdlFilepath); [DllImport(DLLPath)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool DebugSaveCdlFile([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(UTF8Marshaler))]string cdlFilepath); [DllImport(DLLPath)] public static extern void DebugGetCdlRatios(ref CdlRatios ratios); [DllImport(DLLPath)] public static extern void DebugResetCdlLog(); [DllImport(DLLPath)] public static extern void DebugResetMemoryAccessCounts(); [DllImport(DLLPath)] public static extern void DebugResetProfiler(); [DllImport(DLLPath, EntryPoint = "DebugGetCode")] private static extern IntPtr DebugGetCodeWrapper(); public static string DebugGetCode() { return PtrToStringUtf8(InteropEmu.DebugGetCodeWrapper()); } [DllImport(DLLPath, EntryPoint="DebugGetMemoryState")] private static extern UInt32 DebugGetMemoryStateWrapper(DebugMemoryType type, IntPtr buffer); public static byte[] DebugGetMemoryState(DebugMemoryType type) { byte[] buffer = new byte[10485760]; //10mb buffer GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned); try { UInt32 memorySize = InteropEmu.DebugGetMemoryStateWrapper(type, handle.AddrOfPinnedObject()); Array.Resize(ref buffer, (int)memorySize); } finally { handle.Free(); } return buffer; } [DllImport(DLLPath, EntryPoint="DebugSetMemoryState")] private static extern void DebugSetMemoryStateWrapper(DebugMemoryType type, IntPtr buffer); public static void DebugSetMemoryState(DebugMemoryType type, byte[] data) { GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned); try { InteropEmu.DebugSetMemoryStateWrapper(type, handle.AddrOfPinnedObject()); } finally { handle.Free(); } } public static byte[] DebugGetInternalRam() { byte[] buffer = new byte[0x800]; GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned); try { UInt32 memorySize = InteropEmu.DebugGetMemoryStateWrapper(DebugMemoryType.InternalRam, handle.AddrOfPinnedObject()); Array.Resize(ref buffer, (int)memorySize); } finally { handle.Free(); } return buffer; } [DllImport(DLLPath, EntryPoint="DebugGetNametable")] private static extern void DebugGetNametableWrapper(UInt32 nametableIndex, IntPtr frameBuffer, IntPtr tileData, IntPtr attributeData); public static void DebugGetNametable(int nametableIndex, out byte[] frameData, out byte[] tileData, out byte[] attributeData) { frameData = new byte[256*240*4]; tileData = new byte[32*30]; attributeData = new byte[32*30]; GCHandle hFrameData = GCHandle.Alloc(frameData, GCHandleType.Pinned); GCHandle hTileData = GCHandle.Alloc(tileData, GCHandleType.Pinned); GCHandle hAttributeData = GCHandle.Alloc(attributeData, GCHandleType.Pinned); try { InteropEmu.DebugGetNametableWrapper((UInt32)nametableIndex, hFrameData.AddrOfPinnedObject(), hTileData.AddrOfPinnedObject(), hAttributeData.AddrOfPinnedObject()); } finally { hFrameData.Free(); hTileData.Free(); hAttributeData.Free(); } } [DllImport(DLLPath, EntryPoint="DebugGetChrBank")] private static extern void DebugGetChrBankWrapper(UInt32 bankIndex, IntPtr frameBuffer, Byte palette, [MarshalAs(UnmanagedType.I1)]bool largeSprites, CdlHighlightType highlightType); public static byte[] DebugGetChrBank(int bankIndex, int palette, bool largeSprites, CdlHighlightType highlightType) { byte[] frameData = new byte[128*128*4]; GCHandle hFrameData = GCHandle.Alloc(frameData, GCHandleType.Pinned); try { InteropEmu.DebugGetChrBankWrapper((UInt32)bankIndex, hFrameData.AddrOfPinnedObject(), (Byte)palette, largeSprites, highlightType); } finally { hFrameData.Free(); } return frameData; } [DllImport(DLLPath, EntryPoint="DebugGetSprites")] private static extern void DebugGetSpritesWrapper(IntPtr frameBuffer); public static byte[] DebugGetSprites() { byte[] frameData = new byte[64*128*4]; GCHandle hFrameData = GCHandle.Alloc(frameData, GCHandleType.Pinned); try { InteropEmu.DebugGetSpritesWrapper(hFrameData.AddrOfPinnedObject()); } finally { hFrameData.Free(); } return frameData; } [DllImport(DLLPath, EntryPoint="DebugGetPalette")] private static extern void DebugGetPaletteWrapper(IntPtr frameBuffer); public static byte[] DebugGetPalette() { byte[] frameData = new byte[4*8*4]; GCHandle hFrameData = GCHandle.Alloc(frameData, GCHandleType.Pinned); try { InteropEmu.DebugGetPaletteWrapper(hFrameData.AddrOfPinnedObject()); } finally { hFrameData.Free(); } return frameData; } [DllImport(DLLPath, EntryPoint= "DebugGetProfilerData")] private static extern void DebugGetProfilerDataWrapper(IntPtr profilerData, ProfilerDataType dataType); public static Int64[] DebugGetProfilerData(ProfilerDataType dataType) { Int64[] profileData = new Int64[InteropEmu.DebugGetMemorySize(DebugMemoryType.PrgRom) + 2]; GCHandle hProfilerData = GCHandle.Alloc(profileData, GCHandleType.Pinned); try { InteropEmu.DebugGetProfilerDataWrapper(hProfilerData.AddrOfPinnedObject(), dataType); } finally { hProfilerData.Free(); } return profileData; } [DllImport(DLLPath, EntryPoint= "DebugGetMemoryAccessCounts")] private static extern void DebugGetMemoryAccessCountsWrapper(AddressType type, MemoryOperationType operationType, IntPtr counts, [MarshalAs(UnmanagedType.I1)]bool forUninitReads); public static Int32[] DebugGetMemoryAccessCounts(AddressType type, MemoryOperationType operationType, bool forUninitReads) { int size = 0; switch(type) { case AddressType.InternalRam: size = 0x2000; break; case AddressType.PrgRom: size = InteropEmu.DebugGetMemorySize(DebugMemoryType.PrgRom); break; case AddressType.WorkRam: size = InteropEmu.DebugGetMemorySize(DebugMemoryType.WorkRam); break; case AddressType.SaveRam: size = InteropEmu.DebugGetMemorySize(DebugMemoryType.SaveRam); break; } Int32[] counts = new Int32[size]; if(size > 0) { GCHandle hCounts = GCHandle.Alloc(counts, GCHandleType.Pinned); try { InteropEmu.DebugGetMemoryAccessCountsWrapper(type, operationType, hCounts.AddrOfPinnedObject(), forUninitReads); } finally { hCounts.Free(); } } return counts; } [DllImport(DLLPath, EntryPoint="DebugGetCallstack")] private static extern void DebugGetCallstackWrapper(IntPtr callstackAbsolute, IntPtr callstackRelative); public static void DebugGetCallstack(out Int32[] callstackAbsolute, out Int32[] callstackRelative) { callstackAbsolute = new Int32[1024]; callstackRelative = new Int32[1024]; GCHandle hAbsolute = GCHandle.Alloc(callstackAbsolute, GCHandleType.Pinned); GCHandle hRelative = GCHandle.Alloc(callstackRelative, GCHandleType.Pinned); try { InteropEmu.DebugGetCallstackWrapper(hAbsolute.AddrOfPinnedObject(), hRelative.AddrOfPinnedObject()); } finally { hAbsolute.Free(); hRelative.Free(); } } [DllImport(DLLPath, EntryPoint = "DebugGetFunctionEntryPoints")] private static extern void DebugGetFunctionEntryPointsWrapper(IntPtr callstackAbsolute); public static Int32[] DebugGetFunctionEntryPoints() { Int32[] entryPoints = new Int32[32768]; GCHandle hEntryPoints = GCHandle.Alloc(entryPoints, GCHandleType.Pinned); try { InteropEmu.DebugGetFunctionEntryPointsWrapper(hEntryPoints.AddrOfPinnedObject()); } finally { hEntryPoints.Free(); } return entryPoints; } [DllImport(DLLPath, EntryPoint = "DebugGetPpuScroll")] private static extern UInt32 DebugGetPpuScrollWrapper(); public static void DebugGetPpuScroll(out int xScroll, out int yScroll) { UInt32 ppuScroll = InteropEmu.DebugGetPpuScrollWrapper(); xScroll = (int)ppuScroll & 0xFFFF; yScroll = (int)(ppuScroll >> 16) & 0xFFFF; } public static NsfHeader NsfGetHeader() { NsfHeader header = new NsfHeader(); NsfGetHeaderWrapper(out header); return header; } public static RomInfo GetRomInfo(string filename = "", Int32 archiveFileIndex = -1) { InteropRomInfo romInfo = new InteropRomInfo(); InteropEmu.GetRomInfoWrapper(ref romInfo, filename, archiveFileIndex); return new RomInfo(romInfo); } public static ScreenSize GetScreenSize(bool ignoreScale) { ScreenSize size; GetScreenSizeWrapper(out size, ignoreScale); return size; } public static void SetFlag(EmulationFlags flag, bool value) { if(value) { InteropEmu.SetFlags(flag); } else { InteropEmu.ClearFlags(flag); } } public static string GetMesenVersion() { UInt32 version = GetMesenVersionWrapper(); UInt32 revision = version & 0xFF; UInt32 minor = (version >> 8) & 0xFF; UInt32 major = (version >> 16) & 0xFFFF; return string.Format("{0}.{1}.{2}", major.ToString(), minor.ToString(), revision.ToString()); } public static Int32[] GetRgbPalette() { Int32[] paleteData = new Int32[64]; GCHandle hPaletteData = GCHandle.Alloc(paleteData, GCHandleType.Pinned); try { InteropEmu.GetRgbPaletteWrapper(hPaletteData.AddrOfPinnedObject()); } finally { hPaletteData.Free(); } return paleteData; } public static string GetLog() { return PtrToStringUtf8(InteropEmu.GetLogWrapper()).Replace("\n", Environment.NewLine); } public static string GetKeyName(UInt32 key) { return PtrToStringUtf8(InteropEmu.GetKeyNameWrapper(key)); } public static List GetAudioDevices() { return new List(PtrToStringUtf8(InteropEmu.GetAudioDevicesWrapper()).Split(new string[1] { "||" }, StringSplitOptions.RemoveEmptyEntries )); } private static string PtrToStringUtf8(IntPtr ptr) { if(ptr == IntPtr.Zero) { return ""; } int len = 0; while(System.Runtime.InteropServices.Marshal.ReadByte(ptr, len) != 0) { len++; } if(len == 0) { return ""; } byte[] array = new byte[len]; System.Runtime.InteropServices.Marshal.Copy(ptr, array, 0, len); return System.Text.Encoding.UTF8.GetString(array); } public enum ConsoleNotificationType { GameLoaded = 0, StateLoaded = 1, GameReset = 2, GamePaused = 3, GameResumed = 4, GameStopped = 5, CodeBreak = 6, CheatAdded = 7, CheatRemoved = 8, PpuFrameDone = 9, MovieEnded = 10, ResolutionChanged = 11, FdsDiskChanged = 12, FdsBiosNotFound = 13, ConfigChanged = 14, DisconnectedFromServer = 15, PpuViewerDisplayFrame = 16, } public enum ControllerType { None = 0, StandardController = 1, Zapper = 2, ArkanoidController = 3, } public enum ExpansionPortDevice { None = 0, Zapper = 1, FourPlayerAdapter = 2, ArkanoidController = 3, OekaKidsTablet = 4, } public enum VsInputType { Default = 0, TypeA = 1, TypeB = 2, TypeC = 3, TypeD = 4, TypeE = 5 } public enum PpuModel { Ppu2C02 = 0, Ppu2C03 = 1, Ppu2C04A = 2, Ppu2C04B = 3, Ppu2C04C = 4, Ppu2C04D = 5, Ppu2C05A = 6, Ppu2C05B = 7, Ppu2C05C = 8, Ppu2C05D = 9, Ppu2C05E = 10, } public struct KeyMappingSet { public KeyMapping Mapping1; public KeyMapping Mapping2; public KeyMapping Mapping3; public KeyMapping Mapping4; public UInt32 TurboSpeed; } public struct KeyMapping { public UInt32 A; public UInt32 B; public UInt32 Up; public UInt32 Down; public UInt32 Left; public UInt32 Right; public UInt32 Start; public UInt32 Select; public UInt32 TurboA; public UInt32 TurboB; public UInt32 TurboStart; public UInt32 TurboSelect; } public enum StereoFilter { None = 0, Delay = 1, Panning = 2, } public struct ScreenSize { public Int32 Width; public Int32 Height; } public enum InputDisplayPosition { TopLeft = 0, TopRight = 1, BottomLeft = 2, BottomRight = 3 } public class NotificationEventArgs { public ConsoleNotificationType NotificationType; } public class NotificationListener : IDisposable { public delegate void NotificationCallback(int type); public delegate void NotificationEventHandler(NotificationEventArgs e); public event NotificationEventHandler OnNotification; //Need to keep a reference to this callback, or it will get garbage collected (since the only reference to it is on the native side) NotificationCallback _callback; IntPtr _notificationListener; public NotificationListener() { _callback = (int type) => { this.ProcessNotification(type); }; _notificationListener = InteropEmu.RegisterNotificationCallback(_callback); } public void Dispose() { InteropEmu.UnregisterNotificationCallback(_notificationListener); } public void ProcessNotification(int type) { if(this.OnNotification != null) { this.OnNotification(new NotificationEventArgs() { NotificationType = (ConsoleNotificationType)type }); } } } } public struct CdlRatios { public float CodeRatio; public float DataRatio; public float PrgRatio; public float ChrRatio; public float ChrReadRatio; public float ChrDrawnRatio; } public enum CdlHighlightType { None = 0, HighlightUsed = 1, HighlightUnused = 2 } public struct DebugState { public CPUState CPU; public PPUDebugState PPU; public CartridgeState Cartridge; } public struct CartridgeState { public UInt32 PrgRomSize; public UInt32 ChrRomSize; public UInt32 ChrRamSize; public UInt32 PrgPageCount; public UInt32 PrgPageSize; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] public UInt32[] PrgSelectedPages; public UInt32 ChrPageCount; public UInt32 ChrPageSize; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] public UInt32[] ChrSelectedPages; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] public UInt32[] Nametables; } public struct PPUDebugState { public PPUControlFlags ControlFlags; public PPUStatusFlags StatusFlags; public PPUState State; public Int32 Scanline; public UInt32 Cycle; public UInt32 FrameCount; } public struct PPUState { public Byte Control; public Byte Mask; public Byte Status; public UInt32 SpriteRamAddr; public UInt16 VideoRamAddr; public Byte XScroll; public UInt16 TmpVideoRamAddr; public Byte WriteToggle; public UInt16 HighBitShift; public UInt16 LowBitShift; } public struct PPUControlFlags { public Byte VerticalWrite; public UInt16 SpritePatternAddr; public UInt16 BackgroundPatternAddr; public Byte LargeSprites; public Byte VBlank; public Byte Grayscale; public Byte BackgroundMask; public Byte SpriteMask; public Byte BackgroundEnabled; public Byte SpritesEnabled; public Byte IntensifyRed; public Byte IntensifyGreen; public Byte IntensifyBlue; public Byte GetMask() { byte mask = 0; if(Grayscale != 0) mask |= 0x01; if(BackgroundMask != 0) mask |= 0x02; if(SpriteMask != 0) mask |= 0x04; if(BackgroundEnabled != 0) mask |= 0x08; if(SpritesEnabled != 0) mask |= 0x10; if(IntensifyBlue != 0) mask |= 0x80; if(IntensifyRed != 0) mask |= 0x20; if(IntensifyGreen != 0) mask |= 0x40; return mask; } public Byte GetControl() { byte control = 0; if(VerticalWrite != 0) control |= 0x04; if(SpritePatternAddr == 0x1000) control |= 0x08; if(BackgroundPatternAddr != 0x1000) control |= 0x10; if(LargeSprites != 0) control |= 0x20; if(VBlank != 0) control |= 0x80; return control; } } public struct PPUStatusFlags { public Byte SpriteOverflow; public Byte Sprite0Hit; public Byte VerticalBlank; public Byte GetStatus() { byte status = 0; if(SpriteOverflow != 0) status |= 0x20; if(Sprite0Hit != 0) status |= 0x40; if(VerticalBlank != 0) status |= 0x80; return status; } } public struct CPUState { public UInt16 PC; public Byte SP; public Byte A; public Byte X; public Byte Y; public Byte PS; public IRQSource IRQFlag; public Int32 CycleCount; [MarshalAs(UnmanagedType.I1)] public bool NMIFlag; public UInt16 DebugPC; } public struct TraceLoggerOptions { [MarshalAs(UnmanagedType.I1)] public bool ShowByteCode; [MarshalAs(UnmanagedType.I1)] public bool ShowRegisters; [MarshalAs(UnmanagedType.I1)] public bool ShowCpuCycles; [MarshalAs(UnmanagedType.I1)] public bool ShowPpuCycles; [MarshalAs(UnmanagedType.I1)] public bool ShowPpuScanline; [MarshalAs(UnmanagedType.I1)] public bool ShowPpuFrames; [MarshalAs(UnmanagedType.I1)] public bool ShowExtraInfo; [MarshalAs(UnmanagedType.I1)] public bool IndentCode; [MarshalAs(UnmanagedType.I1)] public bool ShowEffectiveAddresses; } public enum ProfilerDataType { FunctionExclusive = 0, FunctionInclusive = 1, Instructions = 2, FunctionCallCount = 3, } [Flags] public enum IRQSource : uint { External = 1, FrameCounter = 2, DMC = 4, } [Flags] public enum PSFlags { Carry = 0x01, Zero = 0x02, Interrupt = 0x04, Decimal = 0x08, Break = 0x10, Reserved = 0x20, Overflow = 0x40, Negative = 0x80 } [Flags] public enum EmulationFlags { Paused = 0x01, ShowFPS = 0x02, VerticalSync = 0x04, AllowInvalidInput = 0x08, RemoveSpriteLimit = 0x10, UseHdPacks = 0x20, HasFourScore = 0x40, PauseOnMovieEnd = 0x0100, PauseWhenInBackground = 0x0200, AllowBackgroundInput = 0x0400, ReduceSoundInBackground = 0x0800, MuteSoundInBackground = 0x1000, FdsFastForwardOnLoad = 0x2000, FdsAutoLoadDisk = 0x4000, Mmc3IrqAltBehavior = 0x8000, SwapDutyCycles = 0x10000, DisableGameDatabase = 0x20000, AutoConfigureInput = 0x40000, ShowLagCounter = 0x80000, SilenceTriangleHighFreq = 0x100000, ReduceDmcPopping = 0x200000, DisableBackground = 0x400000, DisableSprites = 0x800000, ForceBackgroundFirstColumn = 0x1000000, ForceSpritesFirstColumn = 0x2000000, InBackground = 0x40000000, } [Flags] public enum DebuggerFlags { None = 0, PpuPartialDraw = 1, ShowEffectiveAddresses = 2, ShowOnlyDisassembledCode = 4, DisplayOpCodesInLowerCase = 8, DisassembleEverything = 16, DisassembleEverythingButData = 32, } public struct InteropRomInfo { public IntPtr RomNamePointer; public UInt32 Crc32; public UInt32 PrgCrc32; } public class RomInfo { public string RomName; public UInt32 Crc32; public UInt32 PrgCrc32; public RomInfo(InteropRomInfo romInfo) { this.RomName = UTF8Marshaler.GetStringFromIntPtr(romInfo.RomNamePointer); this.Crc32 = romInfo.Crc32; this.PrgCrc32 = romInfo.PrgCrc32; } public string GetRomName() { return Path.GetFileNameWithoutExtension(this.RomName); } public string GetCrcString() { return this.Crc32.ToString("X8"); } public string GetPrgCrcString() { return this.PrgCrc32.ToString("X8"); } }; public struct EmulatorKeyMappingSet { public EmulatorKeyMappings KeySet1; public EmulatorKeyMappings KeySet2; } public struct EmulatorKeyMappings { public UInt32 FastForward; public UInt32 Pause; public UInt32 Reset; public UInt32 MoveToNextStateSlot; public UInt32 MoveToPreviousStateSlot; public UInt32 SaveState; public UInt32 LoadState; public UInt32 SwitchDiskSide; public UInt32 InsertNextDisk; public UInt32 InsertCoin1; public UInt32 InsertCoin2; public UInt32 VsServiceButton; public UInt32 TakeScreenshot; public UInt32 IncreaseSpeed; public UInt32 DecreaseSpeed; } public struct InteropCheatInfo { public CheatType CheatType; public UInt32 ProActionRockyCode; public UInt32 Address; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 9)] public byte[] GameGenieCode; public byte Value; public byte CompareValue; [MarshalAs(UnmanagedType.I1)] public bool UseCompareValue; [MarshalAs(UnmanagedType.I1)] public bool IsRelativeAddress; } public struct InteropBreakpoint { public BreakpointType Type; public Int32 StartAddress; public Int32 EndAddress; [MarshalAs(UnmanagedType.I1)] public bool IsAbsoluteAddress; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1000)] public byte[] Condition; } public struct NsfHeader { [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 5)] public Byte[] Header; public Byte Version; public Byte TotalSongs; public Byte StartingSong; public UInt16 LoadAddress; public UInt16 InitAddress; public UInt16 PlayAddress; [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 256)] public Byte[] SongName; [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 256)] public Byte[] ArtistName; [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 256)] public Byte[] CopyrightHolder; public UInt16 PlaySpeedNtsc; [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 8)] public Byte[] BankSetup; public UInt16 PlaySpeedPal; public Byte Flags; public Byte SoundChips; [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 4)] public Byte[] Padding; [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 256)] public Byte[] RipperName; [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 20000)] public Byte[] TrackName; [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 256)] public Int32[] TrackLength; [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 256)] public Int32[] TrackFade; private string ConvertString(Byte[] input) { string output = Encoding.UTF8.GetString(input, 0, Array.IndexOf(input, (Byte)0)); if(output.Length == 0 || output == "") { return ResourceHelper.GetMessage("NsfUnknownField"); } if(output[0] == 0xFFFD) { //Patch to convert an invalid character at index 0 to a copyright sign //This is usually the case for NSFe files (not sure what the encoding for NSF/NSFe is meant to be. Is it properly defined?) return "©" + output.Substring(1); } return output; } public string GetSongName() { return ConvertString(this.SongName); } public string GetArtistName() { return ConvertString(this.ArtistName); } public string GetCopyrightHolder() { return ConvertString(this.CopyrightHolder); } public string GetRipperName() { return ConvertString(this.RipperName); } public string[] GetTrackNames() { return Encoding.UTF8.GetString(this.TrackName, 0, Array.IndexOf(this.TrackName, (Byte)0)).Split(new string[] { "[!|!]" }, StringSplitOptions.None); } } [Flags] public enum BreakpointType { Global = 0, Execute = 1, Read = 2, Write = 4, ReadVram = 8, WriteVram = 16 } public enum EvalResultType { Numeric = 0, Boolean = 1, Invalid = 2 } public enum NesModel { Auto = 0, NTSC = 1, PAL = 2, Dendy = 3, } public enum ConsoleType { Nes = 0, Famicom = 1 } public enum RamPowerOnState { AllZeros = 0, AllOnes = 1, Random = 2 } public enum AudioChannel { Square1 = 0, Square2 = 1, Triangle = 2, Noise = 3, DMC = 4, FDS = 5, MMC5 = 6, VRC6 = 7, VRC7 = 8, Namco163 = 9, Sunsoft5B = 10 } public enum VideoFilterType { None = 0, NTSC = 1, xBRZ2x = 2, xBRZ3x = 3, xBRZ4x = 4, xBRZ5x = 5, xBRZ6x = 6, HQ2x = 7, HQ3x = 8, HQ4x = 9, Scale2x = 10, Scale3x = 11, Scale4x = 12, _2xSai = 13, Super2xSai = 14, SuperEagle = 15, Prescale2x = 16, Prescale3x = 17, Prescale4x = 18 } public enum VideoResizeFilter { NearestNeighbor = 0, Bilinear = 1 } public enum VideoAspectRatio { Auto = 0, NTSC = 1, PAL = 2, Standard = 3, Widescreen = 4 } public enum DebugMemoryType { CpuMemory = 0, PpuMemory = 1, PaletteMemory = 2, SpriteMemory = 3, SecondarySpriteMemory = 4, PrgRom = 5, ChrRom = 6, ChrRam = 7, WorkRam = 8, SaveRam = 9, InternalRam = 10 } public enum AddressType { InternalRam = 0, PrgRom = 1, WorkRam = 2, SaveRam = 3, Register = 4 } public enum MemoryOperationType { //Note: Not identical to the C++ enum Read = 0, Write = 1, Exec = 2, } public struct AddressTypeInfo { public Int32 Address; public AddressType Type; } public class MD5Helper { public static string GetMD5Hash(string filename) { if(File.Exists(filename)) { var md5 = System.Security.Cryptography.MD5.Create(); return BitConverter.ToString(md5.ComputeHash(File.ReadAllBytes(filename))).Replace("-", ""); } return null; } } public class UTF8Marshaler : ICustomMarshaler { static UTF8Marshaler _instance; public IntPtr MarshalManagedToNative(object managedObj) { if(managedObj == null) { return IntPtr.Zero; } if(!(managedObj is string)) { throw new MarshalDirectiveException("UTF8Marshaler must be used on a string."); } // not null terminated byte[] strbuf = Encoding.UTF8.GetBytes((string)managedObj); IntPtr buffer = Marshal.AllocHGlobal(strbuf.Length + 1); Marshal.Copy(strbuf, 0, buffer, strbuf.Length); // write the terminating null Marshal.WriteByte(buffer + strbuf.Length, 0); return buffer; } public object MarshalNativeToManaged(IntPtr pNativeData) { return GetStringFromIntPtr(pNativeData); } public void CleanUpNativeData(IntPtr pNativeData) { Marshal.FreeHGlobal(pNativeData); } public void CleanUpManagedData(object managedObj) { } public int GetNativeDataSize() { return -1; } public static ICustomMarshaler GetInstance(string cookie) { if(_instance == null) { return _instance = new UTF8Marshaler(); } return _instance; } public static string GetStringFromIntPtr(IntPtr pNativeData) { int offset = 0; byte b = 0; do { b = Marshal.ReadByte(pNativeData, offset); offset++; } while(b != 0); int length = offset - 1; // should not be null terminated byte[] strbuf = new byte[length]; // skip the trailing null Marshal.Copy((IntPtr)pNativeData, strbuf, 0, length); string data = Encoding.UTF8.GetString(strbuf); return data; } } }