/* PCSX2 - PS2 Emulator for PCs * Copyright (C) 2002-2010 PCSX2 Dev Team * * PCSX2 is free software: you can redistribute it and/or modify it under the terms * of the GNU Lesser General Public License as published by the Free Software Found- * ation, either version 3 of the License, or (at your option) any later version. * * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with PCSX2. * If not, see . */ #include "PrecompiledHeader.h" #include "IopCommon.h" #include #include #include #include "GS.h" #include "Gif.h" #include "CDVD/CDVDisoReader.h" #include "Utilities/pxStreams.h" #include "svnrev.h" SysPluginBindings SysPlugins; bool SysPluginBindings::McdIsPresent( uint port, uint slot ) { return !!Mcd->McdIsPresent( (PS2E_THISPTR) Mcd, port, slot ); } void SysPluginBindings::McdGetSizeInfo( uint port, uint slot, PS2E_McdSizeInfo& outways ) { if( Mcd->McdGetSizeInfo ) Mcd->McdGetSizeInfo( (PS2E_THISPTR) Mcd, port, slot, &outways ); } bool SysPluginBindings::McdIsPSX( uint port, uint slot ) { return Mcd->McdIsPSX( (PS2E_THISPTR) Mcd, port, slot ); } void SysPluginBindings::McdRead( uint port, uint slot, u8 *dest, u32 adr, int size ) { Mcd->McdRead( (PS2E_THISPTR) Mcd, port, slot, dest, adr, size ); } void SysPluginBindings::McdSave( uint port, uint slot, const u8 *src, u32 adr, int size ) { Mcd->McdSave( (PS2E_THISPTR) Mcd, port, slot, src, adr, size ); } void SysPluginBindings::McdEraseBlock( uint port, uint slot, u32 adr ) { Mcd->McdEraseBlock( (PS2E_THISPTR) Mcd, port, slot, adr ); } u64 SysPluginBindings::McdGetCRC( uint port, uint slot ) { return Mcd->McdGetCRC( (PS2E_THISPTR) Mcd, port, slot ); } void SysPluginBindings::McdNextFrame( uint port, uint slot ) { Mcd->McdNextFrame( (PS2E_THISPTR) Mcd, port, slot ); } void SysPluginBindings::McdReIndex( uint port, uint slot, const wxString& filter ) { Mcd->McdReIndex( (PS2E_THISPTR) Mcd, port, slot, filter ); } // ---------------------------------------------------------------------------- // Yay, order of this array shouldn't be important. :) // const PluginInfo tbl_PluginInfo[] = { { "GS", PluginId_GS, PS2E_LT_GS, PS2E_GS_VERSION }, { "PAD", PluginId_PAD, PS2E_LT_PAD, PS2E_PAD_VERSION }, { "SPU2", PluginId_SPU2, PS2E_LT_SPU2, PS2E_SPU2_VERSION }, { "CDVD", PluginId_CDVD, PS2E_LT_CDVD, PS2E_CDVD_VERSION }, { "USB", PluginId_USB, PS2E_LT_USB, PS2E_USB_VERSION }, { "FW", PluginId_FW, PS2E_LT_FW, PS2E_FW_VERSION }, { "DEV9", PluginId_DEV9, PS2E_LT_DEV9, PS2E_DEV9_VERSION }, { NULL }, // See PluginEnums_t for details on the MemoryCard plugin hack. { "Mcd", PluginId_Mcd, 0, 0 }, }; typedef void CALLBACK VoidMethod(); typedef void CALLBACK vMeth(); // shorthand for VoidMethod // ---------------------------------------------------------------------------- struct LegacyApi_CommonMethod { const char* MethodName; // fallback is used if the method is null. If the method is null and fallback is null // also, the plugin is considered incomplete or invalid, and an error is generated. VoidMethod* Fallback; // returns the method name as a wxString, converted from UTF8. wxString GetMethodName( PluginsEnum_t pid ) const { return tbl_PluginInfo[pid].GetShortname() + fromUTF8( MethodName ); } }; // ---------------------------------------------------------------------------- struct LegacyApi_ReqMethod { const char* MethodName; VoidMethod** Dest; // Target function where the binding is saved. // fallback is used if the method is null. If the method is null and fallback is null // also, the plugin is considered incomplete or invalid, and an error is generated. VoidMethod* Fallback; // returns the method name as a wxString, converted from UTF8. wxString GetMethodName( ) const { return fromUTF8( MethodName ); } }; // ---------------------------------------------------------------------------- struct LegacyApi_OptMethod { const char* MethodName; VoidMethod** Dest; // Target function where the binding is saved. // returns the method name as a wxString, converted from UTF8. wxString GetMethodName() const { return fromUTF8( MethodName ); } }; static s32 CALLBACK fallback_freeze(int mode, freezeData *data) { if( mode == FREEZE_SIZE ) data->size = 0; return 0; } static void CALLBACK fallback_keyEvent(keyEvent *ev) {} static void CALLBACK fallback_setSettingsDir(const char* dir) {} static void CALLBACK fallback_setLogDir(const char* dir) {} static void CALLBACK fallback_configure() {} static void CALLBACK fallback_about() {} static s32 CALLBACK fallback_test() { return 0; } _GSvsync GSvsync; _GSopen GSopen; _GSopen2 GSopen2; _GSgifTransfer GSgifTransfer; _GSgifTransfer1 GSgifTransfer1; _GSgifTransfer2 GSgifTransfer2; _GSgifTransfer3 GSgifTransfer3; _GSgifSoftReset GSgifSoftReset; _GSreadFIFO GSreadFIFO; _GSreadFIFO2 GSreadFIFO2; _GSinitReadFIFO GSinitReadFIFO; _GSinitReadFIFO2 GSinitReadFIFO2; _GSchangeSaveState GSchangeSaveState; _GSgetTitleInfo2 GSgetTitleInfo2; _GSmakeSnapshot GSmakeSnapshot; _GSmakeSnapshot2 GSmakeSnapshot2; _GSirqCallback GSirqCallback; _GSprintf GSprintf; _GSsetBaseMem GSsetBaseMem; _GSsetGameCRC GSsetGameCRC; _GSsetFrameSkip GSsetFrameSkip; _GSsetVsync GSsetVsync; _GSsetExclusive GSsetExclusive; _GSsetupRecording GSsetupRecording; _GSreset GSreset; _GSwriteCSR GSwriteCSR; static void CALLBACK GS_makeSnapshot(const char *path) {} static void CALLBACK GS_setGameCRC(u32 crc, int gameopts) {} static void CALLBACK GS_irqCallback(void (*callback)()) {} static void CALLBACK GS_setFrameSkip(int frameskip) {} static void CALLBACK GS_setVsync(int enabled) {} static void CALLBACK GS_setExclusive(int isExcl) {} static void CALLBACK GS_changeSaveState( int, const char* filename ) {} static void CALLBACK GS_printf(int timeout, char *fmt, ...) { va_list list; char msg[512]; va_start(list, fmt); vsprintf(msg, fmt, list); va_end(list); Console.WriteLn(msg); } void CALLBACK GS_getTitleInfo2( char* dest, size_t length ) { // Just return a generic "GS" title -- a plugin actually implementing this feature // should return a title such as "GSdx" or "ZZogl" instead. --air dest[0] = 'G'; dest[1] = 'S'; dest[2] = 0; } #if COPY_GS_PACKET_TO_MTGS == 1 // This legacy passthrough function is needed because the old GS plugins tended to assume that // a PATH1 transfer that didn't EOP needed an automatic EOP (which was needed to avoid a crash // in the BIOS when it starts an XGKICK prior to having an EOP written to VU1 memory). The new // MTGS wraps data around the end of the MTGS buffer, so it often splits PATH1 data into two // transfers now. static void CALLBACK GS_Legacy_gifTransfer( const u32* src, u32 data ) { static __aligned16 u128 path1queue[0x400]; static uint path1size = 0; const u128* src128 = (u128*)src; if( (src128 + data) >= &RingBuffer.m_Ring[RingBufferSize] ) { // the transfer is most likely wrapped/partial. We need to queue it into a linear buffer // and then send it on its way on the next copy. memcpy( path1queue, src128, data*16); path1size = data; } else { if (path1size != 0) { // Previous transfer check. *Most* likely this one should be added to it, but to know for // sure we need to check to see if src points to the head of RingBuffer. If its pointing // to like Ringbuffer[1] instead it means the last transfer finished and this transfer is // a new one. if (src128 == RingBuffer.m_Ring) { pxAssert( (data+path1size) <= 0x400 ); memcpy( &path1queue[path1size], src128, data*16); path1size += data; } GSgifTransfer1( (u32*)path1queue, 0 ); path1size = 0; } else { GSgifTransfer1( (u32*)src128, 0 ); } } } #else // In this case the MTGS thread will only be using the "GSgifTransfer" // callback, which falls back to this function if its an old plugin. // Since GSgifTransfer2 is the least hacky old call-back, and MTGS will // just be using a single gif path, we'll just solely use path 2... static void CALLBACK GS_Legacy_gifTransfer(const u32* src, u32 data) { GSgifTransfer2((u32*)src, data); } #endif static void CALLBACK GS_Legacy_GSreadFIFO2(u64* pMem, int qwc) { while(qwc--) GSreadFIFO(pMem); } // PAD _PADinit PADinit; _PADopen PADopen; _PADstartPoll PADstartPoll; _PADpoll PADpoll; _PADquery PADquery; _PADupdate PADupdate; _PADkeyEvent PADkeyEvent; _PADsetSlot PADsetSlot; _PADqueryMtap PADqueryMtap; _PADWriteEvent PADWriteEvent; static void PAD_update( u32 padslot ) { } // SPU2 _SPU2open SPU2open; _SPU2write SPU2write; _SPU2reset SPU2reset; _SPU2read SPU2read; #ifdef ENABLE_NEW_IOPDMA_SPU2 _SPU2dmaRead SPU2dmaRead; _SPU2dmaWrite SPU2dmaWrite; _SPU2dmaInterrupt SPU2dmaInterrupt; #else _SPU2readDMA4Mem SPU2readDMA4Mem; _SPU2writeDMA4Mem SPU2writeDMA4Mem; _SPU2interruptDMA4 SPU2interruptDMA4; _SPU2readDMA7Mem SPU2readDMA7Mem; _SPU2writeDMA7Mem SPU2writeDMA7Mem; _SPU2setDMABaseAddr SPU2setDMABaseAddr; _SPU2interruptDMA7 SPU2interruptDMA7; _SPU2ReadMemAddr SPU2ReadMemAddr; _SPU2WriteMemAddr SPU2WriteMemAddr; #endif _SPU2setupRecording SPU2setupRecording; _SPU2irqCallback SPU2irqCallback; _SPU2setClockPtr SPU2setClockPtr; _SPU2async SPU2async; // DEV9 _DEV9open DEV9open; _DEV9read8 DEV9read8; _DEV9read16 DEV9read16; _DEV9read32 DEV9read32; _DEV9write8 DEV9write8; _DEV9write16 DEV9write16; _DEV9write32 DEV9write32; #ifdef ENABLE_NEW_IOPDMA_DEV9 _DEV9dmaRead DEV9dmaRead; _DEV9dmaWrite DEV9dmaWrite; _DEV9dmaInterrupt DEV9dmaInterrupt; #else _DEV9readDMA8Mem DEV9readDMA8Mem; _DEV9writeDMA8Mem DEV9writeDMA8Mem; #endif _DEV9irqCallback DEV9irqCallback; _DEV9irqHandler DEV9irqHandler; _DEV9async DEV9async; // USB _USBopen USBopen; _USBread8 USBread8; _USBread16 USBread16; _USBread32 USBread32; _USBwrite8 USBwrite8; _USBwrite16 USBwrite16; _USBwrite32 USBwrite32; _USBasync USBasync; _USBirqCallback USBirqCallback; _USBirqHandler USBirqHandler; _USBsetRAM USBsetRAM; // FW _FWopen FWopen; _FWread32 FWread32; _FWwrite32 FWwrite32; _FWirqCallback FWirqCallback; DEV9handler dev9Handler; USBhandler usbHandler; uptr pDsp[2]; static s32 CALLBACK _hack_PADinit() { return PADinit( 3 ); } // ---------------------------------------------------------------------------- // Important: Contents of this array must match the order of the contents of the // LegacyPluginAPI_Common structure defined in Plugins.h. // static const LegacyApi_CommonMethod s_MethMessCommon[] = { { "init", NULL }, { "close", NULL }, { "shutdown", NULL }, { "keyEvent", (vMeth*)fallback_keyEvent }, { "setSettingsDir", (vMeth*)fallback_setSettingsDir }, { "setLogDir", (vMeth*)fallback_setLogDir }, { "freeze", (vMeth*)fallback_freeze }, { "test", (vMeth*)fallback_test }, { "configure", fallback_configure }, { "about", fallback_about }, { NULL } }; // ---------------------------------------------------------------------------- // GS Mess! // ---------------------------------------------------------------------------- static const LegacyApi_ReqMethod s_MethMessReq_GS[] = { { "GSopen", (vMeth**)&GSopen, NULL }, { "GSvsync", (vMeth**)&GSvsync, NULL }, { "GSgifTransfer", (vMeth**)&GSgifTransfer, (vMeth*)GS_Legacy_gifTransfer }, { "GSgifTransfer2", (vMeth**)&GSgifTransfer2, NULL }, { "GSgifTransfer3", (vMeth**)&GSgifTransfer3, NULL }, { "GSreadFIFO2", (vMeth**)&GSreadFIFO2, (vMeth*)GS_Legacy_GSreadFIFO2 }, { "GSmakeSnapshot", (vMeth**)&GSmakeSnapshot, (vMeth*)GS_makeSnapshot }, { "GSirqCallback", (vMeth**)&GSirqCallback, (vMeth*)GS_irqCallback }, { "GSprintf", (vMeth**)&GSprintf, (vMeth*)GS_printf }, { "GSsetBaseMem", (vMeth**)&GSsetBaseMem, NULL }, { "GSwriteCSR", (vMeth**)&GSwriteCSR, NULL }, { "GSsetGameCRC", (vMeth**)&GSsetGameCRC, (vMeth*)GS_setGameCRC }, { "GSsetFrameSkip", (vMeth**)&GSsetFrameSkip, (vMeth*)GS_setFrameSkip }, { "GSsetVsync", (vMeth**)&GSsetVsync, (vMeth*)GS_setVsync }, { "GSsetExclusive", (vMeth**)&GSsetExclusive, (vMeth*)GS_setExclusive }, { "GSchangeSaveState",(vMeth**)&GSchangeSaveState,(vMeth*)GS_changeSaveState }, { "GSgetTitleInfo2", (vMeth**)&GSgetTitleInfo2, (vMeth*)GS_getTitleInfo2 }, { NULL } }; static const LegacyApi_OptMethod s_MethMessOpt_GS[] = { { "GSopen2", (vMeth**)&GSopen2 }, { "GSreset", (vMeth**)&GSreset }, { "GSsetupRecording", (vMeth**)&GSsetupRecording }, { "GSmakeSnapshot2", (vMeth**)&GSmakeSnapshot2 }, { "GSgifSoftReset", (vMeth**)&GSgifSoftReset }, { "GSreadFIFO", (vMeth**)&GSreadFIFO }, { "GSinitReadFIFO", (vMeth**)&GSinitReadFIFO }, { "GSinitReadFIFO2", (vMeth**)&GSinitReadFIFO2 }, { "GSgifTransfer1", (vMeth**)&GSgifTransfer1 }, { NULL } }; // ---------------------------------------------------------------------------- // PAD Mess! // ---------------------------------------------------------------------------- static s32 CALLBACK PAD_queryMtap( u8 slot ) { return 0; } static s32 CALLBACK PAD_setSlot(u8 port, u8 slot) { return 0; } static const LegacyApi_ReqMethod s_MethMessReq_PAD[] = { { "PADopen", (vMeth**)&PADopen, NULL }, { "PADstartPoll", (vMeth**)&PADstartPoll, NULL }, { "PADpoll", (vMeth**)&PADpoll, NULL }, { "PADquery", (vMeth**)&PADquery, NULL }, { "PADkeyEvent", (vMeth**)&PADkeyEvent, NULL }, // fixme - Following functions are new as of some revison post-0.9.6, and // are for multitap support only. They should either be optional or offer // NOP fallbacks, to allow older plugins to retain functionality. { "PADsetSlot", (vMeth**)&PADsetSlot, (vMeth*)PAD_setSlot }, { "PADqueryMtap", (vMeth**)&PADqueryMtap, (vMeth*)PAD_queryMtap }, { NULL }, }; static const LegacyApi_OptMethod s_MethMessOpt_PAD[] = { { "PADupdate", (vMeth**)&PADupdate }, { "PADWriteEvent", (vMeth**)&PADWriteEvent }, { NULL }, }; // ---------------------------------------------------------------------------- // CDVD Mess! // ---------------------------------------------------------------------------- void CALLBACK CDVD_newDiskCB(void (*callback)()) {} extern int lastReadSize, lastLSN; static s32 CALLBACK CDVD_getBuffer2(u8* buffer) { // TEMP: until I fix all the plugins to use this function style u8* pb = CDVD->getBuffer(); if(pb == NULL) return -2; memcpy( buffer, pb, lastReadSize ); return 0; } static s32 CALLBACK CDVD_readSector(u8* buffer, u32 lsn, int mode) { if(CDVD->readTrack(lsn,mode) < 0) return -1; // TEMP: until all the plugins use the new CDVDgetBuffer style switch (mode) { case CDVD_MODE_2352: lastReadSize = 2352; break; case CDVD_MODE_2340: lastReadSize = 2340; break; case CDVD_MODE_2328: lastReadSize = 2328; break; case CDVD_MODE_2048: lastReadSize = 2048; break; } lastLSN = lsn; return CDVD->getBuffer2(buffer); } static s32 CALLBACK CDVD_getDualInfo(s32* dualType, u32* layer1Start) { u8 toc[2064]; // if error getting toc, settle for single layer disc ;) if(CDVD->getTOC(toc)) return 0; if(toc[14] & 0x60) { if(toc[14] & 0x10) { // otp dvd *dualType = 2; *layer1Start = (toc[25]<<16) + (toc[26]<<8) + (toc[27]) - 0x30000 + 1; } else { // ptp dvd *dualType = 1; *layer1Start = (toc[21]<<16) + (toc[22]<<8) + (toc[23]) - 0x30000 + 1; } } else { // single layer dvd *dualType = 0; *layer1Start = (toc[21]<<16) + (toc[22]<<8) + (toc[23]) - 0x30000 + 1; } return 1; } CDVD_API CDVDapi_Plugin = { // All of these are filled by the plugin manager NULL }; CDVD_API* CDVD = NULL; static const LegacyApi_ReqMethod s_MethMessReq_CDVD[] = { { "CDVDopen", (vMeth**)&CDVDapi_Plugin.open, NULL }, { "CDVDclose", (vMeth**)&CDVDapi_Plugin.close, NULL }, { "CDVDreadTrack", (vMeth**)&CDVDapi_Plugin.readTrack, NULL }, { "CDVDgetBuffer", (vMeth**)&CDVDapi_Plugin.getBuffer, NULL }, { "CDVDreadSubQ", (vMeth**)&CDVDapi_Plugin.readSubQ, NULL }, { "CDVDgetTN", (vMeth**)&CDVDapi_Plugin.getTN, NULL }, { "CDVDgetTD", (vMeth**)&CDVDapi_Plugin.getTD, NULL }, { "CDVDgetTOC", (vMeth**)&CDVDapi_Plugin.getTOC, NULL }, { "CDVDgetDiskType", (vMeth**)&CDVDapi_Plugin.getDiskType, NULL }, { "CDVDgetTrayStatus",(vMeth**)&CDVDapi_Plugin.getTrayStatus, NULL }, { "CDVDctrlTrayOpen", (vMeth**)&CDVDapi_Plugin.ctrlTrayOpen, NULL }, { "CDVDctrlTrayClose",(vMeth**)&CDVDapi_Plugin.ctrlTrayClose, NULL }, { "CDVDnewDiskCB", (vMeth**)&CDVDapi_Plugin.newDiskCB, (vMeth*)CDVD_newDiskCB }, { "CDVDreadSector", (vMeth**)&CDVDapi_Plugin.readSector, (vMeth*)CDVD_readSector }, { "CDVDgetBuffer2", (vMeth**)&CDVDapi_Plugin.getBuffer2, (vMeth*)CDVD_getBuffer2 }, { "CDVDgetDualInfo", (vMeth**)&CDVDapi_Plugin.getDualInfo, (vMeth*)CDVD_getDualInfo }, { NULL } }; static const LegacyApi_OptMethod s_MethMessOpt_CDVD[] = { { NULL } }; // ---------------------------------------------------------------------------- // SPU2 Mess! // ---------------------------------------------------------------------------- // manualized reset that writes core reset registers of the SPU2 plugin: static void CALLBACK SPU2_Reset() { SPU2write( 0x1f90019A, 1<<15 ); // core 0 SPU2write( 0x1f90059A, 1<<15 ); // core 1 } static const LegacyApi_ReqMethod s_MethMessReq_SPU2[] = { { "SPU2open", (vMeth**)&SPU2open, NULL }, { "SPU2reset", (vMeth**)&SPU2reset, SPU2_Reset }, { "SPU2write", (vMeth**)&SPU2write, NULL }, { "SPU2read", (vMeth**)&SPU2read, NULL }, #ifdef ENABLE_NEW_IOPDMA_SPU2 { "SPU2dmaRead", (vMeth**)&SPU2dmaRead, NULL }, { "SPU2dmaWrite", (vMeth**)&SPU2dmaWrite, NULL }, { "SPU2dmaInterrupt", (vMeth**)&SPU2dmaInterrupt, NULL }, #else { "SPU2readDMA4Mem", (vMeth**)&SPU2readDMA4Mem, NULL }, { "SPU2readDMA7Mem", (vMeth**)&SPU2readDMA7Mem, NULL }, { "SPU2writeDMA4Mem", (vMeth**)&SPU2writeDMA4Mem, NULL }, { "SPU2writeDMA7Mem", (vMeth**)&SPU2writeDMA7Mem, NULL }, { "SPU2interruptDMA4", (vMeth**)&SPU2interruptDMA4,NULL }, { "SPU2interruptDMA7", (vMeth**)&SPU2interruptDMA7,NULL }, { "SPU2ReadMemAddr", (vMeth**)&SPU2ReadMemAddr, NULL }, #endif { "SPU2irqCallback", (vMeth**)&SPU2irqCallback, NULL }, { NULL } }; static const LegacyApi_OptMethod s_MethMessOpt_SPU2[] = { { "SPU2setClockPtr", (vMeth**)&SPU2setClockPtr }, { "SPU2async", (vMeth**)&SPU2async }, #ifndef ENABLE_NEW_IOPDMA_SPU2 { "SPU2WriteMemAddr", (vMeth**)&SPU2WriteMemAddr }, { "SPU2setDMABaseAddr", (vMeth**)&SPU2setDMABaseAddr}, #endif { "SPU2setupRecording", (vMeth**)&SPU2setupRecording}, { NULL } }; // ---------------------------------------------------------------------------- // DEV9 Mess! // ---------------------------------------------------------------------------- static const LegacyApi_ReqMethod s_MethMessReq_DEV9[] = { { "DEV9open", (vMeth**)&DEV9open, NULL }, { "DEV9read8", (vMeth**)&DEV9read8, NULL }, { "DEV9read16", (vMeth**)&DEV9read16, NULL }, { "DEV9read32", (vMeth**)&DEV9read32, NULL }, { "DEV9write8", (vMeth**)&DEV9write8, NULL }, { "DEV9write16", (vMeth**)&DEV9write16, NULL }, { "DEV9write32", (vMeth**)&DEV9write32, NULL }, #ifdef ENABLE_NEW_IOPDMA_DEV9 { "DEV9dmaRead", (vMeth**)&DEV9dmaRead, NULL }, { "DEV9dmaWrite", (vMeth**)&DEV9dmaWrite, NULL }, { "DEV9dmaInterrupt", (vMeth**)&DEV9dmaInterrupt, NULL }, #else { "DEV9readDMA8Mem", (vMeth**)&DEV9readDMA8Mem, NULL }, { "DEV9writeDMA8Mem", (vMeth**)&DEV9writeDMA8Mem, NULL }, #endif { "DEV9irqCallback", (vMeth**)&DEV9irqCallback, NULL }, { "DEV9irqHandler", (vMeth**)&DEV9irqHandler, NULL }, { NULL } }; static const LegacyApi_OptMethod s_MethMessOpt_DEV9[] = { { "DEV9async", (vMeth**)&DEV9async }, { NULL } }; // ---------------------------------------------------------------------------- // USB Mess! // ---------------------------------------------------------------------------- static const LegacyApi_ReqMethod s_MethMessReq_USB[] = { { "USBopen", (vMeth**)&USBopen, NULL }, { "USBread8", (vMeth**)&USBread8, NULL }, { "USBread16", (vMeth**)&USBread16, NULL }, { "USBread32", (vMeth**)&USBread32, NULL }, { "USBwrite8", (vMeth**)&USBwrite8, NULL }, { "USBwrite16", (vMeth**)&USBwrite16, NULL }, { "USBwrite32", (vMeth**)&USBwrite32, NULL }, { "USBirqCallback", (vMeth**)&USBirqCallback, NULL }, { "USBirqHandler", (vMeth**)&USBirqHandler, NULL }, { NULL } }; static const LegacyApi_OptMethod s_MethMessOpt_USB[] = { { "USBasync", (vMeth**)&USBasync }, { "USBsetRAM", (vMeth**)&USBsetRAM }, { NULL } }; // ---------------------------------------------------------------------------- // FW Mess! // ---------------------------------------------------------------------------- static const LegacyApi_ReqMethod s_MethMessReq_FW[] = { { "FWopen", (vMeth**)&FWopen, NULL }, { "FWread32", (vMeth**)&FWread32, NULL }, { "FWwrite32", (vMeth**)&FWwrite32, NULL }, { "FWirqCallback", (vMeth**)&FWirqCallback, NULL }, { NULL } }; static const LegacyApi_OptMethod s_MethMessOpt_FW[] = { { NULL } }; static const LegacyApi_ReqMethod* const s_MethMessReq[] = { s_MethMessReq_GS, s_MethMessReq_PAD, s_MethMessReq_SPU2, s_MethMessReq_CDVD, s_MethMessReq_USB, s_MethMessReq_FW, s_MethMessReq_DEV9 }; static const LegacyApi_OptMethod* const s_MethMessOpt[] = { s_MethMessOpt_GS, s_MethMessOpt_PAD, s_MethMessOpt_SPU2, s_MethMessOpt_CDVD, s_MethMessOpt_USB, s_MethMessOpt_FW, s_MethMessOpt_DEV9 }; SysCorePlugins *g_plugins = NULL; // --------------------------------------------------------------------------------- // Plugin-related Exception Implementations // --------------------------------------------------------------------------------- wxString Exception::SaveStateLoadError::FormatDiagnosticMessage() const { FastFormatUnicode retval; retval.Write("Savestate is corrupt or incomplete!\n"); _formatDiagMsg(retval); return retval; } wxString Exception::SaveStateLoadError::FormatDisplayMessage() const { FastFormatUnicode retval; retval.Write(_("The savestate cannot be loaded, as it appears to be corrupt or incomplete.")); retval.Write("\n"); _formatUserMsg(retval); return retval; } Exception::PluginOpenError::PluginOpenError( PluginsEnum_t pid ) { PluginId = pid; m_message_diag = L"%s plugin failed to open!"; m_message_user = _("%s plugin failed to open. Your computer may have insufficient resources, or incompatible hardware/drivers."); } Exception::PluginInitError::PluginInitError( PluginsEnum_t pid ) { PluginId = pid; m_message_diag = L"%s plugin initialization failed!"; m_message_user = _("%s plugin failed to initialize. Your system may have insufficient memory or resources needed."); } Exception::PluginLoadError::PluginLoadError( PluginsEnum_t pid ) { PluginId = pid; } wxString Exception::PluginLoadError::FormatDiagnosticMessage() const { return pxsFmt( m_message_diag, WX_STR(tbl_PluginInfo[PluginId].GetShortname()) ) + L"\n\n" + StreamName; } wxString Exception::PluginLoadError::FormatDisplayMessage() const { return pxsFmt( m_message_user, WX_STR(tbl_PluginInfo[PluginId].GetShortname()) ) + L"\n\n" + StreamName; } wxString Exception::PluginError::FormatDiagnosticMessage() const { return pxsFmt( m_message_diag, WX_STR(tbl_PluginInfo[PluginId].GetShortname()) ); } wxString Exception::PluginError::FormatDisplayMessage() const { return pxsFmt( m_message_user, WX_STR(tbl_PluginInfo[PluginId].GetShortname()) ); } wxString Exception::FreezePluginFailure::FormatDiagnosticMessage() const { return pxsFmt( L"%s plugin returned an error while saving the state.\n\n", tbl_PluginInfo[PluginId].shortname ); } wxString Exception::FreezePluginFailure::FormatDisplayMessage() const { // [TODO] return m_message_user; } wxString Exception::ThawPluginFailure::FormatDiagnosticMessage() const { return pxsFmt( L"%s plugin returned an error while loading the state.\n\n", tbl_PluginInfo[PluginId].shortname ); } wxString Exception::ThawPluginFailure::FormatDisplayMessage() const { // [TODO] return m_message_user; } // -------------------------------------------------------------------------------------- // PCSX2 Callbacks passed to Plugins // -------------------------------------------------------------------------------------- // This is currently unimplemented, and should be provided by the AppHost (gui) rather // than the EmuCore. But as a quickhackfix until the new plugin API is fleshed out, this // will suit our needs nicely. :) static BOOL PS2E_CALLBACK pcsx2_GetInt( const char* name, int* dest ) { return FALSE; // not implemented... } static BOOL PS2E_CALLBACK pcsx2_GetBoolean( const char* name, BOOL* result ) { return FALSE; // not implemented... } static BOOL PS2E_CALLBACK pcsx2_GetString( const char* name, char* dest, int maxlen ) { return FALSE; // not implemented... } static char* PS2E_CALLBACK pcsx2_GetStringAlloc( const char* name, void* (PS2E_CALLBACK* allocator)(int size) ) { return FALSE; // not implemented... } static void PS2E_CALLBACK pcsx2_OSD_WriteLn( int icon, const char* msg ) { return; // not implemented... } // --------------------------------------------------------------------------------- // DynamicStaticLibrary // --------------------------------------------------------------------------------- StaticLibrary::StaticLibrary(PluginsEnum_t _pid) : pid(_pid) { } bool StaticLibrary::Load(const wxString& name) { return true; } void* StaticLibrary::GetSymbol(const wxString &name) { #define RETURN_SYMBOL(s) if (name == #s) return (void*)&s; #define RETURN_COMMON_SYMBOL(p) \ RETURN_SYMBOL(p##init) \ RETURN_SYMBOL(p##close) \ RETURN_SYMBOL(p##shutdown) \ RETURN_SYMBOL(p##keyEvent) \ RETURN_SYMBOL(p##setSettingsDir) \ RETURN_SYMBOL(p##setLogDir) \ RETURN_SYMBOL(p##freeze) \ RETURN_SYMBOL(p##test) \ RETURN_SYMBOL(p##configure) \ RETURN_SYMBOL(p##about) #ifdef BUILTIN_GS_PLUGIN RETURN_COMMON_SYMBOL(GS); #endif #ifdef BUILTIN_PAD_PLUGIN RETURN_COMMON_SYMBOL(PAD); #endif #ifdef BUILTIN_SPU2_PLUGIN RETURN_COMMON_SYMBOL(SPU2); #endif #ifdef BUILTIN_CDVD_PLUGIN RETURN_COMMON_SYMBOL(CDVD); #endif #ifdef BUILTIN_DEV9_PLUGIN RETURN_COMMON_SYMBOL(DEV9); #endif #ifdef BUILTIN_USB_PLUGIN RETURN_COMMON_SYMBOL(USB); #endif #ifdef BUILTIN_FW_PLUGIN RETURN_COMMON_SYMBOL(FW); #endif #undef RETURN_COMMON_SYMBOL #undef RETURN_SYMBOL return NULL; } bool StaticLibrary::HasSymbol(const wxString &name) { return false; } DynamicLibrary::DynamicLibrary() : Lib() { } bool DynamicLibrary::Load(const wxString& name) { return Lib.Load(name); } void* DynamicLibrary::GetSymbol(const wxString &name) { return Lib.GetSymbol(name); } bool DynamicLibrary::HasSymbol(const wxString &name) { return Lib.HasSymbol(name); } // --------------------------------------------------------------------------------- // PluginStatus_t Implementations // --------------------------------------------------------------------------------- SysCorePlugins::PluginStatus_t::PluginStatus_t( PluginsEnum_t _pid, const wxString& srcfile ) : Filename( srcfile ) { pid = _pid; IsInitialized = false; IsOpened = false; IsStatic = false; Lib = new DynamicLibrary(); if (IsStatic) { BindCommon( pid ); } else { if( Filename.IsEmpty() ) throw Exception::PluginInitError( pid ).SetDiagMsg( L"Empty plugin filename" ); if( !wxFile::Exists( Filename ) ) throw Exception::PluginLoadError( pid ).SetStreamName(srcfile) .SetBothMsgs(pxL("The configured %s plugin file was not found")); if( !Lib->Load( Filename ) ) throw Exception::PluginLoadError( pid ).SetStreamName(Filename) .SetBothMsgs(pxL("The configured %s plugin file is not a valid dynamic library")); // Try to enumerate the new v2.0 plugin interface first. // If that fails, fall back on the old style interface. //m_libs[i].GetSymbol( L"PS2E_InitAPI" ); // on the TODO list! // 2.0 API Failed; Enumerate the Old Stuff! --> _PS2EgetLibName GetLibName = (_PS2EgetLibName) Lib->GetSymbol( L"PS2EgetLibName" ); _PS2EgetLibVersion2 GetLibVersion2 = (_PS2EgetLibVersion2) Lib->GetSymbol( L"PS2EgetLibVersion2" ); if( GetLibName == NULL || GetLibVersion2 == NULL ) throw Exception::PluginLoadError( pid ).SetStreamName(Filename) .SetDiagMsg(L"%s plugin init failed: Method binding failure on GetLibName or GetLibVersion2.") .SetUserMsg(_( "The configured %s plugin is not a PCSX2 plugin, or is for an older unsupported version of PCSX2.")); // Only Windows GSdx uses this. Should be removed in future after GSdx no longer relies on it to show the new config dialog. #ifdef _WIN32 // Since only Windows Gsdx has this symbol, that means every other plugin is going to cause error messages to be logged. // Let's not do that for a hack function. if (Lib->HasSymbol(L"PS2EsetEmuVersion")) { _PS2EsetEmuVersion SetEmuVersion = (_PS2EsetEmuVersion)Lib->GetSymbol(L"PS2EsetEmuVersion"); if (SetEmuVersion) SetEmuVersion("PCSX2", (PCSX2_VersionHi << 24) | (PCSX2_VersionMid << 16) | (PCSX2_VersionLo << 8) | 0); } #endif Name = fromUTF8( GetLibName() ); int version = GetLibVersion2( tbl_PluginInfo[pid].typemask ); Version.Printf( L"%d.%d.%d", (version>>8)&0xff, version&0xff, (version>>24)&0xff ); // Bind Required Functions // (generate critical error if binding fails) BindCommon( pid ); BindRequired( pid ); BindOptional( pid ); } // Run Plugin's Functionality Test. // A lot of plugins don't bother to implement this function and return 0 (success) // regardless, but some do so let's go ahead and check it. I mean, we're supposed to. :) int testres = CommonBindings.Test(); if( testres != 0 ) throw Exception::PluginLoadError( pid ).SetStreamName(Filename) .SetDiagMsg(wxsFormat( L"Plugin Test failure, return code: %d", testres )) .SetUserMsg(_("The plugin reports that your hardware or software/drivers are not supported.")); } void SysCorePlugins::PluginStatus_t::BindCommon( PluginsEnum_t pid ) { const LegacyApi_CommonMethod* current = s_MethMessCommon; VoidMethod** target = (VoidMethod**)&CommonBindings; wxDoNotLogInThisScope please; while( current->MethodName != NULL ) { *target = (VoidMethod*)Lib->GetSymbol( current->GetMethodName( pid ) ); if( *target == NULL ) *target = current->Fallback; if( *target == NULL ) { throw Exception::PluginLoadError( pid ).SetStreamName(Filename) .SetDiagMsg(wxsFormat( L"\nMethod binding failure on: %s\n", WX_STR(current->GetMethodName( pid )) )) .SetUserMsg(_("Configured plugin is not a PCSX2 plugin, or is for an older unsupported version of PCSX2.")); } target++; current++; } } void SysCorePlugins::PluginStatus_t::BindRequired( PluginsEnum_t pid ) { const LegacyApi_ReqMethod* current = s_MethMessReq[pid]; wxDoNotLogInThisScope please; while( current->MethodName != NULL ) { *(current->Dest) = (VoidMethod*)Lib->GetSymbol( current->GetMethodName() ); if( *(current->Dest) == NULL ) *(current->Dest) = current->Fallback; if( *(current->Dest) == NULL ) { throw Exception::PluginLoadError( pid ).SetStreamName(Filename) .SetDiagMsg(wxsFormat( L"\n%s plugin init error; Method binding failed: %s\n", WX_STR(current->GetMethodName()) )) .SetUserMsg(_( "Configured %s plugin is not a valid PCSX2 plugin, or is for an older unsupported version of PCSX2.")); } current++; } } void SysCorePlugins::PluginStatus_t::BindOptional( PluginsEnum_t pid ) { const LegacyApi_OptMethod* current = s_MethMessOpt[pid]; wxDoNotLogInThisScope please; while( current->MethodName != NULL ) { *(current->Dest) = (VoidMethod*)Lib->GetSymbol( current->GetMethodName() ); current++; } } // ===================================================================================== // SysCorePlugins Implementations // ===================================================================================== SysCorePlugins::SysCorePlugins() { } SysCorePlugins::~SysCorePlugins() throw() { try { Unload(); } DESTRUCTOR_CATCHALL // All library unloading done automatically by wx. } void SysCorePlugins::Load( PluginsEnum_t pid, const wxString& srcfile ) { ScopedLock lock( m_mtx_PluginStatus ); pxAssert( (uint)pid < PluginId_Count ); Console.Indent().WriteLn( L"Binding %4s: %s ", WX_STR(tbl_PluginInfo[pid].GetShortname()), WX_STR(srcfile) ); m_info[pid] = std::unique_ptr(new PluginStatus_t(pid, srcfile)); } void SysCorePlugins::Load( const wxString (&folders)[PluginId_Count] ) { if( !NeedsLoad() ) return; wxDoNotLogInThisScope please; Console.WriteLn( Color_StrongBlue, "\nLoading plugins..." ); ConsoleIndentScope indent; const PluginInfo* pi = tbl_PluginInfo; do { Load( pi->id, folders[pi->id] ); pxYield( 2 ); } while( ++pi, pi->shortname != NULL ); indent.LeaveScope(); CDVDapi_Plugin.newDiskCB( cdvdNewDiskCB ); // Hack for PAD's stupid parameter passed on Init PADinit = (_PADinit)m_info[PluginId_PAD]->CommonBindings.Init; m_info[PluginId_PAD]->CommonBindings.Init = _hack_PADinit; Console.WriteLn( Color_StrongBlue, "Plugins loaded successfully.\n" ); // HACK! Manually bind the Internal MemoryCard plugin for now, until // we get things more completed in the new plugin api. static const PS2E_EmulatorInfo myself = { "PCSX2", { 0, PCSX2_VersionHi, PCSX2_VersionLo, SVN_REV }, (int)x86caps.PhysicalCores, (int)x86caps.LogicalCores, sizeof(wchar_t), { 0,0,0,0,0,0 }, pcsx2_GetInt, pcsx2_GetBoolean, pcsx2_GetString, pcsx2_GetStringAlloc, pcsx2_OSD_WriteLn }; m_mcdPlugin = FileMcd_InitAPI( &myself ); if( m_mcdPlugin == NULL ) { // fixme: use plugin's GetLastError (not implemented yet!) throw Exception::PluginLoadError( PluginId_Mcd ).SetDiagMsg(L"Internal Memorycard Plugin failed to load."); } SendLogFolder(); SendSettingsFolder(); } void SysCorePlugins::Unload(PluginsEnum_t pid) { ScopedLock lock( m_mtx_PluginStatus ); pxAssert( (uint)pid < PluginId_Count ); m_info[pid] = nullptr; } void SysCorePlugins::Unload() { if( NeedsShutdown() ) Console.Warning( "(SysCorePlugins) Warning: Unloading plugins prior to shutdown!" ); //Shutdown(); if( !NeedsUnload() ) return; DbgCon.WriteLn( Color_StrongBlue, "Unloading plugins..." ); for( int i=PluginId_Count-1; i>=0; --i ) Unload( tbl_PluginInfo[i].id ); DbgCon.WriteLn( Color_StrongBlue, "Plugins unloaded successfully." ); } // Exceptions: // FileNotFound - Thrown if one of the configured plugins doesn't exist. // NotPcsxPlugin - Thrown if one of the configured plugins is an invalid or unsupported DLL extern bool renderswitch; extern void spu2DMA4Irq(); extern void spu2DMA7Irq(); extern void spu2Irq(); bool SysCorePlugins::OpenPlugin_CDVD() { return DoCDVDopen(); } bool SysCorePlugins::OpenPlugin_GS() { GetMTGS().Resume(); return true; } bool SysCorePlugins::OpenPlugin_PAD() { return !PADopen( (void*)pDsp ); } bool SysCorePlugins::OpenPlugin_SPU2() { if( SPU2open((void*)pDsp) ) return false; #ifdef ENABLE_NEW_IOPDMA_SPU2 SPU2irqCallback( spu2Irq ); #else SPU2irqCallback( spu2Irq, spu2DMA4Irq, spu2DMA7Irq ); if( SPU2setDMABaseAddr != NULL ) SPU2setDMABaseAddr((uptr)iopMem->Main); #endif if( SPU2setClockPtr != NULL ) SPU2setClockPtr(&psxRegs.cycle); return true; } bool SysCorePlugins::OpenPlugin_DEV9() { dev9Handler = NULL; if( DEV9open( (void*)pDsp ) ) return false; DEV9irqCallback( dev9Irq ); dev9Handler = DEV9irqHandler(); return true; } bool SysCorePlugins::OpenPlugin_USB() { usbHandler = NULL; if( USBopen((void*)pDsp) ) return false; USBirqCallback( usbIrq ); usbHandler = USBirqHandler(); // iopMem is not initialized yet. Moved elsewhere //if( USBsetRAM != NULL ) // USBsetRAM(iopMem->Main); return true; } bool SysCorePlugins::OpenPlugin_FW() { if( FWopen((void*)pDsp) ) return false; FWirqCallback( fwIrq ); return true; } bool SysCorePlugins::OpenPlugin_Mcd() { ScopedLock lock( m_mtx_PluginStatus ); // [TODO] Fix up and implement PS2E_SessionInfo here!! (the currently NULL parameter) if( SysPlugins.Mcd ) SysPlugins.Mcd->Base.EmuOpen( (PS2E_THISPTR) SysPlugins.Mcd, NULL ); return true; } void SysCorePlugins::Open( PluginsEnum_t pid ) { pxAssert( (uint)pid < PluginId_Count ); if( IsOpen(pid) ) return; Console.Indent().WriteLn( "Opening %s", tbl_PluginInfo[pid].shortname ); // Each Open needs to be called explicitly. >_< bool result = true; switch( pid ) { case PluginId_GS: result = OpenPlugin_GS(); break; case PluginId_PAD: result = OpenPlugin_PAD(); break; case PluginId_CDVD: result = OpenPlugin_CDVD(); break; case PluginId_SPU2: result = OpenPlugin_SPU2(); break; case PluginId_USB: result = OpenPlugin_USB(); break; case PluginId_FW: result = OpenPlugin_FW(); break; case PluginId_DEV9: result = OpenPlugin_DEV9(); break; jNO_DEFAULT; } if( !result ) throw Exception::PluginOpenError( pid ); ScopedLock lock( m_mtx_PluginStatus ); if( m_info[pid] ) m_info[pid]->IsOpened = true; } void SysCorePlugins::Open() { Init(); if( !NeedsOpen() ) return; // Spam stopper: returns before writing any logs. >_< Console.WriteLn( Color_StrongBlue, "Opening plugins..." ); SendSettingsFolder(); const PluginInfo* pi = tbl_PluginInfo; do { Open( pi->id ); // If GS doesn't support GSopen2, need to wait until call to GSopen // returns to populate pDsp. If it does, can initialize other plugins // at same time as GS, as long as GSopen2 does not subclass its window. #ifdef __linux__ // On linux, application have also a channel (named display) to communicate with the // Xserver. The safe rule is 1 thread, 1 channel. In our case we use the display in // several places. Save yourself of multithread headache. Wait few seconds the end of // gsopen -- Gregory if (pi->id == PluginId_GS) GetMTGS().WaitForOpen(); #else if (pi->id == PluginId_GS && !GSopen2) GetMTGS().WaitForOpen(); #endif } while( ++pi, pi->shortname != NULL ); if (GSopen2) GetMTGS().WaitForOpen(); if( !m_mcdOpen.exchange(true) ) { DbgCon.Indent().WriteLn( "Opening Memorycards"); OpenPlugin_Mcd(); } Console.WriteLn( Color_StrongBlue, "Plugins opened successfully." ); } void SysCorePlugins::_generalclose( PluginsEnum_t pid ) { ScopedLock lock( m_mtx_PluginStatus ); if( m_info[pid] ) m_info[pid]->CommonBindings.Close(); } void SysCorePlugins::ClosePlugin_GS() { // old-skool: force-close PAD before GS, because the PAD depends on the GS window. if( GetMTGS().IsSelf() ) _generalclose( PluginId_GS ); else { if( !GSopen2 ) Close( PluginId_PAD ); GetMTGS().Suspend(); } } void SysCorePlugins::ClosePlugin_CDVD() { DoCDVDclose(); } void SysCorePlugins::ClosePlugin_PAD() { _generalclose( PluginId_PAD ); } void SysCorePlugins::ClosePlugin_SPU2() { _generalclose( PluginId_SPU2 ); } void SysCorePlugins::ClosePlugin_DEV9() { _generalclose( PluginId_DEV9 ); } void SysCorePlugins::ClosePlugin_USB() { _generalclose( PluginId_USB ); } void SysCorePlugins::ClosePlugin_FW() { _generalclose( PluginId_FW ); } void SysCorePlugins::ClosePlugin_Mcd() { ScopedLock lock( m_mtx_PluginStatus ); if( SysPlugins.Mcd ) SysPlugins.Mcd->Base.EmuClose( (PS2E_THISPTR) SysPlugins.Mcd ); } void SysCorePlugins::Close( PluginsEnum_t pid ) { pxAssert( (uint)pid < PluginId_Count ); if( !IsOpen(pid) ) return; if( !GetMTGS().IsSelf() ) // stop the spam! Console.Indent().WriteLn( "Closing %s", tbl_PluginInfo[pid].shortname ); switch( pid ) { case PluginId_GS: ClosePlugin_GS(); break; case PluginId_PAD: ClosePlugin_PAD(); break; case PluginId_CDVD: ClosePlugin_CDVD(); break; case PluginId_SPU2: ClosePlugin_SPU2(); break; case PluginId_USB: ClosePlugin_USB(); break; case PluginId_FW: ClosePlugin_FW(); break; case PluginId_DEV9: ClosePlugin_DEV9(); break; case PluginId_Mcd: ClosePlugin_Mcd(); break; jNO_DEFAULT; } ScopedLock lock( m_mtx_PluginStatus ); if( m_info[pid] ) m_info[pid]->IsOpened = false; } void SysCorePlugins::Close() { if( !NeedsClose() ) return; // Spam stopper; returns before writing any logs. >_< // Close plugins in reverse order of the initialization procedure, which // ensures the GS gets closed last. Console.WriteLn( Color_StrongBlue, "Closing plugins..." ); if( m_mcdOpen.exchange(false) ) { DbgCon.Indent().WriteLn( "Closing Memorycards"); ClosePlugin_Mcd(); } for( int i=PluginId_Count-1; i>=0; --i ) Close( tbl_PluginInfo[i].id ); Console.WriteLn( Color_StrongBlue, "Plugins closed successfully." ); } void SysCorePlugins::Init( PluginsEnum_t pid ) { ScopedLock lock( m_mtx_PluginStatus ); if( !m_info[pid] || m_info[pid]->IsInitialized ) return; Console.Indent().WriteLn( "Init %s", tbl_PluginInfo[pid].shortname ); if( 0 != m_info[pid]->CommonBindings.Init() ) throw Exception::PluginInitError( pid ); m_info[pid]->IsInitialized = true; } void SysCorePlugins::Shutdown( PluginsEnum_t pid ) { ScopedLock lock( m_mtx_PluginStatus ); if( !m_info[pid] || !m_info[pid]->IsInitialized ) return; DevCon.Indent().WriteLn( "Shutdown %s", tbl_PluginInfo[pid].shortname ); m_info[pid]->IsInitialized = false; m_info[pid]->CommonBindings.Shutdown(); } // Initializes all plugins. Plugin initialization should be done once for every new emulation // session. During a session emulation can be paused/resumed using Open/Close, and should be // terminated using Shutdown(). // // Returns TRUE if an init was performed for any (or all) plugins. Returns FALSE if all // plugins were already in an initialized state (no action taken). // // In a purist emulation sense, Init() and Shutdown() should only ever need be called for when // the PS2's hardware has received a *full* hard reset. Soft resets typically should rely on // the PS2's bios/kernel to re-initialize hardware on the fly. // bool SysCorePlugins::Init() { if( !NeedsInit() ) return false; Console.WriteLn( Color_StrongBlue, "\nInitializing plugins..." ); const PluginInfo* pi = tbl_PluginInfo; do { Init( pi->id ); } while( ++pi, pi->shortname != NULL ); if( SysPlugins.Mcd == NULL ) { SysPlugins.Mcd = (PS2E_ComponentAPI_Mcd*)m_mcdPlugin->NewComponentInstance( PS2E_TYPE_Mcd ); if( SysPlugins.Mcd == NULL ) { // fixme: use plugin's GetLastError (not implemented yet!) throw Exception::PluginInitError( PluginId_Mcd ) .SetBothMsgs(pxLt("Internal Memorycard Plugin failed to initialize.")); } } Console.WriteLn( Color_StrongBlue, "Plugins initialized successfully.\n" ); return true; } // Shuts down all plugins. Plugins are closed first, if necessary. // Returns TRUE if a shutdown was performed for any (or all) plugins. Returns FALSE if all // plugins were already in shutdown state (no action taken). // // In a purist emulation sense, Init() and Shutdown() should only ever need be called for when // the PS2's hardware has received a *full* hard reset. Soft resets typically should rely on // the PS2's bios/kernel to re-initialize hardware on the fly. // bool SysCorePlugins::Shutdown() { if( !NeedsShutdown() ) return false; pxAssertDev( !NeedsClose(), "Cannot shut down plugins prior to Close()" ); GetMTGS().Cancel(); // cancel it for speedier shutdown! Console.WriteLn( Color_StrongGreen, "Shutting down plugins..." ); // Shutdown plugins in reverse order (probably doesn't matter... // ... but what the heck, right?) for( int i=PluginId_Count-1; i>=0; --i ) { Shutdown( tbl_PluginInfo[i].id ); } // More memorycard hacks!! if( (SysPlugins.Mcd != NULL) && (m_mcdPlugin != NULL) ) { m_mcdPlugin->DeleteComponentInstance( (PS2E_THISPTR)SysPlugins.Mcd ); SysPlugins.Mcd = NULL; } Console.WriteLn( Color_StrongGreen, "Plugins shutdown successfully." ); return true; } // For internal use only, unless you're the MTGS. Then it's for you too! // Returns false if the plugin returned an error. bool SysCorePlugins::DoFreeze( PluginsEnum_t pid, int mode, freezeData* data ) { if( (pid == PluginId_GS) && !GetMTGS().IsSelf() ) { // GS needs some thread safety love... MTGS_FreezeData woot = { data, 0 }; GetMTGS().Freeze( mode, woot ); return woot.retval != -1; } else { ScopedLock lock( m_mtx_PluginStatus ); return !m_info[pid] || m_info[pid]->CommonBindings.Freeze( mode, data ) != -1; } } // Thread Safety: // This function should only be called by the Main GUI thread and the GS thread (for GS states only), // as it has special handlers to ensure that GS freeze commands are executed appropriately on the // GS thread. // void SysCorePlugins::Freeze( PluginsEnum_t pid, SaveStateBase& state ) { // No locking leeded -- DoFreeze locks as needed, and this avoids MTGS deadlock. //ScopedLock lock( m_mtx_PluginStatus ); freezeData fP = { 0, NULL }; if( !DoFreeze( pid, FREEZE_SIZE, &fP ) ) fP.size = 0; int fsize = fP.size; state.Freeze( fsize ); Console.Indent().WriteLn( "%s %s", state.IsSaving() ? "Saving" : "Loading", tbl_PluginInfo[pid].shortname ); if( state.IsLoading() && (fsize == 0) ) { // no state data to read, but the plugin expects some state data. // Issue a warning to console... if( fP.size != 0 ) Console.Indent().Warning( "Warning: No data for this plugin was found. Plugin status may be unpredictable." ); return; // Note: Size mismatch check could also be done here on loading, but // some plugins may have built-in version support for non-native formats or // older versions of a different size... or could give different sizes depending // on the status of the plugin when loading, so let's ignore it. } fP.size = fsize; if( fP.size == 0 ) return; state.PrepBlock( fP.size ); fP.data = (s8*)state.GetBlockPtr(); if( state.IsSaving() ) { if( !DoFreeze(pid, FREEZE_SAVE, &fP) ) throw Exception::FreezePluginFailure( pid ); } else { if( !DoFreeze(pid, FREEZE_LOAD, &fP) ) throw Exception::ThawPluginFailure( pid ); } state.CommitBlock( fP.size ); } size_t SysCorePlugins::GetFreezeSize( PluginsEnum_t pid ) { freezeData fP = { 0, NULL }; if (!DoFreeze( pid, FREEZE_SIZE, &fP)) return 0; return fP.size; } void SysCorePlugins::FreezeOut( PluginsEnum_t pid, void* dest ) { // No locking needed -- DoFreeze locks as needed, and this avoids MTGS deadlock. //ScopedLock lock( m_mtx_PluginStatus ); freezeData fP = { 0, (s8*)dest }; if (!DoFreeze( pid, FREEZE_SIZE, &fP)) return; if (!fP.size) return; Console.Indent().WriteLn( "Saving %s", tbl_PluginInfo[pid].shortname ); if (!DoFreeze(pid, FREEZE_SAVE, &fP)) throw Exception::FreezePluginFailure( pid ); } void SysCorePlugins::FreezeOut( PluginsEnum_t pid, pxOutputStream& outfp ) { // No locking needed -- DoFreeze locks as needed, and this avoids MTGS deadlock. //ScopedLock lock( m_mtx_PluginStatus ); freezeData fP = { 0, NULL }; if (!DoFreeze( pid, FREEZE_SIZE, &fP)) return; if (!fP.size) return; Console.Indent().WriteLn( "Saving %s", tbl_PluginInfo[pid].shortname ); ScopedAlloc data( fP.size ); fP.data = data.GetPtr(); if (!DoFreeze(pid, FREEZE_SAVE, &fP)) throw Exception::FreezePluginFailure( pid ); outfp.Write( fP.data, fP.size ); } void SysCorePlugins::FreezeIn( PluginsEnum_t pid, pxInputStream& infp ) { // No locking needed -- DoFreeze locks as needed, and this avoids MTGS deadlock. //ScopedLock lock( m_mtx_PluginStatus ); freezeData fP = { 0, NULL }; if (!DoFreeze( pid, FREEZE_SIZE, &fP )) fP.size = 0; Console.Indent().WriteLn( "Loading %s", tbl_PluginInfo[pid].shortname ); if (!infp.IsOk() || !infp.Length()) { // no state data to read, but the plugin expects some state data? // Issue a warning to console... if( fP.size != 0 ) Console.Indent().Warning( "Warning: No data for this plugin was found. Plugin status may be unpredictable." ); return; // Note: Size mismatch check could also be done here on loading, but // some plugins may have built-in version support for non-native formats or // older versions of a different size... or could give different sizes depending // on the status of the plugin when loading, so let's ignore it. } ScopedAlloc data( fP.size ); fP.data = data.GetPtr(); infp.Read( fP.data, fP.size ); if (!DoFreeze(pid, FREEZE_LOAD, &fP)) throw Exception::ThawPluginFailure( pid ); } bool SysCorePlugins::KeyEvent( const keyEvent& evt ) { ScopedLock lock( m_mtx_PluginStatus ); // [TODO] : The plan here is to give plugins "first chance" handling of keys. // Handling order will be fixed (GS, SPU2, PAD, etc), and the first plugin to // pick up the key and return "true" (for handled) will cause the loop to break. // The current version of PS2E doesn't support it yet, though. const PluginInfo* pi = tbl_PluginInfo; do { if( pi->id != PluginId_PAD && m_info[pi->id] ) m_info[pi->id]->CommonBindings.KeyEvent( const_cast(&evt) ); } while( ++pi, pi->shortname != NULL ); return false; } void SysCorePlugins::SendSettingsFolder() { ScopedLock lock( m_mtx_PluginStatus ); if( m_SettingsFolder.IsEmpty() ) return; wxCharBuffer buffer(m_SettingsFolder.mb_str(wxConvFile)); const PluginInfo* pi = tbl_PluginInfo; do { if( m_info[pi->id] ) m_info[pi->id]->CommonBindings.SetSettingsDir( buffer ); } while( ++pi, pi->shortname != NULL ); } void SysCorePlugins::SetSettingsFolder( const wxString& folder ) { ScopedLock lock( m_mtx_PluginStatus ); wxString fixedfolder( folder ); if( !fixedfolder.IsEmpty() && (fixedfolder[fixedfolder.length()-1] != wxFileName::GetPathSeparator() ) ) { fixedfolder += wxFileName::GetPathSeparator(); } if( m_SettingsFolder == fixedfolder ) return; m_SettingsFolder = fixedfolder; } void SysCorePlugins::SendLogFolder() { ScopedLock lock( m_mtx_PluginStatus ); if( m_LogFolder.IsEmpty() ) return; wxCharBuffer buffer(m_LogFolder.mb_str(wxConvFile)); const PluginInfo* pi = tbl_PluginInfo; do { if( m_info[pi->id] ) m_info[pi->id]->CommonBindings.SetLogDir( buffer ); } while( ++pi, pi->shortname != NULL ); } void SysCorePlugins::SetLogFolder( const wxString& folder ) { ScopedLock lock( m_mtx_PluginStatus ); wxString fixedfolder( folder ); if( !fixedfolder.IsEmpty() && (fixedfolder[fixedfolder.length()-1] != wxFileName::GetPathSeparator() ) ) { fixedfolder += wxFileName::GetPathSeparator(); } if( m_LogFolder == fixedfolder ) return; m_LogFolder = fixedfolder; } void SysCorePlugins::Configure( PluginsEnum_t pid ) { ScopedLock lock( m_mtx_PluginStatus ); if( m_info[pid] ) m_info[pid]->CommonBindings.Configure(); } bool SysCorePlugins::AreLoaded() const { ScopedLock lock( m_mtx_PluginStatus ); for( int i=0; iid) ) return false; } while( ++pi, pi->shortname != NULL ); return true; } bool SysCorePlugins::AreAnyLoaded() const { ScopedLock lock( m_mtx_PluginStatus ); for( int i=0; iid) ) return true; } while( ++pi, pi->shortname != NULL ); return false; } bool SysCorePlugins::IsOpen( PluginsEnum_t pid ) const { pxAssert( (uint)pid < PluginId_Count ); ScopedLock lock( m_mtx_PluginStatus ); return m_info[pid] && m_info[pid]->IsInitialized && m_info[pid]->IsOpened; } bool SysCorePlugins::IsInitialized( PluginsEnum_t pid ) const { pxAssert( (uint)pid < PluginId_Count ); ScopedLock lock( m_mtx_PluginStatus ); return m_info[pid] && m_info[pid]->IsInitialized; } bool SysCorePlugins::IsLoaded( PluginsEnum_t pid ) const { pxAssert( (uint)pid < PluginId_Count ); return !!m_info[pid]; } bool SysCorePlugins::NeedsLoad() const { const PluginInfo* pi = tbl_PluginInfo; do { if( !IsLoaded(pi->id) ) return true; } while( ++pi, pi->shortname != NULL ); return false; } bool SysCorePlugins::NeedsUnload() const { const PluginInfo* pi = tbl_PluginInfo; do { if( IsLoaded(pi->id) ) return true; } while( ++pi, pi->shortname != NULL ); return false; } bool SysCorePlugins::NeedsInit() const { ScopedLock lock( m_mtx_PluginStatus ); const PluginInfo* pi = tbl_PluginInfo; do { if( !IsInitialized(pi->id) ) return true; } while( ++pi, pi->shortname != NULL ); return false; } bool SysCorePlugins::NeedsShutdown() const { ScopedLock lock( m_mtx_PluginStatus ); const PluginInfo* pi = tbl_PluginInfo; do { if( IsInitialized(pi->id) ) return true; } while( ++pi, pi->shortname != NULL ); return false; } bool SysCorePlugins::NeedsOpen() const { const PluginInfo* pi = tbl_PluginInfo; do { if( !IsOpen(pi->id) ) return true; } while( ++pi, pi->shortname != NULL ); return false; } bool SysCorePlugins::NeedsClose() const { const PluginInfo* pi = tbl_PluginInfo; do { if( IsOpen(pi->id) ) return true; } while( ++pi, pi->shortname != NULL ); return false; } const wxString SysCorePlugins::GetName( PluginsEnum_t pid ) const { ScopedLock lock( m_mtx_PluginStatus ); pxAssert( (uint)pid < PluginId_Count ); return m_info[pid] ? m_info[pid]->Name : (wxString)_("Unloaded Plugin"); } const wxString SysCorePlugins::GetVersion( PluginsEnum_t pid ) const { ScopedLock lock( m_mtx_PluginStatus ); pxAssert( (uint)pid < PluginId_Count ); return m_info[pid] ? m_info[pid]->Version : L"0.0"; }