diff --git a/3rdparty/SoundTouch/soundtouch_config.h b/3rdparty/SoundTouch/soundtouch_config.h index 0a7f2b836e..f768fd1640 100644 --- a/3rdparty/SoundTouch/soundtouch_config.h +++ b/3rdparty/SoundTouch/soundtouch_config.h @@ -1,7 +1,7 @@ -#ifndef SOUNDTOUCH_CONFIG_H_INCLUDED -#define SOUNDTOUCH_CONFIG_H_INCLUDED - - - -#endif // SOUNDTOUCH_CONFIG_H_INCLUDED +#ifndef SOUNDTOUCH_CONFIG_H_INCLUDED +#define SOUNDTOUCH_CONFIG_H_INCLUDED + + + +#endif // SOUNDTOUCH_CONFIG_H_INCLUDED diff --git a/3rdparty/portaudio/include/pa_win_ds.h b/3rdparty/portaudio/include/pa_win_ds.h index 47e1d722c8..a0a71dc75a 100644 --- a/3rdparty/portaudio/include/pa_win_ds.h +++ b/3rdparty/portaudio/include/pa_win_ds.h @@ -1,101 +1,101 @@ -#ifndef PA_WIN_DS_H -#define PA_WIN_DS_H -/* - * $Id: $ - * PortAudio Portable Real-Time Audio Library - * DirectSound specific extensions - * - * Copyright (c) 1999-2007 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/* - * The text above constitutes the entire PortAudio license; however, - * the PortAudio community also makes the following non-binding requests: - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. It is also - * requested that these non-binding requests be included along with the - * license above. - */ - -/** @file - @ingroup public_header - @brief DirectSound-specific PortAudio API extension header file. -*/ - - -#include "portaudio.h" -#include "pa_win_waveformat.h" - -#ifdef __cplusplus -extern "C" -{ -#endif /* __cplusplus */ - - -#define paWinDirectSoundUseLowLevelLatencyParameters (0x01) -#define paWinDirectSoundUseChannelMask (0x04) - - -typedef struct PaWinDirectSoundStreamInfo{ - unsigned long size; /**< sizeof(PaWinDirectSoundStreamInfo) */ - PaHostApiTypeId hostApiType; /**< paDirectSound */ - unsigned long version; /**< 1 */ - - unsigned long flags; - - /* low-level latency setting support - TODO ** NOT IMPLEMENTED ** - These settings control the number and size of host buffers in order - to set latency. They will be used instead of the generic parameters - to Pa_OpenStream() if flags contains the paWinDirectSoundUseLowLevelLatencyParameters - flag. - - If PaWinDirectSoundStreamInfo structures with paWinDirectSoundUseLowLevelLatencyParameters - are supplied for both input and output in a full duplex stream, then the - input and output framesPerBuffer must be the same, or the larger of the - two must be a multiple of the smaller, otherwise a - paIncompatibleHostApiSpecificStreamInfo error will be returned from - Pa_OpenStream(). - - unsigned long framesPerBuffer; - */ - - /* - support for WAVEFORMATEXTENSIBLE channel masks. If flags contains - paWinDirectSoundUseChannelMask this allows you to specify which speakers - to address in a multichannel stream. Constants for channelMask - are specified in pa_win_waveformat.h - - */ - PaWinWaveFormatChannelMask channelMask; - -}PaWinDirectSoundStreamInfo; - - - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* PA_WIN_DS_H */ +#ifndef PA_WIN_DS_H +#define PA_WIN_DS_H +/* + * $Id: $ + * PortAudio Portable Real-Time Audio Library + * DirectSound specific extensions + * + * Copyright (c) 1999-2007 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* + * The text above constitutes the entire PortAudio license; however, + * the PortAudio community also makes the following non-binding requests: + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. It is also + * requested that these non-binding requests be included along with the + * license above. + */ + +/** @file + @ingroup public_header + @brief DirectSound-specific PortAudio API extension header file. +*/ + + +#include "portaudio.h" +#include "pa_win_waveformat.h" + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + + +#define paWinDirectSoundUseLowLevelLatencyParameters (0x01) +#define paWinDirectSoundUseChannelMask (0x04) + + +typedef struct PaWinDirectSoundStreamInfo{ + unsigned long size; /**< sizeof(PaWinDirectSoundStreamInfo) */ + PaHostApiTypeId hostApiType; /**< paDirectSound */ + unsigned long version; /**< 1 */ + + unsigned long flags; + + /* low-level latency setting support + TODO ** NOT IMPLEMENTED ** + These settings control the number and size of host buffers in order + to set latency. They will be used instead of the generic parameters + to Pa_OpenStream() if flags contains the paWinDirectSoundUseLowLevelLatencyParameters + flag. + + If PaWinDirectSoundStreamInfo structures with paWinDirectSoundUseLowLevelLatencyParameters + are supplied for both input and output in a full duplex stream, then the + input and output framesPerBuffer must be the same, or the larger of the + two must be a multiple of the smaller, otherwise a + paIncompatibleHostApiSpecificStreamInfo error will be returned from + Pa_OpenStream(). + + unsigned long framesPerBuffer; + */ + + /* + support for WAVEFORMATEXTENSIBLE channel masks. If flags contains + paWinDirectSoundUseChannelMask this allows you to specify which speakers + to address in a multichannel stream. Constants for channelMask + are specified in pa_win_waveformat.h + + */ + PaWinWaveFormatChannelMask channelMask; + +}PaWinDirectSoundStreamInfo; + + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* PA_WIN_DS_H */ diff --git a/3rdparty/portaudio/include/pa_win_wasapi.h b/3rdparty/portaudio/include/pa_win_wasapi.h index 9023ff163d..92ecbed974 100644 --- a/3rdparty/portaudio/include/pa_win_wasapi.h +++ b/3rdparty/portaudio/include/pa_win_wasapi.h @@ -1,268 +1,268 @@ -#ifndef PA_WIN_WASAPI_H -#define PA_WIN_WASAPI_H -/* - * $Id: $ - * PortAudio Portable Real-Time Audio Library - * DirectSound specific extensions - * - * Copyright (c) 1999-2007 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/* - * The text above constitutes the entire PortAudio license; however, - * the PortAudio community also makes the following non-binding requests: - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. It is also - * requested that these non-binding requests be included along with the - * license above. - */ - -/** @file - @ingroup public_header - @brief WASAPI-specific PortAudio API extension header file. -*/ - -#include "portaudio.h" -#include "pa_win_waveformat.h" - -#ifdef __cplusplus -extern "C" -{ -#endif /* __cplusplus */ - - -/* Setup flags */ -typedef enum PaWasapiFlags -{ - /* puts WASAPI into exclusive mode */ - paWinWasapiExclusive = (1 << 0), - - /* allows to skip internal PA processing completely */ - paWinWasapiRedirectHostProcessor = (1 << 1), - - /* assigns custom channel mask */ - paWinWasapiUseChannelMask = (1 << 2), - - /* selects non-Event driven method of data read/write - Note: WASAPI Event driven core is capable of 2ms latency!!!, but Polling - method can only provide 15-20ms latency. */ - paWinWasapiPolling = (1 << 3), - - /* forces custom thread priority setting. must be used if PaWasapiStreamInfo::threadPriority - is set to custom value. */ - paWinWasapiThreadPriority = (1 << 4) -} -PaWasapiFlags; -#define paWinWasapiExclusive (paWinWasapiExclusive) -#define paWinWasapiRedirectHostProcessor (paWinWasapiRedirectHostProcessor) -#define paWinWasapiUseChannelMask (paWinWasapiUseChannelMask) -#define paWinWasapiPolling (paWinWasapiPolling) -#define paWinWasapiThreadPriority (paWinWasapiThreadPriority) - - -/* Host processor. Allows to skip internal PA processing completely. - You must set paWinWasapiRedirectHostProcessor flag to PaWasapiStreamInfo::flags member - in order to have host processor redirected to your callback. - Use with caution! inputFrames and outputFrames depend solely on final device setup (buffer - size is just recommendation) but are not changing during run-time once stream is started. -*/ -typedef void (*PaWasapiHostProcessorCallback) (void *inputBuffer, long inputFrames, - void *outputBuffer, long outputFrames, - void *userData); - -/* Device role */ -typedef enum PaWasapiDeviceRole -{ - eRoleRemoteNetworkDevice = 0, - eRoleSpeakers, - eRoleLineLevel, - eRoleHeadphones, - eRoleMicrophone, - eRoleHeadset, - eRoleHandset, - eRoleUnknownDigitalPassthrough, - eRoleSPDIF, - eRoleHDMI, - eRoleUnknownFormFactor -} -PaWasapiDeviceRole; - - -/* Thread priority */ -typedef enum PaWasapiThreadPriority -{ - eThreadPriorityNone = 0, - eThreadPriorityAudio, //!< Default for Shared mode. - eThreadPriorityCapture, - eThreadPriorityDistribution, - eThreadPriorityGames, - eThreadPriorityPlayback, - eThreadPriorityProAudio, //!< Default for Exclusive mode. - eThreadPriorityWindowManager -} -PaWasapiThreadPriority; - - -/* Stream descriptor. */ -typedef struct PaWasapiStreamInfo -{ - unsigned long size; /**< sizeof(PaWasapiStreamInfo) */ - PaHostApiTypeId hostApiType; /**< paWASAPI */ - unsigned long version; /**< 1 */ - - unsigned long flags; /**< collection of PaWasapiFlags */ - - /* Support for WAVEFORMATEXTENSIBLE channel masks. If flags contains - paWinWasapiUseChannelMask this allows you to specify which speakers - to address in a multichannel stream. Constants for channelMask - are specified in pa_win_waveformat.h. Will be used only if - paWinWasapiUseChannelMask flag is specified. - */ - PaWinWaveFormatChannelMask channelMask; - - /* Delivers raw data to callback obtained from GetBuffer() methods skipping - internal PortAudio processing inventory completely. userData parameter will - be the same that was passed to Pa_OpenStream method. Will be used only if - paWinWasapiRedirectHostProcessor flag is specified. - */ - PaWasapiHostProcessorCallback hostProcessorOutput; - PaWasapiHostProcessorCallback hostProcessorInput; - - /* Specifies thread priority explicitly. Will be used only if paWinWasapiThreadPriority flag - is specified. - - Please note, if Input/Output streams are opened simultaniously (Full-Duplex mode) - you shall specify same value for threadPriority or othervise one of the values will be used - to setup thread priority. - */ - PaWasapiThreadPriority threadPriority; -} -PaWasapiStreamInfo; - - -/** Returns default sound format for device. Format is represented by PaWinWaveFormat or - WAVEFORMATEXTENSIBLE structure. - - @param pFormat pointer to PaWinWaveFormat or WAVEFORMATEXTENSIBLE structure. - @param nFormatSize pize of PaWinWaveFormat or WAVEFORMATEXTENSIBLE structure in bytes. - @param nDevice device index. - - @return A non-negative value indicating the number of bytes copied into format decriptor - or, a PaErrorCode (which are always negative) if PortAudio is not initialized - or an error is encountered. -*/ -int PaWasapi_GetDeviceDefaultFormat( void *pFormat, unsigned int nFormatSize, PaDeviceIndex nDevice ); - - -/** Returns device role (PaWasapiDeviceRole enum). - - @param nDevice device index. - - @return A non-negative value indicating device role or, a PaErrorCode (which are always negative) - if PortAudio is not initialized or an error is encountered. -*/ -int/*PaWasapiDeviceRole*/ PaWasapi_GetDeviceRole( PaDeviceIndex nDevice ); - - -/** Boost thread priority of calling thread (MMCSS). Use it for Blocking Interface only for thread - which makes calls to Pa_WriteStream/Pa_ReadStream. - - @param hTask a handle to pointer to priority task. Must be used with PaWasapi_RevertThreadPriority - method to revert thread priority to initial state. - - @param nPriorityClass an Id of thread priority of PaWasapiThreadPriority type. Specifying - eThreadPriorityNone does nothing. - - @return Error code indicating success or failure. - @see PaWasapi_RevertThreadPriority -*/ -PaError PaWasapi_ThreadPriorityBoost( void **hTask, PaWasapiThreadPriority nPriorityClass ); - - -/** Boost thread priority of calling thread (MMCSS). Use it for Blocking Interface only for thread - which makes calls to Pa_WriteStream/Pa_ReadStream. - - @param hTask Task handle obtained by PaWasapi_BoostThreadPriority method. - @return Error code indicating success or failure. - @see PaWasapi_BoostThreadPriority -*/ -PaError PaWasapi_ThreadPriorityRevert( void *hTask ); - - -/* - IMPORTANT: - - WASAPI is implemented for Callback and Blocking interfaces. It supports Shared and Exclusive - share modes. - - Exclusive Mode: - - Exclusive mode allows to deliver audio data directly to hardware bypassing - software mixing. - Exclusive mode is specified by 'paWinWasapiExclusive' flag. - - Callback Interface: - - Provides best audio quality with low latency. Callback interface is implemented in - two versions: - - 1) Event-Driven: - This is the most powerful WASAPI implementation which is capable to provides glitch-free - audio at 2ms latency in Exclusive mode. Lowest possible latency for this mode is - usually - 2ms for HD Audio class audio chips (including on-board audio, 2ms was achieved - on Realtek ALC888/S/T). For Shared mode latency can not go lower than 20ms. - - 2) Poll-Driven: - Polling is another 2-nd method to operate with WASAPI. It is less efficient than Event-Driven - and provides latency at around 12-13ms. Polling must be used to overcome a system bug - under Windows Vista x64 when application is WOW64(32-bit) and Event-Driven method simply times - out (event handle is never signalled on buffer completion). Please note, such Vista bug - does not exist in Windows 7 x64. - Polling is setup by speciying 'paWinWasapiPolling' flag. - Thread priority can be boosted by specifying 'paWinWasapiBlockingThreadPriorityPro' flag. - - Blocking Interface: - - Blocking interface is implemented but due to above described Poll-Driven method can not - deliver low latency audio. Specifying too low latency in Shared mode will result in - distorted audio although Exclusive mode adds stability. - - Pa_IsFormatSupported: - - To check format with correct Share Mode (Exclusive/Shared) you must supply - PaWasapiStreamInfo with flags paWinWasapiExclusive set through member of - PaStreamParameters::hostApiSpecificStreamInfo structure. - - Pa_OpenStream: - - To set desired Share Mode (Exclusive/Shared) you must supply - PaWasapiStreamInfo with flags paWinWasapiExclusive set through member of - PaStreamParameters::hostApiSpecificStreamInfo structure. -*/ - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* PA_WIN_WASAPI_H */ +#ifndef PA_WIN_WASAPI_H +#define PA_WIN_WASAPI_H +/* + * $Id: $ + * PortAudio Portable Real-Time Audio Library + * DirectSound specific extensions + * + * Copyright (c) 1999-2007 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* + * The text above constitutes the entire PortAudio license; however, + * the PortAudio community also makes the following non-binding requests: + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. It is also + * requested that these non-binding requests be included along with the + * license above. + */ + +/** @file + @ingroup public_header + @brief WASAPI-specific PortAudio API extension header file. +*/ + +#include "portaudio.h" +#include "pa_win_waveformat.h" + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + + +/* Setup flags */ +typedef enum PaWasapiFlags +{ + /* puts WASAPI into exclusive mode */ + paWinWasapiExclusive = (1 << 0), + + /* allows to skip internal PA processing completely */ + paWinWasapiRedirectHostProcessor = (1 << 1), + + /* assigns custom channel mask */ + paWinWasapiUseChannelMask = (1 << 2), + + /* selects non-Event driven method of data read/write + Note: WASAPI Event driven core is capable of 2ms latency!!!, but Polling + method can only provide 15-20ms latency. */ + paWinWasapiPolling = (1 << 3), + + /* forces custom thread priority setting. must be used if PaWasapiStreamInfo::threadPriority + is set to custom value. */ + paWinWasapiThreadPriority = (1 << 4) +} +PaWasapiFlags; +#define paWinWasapiExclusive (paWinWasapiExclusive) +#define paWinWasapiRedirectHostProcessor (paWinWasapiRedirectHostProcessor) +#define paWinWasapiUseChannelMask (paWinWasapiUseChannelMask) +#define paWinWasapiPolling (paWinWasapiPolling) +#define paWinWasapiThreadPriority (paWinWasapiThreadPriority) + + +/* Host processor. Allows to skip internal PA processing completely. + You must set paWinWasapiRedirectHostProcessor flag to PaWasapiStreamInfo::flags member + in order to have host processor redirected to your callback. + Use with caution! inputFrames and outputFrames depend solely on final device setup (buffer + size is just recommendation) but are not changing during run-time once stream is started. +*/ +typedef void (*PaWasapiHostProcessorCallback) (void *inputBuffer, long inputFrames, + void *outputBuffer, long outputFrames, + void *userData); + +/* Device role */ +typedef enum PaWasapiDeviceRole +{ + eRoleRemoteNetworkDevice = 0, + eRoleSpeakers, + eRoleLineLevel, + eRoleHeadphones, + eRoleMicrophone, + eRoleHeadset, + eRoleHandset, + eRoleUnknownDigitalPassthrough, + eRoleSPDIF, + eRoleHDMI, + eRoleUnknownFormFactor +} +PaWasapiDeviceRole; + + +/* Thread priority */ +typedef enum PaWasapiThreadPriority +{ + eThreadPriorityNone = 0, + eThreadPriorityAudio, //!< Default for Shared mode. + eThreadPriorityCapture, + eThreadPriorityDistribution, + eThreadPriorityGames, + eThreadPriorityPlayback, + eThreadPriorityProAudio, //!< Default for Exclusive mode. + eThreadPriorityWindowManager +} +PaWasapiThreadPriority; + + +/* Stream descriptor. */ +typedef struct PaWasapiStreamInfo +{ + unsigned long size; /**< sizeof(PaWasapiStreamInfo) */ + PaHostApiTypeId hostApiType; /**< paWASAPI */ + unsigned long version; /**< 1 */ + + unsigned long flags; /**< collection of PaWasapiFlags */ + + /* Support for WAVEFORMATEXTENSIBLE channel masks. If flags contains + paWinWasapiUseChannelMask this allows you to specify which speakers + to address in a multichannel stream. Constants for channelMask + are specified in pa_win_waveformat.h. Will be used only if + paWinWasapiUseChannelMask flag is specified. + */ + PaWinWaveFormatChannelMask channelMask; + + /* Delivers raw data to callback obtained from GetBuffer() methods skipping + internal PortAudio processing inventory completely. userData parameter will + be the same that was passed to Pa_OpenStream method. Will be used only if + paWinWasapiRedirectHostProcessor flag is specified. + */ + PaWasapiHostProcessorCallback hostProcessorOutput; + PaWasapiHostProcessorCallback hostProcessorInput; + + /* Specifies thread priority explicitly. Will be used only if paWinWasapiThreadPriority flag + is specified. + + Please note, if Input/Output streams are opened simultaniously (Full-Duplex mode) + you shall specify same value for threadPriority or othervise one of the values will be used + to setup thread priority. + */ + PaWasapiThreadPriority threadPriority; +} +PaWasapiStreamInfo; + + +/** Returns default sound format for device. Format is represented by PaWinWaveFormat or + WAVEFORMATEXTENSIBLE structure. + + @param pFormat pointer to PaWinWaveFormat or WAVEFORMATEXTENSIBLE structure. + @param nFormatSize pize of PaWinWaveFormat or WAVEFORMATEXTENSIBLE structure in bytes. + @param nDevice device index. + + @return A non-negative value indicating the number of bytes copied into format decriptor + or, a PaErrorCode (which are always negative) if PortAudio is not initialized + or an error is encountered. +*/ +int PaWasapi_GetDeviceDefaultFormat( void *pFormat, unsigned int nFormatSize, PaDeviceIndex nDevice ); + + +/** Returns device role (PaWasapiDeviceRole enum). + + @param nDevice device index. + + @return A non-negative value indicating device role or, a PaErrorCode (which are always negative) + if PortAudio is not initialized or an error is encountered. +*/ +int/*PaWasapiDeviceRole*/ PaWasapi_GetDeviceRole( PaDeviceIndex nDevice ); + + +/** Boost thread priority of calling thread (MMCSS). Use it for Blocking Interface only for thread + which makes calls to Pa_WriteStream/Pa_ReadStream. + + @param hTask a handle to pointer to priority task. Must be used with PaWasapi_RevertThreadPriority + method to revert thread priority to initial state. + + @param nPriorityClass an Id of thread priority of PaWasapiThreadPriority type. Specifying + eThreadPriorityNone does nothing. + + @return Error code indicating success or failure. + @see PaWasapi_RevertThreadPriority +*/ +PaError PaWasapi_ThreadPriorityBoost( void **hTask, PaWasapiThreadPriority nPriorityClass ); + + +/** Boost thread priority of calling thread (MMCSS). Use it for Blocking Interface only for thread + which makes calls to Pa_WriteStream/Pa_ReadStream. + + @param hTask Task handle obtained by PaWasapi_BoostThreadPriority method. + @return Error code indicating success or failure. + @see PaWasapi_BoostThreadPriority +*/ +PaError PaWasapi_ThreadPriorityRevert( void *hTask ); + + +/* + IMPORTANT: + + WASAPI is implemented for Callback and Blocking interfaces. It supports Shared and Exclusive + share modes. + + Exclusive Mode: + + Exclusive mode allows to deliver audio data directly to hardware bypassing + software mixing. + Exclusive mode is specified by 'paWinWasapiExclusive' flag. + + Callback Interface: + + Provides best audio quality with low latency. Callback interface is implemented in + two versions: + + 1) Event-Driven: + This is the most powerful WASAPI implementation which is capable to provides glitch-free + audio at 2ms latency in Exclusive mode. Lowest possible latency for this mode is + usually - 2ms for HD Audio class audio chips (including on-board audio, 2ms was achieved + on Realtek ALC888/S/T). For Shared mode latency can not go lower than 20ms. + + 2) Poll-Driven: + Polling is another 2-nd method to operate with WASAPI. It is less efficient than Event-Driven + and provides latency at around 12-13ms. Polling must be used to overcome a system bug + under Windows Vista x64 when application is WOW64(32-bit) and Event-Driven method simply times + out (event handle is never signalled on buffer completion). Please note, such Vista bug + does not exist in Windows 7 x64. + Polling is setup by speciying 'paWinWasapiPolling' flag. + Thread priority can be boosted by specifying 'paWinWasapiBlockingThreadPriorityPro' flag. + + Blocking Interface: + + Blocking interface is implemented but due to above described Poll-Driven method can not + deliver low latency audio. Specifying too low latency in Shared mode will result in + distorted audio although Exclusive mode adds stability. + + Pa_IsFormatSupported: + + To check format with correct Share Mode (Exclusive/Shared) you must supply + PaWasapiStreamInfo with flags paWinWasapiExclusive set through member of + PaStreamParameters::hostApiSpecificStreamInfo structure. + + Pa_OpenStream: + + To set desired Share Mode (Exclusive/Shared) you must supply + PaWasapiStreamInfo with flags paWinWasapiExclusive set through member of + PaStreamParameters::hostApiSpecificStreamInfo structure. +*/ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* PA_WIN_WASAPI_H */ diff --git a/3rdparty/portaudio/include/pa_win_waveformat.h b/3rdparty/portaudio/include/pa_win_waveformat.h index dce18c0d93..2c00267117 100644 --- a/3rdparty/portaudio/include/pa_win_waveformat.h +++ b/3rdparty/portaudio/include/pa_win_waveformat.h @@ -1,199 +1,199 @@ -#ifndef PA_WIN_WAVEFORMAT_H -#define PA_WIN_WAVEFORMAT_H - -/* - * PortAudio Portable Real-Time Audio Library - * Windows WAVEFORMAT* data structure utilities - * portaudio.h should be included before this file. - * - * Copyright (c) 2007 Ross Bencina - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/* - * The text above constitutes the entire PortAudio license; however, - * the PortAudio community also makes the following non-binding requests: - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. It is also - * requested that these non-binding requests be included along with the - * license above. - */ - -/** @file - @ingroup public_header - @brief Windows specific PortAudio API extension and utilities header file. -*/ - -#ifdef __cplusplus -extern "C" { -#endif - -/* - The following #defines for speaker channel masks are the same - as those in ksmedia.h, except with PAWIN_ prepended, KSAUDIO_ removed - in some cases, and casts to PaWinWaveFormatChannelMask added. -*/ - -typedef unsigned long PaWinWaveFormatChannelMask; - -/* Speaker Positions: */ -#define PAWIN_SPEAKER_FRONT_LEFT ((PaWinWaveFormatChannelMask)0x1) -#define PAWIN_SPEAKER_FRONT_RIGHT ((PaWinWaveFormatChannelMask)0x2) -#define PAWIN_SPEAKER_FRONT_CENTER ((PaWinWaveFormatChannelMask)0x4) -#define PAWIN_SPEAKER_LOW_FREQUENCY ((PaWinWaveFormatChannelMask)0x8) -#define PAWIN_SPEAKER_BACK_LEFT ((PaWinWaveFormatChannelMask)0x10) -#define PAWIN_SPEAKER_BACK_RIGHT ((PaWinWaveFormatChannelMask)0x20) -#define PAWIN_SPEAKER_FRONT_LEFT_OF_CENTER ((PaWinWaveFormatChannelMask)0x40) -#define PAWIN_SPEAKER_FRONT_RIGHT_OF_CENTER ((PaWinWaveFormatChannelMask)0x80) -#define PAWIN_SPEAKER_BACK_CENTER ((PaWinWaveFormatChannelMask)0x100) -#define PAWIN_SPEAKER_SIDE_LEFT ((PaWinWaveFormatChannelMask)0x200) -#define PAWIN_SPEAKER_SIDE_RIGHT ((PaWinWaveFormatChannelMask)0x400) -#define PAWIN_SPEAKER_TOP_CENTER ((PaWinWaveFormatChannelMask)0x800) -#define PAWIN_SPEAKER_TOP_FRONT_LEFT ((PaWinWaveFormatChannelMask)0x1000) -#define PAWIN_SPEAKER_TOP_FRONT_CENTER ((PaWinWaveFormatChannelMask)0x2000) -#define PAWIN_SPEAKER_TOP_FRONT_RIGHT ((PaWinWaveFormatChannelMask)0x4000) -#define PAWIN_SPEAKER_TOP_BACK_LEFT ((PaWinWaveFormatChannelMask)0x8000) -#define PAWIN_SPEAKER_TOP_BACK_CENTER ((PaWinWaveFormatChannelMask)0x10000) -#define PAWIN_SPEAKER_TOP_BACK_RIGHT ((PaWinWaveFormatChannelMask)0x20000) - -/* Bit mask locations reserved for future use */ -#define PAWIN_SPEAKER_RESERVED ((PaWinWaveFormatChannelMask)0x7FFC0000) - -/* Used to specify that any possible permutation of speaker configurations */ -#define PAWIN_SPEAKER_ALL ((PaWinWaveFormatChannelMask)0x80000000) - -/* DirectSound Speaker Config */ -#define PAWIN_SPEAKER_DIRECTOUT 0 -#define PAWIN_SPEAKER_MONO (PAWIN_SPEAKER_FRONT_CENTER) -#define PAWIN_SPEAKER_STEREO (PAWIN_SPEAKER_FRONT_LEFT | PAWIN_SPEAKER_FRONT_RIGHT) -#define PAWIN_SPEAKER_QUAD (PAWIN_SPEAKER_FRONT_LEFT | PAWIN_SPEAKER_FRONT_RIGHT | \ - PAWIN_SPEAKER_BACK_LEFT | PAWIN_SPEAKER_BACK_RIGHT) -#define PAWIN_SPEAKER_SURROUND (PAWIN_SPEAKER_FRONT_LEFT | PAWIN_SPEAKER_FRONT_RIGHT | \ - PAWIN_SPEAKER_FRONT_CENTER | PAWIN_SPEAKER_BACK_CENTER) -#define PAWIN_SPEAKER_5POINT1 (PAWIN_SPEAKER_FRONT_LEFT | PAWIN_SPEAKER_FRONT_RIGHT | \ - PAWIN_SPEAKER_FRONT_CENTER | PAWIN_SPEAKER_LOW_FREQUENCY | \ - PAWIN_SPEAKER_BACK_LEFT | PAWIN_SPEAKER_BACK_RIGHT) -#define PAWIN_SPEAKER_7POINT1 (PAWIN_SPEAKER_FRONT_LEFT | PAWIN_SPEAKER_FRONT_RIGHT | \ - PAWIN_SPEAKER_FRONT_CENTER | PAWIN_SPEAKER_LOW_FREQUENCY | \ - PAWIN_SPEAKER_BACK_LEFT | PAWIN_SPEAKER_BACK_RIGHT | \ - PAWIN_SPEAKER_FRONT_LEFT_OF_CENTER | PAWIN_SPEAKER_FRONT_RIGHT_OF_CENTER) -#define PAWIN_SPEAKER_5POINT1_SURROUND (PAWIN_SPEAKER_FRONT_LEFT | PAWIN_SPEAKER_FRONT_RIGHT | \ - PAWIN_SPEAKER_FRONT_CENTER | PAWIN_SPEAKER_LOW_FREQUENCY | \ - PAWIN_SPEAKER_SIDE_LEFT | PAWIN_SPEAKER_SIDE_RIGHT) -#define PAWIN_SPEAKER_7POINT1_SURROUND (PAWIN_SPEAKER_FRONT_LEFT | PAWIN_SPEAKER_FRONT_RIGHT | \ - PAWIN_SPEAKER_FRONT_CENTER | PAWIN_SPEAKER_LOW_FREQUENCY | \ - PAWIN_SPEAKER_BACK_LEFT | PAWIN_SPEAKER_BACK_RIGHT | \ - PAWIN_SPEAKER_SIDE_LEFT | PAWIN_SPEAKER_SIDE_RIGHT) -/* - According to the Microsoft documentation: - The following are obsolete 5.1 and 7.1 settings (they lack side speakers). Note this means - that the default 5.1 and 7.1 settings (KSAUDIO_SPEAKER_5POINT1 and KSAUDIO_SPEAKER_7POINT1 are - similarly obsolete but are unchanged for compatibility reasons). -*/ -#define PAWIN_SPEAKER_5POINT1_BACK PAWIN_SPEAKER_5POINT1 -#define PAWIN_SPEAKER_7POINT1_WIDE PAWIN_SPEAKER_7POINT1 - -/* DVD Speaker Positions */ -#define PAWIN_SPEAKER_GROUND_FRONT_LEFT PAWIN_SPEAKER_FRONT_LEFT -#define PAWIN_SPEAKER_GROUND_FRONT_CENTER PAWIN_SPEAKER_FRONT_CENTER -#define PAWIN_SPEAKER_GROUND_FRONT_RIGHT PAWIN_SPEAKER_FRONT_RIGHT -#define PAWIN_SPEAKER_GROUND_REAR_LEFT PAWIN_SPEAKER_BACK_LEFT -#define PAWIN_SPEAKER_GROUND_REAR_RIGHT PAWIN_SPEAKER_BACK_RIGHT -#define PAWIN_SPEAKER_TOP_MIDDLE PAWIN_SPEAKER_TOP_CENTER -#define PAWIN_SPEAKER_SUPER_WOOFER PAWIN_SPEAKER_LOW_FREQUENCY - - -/* - PaWinWaveFormat is defined here to provide compatibility with - compilation environments which don't have headers defining - WAVEFORMATEXTENSIBLE (e.g. older versions of MSVC, Borland C++ etc. - - The fields for WAVEFORMATEX and WAVEFORMATEXTENSIBLE are declared as an - unsigned char array here to avoid clients who include this file having - a dependency on windows.h and mmsystem.h, and also to to avoid having - to write separate packing pragmas for each compiler. -*/ -#define PAWIN_SIZEOF_WAVEFORMATEX 18 -#define PAWIN_SIZEOF_WAVEFORMATEXTENSIBLE (PAWIN_SIZEOF_WAVEFORMATEX + 22) - -typedef struct{ - unsigned char fields[ PAWIN_SIZEOF_WAVEFORMATEXTENSIBLE ]; - unsigned long extraLongForAlignment; /* ensure that compiler aligns struct to DWORD */ -} PaWinWaveFormat; - -/* - WAVEFORMATEXTENSIBLE fields: - - union { - WORD wValidBitsPerSample; - WORD wSamplesPerBlock; - WORD wReserved; - } Samples; - DWORD dwChannelMask; - GUID SubFormat; -*/ - -#define PAWIN_INDEXOF_WVALIDBITSPERSAMPLE (PAWIN_SIZEOF_WAVEFORMATEX+0) -#define PAWIN_INDEXOF_DWCHANNELMASK (PAWIN_SIZEOF_WAVEFORMATEX+2) -#define PAWIN_INDEXOF_SUBFORMAT (PAWIN_SIZEOF_WAVEFORMATEX+6) - - -/* - Valid values to pass for the waveFormatTag PaWin_InitializeWaveFormatEx and - PaWin_InitializeWaveFormatExtensible functions below. These must match - the standard Windows WAVE_FORMAT_* values. -*/ -#define PAWIN_WAVE_FORMAT_PCM (1) -#define PAWIN_WAVE_FORMAT_IEEE_FLOAT (3) -#define PAWIN_WAVE_FORMAT_DOLBY_AC3_SPDIF (0x0092) -#define PAWIN_WAVE_FORMAT_WMA_SPDIF (0x0164) - - -/* - returns PAWIN_WAVE_FORMAT_PCM or PAWIN_WAVE_FORMAT_IEEE_FLOAT - depending on the sampleFormat parameter. -*/ -int PaWin_SampleFormatToLinearWaveFormatTag( PaSampleFormat sampleFormat ); - -/* - Use the following two functions to initialize the waveformat structure. -*/ - -void PaWin_InitializeWaveFormatEx( PaWinWaveFormat *waveFormat, - int numChannels, PaSampleFormat sampleFormat, int waveFormatTag, double sampleRate ); - - -void PaWin_InitializeWaveFormatExtensible( PaWinWaveFormat *waveFormat, - int numChannels, PaSampleFormat sampleFormat, int waveFormatTag, double sampleRate, - PaWinWaveFormatChannelMask channelMask ); - - -/* Map a channel count to a speaker channel mask */ -PaWinWaveFormatChannelMask PaWin_DefaultChannelMask( int numChannels ); - - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - +#ifndef PA_WIN_WAVEFORMAT_H +#define PA_WIN_WAVEFORMAT_H + +/* + * PortAudio Portable Real-Time Audio Library + * Windows WAVEFORMAT* data structure utilities + * portaudio.h should be included before this file. + * + * Copyright (c) 2007 Ross Bencina + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* + * The text above constitutes the entire PortAudio license; however, + * the PortAudio community also makes the following non-binding requests: + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. It is also + * requested that these non-binding requests be included along with the + * license above. + */ + +/** @file + @ingroup public_header + @brief Windows specific PortAudio API extension and utilities header file. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + The following #defines for speaker channel masks are the same + as those in ksmedia.h, except with PAWIN_ prepended, KSAUDIO_ removed + in some cases, and casts to PaWinWaveFormatChannelMask added. +*/ + +typedef unsigned long PaWinWaveFormatChannelMask; + +/* Speaker Positions: */ +#define PAWIN_SPEAKER_FRONT_LEFT ((PaWinWaveFormatChannelMask)0x1) +#define PAWIN_SPEAKER_FRONT_RIGHT ((PaWinWaveFormatChannelMask)0x2) +#define PAWIN_SPEAKER_FRONT_CENTER ((PaWinWaveFormatChannelMask)0x4) +#define PAWIN_SPEAKER_LOW_FREQUENCY ((PaWinWaveFormatChannelMask)0x8) +#define PAWIN_SPEAKER_BACK_LEFT ((PaWinWaveFormatChannelMask)0x10) +#define PAWIN_SPEAKER_BACK_RIGHT ((PaWinWaveFormatChannelMask)0x20) +#define PAWIN_SPEAKER_FRONT_LEFT_OF_CENTER ((PaWinWaveFormatChannelMask)0x40) +#define PAWIN_SPEAKER_FRONT_RIGHT_OF_CENTER ((PaWinWaveFormatChannelMask)0x80) +#define PAWIN_SPEAKER_BACK_CENTER ((PaWinWaveFormatChannelMask)0x100) +#define PAWIN_SPEAKER_SIDE_LEFT ((PaWinWaveFormatChannelMask)0x200) +#define PAWIN_SPEAKER_SIDE_RIGHT ((PaWinWaveFormatChannelMask)0x400) +#define PAWIN_SPEAKER_TOP_CENTER ((PaWinWaveFormatChannelMask)0x800) +#define PAWIN_SPEAKER_TOP_FRONT_LEFT ((PaWinWaveFormatChannelMask)0x1000) +#define PAWIN_SPEAKER_TOP_FRONT_CENTER ((PaWinWaveFormatChannelMask)0x2000) +#define PAWIN_SPEAKER_TOP_FRONT_RIGHT ((PaWinWaveFormatChannelMask)0x4000) +#define PAWIN_SPEAKER_TOP_BACK_LEFT ((PaWinWaveFormatChannelMask)0x8000) +#define PAWIN_SPEAKER_TOP_BACK_CENTER ((PaWinWaveFormatChannelMask)0x10000) +#define PAWIN_SPEAKER_TOP_BACK_RIGHT ((PaWinWaveFormatChannelMask)0x20000) + +/* Bit mask locations reserved for future use */ +#define PAWIN_SPEAKER_RESERVED ((PaWinWaveFormatChannelMask)0x7FFC0000) + +/* Used to specify that any possible permutation of speaker configurations */ +#define PAWIN_SPEAKER_ALL ((PaWinWaveFormatChannelMask)0x80000000) + +/* DirectSound Speaker Config */ +#define PAWIN_SPEAKER_DIRECTOUT 0 +#define PAWIN_SPEAKER_MONO (PAWIN_SPEAKER_FRONT_CENTER) +#define PAWIN_SPEAKER_STEREO (PAWIN_SPEAKER_FRONT_LEFT | PAWIN_SPEAKER_FRONT_RIGHT) +#define PAWIN_SPEAKER_QUAD (PAWIN_SPEAKER_FRONT_LEFT | PAWIN_SPEAKER_FRONT_RIGHT | \ + PAWIN_SPEAKER_BACK_LEFT | PAWIN_SPEAKER_BACK_RIGHT) +#define PAWIN_SPEAKER_SURROUND (PAWIN_SPEAKER_FRONT_LEFT | PAWIN_SPEAKER_FRONT_RIGHT | \ + PAWIN_SPEAKER_FRONT_CENTER | PAWIN_SPEAKER_BACK_CENTER) +#define PAWIN_SPEAKER_5POINT1 (PAWIN_SPEAKER_FRONT_LEFT | PAWIN_SPEAKER_FRONT_RIGHT | \ + PAWIN_SPEAKER_FRONT_CENTER | PAWIN_SPEAKER_LOW_FREQUENCY | \ + PAWIN_SPEAKER_BACK_LEFT | PAWIN_SPEAKER_BACK_RIGHT) +#define PAWIN_SPEAKER_7POINT1 (PAWIN_SPEAKER_FRONT_LEFT | PAWIN_SPEAKER_FRONT_RIGHT | \ + PAWIN_SPEAKER_FRONT_CENTER | PAWIN_SPEAKER_LOW_FREQUENCY | \ + PAWIN_SPEAKER_BACK_LEFT | PAWIN_SPEAKER_BACK_RIGHT | \ + PAWIN_SPEAKER_FRONT_LEFT_OF_CENTER | PAWIN_SPEAKER_FRONT_RIGHT_OF_CENTER) +#define PAWIN_SPEAKER_5POINT1_SURROUND (PAWIN_SPEAKER_FRONT_LEFT | PAWIN_SPEAKER_FRONT_RIGHT | \ + PAWIN_SPEAKER_FRONT_CENTER | PAWIN_SPEAKER_LOW_FREQUENCY | \ + PAWIN_SPEAKER_SIDE_LEFT | PAWIN_SPEAKER_SIDE_RIGHT) +#define PAWIN_SPEAKER_7POINT1_SURROUND (PAWIN_SPEAKER_FRONT_LEFT | PAWIN_SPEAKER_FRONT_RIGHT | \ + PAWIN_SPEAKER_FRONT_CENTER | PAWIN_SPEAKER_LOW_FREQUENCY | \ + PAWIN_SPEAKER_BACK_LEFT | PAWIN_SPEAKER_BACK_RIGHT | \ + PAWIN_SPEAKER_SIDE_LEFT | PAWIN_SPEAKER_SIDE_RIGHT) +/* + According to the Microsoft documentation: + The following are obsolete 5.1 and 7.1 settings (they lack side speakers). Note this means + that the default 5.1 and 7.1 settings (KSAUDIO_SPEAKER_5POINT1 and KSAUDIO_SPEAKER_7POINT1 are + similarly obsolete but are unchanged for compatibility reasons). +*/ +#define PAWIN_SPEAKER_5POINT1_BACK PAWIN_SPEAKER_5POINT1 +#define PAWIN_SPEAKER_7POINT1_WIDE PAWIN_SPEAKER_7POINT1 + +/* DVD Speaker Positions */ +#define PAWIN_SPEAKER_GROUND_FRONT_LEFT PAWIN_SPEAKER_FRONT_LEFT +#define PAWIN_SPEAKER_GROUND_FRONT_CENTER PAWIN_SPEAKER_FRONT_CENTER +#define PAWIN_SPEAKER_GROUND_FRONT_RIGHT PAWIN_SPEAKER_FRONT_RIGHT +#define PAWIN_SPEAKER_GROUND_REAR_LEFT PAWIN_SPEAKER_BACK_LEFT +#define PAWIN_SPEAKER_GROUND_REAR_RIGHT PAWIN_SPEAKER_BACK_RIGHT +#define PAWIN_SPEAKER_TOP_MIDDLE PAWIN_SPEAKER_TOP_CENTER +#define PAWIN_SPEAKER_SUPER_WOOFER PAWIN_SPEAKER_LOW_FREQUENCY + + +/* + PaWinWaveFormat is defined here to provide compatibility with + compilation environments which don't have headers defining + WAVEFORMATEXTENSIBLE (e.g. older versions of MSVC, Borland C++ etc. + + The fields for WAVEFORMATEX and WAVEFORMATEXTENSIBLE are declared as an + unsigned char array here to avoid clients who include this file having + a dependency on windows.h and mmsystem.h, and also to to avoid having + to write separate packing pragmas for each compiler. +*/ +#define PAWIN_SIZEOF_WAVEFORMATEX 18 +#define PAWIN_SIZEOF_WAVEFORMATEXTENSIBLE (PAWIN_SIZEOF_WAVEFORMATEX + 22) + +typedef struct{ + unsigned char fields[ PAWIN_SIZEOF_WAVEFORMATEXTENSIBLE ]; + unsigned long extraLongForAlignment; /* ensure that compiler aligns struct to DWORD */ +} PaWinWaveFormat; + +/* + WAVEFORMATEXTENSIBLE fields: + + union { + WORD wValidBitsPerSample; + WORD wSamplesPerBlock; + WORD wReserved; + } Samples; + DWORD dwChannelMask; + GUID SubFormat; +*/ + +#define PAWIN_INDEXOF_WVALIDBITSPERSAMPLE (PAWIN_SIZEOF_WAVEFORMATEX+0) +#define PAWIN_INDEXOF_DWCHANNELMASK (PAWIN_SIZEOF_WAVEFORMATEX+2) +#define PAWIN_INDEXOF_SUBFORMAT (PAWIN_SIZEOF_WAVEFORMATEX+6) + + +/* + Valid values to pass for the waveFormatTag PaWin_InitializeWaveFormatEx and + PaWin_InitializeWaveFormatExtensible functions below. These must match + the standard Windows WAVE_FORMAT_* values. +*/ +#define PAWIN_WAVE_FORMAT_PCM (1) +#define PAWIN_WAVE_FORMAT_IEEE_FLOAT (3) +#define PAWIN_WAVE_FORMAT_DOLBY_AC3_SPDIF (0x0092) +#define PAWIN_WAVE_FORMAT_WMA_SPDIF (0x0164) + + +/* + returns PAWIN_WAVE_FORMAT_PCM or PAWIN_WAVE_FORMAT_IEEE_FLOAT + depending on the sampleFormat parameter. +*/ +int PaWin_SampleFormatToLinearWaveFormatTag( PaSampleFormat sampleFormat ); + +/* + Use the following two functions to initialize the waveformat structure. +*/ + +void PaWin_InitializeWaveFormatEx( PaWinWaveFormat *waveFormat, + int numChannels, PaSampleFormat sampleFormat, int waveFormatTag, double sampleRate ); + + +void PaWin_InitializeWaveFormatExtensible( PaWinWaveFormat *waveFormat, + int numChannels, PaSampleFormat sampleFormat, int waveFormatTag, double sampleRate, + PaWinWaveFormatChannelMask channelMask ); + + +/* Map a channel count to a speaker channel mask */ +PaWinWaveFormatChannelMask PaWin_DefaultChannelMask( int numChannels ); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + #endif /* PA_WIN_WAVEFORMAT_H */ \ No newline at end of file diff --git a/3rdparty/portaudio/src/common/pa_debugprint.c b/3rdparty/portaudio/src/common/pa_debugprint.c index 7ee8f7f232..6cb114eca1 100644 --- a/3rdparty/portaudio/src/common/pa_debugprint.c +++ b/3rdparty/portaudio/src/common/pa_debugprint.c @@ -1,126 +1,126 @@ -/* - * $Id: pa_log.c $ - * Portable Audio I/O Library Multi-Host API front end - * Validate function parameters and manage multiple host APIs. - * - * Based on the Open Source API proposed by Ross Bencina - * Copyright (c) 1999-2006 Ross Bencina, Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/* - * The text above constitutes the entire PortAudio license; however, - * the PortAudio community also makes the following non-binding requests: - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. It is also - * requested that these non-binding requests be included along with the - * license above. - */ - -/** @file - @ingroup common_src - - @brief Implements log function. - - PaUtil_SetLogPrintFunction can be user called to replace the provided - DefaultLogPrint function, which writes to stderr. - One can NOT pass var_args across compiler/dll boundaries as it is not - "byte code/abi portable". So the technique used here is to allocate a local - a static array, write in it, then callback the user with a pointer to its - start. - - @todo Consider allocating strdump using dynamic allocation. - @todo Consider reentrancy and possibly corrupted strdump buffer. -*/ - -#include -#include - -#include "pa_debugprint.h" - -// for OutputDebugStringA -#if defined(_MSC_VER) && defined(PA_ENABLE_MSVC_DEBUG_OUTPUT) - #define WIN32_LEAN_AND_MEAN // exclude rare headers - #include "windows.h" -#endif - -// User callback -static PaUtilLogCallback userCB = NULL; - -// Sets user callback -void PaUtil_SetDebugPrintFunction(PaUtilLogCallback cb) -{ - userCB = cb; -} - -/* - If your platform doesn’t have vsnprintf, you are stuck with a - VERY dangerous alternative, vsprintf (with no n) -*/ -#if _MSC_VER - /* Some Windows Mobile SDKs don't define vsnprintf but all define _vsnprintf (hopefully). - According to MSDN "vsnprintf is identical to _vsnprintf". So we use _vsnprintf with MSC. - */ - #define VSNPRINTF _vsnprintf -#else - #define VSNPRINTF vsnprintf -#endif - -#define PA_LOG_BUF_SIZE 2048 - -void PaUtil_DebugPrint( const char *format, ... ) -{ - // Optional logging into Output console of Visual Studio -#if defined(_MSC_VER) && defined(PA_ENABLE_MSVC_DEBUG_OUTPUT) - { - char buf[PA_LOG_BUF_SIZE]; - va_list ap; - va_start(ap, format); - VSNPRINTF(buf, sizeof(buf), format, ap); - buf[sizeof(buf)-1] = 0; - OutputDebugStringA(buf); - va_end(ap); - } -#endif - - // Output to User-Callback - if (userCB != NULL) - { - char strdump[PA_LOG_BUF_SIZE]; - va_list ap; - va_start(ap, format); - VSNPRINTF(strdump, sizeof(strdump), format, ap); - strdump[sizeof(strdump)-1] = 0; - userCB(strdump); - va_end(ap); - } - else - // Standard output to stderr - { - va_list ap; - va_start(ap, format); - vfprintf(stderr, format, ap); - va_end(ap); - fflush(stderr); - } -} +/* + * $Id: pa_log.c $ + * Portable Audio I/O Library Multi-Host API front end + * Validate function parameters and manage multiple host APIs. + * + * Based on the Open Source API proposed by Ross Bencina + * Copyright (c) 1999-2006 Ross Bencina, Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* + * The text above constitutes the entire PortAudio license; however, + * the PortAudio community also makes the following non-binding requests: + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. It is also + * requested that these non-binding requests be included along with the + * license above. + */ + +/** @file + @ingroup common_src + + @brief Implements log function. + + PaUtil_SetLogPrintFunction can be user called to replace the provided + DefaultLogPrint function, which writes to stderr. + One can NOT pass var_args across compiler/dll boundaries as it is not + "byte code/abi portable". So the technique used here is to allocate a local + a static array, write in it, then callback the user with a pointer to its + start. + + @todo Consider allocating strdump using dynamic allocation. + @todo Consider reentrancy and possibly corrupted strdump buffer. +*/ + +#include +#include + +#include "pa_debugprint.h" + +// for OutputDebugStringA +#if defined(_MSC_VER) && defined(PA_ENABLE_MSVC_DEBUG_OUTPUT) + #define WIN32_LEAN_AND_MEAN // exclude rare headers + #include "windows.h" +#endif + +// User callback +static PaUtilLogCallback userCB = NULL; + +// Sets user callback +void PaUtil_SetDebugPrintFunction(PaUtilLogCallback cb) +{ + userCB = cb; +} + +/* + If your platform doesn’t have vsnprintf, you are stuck with a + VERY dangerous alternative, vsprintf (with no n) +*/ +#if _MSC_VER + /* Some Windows Mobile SDKs don't define vsnprintf but all define _vsnprintf (hopefully). + According to MSDN "vsnprintf is identical to _vsnprintf". So we use _vsnprintf with MSC. + */ + #define VSNPRINTF _vsnprintf +#else + #define VSNPRINTF vsnprintf +#endif + +#define PA_LOG_BUF_SIZE 2048 + +void PaUtil_DebugPrint( const char *format, ... ) +{ + // Optional logging into Output console of Visual Studio +#if defined(_MSC_VER) && defined(PA_ENABLE_MSVC_DEBUG_OUTPUT) + { + char buf[PA_LOG_BUF_SIZE]; + va_list ap; + va_start(ap, format); + VSNPRINTF(buf, sizeof(buf), format, ap); + buf[sizeof(buf)-1] = 0; + OutputDebugStringA(buf); + va_end(ap); + } +#endif + + // Output to User-Callback + if (userCB != NULL) + { + char strdump[PA_LOG_BUF_SIZE]; + va_list ap; + va_start(ap, format); + VSNPRINTF(strdump, sizeof(strdump), format, ap); + strdump[sizeof(strdump)-1] = 0; + userCB(strdump); + va_end(ap); + } + else + // Standard output to stderr + { + va_list ap; + va_start(ap, format); + vfprintf(stderr, format, ap); + va_end(ap); + fflush(stderr); + } +} diff --git a/3rdparty/portaudio/src/common/pa_debugprint.h b/3rdparty/portaudio/src/common/pa_debugprint.h index f333addbaa..5fba7667dc 100644 --- a/3rdparty/portaudio/src/common/pa_debugprint.h +++ b/3rdparty/portaudio/src/common/pa_debugprint.h @@ -1,149 +1,149 @@ -#ifndef PA_LOG_H -#define PA_LOG_H -/* - * Log file redirector function - * Copyright (c) 1999-2006 Ross Bencina, Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/* - * The text above constitutes the entire PortAudio license; however, - * the PortAudio community also makes the following non-binding requests: - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. It is also - * requested that these non-binding requests be included along with the - * license above. - */ - -/** @file - @ingroup common_src -*/ - - -#ifdef __cplusplus -extern "C" -{ -#endif /* __cplusplus */ - - - -void PaUtil_DebugPrint( const char *format, ... ); - - -/* - The basic format for log messages is described below. If you need to - add any log messages, please follow this format. - - Function entry (void function): - - "FunctionName called.\n" - - Function entry (non void function): - - "FunctionName called:\n" - "\tParam1Type param1: param1Value\n" - "\tParam2Type param2: param2Value\n" (etc...) - - - Function exit (no return value): - - "FunctionName returned.\n" - - Function exit (simple return value): - - "FunctionName returned:\n" - "\tReturnType: returnValue\n" - - If the return type is an error code, the error text is displayed in () - - If the return type is not an error code, but has taken a special value - because an error occurred, then the reason for the error is shown in [] - - If the return type is a struct ptr, the struct is dumped. - - See the code below for examples -*/ - -/** PA_DEBUG() provides a simple debug message printing facility. The macro - passes it's argument to a printf-like function called PaUtil_DebugPrint() - which prints to stderr and always flushes the stream after printing. - Because preprocessor macros cannot directly accept variable length argument - lists, calls to the macro must include an additional set of parenthesis, eg: - PA_DEBUG(("errorno: %d", 1001 )); -*/ - - -#ifdef PA_ENABLE_DEBUG_OUTPUT -#define PA_DEBUG(x) PaUtil_DebugPrint x ; -#else -#define PA_DEBUG(x) -#endif - - -#ifdef PA_LOG_API_CALLS -#define PA_LOGAPI(x) PaUtil_DebugPrint x - -#define PA_LOGAPI_ENTER(functionName) PaUtil_DebugPrint( functionName " called.\n" ) - -#define PA_LOGAPI_ENTER_PARAMS(functionName) PaUtil_DebugPrint( functionName " called:\n" ) - -#define PA_LOGAPI_EXIT(functionName) PaUtil_DebugPrint( functionName " returned.\n" ) - -#define PA_LOGAPI_EXIT_PAERROR( functionName, result ) \ - PaUtil_DebugPrint( functionName " returned:\n" ); \ - PaUtil_DebugPrint("\tPaError: %d ( %s )\n", result, Pa_GetErrorText( result ) ) - -#define PA_LOGAPI_EXIT_T( functionName, resultFormatString, result ) \ - PaUtil_DebugPrint( functionName " returned:\n" ); \ - PaUtil_DebugPrint("\t" resultFormatString "\n", result ) - -#define PA_LOGAPI_EXIT_PAERROR_OR_T_RESULT( functionName, positiveResultFormatString, result ) \ - PaUtil_DebugPrint( functionName " returned:\n" ); \ - if( result > 0 ) \ - PaUtil_DebugPrint("\t" positiveResultFormatString "\n", result ); \ - else \ - PaUtil_DebugPrint("\tPaError: %d ( %s )\n", result, Pa_GetErrorText( result ) ) -#else -#define PA_LOGAPI(x) -#define PA_LOGAPI_ENTER(functionName) -#define PA_LOGAPI_ENTER_PARAMS(functionName) -#define PA_LOGAPI_EXIT(functionName) -#define PA_LOGAPI_EXIT_PAERROR( functionName, result ) -#define PA_LOGAPI_EXIT_T( functionName, resultFormatString, result ) -#define PA_LOGAPI_EXIT_PAERROR_OR_T_RESULT( functionName, positiveResultFormatString, result ) -#endif - - -typedef void (*PaUtilLogCallback ) (const char *log); - -/** - Install user provided log function -*/ -void PaUtil_SetDebugPrintFunction(PaUtilLogCallback cb); - - - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif /* PA_LOG_H */ +#ifndef PA_LOG_H +#define PA_LOG_H +/* + * Log file redirector function + * Copyright (c) 1999-2006 Ross Bencina, Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* + * The text above constitutes the entire PortAudio license; however, + * the PortAudio community also makes the following non-binding requests: + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. It is also + * requested that these non-binding requests be included along with the + * license above. + */ + +/** @file + @ingroup common_src +*/ + + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + + + +void PaUtil_DebugPrint( const char *format, ... ); + + +/* + The basic format for log messages is described below. If you need to + add any log messages, please follow this format. + + Function entry (void function): + + "FunctionName called.\n" + + Function entry (non void function): + + "FunctionName called:\n" + "\tParam1Type param1: param1Value\n" + "\tParam2Type param2: param2Value\n" (etc...) + + + Function exit (no return value): + + "FunctionName returned.\n" + + Function exit (simple return value): + + "FunctionName returned:\n" + "\tReturnType: returnValue\n" + + If the return type is an error code, the error text is displayed in () + + If the return type is not an error code, but has taken a special value + because an error occurred, then the reason for the error is shown in [] + + If the return type is a struct ptr, the struct is dumped. + + See the code below for examples +*/ + +/** PA_DEBUG() provides a simple debug message printing facility. The macro + passes it's argument to a printf-like function called PaUtil_DebugPrint() + which prints to stderr and always flushes the stream after printing. + Because preprocessor macros cannot directly accept variable length argument + lists, calls to the macro must include an additional set of parenthesis, eg: + PA_DEBUG(("errorno: %d", 1001 )); +*/ + + +#ifdef PA_ENABLE_DEBUG_OUTPUT +#define PA_DEBUG(x) PaUtil_DebugPrint x ; +#else +#define PA_DEBUG(x) +#endif + + +#ifdef PA_LOG_API_CALLS +#define PA_LOGAPI(x) PaUtil_DebugPrint x + +#define PA_LOGAPI_ENTER(functionName) PaUtil_DebugPrint( functionName " called.\n" ) + +#define PA_LOGAPI_ENTER_PARAMS(functionName) PaUtil_DebugPrint( functionName " called:\n" ) + +#define PA_LOGAPI_EXIT(functionName) PaUtil_DebugPrint( functionName " returned.\n" ) + +#define PA_LOGAPI_EXIT_PAERROR( functionName, result ) \ + PaUtil_DebugPrint( functionName " returned:\n" ); \ + PaUtil_DebugPrint("\tPaError: %d ( %s )\n", result, Pa_GetErrorText( result ) ) + +#define PA_LOGAPI_EXIT_T( functionName, resultFormatString, result ) \ + PaUtil_DebugPrint( functionName " returned:\n" ); \ + PaUtil_DebugPrint("\t" resultFormatString "\n", result ) + +#define PA_LOGAPI_EXIT_PAERROR_OR_T_RESULT( functionName, positiveResultFormatString, result ) \ + PaUtil_DebugPrint( functionName " returned:\n" ); \ + if( result > 0 ) \ + PaUtil_DebugPrint("\t" positiveResultFormatString "\n", result ); \ + else \ + PaUtil_DebugPrint("\tPaError: %d ( %s )\n", result, Pa_GetErrorText( result ) ) +#else +#define PA_LOGAPI(x) +#define PA_LOGAPI_ENTER(functionName) +#define PA_LOGAPI_ENTER_PARAMS(functionName) +#define PA_LOGAPI_EXIT(functionName) +#define PA_LOGAPI_EXIT_PAERROR( functionName, result ) +#define PA_LOGAPI_EXIT_T( functionName, resultFormatString, result ) +#define PA_LOGAPI_EXIT_PAERROR_OR_T_RESULT( functionName, positiveResultFormatString, result ) +#endif + + +typedef void (*PaUtilLogCallback ) (const char *log); + +/** + Install user provided log function +*/ +void PaUtil_SetDebugPrintFunction(PaUtilLogCallback cb); + + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* PA_LOG_H */ diff --git a/3rdparty/portaudio/src/hostapi/asihpi/pa_linux_asihpi.c b/3rdparty/portaudio/src/hostapi/asihpi/pa_linux_asihpi.c index a5b2df43c3..3ebfafa019 100644 --- a/3rdparty/portaudio/src/hostapi/asihpi/pa_linux_asihpi.c +++ b/3rdparty/portaudio/src/hostapi/asihpi/pa_linux_asihpi.c @@ -1,2942 +1,2942 @@ -/* - * PortAudio Portable Real-Time Audio Library - * Latest Version at: http://www.portaudio.com - * - * PortAudio v18 version of AudioScience HPI driver by Fred Gleason - * PortAudio v19 version of AudioScience HPI driver by Ludwig Schwardt - * - * Copyright (c) 2003 Fred Gleason - * Copyright (c) 2005,2006 Ludwig Schwardt - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/* - * The text above constitutes the entire PortAudio license; however, - * the PortAudio community also makes the following non-binding requests: - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. It is also - * requested that these non-binding requests be included along with the - * license above. - */ - -/* - * Modification History - * 12/2003 - Initial version - * 09/2005 - v19 version [rewrite] - */ - -/** @file - @ingroup hostapi_src - @brief Host API implementation supporting AudioScience cards - via the Linux HPI interface. - -

Overview

- - This is a PortAudio implementation for the AudioScience HPI Audio API - on the Linux platform. AudioScience makes a range of audio adapters customised - for the broadcasting industry, with support for both Windows and Linux. - More information on their products can be found on their website: - - http://www.audioscience.com - - Documentation for the HPI API can be found at: - - http://www.audioscience.com/internet/download/sdk/spchpi.pdf - - The Linux HPI driver itself (a kernel module + library) can be downloaded from: - - http://www.audioscience.com/internet/download/linux_drivers.htm - -

Implementation strategy

- - *Note* Ideally, AudioScience cards should be handled by the PortAudio ALSA - implementation on Linux, as ALSA is the preferred Linux soundcard API. The existence - of this host API implementation might therefore seem a bit flawed. Unfortunately, at - the time of the creation of this implementation (June 2006), the PA ALSA implementation - could not make use of the existing AudioScience ALSA driver. PA ALSA uses the - "memory-mapped" (mmap) ALSA access mode to interact with the ALSA library, while the - AudioScience ALSA driver only supports the "read-write" access mode. The appropriate - solution to this problem is to add "read-write" support to PortAudio ALSA, thereby - extending the range of soundcards it supports (AudioScience cards are not the only - ones with this problem). Given the author's limited knowledge of ALSA and the - simplicity of the HPI API, the second-best solution was born... - - The following mapping between HPI and PA was followed: - HPI subsystem => PortAudio host API - HPI adapter => nothing specific - HPI stream => PortAudio device - - Each HPI stream is either input or output (not both), and can support - different channel counts, sampling rates and sample formats. It is therefore - a more natural fit to a PA device. A PA stream can therefore combine two - HPI streams (one input and one output) into a "full-duplex" stream. These - HPI streams can even be on different physical adapters. The two streams ought to be - sample-synchronised when they reside on the same adapter, as most AudioScience adapters - derive their ADC and DAC clocks from one master clock. When combining two adapters - into one full-duplex stream, however, the use of a word clock connection between the - adapters is strongly recommended. - - The HPI interface is inherently blocking, making use of read and write calls to - transfer data between user buffers and driver buffers. The callback interface therefore - requires a helper thread ("callback engine") which periodically transfers data (one thread - per PA stream, in fact). The current implementation explicitly sleeps via Pa_Sleep() until - enough samples can be transferred (select() or poll() would be better, but currently seems - impossible...). The thread implementation makes use of the Unix thread helper functions - and some pthread calls here and there. If a unified PA thread exists, this host API - implementation might also compile on Windows, as this is the only real Linux-specific - part of the code. - - There is no inherent fixed buffer size in the HPI interface, as in some other host APIs. - The PortAudio implementation contains a buffer that is allocated during OpenStream and - used to transfer data between the callback and the HPI driver buffer. The size of this - buffer is quite flexible and is derived from latency suggestions and matched to the - requested callback buffer size as far as possible. It can become quite huge, as the - AudioScience cards are typically geared towards higher-latency applications and contain - large hardware buffers. - - The HPI interface natively supports most common sample formats and sample rates (some - conversion is done on the adapter itself). - - Stream time is measured based on the number of processed frames, which is adjusted by the - number of frames currently buffered by the HPI driver. - - There is basic support for detecting overflow and underflow. The HPI interface does not - explicitly indicate this, so thresholds on buffer levels are used in combination with - stream state. Recovery from overflow and underflow is left to the PA client. - - Blocking streams are also implemented. It makes use of the same polling routines that - the callback interface uses, in order to prevent the allocation of variable-sized - buffers during reading and writing. The framesPerBuffer parameter is therefore still - relevant, and this can be increased in the blocking case to improve efficiency. - - The implementation contains extensive reporting macros (slightly modified PA_ENSURE and - PA_UNLESS versions) and a useful stream dump routine to provide debugging feedback. - - Output buffer priming via the user callback (i.e. paPrimeOutputBuffersUsingStreamCallback - and friends) is not implemented yet. All output is primed with silence. - - Please send bug reports etc. to Ludwig Schwardt - */ - -#include -#include -#include -#include /* strlen() */ -#include /* pthreads and friends */ -#include /* assert */ -#include /* ceil, floor */ - -#include /* HPI API */ - -#include "portaudio.h" /* PortAudio API */ -#include "pa_util.h" /* PA_DEBUG, other small utilities */ -#include "pa_unix_util.h" /* Unix threading utilities */ -#include "pa_allocation.h" /* Group memory allocation */ -#include "pa_hostapi.h" /* Host API structs */ -#include "pa_stream.h" /* Stream interface structs */ -#include "pa_cpuload.h" /* CPU load measurer */ -#include "pa_process.h" /* Buffer processor */ -#include "pa_converters.h" /* PaUtilZeroer */ -#include "pa_debugprint.h" - -/* -------------------------------------------------------------------------- */ - -/* - * Defines - */ - -/* Error reporting and assertions */ - -/** Evaluate expression, and return on any PortAudio errors */ -#define PA_ENSURE_(expr) \ - do { \ - PaError paError = (expr); \ - if( UNLIKELY( paError < paNoError ) ) \ - { \ - PA_DEBUG(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \ - result = paError; \ - goto error; \ - } \ - } while (0); - -/** Assert expression, else return the provided PaError */ -#define PA_UNLESS_(expr, paError) \ - do { \ - if( UNLIKELY( (expr) == 0 ) ) \ - { \ - PA_DEBUG(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \ - result = (paError); \ - goto error; \ - } \ - } while( 0 ); - -/** Check return value of HPI function, and map it to PaError */ -#define PA_ASIHPI_UNLESS_(expr, paError) \ - do { \ - HW16 hpiError = (expr); \ - /* If HPI error occurred */ \ - if( UNLIKELY( hpiError ) ) \ - { \ - char szError[256]; \ - HPI_GetErrorText( hpiError, szError ); \ - PA_DEBUG(( "HPI error %d occurred: %s\n", hpiError, szError )); \ - /* This message will always be displayed, even if debug info is disabled */ \ - PA_DEBUG(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \ - if( (paError) == paUnanticipatedHostError ) \ - { \ - PA_DEBUG(( "Host error description: %s\n", szError )); \ - /* PaUtil_SetLastHostErrorInfo should only be used in the main thread */ \ - if( pthread_equal( pthread_self(), paUnixMainThread ) ) \ - { \ - PaUtil_SetLastHostErrorInfo( paInDevelopment, hpiError, szError ); \ - } \ - } \ - /* If paNoError is specified, continue as usual */ \ - /* (useful if you only want to print out the debug messages above) */ \ - if( (paError) < 0 ) \ - { \ - result = (paError); \ - goto error; \ - } \ - } \ - } while( 0 ); - -/** Report HPI error code and text */ -#define PA_ASIHPI_REPORT_ERROR_(hpiErrorCode) \ - do { \ - char szError[256]; \ - HPI_GetErrorText( hpiError, szError ); \ - PA_DEBUG(( "HPI error %d occurred: %s\n", hpiError, szError )); \ - /* PaUtil_SetLastHostErrorInfo should only be used in the main thread */ \ - if( pthread_equal( pthread_self(), paUnixMainThread ) ) \ - { \ - PaUtil_SetLastHostErrorInfo( paInDevelopment, (hpiErrorCode), szError ); \ - } \ - } while( 0 ); - -/* Defaults */ - -/** Sample formats available natively on AudioScience hardware */ -#define PA_ASIHPI_AVAILABLE_FORMATS_ (paFloat32 | paInt32 | paInt24 | paInt16 | paUInt8) -/** Enable background bus mastering (BBM) for buffer transfers, if available (see HPI docs) */ -#define PA_ASIHPI_USE_BBM_ 1 -/** Minimum number of frames in HPI buffer (for either data or available space). - If buffer contains less data/space, it indicates xrun or completion. */ -#define PA_ASIHPI_MIN_FRAMES_ 1152 -/** Minimum polling interval in milliseconds, which determines minimum host buffer size */ -#define PA_ASIHPI_MIN_POLLING_INTERVAL_ 10 - -/* -------------------------------------------------------------------------- */ - -/* - * Structures - */ - -/** Host API global data */ -typedef struct PaAsiHpiHostApiRepresentation -{ - /* PortAudio "base class" - keep the baseRep first! (C-style inheritance) */ - PaUtilHostApiRepresentation baseHostApiRep; - PaUtilStreamInterface callbackStreamInterface; - PaUtilStreamInterface blockingStreamInterface; - - PaUtilAllocationGroup *allocations; - - /* implementation specific data goes here */ - - PaHostApiIndex hostApiIndex; - /** HPI subsystem pointer */ - HPI_HSUBSYS *subSys; -} -PaAsiHpiHostApiRepresentation; - - -/** Device data */ -typedef struct PaAsiHpiDeviceInfo -{ - /* PortAudio "base class" - keep the baseRep first! (C-style inheritance) */ - /** Common PortAudio device information */ - PaDeviceInfo baseDeviceInfo; - - /* implementation specific data goes here */ - - /** HPI subsystem (required for most HPI calls) */ - HPI_HSUBSYS *subSys; - /** Adapter index */ - HW16 adapterIndex; - /** Adapter model number (hex) */ - HW16 adapterType; - /** Adapter HW/SW version */ - HW16 adapterVersion; - /** Adapter serial number */ - HW32 adapterSerialNumber; - /** Stream number */ - HW16 streamIndex; - /** 0=Input, 1=Output (HPI streams are either input or output but not both) */ - HW16 streamIsOutput; -} -PaAsiHpiDeviceInfo; - - -/** Stream state as defined by PortAudio. - It seems that the host API implementation has to keep track of the PortAudio stream state. - Please note that this is NOT the same as the state of the underlying HPI stream. By separating - these two concepts, a lot of flexibility is gained. There is a rough match between the two, - of course, but forcing a precise match is difficult. For example, HPI_STATE_DRAINED can occur - during the Active state of PortAudio (due to underruns) and also during CallBackFinished in - the case of an output stream. Similarly, HPI_STATE_STOPPED mostly coincides with the Stopped - PortAudio state, by may also occur in the CallbackFinished state when recording is finished. - - Here is a rough match-up: - - PortAudio state => HPI state - --------------- --------- - Active => HPI_STATE_RECORDING, HPI_STATE_PLAYING, (HPI_STATE_DRAINED) - Stopped => HPI_STATE_STOPPED - CallbackFinished => HPI_STATE_STOPPED, HPI_STATE_DRAINED */ -typedef enum PaAsiHpiStreamState -{ - paAsiHpiStoppedState=0, - paAsiHpiActiveState=1, - paAsiHpiCallbackFinishedState=2 -} -PaAsiHpiStreamState; - - -/** Stream component data (associated with one direction, i.e. either input or output) */ -typedef struct PaAsiHpiStreamComponent -{ - /** Device information (HPI handles, etc) */ - PaAsiHpiDeviceInfo *hpiDevice; - /** Stream handle, as passed to HPI interface. - HACK: we assume types HPI_HISTREAM and HPI_HOSTREAM are the same... - (both are HW32 up to version 3.00 of ASIHPI, and hopefully they stay that way) */ - HPI_HISTREAM hpiStream; - /** Stream format, as passed to HPI interface */ - HPI_FORMAT hpiFormat; - /** Number of bytes per frame, derived from hpiFormat and saved for convenience */ - HW32 bytesPerFrame; - /** Size of hardware (on-card) buffer of stream in bytes */ - HW32 hardwareBufferSize; - /** Size of host (BBM) buffer of stream in bytes (if used) */ - HW32 hostBufferSize; - /** Upper limit on the utilization of output stream buffer (both hardware and host). - This prevents large latencies in an output-only stream with a potentially huge buffer - and a fast data generator, which would otherwise keep the hardware buffer filled to - capacity. See also the "Hardware Buffering=off" option in the AudioScience WAV driver. */ - HW32 outputBufferCap; - /** Sample buffer (halfway station between HPI and buffer processor) */ - HW8 *tempBuffer; - /** Sample buffer size, in bytes */ - HW32 tempBufferSize; -} -PaAsiHpiStreamComponent; - - -/** Stream data */ -typedef struct PaAsiHpiStream -{ - /* PortAudio "base class" - keep the baseRep first! (C-style inheritance) */ - PaUtilStreamRepresentation baseStreamRep; - PaUtilCpuLoadMeasurer cpuLoadMeasurer; - PaUtilBufferProcessor bufferProcessor; - - PaUtilAllocationGroup *allocations; - - /* implementation specific data goes here */ - - /** Separate structs for input and output sides of stream */ - PaAsiHpiStreamComponent *input, *output; - - /** Polling interval (in milliseconds) */ - HW32 pollingInterval; - /** Are we running in callback mode? */ - int callbackMode; - /** Number of frames to transfer at a time to/from HPI */ - unsigned long maxFramesPerHostBuffer; - /** Indicates that the stream is in the paNeverDropInput mode */ - int neverDropInput; - /** Contains copy of user buffers, used by blocking interface to transfer non-interleaved data. - It went here instead of to each stream component, as the stream component buffer setup in - PaAsiHpi_SetupBuffers doesn't know the stream details such as callbackMode. - (Maybe a problem later if ReadStream and WriteStream happens concurrently on same stream.) */ - void **blockingUserBufferCopy; - - /* Thread-related variables */ - - /** Helper thread which will deliver data to user callback */ - PaUnixThread thread; - /** PortAudio stream state (Active/Stopped/CallbackFinished) */ - volatile sig_atomic_t state; - /** Hard abort, i.e. drop frames? */ - volatile sig_atomic_t callbackAbort; - /** True if stream stopped via exiting callback with paComplete/paAbort flag - (as opposed to explicit call to StopStream/AbortStream) */ - volatile sig_atomic_t callbackFinished; -} -PaAsiHpiStream; - - -/** Stream state information, collected together for convenience */ -typedef struct PaAsiHpiStreamInfo -{ - /** HPI stream state (HPI_STATE_STOPPED, HPI_STATE_PLAYING, etc.) */ - HW16 state; - /** Size (in bytes) of recording/playback data buffer in HPI driver */ - HW32 bufferSize; - /** Amount of data (in bytes) available in the buffer */ - HW32 dataSize; - /** Number of frames played/recorded since last stream reset */ - HW32 frameCounter; - /** Amount of data (in bytes) in hardware (on-card) buffer. - This differs from dataSize if bus mastering (BBM) is used, which introduces another - driver-level buffer to which dataSize/bufferSize then refers. */ - HW32 auxDataSize; - /** Total number of data frames currently buffered by HPI driver (host + hw buffers) */ - HW32 totalBufferedData; - /** Size of immediately available data (for input) or space (for output) in frames. - This only checks the first-level buffer (typically host buffer). This amount can be - transferred immediately. */ - HW32 availableFrames; - /** Indicates that hardware buffer is getting too full */ - int overflow; - /** Indicates that hardware buffer is getting too empty */ - int underflow; -} -PaAsiHpiStreamInfo; - -/* -------------------------------------------------------------------------- */ - -/* - * Function prototypes - */ - -#ifdef __cplusplus -extern "C" -{ -#endif /* __cplusplus */ - - /* The only exposed function in the entire host API implementation */ - PaError PaAsiHpi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -static void Terminate( struct PaUtilHostApiRepresentation *hostApi ); -static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate ); - -/* Stream prototypes */ -static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, - PaStream **s, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate, - unsigned long framesPerBuffer, - PaStreamFlags streamFlags, - PaStreamCallback *streamCallback, - void *userData ); -static PaError CloseStream( PaStream *s ); -static PaError StartStream( PaStream *s ); -static PaError StopStream( PaStream *s ); -static PaError AbortStream( PaStream *s ); -static PaError IsStreamStopped( PaStream *s ); -static PaError IsStreamActive( PaStream *s ); -static PaTime GetStreamTime( PaStream *s ); -static double GetStreamCpuLoad( PaStream *s ); - -/* Blocking prototypes */ -static PaError ReadStream( PaStream *s, void *buffer, unsigned long frames ); -static PaError WriteStream( PaStream *s, const void *buffer, unsigned long frames ); -static signed long GetStreamReadAvailable( PaStream *s ); -static signed long GetStreamWriteAvailable( PaStream *s ); - -/* Callback prototypes */ -static void *CallbackThreadFunc( void *userData ); - -/* Functions specific to this API */ -static PaError PaAsiHpi_BuildDeviceList( PaAsiHpiHostApiRepresentation *hpiHostApi ); -static HW16 PaAsiHpi_PaToHpiFormat( PaSampleFormat paFormat ); -static PaSampleFormat PaAsiHpi_HpiToPaFormat( HW16 hpiFormat ); -static PaError PaAsiHpi_CreateFormat( struct PaUtilHostApiRepresentation *hostApi, - const PaStreamParameters *parameters, double sampleRate, - PaAsiHpiDeviceInfo **hpiDevice, HPI_FORMAT *hpiFormat ); -static PaError PaAsiHpi_OpenInput( struct PaUtilHostApiRepresentation *hostApi, - const PaAsiHpiDeviceInfo *hpiDevice, const HPI_FORMAT *hpiFormat, - HPI_HISTREAM *hpiStream ); -static PaError PaAsiHpi_OpenOutput( struct PaUtilHostApiRepresentation *hostApi, - const PaAsiHpiDeviceInfo *hpiDevice, const HPI_FORMAT *hpiFormat, - HPI_HOSTREAM *hpiStream ); -static PaError PaAsiHpi_GetStreamInfo( PaAsiHpiStreamComponent *streamComp, PaAsiHpiStreamInfo *info ); -static void PaAsiHpi_StreamComponentDump( PaAsiHpiStreamComponent *streamComp, PaAsiHpiStream *stream ); -static void PaAsiHpi_StreamDump( PaAsiHpiStream *stream ); -static PaError PaAsiHpi_SetupBuffers( PaAsiHpiStreamComponent *streamComp, HW32 pollingInterval, - unsigned long framesPerPaHostBuffer, PaTime suggestedLatency ); -static PaError PaAsiHpi_PrimeOutputWithSilence( PaAsiHpiStream *stream ); -static PaError PaAsiHpi_StartStream( PaAsiHpiStream *stream, int outputPrimed ); -static PaError PaAsiHpi_StopStream( PaAsiHpiStream *stream, int abort ); -static PaError PaAsiHpi_ExplicitStop( PaAsiHpiStream *stream, int abort ); -static void PaAsiHpi_OnThreadExit( void *userData ); -static PaError PaAsiHpi_WaitForFrames( PaAsiHpiStream *stream, unsigned long *framesAvail, - PaStreamCallbackFlags *cbFlags ); -static void PaAsiHpi_CalculateTimeInfo( PaAsiHpiStream *stream, PaStreamCallbackTimeInfo *timeInfo ); -static PaError PaAsiHpi_BeginProcessing( PaAsiHpiStream* stream, unsigned long* numFrames, - PaStreamCallbackFlags *cbFlags ); -static PaError PaAsiHpi_EndProcessing( PaAsiHpiStream *stream, unsigned long numFrames, - PaStreamCallbackFlags *cbFlags ); - -/* ========================================================================== - * ============================= IMPLEMENTATION ============================= - * ========================================================================== */ - -/* --------------------------- Host API Interface --------------------------- */ - -/** Enumerate all PA devices (= HPI streams). - This compiles a list of all HPI adapters, and registers a PA device for each input and - output stream it finds. Most errors are ignored, as missing or erroneous devices are - simply skipped. - - @param hpiHostApi Pointer to HPI host API struct - - @return PortAudio error code (only paInsufficientMemory in practice) - */ -static PaError PaAsiHpi_BuildDeviceList( PaAsiHpiHostApiRepresentation *hpiHostApi ) -{ - PaError result = paNoError; - PaUtilHostApiRepresentation *hostApi = &hpiHostApi->baseHostApiRep; - PaHostApiInfo *baseApiInfo = &hostApi->info; - PaAsiHpiDeviceInfo *hpiDeviceList; - HW16 adapterList[ HPI_MAX_ADAPTERS ]; - HW16 numAdapters; - HW16 hpiError = 0; - int i, j, deviceCount = 0, deviceIndex = 0; - - assert( hpiHostApi ); - assert( hpiHostApi->subSys ); - - /* Look for adapters (not strictly necessary, as AdapterOpen can do the same, but this */ - /* way we have less errors since we do not try to open adapters we know aren't there) */ - /* Errors not considered critical here (subsystem may report 0 devices), but report them */ - /* in debug mode. */ - PA_ASIHPI_UNLESS_( HPI_SubSysFindAdapters( hpiHostApi->subSys, &numAdapters, - adapterList, HPI_MAX_ADAPTERS ), paNoError ); - - /* First open and count the number of devices (= number of streams), to ease memory allocation */ - for( i=0; i < HPI_MAX_ADAPTERS; ++i ) - { - HW16 inStreams, outStreams; - HW16 version; - HW32 serial; - HW16 type; - - /* If no adapter found at this index, skip it */ - if( adapterList[i] == 0 ) - continue; - - /* Try to open adapter */ - hpiError = HPI_AdapterOpen( hpiHostApi->subSys, i ); - /* Report error and skip to next device on failure */ - if( hpiError ) - { - PA_ASIHPI_REPORT_ERROR_( hpiError ); - continue; - } - hpiError = HPI_AdapterGetInfo( hpiHostApi->subSys, i, - &outStreams, &inStreams, &version, &serial, &type ); - /* Skip to next device on failure */ - if( hpiError ) - { - PA_ASIHPI_REPORT_ERROR_( hpiError ); - continue; - } - else - { - /* Assign default devices if available and increment device count */ - if( (baseApiInfo->defaultInputDevice == paNoDevice) && (inStreams > 0) ) - baseApiInfo->defaultInputDevice = deviceCount; - deviceCount += inStreams; - if( (baseApiInfo->defaultOutputDevice == paNoDevice) && (outStreams > 0) ) - baseApiInfo->defaultOutputDevice = deviceCount; - deviceCount += outStreams; - } - } - - /* Register any discovered devices */ - if( deviceCount > 0 ) - { - /* Memory allocation */ - PA_UNLESS_( hostApi->deviceInfos = (PaDeviceInfo**) PaUtil_GroupAllocateMemory( - hpiHostApi->allocations, sizeof(PaDeviceInfo*) * deviceCount ), - paInsufficientMemory ); - /* Allocate all device info structs in a contiguous block */ - PA_UNLESS_( hpiDeviceList = (PaAsiHpiDeviceInfo*) PaUtil_GroupAllocateMemory( - hpiHostApi->allocations, sizeof(PaAsiHpiDeviceInfo) * deviceCount ), - paInsufficientMemory ); - - /* Now query devices again for information */ - for( i=0; i < HPI_MAX_ADAPTERS; ++i ) - { - HW16 inStreams, outStreams; - HW16 version; - HW32 serial; - HW16 type; - - /* If no adapter found at this index, skip it */ - if( adapterList[i] == 0 ) - continue; - - /* Assume adapter is still open from previous round */ - hpiError = HPI_AdapterGetInfo( hpiHostApi->subSys, i, - &outStreams, &inStreams, &version, &serial, &type ); - /* Report error and skip to next device on failure */ - if( hpiError ) - { - PA_ASIHPI_REPORT_ERROR_( hpiError ); - continue; - } - else - { - PA_DEBUG(( "Found HPI Adapter ID=%4X Idx=%d #In=%d #Out=%d S/N=%d HWver=%c%d DSPver=%03d\n", - type, i, inStreams, outStreams, serial, - ((version>>3)&0xf)+'A', /* Hw version major */ - version&0x7, /* Hw version minor */ - ((version>>13)*100)+((version>>7)&0x3f) /* DSP code version */ - )); - } - - /* First add all input streams as devices */ - for( j=0; j < inStreams; ++j ) - { - PaAsiHpiDeviceInfo *hpiDevice = &hpiDeviceList[deviceIndex]; - PaDeviceInfo *baseDeviceInfo = &hpiDevice->baseDeviceInfo; - char srcName[72]; - char *deviceName; - - memset( hpiDevice, 0, sizeof(PaAsiHpiDeviceInfo) ); - /* Set implementation-specific device details */ - hpiDevice->subSys = hpiHostApi->subSys; - hpiDevice->adapterIndex = i; - hpiDevice->adapterType = type; - hpiDevice->adapterVersion = version; - hpiDevice->adapterSerialNumber = serial; - hpiDevice->streamIndex = j; - hpiDevice->streamIsOutput = 0; - /* Set common PortAudio device stats */ - baseDeviceInfo->structVersion = 2; - /* Make sure name string is owned by API info structure */ - sprintf( srcName, - "Adapter %d (%4X) - Input Stream %d", i+1, type, j+1 ); - PA_UNLESS_( deviceName = (char *) PaUtil_GroupAllocateMemory( - hpiHostApi->allocations, strlen(srcName) + 1 ), paInsufficientMemory ); - strcpy( deviceName, srcName ); - baseDeviceInfo->name = deviceName; - baseDeviceInfo->hostApi = hpiHostApi->hostApiIndex; - baseDeviceInfo->maxInputChannels = HPI_MAX_CHANNELS; - baseDeviceInfo->maxOutputChannels = 0; - /* Default latency values for interactive performance */ - baseDeviceInfo->defaultLowInputLatency = 0.01; - baseDeviceInfo->defaultLowOutputLatency = -1.0; - /* Default latency values for robust non-interactive applications (eg. playing sound files) */ - baseDeviceInfo->defaultHighInputLatency = 0.2; - baseDeviceInfo->defaultHighOutputLatency = -1.0; - /* HPI interface can actually handle any sampling rate to 1 Hz accuracy, - * so this default is as good as any */ - baseDeviceInfo->defaultSampleRate = 44100; - - /* Store device in global PortAudio list */ - hostApi->deviceInfos[deviceIndex++] = (PaDeviceInfo *) hpiDevice; - } - - /* Now add all output streams as devices (I know, the repetition is painful) */ - for( j=0; j < outStreams; ++j ) - { - PaAsiHpiDeviceInfo *hpiDevice = &hpiDeviceList[deviceIndex]; - PaDeviceInfo *baseDeviceInfo = &hpiDevice->baseDeviceInfo; - char srcName[72]; - char *deviceName; - - memset( hpiDevice, 0, sizeof(PaAsiHpiDeviceInfo) ); - /* Set implementation-specific device details */ - hpiDevice->subSys = hpiHostApi->subSys; - hpiDevice->adapterIndex = i; - hpiDevice->adapterType = type; - hpiDevice->adapterVersion = version; - hpiDevice->adapterSerialNumber = serial; - hpiDevice->streamIndex = j; - hpiDevice->streamIsOutput = 1; - /* Set common PortAudio device stats */ - baseDeviceInfo->structVersion = 2; - /* Make sure name string is owned by API info structure */ - sprintf( srcName, - "Adapter %d (%4X) - Output Stream %d", i+1, type, j+1 ); - PA_UNLESS_( deviceName = (char *) PaUtil_GroupAllocateMemory( - hpiHostApi->allocations, strlen(srcName) + 1 ), paInsufficientMemory ); - strcpy( deviceName, srcName ); - baseDeviceInfo->name = deviceName; - baseDeviceInfo->hostApi = hpiHostApi->hostApiIndex; - baseDeviceInfo->maxInputChannels = 0; - baseDeviceInfo->maxOutputChannels = HPI_MAX_CHANNELS; - /* Default latency values for interactive performance. */ - baseDeviceInfo->defaultLowInputLatency = -1.0; - baseDeviceInfo->defaultLowOutputLatency = 0.01; - /* Default latency values for robust non-interactive applications (eg. playing sound files). */ - baseDeviceInfo->defaultHighInputLatency = -1.0; - baseDeviceInfo->defaultHighOutputLatency = 0.2; - /* HPI interface can actually handle any sampling rate to 1 Hz accuracy, - * so this default is as good as any */ - baseDeviceInfo->defaultSampleRate = 44100; - - /* Store device in global PortAudio list */ - hostApi->deviceInfos[deviceIndex++] = (PaDeviceInfo *) hpiDevice; - } - } - } - - /* Finally acknowledge checked devices */ - baseApiInfo->deviceCount = deviceIndex; - -error: - return result; -} - - -/** Initialize host API implementation. - This is the only function exported beyond this file. It is called by PortAudio to initialize - the host API. It stores API info, finds and registers all devices, and sets up callback and - blocking interfaces. - - @param hostApi Pointer to host API struct - - @param hostApiIndex Index of current (HPI) host API - - @return PortAudio error code - */ -PaError PaAsiHpi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex ) -{ - PaError result = paNoError; - PaAsiHpiHostApiRepresentation *hpiHostApi = NULL; - PaHostApiInfo *baseApiInfo; - - /* Allocate host API structure */ - PA_UNLESS_( hpiHostApi = (PaAsiHpiHostApiRepresentation*) PaUtil_AllocateMemory( - sizeof(PaAsiHpiHostApiRepresentation) ), paInsufficientMemory ); - PA_UNLESS_( hpiHostApi->allocations = PaUtil_CreateAllocationGroup(), paInsufficientMemory ); - - hpiHostApi->hostApiIndex = hostApiIndex; - hpiHostApi->subSys = NULL; - - /* Try to initialize HPI subsystem */ - if( ( hpiHostApi->subSys = HPI_SubSysCreate() ) == NULL) - { - /* the V19 development docs say that if an implementation - * detects that it cannot be used, it should return a NULL - * interface and paNoError */ - PA_DEBUG(( "Could not open HPI interface\n" )); - result = paNoError; - *hostApi = NULL; - goto error; - } - else - { - HW32 hpiVersion; - PA_ASIHPI_UNLESS_( HPI_SubSysGetVersion( hpiHostApi->subSys, &hpiVersion ), paUnanticipatedHostError ); - PA_DEBUG(( "HPI interface v%d.%02d\n", - hpiVersion >> 8, 10*((hpiVersion & 0xF0) >> 4) + (hpiVersion & 0x0F) )); - } - - *hostApi = &hpiHostApi->baseHostApiRep; - baseApiInfo = &((*hostApi)->info); - /* Fill in common API details */ - baseApiInfo->structVersion = 1; - baseApiInfo->type = paAudioScienceHPI; - baseApiInfo->name = "AudioScience HPI"; - baseApiInfo->deviceCount = 0; - baseApiInfo->defaultInputDevice = paNoDevice; - baseApiInfo->defaultOutputDevice = paNoDevice; - - PA_ENSURE_( PaAsiHpi_BuildDeviceList( hpiHostApi ) ); - - (*hostApi)->Terminate = Terminate; - (*hostApi)->OpenStream = OpenStream; - (*hostApi)->IsFormatSupported = IsFormatSupported; - - PaUtil_InitializeStreamInterface( &hpiHostApi->callbackStreamInterface, CloseStream, StartStream, - StopStream, AbortStream, IsStreamStopped, IsStreamActive, - GetStreamTime, GetStreamCpuLoad, - PaUtil_DummyRead, PaUtil_DummyWrite, - PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable ); - - PaUtil_InitializeStreamInterface( &hpiHostApi->blockingStreamInterface, CloseStream, StartStream, - StopStream, AbortStream, IsStreamStopped, IsStreamActive, - GetStreamTime, PaUtil_DummyGetCpuLoad, - ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable ); - - /* Store identity of main thread */ - PA_ENSURE_( PaUnixThreading_Initialize() ); - - return result; -error: - /* Clean up memory */ - Terminate( (PaUtilHostApiRepresentation *)hpiHostApi ); - return result; -} - - -/** Terminate host API implementation. - This closes all HPI adapters and frees the HPI subsystem. It also frees the host API struct - memory. It should be called once for every PaAsiHpi_Initialize call. - - @param Pointer to host API struct - */ -static void Terminate( struct PaUtilHostApiRepresentation *hostApi ) -{ - PaAsiHpiHostApiRepresentation *hpiHostApi = (PaAsiHpiHostApiRepresentation*)hostApi; - int i; - PaError result = paNoError; - - if( hpiHostApi ) - { - /* Get rid of HPI-specific structures */ - if( hpiHostApi->subSys ) - { - HW16 lastAdapterIndex = HPI_MAX_ADAPTERS; - /* Iterate through device list and close adapters */ - for( i=0; i < hostApi->info.deviceCount; ++i ) - { - PaAsiHpiDeviceInfo *hpiDevice = (PaAsiHpiDeviceInfo *) hostApi->deviceInfos[ i ]; - /* Close adapter only if it differs from previous one */ - if( hpiDevice->adapterIndex != lastAdapterIndex ) - { - /* Ignore errors (report only during debugging) */ - PA_ASIHPI_UNLESS_( HPI_AdapterClose( hpiHostApi->subSys, - hpiDevice->adapterIndex ), paNoError ); - lastAdapterIndex = hpiDevice->adapterIndex; - } - } - /* Finally dismantle HPI subsystem */ - HPI_SubSysFree( hpiHostApi->subSys ); - } - - if( hpiHostApi->allocations ) - { - PaUtil_FreeAllAllocations( hpiHostApi->allocations ); - PaUtil_DestroyAllocationGroup( hpiHostApi->allocations ); - } - - PaUtil_FreeMemory( hpiHostApi ); - } -error: - return; -} - - -/** Converts PortAudio sample format to equivalent HPI format. - - @param paFormat PortAudio sample format - - @return HPI sample format - */ -static HW16 PaAsiHpi_PaToHpiFormat( PaSampleFormat paFormat ) -{ - /* Ignore interleaving flag */ - switch( paFormat & ~paNonInterleaved ) - { - case paFloat32: - return HPI_FORMAT_PCM32_FLOAT; - - case paInt32: - return HPI_FORMAT_PCM32_SIGNED; - - case paInt24: - return HPI_FORMAT_PCM24_SIGNED; - - case paInt16: - return HPI_FORMAT_PCM16_SIGNED; - - case paUInt8: - return HPI_FORMAT_PCM8_UNSIGNED; - - /* Default is 16-bit signed */ - case paInt8: - default: - return HPI_FORMAT_PCM16_SIGNED; - } -} - - -/** Converts HPI sample format to equivalent PortAudio format. - - @param paFormat HPI sample format - - @return PortAudio sample format - */ -static PaSampleFormat PaAsiHpi_HpiToPaFormat( HW16 hpiFormat ) -{ - switch( hpiFormat ) - { - case HPI_FORMAT_PCM32_FLOAT: - return paFloat32; - - case HPI_FORMAT_PCM32_SIGNED: - return paInt32; - - case HPI_FORMAT_PCM24_SIGNED: - return paInt24; - - case HPI_FORMAT_PCM16_SIGNED: - return paInt16; - - case HPI_FORMAT_PCM8_UNSIGNED: - return paUInt8; - - /* Default is custom format (e.g. for HPI MP3 format) */ - default: - return paCustomFormat; - } -} - - -/** Creates HPI format struct based on PortAudio parameters. - This also does some checks to see whether the desired format is valid, and whether - the device allows it. This only checks the format of one half (input or output) of the - PortAudio stream. - - @param hostApi Pointer to host API struct - - @param parameters Pointer to stream parameter struct - - @param sampleRate Desired sample rate - - @param hpiDevice Pointer to HPI device struct - - @param hpiFormat Resulting HPI format returned here - - @return PortAudio error code (typically indicating a problem with stream format) - */ -static PaError PaAsiHpi_CreateFormat( struct PaUtilHostApiRepresentation *hostApi, - const PaStreamParameters *parameters, double sampleRate, - PaAsiHpiDeviceInfo **hpiDevice, HPI_FORMAT *hpiFormat ) -{ - int maxChannelCount = 0; - PaSampleFormat hostSampleFormat = 0; - HW16 hpiError = 0; - - /* Unless alternate device specification is supported, reject the use of - paUseHostApiSpecificDeviceSpecification */ - if( parameters->device == paUseHostApiSpecificDeviceSpecification ) - return paInvalidDevice; - else - { - assert( parameters->device < hostApi->info.deviceCount ); - *hpiDevice = (PaAsiHpiDeviceInfo*) hostApi->deviceInfos[ parameters->device ]; - } - - /* Validate streamInfo - this implementation doesn't use custom stream info */ - if( parameters->hostApiSpecificStreamInfo ) - return paIncompatibleHostApiSpecificStreamInfo; - - /* Check that device can support channel count */ - if( (*hpiDevice)->streamIsOutput ) - { - maxChannelCount = (*hpiDevice)->baseDeviceInfo.maxOutputChannels; - } - else - { - maxChannelCount = (*hpiDevice)->baseDeviceInfo.maxInputChannels; - } - if( (maxChannelCount == 0) || (parameters->channelCount > maxChannelCount) ) - return paInvalidChannelCount; - - /* All standard sample formats are supported by the buffer adapter, - and this implementation doesn't support any custom sample formats */ - if( parameters->sampleFormat & paCustomFormat ) - return paSampleFormatNotSupported; - - /* Switch to closest HPI native format */ - hostSampleFormat = PaUtil_SelectClosestAvailableFormat(PA_ASIHPI_AVAILABLE_FORMATS_, - parameters->sampleFormat ); - /* Setup format + info objects */ - hpiError = HPI_FormatCreate( hpiFormat, (HW16)parameters->channelCount, - PaAsiHpi_PaToHpiFormat( hostSampleFormat ), - (HW32)sampleRate, 0, 0 ); - if( hpiError ) - { - PA_ASIHPI_REPORT_ERROR_( hpiError ); - switch( hpiError ) - { - case HPI_ERROR_INVALID_FORMAT: - return paSampleFormatNotSupported; - - case HPI_ERROR_INVALID_SAMPLERATE: - case HPI_ERROR_INCOMPATIBLE_SAMPLERATE: - return paInvalidSampleRate; - - case HPI_ERROR_INVALID_CHANNELS: - return paInvalidChannelCount; - } - } - - return paNoError; -} - - -/** Open HPI input stream with given format. - This attempts to open HPI input stream with desired format. If the format is not supported - or the device is unavailable, the stream is closed and a PortAudio error code is returned. - - @param hostApi Pointer to host API struct - - @param hpiDevice Pointer to HPI device struct - - @param hpiFormat Pointer to HPI format struct - - @return PortAudio error code (typically indicating a problem with stream format or device) -*/ -static PaError PaAsiHpi_OpenInput( struct PaUtilHostApiRepresentation *hostApi, - const PaAsiHpiDeviceInfo *hpiDevice, const HPI_FORMAT *hpiFormat, - HPI_HISTREAM *hpiStream ) -{ - PaAsiHpiHostApiRepresentation *hpiHostApi = (PaAsiHpiHostApiRepresentation*)hostApi; - PaError result = paNoError; - HW16 hpiError = 0; - - /* Catch misplaced output devices, as they typically have 0 input channels */ - PA_UNLESS_( !hpiDevice->streamIsOutput, paInvalidChannelCount ); - /* Try to open input stream */ - PA_ASIHPI_UNLESS_( HPI_InStreamOpen( hpiHostApi->subSys, hpiDevice->adapterIndex, - hpiDevice->streamIndex, hpiStream ), paDeviceUnavailable ); - /* Set input format (checking it in the process) */ - /* Could also use HPI_InStreamQueryFormat, but this economizes the process */ - hpiError = HPI_InStreamSetFormat( hpiHostApi->subSys, *hpiStream, (HPI_FORMAT*)hpiFormat ); - if( hpiError ) - { - PA_ASIHPI_REPORT_ERROR_( hpiError ); - PA_ASIHPI_UNLESS_( HPI_InStreamClose( hpiHostApi->subSys, *hpiStream ), paNoError ); - switch( hpiError ) - { - case HPI_ERROR_INVALID_FORMAT: - return paSampleFormatNotSupported; - - case HPI_ERROR_INVALID_SAMPLERATE: - case HPI_ERROR_INCOMPATIBLE_SAMPLERATE: - return paInvalidSampleRate; - - case HPI_ERROR_INVALID_CHANNELS: - return paInvalidChannelCount; - - default: - /* In case anything else went wrong */ - return paInvalidDevice; - } - } - -error: - return result; -} - - -/** Open HPI output stream with given format. - This attempts to open HPI output stream with desired format. If the format is not supported - or the device is unavailable, the stream is closed and a PortAudio error code is returned. - - @param hostApi Pointer to host API struct - - @param hpiDevice Pointer to HPI device struct - - @param hpiFormat Pointer to HPI format struct - - @return PortAudio error code (typically indicating a problem with stream format or device) -*/ -static PaError PaAsiHpi_OpenOutput( struct PaUtilHostApiRepresentation *hostApi, - const PaAsiHpiDeviceInfo *hpiDevice, const HPI_FORMAT *hpiFormat, - HPI_HOSTREAM *hpiStream ) -{ - PaAsiHpiHostApiRepresentation *hpiHostApi = (PaAsiHpiHostApiRepresentation*)hostApi; - PaError result = paNoError; - HW16 hpiError = 0; - - /* Catch misplaced input devices, as they typically have 0 output channels */ - PA_UNLESS_( hpiDevice->streamIsOutput, paInvalidChannelCount ); - /* Try to open output stream */ - PA_ASIHPI_UNLESS_( HPI_OutStreamOpen( hpiHostApi->subSys, hpiDevice->adapterIndex, - hpiDevice->streamIndex, hpiStream ), paDeviceUnavailable ); - - /* Check output format (format is set on first write to output stream) */ - hpiError = HPI_OutStreamQueryFormat( hpiHostApi->subSys, *hpiStream, (HPI_FORMAT*)hpiFormat ); - if( hpiError ) - { - PA_ASIHPI_REPORT_ERROR_( hpiError ); - PA_ASIHPI_UNLESS_( HPI_OutStreamClose( hpiHostApi->subSys, *hpiStream ), paNoError ); - switch( hpiError ) - { - case HPI_ERROR_INVALID_FORMAT: - return paSampleFormatNotSupported; - - case HPI_ERROR_INVALID_SAMPLERATE: - case HPI_ERROR_INCOMPATIBLE_SAMPLERATE: - return paInvalidSampleRate; - - case HPI_ERROR_INVALID_CHANNELS: - return paInvalidChannelCount; - - default: - /* In case anything else went wrong */ - return paInvalidDevice; - } - } - -error: - return result; -} - - -/** Checks whether the desired stream formats and devices are supported - (for both input and output). - This is done by actually opening the appropriate HPI streams and closing them again. - - @param hostApi Pointer to host API struct - - @param inputParameters Pointer to stream parameter struct for input side of stream - - @param outputParameters Pointer to stream parameter struct for output side of stream - - @param sampleRate Desired sample rate - - @return PortAudio error code (paFormatIsSupported on success) - */ -static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate ) -{ - PaError result = paFormatIsSupported; - PaAsiHpiHostApiRepresentation *hpiHostApi = (PaAsiHpiHostApiRepresentation*)hostApi; - PaAsiHpiDeviceInfo *hpiDevice = NULL; - HPI_FORMAT hpiFormat; - - /* Input stream */ - if( inputParameters ) - { - HPI_HISTREAM hpiStream; - PA_DEBUG(( "%s: Checking input params: dev=%d, sr=%d, chans=%d, fmt=%d\n", - __FUNCTION__, inputParameters->device, (int)sampleRate, - inputParameters->channelCount, inputParameters->sampleFormat )); - /* Create and validate format */ - PA_ENSURE_( PaAsiHpi_CreateFormat( hostApi, inputParameters, sampleRate, - &hpiDevice, &hpiFormat ) ); - /* Open stream to further check format */ - PA_ENSURE_( PaAsiHpi_OpenInput( hostApi, hpiDevice, &hpiFormat, &hpiStream ) ); - /* Close stream again */ - PA_ASIHPI_UNLESS_( HPI_InStreamClose( hpiHostApi->subSys, hpiStream ), paNoError ); - } - - /* Output stream */ - if( outputParameters ) - { - HPI_HOSTREAM hpiStream; - PA_DEBUG(( "%s: Checking output params: dev=%d, sr=%d, chans=%d, fmt=%d\n", - __FUNCTION__, outputParameters->device, (int)sampleRate, - outputParameters->channelCount, outputParameters->sampleFormat )); - /* Create and validate format */ - PA_ENSURE_( PaAsiHpi_CreateFormat( hostApi, outputParameters, sampleRate, - &hpiDevice, &hpiFormat ) ); - /* Open stream to further check format */ - PA_ENSURE_( PaAsiHpi_OpenOutput( hostApi, hpiDevice, &hpiFormat, &hpiStream ) ); - /* Close stream again */ - PA_ASIHPI_UNLESS_( HPI_OutStreamClose( hpiHostApi->subSys, hpiStream ), paNoError ); - } - -error: - return result; -} - -/* ---------------------------- Stream Interface ---------------------------- */ - -/** Obtain HPI stream information. - This obtains info such as stream state and available data/space in buffers. It also - estimates whether an underflow or overflow occurred. - - @param streamComp Pointer to stream component (input or output) to query - - @param info Pointer to stream info struct that will contain result - - @return PortAudio error code (either paNoError, paDeviceUnavailable or paUnanticipatedHostError) - */ -static PaError PaAsiHpi_GetStreamInfo( PaAsiHpiStreamComponent *streamComp, PaAsiHpiStreamInfo *info ) -{ - PaError result = paDeviceUnavailable; - HW16 state; - HW32 bufferSize, dataSize, frameCounter, auxDataSize, threshold; - HW32 hwBufferSize, hwDataSize; - - assert( streamComp ); - assert( info ); - - /* First blank the stream info struct, in case something goes wrong below. - This saves the caller from initializing the struct. */ - info->state = 0; - info->bufferSize = 0; - info->dataSize = 0; - info->frameCounter = 0; - info->auxDataSize = 0; - info->totalBufferedData = 0; - info->availableFrames = 0; - info->underflow = 0; - info->overflow = 0; - - if( streamComp->hpiDevice && streamComp->hpiStream ) - { - /* Obtain detailed stream info (either input or output) */ - if( streamComp->hpiDevice->streamIsOutput ) - { - PA_ASIHPI_UNLESS_( HPI_OutStreamGetInfoEx( streamComp->hpiDevice->subSys, - streamComp->hpiStream, - &state, &bufferSize, &dataSize, &frameCounter, - &auxDataSize ), paUnanticipatedHostError ); - } - else - { - PA_ASIHPI_UNLESS_( HPI_InStreamGetInfoEx( streamComp->hpiDevice->subSys, - streamComp->hpiStream, - &state, &bufferSize, &dataSize, &frameCounter, - &auxDataSize ), paUnanticipatedHostError ); - } - /* Load stream info */ - info->state = state; - info->bufferSize = bufferSize; - info->dataSize = dataSize; - info->frameCounter = frameCounter; - info->auxDataSize = auxDataSize; - /* Determine total buffered data */ - info->totalBufferedData = dataSize; - if( streamComp->hostBufferSize > 0 ) - info->totalBufferedData += auxDataSize; - info->totalBufferedData /= streamComp->bytesPerFrame; - /* Determine immediately available frames */ - info->availableFrames = streamComp->hpiDevice->streamIsOutput ? - bufferSize - dataSize : dataSize; - info->availableFrames /= streamComp->bytesPerFrame; - /* Minimum space/data required in buffers */ - threshold = PA_MIN( streamComp->tempBufferSize, - streamComp->bytesPerFrame * PA_ASIHPI_MIN_FRAMES_ ); - /* Obtain hardware buffer stats first, to simplify things */ - hwBufferSize = streamComp->hardwareBufferSize; - hwDataSize = streamComp->hostBufferSize > 0 ? auxDataSize : dataSize; - /* Underflow is a bit tricky */ - info->underflow = streamComp->hpiDevice->streamIsOutput ? - /* Stream seems to start in drained state sometimes, so ignore initial underflow */ - (frameCounter > 0) && ( (state == HPI_STATE_DRAINED) || (hwDataSize == 0) ) : - /* Input streams check the first-level (host) buffer for underflow */ - (state != HPI_STATE_STOPPED) && (dataSize < threshold); - /* Check for overflow in second-level (hardware) buffer for both input and output */ - info->overflow = (state != HPI_STATE_STOPPED) && (hwBufferSize - hwDataSize < threshold); - - return paNoError; - } - -error: - return result; -} - - -/** Display stream component information for debugging purposes. - - @param streamComp Pointer to stream component (input or output) to query - - @param stream Pointer to stream struct which contains the component above - */ -static void PaAsiHpi_StreamComponentDump( PaAsiHpiStreamComponent *streamComp, - PaAsiHpiStream *stream ) -{ - PaAsiHpiStreamInfo streamInfo; - - assert( streamComp ); - assert( stream ); - - /* Name of soundcard/device used by component */ - PA_DEBUG(( "device: %s\n", streamComp->hpiDevice->baseDeviceInfo.name )); - /* Unfortunately some overlap between input and output here */ - if( streamComp->hpiDevice->streamIsOutput ) - { - /* Settings on the user side (as experienced by user callback) */ - PA_DEBUG(( "user: %d-bit, %d ", - 8*stream->bufferProcessor.bytesPerUserOutputSample, - stream->bufferProcessor.outputChannelCount)); - if( stream->bufferProcessor.userOutputIsInterleaved ) - { - PA_DEBUG(( "interleaved channels, " )); - } - else - { - PA_DEBUG(( "non-interleaved channels, " )); - } - PA_DEBUG(( "%d frames/buffer, latency = %5.1f ms\n", - stream->bufferProcessor.framesPerUserBuffer, - 1000*stream->baseStreamRep.streamInfo.outputLatency )); - /* Settings on the host side (internal to PortAudio host API) */ - PA_DEBUG(( "host: %d-bit, %d interleaved channels, %d frames/buffer ", - 8*stream->bufferProcessor.bytesPerHostOutputSample, - stream->bufferProcessor.outputChannelCount, - stream->bufferProcessor.framesPerHostBuffer )); - } - else - { - /* Settings on the user side (as experienced by user callback) */ - PA_DEBUG(( "user: %d-bit, %d ", - 8*stream->bufferProcessor.bytesPerUserInputSample, - stream->bufferProcessor.inputChannelCount)); - if( stream->bufferProcessor.userInputIsInterleaved ) - { - PA_DEBUG(( "interleaved channels, " )); - } - else - { - PA_DEBUG(( "non-interleaved channels, " )); - } - PA_DEBUG(( "%d frames/buffer, latency = %5.1f ms\n", - stream->bufferProcessor.framesPerUserBuffer, - 1000*stream->baseStreamRep.streamInfo.inputLatency )); - /* Settings on the host side (internal to PortAudio host API) */ - PA_DEBUG(( "host: %d-bit, %d interleaved channels, %d frames/buffer ", - 8*stream->bufferProcessor.bytesPerHostInputSample, - stream->bufferProcessor.inputChannelCount, - stream->bufferProcessor.framesPerHostBuffer )); - } - switch( stream->bufferProcessor.hostBufferSizeMode ) - { - case paUtilFixedHostBufferSize: - PA_DEBUG(( "[fixed] " )); - break; - case paUtilBoundedHostBufferSize: - PA_DEBUG(( "[bounded] " )); - break; - case paUtilUnknownHostBufferSize: - PA_DEBUG(( "[unknown] " )); - break; - case paUtilVariableHostBufferSizePartialUsageAllowed: - PA_DEBUG(( "[variable] " )); - break; - } - PA_DEBUG(( "(%d max)\n", streamComp->tempBufferSize / streamComp->bytesPerFrame )); - /* HPI hardware settings */ - PA_DEBUG(( "HPI: adapter %d stream %d, %d-bit, %d-channel, %d Hz\n", - streamComp->hpiDevice->adapterIndex, streamComp->hpiDevice->streamIndex, - 8 * streamComp->bytesPerFrame / streamComp->hpiFormat.wChannels, - streamComp->hpiFormat.wChannels, - streamComp->hpiFormat.dwSampleRate )); - /* Stream state and buffer levels */ - PA_DEBUG(( "HPI: " )); - PaAsiHpi_GetStreamInfo( streamComp, &streamInfo ); - switch( streamInfo.state ) - { - case HPI_STATE_STOPPED: - PA_DEBUG(( "[STOPPED] " )); - break; - case HPI_STATE_PLAYING: - PA_DEBUG(( "[PLAYING] " )); - break; - case HPI_STATE_RECORDING: - PA_DEBUG(( "[RECORDING] " )); - break; - case HPI_STATE_DRAINED: - PA_DEBUG(( "[DRAINED] " )); - break; - default: - PA_DEBUG(( "[unknown state] " )); - break; - } - if( streamComp->hostBufferSize ) - { - PA_DEBUG(( "host = %d/%d B, ", streamInfo.dataSize, streamComp->hostBufferSize )); - PA_DEBUG(( "hw = %d/%d (%d) B, ", streamInfo.auxDataSize, - streamComp->hardwareBufferSize, streamComp->outputBufferCap )); - } - else - { - PA_DEBUG(( "hw = %d/%d B, ", streamInfo.dataSize, streamComp->hardwareBufferSize )); - } - PA_DEBUG(( "count = %d", streamInfo.frameCounter )); - if( streamInfo.overflow ) - { - PA_DEBUG(( " [overflow]" )); - } - else if( streamInfo.underflow ) - { - PA_DEBUG(( " [underflow]" )); - } - PA_DEBUG(( "\n" )); -} - - -/** Display stream information for debugging purposes. - - @param stream Pointer to stream to query - */ -static void PaAsiHpi_StreamDump( PaAsiHpiStream *stream ) -{ - assert( stream ); - - PA_DEBUG(( "\n------------------------- STREAM INFO FOR %p ---------------------------\n", stream )); - /* General stream info (input+output) */ - if( stream->baseStreamRep.streamCallback ) - { - PA_DEBUG(( "[callback] " )); - } - else - { - PA_DEBUG(( "[blocking] " )); - } - PA_DEBUG(( "sr=%d Hz, poll=%d ms, max %d frames/buf ", - (int)stream->baseStreamRep.streamInfo.sampleRate, - stream->pollingInterval, stream->maxFramesPerHostBuffer )); - switch( stream->state ) - { - case paAsiHpiStoppedState: - PA_DEBUG(( "[stopped]\n" )); - break; - case paAsiHpiActiveState: - PA_DEBUG(( "[active]\n" )); - break; - case paAsiHpiCallbackFinishedState: - PA_DEBUG(( "[cb fin]\n" )); - break; - default: - PA_DEBUG(( "[unknown state]\n" )); - break; - } - if( stream->callbackMode ) - { - PA_DEBUG(( "cb info: thread=%p, cbAbort=%d, cbFinished=%d\n", - stream->thread.thread, stream->callbackAbort, stream->callbackFinished )); - } - - PA_DEBUG(( "----------------------------------- Input ------------------------------------\n" )); - if( stream->input ) - { - PaAsiHpi_StreamComponentDump( stream->input, stream ); - } - else - { - PA_DEBUG(( "*none*\n" )); - } - - PA_DEBUG(( "----------------------------------- Output ------------------------------------\n" )); - if( stream->output ) - { - PaAsiHpi_StreamComponentDump( stream->output, stream ); - } - else - { - PA_DEBUG(( "*none*\n" )); - } - PA_DEBUG(( "-------------------------------------------------------------------------------\n\n" )); - -} - - -/** Determine buffer sizes and allocate appropriate stream buffers. - This attempts to allocate a BBM (host) buffer for the HPI stream component (either input - or output, as both have similar buffer needs). Not all AudioScience adapters support BBM, - in which case the hardware buffer has to suffice. The size of the HPI host buffer is chosen - as a multiple of framesPerPaHostBuffer, and also influenced by the suggested latency and the - estimated minimum polling interval. The HPI host and hardware buffer sizes are stored, and an - appropriate cap for the hardware buffer is also calculated. Finally, the temporary stream - buffer which serves as the PortAudio host buffer for this implementation is allocated. - This buffer contains an integer number of user buffers, to simplify buffer adaption in the - buffer processor. The function returns paBufferTooBig if the HPI interface cannot allocate - an HPI host buffer of the desired size. - - @param streamComp Pointer to stream component struct - - @param pollingInterval Polling interval for stream, in milliseconds - - @param framesPerPaHostBuffer Size of PortAudio host buffer, in frames - - @param suggestedLatency Suggested latency for stream component, in seconds - - @return PortAudio error code (possibly paBufferTooBig or paInsufficientMemory) - */ -static PaError PaAsiHpi_SetupBuffers( PaAsiHpiStreamComponent *streamComp, HW32 pollingInterval, - unsigned long framesPerPaHostBuffer, PaTime suggestedLatency ) -{ - PaError result = paNoError; - PaAsiHpiStreamInfo streamInfo; - unsigned long hpiBufferSize = 0, paHostBufferSize = 0; - - assert( streamComp ); - assert( streamComp->hpiDevice ); - - /* Obtain size of hardware buffer of HPI stream, since we will be activating BBM shortly - and afterwards the buffer size will refer to the BBM (host-side) buffer. - This is necessary to enable reliable detection of xruns. */ - PA_ENSURE_( PaAsiHpi_GetStreamInfo( streamComp, &streamInfo ) ); - streamComp->hardwareBufferSize = streamInfo.bufferSize; - hpiBufferSize = streamInfo.bufferSize; - - /* Check if BBM (background bus mastering) is to be enabled */ - if( PA_ASIHPI_USE_BBM_ ) - { - HW32 bbmBufferSize = 0, preLatencyBufferSize = 0; - HW16 hpiError = 0; - PaTime pollingOverhead; - - /* Check overhead of Pa_Sleep() call (minimum sleep duration in ms -> OS dependent) */ - pollingOverhead = PaUtil_GetTime(); - Pa_Sleep( 0 ); - pollingOverhead = 1000*(PaUtil_GetTime() - pollingOverhead); - PA_DEBUG(( "polling overhead = %f ms (length of 0-second sleep)\n", pollingOverhead )); - /* Obtain minimum recommended size for host buffer (in bytes) */ - PA_ASIHPI_UNLESS_( HPI_StreamEstimateBufferSize( &streamComp->hpiFormat, - pollingInterval + (HW32)ceil( pollingOverhead ), - &bbmBufferSize ), paUnanticipatedHostError ); - /* BBM places more stringent requirements on buffer size (see description */ - /* of HPI_StreamEstimateBufferSize in HPI API document) */ - bbmBufferSize *= 3; - /* Make sure the BBM buffer contains multiple PA host buffers */ - if( bbmBufferSize < 3 * streamComp->bytesPerFrame * framesPerPaHostBuffer ) - bbmBufferSize = 3 * streamComp->bytesPerFrame * framesPerPaHostBuffer; - /* Try to honor latency suggested by user by growing buffer (no decrease possible) */ - if( suggestedLatency > 0.0 ) - { - PaTime bufferDuration = ((PaTime)bbmBufferSize) / streamComp->bytesPerFrame - / streamComp->hpiFormat.dwSampleRate; - /* Don't decrease buffer */ - if( bufferDuration < suggestedLatency ) - { - /* Save old buffer size, to be retried if new size proves too big */ - preLatencyBufferSize = bbmBufferSize; - bbmBufferSize = (HW32)ceil( suggestedLatency * streamComp->bytesPerFrame - * streamComp->hpiFormat.dwSampleRate ); - } - } - /* Choose closest memory block boundary (HPI API document states that - "a buffer size of Nx4096 - 20 makes the best use of memory" - (under the entry for HPI_StreamEstimateBufferSize)) */ - bbmBufferSize = ((HW32)ceil((bbmBufferSize + 20)/4096.0))*4096 - 20; - streamComp->hostBufferSize = bbmBufferSize; - /* Allocate BBM host buffer (this enables bus mastering transfers in background) */ - if( streamComp->hpiDevice->streamIsOutput ) - hpiError = HPI_OutStreamHostBufferAllocate( streamComp->hpiDevice->subSys, - streamComp->hpiStream, - bbmBufferSize ); - else - hpiError = HPI_InStreamHostBufferAllocate( streamComp->hpiDevice->subSys, - streamComp->hpiStream, - bbmBufferSize ); - if( hpiError ) - { - PA_ASIHPI_REPORT_ERROR_( hpiError ); - /* Indicate that BBM is disabled */ - streamComp->hostBufferSize = 0; - /* Retry with smaller buffer size (transfers will still work, but not via BBM) */ - if( hpiError == HPI_ERROR_INVALID_DATASIZE ) - { - /* Retry BBM allocation with smaller size if requested latency proved too big */ - if( preLatencyBufferSize > 0 ) - { - PA_DEBUG(( "Retrying BBM allocation with smaller size (%d vs. %d bytes)\n", - preLatencyBufferSize, bbmBufferSize )); - bbmBufferSize = preLatencyBufferSize; - if( streamComp->hpiDevice->streamIsOutput ) - hpiError = HPI_OutStreamHostBufferAllocate( streamComp->hpiDevice->subSys, - streamComp->hpiStream, - bbmBufferSize ); - else - hpiError = HPI_InStreamHostBufferAllocate( streamComp->hpiDevice->subSys, - streamComp->hpiStream, - bbmBufferSize ); - /* Another round of error checking */ - if( hpiError ) - { - PA_ASIHPI_REPORT_ERROR_( hpiError ); - /* No escapes this time */ - if( hpiError == HPI_ERROR_INVALID_DATASIZE ) - { - result = paBufferTooBig; - goto error; - } - else if( hpiError != HPI_ERROR_INVALID_OPERATION ) - { - result = paUnanticipatedHostError; - goto error; - } - } - else - { - streamComp->hostBufferSize = bbmBufferSize; - hpiBufferSize = bbmBufferSize; - } - } - else - { - result = paBufferTooBig; - goto error; - } - } - /* If BBM not supported, foreground transfers will be used, but not a show-stopper */ - /* Anything else is an error */ - else if( hpiError != HPI_ERROR_INVALID_OPERATION ) - { - result = paUnanticipatedHostError; - goto error; - } - } - else - { - hpiBufferSize = bbmBufferSize; - } - } - - /* Final check of buffer size */ - paHostBufferSize = streamComp->bytesPerFrame * framesPerPaHostBuffer; - if( hpiBufferSize < 3*paHostBufferSize ) - { - result = paBufferTooBig; - goto error; - } - /* Set cap on output buffer size, based on latency suggestions */ - if( streamComp->hpiDevice->streamIsOutput ) - { - PaTime latency = suggestedLatency > 0.0 ? suggestedLatency : - streamComp->hpiDevice->baseDeviceInfo.defaultHighOutputLatency; - streamComp->outputBufferCap = - (HW32)ceil( latency * streamComp->bytesPerFrame * streamComp->hpiFormat.dwSampleRate ); - /* The cap should not be too small, to prevent underflow */ - if( streamComp->outputBufferCap < 4*paHostBufferSize ) - streamComp->outputBufferCap = 4*paHostBufferSize; - } - else - { - streamComp->outputBufferCap = 0; - } - /* Temp buffer size should be multiple of PA host buffer size (or 1x, if using fixed blocks) */ - streamComp->tempBufferSize = paHostBufferSize; - /* Allocate temp buffer */ - PA_UNLESS_( streamComp->tempBuffer = (HW8 *)PaUtil_AllocateMemory( streamComp->tempBufferSize ), - paInsufficientMemory ); -error: - return result; -} - - -/** Opens PortAudio stream. - This determines a suitable value for framesPerBuffer, if the user didn't specify it, - based on the suggested latency. It then opens each requested stream direction with the - appropriate stream format, and allocates the required stream buffers. It sets up the - various PortAudio structures dealing with streams, and estimates the stream latency. - - See pa_hostapi.h for a list of validity guarantees made about OpenStream parameters. - - @param hostApi Pointer to host API struct - - @param s List of open streams, where successfully opened stream will go - - @param inputParameters Pointer to stream parameter struct for input side of stream - - @param outputParameters Pointer to stream parameter struct for output side of stream - - @param sampleRate Desired sample rate - - @param framesPerBuffer Desired number of frames per buffer passed to user callback - (or chunk size for blocking stream) - - @param streamFlags Stream flags - - @param streamCallback Pointer to user callback function (zero for blocking interface) - - @param userData Pointer to user data that will be passed to callback function along with data - - @return PortAudio error code -*/ -static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, - PaStream **s, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate, - unsigned long framesPerBuffer, - PaStreamFlags streamFlags, - PaStreamCallback *streamCallback, - void *userData ) -{ - PaError result = paNoError; - PaAsiHpiHostApiRepresentation *hpiHostApi = (PaAsiHpiHostApiRepresentation*)hostApi; - PaAsiHpiStream *stream = NULL; - unsigned long framesPerHostBuffer = framesPerBuffer; - int inputChannelCount = 0, outputChannelCount = 0; - PaSampleFormat inputSampleFormat = 0, outputSampleFormat = 0; - PaSampleFormat hostInputSampleFormat = 0, hostOutputSampleFormat = 0; - PaTime maxSuggestedLatency = 0.0; - - /* Validate platform-specific flags -> none expected for HPI */ - if( (streamFlags & paPlatformSpecificFlags) != 0 ) - return paInvalidFlag; /* unexpected platform-specific flag */ - - /* Create blank stream structure */ - PA_UNLESS_( stream = (PaAsiHpiStream *)PaUtil_AllocateMemory( sizeof(PaAsiHpiStream) ), - paInsufficientMemory ); - memset( stream, 0, sizeof(PaAsiHpiStream) ); - - /* If the number of frames per buffer is unspecified, we have to come up with one. */ - if( framesPerHostBuffer == paFramesPerBufferUnspecified ) - { - if( inputParameters ) - maxSuggestedLatency = inputParameters->suggestedLatency; - if( outputParameters && (outputParameters->suggestedLatency > maxSuggestedLatency) ) - maxSuggestedLatency = outputParameters->suggestedLatency; - /* Use suggested latency if available */ - if( maxSuggestedLatency > 0.0 ) - framesPerHostBuffer = (unsigned long)ceil( maxSuggestedLatency * sampleRate ); - else - /* AudioScience cards like BIG buffers by default */ - framesPerHostBuffer = 4096; - } - /* Lower bounds on host buffer size, due to polling and HPI constraints */ - if( 1000.0*framesPerHostBuffer/sampleRate < PA_ASIHPI_MIN_POLLING_INTERVAL_ ) - framesPerHostBuffer = (unsigned long)ceil( sampleRate * PA_ASIHPI_MIN_POLLING_INTERVAL_ / 1000.0 ); - /* if( framesPerHostBuffer < PA_ASIHPI_MIN_FRAMES_ ) - framesPerHostBuffer = PA_ASIHPI_MIN_FRAMES_; */ - /* Efficient if host buffer size is integer multiple of user buffer size */ - if( framesPerBuffer > 0 ) - framesPerHostBuffer = (unsigned long)ceil( (double)framesPerHostBuffer / framesPerBuffer ) * framesPerBuffer; - /* Buffer should always be a multiple of 4 bytes to facilitate 32-bit PCI transfers. - By keeping the frames a multiple of 4, this is ensured even for 8-bit mono sound. */ - framesPerHostBuffer = (framesPerHostBuffer / 4) * 4; - /* Polling is based on time length (in milliseconds) of user-requested block size */ - stream->pollingInterval = (HW32)ceil( 1000.0*framesPerHostBuffer/sampleRate ); - assert( framesPerHostBuffer > 0 ); - - /* Open underlying streams, check formats and allocate buffers */ - if( inputParameters ) - { - /* Create blank stream component structure */ - PA_UNLESS_( stream->input = (PaAsiHpiStreamComponent *)PaUtil_AllocateMemory( sizeof(PaAsiHpiStreamComponent) ), - paInsufficientMemory ); - memset( stream->input, 0, sizeof(PaAsiHpiStreamComponent) ); - /* Create/validate format */ - PA_ENSURE_( PaAsiHpi_CreateFormat( hostApi, inputParameters, sampleRate, - &stream->input->hpiDevice, &stream->input->hpiFormat ) ); - /* Open stream and set format */ - PA_ENSURE_( PaAsiHpi_OpenInput( hostApi, stream->input->hpiDevice, &stream->input->hpiFormat, - &stream->input->hpiStream ) ); - inputChannelCount = inputParameters->channelCount; - inputSampleFormat = inputParameters->sampleFormat; - hostInputSampleFormat = PaAsiHpi_HpiToPaFormat( stream->input->hpiFormat.wFormat ); - stream->input->bytesPerFrame = inputChannelCount * Pa_GetSampleSize( hostInputSampleFormat ); - assert( stream->input->bytesPerFrame > 0 ); - /* Allocate host and temp buffers of appropriate size */ - PA_ENSURE_( PaAsiHpi_SetupBuffers( stream->input, stream->pollingInterval, - framesPerHostBuffer, inputParameters->suggestedLatency ) ); - } - if( outputParameters ) - { - /* Create blank stream component structure */ - PA_UNLESS_( stream->output = (PaAsiHpiStreamComponent *)PaUtil_AllocateMemory( sizeof(PaAsiHpiStreamComponent) ), - paInsufficientMemory ); - memset( stream->output, 0, sizeof(PaAsiHpiStreamComponent) ); - /* Create/validate format */ - PA_ENSURE_( PaAsiHpi_CreateFormat( hostApi, outputParameters, sampleRate, - &stream->output->hpiDevice, &stream->output->hpiFormat ) ); - /* Open stream and check format */ - PA_ENSURE_( PaAsiHpi_OpenOutput( hostApi, stream->output->hpiDevice, - &stream->output->hpiFormat, - &stream->output->hpiStream ) ); - outputChannelCount = outputParameters->channelCount; - outputSampleFormat = outputParameters->sampleFormat; - hostOutputSampleFormat = PaAsiHpi_HpiToPaFormat( stream->output->hpiFormat.wFormat ); - stream->output->bytesPerFrame = outputChannelCount * Pa_GetSampleSize( hostOutputSampleFormat ); - /* Allocate host and temp buffers of appropriate size */ - PA_ENSURE_( PaAsiHpi_SetupBuffers( stream->output, stream->pollingInterval, - framesPerHostBuffer, outputParameters->suggestedLatency ) ); - } - - /* Determine maximum frames per host buffer (least common denominator of input/output) */ - if( inputParameters && outputParameters ) - { - stream->maxFramesPerHostBuffer = PA_MIN( stream->input->tempBufferSize / stream->input->bytesPerFrame, - stream->output->tempBufferSize / stream->output->bytesPerFrame ); - } - else - { - stream->maxFramesPerHostBuffer = inputParameters ? stream->input->tempBufferSize / stream->input->bytesPerFrame - : stream->output->tempBufferSize / stream->output->bytesPerFrame; - } - assert( stream->maxFramesPerHostBuffer > 0 ); - /* Initialize various other stream parameters */ - stream->neverDropInput = streamFlags & paNeverDropInput; - stream->state = paAsiHpiStoppedState; - - /* Initialize either callback or blocking interface */ - if( streamCallback ) - { - PaUtil_InitializeStreamRepresentation( &stream->baseStreamRep, - &hpiHostApi->callbackStreamInterface, - streamCallback, userData ); - stream->callbackMode = 1; - } - else - { - PaUtil_InitializeStreamRepresentation( &stream->baseStreamRep, - &hpiHostApi->blockingStreamInterface, - streamCallback, userData ); - /* Pre-allocate non-interleaved user buffer pointers for blocking interface */ - PA_UNLESS_( stream->blockingUserBufferCopy = - PaUtil_AllocateMemory( sizeof(void *) * PA_MAX( inputChannelCount, outputChannelCount ) ), - paInsufficientMemory ); - stream->callbackMode = 0; - } - PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate ); - - /* Following pa_linux_alsa's lead, we operate with fixed host buffer size by default, */ - /* since other modes will invariably lead to block adaption (maybe Bounded better?) */ - PA_ENSURE_( PaUtil_InitializeBufferProcessor( &stream->bufferProcessor, - inputChannelCount, inputSampleFormat, hostInputSampleFormat, - outputChannelCount, outputSampleFormat, hostOutputSampleFormat, - sampleRate, streamFlags, - framesPerBuffer, framesPerHostBuffer, paUtilFixedHostBufferSize, - streamCallback, userData ) ); - - stream->baseStreamRep.streamInfo.structVersion = 1; - stream->baseStreamRep.streamInfo.sampleRate = sampleRate; - /* Determine input latency from buffer processor and buffer sizes */ - if( stream->input ) - { - PaTime bufferDuration = ( stream->input->hostBufferSize + stream->input->hardwareBufferSize ) - / sampleRate / stream->input->bytesPerFrame; - stream->baseStreamRep.streamInfo.inputLatency = - PaUtil_GetBufferProcessorInputLatency( &stream->bufferProcessor ) + - bufferDuration - stream->maxFramesPerHostBuffer / sampleRate; - assert( stream->baseStreamRep.streamInfo.inputLatency > 0.0 ); - } - /* Determine output latency from buffer processor and buffer sizes */ - if( stream->output ) - { - PaTime bufferDuration = ( stream->output->hostBufferSize + stream->output->hardwareBufferSize ) - / sampleRate / stream->output->bytesPerFrame; - /* Take buffer size cap into account (see PaAsiHpi_WaitForFrames) */ - if( !stream->input && (stream->output->outputBufferCap > 0) ) - { - bufferDuration = PA_MIN( bufferDuration, - stream->output->outputBufferCap / sampleRate / stream->output->bytesPerFrame ); - } - stream->baseStreamRep.streamInfo.outputLatency = - PaUtil_GetBufferProcessorOutputLatency( &stream->bufferProcessor ) + - bufferDuration - stream->maxFramesPerHostBuffer / sampleRate; - assert( stream->baseStreamRep.streamInfo.outputLatency > 0.0 ); - } - - /* Report stream info, for debugging purposes */ - PaAsiHpi_StreamDump( stream ); - - /* Save initialized stream to PA stream list */ - *s = (PaStream*)stream; - return result; - -error: - CloseStream( (PaStream*)stream ); - return result; -} - - -/** Close PortAudio stream. - When CloseStream() is called, the multi-api layer ensures that the stream has already - been stopped or aborted. This closes the underlying HPI streams and deallocates stream - buffers and structs. - - @param s Pointer to PortAudio stream - - @return PortAudio error code -*/ -static PaError CloseStream( PaStream *s ) -{ - PaError result = paNoError; - PaAsiHpiStream *stream = (PaAsiHpiStream*)s; - - /* If stream is already gone, all is well */ - if( stream == NULL ) - return paNoError; - - /* Generic stream cleanup */ - PaUtil_TerminateBufferProcessor( &stream->bufferProcessor ); - PaUtil_TerminateStreamRepresentation( &stream->baseStreamRep ); - - /* Implementation-specific details - close internal streams */ - if( stream->input ) - { - /* Close HPI stream (freeing BBM host buffer in the process, if used) */ - if( stream->input->hpiStream ) - { - PA_ASIHPI_UNLESS_( HPI_InStreamClose( stream->input->hpiDevice->subSys, - stream->input->hpiStream ), paUnanticipatedHostError ); - } - /* Free temp buffer and stream component */ - PaUtil_FreeMemory( stream->input->tempBuffer ); - PaUtil_FreeMemory( stream->input ); - } - if( stream->output ) - { - /* Close HPI stream (freeing BBM host buffer in the process, if used) */ - if( stream->output->hpiStream ) - { - PA_ASIHPI_UNLESS_( HPI_OutStreamClose( stream->output->hpiDevice->subSys, - stream->output->hpiStream ), paUnanticipatedHostError ); - } - /* Free temp buffer and stream component */ - PaUtil_FreeMemory( stream->output->tempBuffer ); - PaUtil_FreeMemory( stream->output ); - } - - PaUtil_FreeMemory( stream->blockingUserBufferCopy ); - PaUtil_FreeMemory( stream ); - -error: - return result; -} - - -/** Prime HPI output stream with silence. - This resets the output stream and uses PortAudio helper routines to fill the - temp buffer with silence. It then writes two host buffers to the stream. This is supposed - to be called before the stream is started. It has no effect on input-only streams. - - @param stream Pointer to stream struct - - @return PortAudio error code - */ -static PaError PaAsiHpi_PrimeOutputWithSilence( PaAsiHpiStream *stream ) -{ - PaError result = paNoError; - PaAsiHpiStreamComponent *out; - PaUtilZeroer *zeroer; - PaSampleFormat outputFormat; -#if (HPI_VER < HPI_VERSION_CONSTRUCTOR( 3, 5, 5 )) - HPI_DATA data; -#endif - assert( stream ); - out = stream->output; - /* Only continue if stream has output channels */ - if( !out ) - return result; - assert( out->tempBuffer ); - - /* Clear all existing data in hardware playback buffer */ - PA_ASIHPI_UNLESS_( HPI_OutStreamReset( out->hpiDevice->subSys, - out->hpiStream ), paUnanticipatedHostError ); - /* Fill temp buffer with silence */ - outputFormat = PaAsiHpi_HpiToPaFormat( out->hpiFormat.wFormat ); - zeroer = PaUtil_SelectZeroer( outputFormat ); - zeroer(out->tempBuffer, 1, out->tempBufferSize / Pa_GetSampleSize(outputFormat) ); - /* Write temp buffer to hardware fifo twice, to get started */ -#if (HPI_VER >= HPI_VERSION_CONSTRUCTOR( 3, 5, 5 )) - PA_ASIHPI_UNLESS_( HPI_OutStreamWriteBuf( out->hpiDevice->subSys, out->hpiStream, - out->tempBuffer, out->tempBufferSize, &out->hpiFormat), - paUnanticipatedHostError ); - PA_ASIHPI_UNLESS_( HPI_OutStreamWriteBuf( out->hpiDevice->subSys, out->hpiStream, - out->tempBuffer, out->tempBufferSize, &out->hpiFormat), - paUnanticipatedHostError ); -#else - PA_ASIHPI_UNLESS_( HPI_DataCreate( &data, &out->hpiFormat, out->tempBuffer, out->tempBufferSize ), - paUnanticipatedHostError ); - PA_ASIHPI_UNLESS_( HPI_OutStreamWrite( out->hpiDevice->subSys, - out->hpiStream, &data ), paUnanticipatedHostError ); - PA_ASIHPI_UNLESS_( HPI_OutStreamWrite( out->hpiDevice->subSys, - out->hpiStream, &data ), paUnanticipatedHostError ); -#endif -error: - return result; -} - - -/** Start HPI streams (both input + output). - This starts all HPI streams in the PortAudio stream. Output streams are first primed with - silence, if required. After this call the PA stream is in the Active state. - - @todo Implement priming via the user callback - - @param stream Pointer to stream struct - - @param outputPrimed True if output is already primed (if false, silence will be loaded before starting) - - @return PortAudio error code - */ -static PaError PaAsiHpi_StartStream( PaAsiHpiStream *stream, int outputPrimed ) -{ - PaError result = paNoError; - - if( stream->input ) - { - PA_ASIHPI_UNLESS_( HPI_InStreamStart( stream->input->hpiDevice->subSys, - stream->input->hpiStream ), paUnanticipatedHostError ); - } - if( stream->output ) - { - if( !outputPrimed ) - { - /* Buffer isn't primed, so load stream with silence */ - PA_ENSURE_( PaAsiHpi_PrimeOutputWithSilence( stream ) ); - } - PA_ASIHPI_UNLESS_( HPI_OutStreamStart( stream->output->hpiDevice->subSys, - stream->output->hpiStream ), paUnanticipatedHostError ); - } - stream->state = paAsiHpiActiveState; - stream->callbackFinished = 0; - - /* Report stream info for debugging purposes */ - /* PaAsiHpi_StreamDump( stream ); */ - -error: - return result; -} - - -/** Start PortAudio stream. - If the stream has a callback interface, this starts a helper thread to feed the user callback. - The thread will then take care of starting the HPI streams, and this function will block - until the streams actually start. In the case of a blocking interface, the HPI streams - are simply started. - - @param s Pointer to PortAudio stream - - @return PortAudio error code -*/ -static PaError StartStream( PaStream *s ) -{ - PaError result = paNoError; - PaAsiHpiStream *stream = (PaAsiHpiStream*)s; - - assert( stream ); - - /* Ready the processor */ - PaUtil_ResetBufferProcessor( &stream->bufferProcessor ); - - if( stream->callbackMode ) - { - /* Create and start callback engine thread */ - /* Also waits 1 second for stream to be started by engine thread (otherwise aborts) */ - PA_ENSURE_( PaUnixThread_New( &stream->thread, &CallbackThreadFunc, stream, 1., 0 /*rtSched*/ ) ); - } - else - { - PA_ENSURE_( PaAsiHpi_StartStream( stream, 0 ) ); - } - -error: - return result; -} - - -/** Stop HPI streams (input + output), either softly or abruptly. - If abort is false, the function blocks until the output stream is drained, otherwise it - stops immediately and discards data in the stream hardware buffers. - - This function is safe to call from the callback engine thread as well as the main thread. - - @param stream Pointer to stream struct - - @param abort True if samples in output buffer should be discarded (otherwise blocks until stream is done) - - @return PortAudio error code - - */ -static PaError PaAsiHpi_StopStream( PaAsiHpiStream *stream, int abort ) -{ - PaError result = paNoError; - - assert( stream ); - - /* Input channels */ - if( stream->input ) - { - PA_ASIHPI_UNLESS_( HPI_InStreamReset( stream->input->hpiDevice->subSys, - stream->input->hpiStream ), paUnanticipatedHostError ); - } - /* Output channels */ - if( stream->output ) - { - if( !abort ) - { - /* Wait until HPI output stream is drained */ - while( 1 ) - { - PaAsiHpiStreamInfo streamInfo; - PaTime timeLeft; - - /* Obtain number of samples waiting to be played */ - PA_ENSURE_( PaAsiHpi_GetStreamInfo( stream->output, &streamInfo ) ); - /* Check if stream is drained */ - if( (streamInfo.state != HPI_STATE_PLAYING) && - (streamInfo.dataSize < stream->output->bytesPerFrame * PA_ASIHPI_MIN_FRAMES_) ) - break; - /* Sleep amount of time represented by remaining samples */ - timeLeft = 1000.0 * streamInfo.dataSize / stream->output->bytesPerFrame - / stream->baseStreamRep.streamInfo.sampleRate; - Pa_Sleep( (long)ceil( timeLeft ) ); - } - } - PA_ASIHPI_UNLESS_( HPI_OutStreamReset( stream->output->hpiDevice->subSys, - stream->output->hpiStream ), paUnanticipatedHostError ); - } - - /* Report stream info for debugging purposes */ - /* PaAsiHpi_StreamDump( stream ); */ - -error: - return result; -} - - -/** Stop or abort PortAudio stream. - - This function is used to explicitly stop the PortAudio stream (via StopStream/AbortStream), - as opposed to the situation when the callback finishes with a result other than paContinue. - If a stream is in callback mode we will have to inspect whether the background thread has - finished, or we will have to take it out. In either case we join the thread before returning. - In blocking mode, we simply tell HPI to stop abruptly (abort) or finish buffers (drain). - The PortAudio stream will be in the Stopped state after a call to this function. - - Don't call this from the callback engine thread! - - @param stream Pointer to stream struct - - @param abort True if samples in output buffer should be discarded (otherwise blocks until stream is done) - - @return PortAudio error code -*/ -static PaError PaAsiHpi_ExplicitStop( PaAsiHpiStream *stream, int abort ) -{ - PaError result = paNoError; - - /* First deal with the callback thread, cancelling and/or joining it if necessary */ - if( stream->callbackMode ) - { - PaError threadRes; - stream->callbackAbort = abort; - if( abort ) - { - PA_DEBUG(( "Aborting callback\n" )); - } - else - { - PA_DEBUG(( "Stopping callback\n" )); - } - PA_ENSURE_( PaUnixThread_Terminate( &stream->thread, !abort, &threadRes ) ); - if( threadRes != paNoError ) - { - PA_DEBUG(( "Callback thread returned: %d\n", threadRes )); - } - } - else - { - PA_ENSURE_( PaAsiHpi_StopStream( stream, abort ) ); - } - - stream->state = paAsiHpiStoppedState; - -error: - return result; -} - - -/** Stop PortAudio stream. - This blocks until the output buffers are drained. - - @param s Pointer to PortAudio stream - - @return PortAudio error code -*/ -static PaError StopStream( PaStream *s ) -{ - return PaAsiHpi_ExplicitStop( (PaAsiHpiStream *) s, 0 ); -} - - -/** Abort PortAudio stream. - This discards any existing data in output buffers and stops the stream immediately. - - @param s Pointer to PortAudio stream - - @return PortAudio error code -*/ -static PaError AbortStream( PaStream *s ) -{ - return PaAsiHpi_ExplicitStop( (PaAsiHpiStream * ) s, 1 ); -} - - -/** Determine whether the stream is stopped. - A stream is considered to be stopped prior to a successful call to StartStream and after - a successful call to StopStream or AbortStream. If a stream callback returns a value other - than paContinue the stream is NOT considered to be stopped (it is in CallbackFinished state). - - @param s Pointer to PortAudio stream - - @return Returns one (1) when the stream is stopped, zero (0) when the stream is running, or - a PaErrorCode (which are always negative) if PortAudio is not initialized or an - error is encountered. -*/ -static PaError IsStreamStopped( PaStream *s ) -{ - PaAsiHpiStream *stream = (PaAsiHpiStream*)s; - - assert( stream ); - return stream->state == paAsiHpiStoppedState ? 1 : 0; -} - - -/** Determine whether the stream is active. - A stream is active after a successful call to StartStream(), until it becomes inactive either - as a result of a call to StopStream() or AbortStream(), or as a result of a return value - other than paContinue from the stream callback. In the latter case, the stream is considered - inactive after the last buffer has finished playing. - - @param s Pointer to PortAudio stream - - @return Returns one (1) when the stream is active (i.e. playing or recording audio), - zero (0) when not playing, or a PaErrorCode (which are always negative) - if PortAudio is not initialized or an error is encountered. -*/ -static PaError IsStreamActive( PaStream *s ) -{ - PaAsiHpiStream *stream = (PaAsiHpiStream*)s; - - assert( stream ); - return stream->state == paAsiHpiActiveState ? 1 : 0; -} - - -/** Returns current stream time. - This corresponds to the system clock. The clock should run continuously while the stream - is open, i.e. between calls to OpenStream() and CloseStream(), therefore a frame counter - is not good enough. - - @param s Pointer to PortAudio stream - - @return Stream time, in seconds - */ -static PaTime GetStreamTime( PaStream *s ) -{ - return PaUtil_GetTime(); -} - - -/** Returns CPU load. - - @param s Pointer to PortAudio stream - - @return CPU load (0.0 if blocking interface is used) - */ -static double GetStreamCpuLoad( PaStream *s ) -{ - PaAsiHpiStream *stream = (PaAsiHpiStream*)s; - - return stream->callbackMode ? PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer ) : 0.0; -} - -/* --------------------------- Callback Interface --------------------------- */ - -/** Exit routine which is called when callback thread quits. - This takes care of stopping the HPI streams (either waiting for output to finish, or - abruptly). It also calls the user-supplied StreamFinished callback, and sets the - stream state to CallbackFinished if it was reached via a non-paContinue return from - the user callback function. - - @param userData A pointer to an open stream previously created with Pa_OpenStream - */ -static void PaAsiHpi_OnThreadExit( void *userData ) -{ - PaAsiHpiStream *stream = (PaAsiHpiStream *) userData; - - assert( stream ); - - PaUtil_ResetCpuLoadMeasurer( &stream->cpuLoadMeasurer ); - - PA_DEBUG(( "%s: Stopping HPI streams\n", __FUNCTION__ )); - PaAsiHpi_StopStream( stream, stream->callbackAbort ); - PA_DEBUG(( "%s: Stoppage\n", __FUNCTION__ )); - - /* Eventually notify user all buffers have played */ - if( stream->baseStreamRep.streamFinishedCallback ) - { - stream->baseStreamRep.streamFinishedCallback( stream->baseStreamRep.userData ); - } - - /* Unfortunately both explicit calls to Stop/AbortStream (leading to Stopped state) - and implicit stops via paComplete/paAbort (leading to CallbackFinished state) - end up here - need another flag to remind us which is the case */ - if( stream->callbackFinished ) - stream->state = paAsiHpiCallbackFinishedState; -} - - -/** Wait until there is enough frames to fill a host buffer. - The routine attempts to sleep until at least a full host buffer can be retrieved from the - input HPI stream and passed to the output HPI stream. It will first sleep until enough - output space is available, as this is usually easily achievable. If it is an output-only - stream, it will also sleep if the hardware buffer is too full, thereby throttling the - filling of the output buffer and reducing output latency. The routine then blocks until - enough input samples are available, unless this will cause an output underflow. In the - process, input overflows and output underflows are indicated. - - @param stream Pointer to stream struct - - @param framesAvail Returns the number of available frames - - @param cbFlags Overflows and underflows indicated in here - - @return PortAudio error code (only paUnanticipatedHostError expected) - */ -static PaError PaAsiHpi_WaitForFrames( PaAsiHpiStream *stream, unsigned long *framesAvail, - PaStreamCallbackFlags *cbFlags ) -{ - PaError result = paNoError; - double sampleRate; - unsigned long framesTarget; - HW32 outputData = 0, outputSpace = 0, inputData = 0, framesLeft = 0; - - assert( stream ); - assert( stream->input || stream->output ); - - sampleRate = stream->baseStreamRep.streamInfo.sampleRate; - /* We have to come up with this much frames on both input and output */ - framesTarget = stream->bufferProcessor.framesPerHostBuffer; - assert( framesTarget > 0 ); - - while( 1 ) - { - PaAsiHpiStreamInfo info; - /* Check output first, as this takes priority in the default full-duplex mode */ - if( stream->output ) - { - PA_ENSURE_( PaAsiHpi_GetStreamInfo( stream->output, &info ) ); - /* Wait until enough space is available in output buffer to receive a full block */ - if( info.availableFrames < framesTarget ) - { - framesLeft = framesTarget - info.availableFrames; - Pa_Sleep( (long)ceil( 1000 * framesLeft / sampleRate ) ); - continue; - } - /* Wait until the data in hardware buffer has dropped to a sensible level. - Without this, the hardware buffer quickly fills up in the absence of an input - stream to regulate its data rate (if data generation is fast). This leads to - large latencies, as the AudioScience hardware buffers are humongous. - This is similar to the default "Hardware Buffering=off" option in the - AudioScience WAV driver. */ - if( !stream->input && (stream->output->outputBufferCap > 0) && - ( info.totalBufferedData > stream->output->outputBufferCap / stream->output->bytesPerFrame ) ) - { - framesLeft = info.totalBufferedData - stream->output->outputBufferCap / stream->output->bytesPerFrame; - Pa_Sleep( (long)ceil( 1000 * framesLeft / sampleRate ) ); - continue; - } - outputData = info.totalBufferedData; - outputSpace = info.availableFrames; - /* Report output underflow to callback */ - if( info.underflow ) - { - *cbFlags |= paOutputUnderflow; - } - } - - /* Now check input side */ - if( stream->input ) - { - PA_ENSURE_( PaAsiHpi_GetStreamInfo( stream->input, &info ) ); - /* If a full block of samples hasn't been recorded yet, wait for it if possible */ - if( info.availableFrames < framesTarget ) - { - framesLeft = framesTarget - info.availableFrames; - /* As long as output is not disrupted in the process, wait for a full - block of input samples */ - if( !stream->output || (outputData > framesLeft) ) - { - Pa_Sleep( (long)ceil( 1000 * framesLeft / sampleRate ) ); - continue; - } - } - inputData = info.availableFrames; - /** @todo The paInputOverflow flag should be set in the callback containing the - first input sample following the overflow. That means the block currently sitting - at the fore-front of recording, i.e. typically the one containing the newest (last) - sample in the HPI buffer system. This is most likely not the same as the current - block of data being passed to the callback. The current overflow should ideally - be noted in an overflow list of sorts, with an indication of when it should be - reported. The trouble starts if there are several separate overflow incidents, - given a big input buffer. Oh well, something to try out later... */ - if( info.overflow ) - { - *cbFlags |= paInputOverflow; - } - } - break; - } - /* Full-duplex stream */ - if( stream->input && stream->output ) - { - if( outputSpace >= framesTarget ) - *framesAvail = outputSpace; - /* If input didn't make the target, keep the output count instead (input underflow) */ - if( (inputData >= framesTarget) && (inputData < outputSpace) ) - *framesAvail = inputData; - } - else - { - *framesAvail = stream->input ? inputData : outputSpace; - } - -error: - return result; -} - - -/** Obtain recording, current and playback timestamps of stream. - The current time is determined by the system clock. This "now" timestamp occurs at the - forefront of recording (and playback in the full-duplex case), which happens later than the - input timestamp by an amount equal to the total number of recorded frames in the input buffer. - The output timestamp indicates when the next generated sample will actually be played. This - happens after all the samples currently in the output buffer are played. The output timestamp - therefore follows the current timestamp by an amount equal to the number of frames yet to be - played back in the output buffer. - - If the current timestamp is the present, the input timestamp is in the past and the output - timestamp is in the future. - - @param stream Pointer to stream struct - - @param timeInfo Pointer to timeInfo struct that will contain timestamps - */ -static void PaAsiHpi_CalculateTimeInfo( PaAsiHpiStream *stream, PaStreamCallbackTimeInfo *timeInfo ) -{ - PaAsiHpiStreamInfo streamInfo; - double sampleRate; - - assert( stream ); - assert( timeInfo ); - sampleRate = stream->baseStreamRep.streamInfo.sampleRate; - - /* The current time ("now") is at the forefront of both recording and playback */ - timeInfo->currentTime = GetStreamTime( (PaStream *)stream ); - /* The last sample in the input buffer was recorded just now, so the first sample - happened (number of recorded samples)/sampleRate ago */ - timeInfo->inputBufferAdcTime = timeInfo->currentTime; - if( stream->input ) - { - PaAsiHpi_GetStreamInfo( stream->input, &streamInfo ); - timeInfo->inputBufferAdcTime -= streamInfo.totalBufferedData / sampleRate; - } - /* The first of the outgoing samples will be played after all the samples in the output - buffer is done */ - timeInfo->outputBufferDacTime = timeInfo->currentTime; - if( stream->output ) - { - PaAsiHpi_GetStreamInfo( stream->output, &streamInfo ); - timeInfo->outputBufferDacTime += streamInfo.totalBufferedData / sampleRate; - } -} - - -/** Read from HPI input stream and register buffers. - This reads data from the HPI input stream (if it exists) and registers the temp stream - buffers of both input and output streams with the buffer processor. In the process it also - handles input underflows in the full-duplex case. - - @param stream Pointer to stream struct - - @param numFrames On entrance the number of available frames, on exit the number of - received frames - - @param cbFlags Indicates overflows and underflows - - @return PortAudio error code - */ -static PaError PaAsiHpi_BeginProcessing( PaAsiHpiStream *stream, unsigned long *numFrames, - PaStreamCallbackFlags *cbFlags ) -{ - PaError result = paNoError; - - assert( stream ); - if( *numFrames > stream->maxFramesPerHostBuffer ) - *numFrames = stream->maxFramesPerHostBuffer; - - if( stream->input ) - { - PaAsiHpiStreamInfo info; - -#if (HPI_VER < HPI_VERSION_CONSTRUCTOR( 3, 5, 5 )) - HPI_DATA data; -#endif - HW32 framesToGet = *numFrames; - - /* Check for overflows and underflows yet again */ - PA_ENSURE_( PaAsiHpi_GetStreamInfo( stream->input, &info ) ); - if( info.overflow ) - { - *cbFlags |= paInputOverflow; - } - /* Input underflow if less than expected number of samples pitch up */ - if( framesToGet > info.availableFrames ) - { - PaUtilZeroer *zeroer; - PaSampleFormat inputFormat; - - /* Never call an input-only stream with InputUnderflow set */ - if( stream->output ) - *cbFlags |= paInputUnderflow; - framesToGet = info.availableFrames; - /* Fill temp buffer with silence (to make up for missing input samples) */ - inputFormat = PaAsiHpi_HpiToPaFormat( stream->input->hpiFormat.wFormat ); - zeroer = PaUtil_SelectZeroer( inputFormat ); - zeroer(stream->input->tempBuffer, 1, - stream->input->tempBufferSize / Pa_GetSampleSize(inputFormat) ); - } - -#if (HPI_VER >= HPI_VERSION_CONSTRUCTOR( 3, 5, 5 )) - /* Read block of data into temp buffer */ - PA_ASIHPI_UNLESS_( HPI_InStreamReadBuf( stream->input->hpiDevice->subSys, - stream->input->hpiStream, - stream->input->tempBuffer, - framesToGet * stream->input->bytesPerFrame), - paUnanticipatedHostError ); -#else - /* Setup HPI data structure around temp buffer */ - HPI_DataCreate( &data, &stream->input->hpiFormat, stream->input->tempBuffer, - framesToGet * stream->input->bytesPerFrame ); - /* Read block of data into temp buffer */ - PA_ASIHPI_UNLESS_( HPI_InStreamRead( stream->input->hpiDevice->subSys, - stream->input->hpiStream, &data ), - paUnanticipatedHostError ); -#endif - /* Register temp buffer with buffer processor (always FULL buffer) */ - PaUtil_SetInputFrameCount( &stream->bufferProcessor, *numFrames ); - /* HPI interface only allows interleaved channels */ - PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, - 0, stream->input->tempBuffer, - stream->input->hpiFormat.wChannels ); - } - if( stream->output ) - { - /* Register temp buffer with buffer processor */ - PaUtil_SetOutputFrameCount( &stream->bufferProcessor, *numFrames ); - /* HPI interface only allows interleaved channels */ - PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, - 0, stream->output->tempBuffer, - stream->output->hpiFormat.wChannels ); - } - -error: - return result; -} - - -/** Flush output buffers to HPI output stream. - This completes the processing cycle by writing the temp buffer to the HPI interface. - Additional output underflows are caught before data is written to the stream, as this - action typically remedies the underflow and hides it in the process. - - @param stream Pointer to stream struct - - @param numFrames The number of frames to write to the output stream - - @param cbFlags Indicates overflows and underflows - */ -static PaError PaAsiHpi_EndProcessing( PaAsiHpiStream *stream, unsigned long numFrames, - PaStreamCallbackFlags *cbFlags ) -{ - PaError result = paNoError; - - assert( stream ); - - if( stream->output ) - { - PaAsiHpiStreamInfo info; -#if (HPI_VER < HPI_VERSION_CONSTRUCTOR( 3, 5, 5 )) - HPI_DATA data; -#endif - /* Check for underflows after the (potentially time-consuming) callback */ - PA_ENSURE_( PaAsiHpi_GetStreamInfo( stream->output, &info ) ); - if( info.underflow ) - { - *cbFlags |= paOutputUnderflow; - } - -#if (HPI_VER >= HPI_VERSION_CONSTRUCTOR( 3, 5, 5 )) - /* Write temp buffer to HPI stream */ - PA_ASIHPI_UNLESS_( HPI_OutStreamWriteBuf( stream->output->hpiDevice->subSys, - stream->output->hpiStream, - stream->output->tempBuffer, - numFrames * stream->output->bytesPerFrame, - &stream->output->hpiFormat), - paUnanticipatedHostError ); -#else - /* Setup HPI data structure around temp buffer */ - HPI_DataCreate( &data, &stream->output->hpiFormat, stream->output->tempBuffer, - numFrames * stream->output->bytesPerFrame ); - /* Write temp buffer to HPI stream */ - PA_ASIHPI_UNLESS_( HPI_OutStreamWrite( stream->output->hpiDevice->subSys, - stream->output->hpiStream, &data ), - paUnanticipatedHostError ); -#endif - } - -error: - return result; -} - - -/** Main callback engine. - This function runs in a separate thread and does all the work of fetching audio data from - the AudioScience card via the HPI interface, feeding it to the user callback via the buffer - processor, and delivering the resulting output data back to the card via HPI calls. - It is started and terminated when the PortAudio stream is started and stopped, and starts - the HPI streams on startup. - - @param userData A pointer to an open stream previously created with Pa_OpenStream. -*/ -static void *CallbackThreadFunc( void *userData ) -{ - PaError result = paNoError; - PaAsiHpiStream *stream = (PaAsiHpiStream *) userData; - int callbackResult = paContinue; - - assert( stream ); - - /* Cleanup routine stops streams on thread exit */ - pthread_cleanup_push( &PaAsiHpi_OnThreadExit, stream ); - - /* Start HPI streams and notify parent when we're done */ - PA_ENSURE_( PaUnixThread_PrepareNotify( &stream->thread ) ); - /* Buffer will be primed with silence */ - PA_ENSURE_( PaAsiHpi_StartStream( stream, 0 ) ); - PA_ENSURE_( PaUnixThread_NotifyParent( &stream->thread ) ); - - /* MAIN LOOP */ - while( 1 ) - { - PaStreamCallbackFlags cbFlags = 0; - unsigned long framesAvail, framesGot; - - pthread_testcancel(); - - /** @concern StreamStop if the main thread has requested a stop and the stream has not - * been effectively stopped we signal this condition by modifying callbackResult - * (we'll want to flush buffered output). */ - if( PaUnixThread_StopRequested( &stream->thread ) && (callbackResult == paContinue) ) - { - PA_DEBUG(( "Setting callbackResult to paComplete\n" )); - callbackResult = paComplete; - } - - /* Start winding down thread if requested */ - if( callbackResult != paContinue ) - { - stream->callbackAbort = (callbackResult == paAbort); - if( stream->callbackAbort || - /** @concern BlockAdaption: Go on if adaption buffers are empty */ - PaUtil_IsBufferProcessorOutputEmpty( &stream->bufferProcessor ) ) - { - goto end; - } - PA_DEBUG(( "%s: Flushing buffer processor\n", __FUNCTION__ )); - /* There is still buffered output that needs to be processed */ - } - - /* SLEEP */ - /* Wait for data (or buffer space) to become available. This basically sleeps and - polls the HPI interface until a full block of frames can be moved. */ - PA_ENSURE_( PaAsiHpi_WaitForFrames( stream, &framesAvail, &cbFlags ) ); - - /* Consume buffer space. Once we have a number of frames available for consumption we - must retrieve the data from the HPI interface and pass it to the PA buffer processor. - We should be prepared to process several chunks successively. */ - while( framesAvail > 0 ) - { - PaStreamCallbackTimeInfo timeInfo = {0, 0, 0}; - - pthread_testcancel(); - - framesGot = framesAvail; - if( stream->bufferProcessor.hostBufferSizeMode == paUtilFixedHostBufferSize ) - { - /* We've committed to a fixed host buffer size, stick to that */ - framesGot = framesGot >= stream->maxFramesPerHostBuffer ? stream->maxFramesPerHostBuffer : 0; - } - else - { - /* We've committed to an upper bound on the size of host buffers */ - assert( stream->bufferProcessor.hostBufferSizeMode == paUtilBoundedHostBufferSize ); - framesGot = PA_MIN( framesGot, stream->maxFramesPerHostBuffer ); - } - - /* Obtain buffer timestamps */ - PaAsiHpi_CalculateTimeInfo( stream, &timeInfo ); - PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, cbFlags ); - /* CPU load measurement should include processing activivity external to the stream callback */ - PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer ); - if( framesGot > 0 ) - { - /* READ FROM HPI INPUT STREAM */ - PA_ENSURE_( PaAsiHpi_BeginProcessing( stream, &framesGot, &cbFlags ) ); - /* Input overflow in a full-duplex stream makes for interesting times */ - if( stream->input && stream->output && (cbFlags & paInputOverflow) ) - { - /* Special full-duplex paNeverDropInput mode */ - if( stream->neverDropInput ) - { - PaUtil_SetNoOutput( &stream->bufferProcessor ); - cbFlags |= paOutputOverflow; - } - } - /* CALL USER CALLBACK WITH INPUT DATA, AND OBTAIN OUTPUT DATA */ - PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult ); - /* Clear overflow and underflow information (but PaAsiHpi_EndProcessing might - still show up output underflow that will carry over to next round) */ - cbFlags = 0; - /* WRITE TO HPI OUTPUT STREAM */ - PA_ENSURE_( PaAsiHpi_EndProcessing( stream, framesGot, &cbFlags ) ); - /* Advance frame counter */ - framesAvail -= framesGot; - } - PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesGot ); - - if( framesGot == 0 ) - { - /* Go back to polling for more frames */ - break; - - } - if( callbackResult != paContinue ) - break; - } - } - - /* This code is unreachable, but important to include regardless because it - * is possibly a macro with a closing brace to match the opening brace in - * pthread_cleanup_push() above. The documentation states that they must - * always occur in pairs. */ - pthread_cleanup_pop( 1 ); - -end: - /* Indicates normal exit of callback, as opposed to the thread getting killed explicitly */ - stream->callbackFinished = 1; - PA_DEBUG(( "%s: Thread %d exiting (callbackResult = %d)\n ", - __FUNCTION__, pthread_self(), callbackResult )); - /* Exit from thread and report any PortAudio error in the process */ - PaUnixThreading_EXIT( result ); -error: - goto end; -} - -/* --------------------------- Blocking Interface --------------------------- */ - -/* As separate stream interfaces are used for blocking and callback streams, the following - functions can be guaranteed to only be called for blocking streams. */ - -/** Read data from input stream. - This reads the indicated number of frames into the supplied buffer from an input stream, - and blocks until this is done. - - @param s Pointer to PortAudio stream - - @param buffer Pointer to buffer that will receive interleaved data (or an array of pointers - to a buffer for each non-interleaved channel) - - @param frames Number of frames to read from stream - - @return PortAudio error code (also indicates overflow via paInputOverflowed) - */ -static PaError ReadStream( PaStream *s, - void *buffer, - unsigned long frames ) -{ - PaError result = paNoError; - PaAsiHpiStream *stream = (PaAsiHpiStream*)s; - PaAsiHpiStreamInfo info; - void *userBuffer; - - assert( stream ); - PA_UNLESS_( stream->input, paCanNotReadFromAnOutputOnlyStream ); - - /* Check for input overflow since previous call to ReadStream */ - PA_ENSURE_( PaAsiHpi_GetStreamInfo( stream->input, &info ) ); - if( info.overflow ) - { - result = paInputOverflowed; - } - - /* NB Make copy of user buffer pointers, since they are advanced by buffer processor */ - if( stream->bufferProcessor.userInputIsInterleaved ) - { - userBuffer = buffer; - } - else - { - /* Copy channels into local array */ - userBuffer = stream->blockingUserBufferCopy; - memcpy( userBuffer, buffer, sizeof (void *) * stream->input->hpiFormat.wChannels ); - } - - while( frames > 0 ) - { - unsigned long framesGot, framesAvail; - PaStreamCallbackFlags cbFlags = 0; - - PA_ENSURE_( PaAsiHpi_WaitForFrames( stream, &framesAvail, &cbFlags ) ); - framesGot = PA_MIN( framesAvail, frames ); - PA_ENSURE_( PaAsiHpi_BeginProcessing( stream, &framesGot, &cbFlags ) ); - - if( framesGot > 0 ) - { - framesGot = PaUtil_CopyInput( &stream->bufferProcessor, &userBuffer, framesGot ); - PA_ENSURE_( PaAsiHpi_EndProcessing( stream, framesGot, &cbFlags ) ); - /* Advance frame counter */ - frames -= framesGot; - } - } - -error: - return result; -} - - -/** Write data to output stream. - This writes the indicated number of frames from the supplied buffer to an output stream, - and blocks until this is done. - - @param s Pointer to PortAudio stream - - @param buffer Pointer to buffer that provides interleaved data (or an array of pointers - to a buffer for each non-interleaved channel) - - @param frames Number of frames to write to stream - - @return PortAudio error code (also indicates underflow via paOutputUnderflowed) - */ -static PaError WriteStream( PaStream *s, - const void *buffer, - unsigned long frames ) -{ - PaError result = paNoError; - PaAsiHpiStream *stream = (PaAsiHpiStream*)s; - PaAsiHpiStreamInfo info; - const void *userBuffer; - - assert( stream ); - PA_UNLESS_( stream->output, paCanNotWriteToAnInputOnlyStream ); - - /* Check for output underflow since previous call to WriteStream */ - PA_ENSURE_( PaAsiHpi_GetStreamInfo( stream->output, &info ) ); - if( info.underflow ) - { - result = paOutputUnderflowed; - } - - /* NB Make copy of user buffer pointers, since they are advanced by buffer processor */ - if( stream->bufferProcessor.userOutputIsInterleaved ) - { - userBuffer = buffer; - } - else - { - /* Copy channels into local array */ - userBuffer = stream->blockingUserBufferCopy; - memcpy( (void *)userBuffer, buffer, sizeof (void *) * stream->output->hpiFormat.wChannels ); - } - - while( frames > 0 ) - { - unsigned long framesGot, framesAvail; - PaStreamCallbackFlags cbFlags = 0; - - PA_ENSURE_( PaAsiHpi_WaitForFrames( stream, &framesAvail, &cbFlags ) ); - framesGot = PA_MIN( framesAvail, frames ); - PA_ENSURE_( PaAsiHpi_BeginProcessing( stream, &framesGot, &cbFlags ) ); - - if( framesGot > 0 ) - { - framesGot = PaUtil_CopyOutput( &stream->bufferProcessor, &userBuffer, framesGot ); - PA_ENSURE_( PaAsiHpi_EndProcessing( stream, framesGot, &cbFlags ) ); - /* Advance frame counter */ - frames -= framesGot; - } - } - -error: - return result; -} - - -/** Number of frames that can be read from input stream without blocking. - - @param s Pointer to PortAudio stream - - @return Number of frames, or PortAudio error code - */ -static signed long GetStreamReadAvailable( PaStream *s ) -{ - PaError result = paNoError; - PaAsiHpiStream *stream = (PaAsiHpiStream*)s; - PaAsiHpiStreamInfo info; - - assert( stream ); - PA_UNLESS_( stream->input, paCanNotReadFromAnOutputOnlyStream ); - - PA_ENSURE_( PaAsiHpi_GetStreamInfo( stream->input, &info ) ); - /* Round down to the nearest host buffer multiple */ - result = (info.availableFrames / stream->maxFramesPerHostBuffer) * stream->maxFramesPerHostBuffer; - if( info.overflow ) - { - result = paInputOverflowed; - } - -error: - return result; -} - - -/** Number of frames that can be written to output stream without blocking. - - @param s Pointer to PortAudio stream - - @return Number of frames, or PortAudio error code - */ -static signed long GetStreamWriteAvailable( PaStream *s ) -{ - PaError result = paNoError; - PaAsiHpiStream *stream = (PaAsiHpiStream*)s; - PaAsiHpiStreamInfo info; - - assert( stream ); - PA_UNLESS_( stream->output, paCanNotWriteToAnInputOnlyStream ); - - PA_ENSURE_( PaAsiHpi_GetStreamInfo( stream->output, &info ) ); - /* Round down to the nearest host buffer multiple */ - result = (info.availableFrames / stream->maxFramesPerHostBuffer) * stream->maxFramesPerHostBuffer; - if( info.underflow ) - { - result = paOutputUnderflowed; - } - -error: - return result; -} +/* + * PortAudio Portable Real-Time Audio Library + * Latest Version at: http://www.portaudio.com + * + * PortAudio v18 version of AudioScience HPI driver by Fred Gleason + * PortAudio v19 version of AudioScience HPI driver by Ludwig Schwardt + * + * Copyright (c) 2003 Fred Gleason + * Copyright (c) 2005,2006 Ludwig Schwardt + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* + * The text above constitutes the entire PortAudio license; however, + * the PortAudio community also makes the following non-binding requests: + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. It is also + * requested that these non-binding requests be included along with the + * license above. + */ + +/* + * Modification History + * 12/2003 - Initial version + * 09/2005 - v19 version [rewrite] + */ + +/** @file + @ingroup hostapi_src + @brief Host API implementation supporting AudioScience cards + via the Linux HPI interface. + +

Overview

+ + This is a PortAudio implementation for the AudioScience HPI Audio API + on the Linux platform. AudioScience makes a range of audio adapters customised + for the broadcasting industry, with support for both Windows and Linux. + More information on their products can be found on their website: + + http://www.audioscience.com + + Documentation for the HPI API can be found at: + + http://www.audioscience.com/internet/download/sdk/spchpi.pdf + + The Linux HPI driver itself (a kernel module + library) can be downloaded from: + + http://www.audioscience.com/internet/download/linux_drivers.htm + +

Implementation strategy

+ + *Note* Ideally, AudioScience cards should be handled by the PortAudio ALSA + implementation on Linux, as ALSA is the preferred Linux soundcard API. The existence + of this host API implementation might therefore seem a bit flawed. Unfortunately, at + the time of the creation of this implementation (June 2006), the PA ALSA implementation + could not make use of the existing AudioScience ALSA driver. PA ALSA uses the + "memory-mapped" (mmap) ALSA access mode to interact with the ALSA library, while the + AudioScience ALSA driver only supports the "read-write" access mode. The appropriate + solution to this problem is to add "read-write" support to PortAudio ALSA, thereby + extending the range of soundcards it supports (AudioScience cards are not the only + ones with this problem). Given the author's limited knowledge of ALSA and the + simplicity of the HPI API, the second-best solution was born... + + The following mapping between HPI and PA was followed: + HPI subsystem => PortAudio host API + HPI adapter => nothing specific + HPI stream => PortAudio device + + Each HPI stream is either input or output (not both), and can support + different channel counts, sampling rates and sample formats. It is therefore + a more natural fit to a PA device. A PA stream can therefore combine two + HPI streams (one input and one output) into a "full-duplex" stream. These + HPI streams can even be on different physical adapters. The two streams ought to be + sample-synchronised when they reside on the same adapter, as most AudioScience adapters + derive their ADC and DAC clocks from one master clock. When combining two adapters + into one full-duplex stream, however, the use of a word clock connection between the + adapters is strongly recommended. + + The HPI interface is inherently blocking, making use of read and write calls to + transfer data between user buffers and driver buffers. The callback interface therefore + requires a helper thread ("callback engine") which periodically transfers data (one thread + per PA stream, in fact). The current implementation explicitly sleeps via Pa_Sleep() until + enough samples can be transferred (select() or poll() would be better, but currently seems + impossible...). The thread implementation makes use of the Unix thread helper functions + and some pthread calls here and there. If a unified PA thread exists, this host API + implementation might also compile on Windows, as this is the only real Linux-specific + part of the code. + + There is no inherent fixed buffer size in the HPI interface, as in some other host APIs. + The PortAudio implementation contains a buffer that is allocated during OpenStream and + used to transfer data between the callback and the HPI driver buffer. The size of this + buffer is quite flexible and is derived from latency suggestions and matched to the + requested callback buffer size as far as possible. It can become quite huge, as the + AudioScience cards are typically geared towards higher-latency applications and contain + large hardware buffers. + + The HPI interface natively supports most common sample formats and sample rates (some + conversion is done on the adapter itself). + + Stream time is measured based on the number of processed frames, which is adjusted by the + number of frames currently buffered by the HPI driver. + + There is basic support for detecting overflow and underflow. The HPI interface does not + explicitly indicate this, so thresholds on buffer levels are used in combination with + stream state. Recovery from overflow and underflow is left to the PA client. + + Blocking streams are also implemented. It makes use of the same polling routines that + the callback interface uses, in order to prevent the allocation of variable-sized + buffers during reading and writing. The framesPerBuffer parameter is therefore still + relevant, and this can be increased in the blocking case to improve efficiency. + + The implementation contains extensive reporting macros (slightly modified PA_ENSURE and + PA_UNLESS versions) and a useful stream dump routine to provide debugging feedback. + + Output buffer priming via the user callback (i.e. paPrimeOutputBuffersUsingStreamCallback + and friends) is not implemented yet. All output is primed with silence. + + Please send bug reports etc. to Ludwig Schwardt + */ + +#include +#include +#include +#include /* strlen() */ +#include /* pthreads and friends */ +#include /* assert */ +#include /* ceil, floor */ + +#include /* HPI API */ + +#include "portaudio.h" /* PortAudio API */ +#include "pa_util.h" /* PA_DEBUG, other small utilities */ +#include "pa_unix_util.h" /* Unix threading utilities */ +#include "pa_allocation.h" /* Group memory allocation */ +#include "pa_hostapi.h" /* Host API structs */ +#include "pa_stream.h" /* Stream interface structs */ +#include "pa_cpuload.h" /* CPU load measurer */ +#include "pa_process.h" /* Buffer processor */ +#include "pa_converters.h" /* PaUtilZeroer */ +#include "pa_debugprint.h" + +/* -------------------------------------------------------------------------- */ + +/* + * Defines + */ + +/* Error reporting and assertions */ + +/** Evaluate expression, and return on any PortAudio errors */ +#define PA_ENSURE_(expr) \ + do { \ + PaError paError = (expr); \ + if( UNLIKELY( paError < paNoError ) ) \ + { \ + PA_DEBUG(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \ + result = paError; \ + goto error; \ + } \ + } while (0); + +/** Assert expression, else return the provided PaError */ +#define PA_UNLESS_(expr, paError) \ + do { \ + if( UNLIKELY( (expr) == 0 ) ) \ + { \ + PA_DEBUG(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \ + result = (paError); \ + goto error; \ + } \ + } while( 0 ); + +/** Check return value of HPI function, and map it to PaError */ +#define PA_ASIHPI_UNLESS_(expr, paError) \ + do { \ + HW16 hpiError = (expr); \ + /* If HPI error occurred */ \ + if( UNLIKELY( hpiError ) ) \ + { \ + char szError[256]; \ + HPI_GetErrorText( hpiError, szError ); \ + PA_DEBUG(( "HPI error %d occurred: %s\n", hpiError, szError )); \ + /* This message will always be displayed, even if debug info is disabled */ \ + PA_DEBUG(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \ + if( (paError) == paUnanticipatedHostError ) \ + { \ + PA_DEBUG(( "Host error description: %s\n", szError )); \ + /* PaUtil_SetLastHostErrorInfo should only be used in the main thread */ \ + if( pthread_equal( pthread_self(), paUnixMainThread ) ) \ + { \ + PaUtil_SetLastHostErrorInfo( paInDevelopment, hpiError, szError ); \ + } \ + } \ + /* If paNoError is specified, continue as usual */ \ + /* (useful if you only want to print out the debug messages above) */ \ + if( (paError) < 0 ) \ + { \ + result = (paError); \ + goto error; \ + } \ + } \ + } while( 0 ); + +/** Report HPI error code and text */ +#define PA_ASIHPI_REPORT_ERROR_(hpiErrorCode) \ + do { \ + char szError[256]; \ + HPI_GetErrorText( hpiError, szError ); \ + PA_DEBUG(( "HPI error %d occurred: %s\n", hpiError, szError )); \ + /* PaUtil_SetLastHostErrorInfo should only be used in the main thread */ \ + if( pthread_equal( pthread_self(), paUnixMainThread ) ) \ + { \ + PaUtil_SetLastHostErrorInfo( paInDevelopment, (hpiErrorCode), szError ); \ + } \ + } while( 0 ); + +/* Defaults */ + +/** Sample formats available natively on AudioScience hardware */ +#define PA_ASIHPI_AVAILABLE_FORMATS_ (paFloat32 | paInt32 | paInt24 | paInt16 | paUInt8) +/** Enable background bus mastering (BBM) for buffer transfers, if available (see HPI docs) */ +#define PA_ASIHPI_USE_BBM_ 1 +/** Minimum number of frames in HPI buffer (for either data or available space). + If buffer contains less data/space, it indicates xrun or completion. */ +#define PA_ASIHPI_MIN_FRAMES_ 1152 +/** Minimum polling interval in milliseconds, which determines minimum host buffer size */ +#define PA_ASIHPI_MIN_POLLING_INTERVAL_ 10 + +/* -------------------------------------------------------------------------- */ + +/* + * Structures + */ + +/** Host API global data */ +typedef struct PaAsiHpiHostApiRepresentation +{ + /* PortAudio "base class" - keep the baseRep first! (C-style inheritance) */ + PaUtilHostApiRepresentation baseHostApiRep; + PaUtilStreamInterface callbackStreamInterface; + PaUtilStreamInterface blockingStreamInterface; + + PaUtilAllocationGroup *allocations; + + /* implementation specific data goes here */ + + PaHostApiIndex hostApiIndex; + /** HPI subsystem pointer */ + HPI_HSUBSYS *subSys; +} +PaAsiHpiHostApiRepresentation; + + +/** Device data */ +typedef struct PaAsiHpiDeviceInfo +{ + /* PortAudio "base class" - keep the baseRep first! (C-style inheritance) */ + /** Common PortAudio device information */ + PaDeviceInfo baseDeviceInfo; + + /* implementation specific data goes here */ + + /** HPI subsystem (required for most HPI calls) */ + HPI_HSUBSYS *subSys; + /** Adapter index */ + HW16 adapterIndex; + /** Adapter model number (hex) */ + HW16 adapterType; + /** Adapter HW/SW version */ + HW16 adapterVersion; + /** Adapter serial number */ + HW32 adapterSerialNumber; + /** Stream number */ + HW16 streamIndex; + /** 0=Input, 1=Output (HPI streams are either input or output but not both) */ + HW16 streamIsOutput; +} +PaAsiHpiDeviceInfo; + + +/** Stream state as defined by PortAudio. + It seems that the host API implementation has to keep track of the PortAudio stream state. + Please note that this is NOT the same as the state of the underlying HPI stream. By separating + these two concepts, a lot of flexibility is gained. There is a rough match between the two, + of course, but forcing a precise match is difficult. For example, HPI_STATE_DRAINED can occur + during the Active state of PortAudio (due to underruns) and also during CallBackFinished in + the case of an output stream. Similarly, HPI_STATE_STOPPED mostly coincides with the Stopped + PortAudio state, by may also occur in the CallbackFinished state when recording is finished. + + Here is a rough match-up: + + PortAudio state => HPI state + --------------- --------- + Active => HPI_STATE_RECORDING, HPI_STATE_PLAYING, (HPI_STATE_DRAINED) + Stopped => HPI_STATE_STOPPED + CallbackFinished => HPI_STATE_STOPPED, HPI_STATE_DRAINED */ +typedef enum PaAsiHpiStreamState +{ + paAsiHpiStoppedState=0, + paAsiHpiActiveState=1, + paAsiHpiCallbackFinishedState=2 +} +PaAsiHpiStreamState; + + +/** Stream component data (associated with one direction, i.e. either input or output) */ +typedef struct PaAsiHpiStreamComponent +{ + /** Device information (HPI handles, etc) */ + PaAsiHpiDeviceInfo *hpiDevice; + /** Stream handle, as passed to HPI interface. + HACK: we assume types HPI_HISTREAM and HPI_HOSTREAM are the same... + (both are HW32 up to version 3.00 of ASIHPI, and hopefully they stay that way) */ + HPI_HISTREAM hpiStream; + /** Stream format, as passed to HPI interface */ + HPI_FORMAT hpiFormat; + /** Number of bytes per frame, derived from hpiFormat and saved for convenience */ + HW32 bytesPerFrame; + /** Size of hardware (on-card) buffer of stream in bytes */ + HW32 hardwareBufferSize; + /** Size of host (BBM) buffer of stream in bytes (if used) */ + HW32 hostBufferSize; + /** Upper limit on the utilization of output stream buffer (both hardware and host). + This prevents large latencies in an output-only stream with a potentially huge buffer + and a fast data generator, which would otherwise keep the hardware buffer filled to + capacity. See also the "Hardware Buffering=off" option in the AudioScience WAV driver. */ + HW32 outputBufferCap; + /** Sample buffer (halfway station between HPI and buffer processor) */ + HW8 *tempBuffer; + /** Sample buffer size, in bytes */ + HW32 tempBufferSize; +} +PaAsiHpiStreamComponent; + + +/** Stream data */ +typedef struct PaAsiHpiStream +{ + /* PortAudio "base class" - keep the baseRep first! (C-style inheritance) */ + PaUtilStreamRepresentation baseStreamRep; + PaUtilCpuLoadMeasurer cpuLoadMeasurer; + PaUtilBufferProcessor bufferProcessor; + + PaUtilAllocationGroup *allocations; + + /* implementation specific data goes here */ + + /** Separate structs for input and output sides of stream */ + PaAsiHpiStreamComponent *input, *output; + + /** Polling interval (in milliseconds) */ + HW32 pollingInterval; + /** Are we running in callback mode? */ + int callbackMode; + /** Number of frames to transfer at a time to/from HPI */ + unsigned long maxFramesPerHostBuffer; + /** Indicates that the stream is in the paNeverDropInput mode */ + int neverDropInput; + /** Contains copy of user buffers, used by blocking interface to transfer non-interleaved data. + It went here instead of to each stream component, as the stream component buffer setup in + PaAsiHpi_SetupBuffers doesn't know the stream details such as callbackMode. + (Maybe a problem later if ReadStream and WriteStream happens concurrently on same stream.) */ + void **blockingUserBufferCopy; + + /* Thread-related variables */ + + /** Helper thread which will deliver data to user callback */ + PaUnixThread thread; + /** PortAudio stream state (Active/Stopped/CallbackFinished) */ + volatile sig_atomic_t state; + /** Hard abort, i.e. drop frames? */ + volatile sig_atomic_t callbackAbort; + /** True if stream stopped via exiting callback with paComplete/paAbort flag + (as opposed to explicit call to StopStream/AbortStream) */ + volatile sig_atomic_t callbackFinished; +} +PaAsiHpiStream; + + +/** Stream state information, collected together for convenience */ +typedef struct PaAsiHpiStreamInfo +{ + /** HPI stream state (HPI_STATE_STOPPED, HPI_STATE_PLAYING, etc.) */ + HW16 state; + /** Size (in bytes) of recording/playback data buffer in HPI driver */ + HW32 bufferSize; + /** Amount of data (in bytes) available in the buffer */ + HW32 dataSize; + /** Number of frames played/recorded since last stream reset */ + HW32 frameCounter; + /** Amount of data (in bytes) in hardware (on-card) buffer. + This differs from dataSize if bus mastering (BBM) is used, which introduces another + driver-level buffer to which dataSize/bufferSize then refers. */ + HW32 auxDataSize; + /** Total number of data frames currently buffered by HPI driver (host + hw buffers) */ + HW32 totalBufferedData; + /** Size of immediately available data (for input) or space (for output) in frames. + This only checks the first-level buffer (typically host buffer). This amount can be + transferred immediately. */ + HW32 availableFrames; + /** Indicates that hardware buffer is getting too full */ + int overflow; + /** Indicates that hardware buffer is getting too empty */ + int underflow; +} +PaAsiHpiStreamInfo; + +/* -------------------------------------------------------------------------- */ + +/* + * Function prototypes + */ + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + + /* The only exposed function in the entire host API implementation */ + PaError PaAsiHpi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +static void Terminate( struct PaUtilHostApiRepresentation *hostApi ); +static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate ); + +/* Stream prototypes */ +static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, + PaStream **s, + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate, + unsigned long framesPerBuffer, + PaStreamFlags streamFlags, + PaStreamCallback *streamCallback, + void *userData ); +static PaError CloseStream( PaStream *s ); +static PaError StartStream( PaStream *s ); +static PaError StopStream( PaStream *s ); +static PaError AbortStream( PaStream *s ); +static PaError IsStreamStopped( PaStream *s ); +static PaError IsStreamActive( PaStream *s ); +static PaTime GetStreamTime( PaStream *s ); +static double GetStreamCpuLoad( PaStream *s ); + +/* Blocking prototypes */ +static PaError ReadStream( PaStream *s, void *buffer, unsigned long frames ); +static PaError WriteStream( PaStream *s, const void *buffer, unsigned long frames ); +static signed long GetStreamReadAvailable( PaStream *s ); +static signed long GetStreamWriteAvailable( PaStream *s ); + +/* Callback prototypes */ +static void *CallbackThreadFunc( void *userData ); + +/* Functions specific to this API */ +static PaError PaAsiHpi_BuildDeviceList( PaAsiHpiHostApiRepresentation *hpiHostApi ); +static HW16 PaAsiHpi_PaToHpiFormat( PaSampleFormat paFormat ); +static PaSampleFormat PaAsiHpi_HpiToPaFormat( HW16 hpiFormat ); +static PaError PaAsiHpi_CreateFormat( struct PaUtilHostApiRepresentation *hostApi, + const PaStreamParameters *parameters, double sampleRate, + PaAsiHpiDeviceInfo **hpiDevice, HPI_FORMAT *hpiFormat ); +static PaError PaAsiHpi_OpenInput( struct PaUtilHostApiRepresentation *hostApi, + const PaAsiHpiDeviceInfo *hpiDevice, const HPI_FORMAT *hpiFormat, + HPI_HISTREAM *hpiStream ); +static PaError PaAsiHpi_OpenOutput( struct PaUtilHostApiRepresentation *hostApi, + const PaAsiHpiDeviceInfo *hpiDevice, const HPI_FORMAT *hpiFormat, + HPI_HOSTREAM *hpiStream ); +static PaError PaAsiHpi_GetStreamInfo( PaAsiHpiStreamComponent *streamComp, PaAsiHpiStreamInfo *info ); +static void PaAsiHpi_StreamComponentDump( PaAsiHpiStreamComponent *streamComp, PaAsiHpiStream *stream ); +static void PaAsiHpi_StreamDump( PaAsiHpiStream *stream ); +static PaError PaAsiHpi_SetupBuffers( PaAsiHpiStreamComponent *streamComp, HW32 pollingInterval, + unsigned long framesPerPaHostBuffer, PaTime suggestedLatency ); +static PaError PaAsiHpi_PrimeOutputWithSilence( PaAsiHpiStream *stream ); +static PaError PaAsiHpi_StartStream( PaAsiHpiStream *stream, int outputPrimed ); +static PaError PaAsiHpi_StopStream( PaAsiHpiStream *stream, int abort ); +static PaError PaAsiHpi_ExplicitStop( PaAsiHpiStream *stream, int abort ); +static void PaAsiHpi_OnThreadExit( void *userData ); +static PaError PaAsiHpi_WaitForFrames( PaAsiHpiStream *stream, unsigned long *framesAvail, + PaStreamCallbackFlags *cbFlags ); +static void PaAsiHpi_CalculateTimeInfo( PaAsiHpiStream *stream, PaStreamCallbackTimeInfo *timeInfo ); +static PaError PaAsiHpi_BeginProcessing( PaAsiHpiStream* stream, unsigned long* numFrames, + PaStreamCallbackFlags *cbFlags ); +static PaError PaAsiHpi_EndProcessing( PaAsiHpiStream *stream, unsigned long numFrames, + PaStreamCallbackFlags *cbFlags ); + +/* ========================================================================== + * ============================= IMPLEMENTATION ============================= + * ========================================================================== */ + +/* --------------------------- Host API Interface --------------------------- */ + +/** Enumerate all PA devices (= HPI streams). + This compiles a list of all HPI adapters, and registers a PA device for each input and + output stream it finds. Most errors are ignored, as missing or erroneous devices are + simply skipped. + + @param hpiHostApi Pointer to HPI host API struct + + @return PortAudio error code (only paInsufficientMemory in practice) + */ +static PaError PaAsiHpi_BuildDeviceList( PaAsiHpiHostApiRepresentation *hpiHostApi ) +{ + PaError result = paNoError; + PaUtilHostApiRepresentation *hostApi = &hpiHostApi->baseHostApiRep; + PaHostApiInfo *baseApiInfo = &hostApi->info; + PaAsiHpiDeviceInfo *hpiDeviceList; + HW16 adapterList[ HPI_MAX_ADAPTERS ]; + HW16 numAdapters; + HW16 hpiError = 0; + int i, j, deviceCount = 0, deviceIndex = 0; + + assert( hpiHostApi ); + assert( hpiHostApi->subSys ); + + /* Look for adapters (not strictly necessary, as AdapterOpen can do the same, but this */ + /* way we have less errors since we do not try to open adapters we know aren't there) */ + /* Errors not considered critical here (subsystem may report 0 devices), but report them */ + /* in debug mode. */ + PA_ASIHPI_UNLESS_( HPI_SubSysFindAdapters( hpiHostApi->subSys, &numAdapters, + adapterList, HPI_MAX_ADAPTERS ), paNoError ); + + /* First open and count the number of devices (= number of streams), to ease memory allocation */ + for( i=0; i < HPI_MAX_ADAPTERS; ++i ) + { + HW16 inStreams, outStreams; + HW16 version; + HW32 serial; + HW16 type; + + /* If no adapter found at this index, skip it */ + if( adapterList[i] == 0 ) + continue; + + /* Try to open adapter */ + hpiError = HPI_AdapterOpen( hpiHostApi->subSys, i ); + /* Report error and skip to next device on failure */ + if( hpiError ) + { + PA_ASIHPI_REPORT_ERROR_( hpiError ); + continue; + } + hpiError = HPI_AdapterGetInfo( hpiHostApi->subSys, i, + &outStreams, &inStreams, &version, &serial, &type ); + /* Skip to next device on failure */ + if( hpiError ) + { + PA_ASIHPI_REPORT_ERROR_( hpiError ); + continue; + } + else + { + /* Assign default devices if available and increment device count */ + if( (baseApiInfo->defaultInputDevice == paNoDevice) && (inStreams > 0) ) + baseApiInfo->defaultInputDevice = deviceCount; + deviceCount += inStreams; + if( (baseApiInfo->defaultOutputDevice == paNoDevice) && (outStreams > 0) ) + baseApiInfo->defaultOutputDevice = deviceCount; + deviceCount += outStreams; + } + } + + /* Register any discovered devices */ + if( deviceCount > 0 ) + { + /* Memory allocation */ + PA_UNLESS_( hostApi->deviceInfos = (PaDeviceInfo**) PaUtil_GroupAllocateMemory( + hpiHostApi->allocations, sizeof(PaDeviceInfo*) * deviceCount ), + paInsufficientMemory ); + /* Allocate all device info structs in a contiguous block */ + PA_UNLESS_( hpiDeviceList = (PaAsiHpiDeviceInfo*) PaUtil_GroupAllocateMemory( + hpiHostApi->allocations, sizeof(PaAsiHpiDeviceInfo) * deviceCount ), + paInsufficientMemory ); + + /* Now query devices again for information */ + for( i=0; i < HPI_MAX_ADAPTERS; ++i ) + { + HW16 inStreams, outStreams; + HW16 version; + HW32 serial; + HW16 type; + + /* If no adapter found at this index, skip it */ + if( adapterList[i] == 0 ) + continue; + + /* Assume adapter is still open from previous round */ + hpiError = HPI_AdapterGetInfo( hpiHostApi->subSys, i, + &outStreams, &inStreams, &version, &serial, &type ); + /* Report error and skip to next device on failure */ + if( hpiError ) + { + PA_ASIHPI_REPORT_ERROR_( hpiError ); + continue; + } + else + { + PA_DEBUG(( "Found HPI Adapter ID=%4X Idx=%d #In=%d #Out=%d S/N=%d HWver=%c%d DSPver=%03d\n", + type, i, inStreams, outStreams, serial, + ((version>>3)&0xf)+'A', /* Hw version major */ + version&0x7, /* Hw version minor */ + ((version>>13)*100)+((version>>7)&0x3f) /* DSP code version */ + )); + } + + /* First add all input streams as devices */ + for( j=0; j < inStreams; ++j ) + { + PaAsiHpiDeviceInfo *hpiDevice = &hpiDeviceList[deviceIndex]; + PaDeviceInfo *baseDeviceInfo = &hpiDevice->baseDeviceInfo; + char srcName[72]; + char *deviceName; + + memset( hpiDevice, 0, sizeof(PaAsiHpiDeviceInfo) ); + /* Set implementation-specific device details */ + hpiDevice->subSys = hpiHostApi->subSys; + hpiDevice->adapterIndex = i; + hpiDevice->adapterType = type; + hpiDevice->adapterVersion = version; + hpiDevice->adapterSerialNumber = serial; + hpiDevice->streamIndex = j; + hpiDevice->streamIsOutput = 0; + /* Set common PortAudio device stats */ + baseDeviceInfo->structVersion = 2; + /* Make sure name string is owned by API info structure */ + sprintf( srcName, + "Adapter %d (%4X) - Input Stream %d", i+1, type, j+1 ); + PA_UNLESS_( deviceName = (char *) PaUtil_GroupAllocateMemory( + hpiHostApi->allocations, strlen(srcName) + 1 ), paInsufficientMemory ); + strcpy( deviceName, srcName ); + baseDeviceInfo->name = deviceName; + baseDeviceInfo->hostApi = hpiHostApi->hostApiIndex; + baseDeviceInfo->maxInputChannels = HPI_MAX_CHANNELS; + baseDeviceInfo->maxOutputChannels = 0; + /* Default latency values for interactive performance */ + baseDeviceInfo->defaultLowInputLatency = 0.01; + baseDeviceInfo->defaultLowOutputLatency = -1.0; + /* Default latency values for robust non-interactive applications (eg. playing sound files) */ + baseDeviceInfo->defaultHighInputLatency = 0.2; + baseDeviceInfo->defaultHighOutputLatency = -1.0; + /* HPI interface can actually handle any sampling rate to 1 Hz accuracy, + * so this default is as good as any */ + baseDeviceInfo->defaultSampleRate = 44100; + + /* Store device in global PortAudio list */ + hostApi->deviceInfos[deviceIndex++] = (PaDeviceInfo *) hpiDevice; + } + + /* Now add all output streams as devices (I know, the repetition is painful) */ + for( j=0; j < outStreams; ++j ) + { + PaAsiHpiDeviceInfo *hpiDevice = &hpiDeviceList[deviceIndex]; + PaDeviceInfo *baseDeviceInfo = &hpiDevice->baseDeviceInfo; + char srcName[72]; + char *deviceName; + + memset( hpiDevice, 0, sizeof(PaAsiHpiDeviceInfo) ); + /* Set implementation-specific device details */ + hpiDevice->subSys = hpiHostApi->subSys; + hpiDevice->adapterIndex = i; + hpiDevice->adapterType = type; + hpiDevice->adapterVersion = version; + hpiDevice->adapterSerialNumber = serial; + hpiDevice->streamIndex = j; + hpiDevice->streamIsOutput = 1; + /* Set common PortAudio device stats */ + baseDeviceInfo->structVersion = 2; + /* Make sure name string is owned by API info structure */ + sprintf( srcName, + "Adapter %d (%4X) - Output Stream %d", i+1, type, j+1 ); + PA_UNLESS_( deviceName = (char *) PaUtil_GroupAllocateMemory( + hpiHostApi->allocations, strlen(srcName) + 1 ), paInsufficientMemory ); + strcpy( deviceName, srcName ); + baseDeviceInfo->name = deviceName; + baseDeviceInfo->hostApi = hpiHostApi->hostApiIndex; + baseDeviceInfo->maxInputChannels = 0; + baseDeviceInfo->maxOutputChannels = HPI_MAX_CHANNELS; + /* Default latency values for interactive performance. */ + baseDeviceInfo->defaultLowInputLatency = -1.0; + baseDeviceInfo->defaultLowOutputLatency = 0.01; + /* Default latency values for robust non-interactive applications (eg. playing sound files). */ + baseDeviceInfo->defaultHighInputLatency = -1.0; + baseDeviceInfo->defaultHighOutputLatency = 0.2; + /* HPI interface can actually handle any sampling rate to 1 Hz accuracy, + * so this default is as good as any */ + baseDeviceInfo->defaultSampleRate = 44100; + + /* Store device in global PortAudio list */ + hostApi->deviceInfos[deviceIndex++] = (PaDeviceInfo *) hpiDevice; + } + } + } + + /* Finally acknowledge checked devices */ + baseApiInfo->deviceCount = deviceIndex; + +error: + return result; +} + + +/** Initialize host API implementation. + This is the only function exported beyond this file. It is called by PortAudio to initialize + the host API. It stores API info, finds and registers all devices, and sets up callback and + blocking interfaces. + + @param hostApi Pointer to host API struct + + @param hostApiIndex Index of current (HPI) host API + + @return PortAudio error code + */ +PaError PaAsiHpi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex ) +{ + PaError result = paNoError; + PaAsiHpiHostApiRepresentation *hpiHostApi = NULL; + PaHostApiInfo *baseApiInfo; + + /* Allocate host API structure */ + PA_UNLESS_( hpiHostApi = (PaAsiHpiHostApiRepresentation*) PaUtil_AllocateMemory( + sizeof(PaAsiHpiHostApiRepresentation) ), paInsufficientMemory ); + PA_UNLESS_( hpiHostApi->allocations = PaUtil_CreateAllocationGroup(), paInsufficientMemory ); + + hpiHostApi->hostApiIndex = hostApiIndex; + hpiHostApi->subSys = NULL; + + /* Try to initialize HPI subsystem */ + if( ( hpiHostApi->subSys = HPI_SubSysCreate() ) == NULL) + { + /* the V19 development docs say that if an implementation + * detects that it cannot be used, it should return a NULL + * interface and paNoError */ + PA_DEBUG(( "Could not open HPI interface\n" )); + result = paNoError; + *hostApi = NULL; + goto error; + } + else + { + HW32 hpiVersion; + PA_ASIHPI_UNLESS_( HPI_SubSysGetVersion( hpiHostApi->subSys, &hpiVersion ), paUnanticipatedHostError ); + PA_DEBUG(( "HPI interface v%d.%02d\n", + hpiVersion >> 8, 10*((hpiVersion & 0xF0) >> 4) + (hpiVersion & 0x0F) )); + } + + *hostApi = &hpiHostApi->baseHostApiRep; + baseApiInfo = &((*hostApi)->info); + /* Fill in common API details */ + baseApiInfo->structVersion = 1; + baseApiInfo->type = paAudioScienceHPI; + baseApiInfo->name = "AudioScience HPI"; + baseApiInfo->deviceCount = 0; + baseApiInfo->defaultInputDevice = paNoDevice; + baseApiInfo->defaultOutputDevice = paNoDevice; + + PA_ENSURE_( PaAsiHpi_BuildDeviceList( hpiHostApi ) ); + + (*hostApi)->Terminate = Terminate; + (*hostApi)->OpenStream = OpenStream; + (*hostApi)->IsFormatSupported = IsFormatSupported; + + PaUtil_InitializeStreamInterface( &hpiHostApi->callbackStreamInterface, CloseStream, StartStream, + StopStream, AbortStream, IsStreamStopped, IsStreamActive, + GetStreamTime, GetStreamCpuLoad, + PaUtil_DummyRead, PaUtil_DummyWrite, + PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable ); + + PaUtil_InitializeStreamInterface( &hpiHostApi->blockingStreamInterface, CloseStream, StartStream, + StopStream, AbortStream, IsStreamStopped, IsStreamActive, + GetStreamTime, PaUtil_DummyGetCpuLoad, + ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable ); + + /* Store identity of main thread */ + PA_ENSURE_( PaUnixThreading_Initialize() ); + + return result; +error: + /* Clean up memory */ + Terminate( (PaUtilHostApiRepresentation *)hpiHostApi ); + return result; +} + + +/** Terminate host API implementation. + This closes all HPI adapters and frees the HPI subsystem. It also frees the host API struct + memory. It should be called once for every PaAsiHpi_Initialize call. + + @param Pointer to host API struct + */ +static void Terminate( struct PaUtilHostApiRepresentation *hostApi ) +{ + PaAsiHpiHostApiRepresentation *hpiHostApi = (PaAsiHpiHostApiRepresentation*)hostApi; + int i; + PaError result = paNoError; + + if( hpiHostApi ) + { + /* Get rid of HPI-specific structures */ + if( hpiHostApi->subSys ) + { + HW16 lastAdapterIndex = HPI_MAX_ADAPTERS; + /* Iterate through device list and close adapters */ + for( i=0; i < hostApi->info.deviceCount; ++i ) + { + PaAsiHpiDeviceInfo *hpiDevice = (PaAsiHpiDeviceInfo *) hostApi->deviceInfos[ i ]; + /* Close adapter only if it differs from previous one */ + if( hpiDevice->adapterIndex != lastAdapterIndex ) + { + /* Ignore errors (report only during debugging) */ + PA_ASIHPI_UNLESS_( HPI_AdapterClose( hpiHostApi->subSys, + hpiDevice->adapterIndex ), paNoError ); + lastAdapterIndex = hpiDevice->adapterIndex; + } + } + /* Finally dismantle HPI subsystem */ + HPI_SubSysFree( hpiHostApi->subSys ); + } + + if( hpiHostApi->allocations ) + { + PaUtil_FreeAllAllocations( hpiHostApi->allocations ); + PaUtil_DestroyAllocationGroup( hpiHostApi->allocations ); + } + + PaUtil_FreeMemory( hpiHostApi ); + } +error: + return; +} + + +/** Converts PortAudio sample format to equivalent HPI format. + + @param paFormat PortAudio sample format + + @return HPI sample format + */ +static HW16 PaAsiHpi_PaToHpiFormat( PaSampleFormat paFormat ) +{ + /* Ignore interleaving flag */ + switch( paFormat & ~paNonInterleaved ) + { + case paFloat32: + return HPI_FORMAT_PCM32_FLOAT; + + case paInt32: + return HPI_FORMAT_PCM32_SIGNED; + + case paInt24: + return HPI_FORMAT_PCM24_SIGNED; + + case paInt16: + return HPI_FORMAT_PCM16_SIGNED; + + case paUInt8: + return HPI_FORMAT_PCM8_UNSIGNED; + + /* Default is 16-bit signed */ + case paInt8: + default: + return HPI_FORMAT_PCM16_SIGNED; + } +} + + +/** Converts HPI sample format to equivalent PortAudio format. + + @param paFormat HPI sample format + + @return PortAudio sample format + */ +static PaSampleFormat PaAsiHpi_HpiToPaFormat( HW16 hpiFormat ) +{ + switch( hpiFormat ) + { + case HPI_FORMAT_PCM32_FLOAT: + return paFloat32; + + case HPI_FORMAT_PCM32_SIGNED: + return paInt32; + + case HPI_FORMAT_PCM24_SIGNED: + return paInt24; + + case HPI_FORMAT_PCM16_SIGNED: + return paInt16; + + case HPI_FORMAT_PCM8_UNSIGNED: + return paUInt8; + + /* Default is custom format (e.g. for HPI MP3 format) */ + default: + return paCustomFormat; + } +} + + +/** Creates HPI format struct based on PortAudio parameters. + This also does some checks to see whether the desired format is valid, and whether + the device allows it. This only checks the format of one half (input or output) of the + PortAudio stream. + + @param hostApi Pointer to host API struct + + @param parameters Pointer to stream parameter struct + + @param sampleRate Desired sample rate + + @param hpiDevice Pointer to HPI device struct + + @param hpiFormat Resulting HPI format returned here + + @return PortAudio error code (typically indicating a problem with stream format) + */ +static PaError PaAsiHpi_CreateFormat( struct PaUtilHostApiRepresentation *hostApi, + const PaStreamParameters *parameters, double sampleRate, + PaAsiHpiDeviceInfo **hpiDevice, HPI_FORMAT *hpiFormat ) +{ + int maxChannelCount = 0; + PaSampleFormat hostSampleFormat = 0; + HW16 hpiError = 0; + + /* Unless alternate device specification is supported, reject the use of + paUseHostApiSpecificDeviceSpecification */ + if( parameters->device == paUseHostApiSpecificDeviceSpecification ) + return paInvalidDevice; + else + { + assert( parameters->device < hostApi->info.deviceCount ); + *hpiDevice = (PaAsiHpiDeviceInfo*) hostApi->deviceInfos[ parameters->device ]; + } + + /* Validate streamInfo - this implementation doesn't use custom stream info */ + if( parameters->hostApiSpecificStreamInfo ) + return paIncompatibleHostApiSpecificStreamInfo; + + /* Check that device can support channel count */ + if( (*hpiDevice)->streamIsOutput ) + { + maxChannelCount = (*hpiDevice)->baseDeviceInfo.maxOutputChannels; + } + else + { + maxChannelCount = (*hpiDevice)->baseDeviceInfo.maxInputChannels; + } + if( (maxChannelCount == 0) || (parameters->channelCount > maxChannelCount) ) + return paInvalidChannelCount; + + /* All standard sample formats are supported by the buffer adapter, + and this implementation doesn't support any custom sample formats */ + if( parameters->sampleFormat & paCustomFormat ) + return paSampleFormatNotSupported; + + /* Switch to closest HPI native format */ + hostSampleFormat = PaUtil_SelectClosestAvailableFormat(PA_ASIHPI_AVAILABLE_FORMATS_, + parameters->sampleFormat ); + /* Setup format + info objects */ + hpiError = HPI_FormatCreate( hpiFormat, (HW16)parameters->channelCount, + PaAsiHpi_PaToHpiFormat( hostSampleFormat ), + (HW32)sampleRate, 0, 0 ); + if( hpiError ) + { + PA_ASIHPI_REPORT_ERROR_( hpiError ); + switch( hpiError ) + { + case HPI_ERROR_INVALID_FORMAT: + return paSampleFormatNotSupported; + + case HPI_ERROR_INVALID_SAMPLERATE: + case HPI_ERROR_INCOMPATIBLE_SAMPLERATE: + return paInvalidSampleRate; + + case HPI_ERROR_INVALID_CHANNELS: + return paInvalidChannelCount; + } + } + + return paNoError; +} + + +/** Open HPI input stream with given format. + This attempts to open HPI input stream with desired format. If the format is not supported + or the device is unavailable, the stream is closed and a PortAudio error code is returned. + + @param hostApi Pointer to host API struct + + @param hpiDevice Pointer to HPI device struct + + @param hpiFormat Pointer to HPI format struct + + @return PortAudio error code (typically indicating a problem with stream format or device) +*/ +static PaError PaAsiHpi_OpenInput( struct PaUtilHostApiRepresentation *hostApi, + const PaAsiHpiDeviceInfo *hpiDevice, const HPI_FORMAT *hpiFormat, + HPI_HISTREAM *hpiStream ) +{ + PaAsiHpiHostApiRepresentation *hpiHostApi = (PaAsiHpiHostApiRepresentation*)hostApi; + PaError result = paNoError; + HW16 hpiError = 0; + + /* Catch misplaced output devices, as they typically have 0 input channels */ + PA_UNLESS_( !hpiDevice->streamIsOutput, paInvalidChannelCount ); + /* Try to open input stream */ + PA_ASIHPI_UNLESS_( HPI_InStreamOpen( hpiHostApi->subSys, hpiDevice->adapterIndex, + hpiDevice->streamIndex, hpiStream ), paDeviceUnavailable ); + /* Set input format (checking it in the process) */ + /* Could also use HPI_InStreamQueryFormat, but this economizes the process */ + hpiError = HPI_InStreamSetFormat( hpiHostApi->subSys, *hpiStream, (HPI_FORMAT*)hpiFormat ); + if( hpiError ) + { + PA_ASIHPI_REPORT_ERROR_( hpiError ); + PA_ASIHPI_UNLESS_( HPI_InStreamClose( hpiHostApi->subSys, *hpiStream ), paNoError ); + switch( hpiError ) + { + case HPI_ERROR_INVALID_FORMAT: + return paSampleFormatNotSupported; + + case HPI_ERROR_INVALID_SAMPLERATE: + case HPI_ERROR_INCOMPATIBLE_SAMPLERATE: + return paInvalidSampleRate; + + case HPI_ERROR_INVALID_CHANNELS: + return paInvalidChannelCount; + + default: + /* In case anything else went wrong */ + return paInvalidDevice; + } + } + +error: + return result; +} + + +/** Open HPI output stream with given format. + This attempts to open HPI output stream with desired format. If the format is not supported + or the device is unavailable, the stream is closed and a PortAudio error code is returned. + + @param hostApi Pointer to host API struct + + @param hpiDevice Pointer to HPI device struct + + @param hpiFormat Pointer to HPI format struct + + @return PortAudio error code (typically indicating a problem with stream format or device) +*/ +static PaError PaAsiHpi_OpenOutput( struct PaUtilHostApiRepresentation *hostApi, + const PaAsiHpiDeviceInfo *hpiDevice, const HPI_FORMAT *hpiFormat, + HPI_HOSTREAM *hpiStream ) +{ + PaAsiHpiHostApiRepresentation *hpiHostApi = (PaAsiHpiHostApiRepresentation*)hostApi; + PaError result = paNoError; + HW16 hpiError = 0; + + /* Catch misplaced input devices, as they typically have 0 output channels */ + PA_UNLESS_( hpiDevice->streamIsOutput, paInvalidChannelCount ); + /* Try to open output stream */ + PA_ASIHPI_UNLESS_( HPI_OutStreamOpen( hpiHostApi->subSys, hpiDevice->adapterIndex, + hpiDevice->streamIndex, hpiStream ), paDeviceUnavailable ); + + /* Check output format (format is set on first write to output stream) */ + hpiError = HPI_OutStreamQueryFormat( hpiHostApi->subSys, *hpiStream, (HPI_FORMAT*)hpiFormat ); + if( hpiError ) + { + PA_ASIHPI_REPORT_ERROR_( hpiError ); + PA_ASIHPI_UNLESS_( HPI_OutStreamClose( hpiHostApi->subSys, *hpiStream ), paNoError ); + switch( hpiError ) + { + case HPI_ERROR_INVALID_FORMAT: + return paSampleFormatNotSupported; + + case HPI_ERROR_INVALID_SAMPLERATE: + case HPI_ERROR_INCOMPATIBLE_SAMPLERATE: + return paInvalidSampleRate; + + case HPI_ERROR_INVALID_CHANNELS: + return paInvalidChannelCount; + + default: + /* In case anything else went wrong */ + return paInvalidDevice; + } + } + +error: + return result; +} + + +/** Checks whether the desired stream formats and devices are supported + (for both input and output). + This is done by actually opening the appropriate HPI streams and closing them again. + + @param hostApi Pointer to host API struct + + @param inputParameters Pointer to stream parameter struct for input side of stream + + @param outputParameters Pointer to stream parameter struct for output side of stream + + @param sampleRate Desired sample rate + + @return PortAudio error code (paFormatIsSupported on success) + */ +static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate ) +{ + PaError result = paFormatIsSupported; + PaAsiHpiHostApiRepresentation *hpiHostApi = (PaAsiHpiHostApiRepresentation*)hostApi; + PaAsiHpiDeviceInfo *hpiDevice = NULL; + HPI_FORMAT hpiFormat; + + /* Input stream */ + if( inputParameters ) + { + HPI_HISTREAM hpiStream; + PA_DEBUG(( "%s: Checking input params: dev=%d, sr=%d, chans=%d, fmt=%d\n", + __FUNCTION__, inputParameters->device, (int)sampleRate, + inputParameters->channelCount, inputParameters->sampleFormat )); + /* Create and validate format */ + PA_ENSURE_( PaAsiHpi_CreateFormat( hostApi, inputParameters, sampleRate, + &hpiDevice, &hpiFormat ) ); + /* Open stream to further check format */ + PA_ENSURE_( PaAsiHpi_OpenInput( hostApi, hpiDevice, &hpiFormat, &hpiStream ) ); + /* Close stream again */ + PA_ASIHPI_UNLESS_( HPI_InStreamClose( hpiHostApi->subSys, hpiStream ), paNoError ); + } + + /* Output stream */ + if( outputParameters ) + { + HPI_HOSTREAM hpiStream; + PA_DEBUG(( "%s: Checking output params: dev=%d, sr=%d, chans=%d, fmt=%d\n", + __FUNCTION__, outputParameters->device, (int)sampleRate, + outputParameters->channelCount, outputParameters->sampleFormat )); + /* Create and validate format */ + PA_ENSURE_( PaAsiHpi_CreateFormat( hostApi, outputParameters, sampleRate, + &hpiDevice, &hpiFormat ) ); + /* Open stream to further check format */ + PA_ENSURE_( PaAsiHpi_OpenOutput( hostApi, hpiDevice, &hpiFormat, &hpiStream ) ); + /* Close stream again */ + PA_ASIHPI_UNLESS_( HPI_OutStreamClose( hpiHostApi->subSys, hpiStream ), paNoError ); + } + +error: + return result; +} + +/* ---------------------------- Stream Interface ---------------------------- */ + +/** Obtain HPI stream information. + This obtains info such as stream state and available data/space in buffers. It also + estimates whether an underflow or overflow occurred. + + @param streamComp Pointer to stream component (input or output) to query + + @param info Pointer to stream info struct that will contain result + + @return PortAudio error code (either paNoError, paDeviceUnavailable or paUnanticipatedHostError) + */ +static PaError PaAsiHpi_GetStreamInfo( PaAsiHpiStreamComponent *streamComp, PaAsiHpiStreamInfo *info ) +{ + PaError result = paDeviceUnavailable; + HW16 state; + HW32 bufferSize, dataSize, frameCounter, auxDataSize, threshold; + HW32 hwBufferSize, hwDataSize; + + assert( streamComp ); + assert( info ); + + /* First blank the stream info struct, in case something goes wrong below. + This saves the caller from initializing the struct. */ + info->state = 0; + info->bufferSize = 0; + info->dataSize = 0; + info->frameCounter = 0; + info->auxDataSize = 0; + info->totalBufferedData = 0; + info->availableFrames = 0; + info->underflow = 0; + info->overflow = 0; + + if( streamComp->hpiDevice && streamComp->hpiStream ) + { + /* Obtain detailed stream info (either input or output) */ + if( streamComp->hpiDevice->streamIsOutput ) + { + PA_ASIHPI_UNLESS_( HPI_OutStreamGetInfoEx( streamComp->hpiDevice->subSys, + streamComp->hpiStream, + &state, &bufferSize, &dataSize, &frameCounter, + &auxDataSize ), paUnanticipatedHostError ); + } + else + { + PA_ASIHPI_UNLESS_( HPI_InStreamGetInfoEx( streamComp->hpiDevice->subSys, + streamComp->hpiStream, + &state, &bufferSize, &dataSize, &frameCounter, + &auxDataSize ), paUnanticipatedHostError ); + } + /* Load stream info */ + info->state = state; + info->bufferSize = bufferSize; + info->dataSize = dataSize; + info->frameCounter = frameCounter; + info->auxDataSize = auxDataSize; + /* Determine total buffered data */ + info->totalBufferedData = dataSize; + if( streamComp->hostBufferSize > 0 ) + info->totalBufferedData += auxDataSize; + info->totalBufferedData /= streamComp->bytesPerFrame; + /* Determine immediately available frames */ + info->availableFrames = streamComp->hpiDevice->streamIsOutput ? + bufferSize - dataSize : dataSize; + info->availableFrames /= streamComp->bytesPerFrame; + /* Minimum space/data required in buffers */ + threshold = PA_MIN( streamComp->tempBufferSize, + streamComp->bytesPerFrame * PA_ASIHPI_MIN_FRAMES_ ); + /* Obtain hardware buffer stats first, to simplify things */ + hwBufferSize = streamComp->hardwareBufferSize; + hwDataSize = streamComp->hostBufferSize > 0 ? auxDataSize : dataSize; + /* Underflow is a bit tricky */ + info->underflow = streamComp->hpiDevice->streamIsOutput ? + /* Stream seems to start in drained state sometimes, so ignore initial underflow */ + (frameCounter > 0) && ( (state == HPI_STATE_DRAINED) || (hwDataSize == 0) ) : + /* Input streams check the first-level (host) buffer for underflow */ + (state != HPI_STATE_STOPPED) && (dataSize < threshold); + /* Check for overflow in second-level (hardware) buffer for both input and output */ + info->overflow = (state != HPI_STATE_STOPPED) && (hwBufferSize - hwDataSize < threshold); + + return paNoError; + } + +error: + return result; +} + + +/** Display stream component information for debugging purposes. + + @param streamComp Pointer to stream component (input or output) to query + + @param stream Pointer to stream struct which contains the component above + */ +static void PaAsiHpi_StreamComponentDump( PaAsiHpiStreamComponent *streamComp, + PaAsiHpiStream *stream ) +{ + PaAsiHpiStreamInfo streamInfo; + + assert( streamComp ); + assert( stream ); + + /* Name of soundcard/device used by component */ + PA_DEBUG(( "device: %s\n", streamComp->hpiDevice->baseDeviceInfo.name )); + /* Unfortunately some overlap between input and output here */ + if( streamComp->hpiDevice->streamIsOutput ) + { + /* Settings on the user side (as experienced by user callback) */ + PA_DEBUG(( "user: %d-bit, %d ", + 8*stream->bufferProcessor.bytesPerUserOutputSample, + stream->bufferProcessor.outputChannelCount)); + if( stream->bufferProcessor.userOutputIsInterleaved ) + { + PA_DEBUG(( "interleaved channels, " )); + } + else + { + PA_DEBUG(( "non-interleaved channels, " )); + } + PA_DEBUG(( "%d frames/buffer, latency = %5.1f ms\n", + stream->bufferProcessor.framesPerUserBuffer, + 1000*stream->baseStreamRep.streamInfo.outputLatency )); + /* Settings on the host side (internal to PortAudio host API) */ + PA_DEBUG(( "host: %d-bit, %d interleaved channels, %d frames/buffer ", + 8*stream->bufferProcessor.bytesPerHostOutputSample, + stream->bufferProcessor.outputChannelCount, + stream->bufferProcessor.framesPerHostBuffer )); + } + else + { + /* Settings on the user side (as experienced by user callback) */ + PA_DEBUG(( "user: %d-bit, %d ", + 8*stream->bufferProcessor.bytesPerUserInputSample, + stream->bufferProcessor.inputChannelCount)); + if( stream->bufferProcessor.userInputIsInterleaved ) + { + PA_DEBUG(( "interleaved channels, " )); + } + else + { + PA_DEBUG(( "non-interleaved channels, " )); + } + PA_DEBUG(( "%d frames/buffer, latency = %5.1f ms\n", + stream->bufferProcessor.framesPerUserBuffer, + 1000*stream->baseStreamRep.streamInfo.inputLatency )); + /* Settings on the host side (internal to PortAudio host API) */ + PA_DEBUG(( "host: %d-bit, %d interleaved channels, %d frames/buffer ", + 8*stream->bufferProcessor.bytesPerHostInputSample, + stream->bufferProcessor.inputChannelCount, + stream->bufferProcessor.framesPerHostBuffer )); + } + switch( stream->bufferProcessor.hostBufferSizeMode ) + { + case paUtilFixedHostBufferSize: + PA_DEBUG(( "[fixed] " )); + break; + case paUtilBoundedHostBufferSize: + PA_DEBUG(( "[bounded] " )); + break; + case paUtilUnknownHostBufferSize: + PA_DEBUG(( "[unknown] " )); + break; + case paUtilVariableHostBufferSizePartialUsageAllowed: + PA_DEBUG(( "[variable] " )); + break; + } + PA_DEBUG(( "(%d max)\n", streamComp->tempBufferSize / streamComp->bytesPerFrame )); + /* HPI hardware settings */ + PA_DEBUG(( "HPI: adapter %d stream %d, %d-bit, %d-channel, %d Hz\n", + streamComp->hpiDevice->adapterIndex, streamComp->hpiDevice->streamIndex, + 8 * streamComp->bytesPerFrame / streamComp->hpiFormat.wChannels, + streamComp->hpiFormat.wChannels, + streamComp->hpiFormat.dwSampleRate )); + /* Stream state and buffer levels */ + PA_DEBUG(( "HPI: " )); + PaAsiHpi_GetStreamInfo( streamComp, &streamInfo ); + switch( streamInfo.state ) + { + case HPI_STATE_STOPPED: + PA_DEBUG(( "[STOPPED] " )); + break; + case HPI_STATE_PLAYING: + PA_DEBUG(( "[PLAYING] " )); + break; + case HPI_STATE_RECORDING: + PA_DEBUG(( "[RECORDING] " )); + break; + case HPI_STATE_DRAINED: + PA_DEBUG(( "[DRAINED] " )); + break; + default: + PA_DEBUG(( "[unknown state] " )); + break; + } + if( streamComp->hostBufferSize ) + { + PA_DEBUG(( "host = %d/%d B, ", streamInfo.dataSize, streamComp->hostBufferSize )); + PA_DEBUG(( "hw = %d/%d (%d) B, ", streamInfo.auxDataSize, + streamComp->hardwareBufferSize, streamComp->outputBufferCap )); + } + else + { + PA_DEBUG(( "hw = %d/%d B, ", streamInfo.dataSize, streamComp->hardwareBufferSize )); + } + PA_DEBUG(( "count = %d", streamInfo.frameCounter )); + if( streamInfo.overflow ) + { + PA_DEBUG(( " [overflow]" )); + } + else if( streamInfo.underflow ) + { + PA_DEBUG(( " [underflow]" )); + } + PA_DEBUG(( "\n" )); +} + + +/** Display stream information for debugging purposes. + + @param stream Pointer to stream to query + */ +static void PaAsiHpi_StreamDump( PaAsiHpiStream *stream ) +{ + assert( stream ); + + PA_DEBUG(( "\n------------------------- STREAM INFO FOR %p ---------------------------\n", stream )); + /* General stream info (input+output) */ + if( stream->baseStreamRep.streamCallback ) + { + PA_DEBUG(( "[callback] " )); + } + else + { + PA_DEBUG(( "[blocking] " )); + } + PA_DEBUG(( "sr=%d Hz, poll=%d ms, max %d frames/buf ", + (int)stream->baseStreamRep.streamInfo.sampleRate, + stream->pollingInterval, stream->maxFramesPerHostBuffer )); + switch( stream->state ) + { + case paAsiHpiStoppedState: + PA_DEBUG(( "[stopped]\n" )); + break; + case paAsiHpiActiveState: + PA_DEBUG(( "[active]\n" )); + break; + case paAsiHpiCallbackFinishedState: + PA_DEBUG(( "[cb fin]\n" )); + break; + default: + PA_DEBUG(( "[unknown state]\n" )); + break; + } + if( stream->callbackMode ) + { + PA_DEBUG(( "cb info: thread=%p, cbAbort=%d, cbFinished=%d\n", + stream->thread.thread, stream->callbackAbort, stream->callbackFinished )); + } + + PA_DEBUG(( "----------------------------------- Input ------------------------------------\n" )); + if( stream->input ) + { + PaAsiHpi_StreamComponentDump( stream->input, stream ); + } + else + { + PA_DEBUG(( "*none*\n" )); + } + + PA_DEBUG(( "----------------------------------- Output ------------------------------------\n" )); + if( stream->output ) + { + PaAsiHpi_StreamComponentDump( stream->output, stream ); + } + else + { + PA_DEBUG(( "*none*\n" )); + } + PA_DEBUG(( "-------------------------------------------------------------------------------\n\n" )); + +} + + +/** Determine buffer sizes and allocate appropriate stream buffers. + This attempts to allocate a BBM (host) buffer for the HPI stream component (either input + or output, as both have similar buffer needs). Not all AudioScience adapters support BBM, + in which case the hardware buffer has to suffice. The size of the HPI host buffer is chosen + as a multiple of framesPerPaHostBuffer, and also influenced by the suggested latency and the + estimated minimum polling interval. The HPI host and hardware buffer sizes are stored, and an + appropriate cap for the hardware buffer is also calculated. Finally, the temporary stream + buffer which serves as the PortAudio host buffer for this implementation is allocated. + This buffer contains an integer number of user buffers, to simplify buffer adaption in the + buffer processor. The function returns paBufferTooBig if the HPI interface cannot allocate + an HPI host buffer of the desired size. + + @param streamComp Pointer to stream component struct + + @param pollingInterval Polling interval for stream, in milliseconds + + @param framesPerPaHostBuffer Size of PortAudio host buffer, in frames + + @param suggestedLatency Suggested latency for stream component, in seconds + + @return PortAudio error code (possibly paBufferTooBig or paInsufficientMemory) + */ +static PaError PaAsiHpi_SetupBuffers( PaAsiHpiStreamComponent *streamComp, HW32 pollingInterval, + unsigned long framesPerPaHostBuffer, PaTime suggestedLatency ) +{ + PaError result = paNoError; + PaAsiHpiStreamInfo streamInfo; + unsigned long hpiBufferSize = 0, paHostBufferSize = 0; + + assert( streamComp ); + assert( streamComp->hpiDevice ); + + /* Obtain size of hardware buffer of HPI stream, since we will be activating BBM shortly + and afterwards the buffer size will refer to the BBM (host-side) buffer. + This is necessary to enable reliable detection of xruns. */ + PA_ENSURE_( PaAsiHpi_GetStreamInfo( streamComp, &streamInfo ) ); + streamComp->hardwareBufferSize = streamInfo.bufferSize; + hpiBufferSize = streamInfo.bufferSize; + + /* Check if BBM (background bus mastering) is to be enabled */ + if( PA_ASIHPI_USE_BBM_ ) + { + HW32 bbmBufferSize = 0, preLatencyBufferSize = 0; + HW16 hpiError = 0; + PaTime pollingOverhead; + + /* Check overhead of Pa_Sleep() call (minimum sleep duration in ms -> OS dependent) */ + pollingOverhead = PaUtil_GetTime(); + Pa_Sleep( 0 ); + pollingOverhead = 1000*(PaUtil_GetTime() - pollingOverhead); + PA_DEBUG(( "polling overhead = %f ms (length of 0-second sleep)\n", pollingOverhead )); + /* Obtain minimum recommended size for host buffer (in bytes) */ + PA_ASIHPI_UNLESS_( HPI_StreamEstimateBufferSize( &streamComp->hpiFormat, + pollingInterval + (HW32)ceil( pollingOverhead ), + &bbmBufferSize ), paUnanticipatedHostError ); + /* BBM places more stringent requirements on buffer size (see description */ + /* of HPI_StreamEstimateBufferSize in HPI API document) */ + bbmBufferSize *= 3; + /* Make sure the BBM buffer contains multiple PA host buffers */ + if( bbmBufferSize < 3 * streamComp->bytesPerFrame * framesPerPaHostBuffer ) + bbmBufferSize = 3 * streamComp->bytesPerFrame * framesPerPaHostBuffer; + /* Try to honor latency suggested by user by growing buffer (no decrease possible) */ + if( suggestedLatency > 0.0 ) + { + PaTime bufferDuration = ((PaTime)bbmBufferSize) / streamComp->bytesPerFrame + / streamComp->hpiFormat.dwSampleRate; + /* Don't decrease buffer */ + if( bufferDuration < suggestedLatency ) + { + /* Save old buffer size, to be retried if new size proves too big */ + preLatencyBufferSize = bbmBufferSize; + bbmBufferSize = (HW32)ceil( suggestedLatency * streamComp->bytesPerFrame + * streamComp->hpiFormat.dwSampleRate ); + } + } + /* Choose closest memory block boundary (HPI API document states that + "a buffer size of Nx4096 - 20 makes the best use of memory" + (under the entry for HPI_StreamEstimateBufferSize)) */ + bbmBufferSize = ((HW32)ceil((bbmBufferSize + 20)/4096.0))*4096 - 20; + streamComp->hostBufferSize = bbmBufferSize; + /* Allocate BBM host buffer (this enables bus mastering transfers in background) */ + if( streamComp->hpiDevice->streamIsOutput ) + hpiError = HPI_OutStreamHostBufferAllocate( streamComp->hpiDevice->subSys, + streamComp->hpiStream, + bbmBufferSize ); + else + hpiError = HPI_InStreamHostBufferAllocate( streamComp->hpiDevice->subSys, + streamComp->hpiStream, + bbmBufferSize ); + if( hpiError ) + { + PA_ASIHPI_REPORT_ERROR_( hpiError ); + /* Indicate that BBM is disabled */ + streamComp->hostBufferSize = 0; + /* Retry with smaller buffer size (transfers will still work, but not via BBM) */ + if( hpiError == HPI_ERROR_INVALID_DATASIZE ) + { + /* Retry BBM allocation with smaller size if requested latency proved too big */ + if( preLatencyBufferSize > 0 ) + { + PA_DEBUG(( "Retrying BBM allocation with smaller size (%d vs. %d bytes)\n", + preLatencyBufferSize, bbmBufferSize )); + bbmBufferSize = preLatencyBufferSize; + if( streamComp->hpiDevice->streamIsOutput ) + hpiError = HPI_OutStreamHostBufferAllocate( streamComp->hpiDevice->subSys, + streamComp->hpiStream, + bbmBufferSize ); + else + hpiError = HPI_InStreamHostBufferAllocate( streamComp->hpiDevice->subSys, + streamComp->hpiStream, + bbmBufferSize ); + /* Another round of error checking */ + if( hpiError ) + { + PA_ASIHPI_REPORT_ERROR_( hpiError ); + /* No escapes this time */ + if( hpiError == HPI_ERROR_INVALID_DATASIZE ) + { + result = paBufferTooBig; + goto error; + } + else if( hpiError != HPI_ERROR_INVALID_OPERATION ) + { + result = paUnanticipatedHostError; + goto error; + } + } + else + { + streamComp->hostBufferSize = bbmBufferSize; + hpiBufferSize = bbmBufferSize; + } + } + else + { + result = paBufferTooBig; + goto error; + } + } + /* If BBM not supported, foreground transfers will be used, but not a show-stopper */ + /* Anything else is an error */ + else if( hpiError != HPI_ERROR_INVALID_OPERATION ) + { + result = paUnanticipatedHostError; + goto error; + } + } + else + { + hpiBufferSize = bbmBufferSize; + } + } + + /* Final check of buffer size */ + paHostBufferSize = streamComp->bytesPerFrame * framesPerPaHostBuffer; + if( hpiBufferSize < 3*paHostBufferSize ) + { + result = paBufferTooBig; + goto error; + } + /* Set cap on output buffer size, based on latency suggestions */ + if( streamComp->hpiDevice->streamIsOutput ) + { + PaTime latency = suggestedLatency > 0.0 ? suggestedLatency : + streamComp->hpiDevice->baseDeviceInfo.defaultHighOutputLatency; + streamComp->outputBufferCap = + (HW32)ceil( latency * streamComp->bytesPerFrame * streamComp->hpiFormat.dwSampleRate ); + /* The cap should not be too small, to prevent underflow */ + if( streamComp->outputBufferCap < 4*paHostBufferSize ) + streamComp->outputBufferCap = 4*paHostBufferSize; + } + else + { + streamComp->outputBufferCap = 0; + } + /* Temp buffer size should be multiple of PA host buffer size (or 1x, if using fixed blocks) */ + streamComp->tempBufferSize = paHostBufferSize; + /* Allocate temp buffer */ + PA_UNLESS_( streamComp->tempBuffer = (HW8 *)PaUtil_AllocateMemory( streamComp->tempBufferSize ), + paInsufficientMemory ); +error: + return result; +} + + +/** Opens PortAudio stream. + This determines a suitable value for framesPerBuffer, if the user didn't specify it, + based on the suggested latency. It then opens each requested stream direction with the + appropriate stream format, and allocates the required stream buffers. It sets up the + various PortAudio structures dealing with streams, and estimates the stream latency. + + See pa_hostapi.h for a list of validity guarantees made about OpenStream parameters. + + @param hostApi Pointer to host API struct + + @param s List of open streams, where successfully opened stream will go + + @param inputParameters Pointer to stream parameter struct for input side of stream + + @param outputParameters Pointer to stream parameter struct for output side of stream + + @param sampleRate Desired sample rate + + @param framesPerBuffer Desired number of frames per buffer passed to user callback + (or chunk size for blocking stream) + + @param streamFlags Stream flags + + @param streamCallback Pointer to user callback function (zero for blocking interface) + + @param userData Pointer to user data that will be passed to callback function along with data + + @return PortAudio error code +*/ +static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, + PaStream **s, + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate, + unsigned long framesPerBuffer, + PaStreamFlags streamFlags, + PaStreamCallback *streamCallback, + void *userData ) +{ + PaError result = paNoError; + PaAsiHpiHostApiRepresentation *hpiHostApi = (PaAsiHpiHostApiRepresentation*)hostApi; + PaAsiHpiStream *stream = NULL; + unsigned long framesPerHostBuffer = framesPerBuffer; + int inputChannelCount = 0, outputChannelCount = 0; + PaSampleFormat inputSampleFormat = 0, outputSampleFormat = 0; + PaSampleFormat hostInputSampleFormat = 0, hostOutputSampleFormat = 0; + PaTime maxSuggestedLatency = 0.0; + + /* Validate platform-specific flags -> none expected for HPI */ + if( (streamFlags & paPlatformSpecificFlags) != 0 ) + return paInvalidFlag; /* unexpected platform-specific flag */ + + /* Create blank stream structure */ + PA_UNLESS_( stream = (PaAsiHpiStream *)PaUtil_AllocateMemory( sizeof(PaAsiHpiStream) ), + paInsufficientMemory ); + memset( stream, 0, sizeof(PaAsiHpiStream) ); + + /* If the number of frames per buffer is unspecified, we have to come up with one. */ + if( framesPerHostBuffer == paFramesPerBufferUnspecified ) + { + if( inputParameters ) + maxSuggestedLatency = inputParameters->suggestedLatency; + if( outputParameters && (outputParameters->suggestedLatency > maxSuggestedLatency) ) + maxSuggestedLatency = outputParameters->suggestedLatency; + /* Use suggested latency if available */ + if( maxSuggestedLatency > 0.0 ) + framesPerHostBuffer = (unsigned long)ceil( maxSuggestedLatency * sampleRate ); + else + /* AudioScience cards like BIG buffers by default */ + framesPerHostBuffer = 4096; + } + /* Lower bounds on host buffer size, due to polling and HPI constraints */ + if( 1000.0*framesPerHostBuffer/sampleRate < PA_ASIHPI_MIN_POLLING_INTERVAL_ ) + framesPerHostBuffer = (unsigned long)ceil( sampleRate * PA_ASIHPI_MIN_POLLING_INTERVAL_ / 1000.0 ); + /* if( framesPerHostBuffer < PA_ASIHPI_MIN_FRAMES_ ) + framesPerHostBuffer = PA_ASIHPI_MIN_FRAMES_; */ + /* Efficient if host buffer size is integer multiple of user buffer size */ + if( framesPerBuffer > 0 ) + framesPerHostBuffer = (unsigned long)ceil( (double)framesPerHostBuffer / framesPerBuffer ) * framesPerBuffer; + /* Buffer should always be a multiple of 4 bytes to facilitate 32-bit PCI transfers. + By keeping the frames a multiple of 4, this is ensured even for 8-bit mono sound. */ + framesPerHostBuffer = (framesPerHostBuffer / 4) * 4; + /* Polling is based on time length (in milliseconds) of user-requested block size */ + stream->pollingInterval = (HW32)ceil( 1000.0*framesPerHostBuffer/sampleRate ); + assert( framesPerHostBuffer > 0 ); + + /* Open underlying streams, check formats and allocate buffers */ + if( inputParameters ) + { + /* Create blank stream component structure */ + PA_UNLESS_( stream->input = (PaAsiHpiStreamComponent *)PaUtil_AllocateMemory( sizeof(PaAsiHpiStreamComponent) ), + paInsufficientMemory ); + memset( stream->input, 0, sizeof(PaAsiHpiStreamComponent) ); + /* Create/validate format */ + PA_ENSURE_( PaAsiHpi_CreateFormat( hostApi, inputParameters, sampleRate, + &stream->input->hpiDevice, &stream->input->hpiFormat ) ); + /* Open stream and set format */ + PA_ENSURE_( PaAsiHpi_OpenInput( hostApi, stream->input->hpiDevice, &stream->input->hpiFormat, + &stream->input->hpiStream ) ); + inputChannelCount = inputParameters->channelCount; + inputSampleFormat = inputParameters->sampleFormat; + hostInputSampleFormat = PaAsiHpi_HpiToPaFormat( stream->input->hpiFormat.wFormat ); + stream->input->bytesPerFrame = inputChannelCount * Pa_GetSampleSize( hostInputSampleFormat ); + assert( stream->input->bytesPerFrame > 0 ); + /* Allocate host and temp buffers of appropriate size */ + PA_ENSURE_( PaAsiHpi_SetupBuffers( stream->input, stream->pollingInterval, + framesPerHostBuffer, inputParameters->suggestedLatency ) ); + } + if( outputParameters ) + { + /* Create blank stream component structure */ + PA_UNLESS_( stream->output = (PaAsiHpiStreamComponent *)PaUtil_AllocateMemory( sizeof(PaAsiHpiStreamComponent) ), + paInsufficientMemory ); + memset( stream->output, 0, sizeof(PaAsiHpiStreamComponent) ); + /* Create/validate format */ + PA_ENSURE_( PaAsiHpi_CreateFormat( hostApi, outputParameters, sampleRate, + &stream->output->hpiDevice, &stream->output->hpiFormat ) ); + /* Open stream and check format */ + PA_ENSURE_( PaAsiHpi_OpenOutput( hostApi, stream->output->hpiDevice, + &stream->output->hpiFormat, + &stream->output->hpiStream ) ); + outputChannelCount = outputParameters->channelCount; + outputSampleFormat = outputParameters->sampleFormat; + hostOutputSampleFormat = PaAsiHpi_HpiToPaFormat( stream->output->hpiFormat.wFormat ); + stream->output->bytesPerFrame = outputChannelCount * Pa_GetSampleSize( hostOutputSampleFormat ); + /* Allocate host and temp buffers of appropriate size */ + PA_ENSURE_( PaAsiHpi_SetupBuffers( stream->output, stream->pollingInterval, + framesPerHostBuffer, outputParameters->suggestedLatency ) ); + } + + /* Determine maximum frames per host buffer (least common denominator of input/output) */ + if( inputParameters && outputParameters ) + { + stream->maxFramesPerHostBuffer = PA_MIN( stream->input->tempBufferSize / stream->input->bytesPerFrame, + stream->output->tempBufferSize / stream->output->bytesPerFrame ); + } + else + { + stream->maxFramesPerHostBuffer = inputParameters ? stream->input->tempBufferSize / stream->input->bytesPerFrame + : stream->output->tempBufferSize / stream->output->bytesPerFrame; + } + assert( stream->maxFramesPerHostBuffer > 0 ); + /* Initialize various other stream parameters */ + stream->neverDropInput = streamFlags & paNeverDropInput; + stream->state = paAsiHpiStoppedState; + + /* Initialize either callback or blocking interface */ + if( streamCallback ) + { + PaUtil_InitializeStreamRepresentation( &stream->baseStreamRep, + &hpiHostApi->callbackStreamInterface, + streamCallback, userData ); + stream->callbackMode = 1; + } + else + { + PaUtil_InitializeStreamRepresentation( &stream->baseStreamRep, + &hpiHostApi->blockingStreamInterface, + streamCallback, userData ); + /* Pre-allocate non-interleaved user buffer pointers for blocking interface */ + PA_UNLESS_( stream->blockingUserBufferCopy = + PaUtil_AllocateMemory( sizeof(void *) * PA_MAX( inputChannelCount, outputChannelCount ) ), + paInsufficientMemory ); + stream->callbackMode = 0; + } + PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate ); + + /* Following pa_linux_alsa's lead, we operate with fixed host buffer size by default, */ + /* since other modes will invariably lead to block adaption (maybe Bounded better?) */ + PA_ENSURE_( PaUtil_InitializeBufferProcessor( &stream->bufferProcessor, + inputChannelCount, inputSampleFormat, hostInputSampleFormat, + outputChannelCount, outputSampleFormat, hostOutputSampleFormat, + sampleRate, streamFlags, + framesPerBuffer, framesPerHostBuffer, paUtilFixedHostBufferSize, + streamCallback, userData ) ); + + stream->baseStreamRep.streamInfo.structVersion = 1; + stream->baseStreamRep.streamInfo.sampleRate = sampleRate; + /* Determine input latency from buffer processor and buffer sizes */ + if( stream->input ) + { + PaTime bufferDuration = ( stream->input->hostBufferSize + stream->input->hardwareBufferSize ) + / sampleRate / stream->input->bytesPerFrame; + stream->baseStreamRep.streamInfo.inputLatency = + PaUtil_GetBufferProcessorInputLatency( &stream->bufferProcessor ) + + bufferDuration - stream->maxFramesPerHostBuffer / sampleRate; + assert( stream->baseStreamRep.streamInfo.inputLatency > 0.0 ); + } + /* Determine output latency from buffer processor and buffer sizes */ + if( stream->output ) + { + PaTime bufferDuration = ( stream->output->hostBufferSize + stream->output->hardwareBufferSize ) + / sampleRate / stream->output->bytesPerFrame; + /* Take buffer size cap into account (see PaAsiHpi_WaitForFrames) */ + if( !stream->input && (stream->output->outputBufferCap > 0) ) + { + bufferDuration = PA_MIN( bufferDuration, + stream->output->outputBufferCap / sampleRate / stream->output->bytesPerFrame ); + } + stream->baseStreamRep.streamInfo.outputLatency = + PaUtil_GetBufferProcessorOutputLatency( &stream->bufferProcessor ) + + bufferDuration - stream->maxFramesPerHostBuffer / sampleRate; + assert( stream->baseStreamRep.streamInfo.outputLatency > 0.0 ); + } + + /* Report stream info, for debugging purposes */ + PaAsiHpi_StreamDump( stream ); + + /* Save initialized stream to PA stream list */ + *s = (PaStream*)stream; + return result; + +error: + CloseStream( (PaStream*)stream ); + return result; +} + + +/** Close PortAudio stream. + When CloseStream() is called, the multi-api layer ensures that the stream has already + been stopped or aborted. This closes the underlying HPI streams and deallocates stream + buffers and structs. + + @param s Pointer to PortAudio stream + + @return PortAudio error code +*/ +static PaError CloseStream( PaStream *s ) +{ + PaError result = paNoError; + PaAsiHpiStream *stream = (PaAsiHpiStream*)s; + + /* If stream is already gone, all is well */ + if( stream == NULL ) + return paNoError; + + /* Generic stream cleanup */ + PaUtil_TerminateBufferProcessor( &stream->bufferProcessor ); + PaUtil_TerminateStreamRepresentation( &stream->baseStreamRep ); + + /* Implementation-specific details - close internal streams */ + if( stream->input ) + { + /* Close HPI stream (freeing BBM host buffer in the process, if used) */ + if( stream->input->hpiStream ) + { + PA_ASIHPI_UNLESS_( HPI_InStreamClose( stream->input->hpiDevice->subSys, + stream->input->hpiStream ), paUnanticipatedHostError ); + } + /* Free temp buffer and stream component */ + PaUtil_FreeMemory( stream->input->tempBuffer ); + PaUtil_FreeMemory( stream->input ); + } + if( stream->output ) + { + /* Close HPI stream (freeing BBM host buffer in the process, if used) */ + if( stream->output->hpiStream ) + { + PA_ASIHPI_UNLESS_( HPI_OutStreamClose( stream->output->hpiDevice->subSys, + stream->output->hpiStream ), paUnanticipatedHostError ); + } + /* Free temp buffer and stream component */ + PaUtil_FreeMemory( stream->output->tempBuffer ); + PaUtil_FreeMemory( stream->output ); + } + + PaUtil_FreeMemory( stream->blockingUserBufferCopy ); + PaUtil_FreeMemory( stream ); + +error: + return result; +} + + +/** Prime HPI output stream with silence. + This resets the output stream and uses PortAudio helper routines to fill the + temp buffer with silence. It then writes two host buffers to the stream. This is supposed + to be called before the stream is started. It has no effect on input-only streams. + + @param stream Pointer to stream struct + + @return PortAudio error code + */ +static PaError PaAsiHpi_PrimeOutputWithSilence( PaAsiHpiStream *stream ) +{ + PaError result = paNoError; + PaAsiHpiStreamComponent *out; + PaUtilZeroer *zeroer; + PaSampleFormat outputFormat; +#if (HPI_VER < HPI_VERSION_CONSTRUCTOR( 3, 5, 5 )) + HPI_DATA data; +#endif + assert( stream ); + out = stream->output; + /* Only continue if stream has output channels */ + if( !out ) + return result; + assert( out->tempBuffer ); + + /* Clear all existing data in hardware playback buffer */ + PA_ASIHPI_UNLESS_( HPI_OutStreamReset( out->hpiDevice->subSys, + out->hpiStream ), paUnanticipatedHostError ); + /* Fill temp buffer with silence */ + outputFormat = PaAsiHpi_HpiToPaFormat( out->hpiFormat.wFormat ); + zeroer = PaUtil_SelectZeroer( outputFormat ); + zeroer(out->tempBuffer, 1, out->tempBufferSize / Pa_GetSampleSize(outputFormat) ); + /* Write temp buffer to hardware fifo twice, to get started */ +#if (HPI_VER >= HPI_VERSION_CONSTRUCTOR( 3, 5, 5 )) + PA_ASIHPI_UNLESS_( HPI_OutStreamWriteBuf( out->hpiDevice->subSys, out->hpiStream, + out->tempBuffer, out->tempBufferSize, &out->hpiFormat), + paUnanticipatedHostError ); + PA_ASIHPI_UNLESS_( HPI_OutStreamWriteBuf( out->hpiDevice->subSys, out->hpiStream, + out->tempBuffer, out->tempBufferSize, &out->hpiFormat), + paUnanticipatedHostError ); +#else + PA_ASIHPI_UNLESS_( HPI_DataCreate( &data, &out->hpiFormat, out->tempBuffer, out->tempBufferSize ), + paUnanticipatedHostError ); + PA_ASIHPI_UNLESS_( HPI_OutStreamWrite( out->hpiDevice->subSys, + out->hpiStream, &data ), paUnanticipatedHostError ); + PA_ASIHPI_UNLESS_( HPI_OutStreamWrite( out->hpiDevice->subSys, + out->hpiStream, &data ), paUnanticipatedHostError ); +#endif +error: + return result; +} + + +/** Start HPI streams (both input + output). + This starts all HPI streams in the PortAudio stream. Output streams are first primed with + silence, if required. After this call the PA stream is in the Active state. + + @todo Implement priming via the user callback + + @param stream Pointer to stream struct + + @param outputPrimed True if output is already primed (if false, silence will be loaded before starting) + + @return PortAudio error code + */ +static PaError PaAsiHpi_StartStream( PaAsiHpiStream *stream, int outputPrimed ) +{ + PaError result = paNoError; + + if( stream->input ) + { + PA_ASIHPI_UNLESS_( HPI_InStreamStart( stream->input->hpiDevice->subSys, + stream->input->hpiStream ), paUnanticipatedHostError ); + } + if( stream->output ) + { + if( !outputPrimed ) + { + /* Buffer isn't primed, so load stream with silence */ + PA_ENSURE_( PaAsiHpi_PrimeOutputWithSilence( stream ) ); + } + PA_ASIHPI_UNLESS_( HPI_OutStreamStart( stream->output->hpiDevice->subSys, + stream->output->hpiStream ), paUnanticipatedHostError ); + } + stream->state = paAsiHpiActiveState; + stream->callbackFinished = 0; + + /* Report stream info for debugging purposes */ + /* PaAsiHpi_StreamDump( stream ); */ + +error: + return result; +} + + +/** Start PortAudio stream. + If the stream has a callback interface, this starts a helper thread to feed the user callback. + The thread will then take care of starting the HPI streams, and this function will block + until the streams actually start. In the case of a blocking interface, the HPI streams + are simply started. + + @param s Pointer to PortAudio stream + + @return PortAudio error code +*/ +static PaError StartStream( PaStream *s ) +{ + PaError result = paNoError; + PaAsiHpiStream *stream = (PaAsiHpiStream*)s; + + assert( stream ); + + /* Ready the processor */ + PaUtil_ResetBufferProcessor( &stream->bufferProcessor ); + + if( stream->callbackMode ) + { + /* Create and start callback engine thread */ + /* Also waits 1 second for stream to be started by engine thread (otherwise aborts) */ + PA_ENSURE_( PaUnixThread_New( &stream->thread, &CallbackThreadFunc, stream, 1., 0 /*rtSched*/ ) ); + } + else + { + PA_ENSURE_( PaAsiHpi_StartStream( stream, 0 ) ); + } + +error: + return result; +} + + +/** Stop HPI streams (input + output), either softly or abruptly. + If abort is false, the function blocks until the output stream is drained, otherwise it + stops immediately and discards data in the stream hardware buffers. + + This function is safe to call from the callback engine thread as well as the main thread. + + @param stream Pointer to stream struct + + @param abort True if samples in output buffer should be discarded (otherwise blocks until stream is done) + + @return PortAudio error code + + */ +static PaError PaAsiHpi_StopStream( PaAsiHpiStream *stream, int abort ) +{ + PaError result = paNoError; + + assert( stream ); + + /* Input channels */ + if( stream->input ) + { + PA_ASIHPI_UNLESS_( HPI_InStreamReset( stream->input->hpiDevice->subSys, + stream->input->hpiStream ), paUnanticipatedHostError ); + } + /* Output channels */ + if( stream->output ) + { + if( !abort ) + { + /* Wait until HPI output stream is drained */ + while( 1 ) + { + PaAsiHpiStreamInfo streamInfo; + PaTime timeLeft; + + /* Obtain number of samples waiting to be played */ + PA_ENSURE_( PaAsiHpi_GetStreamInfo( stream->output, &streamInfo ) ); + /* Check if stream is drained */ + if( (streamInfo.state != HPI_STATE_PLAYING) && + (streamInfo.dataSize < stream->output->bytesPerFrame * PA_ASIHPI_MIN_FRAMES_) ) + break; + /* Sleep amount of time represented by remaining samples */ + timeLeft = 1000.0 * streamInfo.dataSize / stream->output->bytesPerFrame + / stream->baseStreamRep.streamInfo.sampleRate; + Pa_Sleep( (long)ceil( timeLeft ) ); + } + } + PA_ASIHPI_UNLESS_( HPI_OutStreamReset( stream->output->hpiDevice->subSys, + stream->output->hpiStream ), paUnanticipatedHostError ); + } + + /* Report stream info for debugging purposes */ + /* PaAsiHpi_StreamDump( stream ); */ + +error: + return result; +} + + +/** Stop or abort PortAudio stream. + + This function is used to explicitly stop the PortAudio stream (via StopStream/AbortStream), + as opposed to the situation when the callback finishes with a result other than paContinue. + If a stream is in callback mode we will have to inspect whether the background thread has + finished, or we will have to take it out. In either case we join the thread before returning. + In blocking mode, we simply tell HPI to stop abruptly (abort) or finish buffers (drain). + The PortAudio stream will be in the Stopped state after a call to this function. + + Don't call this from the callback engine thread! + + @param stream Pointer to stream struct + + @param abort True if samples in output buffer should be discarded (otherwise blocks until stream is done) + + @return PortAudio error code +*/ +static PaError PaAsiHpi_ExplicitStop( PaAsiHpiStream *stream, int abort ) +{ + PaError result = paNoError; + + /* First deal with the callback thread, cancelling and/or joining it if necessary */ + if( stream->callbackMode ) + { + PaError threadRes; + stream->callbackAbort = abort; + if( abort ) + { + PA_DEBUG(( "Aborting callback\n" )); + } + else + { + PA_DEBUG(( "Stopping callback\n" )); + } + PA_ENSURE_( PaUnixThread_Terminate( &stream->thread, !abort, &threadRes ) ); + if( threadRes != paNoError ) + { + PA_DEBUG(( "Callback thread returned: %d\n", threadRes )); + } + } + else + { + PA_ENSURE_( PaAsiHpi_StopStream( stream, abort ) ); + } + + stream->state = paAsiHpiStoppedState; + +error: + return result; +} + + +/** Stop PortAudio stream. + This blocks until the output buffers are drained. + + @param s Pointer to PortAudio stream + + @return PortAudio error code +*/ +static PaError StopStream( PaStream *s ) +{ + return PaAsiHpi_ExplicitStop( (PaAsiHpiStream *) s, 0 ); +} + + +/** Abort PortAudio stream. + This discards any existing data in output buffers and stops the stream immediately. + + @param s Pointer to PortAudio stream + + @return PortAudio error code +*/ +static PaError AbortStream( PaStream *s ) +{ + return PaAsiHpi_ExplicitStop( (PaAsiHpiStream * ) s, 1 ); +} + + +/** Determine whether the stream is stopped. + A stream is considered to be stopped prior to a successful call to StartStream and after + a successful call to StopStream or AbortStream. If a stream callback returns a value other + than paContinue the stream is NOT considered to be stopped (it is in CallbackFinished state). + + @param s Pointer to PortAudio stream + + @return Returns one (1) when the stream is stopped, zero (0) when the stream is running, or + a PaErrorCode (which are always negative) if PortAudio is not initialized or an + error is encountered. +*/ +static PaError IsStreamStopped( PaStream *s ) +{ + PaAsiHpiStream *stream = (PaAsiHpiStream*)s; + + assert( stream ); + return stream->state == paAsiHpiStoppedState ? 1 : 0; +} + + +/** Determine whether the stream is active. + A stream is active after a successful call to StartStream(), until it becomes inactive either + as a result of a call to StopStream() or AbortStream(), or as a result of a return value + other than paContinue from the stream callback. In the latter case, the stream is considered + inactive after the last buffer has finished playing. + + @param s Pointer to PortAudio stream + + @return Returns one (1) when the stream is active (i.e. playing or recording audio), + zero (0) when not playing, or a PaErrorCode (which are always negative) + if PortAudio is not initialized or an error is encountered. +*/ +static PaError IsStreamActive( PaStream *s ) +{ + PaAsiHpiStream *stream = (PaAsiHpiStream*)s; + + assert( stream ); + return stream->state == paAsiHpiActiveState ? 1 : 0; +} + + +/** Returns current stream time. + This corresponds to the system clock. The clock should run continuously while the stream + is open, i.e. between calls to OpenStream() and CloseStream(), therefore a frame counter + is not good enough. + + @param s Pointer to PortAudio stream + + @return Stream time, in seconds + */ +static PaTime GetStreamTime( PaStream *s ) +{ + return PaUtil_GetTime(); +} + + +/** Returns CPU load. + + @param s Pointer to PortAudio stream + + @return CPU load (0.0 if blocking interface is used) + */ +static double GetStreamCpuLoad( PaStream *s ) +{ + PaAsiHpiStream *stream = (PaAsiHpiStream*)s; + + return stream->callbackMode ? PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer ) : 0.0; +} + +/* --------------------------- Callback Interface --------------------------- */ + +/** Exit routine which is called when callback thread quits. + This takes care of stopping the HPI streams (either waiting for output to finish, or + abruptly). It also calls the user-supplied StreamFinished callback, and sets the + stream state to CallbackFinished if it was reached via a non-paContinue return from + the user callback function. + + @param userData A pointer to an open stream previously created with Pa_OpenStream + */ +static void PaAsiHpi_OnThreadExit( void *userData ) +{ + PaAsiHpiStream *stream = (PaAsiHpiStream *) userData; + + assert( stream ); + + PaUtil_ResetCpuLoadMeasurer( &stream->cpuLoadMeasurer ); + + PA_DEBUG(( "%s: Stopping HPI streams\n", __FUNCTION__ )); + PaAsiHpi_StopStream( stream, stream->callbackAbort ); + PA_DEBUG(( "%s: Stoppage\n", __FUNCTION__ )); + + /* Eventually notify user all buffers have played */ + if( stream->baseStreamRep.streamFinishedCallback ) + { + stream->baseStreamRep.streamFinishedCallback( stream->baseStreamRep.userData ); + } + + /* Unfortunately both explicit calls to Stop/AbortStream (leading to Stopped state) + and implicit stops via paComplete/paAbort (leading to CallbackFinished state) + end up here - need another flag to remind us which is the case */ + if( stream->callbackFinished ) + stream->state = paAsiHpiCallbackFinishedState; +} + + +/** Wait until there is enough frames to fill a host buffer. + The routine attempts to sleep until at least a full host buffer can be retrieved from the + input HPI stream and passed to the output HPI stream. It will first sleep until enough + output space is available, as this is usually easily achievable. If it is an output-only + stream, it will also sleep if the hardware buffer is too full, thereby throttling the + filling of the output buffer and reducing output latency. The routine then blocks until + enough input samples are available, unless this will cause an output underflow. In the + process, input overflows and output underflows are indicated. + + @param stream Pointer to stream struct + + @param framesAvail Returns the number of available frames + + @param cbFlags Overflows and underflows indicated in here + + @return PortAudio error code (only paUnanticipatedHostError expected) + */ +static PaError PaAsiHpi_WaitForFrames( PaAsiHpiStream *stream, unsigned long *framesAvail, + PaStreamCallbackFlags *cbFlags ) +{ + PaError result = paNoError; + double sampleRate; + unsigned long framesTarget; + HW32 outputData = 0, outputSpace = 0, inputData = 0, framesLeft = 0; + + assert( stream ); + assert( stream->input || stream->output ); + + sampleRate = stream->baseStreamRep.streamInfo.sampleRate; + /* We have to come up with this much frames on both input and output */ + framesTarget = stream->bufferProcessor.framesPerHostBuffer; + assert( framesTarget > 0 ); + + while( 1 ) + { + PaAsiHpiStreamInfo info; + /* Check output first, as this takes priority in the default full-duplex mode */ + if( stream->output ) + { + PA_ENSURE_( PaAsiHpi_GetStreamInfo( stream->output, &info ) ); + /* Wait until enough space is available in output buffer to receive a full block */ + if( info.availableFrames < framesTarget ) + { + framesLeft = framesTarget - info.availableFrames; + Pa_Sleep( (long)ceil( 1000 * framesLeft / sampleRate ) ); + continue; + } + /* Wait until the data in hardware buffer has dropped to a sensible level. + Without this, the hardware buffer quickly fills up in the absence of an input + stream to regulate its data rate (if data generation is fast). This leads to + large latencies, as the AudioScience hardware buffers are humongous. + This is similar to the default "Hardware Buffering=off" option in the + AudioScience WAV driver. */ + if( !stream->input && (stream->output->outputBufferCap > 0) && + ( info.totalBufferedData > stream->output->outputBufferCap / stream->output->bytesPerFrame ) ) + { + framesLeft = info.totalBufferedData - stream->output->outputBufferCap / stream->output->bytesPerFrame; + Pa_Sleep( (long)ceil( 1000 * framesLeft / sampleRate ) ); + continue; + } + outputData = info.totalBufferedData; + outputSpace = info.availableFrames; + /* Report output underflow to callback */ + if( info.underflow ) + { + *cbFlags |= paOutputUnderflow; + } + } + + /* Now check input side */ + if( stream->input ) + { + PA_ENSURE_( PaAsiHpi_GetStreamInfo( stream->input, &info ) ); + /* If a full block of samples hasn't been recorded yet, wait for it if possible */ + if( info.availableFrames < framesTarget ) + { + framesLeft = framesTarget - info.availableFrames; + /* As long as output is not disrupted in the process, wait for a full + block of input samples */ + if( !stream->output || (outputData > framesLeft) ) + { + Pa_Sleep( (long)ceil( 1000 * framesLeft / sampleRate ) ); + continue; + } + } + inputData = info.availableFrames; + /** @todo The paInputOverflow flag should be set in the callback containing the + first input sample following the overflow. That means the block currently sitting + at the fore-front of recording, i.e. typically the one containing the newest (last) + sample in the HPI buffer system. This is most likely not the same as the current + block of data being passed to the callback. The current overflow should ideally + be noted in an overflow list of sorts, with an indication of when it should be + reported. The trouble starts if there are several separate overflow incidents, + given a big input buffer. Oh well, something to try out later... */ + if( info.overflow ) + { + *cbFlags |= paInputOverflow; + } + } + break; + } + /* Full-duplex stream */ + if( stream->input && stream->output ) + { + if( outputSpace >= framesTarget ) + *framesAvail = outputSpace; + /* If input didn't make the target, keep the output count instead (input underflow) */ + if( (inputData >= framesTarget) && (inputData < outputSpace) ) + *framesAvail = inputData; + } + else + { + *framesAvail = stream->input ? inputData : outputSpace; + } + +error: + return result; +} + + +/** Obtain recording, current and playback timestamps of stream. + The current time is determined by the system clock. This "now" timestamp occurs at the + forefront of recording (and playback in the full-duplex case), which happens later than the + input timestamp by an amount equal to the total number of recorded frames in the input buffer. + The output timestamp indicates when the next generated sample will actually be played. This + happens after all the samples currently in the output buffer are played. The output timestamp + therefore follows the current timestamp by an amount equal to the number of frames yet to be + played back in the output buffer. + + If the current timestamp is the present, the input timestamp is in the past and the output + timestamp is in the future. + + @param stream Pointer to stream struct + + @param timeInfo Pointer to timeInfo struct that will contain timestamps + */ +static void PaAsiHpi_CalculateTimeInfo( PaAsiHpiStream *stream, PaStreamCallbackTimeInfo *timeInfo ) +{ + PaAsiHpiStreamInfo streamInfo; + double sampleRate; + + assert( stream ); + assert( timeInfo ); + sampleRate = stream->baseStreamRep.streamInfo.sampleRate; + + /* The current time ("now") is at the forefront of both recording and playback */ + timeInfo->currentTime = GetStreamTime( (PaStream *)stream ); + /* The last sample in the input buffer was recorded just now, so the first sample + happened (number of recorded samples)/sampleRate ago */ + timeInfo->inputBufferAdcTime = timeInfo->currentTime; + if( stream->input ) + { + PaAsiHpi_GetStreamInfo( stream->input, &streamInfo ); + timeInfo->inputBufferAdcTime -= streamInfo.totalBufferedData / sampleRate; + } + /* The first of the outgoing samples will be played after all the samples in the output + buffer is done */ + timeInfo->outputBufferDacTime = timeInfo->currentTime; + if( stream->output ) + { + PaAsiHpi_GetStreamInfo( stream->output, &streamInfo ); + timeInfo->outputBufferDacTime += streamInfo.totalBufferedData / sampleRate; + } +} + + +/** Read from HPI input stream and register buffers. + This reads data from the HPI input stream (if it exists) and registers the temp stream + buffers of both input and output streams with the buffer processor. In the process it also + handles input underflows in the full-duplex case. + + @param stream Pointer to stream struct + + @param numFrames On entrance the number of available frames, on exit the number of + received frames + + @param cbFlags Indicates overflows and underflows + + @return PortAudio error code + */ +static PaError PaAsiHpi_BeginProcessing( PaAsiHpiStream *stream, unsigned long *numFrames, + PaStreamCallbackFlags *cbFlags ) +{ + PaError result = paNoError; + + assert( stream ); + if( *numFrames > stream->maxFramesPerHostBuffer ) + *numFrames = stream->maxFramesPerHostBuffer; + + if( stream->input ) + { + PaAsiHpiStreamInfo info; + +#if (HPI_VER < HPI_VERSION_CONSTRUCTOR( 3, 5, 5 )) + HPI_DATA data; +#endif + HW32 framesToGet = *numFrames; + + /* Check for overflows and underflows yet again */ + PA_ENSURE_( PaAsiHpi_GetStreamInfo( stream->input, &info ) ); + if( info.overflow ) + { + *cbFlags |= paInputOverflow; + } + /* Input underflow if less than expected number of samples pitch up */ + if( framesToGet > info.availableFrames ) + { + PaUtilZeroer *zeroer; + PaSampleFormat inputFormat; + + /* Never call an input-only stream with InputUnderflow set */ + if( stream->output ) + *cbFlags |= paInputUnderflow; + framesToGet = info.availableFrames; + /* Fill temp buffer with silence (to make up for missing input samples) */ + inputFormat = PaAsiHpi_HpiToPaFormat( stream->input->hpiFormat.wFormat ); + zeroer = PaUtil_SelectZeroer( inputFormat ); + zeroer(stream->input->tempBuffer, 1, + stream->input->tempBufferSize / Pa_GetSampleSize(inputFormat) ); + } + +#if (HPI_VER >= HPI_VERSION_CONSTRUCTOR( 3, 5, 5 )) + /* Read block of data into temp buffer */ + PA_ASIHPI_UNLESS_( HPI_InStreamReadBuf( stream->input->hpiDevice->subSys, + stream->input->hpiStream, + stream->input->tempBuffer, + framesToGet * stream->input->bytesPerFrame), + paUnanticipatedHostError ); +#else + /* Setup HPI data structure around temp buffer */ + HPI_DataCreate( &data, &stream->input->hpiFormat, stream->input->tempBuffer, + framesToGet * stream->input->bytesPerFrame ); + /* Read block of data into temp buffer */ + PA_ASIHPI_UNLESS_( HPI_InStreamRead( stream->input->hpiDevice->subSys, + stream->input->hpiStream, &data ), + paUnanticipatedHostError ); +#endif + /* Register temp buffer with buffer processor (always FULL buffer) */ + PaUtil_SetInputFrameCount( &stream->bufferProcessor, *numFrames ); + /* HPI interface only allows interleaved channels */ + PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, + 0, stream->input->tempBuffer, + stream->input->hpiFormat.wChannels ); + } + if( stream->output ) + { + /* Register temp buffer with buffer processor */ + PaUtil_SetOutputFrameCount( &stream->bufferProcessor, *numFrames ); + /* HPI interface only allows interleaved channels */ + PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, + 0, stream->output->tempBuffer, + stream->output->hpiFormat.wChannels ); + } + +error: + return result; +} + + +/** Flush output buffers to HPI output stream. + This completes the processing cycle by writing the temp buffer to the HPI interface. + Additional output underflows are caught before data is written to the stream, as this + action typically remedies the underflow and hides it in the process. + + @param stream Pointer to stream struct + + @param numFrames The number of frames to write to the output stream + + @param cbFlags Indicates overflows and underflows + */ +static PaError PaAsiHpi_EndProcessing( PaAsiHpiStream *stream, unsigned long numFrames, + PaStreamCallbackFlags *cbFlags ) +{ + PaError result = paNoError; + + assert( stream ); + + if( stream->output ) + { + PaAsiHpiStreamInfo info; +#if (HPI_VER < HPI_VERSION_CONSTRUCTOR( 3, 5, 5 )) + HPI_DATA data; +#endif + /* Check for underflows after the (potentially time-consuming) callback */ + PA_ENSURE_( PaAsiHpi_GetStreamInfo( stream->output, &info ) ); + if( info.underflow ) + { + *cbFlags |= paOutputUnderflow; + } + +#if (HPI_VER >= HPI_VERSION_CONSTRUCTOR( 3, 5, 5 )) + /* Write temp buffer to HPI stream */ + PA_ASIHPI_UNLESS_( HPI_OutStreamWriteBuf( stream->output->hpiDevice->subSys, + stream->output->hpiStream, + stream->output->tempBuffer, + numFrames * stream->output->bytesPerFrame, + &stream->output->hpiFormat), + paUnanticipatedHostError ); +#else + /* Setup HPI data structure around temp buffer */ + HPI_DataCreate( &data, &stream->output->hpiFormat, stream->output->tempBuffer, + numFrames * stream->output->bytesPerFrame ); + /* Write temp buffer to HPI stream */ + PA_ASIHPI_UNLESS_( HPI_OutStreamWrite( stream->output->hpiDevice->subSys, + stream->output->hpiStream, &data ), + paUnanticipatedHostError ); +#endif + } + +error: + return result; +} + + +/** Main callback engine. + This function runs in a separate thread and does all the work of fetching audio data from + the AudioScience card via the HPI interface, feeding it to the user callback via the buffer + processor, and delivering the resulting output data back to the card via HPI calls. + It is started and terminated when the PortAudio stream is started and stopped, and starts + the HPI streams on startup. + + @param userData A pointer to an open stream previously created with Pa_OpenStream. +*/ +static void *CallbackThreadFunc( void *userData ) +{ + PaError result = paNoError; + PaAsiHpiStream *stream = (PaAsiHpiStream *) userData; + int callbackResult = paContinue; + + assert( stream ); + + /* Cleanup routine stops streams on thread exit */ + pthread_cleanup_push( &PaAsiHpi_OnThreadExit, stream ); + + /* Start HPI streams and notify parent when we're done */ + PA_ENSURE_( PaUnixThread_PrepareNotify( &stream->thread ) ); + /* Buffer will be primed with silence */ + PA_ENSURE_( PaAsiHpi_StartStream( stream, 0 ) ); + PA_ENSURE_( PaUnixThread_NotifyParent( &stream->thread ) ); + + /* MAIN LOOP */ + while( 1 ) + { + PaStreamCallbackFlags cbFlags = 0; + unsigned long framesAvail, framesGot; + + pthread_testcancel(); + + /** @concern StreamStop if the main thread has requested a stop and the stream has not + * been effectively stopped we signal this condition by modifying callbackResult + * (we'll want to flush buffered output). */ + if( PaUnixThread_StopRequested( &stream->thread ) && (callbackResult == paContinue) ) + { + PA_DEBUG(( "Setting callbackResult to paComplete\n" )); + callbackResult = paComplete; + } + + /* Start winding down thread if requested */ + if( callbackResult != paContinue ) + { + stream->callbackAbort = (callbackResult == paAbort); + if( stream->callbackAbort || + /** @concern BlockAdaption: Go on if adaption buffers are empty */ + PaUtil_IsBufferProcessorOutputEmpty( &stream->bufferProcessor ) ) + { + goto end; + } + PA_DEBUG(( "%s: Flushing buffer processor\n", __FUNCTION__ )); + /* There is still buffered output that needs to be processed */ + } + + /* SLEEP */ + /* Wait for data (or buffer space) to become available. This basically sleeps and + polls the HPI interface until a full block of frames can be moved. */ + PA_ENSURE_( PaAsiHpi_WaitForFrames( stream, &framesAvail, &cbFlags ) ); + + /* Consume buffer space. Once we have a number of frames available for consumption we + must retrieve the data from the HPI interface and pass it to the PA buffer processor. + We should be prepared to process several chunks successively. */ + while( framesAvail > 0 ) + { + PaStreamCallbackTimeInfo timeInfo = {0, 0, 0}; + + pthread_testcancel(); + + framesGot = framesAvail; + if( stream->bufferProcessor.hostBufferSizeMode == paUtilFixedHostBufferSize ) + { + /* We've committed to a fixed host buffer size, stick to that */ + framesGot = framesGot >= stream->maxFramesPerHostBuffer ? stream->maxFramesPerHostBuffer : 0; + } + else + { + /* We've committed to an upper bound on the size of host buffers */ + assert( stream->bufferProcessor.hostBufferSizeMode == paUtilBoundedHostBufferSize ); + framesGot = PA_MIN( framesGot, stream->maxFramesPerHostBuffer ); + } + + /* Obtain buffer timestamps */ + PaAsiHpi_CalculateTimeInfo( stream, &timeInfo ); + PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, cbFlags ); + /* CPU load measurement should include processing activivity external to the stream callback */ + PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer ); + if( framesGot > 0 ) + { + /* READ FROM HPI INPUT STREAM */ + PA_ENSURE_( PaAsiHpi_BeginProcessing( stream, &framesGot, &cbFlags ) ); + /* Input overflow in a full-duplex stream makes for interesting times */ + if( stream->input && stream->output && (cbFlags & paInputOverflow) ) + { + /* Special full-duplex paNeverDropInput mode */ + if( stream->neverDropInput ) + { + PaUtil_SetNoOutput( &stream->bufferProcessor ); + cbFlags |= paOutputOverflow; + } + } + /* CALL USER CALLBACK WITH INPUT DATA, AND OBTAIN OUTPUT DATA */ + PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult ); + /* Clear overflow and underflow information (but PaAsiHpi_EndProcessing might + still show up output underflow that will carry over to next round) */ + cbFlags = 0; + /* WRITE TO HPI OUTPUT STREAM */ + PA_ENSURE_( PaAsiHpi_EndProcessing( stream, framesGot, &cbFlags ) ); + /* Advance frame counter */ + framesAvail -= framesGot; + } + PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesGot ); + + if( framesGot == 0 ) + { + /* Go back to polling for more frames */ + break; + + } + if( callbackResult != paContinue ) + break; + } + } + + /* This code is unreachable, but important to include regardless because it + * is possibly a macro with a closing brace to match the opening brace in + * pthread_cleanup_push() above. The documentation states that they must + * always occur in pairs. */ + pthread_cleanup_pop( 1 ); + +end: + /* Indicates normal exit of callback, as opposed to the thread getting killed explicitly */ + stream->callbackFinished = 1; + PA_DEBUG(( "%s: Thread %d exiting (callbackResult = %d)\n ", + __FUNCTION__, pthread_self(), callbackResult )); + /* Exit from thread and report any PortAudio error in the process */ + PaUnixThreading_EXIT( result ); +error: + goto end; +} + +/* --------------------------- Blocking Interface --------------------------- */ + +/* As separate stream interfaces are used for blocking and callback streams, the following + functions can be guaranteed to only be called for blocking streams. */ + +/** Read data from input stream. + This reads the indicated number of frames into the supplied buffer from an input stream, + and blocks until this is done. + + @param s Pointer to PortAudio stream + + @param buffer Pointer to buffer that will receive interleaved data (or an array of pointers + to a buffer for each non-interleaved channel) + + @param frames Number of frames to read from stream + + @return PortAudio error code (also indicates overflow via paInputOverflowed) + */ +static PaError ReadStream( PaStream *s, + void *buffer, + unsigned long frames ) +{ + PaError result = paNoError; + PaAsiHpiStream *stream = (PaAsiHpiStream*)s; + PaAsiHpiStreamInfo info; + void *userBuffer; + + assert( stream ); + PA_UNLESS_( stream->input, paCanNotReadFromAnOutputOnlyStream ); + + /* Check for input overflow since previous call to ReadStream */ + PA_ENSURE_( PaAsiHpi_GetStreamInfo( stream->input, &info ) ); + if( info.overflow ) + { + result = paInputOverflowed; + } + + /* NB Make copy of user buffer pointers, since they are advanced by buffer processor */ + if( stream->bufferProcessor.userInputIsInterleaved ) + { + userBuffer = buffer; + } + else + { + /* Copy channels into local array */ + userBuffer = stream->blockingUserBufferCopy; + memcpy( userBuffer, buffer, sizeof (void *) * stream->input->hpiFormat.wChannels ); + } + + while( frames > 0 ) + { + unsigned long framesGot, framesAvail; + PaStreamCallbackFlags cbFlags = 0; + + PA_ENSURE_( PaAsiHpi_WaitForFrames( stream, &framesAvail, &cbFlags ) ); + framesGot = PA_MIN( framesAvail, frames ); + PA_ENSURE_( PaAsiHpi_BeginProcessing( stream, &framesGot, &cbFlags ) ); + + if( framesGot > 0 ) + { + framesGot = PaUtil_CopyInput( &stream->bufferProcessor, &userBuffer, framesGot ); + PA_ENSURE_( PaAsiHpi_EndProcessing( stream, framesGot, &cbFlags ) ); + /* Advance frame counter */ + frames -= framesGot; + } + } + +error: + return result; +} + + +/** Write data to output stream. + This writes the indicated number of frames from the supplied buffer to an output stream, + and blocks until this is done. + + @param s Pointer to PortAudio stream + + @param buffer Pointer to buffer that provides interleaved data (or an array of pointers + to a buffer for each non-interleaved channel) + + @param frames Number of frames to write to stream + + @return PortAudio error code (also indicates underflow via paOutputUnderflowed) + */ +static PaError WriteStream( PaStream *s, + const void *buffer, + unsigned long frames ) +{ + PaError result = paNoError; + PaAsiHpiStream *stream = (PaAsiHpiStream*)s; + PaAsiHpiStreamInfo info; + const void *userBuffer; + + assert( stream ); + PA_UNLESS_( stream->output, paCanNotWriteToAnInputOnlyStream ); + + /* Check for output underflow since previous call to WriteStream */ + PA_ENSURE_( PaAsiHpi_GetStreamInfo( stream->output, &info ) ); + if( info.underflow ) + { + result = paOutputUnderflowed; + } + + /* NB Make copy of user buffer pointers, since they are advanced by buffer processor */ + if( stream->bufferProcessor.userOutputIsInterleaved ) + { + userBuffer = buffer; + } + else + { + /* Copy channels into local array */ + userBuffer = stream->blockingUserBufferCopy; + memcpy( (void *)userBuffer, buffer, sizeof (void *) * stream->output->hpiFormat.wChannels ); + } + + while( frames > 0 ) + { + unsigned long framesGot, framesAvail; + PaStreamCallbackFlags cbFlags = 0; + + PA_ENSURE_( PaAsiHpi_WaitForFrames( stream, &framesAvail, &cbFlags ) ); + framesGot = PA_MIN( framesAvail, frames ); + PA_ENSURE_( PaAsiHpi_BeginProcessing( stream, &framesGot, &cbFlags ) ); + + if( framesGot > 0 ) + { + framesGot = PaUtil_CopyOutput( &stream->bufferProcessor, &userBuffer, framesGot ); + PA_ENSURE_( PaAsiHpi_EndProcessing( stream, framesGot, &cbFlags ) ); + /* Advance frame counter */ + frames -= framesGot; + } + } + +error: + return result; +} + + +/** Number of frames that can be read from input stream without blocking. + + @param s Pointer to PortAudio stream + + @return Number of frames, or PortAudio error code + */ +static signed long GetStreamReadAvailable( PaStream *s ) +{ + PaError result = paNoError; + PaAsiHpiStream *stream = (PaAsiHpiStream*)s; + PaAsiHpiStreamInfo info; + + assert( stream ); + PA_UNLESS_( stream->input, paCanNotReadFromAnOutputOnlyStream ); + + PA_ENSURE_( PaAsiHpi_GetStreamInfo( stream->input, &info ) ); + /* Round down to the nearest host buffer multiple */ + result = (info.availableFrames / stream->maxFramesPerHostBuffer) * stream->maxFramesPerHostBuffer; + if( info.overflow ) + { + result = paInputOverflowed; + } + +error: + return result; +} + + +/** Number of frames that can be written to output stream without blocking. + + @param s Pointer to PortAudio stream + + @return Number of frames, or PortAudio error code + */ +static signed long GetStreamWriteAvailable( PaStream *s ) +{ + PaError result = paNoError; + PaAsiHpiStream *stream = (PaAsiHpiStream*)s; + PaAsiHpiStreamInfo info; + + assert( stream ); + PA_UNLESS_( stream->output, paCanNotWriteToAnInputOnlyStream ); + + PA_ENSURE_( PaAsiHpi_GetStreamInfo( stream->output, &info ) ); + /* Round down to the nearest host buffer multiple */ + result = (info.availableFrames / stream->maxFramesPerHostBuffer) * stream->maxFramesPerHostBuffer; + if( info.underflow ) + { + result = paOutputUnderflowed; + } + +error: + return result; +} diff --git a/3rdparty/portaudio/src/hostapi/coreaudio/pa_mac_core_blocking.c b/3rdparty/portaudio/src/hostapi/coreaudio/pa_mac_core_blocking.c index 6d31a713db..15698baa17 100644 --- a/3rdparty/portaudio/src/hostapi/coreaudio/pa_mac_core_blocking.c +++ b/3rdparty/portaudio/src/hostapi/coreaudio/pa_mac_core_blocking.c @@ -1,592 +1,592 @@ -/* - * Implementation of the PortAudio API for Apple AUHAL - * - * PortAudio Portable Real-Time Audio Library - * Latest Version at: http://www.portaudio.com - * - * Written by Bjorn Roche of XO Audio LLC, from PA skeleton code. - * Portions copied from code by Dominic Mazzoni (who wrote a HAL implementation) - * - * Dominic's code was based on code by Phil Burk, Darren Gibbs, - * Gord Peters, Stephane Letz, and Greg Pfiel. - * - * The following people also deserve acknowledgements: - * - * Olivier Tristan for feedback and testing - * Glenn Zelniker and Z-Systems engineering for sponsoring the Blocking I/O - * interface. - * - * - * Based on the Open Source API proposed by Ross Bencina - * Copyright (c) 1999-2002 Ross Bencina, Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/* - * The text above constitutes the entire PortAudio license; however, - * the PortAudio community also makes the following non-binding requests: - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. It is also - * requested that these non-binding requests be included along with the - * license above. - */ - -/** - @file - @ingroup hostapi_src - - This file contains the implementation - required for blocking I/O. It is separated from pa_mac_core.c simply to ease - development. -*/ - -#include "pa_mac_core_blocking.h" -#include "pa_mac_core_internal.h" -#include -#ifdef MOSX_USE_NON_ATOMIC_FLAG_BITS -# define OSAtomicOr32( a, b ) ( (*(b)) |= (a) ) -# define OSAtomicAnd32( a, b ) ( (*(b)) &= (a) ) -#else -# include -#endif - -/* - * This fnuction determines the size of a particular sample format. - * if the format is not recognized, this returns zero. - */ -static size_t computeSampleSizeFromFormat( PaSampleFormat format ) -{ - switch( format ) { - case paFloat32: return 4; - case paInt32: return 4; - case paInt24: return 3; - case paInt16: return 2; - case paInt8: case paUInt8: return 1; - default: return 0; - } -} -/* - * Same as computeSampleSizeFromFormat, except that if - * the size is not a power of two, it returns the next power of two up - */ -static size_t computeSampleSizeFromFormatPow2( PaSampleFormat format ) -{ - switch( format ) { - case paFloat32: return 4; - case paInt32: return 4; - case paInt24: return 4; - case paInt16: return 2; - case paInt8: case paUInt8: return 1; - default: return 0; - } -} - - - -/* - * Functions for initializing, resetting, and destroying BLIO structures. - * - */ - -/* This should be called with the relevant info when initializing a stream for - callback. */ -PaError initializeBlioRingBuffers( - PaMacBlio *blio, - PaSampleFormat inputSampleFormat, - PaSampleFormat outputSampleFormat, - size_t framesPerBuffer, - long ringBufferSize, - int inChan, - int outChan ) -{ - void *data; - int result; - OSStatus err; - - /* zeroify things */ - bzero( blio, sizeof( PaMacBlio ) ); - /* this is redundant, but the buffers are used to check - if the bufffers have been initialized, so we do it explicitly. */ - blio->inputRingBuffer.buffer = NULL; - blio->outputRingBuffer.buffer = NULL; - - /* initialize simple data */ - blio->ringBufferFrames = ringBufferSize; - blio->inputSampleFormat = inputSampleFormat; - blio->inputSampleSizeActual = computeSampleSizeFromFormat(inputSampleFormat); - blio->inputSampleSizePow2 = computeSampleSizeFromFormatPow2(inputSampleFormat); - blio->outputSampleFormat = outputSampleFormat; - blio->outputSampleSizeActual = computeSampleSizeFromFormat(outputSampleFormat); - blio->outputSampleSizePow2 = computeSampleSizeFromFormatPow2(outputSampleFormat); - - blio->framesPerBuffer = framesPerBuffer; - blio->inChan = inChan; - blio->outChan = outChan; - blio->statusFlags = 0; - blio->errors = paNoError; -#ifdef PA_MAC_BLIO_MUTEX - blio->isInputEmpty = false; - blio->isOutputFull = false; -#endif - - /* setup ring buffers */ -#ifdef PA_MAC_BLIO_MUTEX - result = PaMacCore_SetUnixError( pthread_mutex_init(&(blio->inputMutex),NULL), 0 ); - if( result ) - goto error; - result = UNIX_ERR( pthread_cond_init( &(blio->inputCond), NULL ) ); - if( result ) - goto error; - result = UNIX_ERR( pthread_mutex_init(&(blio->outputMutex),NULL) ); - if( result ) - goto error; - result = UNIX_ERR( pthread_cond_init( &(blio->outputCond), NULL ) ); -#endif - if( inChan ) { - data = calloc( ringBufferSize, blio->inputSampleSizePow2*inChan ); - if( !data ) - { - result = paInsufficientMemory; - goto error; - } - - err = PaUtil_InitializeRingBuffer( - &blio->inputRingBuffer, - 1, ringBufferSize*blio->inputSampleSizePow2*inChan, - data ); - assert( !err ); - } - if( outChan ) { - data = calloc( ringBufferSize, blio->outputSampleSizePow2*outChan ); - if( !data ) - { - result = paInsufficientMemory; - goto error; - } - - err = PaUtil_InitializeRingBuffer( - &blio->outputRingBuffer, - 1, ringBufferSize*blio->outputSampleSizePow2*outChan, - data ); - assert( !err ); - } - - result = resetBlioRingBuffers( blio ); - if( result ) - goto error; - - return 0; - - error: - destroyBlioRingBuffers( blio ); - return result; -} - -#ifdef PA_MAC_BLIO_MUTEX -PaError blioSetIsInputEmpty( PaMacBlio *blio, bool isEmpty ) -{ - PaError result = paNoError; - if( isEmpty == blio->isInputEmpty ) - goto done; - - /* we need to update the value. Here's what we do: - * - Lock the mutex, so noone else can write. - * - update the value. - * - unlock. - * - broadcast to all listeners. - */ - result = UNIX_ERR( pthread_mutex_lock( &blio->inputMutex ) ); - if( result ) - goto done; - blio->isInputEmpty = isEmpty; - result = UNIX_ERR( pthread_mutex_unlock( &blio->inputMutex ) ); - if( result ) - goto done; - result = UNIX_ERR( pthread_cond_broadcast( &blio->inputCond ) ); - if( result ) - goto done; - - done: - return result; -} -PaError blioSetIsOutputFull( PaMacBlio *blio, bool isFull ) -{ - PaError result = paNoError; - if( isFull == blio->isOutputFull ) - goto done; - - /* we need to update the value. Here's what we do: - * - Lock the mutex, so noone else can write. - * - update the value. - * - unlock. - * - broadcast to all listeners. - */ - result = UNIX_ERR( pthread_mutex_lock( &blio->outputMutex ) ); - if( result ) - goto done; - blio->isOutputFull = isFull; - result = UNIX_ERR( pthread_mutex_unlock( &blio->outputMutex ) ); - if( result ) - goto done; - result = UNIX_ERR( pthread_cond_broadcast( &blio->outputCond ) ); - if( result ) - goto done; - - done: - return result; -} -#endif - -/* This should be called after stopping or aborting the stream, so that on next - start, the buffers will be ready. */ -PaError resetBlioRingBuffers( PaMacBlio *blio ) -{ -#ifdef PA_MAC__BLIO_MUTEX - int result; -#endif - blio->statusFlags = 0; - if( blio->outputRingBuffer.buffer ) { - PaUtil_FlushRingBuffer( &blio->outputRingBuffer ); - bzero( blio->outputRingBuffer.buffer, - blio->outputRingBuffer.bufferSize ); - /* Advance buffer */ - PaUtil_AdvanceRingBufferWriteIndex( &blio->outputRingBuffer, blio->ringBufferFrames*blio->outputSampleSizeActual*blio->outChan ); - //PaUtil_AdvanceRingBufferWriteIndex( &blio->outputRingBuffer, blio->outputRingBuffer.bufferSize ); - - /* Update isOutputFull. */ -#ifdef PA_MAC__BLIO_MUTEX - result = blioSetIsOutputFull( blio, toAdvance == blio->outputRingBuffer.bufferSize ); - if( result ) - goto error; -#endif -/* - printf( "------%d\n" , blio->framesPerBuffer ); - printf( "------%d\n" , blio->outChan ); - printf( "------%d\n" , blio->outputSampleSize ); - printf( "------%d\n" , blio->framesPerBuffer*blio->outChan*blio->outputSampleSize ); -*/ - } - if( blio->inputRingBuffer.buffer ) { - PaUtil_FlushRingBuffer( &blio->inputRingBuffer ); - bzero( blio->inputRingBuffer.buffer, - blio->inputRingBuffer.bufferSize ); - /* Update isInputEmpty. */ -#ifdef PA_MAC__BLIO_MUTEX - result = blioSetIsInputEmpty( blio, true ); - if( result ) - goto error; -#endif - } - return paNoError; -#ifdef PA_MAC__BLIO_MUTEX - error: - return result; -#endif -} - -/*This should be called when you are done with the blio. It can safely be called - multiple times if there are no exceptions. */ -PaError destroyBlioRingBuffers( PaMacBlio *blio ) -{ - PaError result = paNoError; - if( blio->inputRingBuffer.buffer ) { - free( blio->inputRingBuffer.buffer ); -#ifdef PA_MAC__BLIO_MUTEX - result = UNIX_ERR( pthread_mutex_destroy( & blio->inputMutex ) ); - if( result ) return result; - result = UNIX_ERR( pthread_cond_destroy( & blio->inputCond ) ); - if( result ) return result; -#endif - } - blio->inputRingBuffer.buffer = NULL; - if( blio->outputRingBuffer.buffer ) { - free( blio->outputRingBuffer.buffer ); -#ifdef PA_MAC__BLIO_MUTEX - result = UNIX_ERR( pthread_mutex_destroy( & blio->outputMutex ) ); - if( result ) return result; - result = UNIX_ERR( pthread_cond_destroy( & blio->outputCond ) ); - if( result ) return result; -#endif - } - blio->outputRingBuffer.buffer = NULL; - - return result; -} - -/* - * this is the BlioCallback function. It expects to recieve a PaMacBlio Object - * pointer as userData. - * - */ -int BlioCallback( const void *input, void *output, unsigned long frameCount, - const PaStreamCallbackTimeInfo* timeInfo, - PaStreamCallbackFlags statusFlags, - void *userData ) -{ - PaMacBlio *blio = (PaMacBlio*)userData; - long avail; - long toRead; - long toWrite; - long read; - long written; - - /* set flags returned by OS: */ - OSAtomicOr32( statusFlags, &blio->statusFlags ) ; - - /* --- Handle Input Buffer --- */ - if( blio->inChan ) { - avail = PaUtil_GetRingBufferWriteAvailable( &blio->inputRingBuffer ); - - /* check for underflow */ - if( avail < frameCount * blio->inputSampleSizeActual * blio->inChan ) - OSAtomicOr32( paInputOverflow, &blio->statusFlags ); - - toRead = MIN( avail, frameCount * blio->inputSampleSizeActual * blio->inChan ); - - /* copy the data */ - /*printf( "reading %d\n", toRead );*/ - read = PaUtil_WriteRingBuffer( &blio->inputRingBuffer, input, toRead ); - assert( toRead == read ); -#ifdef PA_MAC__BLIO_MUTEX - /* Priority inversion. See notes below. */ - blioSetIsInputEmpty( blio, false ); -#endif - } - - - /* --- Handle Output Buffer --- */ - if( blio->outChan ) { - avail = PaUtil_GetRingBufferReadAvailable( &blio->outputRingBuffer ); - - /* check for underflow */ - if( avail < frameCount * blio->outputSampleSizeActual * blio->outChan ) - OSAtomicOr32( paOutputUnderflow, &blio->statusFlags ); - - toWrite = MIN( avail, frameCount * blio->outputSampleSizeActual * blio->outChan ); - - if( toWrite != frameCount * blio->outputSampleSizeActual * blio->outChan ) - bzero( ((char *)output)+toWrite, - frameCount * blio->outputSampleSizeActual * blio->outChan - toWrite ); - /* copy the data */ - /*printf( "writing %d\n", toWrite );*/ - written = PaUtil_ReadRingBuffer( &blio->outputRingBuffer, output, toWrite ); - assert( toWrite == written ); -#ifdef PA_MAC__BLIO_MUTEX - /* We have a priority inversion here. However, we will only have to - wait if this was true and is now false, which means we've got - some room in the buffer. - Hopefully problems will be minimized. */ - blioSetIsOutputFull( blio, false ); -#endif - } - - return paContinue; -} - -PaError ReadStream( PaStream* stream, - void *buffer, - unsigned long frames ) -{ - PaMacBlio *blio = & ((PaMacCoreStream*)stream) -> blio; - char *cbuf = (char *) buffer; - PaError ret = paNoError; - VVDBUG(("ReadStream()\n")); - - while( frames > 0 ) { - long avail; - long toRead; - do { - avail = PaUtil_GetRingBufferReadAvailable( &blio->inputRingBuffer ); -/* - printf( "Read Buffer is %%%g full: %ld of %ld.\n", - 100 * (float)avail / (float) blio->inputRingBuffer.bufferSize, - avail, blio->inputRingBuffer.bufferSize ); -*/ - if( avail == 0 ) { -#ifdef PA_MAC_BLIO_MUTEX - /**block when empty*/ - ret = UNIX_ERR( pthread_mutex_lock( &blio->inputMutex ) ); - if( ret ) - return ret; - while( blio->isInputEmpty ) { - ret = UNIX_ERR( pthread_cond_wait( &blio->inputCond, &blio->inputMutex ) ); - if( ret ) - return ret; - } - ret = UNIX_ERR( pthread_mutex_unlock( &blio->inputMutex ) ); - if( ret ) - return ret; -#else - Pa_Sleep( PA_MAC_BLIO_BUSY_WAIT_SLEEP_INTERVAL ); -#endif - } - } while( avail == 0 ); - toRead = MIN( avail, frames * blio->inputSampleSizeActual * blio->inChan ); - toRead -= toRead % blio->inputSampleSizeActual * blio->inChan ; - PaUtil_ReadRingBuffer( &blio->inputRingBuffer, (void *)cbuf, toRead ); - cbuf += toRead; - frames -= toRead / ( blio->inputSampleSizeActual * blio->inChan ); - - if( toRead == avail ) { -#ifdef PA_MAC_BLIO_MUTEX - /* we just emptied the buffer, so we need to mark it as empty. */ - ret = blioSetIsInputEmpty( blio, true ); - if( ret ) - return ret; - /* of course, in the meantime, the callback may have put some sats - in, so - so check for that, too, to avoid a race condition. */ - if( PaUtil_GetRingBufferReadAvailable( &blio->inputRingBuffer ) ) { - blioSetIsInputEmpty( blio, false ); - if( ret ) - return ret; - } -#endif - } - } - - /* Report either paNoError or paInputOverflowed. */ - /* may also want to report other errors, but this is non-standard. */ - ret = blio->statusFlags & paInputOverflow; - - /* report underflow only once: */ - if( ret ) { - OSAtomicAnd32( (uint32_t)(~paInputOverflow), &blio->statusFlags ); - ret = paInputOverflowed; - } - - return ret; -} - - -PaError WriteStream( PaStream* stream, - const void *buffer, - unsigned long frames ) -{ - PaMacBlio *blio = & ((PaMacCoreStream*)stream) -> blio; - char *cbuf = (char *) buffer; - PaError ret = paNoError; - VVDBUG(("WriteStream()\n")); - - while( frames > 0 ) { - long avail = 0; - long toWrite; - - do { - avail = PaUtil_GetRingBufferWriteAvailable( &blio->outputRingBuffer ); -/* - printf( "Write Buffer is %%%g full: %ld of %ld.\n", - 100 - 100 * (float)avail / (float) blio->outputRingBuffer.bufferSize, - avail, blio->outputRingBuffer.bufferSize ); -*/ - if( avail == 0 ) { -#ifdef PA_MAC_BLIO_MUTEX - /*block while full*/ - ret = UNIX_ERR( pthread_mutex_lock( &blio->outputMutex ) ); - if( ret ) - return ret; - while( blio->isOutputFull ) { - ret = UNIX_ERR( pthread_cond_wait( &blio->outputCond, &blio->outputMutex ) ); - if( ret ) - return ret; - } - ret = UNIX_ERR( pthread_mutex_unlock( &blio->outputMutex ) ); - if( ret ) - return ret; -#else - Pa_Sleep( PA_MAC_BLIO_BUSY_WAIT_SLEEP_INTERVAL ); -#endif - } - } while( avail == 0 ); - - toWrite = MIN( avail, frames * blio->outputSampleSizeActual * blio->outChan ); - toWrite -= toWrite % blio->outputSampleSizeActual * blio->outChan ; - PaUtil_WriteRingBuffer( &blio->outputRingBuffer, (void *)cbuf, toWrite ); - cbuf += toWrite; - frames -= toWrite / ( blio->outputSampleSizeActual * blio->outChan ); - -#ifdef PA_MAC_BLIO_MUTEX - if( toWrite == avail ) { - /* we just filled up the buffer, so we need to mark it as filled. */ - ret = blioSetIsOutputFull( blio, true ); - if( ret ) - return ret; - /* of course, in the meantime, we may have emptied the buffer, so - so check for that, too, to avoid a race condition. */ - if( PaUtil_GetRingBufferWriteAvailable( &blio->outputRingBuffer ) ) { - blioSetIsOutputFull( blio, false ); - if( ret ) - return ret; - } - } -#endif - } - - /* Report either paNoError or paOutputUnderflowed. */ - /* may also want to report other errors, but this is non-standard. */ - ret = blio->statusFlags & paOutputUnderflow; - - /* report underflow only once: */ - if( ret ) { - OSAtomicAnd32( (uint32_t)(~paOutputUnderflow), &blio->statusFlags ); - ret = paOutputUnderflowed; - } - - return ret; -} - -/* - * - */ -void waitUntilBlioWriteBufferIsFlushed( PaMacBlio *blio ) -{ - if( blio->outputRingBuffer.buffer ) { - long avail = PaUtil_GetRingBufferWriteAvailable( &blio->outputRingBuffer ); - while( avail != blio->outputRingBuffer.bufferSize ) { - if( avail == 0 ) - Pa_Sleep( PA_MAC_BLIO_BUSY_WAIT_SLEEP_INTERVAL ); - avail = PaUtil_GetRingBufferWriteAvailable( &blio->outputRingBuffer ); - } - } -} - - -signed long GetStreamReadAvailable( PaStream* stream ) -{ - PaMacBlio *blio = & ((PaMacCoreStream*)stream) -> blio; - VVDBUG(("GetStreamReadAvailable()\n")); - - return PaUtil_GetRingBufferReadAvailable( &blio->inputRingBuffer ) - / ( blio->inputSampleSizeActual * blio->inChan ); -} - - -signed long GetStreamWriteAvailable( PaStream* stream ) -{ - PaMacBlio *blio = & ((PaMacCoreStream*)stream) -> blio; - VVDBUG(("GetStreamWriteAvailable()\n")); - - return PaUtil_GetRingBufferWriteAvailable( &blio->outputRingBuffer ) - / ( blio->outputSampleSizeActual * blio->outChan ); -} - +/* + * Implementation of the PortAudio API for Apple AUHAL + * + * PortAudio Portable Real-Time Audio Library + * Latest Version at: http://www.portaudio.com + * + * Written by Bjorn Roche of XO Audio LLC, from PA skeleton code. + * Portions copied from code by Dominic Mazzoni (who wrote a HAL implementation) + * + * Dominic's code was based on code by Phil Burk, Darren Gibbs, + * Gord Peters, Stephane Letz, and Greg Pfiel. + * + * The following people also deserve acknowledgements: + * + * Olivier Tristan for feedback and testing + * Glenn Zelniker and Z-Systems engineering for sponsoring the Blocking I/O + * interface. + * + * + * Based on the Open Source API proposed by Ross Bencina + * Copyright (c) 1999-2002 Ross Bencina, Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* + * The text above constitutes the entire PortAudio license; however, + * the PortAudio community also makes the following non-binding requests: + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. It is also + * requested that these non-binding requests be included along with the + * license above. + */ + +/** + @file + @ingroup hostapi_src + + This file contains the implementation + required for blocking I/O. It is separated from pa_mac_core.c simply to ease + development. +*/ + +#include "pa_mac_core_blocking.h" +#include "pa_mac_core_internal.h" +#include +#ifdef MOSX_USE_NON_ATOMIC_FLAG_BITS +# define OSAtomicOr32( a, b ) ( (*(b)) |= (a) ) +# define OSAtomicAnd32( a, b ) ( (*(b)) &= (a) ) +#else +# include +#endif + +/* + * This fnuction determines the size of a particular sample format. + * if the format is not recognized, this returns zero. + */ +static size_t computeSampleSizeFromFormat( PaSampleFormat format ) +{ + switch( format ) { + case paFloat32: return 4; + case paInt32: return 4; + case paInt24: return 3; + case paInt16: return 2; + case paInt8: case paUInt8: return 1; + default: return 0; + } +} +/* + * Same as computeSampleSizeFromFormat, except that if + * the size is not a power of two, it returns the next power of two up + */ +static size_t computeSampleSizeFromFormatPow2( PaSampleFormat format ) +{ + switch( format ) { + case paFloat32: return 4; + case paInt32: return 4; + case paInt24: return 4; + case paInt16: return 2; + case paInt8: case paUInt8: return 1; + default: return 0; + } +} + + + +/* + * Functions for initializing, resetting, and destroying BLIO structures. + * + */ + +/* This should be called with the relevant info when initializing a stream for + callback. */ +PaError initializeBlioRingBuffers( + PaMacBlio *blio, + PaSampleFormat inputSampleFormat, + PaSampleFormat outputSampleFormat, + size_t framesPerBuffer, + long ringBufferSize, + int inChan, + int outChan ) +{ + void *data; + int result; + OSStatus err; + + /* zeroify things */ + bzero( blio, sizeof( PaMacBlio ) ); + /* this is redundant, but the buffers are used to check + if the bufffers have been initialized, so we do it explicitly. */ + blio->inputRingBuffer.buffer = NULL; + blio->outputRingBuffer.buffer = NULL; + + /* initialize simple data */ + blio->ringBufferFrames = ringBufferSize; + blio->inputSampleFormat = inputSampleFormat; + blio->inputSampleSizeActual = computeSampleSizeFromFormat(inputSampleFormat); + blio->inputSampleSizePow2 = computeSampleSizeFromFormatPow2(inputSampleFormat); + blio->outputSampleFormat = outputSampleFormat; + blio->outputSampleSizeActual = computeSampleSizeFromFormat(outputSampleFormat); + blio->outputSampleSizePow2 = computeSampleSizeFromFormatPow2(outputSampleFormat); + + blio->framesPerBuffer = framesPerBuffer; + blio->inChan = inChan; + blio->outChan = outChan; + blio->statusFlags = 0; + blio->errors = paNoError; +#ifdef PA_MAC_BLIO_MUTEX + blio->isInputEmpty = false; + blio->isOutputFull = false; +#endif + + /* setup ring buffers */ +#ifdef PA_MAC_BLIO_MUTEX + result = PaMacCore_SetUnixError( pthread_mutex_init(&(blio->inputMutex),NULL), 0 ); + if( result ) + goto error; + result = UNIX_ERR( pthread_cond_init( &(blio->inputCond), NULL ) ); + if( result ) + goto error; + result = UNIX_ERR( pthread_mutex_init(&(blio->outputMutex),NULL) ); + if( result ) + goto error; + result = UNIX_ERR( pthread_cond_init( &(blio->outputCond), NULL ) ); +#endif + if( inChan ) { + data = calloc( ringBufferSize, blio->inputSampleSizePow2*inChan ); + if( !data ) + { + result = paInsufficientMemory; + goto error; + } + + err = PaUtil_InitializeRingBuffer( + &blio->inputRingBuffer, + 1, ringBufferSize*blio->inputSampleSizePow2*inChan, + data ); + assert( !err ); + } + if( outChan ) { + data = calloc( ringBufferSize, blio->outputSampleSizePow2*outChan ); + if( !data ) + { + result = paInsufficientMemory; + goto error; + } + + err = PaUtil_InitializeRingBuffer( + &blio->outputRingBuffer, + 1, ringBufferSize*blio->outputSampleSizePow2*outChan, + data ); + assert( !err ); + } + + result = resetBlioRingBuffers( blio ); + if( result ) + goto error; + + return 0; + + error: + destroyBlioRingBuffers( blio ); + return result; +} + +#ifdef PA_MAC_BLIO_MUTEX +PaError blioSetIsInputEmpty( PaMacBlio *blio, bool isEmpty ) +{ + PaError result = paNoError; + if( isEmpty == blio->isInputEmpty ) + goto done; + + /* we need to update the value. Here's what we do: + * - Lock the mutex, so noone else can write. + * - update the value. + * - unlock. + * - broadcast to all listeners. + */ + result = UNIX_ERR( pthread_mutex_lock( &blio->inputMutex ) ); + if( result ) + goto done; + blio->isInputEmpty = isEmpty; + result = UNIX_ERR( pthread_mutex_unlock( &blio->inputMutex ) ); + if( result ) + goto done; + result = UNIX_ERR( pthread_cond_broadcast( &blio->inputCond ) ); + if( result ) + goto done; + + done: + return result; +} +PaError blioSetIsOutputFull( PaMacBlio *blio, bool isFull ) +{ + PaError result = paNoError; + if( isFull == blio->isOutputFull ) + goto done; + + /* we need to update the value. Here's what we do: + * - Lock the mutex, so noone else can write. + * - update the value. + * - unlock. + * - broadcast to all listeners. + */ + result = UNIX_ERR( pthread_mutex_lock( &blio->outputMutex ) ); + if( result ) + goto done; + blio->isOutputFull = isFull; + result = UNIX_ERR( pthread_mutex_unlock( &blio->outputMutex ) ); + if( result ) + goto done; + result = UNIX_ERR( pthread_cond_broadcast( &blio->outputCond ) ); + if( result ) + goto done; + + done: + return result; +} +#endif + +/* This should be called after stopping or aborting the stream, so that on next + start, the buffers will be ready. */ +PaError resetBlioRingBuffers( PaMacBlio *blio ) +{ +#ifdef PA_MAC__BLIO_MUTEX + int result; +#endif + blio->statusFlags = 0; + if( blio->outputRingBuffer.buffer ) { + PaUtil_FlushRingBuffer( &blio->outputRingBuffer ); + bzero( blio->outputRingBuffer.buffer, + blio->outputRingBuffer.bufferSize ); + /* Advance buffer */ + PaUtil_AdvanceRingBufferWriteIndex( &blio->outputRingBuffer, blio->ringBufferFrames*blio->outputSampleSizeActual*blio->outChan ); + //PaUtil_AdvanceRingBufferWriteIndex( &blio->outputRingBuffer, blio->outputRingBuffer.bufferSize ); + + /* Update isOutputFull. */ +#ifdef PA_MAC__BLIO_MUTEX + result = blioSetIsOutputFull( blio, toAdvance == blio->outputRingBuffer.bufferSize ); + if( result ) + goto error; +#endif +/* + printf( "------%d\n" , blio->framesPerBuffer ); + printf( "------%d\n" , blio->outChan ); + printf( "------%d\n" , blio->outputSampleSize ); + printf( "------%d\n" , blio->framesPerBuffer*blio->outChan*blio->outputSampleSize ); +*/ + } + if( blio->inputRingBuffer.buffer ) { + PaUtil_FlushRingBuffer( &blio->inputRingBuffer ); + bzero( blio->inputRingBuffer.buffer, + blio->inputRingBuffer.bufferSize ); + /* Update isInputEmpty. */ +#ifdef PA_MAC__BLIO_MUTEX + result = blioSetIsInputEmpty( blio, true ); + if( result ) + goto error; +#endif + } + return paNoError; +#ifdef PA_MAC__BLIO_MUTEX + error: + return result; +#endif +} + +/*This should be called when you are done with the blio. It can safely be called + multiple times if there are no exceptions. */ +PaError destroyBlioRingBuffers( PaMacBlio *blio ) +{ + PaError result = paNoError; + if( blio->inputRingBuffer.buffer ) { + free( blio->inputRingBuffer.buffer ); +#ifdef PA_MAC__BLIO_MUTEX + result = UNIX_ERR( pthread_mutex_destroy( & blio->inputMutex ) ); + if( result ) return result; + result = UNIX_ERR( pthread_cond_destroy( & blio->inputCond ) ); + if( result ) return result; +#endif + } + blio->inputRingBuffer.buffer = NULL; + if( blio->outputRingBuffer.buffer ) { + free( blio->outputRingBuffer.buffer ); +#ifdef PA_MAC__BLIO_MUTEX + result = UNIX_ERR( pthread_mutex_destroy( & blio->outputMutex ) ); + if( result ) return result; + result = UNIX_ERR( pthread_cond_destroy( & blio->outputCond ) ); + if( result ) return result; +#endif + } + blio->outputRingBuffer.buffer = NULL; + + return result; +} + +/* + * this is the BlioCallback function. It expects to recieve a PaMacBlio Object + * pointer as userData. + * + */ +int BlioCallback( const void *input, void *output, unsigned long frameCount, + const PaStreamCallbackTimeInfo* timeInfo, + PaStreamCallbackFlags statusFlags, + void *userData ) +{ + PaMacBlio *blio = (PaMacBlio*)userData; + long avail; + long toRead; + long toWrite; + long read; + long written; + + /* set flags returned by OS: */ + OSAtomicOr32( statusFlags, &blio->statusFlags ) ; + + /* --- Handle Input Buffer --- */ + if( blio->inChan ) { + avail = PaUtil_GetRingBufferWriteAvailable( &blio->inputRingBuffer ); + + /* check for underflow */ + if( avail < frameCount * blio->inputSampleSizeActual * blio->inChan ) + OSAtomicOr32( paInputOverflow, &blio->statusFlags ); + + toRead = MIN( avail, frameCount * blio->inputSampleSizeActual * blio->inChan ); + + /* copy the data */ + /*printf( "reading %d\n", toRead );*/ + read = PaUtil_WriteRingBuffer( &blio->inputRingBuffer, input, toRead ); + assert( toRead == read ); +#ifdef PA_MAC__BLIO_MUTEX + /* Priority inversion. See notes below. */ + blioSetIsInputEmpty( blio, false ); +#endif + } + + + /* --- Handle Output Buffer --- */ + if( blio->outChan ) { + avail = PaUtil_GetRingBufferReadAvailable( &blio->outputRingBuffer ); + + /* check for underflow */ + if( avail < frameCount * blio->outputSampleSizeActual * blio->outChan ) + OSAtomicOr32( paOutputUnderflow, &blio->statusFlags ); + + toWrite = MIN( avail, frameCount * blio->outputSampleSizeActual * blio->outChan ); + + if( toWrite != frameCount * blio->outputSampleSizeActual * blio->outChan ) + bzero( ((char *)output)+toWrite, + frameCount * blio->outputSampleSizeActual * blio->outChan - toWrite ); + /* copy the data */ + /*printf( "writing %d\n", toWrite );*/ + written = PaUtil_ReadRingBuffer( &blio->outputRingBuffer, output, toWrite ); + assert( toWrite == written ); +#ifdef PA_MAC__BLIO_MUTEX + /* We have a priority inversion here. However, we will only have to + wait if this was true and is now false, which means we've got + some room in the buffer. + Hopefully problems will be minimized. */ + blioSetIsOutputFull( blio, false ); +#endif + } + + return paContinue; +} + +PaError ReadStream( PaStream* stream, + void *buffer, + unsigned long frames ) +{ + PaMacBlio *blio = & ((PaMacCoreStream*)stream) -> blio; + char *cbuf = (char *) buffer; + PaError ret = paNoError; + VVDBUG(("ReadStream()\n")); + + while( frames > 0 ) { + long avail; + long toRead; + do { + avail = PaUtil_GetRingBufferReadAvailable( &blio->inputRingBuffer ); +/* + printf( "Read Buffer is %%%g full: %ld of %ld.\n", + 100 * (float)avail / (float) blio->inputRingBuffer.bufferSize, + avail, blio->inputRingBuffer.bufferSize ); +*/ + if( avail == 0 ) { +#ifdef PA_MAC_BLIO_MUTEX + /**block when empty*/ + ret = UNIX_ERR( pthread_mutex_lock( &blio->inputMutex ) ); + if( ret ) + return ret; + while( blio->isInputEmpty ) { + ret = UNIX_ERR( pthread_cond_wait( &blio->inputCond, &blio->inputMutex ) ); + if( ret ) + return ret; + } + ret = UNIX_ERR( pthread_mutex_unlock( &blio->inputMutex ) ); + if( ret ) + return ret; +#else + Pa_Sleep( PA_MAC_BLIO_BUSY_WAIT_SLEEP_INTERVAL ); +#endif + } + } while( avail == 0 ); + toRead = MIN( avail, frames * blio->inputSampleSizeActual * blio->inChan ); + toRead -= toRead % blio->inputSampleSizeActual * blio->inChan ; + PaUtil_ReadRingBuffer( &blio->inputRingBuffer, (void *)cbuf, toRead ); + cbuf += toRead; + frames -= toRead / ( blio->inputSampleSizeActual * blio->inChan ); + + if( toRead == avail ) { +#ifdef PA_MAC_BLIO_MUTEX + /* we just emptied the buffer, so we need to mark it as empty. */ + ret = blioSetIsInputEmpty( blio, true ); + if( ret ) + return ret; + /* of course, in the meantime, the callback may have put some sats + in, so + so check for that, too, to avoid a race condition. */ + if( PaUtil_GetRingBufferReadAvailable( &blio->inputRingBuffer ) ) { + blioSetIsInputEmpty( blio, false ); + if( ret ) + return ret; + } +#endif + } + } + + /* Report either paNoError or paInputOverflowed. */ + /* may also want to report other errors, but this is non-standard. */ + ret = blio->statusFlags & paInputOverflow; + + /* report underflow only once: */ + if( ret ) { + OSAtomicAnd32( (uint32_t)(~paInputOverflow), &blio->statusFlags ); + ret = paInputOverflowed; + } + + return ret; +} + + +PaError WriteStream( PaStream* stream, + const void *buffer, + unsigned long frames ) +{ + PaMacBlio *blio = & ((PaMacCoreStream*)stream) -> blio; + char *cbuf = (char *) buffer; + PaError ret = paNoError; + VVDBUG(("WriteStream()\n")); + + while( frames > 0 ) { + long avail = 0; + long toWrite; + + do { + avail = PaUtil_GetRingBufferWriteAvailable( &blio->outputRingBuffer ); +/* + printf( "Write Buffer is %%%g full: %ld of %ld.\n", + 100 - 100 * (float)avail / (float) blio->outputRingBuffer.bufferSize, + avail, blio->outputRingBuffer.bufferSize ); +*/ + if( avail == 0 ) { +#ifdef PA_MAC_BLIO_MUTEX + /*block while full*/ + ret = UNIX_ERR( pthread_mutex_lock( &blio->outputMutex ) ); + if( ret ) + return ret; + while( blio->isOutputFull ) { + ret = UNIX_ERR( pthread_cond_wait( &blio->outputCond, &blio->outputMutex ) ); + if( ret ) + return ret; + } + ret = UNIX_ERR( pthread_mutex_unlock( &blio->outputMutex ) ); + if( ret ) + return ret; +#else + Pa_Sleep( PA_MAC_BLIO_BUSY_WAIT_SLEEP_INTERVAL ); +#endif + } + } while( avail == 0 ); + + toWrite = MIN( avail, frames * blio->outputSampleSizeActual * blio->outChan ); + toWrite -= toWrite % blio->outputSampleSizeActual * blio->outChan ; + PaUtil_WriteRingBuffer( &blio->outputRingBuffer, (void *)cbuf, toWrite ); + cbuf += toWrite; + frames -= toWrite / ( blio->outputSampleSizeActual * blio->outChan ); + +#ifdef PA_MAC_BLIO_MUTEX + if( toWrite == avail ) { + /* we just filled up the buffer, so we need to mark it as filled. */ + ret = blioSetIsOutputFull( blio, true ); + if( ret ) + return ret; + /* of course, in the meantime, we may have emptied the buffer, so + so check for that, too, to avoid a race condition. */ + if( PaUtil_GetRingBufferWriteAvailable( &blio->outputRingBuffer ) ) { + blioSetIsOutputFull( blio, false ); + if( ret ) + return ret; + } + } +#endif + } + + /* Report either paNoError or paOutputUnderflowed. */ + /* may also want to report other errors, but this is non-standard. */ + ret = blio->statusFlags & paOutputUnderflow; + + /* report underflow only once: */ + if( ret ) { + OSAtomicAnd32( (uint32_t)(~paOutputUnderflow), &blio->statusFlags ); + ret = paOutputUnderflowed; + } + + return ret; +} + +/* + * + */ +void waitUntilBlioWriteBufferIsFlushed( PaMacBlio *blio ) +{ + if( blio->outputRingBuffer.buffer ) { + long avail = PaUtil_GetRingBufferWriteAvailable( &blio->outputRingBuffer ); + while( avail != blio->outputRingBuffer.bufferSize ) { + if( avail == 0 ) + Pa_Sleep( PA_MAC_BLIO_BUSY_WAIT_SLEEP_INTERVAL ); + avail = PaUtil_GetRingBufferWriteAvailable( &blio->outputRingBuffer ); + } + } +} + + +signed long GetStreamReadAvailable( PaStream* stream ) +{ + PaMacBlio *blio = & ((PaMacCoreStream*)stream) -> blio; + VVDBUG(("GetStreamReadAvailable()\n")); + + return PaUtil_GetRingBufferReadAvailable( &blio->inputRingBuffer ) + / ( blio->inputSampleSizeActual * blio->inChan ); +} + + +signed long GetStreamWriteAvailable( PaStream* stream ) +{ + PaMacBlio *blio = & ((PaMacCoreStream*)stream) -> blio; + VVDBUG(("GetStreamWriteAvailable()\n")); + + return PaUtil_GetRingBufferWriteAvailable( &blio->outputRingBuffer ) + / ( blio->outputSampleSizeActual * blio->outChan ); +} + diff --git a/3rdparty/portaudio/src/hostapi/coreaudio/pa_mac_core_blocking.h b/3rdparty/portaudio/src/hostapi/coreaudio/pa_mac_core_blocking.h index 971223b3cf..a6f0ad5dc1 100644 --- a/3rdparty/portaudio/src/hostapi/coreaudio/pa_mac_core_blocking.h +++ b/3rdparty/portaudio/src/hostapi/coreaudio/pa_mac_core_blocking.h @@ -1,136 +1,136 @@ -/* - * Internal blocking interfaces for PortAudio Apple AUHAL implementation - * - * PortAudio Portable Real-Time Audio Library - * Latest Version at: http://www.portaudio.com - * - * Written by Bjorn Roche of XO Audio LLC, from PA skeleton code. - * Portions copied from code by Dominic Mazzoni (who wrote a HAL implementation) - * - * Dominic's code was based on code by Phil Burk, Darren Gibbs, - * Gord Peters, Stephane Letz, and Greg Pfiel. - * - * The following people also deserve acknowledgements: - * - * Olivier Tristan for feedback and testing - * Glenn Zelniker and Z-Systems engineering for sponsoring the Blocking I/O - * interface. - * - * - * Based on the Open Source API proposed by Ross Bencina - * Copyright (c) 1999-2002 Ross Bencina, Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/* - * The text above constitutes the entire PortAudio license; however, - * the PortAudio community also makes the following non-binding requests: - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. It is also - * requested that these non-binding requests be included along with the - * license above. - */ - -/** - @file - @ingroup hostapi_src -*/ - -#ifndef PA_MAC_CORE_BLOCKING_H_ -#define PA_MAC_CORE_BLOCKING_H_ - -#include "pa_ringbuffer.h" -#include "portaudio.h" -#include "pa_mac_core_utilities.h" - -/* - * Number of miliseconds to busy wait whil waiting for data in blocking calls. - */ -#define PA_MAC_BLIO_BUSY_WAIT_SLEEP_INTERVAL (5) -/* - * Define exactly one of these blocking methods - * PA_MAC_BLIO_MUTEX is not actively maintained. - */ -#define PA_MAC_BLIO_BUSY_WAIT -/* -#define PA_MAC_BLIO_MUTEX -*/ - -typedef struct { - PaUtilRingBuffer inputRingBuffer; - PaUtilRingBuffer outputRingBuffer; - size_t ringBufferFrames; - PaSampleFormat inputSampleFormat; - size_t inputSampleSizeActual; - size_t inputSampleSizePow2; - PaSampleFormat outputSampleFormat; - size_t outputSampleSizeActual; - size_t outputSampleSizePow2; - - size_t framesPerBuffer; - - int inChan; - int outChan; - - //PaStreamCallbackFlags statusFlags; - uint32_t statusFlags; - PaError errors; - - /* Here we handle blocking, using condition variables. */ -#ifdef PA_MAC_BLIO_MUTEX - volatile bool isInputEmpty; - pthread_mutex_t inputMutex; - pthread_cond_t inputCond; - - volatile bool isOutputFull; - pthread_mutex_t outputMutex; - pthread_cond_t outputCond; -#endif -} -PaMacBlio; - -/* - * These functions operate on condition and related variables. - */ - -PaError initializeBlioRingBuffers( - PaMacBlio *blio, - PaSampleFormat inputSampleFormat, - PaSampleFormat outputSampleFormat, - size_t framesPerBuffer, - long ringBufferSize, - int inChan, - int outChan ); -PaError destroyBlioRingBuffers( PaMacBlio *blio ); -PaError resetBlioRingBuffers( PaMacBlio *blio ); - -int BlioCallback( - const void *input, void *output, - unsigned long frameCount, - const PaStreamCallbackTimeInfo* timeInfo, - PaStreamCallbackFlags statusFlags, - void *userData ); - -void waitUntilBlioWriteBufferIsFlushed( PaMacBlio *blio ); - -#endif /*PA_MAC_CORE_BLOCKING_H_*/ +/* + * Internal blocking interfaces for PortAudio Apple AUHAL implementation + * + * PortAudio Portable Real-Time Audio Library + * Latest Version at: http://www.portaudio.com + * + * Written by Bjorn Roche of XO Audio LLC, from PA skeleton code. + * Portions copied from code by Dominic Mazzoni (who wrote a HAL implementation) + * + * Dominic's code was based on code by Phil Burk, Darren Gibbs, + * Gord Peters, Stephane Letz, and Greg Pfiel. + * + * The following people also deserve acknowledgements: + * + * Olivier Tristan for feedback and testing + * Glenn Zelniker and Z-Systems engineering for sponsoring the Blocking I/O + * interface. + * + * + * Based on the Open Source API proposed by Ross Bencina + * Copyright (c) 1999-2002 Ross Bencina, Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* + * The text above constitutes the entire PortAudio license; however, + * the PortAudio community also makes the following non-binding requests: + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. It is also + * requested that these non-binding requests be included along with the + * license above. + */ + +/** + @file + @ingroup hostapi_src +*/ + +#ifndef PA_MAC_CORE_BLOCKING_H_ +#define PA_MAC_CORE_BLOCKING_H_ + +#include "pa_ringbuffer.h" +#include "portaudio.h" +#include "pa_mac_core_utilities.h" + +/* + * Number of miliseconds to busy wait whil waiting for data in blocking calls. + */ +#define PA_MAC_BLIO_BUSY_WAIT_SLEEP_INTERVAL (5) +/* + * Define exactly one of these blocking methods + * PA_MAC_BLIO_MUTEX is not actively maintained. + */ +#define PA_MAC_BLIO_BUSY_WAIT +/* +#define PA_MAC_BLIO_MUTEX +*/ + +typedef struct { + PaUtilRingBuffer inputRingBuffer; + PaUtilRingBuffer outputRingBuffer; + size_t ringBufferFrames; + PaSampleFormat inputSampleFormat; + size_t inputSampleSizeActual; + size_t inputSampleSizePow2; + PaSampleFormat outputSampleFormat; + size_t outputSampleSizeActual; + size_t outputSampleSizePow2; + + size_t framesPerBuffer; + + int inChan; + int outChan; + + //PaStreamCallbackFlags statusFlags; + uint32_t statusFlags; + PaError errors; + + /* Here we handle blocking, using condition variables. */ +#ifdef PA_MAC_BLIO_MUTEX + volatile bool isInputEmpty; + pthread_mutex_t inputMutex; + pthread_cond_t inputCond; + + volatile bool isOutputFull; + pthread_mutex_t outputMutex; + pthread_cond_t outputCond; +#endif +} +PaMacBlio; + +/* + * These functions operate on condition and related variables. + */ + +PaError initializeBlioRingBuffers( + PaMacBlio *blio, + PaSampleFormat inputSampleFormat, + PaSampleFormat outputSampleFormat, + size_t framesPerBuffer, + long ringBufferSize, + int inChan, + int outChan ); +PaError destroyBlioRingBuffers( PaMacBlio *blio ); +PaError resetBlioRingBuffers( PaMacBlio *blio ); + +int BlioCallback( + const void *input, void *output, + unsigned long frameCount, + const PaStreamCallbackTimeInfo* timeInfo, + PaStreamCallbackFlags statusFlags, + void *userData ); + +void waitUntilBlioWriteBufferIsFlushed( PaMacBlio *blio ); + +#endif /*PA_MAC_CORE_BLOCKING_H_*/ diff --git a/3rdparty/portaudio/src/hostapi/wasapi/pa_win_wasapi.c b/3rdparty/portaudio/src/hostapi/wasapi/pa_win_wasapi.c index 071326b3c2..b8e7b30730 100644 --- a/3rdparty/portaudio/src/hostapi/wasapi/pa_win_wasapi.c +++ b/3rdparty/portaudio/src/hostapi/wasapi/pa_win_wasapi.c @@ -1,3292 +1,3292 @@ -/* - * Portable Audio I/O Library WASAPI implementation - * Copyright (c) 2006-2010 David Viens, Dmitry Kostjuchenko - * - * Based on the Open Source API proposed by Ross Bencina - * Copyright (c) 1999-2002 Ross Bencina, Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/* - * The text above constitutes the entire PortAudio license; however, - * the PortAudio community also makes the following non-binding requests: - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. It is also - * requested that these non-binding requests be included along with the - * license above. - */ - -/** @file - @ingroup hostapi_src - @brief WASAPI implementation of support for a host API. - @note pa_wasapi currently requires minimum VC 2005, and the latest Vista SDK -*/ - -#define WIN32_LEAN_AND_MEAN // exclude rare headers -#include -#include -#include -#include -#include // must be before other Wasapi headers -#if defined(_MSC_VER) && (_MSC_VER >= 1400) - #include - #define COBJMACROS - #include - #include - #define INITGUID //<< avoid additional linkage of static libs, uneeded code will be optimized out - #include - #include - #undef INITGUID -#endif - -#include "pa_util.h" -#include "pa_allocation.h" -#include "pa_hostapi.h" -#include "pa_stream.h" -#include "pa_cpuload.h" -#include "pa_process.h" -#include "pa_debugprint.h" -#include "pa_win_wasapi.h" - -#ifndef NTDDI_VERSION - - #ifndef _AVRT_ //<< fix MinGW dummy compile by defining missing type: AVRT_PRIORITY - typedef enum _AVRT_PRIORITY - { - AVRT_PRIORITY_LOW = -1, - AVRT_PRIORITY_NORMAL, - AVRT_PRIORITY_HIGH, - AVRT_PRIORITY_CRITICAL - } AVRT_PRIORITY, *PAVRT_PRIORITY; - #endif - - #include // << for IID/CLSID - #include - #include - - #ifndef __LPCGUID_DEFINED__ - #define __LPCGUID_DEFINED__ - typedef const GUID *LPCGUID; - #endif - - #ifndef PROPERTYKEY_DEFINED - #define PROPERTYKEY_DEFINED - typedef struct _tagpropertykey - { - GUID fmtid; - DWORD pid; - } PROPERTYKEY; - #endif - - #ifdef __midl_proxy - #define __MIDL_CONST - #else - #define __MIDL_CONST const - #endif - - #ifndef WAVE_FORMAT_IEEE_FLOAT - #define WAVE_FORMAT_IEEE_FLOAT 0x0003 // 32-bit floating-point - #endif - - // Speaker Positions: - #define SPEAKER_FRONT_LEFT 0x1 - #define SPEAKER_FRONT_RIGHT 0x2 - #define SPEAKER_FRONT_CENTER 0x4 - #define SPEAKER_LOW_FREQUENCY 0x8 - #define SPEAKER_BACK_LEFT 0x10 - #define SPEAKER_BACK_RIGHT 0x20 - #define SPEAKER_FRONT_LEFT_OF_CENTER 0x40 - #define SPEAKER_FRONT_RIGHT_OF_CENTER 0x80 - #define SPEAKER_BACK_CENTER 0x100 - #define SPEAKER_SIDE_LEFT 0x200 - #define SPEAKER_SIDE_RIGHT 0x400 - #define SPEAKER_TOP_CENTER 0x800 - #define SPEAKER_TOP_FRONT_LEFT 0x1000 - #define SPEAKER_TOP_FRONT_CENTER 0x2000 - #define SPEAKER_TOP_FRONT_RIGHT 0x4000 - #define SPEAKER_TOP_BACK_LEFT 0x8000 - #define SPEAKER_TOP_BACK_CENTER 0x10000 - #define SPEAKER_TOP_BACK_RIGHT 0x20000 - - // Bit mask locations reserved for future use - #define SPEAKER_RESERVED 0x7FFC0000 - - // Used to specify that any possible permutation of speaker configurations - #define SPEAKER_ALL 0x80000000 - - // DirectSound Speaker Config - #if (NTDDI_VERSION >= NTDDI_WINXP) - #define KSAUDIO_SPEAKER_DIRECTOUT 0 - #endif - #define KSAUDIO_SPEAKER_MONO (SPEAKER_FRONT_CENTER) - #define KSAUDIO_SPEAKER_STEREO (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT) - #define KSAUDIO_SPEAKER_QUAD (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | \ - SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT) - #define KSAUDIO_SPEAKER_SURROUND (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | \ - SPEAKER_FRONT_CENTER | SPEAKER_BACK_CENTER) - #define KSAUDIO_SPEAKER_5POINT1 (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | \ - SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | \ - SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT) - #define KSAUDIO_SPEAKER_7POINT1 (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | \ - SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | \ - SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | \ - SPEAKER_FRONT_LEFT_OF_CENTER | SPEAKER_FRONT_RIGHT_OF_CENTER) - - #if ( (NTDDI_VERSION >= NTDDI_WINXPSP2) && (NTDDI_VERSION < NTDDI_WS03) ) || (NTDDI_VERSION >= NTDDI_WS03SP1) - - #define KSAUDIO_SPEAKER_5POINT1_SURROUND (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | \ - SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | \ - SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT) - #define KSAUDIO_SPEAKER_7POINT1_SURROUND (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | \ - SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | \ - SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | \ - SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT) - // The following are obsolete 5.1 and 7.1 settings (they lack side speakers). Note this means - // that the default 5.1 and 7.1 settings (KSAUDIO_SPEAKER_5POINT1 and KSAUDIO_SPEAKER_7POINT1 are - // similarly obsolete but are unchanged for compatibility reasons). - #define KSAUDIO_SPEAKER_5POINT1_BACK KSAUDIO_SPEAKER_5POINT1 - #define KSAUDIO_SPEAKER_7POINT1_WIDE KSAUDIO_SPEAKER_7POINT1 - - #endif // XP SP2 and later (chronologically) - - // DVD Speaker Positions - #define KSAUDIO_SPEAKER_GROUND_FRONT_LEFT SPEAKER_FRONT_LEFT - #define KSAUDIO_SPEAKER_GROUND_FRONT_CENTER SPEAKER_FRONT_CENTER - #define KSAUDIO_SPEAKER_GROUND_FRONT_RIGHT SPEAKER_FRONT_RIGHT - #define KSAUDIO_SPEAKER_GROUND_REAR_LEFT SPEAKER_BACK_LEFT - #define KSAUDIO_SPEAKER_GROUND_REAR_RIGHT SPEAKER_BACK_RIGHT - #define KSAUDIO_SPEAKER_TOP_MIDDLE SPEAKER_TOP_CENTER - #define KSAUDIO_SPEAKER_SUPER_WOOFER SPEAKER_LOW_FREQUENCY - - #ifdef WIN64 - #include - typedef LONG NTSTATUS; - typedef void *PKSEVENT_ENTRY; - typedef void *PKSDEVICE; - typedef void *PKSFILTERFACTORY; - typedef void *PKSFILTER; - typedef void *PIRP; - typedef void *PCM_RESOURCE_LIST; - typedef void *PDEVICE_CAPABILITIES; - typedef void *PKSPIN; - typedef void *PKSPROCESSPIN_INDEXENTRY; - typedef void KSATTRIBUTE_LIST; - typedef void *PKSHANDSHAKE; - typedef void *PKTIMER; - typedef void *PKDPC; - typedef void *PKSSTREAM_POINTER; - typedef void KSPROPERTY_SET; - typedef void KSCLOCK_DISPATCH; - typedef void KSMETHOD_SET; - typedef void KSEVENT_SET; - typedef void KSALLOCATOR_DISPATCH; - typedef void KSDEVICE_DISPATCH; - typedef void KSFILTER_DISPATCH; - typedef void KSFILTER_DESCRIPTOR; - typedef void KSPIN_DESCRIPTOR_EX; - typedef void KSPIN_DISPATCH; - typedef void KSDEVICE_DESCRIPTOR; - typedef void *PFNKSDELETEALLOCATOR; - typedef void *PFNKSDEFAULTALLOCATE; - typedef void *PFNKSDEFAULTFREE; - typedef void KSNODE_DESCRIPTOR; - typedef char KSPIN_DESCRIPTOR; - typedef void *PFNKSINTERSECTHANDLEREX; - typedef void *PDEVICE_OBJECT; - typedef void *PHYSICAL_ADDRESS; - typedef void *PKSSTREAM_POINTER_OFFSET; - typedef void *PKSPROCESSPIN; - typedef void *PMDL; - typedef ULONG KSSTREAM_POINTER_OFFSET; - typedef void KSJACK_DESCRIPTION; - #define FASTCALL - #include - #include - #else - typedef struct _BYTE_BLOB - { - unsigned long clSize; - unsigned char abData[ 1 ]; - } BYTE_BLOB; - typedef /* [unique] */ __RPC_unique_pointer BYTE_BLOB *UP_BYTE_BLOB; - typedef LONGLONG REFERENCE_TIME; - #define NONAMELESSUNION - #endif - - #undef WINVER - #undef _WIN32_WINNT - #include - #include - #define COBJMACROS - #define INITGUID - #include - #include - #include - #include - #undef INITGUID - -#endif // NTDDI_VERSION - -#ifndef GUID_SECT - #define GUID_SECT -#endif - -#define __DEFINE_GUID(n,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) static const GUID n GUID_SECT = {l,w1,w2,{b1,b2,b3,b4,b5,b6,b7,b8}} -#define __DEFINE_IID(n,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) static const IID n GUID_SECT = {l,w1,w2,{b1,b2,b3,b4,b5,b6,b7,b8}} -#define __DEFINE_CLSID(n,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) static const CLSID n GUID_SECT = {l,w1,w2,{b1,b2,b3,b4,b5,b6,b7,b8}} -#define PA_DEFINE_CLSID(className, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ - __DEFINE_CLSID(pa_CLSID_##className, 0x##l, 0x##w1, 0x##w2, 0x##b1, 0x##b2, 0x##b3, 0x##b4, 0x##b5, 0x##b6, 0x##b7, 0x##b8) -#define PA_DEFINE_IID(interfaceName, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ - __DEFINE_IID(pa_IID_##interfaceName, 0x##l, 0x##w1, 0x##w2, 0x##b1, 0x##b2, 0x##b3, 0x##b4, 0x##b5, 0x##b6, 0x##b7, 0x##b8) - -// "1CB9AD4C-DBFA-4c32-B178-C2F568A703B2" -PA_DEFINE_IID(IAudioClient, 1cb9ad4c, dbfa, 4c32, b1, 78, c2, f5, 68, a7, 03, b2); -// "1BE09788-6894-4089-8586-9A2A6C265AC5" -PA_DEFINE_IID(IMMEndpoint, 1be09788, 6894, 4089, 85, 86, 9a, 2a, 6c, 26, 5a, c5); -// "A95664D2-9614-4F35-A746-DE8DB63617E6" -PA_DEFINE_IID(IMMDeviceEnumerator, a95664d2, 9614, 4f35, a7, 46, de, 8d, b6, 36, 17, e6); -// "BCDE0395-E52F-467C-8E3D-C4579291692E" -PA_DEFINE_CLSID(IMMDeviceEnumerator,bcde0395, e52f, 467c, 8e, 3d, c4, 57, 92, 91, 69, 2e); -// "F294ACFC-3146-4483-A7BF-ADDCA7C260E2" -PA_DEFINE_IID(IAudioRenderClient, f294acfc, 3146, 4483, a7, bf, ad, dc, a7, c2, 60, e2); -// "C8ADBD64-E71E-48a0-A4DE-185C395CD317" -PA_DEFINE_IID(IAudioCaptureClient, c8adbd64, e71e, 48a0, a4, de, 18, 5c, 39, 5c, d3, 17); -// Media formats: -__DEFINE_GUID(pa_KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 ); -__DEFINE_GUID(pa_KSDATAFORMAT_SUBTYPE_ADPCM, 0x00000002, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 ); -__DEFINE_GUID(pa_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 ); - -/* use CreateThread for CYGWIN/Windows Mobile, _beginthreadex for all others */ -#if !defined(__CYGWIN__) && !defined(_WIN32_WCE) - #define CREATE_THREAD(PROC) (HANDLE)_beginthreadex( NULL, 0, (PROC), stream, 0, &stream->dwThreadId ) - #define PA_THREAD_FUNC static unsigned WINAPI - #define PA_THREAD_ID unsigned -#else - #define CREATE_THREAD(PROC) CreateThread( NULL, 0, (PROC), stream, 0, &stream->dwThreadId ) - #define PA_THREAD_FUNC static DWORD WINAPI - #define PA_THREAD_ID DWORD -#endif - -// Thread function forward decl. -PA_THREAD_FUNC ProcThreadEvent(void *param); -PA_THREAD_FUNC ProcThreadPoll(void *param); - -// Availabe from Windows 7 -#ifndef AUDCLNT_E_BUFFER_ERROR - #define AUDCLNT_E_BUFFER_ERROR AUDCLNT_ERR(0x018) -#endif -#ifndef AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED - #define AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED AUDCLNT_ERR(0x019) -#endif -#ifndef AUDCLNT_E_INVALID_DEVICE_PERIOD - #define AUDCLNT_E_INVALID_DEVICE_PERIOD AUDCLNT_ERR(0x020) -#endif - -#define MAX_STR_LEN 512 - -enum { S_INPUT = 0, S_OUTPUT, S_COUNT }; - -#define STATIC_ARRAY_SIZE(array) (sizeof(array)/sizeof(array[0])) - -#define PRINT(x) PA_DEBUG(x); - -#define PA_SKELETON_SET_LAST_HOST_ERROR( errorCode, errorText ) \ - PaUtil_SetLastHostErrorInfo( paWASAPI, errorCode, errorText ) - -#ifndef IF_FAILED_JUMP -#define IF_FAILED_JUMP(hr, label) if(FAILED(hr)) goto label; -#endif - -#define SAFE_CLOSE(h) if ((h) != NULL) { CloseHandle((h)); (h) = NULL; } -#define SAFE_RELEASE(punk) if ((punk) != NULL) { (punk)->lpVtbl->Release((punk)); (punk) = NULL; } - -// AVRT is the new "multimedia schedulling stuff" -typedef BOOL (WINAPI *FAvRtCreateThreadOrderingGroup) (PHANDLE,PLARGE_INTEGER,GUID*,PLARGE_INTEGER); -typedef BOOL (WINAPI *FAvRtDeleteThreadOrderingGroup) (HANDLE); -typedef BOOL (WINAPI *FAvRtWaitOnThreadOrderingGroup) (HANDLE); -typedef HANDLE (WINAPI *FAvSetMmThreadCharacteristics) (LPCTSTR,LPDWORD); -typedef BOOL (WINAPI *FAvRevertMmThreadCharacteristics)(HANDLE); -typedef BOOL (WINAPI *FAvSetMmThreadPriority) (HANDLE,AVRT_PRIORITY); - -static HMODULE hDInputDLL = 0; -FAvRtCreateThreadOrderingGroup pAvRtCreateThreadOrderingGroup = NULL; -FAvRtDeleteThreadOrderingGroup pAvRtDeleteThreadOrderingGroup = NULL; -FAvRtWaitOnThreadOrderingGroup pAvRtWaitOnThreadOrderingGroup = NULL; -FAvSetMmThreadCharacteristics pAvSetMmThreadCharacteristics = NULL; -FAvRevertMmThreadCharacteristics pAvRevertMmThreadCharacteristics = NULL; -FAvSetMmThreadPriority pAvSetMmThreadPriority = NULL; - -#define _GetProc(fun, type, name) { \ - fun = (type) GetProcAddress(hDInputDLL,name); \ - if (fun == NULL) { \ - PRINT(("GetProcAddr failed for %s" ,name)); \ - return FALSE; \ - } \ - } \ - -// ------------------------------------------------------------------------------------------ -/* prototypes for functions declared in this file */ -#ifdef __cplusplus -extern "C" -{ -#endif /* __cplusplus */ -PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); -#ifdef __cplusplus -} -#endif /* __cplusplus */ -// dummy entry point for other compilers and sdks -// currently built using RC1 SDK (5600) -//#if _MSC_VER < 1400 -//PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex ) -//{ - //return paNoError; -//} -//#else - -// ------------------------------------------------------------------------------------------ -static void Terminate( struct PaUtilHostApiRepresentation *hostApi ); -static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate ); -static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, - PaStream** s, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate, - unsigned long framesPerBuffer, - PaStreamFlags streamFlags, - PaStreamCallback *streamCallback, - void *userData ); -static PaError CloseStream( PaStream* stream ); -static PaError StartStream( PaStream *stream ); -static PaError StopStream( PaStream *stream ); -static PaError AbortStream( PaStream *stream ); -static PaError IsStreamStopped( PaStream *s ); -static PaError IsStreamActive( PaStream *stream ); -static PaTime GetStreamTime( PaStream *stream ); -static double GetStreamCpuLoad( PaStream* stream ); -static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames ); -static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames ); -static signed long GetStreamReadAvailable( PaStream* stream ); -static signed long GetStreamWriteAvailable( PaStream* stream ); - -// ------------------------------------------------------------------------------------------ -/* - These are fields that can be gathered from IDevice and IAudioDevice PRIOR to Initialize, and - done in first pass i assume that neither of these will cause the Driver to "load", but again, - who knows how they implement their stuff - */ -typedef struct PaWasapiDeviceInfo -{ - // Device - IMMDevice *device; - - // from GetId - WCHAR szDeviceID[MAX_STR_LEN]; - - // from GetState - DWORD state; - - // Fields filled from IMMEndpoint'sGetDataFlow - EDataFlow flow; - - // Fields filled from IAudioDevice (_prior_ to Initialize) - // from GetDevicePeriod( - REFERENCE_TIME DefaultDevicePeriod; - REFERENCE_TIME MinimumDevicePeriod; - - // from GetMixFormat - // WAVEFORMATEX *MixFormat;//needs to be CoTaskMemFree'd after use! - - // Default format (setup through Control Panel by user) - WAVEFORMATEXTENSIBLE DefaultFormat; - - // Formfactor - EndpointFormFactor formFactor; -} -PaWasapiDeviceInfo; - -// ------------------------------------------------------------------------------------------ -/* PaWasapiHostApiRepresentation - host api datastructure specific to this implementation */ -typedef struct -{ - PaUtilHostApiRepresentation inheritedHostApiRep; - PaUtilStreamInterface callbackStreamInterface; - PaUtilStreamInterface blockingStreamInterface; - - PaUtilAllocationGroup *allocations; - - /* implementation specific data goes here */ - - //in case we later need the synch - IMMDeviceEnumerator *enumerator; - - //this is the REAL number of devices, whether they are usefull to PA or not! - UINT32 deviceCount; - - WCHAR defaultRenderer [MAX_STR_LEN]; - WCHAR defaultCapturer [MAX_STR_LEN]; - - PaWasapiDeviceInfo *devInfo; - - // Is true when WOW64 Vista Workaround is needed - BOOL useVistaWOW64Workaround; -} -PaWasapiHostApiRepresentation; - -// ------------------------------------------------------------------------------------------ -/* PaWasapiStream - a stream data structure specifically for this implementation */ -typedef struct PaWasapiSubStream -{ - IAudioClient *client; - WAVEFORMATEXTENSIBLE wavex; - UINT32 bufferSize; - REFERENCE_TIME device_latency; - REFERENCE_TIME period; - double latency_seconds; - UINT32 framesPerHostCallback; - AUDCLNT_SHAREMODE shareMode; - UINT32 streamFlags; // AUDCLNT_STREAMFLAGS_EVENTCALLBACK, ... - UINT32 flags; - - // Used by blocking interface: - UINT32 prevTime; // time ms between calls of WriteStream - UINT32 prevSleep; // time ms to sleep from frames written in previous call -} -PaWasapiSubStream; - -// ------------------------------------------------------------------------------------------ -/* PaWasapiHostProcessor - redirects processing data */ -typedef struct PaWasapiHostProcessor -{ - PaWasapiHostProcessorCallback processor; - void *userData; -} -PaWasapiHostProcessor; - -// ------------------------------------------------------------------------------------------ -typedef struct PaWasapiStream -{ - /* IMPLEMENT ME: rename this */ - PaUtilStreamRepresentation streamRepresentation; - PaUtilCpuLoadMeasurer cpuLoadMeasurer; - PaUtilBufferProcessor bufferProcessor; - - // input - PaWasapiSubStream in; - IAudioCaptureClient *cclient; - IAudioEndpointVolume *inVol; - - // output - PaWasapiSubStream out; - IAudioRenderClient *rclient; - IAudioEndpointVolume *outVol; - - // must be volatile to avoid race condition on user query while - // thread is being started - volatile BOOL running; - - PA_THREAD_ID dwThreadId; - HANDLE hThread; - HANDLE hCloseRequest; - HANDLE hThreadDone; - HANDLE hBlockingOpStreamRD; - HANDLE hBlockingOpStreamWR; - - // Host callback Output overrider - PaWasapiHostProcessor hostProcessOverrideOutput; - - // Host callback Input overrider - PaWasapiHostProcessor hostProcessOverrideInput; - - // Defines blocking/callback interface used - BOOL bBlocking; - - // Av Task (MM thread management) - HANDLE hAvTask; - - // Thread priority level - PaWasapiThreadPriority nThreadPriority; -} -PaWasapiStream; - -// Local stream methods -static void _OnStreamStop(PaWasapiStream *stream); -static void _FinishStream(PaWasapiStream *stream); - -// Local statics -static volatile BOOL g_bCOMInitialized = FALSE; - -// ------------------------------------------------------------------------------------------ -#define LogHostError(HRES) __LogHostError(HRES, __FUNCTION__, __FILE__, __LINE__) -static HRESULT __LogHostError(HRESULT res, const char *func, const char *file, int line) -{ - const char *text = NULL; - switch (res) - { - case S_OK: return res; - case E_POINTER :text ="E_POINTER"; break; - case E_INVALIDARG :text ="E_INVALIDARG"; break; - - case AUDCLNT_E_NOT_INITIALIZED :text ="AUDCLNT_E_NOT_INITIALIZED"; break; - case AUDCLNT_E_ALREADY_INITIALIZED :text ="AUDCLNT_E_ALREADY_INITIALIZED"; break; - case AUDCLNT_E_WRONG_ENDPOINT_TYPE :text ="AUDCLNT_E_WRONG_ENDPOINT_TYPE"; break; - case AUDCLNT_E_DEVICE_INVALIDATED :text ="AUDCLNT_E_DEVICE_INVALIDATED"; break; - case AUDCLNT_E_NOT_STOPPED :text ="AUDCLNT_E_NOT_STOPPED"; break; - case AUDCLNT_E_BUFFER_TOO_LARGE :text ="AUDCLNT_E_BUFFER_TOO_LARGE"; break; - case AUDCLNT_E_OUT_OF_ORDER :text ="AUDCLNT_E_OUT_OF_ORDER"; break; - case AUDCLNT_E_UNSUPPORTED_FORMAT :text ="AUDCLNT_E_UNSUPPORTED_FORMAT"; break; - case AUDCLNT_E_INVALID_SIZE :text ="AUDCLNT_E_INVALID_SIZE"; break; - case AUDCLNT_E_DEVICE_IN_USE :text ="AUDCLNT_E_DEVICE_IN_USE"; break; - case AUDCLNT_E_BUFFER_OPERATION_PENDING :text ="AUDCLNT_E_BUFFER_OPERATION_PENDING"; break; - case AUDCLNT_E_THREAD_NOT_REGISTERED :text ="AUDCLNT_E_THREAD_NOT_REGISTERED"; break; - case AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED :text ="AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED"; break; - case AUDCLNT_E_ENDPOINT_CREATE_FAILED :text ="AUDCLNT_E_ENDPOINT_CREATE_FAILED"; break; - case AUDCLNT_E_SERVICE_NOT_RUNNING :text ="AUDCLNT_E_SERVICE_NOT_RUNNING"; break; - case AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED :text ="AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED"; break; - case AUDCLNT_E_EXCLUSIVE_MODE_ONLY :text ="AUDCLNT_E_EXCLUSIVE_MODE_ONLY"; break; - case AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL :text ="AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL"; break; - case AUDCLNT_E_EVENTHANDLE_NOT_SET :text ="AUDCLNT_E_EVENTHANDLE_NOT_SET"; break; - case AUDCLNT_E_INCORRECT_BUFFER_SIZE :text ="AUDCLNT_E_INCORRECT_BUFFER_SIZE"; break; - case AUDCLNT_E_BUFFER_SIZE_ERROR :text ="AUDCLNT_E_BUFFER_SIZE_ERROR"; break; - case AUDCLNT_E_CPUUSAGE_EXCEEDED :text ="AUDCLNT_E_CPUUSAGE_EXCEEDED"; break; - case AUDCLNT_E_BUFFER_ERROR :text ="AUDCLNT_E_BUFFER_ERROR"; break; - case AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED :text ="AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED"; break; - case AUDCLNT_E_INVALID_DEVICE_PERIOD :text ="AUDCLNT_E_INVALID_DEVICE_PERIOD"; break; - - case AUDCLNT_S_BUFFER_EMPTY :text ="AUDCLNT_S_BUFFER_EMPTY"; break; - case AUDCLNT_S_THREAD_ALREADY_REGISTERED :text ="AUDCLNT_S_THREAD_ALREADY_REGISTERED"; break; - case AUDCLNT_S_POSITION_STALLED :text ="AUDCLNT_S_POSITION_STALLED"; break; - - default: - text = "UNKNOWN ERROR"; - } - PRINT(("WASAPI ERROR HRESULT: 0x%X : %s\n [FUNCTION: %s FILE: %s {LINE: %d}]\n", res, text, func, file, line)); - PA_SKELETON_SET_LAST_HOST_ERROR(res, text); - return res; -} - -// ------------------------------------------------------------------------------------------ -#define LogPaError(PAERR) __LogPaError(PAERR, __FUNCTION__, __FILE__, __LINE__) -static PaError __LogPaError(PaError err, const char *func, const char *file, int line) -{ - if (err == paNoError) - return err; - PRINT(("WASAPI ERROR PAERROR: %i : %s\n [FUNCTION: %s FILE: %s {LINE: %d}]\n", err, Pa_GetErrorText(err), func, file, line)); - return err; -} - -// ------------------------------------------------------------------------------------------ -static double nano100ToMillis(REFERENCE_TIME ref) -{ - // 1 nano = 0.000000001 seconds - //100 nano = 0.0000001 seconds - //100 nano = 0.0001 milliseconds - return ((double)ref)*0.0001; -} - -// ------------------------------------------------------------------------------------------ -static double nano100ToSeconds(REFERENCE_TIME ref) -{ - // 1 nano = 0.000000001 seconds - //100 nano = 0.0000001 seconds - //100 nano = 0.0001 milliseconds - return ((double)ref)*0.0000001; -} - -// ------------------------------------------------------------------------------------------ -static REFERENCE_TIME MillisTonano100(double ref) -{ - // 1 nano = 0.000000001 seconds - //100 nano = 0.0000001 seconds - //100 nano = 0.0001 milliseconds - return (REFERENCE_TIME)(ref/0.0001); -} - -// ------------------------------------------------------------------------------------------ -static REFERENCE_TIME SecondsTonano100(double ref) -{ - // 1 nano = 0.000000001 seconds - //100 nano = 0.0000001 seconds - //100 nano = 0.0001 milliseconds - return (REFERENCE_TIME)(ref/0.0000001); -} - -// ------------------------------------------------------------------------------------------ -// Makes Hns period from frames and sample rate -static REFERENCE_TIME MakeHnsPeriod(UINT32 nFrames, DWORD nSamplesPerSec) -{ - return (REFERENCE_TIME)((10000.0 * 1000 / nSamplesPerSec * nFrames) + 0.5); -} - -// ------------------------------------------------------------------------------------------ -// Converts PaSampleFormat to bits per sample value -static WORD PaSampleFormatToBitsPerSample(PaSampleFormat format_id) -{ - switch (format_id & ~paNonInterleaved) - { - case paFloat32: - case paInt32: return 32; - case paInt24: return 24; - case paInt16: return 16; - case paInt8: - case paUInt8: return 8; - } - return 0; -} - -// ------------------------------------------------------------------------------------------ -// Converts PaSampleFormat to bits per sample value -static WORD PaSampleFormatToBytesPerSample(PaSampleFormat format_id) -{ - return PaSampleFormatToBitsPerSample(format_id) >> 3; // 'bits/8' -} - -// ------------------------------------------------------------------------------------------ -// Converts Hns period into number of frames -static UINT32 MakeFramesFromHns(REFERENCE_TIME hnsPeriod, UINT32 nSamplesPerSec) -{ - UINT32 nFrames = (UINT32)( // frames = - 1.0 * hnsPeriod * // hns * - nSamplesPerSec / // (frames / s) / - 1000 / // (ms / s) / - 10000 // (hns / s) / - + 0.5 // rounding - ); - return nFrames; -} - -// ------------------------------------------------------------------------------------------ -// Aligns v backwards -static UINT32 ALIGN_BWD(UINT32 v, UINT32 align) -{ - return ((v - (align ? v % align : 0))); -} - -// ------------------------------------------------------------------------------------------ -// Aligns WASAPI buffer to 128 byte packet boundary. HD Audio will fail to play if buffer -// is misaligned. This problem was solved in Windows 7 were AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED -// is thrown although we must align for Vista anyway. -static UINT32 AlignFramesPerBuffer(UINT32 nFrames, UINT32 nSamplesPerSec, UINT32 nBlockAlign) -{ -#define HDA_PACKET_SIZE 128 - - long packets_total = 10000 * (nSamplesPerSec * nBlockAlign / HDA_PACKET_SIZE); - long frame_bytes = nFrames * nBlockAlign; - long packets; - - // align to packet size - frame_bytes = ALIGN_BWD(frame_bytes, HDA_PACKET_SIZE); - nFrames = frame_bytes / nBlockAlign; - packets = frame_bytes / HDA_PACKET_SIZE; - - // align to packets count - while (packets && ((packets_total % packets) != 0)) - { - --packets; - } - - frame_bytes = packets * HDA_PACKET_SIZE; - nFrames = frame_bytes / nBlockAlign; - - return nFrames; -} - -// ------------------------------------------------------------------------------------------ -static UINT32 GetFramesSleepTime(UINT32 nFrames, UINT32 nSamplesPerSec) -{ - REFERENCE_TIME nDuration; - if (nSamplesPerSec == 0) - return 0; -#define REFTIMES_PER_SEC 10000000 -#define REFTIMES_PER_MILLISEC 10000 - // Calculate the actual duration of the allocated buffer. - nDuration = (REFERENCE_TIME)((double)REFTIMES_PER_SEC * nFrames / nSamplesPerSec); - return (UINT32)(nDuration/REFTIMES_PER_MILLISEC/2); -} - -// ------------------------------------------------------------------------------------------ -static BOOL SetupAVRT() -{ - hDInputDLL = LoadLibraryA("avrt.dll"); - if (hDInputDLL == NULL) - return FALSE; - - _GetProc(pAvRtCreateThreadOrderingGroup, FAvRtCreateThreadOrderingGroup, "AvRtCreateThreadOrderingGroup"); - _GetProc(pAvRtDeleteThreadOrderingGroup, FAvRtDeleteThreadOrderingGroup, "AvRtDeleteThreadOrderingGroup"); - _GetProc(pAvRtWaitOnThreadOrderingGroup, FAvRtWaitOnThreadOrderingGroup, "AvRtWaitOnThreadOrderingGroup"); - _GetProc(pAvSetMmThreadCharacteristics, FAvSetMmThreadCharacteristics, "AvSetMmThreadCharacteristicsA"); - _GetProc(pAvRevertMmThreadCharacteristics,FAvRevertMmThreadCharacteristics,"AvRevertMmThreadCharacteristics"); - _GetProc(pAvSetMmThreadPriority, FAvSetMmThreadPriority, "AvSetMmThreadPriority"); - - return pAvRtCreateThreadOrderingGroup && - pAvRtDeleteThreadOrderingGroup && - pAvRtWaitOnThreadOrderingGroup && - pAvSetMmThreadCharacteristics && - pAvRevertMmThreadCharacteristics && - pAvSetMmThreadPriority; -} - -// ------------------------------------------------------------------------------------------ -static void CloseAVRT() -{ - if (hDInputDLL != NULL) - FreeLibrary(hDInputDLL); - hDInputDLL = NULL; -} - -// ------------------------------------------------------------------------------------------ -static BOOL IsWow64() -{ - // http://msdn.microsoft.com/en-us/library/ms684139(VS.85).aspx - - typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL); - LPFN_ISWOW64PROCESS fnIsWow64Process; - - BOOL bIsWow64 = FALSE; - - // IsWow64Process is not available on all supported versions of Windows. - // Use GetModuleHandle to get a handle to the DLL that contains the function - // and GetProcAddress to get a pointer to the function if available. - - fnIsWow64Process = (LPFN_ISWOW64PROCESS) GetProcAddress( - GetModuleHandle(TEXT("kernel32")), TEXT("IsWow64Process")); - - if (fnIsWow64Process == NULL) - return FALSE; - - if (!fnIsWow64Process(GetCurrentProcess(), &bIsWow64)) - return FALSE; - - return bIsWow64; -} - -// ------------------------------------------------------------------------------------------ -typedef enum EWindowsVersion -{ - WINDOWS_UNKNOWN, - WINDOWS_VISTA_SERVER2008, - WINDOWS_7_SERVER2008R2 -} -EWindowsVersion; -// The function is limited to Vista/7 mostly as we need just to find out Vista/WOW64 combination -// in order to use WASAPI WOW64 workarounds. -static EWindowsVersion GetWindowsVersion() -{ - DWORD dwVersion = 0; - DWORD dwMajorVersion = 0; - DWORD dwMinorVersion = 0; - DWORD dwBuild = 0; - - typedef DWORD (WINAPI *LPFN_GETVERSION)(VOID); - LPFN_GETVERSION fnGetVersion; - - fnGetVersion = (LPFN_GETVERSION) GetProcAddress( - GetModuleHandle(TEXT("kernel32")), TEXT("GetVersion")); - - if (fnGetVersion == NULL) - return WINDOWS_UNKNOWN; - - dwVersion = fnGetVersion(); - - // Get the Windows version - dwMajorVersion = (DWORD)(LOBYTE(LOWORD(dwVersion))); - dwMinorVersion = (DWORD)(HIBYTE(LOWORD(dwVersion))); - - // Get the build number - if (dwVersion < 0x80000000) - dwBuild = (DWORD)(HIWORD(dwVersion)); - - switch (dwMajorVersion) - { - case 6: - switch (dwMinorVersion) - { - case 0: - return WINDOWS_VISTA_SERVER2008; - case 1: - return WINDOWS_7_SERVER2008R2; - } - } - - return WINDOWS_UNKNOWN; -} - -// ------------------------------------------------------------------------------------------ -static BOOL UseWOW64VistaWorkaround() -{ - return (IsWow64() && (GetWindowsVersion() == WINDOWS_VISTA_SERVER2008)); -} - -// ------------------------------------------------------------------------------------------ -PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex ) -{ - PaError result = paNoError; - PaWasapiHostApiRepresentation *paWasapi; - PaDeviceInfo *deviceInfoArray; - HRESULT hr = S_OK; - IMMDeviceCollection* pEndPoints = NULL; - UINT i; - - if (!SetupAVRT()) - { - PRINT(("WASAPI: No AVRT! (not VISTA?)")); - return paNoError; - } - - /* - If COM is already initialized CoInitialize will either return - FALSE, or RPC_E_CHANGED_MODE if it was initialised in a different - threading mode. In either case we shouldn't consider it an error - but we need to be careful to not call CoUninitialize() if - RPC_E_CHANGED_MODE was returned. - */ - hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); - if (FAILED(hr) && (hr != RPC_E_CHANGED_MODE)) - { - PRINT(("WASAPI: failed CoInitialize")); - return paUnanticipatedHostError; - } - if (hr != RPC_E_CHANGED_MODE) - g_bCOMInitialized = TRUE; - - paWasapi = (PaWasapiHostApiRepresentation *)PaUtil_AllocateMemory( sizeof(PaWasapiHostApiRepresentation) ); - if (paWasapi == NULL) - { - result = paInsufficientMemory; - goto error; - } - - paWasapi->allocations = PaUtil_CreateAllocationGroup(); - if (paWasapi->allocations == NULL) - { - result = paInsufficientMemory; - goto error; - } - - *hostApi = &paWasapi->inheritedHostApiRep; - (*hostApi)->info.structVersion = 1; - (*hostApi)->info.type = paWASAPI; - (*hostApi)->info.name = "Windows WASAPI"; - (*hostApi)->info.deviceCount = 0; - (*hostApi)->info.defaultInputDevice = paNoDevice; - (*hostApi)->info.defaultOutputDevice = paNoDevice; - - paWasapi->enumerator = NULL; - hr = CoCreateInstance(&pa_CLSID_IMMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, - &pa_IID_IMMDeviceEnumerator, (void **)&paWasapi->enumerator); - IF_FAILED_JUMP(hr, error); - - // getting default device ids in the eMultimedia "role" - { - { - IMMDevice *defaultRenderer = NULL; - hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(paWasapi->enumerator, eRender, eMultimedia, &defaultRenderer); - if (hr != S_OK) - { - if (hr != E_NOTFOUND) - IF_FAILED_JUMP(hr, error); - } - else - { - WCHAR *pszDeviceId = NULL; - hr = IMMDevice_GetId(defaultRenderer, &pszDeviceId); - IF_FAILED_JUMP(hr, error); - wcsncpy(paWasapi->defaultRenderer, pszDeviceId, MAX_STR_LEN-1); - CoTaskMemFree(pszDeviceId); - IMMDevice_Release(defaultRenderer); - } - } - - { - IMMDevice *defaultCapturer = NULL; - hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(paWasapi->enumerator, eCapture, eMultimedia, &defaultCapturer); - if (hr != S_OK) - { - if (hr != E_NOTFOUND) - IF_FAILED_JUMP(hr, error); - } - else - { - WCHAR *pszDeviceId = NULL; - hr = IMMDevice_GetId(defaultCapturer, &pszDeviceId); - IF_FAILED_JUMP(hr, error); - wcsncpy(paWasapi->defaultCapturer, pszDeviceId, MAX_STR_LEN-1); - CoTaskMemFree(pszDeviceId); - IMMDevice_Release(defaultCapturer); - } - } - } - - hr = IMMDeviceEnumerator_EnumAudioEndpoints(paWasapi->enumerator, eAll, DEVICE_STATE_ACTIVE, &pEndPoints); - IF_FAILED_JUMP(hr, error); - - hr = IMMDeviceCollection_GetCount(pEndPoints, &paWasapi->deviceCount); - IF_FAILED_JUMP(hr, error); - - paWasapi->devInfo = (PaWasapiDeviceInfo *)malloc(sizeof(PaWasapiDeviceInfo) * paWasapi->deviceCount); - for (i = 0; i < paWasapi->deviceCount; ++i) - memset(&paWasapi->devInfo[i], 0, sizeof(PaWasapiDeviceInfo)); - - if (paWasapi->deviceCount > 0) - { - (*hostApi)->deviceInfos = (PaDeviceInfo **)PaUtil_GroupAllocateMemory( - paWasapi->allocations, sizeof(PaDeviceInfo *) * paWasapi->deviceCount); - if ((*hostApi)->deviceInfos == NULL) - { - result = paInsufficientMemory; - goto error; - } - - /* allocate all device info structs in a contiguous block */ - deviceInfoArray = (PaDeviceInfo *)PaUtil_GroupAllocateMemory( - paWasapi->allocations, sizeof(PaDeviceInfo) * paWasapi->deviceCount); - if (deviceInfoArray == NULL) - { - result = paInsufficientMemory; - goto error; - } - - for (i = 0; i < paWasapi->deviceCount; ++i) - { - DWORD state = 0; - PaDeviceInfo *deviceInfo = &deviceInfoArray[i]; - deviceInfo->structVersion = 2; - deviceInfo->hostApi = hostApiIndex; - - PA_DEBUG(("WASAPI: device i: %d\n", i)); - - hr = IMMDeviceCollection_Item(pEndPoints, i, &paWasapi->devInfo[i].device); - IF_FAILED_JUMP(hr, error); - - // getting ID - { - WCHAR *pszDeviceId = NULL; - hr = IMMDevice_GetId(paWasapi->devInfo[i].device, &pszDeviceId); - IF_FAILED_JUMP(hr, error); - wcsncpy(paWasapi->devInfo[i].szDeviceID, pszDeviceId, MAX_STR_LEN-1); - CoTaskMemFree(pszDeviceId); - - if (lstrcmpW(paWasapi->devInfo[i].szDeviceID, paWasapi->defaultCapturer) == 0) - {// we found the default input! - (*hostApi)->info.defaultInputDevice = (*hostApi)->info.deviceCount; - } - if (lstrcmpW(paWasapi->devInfo[i].szDeviceID, paWasapi->defaultRenderer) == 0) - {// we found the default output! - (*hostApi)->info.defaultOutputDevice = (*hostApi)->info.deviceCount; - } - } - - hr = IMMDevice_GetState(paWasapi->devInfo[i].device, &paWasapi->devInfo[i].state); - IF_FAILED_JUMP(hr, error); - - if (paWasapi->devInfo[i].state != DEVICE_STATE_ACTIVE) - { - PRINT(("WASAPI device: %d is not currently available (state:%d)\n",i,state)); - } - - { - IPropertyStore *pProperty; - hr = IMMDevice_OpenPropertyStore(paWasapi->devInfo[i].device, STGM_READ, &pProperty); - IF_FAILED_JUMP(hr, error); - - // "Friendly" Name - { - char *deviceName; - PROPVARIANT value; - PropVariantInit(&value); - hr = IPropertyStore_GetValue(pProperty, &PKEY_Device_FriendlyName, &value); - IF_FAILED_JUMP(hr, error); - deviceInfo->name = NULL; - deviceName = (char *)PaUtil_GroupAllocateMemory(paWasapi->allocations, MAX_STR_LEN + 1); - if (deviceName == NULL) - { - result = paInsufficientMemory; - goto error; - } - if (value.pwszVal) - wcstombs(deviceName, value.pwszVal, MAX_STR_LEN-1); - else - _snprintf(deviceName, MAX_STR_LEN-1, "baddev%d", i); - deviceInfo->name = deviceName; - PropVariantClear(&value); - } - - // Default format - { - PROPVARIANT value; - PropVariantInit(&value); - hr = IPropertyStore_GetValue(pProperty, &PKEY_AudioEngine_DeviceFormat, &value); - IF_FAILED_JUMP(hr, error); - memcpy(&paWasapi->devInfo[i].DefaultFormat, value.blob.pBlobData, min(sizeof(paWasapi->devInfo[i].DefaultFormat), value.blob.cbSize)); - // cleanup - PropVariantClear(&value); - } - - // Formfactor - { - PROPVARIANT value; - PropVariantInit(&value); - hr = IPropertyStore_GetValue(pProperty, &PKEY_AudioEndpoint_FormFactor, &value); - IF_FAILED_JUMP(hr, error); - // set - #if defined(DUMMYUNIONNAME) && defined(NONAMELESSUNION) - // avoid breaking strict-aliasing rules in such line: (EndpointFormFactor)(*((UINT *)(((WORD *)&value.wReserved3)+1))); - UINT v; - memcpy(&v, (((WORD *)&value.wReserved3)+1), sizeof(v)); - paWasapi->devInfo[i].formFactor = (EndpointFormFactor)v; - #else - paWasapi->devInfo[i].formFactor = (EndpointFormFactor)value.uintVal; - #endif - PA_DEBUG(("WASAPI: device[%s] form-factor: %d\n", deviceInfo->name, paWasapi->devInfo[i].formFactor)); - // cleanup - PropVariantClear(&value); - } - - SAFE_RELEASE(pProperty); - } - - - // Endpoint data - { - IMMEndpoint *endpoint = NULL; - hr = IMMDevice_QueryInterface(paWasapi->devInfo[i].device, &pa_IID_IMMEndpoint, (void **)&endpoint); - if (SUCCEEDED(hr)) - { - hr = IMMEndpoint_GetDataFlow(endpoint, &paWasapi->devInfo[i].flow); - SAFE_RELEASE(endpoint); - } - } - - // Getting a temporary IAudioClient for more fields - // we make sure NOT to call Initialize yet! - { - IAudioClient *tmpClient = NULL; - - hr = IMMDevice_Activate(paWasapi->devInfo[i].device, &pa_IID_IAudioClient, - CLSCTX_INPROC_SERVER, NULL, (void **)&tmpClient); - IF_FAILED_JUMP(hr, error); - - hr = IAudioClient_GetDevicePeriod(tmpClient, - &paWasapi->devInfo[i].DefaultDevicePeriod, - &paWasapi->devInfo[i].MinimumDevicePeriod); - IF_FAILED_JUMP(hr, error); - - //hr = tmpClient->GetMixFormat(&paWasapi->devInfo[i].MixFormat); - - // Release client - SAFE_RELEASE(tmpClient); - - if (hr != S_OK) - { - //davidv: this happened with my hardware, previously for that same device in DirectSound: - //Digital Output (Realtek AC'97 Audio)'s GUID: {0x38f2cf50,0x7b4c,0x4740,0x86,0xeb,0xd4,0x38,0x66,0xd8,0xc8, 0x9f} - //so something must be _really_ wrong with this device, TODO handle this better. We kind of need GetMixFormat - LogHostError(hr); - goto error; - } - } - - // we can now fill in portaudio device data - deviceInfo->maxInputChannels = 0; //for now - deviceInfo->maxOutputChannels = 0; //for now - switch (paWasapi->devInfo[i].flow) - { - case eRender: - // WASAPI accepts exact channels count - deviceInfo->maxOutputChannels = paWasapi->devInfo[i].DefaultFormat.Format.nChannels; - deviceInfo->defaultHighOutputLatency = nano100ToSeconds(paWasapi->devInfo[i].DefaultDevicePeriod); - deviceInfo->defaultLowOutputLatency = nano100ToSeconds(paWasapi->devInfo[i].MinimumDevicePeriod); - break; - case eCapture: - // WASAPI accepts exact channels count - deviceInfo->maxInputChannels = paWasapi->devInfo[i].DefaultFormat.Format.nChannels; - deviceInfo->defaultHighInputLatency = nano100ToSeconds(paWasapi->devInfo[i].DefaultDevicePeriod); - deviceInfo->defaultLowInputLatency = nano100ToSeconds(paWasapi->devInfo[i].MinimumDevicePeriod); - break; - default: - PRINT(("WASAPI: device %d bad Data FLow! \n",i)); - goto error; - break; - } - - deviceInfo->defaultSampleRate = paWasapi->devInfo[i].DefaultFormat.Format.nSamplesPerSec; - - (*hostApi)->deviceInfos[i] = deviceInfo; - ++(*hostApi)->info.deviceCount; - } - } - - (*hostApi)->Terminate = Terminate; - (*hostApi)->OpenStream = OpenStream; - (*hostApi)->IsFormatSupported = IsFormatSupported; - - PaUtil_InitializeStreamInterface( &paWasapi->callbackStreamInterface, CloseStream, StartStream, - StopStream, AbortStream, IsStreamStopped, IsStreamActive, - GetStreamTime, GetStreamCpuLoad, - PaUtil_DummyRead, PaUtil_DummyWrite, - PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable ); - - PaUtil_InitializeStreamInterface( &paWasapi->blockingStreamInterface, CloseStream, StartStream, - StopStream, AbortStream, IsStreamStopped, IsStreamActive, - GetStreamTime, PaUtil_DummyGetCpuLoad, - ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable ); - - - // findout if platform workaround is required - paWasapi->useVistaWOW64Workaround = UseWOW64VistaWorkaround(); - - SAFE_RELEASE(pEndPoints); - - PRINT(("WASAPI: initialized ok")); - - return paNoError; - -error: - - PRINT(("WASAPI: failed %s error[%d|%s]", __FUNCTION__, result, Pa_GetErrorText(result))); - - SAFE_RELEASE(pEndPoints); - - Terminate((PaUtilHostApiRepresentation *)paWasapi); - - return result; -} - -// ------------------------------------------------------------------------------------------ -static void Terminate( PaUtilHostApiRepresentation *hostApi ) -{ - UINT i; - PaWasapiHostApiRepresentation *paWasapi = (PaWasapiHostApiRepresentation*)hostApi; - if (paWasapi == NULL) - return; - - // Release IMMDeviceEnumerator - SAFE_RELEASE(paWasapi->enumerator); - - for (i = 0; i < paWasapi->deviceCount; ++i) - { - PaWasapiDeviceInfo *info = &paWasapi->devInfo[i]; - SAFE_RELEASE(info->device); - - //if (info->MixFormat) - // CoTaskMemFree(info->MixFormat); - } - free(paWasapi->devInfo); - - if (paWasapi->allocations) - { - PaUtil_FreeAllAllocations(paWasapi->allocations); - PaUtil_DestroyAllocationGroup(paWasapi->allocations); - } - - PaUtil_FreeMemory(paWasapi); - - // Close AVRT - CloseAVRT(); - - // Uninit COM - if (g_bCOMInitialized) - CoUninitialize(); -} - -// ------------------------------------------------------------------------------------------ -static PaWasapiHostApiRepresentation *_GetHostApi(PaError *_error) -{ - PaError error; - - PaUtilHostApiRepresentation *pApi; - if ((error = PaUtil_GetHostApiRepresentation(&pApi, paWASAPI)) != paNoError) - { - if (_error != NULL) - (*_error) = error; - - return NULL; - } - return (PaWasapiHostApiRepresentation *)pApi; -} - -// ------------------------------------------------------------------------------------------ -int PaWasapi_GetDeviceDefaultFormat( void *pFormat, unsigned int nFormatSize, PaDeviceIndex nDevice ) -{ - PaError ret; - PaWasapiHostApiRepresentation *paWasapi; - UINT32 size; - PaDeviceIndex index; - - if (pFormat == NULL) - return paBadBufferPtr; - if (nFormatSize <= 0) - return paBufferTooSmall; - - // Get API - paWasapi = _GetHostApi(&ret); - if (paWasapi == NULL) - return ret; - - // Get device index - ret = PaUtil_DeviceIndexToHostApiDeviceIndex(&index, nDevice, &paWasapi->inheritedHostApiRep); - if (ret != paNoError) - return ret; - - // Validate index - if ((UINT32)index >= paWasapi->deviceCount) - return paInvalidDevice; - - size = min(nFormatSize, (UINT32)sizeof(paWasapi->devInfo[ index ].DefaultFormat)); - memcpy(pFormat, &paWasapi->devInfo[ index ].DefaultFormat, size); - - return size; -} - -// ------------------------------------------------------------------------------------------ -int PaWasapi_GetDeviceRole( PaDeviceIndex nDevice ) -{ - PaError ret; - PaDeviceIndex index; - - // Get API - PaWasapiHostApiRepresentation *paWasapi = _GetHostApi(&ret); - if (paWasapi == NULL) - return ret; - - // Get device index - ret = PaUtil_DeviceIndexToHostApiDeviceIndex(&index, nDevice, &paWasapi->inheritedHostApiRep); - if (ret != paNoError) - return ret; - - // Validate index - if ((UINT32)index >= paWasapi->deviceCount) - return paInvalidDevice; - - return paWasapi->devInfo[ index ].formFactor; -} - -// ------------------------------------------------------------------------------------------ -static void LogWAVEFORMATEXTENSIBLE(const WAVEFORMATEXTENSIBLE *in) -{ - const WAVEFORMATEX *old = (WAVEFORMATEX *)in; - switch (old->wFormatTag) - { - case WAVE_FORMAT_EXTENSIBLE: { - - PRINT(("wFormatTag=WAVE_FORMAT_EXTENSIBLE\n")); - - if (IsEqualGUID(&in->SubFormat, &pa_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) - { - PRINT(("SubFormat=KSDATAFORMAT_SUBTYPE_IEEE_FLOAT\n")); - } - else - if (IsEqualGUID(&in->SubFormat, &pa_KSDATAFORMAT_SUBTYPE_PCM)) - { - PRINT(("SubFormat=KSDATAFORMAT_SUBTYPE_PCM\n")); - } - else - { - PRINT(("SubFormat=CUSTOM GUID{%d:%d:%d:%d%d%d%d%d%d%d%d}\n", - in->SubFormat.Data1, - in->SubFormat.Data2, - in->SubFormat.Data3, - (int)in->SubFormat.Data4[0], - (int)in->SubFormat.Data4[1], - (int)in->SubFormat.Data4[2], - (int)in->SubFormat.Data4[3], - (int)in->SubFormat.Data4[4], - (int)in->SubFormat.Data4[5], - (int)in->SubFormat.Data4[6], - (int)in->SubFormat.Data4[7])); - } - PRINT(("Samples.wValidBitsPerSample=%d\n", in->Samples.wValidBitsPerSample)); - PRINT(("dwChannelMask =0x%X\n",in->dwChannelMask)); - - break; } - - case WAVE_FORMAT_PCM: PRINT(("wFormatTag=WAVE_FORMAT_PCM\n")); break; - case WAVE_FORMAT_IEEE_FLOAT: PRINT(("wFormatTag=WAVE_FORMAT_IEEE_FLOAT\n")); break; - default : PRINT(("wFormatTag=UNKNOWN(%d)\n",old->wFormatTag)); break; - } - - PRINT(("nChannels =%d\n",old->nChannels)); - PRINT(("nSamplesPerSec =%d\n",old->nSamplesPerSec)); - PRINT(("nAvgBytesPerSec=%d\n",old->nAvgBytesPerSec)); - PRINT(("nBlockAlign =%d\n",old->nBlockAlign)); - PRINT(("wBitsPerSample =%d\n",old->wBitsPerSample)); - PRINT(("cbSize =%d\n",old->cbSize)); -} - -// ------------------------------------------------------------------------------------------ -static PaSampleFormat waveformatToPaFormat(const WAVEFORMATEXTENSIBLE *in) -{ - const WAVEFORMATEX *old = (WAVEFORMATEX *)in; - - switch (old->wFormatTag) - { - case WAVE_FORMAT_EXTENSIBLE: { - if (IsEqualGUID(&in->SubFormat, &pa_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) - { - if (in->Samples.wValidBitsPerSample == 32) - return paFloat32; - } - else - if (IsEqualGUID(&in->SubFormat, &pa_KSDATAFORMAT_SUBTYPE_PCM)) - { - switch (old->wBitsPerSample) - { - case 32: return paInt32; - case 24: return paInt24; - case 8: return paUInt8; - case 16: return paInt16; - } - } - break; } - - case WAVE_FORMAT_IEEE_FLOAT: - return paFloat32; - - case WAVE_FORMAT_PCM: { - switch (old->wBitsPerSample) - { - case 32: return paInt32; - case 24: return paInt24; - case 8: return paUInt8; - case 16: return paInt16; - } - break; } - } - - return paCustomFormat; -} - -// ------------------------------------------------------------------------------------------ -static PaError MakeWaveFormatFromParams(WAVEFORMATEXTENSIBLE *wavex, const PaStreamParameters *params, - double sampleRate) -{ - WORD bitsPerSample; - WAVEFORMATEX *old; - DWORD channelMask = 0; - PaWasapiStreamInfo *streamInfo = (PaWasapiStreamInfo *)params->hostApiSpecificStreamInfo; - - // Get user assigned channel mask - if ((streamInfo != NULL) && (streamInfo->flags & paWinWasapiUseChannelMask)) - channelMask = streamInfo->channelMask; - - // Convert PaSampleFormat to bits per sample - if ((bitsPerSample = PaSampleFormatToBitsPerSample(params->sampleFormat)) == 0) - return paSampleFormatNotSupported; - - memset(wavex, 0, sizeof(*wavex)); - - old = (WAVEFORMATEX *)wavex; - old->nChannels = (WORD)params->channelCount; - old->nSamplesPerSec = (DWORD)sampleRate; - if ((old->wBitsPerSample = bitsPerSample) > 16) - { - old->wBitsPerSample = 32; // 20 or 24 bits must go in 32 bit containers (ints) - } - old->nBlockAlign = (old->nChannels * (old->wBitsPerSample/8)); - old->nAvgBytesPerSec = (old->nSamplesPerSec * old->nBlockAlign); - - //WAVEFORMATEX - /*if ((params->channelCount <= 2) && ((bitsPerSample == 16) || (bitsPerSample == 8))) - { - old->cbSize = 0; - old->wFormatTag = WAVE_FORMAT_PCM; - } - //WAVEFORMATEXTENSIBLE - else*/ - { - old->wFormatTag = WAVE_FORMAT_EXTENSIBLE; - old->cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX); - - if ((params->sampleFormat & ~paNonInterleaved) == paFloat32) - wavex->SubFormat = pa_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; - else - wavex->SubFormat = pa_KSDATAFORMAT_SUBTYPE_PCM; - - wavex->Samples.wValidBitsPerSample = bitsPerSample; //no extra padding! - - // Set channel mask - if (channelMask != 0) - { - wavex->dwChannelMask = channelMask; - } - else - { - switch (params->channelCount) - { - case 1: wavex->dwChannelMask = KSAUDIO_SPEAKER_MONO; break; - case 2: wavex->dwChannelMask = KSAUDIO_SPEAKER_STEREO; break; - case 4: wavex->dwChannelMask = KSAUDIO_SPEAKER_QUAD; break; - case 6: wavex->dwChannelMask = KSAUDIO_SPEAKER_5POINT1; break; - case 8: wavex->dwChannelMask = KSAUDIO_SPEAKER_7POINT1; break; - default: wavex->dwChannelMask = 0; break; - } - } - } - return paNoError; -} - -// ------------------------------------------------------------------------------------------ -static void wasapiFillWFEXT( WAVEFORMATEXTENSIBLE* pwfext, PaSampleFormat sampleFormat, double sampleRate, int channelCount) -{ - PA_DEBUG(( "sampleFormat = %lx\n" , sampleFormat )); - PA_DEBUG(( "sampleRate = %f\n" , sampleRate )); - PA_DEBUG(( "chanelCount = %d\n", channelCount )); - - pwfext->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; - pwfext->Format.nChannels = (WORD)channelCount; - pwfext->Format.nSamplesPerSec = (DWORD)sampleRate; - if(channelCount == 1) - pwfext->dwChannelMask = KSAUDIO_SPEAKER_DIRECTOUT; - else - pwfext->dwChannelMask = KSAUDIO_SPEAKER_STEREO; - if(sampleFormat == paFloat32) - { - pwfext->Format.nBlockAlign = (WORD)(channelCount * 4); - pwfext->Format.wBitsPerSample = 32; - pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX); - pwfext->Samples.wValidBitsPerSample = 32; - pwfext->SubFormat = pa_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; - } - else if(sampleFormat == paInt32) - { - pwfext->Format.nBlockAlign = (WORD)(channelCount * 4); - pwfext->Format.wBitsPerSample = 32; - pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX); - pwfext->Samples.wValidBitsPerSample = 32; - pwfext->SubFormat = pa_KSDATAFORMAT_SUBTYPE_PCM; - } - else if(sampleFormat == paInt24) - { - pwfext->Format.nBlockAlign = (WORD)(channelCount * 4); - pwfext->Format.wBitsPerSample = 32; // 24-bit in 32-bit int container - pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX); - pwfext->Samples.wValidBitsPerSample = 24; - pwfext->SubFormat = pa_KSDATAFORMAT_SUBTYPE_PCM; - } - else if(sampleFormat == paInt16) - { - pwfext->Format.nBlockAlign = (WORD)(channelCount * 2); - pwfext->Format.wBitsPerSample = 16; - pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX); - pwfext->Samples.wValidBitsPerSample = 16; - pwfext->SubFormat = pa_KSDATAFORMAT_SUBTYPE_PCM; - } - pwfext->Format.nAvgBytesPerSec = pwfext->Format.nSamplesPerSec * pwfext->Format.nBlockAlign; -} - -// ------------------------------------------------------------------------------------------ -static PaError GetClosestFormat(IAudioClient *myClient, double sampleRate, - const PaStreamParameters *params, AUDCLNT_SHAREMODE shareMode, WAVEFORMATEXTENSIBLE *outWavex) -{ - PaError answer = paInvalidSampleRate; - WAVEFORMATEX *sharedClosestMatch = NULL; - HRESULT hr = !S_OK; - - MakeWaveFormatFromParams(outWavex, params, sampleRate); - - hr = IAudioClient_IsFormatSupported(myClient, shareMode, &outWavex->Format, (shareMode == AUDCLNT_SHAREMODE_SHARED ? &sharedClosestMatch : NULL)); - if (hr == S_OK) - answer = paFormatIsSupported; - else - if (sharedClosestMatch) - { - WORD bitsPerSample; - WAVEFORMATEXTENSIBLE *ext = (WAVEFORMATEXTENSIBLE*)sharedClosestMatch; - - GUID subf_guid = GUID_NULL; - if (sharedClosestMatch->wFormatTag == WAVE_FORMAT_EXTENSIBLE) - { - memcpy(outWavex, sharedClosestMatch, sizeof(WAVEFORMATEXTENSIBLE)); - subf_guid = ext->SubFormat; - } - else - memcpy(outWavex, sharedClosestMatch, sizeof(WAVEFORMATEX)); - - CoTaskMemFree(sharedClosestMatch); - - // Make supported by default - answer = paFormatIsSupported; - - // Validate SampleRate - if ((DWORD)sampleRate != outWavex->Format.nSamplesPerSec) - return paInvalidSampleRate; - - // Validate Channel count - if ((WORD)params->channelCount != outWavex->Format.nChannels) - return paInvalidChannelCount; - - // Validate Sample format - if ((bitsPerSample = PaSampleFormatToBitsPerSample(params->sampleFormat)) == 0) - return paSampleFormatNotSupported; - - // Validate Sample format: bit size (WASAPI does not limit 'bit size') - //if (bitsPerSample != outWavex->Format.wBitsPerSample) - // return paSampleFormatNotSupported; - - // Validate Sample format: paFloat32 (WASAPI does not limit 'bit type') - //if ((params->sampleFormat == paFloat32) && (subf_guid != KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) - // return paSampleFormatNotSupported; - - // Validate Sample format: paInt32 (WASAPI does not limit 'bit type') - //if ((params->sampleFormat == paInt32) && (subf_guid != KSDATAFORMAT_SUBTYPE_PCM)) - // return paSampleFormatNotSupported; - } - else - { - //it doesnt suggest anything?? ok lets show it the MENU! - int i; - -#define FORMATTESTS 3 -static const int BestToWorst[FORMATTESTS]={ paFloat32, paInt24, paInt16 }; - - //ok fun time as with pa_win_mme, we know only a refusal of the user-requested - //sampleRate+num Channel is disastrous, as the portaudio buffer processor converts between anything - //so lets only use the number - for (i = 0; i < FORMATTESTS; ++i) - { - WAVEFORMATEXTENSIBLE ext = { 0 }; - wasapiFillWFEXT(&ext,BestToWorst[i],sampleRate,params->channelCount); - - hr = IAudioClient_IsFormatSupported(myClient, shareMode, &ext.Format, (shareMode == AUDCLNT_SHAREMODE_SHARED ? &sharedClosestMatch : NULL)); - if (hr == S_OK) - { - memcpy(outWavex,&ext,sizeof(WAVEFORMATEXTENSIBLE)); - answer = paFormatIsSupported; - break; - } - } - - if (answer != paFormatIsSupported) - { - // try MIX format? - // why did it HAVE to come to this .... - WAVEFORMATEX pcm16WaveFormat = { 0 }; - pcm16WaveFormat.wFormatTag = WAVE_FORMAT_PCM; - pcm16WaveFormat.nChannels = 2; - pcm16WaveFormat.nSamplesPerSec = (DWORD)sampleRate; - pcm16WaveFormat.nBlockAlign = 4; - pcm16WaveFormat.nAvgBytesPerSec = pcm16WaveFormat.nSamplesPerSec*pcm16WaveFormat.nBlockAlign; - pcm16WaveFormat.wBitsPerSample = 16; - pcm16WaveFormat.cbSize = 0; - - hr = IAudioClient_IsFormatSupported(myClient, shareMode, &pcm16WaveFormat, (shareMode == AUDCLNT_SHAREMODE_SHARED ? &sharedClosestMatch : NULL)); - if (hr == S_OK) - { - memcpy(outWavex,&pcm16WaveFormat,sizeof(WAVEFORMATEX)); - answer = paFormatIsSupported; - } - } - - LogHostError(hr); - } - - return answer; -} - -// ------------------------------------------------------------------------------------------ -static PaError IsStreamParamsValid(struct PaUtilHostApiRepresentation *hostApi, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate) -{ - if (hostApi == NULL) - return paHostApiNotFound; - if ((UINT32)sampleRate == 0) - return paInvalidSampleRate; - - if (inputParameters != NULL) - { - /* all standard sample formats are supported by the buffer adapter, - this implementation doesn't support any custom sample formats */ - if (inputParameters->sampleFormat & paCustomFormat) - return paSampleFormatNotSupported; - - /* unless alternate device specification is supported, reject the use of - paUseHostApiSpecificDeviceSpecification */ - if (inputParameters->device == paUseHostApiSpecificDeviceSpecification) - return paInvalidDevice; - - /* check that input device can support inputChannelCount */ - if (inputParameters->channelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels) - return paInvalidChannelCount; - - /* validate inputStreamInfo */ - if (inputParameters->hostApiSpecificStreamInfo) - { - PaWasapiStreamInfo *inputStreamInfo = (PaWasapiStreamInfo *)inputParameters->hostApiSpecificStreamInfo; - if ((inputStreamInfo->size != sizeof(PaWasapiStreamInfo)) || - (inputStreamInfo->version != 1) || - (inputStreamInfo->hostApiType != paWASAPI)) - { - return paIncompatibleHostApiSpecificStreamInfo; - } - } - - return paNoError; - } - - if (outputParameters != NULL) - { - /* all standard sample formats are supported by the buffer adapter, - this implementation doesn't support any custom sample formats */ - if (outputParameters->sampleFormat & paCustomFormat) - return paSampleFormatNotSupported; - - /* unless alternate device specification is supported, reject the use of - paUseHostApiSpecificDeviceSpecification */ - if (outputParameters->device == paUseHostApiSpecificDeviceSpecification) - return paInvalidDevice; - - /* check that output device can support outputChannelCount */ - if (outputParameters->channelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels) - return paInvalidChannelCount; - - /* validate outputStreamInfo */ - if(outputParameters->hostApiSpecificStreamInfo) - { - PaWasapiStreamInfo *outputStreamInfo = (PaWasapiStreamInfo *)outputParameters->hostApiSpecificStreamInfo; - if ((outputStreamInfo->size != sizeof(PaWasapiStreamInfo)) || - (outputStreamInfo->version != 1) || - (outputStreamInfo->hostApiType != paWASAPI)) - { - return paIncompatibleHostApiSpecificStreamInfo; - } - } - - return paNoError; - } - - return (inputParameters || outputParameters ? paNoError : paInternalError); -} - -// ------------------------------------------------------------------------------------------ -static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate ) -{ - IAudioClient *tmpClient = NULL; - PaWasapiHostApiRepresentation *paWasapi = (PaWasapiHostApiRepresentation*)hostApi; - PaWasapiStreamInfo *inputStreamInfo = NULL, *outputStreamInfo = NULL; - - // Validate PaStreamParameters - PaError error; - if ((error = IsStreamParamsValid(hostApi, inputParameters, outputParameters, sampleRate)) != paNoError) - return error; - - if (inputParameters != NULL) - { - WAVEFORMATEXTENSIBLE wavex; - HRESULT hr; - PaError answer; - AUDCLNT_SHAREMODE shareMode = AUDCLNT_SHAREMODE_SHARED; - inputStreamInfo = (PaWasapiStreamInfo *)inputParameters->hostApiSpecificStreamInfo; - - if (inputStreamInfo && (inputStreamInfo->flags & paWinWasapiExclusive)) - shareMode = AUDCLNT_SHAREMODE_EXCLUSIVE; - - hr = IMMDevice_Activate(paWasapi->devInfo[inputParameters->device].device, - &pa_IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, (void **)&tmpClient); - if (hr != S_OK) - { - LogHostError(hr); - return paInvalidDevice; - } - - answer = GetClosestFormat(tmpClient, sampleRate, inputParameters, shareMode, &wavex); - SAFE_RELEASE(tmpClient); - - if (answer != paFormatIsSupported) - return answer; - } - - if (outputParameters != NULL) - { - HRESULT hr; - WAVEFORMATEXTENSIBLE wavex; - PaError answer; - AUDCLNT_SHAREMODE shareMode = AUDCLNT_SHAREMODE_SHARED; - outputStreamInfo = (PaWasapiStreamInfo *)outputParameters->hostApiSpecificStreamInfo; - - if (outputStreamInfo && (outputStreamInfo->flags & paWinWasapiExclusive)) - shareMode = AUDCLNT_SHAREMODE_EXCLUSIVE; - - hr = IMMDevice_Activate(paWasapi->devInfo[outputParameters->device].device, - &pa_IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, (void **)&tmpClient); - if (hr != S_OK) - { - LogHostError(hr); - return paInvalidDevice; - } - - answer = GetClosestFormat(tmpClient, sampleRate, outputParameters, shareMode, &wavex); - SAFE_RELEASE(tmpClient); - - if (answer != paFormatIsSupported) - return answer; - } - - return paFormatIsSupported; -} - -// ------------------------------------------------------------------------------------------ -static HRESULT CreateAudioClient(PaWasapiSubStream *pSubStream, PaWasapiDeviceInfo *info, - const PaStreamParameters *params, UINT32 framesPerLatency, double sampleRate, UINT32 streamFlags, - PaError *pa_error) -{ - PaError error; - HRESULT hr = S_OK; - UINT32 nFrames = 0; - IAudioClient *pAudioClient = NULL; - - if (!pSubStream || !info || !params) - return E_POINTER; - if ((UINT32)sampleRate == 0) - return E_INVALIDARG; - - // Get the audio client - hr = IMMDevice_Activate(info->device, &pa_IID_IAudioClient, CLSCTX_ALL, NULL, (void **)&pAudioClient); - if (hr != S_OK) - { - LogHostError(hr); - goto done; - } - - // Get closest format - if ((error = GetClosestFormat(pAudioClient, sampleRate, params, pSubStream->shareMode, &pSubStream->wavex)) != paFormatIsSupported) - { - if (pa_error) - (*pa_error) = error; - return AUDCLNT_E_UNSUPPORTED_FORMAT; - } - - // Add latency frames - framesPerLatency += MakeFramesFromHns(SecondsTonano100(params->suggestedLatency), pSubStream->wavex.Format.nSamplesPerSec); - - // Align frames to HD Audio packet size of 128 bytes for Exclusive mode only. - // Not aligning on Windows Vista will cause Event timeout, although Windows 7 will - // return AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED error to realign buffer. Aligning is necessary - // for Exclusive mode only! when audio data is feeded directly to hardware. - if (pSubStream->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE) - { - framesPerLatency = AlignFramesPerBuffer(framesPerLatency, - pSubStream->wavex.Format.nSamplesPerSec, pSubStream->wavex.Format.nBlockAlign); - } - - // Calculate period - pSubStream->period = MakeHnsPeriod(framesPerLatency, pSubStream->wavex.Format.nSamplesPerSec); - - // Enforce min/max period for device in Shared mode to avoid distorted sound. - // Avoid doing so for Exclusive mode as alignment will suffer. Exclusive mode processes - // big buffers without problem. Push Exclusive beyond limits if possible. - if (pSubStream->shareMode == AUDCLNT_SHAREMODE_SHARED) - { - if (pSubStream->period < info->DefaultDevicePeriod) - pSubStream->period = info->DefaultDevicePeriod; - } - // Issues with setting exclusive mode due to pSubStream->period beeing wrong somehow - // Most likely reason is that framesPerLatency is hardcoded to 0. - else if (pSubStream->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE) - pSubStream->period = info->MinimumDevicePeriod; // Gives 3ms of latency - - // Open the stream and associate it with an audio session - hr = IAudioClient_Initialize(pAudioClient, - pSubStream->shareMode, - streamFlags/*AUDCLNT_STREAMFLAGS_EVENTCALLBACK*/, - pSubStream->period, - (pSubStream->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? pSubStream->period : 0), - &pSubStream->wavex.Format, - NULL); - - // If the requested buffer size is not aligned... - if (hr == AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED) - { - PRINT(("WASAPI: CreateAudioClient: aligning buffer size")); - - // Get the next aligned frame - hr = IAudioClient_GetBufferSize(pAudioClient, &nFrames); - if (hr != S_OK) - { - LogHostError(hr); - goto done; - } - - // Release the previous allocations - SAFE_RELEASE(pAudioClient); - - // Create a new audio client - hr = IMMDevice_Activate(info->device, &pa_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioClient); - if (hr != S_OK) - { - LogHostError(hr); - goto done; - } - - // Get closest format - if (GetClosestFormat(pAudioClient, sampleRate, params, pSubStream->shareMode, &pSubStream->wavex) != paFormatIsSupported) - return AUDCLNT_E_UNSUPPORTED_FORMAT; - - // Calculate period - pSubStream->period = MakeHnsPeriod(nFrames, pSubStream->wavex.Format.nSamplesPerSec); - - // Open the stream and associate it with an audio session - hr = IAudioClient_Initialize(pAudioClient, - pSubStream->shareMode, - streamFlags/*AUDCLNT_STREAMFLAGS_EVENTCALLBACK*/, - pSubStream->period, - (pSubStream->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? pSubStream->period : 0), - &pSubStream->wavex.Format, - NULL); - if (hr != S_OK) - { - LogHostError(hr); - goto done; - } - } - else - if (hr != S_OK) - { - LogHostError(hr); - goto done; - } - - // Set result - pSubStream->client = pAudioClient; - IAudioClient_AddRef(pSubStream->client); - -done: - - // Clean up - SAFE_RELEASE(pAudioClient); - return hr; -} - -// ------------------------------------------------------------------------------------------ -static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, - PaStream** s, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate, - unsigned long framesPerBuffer, - PaStreamFlags streamFlags, - PaStreamCallback *streamCallback, - void *userData ) -{ - PaError result = paNoError; - HRESULT hr; - PaWasapiHostApiRepresentation *paWasapi = (PaWasapiHostApiRepresentation*)hostApi; - PaWasapiStream *stream = NULL; - int inputChannelCount, outputChannelCount; - PaSampleFormat inputSampleFormat, outputSampleFormat; - PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat; - PaWasapiStreamInfo *inputStreamInfo = NULL, *outputStreamInfo = NULL; - PaWasapiDeviceInfo *info = NULL; - UINT32 maxBufferSize; - PaTime buffer_latency; - ULONG framesPerHostCallback; - PaUtilHostBufferSizeMode bufferMode; - - // validate PaStreamParameters - if ((result = IsStreamParamsValid(hostApi, inputParameters, outputParameters, sampleRate)) != paNoError) - return LogPaError(result); - - // Validate platform specific flags - if ((streamFlags & paPlatformSpecificFlags) != 0) - { - LogPaError(result = paInvalidFlag); /* unexpected platform specific flag */ - goto error; - } - - // Allocate memory for PaWasapiStream - if ((stream = (PaWasapiStream *)PaUtil_AllocateMemory(sizeof(PaWasapiStream))) == NULL) - { - LogPaError(result = paInsufficientMemory); - goto error; - } - - // Default thread priority is Audio: for exclusive mode we will use Pro Audio. - stream->nThreadPriority = eThreadPriorityAudio; - - // Set default number of frames: paFramesPerBufferUnspecified - if (framesPerBuffer == paFramesPerBufferUnspecified) - { - UINT32 framesPerBufferIn = 0, framesPerBufferOut = 0; - if (inputParameters != NULL) - { - info = &paWasapi->devInfo[inputParameters->device]; - framesPerBufferIn = MakeFramesFromHns(info->DefaultDevicePeriod, (UINT32)sampleRate); - } - if (outputParameters != NULL) - { - info = &paWasapi->devInfo[outputParameters->device]; - framesPerBufferOut = MakeFramesFromHns(info->DefaultDevicePeriod, (UINT32)sampleRate); - } - // choosing maximum default size - framesPerBuffer = max(framesPerBufferIn, framesPerBufferOut); - } - if (framesPerBuffer == 0) - framesPerBuffer = ((UINT32)sampleRate / 100) * 2; - - // Try create device: Input - if (inputParameters != NULL) - { - inputChannelCount = inputParameters->channelCount; - inputSampleFormat = inputParameters->sampleFormat; - inputStreamInfo = (PaWasapiStreamInfo *)inputParameters->hostApiSpecificStreamInfo; - info = &paWasapi->devInfo[inputParameters->device]; - stream->in.flags = (inputStreamInfo ? inputStreamInfo->flags : 0); - - // Select Exclusive/Shared mode - stream->in.shareMode = AUDCLNT_SHAREMODE_SHARED; - if ((inputStreamInfo != NULL) && (inputStreamInfo->flags & paWinWasapiExclusive)) - { - // Boost thread priority - stream->nThreadPriority = eThreadPriorityProAudio; - // Make Exclusive - stream->in.shareMode = AUDCLNT_SHAREMODE_EXCLUSIVE; - } - - // If user provided explicit thread priority level, use it - if ((inputStreamInfo != NULL) && (inputStreamInfo->flags & paWinWasapiThreadPriority)) - { - if ((inputStreamInfo->threadPriority > eThreadPriorityNone) && - (inputStreamInfo->threadPriority <= eThreadPriorityWindowManager)) - stream->nThreadPriority = inputStreamInfo->threadPriority; - } - - // Choose processing mode - stream->in.streamFlags = AUDCLNT_STREAMFLAGS_EVENTCALLBACK; - if (paWasapi->useVistaWOW64Workaround) - stream->in.streamFlags = 0; // polling interface - if (streamCallback == NULL) - stream->in.streamFlags = 0; // polling interface - if ((inputStreamInfo != NULL) && (inputStreamInfo->flags & paWinWasapiPolling)) - stream->in.streamFlags = 0; // polling interface - - // Create Audio client - hr = CreateAudioClient(&stream->in, info, inputParameters, 0/*framesPerLatency*/, - sampleRate, stream->in.streamFlags, &result); - if (hr != S_OK) - { - LogHostError(hr); - if (hr != AUDCLNT_E_UNSUPPORTED_FORMAT) - result = paInvalidDevice; - - LogPaError(result); - goto error; - } - LogWAVEFORMATEXTENSIBLE(&stream->in.wavex); - - // Get closest format - hostInputSampleFormat = PaUtil_SelectClosestAvailableFormat( waveformatToPaFormat(&stream->in.wavex), inputSampleFormat ); - - // Create volume mgr - stream->inVol = NULL; - /*hr = info->device->Activate( - __uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, NULL, - (void**)&stream->inVol); - if (hr != S_OK) - return paInvalidDevice;*/ - - // Set user-side custom host processor - if ((inputStreamInfo != NULL) && - (inputStreamInfo->flags & paWinWasapiRedirectHostProcessor)) - { - stream->hostProcessOverrideInput.processor = inputStreamInfo->hostProcessorInput; - stream->hostProcessOverrideInput.userData = userData; - } - - // Get max possible buffer size to check if it is not less than that we request - hr = IAudioClient_GetBufferSize(stream->in.client, &maxBufferSize); - if (hr != S_OK) - { - LogHostError(hr); - LogPaError(result = paInvalidDevice); - goto error; - } - - // Correct buffer to max size if it maxed out result of GetBufferSize - stream->in.bufferSize = maxBufferSize; - - // Get interface latency (actually uneeded as we calculate latency from the size - // of maxBufferSize). - hr = IAudioClient_GetStreamLatency(stream->in.client, &stream->in.device_latency); - if (hr != S_OK) - { - LogHostError(hr); - LogPaError(result = paInvalidDevice); - goto error; - } - //stream->in.latency_seconds = nano100ToSeconds(stream->in.device_latency); - - // Number of frames that are required at each period - stream->in.framesPerHostCallback = maxBufferSize; - - // Calculate buffer latency - buffer_latency = (PaTime)maxBufferSize / stream->in.wavex.Format.nSamplesPerSec; - - // Append buffer latency to interface latency in shared mode (see GetStreamLatency notes) - stream->in.latency_seconds += buffer_latency; - - PRINT(("PaWASAPIOpenStream(input): framesPerHostCallback[ %d ] latency[ %.02fms ]\n", (UINT32)stream->in.framesPerHostCallback, (float)(stream->in.latency_seconds*1000.0f))); - } - else - { - inputChannelCount = 0; - inputSampleFormat = hostInputSampleFormat = paInt16; /* Surpress 'uninitialised var' warnings. */ - } - - // Try create device: Output - if (outputParameters != NULL) - { - outputChannelCount = outputParameters->channelCount; - outputSampleFormat = outputParameters->sampleFormat; - outputStreamInfo = (PaWasapiStreamInfo *)outputParameters->hostApiSpecificStreamInfo; - info = &paWasapi->devInfo[outputParameters->device]; - stream->out.flags = (outputStreamInfo ? outputStreamInfo->flags : 0); - - // Select Exclusive/Shared mode - stream->out.shareMode = AUDCLNT_SHAREMODE_SHARED; - if ((outputStreamInfo != NULL) && (outputStreamInfo->flags & paWinWasapiExclusive)) - { - // Boost thread priority - stream->nThreadPriority = eThreadPriorityProAudio; - // Make Exclusive - stream->out.shareMode = AUDCLNT_SHAREMODE_EXCLUSIVE; - } - - // If user provided explicit thread priority level, use it - if ((outputStreamInfo != NULL) && (outputStreamInfo->flags & paWinWasapiThreadPriority)) - { - if ((outputStreamInfo->threadPriority > eThreadPriorityNone) && - (outputStreamInfo->threadPriority <= eThreadPriorityWindowManager)) - stream->nThreadPriority = outputStreamInfo->threadPriority; - } - - // Choose processing mode - stream->out.streamFlags = AUDCLNT_STREAMFLAGS_EVENTCALLBACK; - if (paWasapi->useVistaWOW64Workaround) - stream->out.streamFlags = 0; // polling interface - if (streamCallback == NULL) - stream->out.streamFlags = 0; // polling interface - if ((outputStreamInfo != NULL) && (outputStreamInfo->flags & paWinWasapiPolling)) - stream->out.streamFlags = 0; // polling interface - - // Create Audio client - hr = CreateAudioClient(&stream->out, info, outputParameters, 0/*framesPerLatency*/, - sampleRate, stream->out.streamFlags, &result); - if (hr != S_OK) - { - LogHostError(hr); - if (hr != AUDCLNT_E_UNSUPPORTED_FORMAT) - result = paInvalidDevice; - - LogPaError(result); - goto error; - } - LogWAVEFORMATEXTENSIBLE(&stream->out.wavex); - - // Get closest format - hostOutputSampleFormat = PaUtil_SelectClosestAvailableFormat( waveformatToPaFormat(&stream->out.wavex), outputSampleFormat ); - - // Activate volume - stream->outVol = NULL; - /*hr = info->device->Activate( - __uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, NULL, - (void**)&stream->outVol); - if (hr != S_OK) - return paInvalidDevice;*/ - - // Set user-side custom host processor - if ((outputStreamInfo != NULL) && - (outputStreamInfo->flags & paWinWasapiRedirectHostProcessor)) - { - stream->hostProcessOverrideOutput.processor = outputStreamInfo->hostProcessorOutput; - stream->hostProcessOverrideOutput.userData = userData; - } - - // Get max possible buffer size to check if it is not less than that we request - hr = IAudioClient_GetBufferSize(stream->out.client, &maxBufferSize); - if (hr != S_OK) - { - LogHostError(hr); - LogPaError(result = paInvalidDevice); - goto error; - } - - // Correct buffer to max size if it maxed out result of GetBufferSize - stream->out.bufferSize = maxBufferSize; - - // Get interface latency (actually uneeded as we calculate latency from the size - // of maxBufferSize). - hr = IAudioClient_GetStreamLatency(stream->out.client, &stream->out.device_latency); - if (hr != S_OK) - { - LogHostError(hr); - LogPaError(result = paInvalidDevice); - goto error; - } - //stream->out.latency_seconds = nano100ToSeconds(stream->out.device_latency); - - // Number of frames that are required at each period - stream->out.framesPerHostCallback = maxBufferSize; - - // Calculate buffer latency - buffer_latency = (PaTime)maxBufferSize / stream->out.wavex.Format.nSamplesPerSec; - - // Append buffer latency to interface latency in shared mode (see GetStreamLatency notes) - stream->out.latency_seconds += buffer_latency; - - PRINT(("PaWASAPIOpenStream(output): framesPerHostCallback[ %d ] latency[ %.02fms ]\n", (UINT32)stream->out.framesPerHostCallback, (float)(stream->out.latency_seconds*1000.0f))); - } - else - { - outputChannelCount = 0; - outputSampleFormat = hostOutputSampleFormat = paInt16; /* Surpress 'uninitialized var' warnings. */ - } - - // paWinWasapiPolling must be on/or not on both streams - if ((inputParameters != NULL) && (outputParameters != NULL)) - { - if ((inputStreamInfo != NULL) && (outputStreamInfo != NULL)) - { - if (((inputStreamInfo->flags & paWinWasapiPolling) && - !(outputStreamInfo->flags & paWinWasapiPolling)) - || - (!(inputStreamInfo->flags & paWinWasapiPolling) && - (outputStreamInfo->flags & paWinWasapiPolling))) - { - LogPaError(result = paInvalidFlag); - goto error; - } - } - } - - // Initialize stream representation - if (streamCallback) - { - stream->bBlocking = FALSE; - PaUtil_InitializeStreamRepresentation(&stream->streamRepresentation, - &paWasapi->callbackStreamInterface, - streamCallback, userData); - } - else - { - stream->bBlocking = TRUE; - PaUtil_InitializeStreamRepresentation(&stream->streamRepresentation, - &paWasapi->blockingStreamInterface, - streamCallback, userData); - } - - // Initialize CPU measurer - PaUtil_InitializeCpuLoadMeasurer(&stream->cpuLoadMeasurer, sampleRate); - - if (outputParameters && inputParameters) - { - // serious problem #1 - if (stream->in.period != stream->out.period) - { - PRINT(("WASAPI: OpenStream: period discrepancy\n")); - goto error; - } - - // serious problem #2 - if (stream->out.framesPerHostCallback != stream->in.framesPerHostCallback) - { - PRINT(("WASAPI: OpenStream: framesPerHostCallback discrepancy\n")); - goto error; - } - } - - framesPerHostCallback = (outputParameters) ? stream->out.framesPerHostCallback: - stream->in.framesPerHostCallback; - - // Choose correct mode of buffer processing: - // Exclusive/Shared non paWinWasapiPolling mode: paUtilFixedHostBufferSize - always fixed - // Exclusive/Shared paWinWasapiPolling mode: paUtilBoundedHostBufferSize - may vary - bufferMode = paUtilFixedHostBufferSize; - if (inputParameters && - (!stream->in.streamFlags || ((stream->in.streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) == 0))) - bufferMode = paUtilBoundedHostBufferSize; - else - if (outputParameters && - (!stream->out.streamFlags || ((stream->out.streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) == 0))) - bufferMode = paUtilBoundedHostBufferSize; - - // Initialize buffer processor - result = PaUtil_InitializeBufferProcessor( - &stream->bufferProcessor, - inputChannelCount, - inputSampleFormat, - hostInputSampleFormat, - outputChannelCount, - outputSampleFormat, - hostOutputSampleFormat, - sampleRate, - streamFlags, - framesPerBuffer, - framesPerHostCallback, - bufferMode, - streamCallback, - userData); - if (result != paNoError) - { - LogPaError(result); - goto error; - } - - // Set Input latency - stream->streamRepresentation.streamInfo.inputLatency = - PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor) - + ((inputParameters)?stream->in.latency_seconds :0); - - // Set Output latency - stream->streamRepresentation.streamInfo.outputLatency = - PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor) - + ((outputParameters)?stream->out.latency_seconds :0); - - // Set SR - stream->streamRepresentation.streamInfo.sampleRate = sampleRate; - - (*s) = (PaStream *)stream; - return result; - -error: - - if (stream != NULL) - { - CloseStream(stream); - PaUtil_FreeMemory(stream); - } - - return result; -} - -// ------------------------------------------------------------------------------------------ -static PaError CloseStream( PaStream* s ) -{ - PaError result = paNoError; - PaWasapiStream *stream = (PaWasapiStream*)s; - - // abort active stream - if (IsStreamActive(s)) - { - if ((result = AbortStream(s)) != paNoError) - return result; - } - - SAFE_RELEASE(stream->out.client); - SAFE_RELEASE(stream->in.client); - SAFE_RELEASE(stream->cclient); - SAFE_RELEASE(stream->rclient); - SAFE_RELEASE(stream->inVol); - SAFE_RELEASE(stream->outVol); - - SAFE_CLOSE(stream->hThread); - SAFE_CLOSE(stream->hThreadDone); - SAFE_CLOSE(stream->hCloseRequest); - SAFE_CLOSE(stream->hBlockingOpStreamRD); - SAFE_CLOSE(stream->hBlockingOpStreamWR); - - PaUtil_TerminateBufferProcessor(&stream->bufferProcessor); - PaUtil_TerminateStreamRepresentation(&stream->streamRepresentation); - PaUtil_FreeMemory(stream); - - return result; -} - -// ------------------------------------------------------------------------------------------ -static PaError StartStream( PaStream *s ) -{ - HRESULT hr; - PaWasapiStream *stream = (PaWasapiStream*)s; - - // check if stream is active already - if (IsStreamActive(s)) - return paStreamIsNotStopped; - - PaUtil_ResetBufferProcessor(&stream->bufferProcessor); - - // Create close event - stream->hCloseRequest = CreateEvent(NULL, TRUE, FALSE, NULL); - - // Create thread - if (!stream->bBlocking) - { - // Create thread done event - stream->hThreadDone = CreateEvent(NULL, TRUE, FALSE, NULL); - - if ((stream->in.client && stream->in.streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) || - (stream->out.client && stream->out.streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)) - { - if ((stream->hThread = CREATE_THREAD(ProcThreadEvent)) == NULL) - return paUnanticipatedHostError; - } - else - { - if ((stream->hThread = CREATE_THREAD(ProcThreadPoll)) == NULL) - return paUnanticipatedHostError; - } - } - else - { - // Create blocking operation events (non-signaled event means - blocking operation is pending) - if (stream->out.client) - stream->hBlockingOpStreamWR = CreateEvent(NULL, TRUE, TRUE, NULL); - if (stream->in.client) - stream->hBlockingOpStreamRD = CreateEvent(NULL, TRUE, TRUE, NULL); - - // Initialize event & start INPUT stream - if (stream->in.client) - { - if ((hr = IAudioClient_GetService(stream->in.client, &pa_IID_IAudioCaptureClient, (void **)&stream->cclient)) != S_OK) - { - LogHostError(hr); - return paUnanticipatedHostError; - } - - if ((hr = IAudioClient_Start(stream->in.client)) != S_OK) - { - LogHostError(hr); - return paUnanticipatedHostError; - } - } - - - // Initialize event & start OUTPUT stream - if (stream->out.client) - { - if ((hr = IAudioClient_GetService(stream->out.client, &pa_IID_IAudioRenderClient, (void **)&stream->rclient)) != S_OK) - { - LogHostError(hr); - return paUnanticipatedHostError; - } - - // Start - if ((hr = IAudioClient_Start(stream->out.client)) != S_OK) - { - LogHostError(hr); - return paUnanticipatedHostError; - } - } - - // Signal: stream running - stream->running = TRUE; - - // Set current time - stream->out.prevTime = timeGetTime(); - stream->out.prevSleep = 0; - } - - return paNoError; -} - -// ------------------------------------------------------------------------------------------ -static void _FinishStream(PaWasapiStream *stream) -{ - // Issue command to thread to stop processing and wait for thread exit - if (!stream->bBlocking) - { - SignalObjectAndWait(stream->hCloseRequest, stream->hThreadDone, INFINITE, FALSE); - } - else - // Blocking mode does not own thread - { - // Signal close event and wait for each of 2 blocking operations to complete - if (stream->out.client) - SignalObjectAndWait(stream->hCloseRequest, stream->hBlockingOpStreamWR, INFINITE, TRUE); - if (stream->out.client) - SignalObjectAndWait(stream->hCloseRequest, stream->hBlockingOpStreamRD, INFINITE, TRUE); - - // Process stop - _OnStreamStop(stream); - } - - // Close thread handles to allow restart - SAFE_CLOSE(stream->hThread); - SAFE_CLOSE(stream->hThreadDone); - SAFE_CLOSE(stream->hCloseRequest); - SAFE_CLOSE(stream->hBlockingOpStreamRD); - SAFE_CLOSE(stream->hBlockingOpStreamWR); - - stream->running = FALSE; -} - -// ------------------------------------------------------------------------------------------ -static PaError StopStream( PaStream *s ) -{ - // Finish stream - _FinishStream((PaWasapiStream *)s); - return paNoError; -} - -// ------------------------------------------------------------------------------------------ -static PaError AbortStream( PaStream *s ) -{ - // Finish stream - _FinishStream((PaWasapiStream *)s); - return paNoError; -} - -// ------------------------------------------------------------------------------------------ -static PaError IsStreamStopped( PaStream *s ) -{ - return !((PaWasapiStream *)s)->running; -} - -// ------------------------------------------------------------------------------------------ -static PaError IsStreamActive( PaStream *s ) -{ - return ((PaWasapiStream *)s)->running; -} - -// ------------------------------------------------------------------------------------------ -static PaTime GetStreamTime( PaStream *s ) -{ - PaWasapiStream *stream = (PaWasapiStream*)s; - - /* suppress unused variable warnings */ - (void) stream; - - /* IMPLEMENT ME, see portaudio.h for required behavior*/ - - //this is lame ds and mme does the same thing, quite useless method imho - //why dont we fetch the time in the pa callbacks? - //at least its doing to be clocked to something - return PaUtil_GetTime(); -} - -// ------------------------------------------------------------------------------------------ -static double GetStreamCpuLoad( PaStream* s ) -{ - return PaUtil_GetCpuLoad(&((PaWasapiStream *)s)->cpuLoadMeasurer); -} - -// ------------------------------------------------------------------------------------------ -/* NOT TESTED */ -static PaError ReadStream( PaStream* s, void *_buffer, unsigned long _frames ) -{ - PaWasapiStream *stream = (PaWasapiStream*)s; - - HRESULT hr = S_OK; - UINT32 frames; - BYTE *buffer = (BYTE *)_buffer; - BYTE *data = NULL; - DWORD flags = 0; - UINT32 buffer_size; - - // validate - if (!stream->running) - return paStreamIsStopped; - if (stream->cclient == NULL) - return paBadStreamPtr; - - // Notify blocking op has begun - ResetEvent(stream->hBlockingOpStreamRD); - - while (_frames != 0) - { - // Get the available data in the shared buffer. - if ((hr = IAudioCaptureClient_GetBuffer(stream->cclient, &data, &frames, &flags, NULL, NULL)) != S_OK) - { - if (hr == AUDCLNT_S_BUFFER_EMPTY) - { - // Check if blocking call must be interrupted - if (WaitForSingleObject(stream->hCloseRequest, 1) != WAIT_TIMEOUT) - break; - } - - return LogHostError(hr); - goto stream_rd_end; - } - - // Detect silence - // if (flags & AUDCLNT_BUFFERFLAGS_SILENT) - // data = NULL; - - // Check if frames <= _frames - if (frames > _frames) - frames = _frames; - - // Copy - buffer_size = frames * stream->in.wavex.Format.nBlockAlign; - memcpy(buffer, data, buffer_size); - buffer += buffer_size; - - // Release buffer - if ((hr = IAudioCaptureClient_ReleaseBuffer(stream->cclient, frames)) != S_OK) - { - LogHostError(hr); - goto stream_rd_end; - } - - _frames -= frames; - } - -stream_rd_end: - - // Notify blocking op has ended - SetEvent(stream->hBlockingOpStreamRD); - - return (hr != S_OK ? paUnanticipatedHostError : paNoError); -} - -// ------------------------------------------------------------------------------------------ -static PaError WriteStream( PaStream* s, const void *_buffer, unsigned long _frames ) -{ - PaWasapiStream *stream = (PaWasapiStream*)s; - - UINT32 frames; - const BYTE *buffer = (BYTE *)_buffer; - BYTE *data; - HRESULT hr = S_OK; - UINT32 next_rev_sleep, blocks, block_sleep_ms; - UINT32 i; - - // validate - if (!stream->running) - return paStreamIsStopped; - if (stream->rclient == NULL) - return paBadStreamPtr; - - // Notify blocking op has begun - ResetEvent(stream->hBlockingOpStreamWR); - - // Calculate sleep time for next call - { - UINT32 remainder = 0; - UINT32 sleep_ms = 0; - DWORD elapsed_ms; - blocks = _frames / stream->out.framesPerHostCallback; - block_sleep_ms = GetFramesSleepTime(stream->out.framesPerHostCallback, stream->out.wavex.Format.nSamplesPerSec); - if (blocks == 0) - { - blocks = 1; - sleep_ms = GetFramesSleepTime(_frames, stream->out.wavex.Format.nSamplesPerSec); // partial - } - else - { - remainder = _frames - blocks * stream->out.framesPerHostCallback; - sleep_ms = block_sleep_ms; // full - } - - // Sleep for remainder - elapsed_ms = timeGetTime() - stream->out.prevTime; - if (sleep_ms >= elapsed_ms) - sleep_ms -= elapsed_ms; - - next_rev_sleep = sleep_ms; - } - - // Sleep diff from last call - if (stream->out.prevSleep) - Sleep(stream->out.prevSleep); - stream->out.prevSleep = next_rev_sleep; - - // Feed engine - for (i = 0; i < blocks; ++i) - { - UINT32 available; - UINT32 buffer_size; - - // Get block frames - frames = stream->out.framesPerHostCallback; - if (frames > _frames) - frames = _frames; - - if (i) - Sleep(block_sleep_ms); - - while (frames != 0) - { - UINT32 padding = 0; - - // Check if blocking call must be interrupted - if (WaitForSingleObject(stream->hCloseRequest, 0) != WAIT_TIMEOUT) - break; - - // Get Read position - hr = IAudioClient_GetCurrentPadding(stream->out.client, &padding); - if (hr != S_OK) - { - LogHostError(hr); - goto stream_wr_end; - } - - // Get frames available - if (frames >= padding) - available = frames - padding; - else - available = frames; - - // Get buffer - if ((hr = IAudioRenderClient_GetBuffer(stream->rclient, available, &data)) != S_OK) - { - // Buffer size is too big, waiting - if (hr == AUDCLNT_E_BUFFER_TOO_LARGE) - continue; - LogHostError(hr); - goto stream_wr_end; - } - - // Copy - buffer_size = available * stream->out.wavex.Format.nBlockAlign; - memcpy(data, buffer, buffer_size); - buffer += buffer_size; - - // Release buffer - if ((hr = IAudioRenderClient_ReleaseBuffer(stream->rclient, available, 0)) != S_OK) - { - LogHostError(hr); - goto stream_wr_end; - } - - frames -= available; - } - - _frames -= frames; - } - -stream_wr_end: - - // Set prev time - stream->out.prevTime = timeGetTime(); - - // Notify blocking op has ended - SetEvent(stream->hBlockingOpStreamWR); - - return (hr != S_OK ? paUnanticipatedHostError : paNoError); -} - -// ------------------------------------------------------------------------------------------ -/* NOT TESTED */ -static signed long GetStreamReadAvailable( PaStream* s ) -{ - PaWasapiStream *stream = (PaWasapiStream*)s; - HRESULT hr; - UINT32 pending = 0; - - // validate - if (!stream->running) - return paStreamIsStopped; - if (stream->cclient == NULL) - return paBadStreamPtr; - - hr = IAudioClient_GetCurrentPadding(stream->in.client, &pending); - if (hr != S_OK) - { - LogHostError(hr); - return paUnanticipatedHostError; - } - - return (long)pending; -} - -// ------------------------------------------------------------------------------------------ -static signed long GetStreamWriteAvailable( PaStream* s ) -{ - PaWasapiStream *stream = (PaWasapiStream*)s; - - UINT32 frames = stream->out.framesPerHostCallback; - HRESULT hr; - UINT32 padding = 0; - - // validate - if (!stream->running) - return paStreamIsStopped; - if (stream->rclient == NULL) - return paBadStreamPtr; - - hr = IAudioClient_GetCurrentPadding(stream->out.client, &padding); - if (hr != S_OK) - { - LogHostError(hr); - return paUnanticipatedHostError; - } - - // Calculate - frames -= padding; - - return frames; -} - -// ------------------------------------------------------------------------------------------ -static void WaspiHostProcessingLoop( void *inputBuffer, long inputFrames, - void *outputBuffer, long outputFrames, - void *userData ) -{ - PaWasapiStream *stream = (PaWasapiStream*)userData; - PaStreamCallbackTimeInfo timeInfo = {0,0,0}; - PaStreamCallbackFlags flags = 0; - int callbackResult; - unsigned long framesProcessed; - HRESULT hr; - UINT32 pending; - - PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer ); - - /* - Pa_GetStreamTime: - - generate timing information - - handle buffer slips - */ - timeInfo.currentTime = PaUtil_GetTime(); - // Query input latency - if (stream->in.client != NULL) - { - PaTime pending_time; - if ((hr = IAudioClient_GetCurrentPadding(stream->in.client, &pending)) == S_OK) - pending_time = (PaTime)pending / (PaTime)stream->in.wavex.Format.nSamplesPerSec; - else - pending_time = (PaTime)stream->in.latency_seconds; - - timeInfo.inputBufferAdcTime = timeInfo.currentTime + pending_time; - } - // Query output current latency - if (stream->out.client != NULL) - { - PaTime pending_time; - if ((hr = IAudioClient_GetCurrentPadding(stream->out.client, &pending)) == S_OK) - pending_time = (PaTime)pending / (PaTime)stream->out.wavex.Format.nSamplesPerSec; - else - pending_time = (PaTime)stream->out.latency_seconds; - - timeInfo.outputBufferDacTime = timeInfo.currentTime + pending_time; - } - - /* - If you need to byte swap or shift inputBuffer to convert it into a - portaudio format, do it here. - */ - - PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, flags ); - - /* - depending on whether the host buffers are interleaved, non-interleaved - or a mixture, you will want to call PaUtil_SetInterleaved*Channels(), - PaUtil_SetNonInterleaved*Channel() or PaUtil_Set*Channel() here. - */ - - if (stream->bufferProcessor.inputChannelCount > 0) - { - PaUtil_SetInputFrameCount( &stream->bufferProcessor, inputFrames ); - PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, - 0, /* first channel of inputBuffer is channel 0 */ - inputBuffer, - 0 ); /* 0 - use inputChannelCount passed to init buffer processor */ - } - - if (stream->bufferProcessor.outputChannelCount > 0) - { - PaUtil_SetOutputFrameCount( &stream->bufferProcessor, outputFrames); - PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, - 0, /* first channel of outputBuffer is channel 0 */ - outputBuffer, - 0 ); /* 0 - use outputChannelCount passed to init buffer processor */ - } - - /* you must pass a valid value of callback result to PaUtil_EndBufferProcessing() - in general you would pass paContinue for normal operation, and - paComplete to drain the buffer processor's internal output buffer. - You can check whether the buffer processor's output buffer is empty - using PaUtil_IsBufferProcessorOuputEmpty( bufferProcessor ) - */ - callbackResult = paContinue; - framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult ); - - /* - If you need to byte swap or shift outputBuffer to convert it to - host format, do it here. - */ - - PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed ); - - if (callbackResult == paContinue) - { - /* nothing special to do */ - } - else - if (callbackResult == paAbort) - { - // stop stream - SetEvent(stream->hCloseRequest); - } - else - { - // stop stream - SetEvent(stream->hCloseRequest); - } -} - -// ------------------------------------------------------------------------------------------ -HANDLE MMCSS_activate(const char *name) -{ - DWORD task_idx = 0; - HANDLE hTask = pAvSetMmThreadCharacteristics(name, &task_idx); - if (hTask == NULL) - { - PRINT(("WASAPI: AvSetMmThreadCharacteristics failed!\n")); - } - - /*BOOL priority_ok = pAvSetMmThreadPriority(hTask, AVRT_PRIORITY_NORMAL); - if (priority_ok == FALSE) - { - PRINT(("WASAPI: AvSetMmThreadPriority failed!\n")); - }*/ - - // debug - { - int cur_priority = GetThreadPriority(GetCurrentThread()); - DWORD cur_priority_class = GetPriorityClass(GetCurrentProcess()); - PRINT(("WASAPI: thread[ priority-0x%X class-0x%X ]\n", cur_priority, cur_priority_class)); - } - - return hTask; -} - -// ------------------------------------------------------------------------------------------ -void MMCSS_deactivate(HANDLE hTask) -{ - if (!hTask) - return; - - if (pAvRevertMmThreadCharacteristics(hTask) == FALSE) - { - PRINT(("WASAPI: AvRevertMmThreadCharacteristics failed!\n")); - } -} - -// ------------------------------------------------------------------------------------------ -PaError PaWasapi_ThreadPriorityBoost(void **hTask, PaWasapiThreadPriority nPriorityClass) -{ - static const char *mmcs_name[] = - { - NULL, - "Audio", - "Capture", - "Distribution", - "Games", - "Playback", - "Pro Audio", - "Window Manager" - }; - HANDLE task; - - if (hTask == NULL) - return paUnanticipatedHostError; - - if ((UINT32)nPriorityClass >= STATIC_ARRAY_SIZE(mmcs_name)) - return paUnanticipatedHostError; - - task = MMCSS_activate(mmcs_name[nPriorityClass]); - if (task == NULL) - return paUnanticipatedHostError; - - (*hTask) = task; - return paNoError; -} - -// ------------------------------------------------------------------------------------------ -PaError PaWasapi_ThreadPriorityRevert(void *hTask) -{ - if (hTask == NULL) - return paUnanticipatedHostError; - - MMCSS_deactivate((HANDLE)hTask); - - return paNoError; -} - -// ------------------------------------------------------------------------------------------ -static HRESULT FillOutputBuffer(PaWasapiStream *stream, PaWasapiHostProcessor *processor, UINT32 frames) -{ - HRESULT hr; - BYTE *data = NULL; - - // Get buffer - if ((hr = IAudioRenderClient_GetBuffer(stream->rclient, frames, &data)) != S_OK) - { - if (stream->out.shareMode == AUDCLNT_SHAREMODE_SHARED) - { - // Using GetCurrentPadding to overcome AUDCLNT_E_BUFFER_TOO_LARGE in - // shared mode results in no sound in Event-driven mode (MSDN does not - // document this, or is it WASAPI bug?), thus we better - // try to acquire buffer next time when GetBuffer allows to do so. -#if 0 - // Get Read position - UINT32 padding = 0; - hr = stream->out.client->GetCurrentPadding(&padding); - if (hr != S_OK) - return LogHostError(hr); - - // Get frames to write - frames -= padding; - if (frames == 0) - return S_OK; - - if ((hr = stream->rclient->GetBuffer(frames, &data)) != S_OK) - return LogHostError(hr); -#else - if (hr == AUDCLNT_E_BUFFER_TOO_LARGE) - return S_OK; // be silent in shared mode, try again next time -#endif - } - else - return LogHostError(hr); - } - - // Process data - processor[S_OUTPUT].processor(NULL, 0, data, frames, processor[S_OUTPUT].userData); - - // Release buffer - if ((hr = IAudioRenderClient_ReleaseBuffer(stream->rclient, frames, 0)) != S_OK) - LogHostError(hr); - - return hr; -} - -// ------------------------------------------------------------------------------------------ -static HRESULT PollInputBuffer(PaWasapiStream *stream, PaWasapiHostProcessor *processor) -{ - HRESULT hr; - UINT32 frames; - BYTE *data = NULL; - DWORD flags = 0; - - for (;;) - { - // Get the available data in the shared buffer. - if ((hr = IAudioCaptureClient_GetBuffer(stream->cclient, &data, &frames, &flags, NULL, NULL)) != S_OK) - { - if (hr == AUDCLNT_S_BUFFER_EMPTY) - break; // capture buffer exhausted - - return LogHostError(hr); - break; - } - - // Detect silence - // if (flags & AUDCLNT_BUFFERFLAGS_SILENT) - // data = NULL; - - // Process data - processor[S_INPUT].processor(data, frames, NULL, 0, processor[S_INPUT].userData); - - // Release buffer - if ((hr = IAudioCaptureClient_ReleaseBuffer(stream->cclient, frames)) != S_OK) - return LogHostError(hr); - } - - return hr; -} - -// ------------------------------------------------------------------------------------------ -void _OnStreamStop(PaWasapiStream *stream) -{ - // Stop INPUT client - if (stream->in.client != NULL) - IAudioClient_Stop(stream->in.client); - // Stop OUTPUT client - if (stream->out.client != NULL) - IAudioClient_Stop(stream->out.client); - - // Restore thread priority - if (stream->hAvTask != NULL) - { - PaWasapi_ThreadPriorityRevert(stream->hAvTask); - stream->hAvTask = NULL; - } - - // Notify - if (stream->streamRepresentation.streamFinishedCallback != NULL) - stream->streamRepresentation.streamFinishedCallback(stream->streamRepresentation.userData); -} - -// ------------------------------------------------------------------------------------------ -PA_THREAD_FUNC ProcThreadEvent(void *param) -{ - HANDLE event[S_COUNT]; - PaWasapiHostProcessor processor[S_COUNT]; - HRESULT hr; - DWORD dwResult; - PaWasapiStream *stream = (PaWasapiStream *)param; - PaWasapiHostProcessor defaultProcessor; - - // Waiting on all events in case of Full-Duplex/Exclusive mode. - BOOL bWaitAllEvents = FALSE; - if ((stream->in.client != NULL) && (stream->out.client != NULL)) - { - bWaitAllEvents = (stream->in.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE) && - (stream->out.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE); - } - - // Setup data processors - defaultProcessor.processor = WaspiHostProcessingLoop; - defaultProcessor.userData = stream; - processor[S_INPUT] = (stream->hostProcessOverrideInput.processor != NULL ? stream->hostProcessOverrideInput : defaultProcessor); - processor[S_OUTPUT] = (stream->hostProcessOverrideOutput.processor != NULL ? stream->hostProcessOverrideOutput : defaultProcessor); - - // Initialize event to NULL first - event[S_INPUT] = CreateEvent(NULL, FALSE, FALSE, NULL); - event[S_OUTPUT] = CreateEvent(NULL, FALSE, FALSE, NULL); - - // Check on low resources and disallow any processing further - if (!event[S_INPUT] || !event[S_OUTPUT]) - { - PRINT(("WASAPI Thread: failed creating one or two event handles\n")); - goto thread_end; - } - - // Boost thread priority - PaWasapi_ThreadPriorityBoost((void **)&stream->hAvTask, stream->nThreadPriority); - - // Initialize event & start INPUT stream - if (stream->in.client) - { - if ((hr = IAudioClient_SetEventHandle(stream->in.client, event[S_INPUT])) != S_OK) - LogHostError(hr); - - if ((hr = IAudioClient_GetService(stream->in.client, &pa_IID_IAudioCaptureClient, (void **)&stream->cclient)) != S_OK) - LogHostError(hr); - - if ((hr = IAudioClient_Start(stream->in.client)) != S_OK) - LogHostError(hr); - } - - // Initialize event & start OUTPUT stream - if (stream->out.client) - { - if ((hr = IAudioClient_SetEventHandle(stream->out.client, event[S_OUTPUT])) != S_OK) - LogHostError(hr); - - if ((hr = IAudioClient_GetService(stream->out.client, &pa_IID_IAudioRenderClient, (void **)&stream->rclient)) != S_OK) - LogHostError(hr); - - // Preload buffer before start - if ((hr = FillOutputBuffer(stream, processor, stream->out.framesPerHostCallback)) != S_OK) - LogHostError(hr); - - // Start - if ((hr = IAudioClient_Start(stream->out.client)) != S_OK) - LogHostError(hr); - } - - // Signal: stream running - stream->running = TRUE; - - // Processing Loop - for (;;) - { - // 2 sec timeout - dwResult = WaitForMultipleObjects(S_COUNT, event, bWaitAllEvents, 10000); - - // Check for close event (after wait for buffers to avoid any calls to user - // callback when hCloseRequest was set) - if (WaitForSingleObject(stream->hCloseRequest, 0) != WAIT_TIMEOUT) - break; - - // Process S_INPUT/S_OUTPUT - switch (dwResult) - { - case WAIT_TIMEOUT: { - PRINT(("WASAPI Thread: WAIT_TIMEOUT - probably bad audio driver or Vista x64 bug: use paWinWasapiPolling instead\n")); - goto processing_stop; - break; } - - // Input stream - case WAIT_OBJECT_0 + S_INPUT: { - if (stream->cclient == NULL) - break; - PollInputBuffer(stream, processor); - break; } - - // Output stream - case WAIT_OBJECT_0 + S_OUTPUT: { - if (stream->rclient == NULL) - break; - FillOutputBuffer(stream, processor, stream->out.framesPerHostCallback); - break; } - } - } - -processing_stop: - - // Process stop - _OnStreamStop(stream); - -thread_end: - - // Close event - CloseHandle(event[S_INPUT]); - CloseHandle(event[S_OUTPUT]); - - // Notify thread is exiting - SetEvent(stream->hThreadDone); - - return 0; -} - -// ------------------------------------------------------------------------------------------ -PA_THREAD_FUNC ProcThreadPoll(void *param) -{ - PaWasapiHostProcessor processor[S_COUNT]; - HRESULT hr; - PaWasapiStream *stream = (PaWasapiStream *)param; - PaWasapiHostProcessor defaultProcessor; - INT32 i; - - // Calculate the actual duration of the allocated buffer. - DWORD sleep_ms = 0; - DWORD sleep_ms_in = GetFramesSleepTime(stream->in.framesPerHostCallback, stream->in.wavex.Format.nSamplesPerSec); - DWORD sleep_ms_out = GetFramesSleepTime(stream->out.framesPerHostCallback, stream->out.wavex.Format.nSamplesPerSec); - if ((sleep_ms_in != 0) && (sleep_ms_out != 0)) - sleep_ms = min(sleep_ms_in, sleep_ms_out); - else - { - sleep_ms = (sleep_ms_in ? sleep_ms_in : sleep_ms_out); - } - - // Setup data processors - defaultProcessor.processor = WaspiHostProcessingLoop; - defaultProcessor.userData = stream; - processor[S_INPUT] = (stream->hostProcessOverrideInput.processor != NULL ? stream->hostProcessOverrideInput : defaultProcessor); - processor[S_OUTPUT] = (stream->hostProcessOverrideOutput.processor != NULL ? stream->hostProcessOverrideOutput : defaultProcessor); - - // Boost thread priority - PaWasapi_ThreadPriorityBoost((void **)&stream->hAvTask, stream->nThreadPriority); - - // Initialize event & start INPUT stream - if (stream->in.client) - { - if ((hr = IAudioClient_GetService(stream->in.client, &pa_IID_IAudioCaptureClient, (void **)&stream->cclient)) != S_OK) - LogHostError(hr); - - if ((hr = IAudioClient_Start(stream->in.client)) != S_OK) - LogHostError(hr); - } - - - // Initialize event & start OUTPUT stream - if (stream->out.client) - { - if ((hr = IAudioClient_GetService(stream->out.client, &pa_IID_IAudioRenderClient, (void **)&stream->rclient)) != S_OK) - LogHostError(hr); - - // Preload buffer (obligatory, othervise ->Start() will fail) - if ((hr = FillOutputBuffer(stream, processor, stream->out.framesPerHostCallback)) != S_OK) - LogHostError(hr); - - // Start - if ((hr = IAudioClient_Start(stream->out.client)) != S_OK) - LogHostError(hr); - } - - // Signal: stream running - stream->running = TRUE; - - // Processing Loop - while (WaitForSingleObject(stream->hCloseRequest, sleep_ms) == WAIT_TIMEOUT) - { - for (i = 0; i < S_COUNT; ++i) - { - // Process S_INPUT/S_OUTPUT - switch (i) - { - // Input stream - case S_INPUT: { - if (stream->cclient == NULL) - break; - PollInputBuffer(stream, processor); - break; } - - // Output stream - case S_OUTPUT: { - - UINT32 frames, padding; - - if (stream->rclient == NULL) - break; - - frames = stream->out.framesPerHostCallback; - - // Get Read position - padding = 0; - hr = IAudioClient_GetCurrentPadding(stream->out.client, &padding); - if (hr != S_OK) - { - LogHostError(hr); - break; - } - - // Fill buffer - frames -= padding; - if (frames != 0) - FillOutputBuffer(stream, processor, frames); - - break; } - } - } - } - - // Process stop - _OnStreamStop(stream); - - // Notify thread is exiting - SetEvent(stream->hThreadDone); - - return 0; -} - -//#endif //VC 2005 - - - - -#if 0 - if(bFirst) { - float masteur; - hr = stream->outVol->GetMasterVolumeLevelScalar(&masteur); - if (hr != S_OK) - LogHostError(hr); - float chan1, chan2; - hr = stream->outVol->GetChannelVolumeLevelScalar(0, &chan1); - if (hr != S_OK) - LogHostError(hr); - hr = stream->outVol->GetChannelVolumeLevelScalar(1, &chan2); - if (hr != S_OK) - LogHostError(hr); - - BOOL bMute; - hr = stream->outVol->GetMute(&bMute); - if (hr != S_OK) - LogHostError(hr); - - stream->outVol->SetMasterVolumeLevelScalar(0.5, NULL); - stream->outVol->SetChannelVolumeLevelScalar(0, 0.5, NULL); - stream->outVol->SetChannelVolumeLevelScalar(1, 0.5, NULL); - stream->outVol->SetMute(FALSE, NULL); - bFirst = FALSE; - } -#endif +/* + * Portable Audio I/O Library WASAPI implementation + * Copyright (c) 2006-2010 David Viens, Dmitry Kostjuchenko + * + * Based on the Open Source API proposed by Ross Bencina + * Copyright (c) 1999-2002 Ross Bencina, Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* + * The text above constitutes the entire PortAudio license; however, + * the PortAudio community also makes the following non-binding requests: + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. It is also + * requested that these non-binding requests be included along with the + * license above. + */ + +/** @file + @ingroup hostapi_src + @brief WASAPI implementation of support for a host API. + @note pa_wasapi currently requires minimum VC 2005, and the latest Vista SDK +*/ + +#define WIN32_LEAN_AND_MEAN // exclude rare headers +#include +#include +#include +#include +#include // must be before other Wasapi headers +#if defined(_MSC_VER) && (_MSC_VER >= 1400) + #include + #define COBJMACROS + #include + #include + #define INITGUID //<< avoid additional linkage of static libs, uneeded code will be optimized out + #include + #include + #undef INITGUID +#endif + +#include "pa_util.h" +#include "pa_allocation.h" +#include "pa_hostapi.h" +#include "pa_stream.h" +#include "pa_cpuload.h" +#include "pa_process.h" +#include "pa_debugprint.h" +#include "pa_win_wasapi.h" + +#ifndef NTDDI_VERSION + + #ifndef _AVRT_ //<< fix MinGW dummy compile by defining missing type: AVRT_PRIORITY + typedef enum _AVRT_PRIORITY + { + AVRT_PRIORITY_LOW = -1, + AVRT_PRIORITY_NORMAL, + AVRT_PRIORITY_HIGH, + AVRT_PRIORITY_CRITICAL + } AVRT_PRIORITY, *PAVRT_PRIORITY; + #endif + + #include // << for IID/CLSID + #include + #include + + #ifndef __LPCGUID_DEFINED__ + #define __LPCGUID_DEFINED__ + typedef const GUID *LPCGUID; + #endif + + #ifndef PROPERTYKEY_DEFINED + #define PROPERTYKEY_DEFINED + typedef struct _tagpropertykey + { + GUID fmtid; + DWORD pid; + } PROPERTYKEY; + #endif + + #ifdef __midl_proxy + #define __MIDL_CONST + #else + #define __MIDL_CONST const + #endif + + #ifndef WAVE_FORMAT_IEEE_FLOAT + #define WAVE_FORMAT_IEEE_FLOAT 0x0003 // 32-bit floating-point + #endif + + // Speaker Positions: + #define SPEAKER_FRONT_LEFT 0x1 + #define SPEAKER_FRONT_RIGHT 0x2 + #define SPEAKER_FRONT_CENTER 0x4 + #define SPEAKER_LOW_FREQUENCY 0x8 + #define SPEAKER_BACK_LEFT 0x10 + #define SPEAKER_BACK_RIGHT 0x20 + #define SPEAKER_FRONT_LEFT_OF_CENTER 0x40 + #define SPEAKER_FRONT_RIGHT_OF_CENTER 0x80 + #define SPEAKER_BACK_CENTER 0x100 + #define SPEAKER_SIDE_LEFT 0x200 + #define SPEAKER_SIDE_RIGHT 0x400 + #define SPEAKER_TOP_CENTER 0x800 + #define SPEAKER_TOP_FRONT_LEFT 0x1000 + #define SPEAKER_TOP_FRONT_CENTER 0x2000 + #define SPEAKER_TOP_FRONT_RIGHT 0x4000 + #define SPEAKER_TOP_BACK_LEFT 0x8000 + #define SPEAKER_TOP_BACK_CENTER 0x10000 + #define SPEAKER_TOP_BACK_RIGHT 0x20000 + + // Bit mask locations reserved for future use + #define SPEAKER_RESERVED 0x7FFC0000 + + // Used to specify that any possible permutation of speaker configurations + #define SPEAKER_ALL 0x80000000 + + // DirectSound Speaker Config + #if (NTDDI_VERSION >= NTDDI_WINXP) + #define KSAUDIO_SPEAKER_DIRECTOUT 0 + #endif + #define KSAUDIO_SPEAKER_MONO (SPEAKER_FRONT_CENTER) + #define KSAUDIO_SPEAKER_STEREO (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT) + #define KSAUDIO_SPEAKER_QUAD (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | \ + SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT) + #define KSAUDIO_SPEAKER_SURROUND (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | \ + SPEAKER_FRONT_CENTER | SPEAKER_BACK_CENTER) + #define KSAUDIO_SPEAKER_5POINT1 (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | \ + SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | \ + SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT) + #define KSAUDIO_SPEAKER_7POINT1 (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | \ + SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | \ + SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | \ + SPEAKER_FRONT_LEFT_OF_CENTER | SPEAKER_FRONT_RIGHT_OF_CENTER) + + #if ( (NTDDI_VERSION >= NTDDI_WINXPSP2) && (NTDDI_VERSION < NTDDI_WS03) ) || (NTDDI_VERSION >= NTDDI_WS03SP1) + + #define KSAUDIO_SPEAKER_5POINT1_SURROUND (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | \ + SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | \ + SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT) + #define KSAUDIO_SPEAKER_7POINT1_SURROUND (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | \ + SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | \ + SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | \ + SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT) + // The following are obsolete 5.1 and 7.1 settings (they lack side speakers). Note this means + // that the default 5.1 and 7.1 settings (KSAUDIO_SPEAKER_5POINT1 and KSAUDIO_SPEAKER_7POINT1 are + // similarly obsolete but are unchanged for compatibility reasons). + #define KSAUDIO_SPEAKER_5POINT1_BACK KSAUDIO_SPEAKER_5POINT1 + #define KSAUDIO_SPEAKER_7POINT1_WIDE KSAUDIO_SPEAKER_7POINT1 + + #endif // XP SP2 and later (chronologically) + + // DVD Speaker Positions + #define KSAUDIO_SPEAKER_GROUND_FRONT_LEFT SPEAKER_FRONT_LEFT + #define KSAUDIO_SPEAKER_GROUND_FRONT_CENTER SPEAKER_FRONT_CENTER + #define KSAUDIO_SPEAKER_GROUND_FRONT_RIGHT SPEAKER_FRONT_RIGHT + #define KSAUDIO_SPEAKER_GROUND_REAR_LEFT SPEAKER_BACK_LEFT + #define KSAUDIO_SPEAKER_GROUND_REAR_RIGHT SPEAKER_BACK_RIGHT + #define KSAUDIO_SPEAKER_TOP_MIDDLE SPEAKER_TOP_CENTER + #define KSAUDIO_SPEAKER_SUPER_WOOFER SPEAKER_LOW_FREQUENCY + + #ifdef WIN64 + #include + typedef LONG NTSTATUS; + typedef void *PKSEVENT_ENTRY; + typedef void *PKSDEVICE; + typedef void *PKSFILTERFACTORY; + typedef void *PKSFILTER; + typedef void *PIRP; + typedef void *PCM_RESOURCE_LIST; + typedef void *PDEVICE_CAPABILITIES; + typedef void *PKSPIN; + typedef void *PKSPROCESSPIN_INDEXENTRY; + typedef void KSATTRIBUTE_LIST; + typedef void *PKSHANDSHAKE; + typedef void *PKTIMER; + typedef void *PKDPC; + typedef void *PKSSTREAM_POINTER; + typedef void KSPROPERTY_SET; + typedef void KSCLOCK_DISPATCH; + typedef void KSMETHOD_SET; + typedef void KSEVENT_SET; + typedef void KSALLOCATOR_DISPATCH; + typedef void KSDEVICE_DISPATCH; + typedef void KSFILTER_DISPATCH; + typedef void KSFILTER_DESCRIPTOR; + typedef void KSPIN_DESCRIPTOR_EX; + typedef void KSPIN_DISPATCH; + typedef void KSDEVICE_DESCRIPTOR; + typedef void *PFNKSDELETEALLOCATOR; + typedef void *PFNKSDEFAULTALLOCATE; + typedef void *PFNKSDEFAULTFREE; + typedef void KSNODE_DESCRIPTOR; + typedef char KSPIN_DESCRIPTOR; + typedef void *PFNKSINTERSECTHANDLEREX; + typedef void *PDEVICE_OBJECT; + typedef void *PHYSICAL_ADDRESS; + typedef void *PKSSTREAM_POINTER_OFFSET; + typedef void *PKSPROCESSPIN; + typedef void *PMDL; + typedef ULONG KSSTREAM_POINTER_OFFSET; + typedef void KSJACK_DESCRIPTION; + #define FASTCALL + #include + #include + #else + typedef struct _BYTE_BLOB + { + unsigned long clSize; + unsigned char abData[ 1 ]; + } BYTE_BLOB; + typedef /* [unique] */ __RPC_unique_pointer BYTE_BLOB *UP_BYTE_BLOB; + typedef LONGLONG REFERENCE_TIME; + #define NONAMELESSUNION + #endif + + #undef WINVER + #undef _WIN32_WINNT + #include + #include + #define COBJMACROS + #define INITGUID + #include + #include + #include + #include + #undef INITGUID + +#endif // NTDDI_VERSION + +#ifndef GUID_SECT + #define GUID_SECT +#endif + +#define __DEFINE_GUID(n,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) static const GUID n GUID_SECT = {l,w1,w2,{b1,b2,b3,b4,b5,b6,b7,b8}} +#define __DEFINE_IID(n,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) static const IID n GUID_SECT = {l,w1,w2,{b1,b2,b3,b4,b5,b6,b7,b8}} +#define __DEFINE_CLSID(n,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) static const CLSID n GUID_SECT = {l,w1,w2,{b1,b2,b3,b4,b5,b6,b7,b8}} +#define PA_DEFINE_CLSID(className, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + __DEFINE_CLSID(pa_CLSID_##className, 0x##l, 0x##w1, 0x##w2, 0x##b1, 0x##b2, 0x##b3, 0x##b4, 0x##b5, 0x##b6, 0x##b7, 0x##b8) +#define PA_DEFINE_IID(interfaceName, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + __DEFINE_IID(pa_IID_##interfaceName, 0x##l, 0x##w1, 0x##w2, 0x##b1, 0x##b2, 0x##b3, 0x##b4, 0x##b5, 0x##b6, 0x##b7, 0x##b8) + +// "1CB9AD4C-DBFA-4c32-B178-C2F568A703B2" +PA_DEFINE_IID(IAudioClient, 1cb9ad4c, dbfa, 4c32, b1, 78, c2, f5, 68, a7, 03, b2); +// "1BE09788-6894-4089-8586-9A2A6C265AC5" +PA_DEFINE_IID(IMMEndpoint, 1be09788, 6894, 4089, 85, 86, 9a, 2a, 6c, 26, 5a, c5); +// "A95664D2-9614-4F35-A746-DE8DB63617E6" +PA_DEFINE_IID(IMMDeviceEnumerator, a95664d2, 9614, 4f35, a7, 46, de, 8d, b6, 36, 17, e6); +// "BCDE0395-E52F-467C-8E3D-C4579291692E" +PA_DEFINE_CLSID(IMMDeviceEnumerator,bcde0395, e52f, 467c, 8e, 3d, c4, 57, 92, 91, 69, 2e); +// "F294ACFC-3146-4483-A7BF-ADDCA7C260E2" +PA_DEFINE_IID(IAudioRenderClient, f294acfc, 3146, 4483, a7, bf, ad, dc, a7, c2, 60, e2); +// "C8ADBD64-E71E-48a0-A4DE-185C395CD317" +PA_DEFINE_IID(IAudioCaptureClient, c8adbd64, e71e, 48a0, a4, de, 18, 5c, 39, 5c, d3, 17); +// Media formats: +__DEFINE_GUID(pa_KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 ); +__DEFINE_GUID(pa_KSDATAFORMAT_SUBTYPE_ADPCM, 0x00000002, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 ); +__DEFINE_GUID(pa_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 ); + +/* use CreateThread for CYGWIN/Windows Mobile, _beginthreadex for all others */ +#if !defined(__CYGWIN__) && !defined(_WIN32_WCE) + #define CREATE_THREAD(PROC) (HANDLE)_beginthreadex( NULL, 0, (PROC), stream, 0, &stream->dwThreadId ) + #define PA_THREAD_FUNC static unsigned WINAPI + #define PA_THREAD_ID unsigned +#else + #define CREATE_THREAD(PROC) CreateThread( NULL, 0, (PROC), stream, 0, &stream->dwThreadId ) + #define PA_THREAD_FUNC static DWORD WINAPI + #define PA_THREAD_ID DWORD +#endif + +// Thread function forward decl. +PA_THREAD_FUNC ProcThreadEvent(void *param); +PA_THREAD_FUNC ProcThreadPoll(void *param); + +// Availabe from Windows 7 +#ifndef AUDCLNT_E_BUFFER_ERROR + #define AUDCLNT_E_BUFFER_ERROR AUDCLNT_ERR(0x018) +#endif +#ifndef AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED + #define AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED AUDCLNT_ERR(0x019) +#endif +#ifndef AUDCLNT_E_INVALID_DEVICE_PERIOD + #define AUDCLNT_E_INVALID_DEVICE_PERIOD AUDCLNT_ERR(0x020) +#endif + +#define MAX_STR_LEN 512 + +enum { S_INPUT = 0, S_OUTPUT, S_COUNT }; + +#define STATIC_ARRAY_SIZE(array) (sizeof(array)/sizeof(array[0])) + +#define PRINT(x) PA_DEBUG(x); + +#define PA_SKELETON_SET_LAST_HOST_ERROR( errorCode, errorText ) \ + PaUtil_SetLastHostErrorInfo( paWASAPI, errorCode, errorText ) + +#ifndef IF_FAILED_JUMP +#define IF_FAILED_JUMP(hr, label) if(FAILED(hr)) goto label; +#endif + +#define SAFE_CLOSE(h) if ((h) != NULL) { CloseHandle((h)); (h) = NULL; } +#define SAFE_RELEASE(punk) if ((punk) != NULL) { (punk)->lpVtbl->Release((punk)); (punk) = NULL; } + +// AVRT is the new "multimedia schedulling stuff" +typedef BOOL (WINAPI *FAvRtCreateThreadOrderingGroup) (PHANDLE,PLARGE_INTEGER,GUID*,PLARGE_INTEGER); +typedef BOOL (WINAPI *FAvRtDeleteThreadOrderingGroup) (HANDLE); +typedef BOOL (WINAPI *FAvRtWaitOnThreadOrderingGroup) (HANDLE); +typedef HANDLE (WINAPI *FAvSetMmThreadCharacteristics) (LPCTSTR,LPDWORD); +typedef BOOL (WINAPI *FAvRevertMmThreadCharacteristics)(HANDLE); +typedef BOOL (WINAPI *FAvSetMmThreadPriority) (HANDLE,AVRT_PRIORITY); + +static HMODULE hDInputDLL = 0; +FAvRtCreateThreadOrderingGroup pAvRtCreateThreadOrderingGroup = NULL; +FAvRtDeleteThreadOrderingGroup pAvRtDeleteThreadOrderingGroup = NULL; +FAvRtWaitOnThreadOrderingGroup pAvRtWaitOnThreadOrderingGroup = NULL; +FAvSetMmThreadCharacteristics pAvSetMmThreadCharacteristics = NULL; +FAvRevertMmThreadCharacteristics pAvRevertMmThreadCharacteristics = NULL; +FAvSetMmThreadPriority pAvSetMmThreadPriority = NULL; + +#define _GetProc(fun, type, name) { \ + fun = (type) GetProcAddress(hDInputDLL,name); \ + if (fun == NULL) { \ + PRINT(("GetProcAddr failed for %s" ,name)); \ + return FALSE; \ + } \ + } \ + +// ------------------------------------------------------------------------------------------ +/* prototypes for functions declared in this file */ +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ +PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); +#ifdef __cplusplus +} +#endif /* __cplusplus */ +// dummy entry point for other compilers and sdks +// currently built using RC1 SDK (5600) +//#if _MSC_VER < 1400 +//PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex ) +//{ + //return paNoError; +//} +//#else + +// ------------------------------------------------------------------------------------------ +static void Terminate( struct PaUtilHostApiRepresentation *hostApi ); +static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate ); +static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, + PaStream** s, + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate, + unsigned long framesPerBuffer, + PaStreamFlags streamFlags, + PaStreamCallback *streamCallback, + void *userData ); +static PaError CloseStream( PaStream* stream ); +static PaError StartStream( PaStream *stream ); +static PaError StopStream( PaStream *stream ); +static PaError AbortStream( PaStream *stream ); +static PaError IsStreamStopped( PaStream *s ); +static PaError IsStreamActive( PaStream *stream ); +static PaTime GetStreamTime( PaStream *stream ); +static double GetStreamCpuLoad( PaStream* stream ); +static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames ); +static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames ); +static signed long GetStreamReadAvailable( PaStream* stream ); +static signed long GetStreamWriteAvailable( PaStream* stream ); + +// ------------------------------------------------------------------------------------------ +/* + These are fields that can be gathered from IDevice and IAudioDevice PRIOR to Initialize, and + done in first pass i assume that neither of these will cause the Driver to "load", but again, + who knows how they implement their stuff + */ +typedef struct PaWasapiDeviceInfo +{ + // Device + IMMDevice *device; + + // from GetId + WCHAR szDeviceID[MAX_STR_LEN]; + + // from GetState + DWORD state; + + // Fields filled from IMMEndpoint'sGetDataFlow + EDataFlow flow; + + // Fields filled from IAudioDevice (_prior_ to Initialize) + // from GetDevicePeriod( + REFERENCE_TIME DefaultDevicePeriod; + REFERENCE_TIME MinimumDevicePeriod; + + // from GetMixFormat + // WAVEFORMATEX *MixFormat;//needs to be CoTaskMemFree'd after use! + + // Default format (setup through Control Panel by user) + WAVEFORMATEXTENSIBLE DefaultFormat; + + // Formfactor + EndpointFormFactor formFactor; +} +PaWasapiDeviceInfo; + +// ------------------------------------------------------------------------------------------ +/* PaWasapiHostApiRepresentation - host api datastructure specific to this implementation */ +typedef struct +{ + PaUtilHostApiRepresentation inheritedHostApiRep; + PaUtilStreamInterface callbackStreamInterface; + PaUtilStreamInterface blockingStreamInterface; + + PaUtilAllocationGroup *allocations; + + /* implementation specific data goes here */ + + //in case we later need the synch + IMMDeviceEnumerator *enumerator; + + //this is the REAL number of devices, whether they are usefull to PA or not! + UINT32 deviceCount; + + WCHAR defaultRenderer [MAX_STR_LEN]; + WCHAR defaultCapturer [MAX_STR_LEN]; + + PaWasapiDeviceInfo *devInfo; + + // Is true when WOW64 Vista Workaround is needed + BOOL useVistaWOW64Workaround; +} +PaWasapiHostApiRepresentation; + +// ------------------------------------------------------------------------------------------ +/* PaWasapiStream - a stream data structure specifically for this implementation */ +typedef struct PaWasapiSubStream +{ + IAudioClient *client; + WAVEFORMATEXTENSIBLE wavex; + UINT32 bufferSize; + REFERENCE_TIME device_latency; + REFERENCE_TIME period; + double latency_seconds; + UINT32 framesPerHostCallback; + AUDCLNT_SHAREMODE shareMode; + UINT32 streamFlags; // AUDCLNT_STREAMFLAGS_EVENTCALLBACK, ... + UINT32 flags; + + // Used by blocking interface: + UINT32 prevTime; // time ms between calls of WriteStream + UINT32 prevSleep; // time ms to sleep from frames written in previous call +} +PaWasapiSubStream; + +// ------------------------------------------------------------------------------------------ +/* PaWasapiHostProcessor - redirects processing data */ +typedef struct PaWasapiHostProcessor +{ + PaWasapiHostProcessorCallback processor; + void *userData; +} +PaWasapiHostProcessor; + +// ------------------------------------------------------------------------------------------ +typedef struct PaWasapiStream +{ + /* IMPLEMENT ME: rename this */ + PaUtilStreamRepresentation streamRepresentation; + PaUtilCpuLoadMeasurer cpuLoadMeasurer; + PaUtilBufferProcessor bufferProcessor; + + // input + PaWasapiSubStream in; + IAudioCaptureClient *cclient; + IAudioEndpointVolume *inVol; + + // output + PaWasapiSubStream out; + IAudioRenderClient *rclient; + IAudioEndpointVolume *outVol; + + // must be volatile to avoid race condition on user query while + // thread is being started + volatile BOOL running; + + PA_THREAD_ID dwThreadId; + HANDLE hThread; + HANDLE hCloseRequest; + HANDLE hThreadDone; + HANDLE hBlockingOpStreamRD; + HANDLE hBlockingOpStreamWR; + + // Host callback Output overrider + PaWasapiHostProcessor hostProcessOverrideOutput; + + // Host callback Input overrider + PaWasapiHostProcessor hostProcessOverrideInput; + + // Defines blocking/callback interface used + BOOL bBlocking; + + // Av Task (MM thread management) + HANDLE hAvTask; + + // Thread priority level + PaWasapiThreadPriority nThreadPriority; +} +PaWasapiStream; + +// Local stream methods +static void _OnStreamStop(PaWasapiStream *stream); +static void _FinishStream(PaWasapiStream *stream); + +// Local statics +static volatile BOOL g_bCOMInitialized = FALSE; + +// ------------------------------------------------------------------------------------------ +#define LogHostError(HRES) __LogHostError(HRES, __FUNCTION__, __FILE__, __LINE__) +static HRESULT __LogHostError(HRESULT res, const char *func, const char *file, int line) +{ + const char *text = NULL; + switch (res) + { + case S_OK: return res; + case E_POINTER :text ="E_POINTER"; break; + case E_INVALIDARG :text ="E_INVALIDARG"; break; + + case AUDCLNT_E_NOT_INITIALIZED :text ="AUDCLNT_E_NOT_INITIALIZED"; break; + case AUDCLNT_E_ALREADY_INITIALIZED :text ="AUDCLNT_E_ALREADY_INITIALIZED"; break; + case AUDCLNT_E_WRONG_ENDPOINT_TYPE :text ="AUDCLNT_E_WRONG_ENDPOINT_TYPE"; break; + case AUDCLNT_E_DEVICE_INVALIDATED :text ="AUDCLNT_E_DEVICE_INVALIDATED"; break; + case AUDCLNT_E_NOT_STOPPED :text ="AUDCLNT_E_NOT_STOPPED"; break; + case AUDCLNT_E_BUFFER_TOO_LARGE :text ="AUDCLNT_E_BUFFER_TOO_LARGE"; break; + case AUDCLNT_E_OUT_OF_ORDER :text ="AUDCLNT_E_OUT_OF_ORDER"; break; + case AUDCLNT_E_UNSUPPORTED_FORMAT :text ="AUDCLNT_E_UNSUPPORTED_FORMAT"; break; + case AUDCLNT_E_INVALID_SIZE :text ="AUDCLNT_E_INVALID_SIZE"; break; + case AUDCLNT_E_DEVICE_IN_USE :text ="AUDCLNT_E_DEVICE_IN_USE"; break; + case AUDCLNT_E_BUFFER_OPERATION_PENDING :text ="AUDCLNT_E_BUFFER_OPERATION_PENDING"; break; + case AUDCLNT_E_THREAD_NOT_REGISTERED :text ="AUDCLNT_E_THREAD_NOT_REGISTERED"; break; + case AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED :text ="AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED"; break; + case AUDCLNT_E_ENDPOINT_CREATE_FAILED :text ="AUDCLNT_E_ENDPOINT_CREATE_FAILED"; break; + case AUDCLNT_E_SERVICE_NOT_RUNNING :text ="AUDCLNT_E_SERVICE_NOT_RUNNING"; break; + case AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED :text ="AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED"; break; + case AUDCLNT_E_EXCLUSIVE_MODE_ONLY :text ="AUDCLNT_E_EXCLUSIVE_MODE_ONLY"; break; + case AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL :text ="AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL"; break; + case AUDCLNT_E_EVENTHANDLE_NOT_SET :text ="AUDCLNT_E_EVENTHANDLE_NOT_SET"; break; + case AUDCLNT_E_INCORRECT_BUFFER_SIZE :text ="AUDCLNT_E_INCORRECT_BUFFER_SIZE"; break; + case AUDCLNT_E_BUFFER_SIZE_ERROR :text ="AUDCLNT_E_BUFFER_SIZE_ERROR"; break; + case AUDCLNT_E_CPUUSAGE_EXCEEDED :text ="AUDCLNT_E_CPUUSAGE_EXCEEDED"; break; + case AUDCLNT_E_BUFFER_ERROR :text ="AUDCLNT_E_BUFFER_ERROR"; break; + case AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED :text ="AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED"; break; + case AUDCLNT_E_INVALID_DEVICE_PERIOD :text ="AUDCLNT_E_INVALID_DEVICE_PERIOD"; break; + + case AUDCLNT_S_BUFFER_EMPTY :text ="AUDCLNT_S_BUFFER_EMPTY"; break; + case AUDCLNT_S_THREAD_ALREADY_REGISTERED :text ="AUDCLNT_S_THREAD_ALREADY_REGISTERED"; break; + case AUDCLNT_S_POSITION_STALLED :text ="AUDCLNT_S_POSITION_STALLED"; break; + + default: + text = "UNKNOWN ERROR"; + } + PRINT(("WASAPI ERROR HRESULT: 0x%X : %s\n [FUNCTION: %s FILE: %s {LINE: %d}]\n", res, text, func, file, line)); + PA_SKELETON_SET_LAST_HOST_ERROR(res, text); + return res; +} + +// ------------------------------------------------------------------------------------------ +#define LogPaError(PAERR) __LogPaError(PAERR, __FUNCTION__, __FILE__, __LINE__) +static PaError __LogPaError(PaError err, const char *func, const char *file, int line) +{ + if (err == paNoError) + return err; + PRINT(("WASAPI ERROR PAERROR: %i : %s\n [FUNCTION: %s FILE: %s {LINE: %d}]\n", err, Pa_GetErrorText(err), func, file, line)); + return err; +} + +// ------------------------------------------------------------------------------------------ +static double nano100ToMillis(REFERENCE_TIME ref) +{ + // 1 nano = 0.000000001 seconds + //100 nano = 0.0000001 seconds + //100 nano = 0.0001 milliseconds + return ((double)ref)*0.0001; +} + +// ------------------------------------------------------------------------------------------ +static double nano100ToSeconds(REFERENCE_TIME ref) +{ + // 1 nano = 0.000000001 seconds + //100 nano = 0.0000001 seconds + //100 nano = 0.0001 milliseconds + return ((double)ref)*0.0000001; +} + +// ------------------------------------------------------------------------------------------ +static REFERENCE_TIME MillisTonano100(double ref) +{ + // 1 nano = 0.000000001 seconds + //100 nano = 0.0000001 seconds + //100 nano = 0.0001 milliseconds + return (REFERENCE_TIME)(ref/0.0001); +} + +// ------------------------------------------------------------------------------------------ +static REFERENCE_TIME SecondsTonano100(double ref) +{ + // 1 nano = 0.000000001 seconds + //100 nano = 0.0000001 seconds + //100 nano = 0.0001 milliseconds + return (REFERENCE_TIME)(ref/0.0000001); +} + +// ------------------------------------------------------------------------------------------ +// Makes Hns period from frames and sample rate +static REFERENCE_TIME MakeHnsPeriod(UINT32 nFrames, DWORD nSamplesPerSec) +{ + return (REFERENCE_TIME)((10000.0 * 1000 / nSamplesPerSec * nFrames) + 0.5); +} + +// ------------------------------------------------------------------------------------------ +// Converts PaSampleFormat to bits per sample value +static WORD PaSampleFormatToBitsPerSample(PaSampleFormat format_id) +{ + switch (format_id & ~paNonInterleaved) + { + case paFloat32: + case paInt32: return 32; + case paInt24: return 24; + case paInt16: return 16; + case paInt8: + case paUInt8: return 8; + } + return 0; +} + +// ------------------------------------------------------------------------------------------ +// Converts PaSampleFormat to bits per sample value +static WORD PaSampleFormatToBytesPerSample(PaSampleFormat format_id) +{ + return PaSampleFormatToBitsPerSample(format_id) >> 3; // 'bits/8' +} + +// ------------------------------------------------------------------------------------------ +// Converts Hns period into number of frames +static UINT32 MakeFramesFromHns(REFERENCE_TIME hnsPeriod, UINT32 nSamplesPerSec) +{ + UINT32 nFrames = (UINT32)( // frames = + 1.0 * hnsPeriod * // hns * + nSamplesPerSec / // (frames / s) / + 1000 / // (ms / s) / + 10000 // (hns / s) / + + 0.5 // rounding + ); + return nFrames; +} + +// ------------------------------------------------------------------------------------------ +// Aligns v backwards +static UINT32 ALIGN_BWD(UINT32 v, UINT32 align) +{ + return ((v - (align ? v % align : 0))); +} + +// ------------------------------------------------------------------------------------------ +// Aligns WASAPI buffer to 128 byte packet boundary. HD Audio will fail to play if buffer +// is misaligned. This problem was solved in Windows 7 were AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED +// is thrown although we must align for Vista anyway. +static UINT32 AlignFramesPerBuffer(UINT32 nFrames, UINT32 nSamplesPerSec, UINT32 nBlockAlign) +{ +#define HDA_PACKET_SIZE 128 + + long packets_total = 10000 * (nSamplesPerSec * nBlockAlign / HDA_PACKET_SIZE); + long frame_bytes = nFrames * nBlockAlign; + long packets; + + // align to packet size + frame_bytes = ALIGN_BWD(frame_bytes, HDA_PACKET_SIZE); + nFrames = frame_bytes / nBlockAlign; + packets = frame_bytes / HDA_PACKET_SIZE; + + // align to packets count + while (packets && ((packets_total % packets) != 0)) + { + --packets; + } + + frame_bytes = packets * HDA_PACKET_SIZE; + nFrames = frame_bytes / nBlockAlign; + + return nFrames; +} + +// ------------------------------------------------------------------------------------------ +static UINT32 GetFramesSleepTime(UINT32 nFrames, UINT32 nSamplesPerSec) +{ + REFERENCE_TIME nDuration; + if (nSamplesPerSec == 0) + return 0; +#define REFTIMES_PER_SEC 10000000 +#define REFTIMES_PER_MILLISEC 10000 + // Calculate the actual duration of the allocated buffer. + nDuration = (REFERENCE_TIME)((double)REFTIMES_PER_SEC * nFrames / nSamplesPerSec); + return (UINT32)(nDuration/REFTIMES_PER_MILLISEC/2); +} + +// ------------------------------------------------------------------------------------------ +static BOOL SetupAVRT() +{ + hDInputDLL = LoadLibraryA("avrt.dll"); + if (hDInputDLL == NULL) + return FALSE; + + _GetProc(pAvRtCreateThreadOrderingGroup, FAvRtCreateThreadOrderingGroup, "AvRtCreateThreadOrderingGroup"); + _GetProc(pAvRtDeleteThreadOrderingGroup, FAvRtDeleteThreadOrderingGroup, "AvRtDeleteThreadOrderingGroup"); + _GetProc(pAvRtWaitOnThreadOrderingGroup, FAvRtWaitOnThreadOrderingGroup, "AvRtWaitOnThreadOrderingGroup"); + _GetProc(pAvSetMmThreadCharacteristics, FAvSetMmThreadCharacteristics, "AvSetMmThreadCharacteristicsA"); + _GetProc(pAvRevertMmThreadCharacteristics,FAvRevertMmThreadCharacteristics,"AvRevertMmThreadCharacteristics"); + _GetProc(pAvSetMmThreadPriority, FAvSetMmThreadPriority, "AvSetMmThreadPriority"); + + return pAvRtCreateThreadOrderingGroup && + pAvRtDeleteThreadOrderingGroup && + pAvRtWaitOnThreadOrderingGroup && + pAvSetMmThreadCharacteristics && + pAvRevertMmThreadCharacteristics && + pAvSetMmThreadPriority; +} + +// ------------------------------------------------------------------------------------------ +static void CloseAVRT() +{ + if (hDInputDLL != NULL) + FreeLibrary(hDInputDLL); + hDInputDLL = NULL; +} + +// ------------------------------------------------------------------------------------------ +static BOOL IsWow64() +{ + // http://msdn.microsoft.com/en-us/library/ms684139(VS.85).aspx + + typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL); + LPFN_ISWOW64PROCESS fnIsWow64Process; + + BOOL bIsWow64 = FALSE; + + // IsWow64Process is not available on all supported versions of Windows. + // Use GetModuleHandle to get a handle to the DLL that contains the function + // and GetProcAddress to get a pointer to the function if available. + + fnIsWow64Process = (LPFN_ISWOW64PROCESS) GetProcAddress( + GetModuleHandle(TEXT("kernel32")), TEXT("IsWow64Process")); + + if (fnIsWow64Process == NULL) + return FALSE; + + if (!fnIsWow64Process(GetCurrentProcess(), &bIsWow64)) + return FALSE; + + return bIsWow64; +} + +// ------------------------------------------------------------------------------------------ +typedef enum EWindowsVersion +{ + WINDOWS_UNKNOWN, + WINDOWS_VISTA_SERVER2008, + WINDOWS_7_SERVER2008R2 +} +EWindowsVersion; +// The function is limited to Vista/7 mostly as we need just to find out Vista/WOW64 combination +// in order to use WASAPI WOW64 workarounds. +static EWindowsVersion GetWindowsVersion() +{ + DWORD dwVersion = 0; + DWORD dwMajorVersion = 0; + DWORD dwMinorVersion = 0; + DWORD dwBuild = 0; + + typedef DWORD (WINAPI *LPFN_GETVERSION)(VOID); + LPFN_GETVERSION fnGetVersion; + + fnGetVersion = (LPFN_GETVERSION) GetProcAddress( + GetModuleHandle(TEXT("kernel32")), TEXT("GetVersion")); + + if (fnGetVersion == NULL) + return WINDOWS_UNKNOWN; + + dwVersion = fnGetVersion(); + + // Get the Windows version + dwMajorVersion = (DWORD)(LOBYTE(LOWORD(dwVersion))); + dwMinorVersion = (DWORD)(HIBYTE(LOWORD(dwVersion))); + + // Get the build number + if (dwVersion < 0x80000000) + dwBuild = (DWORD)(HIWORD(dwVersion)); + + switch (dwMajorVersion) + { + case 6: + switch (dwMinorVersion) + { + case 0: + return WINDOWS_VISTA_SERVER2008; + case 1: + return WINDOWS_7_SERVER2008R2; + } + } + + return WINDOWS_UNKNOWN; +} + +// ------------------------------------------------------------------------------------------ +static BOOL UseWOW64VistaWorkaround() +{ + return (IsWow64() && (GetWindowsVersion() == WINDOWS_VISTA_SERVER2008)); +} + +// ------------------------------------------------------------------------------------------ +PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex ) +{ + PaError result = paNoError; + PaWasapiHostApiRepresentation *paWasapi; + PaDeviceInfo *deviceInfoArray; + HRESULT hr = S_OK; + IMMDeviceCollection* pEndPoints = NULL; + UINT i; + + if (!SetupAVRT()) + { + PRINT(("WASAPI: No AVRT! (not VISTA?)")); + return paNoError; + } + + /* + If COM is already initialized CoInitialize will either return + FALSE, or RPC_E_CHANGED_MODE if it was initialised in a different + threading mode. In either case we shouldn't consider it an error + but we need to be careful to not call CoUninitialize() if + RPC_E_CHANGED_MODE was returned. + */ + hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + if (FAILED(hr) && (hr != RPC_E_CHANGED_MODE)) + { + PRINT(("WASAPI: failed CoInitialize")); + return paUnanticipatedHostError; + } + if (hr != RPC_E_CHANGED_MODE) + g_bCOMInitialized = TRUE; + + paWasapi = (PaWasapiHostApiRepresentation *)PaUtil_AllocateMemory( sizeof(PaWasapiHostApiRepresentation) ); + if (paWasapi == NULL) + { + result = paInsufficientMemory; + goto error; + } + + paWasapi->allocations = PaUtil_CreateAllocationGroup(); + if (paWasapi->allocations == NULL) + { + result = paInsufficientMemory; + goto error; + } + + *hostApi = &paWasapi->inheritedHostApiRep; + (*hostApi)->info.structVersion = 1; + (*hostApi)->info.type = paWASAPI; + (*hostApi)->info.name = "Windows WASAPI"; + (*hostApi)->info.deviceCount = 0; + (*hostApi)->info.defaultInputDevice = paNoDevice; + (*hostApi)->info.defaultOutputDevice = paNoDevice; + + paWasapi->enumerator = NULL; + hr = CoCreateInstance(&pa_CLSID_IMMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, + &pa_IID_IMMDeviceEnumerator, (void **)&paWasapi->enumerator); + IF_FAILED_JUMP(hr, error); + + // getting default device ids in the eMultimedia "role" + { + { + IMMDevice *defaultRenderer = NULL; + hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(paWasapi->enumerator, eRender, eMultimedia, &defaultRenderer); + if (hr != S_OK) + { + if (hr != E_NOTFOUND) + IF_FAILED_JUMP(hr, error); + } + else + { + WCHAR *pszDeviceId = NULL; + hr = IMMDevice_GetId(defaultRenderer, &pszDeviceId); + IF_FAILED_JUMP(hr, error); + wcsncpy(paWasapi->defaultRenderer, pszDeviceId, MAX_STR_LEN-1); + CoTaskMemFree(pszDeviceId); + IMMDevice_Release(defaultRenderer); + } + } + + { + IMMDevice *defaultCapturer = NULL; + hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(paWasapi->enumerator, eCapture, eMultimedia, &defaultCapturer); + if (hr != S_OK) + { + if (hr != E_NOTFOUND) + IF_FAILED_JUMP(hr, error); + } + else + { + WCHAR *pszDeviceId = NULL; + hr = IMMDevice_GetId(defaultCapturer, &pszDeviceId); + IF_FAILED_JUMP(hr, error); + wcsncpy(paWasapi->defaultCapturer, pszDeviceId, MAX_STR_LEN-1); + CoTaskMemFree(pszDeviceId); + IMMDevice_Release(defaultCapturer); + } + } + } + + hr = IMMDeviceEnumerator_EnumAudioEndpoints(paWasapi->enumerator, eAll, DEVICE_STATE_ACTIVE, &pEndPoints); + IF_FAILED_JUMP(hr, error); + + hr = IMMDeviceCollection_GetCount(pEndPoints, &paWasapi->deviceCount); + IF_FAILED_JUMP(hr, error); + + paWasapi->devInfo = (PaWasapiDeviceInfo *)malloc(sizeof(PaWasapiDeviceInfo) * paWasapi->deviceCount); + for (i = 0; i < paWasapi->deviceCount; ++i) + memset(&paWasapi->devInfo[i], 0, sizeof(PaWasapiDeviceInfo)); + + if (paWasapi->deviceCount > 0) + { + (*hostApi)->deviceInfos = (PaDeviceInfo **)PaUtil_GroupAllocateMemory( + paWasapi->allocations, sizeof(PaDeviceInfo *) * paWasapi->deviceCount); + if ((*hostApi)->deviceInfos == NULL) + { + result = paInsufficientMemory; + goto error; + } + + /* allocate all device info structs in a contiguous block */ + deviceInfoArray = (PaDeviceInfo *)PaUtil_GroupAllocateMemory( + paWasapi->allocations, sizeof(PaDeviceInfo) * paWasapi->deviceCount); + if (deviceInfoArray == NULL) + { + result = paInsufficientMemory; + goto error; + } + + for (i = 0; i < paWasapi->deviceCount; ++i) + { + DWORD state = 0; + PaDeviceInfo *deviceInfo = &deviceInfoArray[i]; + deviceInfo->structVersion = 2; + deviceInfo->hostApi = hostApiIndex; + + PA_DEBUG(("WASAPI: device i: %d\n", i)); + + hr = IMMDeviceCollection_Item(pEndPoints, i, &paWasapi->devInfo[i].device); + IF_FAILED_JUMP(hr, error); + + // getting ID + { + WCHAR *pszDeviceId = NULL; + hr = IMMDevice_GetId(paWasapi->devInfo[i].device, &pszDeviceId); + IF_FAILED_JUMP(hr, error); + wcsncpy(paWasapi->devInfo[i].szDeviceID, pszDeviceId, MAX_STR_LEN-1); + CoTaskMemFree(pszDeviceId); + + if (lstrcmpW(paWasapi->devInfo[i].szDeviceID, paWasapi->defaultCapturer) == 0) + {// we found the default input! + (*hostApi)->info.defaultInputDevice = (*hostApi)->info.deviceCount; + } + if (lstrcmpW(paWasapi->devInfo[i].szDeviceID, paWasapi->defaultRenderer) == 0) + {// we found the default output! + (*hostApi)->info.defaultOutputDevice = (*hostApi)->info.deviceCount; + } + } + + hr = IMMDevice_GetState(paWasapi->devInfo[i].device, &paWasapi->devInfo[i].state); + IF_FAILED_JUMP(hr, error); + + if (paWasapi->devInfo[i].state != DEVICE_STATE_ACTIVE) + { + PRINT(("WASAPI device: %d is not currently available (state:%d)\n",i,state)); + } + + { + IPropertyStore *pProperty; + hr = IMMDevice_OpenPropertyStore(paWasapi->devInfo[i].device, STGM_READ, &pProperty); + IF_FAILED_JUMP(hr, error); + + // "Friendly" Name + { + char *deviceName; + PROPVARIANT value; + PropVariantInit(&value); + hr = IPropertyStore_GetValue(pProperty, &PKEY_Device_FriendlyName, &value); + IF_FAILED_JUMP(hr, error); + deviceInfo->name = NULL; + deviceName = (char *)PaUtil_GroupAllocateMemory(paWasapi->allocations, MAX_STR_LEN + 1); + if (deviceName == NULL) + { + result = paInsufficientMemory; + goto error; + } + if (value.pwszVal) + wcstombs(deviceName, value.pwszVal, MAX_STR_LEN-1); + else + _snprintf(deviceName, MAX_STR_LEN-1, "baddev%d", i); + deviceInfo->name = deviceName; + PropVariantClear(&value); + } + + // Default format + { + PROPVARIANT value; + PropVariantInit(&value); + hr = IPropertyStore_GetValue(pProperty, &PKEY_AudioEngine_DeviceFormat, &value); + IF_FAILED_JUMP(hr, error); + memcpy(&paWasapi->devInfo[i].DefaultFormat, value.blob.pBlobData, min(sizeof(paWasapi->devInfo[i].DefaultFormat), value.blob.cbSize)); + // cleanup + PropVariantClear(&value); + } + + // Formfactor + { + PROPVARIANT value; + PropVariantInit(&value); + hr = IPropertyStore_GetValue(pProperty, &PKEY_AudioEndpoint_FormFactor, &value); + IF_FAILED_JUMP(hr, error); + // set + #if defined(DUMMYUNIONNAME) && defined(NONAMELESSUNION) + // avoid breaking strict-aliasing rules in such line: (EndpointFormFactor)(*((UINT *)(((WORD *)&value.wReserved3)+1))); + UINT v; + memcpy(&v, (((WORD *)&value.wReserved3)+1), sizeof(v)); + paWasapi->devInfo[i].formFactor = (EndpointFormFactor)v; + #else + paWasapi->devInfo[i].formFactor = (EndpointFormFactor)value.uintVal; + #endif + PA_DEBUG(("WASAPI: device[%s] form-factor: %d\n", deviceInfo->name, paWasapi->devInfo[i].formFactor)); + // cleanup + PropVariantClear(&value); + } + + SAFE_RELEASE(pProperty); + } + + + // Endpoint data + { + IMMEndpoint *endpoint = NULL; + hr = IMMDevice_QueryInterface(paWasapi->devInfo[i].device, &pa_IID_IMMEndpoint, (void **)&endpoint); + if (SUCCEEDED(hr)) + { + hr = IMMEndpoint_GetDataFlow(endpoint, &paWasapi->devInfo[i].flow); + SAFE_RELEASE(endpoint); + } + } + + // Getting a temporary IAudioClient for more fields + // we make sure NOT to call Initialize yet! + { + IAudioClient *tmpClient = NULL; + + hr = IMMDevice_Activate(paWasapi->devInfo[i].device, &pa_IID_IAudioClient, + CLSCTX_INPROC_SERVER, NULL, (void **)&tmpClient); + IF_FAILED_JUMP(hr, error); + + hr = IAudioClient_GetDevicePeriod(tmpClient, + &paWasapi->devInfo[i].DefaultDevicePeriod, + &paWasapi->devInfo[i].MinimumDevicePeriod); + IF_FAILED_JUMP(hr, error); + + //hr = tmpClient->GetMixFormat(&paWasapi->devInfo[i].MixFormat); + + // Release client + SAFE_RELEASE(tmpClient); + + if (hr != S_OK) + { + //davidv: this happened with my hardware, previously for that same device in DirectSound: + //Digital Output (Realtek AC'97 Audio)'s GUID: {0x38f2cf50,0x7b4c,0x4740,0x86,0xeb,0xd4,0x38,0x66,0xd8,0xc8, 0x9f} + //so something must be _really_ wrong with this device, TODO handle this better. We kind of need GetMixFormat + LogHostError(hr); + goto error; + } + } + + // we can now fill in portaudio device data + deviceInfo->maxInputChannels = 0; //for now + deviceInfo->maxOutputChannels = 0; //for now + switch (paWasapi->devInfo[i].flow) + { + case eRender: + // WASAPI accepts exact channels count + deviceInfo->maxOutputChannels = paWasapi->devInfo[i].DefaultFormat.Format.nChannels; + deviceInfo->defaultHighOutputLatency = nano100ToSeconds(paWasapi->devInfo[i].DefaultDevicePeriod); + deviceInfo->defaultLowOutputLatency = nano100ToSeconds(paWasapi->devInfo[i].MinimumDevicePeriod); + break; + case eCapture: + // WASAPI accepts exact channels count + deviceInfo->maxInputChannels = paWasapi->devInfo[i].DefaultFormat.Format.nChannels; + deviceInfo->defaultHighInputLatency = nano100ToSeconds(paWasapi->devInfo[i].DefaultDevicePeriod); + deviceInfo->defaultLowInputLatency = nano100ToSeconds(paWasapi->devInfo[i].MinimumDevicePeriod); + break; + default: + PRINT(("WASAPI: device %d bad Data FLow! \n",i)); + goto error; + break; + } + + deviceInfo->defaultSampleRate = paWasapi->devInfo[i].DefaultFormat.Format.nSamplesPerSec; + + (*hostApi)->deviceInfos[i] = deviceInfo; + ++(*hostApi)->info.deviceCount; + } + } + + (*hostApi)->Terminate = Terminate; + (*hostApi)->OpenStream = OpenStream; + (*hostApi)->IsFormatSupported = IsFormatSupported; + + PaUtil_InitializeStreamInterface( &paWasapi->callbackStreamInterface, CloseStream, StartStream, + StopStream, AbortStream, IsStreamStopped, IsStreamActive, + GetStreamTime, GetStreamCpuLoad, + PaUtil_DummyRead, PaUtil_DummyWrite, + PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable ); + + PaUtil_InitializeStreamInterface( &paWasapi->blockingStreamInterface, CloseStream, StartStream, + StopStream, AbortStream, IsStreamStopped, IsStreamActive, + GetStreamTime, PaUtil_DummyGetCpuLoad, + ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable ); + + + // findout if platform workaround is required + paWasapi->useVistaWOW64Workaround = UseWOW64VistaWorkaround(); + + SAFE_RELEASE(pEndPoints); + + PRINT(("WASAPI: initialized ok")); + + return paNoError; + +error: + + PRINT(("WASAPI: failed %s error[%d|%s]", __FUNCTION__, result, Pa_GetErrorText(result))); + + SAFE_RELEASE(pEndPoints); + + Terminate((PaUtilHostApiRepresentation *)paWasapi); + + return result; +} + +// ------------------------------------------------------------------------------------------ +static void Terminate( PaUtilHostApiRepresentation *hostApi ) +{ + UINT i; + PaWasapiHostApiRepresentation *paWasapi = (PaWasapiHostApiRepresentation*)hostApi; + if (paWasapi == NULL) + return; + + // Release IMMDeviceEnumerator + SAFE_RELEASE(paWasapi->enumerator); + + for (i = 0; i < paWasapi->deviceCount; ++i) + { + PaWasapiDeviceInfo *info = &paWasapi->devInfo[i]; + SAFE_RELEASE(info->device); + + //if (info->MixFormat) + // CoTaskMemFree(info->MixFormat); + } + free(paWasapi->devInfo); + + if (paWasapi->allocations) + { + PaUtil_FreeAllAllocations(paWasapi->allocations); + PaUtil_DestroyAllocationGroup(paWasapi->allocations); + } + + PaUtil_FreeMemory(paWasapi); + + // Close AVRT + CloseAVRT(); + + // Uninit COM + if (g_bCOMInitialized) + CoUninitialize(); +} + +// ------------------------------------------------------------------------------------------ +static PaWasapiHostApiRepresentation *_GetHostApi(PaError *_error) +{ + PaError error; + + PaUtilHostApiRepresentation *pApi; + if ((error = PaUtil_GetHostApiRepresentation(&pApi, paWASAPI)) != paNoError) + { + if (_error != NULL) + (*_error) = error; + + return NULL; + } + return (PaWasapiHostApiRepresentation *)pApi; +} + +// ------------------------------------------------------------------------------------------ +int PaWasapi_GetDeviceDefaultFormat( void *pFormat, unsigned int nFormatSize, PaDeviceIndex nDevice ) +{ + PaError ret; + PaWasapiHostApiRepresentation *paWasapi; + UINT32 size; + PaDeviceIndex index; + + if (pFormat == NULL) + return paBadBufferPtr; + if (nFormatSize <= 0) + return paBufferTooSmall; + + // Get API + paWasapi = _GetHostApi(&ret); + if (paWasapi == NULL) + return ret; + + // Get device index + ret = PaUtil_DeviceIndexToHostApiDeviceIndex(&index, nDevice, &paWasapi->inheritedHostApiRep); + if (ret != paNoError) + return ret; + + // Validate index + if ((UINT32)index >= paWasapi->deviceCount) + return paInvalidDevice; + + size = min(nFormatSize, (UINT32)sizeof(paWasapi->devInfo[ index ].DefaultFormat)); + memcpy(pFormat, &paWasapi->devInfo[ index ].DefaultFormat, size); + + return size; +} + +// ------------------------------------------------------------------------------------------ +int PaWasapi_GetDeviceRole( PaDeviceIndex nDevice ) +{ + PaError ret; + PaDeviceIndex index; + + // Get API + PaWasapiHostApiRepresentation *paWasapi = _GetHostApi(&ret); + if (paWasapi == NULL) + return ret; + + // Get device index + ret = PaUtil_DeviceIndexToHostApiDeviceIndex(&index, nDevice, &paWasapi->inheritedHostApiRep); + if (ret != paNoError) + return ret; + + // Validate index + if ((UINT32)index >= paWasapi->deviceCount) + return paInvalidDevice; + + return paWasapi->devInfo[ index ].formFactor; +} + +// ------------------------------------------------------------------------------------------ +static void LogWAVEFORMATEXTENSIBLE(const WAVEFORMATEXTENSIBLE *in) +{ + const WAVEFORMATEX *old = (WAVEFORMATEX *)in; + switch (old->wFormatTag) + { + case WAVE_FORMAT_EXTENSIBLE: { + + PRINT(("wFormatTag=WAVE_FORMAT_EXTENSIBLE\n")); + + if (IsEqualGUID(&in->SubFormat, &pa_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) + { + PRINT(("SubFormat=KSDATAFORMAT_SUBTYPE_IEEE_FLOAT\n")); + } + else + if (IsEqualGUID(&in->SubFormat, &pa_KSDATAFORMAT_SUBTYPE_PCM)) + { + PRINT(("SubFormat=KSDATAFORMAT_SUBTYPE_PCM\n")); + } + else + { + PRINT(("SubFormat=CUSTOM GUID{%d:%d:%d:%d%d%d%d%d%d%d%d}\n", + in->SubFormat.Data1, + in->SubFormat.Data2, + in->SubFormat.Data3, + (int)in->SubFormat.Data4[0], + (int)in->SubFormat.Data4[1], + (int)in->SubFormat.Data4[2], + (int)in->SubFormat.Data4[3], + (int)in->SubFormat.Data4[4], + (int)in->SubFormat.Data4[5], + (int)in->SubFormat.Data4[6], + (int)in->SubFormat.Data4[7])); + } + PRINT(("Samples.wValidBitsPerSample=%d\n", in->Samples.wValidBitsPerSample)); + PRINT(("dwChannelMask =0x%X\n",in->dwChannelMask)); + + break; } + + case WAVE_FORMAT_PCM: PRINT(("wFormatTag=WAVE_FORMAT_PCM\n")); break; + case WAVE_FORMAT_IEEE_FLOAT: PRINT(("wFormatTag=WAVE_FORMAT_IEEE_FLOAT\n")); break; + default : PRINT(("wFormatTag=UNKNOWN(%d)\n",old->wFormatTag)); break; + } + + PRINT(("nChannels =%d\n",old->nChannels)); + PRINT(("nSamplesPerSec =%d\n",old->nSamplesPerSec)); + PRINT(("nAvgBytesPerSec=%d\n",old->nAvgBytesPerSec)); + PRINT(("nBlockAlign =%d\n",old->nBlockAlign)); + PRINT(("wBitsPerSample =%d\n",old->wBitsPerSample)); + PRINT(("cbSize =%d\n",old->cbSize)); +} + +// ------------------------------------------------------------------------------------------ +static PaSampleFormat waveformatToPaFormat(const WAVEFORMATEXTENSIBLE *in) +{ + const WAVEFORMATEX *old = (WAVEFORMATEX *)in; + + switch (old->wFormatTag) + { + case WAVE_FORMAT_EXTENSIBLE: { + if (IsEqualGUID(&in->SubFormat, &pa_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) + { + if (in->Samples.wValidBitsPerSample == 32) + return paFloat32; + } + else + if (IsEqualGUID(&in->SubFormat, &pa_KSDATAFORMAT_SUBTYPE_PCM)) + { + switch (old->wBitsPerSample) + { + case 32: return paInt32; + case 24: return paInt24; + case 8: return paUInt8; + case 16: return paInt16; + } + } + break; } + + case WAVE_FORMAT_IEEE_FLOAT: + return paFloat32; + + case WAVE_FORMAT_PCM: { + switch (old->wBitsPerSample) + { + case 32: return paInt32; + case 24: return paInt24; + case 8: return paUInt8; + case 16: return paInt16; + } + break; } + } + + return paCustomFormat; +} + +// ------------------------------------------------------------------------------------------ +static PaError MakeWaveFormatFromParams(WAVEFORMATEXTENSIBLE *wavex, const PaStreamParameters *params, + double sampleRate) +{ + WORD bitsPerSample; + WAVEFORMATEX *old; + DWORD channelMask = 0; + PaWasapiStreamInfo *streamInfo = (PaWasapiStreamInfo *)params->hostApiSpecificStreamInfo; + + // Get user assigned channel mask + if ((streamInfo != NULL) && (streamInfo->flags & paWinWasapiUseChannelMask)) + channelMask = streamInfo->channelMask; + + // Convert PaSampleFormat to bits per sample + if ((bitsPerSample = PaSampleFormatToBitsPerSample(params->sampleFormat)) == 0) + return paSampleFormatNotSupported; + + memset(wavex, 0, sizeof(*wavex)); + + old = (WAVEFORMATEX *)wavex; + old->nChannels = (WORD)params->channelCount; + old->nSamplesPerSec = (DWORD)sampleRate; + if ((old->wBitsPerSample = bitsPerSample) > 16) + { + old->wBitsPerSample = 32; // 20 or 24 bits must go in 32 bit containers (ints) + } + old->nBlockAlign = (old->nChannels * (old->wBitsPerSample/8)); + old->nAvgBytesPerSec = (old->nSamplesPerSec * old->nBlockAlign); + + //WAVEFORMATEX + /*if ((params->channelCount <= 2) && ((bitsPerSample == 16) || (bitsPerSample == 8))) + { + old->cbSize = 0; + old->wFormatTag = WAVE_FORMAT_PCM; + } + //WAVEFORMATEXTENSIBLE + else*/ + { + old->wFormatTag = WAVE_FORMAT_EXTENSIBLE; + old->cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX); + + if ((params->sampleFormat & ~paNonInterleaved) == paFloat32) + wavex->SubFormat = pa_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; + else + wavex->SubFormat = pa_KSDATAFORMAT_SUBTYPE_PCM; + + wavex->Samples.wValidBitsPerSample = bitsPerSample; //no extra padding! + + // Set channel mask + if (channelMask != 0) + { + wavex->dwChannelMask = channelMask; + } + else + { + switch (params->channelCount) + { + case 1: wavex->dwChannelMask = KSAUDIO_SPEAKER_MONO; break; + case 2: wavex->dwChannelMask = KSAUDIO_SPEAKER_STEREO; break; + case 4: wavex->dwChannelMask = KSAUDIO_SPEAKER_QUAD; break; + case 6: wavex->dwChannelMask = KSAUDIO_SPEAKER_5POINT1; break; + case 8: wavex->dwChannelMask = KSAUDIO_SPEAKER_7POINT1; break; + default: wavex->dwChannelMask = 0; break; + } + } + } + return paNoError; +} + +// ------------------------------------------------------------------------------------------ +static void wasapiFillWFEXT( WAVEFORMATEXTENSIBLE* pwfext, PaSampleFormat sampleFormat, double sampleRate, int channelCount) +{ + PA_DEBUG(( "sampleFormat = %lx\n" , sampleFormat )); + PA_DEBUG(( "sampleRate = %f\n" , sampleRate )); + PA_DEBUG(( "chanelCount = %d\n", channelCount )); + + pwfext->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; + pwfext->Format.nChannels = (WORD)channelCount; + pwfext->Format.nSamplesPerSec = (DWORD)sampleRate; + if(channelCount == 1) + pwfext->dwChannelMask = KSAUDIO_SPEAKER_DIRECTOUT; + else + pwfext->dwChannelMask = KSAUDIO_SPEAKER_STEREO; + if(sampleFormat == paFloat32) + { + pwfext->Format.nBlockAlign = (WORD)(channelCount * 4); + pwfext->Format.wBitsPerSample = 32; + pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX); + pwfext->Samples.wValidBitsPerSample = 32; + pwfext->SubFormat = pa_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; + } + else if(sampleFormat == paInt32) + { + pwfext->Format.nBlockAlign = (WORD)(channelCount * 4); + pwfext->Format.wBitsPerSample = 32; + pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX); + pwfext->Samples.wValidBitsPerSample = 32; + pwfext->SubFormat = pa_KSDATAFORMAT_SUBTYPE_PCM; + } + else if(sampleFormat == paInt24) + { + pwfext->Format.nBlockAlign = (WORD)(channelCount * 4); + pwfext->Format.wBitsPerSample = 32; // 24-bit in 32-bit int container + pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX); + pwfext->Samples.wValidBitsPerSample = 24; + pwfext->SubFormat = pa_KSDATAFORMAT_SUBTYPE_PCM; + } + else if(sampleFormat == paInt16) + { + pwfext->Format.nBlockAlign = (WORD)(channelCount * 2); + pwfext->Format.wBitsPerSample = 16; + pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX); + pwfext->Samples.wValidBitsPerSample = 16; + pwfext->SubFormat = pa_KSDATAFORMAT_SUBTYPE_PCM; + } + pwfext->Format.nAvgBytesPerSec = pwfext->Format.nSamplesPerSec * pwfext->Format.nBlockAlign; +} + +// ------------------------------------------------------------------------------------------ +static PaError GetClosestFormat(IAudioClient *myClient, double sampleRate, + const PaStreamParameters *params, AUDCLNT_SHAREMODE shareMode, WAVEFORMATEXTENSIBLE *outWavex) +{ + PaError answer = paInvalidSampleRate; + WAVEFORMATEX *sharedClosestMatch = NULL; + HRESULT hr = !S_OK; + + MakeWaveFormatFromParams(outWavex, params, sampleRate); + + hr = IAudioClient_IsFormatSupported(myClient, shareMode, &outWavex->Format, (shareMode == AUDCLNT_SHAREMODE_SHARED ? &sharedClosestMatch : NULL)); + if (hr == S_OK) + answer = paFormatIsSupported; + else + if (sharedClosestMatch) + { + WORD bitsPerSample; + WAVEFORMATEXTENSIBLE *ext = (WAVEFORMATEXTENSIBLE*)sharedClosestMatch; + + GUID subf_guid = GUID_NULL; + if (sharedClosestMatch->wFormatTag == WAVE_FORMAT_EXTENSIBLE) + { + memcpy(outWavex, sharedClosestMatch, sizeof(WAVEFORMATEXTENSIBLE)); + subf_guid = ext->SubFormat; + } + else + memcpy(outWavex, sharedClosestMatch, sizeof(WAVEFORMATEX)); + + CoTaskMemFree(sharedClosestMatch); + + // Make supported by default + answer = paFormatIsSupported; + + // Validate SampleRate + if ((DWORD)sampleRate != outWavex->Format.nSamplesPerSec) + return paInvalidSampleRate; + + // Validate Channel count + if ((WORD)params->channelCount != outWavex->Format.nChannels) + return paInvalidChannelCount; + + // Validate Sample format + if ((bitsPerSample = PaSampleFormatToBitsPerSample(params->sampleFormat)) == 0) + return paSampleFormatNotSupported; + + // Validate Sample format: bit size (WASAPI does not limit 'bit size') + //if (bitsPerSample != outWavex->Format.wBitsPerSample) + // return paSampleFormatNotSupported; + + // Validate Sample format: paFloat32 (WASAPI does not limit 'bit type') + //if ((params->sampleFormat == paFloat32) && (subf_guid != KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) + // return paSampleFormatNotSupported; + + // Validate Sample format: paInt32 (WASAPI does not limit 'bit type') + //if ((params->sampleFormat == paInt32) && (subf_guid != KSDATAFORMAT_SUBTYPE_PCM)) + // return paSampleFormatNotSupported; + } + else + { + //it doesnt suggest anything?? ok lets show it the MENU! + int i; + +#define FORMATTESTS 3 +static const int BestToWorst[FORMATTESTS]={ paFloat32, paInt24, paInt16 }; + + //ok fun time as with pa_win_mme, we know only a refusal of the user-requested + //sampleRate+num Channel is disastrous, as the portaudio buffer processor converts between anything + //so lets only use the number + for (i = 0; i < FORMATTESTS; ++i) + { + WAVEFORMATEXTENSIBLE ext = { 0 }; + wasapiFillWFEXT(&ext,BestToWorst[i],sampleRate,params->channelCount); + + hr = IAudioClient_IsFormatSupported(myClient, shareMode, &ext.Format, (shareMode == AUDCLNT_SHAREMODE_SHARED ? &sharedClosestMatch : NULL)); + if (hr == S_OK) + { + memcpy(outWavex,&ext,sizeof(WAVEFORMATEXTENSIBLE)); + answer = paFormatIsSupported; + break; + } + } + + if (answer != paFormatIsSupported) + { + // try MIX format? + // why did it HAVE to come to this .... + WAVEFORMATEX pcm16WaveFormat = { 0 }; + pcm16WaveFormat.wFormatTag = WAVE_FORMAT_PCM; + pcm16WaveFormat.nChannels = 2; + pcm16WaveFormat.nSamplesPerSec = (DWORD)sampleRate; + pcm16WaveFormat.nBlockAlign = 4; + pcm16WaveFormat.nAvgBytesPerSec = pcm16WaveFormat.nSamplesPerSec*pcm16WaveFormat.nBlockAlign; + pcm16WaveFormat.wBitsPerSample = 16; + pcm16WaveFormat.cbSize = 0; + + hr = IAudioClient_IsFormatSupported(myClient, shareMode, &pcm16WaveFormat, (shareMode == AUDCLNT_SHAREMODE_SHARED ? &sharedClosestMatch : NULL)); + if (hr == S_OK) + { + memcpy(outWavex,&pcm16WaveFormat,sizeof(WAVEFORMATEX)); + answer = paFormatIsSupported; + } + } + + LogHostError(hr); + } + + return answer; +} + +// ------------------------------------------------------------------------------------------ +static PaError IsStreamParamsValid(struct PaUtilHostApiRepresentation *hostApi, + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate) +{ + if (hostApi == NULL) + return paHostApiNotFound; + if ((UINT32)sampleRate == 0) + return paInvalidSampleRate; + + if (inputParameters != NULL) + { + /* all standard sample formats are supported by the buffer adapter, + this implementation doesn't support any custom sample formats */ + if (inputParameters->sampleFormat & paCustomFormat) + return paSampleFormatNotSupported; + + /* unless alternate device specification is supported, reject the use of + paUseHostApiSpecificDeviceSpecification */ + if (inputParameters->device == paUseHostApiSpecificDeviceSpecification) + return paInvalidDevice; + + /* check that input device can support inputChannelCount */ + if (inputParameters->channelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels) + return paInvalidChannelCount; + + /* validate inputStreamInfo */ + if (inputParameters->hostApiSpecificStreamInfo) + { + PaWasapiStreamInfo *inputStreamInfo = (PaWasapiStreamInfo *)inputParameters->hostApiSpecificStreamInfo; + if ((inputStreamInfo->size != sizeof(PaWasapiStreamInfo)) || + (inputStreamInfo->version != 1) || + (inputStreamInfo->hostApiType != paWASAPI)) + { + return paIncompatibleHostApiSpecificStreamInfo; + } + } + + return paNoError; + } + + if (outputParameters != NULL) + { + /* all standard sample formats are supported by the buffer adapter, + this implementation doesn't support any custom sample formats */ + if (outputParameters->sampleFormat & paCustomFormat) + return paSampleFormatNotSupported; + + /* unless alternate device specification is supported, reject the use of + paUseHostApiSpecificDeviceSpecification */ + if (outputParameters->device == paUseHostApiSpecificDeviceSpecification) + return paInvalidDevice; + + /* check that output device can support outputChannelCount */ + if (outputParameters->channelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels) + return paInvalidChannelCount; + + /* validate outputStreamInfo */ + if(outputParameters->hostApiSpecificStreamInfo) + { + PaWasapiStreamInfo *outputStreamInfo = (PaWasapiStreamInfo *)outputParameters->hostApiSpecificStreamInfo; + if ((outputStreamInfo->size != sizeof(PaWasapiStreamInfo)) || + (outputStreamInfo->version != 1) || + (outputStreamInfo->hostApiType != paWASAPI)) + { + return paIncompatibleHostApiSpecificStreamInfo; + } + } + + return paNoError; + } + + return (inputParameters || outputParameters ? paNoError : paInternalError); +} + +// ------------------------------------------------------------------------------------------ +static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate ) +{ + IAudioClient *tmpClient = NULL; + PaWasapiHostApiRepresentation *paWasapi = (PaWasapiHostApiRepresentation*)hostApi; + PaWasapiStreamInfo *inputStreamInfo = NULL, *outputStreamInfo = NULL; + + // Validate PaStreamParameters + PaError error; + if ((error = IsStreamParamsValid(hostApi, inputParameters, outputParameters, sampleRate)) != paNoError) + return error; + + if (inputParameters != NULL) + { + WAVEFORMATEXTENSIBLE wavex; + HRESULT hr; + PaError answer; + AUDCLNT_SHAREMODE shareMode = AUDCLNT_SHAREMODE_SHARED; + inputStreamInfo = (PaWasapiStreamInfo *)inputParameters->hostApiSpecificStreamInfo; + + if (inputStreamInfo && (inputStreamInfo->flags & paWinWasapiExclusive)) + shareMode = AUDCLNT_SHAREMODE_EXCLUSIVE; + + hr = IMMDevice_Activate(paWasapi->devInfo[inputParameters->device].device, + &pa_IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, (void **)&tmpClient); + if (hr != S_OK) + { + LogHostError(hr); + return paInvalidDevice; + } + + answer = GetClosestFormat(tmpClient, sampleRate, inputParameters, shareMode, &wavex); + SAFE_RELEASE(tmpClient); + + if (answer != paFormatIsSupported) + return answer; + } + + if (outputParameters != NULL) + { + HRESULT hr; + WAVEFORMATEXTENSIBLE wavex; + PaError answer; + AUDCLNT_SHAREMODE shareMode = AUDCLNT_SHAREMODE_SHARED; + outputStreamInfo = (PaWasapiStreamInfo *)outputParameters->hostApiSpecificStreamInfo; + + if (outputStreamInfo && (outputStreamInfo->flags & paWinWasapiExclusive)) + shareMode = AUDCLNT_SHAREMODE_EXCLUSIVE; + + hr = IMMDevice_Activate(paWasapi->devInfo[outputParameters->device].device, + &pa_IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, (void **)&tmpClient); + if (hr != S_OK) + { + LogHostError(hr); + return paInvalidDevice; + } + + answer = GetClosestFormat(tmpClient, sampleRate, outputParameters, shareMode, &wavex); + SAFE_RELEASE(tmpClient); + + if (answer != paFormatIsSupported) + return answer; + } + + return paFormatIsSupported; +} + +// ------------------------------------------------------------------------------------------ +static HRESULT CreateAudioClient(PaWasapiSubStream *pSubStream, PaWasapiDeviceInfo *info, + const PaStreamParameters *params, UINT32 framesPerLatency, double sampleRate, UINT32 streamFlags, + PaError *pa_error) +{ + PaError error; + HRESULT hr = S_OK; + UINT32 nFrames = 0; + IAudioClient *pAudioClient = NULL; + + if (!pSubStream || !info || !params) + return E_POINTER; + if ((UINT32)sampleRate == 0) + return E_INVALIDARG; + + // Get the audio client + hr = IMMDevice_Activate(info->device, &pa_IID_IAudioClient, CLSCTX_ALL, NULL, (void **)&pAudioClient); + if (hr != S_OK) + { + LogHostError(hr); + goto done; + } + + // Get closest format + if ((error = GetClosestFormat(pAudioClient, sampleRate, params, pSubStream->shareMode, &pSubStream->wavex)) != paFormatIsSupported) + { + if (pa_error) + (*pa_error) = error; + return AUDCLNT_E_UNSUPPORTED_FORMAT; + } + + // Add latency frames + framesPerLatency += MakeFramesFromHns(SecondsTonano100(params->suggestedLatency), pSubStream->wavex.Format.nSamplesPerSec); + + // Align frames to HD Audio packet size of 128 bytes for Exclusive mode only. + // Not aligning on Windows Vista will cause Event timeout, although Windows 7 will + // return AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED error to realign buffer. Aligning is necessary + // for Exclusive mode only! when audio data is feeded directly to hardware. + if (pSubStream->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE) + { + framesPerLatency = AlignFramesPerBuffer(framesPerLatency, + pSubStream->wavex.Format.nSamplesPerSec, pSubStream->wavex.Format.nBlockAlign); + } + + // Calculate period + pSubStream->period = MakeHnsPeriod(framesPerLatency, pSubStream->wavex.Format.nSamplesPerSec); + + // Enforce min/max period for device in Shared mode to avoid distorted sound. + // Avoid doing so for Exclusive mode as alignment will suffer. Exclusive mode processes + // big buffers without problem. Push Exclusive beyond limits if possible. + if (pSubStream->shareMode == AUDCLNT_SHAREMODE_SHARED) + { + if (pSubStream->period < info->DefaultDevicePeriod) + pSubStream->period = info->DefaultDevicePeriod; + } + // Issues with setting exclusive mode due to pSubStream->period beeing wrong somehow + // Most likely reason is that framesPerLatency is hardcoded to 0. + else if (pSubStream->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE) + pSubStream->period = info->MinimumDevicePeriod; // Gives 3ms of latency + + // Open the stream and associate it with an audio session + hr = IAudioClient_Initialize(pAudioClient, + pSubStream->shareMode, + streamFlags/*AUDCLNT_STREAMFLAGS_EVENTCALLBACK*/, + pSubStream->period, + (pSubStream->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? pSubStream->period : 0), + &pSubStream->wavex.Format, + NULL); + + // If the requested buffer size is not aligned... + if (hr == AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED) + { + PRINT(("WASAPI: CreateAudioClient: aligning buffer size")); + + // Get the next aligned frame + hr = IAudioClient_GetBufferSize(pAudioClient, &nFrames); + if (hr != S_OK) + { + LogHostError(hr); + goto done; + } + + // Release the previous allocations + SAFE_RELEASE(pAudioClient); + + // Create a new audio client + hr = IMMDevice_Activate(info->device, &pa_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioClient); + if (hr != S_OK) + { + LogHostError(hr); + goto done; + } + + // Get closest format + if (GetClosestFormat(pAudioClient, sampleRate, params, pSubStream->shareMode, &pSubStream->wavex) != paFormatIsSupported) + return AUDCLNT_E_UNSUPPORTED_FORMAT; + + // Calculate period + pSubStream->period = MakeHnsPeriod(nFrames, pSubStream->wavex.Format.nSamplesPerSec); + + // Open the stream and associate it with an audio session + hr = IAudioClient_Initialize(pAudioClient, + pSubStream->shareMode, + streamFlags/*AUDCLNT_STREAMFLAGS_EVENTCALLBACK*/, + pSubStream->period, + (pSubStream->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? pSubStream->period : 0), + &pSubStream->wavex.Format, + NULL); + if (hr != S_OK) + { + LogHostError(hr); + goto done; + } + } + else + if (hr != S_OK) + { + LogHostError(hr); + goto done; + } + + // Set result + pSubStream->client = pAudioClient; + IAudioClient_AddRef(pSubStream->client); + +done: + + // Clean up + SAFE_RELEASE(pAudioClient); + return hr; +} + +// ------------------------------------------------------------------------------------------ +static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, + PaStream** s, + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate, + unsigned long framesPerBuffer, + PaStreamFlags streamFlags, + PaStreamCallback *streamCallback, + void *userData ) +{ + PaError result = paNoError; + HRESULT hr; + PaWasapiHostApiRepresentation *paWasapi = (PaWasapiHostApiRepresentation*)hostApi; + PaWasapiStream *stream = NULL; + int inputChannelCount, outputChannelCount; + PaSampleFormat inputSampleFormat, outputSampleFormat; + PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat; + PaWasapiStreamInfo *inputStreamInfo = NULL, *outputStreamInfo = NULL; + PaWasapiDeviceInfo *info = NULL; + UINT32 maxBufferSize; + PaTime buffer_latency; + ULONG framesPerHostCallback; + PaUtilHostBufferSizeMode bufferMode; + + // validate PaStreamParameters + if ((result = IsStreamParamsValid(hostApi, inputParameters, outputParameters, sampleRate)) != paNoError) + return LogPaError(result); + + // Validate platform specific flags + if ((streamFlags & paPlatformSpecificFlags) != 0) + { + LogPaError(result = paInvalidFlag); /* unexpected platform specific flag */ + goto error; + } + + // Allocate memory for PaWasapiStream + if ((stream = (PaWasapiStream *)PaUtil_AllocateMemory(sizeof(PaWasapiStream))) == NULL) + { + LogPaError(result = paInsufficientMemory); + goto error; + } + + // Default thread priority is Audio: for exclusive mode we will use Pro Audio. + stream->nThreadPriority = eThreadPriorityAudio; + + // Set default number of frames: paFramesPerBufferUnspecified + if (framesPerBuffer == paFramesPerBufferUnspecified) + { + UINT32 framesPerBufferIn = 0, framesPerBufferOut = 0; + if (inputParameters != NULL) + { + info = &paWasapi->devInfo[inputParameters->device]; + framesPerBufferIn = MakeFramesFromHns(info->DefaultDevicePeriod, (UINT32)sampleRate); + } + if (outputParameters != NULL) + { + info = &paWasapi->devInfo[outputParameters->device]; + framesPerBufferOut = MakeFramesFromHns(info->DefaultDevicePeriod, (UINT32)sampleRate); + } + // choosing maximum default size + framesPerBuffer = max(framesPerBufferIn, framesPerBufferOut); + } + if (framesPerBuffer == 0) + framesPerBuffer = ((UINT32)sampleRate / 100) * 2; + + // Try create device: Input + if (inputParameters != NULL) + { + inputChannelCount = inputParameters->channelCount; + inputSampleFormat = inputParameters->sampleFormat; + inputStreamInfo = (PaWasapiStreamInfo *)inputParameters->hostApiSpecificStreamInfo; + info = &paWasapi->devInfo[inputParameters->device]; + stream->in.flags = (inputStreamInfo ? inputStreamInfo->flags : 0); + + // Select Exclusive/Shared mode + stream->in.shareMode = AUDCLNT_SHAREMODE_SHARED; + if ((inputStreamInfo != NULL) && (inputStreamInfo->flags & paWinWasapiExclusive)) + { + // Boost thread priority + stream->nThreadPriority = eThreadPriorityProAudio; + // Make Exclusive + stream->in.shareMode = AUDCLNT_SHAREMODE_EXCLUSIVE; + } + + // If user provided explicit thread priority level, use it + if ((inputStreamInfo != NULL) && (inputStreamInfo->flags & paWinWasapiThreadPriority)) + { + if ((inputStreamInfo->threadPriority > eThreadPriorityNone) && + (inputStreamInfo->threadPriority <= eThreadPriorityWindowManager)) + stream->nThreadPriority = inputStreamInfo->threadPriority; + } + + // Choose processing mode + stream->in.streamFlags = AUDCLNT_STREAMFLAGS_EVENTCALLBACK; + if (paWasapi->useVistaWOW64Workaround) + stream->in.streamFlags = 0; // polling interface + if (streamCallback == NULL) + stream->in.streamFlags = 0; // polling interface + if ((inputStreamInfo != NULL) && (inputStreamInfo->flags & paWinWasapiPolling)) + stream->in.streamFlags = 0; // polling interface + + // Create Audio client + hr = CreateAudioClient(&stream->in, info, inputParameters, 0/*framesPerLatency*/, + sampleRate, stream->in.streamFlags, &result); + if (hr != S_OK) + { + LogHostError(hr); + if (hr != AUDCLNT_E_UNSUPPORTED_FORMAT) + result = paInvalidDevice; + + LogPaError(result); + goto error; + } + LogWAVEFORMATEXTENSIBLE(&stream->in.wavex); + + // Get closest format + hostInputSampleFormat = PaUtil_SelectClosestAvailableFormat( waveformatToPaFormat(&stream->in.wavex), inputSampleFormat ); + + // Create volume mgr + stream->inVol = NULL; + /*hr = info->device->Activate( + __uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, NULL, + (void**)&stream->inVol); + if (hr != S_OK) + return paInvalidDevice;*/ + + // Set user-side custom host processor + if ((inputStreamInfo != NULL) && + (inputStreamInfo->flags & paWinWasapiRedirectHostProcessor)) + { + stream->hostProcessOverrideInput.processor = inputStreamInfo->hostProcessorInput; + stream->hostProcessOverrideInput.userData = userData; + } + + // Get max possible buffer size to check if it is not less than that we request + hr = IAudioClient_GetBufferSize(stream->in.client, &maxBufferSize); + if (hr != S_OK) + { + LogHostError(hr); + LogPaError(result = paInvalidDevice); + goto error; + } + + // Correct buffer to max size if it maxed out result of GetBufferSize + stream->in.bufferSize = maxBufferSize; + + // Get interface latency (actually uneeded as we calculate latency from the size + // of maxBufferSize). + hr = IAudioClient_GetStreamLatency(stream->in.client, &stream->in.device_latency); + if (hr != S_OK) + { + LogHostError(hr); + LogPaError(result = paInvalidDevice); + goto error; + } + //stream->in.latency_seconds = nano100ToSeconds(stream->in.device_latency); + + // Number of frames that are required at each period + stream->in.framesPerHostCallback = maxBufferSize; + + // Calculate buffer latency + buffer_latency = (PaTime)maxBufferSize / stream->in.wavex.Format.nSamplesPerSec; + + // Append buffer latency to interface latency in shared mode (see GetStreamLatency notes) + stream->in.latency_seconds += buffer_latency; + + PRINT(("PaWASAPIOpenStream(input): framesPerHostCallback[ %d ] latency[ %.02fms ]\n", (UINT32)stream->in.framesPerHostCallback, (float)(stream->in.latency_seconds*1000.0f))); + } + else + { + inputChannelCount = 0; + inputSampleFormat = hostInputSampleFormat = paInt16; /* Surpress 'uninitialised var' warnings. */ + } + + // Try create device: Output + if (outputParameters != NULL) + { + outputChannelCount = outputParameters->channelCount; + outputSampleFormat = outputParameters->sampleFormat; + outputStreamInfo = (PaWasapiStreamInfo *)outputParameters->hostApiSpecificStreamInfo; + info = &paWasapi->devInfo[outputParameters->device]; + stream->out.flags = (outputStreamInfo ? outputStreamInfo->flags : 0); + + // Select Exclusive/Shared mode + stream->out.shareMode = AUDCLNT_SHAREMODE_SHARED; + if ((outputStreamInfo != NULL) && (outputStreamInfo->flags & paWinWasapiExclusive)) + { + // Boost thread priority + stream->nThreadPriority = eThreadPriorityProAudio; + // Make Exclusive + stream->out.shareMode = AUDCLNT_SHAREMODE_EXCLUSIVE; + } + + // If user provided explicit thread priority level, use it + if ((outputStreamInfo != NULL) && (outputStreamInfo->flags & paWinWasapiThreadPriority)) + { + if ((outputStreamInfo->threadPriority > eThreadPriorityNone) && + (outputStreamInfo->threadPriority <= eThreadPriorityWindowManager)) + stream->nThreadPriority = outputStreamInfo->threadPriority; + } + + // Choose processing mode + stream->out.streamFlags = AUDCLNT_STREAMFLAGS_EVENTCALLBACK; + if (paWasapi->useVistaWOW64Workaround) + stream->out.streamFlags = 0; // polling interface + if (streamCallback == NULL) + stream->out.streamFlags = 0; // polling interface + if ((outputStreamInfo != NULL) && (outputStreamInfo->flags & paWinWasapiPolling)) + stream->out.streamFlags = 0; // polling interface + + // Create Audio client + hr = CreateAudioClient(&stream->out, info, outputParameters, 0/*framesPerLatency*/, + sampleRate, stream->out.streamFlags, &result); + if (hr != S_OK) + { + LogHostError(hr); + if (hr != AUDCLNT_E_UNSUPPORTED_FORMAT) + result = paInvalidDevice; + + LogPaError(result); + goto error; + } + LogWAVEFORMATEXTENSIBLE(&stream->out.wavex); + + // Get closest format + hostOutputSampleFormat = PaUtil_SelectClosestAvailableFormat( waveformatToPaFormat(&stream->out.wavex), outputSampleFormat ); + + // Activate volume + stream->outVol = NULL; + /*hr = info->device->Activate( + __uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, NULL, + (void**)&stream->outVol); + if (hr != S_OK) + return paInvalidDevice;*/ + + // Set user-side custom host processor + if ((outputStreamInfo != NULL) && + (outputStreamInfo->flags & paWinWasapiRedirectHostProcessor)) + { + stream->hostProcessOverrideOutput.processor = outputStreamInfo->hostProcessorOutput; + stream->hostProcessOverrideOutput.userData = userData; + } + + // Get max possible buffer size to check if it is not less than that we request + hr = IAudioClient_GetBufferSize(stream->out.client, &maxBufferSize); + if (hr != S_OK) + { + LogHostError(hr); + LogPaError(result = paInvalidDevice); + goto error; + } + + // Correct buffer to max size if it maxed out result of GetBufferSize + stream->out.bufferSize = maxBufferSize; + + // Get interface latency (actually uneeded as we calculate latency from the size + // of maxBufferSize). + hr = IAudioClient_GetStreamLatency(stream->out.client, &stream->out.device_latency); + if (hr != S_OK) + { + LogHostError(hr); + LogPaError(result = paInvalidDevice); + goto error; + } + //stream->out.latency_seconds = nano100ToSeconds(stream->out.device_latency); + + // Number of frames that are required at each period + stream->out.framesPerHostCallback = maxBufferSize; + + // Calculate buffer latency + buffer_latency = (PaTime)maxBufferSize / stream->out.wavex.Format.nSamplesPerSec; + + // Append buffer latency to interface latency in shared mode (see GetStreamLatency notes) + stream->out.latency_seconds += buffer_latency; + + PRINT(("PaWASAPIOpenStream(output): framesPerHostCallback[ %d ] latency[ %.02fms ]\n", (UINT32)stream->out.framesPerHostCallback, (float)(stream->out.latency_seconds*1000.0f))); + } + else + { + outputChannelCount = 0; + outputSampleFormat = hostOutputSampleFormat = paInt16; /* Surpress 'uninitialized var' warnings. */ + } + + // paWinWasapiPolling must be on/or not on both streams + if ((inputParameters != NULL) && (outputParameters != NULL)) + { + if ((inputStreamInfo != NULL) && (outputStreamInfo != NULL)) + { + if (((inputStreamInfo->flags & paWinWasapiPolling) && + !(outputStreamInfo->flags & paWinWasapiPolling)) + || + (!(inputStreamInfo->flags & paWinWasapiPolling) && + (outputStreamInfo->flags & paWinWasapiPolling))) + { + LogPaError(result = paInvalidFlag); + goto error; + } + } + } + + // Initialize stream representation + if (streamCallback) + { + stream->bBlocking = FALSE; + PaUtil_InitializeStreamRepresentation(&stream->streamRepresentation, + &paWasapi->callbackStreamInterface, + streamCallback, userData); + } + else + { + stream->bBlocking = TRUE; + PaUtil_InitializeStreamRepresentation(&stream->streamRepresentation, + &paWasapi->blockingStreamInterface, + streamCallback, userData); + } + + // Initialize CPU measurer + PaUtil_InitializeCpuLoadMeasurer(&stream->cpuLoadMeasurer, sampleRate); + + if (outputParameters && inputParameters) + { + // serious problem #1 + if (stream->in.period != stream->out.period) + { + PRINT(("WASAPI: OpenStream: period discrepancy\n")); + goto error; + } + + // serious problem #2 + if (stream->out.framesPerHostCallback != stream->in.framesPerHostCallback) + { + PRINT(("WASAPI: OpenStream: framesPerHostCallback discrepancy\n")); + goto error; + } + } + + framesPerHostCallback = (outputParameters) ? stream->out.framesPerHostCallback: + stream->in.framesPerHostCallback; + + // Choose correct mode of buffer processing: + // Exclusive/Shared non paWinWasapiPolling mode: paUtilFixedHostBufferSize - always fixed + // Exclusive/Shared paWinWasapiPolling mode: paUtilBoundedHostBufferSize - may vary + bufferMode = paUtilFixedHostBufferSize; + if (inputParameters && + (!stream->in.streamFlags || ((stream->in.streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) == 0))) + bufferMode = paUtilBoundedHostBufferSize; + else + if (outputParameters && + (!stream->out.streamFlags || ((stream->out.streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) == 0))) + bufferMode = paUtilBoundedHostBufferSize; + + // Initialize buffer processor + result = PaUtil_InitializeBufferProcessor( + &stream->bufferProcessor, + inputChannelCount, + inputSampleFormat, + hostInputSampleFormat, + outputChannelCount, + outputSampleFormat, + hostOutputSampleFormat, + sampleRate, + streamFlags, + framesPerBuffer, + framesPerHostCallback, + bufferMode, + streamCallback, + userData); + if (result != paNoError) + { + LogPaError(result); + goto error; + } + + // Set Input latency + stream->streamRepresentation.streamInfo.inputLatency = + PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor) + + ((inputParameters)?stream->in.latency_seconds :0); + + // Set Output latency + stream->streamRepresentation.streamInfo.outputLatency = + PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor) + + ((outputParameters)?stream->out.latency_seconds :0); + + // Set SR + stream->streamRepresentation.streamInfo.sampleRate = sampleRate; + + (*s) = (PaStream *)stream; + return result; + +error: + + if (stream != NULL) + { + CloseStream(stream); + PaUtil_FreeMemory(stream); + } + + return result; +} + +// ------------------------------------------------------------------------------------------ +static PaError CloseStream( PaStream* s ) +{ + PaError result = paNoError; + PaWasapiStream *stream = (PaWasapiStream*)s; + + // abort active stream + if (IsStreamActive(s)) + { + if ((result = AbortStream(s)) != paNoError) + return result; + } + + SAFE_RELEASE(stream->out.client); + SAFE_RELEASE(stream->in.client); + SAFE_RELEASE(stream->cclient); + SAFE_RELEASE(stream->rclient); + SAFE_RELEASE(stream->inVol); + SAFE_RELEASE(stream->outVol); + + SAFE_CLOSE(stream->hThread); + SAFE_CLOSE(stream->hThreadDone); + SAFE_CLOSE(stream->hCloseRequest); + SAFE_CLOSE(stream->hBlockingOpStreamRD); + SAFE_CLOSE(stream->hBlockingOpStreamWR); + + PaUtil_TerminateBufferProcessor(&stream->bufferProcessor); + PaUtil_TerminateStreamRepresentation(&stream->streamRepresentation); + PaUtil_FreeMemory(stream); + + return result; +} + +// ------------------------------------------------------------------------------------------ +static PaError StartStream( PaStream *s ) +{ + HRESULT hr; + PaWasapiStream *stream = (PaWasapiStream*)s; + + // check if stream is active already + if (IsStreamActive(s)) + return paStreamIsNotStopped; + + PaUtil_ResetBufferProcessor(&stream->bufferProcessor); + + // Create close event + stream->hCloseRequest = CreateEvent(NULL, TRUE, FALSE, NULL); + + // Create thread + if (!stream->bBlocking) + { + // Create thread done event + stream->hThreadDone = CreateEvent(NULL, TRUE, FALSE, NULL); + + if ((stream->in.client && stream->in.streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) || + (stream->out.client && stream->out.streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)) + { + if ((stream->hThread = CREATE_THREAD(ProcThreadEvent)) == NULL) + return paUnanticipatedHostError; + } + else + { + if ((stream->hThread = CREATE_THREAD(ProcThreadPoll)) == NULL) + return paUnanticipatedHostError; + } + } + else + { + // Create blocking operation events (non-signaled event means - blocking operation is pending) + if (stream->out.client) + stream->hBlockingOpStreamWR = CreateEvent(NULL, TRUE, TRUE, NULL); + if (stream->in.client) + stream->hBlockingOpStreamRD = CreateEvent(NULL, TRUE, TRUE, NULL); + + // Initialize event & start INPUT stream + if (stream->in.client) + { + if ((hr = IAudioClient_GetService(stream->in.client, &pa_IID_IAudioCaptureClient, (void **)&stream->cclient)) != S_OK) + { + LogHostError(hr); + return paUnanticipatedHostError; + } + + if ((hr = IAudioClient_Start(stream->in.client)) != S_OK) + { + LogHostError(hr); + return paUnanticipatedHostError; + } + } + + + // Initialize event & start OUTPUT stream + if (stream->out.client) + { + if ((hr = IAudioClient_GetService(stream->out.client, &pa_IID_IAudioRenderClient, (void **)&stream->rclient)) != S_OK) + { + LogHostError(hr); + return paUnanticipatedHostError; + } + + // Start + if ((hr = IAudioClient_Start(stream->out.client)) != S_OK) + { + LogHostError(hr); + return paUnanticipatedHostError; + } + } + + // Signal: stream running + stream->running = TRUE; + + // Set current time + stream->out.prevTime = timeGetTime(); + stream->out.prevSleep = 0; + } + + return paNoError; +} + +// ------------------------------------------------------------------------------------------ +static void _FinishStream(PaWasapiStream *stream) +{ + // Issue command to thread to stop processing and wait for thread exit + if (!stream->bBlocking) + { + SignalObjectAndWait(stream->hCloseRequest, stream->hThreadDone, INFINITE, FALSE); + } + else + // Blocking mode does not own thread + { + // Signal close event and wait for each of 2 blocking operations to complete + if (stream->out.client) + SignalObjectAndWait(stream->hCloseRequest, stream->hBlockingOpStreamWR, INFINITE, TRUE); + if (stream->out.client) + SignalObjectAndWait(stream->hCloseRequest, stream->hBlockingOpStreamRD, INFINITE, TRUE); + + // Process stop + _OnStreamStop(stream); + } + + // Close thread handles to allow restart + SAFE_CLOSE(stream->hThread); + SAFE_CLOSE(stream->hThreadDone); + SAFE_CLOSE(stream->hCloseRequest); + SAFE_CLOSE(stream->hBlockingOpStreamRD); + SAFE_CLOSE(stream->hBlockingOpStreamWR); + + stream->running = FALSE; +} + +// ------------------------------------------------------------------------------------------ +static PaError StopStream( PaStream *s ) +{ + // Finish stream + _FinishStream((PaWasapiStream *)s); + return paNoError; +} + +// ------------------------------------------------------------------------------------------ +static PaError AbortStream( PaStream *s ) +{ + // Finish stream + _FinishStream((PaWasapiStream *)s); + return paNoError; +} + +// ------------------------------------------------------------------------------------------ +static PaError IsStreamStopped( PaStream *s ) +{ + return !((PaWasapiStream *)s)->running; +} + +// ------------------------------------------------------------------------------------------ +static PaError IsStreamActive( PaStream *s ) +{ + return ((PaWasapiStream *)s)->running; +} + +// ------------------------------------------------------------------------------------------ +static PaTime GetStreamTime( PaStream *s ) +{ + PaWasapiStream *stream = (PaWasapiStream*)s; + + /* suppress unused variable warnings */ + (void) stream; + + /* IMPLEMENT ME, see portaudio.h for required behavior*/ + + //this is lame ds and mme does the same thing, quite useless method imho + //why dont we fetch the time in the pa callbacks? + //at least its doing to be clocked to something + return PaUtil_GetTime(); +} + +// ------------------------------------------------------------------------------------------ +static double GetStreamCpuLoad( PaStream* s ) +{ + return PaUtil_GetCpuLoad(&((PaWasapiStream *)s)->cpuLoadMeasurer); +} + +// ------------------------------------------------------------------------------------------ +/* NOT TESTED */ +static PaError ReadStream( PaStream* s, void *_buffer, unsigned long _frames ) +{ + PaWasapiStream *stream = (PaWasapiStream*)s; + + HRESULT hr = S_OK; + UINT32 frames; + BYTE *buffer = (BYTE *)_buffer; + BYTE *data = NULL; + DWORD flags = 0; + UINT32 buffer_size; + + // validate + if (!stream->running) + return paStreamIsStopped; + if (stream->cclient == NULL) + return paBadStreamPtr; + + // Notify blocking op has begun + ResetEvent(stream->hBlockingOpStreamRD); + + while (_frames != 0) + { + // Get the available data in the shared buffer. + if ((hr = IAudioCaptureClient_GetBuffer(stream->cclient, &data, &frames, &flags, NULL, NULL)) != S_OK) + { + if (hr == AUDCLNT_S_BUFFER_EMPTY) + { + // Check if blocking call must be interrupted + if (WaitForSingleObject(stream->hCloseRequest, 1) != WAIT_TIMEOUT) + break; + } + + return LogHostError(hr); + goto stream_rd_end; + } + + // Detect silence + // if (flags & AUDCLNT_BUFFERFLAGS_SILENT) + // data = NULL; + + // Check if frames <= _frames + if (frames > _frames) + frames = _frames; + + // Copy + buffer_size = frames * stream->in.wavex.Format.nBlockAlign; + memcpy(buffer, data, buffer_size); + buffer += buffer_size; + + // Release buffer + if ((hr = IAudioCaptureClient_ReleaseBuffer(stream->cclient, frames)) != S_OK) + { + LogHostError(hr); + goto stream_rd_end; + } + + _frames -= frames; + } + +stream_rd_end: + + // Notify blocking op has ended + SetEvent(stream->hBlockingOpStreamRD); + + return (hr != S_OK ? paUnanticipatedHostError : paNoError); +} + +// ------------------------------------------------------------------------------------------ +static PaError WriteStream( PaStream* s, const void *_buffer, unsigned long _frames ) +{ + PaWasapiStream *stream = (PaWasapiStream*)s; + + UINT32 frames; + const BYTE *buffer = (BYTE *)_buffer; + BYTE *data; + HRESULT hr = S_OK; + UINT32 next_rev_sleep, blocks, block_sleep_ms; + UINT32 i; + + // validate + if (!stream->running) + return paStreamIsStopped; + if (stream->rclient == NULL) + return paBadStreamPtr; + + // Notify blocking op has begun + ResetEvent(stream->hBlockingOpStreamWR); + + // Calculate sleep time for next call + { + UINT32 remainder = 0; + UINT32 sleep_ms = 0; + DWORD elapsed_ms; + blocks = _frames / stream->out.framesPerHostCallback; + block_sleep_ms = GetFramesSleepTime(stream->out.framesPerHostCallback, stream->out.wavex.Format.nSamplesPerSec); + if (blocks == 0) + { + blocks = 1; + sleep_ms = GetFramesSleepTime(_frames, stream->out.wavex.Format.nSamplesPerSec); // partial + } + else + { + remainder = _frames - blocks * stream->out.framesPerHostCallback; + sleep_ms = block_sleep_ms; // full + } + + // Sleep for remainder + elapsed_ms = timeGetTime() - stream->out.prevTime; + if (sleep_ms >= elapsed_ms) + sleep_ms -= elapsed_ms; + + next_rev_sleep = sleep_ms; + } + + // Sleep diff from last call + if (stream->out.prevSleep) + Sleep(stream->out.prevSleep); + stream->out.prevSleep = next_rev_sleep; + + // Feed engine + for (i = 0; i < blocks; ++i) + { + UINT32 available; + UINT32 buffer_size; + + // Get block frames + frames = stream->out.framesPerHostCallback; + if (frames > _frames) + frames = _frames; + + if (i) + Sleep(block_sleep_ms); + + while (frames != 0) + { + UINT32 padding = 0; + + // Check if blocking call must be interrupted + if (WaitForSingleObject(stream->hCloseRequest, 0) != WAIT_TIMEOUT) + break; + + // Get Read position + hr = IAudioClient_GetCurrentPadding(stream->out.client, &padding); + if (hr != S_OK) + { + LogHostError(hr); + goto stream_wr_end; + } + + // Get frames available + if (frames >= padding) + available = frames - padding; + else + available = frames; + + // Get buffer + if ((hr = IAudioRenderClient_GetBuffer(stream->rclient, available, &data)) != S_OK) + { + // Buffer size is too big, waiting + if (hr == AUDCLNT_E_BUFFER_TOO_LARGE) + continue; + LogHostError(hr); + goto stream_wr_end; + } + + // Copy + buffer_size = available * stream->out.wavex.Format.nBlockAlign; + memcpy(data, buffer, buffer_size); + buffer += buffer_size; + + // Release buffer + if ((hr = IAudioRenderClient_ReleaseBuffer(stream->rclient, available, 0)) != S_OK) + { + LogHostError(hr); + goto stream_wr_end; + } + + frames -= available; + } + + _frames -= frames; + } + +stream_wr_end: + + // Set prev time + stream->out.prevTime = timeGetTime(); + + // Notify blocking op has ended + SetEvent(stream->hBlockingOpStreamWR); + + return (hr != S_OK ? paUnanticipatedHostError : paNoError); +} + +// ------------------------------------------------------------------------------------------ +/* NOT TESTED */ +static signed long GetStreamReadAvailable( PaStream* s ) +{ + PaWasapiStream *stream = (PaWasapiStream*)s; + HRESULT hr; + UINT32 pending = 0; + + // validate + if (!stream->running) + return paStreamIsStopped; + if (stream->cclient == NULL) + return paBadStreamPtr; + + hr = IAudioClient_GetCurrentPadding(stream->in.client, &pending); + if (hr != S_OK) + { + LogHostError(hr); + return paUnanticipatedHostError; + } + + return (long)pending; +} + +// ------------------------------------------------------------------------------------------ +static signed long GetStreamWriteAvailable( PaStream* s ) +{ + PaWasapiStream *stream = (PaWasapiStream*)s; + + UINT32 frames = stream->out.framesPerHostCallback; + HRESULT hr; + UINT32 padding = 0; + + // validate + if (!stream->running) + return paStreamIsStopped; + if (stream->rclient == NULL) + return paBadStreamPtr; + + hr = IAudioClient_GetCurrentPadding(stream->out.client, &padding); + if (hr != S_OK) + { + LogHostError(hr); + return paUnanticipatedHostError; + } + + // Calculate + frames -= padding; + + return frames; +} + +// ------------------------------------------------------------------------------------------ +static void WaspiHostProcessingLoop( void *inputBuffer, long inputFrames, + void *outputBuffer, long outputFrames, + void *userData ) +{ + PaWasapiStream *stream = (PaWasapiStream*)userData; + PaStreamCallbackTimeInfo timeInfo = {0,0,0}; + PaStreamCallbackFlags flags = 0; + int callbackResult; + unsigned long framesProcessed; + HRESULT hr; + UINT32 pending; + + PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer ); + + /* + Pa_GetStreamTime: + - generate timing information + - handle buffer slips + */ + timeInfo.currentTime = PaUtil_GetTime(); + // Query input latency + if (stream->in.client != NULL) + { + PaTime pending_time; + if ((hr = IAudioClient_GetCurrentPadding(stream->in.client, &pending)) == S_OK) + pending_time = (PaTime)pending / (PaTime)stream->in.wavex.Format.nSamplesPerSec; + else + pending_time = (PaTime)stream->in.latency_seconds; + + timeInfo.inputBufferAdcTime = timeInfo.currentTime + pending_time; + } + // Query output current latency + if (stream->out.client != NULL) + { + PaTime pending_time; + if ((hr = IAudioClient_GetCurrentPadding(stream->out.client, &pending)) == S_OK) + pending_time = (PaTime)pending / (PaTime)stream->out.wavex.Format.nSamplesPerSec; + else + pending_time = (PaTime)stream->out.latency_seconds; + + timeInfo.outputBufferDacTime = timeInfo.currentTime + pending_time; + } + + /* + If you need to byte swap or shift inputBuffer to convert it into a + portaudio format, do it here. + */ + + PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, flags ); + + /* + depending on whether the host buffers are interleaved, non-interleaved + or a mixture, you will want to call PaUtil_SetInterleaved*Channels(), + PaUtil_SetNonInterleaved*Channel() or PaUtil_Set*Channel() here. + */ + + if (stream->bufferProcessor.inputChannelCount > 0) + { + PaUtil_SetInputFrameCount( &stream->bufferProcessor, inputFrames ); + PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, + 0, /* first channel of inputBuffer is channel 0 */ + inputBuffer, + 0 ); /* 0 - use inputChannelCount passed to init buffer processor */ + } + + if (stream->bufferProcessor.outputChannelCount > 0) + { + PaUtil_SetOutputFrameCount( &stream->bufferProcessor, outputFrames); + PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, + 0, /* first channel of outputBuffer is channel 0 */ + outputBuffer, + 0 ); /* 0 - use outputChannelCount passed to init buffer processor */ + } + + /* you must pass a valid value of callback result to PaUtil_EndBufferProcessing() + in general you would pass paContinue for normal operation, and + paComplete to drain the buffer processor's internal output buffer. + You can check whether the buffer processor's output buffer is empty + using PaUtil_IsBufferProcessorOuputEmpty( bufferProcessor ) + */ + callbackResult = paContinue; + framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult ); + + /* + If you need to byte swap or shift outputBuffer to convert it to + host format, do it here. + */ + + PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed ); + + if (callbackResult == paContinue) + { + /* nothing special to do */ + } + else + if (callbackResult == paAbort) + { + // stop stream + SetEvent(stream->hCloseRequest); + } + else + { + // stop stream + SetEvent(stream->hCloseRequest); + } +} + +// ------------------------------------------------------------------------------------------ +HANDLE MMCSS_activate(const char *name) +{ + DWORD task_idx = 0; + HANDLE hTask = pAvSetMmThreadCharacteristics(name, &task_idx); + if (hTask == NULL) + { + PRINT(("WASAPI: AvSetMmThreadCharacteristics failed!\n")); + } + + /*BOOL priority_ok = pAvSetMmThreadPriority(hTask, AVRT_PRIORITY_NORMAL); + if (priority_ok == FALSE) + { + PRINT(("WASAPI: AvSetMmThreadPriority failed!\n")); + }*/ + + // debug + { + int cur_priority = GetThreadPriority(GetCurrentThread()); + DWORD cur_priority_class = GetPriorityClass(GetCurrentProcess()); + PRINT(("WASAPI: thread[ priority-0x%X class-0x%X ]\n", cur_priority, cur_priority_class)); + } + + return hTask; +} + +// ------------------------------------------------------------------------------------------ +void MMCSS_deactivate(HANDLE hTask) +{ + if (!hTask) + return; + + if (pAvRevertMmThreadCharacteristics(hTask) == FALSE) + { + PRINT(("WASAPI: AvRevertMmThreadCharacteristics failed!\n")); + } +} + +// ------------------------------------------------------------------------------------------ +PaError PaWasapi_ThreadPriorityBoost(void **hTask, PaWasapiThreadPriority nPriorityClass) +{ + static const char *mmcs_name[] = + { + NULL, + "Audio", + "Capture", + "Distribution", + "Games", + "Playback", + "Pro Audio", + "Window Manager" + }; + HANDLE task; + + if (hTask == NULL) + return paUnanticipatedHostError; + + if ((UINT32)nPriorityClass >= STATIC_ARRAY_SIZE(mmcs_name)) + return paUnanticipatedHostError; + + task = MMCSS_activate(mmcs_name[nPriorityClass]); + if (task == NULL) + return paUnanticipatedHostError; + + (*hTask) = task; + return paNoError; +} + +// ------------------------------------------------------------------------------------------ +PaError PaWasapi_ThreadPriorityRevert(void *hTask) +{ + if (hTask == NULL) + return paUnanticipatedHostError; + + MMCSS_deactivate((HANDLE)hTask); + + return paNoError; +} + +// ------------------------------------------------------------------------------------------ +static HRESULT FillOutputBuffer(PaWasapiStream *stream, PaWasapiHostProcessor *processor, UINT32 frames) +{ + HRESULT hr; + BYTE *data = NULL; + + // Get buffer + if ((hr = IAudioRenderClient_GetBuffer(stream->rclient, frames, &data)) != S_OK) + { + if (stream->out.shareMode == AUDCLNT_SHAREMODE_SHARED) + { + // Using GetCurrentPadding to overcome AUDCLNT_E_BUFFER_TOO_LARGE in + // shared mode results in no sound in Event-driven mode (MSDN does not + // document this, or is it WASAPI bug?), thus we better + // try to acquire buffer next time when GetBuffer allows to do so. +#if 0 + // Get Read position + UINT32 padding = 0; + hr = stream->out.client->GetCurrentPadding(&padding); + if (hr != S_OK) + return LogHostError(hr); + + // Get frames to write + frames -= padding; + if (frames == 0) + return S_OK; + + if ((hr = stream->rclient->GetBuffer(frames, &data)) != S_OK) + return LogHostError(hr); +#else + if (hr == AUDCLNT_E_BUFFER_TOO_LARGE) + return S_OK; // be silent in shared mode, try again next time +#endif + } + else + return LogHostError(hr); + } + + // Process data + processor[S_OUTPUT].processor(NULL, 0, data, frames, processor[S_OUTPUT].userData); + + // Release buffer + if ((hr = IAudioRenderClient_ReleaseBuffer(stream->rclient, frames, 0)) != S_OK) + LogHostError(hr); + + return hr; +} + +// ------------------------------------------------------------------------------------------ +static HRESULT PollInputBuffer(PaWasapiStream *stream, PaWasapiHostProcessor *processor) +{ + HRESULT hr; + UINT32 frames; + BYTE *data = NULL; + DWORD flags = 0; + + for (;;) + { + // Get the available data in the shared buffer. + if ((hr = IAudioCaptureClient_GetBuffer(stream->cclient, &data, &frames, &flags, NULL, NULL)) != S_OK) + { + if (hr == AUDCLNT_S_BUFFER_EMPTY) + break; // capture buffer exhausted + + return LogHostError(hr); + break; + } + + // Detect silence + // if (flags & AUDCLNT_BUFFERFLAGS_SILENT) + // data = NULL; + + // Process data + processor[S_INPUT].processor(data, frames, NULL, 0, processor[S_INPUT].userData); + + // Release buffer + if ((hr = IAudioCaptureClient_ReleaseBuffer(stream->cclient, frames)) != S_OK) + return LogHostError(hr); + } + + return hr; +} + +// ------------------------------------------------------------------------------------------ +void _OnStreamStop(PaWasapiStream *stream) +{ + // Stop INPUT client + if (stream->in.client != NULL) + IAudioClient_Stop(stream->in.client); + // Stop OUTPUT client + if (stream->out.client != NULL) + IAudioClient_Stop(stream->out.client); + + // Restore thread priority + if (stream->hAvTask != NULL) + { + PaWasapi_ThreadPriorityRevert(stream->hAvTask); + stream->hAvTask = NULL; + } + + // Notify + if (stream->streamRepresentation.streamFinishedCallback != NULL) + stream->streamRepresentation.streamFinishedCallback(stream->streamRepresentation.userData); +} + +// ------------------------------------------------------------------------------------------ +PA_THREAD_FUNC ProcThreadEvent(void *param) +{ + HANDLE event[S_COUNT]; + PaWasapiHostProcessor processor[S_COUNT]; + HRESULT hr; + DWORD dwResult; + PaWasapiStream *stream = (PaWasapiStream *)param; + PaWasapiHostProcessor defaultProcessor; + + // Waiting on all events in case of Full-Duplex/Exclusive mode. + BOOL bWaitAllEvents = FALSE; + if ((stream->in.client != NULL) && (stream->out.client != NULL)) + { + bWaitAllEvents = (stream->in.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE) && + (stream->out.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE); + } + + // Setup data processors + defaultProcessor.processor = WaspiHostProcessingLoop; + defaultProcessor.userData = stream; + processor[S_INPUT] = (stream->hostProcessOverrideInput.processor != NULL ? stream->hostProcessOverrideInput : defaultProcessor); + processor[S_OUTPUT] = (stream->hostProcessOverrideOutput.processor != NULL ? stream->hostProcessOverrideOutput : defaultProcessor); + + // Initialize event to NULL first + event[S_INPUT] = CreateEvent(NULL, FALSE, FALSE, NULL); + event[S_OUTPUT] = CreateEvent(NULL, FALSE, FALSE, NULL); + + // Check on low resources and disallow any processing further + if (!event[S_INPUT] || !event[S_OUTPUT]) + { + PRINT(("WASAPI Thread: failed creating one or two event handles\n")); + goto thread_end; + } + + // Boost thread priority + PaWasapi_ThreadPriorityBoost((void **)&stream->hAvTask, stream->nThreadPriority); + + // Initialize event & start INPUT stream + if (stream->in.client) + { + if ((hr = IAudioClient_SetEventHandle(stream->in.client, event[S_INPUT])) != S_OK) + LogHostError(hr); + + if ((hr = IAudioClient_GetService(stream->in.client, &pa_IID_IAudioCaptureClient, (void **)&stream->cclient)) != S_OK) + LogHostError(hr); + + if ((hr = IAudioClient_Start(stream->in.client)) != S_OK) + LogHostError(hr); + } + + // Initialize event & start OUTPUT stream + if (stream->out.client) + { + if ((hr = IAudioClient_SetEventHandle(stream->out.client, event[S_OUTPUT])) != S_OK) + LogHostError(hr); + + if ((hr = IAudioClient_GetService(stream->out.client, &pa_IID_IAudioRenderClient, (void **)&stream->rclient)) != S_OK) + LogHostError(hr); + + // Preload buffer before start + if ((hr = FillOutputBuffer(stream, processor, stream->out.framesPerHostCallback)) != S_OK) + LogHostError(hr); + + // Start + if ((hr = IAudioClient_Start(stream->out.client)) != S_OK) + LogHostError(hr); + } + + // Signal: stream running + stream->running = TRUE; + + // Processing Loop + for (;;) + { + // 2 sec timeout + dwResult = WaitForMultipleObjects(S_COUNT, event, bWaitAllEvents, 10000); + + // Check for close event (after wait for buffers to avoid any calls to user + // callback when hCloseRequest was set) + if (WaitForSingleObject(stream->hCloseRequest, 0) != WAIT_TIMEOUT) + break; + + // Process S_INPUT/S_OUTPUT + switch (dwResult) + { + case WAIT_TIMEOUT: { + PRINT(("WASAPI Thread: WAIT_TIMEOUT - probably bad audio driver or Vista x64 bug: use paWinWasapiPolling instead\n")); + goto processing_stop; + break; } + + // Input stream + case WAIT_OBJECT_0 + S_INPUT: { + if (stream->cclient == NULL) + break; + PollInputBuffer(stream, processor); + break; } + + // Output stream + case WAIT_OBJECT_0 + S_OUTPUT: { + if (stream->rclient == NULL) + break; + FillOutputBuffer(stream, processor, stream->out.framesPerHostCallback); + break; } + } + } + +processing_stop: + + // Process stop + _OnStreamStop(stream); + +thread_end: + + // Close event + CloseHandle(event[S_INPUT]); + CloseHandle(event[S_OUTPUT]); + + // Notify thread is exiting + SetEvent(stream->hThreadDone); + + return 0; +} + +// ------------------------------------------------------------------------------------------ +PA_THREAD_FUNC ProcThreadPoll(void *param) +{ + PaWasapiHostProcessor processor[S_COUNT]; + HRESULT hr; + PaWasapiStream *stream = (PaWasapiStream *)param; + PaWasapiHostProcessor defaultProcessor; + INT32 i; + + // Calculate the actual duration of the allocated buffer. + DWORD sleep_ms = 0; + DWORD sleep_ms_in = GetFramesSleepTime(stream->in.framesPerHostCallback, stream->in.wavex.Format.nSamplesPerSec); + DWORD sleep_ms_out = GetFramesSleepTime(stream->out.framesPerHostCallback, stream->out.wavex.Format.nSamplesPerSec); + if ((sleep_ms_in != 0) && (sleep_ms_out != 0)) + sleep_ms = min(sleep_ms_in, sleep_ms_out); + else + { + sleep_ms = (sleep_ms_in ? sleep_ms_in : sleep_ms_out); + } + + // Setup data processors + defaultProcessor.processor = WaspiHostProcessingLoop; + defaultProcessor.userData = stream; + processor[S_INPUT] = (stream->hostProcessOverrideInput.processor != NULL ? stream->hostProcessOverrideInput : defaultProcessor); + processor[S_OUTPUT] = (stream->hostProcessOverrideOutput.processor != NULL ? stream->hostProcessOverrideOutput : defaultProcessor); + + // Boost thread priority + PaWasapi_ThreadPriorityBoost((void **)&stream->hAvTask, stream->nThreadPriority); + + // Initialize event & start INPUT stream + if (stream->in.client) + { + if ((hr = IAudioClient_GetService(stream->in.client, &pa_IID_IAudioCaptureClient, (void **)&stream->cclient)) != S_OK) + LogHostError(hr); + + if ((hr = IAudioClient_Start(stream->in.client)) != S_OK) + LogHostError(hr); + } + + + // Initialize event & start OUTPUT stream + if (stream->out.client) + { + if ((hr = IAudioClient_GetService(stream->out.client, &pa_IID_IAudioRenderClient, (void **)&stream->rclient)) != S_OK) + LogHostError(hr); + + // Preload buffer (obligatory, othervise ->Start() will fail) + if ((hr = FillOutputBuffer(stream, processor, stream->out.framesPerHostCallback)) != S_OK) + LogHostError(hr); + + // Start + if ((hr = IAudioClient_Start(stream->out.client)) != S_OK) + LogHostError(hr); + } + + // Signal: stream running + stream->running = TRUE; + + // Processing Loop + while (WaitForSingleObject(stream->hCloseRequest, sleep_ms) == WAIT_TIMEOUT) + { + for (i = 0; i < S_COUNT; ++i) + { + // Process S_INPUT/S_OUTPUT + switch (i) + { + // Input stream + case S_INPUT: { + if (stream->cclient == NULL) + break; + PollInputBuffer(stream, processor); + break; } + + // Output stream + case S_OUTPUT: { + + UINT32 frames, padding; + + if (stream->rclient == NULL) + break; + + frames = stream->out.framesPerHostCallback; + + // Get Read position + padding = 0; + hr = IAudioClient_GetCurrentPadding(stream->out.client, &padding); + if (hr != S_OK) + { + LogHostError(hr); + break; + } + + // Fill buffer + frames -= padding; + if (frames != 0) + FillOutputBuffer(stream, processor, frames); + + break; } + } + } + } + + // Process stop + _OnStreamStop(stream); + + // Notify thread is exiting + SetEvent(stream->hThreadDone); + + return 0; +} + +//#endif //VC 2005 + + + + +#if 0 + if(bFirst) { + float masteur; + hr = stream->outVol->GetMasterVolumeLevelScalar(&masteur); + if (hr != S_OK) + LogHostError(hr); + float chan1, chan2; + hr = stream->outVol->GetChannelVolumeLevelScalar(0, &chan1); + if (hr != S_OK) + LogHostError(hr); + hr = stream->outVol->GetChannelVolumeLevelScalar(1, &chan2); + if (hr != S_OK) + LogHostError(hr); + + BOOL bMute; + hr = stream->outVol->GetMute(&bMute); + if (hr != S_OK) + LogHostError(hr); + + stream->outVol->SetMasterVolumeLevelScalar(0.5, NULL); + stream->outVol->SetChannelVolumeLevelScalar(0, 0.5, NULL); + stream->outVol->SetChannelVolumeLevelScalar(1, 0.5, NULL); + stream->outVol->SetMute(FALSE, NULL); + bFirst = FALSE; + } +#endif diff --git a/3rdparty/portaudio/src/os/win/pa_win_waveformat.c b/3rdparty/portaudio/src/os/win/pa_win_waveformat.c index bbf616c344..beae5825e4 100644 --- a/3rdparty/portaudio/src/os/win/pa_win_waveformat.c +++ b/3rdparty/portaudio/src/os/win/pa_win_waveformat.c @@ -1,154 +1,154 @@ -/* - * PortAudio Portable Real-Time Audio Library - * Windows WAVEFORMAT* data structure utilities - * portaudio.h should be included before this file. - * - * Copyright (c) 2007 Ross Bencina - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/* - * The text above constitutes the entire PortAudio license; however, - * the PortAudio community also makes the following non-binding requests: - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. It is also - * requested that these non-binding requests be included along with the - * license above. - */ - -#include -#include - -#include "portaudio.h" -#include "pa_win_waveformat.h" - - -#if !defined(WAVE_FORMAT_EXTENSIBLE) -#define WAVE_FORMAT_EXTENSIBLE 0xFFFE -#endif - -static GUID pawin_ksDataFormatSubtypeGuidBase = - { (USHORT)(WAVE_FORMAT_PCM), 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }; - - -int PaWin_SampleFormatToLinearWaveFormatTag( PaSampleFormat sampleFormat ) -{ - if( sampleFormat == paFloat32 ) - return PAWIN_WAVE_FORMAT_IEEE_FLOAT; - - return PAWIN_WAVE_FORMAT_PCM; -} - - -void PaWin_InitializeWaveFormatEx( PaWinWaveFormat *waveFormat, - int numChannels, PaSampleFormat sampleFormat, int waveFormatTag, double sampleRate ) -{ - WAVEFORMATEX *waveFormatEx = (WAVEFORMATEX*)waveFormat; - int bytesPerSample = Pa_GetSampleSize(sampleFormat); - unsigned long bytesPerFrame = numChannels * bytesPerSample; - - waveFormatEx->wFormatTag = waveFormatTag; - waveFormatEx->nChannels = (WORD)numChannels; - waveFormatEx->nSamplesPerSec = (DWORD)sampleRate; - waveFormatEx->nAvgBytesPerSec = waveFormatEx->nSamplesPerSec * bytesPerFrame; - waveFormatEx->nBlockAlign = (WORD)bytesPerFrame; - waveFormatEx->wBitsPerSample = bytesPerSample * 8; - waveFormatEx->cbSize = 0; -} - - -void PaWin_InitializeWaveFormatExtensible( PaWinWaveFormat *waveFormat, - int numChannels, PaSampleFormat sampleFormat, int waveFormatTag, double sampleRate, - PaWinWaveFormatChannelMask channelMask ) -{ - WAVEFORMATEX *waveFormatEx = (WAVEFORMATEX*)waveFormat; - int bytesPerSample = Pa_GetSampleSize(sampleFormat); - unsigned long bytesPerFrame = numChannels * bytesPerSample; - GUID guid; - - waveFormatEx->wFormatTag = WAVE_FORMAT_EXTENSIBLE; - waveFormatEx->nChannels = (WORD)numChannels; - waveFormatEx->nSamplesPerSec = (DWORD)sampleRate; - waveFormatEx->nAvgBytesPerSec = waveFormatEx->nSamplesPerSec * bytesPerFrame; - waveFormatEx->nBlockAlign = (WORD)bytesPerFrame; - waveFormatEx->wBitsPerSample = bytesPerSample * 8; - waveFormatEx->cbSize = 22; - - *((WORD*)&waveFormat->fields[PAWIN_INDEXOF_WVALIDBITSPERSAMPLE]) = - waveFormatEx->wBitsPerSample; - - *((DWORD*)&waveFormat->fields[PAWIN_INDEXOF_DWCHANNELMASK]) = channelMask; - - guid = pawin_ksDataFormatSubtypeGuidBase; - guid.Data1 = (USHORT)waveFormatTag; - *((GUID*)&waveFormat->fields[PAWIN_INDEXOF_SUBFORMAT]) = guid; -} - - -PaWinWaveFormatChannelMask PaWin_DefaultChannelMask( int numChannels ) -{ - switch( numChannels ){ - case 1: - return PAWIN_SPEAKER_MONO; - case 2: - return PAWIN_SPEAKER_STEREO; - case 3: - return PAWIN_SPEAKER_FRONT_LEFT | PAWIN_SPEAKER_FRONT_CENTER | PAWIN_SPEAKER_FRONT_RIGHT; - case 4: - return PAWIN_SPEAKER_QUAD; - case 5: - return PAWIN_SPEAKER_QUAD | PAWIN_SPEAKER_FRONT_CENTER; - case 6: - /* The meaning of the PAWIN_SPEAKER_5POINT1 flag has changed over time: - http://msdn2.microsoft.com/en-us/library/aa474707.aspx - We use PAWIN_SPEAKER_5POINT1 (not PAWIN_SPEAKER_5POINT1_SURROUND) - because on some cards (eg Audigy) PAWIN_SPEAKER_5POINT1_SURROUND - results in a virtual mixdown placing the rear output in the - front _and_ rear speakers. - */ - return PAWIN_SPEAKER_5POINT1; - /* case 7: */ - case 8: - return PAWIN_SPEAKER_7POINT1; - } - - /* Apparently some Audigy drivers will output silence - if the direct-out constant (0) is used. So this is not ideal. - */ - return PAWIN_SPEAKER_DIRECTOUT; - - /* Note that Alec Rogers proposed the following as an alternate method to - generate the default channel mask, however it doesn't seem to be an improvement - over the above, since some drivers will matrix outputs mapping to non-present - speakers accross multiple physical speakers. - - if(nChannels==1) { - pwfFormat->dwChannelMask = SPEAKER_FRONT_CENTER; - } - else { - pwfFormat->dwChannelMask = 0; - for(i=0; idwChannelMask = (pwfFormat->dwChannelMask << 1) | 0x1; - } - */ -} +/* + * PortAudio Portable Real-Time Audio Library + * Windows WAVEFORMAT* data structure utilities + * portaudio.h should be included before this file. + * + * Copyright (c) 2007 Ross Bencina + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* + * The text above constitutes the entire PortAudio license; however, + * the PortAudio community also makes the following non-binding requests: + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. It is also + * requested that these non-binding requests be included along with the + * license above. + */ + +#include +#include + +#include "portaudio.h" +#include "pa_win_waveformat.h" + + +#if !defined(WAVE_FORMAT_EXTENSIBLE) +#define WAVE_FORMAT_EXTENSIBLE 0xFFFE +#endif + +static GUID pawin_ksDataFormatSubtypeGuidBase = + { (USHORT)(WAVE_FORMAT_PCM), 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }; + + +int PaWin_SampleFormatToLinearWaveFormatTag( PaSampleFormat sampleFormat ) +{ + if( sampleFormat == paFloat32 ) + return PAWIN_WAVE_FORMAT_IEEE_FLOAT; + + return PAWIN_WAVE_FORMAT_PCM; +} + + +void PaWin_InitializeWaveFormatEx( PaWinWaveFormat *waveFormat, + int numChannels, PaSampleFormat sampleFormat, int waveFormatTag, double sampleRate ) +{ + WAVEFORMATEX *waveFormatEx = (WAVEFORMATEX*)waveFormat; + int bytesPerSample = Pa_GetSampleSize(sampleFormat); + unsigned long bytesPerFrame = numChannels * bytesPerSample; + + waveFormatEx->wFormatTag = waveFormatTag; + waveFormatEx->nChannels = (WORD)numChannels; + waveFormatEx->nSamplesPerSec = (DWORD)sampleRate; + waveFormatEx->nAvgBytesPerSec = waveFormatEx->nSamplesPerSec * bytesPerFrame; + waveFormatEx->nBlockAlign = (WORD)bytesPerFrame; + waveFormatEx->wBitsPerSample = bytesPerSample * 8; + waveFormatEx->cbSize = 0; +} + + +void PaWin_InitializeWaveFormatExtensible( PaWinWaveFormat *waveFormat, + int numChannels, PaSampleFormat sampleFormat, int waveFormatTag, double sampleRate, + PaWinWaveFormatChannelMask channelMask ) +{ + WAVEFORMATEX *waveFormatEx = (WAVEFORMATEX*)waveFormat; + int bytesPerSample = Pa_GetSampleSize(sampleFormat); + unsigned long bytesPerFrame = numChannels * bytesPerSample; + GUID guid; + + waveFormatEx->wFormatTag = WAVE_FORMAT_EXTENSIBLE; + waveFormatEx->nChannels = (WORD)numChannels; + waveFormatEx->nSamplesPerSec = (DWORD)sampleRate; + waveFormatEx->nAvgBytesPerSec = waveFormatEx->nSamplesPerSec * bytesPerFrame; + waveFormatEx->nBlockAlign = (WORD)bytesPerFrame; + waveFormatEx->wBitsPerSample = bytesPerSample * 8; + waveFormatEx->cbSize = 22; + + *((WORD*)&waveFormat->fields[PAWIN_INDEXOF_WVALIDBITSPERSAMPLE]) = + waveFormatEx->wBitsPerSample; + + *((DWORD*)&waveFormat->fields[PAWIN_INDEXOF_DWCHANNELMASK]) = channelMask; + + guid = pawin_ksDataFormatSubtypeGuidBase; + guid.Data1 = (USHORT)waveFormatTag; + *((GUID*)&waveFormat->fields[PAWIN_INDEXOF_SUBFORMAT]) = guid; +} + + +PaWinWaveFormatChannelMask PaWin_DefaultChannelMask( int numChannels ) +{ + switch( numChannels ){ + case 1: + return PAWIN_SPEAKER_MONO; + case 2: + return PAWIN_SPEAKER_STEREO; + case 3: + return PAWIN_SPEAKER_FRONT_LEFT | PAWIN_SPEAKER_FRONT_CENTER | PAWIN_SPEAKER_FRONT_RIGHT; + case 4: + return PAWIN_SPEAKER_QUAD; + case 5: + return PAWIN_SPEAKER_QUAD | PAWIN_SPEAKER_FRONT_CENTER; + case 6: + /* The meaning of the PAWIN_SPEAKER_5POINT1 flag has changed over time: + http://msdn2.microsoft.com/en-us/library/aa474707.aspx + We use PAWIN_SPEAKER_5POINT1 (not PAWIN_SPEAKER_5POINT1_SURROUND) + because on some cards (eg Audigy) PAWIN_SPEAKER_5POINT1_SURROUND + results in a virtual mixdown placing the rear output in the + front _and_ rear speakers. + */ + return PAWIN_SPEAKER_5POINT1; + /* case 7: */ + case 8: + return PAWIN_SPEAKER_7POINT1; + } + + /* Apparently some Audigy drivers will output silence + if the direct-out constant (0) is used. So this is not ideal. + */ + return PAWIN_SPEAKER_DIRECTOUT; + + /* Note that Alec Rogers proposed the following as an alternate method to + generate the default channel mask, however it doesn't seem to be an improvement + over the above, since some drivers will matrix outputs mapping to non-present + speakers accross multiple physical speakers. + + if(nChannels==1) { + pwfFormat->dwChannelMask = SPEAKER_FRONT_CENTER; + } + else { + pwfFormat->dwChannelMask = 0; + for(i=0; idwChannelMask = (pwfFormat->dwChannelMask << 1) | 0x1; + } + */ +} diff --git a/3rdparty/portaudio/src/os/win/pa_win_wdmks_utils.c b/3rdparty/portaudio/src/os/win/pa_win_wdmks_utils.c index 37393e927d..824ebdac40 100644 --- a/3rdparty/portaudio/src/os/win/pa_win_wdmks_utils.c +++ b/3rdparty/portaudio/src/os/win/pa_win_wdmks_utils.c @@ -1,293 +1,293 @@ -/* - * PortAudio Portable Real-Time Audio Library - * Windows WDM KS utilities - * - * Copyright (c) 1999 - 2007 Andrew Baldwin, Ross Bencina - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/* - * The text above constitutes the entire PortAudio license; however, - * the PortAudio community also makes the following non-binding requests: - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. It is also - * requested that these non-binding requests be included along with the - * license above. - */ - -#include -#include -#include -#include -#include // just for some development printfs - -#include "portaudio.h" -#include "pa_util.h" -#include "pa_win_wdmks_utils.h" - - -#ifndef PA_WDMKS_NO_KSGUID_LIB - -#if (defined(WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1200))) /* MSC version 6 and above */ -#pragma comment( lib, "ksguid.lib" ) -#endif - -#define pa_KSDATAFORMAT_TYPE_AUDIO KSDATAFORMAT_TYPE_AUDIO -#define pa_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT KSDATAFORMAT_SUBTYPE_IEEE_FLOAT -#define pa_KSDATAFORMAT_SUBTYPE_PCM KSDATAFORMAT_SUBTYPE_PCM -#define pa_KSDATAFORMAT_SUBTYPE_WAVEFORMATEX KSDATAFORMAT_SUBTYPE_WAVEFORMATEX -#define pa_KSMEDIUMSETID_Standard KSMEDIUMSETID_Standard -#define pa_KSINTERFACESETID_Standard KSINTERFACESETID_Standard -#define pa_KSPROPSETID_Pin KSPROPSETID_Pin - -#else - -static const GUID pa_KSDATAFORMAT_TYPE_AUDIO = { STATIC_KSDATAFORMAT_TYPE_AUDIO }; -static const GUID pa_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = { STATIC_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT }; -static const GUID pa_KSDATAFORMAT_SUBTYPE_PCM = { STATIC_KSDATAFORMAT_SUBTYPE_PCM }; -static const GUID pa_KSDATAFORMAT_SUBTYPE_WAVEFORMATEX = { STATIC_KSDATAFORMAT_SUBTYPE_WAVEFORMATEX }; -static const GUID pa_KSMEDIUMSETID_Standard = { STATIC_KSMEDIUMSETID_Standard }; -static const GUID pa_KSINTERFACESETID_Standard = { STATIC_KSINTERFACESETID_Standard }; -static const GUID pa_KSPROPSETID_Pin = { STATIC_KSPROPSETID_Pin }; - -#endif - - -#define pa_IS_VALID_WAVEFORMATEX_GUID(Guid)\ - (!memcmp(((PUSHORT)&pa_KSDATAFORMAT_SUBTYPE_WAVEFORMATEX) + 1, ((PUSHORT)(Guid)) + 1, sizeof(GUID) - sizeof(USHORT))) - - - -static PaError WdmGetPinPropertySimple( - HANDLE handle, - unsigned long pinId, - unsigned long property, - void* value, - unsigned long valueSize ) -{ - DWORD bytesReturned; - KSP_PIN ksPProp; - ksPProp.Property.Set = pa_KSPROPSETID_Pin; - ksPProp.Property.Id = property; - ksPProp.Property.Flags = KSPROPERTY_TYPE_GET; - ksPProp.PinId = pinId; - ksPProp.Reserved = 0; - - if( DeviceIoControl( handle, IOCTL_KS_PROPERTY, &ksPProp, sizeof(KSP_PIN), - value, valueSize, &bytesReturned, NULL ) == 0 || bytesReturned != valueSize ) - { - return paUnanticipatedHostError; - } - else - { - return paNoError; - } -} - - -static PaError WdmGetPinPropertyMulti( - HANDLE handle, - unsigned long pinId, - unsigned long property, - KSMULTIPLE_ITEM** ksMultipleItem) -{ - unsigned long multipleItemSize = 0; - KSP_PIN ksPProp; - DWORD bytesReturned; - - *ksMultipleItem = 0; - - ksPProp.Property.Set = pa_KSPROPSETID_Pin; - ksPProp.Property.Id = property; - ksPProp.Property.Flags = KSPROPERTY_TYPE_GET; - ksPProp.PinId = pinId; - ksPProp.Reserved = 0; - - if( DeviceIoControl( handle, IOCTL_KS_PROPERTY, &ksPProp.Property, - sizeof(KSP_PIN), NULL, 0, &multipleItemSize, NULL ) == 0 && GetLastError() != ERROR_MORE_DATA ) - { - return paUnanticipatedHostError; - } - - *ksMultipleItem = (KSMULTIPLE_ITEM*)PaUtil_AllocateMemory( multipleItemSize ); - if( !*ksMultipleItem ) - { - return paInsufficientMemory; - } - - if( DeviceIoControl( handle, IOCTL_KS_PROPERTY, &ksPProp, sizeof(KSP_PIN), - (void*)*ksMultipleItem, multipleItemSize, &bytesReturned, NULL ) == 0 || bytesReturned != multipleItemSize ) - { - PaUtil_FreeMemory( ksMultipleItem ); - return paUnanticipatedHostError; - } - - return paNoError; -} - - -static int GetKSFilterPinCount( HANDLE deviceHandle ) -{ - DWORD result; - - if( WdmGetPinPropertySimple( deviceHandle, 0, KSPROPERTY_PIN_CTYPES, &result, sizeof(result) ) == paNoError ){ - return result; - }else{ - return 0; - } -} - - -static KSPIN_COMMUNICATION GetKSFilterPinPropertyCommunication( HANDLE deviceHandle, int pinId ) -{ - KSPIN_COMMUNICATION result; - - if( WdmGetPinPropertySimple( deviceHandle, pinId, KSPROPERTY_PIN_COMMUNICATION, &result, sizeof(result) ) == paNoError ){ - return result; - }else{ - return KSPIN_COMMUNICATION_NONE; - } -} - - -static KSPIN_DATAFLOW GetKSFilterPinPropertyDataflow( HANDLE deviceHandle, int pinId ) -{ - KSPIN_DATAFLOW result; - - if( WdmGetPinPropertySimple( deviceHandle, pinId, KSPROPERTY_PIN_DATAFLOW, &result, sizeof(result) ) == paNoError ){ - return result; - }else{ - return (KSPIN_DATAFLOW)0; - } -} - - -static int KSFilterPinPropertyIdentifiersInclude( - HANDLE deviceHandle, int pinId, unsigned long property, const GUID *identifierSet, unsigned long identifierId ) -{ - KSMULTIPLE_ITEM* item = NULL; - KSIDENTIFIER* identifier; - int i; - int result = 0; - - if( WdmGetPinPropertyMulti( deviceHandle, pinId, property, &item) != paNoError ) - return 0; - - identifier = (KSIDENTIFIER*)(item+1); - - for( i = 0; i < (int)item->Count; i++ ) - { - if( !memcmp( (void*)&identifier[i].Set, (void*)identifierSet, sizeof( GUID ) ) && - ( identifier[i].Id == identifierId ) ) - { - result = 1; - break; - } - } - - PaUtil_FreeMemory( item ); - - return result; -} - - -/* return the maximum channel count supported by any pin on the device. - if isInput is non-zero we query input pins, otherwise output pins. -*/ -int PaWin_WDMKS_QueryFilterMaximumChannelCount( void *wcharDevicePath, int isInput ) -{ - HANDLE deviceHandle; - ULONG i; - int pinCount, pinId; - int result = 0; - KSPIN_DATAFLOW requiredDataflowDirection = (isInput ? KSPIN_DATAFLOW_OUT : KSPIN_DATAFLOW_IN ); - - if( !wcharDevicePath ) - return 0; - - deviceHandle = CreateFileW( (LPCWSTR)wcharDevicePath, FILE_SHARE_READ|FILE_SHARE_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL ); - if( deviceHandle == INVALID_HANDLE_VALUE ) - return 0; - - pinCount = GetKSFilterPinCount( deviceHandle ); - for( pinId = 0; pinId < pinCount; ++pinId ) - { - KSPIN_COMMUNICATION communication = GetKSFilterPinPropertyCommunication( deviceHandle, pinId ); - KSPIN_DATAFLOW dataflow = GetKSFilterPinPropertyDataflow( deviceHandle, pinId ); - if( ( dataflow == requiredDataflowDirection ) && - (( communication == KSPIN_COMMUNICATION_SINK) || - ( communication == KSPIN_COMMUNICATION_BOTH)) - && ( KSFilterPinPropertyIdentifiersInclude( deviceHandle, pinId, - KSPROPERTY_PIN_INTERFACES, &pa_KSINTERFACESETID_Standard, KSINTERFACE_STANDARD_STREAMING ) - || KSFilterPinPropertyIdentifiersInclude( deviceHandle, pinId, - KSPROPERTY_PIN_INTERFACES, &pa_KSINTERFACESETID_Standard, KSINTERFACE_STANDARD_LOOPED_STREAMING ) ) - && KSFilterPinPropertyIdentifiersInclude( deviceHandle, pinId, - KSPROPERTY_PIN_MEDIUMS, &pa_KSMEDIUMSETID_Standard, KSMEDIUM_STANDARD_DEVIO ) ) - { - KSMULTIPLE_ITEM* item = NULL; - if( WdmGetPinPropertyMulti( deviceHandle, pinId, KSPROPERTY_PIN_DATARANGES, &item ) == paNoError ) - { - KSDATARANGE *dataRange = (KSDATARANGE*)(item+1); - - for( i=0; i < item->Count; ++i ){ - - if( pa_IS_VALID_WAVEFORMATEX_GUID(&dataRange->SubFormat) - || memcmp( (void*)&dataRange->SubFormat, (void*)&pa_KSDATAFORMAT_SUBTYPE_PCM, sizeof(GUID) ) == 0 - || memcmp( (void*)&dataRange->SubFormat, (void*)&pa_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, sizeof(GUID) ) == 0 - || ( ( memcmp( (void*)&dataRange->MajorFormat, (void*)&pa_KSDATAFORMAT_TYPE_AUDIO, sizeof(GUID) ) == 0 ) - && ( memcmp( (void*)&dataRange->SubFormat, (void*)&KSDATAFORMAT_SUBTYPE_WILDCARD, sizeof(GUID) ) == 0 ) ) ) - { - KSDATARANGE_AUDIO *dataRangeAudio = (KSDATARANGE_AUDIO*)dataRange; - - /* - printf( ">>> %d %d %d %d %S\n", isInput, dataflow, communication, dataRangeAudio->MaximumChannels, devicePath ); - - if( memcmp((void*)&dataRange->Specifier, (void*)&KSDATAFORMAT_SPECIFIER_WAVEFORMATEX, sizeof(GUID) ) == 0 ) - printf( "\tspecifier: KSDATAFORMAT_SPECIFIER_WAVEFORMATEX\n" ); - else if( memcmp((void*)&dataRange->Specifier, (void*)&KSDATAFORMAT_SPECIFIER_DSOUND, sizeof(GUID) ) == 0 ) - printf( "\tspecifier: KSDATAFORMAT_SPECIFIER_DSOUND\n" ); - else if( memcmp((void*)&dataRange->Specifier, (void*)&KSDATAFORMAT_SPECIFIER_WILDCARD, sizeof(GUID) ) == 0 ) - printf( "\tspecifier: KSDATAFORMAT_SPECIFIER_WILDCARD\n" ); - else - printf( "\tspecifier: ?\n" ); - */ - - /* - We assume that very high values for MaximumChannels are not useful and indicate - that the driver isn't prepared to tell us the real number of channels which it supports. - */ - if( dataRangeAudio->MaximumChannels < 0xFFFFUL && (int)dataRangeAudio->MaximumChannels > result ) - result = (int)dataRangeAudio->MaximumChannels; - } - - dataRange = (KSDATARANGE*)( ((char*)dataRange) + dataRange->FormatSize); - } - - PaUtil_FreeMemory( item ); - } - } - } - - CloseHandle( deviceHandle ); - return result; -} +/* + * PortAudio Portable Real-Time Audio Library + * Windows WDM KS utilities + * + * Copyright (c) 1999 - 2007 Andrew Baldwin, Ross Bencina + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* + * The text above constitutes the entire PortAudio license; however, + * the PortAudio community also makes the following non-binding requests: + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. It is also + * requested that these non-binding requests be included along with the + * license above. + */ + +#include +#include +#include +#include +#include // just for some development printfs + +#include "portaudio.h" +#include "pa_util.h" +#include "pa_win_wdmks_utils.h" + + +#ifndef PA_WDMKS_NO_KSGUID_LIB + +#if (defined(WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1200))) /* MSC version 6 and above */ +#pragma comment( lib, "ksguid.lib" ) +#endif + +#define pa_KSDATAFORMAT_TYPE_AUDIO KSDATAFORMAT_TYPE_AUDIO +#define pa_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT KSDATAFORMAT_SUBTYPE_IEEE_FLOAT +#define pa_KSDATAFORMAT_SUBTYPE_PCM KSDATAFORMAT_SUBTYPE_PCM +#define pa_KSDATAFORMAT_SUBTYPE_WAVEFORMATEX KSDATAFORMAT_SUBTYPE_WAVEFORMATEX +#define pa_KSMEDIUMSETID_Standard KSMEDIUMSETID_Standard +#define pa_KSINTERFACESETID_Standard KSINTERFACESETID_Standard +#define pa_KSPROPSETID_Pin KSPROPSETID_Pin + +#else + +static const GUID pa_KSDATAFORMAT_TYPE_AUDIO = { STATIC_KSDATAFORMAT_TYPE_AUDIO }; +static const GUID pa_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = { STATIC_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT }; +static const GUID pa_KSDATAFORMAT_SUBTYPE_PCM = { STATIC_KSDATAFORMAT_SUBTYPE_PCM }; +static const GUID pa_KSDATAFORMAT_SUBTYPE_WAVEFORMATEX = { STATIC_KSDATAFORMAT_SUBTYPE_WAVEFORMATEX }; +static const GUID pa_KSMEDIUMSETID_Standard = { STATIC_KSMEDIUMSETID_Standard }; +static const GUID pa_KSINTERFACESETID_Standard = { STATIC_KSINTERFACESETID_Standard }; +static const GUID pa_KSPROPSETID_Pin = { STATIC_KSPROPSETID_Pin }; + +#endif + + +#define pa_IS_VALID_WAVEFORMATEX_GUID(Guid)\ + (!memcmp(((PUSHORT)&pa_KSDATAFORMAT_SUBTYPE_WAVEFORMATEX) + 1, ((PUSHORT)(Guid)) + 1, sizeof(GUID) - sizeof(USHORT))) + + + +static PaError WdmGetPinPropertySimple( + HANDLE handle, + unsigned long pinId, + unsigned long property, + void* value, + unsigned long valueSize ) +{ + DWORD bytesReturned; + KSP_PIN ksPProp; + ksPProp.Property.Set = pa_KSPROPSETID_Pin; + ksPProp.Property.Id = property; + ksPProp.Property.Flags = KSPROPERTY_TYPE_GET; + ksPProp.PinId = pinId; + ksPProp.Reserved = 0; + + if( DeviceIoControl( handle, IOCTL_KS_PROPERTY, &ksPProp, sizeof(KSP_PIN), + value, valueSize, &bytesReturned, NULL ) == 0 || bytesReturned != valueSize ) + { + return paUnanticipatedHostError; + } + else + { + return paNoError; + } +} + + +static PaError WdmGetPinPropertyMulti( + HANDLE handle, + unsigned long pinId, + unsigned long property, + KSMULTIPLE_ITEM** ksMultipleItem) +{ + unsigned long multipleItemSize = 0; + KSP_PIN ksPProp; + DWORD bytesReturned; + + *ksMultipleItem = 0; + + ksPProp.Property.Set = pa_KSPROPSETID_Pin; + ksPProp.Property.Id = property; + ksPProp.Property.Flags = KSPROPERTY_TYPE_GET; + ksPProp.PinId = pinId; + ksPProp.Reserved = 0; + + if( DeviceIoControl( handle, IOCTL_KS_PROPERTY, &ksPProp.Property, + sizeof(KSP_PIN), NULL, 0, &multipleItemSize, NULL ) == 0 && GetLastError() != ERROR_MORE_DATA ) + { + return paUnanticipatedHostError; + } + + *ksMultipleItem = (KSMULTIPLE_ITEM*)PaUtil_AllocateMemory( multipleItemSize ); + if( !*ksMultipleItem ) + { + return paInsufficientMemory; + } + + if( DeviceIoControl( handle, IOCTL_KS_PROPERTY, &ksPProp, sizeof(KSP_PIN), + (void*)*ksMultipleItem, multipleItemSize, &bytesReturned, NULL ) == 0 || bytesReturned != multipleItemSize ) + { + PaUtil_FreeMemory( ksMultipleItem ); + return paUnanticipatedHostError; + } + + return paNoError; +} + + +static int GetKSFilterPinCount( HANDLE deviceHandle ) +{ + DWORD result; + + if( WdmGetPinPropertySimple( deviceHandle, 0, KSPROPERTY_PIN_CTYPES, &result, sizeof(result) ) == paNoError ){ + return result; + }else{ + return 0; + } +} + + +static KSPIN_COMMUNICATION GetKSFilterPinPropertyCommunication( HANDLE deviceHandle, int pinId ) +{ + KSPIN_COMMUNICATION result; + + if( WdmGetPinPropertySimple( deviceHandle, pinId, KSPROPERTY_PIN_COMMUNICATION, &result, sizeof(result) ) == paNoError ){ + return result; + }else{ + return KSPIN_COMMUNICATION_NONE; + } +} + + +static KSPIN_DATAFLOW GetKSFilterPinPropertyDataflow( HANDLE deviceHandle, int pinId ) +{ + KSPIN_DATAFLOW result; + + if( WdmGetPinPropertySimple( deviceHandle, pinId, KSPROPERTY_PIN_DATAFLOW, &result, sizeof(result) ) == paNoError ){ + return result; + }else{ + return (KSPIN_DATAFLOW)0; + } +} + + +static int KSFilterPinPropertyIdentifiersInclude( + HANDLE deviceHandle, int pinId, unsigned long property, const GUID *identifierSet, unsigned long identifierId ) +{ + KSMULTIPLE_ITEM* item = NULL; + KSIDENTIFIER* identifier; + int i; + int result = 0; + + if( WdmGetPinPropertyMulti( deviceHandle, pinId, property, &item) != paNoError ) + return 0; + + identifier = (KSIDENTIFIER*)(item+1); + + for( i = 0; i < (int)item->Count; i++ ) + { + if( !memcmp( (void*)&identifier[i].Set, (void*)identifierSet, sizeof( GUID ) ) && + ( identifier[i].Id == identifierId ) ) + { + result = 1; + break; + } + } + + PaUtil_FreeMemory( item ); + + return result; +} + + +/* return the maximum channel count supported by any pin on the device. + if isInput is non-zero we query input pins, otherwise output pins. +*/ +int PaWin_WDMKS_QueryFilterMaximumChannelCount( void *wcharDevicePath, int isInput ) +{ + HANDLE deviceHandle; + ULONG i; + int pinCount, pinId; + int result = 0; + KSPIN_DATAFLOW requiredDataflowDirection = (isInput ? KSPIN_DATAFLOW_OUT : KSPIN_DATAFLOW_IN ); + + if( !wcharDevicePath ) + return 0; + + deviceHandle = CreateFileW( (LPCWSTR)wcharDevicePath, FILE_SHARE_READ|FILE_SHARE_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL ); + if( deviceHandle == INVALID_HANDLE_VALUE ) + return 0; + + pinCount = GetKSFilterPinCount( deviceHandle ); + for( pinId = 0; pinId < pinCount; ++pinId ) + { + KSPIN_COMMUNICATION communication = GetKSFilterPinPropertyCommunication( deviceHandle, pinId ); + KSPIN_DATAFLOW dataflow = GetKSFilterPinPropertyDataflow( deviceHandle, pinId ); + if( ( dataflow == requiredDataflowDirection ) && + (( communication == KSPIN_COMMUNICATION_SINK) || + ( communication == KSPIN_COMMUNICATION_BOTH)) + && ( KSFilterPinPropertyIdentifiersInclude( deviceHandle, pinId, + KSPROPERTY_PIN_INTERFACES, &pa_KSINTERFACESETID_Standard, KSINTERFACE_STANDARD_STREAMING ) + || KSFilterPinPropertyIdentifiersInclude( deviceHandle, pinId, + KSPROPERTY_PIN_INTERFACES, &pa_KSINTERFACESETID_Standard, KSINTERFACE_STANDARD_LOOPED_STREAMING ) ) + && KSFilterPinPropertyIdentifiersInclude( deviceHandle, pinId, + KSPROPERTY_PIN_MEDIUMS, &pa_KSMEDIUMSETID_Standard, KSMEDIUM_STANDARD_DEVIO ) ) + { + KSMULTIPLE_ITEM* item = NULL; + if( WdmGetPinPropertyMulti( deviceHandle, pinId, KSPROPERTY_PIN_DATARANGES, &item ) == paNoError ) + { + KSDATARANGE *dataRange = (KSDATARANGE*)(item+1); + + for( i=0; i < item->Count; ++i ){ + + if( pa_IS_VALID_WAVEFORMATEX_GUID(&dataRange->SubFormat) + || memcmp( (void*)&dataRange->SubFormat, (void*)&pa_KSDATAFORMAT_SUBTYPE_PCM, sizeof(GUID) ) == 0 + || memcmp( (void*)&dataRange->SubFormat, (void*)&pa_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, sizeof(GUID) ) == 0 + || ( ( memcmp( (void*)&dataRange->MajorFormat, (void*)&pa_KSDATAFORMAT_TYPE_AUDIO, sizeof(GUID) ) == 0 ) + && ( memcmp( (void*)&dataRange->SubFormat, (void*)&KSDATAFORMAT_SUBTYPE_WILDCARD, sizeof(GUID) ) == 0 ) ) ) + { + KSDATARANGE_AUDIO *dataRangeAudio = (KSDATARANGE_AUDIO*)dataRange; + + /* + printf( ">>> %d %d %d %d %S\n", isInput, dataflow, communication, dataRangeAudio->MaximumChannels, devicePath ); + + if( memcmp((void*)&dataRange->Specifier, (void*)&KSDATAFORMAT_SPECIFIER_WAVEFORMATEX, sizeof(GUID) ) == 0 ) + printf( "\tspecifier: KSDATAFORMAT_SPECIFIER_WAVEFORMATEX\n" ); + else if( memcmp((void*)&dataRange->Specifier, (void*)&KSDATAFORMAT_SPECIFIER_DSOUND, sizeof(GUID) ) == 0 ) + printf( "\tspecifier: KSDATAFORMAT_SPECIFIER_DSOUND\n" ); + else if( memcmp((void*)&dataRange->Specifier, (void*)&KSDATAFORMAT_SPECIFIER_WILDCARD, sizeof(GUID) ) == 0 ) + printf( "\tspecifier: KSDATAFORMAT_SPECIFIER_WILDCARD\n" ); + else + printf( "\tspecifier: ?\n" ); + */ + + /* + We assume that very high values for MaximumChannels are not useful and indicate + that the driver isn't prepared to tell us the real number of channels which it supports. + */ + if( dataRangeAudio->MaximumChannels < 0xFFFFUL && (int)dataRangeAudio->MaximumChannels > result ) + result = (int)dataRangeAudio->MaximumChannels; + } + + dataRange = (KSDATARANGE*)( ((char*)dataRange) + dataRange->FormatSize); + } + + PaUtil_FreeMemory( item ); + } + } + } + + CloseHandle( deviceHandle ); + return result; +} diff --git a/3rdparty/portaudio/src/os/win/pa_win_wdmks_utils.h b/3rdparty/portaudio/src/os/win/pa_win_wdmks_utils.h index f54035f451..b8e923a615 100644 --- a/3rdparty/portaudio/src/os/win/pa_win_wdmks_utils.h +++ b/3rdparty/portaudio/src/os/win/pa_win_wdmks_utils.h @@ -1,65 +1,65 @@ -#ifndef PA_WIN_WDMKS_UTILS_H -#define PA_WIN_WDMKS_UTILS_H - -/* - * PortAudio Portable Real-Time Audio Library - * Windows WDM KS utilities - * - * Copyright (c) 1999 - 2007 Ross Bencina, Andrew Baldwin - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/* - * The text above constitutes the entire PortAudio license; however, - * the PortAudio community also makes the following non-binding requests: - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. It is also - * requested that these non-binding requests be included along with the - * license above. - */ - -/** @file - @brief Utilities for working with the Windows WDM KS API -*/ - -#ifdef __cplusplus -extern "C" { -#endif - -/** - Query for the maximum number of channels supported by any pin of the - specified device. Returns 0 if the query fails for any reason. - - @param wcharDevicePath A system level PnP interface path, supplied as a WCHAR unicode string. - Declard as void* to avoid introducing a dependency on wchar_t here. - - @param isInput A flag specifying whether to query for input (non-zero) or output (zero) channels. -*/ -int PaWin_WDMKS_QueryFilterMaximumChannelCount( void *wcharDevicePath, int isInput ); - - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - +#ifndef PA_WIN_WDMKS_UTILS_H +#define PA_WIN_WDMKS_UTILS_H + +/* + * PortAudio Portable Real-Time Audio Library + * Windows WDM KS utilities + * + * Copyright (c) 1999 - 2007 Ross Bencina, Andrew Baldwin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* + * The text above constitutes the entire PortAudio license; however, + * the PortAudio community also makes the following non-binding requests: + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. It is also + * requested that these non-binding requests be included along with the + * license above. + */ + +/** @file + @brief Utilities for working with the Windows WDM KS API +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Query for the maximum number of channels supported by any pin of the + specified device. Returns 0 if the query fails for any reason. + + @param wcharDevicePath A system level PnP interface path, supplied as a WCHAR unicode string. + Declard as void* to avoid introducing a dependency on wchar_t here. + + @param isInput A flag specifying whether to query for input (non-zero) or output (zero) channels. +*/ +int PaWin_WDMKS_QueryFilterMaximumChannelCount( void *wcharDevicePath, int isInput ); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + #endif /* PA_WIN_WDMKS_UTILS_H */ \ No newline at end of file diff --git a/3rdparty/wxWidgets/src/msw/HeapAllocator.cpp b/3rdparty/wxWidgets/src/msw/HeapAllocator.cpp index 7f1ca657dd..3b746ea5da 100644 --- a/3rdparty/wxWidgets/src/msw/HeapAllocator.cpp +++ b/3rdparty/wxWidgets/src/msw/HeapAllocator.cpp @@ -1,76 +1,76 @@ -// For compilers that support precompilation, includes "wx.h". -#include "wx/wxprec.h" - -#ifdef __BORLANDC__ - #pragma hdrstop -#endif - -#ifndef WX_PRECOMP - #include "wx/string.h" - #include "wx/intl.h" - #include "wx/thread.h" -#endif - -#include - -#ifndef __WXWINCE__ - #include -#endif - -#include -#include - -static HANDLE win32_string_heap = INVALID_HANDLE_VALUE; -static int win32_string_heap_refcount; - -static HANDLE win32_object_heap = INVALID_HANDLE_VALUE; -static int win32_object_heap_refcount; - -void _allocateHeap_wxString() -{ - if( win32_string_heap == INVALID_HANDLE_VALUE ) - { - //wxASSERT( win32_string_heap_refcount == 0 ); - win32_string_heap = HeapCreate( 0, 0x200000, 0 ); - } -} - -void _destroyHeap_wxString() -{ - if( win32_string_heap == INVALID_HANDLE_VALUE ) return; -} - -void* _allocHeap_wxString( size_t size ) -{ - return (void*)HeapAlloc( win32_string_heap, 0, size ); -} - -void _freeHeap_wxString( void* ptr ) -{ - HeapFree( win32_string_heap, 0, ptr ); -} - - -void _allocateHeap_wxObject() -{ - if( win32_object_heap == INVALID_HANDLE_VALUE ) - { - //wxASSERT( win32_object_heap_refcount == 0 ); - win32_object_heap = HeapCreate( 0, 0x200000, 0 ); - } -} - -void _destroyHeap_wxObject() -{ - if( win32_object_heap == INVALID_HANDLE_VALUE ) return; -} - -void* _allocHeap_wxObject( size_t size ) -{ - return (void*)HeapAlloc( win32_object_heap, 0, size ); -} - -void _freeHeap_wxObject( void* ptr ) -{ - HeapFree( win32_object_heap, 0, ptr ); -} +// For compilers that support precompilation, includes "wx.h". +#include "wx/wxprec.h" + +#ifdef __BORLANDC__ + #pragma hdrstop +#endif + +#ifndef WX_PRECOMP + #include "wx/string.h" + #include "wx/intl.h" + #include "wx/thread.h" +#endif + +#include + +#ifndef __WXWINCE__ + #include +#endif + +#include +#include + +static HANDLE win32_string_heap = INVALID_HANDLE_VALUE; +static int win32_string_heap_refcount; + +static HANDLE win32_object_heap = INVALID_HANDLE_VALUE; +static int win32_object_heap_refcount; + +void _allocateHeap_wxString() +{ + if( win32_string_heap == INVALID_HANDLE_VALUE ) + { + //wxASSERT( win32_string_heap_refcount == 0 ); + win32_string_heap = HeapCreate( 0, 0x200000, 0 ); + } +} + +void _destroyHeap_wxString() +{ + if( win32_string_heap == INVALID_HANDLE_VALUE ) return; +} + +void* _allocHeap_wxString( size_t size ) +{ + return (void*)HeapAlloc( win32_string_heap, 0, size ); +} + +void _freeHeap_wxString( void* ptr ) +{ + HeapFree( win32_string_heap, 0, ptr ); +} + + +void _allocateHeap_wxObject() +{ + if( win32_object_heap == INVALID_HANDLE_VALUE ) + { + //wxASSERT( win32_object_heap_refcount == 0 ); + win32_object_heap = HeapCreate( 0, 0x200000, 0 ); + } +} + +void _destroyHeap_wxObject() +{ + if( win32_object_heap == INVALID_HANDLE_VALUE ) return; +} + +void* _allocHeap_wxObject( size_t size ) +{ + return (void*)HeapAlloc( win32_object_heap, 0, size ); +} + +void _freeHeap_wxObject( void* ptr ) +{ + HeapFree( win32_object_heap, 0, ptr ); +} diff --git a/3rdparty/zlib/gzclose.c b/3rdparty/zlib/gzclose.c index cbf0db663b..caeb99a317 100644 --- a/3rdparty/zlib/gzclose.c +++ b/3rdparty/zlib/gzclose.c @@ -1,25 +1,25 @@ -/* gzclose.c -- zlib gzclose() function - * Copyright (C) 2004, 2010 Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -#include "gzguts.h" - -/* gzclose() is in a separate file so that it is linked in only if it is used. - That way the other gzclose functions can be used instead to avoid linking in - unneeded compression or decompression routines. */ -int ZEXPORT gzclose(file) - gzFile file; -{ -#ifndef NO_GZCOMPRESS - gz_statep state; - - if (file == NULL) - return Z_STREAM_ERROR; - state = (gz_statep)file; - - return state->mode == GZ_READ ? gzclose_r(file) : gzclose_w(file); -#else - return gzclose_r(file); -#endif -} +/* gzclose.c -- zlib gzclose() function + * Copyright (C) 2004, 2010 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "gzguts.h" + +/* gzclose() is in a separate file so that it is linked in only if it is used. + That way the other gzclose functions can be used instead to avoid linking in + unneeded compression or decompression routines. */ +int ZEXPORT gzclose(file) + gzFile file; +{ +#ifndef NO_GZCOMPRESS + gz_statep state; + + if (file == NULL) + return Z_STREAM_ERROR; + state = (gz_statep)file; + + return state->mode == GZ_READ ? gzclose_r(file) : gzclose_w(file); +#else + return gzclose_r(file); +#endif +} diff --git a/3rdparty/zlib/gzguts.h b/3rdparty/zlib/gzguts.h index b2d7f4b0cb..0e7ed4330a 100644 --- a/3rdparty/zlib/gzguts.h +++ b/3rdparty/zlib/gzguts.h @@ -1,132 +1,132 @@ -/* gzguts.h -- zlib internal header definitions for gz* operations - * Copyright (C) 2004, 2005, 2010 Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -#ifdef _LARGEFILE64_SOURCE -# ifndef _LARGEFILE_SOURCE -# define _LARGEFILE_SOURCE -# endif -# ifdef _FILE_OFFSET_BITS -# undef _FILE_OFFSET_BITS -# endif -#endif - -#define ZLIB_INTERNAL - -#include -#include "zlib.h" -#ifdef STDC -# include -# include -# include -#endif -#include - -#ifdef NO_DEFLATE /* for compatibility with old definition */ -# define NO_GZCOMPRESS -#endif - -#ifdef _MSC_VER -# include -# define vsnprintf _vsnprintf -#endif - -#ifndef local -# define local static -#endif -/* compile with -Dlocal if your debugger can't find static symbols */ - -/* gz* functions always use library allocation functions */ -#ifndef STDC - extern voidp malloc OF((uInt size)); - extern void free OF((voidpf ptr)); -#endif - -/* get errno and strerror definition */ -#if defined UNDER_CE && defined NO_ERRNO_H -# include -# define zstrerror() gz_strwinerror((DWORD)GetLastError()) -#else -# ifdef STDC -# include -# define zstrerror() strerror(errno) -# else -# define zstrerror() "stdio error (consult errno)" -# endif -#endif - -/* MVS fdopen() */ -#ifdef __MVS__ - #pragma map (fdopen , "\174\174FDOPEN") - FILE *fdopen(int, const char *); -#endif - -#ifdef _LARGEFILE64_SOURCE -# define z_off64_t off64_t -#else -# define z_off64_t z_off_t -#endif - -/* default i/o buffer size -- double this for output when reading */ -#define GZBUFSIZE 8192 - -/* gzip modes, also provide a little integrity check on the passed structure */ -#define GZ_NONE 0 -#define GZ_READ 7247 -#define GZ_WRITE 31153 -#define GZ_APPEND 1 /* mode set to GZ_WRITE after the file is opened */ - -/* values for gz_state how */ -#define LOOK 0 /* look for a gzip header */ -#define COPY 1 /* copy input directly */ -#define GZIP 2 /* decompress a gzip stream */ - -/* internal gzip file state data structure */ -typedef struct { - /* used for both reading and writing */ - int mode; /* see gzip modes above */ - int fd; /* file descriptor */ - char *path; /* path or fd for error messages */ - z_off64_t pos; /* current position in uncompressed data */ - unsigned size; /* buffer size, zero if not allocated yet */ - unsigned want; /* requested buffer size, default is GZBUFSIZE */ - unsigned char *in; /* input buffer */ - unsigned char *out; /* output buffer (double-sized when reading) */ - unsigned char *next; /* next output data to deliver or write */ - /* just for reading */ - unsigned have; /* amount of output data unused at next */ - int eof; /* true if end of input file reached */ - z_off64_t start; /* where the gzip data started, for rewinding */ - z_off64_t raw; /* where the raw data started, for seeking */ - int how; /* 0: get header, 1: copy, 2: decompress */ - int direct; /* true if last read direct, false if gzip */ - /* just for writing */ - int level; /* compression level */ - int strategy; /* compression strategy */ - /* seek request */ - z_off64_t skip; /* amount to skip (already rewound if backwards) */ - int seek; /* true if seek request pending */ - /* error information */ - int err; /* error code */ - char *msg; /* error message */ - /* zlib inflate or deflate stream */ - z_stream strm; /* stream structure in-place (not a pointer) */ -} gz_state; -typedef gz_state FAR *gz_statep; - -/* shared functions */ -ZEXTERN void ZEXPORT gz_error OF((gz_statep, int, const char *)); -#if defined UNDER_CE && defined NO_ERRNO_H -ZEXTERN char ZEXPORT *gz_strwinerror OF((DWORD error)); -#endif - -/* GT_OFF(x), where x is an unsigned value, is true if x > maximum z_off64_t - value -- needed when comparing unsigned to z_off64_t, which is signed - (possible z_off64_t types off_t, off64_t, and long are all signed) */ -#ifdef INT_MAX -# define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > INT_MAX) -#else -ZEXTERN unsigned ZEXPORT gz_intmax OF((void)); -# define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > gz_intmax()) -#endif +/* gzguts.h -- zlib internal header definitions for gz* operations + * Copyright (C) 2004, 2005, 2010 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#ifdef _LARGEFILE64_SOURCE +# ifndef _LARGEFILE_SOURCE +# define _LARGEFILE_SOURCE +# endif +# ifdef _FILE_OFFSET_BITS +# undef _FILE_OFFSET_BITS +# endif +#endif + +#define ZLIB_INTERNAL + +#include +#include "zlib.h" +#ifdef STDC +# include +# include +# include +#endif +#include + +#ifdef NO_DEFLATE /* for compatibility with old definition */ +# define NO_GZCOMPRESS +#endif + +#ifdef _MSC_VER +# include +# define vsnprintf _vsnprintf +#endif + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + +/* gz* functions always use library allocation functions */ +#ifndef STDC + extern voidp malloc OF((uInt size)); + extern void free OF((voidpf ptr)); +#endif + +/* get errno and strerror definition */ +#if defined UNDER_CE && defined NO_ERRNO_H +# include +# define zstrerror() gz_strwinerror((DWORD)GetLastError()) +#else +# ifdef STDC +# include +# define zstrerror() strerror(errno) +# else +# define zstrerror() "stdio error (consult errno)" +# endif +#endif + +/* MVS fdopen() */ +#ifdef __MVS__ + #pragma map (fdopen , "\174\174FDOPEN") + FILE *fdopen(int, const char *); +#endif + +#ifdef _LARGEFILE64_SOURCE +# define z_off64_t off64_t +#else +# define z_off64_t z_off_t +#endif + +/* default i/o buffer size -- double this for output when reading */ +#define GZBUFSIZE 8192 + +/* gzip modes, also provide a little integrity check on the passed structure */ +#define GZ_NONE 0 +#define GZ_READ 7247 +#define GZ_WRITE 31153 +#define GZ_APPEND 1 /* mode set to GZ_WRITE after the file is opened */ + +/* values for gz_state how */ +#define LOOK 0 /* look for a gzip header */ +#define COPY 1 /* copy input directly */ +#define GZIP 2 /* decompress a gzip stream */ + +/* internal gzip file state data structure */ +typedef struct { + /* used for both reading and writing */ + int mode; /* see gzip modes above */ + int fd; /* file descriptor */ + char *path; /* path or fd for error messages */ + z_off64_t pos; /* current position in uncompressed data */ + unsigned size; /* buffer size, zero if not allocated yet */ + unsigned want; /* requested buffer size, default is GZBUFSIZE */ + unsigned char *in; /* input buffer */ + unsigned char *out; /* output buffer (double-sized when reading) */ + unsigned char *next; /* next output data to deliver or write */ + /* just for reading */ + unsigned have; /* amount of output data unused at next */ + int eof; /* true if end of input file reached */ + z_off64_t start; /* where the gzip data started, for rewinding */ + z_off64_t raw; /* where the raw data started, for seeking */ + int how; /* 0: get header, 1: copy, 2: decompress */ + int direct; /* true if last read direct, false if gzip */ + /* just for writing */ + int level; /* compression level */ + int strategy; /* compression strategy */ + /* seek request */ + z_off64_t skip; /* amount to skip (already rewound if backwards) */ + int seek; /* true if seek request pending */ + /* error information */ + int err; /* error code */ + char *msg; /* error message */ + /* zlib inflate or deflate stream */ + z_stream strm; /* stream structure in-place (not a pointer) */ +} gz_state; +typedef gz_state FAR *gz_statep; + +/* shared functions */ +ZEXTERN void ZEXPORT gz_error OF((gz_statep, int, const char *)); +#if defined UNDER_CE && defined NO_ERRNO_H +ZEXTERN char ZEXPORT *gz_strwinerror OF((DWORD error)); +#endif + +/* GT_OFF(x), where x is an unsigned value, is true if x > maximum z_off64_t + value -- needed when comparing unsigned to z_off64_t, which is signed + (possible z_off64_t types off_t, off64_t, and long are all signed) */ +#ifdef INT_MAX +# define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > INT_MAX) +#else +ZEXTERN unsigned ZEXPORT gz_intmax OF((void)); +# define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > gz_intmax()) +#endif diff --git a/3rdparty/zlib/gzlib.c b/3rdparty/zlib/gzlib.c index 2ebf653785..6fdb08a814 100644 --- a/3rdparty/zlib/gzlib.c +++ b/3rdparty/zlib/gzlib.c @@ -1,535 +1,535 @@ -/* gzlib.c -- zlib functions common to reading and writing gzip files - * Copyright (C) 2004, 2010 Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -#include "gzguts.h" - -#ifdef _LARGEFILE64_SOURCE -# define LSEEK lseek64 -#else -# define LSEEK lseek -#endif - -/* Local functions */ -local void gz_reset OF((gz_statep)); -local gzFile gz_open OF((const char *, int, const char *)); - -#if defined UNDER_CE && defined NO_ERRNO_H - -/* Map the Windows error number in ERROR to a locale-dependent error message - string and return a pointer to it. Typically, the values for ERROR come - from GetLastError. - - The string pointed to shall not be modified by the application, but may be - overwritten by a subsequent call to gz_strwinerror - - The gz_strwinerror function does not change the current setting of - GetLastError. */ -char ZEXPORT *gz_strwinerror (error) - DWORD error; -{ - static char buf[1024]; - - wchar_t *msgbuf; - DWORD lasterr = GetLastError(); - DWORD chars = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM - | FORMAT_MESSAGE_ALLOCATE_BUFFER, - NULL, - error, - 0, /* Default language */ - (LPVOID)&msgbuf, - 0, - NULL); - if (chars != 0) { - /* If there is an \r\n appended, zap it. */ - if (chars >= 2 - && msgbuf[chars - 2] == '\r' && msgbuf[chars - 1] == '\n') { - chars -= 2; - msgbuf[chars] = 0; - } - - if (chars > sizeof (buf) - 1) { - chars = sizeof (buf) - 1; - msgbuf[chars] = 0; - } - - wcstombs(buf, msgbuf, chars + 1); - LocalFree(msgbuf); - } - else { - sprintf(buf, "unknown win32 error (%ld)", error); - } - - SetLastError(lasterr); - return buf; -} - -#endif /* UNDER_CE && NO_ERRNO_H */ - -/* Reset gzip file state */ -local void gz_reset(state) - gz_statep state; -{ - if (state->mode == GZ_READ) { /* for reading ... */ - state->have = 0; /* no output data available */ - state->eof = 0; /* not at end of file */ - state->how = LOOK; /* look for gzip header */ - state->direct = 1; /* default for empty file */ - } - state->seek = 0; /* no seek request pending */ - gz_error(state, Z_OK, NULL); /* clear error */ - state->pos = 0; /* no uncompressed data yet */ - state->strm.avail_in = 0; /* no input data yet */ -} - -/* Open a gzip file either by name or file descriptor. */ -local gzFile gz_open(path, fd, mode) - const char *path; - int fd; - const char *mode; -{ - gz_statep state; - - /* allocate gzFile structure to return */ - state = malloc(sizeof(gz_state)); - if (state == NULL) - return NULL; - state->size = 0; /* no buffers allocated yet */ - state->want = GZBUFSIZE; /* requested buffer size */ - state->msg = NULL; /* no error message yet */ - - /* interpret mode */ - state->mode = GZ_NONE; - state->level = Z_DEFAULT_COMPRESSION; - state->strategy = Z_DEFAULT_STRATEGY; - while (*mode) { - if (*mode >= '0' && *mode <= '9') - state->level = *mode - '0'; - else - switch (*mode) { - case 'r': - state->mode = GZ_READ; - break; -#ifndef NO_GZCOMPRESS - case 'w': - state->mode = GZ_WRITE; - break; - case 'a': - state->mode = GZ_APPEND; - break; -#endif - case '+': /* can't read and write at the same time */ - free(state); - return NULL; - case 'b': /* ignore -- will request binary anyway */ - break; - case 'f': - state->strategy = Z_FILTERED; - break; - case 'h': - state->strategy = Z_HUFFMAN_ONLY; - break; - case 'R': - state->strategy = Z_RLE; - break; - case 'F': - state->strategy = Z_FIXED; - default: /* could consider as an error, but just ignore */ - ; - } - mode++; - } - - /* must provide an "r", "w", or "a" */ - if (state->mode == GZ_NONE) { - free(state); - return NULL; - } - - /* save the path name for error messages */ - state->path = malloc(strlen(path) + 1); - if (state->path == NULL) { - free(state); - return NULL; - } - strcpy(state->path, path); - - /* open the file with the appropriate mode (or just use fd) */ - state->fd = fd != -1 ? fd : - open(path, -#ifdef O_LARGEFILE - O_LARGEFILE | -#endif -#ifdef O_BINARY - O_BINARY | -#endif - (state->mode == GZ_READ ? - O_RDONLY : - (O_WRONLY | O_CREAT | ( - state->mode == GZ_WRITE ? - O_TRUNC : - O_APPEND))), - 0666); - if (state->fd == -1) { - free(state); - return NULL; - } - if (state->mode == GZ_APPEND) - state->mode = GZ_WRITE; /* simplify later checks */ - - /* save the current position for rewinding (only if reading) */ - if (state->mode == GZ_READ) { - state->start = LSEEK(state->fd, 0, SEEK_CUR); - if (state->start == -1) state->start = 0; - } - - /* initialize stream */ - gz_reset(state); - - /* return stream */ - return (gzFile)state; -} - -/* -- see zlib.h -- */ -gzFile ZEXPORT gzopen(path, mode) - const char *path; - const char *mode; -{ - return gz_open(path, -1, mode); -} - -/* -- see zlib.h -- */ -gzFile ZEXPORT gzopen64(path, mode) - const char *path; - const char *mode; -{ - return gz_open(path, -1, mode); -} - -/* -- see zlib.h -- */ -gzFile ZEXPORT gzdopen(fd, mode) - int fd; - const char *mode; -{ - char *path; /* identifier for error messages */ - gzFile gz; - - if (fd == -1 || (path = malloc(7 + 3 * sizeof(int))) == NULL) - return NULL; - sprintf(path, "", fd); - gz = gz_open(path, fd, mode); - free(path); - return gz; -} - -/* -- see zlib.h -- */ -int ZEXPORT gzbuffer(file, size) - gzFile file; - unsigned size; -{ - gz_statep state; - - /* get internal structure and check integrity */ - if (file == NULL) - return -1; - state = (gz_statep)file; - if (state->mode != GZ_READ && state->mode != GZ_WRITE) - return -1; - - /* make sure we haven't already allocated memory */ - if (state->size != 0) - return -1; - - /* check and set requested size */ - if (size == 0) - return -1; - state->want = size; - return 0; -} - -/* -- see zlib.h -- */ -int ZEXPORT gzrewind(file) - gzFile file; -{ - gz_statep state; - - /* get internal structure */ - if (file == NULL) - return -1; - state = (gz_statep)file; - - /* check that we're reading and that there's no error */ - if (state->mode != GZ_READ || state->err != Z_OK) - return -1; - - /* back up and start over */ - if (LSEEK(state->fd, state->start, SEEK_SET) == -1) - return -1; - gz_reset(state); - return 0; -} - -/* -- see zlib.h -- */ -z_off64_t ZEXPORT gzseek64(file, offset, whence) - gzFile file; - z_off64_t offset; - int whence; -{ - unsigned n; - z_off64_t ret; - gz_statep state; - - /* get internal structure and check integrity */ - if (file == NULL) - return -1; - state = (gz_statep)file; - if (state->mode != GZ_READ && state->mode != GZ_WRITE) - return -1; - - /* check that there's no error */ - if (state->err != Z_OK) - return -1; - - /* can only seek from start or relative to current position */ - if (whence != SEEK_SET && whence != SEEK_CUR) - return -1; - - /* normalize offset to a SEEK_CUR specification */ - if (whence == SEEK_SET) - offset -= state->pos; - else if (state->seek) - offset += state->skip; - state->seek = 0; - - /* if within raw area while reading, just go there */ - if (state->mode == GZ_READ && state->how == COPY && - state->pos + offset >= state->raw) { - ret = LSEEK(state->fd, offset, SEEK_CUR); - if (ret == -1) - return -1; - state->have = 0; - state->eof = 0; - state->seek = 0; - gz_error(state, Z_OK, NULL); - state->strm.avail_in = 0; - state->pos += offset; - return state->pos; - } - - /* calculate skip amount, rewinding if needed for back seek when reading */ - if (offset < 0) { - if (state->mode != GZ_READ) /* writing -- can't go backwards */ - return -1; - offset += state->pos; - if (offset < 0) /* before start of file! */ - return -1; - if (gzrewind(file) == -1) /* rewind, then skip to offset */ - return -1; - } - - /* if reading, skip what's in output buffer (one less gzgetc() check) */ - if (state->mode == GZ_READ) { - n = GT_OFF(state->have) || (z_off64_t)state->have > offset ? - (unsigned)offset : state->have; - state->have -= n; - state->next += n; - state->pos += n; - offset -= n; - } - - /* request skip (if not zero) */ - if (offset) { - state->seek = 1; - state->skip = offset; - } - return state->pos + offset; -} - -/* -- see zlib.h -- */ -z_off_t ZEXPORT gzseek(file, offset, whence) - gzFile file; - z_off_t offset; - int whence; -{ - z_off64_t ret; - - ret = gzseek64(file, (z_off64_t)offset, whence); - return ret == (z_off_t)ret ? (z_off_t)ret : -1; -} - -/* -- see zlib.h -- */ -z_off64_t ZEXPORT gztell64(file) - gzFile file; -{ - gz_statep state; - - /* get internal structure and check integrity */ - if (file == NULL) - return -1; - state = (gz_statep)file; - if (state->mode != GZ_READ && state->mode != GZ_WRITE) - return -1; - - /* return position */ - return state->pos + (state->seek ? state->skip : 0); -} - -/* -- see zlib.h -- */ -z_off_t ZEXPORT gztell(file) - gzFile file; -{ - z_off64_t ret; - - ret = gztell64(file); - return ret == (z_off_t)ret ? (z_off_t)ret : -1; -} - -/* -- see zlib.h -- */ -z_off64_t ZEXPORT gzoffset64(file) - gzFile file; -{ - z_off64_t offset; - gz_statep state; - - /* get internal structure and check integrity */ - if (file == NULL) - return -1; - state = (gz_statep)file; - if (state->mode != GZ_READ && state->mode != GZ_WRITE) - return -1; - - /* compute and return effective offset in file */ - offset = LSEEK(state->fd, 0, SEEK_CUR); - if (offset == -1) - return -1; - if (state->mode == GZ_READ) /* reading */ - offset -= state->strm.avail_in; /* don't count buffered input */ - return offset; -} - -/* -- see zlib.h -- */ -z_off_t ZEXPORT gzoffset(file) - gzFile file; -{ - z_off64_t ret; - - ret = gzoffset64(file); - return ret == (z_off_t)ret ? (z_off_t)ret : -1; -} - -/* -- see zlib.h -- */ -int ZEXPORT gzeof(file) - gzFile file; -{ - gz_statep state; - - /* get internal structure and check integrity */ - if (file == NULL) - return 0; - state = (gz_statep)file; - if (state->mode != GZ_READ && state->mode != GZ_WRITE) - return 0; - - /* return end-of-file state */ - return state->mode == GZ_READ ? (state->eof && state->have == 0) : 0; -} - -/* -- see zlib.h -- */ -const char * ZEXPORT gzerror(file, errnum) - gzFile file; - int *errnum; -{ - gz_statep state; - - /* get internal structure and check integrity */ - if (file == NULL) - return NULL; - state = (gz_statep)file; - if (state->mode != GZ_READ && state->mode != GZ_WRITE) - return NULL; - - /* return error information */ - if (errnum != NULL) - *errnum = state->err; - return state->msg == NULL ? "" : state->msg; -} - -/* -- see zlib.h -- */ -void ZEXPORT gzclearerr(file) - gzFile file; -{ - gz_statep state; - - /* get internal structure and check integrity */ - if (file == NULL) - return; - state = (gz_statep)file; - if (state->mode != GZ_READ && state->mode != GZ_WRITE) - return; - - /* clear error and end-of-file */ - if (state->mode == GZ_READ) - state->eof = 0; - gz_error(state, Z_OK, NULL); -} - -/* Create an error message in allocated memory and set state->err and - state->msg accordingly. Free any previous error message already there. Do - not try to free or allocate space if the error is Z_MEM_ERROR (out of - memory). Simply save the error message as a static string. If there is an - allocation failure constructing the error message, then convert the error to - out of memory. */ -void ZEXPORT gz_error(state, err, msg) - gz_statep state; - int err; - const char *msg; -{ - /* free previously allocated message and clear */ - if (state->msg != NULL) { - if (state->err != Z_MEM_ERROR) - free(state->msg); - state->msg = NULL; - } - - /* set error code, and if no message, then done */ - state->err = err; - if (msg == NULL) - return; - - /* for an out of memory error, save as static string */ - if (err == Z_MEM_ERROR) { - state->msg = (char *)msg; - return; - } - - /* construct error message with path */ - if ((state->msg = malloc(strlen(state->path) + strlen(msg) + 3)) == NULL) { - state->err = Z_MEM_ERROR; - state->msg = (char *)"out of memory"; - return; - } - strcpy(state->msg, state->path); - strcat(state->msg, ": "); - strcat(state->msg, msg); - return; -} - -#ifndef INT_MAX -/* portably return maximum value for an int (when limits.h presumed not - available) -- we need to do this to cover cases where 2's complement not - used, since C standard permits 1's complement and sign-bit representations, - otherwise we could just use ((unsigned)-1) >> 1 */ -unsigned ZEXPORT gz_intmax() -{ - unsigned p, q; - - p = 1; - do { - q = p; - p <<= 1; - p++; - } while (p > q); - return q >> 1; -} -#endif +/* gzlib.c -- zlib functions common to reading and writing gzip files + * Copyright (C) 2004, 2010 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "gzguts.h" + +#ifdef _LARGEFILE64_SOURCE +# define LSEEK lseek64 +#else +# define LSEEK lseek +#endif + +/* Local functions */ +local void gz_reset OF((gz_statep)); +local gzFile gz_open OF((const char *, int, const char *)); + +#if defined UNDER_CE && defined NO_ERRNO_H + +/* Map the Windows error number in ERROR to a locale-dependent error message + string and return a pointer to it. Typically, the values for ERROR come + from GetLastError. + + The string pointed to shall not be modified by the application, but may be + overwritten by a subsequent call to gz_strwinerror + + The gz_strwinerror function does not change the current setting of + GetLastError. */ +char ZEXPORT *gz_strwinerror (error) + DWORD error; +{ + static char buf[1024]; + + wchar_t *msgbuf; + DWORD lasterr = GetLastError(); + DWORD chars = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM + | FORMAT_MESSAGE_ALLOCATE_BUFFER, + NULL, + error, + 0, /* Default language */ + (LPVOID)&msgbuf, + 0, + NULL); + if (chars != 0) { + /* If there is an \r\n appended, zap it. */ + if (chars >= 2 + && msgbuf[chars - 2] == '\r' && msgbuf[chars - 1] == '\n') { + chars -= 2; + msgbuf[chars] = 0; + } + + if (chars > sizeof (buf) - 1) { + chars = sizeof (buf) - 1; + msgbuf[chars] = 0; + } + + wcstombs(buf, msgbuf, chars + 1); + LocalFree(msgbuf); + } + else { + sprintf(buf, "unknown win32 error (%ld)", error); + } + + SetLastError(lasterr); + return buf; +} + +#endif /* UNDER_CE && NO_ERRNO_H */ + +/* Reset gzip file state */ +local void gz_reset(state) + gz_statep state; +{ + if (state->mode == GZ_READ) { /* for reading ... */ + state->have = 0; /* no output data available */ + state->eof = 0; /* not at end of file */ + state->how = LOOK; /* look for gzip header */ + state->direct = 1; /* default for empty file */ + } + state->seek = 0; /* no seek request pending */ + gz_error(state, Z_OK, NULL); /* clear error */ + state->pos = 0; /* no uncompressed data yet */ + state->strm.avail_in = 0; /* no input data yet */ +} + +/* Open a gzip file either by name or file descriptor. */ +local gzFile gz_open(path, fd, mode) + const char *path; + int fd; + const char *mode; +{ + gz_statep state; + + /* allocate gzFile structure to return */ + state = malloc(sizeof(gz_state)); + if (state == NULL) + return NULL; + state->size = 0; /* no buffers allocated yet */ + state->want = GZBUFSIZE; /* requested buffer size */ + state->msg = NULL; /* no error message yet */ + + /* interpret mode */ + state->mode = GZ_NONE; + state->level = Z_DEFAULT_COMPRESSION; + state->strategy = Z_DEFAULT_STRATEGY; + while (*mode) { + if (*mode >= '0' && *mode <= '9') + state->level = *mode - '0'; + else + switch (*mode) { + case 'r': + state->mode = GZ_READ; + break; +#ifndef NO_GZCOMPRESS + case 'w': + state->mode = GZ_WRITE; + break; + case 'a': + state->mode = GZ_APPEND; + break; +#endif + case '+': /* can't read and write at the same time */ + free(state); + return NULL; + case 'b': /* ignore -- will request binary anyway */ + break; + case 'f': + state->strategy = Z_FILTERED; + break; + case 'h': + state->strategy = Z_HUFFMAN_ONLY; + break; + case 'R': + state->strategy = Z_RLE; + break; + case 'F': + state->strategy = Z_FIXED; + default: /* could consider as an error, but just ignore */ + ; + } + mode++; + } + + /* must provide an "r", "w", or "a" */ + if (state->mode == GZ_NONE) { + free(state); + return NULL; + } + + /* save the path name for error messages */ + state->path = malloc(strlen(path) + 1); + if (state->path == NULL) { + free(state); + return NULL; + } + strcpy(state->path, path); + + /* open the file with the appropriate mode (or just use fd) */ + state->fd = fd != -1 ? fd : + open(path, +#ifdef O_LARGEFILE + O_LARGEFILE | +#endif +#ifdef O_BINARY + O_BINARY | +#endif + (state->mode == GZ_READ ? + O_RDONLY : + (O_WRONLY | O_CREAT | ( + state->mode == GZ_WRITE ? + O_TRUNC : + O_APPEND))), + 0666); + if (state->fd == -1) { + free(state); + return NULL; + } + if (state->mode == GZ_APPEND) + state->mode = GZ_WRITE; /* simplify later checks */ + + /* save the current position for rewinding (only if reading) */ + if (state->mode == GZ_READ) { + state->start = LSEEK(state->fd, 0, SEEK_CUR); + if (state->start == -1) state->start = 0; + } + + /* initialize stream */ + gz_reset(state); + + /* return stream */ + return (gzFile)state; +} + +/* -- see zlib.h -- */ +gzFile ZEXPORT gzopen(path, mode) + const char *path; + const char *mode; +{ + return gz_open(path, -1, mode); +} + +/* -- see zlib.h -- */ +gzFile ZEXPORT gzopen64(path, mode) + const char *path; + const char *mode; +{ + return gz_open(path, -1, mode); +} + +/* -- see zlib.h -- */ +gzFile ZEXPORT gzdopen(fd, mode) + int fd; + const char *mode; +{ + char *path; /* identifier for error messages */ + gzFile gz; + + if (fd == -1 || (path = malloc(7 + 3 * sizeof(int))) == NULL) + return NULL; + sprintf(path, "", fd); + gz = gz_open(path, fd, mode); + free(path); + return gz; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzbuffer(file, size) + gzFile file; + unsigned size; +{ + gz_statep state; + + /* get internal structure and check integrity */ + if (file == NULL) + return -1; + state = (gz_statep)file; + if (state->mode != GZ_READ && state->mode != GZ_WRITE) + return -1; + + /* make sure we haven't already allocated memory */ + if (state->size != 0) + return -1; + + /* check and set requested size */ + if (size == 0) + return -1; + state->want = size; + return 0; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzrewind(file) + gzFile file; +{ + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return -1; + state = (gz_statep)file; + + /* check that we're reading and that there's no error */ + if (state->mode != GZ_READ || state->err != Z_OK) + return -1; + + /* back up and start over */ + if (LSEEK(state->fd, state->start, SEEK_SET) == -1) + return -1; + gz_reset(state); + return 0; +} + +/* -- see zlib.h -- */ +z_off64_t ZEXPORT gzseek64(file, offset, whence) + gzFile file; + z_off64_t offset; + int whence; +{ + unsigned n; + z_off64_t ret; + gz_statep state; + + /* get internal structure and check integrity */ + if (file == NULL) + return -1; + state = (gz_statep)file; + if (state->mode != GZ_READ && state->mode != GZ_WRITE) + return -1; + + /* check that there's no error */ + if (state->err != Z_OK) + return -1; + + /* can only seek from start or relative to current position */ + if (whence != SEEK_SET && whence != SEEK_CUR) + return -1; + + /* normalize offset to a SEEK_CUR specification */ + if (whence == SEEK_SET) + offset -= state->pos; + else if (state->seek) + offset += state->skip; + state->seek = 0; + + /* if within raw area while reading, just go there */ + if (state->mode == GZ_READ && state->how == COPY && + state->pos + offset >= state->raw) { + ret = LSEEK(state->fd, offset, SEEK_CUR); + if (ret == -1) + return -1; + state->have = 0; + state->eof = 0; + state->seek = 0; + gz_error(state, Z_OK, NULL); + state->strm.avail_in = 0; + state->pos += offset; + return state->pos; + } + + /* calculate skip amount, rewinding if needed for back seek when reading */ + if (offset < 0) { + if (state->mode != GZ_READ) /* writing -- can't go backwards */ + return -1; + offset += state->pos; + if (offset < 0) /* before start of file! */ + return -1; + if (gzrewind(file) == -1) /* rewind, then skip to offset */ + return -1; + } + + /* if reading, skip what's in output buffer (one less gzgetc() check) */ + if (state->mode == GZ_READ) { + n = GT_OFF(state->have) || (z_off64_t)state->have > offset ? + (unsigned)offset : state->have; + state->have -= n; + state->next += n; + state->pos += n; + offset -= n; + } + + /* request skip (if not zero) */ + if (offset) { + state->seek = 1; + state->skip = offset; + } + return state->pos + offset; +} + +/* -- see zlib.h -- */ +z_off_t ZEXPORT gzseek(file, offset, whence) + gzFile file; + z_off_t offset; + int whence; +{ + z_off64_t ret; + + ret = gzseek64(file, (z_off64_t)offset, whence); + return ret == (z_off_t)ret ? (z_off_t)ret : -1; +} + +/* -- see zlib.h -- */ +z_off64_t ZEXPORT gztell64(file) + gzFile file; +{ + gz_statep state; + + /* get internal structure and check integrity */ + if (file == NULL) + return -1; + state = (gz_statep)file; + if (state->mode != GZ_READ && state->mode != GZ_WRITE) + return -1; + + /* return position */ + return state->pos + (state->seek ? state->skip : 0); +} + +/* -- see zlib.h -- */ +z_off_t ZEXPORT gztell(file) + gzFile file; +{ + z_off64_t ret; + + ret = gztell64(file); + return ret == (z_off_t)ret ? (z_off_t)ret : -1; +} + +/* -- see zlib.h -- */ +z_off64_t ZEXPORT gzoffset64(file) + gzFile file; +{ + z_off64_t offset; + gz_statep state; + + /* get internal structure and check integrity */ + if (file == NULL) + return -1; + state = (gz_statep)file; + if (state->mode != GZ_READ && state->mode != GZ_WRITE) + return -1; + + /* compute and return effective offset in file */ + offset = LSEEK(state->fd, 0, SEEK_CUR); + if (offset == -1) + return -1; + if (state->mode == GZ_READ) /* reading */ + offset -= state->strm.avail_in; /* don't count buffered input */ + return offset; +} + +/* -- see zlib.h -- */ +z_off_t ZEXPORT gzoffset(file) + gzFile file; +{ + z_off64_t ret; + + ret = gzoffset64(file); + return ret == (z_off_t)ret ? (z_off_t)ret : -1; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzeof(file) + gzFile file; +{ + gz_statep state; + + /* get internal structure and check integrity */ + if (file == NULL) + return 0; + state = (gz_statep)file; + if (state->mode != GZ_READ && state->mode != GZ_WRITE) + return 0; + + /* return end-of-file state */ + return state->mode == GZ_READ ? (state->eof && state->have == 0) : 0; +} + +/* -- see zlib.h -- */ +const char * ZEXPORT gzerror(file, errnum) + gzFile file; + int *errnum; +{ + gz_statep state; + + /* get internal structure and check integrity */ + if (file == NULL) + return NULL; + state = (gz_statep)file; + if (state->mode != GZ_READ && state->mode != GZ_WRITE) + return NULL; + + /* return error information */ + if (errnum != NULL) + *errnum = state->err; + return state->msg == NULL ? "" : state->msg; +} + +/* -- see zlib.h -- */ +void ZEXPORT gzclearerr(file) + gzFile file; +{ + gz_statep state; + + /* get internal structure and check integrity */ + if (file == NULL) + return; + state = (gz_statep)file; + if (state->mode != GZ_READ && state->mode != GZ_WRITE) + return; + + /* clear error and end-of-file */ + if (state->mode == GZ_READ) + state->eof = 0; + gz_error(state, Z_OK, NULL); +} + +/* Create an error message in allocated memory and set state->err and + state->msg accordingly. Free any previous error message already there. Do + not try to free or allocate space if the error is Z_MEM_ERROR (out of + memory). Simply save the error message as a static string. If there is an + allocation failure constructing the error message, then convert the error to + out of memory. */ +void ZEXPORT gz_error(state, err, msg) + gz_statep state; + int err; + const char *msg; +{ + /* free previously allocated message and clear */ + if (state->msg != NULL) { + if (state->err != Z_MEM_ERROR) + free(state->msg); + state->msg = NULL; + } + + /* set error code, and if no message, then done */ + state->err = err; + if (msg == NULL) + return; + + /* for an out of memory error, save as static string */ + if (err == Z_MEM_ERROR) { + state->msg = (char *)msg; + return; + } + + /* construct error message with path */ + if ((state->msg = malloc(strlen(state->path) + strlen(msg) + 3)) == NULL) { + state->err = Z_MEM_ERROR; + state->msg = (char *)"out of memory"; + return; + } + strcpy(state->msg, state->path); + strcat(state->msg, ": "); + strcat(state->msg, msg); + return; +} + +#ifndef INT_MAX +/* portably return maximum value for an int (when limits.h presumed not + available) -- we need to do this to cover cases where 2's complement not + used, since C standard permits 1's complement and sign-bit representations, + otherwise we could just use ((unsigned)-1) >> 1 */ +unsigned ZEXPORT gz_intmax() +{ + unsigned p, q; + + p = 1; + do { + q = p; + p <<= 1; + p++; + } while (p > q); + return q >> 1; +} +#endif diff --git a/3rdparty/zlib/gzread.c b/3rdparty/zlib/gzread.c index 9c66f2bf9c..434ef023e0 100644 --- a/3rdparty/zlib/gzread.c +++ b/3rdparty/zlib/gzread.c @@ -1,652 +1,652 @@ -/* gzread.c -- zlib functions for reading gzip files - * Copyright (C) 2004, 2005, 2010 Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -#include "gzguts.h" - -/* Local functions */ -local int gz_load OF((gz_statep, unsigned char *, unsigned, unsigned *)); -local int gz_avail OF((gz_statep)); -local int gz_next4 OF((gz_statep, unsigned long *)); -local int gz_head OF((gz_statep)); -local int gz_decomp OF((gz_statep)); -local int gz_make OF((gz_statep)); -local int gz_skip OF((gz_statep, z_off64_t)); - -/* Use read() to load a buffer -- return -1 on error, otherwise 0. Read from - state->fd, and update state->eof, state->err, and state->msg as appropriate. - This function needs to loop on read(), since read() is not guaranteed to - read the number of bytes requested, depending on the type of descriptor. */ -local int gz_load(state, buf, len, have) - gz_statep state; - unsigned char *buf; - unsigned len; - unsigned *have; -{ - int ret; - - *have = 0; - do { - ret = read(state->fd, buf + *have, len - *have); - if (ret <= 0) - break; - *have += ret; - } while (*have < len); - if (ret < 0) { - gz_error(state, Z_ERRNO, zstrerror()); - return -1; - } - if (ret == 0) - state->eof = 1; - return 0; -} - -/* Load up input buffer and set eof flag if last data loaded -- return -1 on - error, 0 otherwise. Note that the eof flag is set when the end of the input - file is reached, even though there may be unused data in the buffer. Once - that data has been used, no more attempts will be made to read the file. - gz_avail() assumes that strm->avail_in == 0. */ -local int gz_avail(state) - gz_statep state; -{ - z_streamp strm = &(state->strm); - - if (state->err != Z_OK) - return -1; - if (state->eof == 0) { - if (gz_load(state, state->in, state->size, &(strm->avail_in)) == -1) - return -1; - strm->next_in = state->in; - } - return 0; -} - -/* Get next byte from input, or -1 if end or error. */ -#define NEXT() ((strm->avail_in == 0 && gz_avail(state) == -1) ? -1 : \ - (strm->avail_in == 0 ? -1 : \ - (strm->avail_in--, *(strm->next_in)++))) - -/* Get a four-byte little-endian integer and return 0 on success and the value - in *ret. Otherwise -1 is returned and *ret is not modified. */ -local int gz_next4(state, ret) - gz_statep state; - unsigned long *ret; -{ - int ch; - unsigned long val; - z_streamp strm = &(state->strm); - - val = NEXT(); - val += (unsigned)NEXT() << 8; - val += (unsigned long)NEXT() << 16; - ch = NEXT(); - if (ch == -1) - return -1; - val += (unsigned long)ch << 24; - *ret = val; - return 0; -} - -/* Look for gzip header, set up for inflate or copy. state->have must be zero. - If this is the first time in, allocate required memory. state->how will be - left unchanged if there is no more input data available, will be set to COPY - if there is no gzip header and direct copying will be performed, or it will - be set to GZIP for decompression, and the gzip header will be skipped so - that the next available input data is the raw deflate stream. If direct - copying, then leftover input data from the input buffer will be copied to - the output buffer. In that case, all further file reads will be directly to - either the output buffer or a user buffer. If decompressing, the inflate - state and the check value will be initialized. gz_head() will return 0 on - success or -1 on failure. Failures may include read errors or gzip header - errors. */ -local int gz_head(state) - gz_statep state; -{ - z_streamp strm = &(state->strm); - int flags; - unsigned len; - - /* allocate read buffers and inflate memory */ - if (state->size == 0) { - /* allocate buffers */ - state->in = malloc(state->want); - state->out = malloc(state->want << 1); - if (state->in == NULL || state->out == NULL) { - if (state->out != NULL) - free(state->out); - if (state->in != NULL) - free(state->in); - gz_error(state, Z_MEM_ERROR, "out of memory"); - return -1; - } - state->size = state->want; - - /* allocate inflate memory */ - state->strm.zalloc = Z_NULL; - state->strm.zfree = Z_NULL; - state->strm.opaque = Z_NULL; - state->strm.avail_in = 0; - state->strm.next_in = Z_NULL; - if (inflateInit2(&(state->strm), -15) != Z_OK) { /* raw inflate */ - free(state->out); - free(state->in); - state->size = 0; - gz_error(state, Z_MEM_ERROR, "out of memory"); - return -1; - } - } - - /* get some data in the input buffer */ - if (strm->avail_in == 0) { - if (gz_avail(state) == -1) - return -1; - if (strm->avail_in == 0) - return 0; - } - - /* look for the gzip magic header bytes 31 and 139 */ - if (strm->next_in[0] == 31) { - strm->avail_in--; - strm->next_in++; - if (strm->avail_in == 0 && gz_avail(state) == -1) - return -1; - if (strm->avail_in && strm->next_in[0] == 139) { - /* we have a gzip header, woo hoo! */ - strm->avail_in--; - strm->next_in++; - - /* skip rest of header */ - if (NEXT() != 8) { /* compression method */ - gz_error(state, Z_DATA_ERROR, "unknown compression method"); - return -1; - } - flags = NEXT(); - if (flags & 0xe0) { /* reserved flag bits */ - gz_error(state, Z_DATA_ERROR, "unknown header flags set"); - return -1; - } - NEXT(); /* modification time */ - NEXT(); - NEXT(); - NEXT(); - NEXT(); /* extra flags */ - NEXT(); /* operating system */ - if (flags & 4) { /* extra field */ - len = (unsigned)NEXT(); - len += (unsigned)NEXT() << 8; - while (len--) - if (NEXT() < 0) - break; - } - if (flags & 8) /* file name */ - while (NEXT() > 0) - ; - if (flags & 16) /* comment */ - while (NEXT() > 0) - ; - if (flags & 2) { /* header crc */ - NEXT(); - NEXT(); - } - /* an unexpected end of file is not checked for here -- it will be - noticed on the first request for uncompressed data */ - - /* set up for decompression */ - inflateReset(strm); - strm->adler = crc32(0L, Z_NULL, 0); - state->how = GZIP; - state->direct = 0; - return 0; - } - else { - /* not a gzip file -- save first byte (31) and fall to raw i/o */ - state->out[0] = 31; - state->have = 1; - } - } - - /* doing raw i/o, save start of raw data for seeking, copy any leftover - input to output -- this assumes that the output buffer is larger than - the input buffer, which also assures space for gzungetc() */ - state->raw = state->pos; - state->next = state->out; - if (strm->avail_in) { - memcpy(state->next + state->have, strm->next_in, strm->avail_in); - state->have += strm->avail_in; - strm->avail_in = 0; - } - state->how = COPY; - state->direct = 1; - return 0; -} - -/* Decompress from input to the provided next_out and avail_out in the state. - If the end of the compressed data is reached, then verify the gzip trailer - check value and length (modulo 2^32). state->have and state->next are set - to point to the just decompressed data, and the crc is updated. If the - trailer is verified, state->how is reset to LOOK to look for the next gzip - stream or raw data, once state->have is depleted. Returns 0 on success, -1 - on failure. Failures may include invalid compressed data or a failed gzip - trailer verification. */ -local int gz_decomp(state) - gz_statep state; -{ - int ret; - unsigned had; - unsigned long crc, len; - z_streamp strm = &(state->strm); - - /* fill output buffer up to end of deflate stream */ - had = strm->avail_out; - do { - /* get more input for inflate() */ - if (strm->avail_in == 0 && gz_avail(state) == -1) - return -1; - if (strm->avail_in == 0) { - gz_error(state, Z_DATA_ERROR, "unexpected end of file"); - return -1; - } - - /* decompress and handle errors */ - ret = inflate(strm, Z_NO_FLUSH); - if (ret == Z_STREAM_ERROR || ret == Z_NEED_DICT) { - gz_error(state, Z_STREAM_ERROR, - "internal error: inflate stream corrupt"); - return -1; - } - if (ret == Z_MEM_ERROR) { - gz_error(state, Z_MEM_ERROR, "out of memory"); - return -1; - } - if (ret == Z_DATA_ERROR) { /* deflate stream invalid */ - gz_error(state, Z_DATA_ERROR, - strm->msg == NULL ? "compressed data error" : strm->msg); - return -1; - } - } while (strm->avail_out && ret != Z_STREAM_END); - - /* update available output and crc check value */ - state->have = had - strm->avail_out; - state->next = strm->next_out - state->have; - strm->adler = crc32(strm->adler, state->next, state->have); - - /* check gzip trailer if at end of deflate stream */ - if (ret == Z_STREAM_END) { - if (gz_next4(state, &crc) == -1 || gz_next4(state, &len) == -1) { - gz_error(state, Z_DATA_ERROR, "unexpected end of file"); - return -1; - } - if (crc != strm->adler) { - gz_error(state, Z_DATA_ERROR, "incorrect data check"); - return -1; - } - if (len != (strm->total_out & 0xffffffffL)) { - gz_error(state, Z_DATA_ERROR, "incorrect length check"); - return -1; - } - state->how = LOOK; /* ready for next stream, once have is 0 (leave - state->direct unchanged to remember how) */ - } - - /* good decompression */ - return 0; -} - -/* Make data and put in the output buffer. Assumes that state->have == 0. - Data is either copied from the input file or decompressed from the input - file depending on state->how. If state->how is LOOK, then a gzip header is - looked for (and skipped if found) to determine wither to copy or decompress. - Returns -1 on error, otherwise 0. gz_make() will leave state->have as COPY - or GZIP unless the end of the input file has been reached and all data has - been processed. */ -local int gz_make(state) - gz_statep state; -{ - z_streamp strm = &(state->strm); - - if (state->how == LOOK) { /* look for gzip header */ - if (gz_head(state) == -1) - return -1; - if (state->have) /* got some data from gz_head() */ - return 0; - } - if (state->how == COPY) { /* straight copy */ - if (gz_load(state, state->out, state->size << 1, &(state->have)) == -1) - return -1; - state->next = state->out; - } - else if (state->how == GZIP) { /* decompress */ - strm->avail_out = state->size << 1; - strm->next_out = state->out; - if (gz_decomp(state) == -1) - return -1; - } - return 0; -} - -/* Skip len uncompressed bytes of output. Return -1 on error, 0 on success. */ -local int gz_skip(state, len) - gz_statep state; - z_off64_t len; -{ - unsigned n; - - /* skip over len bytes or reach end-of-file, whichever comes first */ - while (len) - /* skip over whatever is in output buffer */ - if (state->have) { - n = GT_OFF(state->have) || (z_off64_t)state->have > len ? - (unsigned)len : state->have; - state->have -= n; - state->next += n; - state->pos += n; - len -= n; - } - - /* output buffer empty -- return if we're at the end of the input */ - else if (state->eof && state->strm.avail_in == 0) - break; - - /* need more data to skip -- load up output buffer */ - else { - /* get more output, looking for header if required */ - if (gz_make(state) == -1) - return -1; - } - return 0; -} - -/* -- see zlib.h -- */ -int ZEXPORT gzread(file, buf, len) - gzFile file; - voidp buf; - unsigned len; -{ - unsigned got, n; - gz_statep state; - z_streamp strm; - - /* get internal structure */ - if (file == NULL) - return -1; - state = (gz_statep)file; - strm = &(state->strm); - - /* check that we're reading and that there's no error */ - if (state->mode != GZ_READ || state->err != Z_OK) - return -1; - - /* since an int is returned, make sure len fits in one, otherwise return - with an error (this avoids the flaw in the interface) */ - if ((int)len < 0) { - gz_error(state, Z_BUF_ERROR, "requested length does not fit in int"); - return -1; - } - - /* if len is zero, avoid unnecessary operations */ - if (len == 0) - return 0; - - /* process a skip request */ - if (state->seek) { - state->seek = 0; - if (gz_skip(state, state->skip) == -1) - return -1; - } - - /* get len bytes to buf, or less than len if at the end */ - got = 0; - do { - /* first just try copying data from the output buffer */ - if (state->have) { - n = state->have > len ? len : state->have; - memcpy(buf, state->next, n); - state->next += n; - state->have -= n; - } - - /* output buffer empty -- return if we're at the end of the input */ - else if (state->eof && strm->avail_in == 0) - break; - - /* need output data -- for small len or new stream load up our output - buffer */ - else if (state->how == LOOK || len < (state->size << 1)) { - /* get more output, looking for header if required */ - if (gz_make(state) == -1) - return -1; - continue; /* no progress yet -- go back to memcpy() above */ - /* the copy above assures that we will leave with space in the - output buffer, allowing at least one gzungetc() to succeed */ - } - - /* large len -- read directly into user buffer */ - else if (state->how == COPY) { /* read directly */ - if (gz_load(state, buf, len, &n) == -1) - return -1; - } - - /* large len -- decompress directly into user buffer */ - else { /* state->how == GZIP */ - strm->avail_out = len; - strm->next_out = buf; - if (gz_decomp(state) == -1) - return -1; - n = state->have; - state->have = 0; - } - - /* update progress */ - len -= n; - buf = (char *)buf + n; - got += n; - state->pos += n; - } while (len); - - /* return number of bytes read into user buffer (will fit in int) */ - return (int)got; -} - -/* -- see zlib.h -- */ -int ZEXPORT gzgetc(file) - gzFile file; -{ - int ret; - unsigned char buf[1]; - gz_statep state; - - /* get internal structure */ - if (file == NULL) - return -1; - state = (gz_statep)file; - - /* check that we're reading and that there's no error */ - if (state->mode != GZ_READ || state->err != Z_OK) - return -1; - - /* try output buffer (no need to check for skip request) */ - if (state->have) { - state->have--; - state->pos++; - return *(state->next)++; - } - - /* nothing there -- try gzread() */ - ret = gzread(file, buf, 1); - return ret < 1 ? -1 : buf[0]; -} - -/* -- see zlib.h -- */ -int ZEXPORT gzungetc(c, file) - int c; - gzFile file; -{ - gz_statep state; - - /* get internal structure */ - if (file == NULL) - return -1; - state = (gz_statep)file; - - /* check that we're reading and that there's no error */ - if (state->mode != GZ_READ || state->err != Z_OK) - return -1; - - /* process a skip request */ - if (state->seek) { - state->seek = 0; - if (gz_skip(state, state->skip) == -1) - return -1; - } - - /* can't push EOF */ - if (c < 0) - return -1; - - /* if output buffer empty, put byte at end (allows more pushing) */ - if (state->have == 0) { - state->have = 1; - state->next = state->out + (state->size << 1) - 1; - state->next[0] = c; - state->pos--; - return c; - } - - /* if no room, give up (must have already done a gzungetc()) */ - if (state->have == (state->size << 1)) { - gz_error(state, Z_BUF_ERROR, "out of room to push characters"); - return -1; - } - - /* slide output data if needed and insert byte before existing data */ - if (state->next == state->out) { - unsigned char *src = state->out + state->have; - unsigned char *dest = state->out + (state->size << 1); - while (src > state->out) - *--dest = *--src; - state->next = dest; - } - state->have++; - state->next--; - state->next[0] = c; - state->pos--; - return c; -} - -/* -- see zlib.h -- */ -char * ZEXPORT gzgets(file, buf, len) - gzFile file; - char *buf; - int len; -{ - unsigned left, n; - char *str; - unsigned char *eol; - gz_statep state; - - /* check parameters and get internal structure */ - if (file == NULL || buf == NULL || len < 1) - return NULL; - state = (gz_statep)file; - - /* check that we're reading and that there's no error */ - if (state->mode != GZ_READ || state->err != Z_OK) - return NULL; - - /* process a skip request */ - if (state->seek) { - state->seek = 0; - if (gz_skip(state, state->skip) == -1) - return NULL; - } - - /* copy output bytes up to new line or len - 1, whichever comes first -- - append a terminating zero to the string (we don't check for a zero in - the contents, let the user worry about that) */ - str = buf; - left = (unsigned)len - 1; - if (left) do { - /* assure that something is in the output buffer */ - if (state->have == 0) { - if (gz_make(state) == -1) - return NULL; /* error */ - if (state->have == 0) { /* end of file */ - if (buf == str) /* got bupkus */ - return NULL; - break; /* got something -- return it */ - } - } - - /* look for end-of-line in current output buffer */ - n = state->have > left ? left : state->have; - eol = memchr(state->next, '\n', n); - if (eol != NULL) - n = (unsigned)(eol - state->next) + 1; - - /* copy through end-of-line, or remainder if not found */ - memcpy(buf, state->next, n); - state->have -= n; - state->next += n; - state->pos += n; - left -= n; - buf += n; - } while (left && eol == NULL); - - /* found end-of-line or out of space -- terminate string and return it */ - buf[0] = 0; - return str; -} - -/* -- see zlib.h -- */ -int ZEXPORT gzdirect(file) - gzFile file; -{ - gz_statep state; - - /* get internal structure */ - if (file == NULL) - return 0; - state = (gz_statep)file; - - /* check that we're reading */ - if (state->mode != GZ_READ) - return 0; - - /* if the state is not known, but we can find out, then do so (this is - mainly for right after a gzopen() or gzdopen()) */ - if (state->how == LOOK && state->have == 0) - (void)gz_head(state); - - /* return 1 if reading direct, 0 if decompressing a gzip stream */ - return state->direct; -} - -/* -- see zlib.h -- */ -int ZEXPORT gzclose_r(file) - gzFile file; -{ - int ret; - gz_statep state; - - /* get internal structure */ - if (file == NULL) - return Z_STREAM_ERROR; - state = (gz_statep)file; - - /* check that we're reading */ - if (state->mode != GZ_READ) - return Z_STREAM_ERROR; - - /* free memory and close file */ - if (state->size) { - inflateEnd(&(state->strm)); - free(state->out); - free(state->in); - } - gz_error(state, Z_OK, NULL); - free(state->path); - ret = close(state->fd); - free(state); - return ret ? Z_ERRNO : Z_OK; -} +/* gzread.c -- zlib functions for reading gzip files + * Copyright (C) 2004, 2005, 2010 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "gzguts.h" + +/* Local functions */ +local int gz_load OF((gz_statep, unsigned char *, unsigned, unsigned *)); +local int gz_avail OF((gz_statep)); +local int gz_next4 OF((gz_statep, unsigned long *)); +local int gz_head OF((gz_statep)); +local int gz_decomp OF((gz_statep)); +local int gz_make OF((gz_statep)); +local int gz_skip OF((gz_statep, z_off64_t)); + +/* Use read() to load a buffer -- return -1 on error, otherwise 0. Read from + state->fd, and update state->eof, state->err, and state->msg as appropriate. + This function needs to loop on read(), since read() is not guaranteed to + read the number of bytes requested, depending on the type of descriptor. */ +local int gz_load(state, buf, len, have) + gz_statep state; + unsigned char *buf; + unsigned len; + unsigned *have; +{ + int ret; + + *have = 0; + do { + ret = read(state->fd, buf + *have, len - *have); + if (ret <= 0) + break; + *have += ret; + } while (*have < len); + if (ret < 0) { + gz_error(state, Z_ERRNO, zstrerror()); + return -1; + } + if (ret == 0) + state->eof = 1; + return 0; +} + +/* Load up input buffer and set eof flag if last data loaded -- return -1 on + error, 0 otherwise. Note that the eof flag is set when the end of the input + file is reached, even though there may be unused data in the buffer. Once + that data has been used, no more attempts will be made to read the file. + gz_avail() assumes that strm->avail_in == 0. */ +local int gz_avail(state) + gz_statep state; +{ + z_streamp strm = &(state->strm); + + if (state->err != Z_OK) + return -1; + if (state->eof == 0) { + if (gz_load(state, state->in, state->size, &(strm->avail_in)) == -1) + return -1; + strm->next_in = state->in; + } + return 0; +} + +/* Get next byte from input, or -1 if end or error. */ +#define NEXT() ((strm->avail_in == 0 && gz_avail(state) == -1) ? -1 : \ + (strm->avail_in == 0 ? -1 : \ + (strm->avail_in--, *(strm->next_in)++))) + +/* Get a four-byte little-endian integer and return 0 on success and the value + in *ret. Otherwise -1 is returned and *ret is not modified. */ +local int gz_next4(state, ret) + gz_statep state; + unsigned long *ret; +{ + int ch; + unsigned long val; + z_streamp strm = &(state->strm); + + val = NEXT(); + val += (unsigned)NEXT() << 8; + val += (unsigned long)NEXT() << 16; + ch = NEXT(); + if (ch == -1) + return -1; + val += (unsigned long)ch << 24; + *ret = val; + return 0; +} + +/* Look for gzip header, set up for inflate or copy. state->have must be zero. + If this is the first time in, allocate required memory. state->how will be + left unchanged if there is no more input data available, will be set to COPY + if there is no gzip header and direct copying will be performed, or it will + be set to GZIP for decompression, and the gzip header will be skipped so + that the next available input data is the raw deflate stream. If direct + copying, then leftover input data from the input buffer will be copied to + the output buffer. In that case, all further file reads will be directly to + either the output buffer or a user buffer. If decompressing, the inflate + state and the check value will be initialized. gz_head() will return 0 on + success or -1 on failure. Failures may include read errors or gzip header + errors. */ +local int gz_head(state) + gz_statep state; +{ + z_streamp strm = &(state->strm); + int flags; + unsigned len; + + /* allocate read buffers and inflate memory */ + if (state->size == 0) { + /* allocate buffers */ + state->in = malloc(state->want); + state->out = malloc(state->want << 1); + if (state->in == NULL || state->out == NULL) { + if (state->out != NULL) + free(state->out); + if (state->in != NULL) + free(state->in); + gz_error(state, Z_MEM_ERROR, "out of memory"); + return -1; + } + state->size = state->want; + + /* allocate inflate memory */ + state->strm.zalloc = Z_NULL; + state->strm.zfree = Z_NULL; + state->strm.opaque = Z_NULL; + state->strm.avail_in = 0; + state->strm.next_in = Z_NULL; + if (inflateInit2(&(state->strm), -15) != Z_OK) { /* raw inflate */ + free(state->out); + free(state->in); + state->size = 0; + gz_error(state, Z_MEM_ERROR, "out of memory"); + return -1; + } + } + + /* get some data in the input buffer */ + if (strm->avail_in == 0) { + if (gz_avail(state) == -1) + return -1; + if (strm->avail_in == 0) + return 0; + } + + /* look for the gzip magic header bytes 31 and 139 */ + if (strm->next_in[0] == 31) { + strm->avail_in--; + strm->next_in++; + if (strm->avail_in == 0 && gz_avail(state) == -1) + return -1; + if (strm->avail_in && strm->next_in[0] == 139) { + /* we have a gzip header, woo hoo! */ + strm->avail_in--; + strm->next_in++; + + /* skip rest of header */ + if (NEXT() != 8) { /* compression method */ + gz_error(state, Z_DATA_ERROR, "unknown compression method"); + return -1; + } + flags = NEXT(); + if (flags & 0xe0) { /* reserved flag bits */ + gz_error(state, Z_DATA_ERROR, "unknown header flags set"); + return -1; + } + NEXT(); /* modification time */ + NEXT(); + NEXT(); + NEXT(); + NEXT(); /* extra flags */ + NEXT(); /* operating system */ + if (flags & 4) { /* extra field */ + len = (unsigned)NEXT(); + len += (unsigned)NEXT() << 8; + while (len--) + if (NEXT() < 0) + break; + } + if (flags & 8) /* file name */ + while (NEXT() > 0) + ; + if (flags & 16) /* comment */ + while (NEXT() > 0) + ; + if (flags & 2) { /* header crc */ + NEXT(); + NEXT(); + } + /* an unexpected end of file is not checked for here -- it will be + noticed on the first request for uncompressed data */ + + /* set up for decompression */ + inflateReset(strm); + strm->adler = crc32(0L, Z_NULL, 0); + state->how = GZIP; + state->direct = 0; + return 0; + } + else { + /* not a gzip file -- save first byte (31) and fall to raw i/o */ + state->out[0] = 31; + state->have = 1; + } + } + + /* doing raw i/o, save start of raw data for seeking, copy any leftover + input to output -- this assumes that the output buffer is larger than + the input buffer, which also assures space for gzungetc() */ + state->raw = state->pos; + state->next = state->out; + if (strm->avail_in) { + memcpy(state->next + state->have, strm->next_in, strm->avail_in); + state->have += strm->avail_in; + strm->avail_in = 0; + } + state->how = COPY; + state->direct = 1; + return 0; +} + +/* Decompress from input to the provided next_out and avail_out in the state. + If the end of the compressed data is reached, then verify the gzip trailer + check value and length (modulo 2^32). state->have and state->next are set + to point to the just decompressed data, and the crc is updated. If the + trailer is verified, state->how is reset to LOOK to look for the next gzip + stream or raw data, once state->have is depleted. Returns 0 on success, -1 + on failure. Failures may include invalid compressed data or a failed gzip + trailer verification. */ +local int gz_decomp(state) + gz_statep state; +{ + int ret; + unsigned had; + unsigned long crc, len; + z_streamp strm = &(state->strm); + + /* fill output buffer up to end of deflate stream */ + had = strm->avail_out; + do { + /* get more input for inflate() */ + if (strm->avail_in == 0 && gz_avail(state) == -1) + return -1; + if (strm->avail_in == 0) { + gz_error(state, Z_DATA_ERROR, "unexpected end of file"); + return -1; + } + + /* decompress and handle errors */ + ret = inflate(strm, Z_NO_FLUSH); + if (ret == Z_STREAM_ERROR || ret == Z_NEED_DICT) { + gz_error(state, Z_STREAM_ERROR, + "internal error: inflate stream corrupt"); + return -1; + } + if (ret == Z_MEM_ERROR) { + gz_error(state, Z_MEM_ERROR, "out of memory"); + return -1; + } + if (ret == Z_DATA_ERROR) { /* deflate stream invalid */ + gz_error(state, Z_DATA_ERROR, + strm->msg == NULL ? "compressed data error" : strm->msg); + return -1; + } + } while (strm->avail_out && ret != Z_STREAM_END); + + /* update available output and crc check value */ + state->have = had - strm->avail_out; + state->next = strm->next_out - state->have; + strm->adler = crc32(strm->adler, state->next, state->have); + + /* check gzip trailer if at end of deflate stream */ + if (ret == Z_STREAM_END) { + if (gz_next4(state, &crc) == -1 || gz_next4(state, &len) == -1) { + gz_error(state, Z_DATA_ERROR, "unexpected end of file"); + return -1; + } + if (crc != strm->adler) { + gz_error(state, Z_DATA_ERROR, "incorrect data check"); + return -1; + } + if (len != (strm->total_out & 0xffffffffL)) { + gz_error(state, Z_DATA_ERROR, "incorrect length check"); + return -1; + } + state->how = LOOK; /* ready for next stream, once have is 0 (leave + state->direct unchanged to remember how) */ + } + + /* good decompression */ + return 0; +} + +/* Make data and put in the output buffer. Assumes that state->have == 0. + Data is either copied from the input file or decompressed from the input + file depending on state->how. If state->how is LOOK, then a gzip header is + looked for (and skipped if found) to determine wither to copy or decompress. + Returns -1 on error, otherwise 0. gz_make() will leave state->have as COPY + or GZIP unless the end of the input file has been reached and all data has + been processed. */ +local int gz_make(state) + gz_statep state; +{ + z_streamp strm = &(state->strm); + + if (state->how == LOOK) { /* look for gzip header */ + if (gz_head(state) == -1) + return -1; + if (state->have) /* got some data from gz_head() */ + return 0; + } + if (state->how == COPY) { /* straight copy */ + if (gz_load(state, state->out, state->size << 1, &(state->have)) == -1) + return -1; + state->next = state->out; + } + else if (state->how == GZIP) { /* decompress */ + strm->avail_out = state->size << 1; + strm->next_out = state->out; + if (gz_decomp(state) == -1) + return -1; + } + return 0; +} + +/* Skip len uncompressed bytes of output. Return -1 on error, 0 on success. */ +local int gz_skip(state, len) + gz_statep state; + z_off64_t len; +{ + unsigned n; + + /* skip over len bytes or reach end-of-file, whichever comes first */ + while (len) + /* skip over whatever is in output buffer */ + if (state->have) { + n = GT_OFF(state->have) || (z_off64_t)state->have > len ? + (unsigned)len : state->have; + state->have -= n; + state->next += n; + state->pos += n; + len -= n; + } + + /* output buffer empty -- return if we're at the end of the input */ + else if (state->eof && state->strm.avail_in == 0) + break; + + /* need more data to skip -- load up output buffer */ + else { + /* get more output, looking for header if required */ + if (gz_make(state) == -1) + return -1; + } + return 0; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzread(file, buf, len) + gzFile file; + voidp buf; + unsigned len; +{ + unsigned got, n; + gz_statep state; + z_streamp strm; + + /* get internal structure */ + if (file == NULL) + return -1; + state = (gz_statep)file; + strm = &(state->strm); + + /* check that we're reading and that there's no error */ + if (state->mode != GZ_READ || state->err != Z_OK) + return -1; + + /* since an int is returned, make sure len fits in one, otherwise return + with an error (this avoids the flaw in the interface) */ + if ((int)len < 0) { + gz_error(state, Z_BUF_ERROR, "requested length does not fit in int"); + return -1; + } + + /* if len is zero, avoid unnecessary operations */ + if (len == 0) + return 0; + + /* process a skip request */ + if (state->seek) { + state->seek = 0; + if (gz_skip(state, state->skip) == -1) + return -1; + } + + /* get len bytes to buf, or less than len if at the end */ + got = 0; + do { + /* first just try copying data from the output buffer */ + if (state->have) { + n = state->have > len ? len : state->have; + memcpy(buf, state->next, n); + state->next += n; + state->have -= n; + } + + /* output buffer empty -- return if we're at the end of the input */ + else if (state->eof && strm->avail_in == 0) + break; + + /* need output data -- for small len or new stream load up our output + buffer */ + else if (state->how == LOOK || len < (state->size << 1)) { + /* get more output, looking for header if required */ + if (gz_make(state) == -1) + return -1; + continue; /* no progress yet -- go back to memcpy() above */ + /* the copy above assures that we will leave with space in the + output buffer, allowing at least one gzungetc() to succeed */ + } + + /* large len -- read directly into user buffer */ + else if (state->how == COPY) { /* read directly */ + if (gz_load(state, buf, len, &n) == -1) + return -1; + } + + /* large len -- decompress directly into user buffer */ + else { /* state->how == GZIP */ + strm->avail_out = len; + strm->next_out = buf; + if (gz_decomp(state) == -1) + return -1; + n = state->have; + state->have = 0; + } + + /* update progress */ + len -= n; + buf = (char *)buf + n; + got += n; + state->pos += n; + } while (len); + + /* return number of bytes read into user buffer (will fit in int) */ + return (int)got; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzgetc(file) + gzFile file; +{ + int ret; + unsigned char buf[1]; + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return -1; + state = (gz_statep)file; + + /* check that we're reading and that there's no error */ + if (state->mode != GZ_READ || state->err != Z_OK) + return -1; + + /* try output buffer (no need to check for skip request) */ + if (state->have) { + state->have--; + state->pos++; + return *(state->next)++; + } + + /* nothing there -- try gzread() */ + ret = gzread(file, buf, 1); + return ret < 1 ? -1 : buf[0]; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzungetc(c, file) + int c; + gzFile file; +{ + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return -1; + state = (gz_statep)file; + + /* check that we're reading and that there's no error */ + if (state->mode != GZ_READ || state->err != Z_OK) + return -1; + + /* process a skip request */ + if (state->seek) { + state->seek = 0; + if (gz_skip(state, state->skip) == -1) + return -1; + } + + /* can't push EOF */ + if (c < 0) + return -1; + + /* if output buffer empty, put byte at end (allows more pushing) */ + if (state->have == 0) { + state->have = 1; + state->next = state->out + (state->size << 1) - 1; + state->next[0] = c; + state->pos--; + return c; + } + + /* if no room, give up (must have already done a gzungetc()) */ + if (state->have == (state->size << 1)) { + gz_error(state, Z_BUF_ERROR, "out of room to push characters"); + return -1; + } + + /* slide output data if needed and insert byte before existing data */ + if (state->next == state->out) { + unsigned char *src = state->out + state->have; + unsigned char *dest = state->out + (state->size << 1); + while (src > state->out) + *--dest = *--src; + state->next = dest; + } + state->have++; + state->next--; + state->next[0] = c; + state->pos--; + return c; +} + +/* -- see zlib.h -- */ +char * ZEXPORT gzgets(file, buf, len) + gzFile file; + char *buf; + int len; +{ + unsigned left, n; + char *str; + unsigned char *eol; + gz_statep state; + + /* check parameters and get internal structure */ + if (file == NULL || buf == NULL || len < 1) + return NULL; + state = (gz_statep)file; + + /* check that we're reading and that there's no error */ + if (state->mode != GZ_READ || state->err != Z_OK) + return NULL; + + /* process a skip request */ + if (state->seek) { + state->seek = 0; + if (gz_skip(state, state->skip) == -1) + return NULL; + } + + /* copy output bytes up to new line or len - 1, whichever comes first -- + append a terminating zero to the string (we don't check for a zero in + the contents, let the user worry about that) */ + str = buf; + left = (unsigned)len - 1; + if (left) do { + /* assure that something is in the output buffer */ + if (state->have == 0) { + if (gz_make(state) == -1) + return NULL; /* error */ + if (state->have == 0) { /* end of file */ + if (buf == str) /* got bupkus */ + return NULL; + break; /* got something -- return it */ + } + } + + /* look for end-of-line in current output buffer */ + n = state->have > left ? left : state->have; + eol = memchr(state->next, '\n', n); + if (eol != NULL) + n = (unsigned)(eol - state->next) + 1; + + /* copy through end-of-line, or remainder if not found */ + memcpy(buf, state->next, n); + state->have -= n; + state->next += n; + state->pos += n; + left -= n; + buf += n; + } while (left && eol == NULL); + + /* found end-of-line or out of space -- terminate string and return it */ + buf[0] = 0; + return str; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzdirect(file) + gzFile file; +{ + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return 0; + state = (gz_statep)file; + + /* check that we're reading */ + if (state->mode != GZ_READ) + return 0; + + /* if the state is not known, but we can find out, then do so (this is + mainly for right after a gzopen() or gzdopen()) */ + if (state->how == LOOK && state->have == 0) + (void)gz_head(state); + + /* return 1 if reading direct, 0 if decompressing a gzip stream */ + return state->direct; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzclose_r(file) + gzFile file; +{ + int ret; + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return Z_STREAM_ERROR; + state = (gz_statep)file; + + /* check that we're reading */ + if (state->mode != GZ_READ) + return Z_STREAM_ERROR; + + /* free memory and close file */ + if (state->size) { + inflateEnd(&(state->strm)); + free(state->out); + free(state->in); + } + gz_error(state, Z_OK, NULL); + free(state->path); + ret = close(state->fd); + free(state); + return ret ? Z_ERRNO : Z_OK; +} diff --git a/3rdparty/zlib/gzwrite.c b/3rdparty/zlib/gzwrite.c index e2bcf9d2b8..e8defc6887 100644 --- a/3rdparty/zlib/gzwrite.c +++ b/3rdparty/zlib/gzwrite.c @@ -1,531 +1,531 @@ -/* gzwrite.c -- zlib functions for writing gzip files - * Copyright (C) 2004, 2005, 2010 Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -#include "gzguts.h" - -/* Local functions */ -local int gz_init OF((gz_statep)); -local int gz_comp OF((gz_statep, int)); -local int gz_zero OF((gz_statep, z_off64_t)); - -/* Initialize state for writing a gzip file. Mark initialization by setting - state->size to non-zero. Return -1 on failure or 0 on success. */ -local int gz_init(state) - gz_statep state; -{ - int ret; - z_streamp strm = &(state->strm); - - /* allocate input and output buffers */ - state->in = malloc(state->want); - state->out = malloc(state->want); - if (state->in == NULL || state->out == NULL) { - if (state->out != NULL) - free(state->out); - if (state->in != NULL) - free(state->in); - gz_error(state, Z_MEM_ERROR, "out of memory"); - return -1; - } - - /* allocate deflate memory, set up for gzip compression */ - strm->zalloc = Z_NULL; - strm->zfree = Z_NULL; - strm->opaque = Z_NULL; - ret = deflateInit2(strm, state->level, Z_DEFLATED, - 15 + 16, 8, state->strategy); - if (ret != Z_OK) { - free(state->in); - gz_error(state, Z_MEM_ERROR, "out of memory"); - return -1; - } - - /* mark state as initialized */ - state->size = state->want; - - /* initialize write buffer */ - strm->avail_out = state->size; - strm->next_out = state->out; - state->next = strm->next_out; - return 0; -} - -/* Compress whatever is at avail_in and next_in and write to the output file. - Return -1 if there is an error writing to the output file, otherwise 0. - flush is assumed to be a valid deflate() flush value. If flush is Z_FINISH, - then the deflate() state is reset to start a new gzip stream. */ -local int gz_comp(state, flush) - gz_statep state; - int flush; -{ - int ret, got; - unsigned have; - z_streamp strm = &(state->strm); - - /* allocate memory if this is the first time through */ - if (state->size == 0 && gz_init(state) == -1) - return -1; - - /* run deflate() on provided input until it produces no more output */ - ret = Z_OK; - do { - /* write out current buffer contents if full, or if flushing, but if - doing Z_FINISH then don't write until we get to Z_STREAM_END */ - if (strm->avail_out == 0 || (flush != Z_NO_FLUSH && - (flush != Z_FINISH || ret == Z_STREAM_END))) { - have = (unsigned)(strm->next_out - state->next); - if (have && ((got = write(state->fd, state->next, have)) < 0 || - (unsigned)got != have)) { - gz_error(state, Z_ERRNO, zstrerror()); - return -1; - } - if (strm->avail_out == 0) { - strm->avail_out = state->size; - strm->next_out = state->out; - } - state->next = strm->next_out; - } - - /* compress */ - have = strm->avail_out; - ret = deflate(strm, flush); - if (ret == Z_STREAM_ERROR) { - gz_error(state, Z_STREAM_ERROR, - "internal error: deflate stream corrupt"); - return -1; - } - have -= strm->avail_out; - } while (have); - - /* if that completed a deflate stream, allow another to start */ - if (flush == Z_FINISH) - deflateReset(strm); - - /* all done, no errors */ - return 0; -} - -/* Compress len zeros to output. Return -1 on error, 0 on success. */ -local int gz_zero(state, len) - gz_statep state; - z_off64_t len; -{ - int first; - unsigned n; - z_streamp strm = &(state->strm); - - /* consume whatever's left in the input buffer */ - if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1) - return -1; - - /* compress len zeros (len guaranteed > 0) */ - first = 1; - while (len) { - n = GT_OFF(state->size) || (z_off64_t)state->size > len ? - (unsigned)len : state->size; - if (first) { - memset(state->in, 0, n); - first = 0; - } - strm->avail_in = n; - strm->next_in = state->in; - state->pos += n; - if (gz_comp(state, Z_NO_FLUSH) == -1) - return -1; - len -= n; - } - return 0; -} - -/* -- see zlib.h -- */ -int ZEXPORT gzwrite(file, buf, len) - gzFile file; - voidpc buf; - unsigned len; -{ - unsigned put = len; - unsigned n; - gz_statep state; - z_streamp strm; - - /* get internal structure */ - if (file == NULL) - return 0; - state = (gz_statep)file; - strm = &(state->strm); - - /* check that we're writing and that there's no error */ - if (state->mode != GZ_WRITE || state->err != Z_OK) - return 0; - - /* since an int is returned, make sure len fits in one, otherwise return - with an error (this avoids the flaw in the interface) */ - if ((int)len < 0) { - gz_error(state, Z_BUF_ERROR, "requested length does not fit in int"); - return 0; - } - - /* if len is zero, avoid unnecessary operations */ - if (len == 0) - return 0; - - /* allocate memory if this is the first time through */ - if (state->size == 0 && gz_init(state) == -1) - return 0; - - /* check for seek request */ - if (state->seek) { - state->seek = 0; - if (gz_zero(state, state->skip) == -1) - return 0; - } - - /* for small len, copy to input buffer, otherwise compress directly */ - if (len < state->size) { - /* copy to input buffer, compress when full */ - do { - if (strm->avail_in == 0) - strm->next_in = state->in; - n = state->size - strm->avail_in; - if (n > len) - n = len; - memcpy(strm->next_in + strm->avail_in, buf, n); - strm->avail_in += n; - state->pos += n; - buf = (char *)buf + n; - len -= n; - if (len && gz_comp(state, Z_NO_FLUSH) == -1) - return 0; - } while (len); - } - else { - /* consume whatever's left in the input buffer */ - if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1) - return 0; - - /* directly compress user buffer to file */ - strm->avail_in = len; - strm->next_in = (voidp)buf; - state->pos += len; - if (gz_comp(state, Z_NO_FLUSH) == -1) - return 0; - } - - /* input was all buffered or compressed (put will fit in int) */ - return (int)put; -} - -/* -- see zlib.h -- */ -int ZEXPORT gzputc(file, c) - gzFile file; - int c; -{ - unsigned char buf[1]; - gz_statep state; - z_streamp strm; - - /* get internal structure */ - if (file == NULL) - return -1; - state = (gz_statep)file; - strm = &(state->strm); - - /* check that we're writing and that there's no error */ - if (state->mode != GZ_WRITE || state->err != Z_OK) - return -1; - - /* check for seek request */ - if (state->seek) { - state->seek = 0; - if (gz_zero(state, state->skip) == -1) - return -1; - } - - /* try writing to input buffer for speed (state->size == 0 if buffer not - initialized) */ - if (strm->avail_in < state->size) { - if (strm->avail_in == 0) - strm->next_in = state->in; - strm->next_in[strm->avail_in++] = c; - state->pos++; - return c; - } - - /* no room in buffer or not initialized, use gz_write() */ - buf[0] = c; - if (gzwrite(file, buf, 1) != 1) - return -1; - return c; -} - -/* -- see zlib.h -- */ -int ZEXPORT gzputs(file, str) - gzFile file; - const char *str; -{ - int ret; - unsigned len; - - /* write string */ - len = (unsigned)strlen(str); - ret = gzwrite(file, str, len); - return ret == 0 && len != 0 ? -1 : ret; -} - -#ifdef STDC -#include - -/* -- see zlib.h -- */ -int ZEXPORTVA gzprintf (gzFile file, const char *format, ...) -{ - int size, len; - gz_statep state; - z_streamp strm; - va_list va; - - /* get internal structure */ - if (file == NULL) - return -1; - state = (gz_statep)file; - strm = &(state->strm); - - /* check that we're writing and that there's no error */ - if (state->mode != GZ_WRITE || state->err != Z_OK) - return 0; - - /* make sure we have some buffer space */ - if (state->size == 0 && gz_init(state) == -1) - return 0; - - /* check for seek request */ - if (state->seek) { - state->seek = 0; - if (gz_zero(state, state->skip) == -1) - return 0; - } - - /* consume whatever's left in the input buffer */ - if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1) - return 0; - - /* do the printf() into the input buffer, put length in len */ - size = (int)(state->size); - state->in[size - 1] = 0; - va_start(va, format); -#ifdef NO_vsnprintf -# ifdef HAS_vsprintf_void - (void)vsprintf(state->in, format, va); - va_end(va); - for (len = 0; len < size; len++) - if (state->in[len] == 0) break; -# else - len = vsprintf(state->in, format, va); - va_end(va); -# endif -#else -# ifdef HAS_vsnprintf_void - (void)vsnprintf(state->in, size, format, va); - va_end(va); - len = strlen(state->in); -# else - len = vsnprintf((char *)(state->in), size, format, va); - va_end(va); -# endif -#endif - - /* check that printf() results fit in buffer */ - if (len <= 0 || len >= (int)size || state->in[size - 1] != 0) - return 0; - - /* update buffer and position, defer compression until needed */ - strm->avail_in = (unsigned)len; - strm->next_in = state->in; - state->pos += len; - return len; -} - -#else /* !STDC */ - -/* -- see zlib.h -- */ -int ZEXPORTVA gzprintf (file, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, - a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) - gzFile file; - const char *format; - int a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, - a11, a12, a13, a14, a15, a16, a17, a18, a19, a20; -{ - int size, len; - gz_statep state; - z_streamp strm; - - /* get internal structure */ - if (file == NULL) - return -1; - state = (gz_statep)file; - strm = &(state->strm); - - /* check that we're writing and that there's no error */ - if (state->mode != GZ_WRITE || state->err != Z_OK) - return 0; - - /* make sure we have some buffer space */ - if (state->size == 0 && gz_init(state) == -1) - return 0; - - /* check for seek request */ - if (state->seek) { - state->seek = 0; - if (gz_zero(state, state->skip) == -1) - return 0; - } - - /* consume whatever's left in the input buffer */ - if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1) - return 0; - - /* do the printf() into the input buffer, put length in len */ - size = (int)(state->size); - state->in[size - 1] = 0; -#ifdef NO_snprintf -# ifdef HAS_sprintf_void - sprintf(state->in, format, a1, a2, a3, a4, a5, a6, a7, a8, - a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); - for (len = 0; len < size; len++) - if (state->in[len] == 0) break; -# else - len = sprintf(state->in, format, a1, a2, a3, a4, a5, a6, a7, a8, - a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); -# endif -#else -# ifdef HAS_snprintf_void - snprintf(state->in, size, format, a1, a2, a3, a4, a5, a6, a7, a8, - a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); - len = strlen(state->in); -# else - len = snprintf(state->in, size, format, a1, a2, a3, a4, a5, a6, a7, a8, - a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); -# endif -#endif - - /* check that printf() results fit in buffer */ - if (len <= 0 || len >= (int)size || state->in[size - 1] != 0) - return 0; - - /* update buffer and position, defer compression until needed */ - strm->avail_in = (unsigned)len; - strm->next_in = state->in; - state->pos += len; - return len; -} - -#endif - -/* -- see zlib.h -- */ -int ZEXPORT gzflush(file, flush) - gzFile file; - int flush; -{ - gz_statep state; - - /* get internal structure */ - if (file == NULL) - return -1; - state = (gz_statep)file; - - /* check that we're writing and that there's no error */ - if (state->mode != GZ_WRITE || state->err != Z_OK) - return Z_STREAM_ERROR; - - /* check flush parameter */ - if (flush < 0 || flush > Z_FINISH) - return Z_STREAM_ERROR; - - /* check for seek request */ - if (state->seek) { - state->seek = 0; - if (gz_zero(state, state->skip) == -1) - return -1; - } - - /* compress remaining data with requested flush */ - gz_comp(state, flush); - return state->err; -} - -/* -- see zlib.h -- */ -int ZEXPORT gzsetparams(file, level, strategy) - gzFile file; - int level; - int strategy; -{ - gz_statep state; - z_streamp strm; - - /* get internal structure */ - if (file == NULL) - return Z_STREAM_ERROR; - state = (gz_statep)file; - strm = &(state->strm); - - /* check that we're writing and that there's no error */ - if (state->mode != GZ_WRITE || state->err != Z_OK) - return Z_STREAM_ERROR; - - /* if no change is requested, then do nothing */ - if (level == state->level && strategy == state->strategy) - return Z_OK; - - /* check for seek request */ - if (state->seek) { - state->seek = 0; - if (gz_zero(state, state->skip) == -1) - return -1; - } - - /* change compression parameters for subsequent input */ - if (state->size) { - /* flush previous input with previous parameters before changing */ - if (strm->avail_in && gz_comp(state, Z_PARTIAL_FLUSH) == -1) - return state->err; - deflateParams(strm, level, strategy); - } - state->level = level; - state->strategy = strategy; - return Z_OK; -} - -/* -- see zlib.h -- */ -int ZEXPORT gzclose_w(file) - gzFile file; -{ - int ret = 0; - gz_statep state; - - /* get internal structure */ - if (file == NULL) - return Z_STREAM_ERROR; - state = (gz_statep)file; - - /* check that we're writing */ - if (state->mode != GZ_WRITE) - return Z_STREAM_ERROR; - - /* check for seek request */ - if (state->seek) { - state->seek = 0; - ret += gz_zero(state, state->skip); - } - - /* flush, free memory, and close file */ - ret += gz_comp(state, Z_FINISH); - (void)deflateEnd(&(state->strm)); - free(state->out); - free(state->in); - gz_error(state, Z_OK, NULL); - free(state->path); - ret += close(state->fd); - free(state); - return ret ? Z_ERRNO : Z_OK; -} +/* gzwrite.c -- zlib functions for writing gzip files + * Copyright (C) 2004, 2005, 2010 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "gzguts.h" + +/* Local functions */ +local int gz_init OF((gz_statep)); +local int gz_comp OF((gz_statep, int)); +local int gz_zero OF((gz_statep, z_off64_t)); + +/* Initialize state for writing a gzip file. Mark initialization by setting + state->size to non-zero. Return -1 on failure or 0 on success. */ +local int gz_init(state) + gz_statep state; +{ + int ret; + z_streamp strm = &(state->strm); + + /* allocate input and output buffers */ + state->in = malloc(state->want); + state->out = malloc(state->want); + if (state->in == NULL || state->out == NULL) { + if (state->out != NULL) + free(state->out); + if (state->in != NULL) + free(state->in); + gz_error(state, Z_MEM_ERROR, "out of memory"); + return -1; + } + + /* allocate deflate memory, set up for gzip compression */ + strm->zalloc = Z_NULL; + strm->zfree = Z_NULL; + strm->opaque = Z_NULL; + ret = deflateInit2(strm, state->level, Z_DEFLATED, + 15 + 16, 8, state->strategy); + if (ret != Z_OK) { + free(state->in); + gz_error(state, Z_MEM_ERROR, "out of memory"); + return -1; + } + + /* mark state as initialized */ + state->size = state->want; + + /* initialize write buffer */ + strm->avail_out = state->size; + strm->next_out = state->out; + state->next = strm->next_out; + return 0; +} + +/* Compress whatever is at avail_in and next_in and write to the output file. + Return -1 if there is an error writing to the output file, otherwise 0. + flush is assumed to be a valid deflate() flush value. If flush is Z_FINISH, + then the deflate() state is reset to start a new gzip stream. */ +local int gz_comp(state, flush) + gz_statep state; + int flush; +{ + int ret, got; + unsigned have; + z_streamp strm = &(state->strm); + + /* allocate memory if this is the first time through */ + if (state->size == 0 && gz_init(state) == -1) + return -1; + + /* run deflate() on provided input until it produces no more output */ + ret = Z_OK; + do { + /* write out current buffer contents if full, or if flushing, but if + doing Z_FINISH then don't write until we get to Z_STREAM_END */ + if (strm->avail_out == 0 || (flush != Z_NO_FLUSH && + (flush != Z_FINISH || ret == Z_STREAM_END))) { + have = (unsigned)(strm->next_out - state->next); + if (have && ((got = write(state->fd, state->next, have)) < 0 || + (unsigned)got != have)) { + gz_error(state, Z_ERRNO, zstrerror()); + return -1; + } + if (strm->avail_out == 0) { + strm->avail_out = state->size; + strm->next_out = state->out; + } + state->next = strm->next_out; + } + + /* compress */ + have = strm->avail_out; + ret = deflate(strm, flush); + if (ret == Z_STREAM_ERROR) { + gz_error(state, Z_STREAM_ERROR, + "internal error: deflate stream corrupt"); + return -1; + } + have -= strm->avail_out; + } while (have); + + /* if that completed a deflate stream, allow another to start */ + if (flush == Z_FINISH) + deflateReset(strm); + + /* all done, no errors */ + return 0; +} + +/* Compress len zeros to output. Return -1 on error, 0 on success. */ +local int gz_zero(state, len) + gz_statep state; + z_off64_t len; +{ + int first; + unsigned n; + z_streamp strm = &(state->strm); + + /* consume whatever's left in the input buffer */ + if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1) + return -1; + + /* compress len zeros (len guaranteed > 0) */ + first = 1; + while (len) { + n = GT_OFF(state->size) || (z_off64_t)state->size > len ? + (unsigned)len : state->size; + if (first) { + memset(state->in, 0, n); + first = 0; + } + strm->avail_in = n; + strm->next_in = state->in; + state->pos += n; + if (gz_comp(state, Z_NO_FLUSH) == -1) + return -1; + len -= n; + } + return 0; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzwrite(file, buf, len) + gzFile file; + voidpc buf; + unsigned len; +{ + unsigned put = len; + unsigned n; + gz_statep state; + z_streamp strm; + + /* get internal structure */ + if (file == NULL) + return 0; + state = (gz_statep)file; + strm = &(state->strm); + + /* check that we're writing and that there's no error */ + if (state->mode != GZ_WRITE || state->err != Z_OK) + return 0; + + /* since an int is returned, make sure len fits in one, otherwise return + with an error (this avoids the flaw in the interface) */ + if ((int)len < 0) { + gz_error(state, Z_BUF_ERROR, "requested length does not fit in int"); + return 0; + } + + /* if len is zero, avoid unnecessary operations */ + if (len == 0) + return 0; + + /* allocate memory if this is the first time through */ + if (state->size == 0 && gz_init(state) == -1) + return 0; + + /* check for seek request */ + if (state->seek) { + state->seek = 0; + if (gz_zero(state, state->skip) == -1) + return 0; + } + + /* for small len, copy to input buffer, otherwise compress directly */ + if (len < state->size) { + /* copy to input buffer, compress when full */ + do { + if (strm->avail_in == 0) + strm->next_in = state->in; + n = state->size - strm->avail_in; + if (n > len) + n = len; + memcpy(strm->next_in + strm->avail_in, buf, n); + strm->avail_in += n; + state->pos += n; + buf = (char *)buf + n; + len -= n; + if (len && gz_comp(state, Z_NO_FLUSH) == -1) + return 0; + } while (len); + } + else { + /* consume whatever's left in the input buffer */ + if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1) + return 0; + + /* directly compress user buffer to file */ + strm->avail_in = len; + strm->next_in = (voidp)buf; + state->pos += len; + if (gz_comp(state, Z_NO_FLUSH) == -1) + return 0; + } + + /* input was all buffered or compressed (put will fit in int) */ + return (int)put; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzputc(file, c) + gzFile file; + int c; +{ + unsigned char buf[1]; + gz_statep state; + z_streamp strm; + + /* get internal structure */ + if (file == NULL) + return -1; + state = (gz_statep)file; + strm = &(state->strm); + + /* check that we're writing and that there's no error */ + if (state->mode != GZ_WRITE || state->err != Z_OK) + return -1; + + /* check for seek request */ + if (state->seek) { + state->seek = 0; + if (gz_zero(state, state->skip) == -1) + return -1; + } + + /* try writing to input buffer for speed (state->size == 0 if buffer not + initialized) */ + if (strm->avail_in < state->size) { + if (strm->avail_in == 0) + strm->next_in = state->in; + strm->next_in[strm->avail_in++] = c; + state->pos++; + return c; + } + + /* no room in buffer or not initialized, use gz_write() */ + buf[0] = c; + if (gzwrite(file, buf, 1) != 1) + return -1; + return c; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzputs(file, str) + gzFile file; + const char *str; +{ + int ret; + unsigned len; + + /* write string */ + len = (unsigned)strlen(str); + ret = gzwrite(file, str, len); + return ret == 0 && len != 0 ? -1 : ret; +} + +#ifdef STDC +#include + +/* -- see zlib.h -- */ +int ZEXPORTVA gzprintf (gzFile file, const char *format, ...) +{ + int size, len; + gz_statep state; + z_streamp strm; + va_list va; + + /* get internal structure */ + if (file == NULL) + return -1; + state = (gz_statep)file; + strm = &(state->strm); + + /* check that we're writing and that there's no error */ + if (state->mode != GZ_WRITE || state->err != Z_OK) + return 0; + + /* make sure we have some buffer space */ + if (state->size == 0 && gz_init(state) == -1) + return 0; + + /* check for seek request */ + if (state->seek) { + state->seek = 0; + if (gz_zero(state, state->skip) == -1) + return 0; + } + + /* consume whatever's left in the input buffer */ + if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1) + return 0; + + /* do the printf() into the input buffer, put length in len */ + size = (int)(state->size); + state->in[size - 1] = 0; + va_start(va, format); +#ifdef NO_vsnprintf +# ifdef HAS_vsprintf_void + (void)vsprintf(state->in, format, va); + va_end(va); + for (len = 0; len < size; len++) + if (state->in[len] == 0) break; +# else + len = vsprintf(state->in, format, va); + va_end(va); +# endif +#else +# ifdef HAS_vsnprintf_void + (void)vsnprintf(state->in, size, format, va); + va_end(va); + len = strlen(state->in); +# else + len = vsnprintf((char *)(state->in), size, format, va); + va_end(va); +# endif +#endif + + /* check that printf() results fit in buffer */ + if (len <= 0 || len >= (int)size || state->in[size - 1] != 0) + return 0; + + /* update buffer and position, defer compression until needed */ + strm->avail_in = (unsigned)len; + strm->next_in = state->in; + state->pos += len; + return len; +} + +#else /* !STDC */ + +/* -- see zlib.h -- */ +int ZEXPORTVA gzprintf (file, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, + a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) + gzFile file; + const char *format; + int a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, + a11, a12, a13, a14, a15, a16, a17, a18, a19, a20; +{ + int size, len; + gz_statep state; + z_streamp strm; + + /* get internal structure */ + if (file == NULL) + return -1; + state = (gz_statep)file; + strm = &(state->strm); + + /* check that we're writing and that there's no error */ + if (state->mode != GZ_WRITE || state->err != Z_OK) + return 0; + + /* make sure we have some buffer space */ + if (state->size == 0 && gz_init(state) == -1) + return 0; + + /* check for seek request */ + if (state->seek) { + state->seek = 0; + if (gz_zero(state, state->skip) == -1) + return 0; + } + + /* consume whatever's left in the input buffer */ + if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1) + return 0; + + /* do the printf() into the input buffer, put length in len */ + size = (int)(state->size); + state->in[size - 1] = 0; +#ifdef NO_snprintf +# ifdef HAS_sprintf_void + sprintf(state->in, format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); + for (len = 0; len < size; len++) + if (state->in[len] == 0) break; +# else + len = sprintf(state->in, format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); +# endif +#else +# ifdef HAS_snprintf_void + snprintf(state->in, size, format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); + len = strlen(state->in); +# else + len = snprintf(state->in, size, format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); +# endif +#endif + + /* check that printf() results fit in buffer */ + if (len <= 0 || len >= (int)size || state->in[size - 1] != 0) + return 0; + + /* update buffer and position, defer compression until needed */ + strm->avail_in = (unsigned)len; + strm->next_in = state->in; + state->pos += len; + return len; +} + +#endif + +/* -- see zlib.h -- */ +int ZEXPORT gzflush(file, flush) + gzFile file; + int flush; +{ + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return -1; + state = (gz_statep)file; + + /* check that we're writing and that there's no error */ + if (state->mode != GZ_WRITE || state->err != Z_OK) + return Z_STREAM_ERROR; + + /* check flush parameter */ + if (flush < 0 || flush > Z_FINISH) + return Z_STREAM_ERROR; + + /* check for seek request */ + if (state->seek) { + state->seek = 0; + if (gz_zero(state, state->skip) == -1) + return -1; + } + + /* compress remaining data with requested flush */ + gz_comp(state, flush); + return state->err; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzsetparams(file, level, strategy) + gzFile file; + int level; + int strategy; +{ + gz_statep state; + z_streamp strm; + + /* get internal structure */ + if (file == NULL) + return Z_STREAM_ERROR; + state = (gz_statep)file; + strm = &(state->strm); + + /* check that we're writing and that there's no error */ + if (state->mode != GZ_WRITE || state->err != Z_OK) + return Z_STREAM_ERROR; + + /* if no change is requested, then do nothing */ + if (level == state->level && strategy == state->strategy) + return Z_OK; + + /* check for seek request */ + if (state->seek) { + state->seek = 0; + if (gz_zero(state, state->skip) == -1) + return -1; + } + + /* change compression parameters for subsequent input */ + if (state->size) { + /* flush previous input with previous parameters before changing */ + if (strm->avail_in && gz_comp(state, Z_PARTIAL_FLUSH) == -1) + return state->err; + deflateParams(strm, level, strategy); + } + state->level = level; + state->strategy = strategy; + return Z_OK; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzclose_w(file) + gzFile file; +{ + int ret = 0; + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return Z_STREAM_ERROR; + state = (gz_statep)file; + + /* check that we're writing */ + if (state->mode != GZ_WRITE) + return Z_STREAM_ERROR; + + /* check for seek request */ + if (state->seek) { + state->seek = 0; + ret += gz_zero(state, state->skip); + } + + /* flush, free memory, and close file */ + ret += gz_comp(state, Z_FINISH); + (void)deflateEnd(&(state->strm)); + free(state->out); + free(state->in); + gz_error(state, Z_OK, NULL); + free(state->path); + ret += close(state->fd); + free(state); + return ret ? Z_ERRNO : Z_OK; +} diff --git a/pcsx2/IopIrq.cpp b/pcsx2/IopIrq.cpp index e0ffe21683..2b505a444a 100644 --- a/pcsx2/IopIrq.cpp +++ b/pcsx2/IopIrq.cpp @@ -1,66 +1,66 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2009 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" - -using namespace R3000A; - -void dev9Interrupt() -{ - if ((dev9Handler != NULL) && (dev9Handler() != 1)) return; - - iopIntcIrq(13); - hwIntcIrq(INTC_SBUS); -} - -void dev9Irq(int cycles) -{ - PSX_INT(IopEvt_DEV9, cycles); -} - -void usbInterrupt() -{ - if (usbHandler != NULL && (usbHandler() != 1)) return; - - iopIntcIrq(22); - hwIntcIrq(INTC_SBUS); -} - -void usbIrq(int cycles) -{ - PSX_INT(IopEvt_USB, cycles); -} - -void fwIrq() -{ - iopIntcIrq(24); - hwIntcIrq(INTC_SBUS); -} - -void spu2Irq() -{ - #ifdef SPU2IRQTEST - Console.Warning("spu2Irq"); - #endif - iopIntcIrq(9); - hwIntcIrq(INTC_SBUS); -} - -void iopIntcIrq(uint irqType) -{ - psxHu32(0x1070) |= 1 << irqType; - iopTestIntc(); -} +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2009 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" + +using namespace R3000A; + +void dev9Interrupt() +{ + if ((dev9Handler != NULL) && (dev9Handler() != 1)) return; + + iopIntcIrq(13); + hwIntcIrq(INTC_SBUS); +} + +void dev9Irq(int cycles) +{ + PSX_INT(IopEvt_DEV9, cycles); +} + +void usbInterrupt() +{ + if (usbHandler != NULL && (usbHandler() != 1)) return; + + iopIntcIrq(22); + hwIntcIrq(INTC_SBUS); +} + +void usbIrq(int cycles) +{ + PSX_INT(IopEvt_USB, cycles); +} + +void fwIrq() +{ + iopIntcIrq(24); + hwIntcIrq(INTC_SBUS); +} + +void spu2Irq() +{ + #ifdef SPU2IRQTEST + Console.Warning("spu2Irq"); + #endif + iopIntcIrq(9); + hwIntcIrq(INTC_SBUS); +} + +void iopIntcIrq(uint irqType) +{ + psxHu32(0x1070) |= 1 << irqType; + iopTestIntc(); +} diff --git a/pcsx2/Sif0.cpp b/pcsx2/Sif0.cpp index 4528415897..b4219a5203 100644 --- a/pcsx2/Sif0.cpp +++ b/pcsx2/Sif0.cpp @@ -1,328 +1,328 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2009 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" - -#define _PC_ // disables MIPS opcode macros. - -#include "IopCommon.h" -#include "Sif.h" - -_sif sif0; - -static bool done = false; - -static __forceinline void Sif0Init() -{ - SIF_LOG("SIF0 DMA start..."); - done = false; - sif0.ee.cycles = 0; - sif0.iop.cycles = 0; -} - -// Write from Fifo to EE. -static __forceinline bool WriteFifoToEE() -{ - const int readSize = min((s32)sif0dma->qwc, sif0.fifo.size >> 2); - - tDMA_TAG *ptag; - - //SIF_LOG(" EE SIF doing transfer %04Xqw to %08X", readSize, sif0dma->madr); - SIF_LOG("Write Fifo to EE: ----------- %lX of %lX", readSize << 2, sif0dma->qwc << 2); - - ptag = sif0dma->getAddr(sif0dma->madr, DMAC_SIF0, true); - if (ptag == NULL) - { - DevCon.Warning("Write Fifo to EE: ptag == NULL"); - return false; - } - - sif0.fifo.read((u32*)ptag, readSize << 2); - - // Clearing handled by vtlb memory protection and manual blocks. - //Cpu->Clear(sif0dma->madr, readSize*4); - - sif0dma->madr += readSize << 4; - sif0.ee.cycles += readSize; // fixme : BIAS is factored in above - sif0dma->qwc -= readSize; - - return true; -} - -// Write IOP to Fifo. -static __forceinline bool WriteIOPtoFifo() -{ - // There's some data ready to transfer into the fifo.. - const int writeSize = min(sif0.iop.counter, sif0.fifo.free()); - - SIF_LOG("Write IOP to Fifo: +++++++++++ %lX of %lX", writeSize, sif0.iop.counter); - - sif0.fifo.write((u32*)iopPhysMem(hw_dma(9).madr), writeSize); - hw_dma(9).madr += writeSize << 2; - - // iop is 1/8th the clock rate of the EE and psxcycles is in words (not quadwords). - sif0.iop.cycles += (writeSize >> 2) * BIAS; // fixme : should be >> 4 - sif0.iop.counter -= writeSize; - - return true; -} - -// Read Fifo into an ee tag, transfer it to sif0dma, and process it. -static __forceinline bool ProcessEETag() -{ - static __aligned16 u32 tag[4]; - - sif0.fifo.read((u32*)&tag[0], 4); // Tag - SIF_LOG("SIF0 EE read tag: %x %x %x %x", tag[0], tag[1], tag[2], tag[3]); - - sif0dma->unsafeTransfer(((tDMA_TAG*)(tag))); - sif0dma->madr = tag[1]; - tDMA_TAG ptag(tag[0]); - - SIF_LOG("SIF0 EE dest chain tag madr:%08X qwc:%04X id:%X irq:%d(%08X_%08X)", - sif0dma->madr, sif0dma->qwc, ptag.ID, ptag.IRQ, tag[1], tag[0]); - - if (sif0dma->chcr.TIE && ptag.IRQ) - { - //Console.WriteLn("SIF0 TIE"); - sif0.ee.end = true; - } - - switch (ptag.ID) - { - case TAG_REFE: - sif0.ee.end = true; - if (dmacRegs->ctrl.STS != NO_STS) - dmacRegs->stadr.ADDR = sif0dma->madr + (sif0dma->qwc * 16); - break; - - case TAG_REFS: - if (dmacRegs->ctrl.STS != NO_STS) - dmacRegs->stadr.ADDR = sif0dma->madr + (sif0dma->qwc * 16); - break; - - case TAG_END: - sif0.ee.end = true; - break; - } - return true; -} - -// Read Fifo into an iop tag, and transfer it to hw_dma(9). And presumably process it. -static __forceinline bool ProcessIOPTag() -{ - // Process DMA tag at hw_dma(9).tadr - sif0.iop.data = *(sifData *)iopPhysMem(hw_dma(9).tadr); - sif0.iop.data.words = (sif0.iop.data.words + 3) & 0xfffffffc; // Round up to nearest 4. - sif0.fifo.write((u32*)iopPhysMem(hw_dma(9).tadr + 8), 4); - - hw_dma(9).tadr += 16; ///hw_dma(9).madr + 16 + sif0.sifData.words << 2; - - // We're only copying the first 24 bits. - hw_dma(9).madr = sif0data & 0xFFFFFF; - sif0.iop.counter = sif0words; - - if (sif0tag.IRQ || (sif0tag.ID & 4)) sif0.iop.end = true; - SIF_LOG("SIF0 IOP Tag: madr=%lx, tadr=%lx, counter=%lx (%08X_%08X)", hw_dma(9).madr, hw_dma(9).tadr, sif0.iop.counter, sif0words, sif0data); - - return true; -} - -// Stop transferring ee, and signal an interrupt. -static __forceinline void EndEE() -{ - SIF_LOG("Sif0: End EE"); - sif0.ee.end = false; - sif0.ee.busy = false; - if (sif0.ee.cycles == 0) - { - SIF_LOG("SIF0 EE: cycles = 0"); - sif0.ee.cycles = 1; - } - - CPU_INT(DMAC_SIF0, sif0.ee.cycles*BIAS); -} - -// Stop transferring iop, and signal an interrupt. -static __forceinline void EndIOP() -{ - SIF_LOG("Sif0: End IOP"); - sif0data = 0; - sif0.iop.end = false; - sif0.iop.busy = false; - - if (sif0.iop.cycles == 0) - { - DevCon.Warning("SIF0 IOP: cycles = 0"); - sif0.iop.cycles = 1; - } - // iop is 1/8th the clock rate of the EE and psxcycles is in words (not quadwords) - // So when we're all done, the equation looks like thus: - //PSX_INT(IopEvt_SIF0, ( ( sif0.iop.cycles*BIAS ) / 4 ) / 8); - PSX_INT(IopEvt_SIF0, sif0.iop.cycles); -} - -// Handle the EE transfer. -static __forceinline void HandleEETransfer() -{ - if (dmacRegs->ctrl.STS == STS_SIF0) - { - DevCon.Warning("SIF0 stall control"); - } - - if (sif0dma->qwc == 0) - if (sif0dma->chcr.MOD == NORMAL_MODE) - if (!sif0.ee.end){ - DevCon.Warning("sif0 irq prevented"); - done = true; - return; - } - - if (sif0dma->qwc <= 0) - { - if ((sif0dma->chcr.MOD == NORMAL_MODE) || sif0.ee.end) - { - // Stop transferring ee, and signal an interrupt. - done = true; - EndEE(); - } - else if (sif0.fifo.size >= 4) // Read a tag - { - // Read Fifo into an ee tag, transfer it to sif0dma - // and process it. - ProcessEETag(); - } - } - - if (sif0dma->qwc > 0) // If we're writing something, continue to do so. - { - // Write from Fifo to EE. - if (sif0.fifo.size > 0) - { - WriteFifoToEE(); - } - } -} - -// Handle the IOP transfer. -// Note: Test any changes in this function against Grandia III. -// What currently happens is this: -// SIF0 DMA start... -// SIF + 4 = 4 (pos=4) -// SIF0 IOP Tag: madr=19870, tadr=179cc, counter=8 (00000008_80019870) -// SIF - 4 = 0 (pos=4) -// SIF0 EE read tag: 90000002 935c0 0 0 -// SIF0 EE dest chain tag madr:000935C0 qwc:0002 id:1 irq:1(000935C0_90000002) -// Write Fifo to EE: ----------- 0 of 8 -// SIF - 0 = 0 (pos=4) -// Write IOP to Fifo: +++++++++++ 8 of 8 -// SIF + 8 = 8 (pos=12) -// Write Fifo to EE: ----------- 8 of 8 -// SIF - 8 = 0 (pos=12) -// Sif0: End IOP -// Sif0: End EE -// SIF0 DMA end... - -// What happens if (sif0.iop.counter > 0) is handled first is this - -// SIF0 DMA start... -// ... -// SIF + 8 = 8 (pos=12) -// Sif0: End IOP -// Write Fifo to EE: ----------- 8 of 8 -// SIF - 8 = 0 (pos=12) -// SIF0 DMA end... - -static __forceinline void HandleIOPTransfer() -{ - if (sif0.iop.counter <= 0) // If there's no more to transfer - { - if (sif0.iop.end) - { - // Stop transferring iop, and signal an interrupt. - done = true; - EndIOP(); - } - else - { - // Read Fifo into an iop tag, and transfer it to hw_dma(9). - // And presumably process it. - ProcessIOPTag(); - } - } - else - { - // Write IOP to Fifo. - if (sif0.fifo.free() > 0) - { - WriteIOPtoFifo(); - } - } -} - -static __forceinline void Sif0End() -{ - SIF_LOG("SIF0 DMA end..."); -} - -// Transfer IOP to EE, putting data in the fifo as an intermediate step. -__forceinline void SIF0Dma() -{ - Sif0Init(); - - do - { - if (sif0.iop.busy) HandleIOPTransfer(); - if (sif0.ee.busy) HandleEETransfer(); - } while (!done); // Substituting (sif0.ee.busy || sif0.iop.busy) breaks things. - - Sif0End(); -} - -__forceinline void sif0Interrupt() -{ - HW_DMA9_CHCR &= ~0x01000000; - psxDmaInterrupt2(2); -} - -__forceinline void EEsif0Interrupt() -{ - hwDmacIrq(DMAC_SIF0); - sif0dma->chcr.STR = false; -} - -__forceinline void dmaSIF0() -{ - SIF_LOG(wxString(L"dmaSIF0" + sif0dma->cmqt_to_str()).To8BitData()); - - if (sif0.fifo.readPos != sif0.fifo.writePos) - { - SIF_LOG("warning, sif0.fifoReadPos != sif0.fifoWritePos"); - } - - if(sif0dma->chcr.MOD == CHAIN_MODE && sif0dma->qwc > 0) DevCon.Warning(L"SIF0 QWC on Chain CHCR " + sif0dma->chcr.desc()); - psHu32(SBUS_F240) |= 0x2000; - sif0.ee.busy = true; - - if (sif0.iop.busy) - { - XMMRegisters::Freeze(); - hwIntcIrq(INTC_SBUS); - SIF0Dma(); - psHu32(SBUS_F240) &= ~0x20; - psHu32(SBUS_F240) &= ~0x2000; - XMMRegisters::Thaw(); - } -} +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2009 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" + +#define _PC_ // disables MIPS opcode macros. + +#include "IopCommon.h" +#include "Sif.h" + +_sif sif0; + +static bool done = false; + +static __forceinline void Sif0Init() +{ + SIF_LOG("SIF0 DMA start..."); + done = false; + sif0.ee.cycles = 0; + sif0.iop.cycles = 0; +} + +// Write from Fifo to EE. +static __forceinline bool WriteFifoToEE() +{ + const int readSize = min((s32)sif0dma->qwc, sif0.fifo.size >> 2); + + tDMA_TAG *ptag; + + //SIF_LOG(" EE SIF doing transfer %04Xqw to %08X", readSize, sif0dma->madr); + SIF_LOG("Write Fifo to EE: ----------- %lX of %lX", readSize << 2, sif0dma->qwc << 2); + + ptag = sif0dma->getAddr(sif0dma->madr, DMAC_SIF0, true); + if (ptag == NULL) + { + DevCon.Warning("Write Fifo to EE: ptag == NULL"); + return false; + } + + sif0.fifo.read((u32*)ptag, readSize << 2); + + // Clearing handled by vtlb memory protection and manual blocks. + //Cpu->Clear(sif0dma->madr, readSize*4); + + sif0dma->madr += readSize << 4; + sif0.ee.cycles += readSize; // fixme : BIAS is factored in above + sif0dma->qwc -= readSize; + + return true; +} + +// Write IOP to Fifo. +static __forceinline bool WriteIOPtoFifo() +{ + // There's some data ready to transfer into the fifo.. + const int writeSize = min(sif0.iop.counter, sif0.fifo.free()); + + SIF_LOG("Write IOP to Fifo: +++++++++++ %lX of %lX", writeSize, sif0.iop.counter); + + sif0.fifo.write((u32*)iopPhysMem(hw_dma(9).madr), writeSize); + hw_dma(9).madr += writeSize << 2; + + // iop is 1/8th the clock rate of the EE and psxcycles is in words (not quadwords). + sif0.iop.cycles += (writeSize >> 2) * BIAS; // fixme : should be >> 4 + sif0.iop.counter -= writeSize; + + return true; +} + +// Read Fifo into an ee tag, transfer it to sif0dma, and process it. +static __forceinline bool ProcessEETag() +{ + static __aligned16 u32 tag[4]; + + sif0.fifo.read((u32*)&tag[0], 4); // Tag + SIF_LOG("SIF0 EE read tag: %x %x %x %x", tag[0], tag[1], tag[2], tag[3]); + + sif0dma->unsafeTransfer(((tDMA_TAG*)(tag))); + sif0dma->madr = tag[1]; + tDMA_TAG ptag(tag[0]); + + SIF_LOG("SIF0 EE dest chain tag madr:%08X qwc:%04X id:%X irq:%d(%08X_%08X)", + sif0dma->madr, sif0dma->qwc, ptag.ID, ptag.IRQ, tag[1], tag[0]); + + if (sif0dma->chcr.TIE && ptag.IRQ) + { + //Console.WriteLn("SIF0 TIE"); + sif0.ee.end = true; + } + + switch (ptag.ID) + { + case TAG_REFE: + sif0.ee.end = true; + if (dmacRegs->ctrl.STS != NO_STS) + dmacRegs->stadr.ADDR = sif0dma->madr + (sif0dma->qwc * 16); + break; + + case TAG_REFS: + if (dmacRegs->ctrl.STS != NO_STS) + dmacRegs->stadr.ADDR = sif0dma->madr + (sif0dma->qwc * 16); + break; + + case TAG_END: + sif0.ee.end = true; + break; + } + return true; +} + +// Read Fifo into an iop tag, and transfer it to hw_dma(9). And presumably process it. +static __forceinline bool ProcessIOPTag() +{ + // Process DMA tag at hw_dma(9).tadr + sif0.iop.data = *(sifData *)iopPhysMem(hw_dma(9).tadr); + sif0.iop.data.words = (sif0.iop.data.words + 3) & 0xfffffffc; // Round up to nearest 4. + sif0.fifo.write((u32*)iopPhysMem(hw_dma(9).tadr + 8), 4); + + hw_dma(9).tadr += 16; ///hw_dma(9).madr + 16 + sif0.sifData.words << 2; + + // We're only copying the first 24 bits. + hw_dma(9).madr = sif0data & 0xFFFFFF; + sif0.iop.counter = sif0words; + + if (sif0tag.IRQ || (sif0tag.ID & 4)) sif0.iop.end = true; + SIF_LOG("SIF0 IOP Tag: madr=%lx, tadr=%lx, counter=%lx (%08X_%08X)", hw_dma(9).madr, hw_dma(9).tadr, sif0.iop.counter, sif0words, sif0data); + + return true; +} + +// Stop transferring ee, and signal an interrupt. +static __forceinline void EndEE() +{ + SIF_LOG("Sif0: End EE"); + sif0.ee.end = false; + sif0.ee.busy = false; + if (sif0.ee.cycles == 0) + { + SIF_LOG("SIF0 EE: cycles = 0"); + sif0.ee.cycles = 1; + } + + CPU_INT(DMAC_SIF0, sif0.ee.cycles*BIAS); +} + +// Stop transferring iop, and signal an interrupt. +static __forceinline void EndIOP() +{ + SIF_LOG("Sif0: End IOP"); + sif0data = 0; + sif0.iop.end = false; + sif0.iop.busy = false; + + if (sif0.iop.cycles == 0) + { + DevCon.Warning("SIF0 IOP: cycles = 0"); + sif0.iop.cycles = 1; + } + // iop is 1/8th the clock rate of the EE and psxcycles is in words (not quadwords) + // So when we're all done, the equation looks like thus: + //PSX_INT(IopEvt_SIF0, ( ( sif0.iop.cycles*BIAS ) / 4 ) / 8); + PSX_INT(IopEvt_SIF0, sif0.iop.cycles); +} + +// Handle the EE transfer. +static __forceinline void HandleEETransfer() +{ + if (dmacRegs->ctrl.STS == STS_SIF0) + { + DevCon.Warning("SIF0 stall control"); + } + + if (sif0dma->qwc == 0) + if (sif0dma->chcr.MOD == NORMAL_MODE) + if (!sif0.ee.end){ + DevCon.Warning("sif0 irq prevented"); + done = true; + return; + } + + if (sif0dma->qwc <= 0) + { + if ((sif0dma->chcr.MOD == NORMAL_MODE) || sif0.ee.end) + { + // Stop transferring ee, and signal an interrupt. + done = true; + EndEE(); + } + else if (sif0.fifo.size >= 4) // Read a tag + { + // Read Fifo into an ee tag, transfer it to sif0dma + // and process it. + ProcessEETag(); + } + } + + if (sif0dma->qwc > 0) // If we're writing something, continue to do so. + { + // Write from Fifo to EE. + if (sif0.fifo.size > 0) + { + WriteFifoToEE(); + } + } +} + +// Handle the IOP transfer. +// Note: Test any changes in this function against Grandia III. +// What currently happens is this: +// SIF0 DMA start... +// SIF + 4 = 4 (pos=4) +// SIF0 IOP Tag: madr=19870, tadr=179cc, counter=8 (00000008_80019870) +// SIF - 4 = 0 (pos=4) +// SIF0 EE read tag: 90000002 935c0 0 0 +// SIF0 EE dest chain tag madr:000935C0 qwc:0002 id:1 irq:1(000935C0_90000002) +// Write Fifo to EE: ----------- 0 of 8 +// SIF - 0 = 0 (pos=4) +// Write IOP to Fifo: +++++++++++ 8 of 8 +// SIF + 8 = 8 (pos=12) +// Write Fifo to EE: ----------- 8 of 8 +// SIF - 8 = 0 (pos=12) +// Sif0: End IOP +// Sif0: End EE +// SIF0 DMA end... + +// What happens if (sif0.iop.counter > 0) is handled first is this + +// SIF0 DMA start... +// ... +// SIF + 8 = 8 (pos=12) +// Sif0: End IOP +// Write Fifo to EE: ----------- 8 of 8 +// SIF - 8 = 0 (pos=12) +// SIF0 DMA end... + +static __forceinline void HandleIOPTransfer() +{ + if (sif0.iop.counter <= 0) // If there's no more to transfer + { + if (sif0.iop.end) + { + // Stop transferring iop, and signal an interrupt. + done = true; + EndIOP(); + } + else + { + // Read Fifo into an iop tag, and transfer it to hw_dma(9). + // And presumably process it. + ProcessIOPTag(); + } + } + else + { + // Write IOP to Fifo. + if (sif0.fifo.free() > 0) + { + WriteIOPtoFifo(); + } + } +} + +static __forceinline void Sif0End() +{ + SIF_LOG("SIF0 DMA end..."); +} + +// Transfer IOP to EE, putting data in the fifo as an intermediate step. +__forceinline void SIF0Dma() +{ + Sif0Init(); + + do + { + if (sif0.iop.busy) HandleIOPTransfer(); + if (sif0.ee.busy) HandleEETransfer(); + } while (!done); // Substituting (sif0.ee.busy || sif0.iop.busy) breaks things. + + Sif0End(); +} + +__forceinline void sif0Interrupt() +{ + HW_DMA9_CHCR &= ~0x01000000; + psxDmaInterrupt2(2); +} + +__forceinline void EEsif0Interrupt() +{ + hwDmacIrq(DMAC_SIF0); + sif0dma->chcr.STR = false; +} + +__forceinline void dmaSIF0() +{ + SIF_LOG(wxString(L"dmaSIF0" + sif0dma->cmqt_to_str()).To8BitData()); + + if (sif0.fifo.readPos != sif0.fifo.writePos) + { + SIF_LOG("warning, sif0.fifoReadPos != sif0.fifoWritePos"); + } + + if(sif0dma->chcr.MOD == CHAIN_MODE && sif0dma->qwc > 0) DevCon.Warning(L"SIF0 QWC on Chain CHCR " + sif0dma->chcr.desc()); + psHu32(SBUS_F240) |= 0x2000; + sif0.ee.busy = true; + + if (sif0.iop.busy) + { + XMMRegisters::Freeze(); + hwIntcIrq(INTC_SBUS); + SIF0Dma(); + psHu32(SBUS_F240) &= ~0x20; + psHu32(SBUS_F240) &= ~0x2000; + XMMRegisters::Thaw(); + } +} diff --git a/pcsx2/VUmicro.cpp b/pcsx2/VUmicro.cpp index 2ed4b0d2b9..3ece600966 100644 --- a/pcsx2/VUmicro.cpp +++ b/pcsx2/VUmicro.cpp @@ -1,102 +1,102 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2009 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 "Common.h" -#include "VUmicro.h" - -#define useDeltaTime 1 - -#if useDeltaTime - -// Executes a Block based on EE delta time -void BaseVUmicroCPU::ExecuteBlock(bool startUp) { - const u32& stat = VU0.VI[REG_VPU_STAT].UL; - const int test = m_Idx ? 0x100 : 1; - const int s = 1024*6; // Kick Start Cycles (Silver Surfer needs this amount) - const int c = 1024*1; // Continue Cycles - if (!(stat & test)) return; - if (startUp) { // Start Executing a microprogram - Execute(s); // Kick start VU - - // Let VUs run behind EE instead of ahead - if (stat & test) { - cpuSetNextBranchDelta((s+c)*2); - m_lastEEcycles = cpuRegs.cycle + (s*2); - } - } - else { // Continue Executing (VU roughly half the mhz of EE) - s32 delta = (s32)(u32)(cpuRegs.cycle - m_lastEEcycles) & ~1; - if (delta > 0) { // Enough time has passed - delta >>= 1; // Divide by 2 (unsigned) - Execute(delta); // Execute the time since the last call - if (stat & test) { - cpuSetNextBranchDelta(c*2); - m_lastEEcycles = cpuRegs.cycle; - } - } - else cpuSetNextBranchDelta(-delta); // Haven't caught-up from kick start - } -} - -// This function is called by VU0 Macro (COP2) after transferring some -// EE data to VU0's registers. We want to run VU0 Micro right after this -// to ensure that the register is used at the correct time. -// This fixes spinning/hanging in some games like Ratchet and Clank's Intro. -void __fastcall BaseVUmicroCPU::ExecuteBlockJIT(BaseVUmicroCPU* cpu) { - const u32& stat = VU0.VI[REG_VPU_STAT].UL; - const int test = cpu->m_Idx ? 0x100 : 1; - const int c = 128; // VU Execution Cycles - if (stat & test) { // VU is running - cpu->Execute(c); // Execute VU - if (stat & test) { - cpu->m_lastEEcycles+=(c*2); - cpuSetNextBranchDelta(c*2); - } - } -} - -#else - -// Executes a Block based on static preset cycles -void BaseVUmicroCPU::ExecuteBlock(bool startUp) { - const int vuRunning = m_Idx ? 0x100 : 1; - if (!(VU0.VI[REG_VPU_STAT].UL & vuRunning)) return; - if (startUp) { // Start Executing a microprogram - Execute(vu0RunCycles); // Kick start VU - - // If the VU0 program didn't finish then we'll want to finish it up - // pretty soon. This fixes vmhacks in some games (Naruto Ultimate Ninja 2) - if(VU0.VI[REG_VPU_STAT].UL & vuRunning) - cpuSetNextBranchDelta( 192 ); // fixme : ideally this should be higher, like 512 or so. - } - else { - Execute(vu0RunCycles); - //DevCon.Warning("VU0 running when in BranchTest"); - - // This helps keep the EE and VU0 in sync. - // Check Silver Surfer. Currently has SPS varying with different branch deltas set below. - if(VU0.VI[REG_VPU_STAT].UL & vuRunning) - cpuSetNextBranchDelta( 768 ); - } -} - -// This function is called by VU0 Macro (COP2) after transferring -// EE data to a VU0 register... -void __fastcall BaseVUmicroCPU::ExecuteBlockJIT(BaseVUmicroCPU* cpu) { - cpu->Execute(128); -} - -#endif +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2009 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 "Common.h" +#include "VUmicro.h" + +#define useDeltaTime 1 + +#if useDeltaTime + +// Executes a Block based on EE delta time +void BaseVUmicroCPU::ExecuteBlock(bool startUp) { + const u32& stat = VU0.VI[REG_VPU_STAT].UL; + const int test = m_Idx ? 0x100 : 1; + const int s = 1024*6; // Kick Start Cycles (Silver Surfer needs this amount) + const int c = 1024*1; // Continue Cycles + if (!(stat & test)) return; + if (startUp) { // Start Executing a microprogram + Execute(s); // Kick start VU + + // Let VUs run behind EE instead of ahead + if (stat & test) { + cpuSetNextBranchDelta((s+c)*2); + m_lastEEcycles = cpuRegs.cycle + (s*2); + } + } + else { // Continue Executing (VU roughly half the mhz of EE) + s32 delta = (s32)(u32)(cpuRegs.cycle - m_lastEEcycles) & ~1; + if (delta > 0) { // Enough time has passed + delta >>= 1; // Divide by 2 (unsigned) + Execute(delta); // Execute the time since the last call + if (stat & test) { + cpuSetNextBranchDelta(c*2); + m_lastEEcycles = cpuRegs.cycle; + } + } + else cpuSetNextBranchDelta(-delta); // Haven't caught-up from kick start + } +} + +// This function is called by VU0 Macro (COP2) after transferring some +// EE data to VU0's registers. We want to run VU0 Micro right after this +// to ensure that the register is used at the correct time. +// This fixes spinning/hanging in some games like Ratchet and Clank's Intro. +void __fastcall BaseVUmicroCPU::ExecuteBlockJIT(BaseVUmicroCPU* cpu) { + const u32& stat = VU0.VI[REG_VPU_STAT].UL; + const int test = cpu->m_Idx ? 0x100 : 1; + const int c = 128; // VU Execution Cycles + if (stat & test) { // VU is running + cpu->Execute(c); // Execute VU + if (stat & test) { + cpu->m_lastEEcycles+=(c*2); + cpuSetNextBranchDelta(c*2); + } + } +} + +#else + +// Executes a Block based on static preset cycles +void BaseVUmicroCPU::ExecuteBlock(bool startUp) { + const int vuRunning = m_Idx ? 0x100 : 1; + if (!(VU0.VI[REG_VPU_STAT].UL & vuRunning)) return; + if (startUp) { // Start Executing a microprogram + Execute(vu0RunCycles); // Kick start VU + + // If the VU0 program didn't finish then we'll want to finish it up + // pretty soon. This fixes vmhacks in some games (Naruto Ultimate Ninja 2) + if(VU0.VI[REG_VPU_STAT].UL & vuRunning) + cpuSetNextBranchDelta( 192 ); // fixme : ideally this should be higher, like 512 or so. + } + else { + Execute(vu0RunCycles); + //DevCon.Warning("VU0 running when in BranchTest"); + + // This helps keep the EE and VU0 in sync. + // Check Silver Surfer. Currently has SPS varying with different branch deltas set below. + if(VU0.VI[REG_VPU_STAT].UL & vuRunning) + cpuSetNextBranchDelta( 768 ); + } +} + +// This function is called by VU0 Macro (COP2) after transferring +// EE data to a VU0 register... +void __fastcall BaseVUmicroCPU::ExecuteBlockJIT(BaseVUmicroCPU* cpu) { + cpu->Execute(128); +} + +#endif diff --git a/pcsx2/Vif0_Dma.cpp b/pcsx2/Vif0_Dma.cpp index 1c90dbf2c6..55f8d9b6bd 100644 --- a/pcsx2/Vif0_Dma.cpp +++ b/pcsx2/Vif0_Dma.cpp @@ -1,220 +1,220 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2009 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 "Common.h" -#include "Vif_Dma.h" -#include "VUmicro.h" -#include "newVif.h" - -// Run VU0 until finish, don't add cycles to EE -// because its vif stalling not the EE core... -__forceinline void vif0FLUSH() -{ - if (!(VU0.VI[REG_VPU_STAT].UL & 1)) return; - int _cycles = VU0.cycle; - vu0Finish(); - g_vifCycles += (VU0.cycle - _cycles) * BIAS; -} - -bool _VIF0chain() -{ - u32 *pMem; - - if (vif0ch->qwc == 0) - { - vif0.inprogress = 0; - return true; - } - - pMem = (u32*)dmaGetAddr(vif0ch->madr, false); - if (pMem == NULL) - { - vif0.cmd = 0; - vif0.tag.size = 0; - vif0ch->qwc = 0; - return true; - } - - VIF_LOG("VIF0chain size=%d, madr=%lx, tadr=%lx", - vif0ch->qwc, vif0ch->madr, vif0ch->tadr); - - if (vif0.vifstalled) - return VIF0transfer(pMem + vif0.irqoffset, vif0ch->qwc * 4 - vif0.irqoffset, false); - else - return VIF0transfer(pMem, vif0ch->qwc * 4, false); -} - -__forceinline void vif0SetupTransfer() -{ - tDMA_TAG *ptag; - - switch (vif0.dmamode) - { - case VIF_NORMAL_TO_MEM_MODE: - vif0.inprogress = 1; - vif0.done = true; - g_vifCycles = 2; - break; - - case VIF_CHAIN_MODE: - ptag = dmaGetAddr(vif0ch->tadr, false); //Set memory pointer to TADR - - if (!(vif0ch->transfer("vif0 Tag", ptag))) return; - - vif0ch->madr = ptag[1]._u32; //MADR = ADDR field + SPR - g_vifCycles += 1; // Add 1 g_vifCycles from the QW read for the tag - - // Transfer dma tag if tte is set - - VIF_LOG("vif0 Tag %8.8x_%8.8x size=%d, id=%d, madr=%lx, tadr=%lx\n", - ptag[1]._u32, ptag[0]._u32, vif0ch->qwc, ptag->ID, vif0ch->madr, vif0ch->tadr); - - vif0.inprogress = 1; - - if (vif0ch->chcr.TTE) - { - bool ret; - - if (vif0.vifstalled) - ret = VIF0transfer((u32*)ptag + (2 + vif0.irqoffset), 2 - vif0.irqoffset, true); //Transfer Tag on stall - else - ret = VIF0transfer((u32*)ptag + 2, 2, true); //Transfer Tag - - if ((ret == false) && vif0.irqoffset < 2) - { - vif0.inprogress = 0; //Better clear this so it has to do it again (Jak 1) - return; //There has been an error or an interrupt - } - } - - vif0.irqoffset = 0; - vif0.done |= hwDmacSrcChainWithStack(vif0ch, ptag->ID); - - //Check TIE bit of CHCR and IRQ bit of tag - if (vif0ch->chcr.TIE && ptag->IRQ) - { - VIF_LOG("dmaIrq Set"); - - //End Transfer - vif0.done = true; - return; - } - break; - } -} - -__forceinline void vif0Interrupt() -{ - VIF_LOG("vif0Interrupt: %8.8x", cpuRegs.cycle); - - g_vifCycles = 0; - - if (!(vif0ch->chcr.STR)) Console.WriteLn("vif0 running when CHCR == %x", vif0ch->chcr._u32); - - if (vif0.irq && vif0.tag.size == 0) - { - vif0Regs->stat.INT = true; - hwIntcIrq(VIF0intc); - --vif0.irq; - if (vif0Regs->stat.test(VIF0_STAT_VSS | VIF0_STAT_VIS | VIF0_STAT_VFS)) - { - vif0Regs->stat.FQC = 0; - - // One game doesn't like vif stalling at end, can't remember what. Spiderman isn't keen on it tho - vif0ch->chcr.STR = false; - return; - } - else if ((vif0ch->qwc > 0) || (vif0.irqoffset > 0)) - { - if (vif0.stallontag) - vif0SetupTransfer(); - else - _VIF0chain();//CPU_INT(DMAC_STALL_SIS, vif0ch->qwc * BIAS); - } - } - - if (vif0.inprogress & 0x1) - { - _VIF0chain(); - // VIF_NORMAL_FROM_MEM_MODE is a very slow operation. - // Timesplitters 2 depends on this beeing a bit higher than 128. - if (vif0.dmamode == VIF_NORMAL_FROM_MEM_MODE ) CPU_INT(DMAC_VIF0, 1024); - else CPU_INT(DMAC_VIF0, g_vifCycles); - return; - } - - if (!vif0.done) - { - - if (!(dmacRegs->ctrl.DMAE)) - { - Console.WriteLn("vif0 dma masked"); - return; - } - - if ((vif0.inprogress & 0x1) == 0) vif0SetupTransfer(); - - CPU_INT(DMAC_VIF0, g_vifCycles); - return; - } - - if (vif0.vifstalled && vif0.irq) - { - DevCon.WriteLn("VIF0 looping on stall\n"); - CPU_INT(DMAC_VIF0, 0); - return; //Dont want to end if vif is stalled. - } -#ifdef PCSX2_DEVBUILD - if (vif0ch->qwc > 0) Console.WriteLn("vif0 Ending with %x QWC left"); - if (vif0.cmd != 0) Console.WriteLn("vif0.cmd still set %x tag size %x", vif0.cmd, vif0.tag.size); -#endif - - vif0Regs->stat.VPS = VPS_IDLE; //Vif goes idle as the stall happened between commands; - vif0ch->chcr.STR = false; - g_vifCycles = 0; - hwDmacIrq(DMAC_VIF0); - vif0Regs->stat.FQC = 0; -} - -void dmaVIF0() -{ - VIF_LOG("dmaVIF0 chcr = %lx, madr = %lx, qwc = %lx\n" - " tadr = %lx, asr0 = %lx, asr1 = %lx", - vif0ch->chcr._u32, vif0ch->madr, vif0ch->qwc, - vif0ch->tadr, vif0ch->asr0, vif0ch->asr1); - - g_vifCycles = 0; - vif0.inprogress = 0; - - if ((vif0ch->chcr.MOD == NORMAL_MODE) || vif0ch->qwc > 0) // Normal Mode - { - vif0.dmamode = VIF_NORMAL_TO_MEM_MODE; - if(vif0ch->chcr.MOD == CHAIN_MODE && vif0ch->qwc > 0) DevCon.Warning(L"VIF0 QWC on Chain CHCR " + vif0ch->chcr.desc()); - } - else - { - vif0.dmamode = VIF_CHAIN_MODE; - } - - if (vif0.dmamode != VIF_NORMAL_FROM_MEM_MODE) - vif0Regs->stat.FQC = 0x8; - else - vif0Regs->stat.FQC = min((u16)0x8, vif0ch->qwc); - - // Chain Mode - vif0.done = false; - vif0Interrupt(); +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2009 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 "Common.h" +#include "Vif_Dma.h" +#include "VUmicro.h" +#include "newVif.h" + +// Run VU0 until finish, don't add cycles to EE +// because its vif stalling not the EE core... +__forceinline void vif0FLUSH() +{ + if (!(VU0.VI[REG_VPU_STAT].UL & 1)) return; + int _cycles = VU0.cycle; + vu0Finish(); + g_vifCycles += (VU0.cycle - _cycles) * BIAS; +} + +bool _VIF0chain() +{ + u32 *pMem; + + if (vif0ch->qwc == 0) + { + vif0.inprogress = 0; + return true; + } + + pMem = (u32*)dmaGetAddr(vif0ch->madr, false); + if (pMem == NULL) + { + vif0.cmd = 0; + vif0.tag.size = 0; + vif0ch->qwc = 0; + return true; + } + + VIF_LOG("VIF0chain size=%d, madr=%lx, tadr=%lx", + vif0ch->qwc, vif0ch->madr, vif0ch->tadr); + + if (vif0.vifstalled) + return VIF0transfer(pMem + vif0.irqoffset, vif0ch->qwc * 4 - vif0.irqoffset, false); + else + return VIF0transfer(pMem, vif0ch->qwc * 4, false); +} + +__forceinline void vif0SetupTransfer() +{ + tDMA_TAG *ptag; + + switch (vif0.dmamode) + { + case VIF_NORMAL_TO_MEM_MODE: + vif0.inprogress = 1; + vif0.done = true; + g_vifCycles = 2; + break; + + case VIF_CHAIN_MODE: + ptag = dmaGetAddr(vif0ch->tadr, false); //Set memory pointer to TADR + + if (!(vif0ch->transfer("vif0 Tag", ptag))) return; + + vif0ch->madr = ptag[1]._u32; //MADR = ADDR field + SPR + g_vifCycles += 1; // Add 1 g_vifCycles from the QW read for the tag + + // Transfer dma tag if tte is set + + VIF_LOG("vif0 Tag %8.8x_%8.8x size=%d, id=%d, madr=%lx, tadr=%lx\n", + ptag[1]._u32, ptag[0]._u32, vif0ch->qwc, ptag->ID, vif0ch->madr, vif0ch->tadr); + + vif0.inprogress = 1; + + if (vif0ch->chcr.TTE) + { + bool ret; + + if (vif0.vifstalled) + ret = VIF0transfer((u32*)ptag + (2 + vif0.irqoffset), 2 - vif0.irqoffset, true); //Transfer Tag on stall + else + ret = VIF0transfer((u32*)ptag + 2, 2, true); //Transfer Tag + + if ((ret == false) && vif0.irqoffset < 2) + { + vif0.inprogress = 0; //Better clear this so it has to do it again (Jak 1) + return; //There has been an error or an interrupt + } + } + + vif0.irqoffset = 0; + vif0.done |= hwDmacSrcChainWithStack(vif0ch, ptag->ID); + + //Check TIE bit of CHCR and IRQ bit of tag + if (vif0ch->chcr.TIE && ptag->IRQ) + { + VIF_LOG("dmaIrq Set"); + + //End Transfer + vif0.done = true; + return; + } + break; + } +} + +__forceinline void vif0Interrupt() +{ + VIF_LOG("vif0Interrupt: %8.8x", cpuRegs.cycle); + + g_vifCycles = 0; + + if (!(vif0ch->chcr.STR)) Console.WriteLn("vif0 running when CHCR == %x", vif0ch->chcr._u32); + + if (vif0.irq && vif0.tag.size == 0) + { + vif0Regs->stat.INT = true; + hwIntcIrq(VIF0intc); + --vif0.irq; + if (vif0Regs->stat.test(VIF0_STAT_VSS | VIF0_STAT_VIS | VIF0_STAT_VFS)) + { + vif0Regs->stat.FQC = 0; + + // One game doesn't like vif stalling at end, can't remember what. Spiderman isn't keen on it tho + vif0ch->chcr.STR = false; + return; + } + else if ((vif0ch->qwc > 0) || (vif0.irqoffset > 0)) + { + if (vif0.stallontag) + vif0SetupTransfer(); + else + _VIF0chain();//CPU_INT(DMAC_STALL_SIS, vif0ch->qwc * BIAS); + } + } + + if (vif0.inprogress & 0x1) + { + _VIF0chain(); + // VIF_NORMAL_FROM_MEM_MODE is a very slow operation. + // Timesplitters 2 depends on this beeing a bit higher than 128. + if (vif0.dmamode == VIF_NORMAL_FROM_MEM_MODE ) CPU_INT(DMAC_VIF0, 1024); + else CPU_INT(DMAC_VIF0, g_vifCycles); + return; + } + + if (!vif0.done) + { + + if (!(dmacRegs->ctrl.DMAE)) + { + Console.WriteLn("vif0 dma masked"); + return; + } + + if ((vif0.inprogress & 0x1) == 0) vif0SetupTransfer(); + + CPU_INT(DMAC_VIF0, g_vifCycles); + return; + } + + if (vif0.vifstalled && vif0.irq) + { + DevCon.WriteLn("VIF0 looping on stall\n"); + CPU_INT(DMAC_VIF0, 0); + return; //Dont want to end if vif is stalled. + } +#ifdef PCSX2_DEVBUILD + if (vif0ch->qwc > 0) Console.WriteLn("vif0 Ending with %x QWC left"); + if (vif0.cmd != 0) Console.WriteLn("vif0.cmd still set %x tag size %x", vif0.cmd, vif0.tag.size); +#endif + + vif0Regs->stat.VPS = VPS_IDLE; //Vif goes idle as the stall happened between commands; + vif0ch->chcr.STR = false; + g_vifCycles = 0; + hwDmacIrq(DMAC_VIF0); + vif0Regs->stat.FQC = 0; +} + +void dmaVIF0() +{ + VIF_LOG("dmaVIF0 chcr = %lx, madr = %lx, qwc = %lx\n" + " tadr = %lx, asr0 = %lx, asr1 = %lx", + vif0ch->chcr._u32, vif0ch->madr, vif0ch->qwc, + vif0ch->tadr, vif0ch->asr0, vif0ch->asr1); + + g_vifCycles = 0; + vif0.inprogress = 0; + + if ((vif0ch->chcr.MOD == NORMAL_MODE) || vif0ch->qwc > 0) // Normal Mode + { + vif0.dmamode = VIF_NORMAL_TO_MEM_MODE; + if(vif0ch->chcr.MOD == CHAIN_MODE && vif0ch->qwc > 0) DevCon.Warning(L"VIF0 QWC on Chain CHCR " + vif0ch->chcr.desc()); + } + else + { + vif0.dmamode = VIF_CHAIN_MODE; + } + + if (vif0.dmamode != VIF_NORMAL_FROM_MEM_MODE) + vif0Regs->stat.FQC = 0x8; + else + vif0Regs->stat.FQC = min((u16)0x8, vif0ch->qwc); + + // Chain Mode + vif0.done = false; + vif0Interrupt(); } diff --git a/pcsx2/Vif1_Dma.cpp b/pcsx2/Vif1_Dma.cpp index 27b006d064..bff000a95a 100644 --- a/pcsx2/Vif1_Dma.cpp +++ b/pcsx2/Vif1_Dma.cpp @@ -1,347 +1,347 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2009 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 "Common.h" -#include "Vif_Dma.h" -#include "GS.h" -#include "Gif.h" -#include "VUmicro.h" -#include "newVif.h" - -__forceinline void vif1FLUSH() -{ - if (!(VU0.VI[REG_VPU_STAT].UL & 0x100)) return; - int _cycles = VU1.cycle; - vu1Finish(); - g_vifCycles += (VU1.cycle - _cycles) * BIAS; -} - -void vif1TransferToMemory() -{ - int size; - u64* pMem = (u64*)dmaGetAddr(vif1ch->madr, false); - - // VIF from gsMemory - if (pMem == NULL) //Is vif0ptag empty? - { - Console.WriteLn("Vif1 Tag BUSERR"); - dmacRegs->stat.BEIS = true; //Bus Error - vif1Regs->stat.FQC = 0; - - vif1ch->qwc = 0; - vif1.done = true; - CPU_INT(DMAC_VIF1, 0); - return; //An error has occurred. - } - - // MTGS concerns: The MTGS is inherently disagreeable with the idea of downloading - // stuff from the GS. The *only* way to handle this case safely is to flush the GS - // completely and execute the transfer there-after. - //Console.Warning("Real QWC %x", vif1ch->qwc); - XMMRegisters::Freeze(); - - if (GSreadFIFO2 == NULL) - { - for (size = vif1ch->qwc; size > 0; --size) - { - if (size > 1) - { - GetMTGS().WaitGS(); - GSreadFIFO(&psHu64(VIF1_FIFO)); - } - pMem[0] = psHu64(VIF1_FIFO); - pMem[1] = psHu64(VIF1_FIFO + 8); - pMem += 2; - } - } - else - { - GetMTGS().WaitGS(); - GSreadFIFO2(pMem, vif1ch->qwc); - - // set incase read - psHu64(VIF1_FIFO) = pMem[2*vif1ch->qwc-2]; - psHu64(VIF1_FIFO + 8) = pMem[2*vif1ch->qwc-1]; - } - - XMMRegisters::Thaw(); - - g_vifCycles += vif1ch->qwc * 2; - vif1ch->madr += vif1ch->qwc * 16; // mgs3 scene changes - if(vif1.GSLastTRXPOS > vif1ch->qwc) - vif1Regs->stat.FQC = vif1.GSLastTRXPOS - vif1ch->qwc; - else - vif1Regs->stat.FQC = 0; - - vif1ch->qwc = 0; -} - -bool _VIF1chain() -{ - u32 *pMem; - - if (vif1ch->qwc == 0) - { - vif1.inprogress = 0; - return true; - } - - // Clarification - this is TO memory mode, for some reason i used the other way round >.< - if (vif1.dmamode == VIF_NORMAL_TO_MEM_MODE) - { - vif1TransferToMemory(); - vif1.inprogress = 0; - return true; - } - - pMem = (u32*)dmaGetAddr(vif1ch->madr, !vif1ch->chcr.DIR); - if (pMem == NULL) - { - vif1.cmd = 0; - vif1.tag.size = 0; - vif1ch->qwc = 0; - return true; - } - - VIF_LOG("VIF1chain size=%d, madr=%lx, tadr=%lx", - vif1ch->qwc, vif1ch->madr, vif1ch->tadr); - - if (vif1.vifstalled) - return VIF1transfer(pMem + vif1.irqoffset, vif1ch->qwc * 4 - vif1.irqoffset, false); - else - return VIF1transfer(pMem, vif1ch->qwc * 4, false); -} - -__forceinline void vif1SetupTransfer() -{ - tDMA_TAG *ptag; - - switch (vif1.dmamode) - { - case VIF_NORMAL_TO_MEM_MODE: - case VIF_NORMAL_FROM_MEM_MODE: - vif1.inprogress = 1; - vif1.done = true; - g_vifCycles = 2; - break; - - case VIF_CHAIN_MODE: - ptag = dmaGetAddr(vif1ch->tadr, false); //Set memory pointer to TADR - - if (!(vif1ch->transfer("Vif1 Tag", ptag))) return; - - vif1ch->madr = ptag[1]._u32; //MADR = ADDR field + SPR - g_vifCycles += 1; // Add 1 g_vifCycles from the QW read for the tag - - // Transfer dma tag if tte is set - - VIF_LOG("VIF1 Tag %8.8x_%8.8x size=%d, id=%d, madr=%lx, tadr=%lx\n", - ptag[1]._u32, ptag[0]._u32, vif1ch->qwc, ptag->ID, vif1ch->madr, vif1ch->tadr); - - if (!vif1.done && ((dmacRegs->ctrl.STD == STD_VIF1) && (ptag->ID == TAG_REFS))) // STD == VIF1 - { - // there are still bugs, need to also check if gif->madr +16*qwc >= stadr, if not, stall - if ((vif1ch->madr + vif1ch->qwc * 16) >= dmacRegs->stadr.ADDR) - { - // stalled - hwDmacIrq(DMAC_STALL_SIS); - return; - } - } - - vif1.inprogress = 1; - - if (vif1ch->chcr.TTE) - { - bool ret; - - if (vif1.vifstalled) - ret = VIF1transfer((u32*)ptag + (2 + vif1.irqoffset), 2 - vif1.irqoffset, true); //Transfer Tag on stall - else - ret = VIF1transfer((u32*)ptag + 2, 2, true); //Transfer Tag - - if ((ret == false) && vif1.irqoffset < 2) - { - vif1.inprogress = 0; //Better clear this so it has to do it again (Jak 1) - return; //There has been an error or an interrupt - } - } - - vif1.irqoffset = 0; - vif1.done |= hwDmacSrcChainWithStack(vif1ch, ptag->ID); - - //Check TIE bit of CHCR and IRQ bit of tag - if (vif1ch->chcr.TIE && ptag->IRQ) - { - VIF_LOG("dmaIrq Set"); - - //End Transfer - vif1.done = true; - return; - } - break; - } -} - -__forceinline void vif1Interrupt() -{ - VIF_LOG("vif1Interrupt: %8.8x", cpuRegs.cycle); - - g_vifCycles = 0; - - //Some games (Fahrenheit being one) start vif first, let it loop through blankness while it sets MFIFO mode, so we need to check it here. - if (dmacRegs->ctrl.MFD == MFD_VIF1) // VIF MFIFO - { - //Console.WriteLn("VIFMFIFO\n"); - // Test changed because the Final Fantasy 12 opening somehow has the tag in *Undefined* mode, which is not in the documentation that I saw. - if (vif1ch->chcr.MOD == NORMAL_MODE) Console.WriteLn("MFIFO mode is normal (which isn't normal here)! %x", vif1ch->chcr._u32); - vif1Regs->stat.FQC = min((u16)0x10, vif1ch->qwc); - vifMFIFOInterrupt(); - return; - } - - if (vif1ch->chcr.DIR) vif1Regs->stat.FQC = min(vif1ch->qwc, (u16)16); - //Simulated GS transfer time done, clear the flags - if(gifRegs->stat.APATH == GIF_APATH2 && (vif1.cmd & 0x70) != 0x50) - { - gifRegs->stat.clear_flags(GIF_STAT_APATH2|GIF_STAT_OPH); - } - - if (schedulepath3msk) Vif1MskPath3(); - - if ((vif1Regs->stat.VGW)) - { - if (GSTransferStatus.PTH3 < STOPPED_MODE || GSTransferStatus.PTH1 != STOPPED_MODE) - { - CPU_INT(DMAC_VIF1, 4); - return; - } - else - { - vif1Regs->stat.VGW = false; - } - } - - if (!(vif1ch->chcr.STR)) Console.WriteLn("Vif1 running when CHCR == %x", vif1ch->chcr._u32); - - if (vif1.irq && vif1.tag.size == 0) - { - vif1Regs->stat.INT = true; - hwIntcIrq(VIF1intc); - --vif1.irq; - if (vif1Regs->stat.test(VIF1_STAT_VSS | VIF1_STAT_VIS | VIF1_STAT_VFS)) - { - //vif1Regs->stat.FQC = 0; - - // One game doesn't like vif stalling at end, can't remember what. Spiderman isn't keen on it tho - vif1ch->chcr.STR = false; - return; - } - else if ((vif1ch->qwc > 0) || (vif1.irqoffset > 0)) - { - if (vif1.stallontag) - vif1SetupTransfer(); - else - _VIF1chain();//CPU_INT(DMAC_STALL_SIS, vif1ch->qwc * BIAS); - } - } - - if (vif1.inprogress & 0x1) - { - _VIF1chain(); - // VIF_NORMAL_FROM_MEM_MODE is a very slow operation. - // Timesplitters 2 depends on this beeing a bit higher than 128. - if (vif1ch->chcr.DIR) vif1Regs->stat.FQC = min(vif1ch->qwc, (u16)16); - // Refraction - Removing voodoo timings for now, completely messes a lot of Path3 masked games. - /*if (vif1.dmamode == VIF_NORMAL_FROM_MEM_MODE ) CPU_INT(DMAC_VIF1, 1024); - else */CPU_INT(DMAC_VIF1, g_vifCycles /*VifCycleVoodoo*/); - return; - } - - if (!vif1.done) - { - - if (!(dmacRegs->ctrl.DMAE)) - { - Console.WriteLn("vif1 dma masked"); - return; - } - - if ((vif1.inprogress & 0x1) == 0) vif1SetupTransfer(); - if (vif1ch->chcr.DIR) vif1Regs->stat.FQC = min(vif1ch->qwc, (u16)16); - CPU_INT(DMAC_VIF1, g_vifCycles); - return; - } - - if (vif1.vifstalled && vif1.irq) - { - DevCon.WriteLn("VIF1 looping on stall\n"); - CPU_INT(DMAC_VIF1, 0); - return; //Dont want to end if vif is stalled. - } -#ifdef PCSX2_DEVBUILD - if (vif1ch->qwc > 0) Console.WriteLn("VIF1 Ending with %x QWC left"); - if (vif1.cmd != 0) Console.WriteLn("vif1.cmd still set %x tag size %x", vif1.cmd, vif1.tag.size); -#endif - - vif1Regs->stat.VPS = VPS_IDLE; //Vif goes idle as the stall happened between commands; - vif1ch->chcr.STR = false; - g_vifCycles = 0; - hwDmacIrq(DMAC_VIF1); - -} - -void dmaVIF1() -{ - VIF_LOG("dmaVIF1 chcr = %lx, madr = %lx, qwc = %lx\n" - " tadr = %lx, asr0 = %lx, asr1 = %lx", - vif1ch->chcr._u32, vif1ch->madr, vif1ch->qwc, - vif1ch->tadr, vif1ch->asr0, vif1ch->asr1); - - vif1.done = false; - g_vifCycles = 0; - vif1.inprogress = 0; - -#ifdef PCSX2_DEVBUILD - if (dmacRegs->ctrl.STD == STD_VIF1) - { - //DevCon.WriteLn("VIF Stall Control Source = %x, Drain = %x", (psHu32(0xe000) >> 4) & 0x3, (psHu32(0xe000) >> 6) & 0x3); - } -#endif - - if ((vif1ch->chcr.MOD == NORMAL_MODE) || vif1ch->qwc > 0) // Normal Mode - { - - if (dmacRegs->ctrl.STD == STD_VIF1) - Console.WriteLn("DMA Stall Control on VIF1 normal"); - - if (vif1ch->chcr.DIR) // to Memory - vif1.dmamode = VIF_NORMAL_FROM_MEM_MODE; - else - vif1.dmamode = VIF_NORMAL_TO_MEM_MODE; - - if(vif1ch->chcr.MOD == CHAIN_MODE && vif1ch->qwc > 0) DevCon.Warning(L"VIF1 QWC on Chain CHCR " + vif1ch->chcr.desc()); - } - else - { - vif1.dmamode = VIF_CHAIN_MODE; - } - - vif1Regs->stat.FQC = min((u16)0x10, vif1ch->qwc); - - // Chain Mode - vif1Interrupt(); +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2009 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 "Common.h" +#include "Vif_Dma.h" +#include "GS.h" +#include "Gif.h" +#include "VUmicro.h" +#include "newVif.h" + +__forceinline void vif1FLUSH() +{ + if (!(VU0.VI[REG_VPU_STAT].UL & 0x100)) return; + int _cycles = VU1.cycle; + vu1Finish(); + g_vifCycles += (VU1.cycle - _cycles) * BIAS; +} + +void vif1TransferToMemory() +{ + int size; + u64* pMem = (u64*)dmaGetAddr(vif1ch->madr, false); + + // VIF from gsMemory + if (pMem == NULL) //Is vif0ptag empty? + { + Console.WriteLn("Vif1 Tag BUSERR"); + dmacRegs->stat.BEIS = true; //Bus Error + vif1Regs->stat.FQC = 0; + + vif1ch->qwc = 0; + vif1.done = true; + CPU_INT(DMAC_VIF1, 0); + return; //An error has occurred. + } + + // MTGS concerns: The MTGS is inherently disagreeable with the idea of downloading + // stuff from the GS. The *only* way to handle this case safely is to flush the GS + // completely and execute the transfer there-after. + //Console.Warning("Real QWC %x", vif1ch->qwc); + XMMRegisters::Freeze(); + + if (GSreadFIFO2 == NULL) + { + for (size = vif1ch->qwc; size > 0; --size) + { + if (size > 1) + { + GetMTGS().WaitGS(); + GSreadFIFO(&psHu64(VIF1_FIFO)); + } + pMem[0] = psHu64(VIF1_FIFO); + pMem[1] = psHu64(VIF1_FIFO + 8); + pMem += 2; + } + } + else + { + GetMTGS().WaitGS(); + GSreadFIFO2(pMem, vif1ch->qwc); + + // set incase read + psHu64(VIF1_FIFO) = pMem[2*vif1ch->qwc-2]; + psHu64(VIF1_FIFO + 8) = pMem[2*vif1ch->qwc-1]; + } + + XMMRegisters::Thaw(); + + g_vifCycles += vif1ch->qwc * 2; + vif1ch->madr += vif1ch->qwc * 16; // mgs3 scene changes + if(vif1.GSLastTRXPOS > vif1ch->qwc) + vif1Regs->stat.FQC = vif1.GSLastTRXPOS - vif1ch->qwc; + else + vif1Regs->stat.FQC = 0; + + vif1ch->qwc = 0; +} + +bool _VIF1chain() +{ + u32 *pMem; + + if (vif1ch->qwc == 0) + { + vif1.inprogress = 0; + return true; + } + + // Clarification - this is TO memory mode, for some reason i used the other way round >.< + if (vif1.dmamode == VIF_NORMAL_TO_MEM_MODE) + { + vif1TransferToMemory(); + vif1.inprogress = 0; + return true; + } + + pMem = (u32*)dmaGetAddr(vif1ch->madr, !vif1ch->chcr.DIR); + if (pMem == NULL) + { + vif1.cmd = 0; + vif1.tag.size = 0; + vif1ch->qwc = 0; + return true; + } + + VIF_LOG("VIF1chain size=%d, madr=%lx, tadr=%lx", + vif1ch->qwc, vif1ch->madr, vif1ch->tadr); + + if (vif1.vifstalled) + return VIF1transfer(pMem + vif1.irqoffset, vif1ch->qwc * 4 - vif1.irqoffset, false); + else + return VIF1transfer(pMem, vif1ch->qwc * 4, false); +} + +__forceinline void vif1SetupTransfer() +{ + tDMA_TAG *ptag; + + switch (vif1.dmamode) + { + case VIF_NORMAL_TO_MEM_MODE: + case VIF_NORMAL_FROM_MEM_MODE: + vif1.inprogress = 1; + vif1.done = true; + g_vifCycles = 2; + break; + + case VIF_CHAIN_MODE: + ptag = dmaGetAddr(vif1ch->tadr, false); //Set memory pointer to TADR + + if (!(vif1ch->transfer("Vif1 Tag", ptag))) return; + + vif1ch->madr = ptag[1]._u32; //MADR = ADDR field + SPR + g_vifCycles += 1; // Add 1 g_vifCycles from the QW read for the tag + + // Transfer dma tag if tte is set + + VIF_LOG("VIF1 Tag %8.8x_%8.8x size=%d, id=%d, madr=%lx, tadr=%lx\n", + ptag[1]._u32, ptag[0]._u32, vif1ch->qwc, ptag->ID, vif1ch->madr, vif1ch->tadr); + + if (!vif1.done && ((dmacRegs->ctrl.STD == STD_VIF1) && (ptag->ID == TAG_REFS))) // STD == VIF1 + { + // there are still bugs, need to also check if gif->madr +16*qwc >= stadr, if not, stall + if ((vif1ch->madr + vif1ch->qwc * 16) >= dmacRegs->stadr.ADDR) + { + // stalled + hwDmacIrq(DMAC_STALL_SIS); + return; + } + } + + vif1.inprogress = 1; + + if (vif1ch->chcr.TTE) + { + bool ret; + + if (vif1.vifstalled) + ret = VIF1transfer((u32*)ptag + (2 + vif1.irqoffset), 2 - vif1.irqoffset, true); //Transfer Tag on stall + else + ret = VIF1transfer((u32*)ptag + 2, 2, true); //Transfer Tag + + if ((ret == false) && vif1.irqoffset < 2) + { + vif1.inprogress = 0; //Better clear this so it has to do it again (Jak 1) + return; //There has been an error or an interrupt + } + } + + vif1.irqoffset = 0; + vif1.done |= hwDmacSrcChainWithStack(vif1ch, ptag->ID); + + //Check TIE bit of CHCR and IRQ bit of tag + if (vif1ch->chcr.TIE && ptag->IRQ) + { + VIF_LOG("dmaIrq Set"); + + //End Transfer + vif1.done = true; + return; + } + break; + } +} + +__forceinline void vif1Interrupt() +{ + VIF_LOG("vif1Interrupt: %8.8x", cpuRegs.cycle); + + g_vifCycles = 0; + + //Some games (Fahrenheit being one) start vif first, let it loop through blankness while it sets MFIFO mode, so we need to check it here. + if (dmacRegs->ctrl.MFD == MFD_VIF1) // VIF MFIFO + { + //Console.WriteLn("VIFMFIFO\n"); + // Test changed because the Final Fantasy 12 opening somehow has the tag in *Undefined* mode, which is not in the documentation that I saw. + if (vif1ch->chcr.MOD == NORMAL_MODE) Console.WriteLn("MFIFO mode is normal (which isn't normal here)! %x", vif1ch->chcr._u32); + vif1Regs->stat.FQC = min((u16)0x10, vif1ch->qwc); + vifMFIFOInterrupt(); + return; + } + + if (vif1ch->chcr.DIR) vif1Regs->stat.FQC = min(vif1ch->qwc, (u16)16); + //Simulated GS transfer time done, clear the flags + if(gifRegs->stat.APATH == GIF_APATH2 && (vif1.cmd & 0x70) != 0x50) + { + gifRegs->stat.clear_flags(GIF_STAT_APATH2|GIF_STAT_OPH); + } + + if (schedulepath3msk) Vif1MskPath3(); + + if ((vif1Regs->stat.VGW)) + { + if (GSTransferStatus.PTH3 < STOPPED_MODE || GSTransferStatus.PTH1 != STOPPED_MODE) + { + CPU_INT(DMAC_VIF1, 4); + return; + } + else + { + vif1Regs->stat.VGW = false; + } + } + + if (!(vif1ch->chcr.STR)) Console.WriteLn("Vif1 running when CHCR == %x", vif1ch->chcr._u32); + + if (vif1.irq && vif1.tag.size == 0) + { + vif1Regs->stat.INT = true; + hwIntcIrq(VIF1intc); + --vif1.irq; + if (vif1Regs->stat.test(VIF1_STAT_VSS | VIF1_STAT_VIS | VIF1_STAT_VFS)) + { + //vif1Regs->stat.FQC = 0; + + // One game doesn't like vif stalling at end, can't remember what. Spiderman isn't keen on it tho + vif1ch->chcr.STR = false; + return; + } + else if ((vif1ch->qwc > 0) || (vif1.irqoffset > 0)) + { + if (vif1.stallontag) + vif1SetupTransfer(); + else + _VIF1chain();//CPU_INT(DMAC_STALL_SIS, vif1ch->qwc * BIAS); + } + } + + if (vif1.inprogress & 0x1) + { + _VIF1chain(); + // VIF_NORMAL_FROM_MEM_MODE is a very slow operation. + // Timesplitters 2 depends on this beeing a bit higher than 128. + if (vif1ch->chcr.DIR) vif1Regs->stat.FQC = min(vif1ch->qwc, (u16)16); + // Refraction - Removing voodoo timings for now, completely messes a lot of Path3 masked games. + /*if (vif1.dmamode == VIF_NORMAL_FROM_MEM_MODE ) CPU_INT(DMAC_VIF1, 1024); + else */CPU_INT(DMAC_VIF1, g_vifCycles /*VifCycleVoodoo*/); + return; + } + + if (!vif1.done) + { + + if (!(dmacRegs->ctrl.DMAE)) + { + Console.WriteLn("vif1 dma masked"); + return; + } + + if ((vif1.inprogress & 0x1) == 0) vif1SetupTransfer(); + if (vif1ch->chcr.DIR) vif1Regs->stat.FQC = min(vif1ch->qwc, (u16)16); + CPU_INT(DMAC_VIF1, g_vifCycles); + return; + } + + if (vif1.vifstalled && vif1.irq) + { + DevCon.WriteLn("VIF1 looping on stall\n"); + CPU_INT(DMAC_VIF1, 0); + return; //Dont want to end if vif is stalled. + } +#ifdef PCSX2_DEVBUILD + if (vif1ch->qwc > 0) Console.WriteLn("VIF1 Ending with %x QWC left"); + if (vif1.cmd != 0) Console.WriteLn("vif1.cmd still set %x tag size %x", vif1.cmd, vif1.tag.size); +#endif + + vif1Regs->stat.VPS = VPS_IDLE; //Vif goes idle as the stall happened between commands; + vif1ch->chcr.STR = false; + g_vifCycles = 0; + hwDmacIrq(DMAC_VIF1); + +} + +void dmaVIF1() +{ + VIF_LOG("dmaVIF1 chcr = %lx, madr = %lx, qwc = %lx\n" + " tadr = %lx, asr0 = %lx, asr1 = %lx", + vif1ch->chcr._u32, vif1ch->madr, vif1ch->qwc, + vif1ch->tadr, vif1ch->asr0, vif1ch->asr1); + + vif1.done = false; + g_vifCycles = 0; + vif1.inprogress = 0; + +#ifdef PCSX2_DEVBUILD + if (dmacRegs->ctrl.STD == STD_VIF1) + { + //DevCon.WriteLn("VIF Stall Control Source = %x, Drain = %x", (psHu32(0xe000) >> 4) & 0x3, (psHu32(0xe000) >> 6) & 0x3); + } +#endif + + if ((vif1ch->chcr.MOD == NORMAL_MODE) || vif1ch->qwc > 0) // Normal Mode + { + + if (dmacRegs->ctrl.STD == STD_VIF1) + Console.WriteLn("DMA Stall Control on VIF1 normal"); + + if (vif1ch->chcr.DIR) // to Memory + vif1.dmamode = VIF_NORMAL_FROM_MEM_MODE; + else + vif1.dmamode = VIF_NORMAL_TO_MEM_MODE; + + if(vif1ch->chcr.MOD == CHAIN_MODE && vif1ch->qwc > 0) DevCon.Warning(L"VIF1 QWC on Chain CHCR " + vif1ch->chcr.desc()); + } + else + { + vif1.dmamode = VIF_CHAIN_MODE; + } + + vif1Regs->stat.FQC = min((u16)0x10, vif1ch->qwc); + + // Chain Mode + vif1Interrupt(); } diff --git a/pcsx2/Vif1_MFIFO.cpp b/pcsx2/Vif1_MFIFO.cpp index 1a9e7adef4..d4b400c2d2 100644 --- a/pcsx2/Vif1_MFIFO.cpp +++ b/pcsx2/Vif1_MFIFO.cpp @@ -1,307 +1,307 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2009 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 "Common.h" -#include "Vif.h" -#include "Gif.h" -#include "Vif_Dma.h" - -VIFregisters *vifRegs; -vifStruct *vif; -u16 vifqwc = 0; -int g_vifCycles = 0; - -__aligned16 VifMaskTypes g_vifmask; - -extern int g_vifCycles; - -static __forceinline bool mfifoVIF1rbTransfer() -{ - u32 maddr = dmacRegs->rbor.ADDR; - u32 msize = dmacRegs->rbor.ADDR + dmacRegs->rbsr.RMSK + 16; - u16 mfifoqwc = std::min(vif1ch->qwc, vifqwc); - u32 *src; - bool ret; - - /* Check if the transfer should wrap around the ring buffer */ - if ((vif1ch->madr + (mfifoqwc << 4)) > (msize)) - { - int s1 = ((msize) - vif1ch->madr) >> 2; - - SPR_LOG("Split MFIFO"); - - /* it does, so first copy 's1' bytes from 'addr' to 'data' */ - src = (u32*)PSM(vif1ch->madr); - if (src == NULL) return false; - - if (vif1.vifstalled) - ret = VIF1transfer(src + vif1.irqoffset, s1 - vif1.irqoffset, false); - else - ret = VIF1transfer(src, s1, false); - - if (ret) - { - /* and second copy 's2' bytes from 'maddr' to '&data[s1]' */ - vif1ch->madr = maddr; - - src = (u32*)PSM(maddr); - if (src == NULL) return false; - VIF1transfer(src, ((mfifoqwc << 2) - s1), false); - } - } - else - { - SPR_LOG("Direct MFIFO"); - - /* it doesn't, so just transfer 'qwc*4' words */ - src = (u32*)PSM(vif1ch->madr); - if (src == NULL) return false; - - if (vif1.vifstalled) - ret = VIF1transfer(src + vif1.irqoffset, mfifoqwc * 4 - vif1.irqoffset, false); - else - ret = VIF1transfer(src, mfifoqwc << 2, false); - } - return ret; -} - -static __forceinline bool mfifo_VIF1chain() -{ - bool ret; - - /* Is QWC = 0? if so there is nothing to transfer */ - if ((vif1ch->qwc == 0) && (!vif1.vifstalled)) - { - vif1.inprogress &= ~1; - return true; - } - - if (vif1ch->madr >= dmacRegs->rbor.ADDR && - vif1ch->madr <= (dmacRegs->rbor.ADDR + dmacRegs->rbsr.RMSK)) - { - u16 startqwc = vif1ch->qwc; - ret = mfifoVIF1rbTransfer(); - vifqwc -= startqwc - vif1ch->qwc; - } - else - { - tDMA_TAG *pMem = dmaGetAddr(vif1ch->madr, !vif1ch->chcr.DIR); - SPR_LOG("Non-MFIFO Location"); - - if (pMem == NULL) return false; - - if (vif1.vifstalled) - ret = VIF1transfer((u32*)pMem + vif1.irqoffset, vif1ch->qwc * 4 - vif1.irqoffset, false); - else - ret = VIF1transfer((u32*)pMem, vif1ch->qwc << 2, false); - } - return ret; -} - -static u32 qwctag(u32 mask) -{ - return (dmacRegs->rbor.ADDR + (mask & dmacRegs->rbsr.RMSK)); -} - -void mfifoVIF1transfer(int qwc) -{ - tDMA_TAG *ptag; - - g_vifCycles = 0; - - if (qwc > 0) - { - vifqwc += qwc; - SPR_LOG("Added %x qw to mfifo, total now %x - Vif CHCR %x Stalled %x done %x", qwc, vifqwc, vif1ch->chcr._u32, vif1.vifstalled, vif1.done); - if (vif1.inprogress & 0x10) - { - if (vif1ch->madr >= dmacRegs->rbor.ADDR && vif1ch->madr <= (dmacRegs->rbor.ADDR + dmacRegs->rbsr.RMSK)) - CPU_INT(10, 0); - else - CPU_INT(10, vif1ch->qwc * BIAS); - - vif1Regs->stat.FQC = 0x10; // FQC=16 - } - vif1.inprogress &= ~0x10; - - return; - } - - if (vif1ch->qwc == 0 && vifqwc > 0) - { - ptag = dmaGetAddr(vif1ch->tadr, false); - - if (vif1ch->chcr.TTE) - { - bool ret; - - if (vif1.stallontag) - ret = VIF1transfer((u32*)ptag + (2 + vif1.irqoffset), 2 - vif1.irqoffset, true); //Transfer Tag on Stall - else - ret = VIF1transfer((u32*)ptag + 2, 2, true); //Transfer Tag - - if (!(ret)) - { - VIF_LOG("MFIFO Stall on tag"); - vif1.stallontag = true; - return; //IRQ set by VIFTransfer - } - } - - vif1ch->unsafeTransfer(ptag); - - vif1ch->madr = ptag[1]._u32; - vifqwc--; - - SPR_LOG("dmaChain %8.8x_%8.8x size=%d, id=%d, madr=%lx, tadr=%lx mfifo qwc = %x spr0 madr = %x", - ptag[1]._u32, ptag[0]._u32, vif1ch->qwc, ptag->ID, vif1ch->madr, vif1ch->tadr, vifqwc, spr0->madr); - - switch (ptag->ID) - { - case TAG_REFE: // Refe - Transfer Packet According to ADDR field - vif1ch->tadr = qwctag(vif1ch->tadr + 16); - vif1.done = true; //End Transfer - break; - - case TAG_CNT: // CNT - Transfer QWC following the tag. - vif1ch->madr = qwctag(vif1ch->tadr + 16); //Set MADR to QW after Tag - vif1ch->tadr = qwctag(vif1ch->madr + (vif1ch->qwc << 4)); //Set TADR to QW following the data - vif1.done = false; - break; - - case TAG_NEXT: // Next - Transfer QWC following tag. TADR = ADDR - { - int temp = vif1ch->madr; //Temporarily Store ADDR - vif1ch->madr = qwctag(vif1ch->tadr + 16); //Set MADR to QW following the tag - vif1ch->tadr = temp; //Copy temporarily stored ADDR to Tag - if ((temp & dmacRegs->rbsr.RMSK) != dmacRegs->rbor.ADDR) Console.WriteLn("Next tag = %x outside ring %x size %x", temp, psHu32(DMAC_RBOR), psHu32(DMAC_RBSR)); - vif1.done = false; - break; - } - - case TAG_REF: // Ref - Transfer QWC from ADDR field - case TAG_REFS: // Refs - Transfer QWC from ADDR field (Stall Control) - vif1ch->tadr = qwctag(vif1ch->tadr + 16); //Set TADR to next tag - vif1.done = false; - break; - - case TAG_END: // End - Transfer QWC following the tag - vif1ch->madr = qwctag(vif1ch->tadr + 16); //Set MADR to data following the tag - vif1ch->tadr = qwctag(vif1ch->madr + (vif1ch->qwc << 4)); //Set TADR to QW following the data - vif1.done = true; //End Transfer - break; - } - - if (vif1ch->chcr.TIE && ptag->IRQ) - { - VIF_LOG("dmaIrq Set"); - vif1.done = true; - } - } - vif1Regs->stat.FQC = min(vif1ch->qwc, (u16)16); - vif1.inprogress |= 1; - - SPR_LOG("mfifoVIF1transfer end %x madr %x, tadr %x vifqwc %x", vif1ch->chcr._u32, vif1ch->madr, vif1ch->tadr, vifqwc); -} - -void vifMFIFOInterrupt() -{ - g_vifCycles = 0; - VIF_LOG("vif mfifo interrupt"); - - if (schedulepath3msk) Vif1MskPath3(); - - if ((vif1Regs->stat.VGW)) - { - if (GSTransferStatus.PTH3 < STOPPED_MODE || GSTransferStatus.PTH1 != STOPPED_MODE) - { - CPU_INT(10, 4); - return; - } - else - { - vif1Regs->stat.VGW = false; - } - - } - - if ((spr0->chcr.STR) && (spr0->qwc == 0)) - { - spr0->chcr.STR = false; - hwDmacIrq(DMAC_FROM_SPR); - } - - if (vif1.irq && vif1.tag.size == 0) - { - vif1Regs->stat.INT = true; - hwIntcIrq(INTC_VIF1); - --vif1.irq; - - if (vif1Regs->stat.test(VIF1_STAT_VSS | VIF1_STAT_VIS | VIF1_STAT_VFS)) - { - vif1Regs->stat.FQC = 0; // FQC=0 - vif1ch->chcr.STR = false; - return; - } - } - - if (vif1.done == false || vif1ch->qwc) - { - switch(vif1.inprogress & 1) - { - case 0: //Set up transfer - if (vif1ch->tadr == spr0->madr) - { - // Console.WriteLn("Empty 1"); - vifqwc = 0; - vif1.inprogress |= 0x10; - vif1Regs->stat.FQC = 0; - hwDmacIrq(DMAC_MFIFO_EMPTY); - return; - } - - mfifoVIF1transfer(0); - if ((vif1ch->madr >= dmacRegs->rbor.ADDR) && (vif1ch->madr <= (dmacRegs->rbor.ADDR + dmacRegs->rbsr.RMSK))) - CPU_INT(10, 0); - else - CPU_INT(10, vif1ch->qwc * BIAS); - - return; - - case 1: //Transfer data - mfifo_VIF1chain(); - CPU_INT(10, 0); - return; - } - return; - } - - /*if (vifqwc <= 0) - { - //Console.WriteLn("Empty 2"); - //vif1.inprogress |= 0x10; - vif1Regs->stat.FQC = 0; // FQC=0 - hwDmacIrq(DMAC_MFIFO_EMPTY); - }*/ - - vif1.done = 1; - g_vifCycles = 0; - vif1ch->chcr.STR = false; - hwDmacIrq(DMAC_VIF1); - VIF_LOG("vif mfifo dma end"); - - vif1Regs->stat.FQC = 0; -} +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2009 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 "Common.h" +#include "Vif.h" +#include "Gif.h" +#include "Vif_Dma.h" + +VIFregisters *vifRegs; +vifStruct *vif; +u16 vifqwc = 0; +int g_vifCycles = 0; + +__aligned16 VifMaskTypes g_vifmask; + +extern int g_vifCycles; + +static __forceinline bool mfifoVIF1rbTransfer() +{ + u32 maddr = dmacRegs->rbor.ADDR; + u32 msize = dmacRegs->rbor.ADDR + dmacRegs->rbsr.RMSK + 16; + u16 mfifoqwc = std::min(vif1ch->qwc, vifqwc); + u32 *src; + bool ret; + + /* Check if the transfer should wrap around the ring buffer */ + if ((vif1ch->madr + (mfifoqwc << 4)) > (msize)) + { + int s1 = ((msize) - vif1ch->madr) >> 2; + + SPR_LOG("Split MFIFO"); + + /* it does, so first copy 's1' bytes from 'addr' to 'data' */ + src = (u32*)PSM(vif1ch->madr); + if (src == NULL) return false; + + if (vif1.vifstalled) + ret = VIF1transfer(src + vif1.irqoffset, s1 - vif1.irqoffset, false); + else + ret = VIF1transfer(src, s1, false); + + if (ret) + { + /* and second copy 's2' bytes from 'maddr' to '&data[s1]' */ + vif1ch->madr = maddr; + + src = (u32*)PSM(maddr); + if (src == NULL) return false; + VIF1transfer(src, ((mfifoqwc << 2) - s1), false); + } + } + else + { + SPR_LOG("Direct MFIFO"); + + /* it doesn't, so just transfer 'qwc*4' words */ + src = (u32*)PSM(vif1ch->madr); + if (src == NULL) return false; + + if (vif1.vifstalled) + ret = VIF1transfer(src + vif1.irqoffset, mfifoqwc * 4 - vif1.irqoffset, false); + else + ret = VIF1transfer(src, mfifoqwc << 2, false); + } + return ret; +} + +static __forceinline bool mfifo_VIF1chain() +{ + bool ret; + + /* Is QWC = 0? if so there is nothing to transfer */ + if ((vif1ch->qwc == 0) && (!vif1.vifstalled)) + { + vif1.inprogress &= ~1; + return true; + } + + if (vif1ch->madr >= dmacRegs->rbor.ADDR && + vif1ch->madr <= (dmacRegs->rbor.ADDR + dmacRegs->rbsr.RMSK)) + { + u16 startqwc = vif1ch->qwc; + ret = mfifoVIF1rbTransfer(); + vifqwc -= startqwc - vif1ch->qwc; + } + else + { + tDMA_TAG *pMem = dmaGetAddr(vif1ch->madr, !vif1ch->chcr.DIR); + SPR_LOG("Non-MFIFO Location"); + + if (pMem == NULL) return false; + + if (vif1.vifstalled) + ret = VIF1transfer((u32*)pMem + vif1.irqoffset, vif1ch->qwc * 4 - vif1.irqoffset, false); + else + ret = VIF1transfer((u32*)pMem, vif1ch->qwc << 2, false); + } + return ret; +} + +static u32 qwctag(u32 mask) +{ + return (dmacRegs->rbor.ADDR + (mask & dmacRegs->rbsr.RMSK)); +} + +void mfifoVIF1transfer(int qwc) +{ + tDMA_TAG *ptag; + + g_vifCycles = 0; + + if (qwc > 0) + { + vifqwc += qwc; + SPR_LOG("Added %x qw to mfifo, total now %x - Vif CHCR %x Stalled %x done %x", qwc, vifqwc, vif1ch->chcr._u32, vif1.vifstalled, vif1.done); + if (vif1.inprogress & 0x10) + { + if (vif1ch->madr >= dmacRegs->rbor.ADDR && vif1ch->madr <= (dmacRegs->rbor.ADDR + dmacRegs->rbsr.RMSK)) + CPU_INT(10, 0); + else + CPU_INT(10, vif1ch->qwc * BIAS); + + vif1Regs->stat.FQC = 0x10; // FQC=16 + } + vif1.inprogress &= ~0x10; + + return; + } + + if (vif1ch->qwc == 0 && vifqwc > 0) + { + ptag = dmaGetAddr(vif1ch->tadr, false); + + if (vif1ch->chcr.TTE) + { + bool ret; + + if (vif1.stallontag) + ret = VIF1transfer((u32*)ptag + (2 + vif1.irqoffset), 2 - vif1.irqoffset, true); //Transfer Tag on Stall + else + ret = VIF1transfer((u32*)ptag + 2, 2, true); //Transfer Tag + + if (!(ret)) + { + VIF_LOG("MFIFO Stall on tag"); + vif1.stallontag = true; + return; //IRQ set by VIFTransfer + } + } + + vif1ch->unsafeTransfer(ptag); + + vif1ch->madr = ptag[1]._u32; + vifqwc--; + + SPR_LOG("dmaChain %8.8x_%8.8x size=%d, id=%d, madr=%lx, tadr=%lx mfifo qwc = %x spr0 madr = %x", + ptag[1]._u32, ptag[0]._u32, vif1ch->qwc, ptag->ID, vif1ch->madr, vif1ch->tadr, vifqwc, spr0->madr); + + switch (ptag->ID) + { + case TAG_REFE: // Refe - Transfer Packet According to ADDR field + vif1ch->tadr = qwctag(vif1ch->tadr + 16); + vif1.done = true; //End Transfer + break; + + case TAG_CNT: // CNT - Transfer QWC following the tag. + vif1ch->madr = qwctag(vif1ch->tadr + 16); //Set MADR to QW after Tag + vif1ch->tadr = qwctag(vif1ch->madr + (vif1ch->qwc << 4)); //Set TADR to QW following the data + vif1.done = false; + break; + + case TAG_NEXT: // Next - Transfer QWC following tag. TADR = ADDR + { + int temp = vif1ch->madr; //Temporarily Store ADDR + vif1ch->madr = qwctag(vif1ch->tadr + 16); //Set MADR to QW following the tag + vif1ch->tadr = temp; //Copy temporarily stored ADDR to Tag + if ((temp & dmacRegs->rbsr.RMSK) != dmacRegs->rbor.ADDR) Console.WriteLn("Next tag = %x outside ring %x size %x", temp, psHu32(DMAC_RBOR), psHu32(DMAC_RBSR)); + vif1.done = false; + break; + } + + case TAG_REF: // Ref - Transfer QWC from ADDR field + case TAG_REFS: // Refs - Transfer QWC from ADDR field (Stall Control) + vif1ch->tadr = qwctag(vif1ch->tadr + 16); //Set TADR to next tag + vif1.done = false; + break; + + case TAG_END: // End - Transfer QWC following the tag + vif1ch->madr = qwctag(vif1ch->tadr + 16); //Set MADR to data following the tag + vif1ch->tadr = qwctag(vif1ch->madr + (vif1ch->qwc << 4)); //Set TADR to QW following the data + vif1.done = true; //End Transfer + break; + } + + if (vif1ch->chcr.TIE && ptag->IRQ) + { + VIF_LOG("dmaIrq Set"); + vif1.done = true; + } + } + vif1Regs->stat.FQC = min(vif1ch->qwc, (u16)16); + vif1.inprogress |= 1; + + SPR_LOG("mfifoVIF1transfer end %x madr %x, tadr %x vifqwc %x", vif1ch->chcr._u32, vif1ch->madr, vif1ch->tadr, vifqwc); +} + +void vifMFIFOInterrupt() +{ + g_vifCycles = 0; + VIF_LOG("vif mfifo interrupt"); + + if (schedulepath3msk) Vif1MskPath3(); + + if ((vif1Regs->stat.VGW)) + { + if (GSTransferStatus.PTH3 < STOPPED_MODE || GSTransferStatus.PTH1 != STOPPED_MODE) + { + CPU_INT(10, 4); + return; + } + else + { + vif1Regs->stat.VGW = false; + } + + } + + if ((spr0->chcr.STR) && (spr0->qwc == 0)) + { + spr0->chcr.STR = false; + hwDmacIrq(DMAC_FROM_SPR); + } + + if (vif1.irq && vif1.tag.size == 0) + { + vif1Regs->stat.INT = true; + hwIntcIrq(INTC_VIF1); + --vif1.irq; + + if (vif1Regs->stat.test(VIF1_STAT_VSS | VIF1_STAT_VIS | VIF1_STAT_VFS)) + { + vif1Regs->stat.FQC = 0; // FQC=0 + vif1ch->chcr.STR = false; + return; + } + } + + if (vif1.done == false || vif1ch->qwc) + { + switch(vif1.inprogress & 1) + { + case 0: //Set up transfer + if (vif1ch->tadr == spr0->madr) + { + // Console.WriteLn("Empty 1"); + vifqwc = 0; + vif1.inprogress |= 0x10; + vif1Regs->stat.FQC = 0; + hwDmacIrq(DMAC_MFIFO_EMPTY); + return; + } + + mfifoVIF1transfer(0); + if ((vif1ch->madr >= dmacRegs->rbor.ADDR) && (vif1ch->madr <= (dmacRegs->rbor.ADDR + dmacRegs->rbsr.RMSK))) + CPU_INT(10, 0); + else + CPU_INT(10, vif1ch->qwc * BIAS); + + return; + + case 1: //Transfer data + mfifo_VIF1chain(); + CPU_INT(10, 0); + return; + } + return; + } + + /*if (vifqwc <= 0) + { + //Console.WriteLn("Empty 2"); + //vif1.inprogress |= 0x10; + vif1Regs->stat.FQC = 0; // FQC=0 + hwDmacIrq(DMAC_MFIFO_EMPTY); + }*/ + + vif1.done = 1; + g_vifCycles = 0; + vif1ch->chcr.STR = false; + hwDmacIrq(DMAC_VIF1); + VIF_LOG("vif mfifo dma end"); + + vif1Regs->stat.FQC = 0; +} diff --git a/pcsx2/Vif_Codes.cpp b/pcsx2/Vif_Codes.cpp index bfe782e436..107e41e411 100644 --- a/pcsx2/Vif_Codes.cpp +++ b/pcsx2/Vif_Codes.cpp @@ -1,475 +1,475 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2009 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 "Common.h" -#include "GS.h" -#include "Gif.h" -#include "Vif_Dma.h" -#include "newVif.h" -#include "VUmicro.h" - -#define vifOp(vifCodeName) _vifT int __fastcall vifCodeName(int pass, u32 *data) -#define pass1 if (pass == 0) -#define pass2 if (pass == 1) -#define pass3 if (pass == 2) -#define vif1Only() { if (!idx) return vifCode_Null(pass, (u32*)data); } -vifOp(vifCode_Null); - -//------------------------------------------------------------------ -// Vif0/Vif1 Misc Functions -//------------------------------------------------------------------ - -static _f void vifFlush(int idx) { - if (!idx) vif0FLUSH(); - else vif1FLUSH(); -} - -static _f void vuExecMicro(int idx, u32 addr) { - VURegs* VU = nVif[idx].VU; - vifFlush(idx); - - if (VU->vifRegs->itops > (idx ? 0x3ffu : 0xffu)) { - Console.WriteLn("VIF%d ITOP overrun! %x", idx, VU->vifRegs->itops); - VU->vifRegs->itops &= (idx ? 0x3ffu : 0xffu); - } - - VU->vifRegs->itop = VU->vifRegs->itops; - - if (idx) { - // in case we're handling a VIF1 execMicro, set the top with the tops value - VU->vifRegs->top = VU->vifRegs->tops & 0x3ff; - - // is DBF flag set in VIF_STAT? - if (VU->vifRegs->stat.DBF) { - // it is, so set tops with base, and clear the stat DBF flag - VU->vifRegs->tops = VU->vifRegs->base; - VU->vifRegs->stat.DBF = false; - } - else { - // it is not, so set tops with base + offset, and set stat DBF flag - VU->vifRegs->tops = VU->vifRegs->base + VU->vifRegs->ofst; - VU->vifRegs->stat.DBF = true; - } - } - - if (!idx) vu0ExecMicro(addr); - else vu1ExecMicro(addr); -} - -u8 schedulepath3msk = 0; - -void Vif1MskPath3() { - - vif1Regs->mskpath3 = schedulepath3msk & 0x1; - //Console.WriteLn("VIF MSKPATH3 %x", vif1Regs->mskpath3); - gifRegs->stat.M3P = vif1Regs->mskpath3; - if (!vif1Regs->mskpath3) { - //Let the Gif know it can transfer again (making sure any vif stall isnt unset prematurely) - if(gif->chcr.STR == true) - { - GSTransferStatus.PTH3 = 3; - CPU_INT(DMAC_GIF, 4); - } - - } - else gifRegs->stat.M3P = true; - - schedulepath3msk = 0; -} - -//------------------------------------------------------------------ -// Vif0/Vif1 Code Implementations -//------------------------------------------------------------------ - -vifOp(vifCode_Base) { - vif1Only(); - pass1 { vif1Regs->base = vif1Regs->code & 0x3ff; vif1.cmd = 0; } - pass3 { DevCon.WriteLn("vifCode_Base"); } - return 0; -} - -template _f int _vifCode_Direct(int pass, u8* data, bool isDirectHL) { - pass1 { - vif1Only(); - int vifImm = (u16)vif1Regs->code; - vif1.tag.size = vifImm ? (vifImm*4) : (65536*4); - return 1; - } - pass2 { - vif1Only(); - //return vifTrans_DirectHL((u32*)data); - gifRegs->stat.P2Q = true; - //Should probably do this for both types of transfer seen as the GS hates taking 2 seperate chunks - //if (isDirectHL) { - if (GSTransferStatus.PTH3 < STOPPED_MODE || GSTransferStatus.PTH1 != STOPPED_MODE) - { - /*if(!isDirectHL) DevCon.WriteLn("Direct: Waiting for Path3 to finish!"); - else DevCon.WriteLn("DirectHL: Waiting for Path3 to finish!");*/ - //VIF_LOG("Mask %x, GIF STR %x, PTH1 %x, PTH2 %x, PTH3 %x", vif1Regs->mskpath3, gif->chcr.STR, GSTransferStatus.PTH1, GSTransferStatus.PTH2, GSTransferStatus.PTH3); - vif1Regs->stat.VGW = true; // PATH3 is in image mode, so wait for end of transfer - vif1.vifstalled = true; - return 0; - } - //} - gifRegs->stat.clear_flags(GIF_STAT_P2Q); - gifRegs->stat.APATH = GIF_APATH2; - gifRegs->stat.OPH = true; - - Registers::Freeze(); - nVifStruct& v = nVif[1]; - const int ret = aMin(vif1.vifpacketsize, vif1.tag.size); - u32 size = ret << 2; - - if (ret == v.vif->tag.size) { // Full Transfer - if (v.bSize) { // Last transfer was partial - memcpy_fast(&v.buffer[v.bSize], data, size); - v.bSize += size; - data = v.buffer; - size = v.bSize; - } - if (!size) { DevCon.WriteLn("Path2: No Data Transfer?"); } - const uint count = GetMTGS().PrepDataPacket(GIF_PATH_2, data, size >> 4); - memcpy_fast(GetMTGS().GetDataPacketPtr(), data, count << 4); - GetMTGS().SendDataPacket(); - vif1.tag.size = 0; - vif1.cmd = 0; - v.bSize = 0; - gifRegs->stat.clear_flags(GIF_STAT_APATH2 | GIF_STAT_OPH); - } - else { // Partial Transfer - //DevCon.WriteLn("DirectHL: Partial Transfer [%d]", size); - gifRegs->stat.set_flags(GIF_STAT_APATH2 | GIF_STAT_OPH); - memcpy_fast(&v.buffer[v.bSize], data, size); - v.bSize += size; - vif1.tag.size -= ret; - } - - Registers::Thaw(); - return ret; - } - return 0; -} - -vifOp(vifCode_Direct) { - pass3 { DevCon.WriteLn("vifCode_Direct"); } - return _vifCode_Direct(pass, (u8*)data, 0); -} - -vifOp(vifCode_DirectHL) { - pass3 { DevCon.WriteLn("vifCode_DirectHL"); } - return _vifCode_Direct(pass, (u8*)data, 1); -} - -// ToDo: FixMe -vifOp(vifCode_Flush) { - vif1Only(); - pass1 { vifFlush(idx); vifX.cmd = 0; } - pass3 { DevCon.WriteLn("vifCode_Flush"); } - return 0; -} - -// ToDo: FixMe -vifOp(vifCode_FlushA) { - vif1Only(); - pass1 { - // Gif is already transferring so wait for it. - if (GSTransferStatus.PTH3 < STOPPED_MODE) { - //DevCon.WriteLn("FlushA path3 Wait! PTH3 MD %x STR %x", GSTransferStatus.PTH3, gif->chcr.STR); - vif1Regs->stat.VGW = true; - vifX.vifstalled = true; - } - vifFlush(idx); - vifX.cmd = 0; - } - pass3 { DevCon.WriteLn("vifCode_FlushA"); } - return 0; -} - -// ToDo: FixMe -vifOp(vifCode_FlushE) { - pass1 { vifFlush(idx); vifX.cmd = 0; } - pass3 { DevCon.WriteLn("vifCode_FlushE"); } - return 0; -} - -vifOp(vifCode_ITop) { - pass1 { vifXRegs->itops = vifXRegs->code & 0x3ff; vifX.cmd = 0; } - pass3 { DevCon.WriteLn("vifCode_ITop"); } - return 0; -} - -vifOp(vifCode_Mark) { - pass1 { - vifXRegs->mark = (u16)vifXRegs->code; - vifXRegs->stat.MRK = true; - vifX.cmd = 0; - } - pass3 { DevCon.WriteLn("vifCode_Mark"); } - return 0; -} - -_f void _vifCode_MPG(int idx, u32 addr, u32 *data, int size) { - VURegs& VUx = idx ? VU1 : VU0; - pxAssume(VUx.Micro > 0); - - if (memcmp(VUx.Micro + addr, data, size << 2)) { - if (!idx) CpuVU0->Clear(addr, size << 2); // Clear before writing! - else CpuVU1->Clear(addr, size << 2); // Clear before writing! - memcpy_fast(VUx.Micro + addr, data, size << 2); - } -} - -vifOp(vifCode_MPG) { - pass1 { - int vifNum = (u8)(vifXRegs->code >> 16); - vifX.tag.addr = (u16)(vifXRegs->code << 3) & (idx ? 0x3fff : 0xfff); - vifX.tag.size = vifNum ? (vifNum*2) : 512; - return 1; - } - pass2 { - vifFlush(idx); - if (vifX.vifpacketsize < vifX.tag.size) { // Partial Transfer - if((vifX.tag.addr + vifX.vifpacketsize) > (idx ? 0x4000 : 0x1000)) { - DevCon.Warning("Vif%d MPG Split Overflow", idx); - } - _vifCode_MPG(idx, vifX.tag.addr, data, vifX.vifpacketsize); - vifX.tag.addr += vifX.vifpacketsize << 2; - vifX.tag.size -= vifX.vifpacketsize; - return vifX.vifpacketsize; - } - else { // Full Transfer - if((vifX.tag.addr + vifX.tag.size) > (idx ? 0x4000 : 0x1000)) { - DevCon.Warning("Vif%d MPG Split Overflow", idx); - } - _vifCode_MPG(idx, vifX.tag.addr, data, vifX.tag.size); - int ret = vifX.tag.size; - vifX.tag.size = 0; - vifX.cmd = 0; - return ret; - } - } - pass3 { DevCon.WriteLn("vifCode_MPG"); } - return 0; -} - -vifOp(vifCode_MSCAL) { - pass1 { vuExecMicro(idx, (u16)(vifXRegs->code) << 3); vifX.cmd = 0; } - pass3 { DevCon.WriteLn("vifCode_MSCAL"); } - return 0; -} - -vifOp(vifCode_MSCALF) { - pass1 { vuExecMicro(idx, (u16)(vifXRegs->code) << 3); vifX.cmd = 0; } - pass3 { DevCon.WriteLn("vifCode_MSCALF"); } - return 0; -} - -vifOp(vifCode_MSCNT) { - pass1 { vuExecMicro(idx, -1); vifX.cmd = 0; } - pass3 { DevCon.WriteLn("vifCode_MSCNT"); } - return 0; -} - -// ToDo: FixMe -vifOp(vifCode_MskPath3) { - vif1Only(); - pass1 { - if (vif1ch->chcr.STR) { - schedulepath3msk = 0x10 | ((vif1Regs->code >> 15) & 0x1); - vif1.vifstalled = true; - } - else { - schedulepath3msk = (vif1Regs->code >> 15) & 0x1; - Vif1MskPath3(); - } - vifX.cmd = 0; - } - pass3 { DevCon.WriteLn("vifCode_MskPath3"); } - return 0; -} - -vifOp(vifCode_Nop) { - pass1 { vifX.cmd = 0; } - pass3 { DevCon.WriteLn("vifCode_Nop"); } - return 0; -} - -// ToDo: Review Flags -vifOp(vifCode_Null) { - pass1 { - // if ME1, then force the vif to interrupt - if (!(vifXRegs->err.ME1)) { // Ignore vifcode and tag mismatch error - Console.WriteLn("Vif%d: Unknown VifCmd! [%x]", idx, vifX.cmd); - vifXRegs->stat.ER1 = true; - vifX.vifstalled = true; - //vifX.irq++; - } - vifX.cmd = 0; - } - pass2 { Console.Error("Vif%d bad vifcode! [CMD = %x]", idx, vifX.cmd); } - pass3 { DevCon.WriteLn("vifCode_Null"); } - return 0; -} - -vifOp(vifCode_Offset) { - vif1Only(); - pass1 { - vif1Regs->stat.DBF = false; - vif1Regs->ofst = vif1Regs->code & 0x3ff; - vif1Regs->tops = vif1Regs->base; - vifX.cmd = 0; - } - pass3 { DevCon.WriteLn("vifCode_Offset"); } - return 0; -} - -template _f int _vifCode_STColRow(u32* data, u32* pmem1, u32* pmem2) { - int ret; - ret = min(4 - vifX.tag.addr, vifX.vifpacketsize); - pxAssume(vifX.tag.addr < 4); - pxAssume(ret > 0); - - switch (ret) { - case 4: - pmem1[12] = data[3]; - pmem2[3] = data[3]; - case 3: - pmem1[8] = data[2]; - pmem2[2] = data[2]; - case 2: - pmem1[4] = data[1]; - pmem2[1] = data[1]; - case 1: - pmem1[0] = data[0]; - pmem2[0] = data[0]; - break; - jNO_DEFAULT - } - - vifX.tag.addr += ret; - vifX.tag.size -= ret; - if (!vifX.tag.size) vifX.cmd = 0; - - return ret; -} - -vifOp(vifCode_STCol) { - pass1 { - vifX.tag.addr = 0; - vifX.tag.size = 4; - return 1; - } - pass2 { - u32* cols = idx ? g_vifmask.Col1 : g_vifmask.Col0; - u32* pmem1 = &vifXRegs->c0 + (vifX.tag.addr << 2); - u32* pmem2 = cols + vifX.tag.addr; - return _vifCode_STColRow(data, pmem1, pmem2); - } - pass3 { DevCon.WriteLn("vifCode_STCol"); } - return 0; -} - -vifOp(vifCode_STRow) { - pass1 { - vifX.tag.addr = 0; - vifX.tag.size = 4; - return 1; - } - pass2 { - u32* rows = idx ? g_vifmask.Row1 : g_vifmask.Row0; - u32* pmem1 = &vifXRegs->r0 + (vifX.tag.addr << 2); - u32* pmem2 = rows + vifX.tag.addr; - return _vifCode_STColRow(data, pmem1, pmem2); - } - pass3 { DevCon.WriteLn("vifCode_STRow"); } - return 0; -} - -vifOp(vifCode_STCycl) { - pass1 { - vifXRegs->cycle.cl = (u8)(vifXRegs->code); - vifXRegs->cycle.wl = (u8)(vifXRegs->code >> 8); - vifX.cmd = 0; - } - pass3 { DevCon.WriteLn("vifCode_STCycl"); } - return 0; -} - -vifOp(vifCode_STMask) { - pass1 { vifX.tag.size = 1; } - pass2 { vifXRegs->mask = data[0]; vifX.tag.size = 0; vifX.cmd = 0; } - pass3 { DevCon.WriteLn("vifCode_STMask"); } - return 1; -} - -vifOp(vifCode_STMod) { - pass1 { vifXRegs->mode = vifXRegs->code & 0x3; vifX.cmd = 0; } - pass3 { DevCon.WriteLn("vifCode_STMod"); } - return 0; -} - -vifOp(vifCode_Unpack) { - pass1 { - if (!idx) vif0UnpackSetup(data); - else vif1UnpackSetup(data); - return 1; - } - pass2 { return nVifUnpack(idx, (u8*)data); } - pass3 { DevCon.WriteLn("vifCode_Unpack"); } - return 0; -} - -//------------------------------------------------------------------ -// Vif0/Vif1 Code Tables -//------------------------------------------------------------------ - -int (__fastcall *vif0Code[128])(int pass, u32 *data) = { - vifCode_Nop<0> , vifCode_STCycl<0> , vifCode_Offset<0> , vifCode_Base<0> , vifCode_ITop<0> , vifCode_STMod<0> , vifCode_MskPath3<0>, vifCode_Mark<0>, /*0x00*/ - vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0>, /*0x08*/ - vifCode_FlushE<0> , vifCode_Flush<0> , vifCode_Null<0> , vifCode_FlushA<0> , vifCode_MSCAL<0> , vifCode_MSCALF<0> , vifCode_Null<0> , vifCode_MSCNT<0>, /*0x10*/ - vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0>, /*0x18*/ - vifCode_STMask<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0>, /*0x20*/ - vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0>, /*0x28*/ - vifCode_STRow<0> , vifCode_STCol<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0>, /*0x30*/ - vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0>, /*0x38*/ - vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0>, /*0x40*/ - vifCode_Null<0> , vifCode_Null<0> , vifCode_MPG<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0>, /*0x48*/ - vifCode_Direct<0> , vifCode_DirectHL<0>, vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0>, /*0x50*/ - vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0>, /*0x58*/ - vifCode_Unpack<0> , vifCode_Unpack<0> , vifCode_Unpack<0> , vifCode_Unpack<0> , vifCode_Unpack<0> , vifCode_Unpack<0> , vifCode_Unpack<0> , vifCode_Null<0>, /*0x60*/ - vifCode_Unpack<0> , vifCode_Unpack<0> , vifCode_Unpack<0> , vifCode_Unpack<0> , vifCode_Unpack<0> , vifCode_Unpack<0> , vifCode_Unpack<0> , vifCode_Unpack<0>, /*0x68*/ - vifCode_Unpack<0> , vifCode_Unpack<0> , vifCode_Unpack<0> , vifCode_Unpack<0> , vifCode_Unpack<0> , vifCode_Unpack<0> , vifCode_Unpack<0> , vifCode_Null<0>, /*0x70*/ - vifCode_Unpack<0> , vifCode_Unpack<0> , vifCode_Unpack<0> , vifCode_Null<0> , vifCode_Unpack<0> , vifCode_Unpack<0> , vifCode_Unpack<0> , vifCode_Unpack<0> /*0x78*/ -}; - -int (__fastcall *vif1Code[128])(int pass, u32 *data) = { - vifCode_Nop<1> , vifCode_STCycl<1> , vifCode_Offset<1> , vifCode_Base<1> , vifCode_ITop<1> , vifCode_STMod<1> , vifCode_MskPath3<1>, vifCode_Mark<1>, /*0x00*/ - vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1>, /*0x08*/ - vifCode_FlushE<1> , vifCode_Flush<1> , vifCode_Null<1> , vifCode_FlushA<1> , vifCode_MSCAL<1> , vifCode_MSCALF<1> , vifCode_Null<1> , vifCode_MSCNT<1>, /*0x10*/ - vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1>, /*0x18*/ - vifCode_STMask<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1>, /*0x20*/ - vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1>, /*0x28*/ - vifCode_STRow<1> , vifCode_STCol<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1>, /*0x30*/ - vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1>, /*0x38*/ - vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1>, /*0x40*/ - vifCode_Null<1> , vifCode_Null<1> , vifCode_MPG<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1>, /*0x48*/ - vifCode_Direct<1> , vifCode_DirectHL<1>, vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1>, /*0x50*/ - vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1>, /*0x58*/ - vifCode_Unpack<1> , vifCode_Unpack<1> , vifCode_Unpack<1> , vifCode_Unpack<1> , vifCode_Unpack<1> , vifCode_Unpack<1> , vifCode_Unpack<1> , vifCode_Null<1>, /*0x60*/ - vifCode_Unpack<1> , vifCode_Unpack<1> , vifCode_Unpack<1> , vifCode_Unpack<1> , vifCode_Unpack<1> , vifCode_Unpack<1> , vifCode_Unpack<1> , vifCode_Unpack<1>, /*0x68*/ - vifCode_Unpack<1> , vifCode_Unpack<1> , vifCode_Unpack<1> , vifCode_Unpack<1> , vifCode_Unpack<1> , vifCode_Unpack<1> , vifCode_Unpack<1> , vifCode_Null<1>, /*0x70*/ - vifCode_Unpack<1> , vifCode_Unpack<1> , vifCode_Unpack<1> , vifCode_Null<1> , vifCode_Unpack<1> , vifCode_Unpack<1> , vifCode_Unpack<1> , vifCode_Unpack<1> /*0x78*/ -}; +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2009 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 "Common.h" +#include "GS.h" +#include "Gif.h" +#include "Vif_Dma.h" +#include "newVif.h" +#include "VUmicro.h" + +#define vifOp(vifCodeName) _vifT int __fastcall vifCodeName(int pass, u32 *data) +#define pass1 if (pass == 0) +#define pass2 if (pass == 1) +#define pass3 if (pass == 2) +#define vif1Only() { if (!idx) return vifCode_Null(pass, (u32*)data); } +vifOp(vifCode_Null); + +//------------------------------------------------------------------ +// Vif0/Vif1 Misc Functions +//------------------------------------------------------------------ + +static _f void vifFlush(int idx) { + if (!idx) vif0FLUSH(); + else vif1FLUSH(); +} + +static _f void vuExecMicro(int idx, u32 addr) { + VURegs* VU = nVif[idx].VU; + vifFlush(idx); + + if (VU->vifRegs->itops > (idx ? 0x3ffu : 0xffu)) { + Console.WriteLn("VIF%d ITOP overrun! %x", idx, VU->vifRegs->itops); + VU->vifRegs->itops &= (idx ? 0x3ffu : 0xffu); + } + + VU->vifRegs->itop = VU->vifRegs->itops; + + if (idx) { + // in case we're handling a VIF1 execMicro, set the top with the tops value + VU->vifRegs->top = VU->vifRegs->tops & 0x3ff; + + // is DBF flag set in VIF_STAT? + if (VU->vifRegs->stat.DBF) { + // it is, so set tops with base, and clear the stat DBF flag + VU->vifRegs->tops = VU->vifRegs->base; + VU->vifRegs->stat.DBF = false; + } + else { + // it is not, so set tops with base + offset, and set stat DBF flag + VU->vifRegs->tops = VU->vifRegs->base + VU->vifRegs->ofst; + VU->vifRegs->stat.DBF = true; + } + } + + if (!idx) vu0ExecMicro(addr); + else vu1ExecMicro(addr); +} + +u8 schedulepath3msk = 0; + +void Vif1MskPath3() { + + vif1Regs->mskpath3 = schedulepath3msk & 0x1; + //Console.WriteLn("VIF MSKPATH3 %x", vif1Regs->mskpath3); + gifRegs->stat.M3P = vif1Regs->mskpath3; + if (!vif1Regs->mskpath3) { + //Let the Gif know it can transfer again (making sure any vif stall isnt unset prematurely) + if(gif->chcr.STR == true) + { + GSTransferStatus.PTH3 = 3; + CPU_INT(DMAC_GIF, 4); + } + + } + else gifRegs->stat.M3P = true; + + schedulepath3msk = 0; +} + +//------------------------------------------------------------------ +// Vif0/Vif1 Code Implementations +//------------------------------------------------------------------ + +vifOp(vifCode_Base) { + vif1Only(); + pass1 { vif1Regs->base = vif1Regs->code & 0x3ff; vif1.cmd = 0; } + pass3 { DevCon.WriteLn("vifCode_Base"); } + return 0; +} + +template _f int _vifCode_Direct(int pass, u8* data, bool isDirectHL) { + pass1 { + vif1Only(); + int vifImm = (u16)vif1Regs->code; + vif1.tag.size = vifImm ? (vifImm*4) : (65536*4); + return 1; + } + pass2 { + vif1Only(); + //return vifTrans_DirectHL((u32*)data); + gifRegs->stat.P2Q = true; + //Should probably do this for both types of transfer seen as the GS hates taking 2 seperate chunks + //if (isDirectHL) { + if (GSTransferStatus.PTH3 < STOPPED_MODE || GSTransferStatus.PTH1 != STOPPED_MODE) + { + /*if(!isDirectHL) DevCon.WriteLn("Direct: Waiting for Path3 to finish!"); + else DevCon.WriteLn("DirectHL: Waiting for Path3 to finish!");*/ + //VIF_LOG("Mask %x, GIF STR %x, PTH1 %x, PTH2 %x, PTH3 %x", vif1Regs->mskpath3, gif->chcr.STR, GSTransferStatus.PTH1, GSTransferStatus.PTH2, GSTransferStatus.PTH3); + vif1Regs->stat.VGW = true; // PATH3 is in image mode, so wait for end of transfer + vif1.vifstalled = true; + return 0; + } + //} + gifRegs->stat.clear_flags(GIF_STAT_P2Q); + gifRegs->stat.APATH = GIF_APATH2; + gifRegs->stat.OPH = true; + + Registers::Freeze(); + nVifStruct& v = nVif[1]; + const int ret = aMin(vif1.vifpacketsize, vif1.tag.size); + u32 size = ret << 2; + + if (ret == v.vif->tag.size) { // Full Transfer + if (v.bSize) { // Last transfer was partial + memcpy_fast(&v.buffer[v.bSize], data, size); + v.bSize += size; + data = v.buffer; + size = v.bSize; + } + if (!size) { DevCon.WriteLn("Path2: No Data Transfer?"); } + const uint count = GetMTGS().PrepDataPacket(GIF_PATH_2, data, size >> 4); + memcpy_fast(GetMTGS().GetDataPacketPtr(), data, count << 4); + GetMTGS().SendDataPacket(); + vif1.tag.size = 0; + vif1.cmd = 0; + v.bSize = 0; + gifRegs->stat.clear_flags(GIF_STAT_APATH2 | GIF_STAT_OPH); + } + else { // Partial Transfer + //DevCon.WriteLn("DirectHL: Partial Transfer [%d]", size); + gifRegs->stat.set_flags(GIF_STAT_APATH2 | GIF_STAT_OPH); + memcpy_fast(&v.buffer[v.bSize], data, size); + v.bSize += size; + vif1.tag.size -= ret; + } + + Registers::Thaw(); + return ret; + } + return 0; +} + +vifOp(vifCode_Direct) { + pass3 { DevCon.WriteLn("vifCode_Direct"); } + return _vifCode_Direct(pass, (u8*)data, 0); +} + +vifOp(vifCode_DirectHL) { + pass3 { DevCon.WriteLn("vifCode_DirectHL"); } + return _vifCode_Direct(pass, (u8*)data, 1); +} + +// ToDo: FixMe +vifOp(vifCode_Flush) { + vif1Only(); + pass1 { vifFlush(idx); vifX.cmd = 0; } + pass3 { DevCon.WriteLn("vifCode_Flush"); } + return 0; +} + +// ToDo: FixMe +vifOp(vifCode_FlushA) { + vif1Only(); + pass1 { + // Gif is already transferring so wait for it. + if (GSTransferStatus.PTH3 < STOPPED_MODE) { + //DevCon.WriteLn("FlushA path3 Wait! PTH3 MD %x STR %x", GSTransferStatus.PTH3, gif->chcr.STR); + vif1Regs->stat.VGW = true; + vifX.vifstalled = true; + } + vifFlush(idx); + vifX.cmd = 0; + } + pass3 { DevCon.WriteLn("vifCode_FlushA"); } + return 0; +} + +// ToDo: FixMe +vifOp(vifCode_FlushE) { + pass1 { vifFlush(idx); vifX.cmd = 0; } + pass3 { DevCon.WriteLn("vifCode_FlushE"); } + return 0; +} + +vifOp(vifCode_ITop) { + pass1 { vifXRegs->itops = vifXRegs->code & 0x3ff; vifX.cmd = 0; } + pass3 { DevCon.WriteLn("vifCode_ITop"); } + return 0; +} + +vifOp(vifCode_Mark) { + pass1 { + vifXRegs->mark = (u16)vifXRegs->code; + vifXRegs->stat.MRK = true; + vifX.cmd = 0; + } + pass3 { DevCon.WriteLn("vifCode_Mark"); } + return 0; +} + +_f void _vifCode_MPG(int idx, u32 addr, u32 *data, int size) { + VURegs& VUx = idx ? VU1 : VU0; + pxAssume(VUx.Micro > 0); + + if (memcmp(VUx.Micro + addr, data, size << 2)) { + if (!idx) CpuVU0->Clear(addr, size << 2); // Clear before writing! + else CpuVU1->Clear(addr, size << 2); // Clear before writing! + memcpy_fast(VUx.Micro + addr, data, size << 2); + } +} + +vifOp(vifCode_MPG) { + pass1 { + int vifNum = (u8)(vifXRegs->code >> 16); + vifX.tag.addr = (u16)(vifXRegs->code << 3) & (idx ? 0x3fff : 0xfff); + vifX.tag.size = vifNum ? (vifNum*2) : 512; + return 1; + } + pass2 { + vifFlush(idx); + if (vifX.vifpacketsize < vifX.tag.size) { // Partial Transfer + if((vifX.tag.addr + vifX.vifpacketsize) > (idx ? 0x4000 : 0x1000)) { + DevCon.Warning("Vif%d MPG Split Overflow", idx); + } + _vifCode_MPG(idx, vifX.tag.addr, data, vifX.vifpacketsize); + vifX.tag.addr += vifX.vifpacketsize << 2; + vifX.tag.size -= vifX.vifpacketsize; + return vifX.vifpacketsize; + } + else { // Full Transfer + if((vifX.tag.addr + vifX.tag.size) > (idx ? 0x4000 : 0x1000)) { + DevCon.Warning("Vif%d MPG Split Overflow", idx); + } + _vifCode_MPG(idx, vifX.tag.addr, data, vifX.tag.size); + int ret = vifX.tag.size; + vifX.tag.size = 0; + vifX.cmd = 0; + return ret; + } + } + pass3 { DevCon.WriteLn("vifCode_MPG"); } + return 0; +} + +vifOp(vifCode_MSCAL) { + pass1 { vuExecMicro(idx, (u16)(vifXRegs->code) << 3); vifX.cmd = 0; } + pass3 { DevCon.WriteLn("vifCode_MSCAL"); } + return 0; +} + +vifOp(vifCode_MSCALF) { + pass1 { vuExecMicro(idx, (u16)(vifXRegs->code) << 3); vifX.cmd = 0; } + pass3 { DevCon.WriteLn("vifCode_MSCALF"); } + return 0; +} + +vifOp(vifCode_MSCNT) { + pass1 { vuExecMicro(idx, -1); vifX.cmd = 0; } + pass3 { DevCon.WriteLn("vifCode_MSCNT"); } + return 0; +} + +// ToDo: FixMe +vifOp(vifCode_MskPath3) { + vif1Only(); + pass1 { + if (vif1ch->chcr.STR) { + schedulepath3msk = 0x10 | ((vif1Regs->code >> 15) & 0x1); + vif1.vifstalled = true; + } + else { + schedulepath3msk = (vif1Regs->code >> 15) & 0x1; + Vif1MskPath3(); + } + vifX.cmd = 0; + } + pass3 { DevCon.WriteLn("vifCode_MskPath3"); } + return 0; +} + +vifOp(vifCode_Nop) { + pass1 { vifX.cmd = 0; } + pass3 { DevCon.WriteLn("vifCode_Nop"); } + return 0; +} + +// ToDo: Review Flags +vifOp(vifCode_Null) { + pass1 { + // if ME1, then force the vif to interrupt + if (!(vifXRegs->err.ME1)) { // Ignore vifcode and tag mismatch error + Console.WriteLn("Vif%d: Unknown VifCmd! [%x]", idx, vifX.cmd); + vifXRegs->stat.ER1 = true; + vifX.vifstalled = true; + //vifX.irq++; + } + vifX.cmd = 0; + } + pass2 { Console.Error("Vif%d bad vifcode! [CMD = %x]", idx, vifX.cmd); } + pass3 { DevCon.WriteLn("vifCode_Null"); } + return 0; +} + +vifOp(vifCode_Offset) { + vif1Only(); + pass1 { + vif1Regs->stat.DBF = false; + vif1Regs->ofst = vif1Regs->code & 0x3ff; + vif1Regs->tops = vif1Regs->base; + vifX.cmd = 0; + } + pass3 { DevCon.WriteLn("vifCode_Offset"); } + return 0; +} + +template _f int _vifCode_STColRow(u32* data, u32* pmem1, u32* pmem2) { + int ret; + ret = min(4 - vifX.tag.addr, vifX.vifpacketsize); + pxAssume(vifX.tag.addr < 4); + pxAssume(ret > 0); + + switch (ret) { + case 4: + pmem1[12] = data[3]; + pmem2[3] = data[3]; + case 3: + pmem1[8] = data[2]; + pmem2[2] = data[2]; + case 2: + pmem1[4] = data[1]; + pmem2[1] = data[1]; + case 1: + pmem1[0] = data[0]; + pmem2[0] = data[0]; + break; + jNO_DEFAULT + } + + vifX.tag.addr += ret; + vifX.tag.size -= ret; + if (!vifX.tag.size) vifX.cmd = 0; + + return ret; +} + +vifOp(vifCode_STCol) { + pass1 { + vifX.tag.addr = 0; + vifX.tag.size = 4; + return 1; + } + pass2 { + u32* cols = idx ? g_vifmask.Col1 : g_vifmask.Col0; + u32* pmem1 = &vifXRegs->c0 + (vifX.tag.addr << 2); + u32* pmem2 = cols + vifX.tag.addr; + return _vifCode_STColRow(data, pmem1, pmem2); + } + pass3 { DevCon.WriteLn("vifCode_STCol"); } + return 0; +} + +vifOp(vifCode_STRow) { + pass1 { + vifX.tag.addr = 0; + vifX.tag.size = 4; + return 1; + } + pass2 { + u32* rows = idx ? g_vifmask.Row1 : g_vifmask.Row0; + u32* pmem1 = &vifXRegs->r0 + (vifX.tag.addr << 2); + u32* pmem2 = rows + vifX.tag.addr; + return _vifCode_STColRow(data, pmem1, pmem2); + } + pass3 { DevCon.WriteLn("vifCode_STRow"); } + return 0; +} + +vifOp(vifCode_STCycl) { + pass1 { + vifXRegs->cycle.cl = (u8)(vifXRegs->code); + vifXRegs->cycle.wl = (u8)(vifXRegs->code >> 8); + vifX.cmd = 0; + } + pass3 { DevCon.WriteLn("vifCode_STCycl"); } + return 0; +} + +vifOp(vifCode_STMask) { + pass1 { vifX.tag.size = 1; } + pass2 { vifXRegs->mask = data[0]; vifX.tag.size = 0; vifX.cmd = 0; } + pass3 { DevCon.WriteLn("vifCode_STMask"); } + return 1; +} + +vifOp(vifCode_STMod) { + pass1 { vifXRegs->mode = vifXRegs->code & 0x3; vifX.cmd = 0; } + pass3 { DevCon.WriteLn("vifCode_STMod"); } + return 0; +} + +vifOp(vifCode_Unpack) { + pass1 { + if (!idx) vif0UnpackSetup(data); + else vif1UnpackSetup(data); + return 1; + } + pass2 { return nVifUnpack(idx, (u8*)data); } + pass3 { DevCon.WriteLn("vifCode_Unpack"); } + return 0; +} + +//------------------------------------------------------------------ +// Vif0/Vif1 Code Tables +//------------------------------------------------------------------ + +int (__fastcall *vif0Code[128])(int pass, u32 *data) = { + vifCode_Nop<0> , vifCode_STCycl<0> , vifCode_Offset<0> , vifCode_Base<0> , vifCode_ITop<0> , vifCode_STMod<0> , vifCode_MskPath3<0>, vifCode_Mark<0>, /*0x00*/ + vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0>, /*0x08*/ + vifCode_FlushE<0> , vifCode_Flush<0> , vifCode_Null<0> , vifCode_FlushA<0> , vifCode_MSCAL<0> , vifCode_MSCALF<0> , vifCode_Null<0> , vifCode_MSCNT<0>, /*0x10*/ + vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0>, /*0x18*/ + vifCode_STMask<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0>, /*0x20*/ + vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0>, /*0x28*/ + vifCode_STRow<0> , vifCode_STCol<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0>, /*0x30*/ + vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0>, /*0x38*/ + vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0>, /*0x40*/ + vifCode_Null<0> , vifCode_Null<0> , vifCode_MPG<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0>, /*0x48*/ + vifCode_Direct<0> , vifCode_DirectHL<0>, vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0>, /*0x50*/ + vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0> , vifCode_Null<0>, /*0x58*/ + vifCode_Unpack<0> , vifCode_Unpack<0> , vifCode_Unpack<0> , vifCode_Unpack<0> , vifCode_Unpack<0> , vifCode_Unpack<0> , vifCode_Unpack<0> , vifCode_Null<0>, /*0x60*/ + vifCode_Unpack<0> , vifCode_Unpack<0> , vifCode_Unpack<0> , vifCode_Unpack<0> , vifCode_Unpack<0> , vifCode_Unpack<0> , vifCode_Unpack<0> , vifCode_Unpack<0>, /*0x68*/ + vifCode_Unpack<0> , vifCode_Unpack<0> , vifCode_Unpack<0> , vifCode_Unpack<0> , vifCode_Unpack<0> , vifCode_Unpack<0> , vifCode_Unpack<0> , vifCode_Null<0>, /*0x70*/ + vifCode_Unpack<0> , vifCode_Unpack<0> , vifCode_Unpack<0> , vifCode_Null<0> , vifCode_Unpack<0> , vifCode_Unpack<0> , vifCode_Unpack<0> , vifCode_Unpack<0> /*0x78*/ +}; + +int (__fastcall *vif1Code[128])(int pass, u32 *data) = { + vifCode_Nop<1> , vifCode_STCycl<1> , vifCode_Offset<1> , vifCode_Base<1> , vifCode_ITop<1> , vifCode_STMod<1> , vifCode_MskPath3<1>, vifCode_Mark<1>, /*0x00*/ + vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1>, /*0x08*/ + vifCode_FlushE<1> , vifCode_Flush<1> , vifCode_Null<1> , vifCode_FlushA<1> , vifCode_MSCAL<1> , vifCode_MSCALF<1> , vifCode_Null<1> , vifCode_MSCNT<1>, /*0x10*/ + vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1>, /*0x18*/ + vifCode_STMask<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1>, /*0x20*/ + vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1>, /*0x28*/ + vifCode_STRow<1> , vifCode_STCol<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1>, /*0x30*/ + vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1>, /*0x38*/ + vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1>, /*0x40*/ + vifCode_Null<1> , vifCode_Null<1> , vifCode_MPG<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1>, /*0x48*/ + vifCode_Direct<1> , vifCode_DirectHL<1>, vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1>, /*0x50*/ + vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1> , vifCode_Null<1>, /*0x58*/ + vifCode_Unpack<1> , vifCode_Unpack<1> , vifCode_Unpack<1> , vifCode_Unpack<1> , vifCode_Unpack<1> , vifCode_Unpack<1> , vifCode_Unpack<1> , vifCode_Null<1>, /*0x60*/ + vifCode_Unpack<1> , vifCode_Unpack<1> , vifCode_Unpack<1> , vifCode_Unpack<1> , vifCode_Unpack<1> , vifCode_Unpack<1> , vifCode_Unpack<1> , vifCode_Unpack<1>, /*0x68*/ + vifCode_Unpack<1> , vifCode_Unpack<1> , vifCode_Unpack<1> , vifCode_Unpack<1> , vifCode_Unpack<1> , vifCode_Unpack<1> , vifCode_Unpack<1> , vifCode_Null<1>, /*0x70*/ + vifCode_Unpack<1> , vifCode_Unpack<1> , vifCode_Unpack<1> , vifCode_Null<1> , vifCode_Unpack<1> , vifCode_Unpack<1> , vifCode_Unpack<1> , vifCode_Unpack<1> /*0x78*/ +}; diff --git a/pcsx2/Vif_Dma.h b/pcsx2/Vif_Dma.h index 2510806fb0..864d457c9b 100644 --- a/pcsx2/Vif_Dma.h +++ b/pcsx2/Vif_Dma.h @@ -1,111 +1,111 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2009 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 . - */ - -#pragma once -#include "Vif_Unpack.h" - -struct vifCode { - u32 addr; - u32 size; - u32 cmd; - u16 wl; - u16 cl; -}; - -union tBITBLT { - struct { - u32 reserved : 8; - u32 BLTDIVIDE : 8; // This is the value we want to work out the divider for the reverse transfer - u32 reserved2 : 6; - u32 TRXPOS : 16; - }; - u32 _u32; - - -}; - -// NOTE, if debugging vif stalls, use sega classics, spyro, gt4, and taito -struct vifStruct { - vifCode tag; - int cmd; - int irq; - int cl; - int qwcalign; - u8 usn; - - bool done; - bool vifstalled; - bool stallontag; - tBITBLT TRXPOS; //used for reversed fifo operations, sometimes only the GS knows how big (like on HW register fifo read)! - u32 GSLastTRXPOS; - - - u8 irqoffset; // 32bit offset where next vif code is - u32 savedtag; // need this for backwards compat with save states - u32 vifpacketsize; - u8 inprogress; - u8 dmamode; -}; - -extern vifStruct* vif; -extern vifStruct vif0, vif1; -extern u8 schedulepath3msk; - -extern void vif0Init(); -extern void vif0Interrupt(); -extern void vif0Write32(u32 mem, u32 value); -extern void vif0Reset(); - -extern void vif1Interrupt(); -extern void vif1Init(); -extern void Vif1MskPath3(); -extern void vif1Write32(u32 mem, u32 value); -extern void vif1Reset(); - -extern int (__fastcall *vif0Code[128])(int pass, u32 *data); -extern int (__fastcall *vif1Code[128])(int pass, u32 *data); - -__forceinline static int _limit(int a, int max) -{ - return ((a > max) ? max : a); -} - -enum VifModes -{ - VIF_NORMAL_TO_MEM_MODE = 0, - VIF_NORMAL_FROM_MEM_MODE = 1, - VIF_CHAIN_MODE = 2 -}; - -// Generic constants -static const unsigned int VIF0intc = 4; -static const unsigned int VIF1intc = 5; - -extern int g_vifCycles; - -extern void vif0FLUSH(); -extern void vif1FLUSH(); - -//------------------------------------------------------------------ -// newVif SSE-optimized Row/Col Structs -//------------------------------------------------------------------ - -struct VifMaskTypes -{ - u32 Row0[4], Col0[4]; - u32 Row1[4], Col1[4]; -}; - +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2009 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 . + */ + +#pragma once +#include "Vif_Unpack.h" + +struct vifCode { + u32 addr; + u32 size; + u32 cmd; + u16 wl; + u16 cl; +}; + +union tBITBLT { + struct { + u32 reserved : 8; + u32 BLTDIVIDE : 8; // This is the value we want to work out the divider for the reverse transfer + u32 reserved2 : 6; + u32 TRXPOS : 16; + }; + u32 _u32; + + +}; + +// NOTE, if debugging vif stalls, use sega classics, spyro, gt4, and taito +struct vifStruct { + vifCode tag; + int cmd; + int irq; + int cl; + int qwcalign; + u8 usn; + + bool done; + bool vifstalled; + bool stallontag; + tBITBLT TRXPOS; //used for reversed fifo operations, sometimes only the GS knows how big (like on HW register fifo read)! + u32 GSLastTRXPOS; + + + u8 irqoffset; // 32bit offset where next vif code is + u32 savedtag; // need this for backwards compat with save states + u32 vifpacketsize; + u8 inprogress; + u8 dmamode; +}; + +extern vifStruct* vif; +extern vifStruct vif0, vif1; +extern u8 schedulepath3msk; + +extern void vif0Init(); +extern void vif0Interrupt(); +extern void vif0Write32(u32 mem, u32 value); +extern void vif0Reset(); + +extern void vif1Interrupt(); +extern void vif1Init(); +extern void Vif1MskPath3(); +extern void vif1Write32(u32 mem, u32 value); +extern void vif1Reset(); + +extern int (__fastcall *vif0Code[128])(int pass, u32 *data); +extern int (__fastcall *vif1Code[128])(int pass, u32 *data); + +__forceinline static int _limit(int a, int max) +{ + return ((a > max) ? max : a); +} + +enum VifModes +{ + VIF_NORMAL_TO_MEM_MODE = 0, + VIF_NORMAL_FROM_MEM_MODE = 1, + VIF_CHAIN_MODE = 2 +}; + +// Generic constants +static const unsigned int VIF0intc = 4; +static const unsigned int VIF1intc = 5; + +extern int g_vifCycles; + +extern void vif0FLUSH(); +extern void vif1FLUSH(); + +//------------------------------------------------------------------ +// newVif SSE-optimized Row/Col Structs +//------------------------------------------------------------------ + +struct VifMaskTypes +{ + u32 Row0[4], Col0[4]; + u32 Row1[4], Col1[4]; +}; + extern __aligned16 VifMaskTypes g_vifmask; // This struct is used by newVif \ No newline at end of file diff --git a/pcsx2/Vif_Transfer.cpp b/pcsx2/Vif_Transfer.cpp index f6d4dd56e2..3b4905130e 100644 --- a/pcsx2/Vif_Transfer.cpp +++ b/pcsx2/Vif_Transfer.cpp @@ -1,133 +1,133 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2009 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 "Common.h" -#include "Vif_Dma.h" -#include "newVif.h" - -//------------------------------------------------------------------ -// VifCode Transfer Interpreter (Vif0/Vif1) -//------------------------------------------------------------------ - -// Doesn't stall if the next vifCode is the Mark command -_vifT bool runMark(u32* &data) { - if (((vifXRegs->code >> 24) & 0x7f) == 0x7) { - Console.WriteLn("Vif%d: Running Mark with I-bit", idx); - return 1; // No Stall? - } - return 1; // Stall -} - -// Returns 1 if i-bit && finished vifcode && i-bit not masked -_vifT bool analyzeIbit(u32* &data, int iBit) { - if (iBit && !vifX.cmd && !vifXRegs->err.MII) { - //DevCon.WriteLn("Vif I-Bit IRQ"); - vifX.irq++; - // On i-bit, the command is run, vif stalls etc, - // however if the vifcode is MARK, you do NOT stall, just send IRQ. - Max Payne shows this up. - //if(((vifXRegs->code >> 24) & 0x7f) == 0x7) return 0; - - // If we have a vifcode with i-bit, the following instruction - // should stall unless its MARK?.. we test that case here... - // Not 100% sure if this is the correct behavior, so printing - // a console message to see games that use this. (cottonvibes) - - // Okay did some testing with Max Payne, it does this - // VifMark value = 0x666 (i know, evil!) - // NOP with I Bit - // VifMark value = 0 - // - // If you break after the 2nd Mark has run, the game reports invalid mark 0 and the game dies. - // So it has to occur here, testing a theory that it only doesn't stall if the command with - // the iBit IS mark, but still sends the IRQ to let the cpu know the mark is there. (Refraction) - return runMark(data); - } - return 0; -} - -// Interprets packet -_vifT void vifTransferLoop(u32* &data) { - u32& pSize = vifX.vifpacketsize; - int iBit = vifX.cmd >> 7; - - vifXRegs->stat.VPS |= VPS_TRANSFERRING; - vifXRegs->stat.ER1 = false; - - while (pSize > 0 && !vifX.vifstalled) { - - if(!vifX.cmd) { // Get new VifCode - vifXRegs->code = data[0]; - vifX.cmd = data[0] >> 24; - iBit = data[0] >> 31; - VIF_LOG("New VifCMD %x tagsize %x", vifX.cmd, vifX.tag.size); - vifXCode[vifX.cmd & 0x7f](0, data); - data++; pSize--; - if (analyzeIbit(data, iBit)) break; - continue; - } - - int ret = vifXCode[vifX.cmd & 0x7f](1, data); - data += ret; - pSize -= ret; - if (analyzeIbit(data, iBit)) break; - } - if (vifX.cmd) vifXRegs->stat.VPS = VPS_WAITING; - else vifXRegs->stat.VPS = VPS_IDLE; - if (pSize) vifX.vifstalled = true; -} - -_vifT _f bool vifTransfer(u32 *data, int size) { - // irqoffset necessary to add up the right qws, or else will spin (spiderman) - int transferred = vifX.vifstalled ? vifX.irqoffset : 0; - - vifX.irqoffset = 0; - vifX.vifstalled = false; - vifX.stallontag = false; - vifX.vifpacketsize = size; - - vifTransferLoop(data); - - transferred += size - vifX.vifpacketsize; - g_vifCycles +=(transferred >> 2) * BIAS; /* guessing */ - vifX.irqoffset = transferred % 4; // cannot lose the offset - - transferred = transferred >> 2; - vifXch->madr +=(transferred << 4); - vifXch->qwc -= transferred; - - if (!vifXch->qwc && !vifX.irqoffset) vifX.inprogress &= ~0x1; - - if (vifX.irq && vifX.cmd == 0) { - //DevCon.WriteLn("Vif IRQ!"); - if(((vifXRegs->code >> 24) & 0x7f) != 0x7) - { - vifX.vifstalled = true; - vifXRegs->stat.VIS = true; // Note: commenting this out fixes WALL-E? - } - - if (!vifXch->qwc && !vifX.irqoffset) vifX.inprogress = 0; - return false; - } - - return !vifX.vifstalled; -} - -bool VIF0transfer(u32 *data, int size, bool istag) { - return vifTransfer<0>(data, size); -} -bool VIF1transfer(u32 *data, int size, bool istag) { - return vifTransfer<1>(data, size); -} +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2009 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 "Common.h" +#include "Vif_Dma.h" +#include "newVif.h" + +//------------------------------------------------------------------ +// VifCode Transfer Interpreter (Vif0/Vif1) +//------------------------------------------------------------------ + +// Doesn't stall if the next vifCode is the Mark command +_vifT bool runMark(u32* &data) { + if (((vifXRegs->code >> 24) & 0x7f) == 0x7) { + Console.WriteLn("Vif%d: Running Mark with I-bit", idx); + return 1; // No Stall? + } + return 1; // Stall +} + +// Returns 1 if i-bit && finished vifcode && i-bit not masked +_vifT bool analyzeIbit(u32* &data, int iBit) { + if (iBit && !vifX.cmd && !vifXRegs->err.MII) { + //DevCon.WriteLn("Vif I-Bit IRQ"); + vifX.irq++; + // On i-bit, the command is run, vif stalls etc, + // however if the vifcode is MARK, you do NOT stall, just send IRQ. - Max Payne shows this up. + //if(((vifXRegs->code >> 24) & 0x7f) == 0x7) return 0; + + // If we have a vifcode with i-bit, the following instruction + // should stall unless its MARK?.. we test that case here... + // Not 100% sure if this is the correct behavior, so printing + // a console message to see games that use this. (cottonvibes) + + // Okay did some testing with Max Payne, it does this + // VifMark value = 0x666 (i know, evil!) + // NOP with I Bit + // VifMark value = 0 + // + // If you break after the 2nd Mark has run, the game reports invalid mark 0 and the game dies. + // So it has to occur here, testing a theory that it only doesn't stall if the command with + // the iBit IS mark, but still sends the IRQ to let the cpu know the mark is there. (Refraction) + return runMark(data); + } + return 0; +} + +// Interprets packet +_vifT void vifTransferLoop(u32* &data) { + u32& pSize = vifX.vifpacketsize; + int iBit = vifX.cmd >> 7; + + vifXRegs->stat.VPS |= VPS_TRANSFERRING; + vifXRegs->stat.ER1 = false; + + while (pSize > 0 && !vifX.vifstalled) { + + if(!vifX.cmd) { // Get new VifCode + vifXRegs->code = data[0]; + vifX.cmd = data[0] >> 24; + iBit = data[0] >> 31; + VIF_LOG("New VifCMD %x tagsize %x", vifX.cmd, vifX.tag.size); + vifXCode[vifX.cmd & 0x7f](0, data); + data++; pSize--; + if (analyzeIbit(data, iBit)) break; + continue; + } + + int ret = vifXCode[vifX.cmd & 0x7f](1, data); + data += ret; + pSize -= ret; + if (analyzeIbit(data, iBit)) break; + } + if (vifX.cmd) vifXRegs->stat.VPS = VPS_WAITING; + else vifXRegs->stat.VPS = VPS_IDLE; + if (pSize) vifX.vifstalled = true; +} + +_vifT _f bool vifTransfer(u32 *data, int size) { + // irqoffset necessary to add up the right qws, or else will spin (spiderman) + int transferred = vifX.vifstalled ? vifX.irqoffset : 0; + + vifX.irqoffset = 0; + vifX.vifstalled = false; + vifX.stallontag = false; + vifX.vifpacketsize = size; + + vifTransferLoop(data); + + transferred += size - vifX.vifpacketsize; + g_vifCycles +=(transferred >> 2) * BIAS; /* guessing */ + vifX.irqoffset = transferred % 4; // cannot lose the offset + + transferred = transferred >> 2; + vifXch->madr +=(transferred << 4); + vifXch->qwc -= transferred; + + if (!vifXch->qwc && !vifX.irqoffset) vifX.inprogress &= ~0x1; + + if (vifX.irq && vifX.cmd == 0) { + //DevCon.WriteLn("Vif IRQ!"); + if(((vifXRegs->code >> 24) & 0x7f) != 0x7) + { + vifX.vifstalled = true; + vifXRegs->stat.VIS = true; // Note: commenting this out fixes WALL-E? + } + + if (!vifXch->qwc && !vifX.irqoffset) vifX.inprogress = 0; + return false; + } + + return !vifX.vifstalled; +} + +bool VIF0transfer(u32 *data, int size, bool istag) { + return vifTransfer<0>(data, size); +} +bool VIF1transfer(u32 *data, int size, bool istag) { + return vifTransfer<1>(data, size); +} diff --git a/pcsx2/Vif_Unpack.cpp b/pcsx2/Vif_Unpack.cpp index 153f17f4f4..1129da9141 100644 --- a/pcsx2/Vif_Unpack.cpp +++ b/pcsx2/Vif_Unpack.cpp @@ -1,339 +1,339 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2009 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 "Common.h" -#include "Vif.h" -#include "Vif_Dma.h" - -enum UnpackOffset { - OFFSET_X = 0, - OFFSET_Y = 1, - OFFSET_Z = 2, - OFFSET_W = 3 -}; - -static __forceinline u32 setVifRowRegs(u32 reg, u32 data) { - switch (reg) { - case 0: vifRegs->r0 = data; break; - case 1: vifRegs->r1 = data; break; - case 2: vifRegs->r2 = data; break; - case 3: vifRegs->r3 = data; break; - jNO_DEFAULT; - } - return data; -} - -static __forceinline u32 getVifRowRegs(u32 reg) { - switch (reg) { - case 0: return vifRegs->r0; break; - case 1: return vifRegs->r1; break; - case 2: return vifRegs->r2; break; - case 3: return vifRegs->r3; break; - jNO_DEFAULT; - } - return 0; // unreachable... -} - -static __forceinline u32 getVifColRegs(u32 reg) { - switch (reg) { - case 0: return vifRegs->c0; break; - case 1: return vifRegs->c1; break; - case 2: return vifRegs->c2; break; - default: return vifRegs->c3; break; - } - return 0; // unreachable... -} - -template< bool doMask > -static __releaseinline void writeXYZW(u32 offnum, u32 &dest, u32 data) { - u32 vifRowReg = getVifRowRegs(offnum); - int n = 0; - - if (doMask) { - switch (vif->cl) { - case 0: n = (vifRegs->mask >> (offnum * 2)) & 0x3; break; - case 1: n = (vifRegs->mask >> ( 8 + (offnum * 2))) & 0x3; break; - case 2: n = (vifRegs->mask >> (16 + (offnum * 2))) & 0x3; break; - default: n = (vifRegs->mask >> (24 + (offnum * 2))) & 0x3; break; - } - } - - switch (n) { - case 0: - if ((vif->cmd & 0x6F) != 0x6f) { - switch (vifRegs->mode) { - case 1: dest = data + vifRowReg; break; - case 2: dest = setVifRowRegs(offnum, vifRowReg + data); break; - default: dest = data; break; - } - } - else dest = data; // v4-5 Unpack Mode - break; - case 1: dest = vifRowReg; break; - case 2: dest = getVifColRegs(vif->cl); break; - case 3: break; - } -} - -template < bool doMask, class T > -static __forceinline void __fastcall UNPACK_S(u32 *dest, T *data, int size) -{ - //S-# will always be a complete packet, no matter what. So we can skip the offset bits - writeXYZW(OFFSET_X, *dest++, *data); - writeXYZW(OFFSET_Y, *dest++, *data); - writeXYZW(OFFSET_Z, *dest++, *data); - writeXYZW(OFFSET_W, *dest , *data); -} - -template -static __forceinline void __fastcall UNPACK_V2(u32 *dest, T *data, int size) -{ - if (vifRegs->offset == OFFSET_X) - { - if (size > 0) - { - writeXYZW(vifRegs->offset, *dest++, *data++); - vifRegs->offset = OFFSET_Y; - size--; - } - } - - if (vifRegs->offset == OFFSET_Y) - { - if (size > 0) - { - writeXYZW(vifRegs->offset, *dest++, *data); - vifRegs->offset = OFFSET_Z; - size--; - } - } - - if (vifRegs->offset == OFFSET_Z) - { - writeXYZW(vifRegs->offset, *dest++, *dest-2); - vifRegs->offset = OFFSET_W; - } - - if (vifRegs->offset == OFFSET_W) - { - writeXYZW(vifRegs->offset, *dest, *data); - vifRegs->offset = OFFSET_X; - } -} - -template -static __forceinline void __fastcall UNPACK_V3(u32 *dest, T *data, int size) -{ - if(vifRegs->offset == OFFSET_X) - { - if (size > 0) - { - writeXYZW(vifRegs->offset, *dest++, *data++); - vifRegs->offset = OFFSET_Y; - size--; - } - } - - if(vifRegs->offset == OFFSET_Y) - { - if (size > 0) - { - writeXYZW(vifRegs->offset, *dest++, *data++); - vifRegs->offset = OFFSET_Z; - size--; - } - } - - if(vifRegs->offset == OFFSET_Z) - { - if (size > 0) - { - writeXYZW(vifRegs->offset, *dest++, *data++); - vifRegs->offset = OFFSET_W; - size--; - } - } - - if(vifRegs->offset == OFFSET_W) - { - // V3-# does some bizarre thing with alignment, every 6qw of data the W becomes 0 (strange console!) - // Ape Escape doesn't seem to like it tho (what the hell?) gonna have to investigate - writeXYZW(vifRegs->offset, *dest, *data); - vifRegs->offset = OFFSET_X; - } -} - -template -static __forceinline void __fastcall UNPACK_V4(u32 *dest, T *data , int size) -{ - while (size > 0) - { - writeXYZW(vifRegs->offset, *dest++, *data++); - vifRegs->offset++; - size--; - } - - if (vifRegs->offset > OFFSET_W) vifRegs->offset = OFFSET_X; -} - -template< bool doMask > -static __releaseinline void __fastcall UNPACK_V4_5(u32 *dest, u32 *data, int size) -{ - //As with S-#, this will always be a complete packet - writeXYZW(OFFSET_X, *dest++, ((*data & 0x001f) << 3)); - writeXYZW(OFFSET_Y, *dest++, ((*data & 0x03e0) >> 2)); - writeXYZW(OFFSET_Z, *dest++, ((*data & 0x7c00) >> 7)); - writeXYZW(OFFSET_W, *dest, ((*data & 0x8000) >> 8)); -} - -// ===================================================================================================== - -template < bool doMask, int size, class T > -static void __fastcall fUNPACK_S(u32 *dest, T *data) -{ - UNPACK_S( dest, data, size ); -} - -template -static void __fastcall fUNPACK_V2(u32 *dest, T *data) -{ - UNPACK_V2( dest, data, size ); -} - -template -static void __fastcall fUNPACK_V3(u32 *dest, T *data) -{ - UNPACK_V3( dest, data, size ); -} - -template -static void __fastcall fUNPACK_V4(u32 *dest, T *data) -{ - UNPACK_V4( dest, data, size ); -} - -template< bool doMask > -static void __fastcall fUNPACK_V4_5(u32 *dest, u32 *data) -{ - UNPACK_V4_5(dest, data, 0); // size is ignored. -} - -// -------------------------------------------------------------------------------------- -// Main table for function unpacking. -// -------------------------------------------------------------------------------------- -// The extra data bsize/dsize/etc are all duplicated between the doMask enabled and -// disabled versions. This is probably simpler and more efficient than bothering -// to generate separate tables. -// -// The double-cast function pointer nonsense is to appease GCC, which gives some rather -// cryptic error about being unable to deduce the type parameters (I think it's a bug -// relating to __fastcall, which I recall having some other places as well). It's fixed -// by explicitly casting the function to itself prior to casting it to what we need it -// to be cast as. --air -// - -#define _upk (UNPACKFUNCTYPE) -#define _odd (UNPACKFUNCTYPE_ODD) -#define _unpk_s(bits) (UNPACKFUNCTYPE_S##bits) -#define _odd_s(bits) (UNPACKFUNCTYPE_ODD_S##bits) -#define _unpk_u(bits) (UNPACKFUNCTYPE_U##bits) -#define _odd_u(bits) (UNPACKFUNCTYPE_ODD_U##bits) - -// 32-bits versions are unsigned-only!! -#define UnpackFuncPair32( sizefac, vt, doMask ) \ - (UNPACKFUNCTYPE)_unpk_u(32) fUNPACK_##vt, \ - (UNPACKFUNCTYPE)_unpk_u(32) fUNPACK_##vt, \ - (UNPACKFUNCTYPE_ODD)_odd_u(32) UNPACK_##vt, \ - (UNPACKFUNCTYPE_ODD)_odd_u(32) UNPACK_##vt, - -#define UnpackFuncPair( sizefac, vt, bits, doMask ) \ - (UNPACKFUNCTYPE)_unpk_u(bits) fUNPACK_##vt, \ - (UNPACKFUNCTYPE)_unpk_s(bits) fUNPACK_##vt, \ - (UNPACKFUNCTYPE_ODD)_odd_u(bits) UNPACK_##vt, \ - (UNPACKFUNCTYPE_ODD)_odd_s(bits) UNPACK_##vt, - -#define UnpackFuncSet( doMask ) \ - { UnpackFuncPair32( 4, S, doMask ) 1, 4, 4, 4 }, /* 0x0 - S-32 */ \ - { UnpackFuncPair ( 4, S, 16, doMask ) 2, 2, 2, 4 }, /* 0x1 - S-16 */ \ - { UnpackFuncPair ( 4, S, 8, doMask ) 4, 1, 1, 4 }, /* 0x2 - S-8 */ \ - { NULL, NULL, NULL, NULL, 0, 0, 0, 0 }, /* 0x3 (NULL) */ \ - { UnpackFuncPair32( 2, V2, doMask ) 24, 4, 8, 2 }, /* 0x4 - V2-32 */ \ - { UnpackFuncPair ( 2, V2, 16, doMask ) 12, 2, 4, 2 }, /* 0x5 - V2-16 */ \ - { UnpackFuncPair ( 2, V2, 8, doMask ) 6, 1, 2, 2 }, /* 0x6 - V2-8 */ \ - { NULL, NULL, NULL, NULL,0, 0, 0, 0 }, /* 0x7 (NULL) */ \ - { UnpackFuncPair32( 3, V3, doMask ) 36, 4, 12, 3 }, /* 0x8 - V3-32 */ \ - { UnpackFuncPair ( 3, V3, 16, doMask ) 18, 2, 6, 3 }, /* 0x9 - V3-16 */ \ - { UnpackFuncPair ( 3, V3, 8, doMask ) 9, 1, 3, 3 }, /* 0xA - V3-8 */ \ - { NULL, NULL, NULL, NULL,0, 0, 0, 0 }, /* 0xB (NULL) */ \ - { UnpackFuncPair32( 4, V4, doMask ) 48, 4, 16, 4 }, /* 0xC - V4-32 */ \ - { UnpackFuncPair ( 4, V4, 16, doMask ) 24, 2, 8, 4 }, /* 0xD - V4-16 */ \ - { UnpackFuncPair ( 4, V4, 8, doMask ) 12, 1, 4, 4 }, /* 0xE - V4-8 */ \ - { /* 0xF - V4-5 */ \ - (UNPACKFUNCTYPE)_unpk_u(32) fUNPACK_V4_5, \ - (UNPACKFUNCTYPE)_unpk_u(32) fUNPACK_V4_5, \ - (UNPACKFUNCTYPE_ODD)_odd_u(32) UNPACK_V4_5, \ - (UNPACKFUNCTYPE_ODD)_odd_u(32) UNPACK_V4_5, \ - 6, 2, 2, 4 }, - -const __aligned16 VIFUnpackFuncTable VIFfuncTable[32] = -{ - UnpackFuncSet( false ) - UnpackFuncSet( true ) -}; - -//---------------------------------------------------------------------------- -// Unpack Setup Code -//---------------------------------------------------------------------------- - -_vifT void vifUnpackSetup(u32 *data) { - - if ((vifXRegs->cycle.wl == 0) && (vifXRegs->cycle.wl < vifXRegs->cycle.cl)) { - Console.WriteLn("Vif%d CL %d, WL %d", idx, vifXRegs->cycle.cl, vifXRegs->cycle.wl); - vifX.cmd = 0; - return; // Skipping write and 0 write-cycles, so do nothing! - } - - if (!idx) vif0FLUSH(); // Only VU0? - - vifX.usn = (vifXRegs->code >> 14) & 0x01; - int vifNum = (vifXRegs->code >> 16) & 0xff; - - if (vifNum == 0) vifNum = 256; - vifXRegs->num = vifNum; - - if (vifXRegs->cycle.wl <= vifXRegs->cycle.cl) { - if (!idx) vif0.tag.size = ((vifNum * VIFfuncTable[ vif0.cmd & 0xf ].gsize) + 3) >> 2; - else vif1.tag.size = ((vifNum * VIFfuncTable[ vif1.cmd & 0xf ].gsize) + 3) >> 2; - } - else { - int n = vifXRegs->cycle.cl * (vifNum / vifXRegs->cycle.wl) + - _limit(vifNum % vifXRegs->cycle.wl, vifXRegs->cycle.cl); - - if (!idx) vif0.tag.size = ((n * VIFfuncTable[ vif0.cmd & 0xf ].gsize) + 3) >> 2; - else vif1.tag.size = ((n * VIFfuncTable[ vif1.cmd & 0xf ].gsize) + 3) >> 2; - } - - u32 addr = vifXRegs->code; - if (idx && ((addr>>15)&1)) addr += vif1Regs->tops; - vifX.tag.addr = (addr<<4) & (idx ? 0x3ff0 : 0xff0); - - vifX.cl = 0; - vifX.tag.cmd = vifX.cmd; - vifXRegs->offset = 0; -} - -void vif0UnpackSetup(u32 *data) { vifUnpackSetup<0>(data); } -void vif1UnpackSetup(u32 *data) { vifUnpackSetup<1>(data); } +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2009 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 "Common.h" +#include "Vif.h" +#include "Vif_Dma.h" + +enum UnpackOffset { + OFFSET_X = 0, + OFFSET_Y = 1, + OFFSET_Z = 2, + OFFSET_W = 3 +}; + +static __forceinline u32 setVifRowRegs(u32 reg, u32 data) { + switch (reg) { + case 0: vifRegs->r0 = data; break; + case 1: vifRegs->r1 = data; break; + case 2: vifRegs->r2 = data; break; + case 3: vifRegs->r3 = data; break; + jNO_DEFAULT; + } + return data; +} + +static __forceinline u32 getVifRowRegs(u32 reg) { + switch (reg) { + case 0: return vifRegs->r0; break; + case 1: return vifRegs->r1; break; + case 2: return vifRegs->r2; break; + case 3: return vifRegs->r3; break; + jNO_DEFAULT; + } + return 0; // unreachable... +} + +static __forceinline u32 getVifColRegs(u32 reg) { + switch (reg) { + case 0: return vifRegs->c0; break; + case 1: return vifRegs->c1; break; + case 2: return vifRegs->c2; break; + default: return vifRegs->c3; break; + } + return 0; // unreachable... +} + +template< bool doMask > +static __releaseinline void writeXYZW(u32 offnum, u32 &dest, u32 data) { + u32 vifRowReg = getVifRowRegs(offnum); + int n = 0; + + if (doMask) { + switch (vif->cl) { + case 0: n = (vifRegs->mask >> (offnum * 2)) & 0x3; break; + case 1: n = (vifRegs->mask >> ( 8 + (offnum * 2))) & 0x3; break; + case 2: n = (vifRegs->mask >> (16 + (offnum * 2))) & 0x3; break; + default: n = (vifRegs->mask >> (24 + (offnum * 2))) & 0x3; break; + } + } + + switch (n) { + case 0: + if ((vif->cmd & 0x6F) != 0x6f) { + switch (vifRegs->mode) { + case 1: dest = data + vifRowReg; break; + case 2: dest = setVifRowRegs(offnum, vifRowReg + data); break; + default: dest = data; break; + } + } + else dest = data; // v4-5 Unpack Mode + break; + case 1: dest = vifRowReg; break; + case 2: dest = getVifColRegs(vif->cl); break; + case 3: break; + } +} + +template < bool doMask, class T > +static __forceinline void __fastcall UNPACK_S(u32 *dest, T *data, int size) +{ + //S-# will always be a complete packet, no matter what. So we can skip the offset bits + writeXYZW(OFFSET_X, *dest++, *data); + writeXYZW(OFFSET_Y, *dest++, *data); + writeXYZW(OFFSET_Z, *dest++, *data); + writeXYZW(OFFSET_W, *dest , *data); +} + +template +static __forceinline void __fastcall UNPACK_V2(u32 *dest, T *data, int size) +{ + if (vifRegs->offset == OFFSET_X) + { + if (size > 0) + { + writeXYZW(vifRegs->offset, *dest++, *data++); + vifRegs->offset = OFFSET_Y; + size--; + } + } + + if (vifRegs->offset == OFFSET_Y) + { + if (size > 0) + { + writeXYZW(vifRegs->offset, *dest++, *data); + vifRegs->offset = OFFSET_Z; + size--; + } + } + + if (vifRegs->offset == OFFSET_Z) + { + writeXYZW(vifRegs->offset, *dest++, *dest-2); + vifRegs->offset = OFFSET_W; + } + + if (vifRegs->offset == OFFSET_W) + { + writeXYZW(vifRegs->offset, *dest, *data); + vifRegs->offset = OFFSET_X; + } +} + +template +static __forceinline void __fastcall UNPACK_V3(u32 *dest, T *data, int size) +{ + if(vifRegs->offset == OFFSET_X) + { + if (size > 0) + { + writeXYZW(vifRegs->offset, *dest++, *data++); + vifRegs->offset = OFFSET_Y; + size--; + } + } + + if(vifRegs->offset == OFFSET_Y) + { + if (size > 0) + { + writeXYZW(vifRegs->offset, *dest++, *data++); + vifRegs->offset = OFFSET_Z; + size--; + } + } + + if(vifRegs->offset == OFFSET_Z) + { + if (size > 0) + { + writeXYZW(vifRegs->offset, *dest++, *data++); + vifRegs->offset = OFFSET_W; + size--; + } + } + + if(vifRegs->offset == OFFSET_W) + { + // V3-# does some bizarre thing with alignment, every 6qw of data the W becomes 0 (strange console!) + // Ape Escape doesn't seem to like it tho (what the hell?) gonna have to investigate + writeXYZW(vifRegs->offset, *dest, *data); + vifRegs->offset = OFFSET_X; + } +} + +template +static __forceinline void __fastcall UNPACK_V4(u32 *dest, T *data , int size) +{ + while (size > 0) + { + writeXYZW(vifRegs->offset, *dest++, *data++); + vifRegs->offset++; + size--; + } + + if (vifRegs->offset > OFFSET_W) vifRegs->offset = OFFSET_X; +} + +template< bool doMask > +static __releaseinline void __fastcall UNPACK_V4_5(u32 *dest, u32 *data, int size) +{ + //As with S-#, this will always be a complete packet + writeXYZW(OFFSET_X, *dest++, ((*data & 0x001f) << 3)); + writeXYZW(OFFSET_Y, *dest++, ((*data & 0x03e0) >> 2)); + writeXYZW(OFFSET_Z, *dest++, ((*data & 0x7c00) >> 7)); + writeXYZW(OFFSET_W, *dest, ((*data & 0x8000) >> 8)); +} + +// ===================================================================================================== + +template < bool doMask, int size, class T > +static void __fastcall fUNPACK_S(u32 *dest, T *data) +{ + UNPACK_S( dest, data, size ); +} + +template +static void __fastcall fUNPACK_V2(u32 *dest, T *data) +{ + UNPACK_V2( dest, data, size ); +} + +template +static void __fastcall fUNPACK_V3(u32 *dest, T *data) +{ + UNPACK_V3( dest, data, size ); +} + +template +static void __fastcall fUNPACK_V4(u32 *dest, T *data) +{ + UNPACK_V4( dest, data, size ); +} + +template< bool doMask > +static void __fastcall fUNPACK_V4_5(u32 *dest, u32 *data) +{ + UNPACK_V4_5(dest, data, 0); // size is ignored. +} + +// -------------------------------------------------------------------------------------- +// Main table for function unpacking. +// -------------------------------------------------------------------------------------- +// The extra data bsize/dsize/etc are all duplicated between the doMask enabled and +// disabled versions. This is probably simpler and more efficient than bothering +// to generate separate tables. +// +// The double-cast function pointer nonsense is to appease GCC, which gives some rather +// cryptic error about being unable to deduce the type parameters (I think it's a bug +// relating to __fastcall, which I recall having some other places as well). It's fixed +// by explicitly casting the function to itself prior to casting it to what we need it +// to be cast as. --air +// + +#define _upk (UNPACKFUNCTYPE) +#define _odd (UNPACKFUNCTYPE_ODD) +#define _unpk_s(bits) (UNPACKFUNCTYPE_S##bits) +#define _odd_s(bits) (UNPACKFUNCTYPE_ODD_S##bits) +#define _unpk_u(bits) (UNPACKFUNCTYPE_U##bits) +#define _odd_u(bits) (UNPACKFUNCTYPE_ODD_U##bits) + +// 32-bits versions are unsigned-only!! +#define UnpackFuncPair32( sizefac, vt, doMask ) \ + (UNPACKFUNCTYPE)_unpk_u(32) fUNPACK_##vt, \ + (UNPACKFUNCTYPE)_unpk_u(32) fUNPACK_##vt, \ + (UNPACKFUNCTYPE_ODD)_odd_u(32) UNPACK_##vt, \ + (UNPACKFUNCTYPE_ODD)_odd_u(32) UNPACK_##vt, + +#define UnpackFuncPair( sizefac, vt, bits, doMask ) \ + (UNPACKFUNCTYPE)_unpk_u(bits) fUNPACK_##vt, \ + (UNPACKFUNCTYPE)_unpk_s(bits) fUNPACK_##vt, \ + (UNPACKFUNCTYPE_ODD)_odd_u(bits) UNPACK_##vt, \ + (UNPACKFUNCTYPE_ODD)_odd_s(bits) UNPACK_##vt, + +#define UnpackFuncSet( doMask ) \ + { UnpackFuncPair32( 4, S, doMask ) 1, 4, 4, 4 }, /* 0x0 - S-32 */ \ + { UnpackFuncPair ( 4, S, 16, doMask ) 2, 2, 2, 4 }, /* 0x1 - S-16 */ \ + { UnpackFuncPair ( 4, S, 8, doMask ) 4, 1, 1, 4 }, /* 0x2 - S-8 */ \ + { NULL, NULL, NULL, NULL, 0, 0, 0, 0 }, /* 0x3 (NULL) */ \ + { UnpackFuncPair32( 2, V2, doMask ) 24, 4, 8, 2 }, /* 0x4 - V2-32 */ \ + { UnpackFuncPair ( 2, V2, 16, doMask ) 12, 2, 4, 2 }, /* 0x5 - V2-16 */ \ + { UnpackFuncPair ( 2, V2, 8, doMask ) 6, 1, 2, 2 }, /* 0x6 - V2-8 */ \ + { NULL, NULL, NULL, NULL,0, 0, 0, 0 }, /* 0x7 (NULL) */ \ + { UnpackFuncPair32( 3, V3, doMask ) 36, 4, 12, 3 }, /* 0x8 - V3-32 */ \ + { UnpackFuncPair ( 3, V3, 16, doMask ) 18, 2, 6, 3 }, /* 0x9 - V3-16 */ \ + { UnpackFuncPair ( 3, V3, 8, doMask ) 9, 1, 3, 3 }, /* 0xA - V3-8 */ \ + { NULL, NULL, NULL, NULL,0, 0, 0, 0 }, /* 0xB (NULL) */ \ + { UnpackFuncPair32( 4, V4, doMask ) 48, 4, 16, 4 }, /* 0xC - V4-32 */ \ + { UnpackFuncPair ( 4, V4, 16, doMask ) 24, 2, 8, 4 }, /* 0xD - V4-16 */ \ + { UnpackFuncPair ( 4, V4, 8, doMask ) 12, 1, 4, 4 }, /* 0xE - V4-8 */ \ + { /* 0xF - V4-5 */ \ + (UNPACKFUNCTYPE)_unpk_u(32) fUNPACK_V4_5, \ + (UNPACKFUNCTYPE)_unpk_u(32) fUNPACK_V4_5, \ + (UNPACKFUNCTYPE_ODD)_odd_u(32) UNPACK_V4_5, \ + (UNPACKFUNCTYPE_ODD)_odd_u(32) UNPACK_V4_5, \ + 6, 2, 2, 4 }, + +const __aligned16 VIFUnpackFuncTable VIFfuncTable[32] = +{ + UnpackFuncSet( false ) + UnpackFuncSet( true ) +}; + +//---------------------------------------------------------------------------- +// Unpack Setup Code +//---------------------------------------------------------------------------- + +_vifT void vifUnpackSetup(u32 *data) { + + if ((vifXRegs->cycle.wl == 0) && (vifXRegs->cycle.wl < vifXRegs->cycle.cl)) { + Console.WriteLn("Vif%d CL %d, WL %d", idx, vifXRegs->cycle.cl, vifXRegs->cycle.wl); + vifX.cmd = 0; + return; // Skipping write and 0 write-cycles, so do nothing! + } + + if (!idx) vif0FLUSH(); // Only VU0? + + vifX.usn = (vifXRegs->code >> 14) & 0x01; + int vifNum = (vifXRegs->code >> 16) & 0xff; + + if (vifNum == 0) vifNum = 256; + vifXRegs->num = vifNum; + + if (vifXRegs->cycle.wl <= vifXRegs->cycle.cl) { + if (!idx) vif0.tag.size = ((vifNum * VIFfuncTable[ vif0.cmd & 0xf ].gsize) + 3) >> 2; + else vif1.tag.size = ((vifNum * VIFfuncTable[ vif1.cmd & 0xf ].gsize) + 3) >> 2; + } + else { + int n = vifXRegs->cycle.cl * (vifNum / vifXRegs->cycle.wl) + + _limit(vifNum % vifXRegs->cycle.wl, vifXRegs->cycle.cl); + + if (!idx) vif0.tag.size = ((n * VIFfuncTable[ vif0.cmd & 0xf ].gsize) + 3) >> 2; + else vif1.tag.size = ((n * VIFfuncTable[ vif1.cmd & 0xf ].gsize) + 3) >> 2; + } + + u32 addr = vifXRegs->code; + if (idx && ((addr>>15)&1)) addr += vif1Regs->tops; + vifX.tag.addr = (addr<<4) & (idx ? 0x3ff0 : 0xff0); + + vifX.cl = 0; + vifX.tag.cmd = vifX.cmd; + vifXRegs->offset = 0; +} + +void vif0UnpackSetup(u32 *data) { vifUnpackSetup<0>(data); } +void vif1UnpackSetup(u32 *data) { vifUnpackSetup<1>(data); } diff --git a/pcsx2/Vif_Unpack.h b/pcsx2/Vif_Unpack.h index 37600e42e6..0271bfdc65 100644 --- a/pcsx2/Vif_Unpack.h +++ b/pcsx2/Vif_Unpack.h @@ -1,59 +1,59 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2009 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 . - */ - -#pragma once - -typedef void (__fastcall *UNPACKFUNCTYPE)(u32 *dest, u32 *data); -typedef void (__fastcall *UNPACKFUNCTYPE_ODD)(u32 *dest, u32 *data, int size); -typedef int (*UNPACKPARTFUNCTYPESSE)(u32 *dest, u32 *data, int size); - -#define create_unpack_u_type(bits) typedef void (__fastcall *UNPACKFUNCTYPE_U##bits)(u32 *dest, u##bits *data); -#define create_unpack_odd_u_type(bits) typedef void (__fastcall *UNPACKFUNCTYPE_ODD_U##bits)(u32 *dest, u##bits *data, int size); -#define create_unpack_s_type(bits) typedef void (__fastcall *UNPACKFUNCTYPE_S##bits)(u32 *dest, s##bits *data); -#define create_unpack_odd_s_type(bits) typedef void (__fastcall *UNPACKFUNCTYPE_ODD_S##bits)(u32 *dest, s##bits *data, int size); - -#define create_some_unpacks(bits) \ - create_unpack_u_type(bits); \ - create_unpack_odd_u_type(bits); \ - create_unpack_s_type(bits); \ - create_unpack_odd_s_type(bits); - -create_some_unpacks(32); -create_some_unpacks(16); -create_some_unpacks(8); - -struct VIFUnpackFuncTable -{ - UNPACKFUNCTYPE funcU; - UNPACKFUNCTYPE funcS; - - UNPACKFUNCTYPE_ODD oddU; // needed for old-style vif only, remove when old vif is removed. - UNPACKFUNCTYPE_ODD oddS; // needed for old-style vif only, remove when old vif is removed. - - u8 bsize; // currently unused - u8 dsize; // byte size of one channel - u8 gsize; // size of data in bytes used for each write cycle - u8 qsize; // used for unpack parts, num of vectors that - // will be decompressed from data for 1 cycle -}; - -extern const __aligned16 VIFUnpackFuncTable VIFfuncTable[32]; - -extern int nVifUnpack (int idx, u8 *data); -extern void initNewVif (int idx); -extern void resetNewVif(int idx); - -extern __forceinline void vif0UnpackSetup(u32 *data); -extern __forceinline void vif1UnpackSetup(u32 *data); +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2009 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 . + */ + +#pragma once + +typedef void (__fastcall *UNPACKFUNCTYPE)(u32 *dest, u32 *data); +typedef void (__fastcall *UNPACKFUNCTYPE_ODD)(u32 *dest, u32 *data, int size); +typedef int (*UNPACKPARTFUNCTYPESSE)(u32 *dest, u32 *data, int size); + +#define create_unpack_u_type(bits) typedef void (__fastcall *UNPACKFUNCTYPE_U##bits)(u32 *dest, u##bits *data); +#define create_unpack_odd_u_type(bits) typedef void (__fastcall *UNPACKFUNCTYPE_ODD_U##bits)(u32 *dest, u##bits *data, int size); +#define create_unpack_s_type(bits) typedef void (__fastcall *UNPACKFUNCTYPE_S##bits)(u32 *dest, s##bits *data); +#define create_unpack_odd_s_type(bits) typedef void (__fastcall *UNPACKFUNCTYPE_ODD_S##bits)(u32 *dest, s##bits *data, int size); + +#define create_some_unpacks(bits) \ + create_unpack_u_type(bits); \ + create_unpack_odd_u_type(bits); \ + create_unpack_s_type(bits); \ + create_unpack_odd_s_type(bits); + +create_some_unpacks(32); +create_some_unpacks(16); +create_some_unpacks(8); + +struct VIFUnpackFuncTable +{ + UNPACKFUNCTYPE funcU; + UNPACKFUNCTYPE funcS; + + UNPACKFUNCTYPE_ODD oddU; // needed for old-style vif only, remove when old vif is removed. + UNPACKFUNCTYPE_ODD oddS; // needed for old-style vif only, remove when old vif is removed. + + u8 bsize; // currently unused + u8 dsize; // byte size of one channel + u8 gsize; // size of data in bytes used for each write cycle + u8 qsize; // used for unpack parts, num of vectors that + // will be decompressed from data for 1 cycle +}; + +extern const __aligned16 VIFUnpackFuncTable VIFfuncTable[32]; + +extern int nVifUnpack (int idx, u8 *data); +extern void initNewVif (int idx); +extern void resetNewVif(int idx); + +extern __forceinline void vif0UnpackSetup(u32 *data); +extern __forceinline void vif1UnpackSetup(u32 *data); diff --git a/pcsx2/gui/AppEventListeners.h b/pcsx2/gui/AppEventListeners.h index 34db5bd9d7..eacf45019e 100644 --- a/pcsx2/gui/AppEventListeners.h +++ b/pcsx2/gui/AppEventListeners.h @@ -1,244 +1,244 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2009 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 . - */ - -#pragma once - -#include "Utilities/EventSource.h" - -enum CoreThreadStatus -{ - CoreThread_Indeterminate, - CoreThread_Started, - CoreThread_Resumed, - CoreThread_Suspended, - CoreThread_Reset, - CoreThread_Stopped, -}; - -enum AppEventType -{ - AppStatus_SettingsLoaded, - AppStatus_SettingsSaved, - AppStatus_SettingsApplied, - AppStatus_Exiting -}; - -enum PluginEventType -{ - CorePlugins_Loaded, - CorePlugins_Init, - CorePlugins_Opening, // dispatched prior to plugins being opened - CorePlugins_Opened, // dispatched after plugins are opened - CorePlugins_Closing, // dispatched prior to plugins being closed - CorePlugins_Closed, // dispatched after plugins are closed - CorePlugins_Shutdown, - CorePlugins_Unloaded, -}; - -struct AppEventInfo -{ - AppEventType evt_type; - - AppEventInfo( AppEventType type ) - { - evt_type = type; - } -}; - -struct AppSettingsEventInfo : AppEventInfo -{ - IniInterface& m_ini; - - AppSettingsEventInfo( IniInterface& ini ); - - IniInterface& GetIni() const - { - return const_cast(m_ini); - } -}; - -// -------------------------------------------------------------------------------------- -// IEventListener_CoreThread -// -------------------------------------------------------------------------------------- -class IEventListener_CoreThread : public IEventDispatcher -{ -public: - typedef CoreThreadStatus EvtParams; - -public: - virtual ~IEventListener_CoreThread() throw() {} - - virtual void DispatchEvent( const CoreThreadStatus& status ); - -protected: - virtual void CoreThread_OnStarted() {} - virtual void CoreThread_OnResumed() {} - virtual void CoreThread_OnSuspended() {} - virtual void CoreThread_OnReset() {} - virtual void CoreThread_OnStopped() {} -}; - -class EventListener_CoreThread : public IEventListener_CoreThread -{ -public: - EventListener_CoreThread(); - virtual ~EventListener_CoreThread() throw(); -}; - -// -------------------------------------------------------------------------------------- -// IEventListener_Plugins -// -------------------------------------------------------------------------------------- -class IEventListener_Plugins : public IEventDispatcher -{ -public: - typedef PluginEventType EvtParams; - -public: - virtual ~IEventListener_Plugins() throw() {} - - virtual void DispatchEvent( const PluginEventType& pevt ); - -protected: - virtual void CorePlugins_OnLoaded() {} - virtual void CorePlugins_OnInit() {} - virtual void CorePlugins_OnOpening() {} // dispatched prior to plugins being opened - virtual void CorePlugins_OnOpened() {} // dispatched after plugins are opened - virtual void CorePlugins_OnClosing() {} // dispatched prior to plugins being closed - virtual void CorePlugins_OnClosed() {} // dispatched after plugins are closed - virtual void CorePlugins_OnShutdown() {} - virtual void CorePlugins_OnUnloaded() {} -}; - -class EventListener_Plugins : public IEventListener_Plugins -{ -public: - EventListener_Plugins(); - virtual ~EventListener_Plugins() throw(); -}; - -// -------------------------------------------------------------------------------------- -// IEventListener_AppStatus -// -------------------------------------------------------------------------------------- -class IEventListener_AppStatus : public IEventDispatcher -{ -public: - typedef AppEventInfo EvtParams; - -public: - virtual ~IEventListener_AppStatus() throw() {} - - virtual void DispatchEvent( const AppEventInfo& evtinfo ); - -protected: - virtual void AppStatusEvent_OnSettingsLoadSave( const AppSettingsEventInfo& evtinfo ) {} - virtual void AppStatusEvent_OnSettingsApplied() {} - virtual void AppStatusEvent_OnExit() {} -}; - -class EventListener_AppStatus : public IEventListener_AppStatus -{ -public: - EventListener_AppStatus(); - virtual ~EventListener_AppStatus() throw(); -}; - -// -------------------------------------------------------------------------------------- -// EventListenerHelpers (CoreThread / Plugins / AppStatus) -// -------------------------------------------------------------------------------------- -// Welcome to the awkward world of C++ multi-inheritence. wxWidgets' Connect() system is -// incompatible because of limitations in C++ class member function pointers, so we need -// this second layer class to act as a bridge between the event system and the class's -// handler implementations. -// - -template< typename TypeToDispatchTo > -class EventListenerHelper_CoreThread : public EventListener_CoreThread -{ -public: - TypeToDispatchTo& Owner; - -public: - EventListenerHelper_CoreThread( TypeToDispatchTo& dispatchTo ) - : Owner( dispatchTo ) { } - - EventListenerHelper_CoreThread( TypeToDispatchTo* dispatchTo ) - : Owner( *dispatchTo ) - { - pxAssume(dispatchTo != NULL); - } - - virtual ~EventListenerHelper_CoreThread() throw() {} - -protected: - void OnCoreThread_Indeterminate() { Owner.OnCoreThread_Indeterminate(); } - void CoreThread_OnStarted() { Owner.OnCoreThread_Started(); } - void CoreThread_OnResumed() { Owner.OnCoreThread_Resumed(); } - void CoreThread_OnSuspended() { Owner.OnCoreThread_Suspended(); } - void CoreThread_OnReset() { Owner.OnCoreThread_Reset(); } - void CoreThread_OnStopped() { Owner.OnCoreThread_Stopped(); } -}; - -template< typename TypeToDispatchTo > -class EventListenerHelper_Plugins : public EventListener_Plugins -{ -public: - TypeToDispatchTo& Owner; - -public: - EventListenerHelper_Plugins( TypeToDispatchTo& dispatchTo ) - : Owner( dispatchTo ) { } - - EventListenerHelper_Plugins( TypeToDispatchTo* dispatchTo ) - : Owner( *dispatchTo ) - { - pxAssume(dispatchTo != NULL); - } - - virtual ~EventListenerHelper_Plugins() throw() {} - -protected: - void CorePlugins_OnLoaded() { Owner.OnCorePlugins_Loaded(); } - void CorePlugins_OnInit() { Owner.OnCorePlugins_Init(); } - void CorePlugins_OnOpening() { Owner.OnCorePlugins_Opening(); } - void CorePlugins_OnOpened() { Owner.OnCorePlugins_Opened(); } - void CorePlugins_OnClosing() { Owner.OnCorePlugins_Closing(); } - void CorePlugins_OnClosed() { Owner.OnCorePlugins_Closed(); } - void CorePlugins_OnShutdown() { Owner.OnCorePlugins_Shutdown(); } - void CorePlugins_OnUnloaded() { Owner.OnCorePlugins_Unloaded(); } -}; - -template< typename TypeToDispatchTo > -class EventListenerHelper_AppStatus : public EventListener_AppStatus -{ -public: - TypeToDispatchTo& Owner; - -public: - EventListenerHelper_AppStatus( TypeToDispatchTo& dispatchTo ) - : Owner( dispatchTo ) { } - - EventListenerHelper_AppStatus( TypeToDispatchTo* dispatchTo ) - : Owner( *dispatchTo ) - { - pxAssume(dispatchTo != NULL); - } - - virtual ~EventListenerHelper_AppStatus() throw() {} - -protected: - virtual void AppStatusEvent_OnSettingsLoadSave( const AppSettingsEventInfo& evtinfo ) { Owner.AppStatusEvent_OnSettingsLoadSave( evtinfo ); } - virtual void AppStatusEvent_OnSettingsApplied() { Owner.AppStatusEvent_OnSettingsApplied(); } - virtual void AppStatusEvent_OnExit() { Owner.AppStatusEvent_OnExit(); } -}; +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2009 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 . + */ + +#pragma once + +#include "Utilities/EventSource.h" + +enum CoreThreadStatus +{ + CoreThread_Indeterminate, + CoreThread_Started, + CoreThread_Resumed, + CoreThread_Suspended, + CoreThread_Reset, + CoreThread_Stopped, +}; + +enum AppEventType +{ + AppStatus_SettingsLoaded, + AppStatus_SettingsSaved, + AppStatus_SettingsApplied, + AppStatus_Exiting +}; + +enum PluginEventType +{ + CorePlugins_Loaded, + CorePlugins_Init, + CorePlugins_Opening, // dispatched prior to plugins being opened + CorePlugins_Opened, // dispatched after plugins are opened + CorePlugins_Closing, // dispatched prior to plugins being closed + CorePlugins_Closed, // dispatched after plugins are closed + CorePlugins_Shutdown, + CorePlugins_Unloaded, +}; + +struct AppEventInfo +{ + AppEventType evt_type; + + AppEventInfo( AppEventType type ) + { + evt_type = type; + } +}; + +struct AppSettingsEventInfo : AppEventInfo +{ + IniInterface& m_ini; + + AppSettingsEventInfo( IniInterface& ini ); + + IniInterface& GetIni() const + { + return const_cast(m_ini); + } +}; + +// -------------------------------------------------------------------------------------- +// IEventListener_CoreThread +// -------------------------------------------------------------------------------------- +class IEventListener_CoreThread : public IEventDispatcher +{ +public: + typedef CoreThreadStatus EvtParams; + +public: + virtual ~IEventListener_CoreThread() throw() {} + + virtual void DispatchEvent( const CoreThreadStatus& status ); + +protected: + virtual void CoreThread_OnStarted() {} + virtual void CoreThread_OnResumed() {} + virtual void CoreThread_OnSuspended() {} + virtual void CoreThread_OnReset() {} + virtual void CoreThread_OnStopped() {} +}; + +class EventListener_CoreThread : public IEventListener_CoreThread +{ +public: + EventListener_CoreThread(); + virtual ~EventListener_CoreThread() throw(); +}; + +// -------------------------------------------------------------------------------------- +// IEventListener_Plugins +// -------------------------------------------------------------------------------------- +class IEventListener_Plugins : public IEventDispatcher +{ +public: + typedef PluginEventType EvtParams; + +public: + virtual ~IEventListener_Plugins() throw() {} + + virtual void DispatchEvent( const PluginEventType& pevt ); + +protected: + virtual void CorePlugins_OnLoaded() {} + virtual void CorePlugins_OnInit() {} + virtual void CorePlugins_OnOpening() {} // dispatched prior to plugins being opened + virtual void CorePlugins_OnOpened() {} // dispatched after plugins are opened + virtual void CorePlugins_OnClosing() {} // dispatched prior to plugins being closed + virtual void CorePlugins_OnClosed() {} // dispatched after plugins are closed + virtual void CorePlugins_OnShutdown() {} + virtual void CorePlugins_OnUnloaded() {} +}; + +class EventListener_Plugins : public IEventListener_Plugins +{ +public: + EventListener_Plugins(); + virtual ~EventListener_Plugins() throw(); +}; + +// -------------------------------------------------------------------------------------- +// IEventListener_AppStatus +// -------------------------------------------------------------------------------------- +class IEventListener_AppStatus : public IEventDispatcher +{ +public: + typedef AppEventInfo EvtParams; + +public: + virtual ~IEventListener_AppStatus() throw() {} + + virtual void DispatchEvent( const AppEventInfo& evtinfo ); + +protected: + virtual void AppStatusEvent_OnSettingsLoadSave( const AppSettingsEventInfo& evtinfo ) {} + virtual void AppStatusEvent_OnSettingsApplied() {} + virtual void AppStatusEvent_OnExit() {} +}; + +class EventListener_AppStatus : public IEventListener_AppStatus +{ +public: + EventListener_AppStatus(); + virtual ~EventListener_AppStatus() throw(); +}; + +// -------------------------------------------------------------------------------------- +// EventListenerHelpers (CoreThread / Plugins / AppStatus) +// -------------------------------------------------------------------------------------- +// Welcome to the awkward world of C++ multi-inheritence. wxWidgets' Connect() system is +// incompatible because of limitations in C++ class member function pointers, so we need +// this second layer class to act as a bridge between the event system and the class's +// handler implementations. +// + +template< typename TypeToDispatchTo > +class EventListenerHelper_CoreThread : public EventListener_CoreThread +{ +public: + TypeToDispatchTo& Owner; + +public: + EventListenerHelper_CoreThread( TypeToDispatchTo& dispatchTo ) + : Owner( dispatchTo ) { } + + EventListenerHelper_CoreThread( TypeToDispatchTo* dispatchTo ) + : Owner( *dispatchTo ) + { + pxAssume(dispatchTo != NULL); + } + + virtual ~EventListenerHelper_CoreThread() throw() {} + +protected: + void OnCoreThread_Indeterminate() { Owner.OnCoreThread_Indeterminate(); } + void CoreThread_OnStarted() { Owner.OnCoreThread_Started(); } + void CoreThread_OnResumed() { Owner.OnCoreThread_Resumed(); } + void CoreThread_OnSuspended() { Owner.OnCoreThread_Suspended(); } + void CoreThread_OnReset() { Owner.OnCoreThread_Reset(); } + void CoreThread_OnStopped() { Owner.OnCoreThread_Stopped(); } +}; + +template< typename TypeToDispatchTo > +class EventListenerHelper_Plugins : public EventListener_Plugins +{ +public: + TypeToDispatchTo& Owner; + +public: + EventListenerHelper_Plugins( TypeToDispatchTo& dispatchTo ) + : Owner( dispatchTo ) { } + + EventListenerHelper_Plugins( TypeToDispatchTo* dispatchTo ) + : Owner( *dispatchTo ) + { + pxAssume(dispatchTo != NULL); + } + + virtual ~EventListenerHelper_Plugins() throw() {} + +protected: + void CorePlugins_OnLoaded() { Owner.OnCorePlugins_Loaded(); } + void CorePlugins_OnInit() { Owner.OnCorePlugins_Init(); } + void CorePlugins_OnOpening() { Owner.OnCorePlugins_Opening(); } + void CorePlugins_OnOpened() { Owner.OnCorePlugins_Opened(); } + void CorePlugins_OnClosing() { Owner.OnCorePlugins_Closing(); } + void CorePlugins_OnClosed() { Owner.OnCorePlugins_Closed(); } + void CorePlugins_OnShutdown() { Owner.OnCorePlugins_Shutdown(); } + void CorePlugins_OnUnloaded() { Owner.OnCorePlugins_Unloaded(); } +}; + +template< typename TypeToDispatchTo > +class EventListenerHelper_AppStatus : public EventListener_AppStatus +{ +public: + TypeToDispatchTo& Owner; + +public: + EventListenerHelper_AppStatus( TypeToDispatchTo& dispatchTo ) + : Owner( dispatchTo ) { } + + EventListenerHelper_AppStatus( TypeToDispatchTo* dispatchTo ) + : Owner( *dispatchTo ) + { + pxAssume(dispatchTo != NULL); + } + + virtual ~EventListenerHelper_AppStatus() throw() {} + +protected: + virtual void AppStatusEvent_OnSettingsLoadSave( const AppSettingsEventInfo& evtinfo ) { Owner.AppStatusEvent_OnSettingsLoadSave( evtinfo ); } + virtual void AppStatusEvent_OnSettingsApplied() { Owner.AppStatusEvent_OnSettingsApplied(); } + virtual void AppStatusEvent_OnExit() { Owner.AppStatusEvent_OnExit(); } +}; diff --git a/pcsx2/gui/AppEventSources.cpp b/pcsx2/gui/AppEventSources.cpp index d53d9a7c8f..1600610fc8 100644 --- a/pcsx2/gui/AppEventSources.cpp +++ b/pcsx2/gui/AppEventSources.cpp @@ -1,104 +1,104 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2009 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 "App.h" -#include "IniInterface.h" -#include "Utilities/EventSource.inl" - -template class EventSource< IEventListener_CoreThread >; -template class EventSource< IEventListener_Plugins >; -template class EventSource< IEventListener_AppStatus >; - -AppSettingsEventInfo::AppSettingsEventInfo( IniInterface& ini ) - : AppEventInfo( ini.IsSaving() ? AppStatus_SettingsSaved : AppStatus_SettingsLoaded ) - , m_ini( ini ) -{ -} - -EventListener_CoreThread::EventListener_CoreThread() -{ - wxGetApp().AddListener( this ); -} - -EventListener_CoreThread::~EventListener_CoreThread() throw() -{ - wxGetApp().RemoveListener( this ); -} - -void IEventListener_CoreThread::DispatchEvent( const CoreThreadStatus& status ) -{ - switch( status ) - { - case CoreThread_Started: CoreThread_OnStarted(); break; - case CoreThread_Resumed: CoreThread_OnResumed(); break; - case CoreThread_Suspended: CoreThread_OnSuspended(); break; - case CoreThread_Reset: CoreThread_OnReset(); break; - case CoreThread_Stopped: CoreThread_OnStopped(); break; - - jNO_DEFAULT; - } -} - -EventListener_Plugins::EventListener_Plugins() -{ - wxGetApp().AddListener( this ); -} - -EventListener_Plugins::~EventListener_Plugins() throw() -{ - wxGetApp().RemoveListener( this ); -} - -void IEventListener_Plugins::DispatchEvent( const PluginEventType& pevt ) -{ - switch( pevt ) - { - case CorePlugins_Loaded: CorePlugins_OnLoaded(); break; - case CorePlugins_Init: CorePlugins_OnInit(); break; - case CorePlugins_Opening: CorePlugins_OnOpening(); break; - case CorePlugins_Opened: CorePlugins_OnOpened(); break; - case CorePlugins_Closing: CorePlugins_OnClosing(); break; - case CorePlugins_Closed: CorePlugins_OnClosed(); break; - case CorePlugins_Shutdown: CorePlugins_OnShutdown(); break; - case CorePlugins_Unloaded: CorePlugins_OnUnloaded(); break; - - jNO_DEFAULT; - } -} - -EventListener_AppStatus::EventListener_AppStatus() -{ - wxGetApp().AddListener( this ); -} - -EventListener_AppStatus::~EventListener_AppStatus() throw() -{ - wxGetApp().RemoveListener( this ); -} - -void IEventListener_AppStatus::DispatchEvent( const AppEventInfo& evtinfo ) -{ - switch( evtinfo.evt_type ) - { - case AppStatus_SettingsLoaded: - case AppStatus_SettingsSaved: - AppStatusEvent_OnSettingsLoadSave( (const AppSettingsEventInfo&)evtinfo ); - break; - - case AppStatus_SettingsApplied: AppStatusEvent_OnSettingsApplied(); break; - case AppStatus_Exiting: AppStatusEvent_OnExit(); break; - } -} +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2009 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 "App.h" +#include "IniInterface.h" +#include "Utilities/EventSource.inl" + +template class EventSource< IEventListener_CoreThread >; +template class EventSource< IEventListener_Plugins >; +template class EventSource< IEventListener_AppStatus >; + +AppSettingsEventInfo::AppSettingsEventInfo( IniInterface& ini ) + : AppEventInfo( ini.IsSaving() ? AppStatus_SettingsSaved : AppStatus_SettingsLoaded ) + , m_ini( ini ) +{ +} + +EventListener_CoreThread::EventListener_CoreThread() +{ + wxGetApp().AddListener( this ); +} + +EventListener_CoreThread::~EventListener_CoreThread() throw() +{ + wxGetApp().RemoveListener( this ); +} + +void IEventListener_CoreThread::DispatchEvent( const CoreThreadStatus& status ) +{ + switch( status ) + { + case CoreThread_Started: CoreThread_OnStarted(); break; + case CoreThread_Resumed: CoreThread_OnResumed(); break; + case CoreThread_Suspended: CoreThread_OnSuspended(); break; + case CoreThread_Reset: CoreThread_OnReset(); break; + case CoreThread_Stopped: CoreThread_OnStopped(); break; + + jNO_DEFAULT; + } +} + +EventListener_Plugins::EventListener_Plugins() +{ + wxGetApp().AddListener( this ); +} + +EventListener_Plugins::~EventListener_Plugins() throw() +{ + wxGetApp().RemoveListener( this ); +} + +void IEventListener_Plugins::DispatchEvent( const PluginEventType& pevt ) +{ + switch( pevt ) + { + case CorePlugins_Loaded: CorePlugins_OnLoaded(); break; + case CorePlugins_Init: CorePlugins_OnInit(); break; + case CorePlugins_Opening: CorePlugins_OnOpening(); break; + case CorePlugins_Opened: CorePlugins_OnOpened(); break; + case CorePlugins_Closing: CorePlugins_OnClosing(); break; + case CorePlugins_Closed: CorePlugins_OnClosed(); break; + case CorePlugins_Shutdown: CorePlugins_OnShutdown(); break; + case CorePlugins_Unloaded: CorePlugins_OnUnloaded(); break; + + jNO_DEFAULT; + } +} + +EventListener_AppStatus::EventListener_AppStatus() +{ + wxGetApp().AddListener( this ); +} + +EventListener_AppStatus::~EventListener_AppStatus() throw() +{ + wxGetApp().RemoveListener( this ); +} + +void IEventListener_AppStatus::DispatchEvent( const AppEventInfo& evtinfo ) +{ + switch( evtinfo.evt_type ) + { + case AppStatus_SettingsLoaded: + case AppStatus_SettingsSaved: + AppStatusEvent_OnSettingsLoadSave( (const AppSettingsEventInfo&)evtinfo ); + break; + + case AppStatus_SettingsApplied: AppStatusEvent_OnSettingsApplied(); break; + case AppStatus_Exiting: AppStatusEvent_OnExit(); break; + } +} diff --git a/pcsx2/gui/AppForwardDefs.h b/pcsx2/gui/AppForwardDefs.h index 28b359e958..ff611ccab2 100644 --- a/pcsx2/gui/AppForwardDefs.h +++ b/pcsx2/gui/AppForwardDefs.h @@ -1,50 +1,50 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2009 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 . - */ - -#pragma once - -// AppForwardDefs.h -// -// Purpose: -// This header file is meant to be a dependency-free include that provides a relatively -// full compliment of forward defines for PCSX2/App and wxwidgets types. When -// forward defined in this way, these types can be used by method and class definitions -// as either pointers or handles without running into complicated header file -// inter-dependence. -// - -class MainEmuFrame; -class GSFrame; -class ConsoleLogFrame; -class PipeRedirectionBase; -class AppCoreThread; -class pxInvokeAppMethodEvent; -class IniInterface; - -// wxWidgets forward declarations - -class wxConfigBase; -class wxFileConfig; -class wxDirPickerCtrl; -class wxFilePickerCtrl; -class wxFileDirPickerEvent; -class wxListBox; -class wxListCtrl; -class wxListView; -class wxListbook; -class wxSpinCtrl; -class wxBookCtrlBase; - -class wxListEvent; +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2009 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 . + */ + +#pragma once + +// AppForwardDefs.h +// +// Purpose: +// This header file is meant to be a dependency-free include that provides a relatively +// full compliment of forward defines for PCSX2/App and wxwidgets types. When +// forward defined in this way, these types can be used by method and class definitions +// as either pointers or handles without running into complicated header file +// inter-dependence. +// + +class MainEmuFrame; +class GSFrame; +class ConsoleLogFrame; +class PipeRedirectionBase; +class AppCoreThread; +class pxInvokeAppMethodEvent; +class IniInterface; + +// wxWidgets forward declarations + +class wxConfigBase; +class wxFileConfig; +class wxDirPickerCtrl; +class wxFilePickerCtrl; +class wxFileDirPickerEvent; +class wxListBox; +class wxListCtrl; +class wxListView; +class wxListbook; +class wxSpinCtrl; +class wxBookCtrlBase; + +class wxListEvent; diff --git a/pcsx2/gui/Dialogs/StuckThreadDialog.cpp b/pcsx2/gui/Dialogs/StuckThreadDialog.cpp index baade203fa..c683705bb9 100644 --- a/pcsx2/gui/Dialogs/StuckThreadDialog.cpp +++ b/pcsx2/gui/Dialogs/StuckThreadDialog.cpp @@ -1,52 +1,52 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2009 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 "App.h" -#include "Dialogs/ModalPopups.h" - -using namespace pxSizerFlags; -using namespace Threading; - -Dialogs::StuckThreadDialog::StuckThreadDialog( wxWindow* parent, StuckThreadActionType action, PersistentThread& stuck_thread ) - : wxDialogWithHelpers( parent, _("PCSX2 Thread is not responding"), wxVERTICAL ) -{ - //m_idealWidth = 720; - - stuck_thread.AddListener( this ); - - *this += Heading( wxsFormat( - pxE( ".Dialog:StuckThread:Heading", - L"The thread '%s' is not responding. It could be deadlocked, or it might " - L"just be running *really* slowly." - ), - stuck_thread.GetName().data() - ) ); - - - *this += Heading( - L"\nDo you want to stop the program [Yes/No]?" - L"\nOr press [Ignore] to suppress further assertions." - ); - - *this += new ModalButtonPanel( this, MsgButtons().Cancel().Custom(L"Wait") ) | StdCenter(); - - if( wxWindow* idyes = FindWindowById( wxID_YES ) ) - idyes->SetFocus(); -} - -void Dialogs::StuckThreadDialog::OnThreadCleanup() -{ -} +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2009 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 "App.h" +#include "Dialogs/ModalPopups.h" + +using namespace pxSizerFlags; +using namespace Threading; + +Dialogs::StuckThreadDialog::StuckThreadDialog( wxWindow* parent, StuckThreadActionType action, PersistentThread& stuck_thread ) + : wxDialogWithHelpers( parent, _("PCSX2 Thread is not responding"), wxVERTICAL ) +{ + //m_idealWidth = 720; + + stuck_thread.AddListener( this ); + + *this += Heading( wxsFormat( + pxE( ".Dialog:StuckThread:Heading", + L"The thread '%s' is not responding. It could be deadlocked, or it might " + L"just be running *really* slowly." + ), + stuck_thread.GetName().data() + ) ); + + + *this += Heading( + L"\nDo you want to stop the program [Yes/No]?" + L"\nOr press [Ignore] to suppress further assertions." + ); + + *this += new ModalButtonPanel( this, MsgButtons().Cancel().Custom(L"Wait") ) | StdCenter(); + + if( wxWindow* idyes = FindWindowById( wxID_YES ) ) + idyes->SetFocus(); +} + +void Dialogs::StuckThreadDialog::OnThreadCleanup() +{ +} diff --git a/pcsx2/gui/Panels/BaseApplicableConfigPanel.cpp b/pcsx2/gui/Panels/BaseApplicableConfigPanel.cpp index 632da6c97a..bd39850b2b 100644 --- a/pcsx2/gui/Panels/BaseApplicableConfigPanel.cpp +++ b/pcsx2/gui/Panels/BaseApplicableConfigPanel.cpp @@ -1,206 +1,206 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2009 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 "App.h" - -#include "ConfigurationPanels.h" -#include "Dialogs/ConfigurationDialog.h" - -#include - -using namespace Dialogs; - -// ----------------------------------------------------------------------- -// This method should be called by the parent dalog box of a configuration -// on dialog destruction. It asserts if the ApplyList hasn't been cleaned up -// and then cleans it up forcefully. -// -void ApplyStateStruct::DoCleanup() throw() -{ - pxAssertMsg( PanelList.size() != 0, L"PanelList list hasn't been cleaned up." ); - PanelList.clear(); - ParentBook = NULL; -} - -void ApplyStateStruct::StartBook( wxBookCtrlBase* book ) -{ - pxAssertDev( ParentBook == NULL, "An ApplicableConfig session is already in progress." ); - ParentBook = book; -} - -void ApplyStateStruct::StartWizard() -{ - pxAssertDev( ParentBook == NULL, "An ApplicableConfig session is already in progress." ); -} - -// ----------------------------------------------------------------------- -// -// Parameters: -// pageid - identifier of the page to apply settings for. All other pages will be -// skipped. If pageid is negative (-1) then all pages are applied. -// -// Returns false if one of the panels fails input validation (in which case dialogs -// should not be closed, etc). -// -bool ApplyStateStruct::ApplyPage( int pageid ) -{ - bool retval = true; - - // Save these settings so we can restore them if the Apply fails. - - DocsModeType oldDocsMode = DocsFolderMode; - wxDirName oldSettingsFolder = SettingsFolder; - bool oldUseDefSet = UseDefaultSettingsFolder; - - AppConfig confcopy( *g_Conf ); - - try - { - PanelApplyList_t::iterator yay = PanelList.begin(); - while( yay != PanelList.end() ) - { - //DbgCon.Status( L"Writing settings for: " + (*yay)->GetLabel() ); - if( (pageid < 0) || (*yay)->IsOnPage( pageid ) ) - (*yay)->Apply(); - yay++; - } - - // If an exception is thrown above, this code below won't get run. - // (conveniently skipping any option application! :D) - - // Note: apply first, then save -- in case the apply fails. - - AppApplySettings( &confcopy ); - } - catch( Exception::CannotApplySettings& ex ) - { - DocsFolderMode = oldDocsMode; - SettingsFolder = oldSettingsFolder; - UseDefaultSettingsFolder = oldUseDefSet; - *g_Conf = confcopy; - - if( ex.IsVerbose ) - { - wxMessageBox( ex.FormatDisplayMessage(), _("Cannot apply settings...") ); - - if( ex.GetPanel() != NULL ) - ex.GetPanel()->SetFocusToMe(); - } - - retval = false; - } - catch( ... ) - { - DocsFolderMode = oldDocsMode; - SettingsFolder = oldSettingsFolder; - UseDefaultSettingsFolder = oldUseDefSet; - *g_Conf = confcopy; - - throw; - } - - return retval; -} - -// Returns false if one of the panels fails input validation (in which case dialogs -// should not be closed, etc). -bool ApplyStateStruct::ApplyAll() -{ - return ApplyPage( -1 ); -} - -// -------------------------------------------------------------------------------------- -// BaseApplicableConfigPanel Implementations -// -------------------------------------------------------------------------------------- -IApplyState* BaseApplicableConfigPanel::FindApplyStateManager() const -{ - wxWindow* millrun = this->GetParent(); - while( millrun != NULL ) - { - if( BaseApplicableDialog* dialog = wxDynamicCast( millrun, BaseApplicableDialog ) ) - return (IApplyState*)dialog; - - if( ApplicableWizardPage* wizpage = wxDynamicCast( millrun, ApplicableWizardPage ) ) - return (IApplyState*)wizpage; - - millrun = millrun->GetParent(); - } - return NULL; -} - -BaseApplicableConfigPanel::~BaseApplicableConfigPanel() throw() -{ - if( IApplyState* iapp = FindApplyStateManager() ) - iapp->GetApplyState().PanelList.remove( this ); -} - -BaseApplicableConfigPanel::BaseApplicableConfigPanel( wxWindow* parent, wxOrientation orient ) - : wxPanelWithHelpers( parent, orient ) - , m_AppStatusHelper( this ) -{ - Init(); -} - -BaseApplicableConfigPanel::BaseApplicableConfigPanel( wxWindow* parent, wxOrientation orient, const wxString& staticLabel ) - : wxPanelWithHelpers( parent, orient, staticLabel ) - , m_AppStatusHelper( this ) -{ - Init(); -} - -void BaseApplicableConfigPanel::SetFocusToMe() -{ - if( (m_OwnerBook == NULL) || (m_OwnerPage == wxID_NONE) ) return; - m_OwnerBook->SetSelection( m_OwnerPage ); -} - -BEGIN_DECLARE_EVENT_TYPES() - DECLARE_EVENT_TYPE( pxEvt_ApplySettings, -1 ) -END_DECLARE_EVENT_TYPES() - -DEFINE_EVENT_TYPE( pxEvt_ApplySettings ) - -void BaseApplicableConfigPanel::Init() -{ - // We need to bind to an event that occurs *after* all window and child - // window mess has been created. Unfortunately the WindowCreate event handler - // is immediate, and depends on the platform for how it "works", and thus - // useless. Solution: Create our own! :) - - //Connect( wxEVT_CREATE, wxWindowCreateEventHandler (BaseApplicableConfigPanel::OnCreateWindow) ); - Connect( pxEvt_ApplySettings, wxCommandEventHandler (BaseApplicableConfigPanel::OnSettingsApplied) ); - - if( IApplyState* iapp = FindApplyStateManager() ) - { - ApplyStateStruct& applyState( iapp->GetApplyState() ); - m_OwnerPage = applyState.CurOwnerPage; - m_OwnerBook = applyState.ParentBook; - applyState.PanelList.push_back( this ); - } - - wxCommandEvent applyEvent( pxEvt_ApplySettings ); - applyEvent.SetId( GetId() ); - AddPendingEvent( applyEvent ); -} - -//void BaseApplicableConfigPanel::OnCreateWindow( wxWindowCreateEvent& evt ) -void BaseApplicableConfigPanel::OnSettingsApplied( wxCommandEvent& evt ) -{ - evt.Skip(); - if( evt.GetId() == GetId() ) AppStatusEvent_OnSettingsApplied(); -} - -void BaseApplicableConfigPanel::AppStatusEvent_OnSettingsApplied() {} +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2009 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 "App.h" + +#include "ConfigurationPanels.h" +#include "Dialogs/ConfigurationDialog.h" + +#include + +using namespace Dialogs; + +// ----------------------------------------------------------------------- +// This method should be called by the parent dalog box of a configuration +// on dialog destruction. It asserts if the ApplyList hasn't been cleaned up +// and then cleans it up forcefully. +// +void ApplyStateStruct::DoCleanup() throw() +{ + pxAssertMsg( PanelList.size() != 0, L"PanelList list hasn't been cleaned up." ); + PanelList.clear(); + ParentBook = NULL; +} + +void ApplyStateStruct::StartBook( wxBookCtrlBase* book ) +{ + pxAssertDev( ParentBook == NULL, "An ApplicableConfig session is already in progress." ); + ParentBook = book; +} + +void ApplyStateStruct::StartWizard() +{ + pxAssertDev( ParentBook == NULL, "An ApplicableConfig session is already in progress." ); +} + +// ----------------------------------------------------------------------- +// +// Parameters: +// pageid - identifier of the page to apply settings for. All other pages will be +// skipped. If pageid is negative (-1) then all pages are applied. +// +// Returns false if one of the panels fails input validation (in which case dialogs +// should not be closed, etc). +// +bool ApplyStateStruct::ApplyPage( int pageid ) +{ + bool retval = true; + + // Save these settings so we can restore them if the Apply fails. + + DocsModeType oldDocsMode = DocsFolderMode; + wxDirName oldSettingsFolder = SettingsFolder; + bool oldUseDefSet = UseDefaultSettingsFolder; + + AppConfig confcopy( *g_Conf ); + + try + { + PanelApplyList_t::iterator yay = PanelList.begin(); + while( yay != PanelList.end() ) + { + //DbgCon.Status( L"Writing settings for: " + (*yay)->GetLabel() ); + if( (pageid < 0) || (*yay)->IsOnPage( pageid ) ) + (*yay)->Apply(); + yay++; + } + + // If an exception is thrown above, this code below won't get run. + // (conveniently skipping any option application! :D) + + // Note: apply first, then save -- in case the apply fails. + + AppApplySettings( &confcopy ); + } + catch( Exception::CannotApplySettings& ex ) + { + DocsFolderMode = oldDocsMode; + SettingsFolder = oldSettingsFolder; + UseDefaultSettingsFolder = oldUseDefSet; + *g_Conf = confcopy; + + if( ex.IsVerbose ) + { + wxMessageBox( ex.FormatDisplayMessage(), _("Cannot apply settings...") ); + + if( ex.GetPanel() != NULL ) + ex.GetPanel()->SetFocusToMe(); + } + + retval = false; + } + catch( ... ) + { + DocsFolderMode = oldDocsMode; + SettingsFolder = oldSettingsFolder; + UseDefaultSettingsFolder = oldUseDefSet; + *g_Conf = confcopy; + + throw; + } + + return retval; +} + +// Returns false if one of the panels fails input validation (in which case dialogs +// should not be closed, etc). +bool ApplyStateStruct::ApplyAll() +{ + return ApplyPage( -1 ); +} + +// -------------------------------------------------------------------------------------- +// BaseApplicableConfigPanel Implementations +// -------------------------------------------------------------------------------------- +IApplyState* BaseApplicableConfigPanel::FindApplyStateManager() const +{ + wxWindow* millrun = this->GetParent(); + while( millrun != NULL ) + { + if( BaseApplicableDialog* dialog = wxDynamicCast( millrun, BaseApplicableDialog ) ) + return (IApplyState*)dialog; + + if( ApplicableWizardPage* wizpage = wxDynamicCast( millrun, ApplicableWizardPage ) ) + return (IApplyState*)wizpage; + + millrun = millrun->GetParent(); + } + return NULL; +} + +BaseApplicableConfigPanel::~BaseApplicableConfigPanel() throw() +{ + if( IApplyState* iapp = FindApplyStateManager() ) + iapp->GetApplyState().PanelList.remove( this ); +} + +BaseApplicableConfigPanel::BaseApplicableConfigPanel( wxWindow* parent, wxOrientation orient ) + : wxPanelWithHelpers( parent, orient ) + , m_AppStatusHelper( this ) +{ + Init(); +} + +BaseApplicableConfigPanel::BaseApplicableConfigPanel( wxWindow* parent, wxOrientation orient, const wxString& staticLabel ) + : wxPanelWithHelpers( parent, orient, staticLabel ) + , m_AppStatusHelper( this ) +{ + Init(); +} + +void BaseApplicableConfigPanel::SetFocusToMe() +{ + if( (m_OwnerBook == NULL) || (m_OwnerPage == wxID_NONE) ) return; + m_OwnerBook->SetSelection( m_OwnerPage ); +} + +BEGIN_DECLARE_EVENT_TYPES() + DECLARE_EVENT_TYPE( pxEvt_ApplySettings, -1 ) +END_DECLARE_EVENT_TYPES() + +DEFINE_EVENT_TYPE( pxEvt_ApplySettings ) + +void BaseApplicableConfigPanel::Init() +{ + // We need to bind to an event that occurs *after* all window and child + // window mess has been created. Unfortunately the WindowCreate event handler + // is immediate, and depends on the platform for how it "works", and thus + // useless. Solution: Create our own! :) + + //Connect( wxEVT_CREATE, wxWindowCreateEventHandler (BaseApplicableConfigPanel::OnCreateWindow) ); + Connect( pxEvt_ApplySettings, wxCommandEventHandler (BaseApplicableConfigPanel::OnSettingsApplied) ); + + if( IApplyState* iapp = FindApplyStateManager() ) + { + ApplyStateStruct& applyState( iapp->GetApplyState() ); + m_OwnerPage = applyState.CurOwnerPage; + m_OwnerBook = applyState.ParentBook; + applyState.PanelList.push_back( this ); + } + + wxCommandEvent applyEvent( pxEvt_ApplySettings ); + applyEvent.SetId( GetId() ); + AddPendingEvent( applyEvent ); +} + +//void BaseApplicableConfigPanel::OnCreateWindow( wxWindowCreateEvent& evt ) +void BaseApplicableConfigPanel::OnSettingsApplied( wxCommandEvent& evt ) +{ + evt.Skip(); + if( evt.GetId() == GetId() ) AppStatusEvent_OnSettingsApplied(); +} + +void BaseApplicableConfigPanel::AppStatusEvent_OnSettingsApplied() {} diff --git a/pcsx2/gui/Panels/MemoryCardListPanel.cpp b/pcsx2/gui/Panels/MemoryCardListPanel.cpp index 610ccb95d6..0f79a688ee 100644 --- a/pcsx2/gui/Panels/MemoryCardListPanel.cpp +++ b/pcsx2/gui/Panels/MemoryCardListPanel.cpp @@ -1,195 +1,195 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2009 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 "ConfigurationPanels.h" -#include "MemoryCardListView.h" - -#include -#include -#include - - -using namespace pxSizerFlags; -using namespace Panels; - -static bool IsMcdFormatted( wxFFile& fhand ) -{ - static const char formatted_string[] = "Sony PS2 Memory Card Format"; - static const int fmtstrlen = sizeof( formatted_string )-1; - - char dest[fmtstrlen]; - - fhand.Read( dest, fmtstrlen ); - return memcmp( formatted_string, dest, fmtstrlen ) == 0; -} - -static bool EnumerateMemoryCard( McdListItem& dest, const wxString& filename ) -{ - Console.WriteLn( filename.c_str() ); - wxFFile mcdFile( filename ); - if( !mcdFile.IsOpened() ) return false; // wx should log the error for us. - if( mcdFile.Length() < (1024*528) ) - { - Console.Warning( "... MemoryCard appears to be truncated. Ignoring." ); - return false; - } - - dest.Filename = filename; - dest.SizeInMB = (uint)(mcdFile.Length() / (1024 * 528 * 2)); - dest.IsFormatted = IsMcdFormatted( mcdFile ); - wxFileName(filename).GetTimes( NULL, &dest.DateModified, &dest.DateCreated ); - - return true; -} - -static int EnumerateMemoryCards( McdList& dest, const wxArrayString& files ) -{ - int pushed = 0; - Console.WriteLn( Color_StrongBlue, "Enumerating MemoryCards..." ); - for( size_t i=0; i 0 ) - Console.WriteLn( Color_StrongBlue, "MemoryCard Enumeration Complete." ); - else - Console.WriteLn( Color_StrongBlue, "No valid MemoryCards found." ); - - return pushed; -} - - -// ===================================================================================================== -// MemoryCardListPanel -// ===================================================================================================== -MemoryCardListPanel::MemoryCardListPanel( wxWindow* parent ) - : BaseSelectorPanel( parent ) -{ - m_FolderPicker = new DirPickerPanel( this, FolderId_MemoryCards, - //_("MemoryCard Search Path:"), // static box label - _("Select folder with PS2 MemoryCards") // dir picker popup label - ); - - m_listview = new MemoryCardListView(this); - - wxButton* button_Create = new wxButton(this, wxID_ANY, _("Create new card...")); - - //Connect( m_list_AllKnownCards->GetId(), wxEVT_COMMAND_LEFT_CLICK, MemoryCardsPanel::OnListDrag); - - // ------------------------------------ - // Sizer / Layout Section - // ------------------------------------ - - wxBoxSizer& s_buttons (*new wxBoxSizer( wxHORIZONTAL )); - wxBoxSizer& s_leftside (*new wxBoxSizer( wxHORIZONTAL )); - wxBoxSizer& s_rightside (*new wxBoxSizer( wxHORIZONTAL )); - - s_leftside += button_Create | StdSpace(); - - s_buttons += s_leftside | pxAlignLeft; - s_buttons += pxStretchSpacer(); - s_buttons += s_rightside | pxAlignRight; - - *this += m_FolderPicker | pxExpand; - *this += m_listview | pxExpand; - *this += s_buttons | pxExpand; - - m_listview->SetMinSize( wxSize( wxDefaultCoord, 120 ) ); - - AppStatusEvent_OnSettingsApplied(); - - Disable(); -} - -void MemoryCardListPanel::Apply() -{ -} - -void MemoryCardListPanel::AppStatusEvent_OnSettingsApplied() -{ - -} - -bool Panels::MemoryCardListPanel::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& filenames) -{ - if( filenames.GetCount() == 1 && wxFileName(filenames[0]).IsDir() ) - { - m_FolderPicker->SetPath( filenames[0] ); - return true; - } - return false; -} - -bool MemoryCardListPanel::ValidateEnumerationStatus() -{ - bool validated = true; - - // Impl Note: ScopedPtr used so that resources get cleaned up if an exception - // occurs during file enumeration. - ScopedPtr mcdlist( new McdList() ); - - if( m_FolderPicker->GetPath().Exists() ) - { - wxArrayString files; - wxDir::GetAllFiles( m_FolderPicker->GetPath().ToString(), &files, L"*.ps2", wxDIR_FILES ); - EnumerateMemoryCards( *mcdlist, files ); - } - - if( !m_KnownCards || (*mcdlist != *m_KnownCards) ) - validated = false; - - m_listview->AssignCardsList( NULL ); - m_KnownCards.SwapPtr( mcdlist ); - - return validated; -} - -void MemoryCardListPanel::DoRefresh() -{ - if( !m_KnownCards ) return; - - //m_listview->ClearAll(); - //m_listview->CreateColumns(); - - for( size_t i=0; isize(); ++i ) - { - McdListItem& mcditem( (*m_KnownCards)[i] ); - - for( int port=0; port<2; ++port ) - for( int slot=0; slot<4; ++slot ) - { - wxFileName right( g_Conf->FullpathToMcd(port, slot) ); - right.MakeAbsolute(); - - wxFileName left( mcditem.Filename ); - left.MakeAbsolute(); - - if( left == right ) - { - mcditem.Port = port; - mcditem.Slot = slot; - } - } - } - - m_listview->AssignCardsList( m_KnownCards ); -} +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2009 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 "ConfigurationPanels.h" +#include "MemoryCardListView.h" + +#include +#include +#include + + +using namespace pxSizerFlags; +using namespace Panels; + +static bool IsMcdFormatted( wxFFile& fhand ) +{ + static const char formatted_string[] = "Sony PS2 Memory Card Format"; + static const int fmtstrlen = sizeof( formatted_string )-1; + + char dest[fmtstrlen]; + + fhand.Read( dest, fmtstrlen ); + return memcmp( formatted_string, dest, fmtstrlen ) == 0; +} + +static bool EnumerateMemoryCard( McdListItem& dest, const wxString& filename ) +{ + Console.WriteLn( filename.c_str() ); + wxFFile mcdFile( filename ); + if( !mcdFile.IsOpened() ) return false; // wx should log the error for us. + if( mcdFile.Length() < (1024*528) ) + { + Console.Warning( "... MemoryCard appears to be truncated. Ignoring." ); + return false; + } + + dest.Filename = filename; + dest.SizeInMB = (uint)(mcdFile.Length() / (1024 * 528 * 2)); + dest.IsFormatted = IsMcdFormatted( mcdFile ); + wxFileName(filename).GetTimes( NULL, &dest.DateModified, &dest.DateCreated ); + + return true; +} + +static int EnumerateMemoryCards( McdList& dest, const wxArrayString& files ) +{ + int pushed = 0; + Console.WriteLn( Color_StrongBlue, "Enumerating MemoryCards..." ); + for( size_t i=0; i 0 ) + Console.WriteLn( Color_StrongBlue, "MemoryCard Enumeration Complete." ); + else + Console.WriteLn( Color_StrongBlue, "No valid MemoryCards found." ); + + return pushed; +} + + +// ===================================================================================================== +// MemoryCardListPanel +// ===================================================================================================== +MemoryCardListPanel::MemoryCardListPanel( wxWindow* parent ) + : BaseSelectorPanel( parent ) +{ + m_FolderPicker = new DirPickerPanel( this, FolderId_MemoryCards, + //_("MemoryCard Search Path:"), // static box label + _("Select folder with PS2 MemoryCards") // dir picker popup label + ); + + m_listview = new MemoryCardListView(this); + + wxButton* button_Create = new wxButton(this, wxID_ANY, _("Create new card...")); + + //Connect( m_list_AllKnownCards->GetId(), wxEVT_COMMAND_LEFT_CLICK, MemoryCardsPanel::OnListDrag); + + // ------------------------------------ + // Sizer / Layout Section + // ------------------------------------ + + wxBoxSizer& s_buttons (*new wxBoxSizer( wxHORIZONTAL )); + wxBoxSizer& s_leftside (*new wxBoxSizer( wxHORIZONTAL )); + wxBoxSizer& s_rightside (*new wxBoxSizer( wxHORIZONTAL )); + + s_leftside += button_Create | StdSpace(); + + s_buttons += s_leftside | pxAlignLeft; + s_buttons += pxStretchSpacer(); + s_buttons += s_rightside | pxAlignRight; + + *this += m_FolderPicker | pxExpand; + *this += m_listview | pxExpand; + *this += s_buttons | pxExpand; + + m_listview->SetMinSize( wxSize( wxDefaultCoord, 120 ) ); + + AppStatusEvent_OnSettingsApplied(); + + Disable(); +} + +void MemoryCardListPanel::Apply() +{ +} + +void MemoryCardListPanel::AppStatusEvent_OnSettingsApplied() +{ + +} + +bool Panels::MemoryCardListPanel::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& filenames) +{ + if( filenames.GetCount() == 1 && wxFileName(filenames[0]).IsDir() ) + { + m_FolderPicker->SetPath( filenames[0] ); + return true; + } + return false; +} + +bool MemoryCardListPanel::ValidateEnumerationStatus() +{ + bool validated = true; + + // Impl Note: ScopedPtr used so that resources get cleaned up if an exception + // occurs during file enumeration. + ScopedPtr mcdlist( new McdList() ); + + if( m_FolderPicker->GetPath().Exists() ) + { + wxArrayString files; + wxDir::GetAllFiles( m_FolderPicker->GetPath().ToString(), &files, L"*.ps2", wxDIR_FILES ); + EnumerateMemoryCards( *mcdlist, files ); + } + + if( !m_KnownCards || (*mcdlist != *m_KnownCards) ) + validated = false; + + m_listview->AssignCardsList( NULL ); + m_KnownCards.SwapPtr( mcdlist ); + + return validated; +} + +void MemoryCardListPanel::DoRefresh() +{ + if( !m_KnownCards ) return; + + //m_listview->ClearAll(); + //m_listview->CreateColumns(); + + for( size_t i=0; isize(); ++i ) + { + McdListItem& mcditem( (*m_KnownCards)[i] ); + + for( int port=0; port<2; ++port ) + for( int slot=0; slot<4; ++slot ) + { + wxFileName right( g_Conf->FullpathToMcd(port, slot) ); + right.MakeAbsolute(); + + wxFileName left( mcditem.Filename ); + left.MakeAbsolute(); + + if( left == right ) + { + mcditem.Port = port; + mcditem.Slot = slot; + } + } + } + + m_listview->AssignCardsList( m_KnownCards ); +} diff --git a/pcsx2/pcsx2hostfs.cpp b/pcsx2/pcsx2hostfs.cpp index 87ba70bac4..a3193ec0a3 100644 --- a/pcsx2/pcsx2hostfs.cpp +++ b/pcsx2/pcsx2hostfs.cpp @@ -1,212 +1,212 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2009 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 "Utilities/Console.h" - -#ifndef __LINUX__ -#include -//#include "dirent.h" -#else -#include -#endif - -#pragma optimize("", off) - -#define PS2E_FIO_OPEN_CMD 0xcc2e0101 -#define PS2E_FIO_CLOSE_CMD 0xcc2e0102 -#define PS2E_FIO_READ_CMD 0xcc2e0103 -#define PS2E_FIO_WRITE_CMD 0xcc2e0104 -#define PS2E_FIO_LSEEK_CMD 0xcc2e0105 -#define PS2E_FIO_OPENDIR_CMD 0xcc2e0106 -#define PS2E_FIO_CLOSEDIR_CMD 0xcc2e0107 -#define PS2E_FIO_READDIR_CMD 0xcc2e0108 -#define PS2E_FIO_REMOVE_CMD 0xcc2e0109 -#define PS2E_FIO_MKDIR_CMD 0xcc2e010a -#define PS2E_FIO_RMDIR_CMD 0xcc2e010b - -#define PS2E_FIO_PRINTF_CMD 0xcc2e0201 - -#define PS2E_FIO_MAGIC 'E2SP' - -u32 functionId; -u32 paramsAddress; -u32 paramsLength; +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2009 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 "Utilities/Console.h" + +#ifndef __LINUX__ +#include +//#include "dirent.h" +#else +#include +#endif + +#pragma optimize("", off) + +#define PS2E_FIO_OPEN_CMD 0xcc2e0101 +#define PS2E_FIO_CLOSE_CMD 0xcc2e0102 +#define PS2E_FIO_READ_CMD 0xcc2e0103 +#define PS2E_FIO_WRITE_CMD 0xcc2e0104 +#define PS2E_FIO_LSEEK_CMD 0xcc2e0105 +#define PS2E_FIO_OPENDIR_CMD 0xcc2e0106 +#define PS2E_FIO_CLOSEDIR_CMD 0xcc2e0107 +#define PS2E_FIO_READDIR_CMD 0xcc2e0108 +#define PS2E_FIO_REMOVE_CMD 0xcc2e0109 +#define PS2E_FIO_MKDIR_CMD 0xcc2e010a +#define PS2E_FIO_RMDIR_CMD 0xcc2e010b + +#define PS2E_FIO_PRINTF_CMD 0xcc2e0201 + +#define PS2E_FIO_MAGIC 'E2SP' + +u32 functionId; +u32 paramsAddress; +u32 paramsLength; u32 returnValue; - + #ifndef O_BINARY # define O_BINARY 0 #endif - -#define IOP_RDONLY 0x0001 -#define IOP_WRONLY 0x0002 -#define IOP_RDWR 0x0003 -#define IOP_NBLOCK 0x0010 -#define IOP_APPEND 0x0100 -#define IOP_CREAT 0x0200 -#define IOP_TRUNC 0x0400 -#define IOP_NOWAIT 0x8000 - -int pcsx2fio_open_file(char *path, int flags) -{ - int mode = O_BINARY; - - switch(flags&IOP_RDWR) - { - case IOP_RDONLY: - mode |= O_RDONLY; - break; - case IOP_WRONLY: - mode |= O_WRONLY; - break; - case IOP_RDWR: - mode |= O_RDWR; - break; - } - if(flags&IOP_CREAT) - mode |= O_CREAT; - if(flags&IOP_APPEND) - mode |= O_APPEND; - if(flags&IOP_TRUNC) - mode |= O_TRUNC; - - return open(path,mode); -} - -int pcsx2fio_close_file(int fd) -{ - return close(fd); -} - -int pcsx2fio_lseek_file(int fd, unsigned int offset, int whence) -{ - return lseek(fd,offset,whence); -} - -int pcsx2fio_write_file(int fd, char *buf, int length) -{ - return write(fd,buf,length); -} - -int pcsx2fio_read_file(int fd, char *buf, int length) -{ - return read(fd,buf,length); -} - -int pcsx2fio_remove(char *name) -{ - return unlink(name); -} - -int pcsx2fio_mkdir(char *name, int mode) -{ -#ifdef __LINUX__ - return mkdir(name,mode); -#else - return mkdir(name); -#endif -} - -int pcsx2fio_rmdir(char *name) -{ - return rmdir(name); -} - -int pcsx2fio_open_dir(char *path) -{ - return -1; -} - -int pcsx2fio_read_dir(int fd, void *buf) -{ - return -1; -} - -int pcsx2fio_close_dir(int fd) -{ - return -1; -} - -int pcsx2fio_write_tty(const char* text, int length) -{ - wxString s = wxString::FromUTF8(text,length); - - return printf("%s",s.ToAscii().data()); -} - -#define PARAM(offset,type) (*(type*)(buffer+(offset))) -#define PARAMP(offset,type) (type*)iopPhysMem(PARAM(offset,u32)) -#define PARAMSTR(offset) ((char*)(buffer+(offset))) -void Pcsx2HostFsExec() -{ - u8* buffer = (u8*)iopPhysMem(paramsAddress); - - switch(functionId) - { - case PS2E_FIO_OPEN_CMD: returnValue = pcsx2fio_open_file(PARAMSTR(4),PARAM(0,s32)); break; - case PS2E_FIO_CLOSE_CMD: returnValue = pcsx2fio_close_file(PARAM(0,s32)); break; - case PS2E_FIO_READ_CMD: returnValue = pcsx2fio_read_file(PARAM(0,s32),PARAMP(4,char),PARAM(8,s32)); break; - case PS2E_FIO_WRITE_CMD: returnValue = pcsx2fio_write_file(PARAM(0,s32),PARAMP(4,char),PARAM(8,s32)); break; - case PS2E_FIO_LSEEK_CMD: returnValue = pcsx2fio_lseek_file(PARAM(0,s32),PARAM(4,s32),PARAM(8,s32)); break; - case PS2E_FIO_REMOVE_CMD: returnValue = pcsx2fio_remove(PARAMSTR(0)); break; - case PS2E_FIO_OPENDIR_CMD: returnValue = pcsx2fio_open_dir(PARAMSTR(0)); break; - case PS2E_FIO_CLOSEDIR_CMD: returnValue = pcsx2fio_close_dir(PARAM(0,s32)); break; - case PS2E_FIO_READDIR_CMD: returnValue = pcsx2fio_read_dir(PARAM(0,s32),PARAMP(4,void)); break; - case PS2E_FIO_MKDIR_CMD: returnValue = pcsx2fio_mkdir(PARAMSTR(4),PARAM(0,s32)); break; - case PS2E_FIO_RMDIR_CMD: returnValue = pcsx2fio_rmdir(PARAMSTR(0)); break; - - case PS2E_FIO_PRINTF_CMD: returnValue = pcsx2fio_write_tty(PARAMSTR(0),paramsLength); break; - } -} - -void Pcsx2HostFSwrite32(u32 addr, u32 value) -{ - switch(addr&0xF) - { - case 0: - if(value==1) - Pcsx2HostFsExec(); - break; - case 4: - paramsLength = value; - break; - case 8: - paramsAddress = value; - break; - case 12: - functionId = value; - break; - } -} - -u32 Pcsx2HostFSread32(u32 addr) -{ - switch(addr&0xF) - { - case 0: - return PS2E_FIO_MAGIC; - break; - case 4: - break; - case 8: - break; - case 12: - return returnValue; - break; - } - return 0; -} + +#define IOP_RDONLY 0x0001 +#define IOP_WRONLY 0x0002 +#define IOP_RDWR 0x0003 +#define IOP_NBLOCK 0x0010 +#define IOP_APPEND 0x0100 +#define IOP_CREAT 0x0200 +#define IOP_TRUNC 0x0400 +#define IOP_NOWAIT 0x8000 + +int pcsx2fio_open_file(char *path, int flags) +{ + int mode = O_BINARY; + + switch(flags&IOP_RDWR) + { + case IOP_RDONLY: + mode |= O_RDONLY; + break; + case IOP_WRONLY: + mode |= O_WRONLY; + break; + case IOP_RDWR: + mode |= O_RDWR; + break; + } + if(flags&IOP_CREAT) + mode |= O_CREAT; + if(flags&IOP_APPEND) + mode |= O_APPEND; + if(flags&IOP_TRUNC) + mode |= O_TRUNC; + + return open(path,mode); +} + +int pcsx2fio_close_file(int fd) +{ + return close(fd); +} + +int pcsx2fio_lseek_file(int fd, unsigned int offset, int whence) +{ + return lseek(fd,offset,whence); +} + +int pcsx2fio_write_file(int fd, char *buf, int length) +{ + return write(fd,buf,length); +} + +int pcsx2fio_read_file(int fd, char *buf, int length) +{ + return read(fd,buf,length); +} + +int pcsx2fio_remove(char *name) +{ + return unlink(name); +} + +int pcsx2fio_mkdir(char *name, int mode) +{ +#ifdef __LINUX__ + return mkdir(name,mode); +#else + return mkdir(name); +#endif +} + +int pcsx2fio_rmdir(char *name) +{ + return rmdir(name); +} + +int pcsx2fio_open_dir(char *path) +{ + return -1; +} + +int pcsx2fio_read_dir(int fd, void *buf) +{ + return -1; +} + +int pcsx2fio_close_dir(int fd) +{ + return -1; +} + +int pcsx2fio_write_tty(const char* text, int length) +{ + wxString s = wxString::FromUTF8(text,length); + + return printf("%s",s.ToAscii().data()); +} + +#define PARAM(offset,type) (*(type*)(buffer+(offset))) +#define PARAMP(offset,type) (type*)iopPhysMem(PARAM(offset,u32)) +#define PARAMSTR(offset) ((char*)(buffer+(offset))) +void Pcsx2HostFsExec() +{ + u8* buffer = (u8*)iopPhysMem(paramsAddress); + + switch(functionId) + { + case PS2E_FIO_OPEN_CMD: returnValue = pcsx2fio_open_file(PARAMSTR(4),PARAM(0,s32)); break; + case PS2E_FIO_CLOSE_CMD: returnValue = pcsx2fio_close_file(PARAM(0,s32)); break; + case PS2E_FIO_READ_CMD: returnValue = pcsx2fio_read_file(PARAM(0,s32),PARAMP(4,char),PARAM(8,s32)); break; + case PS2E_FIO_WRITE_CMD: returnValue = pcsx2fio_write_file(PARAM(0,s32),PARAMP(4,char),PARAM(8,s32)); break; + case PS2E_FIO_LSEEK_CMD: returnValue = pcsx2fio_lseek_file(PARAM(0,s32),PARAM(4,s32),PARAM(8,s32)); break; + case PS2E_FIO_REMOVE_CMD: returnValue = pcsx2fio_remove(PARAMSTR(0)); break; + case PS2E_FIO_OPENDIR_CMD: returnValue = pcsx2fio_open_dir(PARAMSTR(0)); break; + case PS2E_FIO_CLOSEDIR_CMD: returnValue = pcsx2fio_close_dir(PARAM(0,s32)); break; + case PS2E_FIO_READDIR_CMD: returnValue = pcsx2fio_read_dir(PARAM(0,s32),PARAMP(4,void)); break; + case PS2E_FIO_MKDIR_CMD: returnValue = pcsx2fio_mkdir(PARAMSTR(4),PARAM(0,s32)); break; + case PS2E_FIO_RMDIR_CMD: returnValue = pcsx2fio_rmdir(PARAMSTR(0)); break; + + case PS2E_FIO_PRINTF_CMD: returnValue = pcsx2fio_write_tty(PARAMSTR(0),paramsLength); break; + } +} + +void Pcsx2HostFSwrite32(u32 addr, u32 value) +{ + switch(addr&0xF) + { + case 0: + if(value==1) + Pcsx2HostFsExec(); + break; + case 4: + paramsLength = value; + break; + case 8: + paramsAddress = value; + break; + case 12: + functionId = value; + break; + } +} + +u32 Pcsx2HostFSread32(u32 addr) +{ + switch(addr&0xF) + { + case 0: + return PS2E_FIO_MAGIC; + break; + case 4: + break; + case 8: + break; + case 12: + return returnValue; + break; + } + return 0; +} diff --git a/pcsx2/windows/DwmSetup.cpp b/pcsx2/windows/DwmSetup.cpp index bea07a1791..410bcddeba 100644 --- a/pcsx2/windows/DwmSetup.cpp +++ b/pcsx2/windows/DwmSetup.cpp @@ -1,77 +1,77 @@ - -#include "PrecompiledHeader.h" -#include "Utilities/Console.h" -#include "MSWstuff.h" - -#include -#include - -#include - -typedef HRESULT WINAPI Fntype_DwmEnableMMCSS(DWORD enable); -typedef HRESULT WINAPI Fntype_DwmSetPresentParameters( HWND hwnd, DWM_PRESENT_PARAMETERS *pPresentParams ); - -static wxDynamicLibrary lib_dwmapi; - -// This could potentially reduce lag while running in Aero, -// by telling the DWM the application requires -// multimedia-class scheduling for smooth display. -void pxDwm_Load() -{ - wxDoNotLogInThisScope please; - - // Version test is not needed since we're using LoadLibrary. --air - - /*OSVERSIONINFOEX info = {0}; - info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); - info.dwMajorVersion = 6; - - DWORDLONG mask=0; - VER_SET_CONDITION(mask,VER_MAJORVERSION, VER_GREATER_EQUAL); - VER_SET_CONDITION(mask,VER_MINORVERSION, VER_GREATER_EQUAL); - VER_SET_CONDITION(mask,VER_SERVICEPACKMAJOR,VER_GREATER_EQUAL); - VER_SET_CONDITION(mask,VER_SERVICEPACKMINOR,VER_GREATER_EQUAL); - - //info - if(VerifyVersionInfo(&info, - VER_MAJORVERSION|VER_MINORVERSION|VER_SERVICEPACKMAJOR|VER_SERVICEPACKMINOR, - mask))*/ - - lib_dwmapi.Load( L"dwmapi.dll" ); - if( !lib_dwmapi.IsLoaded() ) return; - - if( Fntype_DwmEnableMMCSS* pDwmEnableMMCSS = (Fntype_DwmEnableMMCSS*)lib_dwmapi.GetSymbol(L"DwmEnableMMCSS") ) - { - Console.WriteLn( "[Dwm] Desktop Window Manager detected." ); - - if(FAILED(pDwmEnableMMCSS(TRUE))) - Console.WriteLn("[Dwm] DwmEnableMMCSS returned a failure code."); - } -} - -// wnd - this parameter should be the GS display panel or the top level frame that holds it (not -// sure if it's supposed to be the actual gsPanel or the top level window/frame that the -// panel belongs to) -// -void pxDwm_SetPresentParams( WXWidget wnd ) -{ - if( !lib_dwmapi.IsLoaded() ) return; - Fntype_DwmSetPresentParameters* pDwmSetPresentParameters = (Fntype_DwmSetPresentParameters*)lib_dwmapi.GetSymbol(L"DwmSetPresentParameters"); - - if( pDwmSetPresentParameters == NULL ) return; - - DWM_PRESENT_PARAMETERS params; - - params.cbSize = sizeof(DWM_PRESENT_PARAMETERS); - params.fQueue = FALSE; - - if(FAILED(pDwmSetPresentParameters( (HWND)wnd, ¶ms ))) - Console.WriteLn("[Dwm] DwmSetPresentParameters returned a failure code."); - - //DwmSetDxFrameDuration(hMainWindow,1); -} - -void pxDwm_Unload() -{ - lib_dwmapi.Unload(); + +#include "PrecompiledHeader.h" +#include "Utilities/Console.h" +#include "MSWstuff.h" + +#include +#include + +#include + +typedef HRESULT WINAPI Fntype_DwmEnableMMCSS(DWORD enable); +typedef HRESULT WINAPI Fntype_DwmSetPresentParameters( HWND hwnd, DWM_PRESENT_PARAMETERS *pPresentParams ); + +static wxDynamicLibrary lib_dwmapi; + +// This could potentially reduce lag while running in Aero, +// by telling the DWM the application requires +// multimedia-class scheduling for smooth display. +void pxDwm_Load() +{ + wxDoNotLogInThisScope please; + + // Version test is not needed since we're using LoadLibrary. --air + + /*OSVERSIONINFOEX info = {0}; + info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); + info.dwMajorVersion = 6; + + DWORDLONG mask=0; + VER_SET_CONDITION(mask,VER_MAJORVERSION, VER_GREATER_EQUAL); + VER_SET_CONDITION(mask,VER_MINORVERSION, VER_GREATER_EQUAL); + VER_SET_CONDITION(mask,VER_SERVICEPACKMAJOR,VER_GREATER_EQUAL); + VER_SET_CONDITION(mask,VER_SERVICEPACKMINOR,VER_GREATER_EQUAL); + + //info + if(VerifyVersionInfo(&info, + VER_MAJORVERSION|VER_MINORVERSION|VER_SERVICEPACKMAJOR|VER_SERVICEPACKMINOR, + mask))*/ + + lib_dwmapi.Load( L"dwmapi.dll" ); + if( !lib_dwmapi.IsLoaded() ) return; + + if( Fntype_DwmEnableMMCSS* pDwmEnableMMCSS = (Fntype_DwmEnableMMCSS*)lib_dwmapi.GetSymbol(L"DwmEnableMMCSS") ) + { + Console.WriteLn( "[Dwm] Desktop Window Manager detected." ); + + if(FAILED(pDwmEnableMMCSS(TRUE))) + Console.WriteLn("[Dwm] DwmEnableMMCSS returned a failure code."); + } +} + +// wnd - this parameter should be the GS display panel or the top level frame that holds it (not +// sure if it's supposed to be the actual gsPanel or the top level window/frame that the +// panel belongs to) +// +void pxDwm_SetPresentParams( WXWidget wnd ) +{ + if( !lib_dwmapi.IsLoaded() ) return; + Fntype_DwmSetPresentParameters* pDwmSetPresentParameters = (Fntype_DwmSetPresentParameters*)lib_dwmapi.GetSymbol(L"DwmSetPresentParameters"); + + if( pDwmSetPresentParameters == NULL ) return; + + DWM_PRESENT_PARAMETERS params; + + params.cbSize = sizeof(DWM_PRESENT_PARAMETERS); + params.fQueue = FALSE; + + if(FAILED(pDwmSetPresentParameters( (HWND)wnd, ¶ms ))) + Console.WriteLn("[Dwm] DwmSetPresentParameters returned a failure code."); + + //DwmSetDxFrameDuration(hMainWindow,1); +} + +void pxDwm_Unload() +{ + lib_dwmapi.Unload(); } \ No newline at end of file diff --git a/pcsx2/windows/VCprojects/IopSif.cpp b/pcsx2/windows/VCprojects/IopSif.cpp index 92b11ac744..3beb51e77f 100644 --- a/pcsx2/windows/VCprojects/IopSif.cpp +++ b/pcsx2/windows/VCprojects/IopSif.cpp @@ -1,20 +1,20 @@ -#include "PrecompiledHeader.h" -#include "IopCommon.h" - -#include "Sif.h" - -#if FALSE - -const u32 fifoSize = 0x1000; // in bytes - -s32 PrepareEEWrite() -{ - - return 0; -} - -s32 PrepareEERead() -{ +#include "PrecompiledHeader.h" +#include "IopCommon.h" + +#include "Sif.h" + +#if FALSE + +const u32 fifoSize = 0x1000; // in bytes + +s32 PrepareEEWrite() +{ + + return 0; +} + +s32 PrepareEERead() +{ static __aligned16 u32 tag[4]; // Process DMA tag at hw_dma(9).tadr @@ -63,32 +63,32 @@ s32 PrepareEERead() sif0.ee.end = true; break; } - return true; -} - -void FinalizeEERead() -{ + return true; +} + +void FinalizeEERead() +{ SIF_LOG("Sif0: End EE"); sif0.ee.end = false; sif0.ee.busy = false; SIF_LOG("CPU INT FIRED SIF0"); CPU_INT(DMAC_SIF0, 16); -} - -s32 DoSIFWrite(u32 iopAvailable) -{ - u32 eeAvailable = PrepareEEWrite(); - -} - -s32 DoSifRead(u32 iopAvailable) -{ - u32 eeAvailable = PrepareEERead(); - - u32 transferSizeBytes = min(min(iopAvailable,eeAvailable),fifoSize); - u32 transferSizeWords = transferSizeBytes >> 2; - u32 transferSizeQWords = transferSizeBytes >> 4; - +} + +s32 DoSIFWrite(u32 iopAvailable) +{ + u32 eeAvailable = PrepareEEWrite(); + +} + +s32 DoSifRead(u32 iopAvailable) +{ + u32 eeAvailable = PrepareEERead(); + + u32 transferSizeBytes = min(min(iopAvailable,eeAvailable),fifoSize); + u32 transferSizeWords = transferSizeBytes >> 2; + u32 transferSizeQWords = transferSizeBytes >> 4; + SIF_LOG("Write IOP to EE: +++++++++++ %lX of %lX", transferSizeWords, sif0.iop.counter); tDMA_TAG *ptag = sif0dma->getAddr(sif0dma->madr, DMAC_SIF0, true); @@ -106,55 +106,55 @@ s32 DoSifRead(u32 iopAvailable) sif0dma->madr += transferSizeBytes; sif0.ee.cycles += transferSizeQWords * 2; sif0dma->qwc -= transferSizeQWords; - - return transferSizeBytes; -} - -s32 CALLBACK sif0DmaRead (s32 channel, u32* data, u32 bytesLeft, u32* bytesProcessed) -{ - s32 processed = DoSIFWrite(bytesLeft); - - if(processed>0) - { - bytesLeft -= processed; - if(bytesLeft == 0) - FinalizeEERead(); - - *bytesProcessed = processed; - return 0; - } - - - *bytesProcessed=0; - return -processed; -} - -s32 CALLBACK sif0DmaWrite (s32 channel, u32* data, u32 bytesLeft, u32* bytesProcessed) -{ - DevCon.Warning("SIF0 Dma Write to iop?!"); - *bytesProcessed=0; return 0; -} - -void CALLBACK sif0DmaInterrupt (s32 channel) -{ - -} - -s32 CALLBACK sif1DmaRead (s32 channel, u32* data, u32 bytesLeft, u32* bytesProcessed) -{ - DevCon.Warning("SIF1 Dma Read from iop?!"); - *bytesProcessed=0; return 0; -} - -s32 CALLBACK sif1DmaWrite (s32 channel, u32* data, u32 bytesLeft, u32* bytesProcessed) -{ - *bytesProcessed=0; - return 0; -} - -void CALLBACK sif1DmaInterrupt (s32 channel) -{ - -} - -#endif \ No newline at end of file + + return transferSizeBytes; +} + +s32 CALLBACK sif0DmaRead (s32 channel, u32* data, u32 bytesLeft, u32* bytesProcessed) +{ + s32 processed = DoSIFWrite(bytesLeft); + + if(processed>0) + { + bytesLeft -= processed; + if(bytesLeft == 0) + FinalizeEERead(); + + *bytesProcessed = processed; + return 0; + } + + + *bytesProcessed=0; + return -processed; +} + +s32 CALLBACK sif0DmaWrite (s32 channel, u32* data, u32 bytesLeft, u32* bytesProcessed) +{ + DevCon.Warning("SIF0 Dma Write to iop?!"); + *bytesProcessed=0; return 0; +} + +void CALLBACK sif0DmaInterrupt (s32 channel) +{ + +} + +s32 CALLBACK sif1DmaRead (s32 channel, u32* data, u32 bytesLeft, u32* bytesProcessed) +{ + DevCon.Warning("SIF1 Dma Read from iop?!"); + *bytesProcessed=0; return 0; +} + +s32 CALLBACK sif1DmaWrite (s32 channel, u32* data, u32 bytesLeft, u32* bytesProcessed) +{ + *bytesProcessed=0; + return 0; +} + +void CALLBACK sif1DmaInterrupt (s32 channel) +{ + +} + +#endif diff --git a/pcsx2/x86/iFPUd.cpp b/pcsx2/x86/iFPUd.cpp index 84f72d1495..0755b5cfa0 100644 --- a/pcsx2/x86/iFPUd.cpp +++ b/pcsx2/x86/iFPUd.cpp @@ -1,1090 +1,1090 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2009 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 "Common.h" -#include "R5900OpcodeTables.h" -#include "x86emitter/x86emitter.h" -#include "iR5900.h" -#include "iFPU.h" - -/* This is a version of the FPU that emulates an exponent of 0xff and overflow/underflow flags */ - -/* Can be made faster by not converting stuff back and forth between instructions. */ - - -//---------------------------------------------------------------- -// FPU emulation status: -// ADD, SUB (incl. accumulation stage of MADD/MSUB) - no known problems. -// Mul (incl. multiplication stage of MADD/MSUB) - incorrect. PS2's result mantissa is sometimes -// smaller by 0x1 than IEEE's result (with round to zero). -// DIV, SQRT, RSQRT - incorrect. PS2's result varies between IEEE's result with round to zero -// and IEEE's result with round to +/-infinity. -// other stuff - no known problems. -//---------------------------------------------------------------- - - -using namespace x86Emitter; - -// Set overflow flag (define only if FPU_RESULT is 1) -#define FPU_FLAGS_OVERFLOW 1 -// Set underflow flag (define only if FPU_RESULT is 1) -#define FPU_FLAGS_UNDERFLOW 1 - -// If 1, result is not clamped (Gives correct results as in PS2, -// but can cause problems due to insufficient clamping levels in the VUs) -#define FPU_RESULT 1 - -// Set I&D flags. also impacts other aspects of DIV/R/SQRT correctness -#define FPU_FLAGS_ID 1 - -// Add/Sub opcodes produce the same results as the ps2 -#define FPU_CORRECT_ADD_SUB 1 - -#ifdef FPU_RECOMPILE - -//------------------------------------------------------------------ -namespace R5900 { -namespace Dynarec { -namespace OpcodeImpl { -namespace COP1 { - -u32 __fastcall FPU_MUL_HACK(u32 s, u32 t); - -namespace DOUBLE { - -//------------------------------------------------------------------ -// Helper Macros -//------------------------------------------------------------------ -#define _Ft_ _Rt_ -#define _Fs_ _Rd_ -#define _Fd_ _Sa_ - -// FCR31 Flags -#define FPUflagC 0X00800000 -#define FPUflagI 0X00020000 -#define FPUflagD 0X00010000 -#define FPUflagO 0X00008000 -#define FPUflagU 0X00004000 -#define FPUflagSI 0X00000040 -#define FPUflagSD 0X00000020 -#define FPUflagSO 0X00000010 -#define FPUflagSU 0X00000008 - -#define REC_FPUBRANCH(f) \ - void f(); \ - void rec##f() { \ - iFlushCall(FLUSH_INTERPRETER); \ - CALLFunc((uptr)R5900::Interpreter::OpcodeImpl::COP1::f); \ - branch = 2; \ -} - -#define REC_FPUFUNC(f) \ - void f(); \ - void rec##f() { \ - iFlushCall(FLUSH_INTERPRETER); \ - CALLFunc((uptr)R5900::Interpreter::OpcodeImpl::COP1::f); \ -} -//------------------------------------------------------------------ - -//------------------------------------------------------------------ -// *FPU Opcodes!* -//------------------------------------------------------------------ - -//------------------------------------------------------------------ -// PS2 -> DOUBLE -//------------------------------------------------------------------ - -#define SINGLE(sign, exp, mant) (((sign)<<31) | ((exp)<<23) | (mant)) -#define DOUBLE(sign, exp, mant) (((sign ## ULL)<<63) | ((exp ## ULL)<<52) | (mant ## ULL)) - -struct FPUd_Globals -{ - u32 neg[4], pos[4]; - - u32 pos_inf[4], neg_inf[4], - one_exp[4]; - - u64 dbl_one_exp[2]; - - u64 dbl_cvt_overflow, // needs special code if above or equal - dbl_ps2_overflow, // overflow & clamp if above or equal - dbl_underflow; // underflow if below - - u64 padding; - - u64 dbl_s_pos[2]; - //u64 dlb_s_neg[2]; -}; - -static const __aligned(32) FPUd_Globals s_const = -{ - { 0x80000000, 0xffffffff, 0xffffffff, 0xffffffff }, - { 0x7fffffff, 0xffffffff, 0xffffffff, 0xffffffff }, - - {SINGLE(0,0xff,0), 0, 0, 0}, - {SINGLE(1,0xff,0), 0, 0, 0}, - {SINGLE(0,1,0), 0, 0, 0}, - - {DOUBLE(0,1,0), 0}, - - DOUBLE(0,1151,0), // cvt_overflow - DOUBLE(0,1152,0), // ps2_overflow - DOUBLE(0,897,0), // underflow - - 0, // Padding!! - - {0x7fffffffffffffffULL, 0}, - //{0x8000000000000000ULL, 0}, -}; - - -// ToDouble : converts single-precision PS2 float to double-precision IEEE float - -void ToDouble(int reg) -{ - SSE_UCOMISS_M32_to_XMM(reg, (uptr)s_const.pos_inf); // Sets ZF if reg is equal or incomparable to pos_inf - u8 *to_complex = JE8(0); // Complex conversion if positive infinity or NaN - SSE_UCOMISS_M32_to_XMM(reg, (uptr)s_const.neg_inf); - u8 *to_complex2 = JE8(0); // Complex conversion if negative infinity - - SSE2_CVTSS2SD_XMM_to_XMM(reg, reg); // Simply convert - u8 *end = JMP8(0); - - x86SetJ8(to_complex); - x86SetJ8(to_complex2); - - // Special conversion for when IEEE sees the value in reg as an INF/NaN - SSE2_PSUBD_M128_to_XMM(reg, (uptr)s_const.one_exp); // Lower exponent by one - SSE2_CVTSS2SD_XMM_to_XMM(reg, reg); - SSE2_PADDQ_M128_to_XMM(reg, (uptr)s_const.dbl_one_exp); // Raise exponent by one - - x86SetJ8(end); -} - -//------------------------------------------------------------------ -// DOUBLE -> PS2 -//------------------------------------------------------------------ - -/* - if FPU_RESULT is defined, results are more like the real PS2's FPU. But new issues may happen if - the VU isn't clamping all operands since games may transfer FPU results into the VU. - Ar tonelico 1 does this with the result from DIV/RSQRT (when a division by zero occurs) - otherwise, results are still usually better than iFPU.cpp. -*/ - -// ToPS2FPU_Full - converts double-precision IEEE float to single-precision PS2 float - -// converts small normal numbers to PS2 equivalent -// converts large normal numbers to PS2 equivalent (which represent NaN/inf in IEEE) -// converts really large normal numbers to PS2 signed max -// converts really small normal numbers to zero (flush) -// doesn't handle inf/nan/denormal - -void ToPS2FPU_Full(int reg, bool flags, int absreg, bool acc, bool addsub) -{ - if (flags) - AND32ItoM((uptr)&fpuRegs.fprc[31], ~(FPUflagO | FPUflagU)); - if (flags && acc) - AND32ItoM((uptr)&fpuRegs.ACCflag, ~1); - - SSE_MOVAPS_XMM_to_XMM(absreg, reg); - SSE2_ANDPD_M128_to_XMM(absreg, (uptr)&s_const.dbl_s_pos); - - SSE2_UCOMISD_M64_to_XMM(absreg, (uptr)&s_const.dbl_cvt_overflow); - u8 *to_complex = JAE8(0); - - SSE2_UCOMISD_M64_to_XMM(absreg, (uptr)&s_const.dbl_underflow); - u8 *to_underflow = JB8(0); - - SSE2_CVTSD2SS_XMM_to_XMM(reg, reg); //simply convert - u8 *end = JMP8(0); - - x86SetJ8(to_complex); - SSE2_UCOMISD_M64_to_XMM(absreg, (uptr)&s_const.dbl_ps2_overflow); - u8 *to_overflow = JAE8(0); - - SSE2_PSUBQ_M128_to_XMM(reg, (uptr)&s_const.dbl_one_exp); //lower exponent - SSE2_CVTSD2SS_XMM_to_XMM(reg, reg); //convert - SSE2_PADDD_M128_to_XMM(reg, (uptr)s_const.one_exp); //raise exponent - u8 *end2 = JMP8(0); - - x86SetJ8(to_overflow); - SSE2_CVTSD2SS_XMM_to_XMM(reg, reg); - SSE_ORPS_M128_to_XMM(reg, (uptr)&s_const.pos); //clamp - if (flags && FPU_FLAGS_OVERFLOW) - OR32ItoM((uptr)&fpuRegs.fprc[31], (FPUflagO | FPUflagSO)); - if (flags && FPU_FLAGS_OVERFLOW && acc) - OR32ItoM((uptr)&fpuRegs.ACCflag, 1); - u8 *end3 = JMP8(0); - - x86SetJ8(to_underflow); - u8 *end4; - if (flags && FPU_FLAGS_UNDERFLOW) //set underflow flags if not zero - { - SSE2_XORPD_XMM_to_XMM(absreg, absreg); - SSE2_UCOMISD_XMM_to_XMM(reg, absreg); - u8 *is_zero = JE8(0); - - OR32ItoM((uptr)&fpuRegs.fprc[31], (FPUflagU | FPUflagSU)); - if (addsub) - { - //On ADD/SUB, the PS2 simply leaves the mantissa bits as they are (after normalization) - //IEEE either clears them (FtZ) or returns the denormalized result. - //not thoroughly tested : other operations such as MUL and DIV seem to clear all mantissa bits? - SSE_MOVAPS_XMM_to_XMM(absreg, reg); - SSE2_PSLLQ_I8_to_XMM(reg, 12); //mantissa bits - SSE2_PSRLQ_I8_to_XMM(reg, 41); - SSE2_PSRLQ_I8_to_XMM(absreg, 63); //sign bit - SSE2_PSLLQ_I8_to_XMM(absreg, 31); - SSE2_POR_XMM_to_XMM(reg, absreg); - end4 = JMP8(0); - } - - x86SetJ8(is_zero); - } - SSE2_CVTSD2SS_XMM_to_XMM(reg, reg); - SSE_ANDPS_M128_to_XMM(reg, (uptr)s_const.neg); //flush to zero - - x86SetJ8(end); - x86SetJ8(end2); - x86SetJ8(end3); - if (flags && FPU_FLAGS_UNDERFLOW && addsub) - x86SetJ8(end4); -} - -void ToPS2FPU(int reg, bool flags, int absreg, bool acc, bool addsub = false) -{ - if (FPU_RESULT) - ToPS2FPU_Full(reg, flags, absreg, acc, addsub); - else - { - SSE2_CVTSD2SS_XMM_to_XMM(reg, reg); //clamp - SSE_MINSS_M32_to_XMM(reg, (uptr)&g_maxvals[0]); - SSE_MAXSS_M32_to_XMM(reg, (uptr)&g_minvals[0]); - } -} - -//sets the maximum (positive or negative) value into regd. -void SetMaxValue(int regd) -{ - if (FPU_RESULT) - SSE_ORPS_M128_to_XMM(regd, (uptr)&s_const.pos[0]); // set regd to maximum - else - { - SSE_ANDPS_M128_to_XMM(regd, (uptr)&s_const.neg[0]); // Get the sign bit - SSE_ORPS_M128_to_XMM(regd, (uptr)&g_maxvals[0]); // regd = +/- Maximum (CLAMP)! - } -} - -#define GET_S(sreg) { \ - if( info & PROCESS_EE_S ) SSE_MOVSS_XMM_to_XMM(sreg, EEREC_S); \ - else SSE_MOVSS_M32_to_XMM(sreg, (uptr)&fpuRegs.fpr[_Fs_]); } - -#define ALLOC_S(sreg) { sreg = _allocTempXMMreg(XMMT_FPS, -1); GET_S(sreg); } - -#define GET_T(treg) { \ - if( info & PROCESS_EE_T ) SSE_MOVSS_XMM_to_XMM(treg, EEREC_T); \ - else SSE_MOVSS_M32_to_XMM(treg, (uptr)&fpuRegs.fpr[_Ft_]); } - -#define ALLOC_T(treg) { treg = _allocTempXMMreg(XMMT_FPS, -1); GET_T(treg); } - -#define GET_ACC(areg) { \ - if( info & PROCESS_EE_ACC ) SSE_MOVSS_XMM_to_XMM(areg, EEREC_ACC); \ - else SSE_MOVSS_M32_to_XMM(areg, (uptr)&fpuRegs.ACC); } - -#define ALLOC_ACC(areg) { areg = _allocTempXMMreg(XMMT_FPS, -1); GET_ACC(areg); } - -#define CLEAR_OU_FLAGS { AND32ItoM((uptr)&fpuRegs.fprc[31], ~(FPUflagO | FPUflagU)); } - - -//------------------------------------------------------------------ -// ABS XMM -//------------------------------------------------------------------ -void recABS_S_xmm(int info) -{ - GET_S(EEREC_D); - - CLEAR_OU_FLAGS; - - SSE_ANDPS_M128_to_XMM(EEREC_D, (uptr)s_const.pos); -} - -FPURECOMPILE_CONSTCODE(ABS_S, XMMINFO_WRITED|XMMINFO_READS); -//------------------------------------------------------------------ - - -//------------------------------------------------------------------ -// FPU_ADD_SUB (Used to mimic PS2's FPU add/sub behavior) -//------------------------------------------------------------------ -// Compliant IEEE FPU uses, in computations, uses additional "guard" bits to the right of the mantissa -// but EE-FPU doesn't. Substraction (and addition of positive and negative) may shift the mantissa left, -// causing those bits to appear in the result; this function masks out the bits of the mantissa that will -// get shifted right to the guard bits to ensure that the guard bits are empty. -// The difference of the exponents = the amount that the smaller operand will be shifted right by. -// Modification - the PS2 uses a single guard bit? (Coded by Nneeve) -//------------------------------------------------------------------ -void FPU_ADD_SUB(int tempd, int tempt) //tempd and tempt are overwritten, they are floats -{ - int tempecx = _allocX86reg(ECX, X86TYPE_TEMP, 0, 0); //receives regd - int temp2 = _allocX86reg(-1, X86TYPE_TEMP, 0, 0); //receives regt - int xmmtemp = _allocTempXMMreg(XMMT_FPS, -1); //temporary for anding with regd/regt - - if (tempecx != ECX) { Console.Error("FPU: ADD/SUB Allocation Error!"); tempecx = ECX;} - if (temp2 == -1) { Console.Error("FPU: ADD/SUB Allocation Error!"); temp2 = EAX;} - if (xmmtemp == -1) { Console.Error("FPU: ADD/SUB Allocation Error!"); xmmtemp = XMM0;} - - SSE2_MOVD_XMM_to_R(tempecx, tempd); - SSE2_MOVD_XMM_to_R(temp2, tempt); - - //mask the exponents - SHR32ItoR(tempecx, 23); - SHR32ItoR(temp2, 23); - AND32ItoR(tempecx, 0xff); - AND32ItoR(temp2, 0xff); - - SUB32RtoR(tempecx, temp2); //tempecx = exponent difference - CMP32ItoR(tempecx, 25); - j8Ptr[0] = JGE8(0); - CMP32ItoR(tempecx, 0); - j8Ptr[1] = JG8(0); - j8Ptr[2] = JE8(0); - CMP32ItoR(tempecx, -25); - j8Ptr[3] = JLE8(0); - - //diff = -24 .. -1 , expd < expt - NEG32R(tempecx); - DEC32R(tempecx); - MOV32ItoR(temp2, 0xffffffff); - SHL32CLtoR(temp2); //temp2 = 0xffffffff << tempecx - SSE2_MOVD_R_to_XMM(xmmtemp, temp2); - SSE_ANDPS_XMM_to_XMM(tempd, xmmtemp); - j8Ptr[4] = JMP8(0); - - x86SetJ8(j8Ptr[0]); - //diff = 25 .. 255 , expt < expd - SSE_ANDPS_M128_to_XMM(tempt, (uptr)s_const.neg); - j8Ptr[5] = JMP8(0); - - x86SetJ8(j8Ptr[1]); - //diff = 1 .. 24, expt < expd - DEC32R(tempecx); - MOV32ItoR(temp2, 0xffffffff); - SHL32CLtoR(temp2); //temp2 = 0xffffffff << tempecx - SSE2_MOVD_R_to_XMM(xmmtemp, temp2); - SSE_ANDPS_XMM_to_XMM(tempt, xmmtemp); - j8Ptr[6] = JMP8(0); - - x86SetJ8(j8Ptr[3]); - //diff = -255 .. -25, expd < expt - SSE_ANDPS_M128_to_XMM(tempd, (uptr)s_const.neg); - j8Ptr[7] = JMP8(0); - - x86SetJ8(j8Ptr[2]); - //diff == 0 - - x86SetJ8(j8Ptr[4]); - x86SetJ8(j8Ptr[5]); - x86SetJ8(j8Ptr[6]); - x86SetJ8(j8Ptr[7]); - - _freeXMMreg(xmmtemp); - _freeX86reg(temp2); - _freeX86reg(tempecx); -} - -void FPU_MUL(int info, int regd, int sreg, int treg, bool acc) -{ - u8 *noHack; - u32 *endMul; - - if (CHECK_FPUMULHACK) - { - SSE2_MOVD_XMM_to_R(ECX, sreg); - SSE2_MOVD_XMM_to_R(EDX, treg); - CALLFunc( (uptr)&FPU_MUL_HACK ); //returns the hacked result or 0 - TEST32RtoR(EAX, EAX); - noHack = JZ8(0); - SSE2_MOVD_R_to_XMM(regd, EAX); - endMul = JMP32(0); - x86SetJ8(noHack); - } - - ToDouble(sreg); ToDouble(treg); - SSE2_MULSD_XMM_to_XMM(sreg, treg); - ToPS2FPU(sreg, true, treg, acc); - SSE_MOVSS_XMM_to_XMM(regd, sreg); - - if (CHECK_FPUMULHACK) - x86SetJ32(endMul); -} - -//------------------------------------------------------------------ -// CommutativeOp XMM (used for ADD and SUB opcodes. that's it.) -//------------------------------------------------------------------ -static void (*recFPUOpXMM_to_XMM[] )(x86SSERegType, x86SSERegType) = { - SSE2_ADDSD_XMM_to_XMM, SSE2_SUBSD_XMM_to_XMM }; - -void recFPUOp(int info, int regd, int op, bool acc) -{ - int sreg, treg; - ALLOC_S(sreg); ALLOC_T(treg); - - if (FPU_CORRECT_ADD_SUB) - FPU_ADD_SUB(sreg, treg); - - ToDouble(sreg); ToDouble(treg); - - recFPUOpXMM_to_XMM[op](sreg, treg); - - ToPS2FPU(sreg, true, treg, acc, true); - SSE_MOVSS_XMM_to_XMM(regd, sreg); - - _freeXMMreg(sreg); _freeXMMreg(treg); -} -//------------------------------------------------------------------ - - -//------------------------------------------------------------------ -// ADD XMM -//------------------------------------------------------------------ -void recADD_S_xmm(int info) -{ - recFPUOp(info, EEREC_D, 0, false); -} - -FPURECOMPILE_CONSTCODE(ADD_S, XMMINFO_WRITED|XMMINFO_READS|XMMINFO_READT); - -void recADDA_S_xmm(int info) -{ - recFPUOp(info, EEREC_ACC, 0, true); -} - -FPURECOMPILE_CONSTCODE(ADDA_S, XMMINFO_WRITEACC|XMMINFO_READS|XMMINFO_READT); -//------------------------------------------------------------------ - -void recCMP(int info) -{ - int sreg, treg; - ALLOC_S(sreg); ALLOC_T(treg); - ToDouble(sreg); ToDouble(treg); - - SSE2_UCOMISD_XMM_to_XMM(sreg, treg); - - _freeXMMreg(sreg); _freeXMMreg(treg); -} - -//------------------------------------------------------------------ -// C.x.S XMM -//------------------------------------------------------------------ -void recC_EQ_xmm(int info) -{ - recCMP(info); - - j8Ptr[0] = JZ8(0); - AND32ItoM( (uptr)&fpuRegs.fprc[31], ~FPUflagC ); - j8Ptr[1] = JMP8(0); - x86SetJ8(j8Ptr[0]); - OR32ItoM((uptr)&fpuRegs.fprc[31], FPUflagC); - x86SetJ8(j8Ptr[1]); -} - -FPURECOMPILE_CONSTCODE(C_EQ, XMMINFO_READS|XMMINFO_READT); - -void recC_LE_xmm(int info ) -{ - recCMP(info); - - j8Ptr[0] = JBE8(0); - AND32ItoM( (uptr)&fpuRegs.fprc[31], ~FPUflagC ); - j8Ptr[1] = JMP8(0); - x86SetJ8(j8Ptr[0]); - OR32ItoM((uptr)&fpuRegs.fprc[31], FPUflagC); - x86SetJ8(j8Ptr[1]); -} - -FPURECOMPILE_CONSTCODE(C_LE, XMMINFO_READS|XMMINFO_READT); - -void recC_LT_xmm(int info) -{ - recCMP(info); - - j8Ptr[0] = JB8(0); - AND32ItoM( (uptr)&fpuRegs.fprc[31], ~FPUflagC ); - j8Ptr[1] = JMP8(0); - x86SetJ8(j8Ptr[0]); - OR32ItoM((uptr)&fpuRegs.fprc[31], FPUflagC); - x86SetJ8(j8Ptr[1]); -} - -FPURECOMPILE_CONSTCODE(C_LT, XMMINFO_READS|XMMINFO_READT); -//------------------------------------------------------------------ - - -//------------------------------------------------------------------ -// CVT.x XMM -//------------------------------------------------------------------ -void recCVT_S_xmm(int info) -{ - if( !(info&PROCESS_EE_S) || (EEREC_D != EEREC_S && !(info&PROCESS_EE_MODEWRITES)) ) { - SSE_CVTSI2SS_M32_to_XMM(EEREC_D, (uptr)&fpuRegs.fpr[_Fs_]); - } - else { - SSE2_CVTDQ2PS_XMM_to_XMM(EEREC_D, EEREC_S); - } -} - -FPURECOMPILE_CONSTCODE(CVT_S, XMMINFO_WRITED|XMMINFO_READS); - -void recCVT_W() //called from iFPU.cpp's recCVT_W -{ - int regs = _checkXMMreg(XMMTYPE_FPREG, _Fs_, MODE_READ); - - if( regs >= 0 ) - { - SSE_CVTTSS2SI_XMM_to_R32(EAX, regs); - SSE_MOVMSKPS_XMM_to_R32(EDX, regs); //extract the signs - AND32ItoR(EDX,1); //keep only LSB - } - else - { - SSE_CVTTSS2SI_M32_to_R32(EAX, (uptr)&fpuRegs.fpr[ _Fs_ ]); - MOV32MtoR(EDX, (uptr)&fpuRegs.fpr[ _Fs_ ]); - SHR32ItoR(EDX, 31); //mov sign to lsb - } - - //kill register allocation for dst because we write directly to fpuRegs.fpr[_Fd_] - _deleteFPtoXMMreg(_Fd_, 2); - - ADD32ItoR(EDX, 0x7FFFFFFF); //0x7FFFFFFF if positive, 0x8000 0000 if negative - - CMP32ItoR(EAX, 0x80000000); //If the result is indefinitive - CMOVE32RtoR(EAX, EDX); //Saturate it - - //Write the result - MOV32RtoM((uptr)&fpuRegs.fpr[_Fd_], EAX); -} -//------------------------------------------------------------------ - - -//------------------------------------------------------------------ -// DIV XMM -//------------------------------------------------------------------ -void recDIVhelper1(int regd, int regt) // Sets flags -{ - u8 *pjmp1, *pjmp2; - u32 *ajmp32, *bjmp32; - int t1reg = _allocTempXMMreg(XMMT_FPS, -1); - int tempReg = _allocX86reg(-1, X86TYPE_TEMP, 0, 0); - if (t1reg == -1) {Console.Error("FPU: DIV Allocation Error!");} - if (tempReg == -1) {Console.Error("FPU: DIV Allocation Error!"); tempReg = EAX;} - - AND32ItoM((uptr)&fpuRegs.fprc[31], ~(FPUflagI|FPUflagD)); // Clear I and D flags - - //--- Check for divide by zero --- - SSE_XORPS_XMM_to_XMM(t1reg, t1reg); - SSE_CMPEQSS_XMM_to_XMM(t1reg, regt); - SSE_MOVMSKPS_XMM_to_R32(tempReg, t1reg); - AND32ItoR(tempReg, 1); //Check sign (if regt == zero, sign will be set) - ajmp32 = JZ32(0); //Skip if not set - - //--- Check for 0/0 --- - SSE_XORPS_XMM_to_XMM(t1reg, t1reg); - SSE_CMPEQSS_XMM_to_XMM(t1reg, regd); - SSE_MOVMSKPS_XMM_to_R32(tempReg, t1reg); - AND32ItoR(tempReg, 1); //Check sign (if regd == zero, sign will be set) - pjmp1 = JZ8(0); //Skip if not set - OR32ItoM((uptr)&fpuRegs.fprc[31], FPUflagI|FPUflagSI); // Set I and SI flags ( 0/0 ) - pjmp2 = JMP8(0); - x86SetJ8(pjmp1); //x/0 but not 0/0 - OR32ItoM((uptr)&fpuRegs.fprc[31], FPUflagD|FPUflagSD); // Set D and SD flags ( x/0 ) - x86SetJ8(pjmp2); - - //--- Make regd +/- Maximum --- - SSE_XORPS_XMM_to_XMM(regd, regt); // Make regd Positive or Negative - SetMaxValue(regd); //clamp to max - bjmp32 = JMP32(0); - - x86SetJ32(ajmp32); - - //--- Normal Divide --- - ToDouble(regd); ToDouble(regt); - - SSE2_DIVSD_XMM_to_XMM(regd, regt); - - ToPS2FPU(regd, false, regt, false); - - x86SetJ32(bjmp32); - - _freeXMMreg(t1reg); - _freeX86reg(tempReg); -} - -void recDIVhelper2(int regd, int regt) // Doesn't sets flags -{ - ToDouble(regd); ToDouble(regt); - - SSE2_DIVSD_XMM_to_XMM(regd, regt); - - ToPS2FPU(regd, false, regt, false); -} - -static __aligned16 SSE_MXCSR roundmode_nearest, roundmode_neg; - -void recDIV_S_xmm(int info) -{ - bool roundmodeFlag = false; - //if (t0reg == -1) {Console.Error("FPU: DIV Allocation Error!");} - //Console.WriteLn("DIV"); - - if( CHECK_FPUNEGDIVHACK ) - { - if (g_sseMXCSR.GetRoundMode() != SSEround_NegInf) - { - // Set roundmode to nearest since it isn't already - //Console.WriteLn("div to negative inf"); - - roundmode_neg = g_sseMXCSR; - roundmode_neg.SetRoundMode( SSEround_NegInf ); - xLDMXCSR( roundmode_neg ); - roundmodeFlag = true; - } - } - else - { - if (g_sseMXCSR.GetRoundMode() != SSEround_Nearest) - { - // Set roundmode to nearest since it isn't already - //Console.WriteLn("div to nearest"); - - roundmode_nearest = g_sseMXCSR; - roundmode_nearest.SetRoundMode( SSEround_Nearest ); - xLDMXCSR( roundmode_nearest ); - roundmodeFlag = true; - } - } - - int sreg, treg; - - ALLOC_S(sreg); ALLOC_T(treg); - - if (FPU_FLAGS_ID) - recDIVhelper1(sreg, treg); - else - recDIVhelper2(sreg, treg); - - SSE_MOVSS_XMM_to_XMM(EEREC_D, sreg); - - if (roundmodeFlag) xLDMXCSR (g_sseMXCSR); - _freeXMMreg(sreg); _freeXMMreg(treg); -} - -FPURECOMPILE_CONSTCODE(DIV_S, XMMINFO_WRITED|XMMINFO_READS|XMMINFO_READT); -//------------------------------------------------------------------ - - -//------------------------------------------------------------------ -// MADD/MSUB XMM -//------------------------------------------------------------------ - -// Unlike what the documentation implies, it seems that MADD/MSUB support all numbers just like other operations -// The complex overflow conditions the document describes apparently test whether the multiplication's result -// has overflowed and whether the last operation that used ACC as a destination has overflowed. -// For example, { adda.s -MAX, 0.0 ; madd.s fd, MAX, 1.0 } -> fd = 0 -// while { adda.s -MAX, -MAX ; madd.s fd, MAX, 1.0 } -> fd = -MAX -// (where MAX is 0x7fffffff and -MAX is 0xffffffff) -void recMaddsub(int info, int regd, int op, bool acc) -{ - int sreg, treg; - ALLOC_S(sreg); ALLOC_T(treg); - - FPU_MUL(info, sreg, sreg, treg, false); - - GET_ACC(treg); - - if (FPU_CORRECT_ADD_SUB) - FPU_ADD_SUB(treg, sreg); //might be problematic for something!!!! - - // TEST FOR ACC/MUL OVERFLOWS, PROPOGATE THEM IF THEY OCCUR - - TEST32ItoM((uptr)&fpuRegs.fprc[31], FPUflagO); - u8 *mulovf = JNZ8(0); - ToDouble(sreg); //else, convert - - TEST32ItoM((uptr)&fpuRegs.ACCflag, 1); - u8 *accovf = JNZ8(0); - ToDouble(treg); //else, convert - u8 *operation = JMP8(0); - - x86SetJ8(mulovf); - if (op == 1) //sub - SSE_XORPS_M128_to_XMM(sreg, (uptr)s_const.neg); - SSE_MOVAPS_XMM_to_XMM(treg, sreg); //fall through below - - x86SetJ8(accovf); - SetMaxValue(treg); //just in case... I think it has to be a MaxValue already here - CLEAR_OU_FLAGS; //clear U flag - if (FPU_FLAGS_OVERFLOW) - OR32ItoM((uptr)&fpuRegs.fprc[31], FPUflagO | FPUflagSO); - if (FPU_FLAGS_OVERFLOW && acc) - OR32ItoM((uptr)&fpuRegs.ACCflag, 1); - u32 *skipall = JMP32(0); - - // PERFORM THE ACCUMULATION AND TEST RESULT. CONVERT TO SINGLE - - x86SetJ8(operation); - if (op == 1) - SSE2_SUBSD_XMM_to_XMM(treg, sreg); - else - SSE2_ADDSD_XMM_to_XMM(treg, sreg); - - ToPS2FPU(treg, true, sreg, acc, true); - x86SetJ32(skipall); - - SSE_MOVSS_XMM_to_XMM(regd, treg); - - _freeXMMreg(sreg); _freeXMMreg(treg); -} - -void recMADD_S_xmm(int info) -{ - recMaddsub(info, EEREC_D, 0, false); -} - -FPURECOMPILE_CONSTCODE(MADD_S, XMMINFO_WRITED|XMMINFO_READACC|XMMINFO_READS|XMMINFO_READT); - -void recMADDA_S_xmm(int info) -{ - recMaddsub(info, EEREC_ACC, 0, true); -} - -FPURECOMPILE_CONSTCODE(MADDA_S, XMMINFO_WRITEACC|XMMINFO_READACC|XMMINFO_READS|XMMINFO_READT); -//------------------------------------------------------------------ - - -//------------------------------------------------------------------ -// MAX / MIN XMM -//------------------------------------------------------------------ - -static const __aligned16 u32 minmax_mask[8] = -{ - 0xffffffff, 0x80000000, 0, 0, - 0, 0x40000000, 0, 0 -}; -// FPU's MAX/MIN work with all numbers (including "denormals"). Check VU's logical min max for more info. -void recMINMAX(int info, bool ismin) -{ - int sreg, treg; - ALLOC_S(sreg); ALLOC_T(treg); - - CLEAR_OU_FLAGS; - - SSE2_PSHUFD_XMM_to_XMM(sreg, sreg, 0x00); - SSE2_PAND_M128_to_XMM(sreg, (uptr)minmax_mask); - SSE2_POR_M128_to_XMM(sreg, (uptr)&minmax_mask[4]); - SSE2_PSHUFD_XMM_to_XMM(treg, treg, 0x00); - SSE2_PAND_M128_to_XMM(treg, (uptr)minmax_mask); - SSE2_POR_M128_to_XMM(treg, (uptr)&minmax_mask[4]); - if (ismin) - SSE2_MINSD_XMM_to_XMM(sreg, treg); - else - SSE2_MAXSD_XMM_to_XMM(sreg, treg); - - SSE_MOVSS_XMM_to_XMM(EEREC_D, sreg); - - _freeXMMreg(sreg); _freeXMMreg(treg); -} - -void recMAX_S_xmm(int info) -{ - recMINMAX(info, false); -} - -FPURECOMPILE_CONSTCODE(MAX_S, XMMINFO_WRITED|XMMINFO_READS|XMMINFO_READT); - -void recMIN_S_xmm(int info) -{ - recMINMAX(info, true); -} - -FPURECOMPILE_CONSTCODE(MIN_S, XMMINFO_WRITED|XMMINFO_READS|XMMINFO_READT); -//------------------------------------------------------------------ - - -//------------------------------------------------------------------ -// MOV XMM -//------------------------------------------------------------------ -void recMOV_S_xmm(int info) -{ - GET_S(EEREC_D); -} - -FPURECOMPILE_CONSTCODE(MOV_S, XMMINFO_WRITED|XMMINFO_READS); -//------------------------------------------------------------------ - - -//------------------------------------------------------------------ -// MSUB XMM -//------------------------------------------------------------------ - -void recMSUB_S_xmm(int info) -{ - recMaddsub(info, EEREC_D, 1, false); -} - -FPURECOMPILE_CONSTCODE(MSUB_S, XMMINFO_WRITED|XMMINFO_READACC|XMMINFO_READS|XMMINFO_READT); - -void recMSUBA_S_xmm(int info) -{ - recMaddsub(info, EEREC_ACC, 1, true); -} - -FPURECOMPILE_CONSTCODE(MSUBA_S, XMMINFO_WRITEACC|XMMINFO_READACC|XMMINFO_READS|XMMINFO_READT); -//------------------------------------------------------------------ - -//------------------------------------------------------------------ -// MUL XMM -//------------------------------------------------------------------ -void recMUL_S_xmm(int info) -{ - int sreg, treg; - ALLOC_S(sreg); ALLOC_T(treg); - - FPU_MUL(info, EEREC_D, sreg, treg, false); - _freeXMMreg(sreg); _freeXMMreg(treg); -} - -FPURECOMPILE_CONSTCODE(MUL_S, XMMINFO_WRITED|XMMINFO_READS|XMMINFO_READT); - -void recMULA_S_xmm(int info) -{ - int sreg, treg; - ALLOC_S(sreg); ALLOC_T(treg); - - FPU_MUL(info, EEREC_ACC, sreg, treg, true); - _freeXMMreg(sreg); _freeXMMreg(treg); -} - -FPURECOMPILE_CONSTCODE(MULA_S, XMMINFO_WRITEACC|XMMINFO_READS|XMMINFO_READT); -//------------------------------------------------------------------ - - -//------------------------------------------------------------------ -// NEG XMM -//------------------------------------------------------------------ -void recNEG_S_xmm(int info) { - - GET_S(EEREC_D); - - CLEAR_OU_FLAGS; - - SSE_XORPS_M128_to_XMM(EEREC_D, (uptr)&s_const.neg[0]); -} - -FPURECOMPILE_CONSTCODE(NEG_S, XMMINFO_WRITED|XMMINFO_READS); -//------------------------------------------------------------------ - - -//------------------------------------------------------------------ -// SUB XMM -//------------------------------------------------------------------ - -void recSUB_S_xmm(int info) -{ - recFPUOp(info, EEREC_D, 1, false); -} - -FPURECOMPILE_CONSTCODE(SUB_S, XMMINFO_WRITED|XMMINFO_READS|XMMINFO_READT); - - -void recSUBA_S_xmm(int info) -{ - recFPUOp(info, EEREC_ACC, 1, true); -} - -FPURECOMPILE_CONSTCODE(SUBA_S, XMMINFO_WRITEACC|XMMINFO_READS|XMMINFO_READT); -//------------------------------------------------------------------ - - -//------------------------------------------------------------------ -// SQRT XMM -//------------------------------------------------------------------ -void recSQRT_S_xmm(int info) -{ - u8 *pjmp; - int roundmodeFlag = 0; - int tempReg = _allocX86reg(-1, X86TYPE_TEMP, 0, 0); - if (tempReg == -1) {Console.Error("FPU: SQRT Allocation Error!"); tempReg = EAX;} - int t1reg = _allocTempXMMreg(XMMT_FPS, -1); - if (t1reg == -1) {Console.Error("FPU: SQRT Allocation Error!");} - //Console.WriteLn("FPU: SQRT"); - - if (g_sseMXCSR.GetRoundMode() != SSEround_Nearest) - { - // Set roundmode to nearest if it isn't already - //Console.WriteLn("sqrt to nearest"); - roundmode_nearest = g_sseMXCSR; - roundmode_nearest.SetRoundMode( SSEround_Nearest ); - xLDMXCSR (roundmode_nearest); - roundmodeFlag = 1; - } - - GET_T(EEREC_D); - - if (FPU_FLAGS_ID) { - AND32ItoM((uptr)&fpuRegs.fprc[31], ~(FPUflagI|FPUflagD)); // Clear I and D flags - - //--- Check for negative SQRT --- (sqrt(-0) = 0, unlike what the docs say) - SSE_MOVMSKPS_XMM_to_R32(tempReg, EEREC_D); - AND32ItoR(tempReg, 1); //Check sign - pjmp = JZ8(0); //Skip if none are - OR32ItoM((uptr)&fpuRegs.fprc[31], FPUflagI|FPUflagSI); // Set I and SI flags - SSE_ANDPS_M128_to_XMM(EEREC_D, (uptr)&s_const.pos[0]); // Make EEREC_D Positive - x86SetJ8(pjmp); - } - else - { - SSE_ANDPS_M128_to_XMM(EEREC_D, (uptr)&s_const.pos[0]); // Make EEREC_D Positive - } - - - ToDouble(EEREC_D); - - SSE2_SQRTSD_XMM_to_XMM(EEREC_D, EEREC_D); - - ToPS2FPU(EEREC_D, false, t1reg, false); - - if (roundmodeFlag == 1) { - xLDMXCSR (g_sseMXCSR); - } - - _freeX86reg(tempReg); - _freeXMMreg(t1reg); -} - -FPURECOMPILE_CONSTCODE(SQRT_S, XMMINFO_WRITED|XMMINFO_READT); -//------------------------------------------------------------------ - - -//------------------------------------------------------------------ -// RSQRT XMM -//------------------------------------------------------------------ -void recRSQRThelper1(int regd, int regt) // Preforms the RSQRT function when regd <- Fs and regt <- Ft (Sets correct flags) -{ - u8 *pjmp1, *pjmp2; - u8 *qjmp1, *qjmp2; - u32 *pjmp32; - int t1reg = _allocTempXMMreg(XMMT_FPS, -1); - int tempReg = _allocX86reg(-1, X86TYPE_TEMP, 0, 0); - if (t1reg == -1) {Console.Error("FPU: RSQRT Allocation Error!");} - if (tempReg == -1) {Console.Error("FPU: RSQRT Allocation Error!"); tempReg = EAX;} - - AND32ItoM((uptr)&fpuRegs.fprc[31], ~(FPUflagI|FPUflagD)); // Clear I and D flags - - //--- (first) Check for negative SQRT --- - SSE_MOVMSKPS_XMM_to_R32(tempReg, regt); - AND32ItoR(tempReg, 1); //Check sign - pjmp2 = JZ8(0); //Skip if not set - OR32ItoM((uptr)&fpuRegs.fprc[31], FPUflagI|FPUflagSI); // Set I and SI flags - SSE_ANDPS_M128_to_XMM(regt, (uptr)&s_const.pos[0]); // Make regt Positive - x86SetJ8(pjmp2); - - //--- Check for zero --- - SSE_XORPS_XMM_to_XMM(t1reg, t1reg); - SSE_CMPEQSS_XMM_to_XMM(t1reg, regt); - SSE_MOVMSKPS_XMM_to_R32(tempReg, t1reg); - AND32ItoR(tempReg, 1); //Check sign (if regt == zero, sign will be set) - pjmp1 = JZ8(0); //Skip if not set - - //--- Check for 0/0 --- - SSE_XORPS_XMM_to_XMM(t1reg, t1reg); - SSE_CMPEQSS_XMM_to_XMM(t1reg, regd); - SSE_MOVMSKPS_XMM_to_R32(tempReg, t1reg); - AND32ItoR(tempReg, 1); //Check sign (if regd == zero, sign will be set) - qjmp1 = JZ8(0); //Skip if not set - OR32ItoM((uptr)&fpuRegs.fprc[31], FPUflagI|FPUflagSI); // Set I and SI flags ( 0/0 ) - qjmp2 = JMP8(0); - x86SetJ8(qjmp1); //x/0 but not 0/0 - OR32ItoM((uptr)&fpuRegs.fprc[31], FPUflagD|FPUflagSD); // Set D and SD flags ( x/0 ) - x86SetJ8(qjmp2); - - SetMaxValue(regd); //clamp to max - pjmp32 = JMP32(0); - x86SetJ8(pjmp1); - - ToDouble(regt); ToDouble(regd); - - SSE2_SQRTSD_XMM_to_XMM(regt, regt); - SSE2_DIVSD_XMM_to_XMM(regd, regt); - - ToPS2FPU(regd, false, regt, false); - x86SetJ32(pjmp32); - - _freeXMMreg(t1reg); - _freeX86reg(tempReg); -} - -void recRSQRThelper2(int regd, int regt) // Preforms the RSQRT function when regd <- Fs and regt <- Ft (Doesn't set flags) -{ - SSE_ANDPS_M128_to_XMM(regt, (uptr)&s_const.pos[0]); // Make regt Positive - - ToDouble(regt); ToDouble(regd); - - SSE2_SQRTSD_XMM_to_XMM(regt, regt); - SSE2_DIVSD_XMM_to_XMM(regd, regt); - - ToPS2FPU(regd, false, regt, false); -} - -void recRSQRT_S_xmm(int info) -{ - int sreg, treg; - - // iFPU (regular FPU) doesn't touch roundmode for rSQRT. - // Should this do the same? or is changing the roundmode to nearest the better - // behavior for both recs? --air - - bool roundmodeFlag = false; - if (g_sseMXCSR.GetRoundMode() != SSEround_Nearest) - { - // Set roundmode to nearest if it isn't already - //Console.WriteLn("sqrt to nearest"); - roundmode_nearest = g_sseMXCSR; - roundmode_nearest.SetRoundMode( SSEround_Nearest ); - xLDMXCSR (roundmode_nearest); - roundmodeFlag = true; - } - - ALLOC_S(sreg); ALLOC_T(treg); - - if (FPU_FLAGS_ID) - recRSQRThelper1(sreg, treg); - else - recRSQRThelper2(sreg, treg); - - SSE_MOVSS_XMM_to_XMM(EEREC_D, sreg); - - _freeXMMreg(treg); _freeXMMreg(sreg); - - if (roundmodeFlag) xLDMXCSR (g_sseMXCSR); -} - -FPURECOMPILE_CONSTCODE(RSQRT_S, XMMINFO_WRITED|XMMINFO_READS|XMMINFO_READT); - - -} } } } } +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2009 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 "Common.h" +#include "R5900OpcodeTables.h" +#include "x86emitter/x86emitter.h" +#include "iR5900.h" +#include "iFPU.h" + +/* This is a version of the FPU that emulates an exponent of 0xff and overflow/underflow flags */ + +/* Can be made faster by not converting stuff back and forth between instructions. */ + + +//---------------------------------------------------------------- +// FPU emulation status: +// ADD, SUB (incl. accumulation stage of MADD/MSUB) - no known problems. +// Mul (incl. multiplication stage of MADD/MSUB) - incorrect. PS2's result mantissa is sometimes +// smaller by 0x1 than IEEE's result (with round to zero). +// DIV, SQRT, RSQRT - incorrect. PS2's result varies between IEEE's result with round to zero +// and IEEE's result with round to +/-infinity. +// other stuff - no known problems. +//---------------------------------------------------------------- + + +using namespace x86Emitter; + +// Set overflow flag (define only if FPU_RESULT is 1) +#define FPU_FLAGS_OVERFLOW 1 +// Set underflow flag (define only if FPU_RESULT is 1) +#define FPU_FLAGS_UNDERFLOW 1 + +// If 1, result is not clamped (Gives correct results as in PS2, +// but can cause problems due to insufficient clamping levels in the VUs) +#define FPU_RESULT 1 + +// Set I&D flags. also impacts other aspects of DIV/R/SQRT correctness +#define FPU_FLAGS_ID 1 + +// Add/Sub opcodes produce the same results as the ps2 +#define FPU_CORRECT_ADD_SUB 1 + +#ifdef FPU_RECOMPILE + +//------------------------------------------------------------------ +namespace R5900 { +namespace Dynarec { +namespace OpcodeImpl { +namespace COP1 { + +u32 __fastcall FPU_MUL_HACK(u32 s, u32 t); + +namespace DOUBLE { + +//------------------------------------------------------------------ +// Helper Macros +//------------------------------------------------------------------ +#define _Ft_ _Rt_ +#define _Fs_ _Rd_ +#define _Fd_ _Sa_ + +// FCR31 Flags +#define FPUflagC 0X00800000 +#define FPUflagI 0X00020000 +#define FPUflagD 0X00010000 +#define FPUflagO 0X00008000 +#define FPUflagU 0X00004000 +#define FPUflagSI 0X00000040 +#define FPUflagSD 0X00000020 +#define FPUflagSO 0X00000010 +#define FPUflagSU 0X00000008 + +#define REC_FPUBRANCH(f) \ + void f(); \ + void rec##f() { \ + iFlushCall(FLUSH_INTERPRETER); \ + CALLFunc((uptr)R5900::Interpreter::OpcodeImpl::COP1::f); \ + branch = 2; \ +} + +#define REC_FPUFUNC(f) \ + void f(); \ + void rec##f() { \ + iFlushCall(FLUSH_INTERPRETER); \ + CALLFunc((uptr)R5900::Interpreter::OpcodeImpl::COP1::f); \ +} +//------------------------------------------------------------------ + +//------------------------------------------------------------------ +// *FPU Opcodes!* +//------------------------------------------------------------------ + +//------------------------------------------------------------------ +// PS2 -> DOUBLE +//------------------------------------------------------------------ + +#define SINGLE(sign, exp, mant) (((sign)<<31) | ((exp)<<23) | (mant)) +#define DOUBLE(sign, exp, mant) (((sign ## ULL)<<63) | ((exp ## ULL)<<52) | (mant ## ULL)) + +struct FPUd_Globals +{ + u32 neg[4], pos[4]; + + u32 pos_inf[4], neg_inf[4], + one_exp[4]; + + u64 dbl_one_exp[2]; + + u64 dbl_cvt_overflow, // needs special code if above or equal + dbl_ps2_overflow, // overflow & clamp if above or equal + dbl_underflow; // underflow if below + + u64 padding; + + u64 dbl_s_pos[2]; + //u64 dlb_s_neg[2]; +}; + +static const __aligned(32) FPUd_Globals s_const = +{ + { 0x80000000, 0xffffffff, 0xffffffff, 0xffffffff }, + { 0x7fffffff, 0xffffffff, 0xffffffff, 0xffffffff }, + + {SINGLE(0,0xff,0), 0, 0, 0}, + {SINGLE(1,0xff,0), 0, 0, 0}, + {SINGLE(0,1,0), 0, 0, 0}, + + {DOUBLE(0,1,0), 0}, + + DOUBLE(0,1151,0), // cvt_overflow + DOUBLE(0,1152,0), // ps2_overflow + DOUBLE(0,897,0), // underflow + + 0, // Padding!! + + {0x7fffffffffffffffULL, 0}, + //{0x8000000000000000ULL, 0}, +}; + + +// ToDouble : converts single-precision PS2 float to double-precision IEEE float + +void ToDouble(int reg) +{ + SSE_UCOMISS_M32_to_XMM(reg, (uptr)s_const.pos_inf); // Sets ZF if reg is equal or incomparable to pos_inf + u8 *to_complex = JE8(0); // Complex conversion if positive infinity or NaN + SSE_UCOMISS_M32_to_XMM(reg, (uptr)s_const.neg_inf); + u8 *to_complex2 = JE8(0); // Complex conversion if negative infinity + + SSE2_CVTSS2SD_XMM_to_XMM(reg, reg); // Simply convert + u8 *end = JMP8(0); + + x86SetJ8(to_complex); + x86SetJ8(to_complex2); + + // Special conversion for when IEEE sees the value in reg as an INF/NaN + SSE2_PSUBD_M128_to_XMM(reg, (uptr)s_const.one_exp); // Lower exponent by one + SSE2_CVTSS2SD_XMM_to_XMM(reg, reg); + SSE2_PADDQ_M128_to_XMM(reg, (uptr)s_const.dbl_one_exp); // Raise exponent by one + + x86SetJ8(end); +} + +//------------------------------------------------------------------ +// DOUBLE -> PS2 +//------------------------------------------------------------------ + +/* + if FPU_RESULT is defined, results are more like the real PS2's FPU. But new issues may happen if + the VU isn't clamping all operands since games may transfer FPU results into the VU. + Ar tonelico 1 does this with the result from DIV/RSQRT (when a division by zero occurs) + otherwise, results are still usually better than iFPU.cpp. +*/ + +// ToPS2FPU_Full - converts double-precision IEEE float to single-precision PS2 float + +// converts small normal numbers to PS2 equivalent +// converts large normal numbers to PS2 equivalent (which represent NaN/inf in IEEE) +// converts really large normal numbers to PS2 signed max +// converts really small normal numbers to zero (flush) +// doesn't handle inf/nan/denormal + +void ToPS2FPU_Full(int reg, bool flags, int absreg, bool acc, bool addsub) +{ + if (flags) + AND32ItoM((uptr)&fpuRegs.fprc[31], ~(FPUflagO | FPUflagU)); + if (flags && acc) + AND32ItoM((uptr)&fpuRegs.ACCflag, ~1); + + SSE_MOVAPS_XMM_to_XMM(absreg, reg); + SSE2_ANDPD_M128_to_XMM(absreg, (uptr)&s_const.dbl_s_pos); + + SSE2_UCOMISD_M64_to_XMM(absreg, (uptr)&s_const.dbl_cvt_overflow); + u8 *to_complex = JAE8(0); + + SSE2_UCOMISD_M64_to_XMM(absreg, (uptr)&s_const.dbl_underflow); + u8 *to_underflow = JB8(0); + + SSE2_CVTSD2SS_XMM_to_XMM(reg, reg); //simply convert + u8 *end = JMP8(0); + + x86SetJ8(to_complex); + SSE2_UCOMISD_M64_to_XMM(absreg, (uptr)&s_const.dbl_ps2_overflow); + u8 *to_overflow = JAE8(0); + + SSE2_PSUBQ_M128_to_XMM(reg, (uptr)&s_const.dbl_one_exp); //lower exponent + SSE2_CVTSD2SS_XMM_to_XMM(reg, reg); //convert + SSE2_PADDD_M128_to_XMM(reg, (uptr)s_const.one_exp); //raise exponent + u8 *end2 = JMP8(0); + + x86SetJ8(to_overflow); + SSE2_CVTSD2SS_XMM_to_XMM(reg, reg); + SSE_ORPS_M128_to_XMM(reg, (uptr)&s_const.pos); //clamp + if (flags && FPU_FLAGS_OVERFLOW) + OR32ItoM((uptr)&fpuRegs.fprc[31], (FPUflagO | FPUflagSO)); + if (flags && FPU_FLAGS_OVERFLOW && acc) + OR32ItoM((uptr)&fpuRegs.ACCflag, 1); + u8 *end3 = JMP8(0); + + x86SetJ8(to_underflow); + u8 *end4; + if (flags && FPU_FLAGS_UNDERFLOW) //set underflow flags if not zero + { + SSE2_XORPD_XMM_to_XMM(absreg, absreg); + SSE2_UCOMISD_XMM_to_XMM(reg, absreg); + u8 *is_zero = JE8(0); + + OR32ItoM((uptr)&fpuRegs.fprc[31], (FPUflagU | FPUflagSU)); + if (addsub) + { + //On ADD/SUB, the PS2 simply leaves the mantissa bits as they are (after normalization) + //IEEE either clears them (FtZ) or returns the denormalized result. + //not thoroughly tested : other operations such as MUL and DIV seem to clear all mantissa bits? + SSE_MOVAPS_XMM_to_XMM(absreg, reg); + SSE2_PSLLQ_I8_to_XMM(reg, 12); //mantissa bits + SSE2_PSRLQ_I8_to_XMM(reg, 41); + SSE2_PSRLQ_I8_to_XMM(absreg, 63); //sign bit + SSE2_PSLLQ_I8_to_XMM(absreg, 31); + SSE2_POR_XMM_to_XMM(reg, absreg); + end4 = JMP8(0); + } + + x86SetJ8(is_zero); + } + SSE2_CVTSD2SS_XMM_to_XMM(reg, reg); + SSE_ANDPS_M128_to_XMM(reg, (uptr)s_const.neg); //flush to zero + + x86SetJ8(end); + x86SetJ8(end2); + x86SetJ8(end3); + if (flags && FPU_FLAGS_UNDERFLOW && addsub) + x86SetJ8(end4); +} + +void ToPS2FPU(int reg, bool flags, int absreg, bool acc, bool addsub = false) +{ + if (FPU_RESULT) + ToPS2FPU_Full(reg, flags, absreg, acc, addsub); + else + { + SSE2_CVTSD2SS_XMM_to_XMM(reg, reg); //clamp + SSE_MINSS_M32_to_XMM(reg, (uptr)&g_maxvals[0]); + SSE_MAXSS_M32_to_XMM(reg, (uptr)&g_minvals[0]); + } +} + +//sets the maximum (positive or negative) value into regd. +void SetMaxValue(int regd) +{ + if (FPU_RESULT) + SSE_ORPS_M128_to_XMM(regd, (uptr)&s_const.pos[0]); // set regd to maximum + else + { + SSE_ANDPS_M128_to_XMM(regd, (uptr)&s_const.neg[0]); // Get the sign bit + SSE_ORPS_M128_to_XMM(regd, (uptr)&g_maxvals[0]); // regd = +/- Maximum (CLAMP)! + } +} + +#define GET_S(sreg) { \ + if( info & PROCESS_EE_S ) SSE_MOVSS_XMM_to_XMM(sreg, EEREC_S); \ + else SSE_MOVSS_M32_to_XMM(sreg, (uptr)&fpuRegs.fpr[_Fs_]); } + +#define ALLOC_S(sreg) { sreg = _allocTempXMMreg(XMMT_FPS, -1); GET_S(sreg); } + +#define GET_T(treg) { \ + if( info & PROCESS_EE_T ) SSE_MOVSS_XMM_to_XMM(treg, EEREC_T); \ + else SSE_MOVSS_M32_to_XMM(treg, (uptr)&fpuRegs.fpr[_Ft_]); } + +#define ALLOC_T(treg) { treg = _allocTempXMMreg(XMMT_FPS, -1); GET_T(treg); } + +#define GET_ACC(areg) { \ + if( info & PROCESS_EE_ACC ) SSE_MOVSS_XMM_to_XMM(areg, EEREC_ACC); \ + else SSE_MOVSS_M32_to_XMM(areg, (uptr)&fpuRegs.ACC); } + +#define ALLOC_ACC(areg) { areg = _allocTempXMMreg(XMMT_FPS, -1); GET_ACC(areg); } + +#define CLEAR_OU_FLAGS { AND32ItoM((uptr)&fpuRegs.fprc[31], ~(FPUflagO | FPUflagU)); } + + +//------------------------------------------------------------------ +// ABS XMM +//------------------------------------------------------------------ +void recABS_S_xmm(int info) +{ + GET_S(EEREC_D); + + CLEAR_OU_FLAGS; + + SSE_ANDPS_M128_to_XMM(EEREC_D, (uptr)s_const.pos); +} + +FPURECOMPILE_CONSTCODE(ABS_S, XMMINFO_WRITED|XMMINFO_READS); +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// FPU_ADD_SUB (Used to mimic PS2's FPU add/sub behavior) +//------------------------------------------------------------------ +// Compliant IEEE FPU uses, in computations, uses additional "guard" bits to the right of the mantissa +// but EE-FPU doesn't. Substraction (and addition of positive and negative) may shift the mantissa left, +// causing those bits to appear in the result; this function masks out the bits of the mantissa that will +// get shifted right to the guard bits to ensure that the guard bits are empty. +// The difference of the exponents = the amount that the smaller operand will be shifted right by. +// Modification - the PS2 uses a single guard bit? (Coded by Nneeve) +//------------------------------------------------------------------ +void FPU_ADD_SUB(int tempd, int tempt) //tempd and tempt are overwritten, they are floats +{ + int tempecx = _allocX86reg(ECX, X86TYPE_TEMP, 0, 0); //receives regd + int temp2 = _allocX86reg(-1, X86TYPE_TEMP, 0, 0); //receives regt + int xmmtemp = _allocTempXMMreg(XMMT_FPS, -1); //temporary for anding with regd/regt + + if (tempecx != ECX) { Console.Error("FPU: ADD/SUB Allocation Error!"); tempecx = ECX;} + if (temp2 == -1) { Console.Error("FPU: ADD/SUB Allocation Error!"); temp2 = EAX;} + if (xmmtemp == -1) { Console.Error("FPU: ADD/SUB Allocation Error!"); xmmtemp = XMM0;} + + SSE2_MOVD_XMM_to_R(tempecx, tempd); + SSE2_MOVD_XMM_to_R(temp2, tempt); + + //mask the exponents + SHR32ItoR(tempecx, 23); + SHR32ItoR(temp2, 23); + AND32ItoR(tempecx, 0xff); + AND32ItoR(temp2, 0xff); + + SUB32RtoR(tempecx, temp2); //tempecx = exponent difference + CMP32ItoR(tempecx, 25); + j8Ptr[0] = JGE8(0); + CMP32ItoR(tempecx, 0); + j8Ptr[1] = JG8(0); + j8Ptr[2] = JE8(0); + CMP32ItoR(tempecx, -25); + j8Ptr[3] = JLE8(0); + + //diff = -24 .. -1 , expd < expt + NEG32R(tempecx); + DEC32R(tempecx); + MOV32ItoR(temp2, 0xffffffff); + SHL32CLtoR(temp2); //temp2 = 0xffffffff << tempecx + SSE2_MOVD_R_to_XMM(xmmtemp, temp2); + SSE_ANDPS_XMM_to_XMM(tempd, xmmtemp); + j8Ptr[4] = JMP8(0); + + x86SetJ8(j8Ptr[0]); + //diff = 25 .. 255 , expt < expd + SSE_ANDPS_M128_to_XMM(tempt, (uptr)s_const.neg); + j8Ptr[5] = JMP8(0); + + x86SetJ8(j8Ptr[1]); + //diff = 1 .. 24, expt < expd + DEC32R(tempecx); + MOV32ItoR(temp2, 0xffffffff); + SHL32CLtoR(temp2); //temp2 = 0xffffffff << tempecx + SSE2_MOVD_R_to_XMM(xmmtemp, temp2); + SSE_ANDPS_XMM_to_XMM(tempt, xmmtemp); + j8Ptr[6] = JMP8(0); + + x86SetJ8(j8Ptr[3]); + //diff = -255 .. -25, expd < expt + SSE_ANDPS_M128_to_XMM(tempd, (uptr)s_const.neg); + j8Ptr[7] = JMP8(0); + + x86SetJ8(j8Ptr[2]); + //diff == 0 + + x86SetJ8(j8Ptr[4]); + x86SetJ8(j8Ptr[5]); + x86SetJ8(j8Ptr[6]); + x86SetJ8(j8Ptr[7]); + + _freeXMMreg(xmmtemp); + _freeX86reg(temp2); + _freeX86reg(tempecx); +} + +void FPU_MUL(int info, int regd, int sreg, int treg, bool acc) +{ + u8 *noHack; + u32 *endMul; + + if (CHECK_FPUMULHACK) + { + SSE2_MOVD_XMM_to_R(ECX, sreg); + SSE2_MOVD_XMM_to_R(EDX, treg); + CALLFunc( (uptr)&FPU_MUL_HACK ); //returns the hacked result or 0 + TEST32RtoR(EAX, EAX); + noHack = JZ8(0); + SSE2_MOVD_R_to_XMM(regd, EAX); + endMul = JMP32(0); + x86SetJ8(noHack); + } + + ToDouble(sreg); ToDouble(treg); + SSE2_MULSD_XMM_to_XMM(sreg, treg); + ToPS2FPU(sreg, true, treg, acc); + SSE_MOVSS_XMM_to_XMM(regd, sreg); + + if (CHECK_FPUMULHACK) + x86SetJ32(endMul); +} + +//------------------------------------------------------------------ +// CommutativeOp XMM (used for ADD and SUB opcodes. that's it.) +//------------------------------------------------------------------ +static void (*recFPUOpXMM_to_XMM[] )(x86SSERegType, x86SSERegType) = { + SSE2_ADDSD_XMM_to_XMM, SSE2_SUBSD_XMM_to_XMM }; + +void recFPUOp(int info, int regd, int op, bool acc) +{ + int sreg, treg; + ALLOC_S(sreg); ALLOC_T(treg); + + if (FPU_CORRECT_ADD_SUB) + FPU_ADD_SUB(sreg, treg); + + ToDouble(sreg); ToDouble(treg); + + recFPUOpXMM_to_XMM[op](sreg, treg); + + ToPS2FPU(sreg, true, treg, acc, true); + SSE_MOVSS_XMM_to_XMM(regd, sreg); + + _freeXMMreg(sreg); _freeXMMreg(treg); +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// ADD XMM +//------------------------------------------------------------------ +void recADD_S_xmm(int info) +{ + recFPUOp(info, EEREC_D, 0, false); +} + +FPURECOMPILE_CONSTCODE(ADD_S, XMMINFO_WRITED|XMMINFO_READS|XMMINFO_READT); + +void recADDA_S_xmm(int info) +{ + recFPUOp(info, EEREC_ACC, 0, true); +} + +FPURECOMPILE_CONSTCODE(ADDA_S, XMMINFO_WRITEACC|XMMINFO_READS|XMMINFO_READT); +//------------------------------------------------------------------ + +void recCMP(int info) +{ + int sreg, treg; + ALLOC_S(sreg); ALLOC_T(treg); + ToDouble(sreg); ToDouble(treg); + + SSE2_UCOMISD_XMM_to_XMM(sreg, treg); + + _freeXMMreg(sreg); _freeXMMreg(treg); +} + +//------------------------------------------------------------------ +// C.x.S XMM +//------------------------------------------------------------------ +void recC_EQ_xmm(int info) +{ + recCMP(info); + + j8Ptr[0] = JZ8(0); + AND32ItoM( (uptr)&fpuRegs.fprc[31], ~FPUflagC ); + j8Ptr[1] = JMP8(0); + x86SetJ8(j8Ptr[0]); + OR32ItoM((uptr)&fpuRegs.fprc[31], FPUflagC); + x86SetJ8(j8Ptr[1]); +} + +FPURECOMPILE_CONSTCODE(C_EQ, XMMINFO_READS|XMMINFO_READT); + +void recC_LE_xmm(int info ) +{ + recCMP(info); + + j8Ptr[0] = JBE8(0); + AND32ItoM( (uptr)&fpuRegs.fprc[31], ~FPUflagC ); + j8Ptr[1] = JMP8(0); + x86SetJ8(j8Ptr[0]); + OR32ItoM((uptr)&fpuRegs.fprc[31], FPUflagC); + x86SetJ8(j8Ptr[1]); +} + +FPURECOMPILE_CONSTCODE(C_LE, XMMINFO_READS|XMMINFO_READT); + +void recC_LT_xmm(int info) +{ + recCMP(info); + + j8Ptr[0] = JB8(0); + AND32ItoM( (uptr)&fpuRegs.fprc[31], ~FPUflagC ); + j8Ptr[1] = JMP8(0); + x86SetJ8(j8Ptr[0]); + OR32ItoM((uptr)&fpuRegs.fprc[31], FPUflagC); + x86SetJ8(j8Ptr[1]); +} + +FPURECOMPILE_CONSTCODE(C_LT, XMMINFO_READS|XMMINFO_READT); +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// CVT.x XMM +//------------------------------------------------------------------ +void recCVT_S_xmm(int info) +{ + if( !(info&PROCESS_EE_S) || (EEREC_D != EEREC_S && !(info&PROCESS_EE_MODEWRITES)) ) { + SSE_CVTSI2SS_M32_to_XMM(EEREC_D, (uptr)&fpuRegs.fpr[_Fs_]); + } + else { + SSE2_CVTDQ2PS_XMM_to_XMM(EEREC_D, EEREC_S); + } +} + +FPURECOMPILE_CONSTCODE(CVT_S, XMMINFO_WRITED|XMMINFO_READS); + +void recCVT_W() //called from iFPU.cpp's recCVT_W +{ + int regs = _checkXMMreg(XMMTYPE_FPREG, _Fs_, MODE_READ); + + if( regs >= 0 ) + { + SSE_CVTTSS2SI_XMM_to_R32(EAX, regs); + SSE_MOVMSKPS_XMM_to_R32(EDX, regs); //extract the signs + AND32ItoR(EDX,1); //keep only LSB + } + else + { + SSE_CVTTSS2SI_M32_to_R32(EAX, (uptr)&fpuRegs.fpr[ _Fs_ ]); + MOV32MtoR(EDX, (uptr)&fpuRegs.fpr[ _Fs_ ]); + SHR32ItoR(EDX, 31); //mov sign to lsb + } + + //kill register allocation for dst because we write directly to fpuRegs.fpr[_Fd_] + _deleteFPtoXMMreg(_Fd_, 2); + + ADD32ItoR(EDX, 0x7FFFFFFF); //0x7FFFFFFF if positive, 0x8000 0000 if negative + + CMP32ItoR(EAX, 0x80000000); //If the result is indefinitive + CMOVE32RtoR(EAX, EDX); //Saturate it + + //Write the result + MOV32RtoM((uptr)&fpuRegs.fpr[_Fd_], EAX); +} +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// DIV XMM +//------------------------------------------------------------------ +void recDIVhelper1(int regd, int regt) // Sets flags +{ + u8 *pjmp1, *pjmp2; + u32 *ajmp32, *bjmp32; + int t1reg = _allocTempXMMreg(XMMT_FPS, -1); + int tempReg = _allocX86reg(-1, X86TYPE_TEMP, 0, 0); + if (t1reg == -1) {Console.Error("FPU: DIV Allocation Error!");} + if (tempReg == -1) {Console.Error("FPU: DIV Allocation Error!"); tempReg = EAX;} + + AND32ItoM((uptr)&fpuRegs.fprc[31], ~(FPUflagI|FPUflagD)); // Clear I and D flags + + //--- Check for divide by zero --- + SSE_XORPS_XMM_to_XMM(t1reg, t1reg); + SSE_CMPEQSS_XMM_to_XMM(t1reg, regt); + SSE_MOVMSKPS_XMM_to_R32(tempReg, t1reg); + AND32ItoR(tempReg, 1); //Check sign (if regt == zero, sign will be set) + ajmp32 = JZ32(0); //Skip if not set + + //--- Check for 0/0 --- + SSE_XORPS_XMM_to_XMM(t1reg, t1reg); + SSE_CMPEQSS_XMM_to_XMM(t1reg, regd); + SSE_MOVMSKPS_XMM_to_R32(tempReg, t1reg); + AND32ItoR(tempReg, 1); //Check sign (if regd == zero, sign will be set) + pjmp1 = JZ8(0); //Skip if not set + OR32ItoM((uptr)&fpuRegs.fprc[31], FPUflagI|FPUflagSI); // Set I and SI flags ( 0/0 ) + pjmp2 = JMP8(0); + x86SetJ8(pjmp1); //x/0 but not 0/0 + OR32ItoM((uptr)&fpuRegs.fprc[31], FPUflagD|FPUflagSD); // Set D and SD flags ( x/0 ) + x86SetJ8(pjmp2); + + //--- Make regd +/- Maximum --- + SSE_XORPS_XMM_to_XMM(regd, regt); // Make regd Positive or Negative + SetMaxValue(regd); //clamp to max + bjmp32 = JMP32(0); + + x86SetJ32(ajmp32); + + //--- Normal Divide --- + ToDouble(regd); ToDouble(regt); + + SSE2_DIVSD_XMM_to_XMM(regd, regt); + + ToPS2FPU(regd, false, regt, false); + + x86SetJ32(bjmp32); + + _freeXMMreg(t1reg); + _freeX86reg(tempReg); +} + +void recDIVhelper2(int regd, int regt) // Doesn't sets flags +{ + ToDouble(regd); ToDouble(regt); + + SSE2_DIVSD_XMM_to_XMM(regd, regt); + + ToPS2FPU(regd, false, regt, false); +} + +static __aligned16 SSE_MXCSR roundmode_nearest, roundmode_neg; + +void recDIV_S_xmm(int info) +{ + bool roundmodeFlag = false; + //if (t0reg == -1) {Console.Error("FPU: DIV Allocation Error!");} + //Console.WriteLn("DIV"); + + if( CHECK_FPUNEGDIVHACK ) + { + if (g_sseMXCSR.GetRoundMode() != SSEround_NegInf) + { + // Set roundmode to nearest since it isn't already + //Console.WriteLn("div to negative inf"); + + roundmode_neg = g_sseMXCSR; + roundmode_neg.SetRoundMode( SSEround_NegInf ); + xLDMXCSR( roundmode_neg ); + roundmodeFlag = true; + } + } + else + { + if (g_sseMXCSR.GetRoundMode() != SSEround_Nearest) + { + // Set roundmode to nearest since it isn't already + //Console.WriteLn("div to nearest"); + + roundmode_nearest = g_sseMXCSR; + roundmode_nearest.SetRoundMode( SSEround_Nearest ); + xLDMXCSR( roundmode_nearest ); + roundmodeFlag = true; + } + } + + int sreg, treg; + + ALLOC_S(sreg); ALLOC_T(treg); + + if (FPU_FLAGS_ID) + recDIVhelper1(sreg, treg); + else + recDIVhelper2(sreg, treg); + + SSE_MOVSS_XMM_to_XMM(EEREC_D, sreg); + + if (roundmodeFlag) xLDMXCSR (g_sseMXCSR); + _freeXMMreg(sreg); _freeXMMreg(treg); +} + +FPURECOMPILE_CONSTCODE(DIV_S, XMMINFO_WRITED|XMMINFO_READS|XMMINFO_READT); +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// MADD/MSUB XMM +//------------------------------------------------------------------ + +// Unlike what the documentation implies, it seems that MADD/MSUB support all numbers just like other operations +// The complex overflow conditions the document describes apparently test whether the multiplication's result +// has overflowed and whether the last operation that used ACC as a destination has overflowed. +// For example, { adda.s -MAX, 0.0 ; madd.s fd, MAX, 1.0 } -> fd = 0 +// while { adda.s -MAX, -MAX ; madd.s fd, MAX, 1.0 } -> fd = -MAX +// (where MAX is 0x7fffffff and -MAX is 0xffffffff) +void recMaddsub(int info, int regd, int op, bool acc) +{ + int sreg, treg; + ALLOC_S(sreg); ALLOC_T(treg); + + FPU_MUL(info, sreg, sreg, treg, false); + + GET_ACC(treg); + + if (FPU_CORRECT_ADD_SUB) + FPU_ADD_SUB(treg, sreg); //might be problematic for something!!!! + + // TEST FOR ACC/MUL OVERFLOWS, PROPOGATE THEM IF THEY OCCUR + + TEST32ItoM((uptr)&fpuRegs.fprc[31], FPUflagO); + u8 *mulovf = JNZ8(0); + ToDouble(sreg); //else, convert + + TEST32ItoM((uptr)&fpuRegs.ACCflag, 1); + u8 *accovf = JNZ8(0); + ToDouble(treg); //else, convert + u8 *operation = JMP8(0); + + x86SetJ8(mulovf); + if (op == 1) //sub + SSE_XORPS_M128_to_XMM(sreg, (uptr)s_const.neg); + SSE_MOVAPS_XMM_to_XMM(treg, sreg); //fall through below + + x86SetJ8(accovf); + SetMaxValue(treg); //just in case... I think it has to be a MaxValue already here + CLEAR_OU_FLAGS; //clear U flag + if (FPU_FLAGS_OVERFLOW) + OR32ItoM((uptr)&fpuRegs.fprc[31], FPUflagO | FPUflagSO); + if (FPU_FLAGS_OVERFLOW && acc) + OR32ItoM((uptr)&fpuRegs.ACCflag, 1); + u32 *skipall = JMP32(0); + + // PERFORM THE ACCUMULATION AND TEST RESULT. CONVERT TO SINGLE + + x86SetJ8(operation); + if (op == 1) + SSE2_SUBSD_XMM_to_XMM(treg, sreg); + else + SSE2_ADDSD_XMM_to_XMM(treg, sreg); + + ToPS2FPU(treg, true, sreg, acc, true); + x86SetJ32(skipall); + + SSE_MOVSS_XMM_to_XMM(regd, treg); + + _freeXMMreg(sreg); _freeXMMreg(treg); +} + +void recMADD_S_xmm(int info) +{ + recMaddsub(info, EEREC_D, 0, false); +} + +FPURECOMPILE_CONSTCODE(MADD_S, XMMINFO_WRITED|XMMINFO_READACC|XMMINFO_READS|XMMINFO_READT); + +void recMADDA_S_xmm(int info) +{ + recMaddsub(info, EEREC_ACC, 0, true); +} + +FPURECOMPILE_CONSTCODE(MADDA_S, XMMINFO_WRITEACC|XMMINFO_READACC|XMMINFO_READS|XMMINFO_READT); +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// MAX / MIN XMM +//------------------------------------------------------------------ + +static const __aligned16 u32 minmax_mask[8] = +{ + 0xffffffff, 0x80000000, 0, 0, + 0, 0x40000000, 0, 0 +}; +// FPU's MAX/MIN work with all numbers (including "denormals"). Check VU's logical min max for more info. +void recMINMAX(int info, bool ismin) +{ + int sreg, treg; + ALLOC_S(sreg); ALLOC_T(treg); + + CLEAR_OU_FLAGS; + + SSE2_PSHUFD_XMM_to_XMM(sreg, sreg, 0x00); + SSE2_PAND_M128_to_XMM(sreg, (uptr)minmax_mask); + SSE2_POR_M128_to_XMM(sreg, (uptr)&minmax_mask[4]); + SSE2_PSHUFD_XMM_to_XMM(treg, treg, 0x00); + SSE2_PAND_M128_to_XMM(treg, (uptr)minmax_mask); + SSE2_POR_M128_to_XMM(treg, (uptr)&minmax_mask[4]); + if (ismin) + SSE2_MINSD_XMM_to_XMM(sreg, treg); + else + SSE2_MAXSD_XMM_to_XMM(sreg, treg); + + SSE_MOVSS_XMM_to_XMM(EEREC_D, sreg); + + _freeXMMreg(sreg); _freeXMMreg(treg); +} + +void recMAX_S_xmm(int info) +{ + recMINMAX(info, false); +} + +FPURECOMPILE_CONSTCODE(MAX_S, XMMINFO_WRITED|XMMINFO_READS|XMMINFO_READT); + +void recMIN_S_xmm(int info) +{ + recMINMAX(info, true); +} + +FPURECOMPILE_CONSTCODE(MIN_S, XMMINFO_WRITED|XMMINFO_READS|XMMINFO_READT); +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// MOV XMM +//------------------------------------------------------------------ +void recMOV_S_xmm(int info) +{ + GET_S(EEREC_D); +} + +FPURECOMPILE_CONSTCODE(MOV_S, XMMINFO_WRITED|XMMINFO_READS); +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// MSUB XMM +//------------------------------------------------------------------ + +void recMSUB_S_xmm(int info) +{ + recMaddsub(info, EEREC_D, 1, false); +} + +FPURECOMPILE_CONSTCODE(MSUB_S, XMMINFO_WRITED|XMMINFO_READACC|XMMINFO_READS|XMMINFO_READT); + +void recMSUBA_S_xmm(int info) +{ + recMaddsub(info, EEREC_ACC, 1, true); +} + +FPURECOMPILE_CONSTCODE(MSUBA_S, XMMINFO_WRITEACC|XMMINFO_READACC|XMMINFO_READS|XMMINFO_READT); +//------------------------------------------------------------------ + +//------------------------------------------------------------------ +// MUL XMM +//------------------------------------------------------------------ +void recMUL_S_xmm(int info) +{ + int sreg, treg; + ALLOC_S(sreg); ALLOC_T(treg); + + FPU_MUL(info, EEREC_D, sreg, treg, false); + _freeXMMreg(sreg); _freeXMMreg(treg); +} + +FPURECOMPILE_CONSTCODE(MUL_S, XMMINFO_WRITED|XMMINFO_READS|XMMINFO_READT); + +void recMULA_S_xmm(int info) +{ + int sreg, treg; + ALLOC_S(sreg); ALLOC_T(treg); + + FPU_MUL(info, EEREC_ACC, sreg, treg, true); + _freeXMMreg(sreg); _freeXMMreg(treg); +} + +FPURECOMPILE_CONSTCODE(MULA_S, XMMINFO_WRITEACC|XMMINFO_READS|XMMINFO_READT); +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// NEG XMM +//------------------------------------------------------------------ +void recNEG_S_xmm(int info) { + + GET_S(EEREC_D); + + CLEAR_OU_FLAGS; + + SSE_XORPS_M128_to_XMM(EEREC_D, (uptr)&s_const.neg[0]); +} + +FPURECOMPILE_CONSTCODE(NEG_S, XMMINFO_WRITED|XMMINFO_READS); +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// SUB XMM +//------------------------------------------------------------------ + +void recSUB_S_xmm(int info) +{ + recFPUOp(info, EEREC_D, 1, false); +} + +FPURECOMPILE_CONSTCODE(SUB_S, XMMINFO_WRITED|XMMINFO_READS|XMMINFO_READT); + + +void recSUBA_S_xmm(int info) +{ + recFPUOp(info, EEREC_ACC, 1, true); +} + +FPURECOMPILE_CONSTCODE(SUBA_S, XMMINFO_WRITEACC|XMMINFO_READS|XMMINFO_READT); +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// SQRT XMM +//------------------------------------------------------------------ +void recSQRT_S_xmm(int info) +{ + u8 *pjmp; + int roundmodeFlag = 0; + int tempReg = _allocX86reg(-1, X86TYPE_TEMP, 0, 0); + if (tempReg == -1) {Console.Error("FPU: SQRT Allocation Error!"); tempReg = EAX;} + int t1reg = _allocTempXMMreg(XMMT_FPS, -1); + if (t1reg == -1) {Console.Error("FPU: SQRT Allocation Error!");} + //Console.WriteLn("FPU: SQRT"); + + if (g_sseMXCSR.GetRoundMode() != SSEround_Nearest) + { + // Set roundmode to nearest if it isn't already + //Console.WriteLn("sqrt to nearest"); + roundmode_nearest = g_sseMXCSR; + roundmode_nearest.SetRoundMode( SSEround_Nearest ); + xLDMXCSR (roundmode_nearest); + roundmodeFlag = 1; + } + + GET_T(EEREC_D); + + if (FPU_FLAGS_ID) { + AND32ItoM((uptr)&fpuRegs.fprc[31], ~(FPUflagI|FPUflagD)); // Clear I and D flags + + //--- Check for negative SQRT --- (sqrt(-0) = 0, unlike what the docs say) + SSE_MOVMSKPS_XMM_to_R32(tempReg, EEREC_D); + AND32ItoR(tempReg, 1); //Check sign + pjmp = JZ8(0); //Skip if none are + OR32ItoM((uptr)&fpuRegs.fprc[31], FPUflagI|FPUflagSI); // Set I and SI flags + SSE_ANDPS_M128_to_XMM(EEREC_D, (uptr)&s_const.pos[0]); // Make EEREC_D Positive + x86SetJ8(pjmp); + } + else + { + SSE_ANDPS_M128_to_XMM(EEREC_D, (uptr)&s_const.pos[0]); // Make EEREC_D Positive + } + + + ToDouble(EEREC_D); + + SSE2_SQRTSD_XMM_to_XMM(EEREC_D, EEREC_D); + + ToPS2FPU(EEREC_D, false, t1reg, false); + + if (roundmodeFlag == 1) { + xLDMXCSR (g_sseMXCSR); + } + + _freeX86reg(tempReg); + _freeXMMreg(t1reg); +} + +FPURECOMPILE_CONSTCODE(SQRT_S, XMMINFO_WRITED|XMMINFO_READT); +//------------------------------------------------------------------ + + +//------------------------------------------------------------------ +// RSQRT XMM +//------------------------------------------------------------------ +void recRSQRThelper1(int regd, int regt) // Preforms the RSQRT function when regd <- Fs and regt <- Ft (Sets correct flags) +{ + u8 *pjmp1, *pjmp2; + u8 *qjmp1, *qjmp2; + u32 *pjmp32; + int t1reg = _allocTempXMMreg(XMMT_FPS, -1); + int tempReg = _allocX86reg(-1, X86TYPE_TEMP, 0, 0); + if (t1reg == -1) {Console.Error("FPU: RSQRT Allocation Error!");} + if (tempReg == -1) {Console.Error("FPU: RSQRT Allocation Error!"); tempReg = EAX;} + + AND32ItoM((uptr)&fpuRegs.fprc[31], ~(FPUflagI|FPUflagD)); // Clear I and D flags + + //--- (first) Check for negative SQRT --- + SSE_MOVMSKPS_XMM_to_R32(tempReg, regt); + AND32ItoR(tempReg, 1); //Check sign + pjmp2 = JZ8(0); //Skip if not set + OR32ItoM((uptr)&fpuRegs.fprc[31], FPUflagI|FPUflagSI); // Set I and SI flags + SSE_ANDPS_M128_to_XMM(regt, (uptr)&s_const.pos[0]); // Make regt Positive + x86SetJ8(pjmp2); + + //--- Check for zero --- + SSE_XORPS_XMM_to_XMM(t1reg, t1reg); + SSE_CMPEQSS_XMM_to_XMM(t1reg, regt); + SSE_MOVMSKPS_XMM_to_R32(tempReg, t1reg); + AND32ItoR(tempReg, 1); //Check sign (if regt == zero, sign will be set) + pjmp1 = JZ8(0); //Skip if not set + + //--- Check for 0/0 --- + SSE_XORPS_XMM_to_XMM(t1reg, t1reg); + SSE_CMPEQSS_XMM_to_XMM(t1reg, regd); + SSE_MOVMSKPS_XMM_to_R32(tempReg, t1reg); + AND32ItoR(tempReg, 1); //Check sign (if regd == zero, sign will be set) + qjmp1 = JZ8(0); //Skip if not set + OR32ItoM((uptr)&fpuRegs.fprc[31], FPUflagI|FPUflagSI); // Set I and SI flags ( 0/0 ) + qjmp2 = JMP8(0); + x86SetJ8(qjmp1); //x/0 but not 0/0 + OR32ItoM((uptr)&fpuRegs.fprc[31], FPUflagD|FPUflagSD); // Set D and SD flags ( x/0 ) + x86SetJ8(qjmp2); + + SetMaxValue(regd); //clamp to max + pjmp32 = JMP32(0); + x86SetJ8(pjmp1); + + ToDouble(regt); ToDouble(regd); + + SSE2_SQRTSD_XMM_to_XMM(regt, regt); + SSE2_DIVSD_XMM_to_XMM(regd, regt); + + ToPS2FPU(regd, false, regt, false); + x86SetJ32(pjmp32); + + _freeXMMreg(t1reg); + _freeX86reg(tempReg); +} + +void recRSQRThelper2(int regd, int regt) // Preforms the RSQRT function when regd <- Fs and regt <- Ft (Doesn't set flags) +{ + SSE_ANDPS_M128_to_XMM(regt, (uptr)&s_const.pos[0]); // Make regt Positive + + ToDouble(regt); ToDouble(regd); + + SSE2_SQRTSD_XMM_to_XMM(regt, regt); + SSE2_DIVSD_XMM_to_XMM(regd, regt); + + ToPS2FPU(regd, false, regt, false); +} + +void recRSQRT_S_xmm(int info) +{ + int sreg, treg; + + // iFPU (regular FPU) doesn't touch roundmode for rSQRT. + // Should this do the same? or is changing the roundmode to nearest the better + // behavior for both recs? --air + + bool roundmodeFlag = false; + if (g_sseMXCSR.GetRoundMode() != SSEround_Nearest) + { + // Set roundmode to nearest if it isn't already + //Console.WriteLn("sqrt to nearest"); + roundmode_nearest = g_sseMXCSR; + roundmode_nearest.SetRoundMode( SSEround_Nearest ); + xLDMXCSR (roundmode_nearest); + roundmodeFlag = true; + } + + ALLOC_S(sreg); ALLOC_T(treg); + + if (FPU_FLAGS_ID) + recRSQRThelper1(sreg, treg); + else + recRSQRThelper2(sreg, treg); + + SSE_MOVSS_XMM_to_XMM(EEREC_D, sreg); + + _freeXMMreg(treg); _freeXMMreg(sreg); + + if (roundmodeFlag) xLDMXCSR (g_sseMXCSR); +} + +FPURECOMPILE_CONSTCODE(RSQRT_S, XMMINFO_WRITED|XMMINFO_READS|XMMINFO_READT); + + +} } } } } #endif \ No newline at end of file diff --git a/pcsx2hostfs/ee/cmdHandler.c b/pcsx2hostfs/ee/cmdHandler.c index bb5823316d..4453241dc7 100644 --- a/pcsx2hostfs/ee/cmdHandler.c +++ b/pcsx2hostfs/ee/cmdHandler.c @@ -1,435 +1,435 @@ -/********************************************************************* - * Copyright (C) 2003 Tord Lindstrom (pukko@home.se) - * Copyright (C) 2004 adresd (adresd_ps2dev@yahoo.com) - * This file is subject to the terms and conditions of the PS2Link License. - * See the file LICENSE in the main directory of this distribution for more - * details. - */ - -#include "stdio.h" -#include -#include -#include -#include -#include -#include -#include - -#include "debug.h" -#include "excepHandler.h" - -#include "byteorder.h" -#include "ps2regs.h" -#include "hostlink.h" - -//#define scr_printf(args...) printf(args) -//#define init_scr() do { } while(0) -#ifdef DEBUG -#define dbgprintf(args...) printf(args) -#else -#define dbgprintf(args...) do { } while(0) -#endif - -#if HOOK_THREADS -extern void KillActiveThreads(void); -#endif - -//////////////////////////////////////////////////////////////////////// -// Prototypes -static int cmdThread(void); -static int pkoStopVU(pko_pkt_stop_vu *); -static int pkoStartVU(pko_pkt_start_vu *); -static int pkoDumpMem(pko_pkt_mem_io *); -static int pkoDumpReg(pko_pkt_dump_regs *); -void pkoReset(void); -static int pkoLoadElf(char *path); -static int pkoGSExec(pko_pkt_gsexec_req *); -static int pkoWriteMem(pko_pkt_mem_io *); - - -// Flags for which type of boot (oh crap, make a header file dammit) -#define B_CD 1 -#define B_MC 2 -#define B_HOST 3 -#define B_CC 4 -#define B_UNKN 5 - -//////////////////////////////////////////////////////////////////////// -// Globals -extern u32 _start; -extern int _gp; -extern int boot; -extern char elfName[]; - -int userThreadID = 0; -static int cmdThreadID = 0; -static char userThreadStack[16*1024] __attribute__((aligned(16))); -static char cmdThreadStack[16*1024] __attribute__((aligned(64))); -static char dataBuffer[16384] __attribute__((aligned(16))); - -// The 'global' argv string area -static char argvStrings[PKO_MAX_PATH]; - -//////////////////////////////////////////////////////////////////////// -// How about that header file again? -#define MAX_ARGS 16 -#define MAX_ARGLEN 256 - -struct argData -{ - int flag; // Contains thread id atm - int argc; - char *argv[MAX_ARGS]; -} __attribute__((packed)) userArgs; - -//////////////////////////////////////////////////////////////////////// -int sifCmdSema; -int sif0HandlerId = 0; -// XXX: Hardcoded address atm.. Should be configurable!! -unsigned int *sifDmaDataPtr =(unsigned int*)(0x20100000-2048); -int excepscrdump = 1; - -extern int pkoExecEE(pko_pkt_execee_req *cmd); - -//////////////////////////////////////////////////////////////////////// -// Create the argument struct to send to the user thread -int -makeArgs(int cmdargc, char *cmdargv, struct argData *arg_data) -{ - int i; - int t; - int argc; - - argc = 0; - - if (cmdargc > MAX_ARGS) - cmdargc = MAX_ARGS; - cmdargv[PKO_MAX_PATH-1] = '\0'; - - memcpy(argvStrings, cmdargv, PKO_MAX_PATH); - - dbgprintf("cmd->argc %d, argv[0]: %s\n", cmdargc, cmdargv); - - i = 0; - t = 0; - do { - arg_data->argv[i] = &argvStrings[t]; - dbgprintf("arg_data[%d]=%s\n", i, arg_data->argv[i]); - dbgprintf("argvStrings[%d]=%s\n", t, &argvStrings[t]); - t += strlen(&argvStrings[t]); - t++; - i++; - } while ((i < cmdargc) && (t < PKO_MAX_PATH)); - - arg_data->argc = i; - - return 0; -} - -//////////////////////////////////////////////////////////////////////// -// Load the actual elf, and create a thread for it -// Return the thread id -static int -pkoLoadElf(char *path) -{ - ee_thread_t th_attr; - t_ExecData elfdata; - int ret; - int pid; - - ret = SifLoadElf(path, &elfdata); - - FlushCache(0); - FlushCache(2); - - dbgprintf("EE: LoadElf returned %d\n", ret); - - dbgprintf("EE: Creating user thread (ent: %x, gp: %x, st: %x)\n", - elfdata.epc, elfdata.gp, elfdata.sp); - - if (elfdata.epc == 0) { - dbgprintf("EE: Could not load file\n"); - return -1; - } - - th_attr.func = (void *)elfdata.epc; - th_attr.stack = userThreadStack; - th_attr.stack_size = sizeof(userThreadStack); - th_attr.gp_reg = (void *)elfdata.gp; - th_attr.initial_priority = 64; - - pid = CreateThread(&th_attr); - if (pid < 0) { - printf("EE: Create user thread failed %d\n", pid); - return -1; - } - - dbgprintf("EE: Created user thread: %d\n", pid); - - return pid; -} - - -//////////////////////////////////////////////////////////////////////// -// Load and start the requested elf -extern int pkoExecEE(pko_pkt_execee_req *cmd) -{ - char path[PKO_MAX_PATH]; - int ret; - int pid; - - if (userThreadID) { - return -1; - } - - dbgprintf("EE: Executing file %s...\n", cmd->argv); - memcpy(path, cmd->argv, PKO_MAX_PATH); - - scr_printf("Executing file %s...\n", path); - - pid = pkoLoadElf(path); - if (pid < 0) { - scr_printf("Could not execute file %s\n", path); - return -1; - } - - FlushCache(0); - FlushCache(2); - - userThreadID = pid; - - makeArgs(ntohl(cmd->argc), path, &userArgs); - - // Hack away.. - userArgs.flag = (int)&userThreadID; - - ret = StartThread(userThreadID, &userArgs); - if (ret < 0) { - printf("EE: Start user thread failed %d\n", ret); - cmdThreadID = 0; - DeleteThread(userThreadID); - return -1; - } - return ret; -} - -//////////////////////////////////////////////////////////////////////// -// takes cmd->data and sends it to GIF via path1 -static int -pkoGSExec(pko_pkt_gsexec_req *cmd) { - int fd; - int len; - fd = fioOpen(cmd->file, O_RDONLY); - if ( fd < 0 ) { - return fd; - } - len = fioRead(fd, dataBuffer, 128); - fioClose(fd); - // stop/reset dma02 - - // dmasend via GIF channel - asm __volatile__( - "move $10, %0;" - "move $11, %1;" - "lui $8, 0x1001;" - "sw $11, -24544($8);" - "sw $10, -24560($8);" - "ori $9, $0, 0x101;" - "sw $9, -24576($8);" - :: "r" (dataBuffer), "r" ((ntohs(cmd->size))/16) ); - - // dmawait for GIF channel - asm __volatile__( - "lui $8, 0x1001;" - "dmawait:" - "lw $9, -24576($8);" - "nop;" - "andi $9, $9, 0x100;" - "nop;" - "bnez $9, dmawait;" - "nop;" - ); - return 0; -} - -//////////////////////////////////////////////////////////////////////// -// command to dump cmd->size bytes of memory from cmd->offset -static int -pkoDumpMem(pko_pkt_mem_io *cmd) { - int fd; - int total, len; - unsigned int offset; - unsigned int size; - char path[PKO_MAX_PATH]; - int ret = 0; - size = ntohl(cmd->size); - offset = ntohl(cmd->offset); - scr_printf("dump mem from 0x%x, size %d\n", - ntohl(cmd->offset), ntohl(cmd->size) - ); - memcpy(path, cmd->argv, PKO_MAX_PATH); - fd = fioOpen(path, O_WRONLY|O_CREAT); - total = 0; - len = 16*1024; - if ( fd > 0 ) { - while(total < size) { - if ( size < len) { - len = size; - } else if ((size - total) < len) { - len = size - total; - } - - memcpy(dataBuffer, (void *)offset, len); - - if ((ret = fioWrite(fd, (void *)dataBuffer, len)) > 0) { - } else { - printf("EE: pkoDumpMem() fioWrite failed\n"); - return fd; - } - offset = offset + len; - total += len; - } - } - fioClose(fd); - return ret; -} - -static int -pkoWriteMem(pko_pkt_mem_io *cmd) { - int fd, len, total; - unsigned int offset; - unsigned int size; - char path[PKO_MAX_PATH]; - int ret = 0; - size = ntohl(cmd->size); - offset = ntohl(cmd->offset); - scr_printf("write mem to 0x%x, size %d\n", - ntohl(cmd->offset), ntohl(cmd->size) - ); - memcpy(path, cmd->argv, PKO_MAX_PATH); - fd = fioOpen(path, O_RDONLY); - total = 0; - len = 16*1024; - if( fd > 0) { - while(total < size) { - if ( size < len) { - len = size; - } else if ((size - total) < len) { - len = size - total; - } - - if ((ret = fioRead(fd, (void *)dataBuffer, len)) > 0) { - } else { - printf("EE: pkoDumpMem() fioWrite failed\n"); - return fd; - } - - memcpy((void *)offset, dataBuffer, len); - offset = offset + len; - total += len; - } - } - fioClose(fd); - return ret; -} - -//////////////////////////////////////////////////////////////////////// -// command to stop VU0/1 and VIF0/1 -static int -pkoStopVU(pko_pkt_stop_vu *cmd) { - if ( ntohs(cmd->vpu) == 0 ) { - asm( - "ori $25, $0, 0x1;" - "ctc2 $25, $28;" - "sync.p;" - "vnop;" - ); - VIF0_FBRST = 0x2; - } else if ( ntohs(cmd->vpu) == 1 ) { - asm( - "ori $25, $0, 0x100;" - "ctc2 $25, $28;" - "sync.p;" - "vnop;" - ); - VIF1_FBRST = 0x2; - } - return 0; -} - -//////////////////////////////////////////////////////////////////////// -// command to reset VU0/1 VIF0/1 -static int -pkoStartVU(pko_pkt_start_vu *cmd) { - if ( ntohs(cmd->vpu) == 0 ) { - asm( - "ori $25, $0, 0x2;" - "ctc2 $25, $28;" - "sync.p;" - "vnop;" - ); - VIF0_FBRST = 0x8; - } else if ( ntohs(cmd->vpu) == 1 ) { - asm( - "ori $25, $0, 0x200;" - "ctc2 $25, $28;" - "sync.p;" - "vnop;" - ); - VIF1_FBRST = 0x8; - } - return 0; -} - -//////////////////////////////////////////////////////////////////////// -void -pkoReset(void) -{ - char *argv[1]; - // Check if user thread is running, if so kill it - -#if 1 - -#if HOOK_THREADS - KillActiveThreads(); -#else - if (userThreadID) { - TerminateThread(userThreadID); - DeleteThread(userThreadID); - } -#endif -#endif - userThreadID = 0; - - RemoveDmacHandler(5, sif0HandlerId); - sif0HandlerId = 0; - - SifInitRpc(0); - //cdvdInit(CDVD_INIT_NOWAIT); - //cdvdInit(CDVD_EXIT); - SifIopReset(NULL, 0); - SifExitRpc(); - while(SifIopSync()); -#if 1 - SifInitRpc(0); - //cdvdExit(); - SifExitRpc(); -#endif - FlushCache(0); - -#ifdef USE_CACHED_CFG - argv[0] = elfName; - SifLoadFileExit(); - ExecPS2(&_start, 0, 1, argv); - return; -#endif - - if ((boot == B_MC) || (boot == B_HOST) || (boot == B_UNKN) || (boot == B_CC)) { - argv[0] = elfName; - SifLoadFileExit(); - ExecPS2(&_start, 0, 1, argv); - } - else { - LoadExecPS2(elfName, 0, NULL); - } -} +/********************************************************************* + * Copyright (C) 2003 Tord Lindstrom (pukko@home.se) + * Copyright (C) 2004 adresd (adresd_ps2dev@yahoo.com) + * This file is subject to the terms and conditions of the PS2Link License. + * See the file LICENSE in the main directory of this distribution for more + * details. + */ + +#include "stdio.h" +#include +#include +#include +#include +#include +#include +#include + +#include "debug.h" +#include "excepHandler.h" + +#include "byteorder.h" +#include "ps2regs.h" +#include "hostlink.h" + +//#define scr_printf(args...) printf(args) +//#define init_scr() do { } while(0) +#ifdef DEBUG +#define dbgprintf(args...) printf(args) +#else +#define dbgprintf(args...) do { } while(0) +#endif + +#if HOOK_THREADS +extern void KillActiveThreads(void); +#endif + +//////////////////////////////////////////////////////////////////////// +// Prototypes +static int cmdThread(void); +static int pkoStopVU(pko_pkt_stop_vu *); +static int pkoStartVU(pko_pkt_start_vu *); +static int pkoDumpMem(pko_pkt_mem_io *); +static int pkoDumpReg(pko_pkt_dump_regs *); +void pkoReset(void); +static int pkoLoadElf(char *path); +static int pkoGSExec(pko_pkt_gsexec_req *); +static int pkoWriteMem(pko_pkt_mem_io *); + + +// Flags for which type of boot (oh crap, make a header file dammit) +#define B_CD 1 +#define B_MC 2 +#define B_HOST 3 +#define B_CC 4 +#define B_UNKN 5 + +//////////////////////////////////////////////////////////////////////// +// Globals +extern u32 _start; +extern int _gp; +extern int boot; +extern char elfName[]; + +int userThreadID = 0; +static int cmdThreadID = 0; +static char userThreadStack[16*1024] __attribute__((aligned(16))); +static char cmdThreadStack[16*1024] __attribute__((aligned(64))); +static char dataBuffer[16384] __attribute__((aligned(16))); + +// The 'global' argv string area +static char argvStrings[PKO_MAX_PATH]; + +//////////////////////////////////////////////////////////////////////// +// How about that header file again? +#define MAX_ARGS 16 +#define MAX_ARGLEN 256 + +struct argData +{ + int flag; // Contains thread id atm + int argc; + char *argv[MAX_ARGS]; +} __attribute__((packed)) userArgs; + +//////////////////////////////////////////////////////////////////////// +int sifCmdSema; +int sif0HandlerId = 0; +// XXX: Hardcoded address atm.. Should be configurable!! +unsigned int *sifDmaDataPtr =(unsigned int*)(0x20100000-2048); +int excepscrdump = 1; + +extern int pkoExecEE(pko_pkt_execee_req *cmd); + +//////////////////////////////////////////////////////////////////////// +// Create the argument struct to send to the user thread +int +makeArgs(int cmdargc, char *cmdargv, struct argData *arg_data) +{ + int i; + int t; + int argc; + + argc = 0; + + if (cmdargc > MAX_ARGS) + cmdargc = MAX_ARGS; + cmdargv[PKO_MAX_PATH-1] = '\0'; + + memcpy(argvStrings, cmdargv, PKO_MAX_PATH); + + dbgprintf("cmd->argc %d, argv[0]: %s\n", cmdargc, cmdargv); + + i = 0; + t = 0; + do { + arg_data->argv[i] = &argvStrings[t]; + dbgprintf("arg_data[%d]=%s\n", i, arg_data->argv[i]); + dbgprintf("argvStrings[%d]=%s\n", t, &argvStrings[t]); + t += strlen(&argvStrings[t]); + t++; + i++; + } while ((i < cmdargc) && (t < PKO_MAX_PATH)); + + arg_data->argc = i; + + return 0; +} + +//////////////////////////////////////////////////////////////////////// +// Load the actual elf, and create a thread for it +// Return the thread id +static int +pkoLoadElf(char *path) +{ + ee_thread_t th_attr; + t_ExecData elfdata; + int ret; + int pid; + + ret = SifLoadElf(path, &elfdata); + + FlushCache(0); + FlushCache(2); + + dbgprintf("EE: LoadElf returned %d\n", ret); + + dbgprintf("EE: Creating user thread (ent: %x, gp: %x, st: %x)\n", + elfdata.epc, elfdata.gp, elfdata.sp); + + if (elfdata.epc == 0) { + dbgprintf("EE: Could not load file\n"); + return -1; + } + + th_attr.func = (void *)elfdata.epc; + th_attr.stack = userThreadStack; + th_attr.stack_size = sizeof(userThreadStack); + th_attr.gp_reg = (void *)elfdata.gp; + th_attr.initial_priority = 64; + + pid = CreateThread(&th_attr); + if (pid < 0) { + printf("EE: Create user thread failed %d\n", pid); + return -1; + } + + dbgprintf("EE: Created user thread: %d\n", pid); + + return pid; +} + + +//////////////////////////////////////////////////////////////////////// +// Load and start the requested elf +extern int pkoExecEE(pko_pkt_execee_req *cmd) +{ + char path[PKO_MAX_PATH]; + int ret; + int pid; + + if (userThreadID) { + return -1; + } + + dbgprintf("EE: Executing file %s...\n", cmd->argv); + memcpy(path, cmd->argv, PKO_MAX_PATH); + + scr_printf("Executing file %s...\n", path); + + pid = pkoLoadElf(path); + if (pid < 0) { + scr_printf("Could not execute file %s\n", path); + return -1; + } + + FlushCache(0); + FlushCache(2); + + userThreadID = pid; + + makeArgs(ntohl(cmd->argc), path, &userArgs); + + // Hack away.. + userArgs.flag = (int)&userThreadID; + + ret = StartThread(userThreadID, &userArgs); + if (ret < 0) { + printf("EE: Start user thread failed %d\n", ret); + cmdThreadID = 0; + DeleteThread(userThreadID); + return -1; + } + return ret; +} + +//////////////////////////////////////////////////////////////////////// +// takes cmd->data and sends it to GIF via path1 +static int +pkoGSExec(pko_pkt_gsexec_req *cmd) { + int fd; + int len; + fd = fioOpen(cmd->file, O_RDONLY); + if ( fd < 0 ) { + return fd; + } + len = fioRead(fd, dataBuffer, 128); + fioClose(fd); + // stop/reset dma02 + + // dmasend via GIF channel + asm __volatile__( + "move $10, %0;" + "move $11, %1;" + "lui $8, 0x1001;" + "sw $11, -24544($8);" + "sw $10, -24560($8);" + "ori $9, $0, 0x101;" + "sw $9, -24576($8);" + :: "r" (dataBuffer), "r" ((ntohs(cmd->size))/16) ); + + // dmawait for GIF channel + asm __volatile__( + "lui $8, 0x1001;" + "dmawait:" + "lw $9, -24576($8);" + "nop;" + "andi $9, $9, 0x100;" + "nop;" + "bnez $9, dmawait;" + "nop;" + ); + return 0; +} + +//////////////////////////////////////////////////////////////////////// +// command to dump cmd->size bytes of memory from cmd->offset +static int +pkoDumpMem(pko_pkt_mem_io *cmd) { + int fd; + int total, len; + unsigned int offset; + unsigned int size; + char path[PKO_MAX_PATH]; + int ret = 0; + size = ntohl(cmd->size); + offset = ntohl(cmd->offset); + scr_printf("dump mem from 0x%x, size %d\n", + ntohl(cmd->offset), ntohl(cmd->size) + ); + memcpy(path, cmd->argv, PKO_MAX_PATH); + fd = fioOpen(path, O_WRONLY|O_CREAT); + total = 0; + len = 16*1024; + if ( fd > 0 ) { + while(total < size) { + if ( size < len) { + len = size; + } else if ((size - total) < len) { + len = size - total; + } + + memcpy(dataBuffer, (void *)offset, len); + + if ((ret = fioWrite(fd, (void *)dataBuffer, len)) > 0) { + } else { + printf("EE: pkoDumpMem() fioWrite failed\n"); + return fd; + } + offset = offset + len; + total += len; + } + } + fioClose(fd); + return ret; +} + +static int +pkoWriteMem(pko_pkt_mem_io *cmd) { + int fd, len, total; + unsigned int offset; + unsigned int size; + char path[PKO_MAX_PATH]; + int ret = 0; + size = ntohl(cmd->size); + offset = ntohl(cmd->offset); + scr_printf("write mem to 0x%x, size %d\n", + ntohl(cmd->offset), ntohl(cmd->size) + ); + memcpy(path, cmd->argv, PKO_MAX_PATH); + fd = fioOpen(path, O_RDONLY); + total = 0; + len = 16*1024; + if( fd > 0) { + while(total < size) { + if ( size < len) { + len = size; + } else if ((size - total) < len) { + len = size - total; + } + + if ((ret = fioRead(fd, (void *)dataBuffer, len)) > 0) { + } else { + printf("EE: pkoDumpMem() fioWrite failed\n"); + return fd; + } + + memcpy((void *)offset, dataBuffer, len); + offset = offset + len; + total += len; + } + } + fioClose(fd); + return ret; +} + +//////////////////////////////////////////////////////////////////////// +// command to stop VU0/1 and VIF0/1 +static int +pkoStopVU(pko_pkt_stop_vu *cmd) { + if ( ntohs(cmd->vpu) == 0 ) { + asm( + "ori $25, $0, 0x1;" + "ctc2 $25, $28;" + "sync.p;" + "vnop;" + ); + VIF0_FBRST = 0x2; + } else if ( ntohs(cmd->vpu) == 1 ) { + asm( + "ori $25, $0, 0x100;" + "ctc2 $25, $28;" + "sync.p;" + "vnop;" + ); + VIF1_FBRST = 0x2; + } + return 0; +} + +//////////////////////////////////////////////////////////////////////// +// command to reset VU0/1 VIF0/1 +static int +pkoStartVU(pko_pkt_start_vu *cmd) { + if ( ntohs(cmd->vpu) == 0 ) { + asm( + "ori $25, $0, 0x2;" + "ctc2 $25, $28;" + "sync.p;" + "vnop;" + ); + VIF0_FBRST = 0x8; + } else if ( ntohs(cmd->vpu) == 1 ) { + asm( + "ori $25, $0, 0x200;" + "ctc2 $25, $28;" + "sync.p;" + "vnop;" + ); + VIF1_FBRST = 0x8; + } + return 0; +} + +//////////////////////////////////////////////////////////////////////// +void +pkoReset(void) +{ + char *argv[1]; + // Check if user thread is running, if so kill it + +#if 1 + +#if HOOK_THREADS + KillActiveThreads(); +#else + if (userThreadID) { + TerminateThread(userThreadID); + DeleteThread(userThreadID); + } +#endif +#endif + userThreadID = 0; + + RemoveDmacHandler(5, sif0HandlerId); + sif0HandlerId = 0; + + SifInitRpc(0); + //cdvdInit(CDVD_INIT_NOWAIT); + //cdvdInit(CDVD_EXIT); + SifIopReset(NULL, 0); + SifExitRpc(); + while(SifIopSync()); +#if 1 + SifInitRpc(0); + //cdvdExit(); + SifExitRpc(); +#endif + FlushCache(0); + +#ifdef USE_CACHED_CFG + argv[0] = elfName; + SifLoadFileExit(); + ExecPS2(&_start, 0, 1, argv); + return; +#endif + + if ((boot == B_MC) || (boot == B_HOST) || (boot == B_UNKN) || (boot == B_CC)) { + argv[0] = elfName; + SifLoadFileExit(); + ExecPS2(&_start, 0, 1, argv); + } + else { + LoadExecPS2(elfName, 0, NULL); + } +} diff --git a/pcsx2hostfs/ee/excepHandler.c b/pcsx2hostfs/ee/excepHandler.c index 307ae3ce8c..657bca0857 100644 --- a/pcsx2hostfs/ee/excepHandler.c +++ b/pcsx2hostfs/ee/excepHandler.c @@ -1,170 +1,170 @@ -/********************************************************************* - * Copyright (C) 2003 Tord Lindstrom (pukko@home.se) - * Copyright (C) 2004 adresd (adresd_ps2dev@yahoo.com) - * This file is subject to the terms and conditions of the PS2Link License. - * See the file LICENSE in the main directory of this distribution for more - * details. - */ - -#include "stdio.h" -#include -#include -#include "excepHandler.h" -#include "debug.h" - -extern int _gp; -int userThreadID; -int excepscrdump; - -//////////////////////////////////////////////////////////////////////// -typedef union -{ - unsigned int uint128 __attribute__(( mode(TI) )); - unsigned long uint64[2]; -} eeReg __attribute((packed)); - -//////////////////////////////////////////////////////////////////////// -// Prototypes -void pkoDebug(int cause, int badvaddr, int status, int epc, eeReg *regs); - -extern void pkoExceptionHandler(void); - -//////////////////////////////////////////////////////////////////////// -static const unsigned char regName[32][5] = -{ - "zero", "at", "v0", "v1", "a0", "a1", "a2", "a3", - "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", - "t8", "t9", "s0", "s1", "s2", "s3", "s4", "s5", - "s6", "s7", "k0", "k1", "gp", "sp", "fp", "ra" -}; - -static char codeTxt[14][24] = -{ - "Interrupt", "TLB modification", "TLB load/inst fetch", "TLB store", - "Address load/inst fetch", "Address store", "Bus error (instr)", - "Bus error (data)", "Syscall", "Breakpoint", "Reserved instruction", - "Coprocessor unusable", "Arithmetic overflow", "Trap" -}; - -char _exceptionStack[8*1024] __attribute__((aligned(16))); -eeReg _savedRegs[32+4] __attribute__((aligned(16))); - -//////////////////////////////////////////////////////////////////////// -// The 'ee exception handler', only dumps registers to console or screen atm -void -pkoDebug(int cause, int badvaddr, int status, int epc, eeReg *regs) -{ - int i; - int code; - // extern void printf(char *, ...); - static void (* excpPrintf)(const char *, ...); - - FlushCache(0); - FlushCache(2); - -#if 1 - if (userThreadID) { - TerminateThread(userThreadID); - DeleteThread(userThreadID); - } -#endif - - code = cause & 0x7c; - - if (excepscrdump) - { - init_scr(); - excpPrintf = scr_printf; - } - else excpPrintf = (void*)printf; - - excpPrintf("\n\n EE Exception handler: %s exception\n\n", - codeTxt[code>>2]); - - excpPrintf(" Cause %08X BadVAddr %08X Status %08X EPC %08X\n\n", - cause, badvaddr, status, epc); - - for(i = 0; i < 32/2; i++) { - excpPrintf("%4s: %016lX%016lX %4s: %016lX%016lX\n", - regName[i], regs[i].uint64[1], regs[i].uint64[0], - regName[i+16], regs[i+16].uint64[1], regs[i+16].uint64[0]); - } - excpPrintf("\n"); - SleepThread(); -} - -//////////////////////////////////////////////////////////////////////// -// The 'iop exception handler', only dumps registers to console or screen atm - -void iopException(int cause, int badvaddr, int status, int epc, u32 *regs, int repc, char* name) -{ - int i; - int code; - // extern void printf(char *, ...); - static void (* excpPrintf)(const char *, ...); - - FlushCache(0); - FlushCache(2); - -#if 1 - if (userThreadID) { - TerminateThread(userThreadID); - DeleteThread(userThreadID); - } -#endif - - code = cause & 0x7c; - - if(excepscrdump) - { - init_scr(); - excpPrintf = scr_printf; - } - else excpPrintf = (void*)printf; - - excpPrintf("\n\n IOP Exception handler: %s exception\n\n", - codeTxt[code>>2]); - - excpPrintf(" Module Name \"%s\" Relative EPC %08X\n\n", - name, repc); - - - excpPrintf(" Cause %08X BadVAddr %08X Status %08X EPC %08X\n\n", - cause, badvaddr, status, epc); - - for(i = 0; i < 32/4; i++) - { - excpPrintf(" %4s: %08X %4s: %08X %4s: %08X %4s: %08X\n", - regName[i], regs[i], regName[i+8], regs[i+8], - regName[i+16], regs[i+16], regName[i+24], regs[i+24]); - } - - excpPrintf("\n"); - - - - SleepThread(); -} - - -//////////////////////////////////////////////////////////////////////// -// Installs ee exception handlers for the 'usual' exceptions and iop -// exception callback -void -installExceptionHandlers(void) -{ - int i; - - // Skip exception #8 (syscall) & 9 (breakpoint) - for (i = 1; i < 4; i++) { - SetVTLBRefillHandler(i, pkoExceptionHandler); - } - for (i = 4; i < 8; i++) { - SetVCommonHandler(i, pkoExceptionHandler); - } - for (i = 10; i < 14; i++) { - SetVCommonHandler(i, pkoExceptionHandler); - } - -} - +/********************************************************************* + * Copyright (C) 2003 Tord Lindstrom (pukko@home.se) + * Copyright (C) 2004 adresd (adresd_ps2dev@yahoo.com) + * This file is subject to the terms and conditions of the PS2Link License. + * See the file LICENSE in the main directory of this distribution for more + * details. + */ + +#include "stdio.h" +#include +#include +#include "excepHandler.h" +#include "debug.h" + +extern int _gp; +int userThreadID; +int excepscrdump; + +//////////////////////////////////////////////////////////////////////// +typedef union +{ + unsigned int uint128 __attribute__(( mode(TI) )); + unsigned long uint64[2]; +} eeReg __attribute((packed)); + +//////////////////////////////////////////////////////////////////////// +// Prototypes +void pkoDebug(int cause, int badvaddr, int status, int epc, eeReg *regs); + +extern void pkoExceptionHandler(void); + +//////////////////////////////////////////////////////////////////////// +static const unsigned char regName[32][5] = +{ + "zero", "at", "v0", "v1", "a0", "a1", "a2", "a3", + "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", + "t8", "t9", "s0", "s1", "s2", "s3", "s4", "s5", + "s6", "s7", "k0", "k1", "gp", "sp", "fp", "ra" +}; + +static char codeTxt[14][24] = +{ + "Interrupt", "TLB modification", "TLB load/inst fetch", "TLB store", + "Address load/inst fetch", "Address store", "Bus error (instr)", + "Bus error (data)", "Syscall", "Breakpoint", "Reserved instruction", + "Coprocessor unusable", "Arithmetic overflow", "Trap" +}; + +char _exceptionStack[8*1024] __attribute__((aligned(16))); +eeReg _savedRegs[32+4] __attribute__((aligned(16))); + +//////////////////////////////////////////////////////////////////////// +// The 'ee exception handler', only dumps registers to console or screen atm +void +pkoDebug(int cause, int badvaddr, int status, int epc, eeReg *regs) +{ + int i; + int code; + // extern void printf(char *, ...); + static void (* excpPrintf)(const char *, ...); + + FlushCache(0); + FlushCache(2); + +#if 1 + if (userThreadID) { + TerminateThread(userThreadID); + DeleteThread(userThreadID); + } +#endif + + code = cause & 0x7c; + + if (excepscrdump) + { + init_scr(); + excpPrintf = scr_printf; + } + else excpPrintf = (void*)printf; + + excpPrintf("\n\n EE Exception handler: %s exception\n\n", + codeTxt[code>>2]); + + excpPrintf(" Cause %08X BadVAddr %08X Status %08X EPC %08X\n\n", + cause, badvaddr, status, epc); + + for(i = 0; i < 32/2; i++) { + excpPrintf("%4s: %016lX%016lX %4s: %016lX%016lX\n", + regName[i], regs[i].uint64[1], regs[i].uint64[0], + regName[i+16], regs[i+16].uint64[1], regs[i+16].uint64[0]); + } + excpPrintf("\n"); + SleepThread(); +} + +//////////////////////////////////////////////////////////////////////// +// The 'iop exception handler', only dumps registers to console or screen atm + +void iopException(int cause, int badvaddr, int status, int epc, u32 *regs, int repc, char* name) +{ + int i; + int code; + // extern void printf(char *, ...); + static void (* excpPrintf)(const char *, ...); + + FlushCache(0); + FlushCache(2); + +#if 1 + if (userThreadID) { + TerminateThread(userThreadID); + DeleteThread(userThreadID); + } +#endif + + code = cause & 0x7c; + + if(excepscrdump) + { + init_scr(); + excpPrintf = scr_printf; + } + else excpPrintf = (void*)printf; + + excpPrintf("\n\n IOP Exception handler: %s exception\n\n", + codeTxt[code>>2]); + + excpPrintf(" Module Name \"%s\" Relative EPC %08X\n\n", + name, repc); + + + excpPrintf(" Cause %08X BadVAddr %08X Status %08X EPC %08X\n\n", + cause, badvaddr, status, epc); + + for(i = 0; i < 32/4; i++) + { + excpPrintf(" %4s: %08X %4s: %08X %4s: %08X %4s: %08X\n", + regName[i], regs[i], regName[i+8], regs[i+8], + regName[i+16], regs[i+16], regName[i+24], regs[i+24]); + } + + excpPrintf("\n"); + + + + SleepThread(); +} + + +//////////////////////////////////////////////////////////////////////// +// Installs ee exception handlers for the 'usual' exceptions and iop +// exception callback +void +installExceptionHandlers(void) +{ + int i; + + // Skip exception #8 (syscall) & 9 (breakpoint) + for (i = 1; i < 4; i++) { + SetVTLBRefillHandler(i, pkoExceptionHandler); + } + for (i = 4; i < 8; i++) { + SetVCommonHandler(i, pkoExceptionHandler); + } + for (i = 10; i < 14; i++) { + SetVCommonHandler(i, pkoExceptionHandler); + } + +} + diff --git a/pcsx2hostfs/ee/excepHandler.h b/pcsx2hostfs/ee/excepHandler.h index 23bcb6b2f4..f4a61b8a61 100644 --- a/pcsx2hostfs/ee/excepHandler.h +++ b/pcsx2hostfs/ee/excepHandler.h @@ -1,14 +1,14 @@ -/********************************************************************* - * Copyright (C) 2003 Tord Lindstrom (pukko@home.se) - * This file is subject to the terms and conditions of the PS2Link License. - * See the file LICENSE in the main directory of this distribution for more - * details. - */ - -#ifndef _EXCEPTION_H_ -#define _EXCEPTION_H_ - -void installExceptionHandlers(void); -void iopException(int cause, int badvaddr, int status, int epc, u32 *regs, int repc, char* name); - -#endif +/********************************************************************* + * Copyright (C) 2003 Tord Lindstrom (pukko@home.se) + * This file is subject to the terms and conditions of the PS2Link License. + * See the file LICENSE in the main directory of this distribution for more + * details. + */ + +#ifndef _EXCEPTION_H_ +#define _EXCEPTION_H_ + +void installExceptionHandlers(void); +void iopException(int cause, int badvaddr, int status, int epc, u32 *regs, int repc, char* name); + +#endif diff --git a/pcsx2hostfs/ee/ps2link.c b/pcsx2hostfs/ee/ps2link.c index 582147eb0e..c6c0548ba0 100644 --- a/pcsx2hostfs/ee/ps2link.c +++ b/pcsx2hostfs/ee/ps2link.c @@ -1,838 +1,838 @@ -/********************************************************************* - * Copyright (C) 2003 Tord Lindstrom (pukko@home.se) - * Copyright (C) 2003,2004 adresd (adresd_ps2dev@yahoo.com) - * This file is subject to the terms and conditions of the PS2Link License. - * See the file LICENSE in the main directory of this distribution for more - * details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "debug.h" -#include "hostlink.h" -#include "excepHandler.h" -#include "stdio.h" - -#include - -#define WELCOME_STRING "Welcome to pcsx2hostfs v1.52\n" - -#ifdef DEBUG -#define dbgprintf(args...) printf(args) -#define dbgscr_printf(args...) scr_printf(args) -#else -#define dbgprintf(args...) do { } while(0) -#define dbgscr_printf(args...) do { } while(0) -#endif - -//#define BGCOLOR ((volatile unsigned long*)0x120000e0) -//#define GS_SET_BGCOLOR(R,G,B) *BGCOLOR = ((u64)(R)<< 0) | ((u64)(G)<< 8) | ((u64)(B)<< 16) - -#define IRX_BUFFER_BASE 0x1F80000 -int irx_buffer_addr = 0; - -//////////////////////////////////////////////////////////////////////// -// Globals -extern int userThreadID; - -// Argv name+path & just path -char elfName[256] __attribute__((aligned(16))); -char elfPath[256]; - -#ifndef BUILTIN_IRXS -char ioptrap_path[PKO_MAX_PATH]; -char pcsx2hostfs_path[PKO_MAX_PATH]; - -void *ioptrap_mod = NULL, *pcsx2hostfs_mod = NULL; -int ioptrap_size = 0, pcsx2hostfs_size = 0; -#else -extern unsigned char _binary_ioptrap_irx_start[], _binary_pcsx2hostfs_irx_start[]; -extern unsigned char _binary_ioptrap_irx_end[], _binary_pcsx2hostfs_irx_end[]; -static unsigned int _binary_ioptrap_irx_size, _binary_pcsx2hostfs_irx_size; -#endif - -char *imgcmd = "rom0:UDNL rom0:EELOADCNF"; - -// Flags for which type of boot -#define B_CD 1 -#define B_MC 2 -#define B_HOST 3 -#define B_CC 4 -#define B_UNKN 5 -int boot; - -//////////////////////////////////////////////////////////////////////// -// Prototypes -static void loadModules(void); -static void pkoLoadModule(char *path, int argc, char *argv); -static void getIpConfig(void); - -extern int pkoExecEE(pko_pkt_execee_req *cmd); - -//////////////////////////////////////////////////////////////////////// -#define IPCONF_MAX_LEN 1024 - -char if_conf[IPCONF_MAX_LEN] = ""; -char fExtraConfig[256]; -int load_extra_conf = 0; -int if_conf_len = 0; - -char ip[16] __attribute__((aligned(16))) = "192.168.0.10"; -char netmask[16] __attribute__((aligned(16))) = "255.255.255.0"; -char gw[16] __attribute__((aligned(16))) = "192.168.0.1"; - -//////////////////////////////////////////////////////////////////////// -// Parse network configuration from IPCONFIG.DAT -// Note: parsing really should be made more robust... - -static int getBufferValue(char* result, char* buffer, u32 buffer_size, char* field) -{ - u32 len = strlen(field); - u32 i = 0; - s32 j = 0; - char* start = 0; - char* end = 0; - - while(strncmp(&buffer[i], field, len) != 0) - { - i++; - if(i == buffer_size) return -1; - } - - // Look for # comment - j = i; - - while((j > 0) && (buffer[j] != '\n') && (buffer[j] !='\r')) - { - j--; - - if(buffer[j] == '#') - return -2; - } - - - while(buffer[i] != '=') - { - i++; - if(i == buffer_size) return -3; - } - i++; - - while(buffer[i] == ' ') - { - i++; - if(i == buffer_size) return -4; - } - i++; - - if((buffer[i] != '\r') && (buffer[i] != '\n') && (buffer[i] != ';')) - { - start = &buffer[i]; - } - else - { - return -5; - } - - while(buffer[i] != ';') - { - i++; - if(i == buffer_size) return -5; - } - - end = &buffer[i]; - - len = end - start; - - if(len > 256) return -6; - - strncpy(result, start, len); - - return 0; -} - -static void -getIpConfig(void) -{ - int fd; - int i; - int t; - int len; - char c; - char buf[IPCONF_MAX_LEN+256]; - static char path[256]; - - if (boot == B_CD) { - sprintf(path, "%s%s;1", elfPath, "IPCONFIG.DAT"); - } - else if (boot == B_CC) { - strcpy(path, "mc0:/BOOT/IPCONFIG.DAT"); - } - else { - sprintf(path, "%s%s", elfPath, "IPCONFIG.DAT"); - } - fd = fioOpen(path, O_RDONLY); - - if (fd < 0) - { - scr_printf("Could not find IPCONFIG.DAT, using defaults\n" - "Net config: %s %s %s\n", ip, netmask, gw); - // Set defaults - memset(if_conf, 0x00, IPCONF_MAX_LEN); - i = 0; - strncpy(&if_conf[i], ip, 15); - i += strlen(ip) + 1; - - strncpy(&if_conf[i], netmask, 15); - i += strlen(netmask) + 1; - - strncpy(&if_conf[i], gw, 15); - i += strlen(gw) + 1; - - if_conf_len = i; - dbgscr_printf("conf len %d\n", if_conf_len); - return; - } - - memset(if_conf, 0x00, IPCONF_MAX_LEN); - memset(buf, 0x00, IPCONF_MAX_LEN+256); - - len = fioRead(fd, buf, IPCONF_MAX_LEN + 256 - 1); // Let the last byte be '\0' - fioClose(fd); - - if (len < 0) { - dbgprintf("Error reading ipconfig.dat\n"); - return; - } - - dbgscr_printf("ipconfig: Read %d bytes\n", len); - - i = 0; - // Clear out spaces (and potential ending CR/LF) - while ((c = buf[i]) != '\0') { - if ((c == ' ') || (c == '\r') || (c == '\n')) - buf[i] = '\0'; - i++; - } - - scr_printf("Net config: "); - for (t = 0, i = 0; t < 3; t++) { - strncpy(&if_conf[i], &buf[i], 15); - scr_printf("%s ", &if_conf[i]); - i += strlen(&if_conf[i]) + 1; - } - scr_printf("\n"); - // get extra config filename - -#ifndef USE_CACHED_CFG - if(getBufferValue(fExtraConfig, buf, len, "EXTRACNF") == 0) - { - scr_printf("extra conf: %s\n", fExtraConfig); - - load_extra_conf = 1; - } - else - { - load_extra_conf = 0; - } -#else - load_extra_conf = 0; -#endif - - if_conf_len = i; -} - -void -getExtraConfig() -{ - int fd, size, ret; - char *buf, *ptr, *ptr2; - fd = fioOpen(fExtraConfig, O_RDONLY); - - if ( fd < 0 ) - { - scr_printf("failed to open extra conf file\n"); - return; - } - - size = fioLseek(fd, 0, SEEK_END); - fioLseek(fd, 0, SEEK_SET); - buf = malloc(size + 1); - ret = fioRead(fd, buf, size); - buf[size] = 0; - fioClose(fd); - ptr = buf; - ptr2 = buf; - while(ptr < buf+size) { - ptr2 = strstr(ptr, ";"); - if ( ptr2 == 0 ) { - break; - } - ptr[ptr2-ptr] = 0; - scr_printf("loading %s\n", ptr); - pkoLoadModule(ptr, 0, NULL); - ptr = ptr2+1; - } - free(buf); - return; -} - -//////////////////////////////////////////////////////////////////////// -// Wrapper to load irx module from disc/rom -static void -pkoLoadModule(char *path, int argc, char *argv) -{ - int ret; - - ret = SifLoadModule(path, argc, argv); - if (ret < 0) { - scr_printf("Could not load module %s: %d\n", path, ret); - SleepThread(); - } - dbgscr_printf("[%d] returned\n", ret); -} - -#ifndef BUILTIN_IRXS -/* Load a module into RAM. */ -void * modbuf_load(const char *filename, int *filesize) -{ - void *res = NULL; - int fd = -1, size; - - if ((fd = fioOpen(filename, O_RDONLY)) < 0) - goto out; - - if ((size = fioLseek(fd, 0, SEEK_END)) < 0) - goto out; - - fioLseek(fd, 0, SEEK_SET); - fioLseek(fd, 0, SEEK_SET); - - res = (void *)irx_buffer_addr; - irx_buffer_addr += size + 32 - (size % 16); - dbgscr_printf(" modbuf_load : %s , %d (0x%X)\n",filename,size,(int)res); - - if (fioRead(fd, res, size) != size) - res = NULL; - - if (filesize) - *filesize = size; - -out: - if (fd >= 0) - fioClose(fd); - - return res; -} - -static int loadHostModBuffers() -{ - if (irx_buffer_addr == 0) - { - irx_buffer_addr = IRX_BUFFER_BASE; - - getIpConfig(); - - if (!(ioptrap_mod = modbuf_load(ioptrap_path, &ioptrap_size))) - {return -1;} - - if (!(pcsx2hostfs_mod = modbuf_load(pcsx2hostfs_path, &pcsx2hostfs_size))) - return -1; - } - - else - { - dbgscr_printf("Using Cached Modules\n"); - } - return 0; -} -#endif - -//////////////////////////////////////////////////////////////////////// -// Load all the irx modules we need, according to 'boot mode' -static void -loadModules(void) -{ - int ret; -#ifdef USE_CACHED_CFG - int i,t; -#endif - - dbgscr_printf("loadModules \n"); - -#ifdef USE_CACHED_CFG - if(if_conf_len==0) - { -#endif - if ((boot == B_MC) || (boot == B_CC)) - { - pkoLoadModule("rom0:SIO2MAN", 0, NULL); - pkoLoadModule("rom0:MCMAN", 0, NULL); - pkoLoadModule("rom0:MCSERV", 0, NULL); - } -#ifdef USE_CACHED_CFG - return; - } -#endif - -#ifdef BUILTIN_IRXS - _binary_ioptrap_irx_size = _binary_ioptrap_irx_end - _binary_ioptrap_irx_start; - _binary_pcsx2hostfs_irx_size = _binary_pcsx2hostfs_irx_end - _binary_pcsx2hostfs_irx_start; - -#ifdef USE_CACHED_CFG - if(if_conf_len==0) - { - getIpConfig(); - } - else - { - i=0; - scr_printf("Net config: "); - for (t = 0, i = 0; t < 3; t++) { - scr_printf("%s ", &if_conf[i]); - i += strlen(&if_conf[i]) + 1; - } - scr_printf("\n"); - } - -#else - getIpConfig(); -#endif - - dbgscr_printf("Exec ioptrap module. (%x,%d) ", (unsigned int)_binary_ioptrap_irx_start, _binary_ioptrap_irx_size); - SifExecModuleBuffer(_binary_ioptrap_irx_start, _binary_ioptrap_irx_size, 0, NULL,&ret); - dbgscr_printf("[%d] returned\n", ret); - dbgscr_printf("Exec pcsx2hostfs module. (%x,%d) ", (unsigned int)_binary_pcsx2hostfs_irx_start, _binary_pcsx2hostfs_irx_size); - SifExecModuleBuffer(_binary_pcsx2hostfs_irx_start, _binary_pcsx2hostfs_irx_size, 0, NULL,&ret); - dbgscr_printf("[%d] returned\n", ret); - dbgscr_printf("All modules loaded on IOP.\n"); -#else - if (boot == B_HOST) { - - dbgscr_printf("Exec ioptrap module. (%x,%d) ", (u32)ioptrap_mod, ioptrap_size); - SifExecModuleBuffer(ioptrap_mod, ioptrap_size, 0, NULL,&ret); - dbgscr_printf("[%d] returned\n", ret); - dbgscr_printf("Exec pcsx2hostfs module. (%x,%d) ", (u32)pcsx2hostfs_mod, pcsx2hostfs_size); - SifExecModuleBuffer(pcsx2hostfs_mod, pcsx2hostfs_size, 0, NULL,&ret); - dbgscr_printf("[%d] returned\n", ret); - - - dbgscr_printf("All modules loaded on IOP.\n"); - } else { - getIpConfig(); - dbgscr_printf("Exec ioptrap module. "); - pkoLoadModule(ioptrap_path, 0, NULL); - dbgscr_printf("Exec pcsx2hostfs module. "); - pkoLoadModule(pcsx2hostfs_path, 0, NULL); - dbgscr_printf("All modules loaded on IOP. "); - } -#endif -} - -//////////////////////////////////////////////////////////////////////// -// C standard strrchr func.. -char *strrchr(const char *s, int i) -{ - const char *last = NULL; - char c = i; - - while (*s) { - if (*s == c) { - last = s; - } - s++; - } - - if (*s == c) { - last = s; - } - - return (char *) last; -} - -//////////////////////////////////////////////////////////////////////// -// Split path (argv[0]) at the last '/', '\' or ':' and initialise -// elfName (whole path & name to the elf, for example 'cdrom:\pukklink.elf') -// elfPath (path to where the elf was started, for example 'cdrom:\') -static void -setPathInfo(char *path) -{ - char *ptr; - - strncpy(elfName, path, 255); - strncpy(elfPath, path, 255); - elfName[255] = '\0'; - elfPath[255] = '\0'; - - - ptr = strrchr(elfPath, '/'); - if (ptr == NULL) { - ptr = strrchr(elfPath, '\\'); - if (ptr == NULL) { - ptr = strrchr(elfPath, ':'); - if (ptr == NULL) { - scr_printf("Did not find path (%s)!\n", path); - SleepThread(); - } - } - } - - ptr++; - *ptr = '\0'; - -#ifndef BUILTIN_IRXS - /* Paths to modules. */ - - sprintf(pcsx2hostfs_path, "%s%s", elfPath, "PS2LINK.IRX"); - sprintf(ioptrap_path, "%s%s", elfPath, "IOPTRAP.IRX"); - - if (boot == B_CD) { - strcat(ioptrap_path, ";1"); - strcat(pcsx2hostfs_path, ";1"); - } -#endif - - dbgscr_printf("path is %s\n", elfPath); -} - -//////////////////////////////////////////////////////////////////////// -// Clear user memory -void -wipeUserMem(void) -{ - int i; - // Whipe user mem - for (i = 0x100000; i < 0x2000000 ; i += 64) { - asm ( - "\tsq $0, 0(%0) \n" - "\tsq $0, 16(%0) \n" - "\tsq $0, 32(%0) \n" - "\tsq $0, 48(%0) \n" - :: "r" (i) ); - } -} - -//////////////////////////////////////////////////////////////////////// -// Clear user memory - Load High and Host version -void -wipeUserMemLoadHigh(void) -{ - int i; - // Whipe user mem, apart from last bit - for (i = 0x100000; i < 0x1F00000 ; i += 64) { - asm ( - "\tsq $0, 0(%0) \n" - "\tsq $0, 16(%0) \n" - "\tsq $0, 32(%0) \n" - "\tsq $0, 48(%0) \n" - :: "r" (i) ); - } -} - - -void -restartIOP() -{ - fioExit(); - SifExitIopHeap(); - SifLoadFileExit(); - SifExitRpc(); - - dbgscr_printf("reset iop\n"); - SifIopReset(imgcmd, 0); - while (SifIopSync()) ; - - dbgscr_printf("rpc init\n"); - SifInitRpc(0); - - scr_printf("Initializing...\n"); - sio_printf("Initializing...\n"); - sbv_patch_enable_lmb(); - sbv_patch_disable_prefix_check(); - -// SifLoadFileReset(); - dbgscr_printf("loading modules\n"); - loadModules(); -} - -#if HOOK_THREADS - -// we can spare 1k to allow a generous number of threads.. -#define MAX_MONITORED_THREADS 256 - -int _first_load = 1; - -int th_count; -int active_threads[MAX_MONITORED_THREADS]; - -void *addr_LoadExecPS2; -void *addr_CreateThread; -void *addr_DeleteThread; - -void InstallKernelHooks(void) -{ - // get the current address of each syscall we want to hook then replace the entry - // in the syscall table with that of our hook function. - - addr_LoadExecPS2 = GetSyscall(6); - SetSyscall(6, &Hook_LoadExecPS2); - - addr_CreateThread = GetSyscall(32); - SetSyscall(32, &Hook_CreateThread); - - addr_DeleteThread = GetSyscall(33); - SetSyscall(33, &Hook_DeleteThread); -} - -void RemoveKernelHooks(void) -{ - SetSyscall(6, addr_LoadExecPS2); - SetSyscall(32, addr_CreateThread); - SetSyscall(33, addr_DeleteThread); -} - -int Hook_LoadExecPS2(char *fname, int argc, char *argv[]) -{ - // kill any active threads - KillActiveThreads(); - - // remove our kernel hooks - RemoveKernelHooks(); - - // call the real LoadExecPS2 handler, this will never return - return(((int (*)(char *, int, char **)) (addr_LoadExecPS2))(fname, argc, argv)); -} - -int Hook_CreateThread(ee_thread_t *thread_p) -{ - int i; - for(i = 0; i < MAX_MONITORED_THREADS; i++) - { - if(active_threads[i] < 0) { break; } - } - - if(i >= MAX_MONITORED_THREADS) { return(-1); } - - // call the real CreateThread handler, saving the thread id in our list - return(active_threads[i] = ((int (*)(ee_thread_t *)) (addr_CreateThread))(thread_p)); -} - -int Hook_DeleteThread(int th_id) -{ - int i; - - for(i = 0; i < MAX_MONITORED_THREADS; i++) - { - if(active_threads[i] == th_id) - { - // remove the entry from the active thread list. - active_threads[i] = -1; - break; - } - } - - // call the real DeleteThread handler - return(((int (*)(int)) (addr_DeleteThread))(th_id)); -} - -// kill all threads created and not deleted since PS2LINK.ELF started except for the calling thread. -void KillActiveThreads(void) -{ - int my_id = GetThreadId(); - int i; - - for(i = 0; i < MAX_MONITORED_THREADS; i++) - { - if((active_threads[i] >= 0) && (active_threads[i] != my_id)) - { - TerminateThread(active_threads[i]); - DeleteThread(active_threads[i]); - active_threads[i] = -1; - } - } -} - -void ResetActiveThreads(void) -{ - int i; - for(i = 0; i < MAX_MONITORED_THREADS; i++) { active_threads[i] = -1; } -} -#endif - -extern void _start(void); -extern int _end; - -//////////////////////////////////////////////////////////////////////// -int -main(int argc, char *argv[]) -{ - // int ret; - char *bootPath; - - init_scr(); - scr_printf(WELCOME_STRING); -#ifdef _LOADHIGHVER - scr_printf("Highload version\n"); -#endif - -#ifdef SCREENSHOTS - scr_printf("Screenshot capable\n"); -#endif - - scr_printf("pcsx2hostfs loaded at 0x%08X-0x%08X\n", ((u32) _start) - 8, (u32) &_end); - - installExceptionHandlers(); - -#if HOOK_THREADS - if(_first_load) - { - _first_load = 0; - InstallKernelHooks(); - } - - ResetActiveThreads(); -#endif - - // argc == 0 usually means naplink.. - if (argc == 0) { - bootPath = "host:"; - } - // reload1 usually gives an argc > 60000 (yea, this is kinda a hack..) - else if (argc != 1) { - bootPath = "mc0:/BWLINUX/"; - } - else { - bootPath = argv[0]; - } - - SifInitRpc(0); - dbgscr_printf("Checking argv\n"); - boot = 0; - - setPathInfo(bootPath); - - if(!strncmp(bootPath, "mc", strlen("mc"))) { - // Booting from my mc - scr_printf("Booting from mc dir (%s)\n", bootPath); - boot = B_MC; - } - else if(!strncmp(bootPath, "host", strlen("host"))) { - // Host - scr_printf("Booting from host (%s)\n", bootPath); - boot = B_HOST; - } - else if(!strncmp(bootPath, "rom0:OSDSYS", strlen("rom0:OSDSYS"))) { - // From CC's firmware - scr_printf("Booting as CC firmware\n"); - boot = B_CC; - } - else { - // Unknown - scr_printf("Booting from unrecognized place %s\n", bootPath); - boot = B_UNKN; - } - -#ifdef USE_CACHED_CFG - if(if_conf_len==0) - { - scr_printf("Initial boot, will load config then reset\n"); - if(boot == B_MC || boot == B_CC) - restartIOP(); - - getIpConfig(); - - } - else - { - scr_printf("Using cached config\n"); - } -#endif - - // System initalisation -#ifndef BUILTIN_IRXS - if (boot == B_HOST) { - if (loadHostModBuffers() < 0) { - dbgscr_printf("Unable to load modules from host:!\n"); - SleepThread(); - } - } -#endif - - restartIOP(); - -// CLEARSPU seems to cause exceptions in the Multi_Thread_Manager module. -// I suspect this is caused by the interrupt handlers or some such. -/* - dbgscr_printf("clearing spu\n"); - if (SifLoadModule("rom0:CLEARSPU", 0, NULL)<0) - { - scr_printf("rom0:CLEARSPU failed\n"); - sio_printf("rom0:CLEARSPU failed\n"); - } -*/ - - // get extra config - if(load_extra_conf) - { - dbgscr_printf("getting extra config\n"); - getExtraConfig(); - } - - scr_printf("Ready\n"); - sio_printf("Ready\n"); - nprintf("Ready\n"); - - const char elfFile[PS2E_FIO_MAX_PATH]; - - if(argc<2 || strlen(argv[1]) == 0) - { - nprintf("No elf specified. Trying PS2E Hack.\n"); - - char* params_ptr = (char*)0x1FFFC00; - int* params_magic = params_ptr + 0; - int* params_argc = params_ptr + 4; - char* params_argv = params_ptr + 8; - - nprintf("Magic = %c%c%c%c, argc=%d, argv[0]=%s\n", params_ptr[0], params_ptr[1], params_ptr[2], params_ptr[3], *params_argc, params_argv ); - - if(params_ptr[0] == 'E' && params_ptr[1] == '2' && params_ptr[2] == 'S' && params_ptr[3] == 'P') - { - int i; - argc = *params_argc; - for(i=0;i<=argc;i++) - { - argv[i] = params_argv; - params_argv += strlen(params_argv)+1; - } - } - else - { - nprintf("No elf to run. Looping infinitely.\n"); - for(;;); - } - } - - strcpy(elfFile,"host0:"); - strcat(elfFile,argv[1]); - - nprintf("Loading elf '%s'...", elfFile); - - pko_pkt_execee_req cmd; - - cmd.argc = 1; - strcpy(cmd.argv, elfFile); - cmd.argv[strlen(cmd.argv)+1] = 0; // second null char to end argv - - pkoExecEE(&cmd); - -// SleepThread(); - ExitDeleteThread(); - return 0; -} +/********************************************************************* + * Copyright (C) 2003 Tord Lindstrom (pukko@home.se) + * Copyright (C) 2003,2004 adresd (adresd_ps2dev@yahoo.com) + * This file is subject to the terms and conditions of the PS2Link License. + * See the file LICENSE in the main directory of this distribution for more + * details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "debug.h" +#include "hostlink.h" +#include "excepHandler.h" +#include "stdio.h" + +#include + +#define WELCOME_STRING "Welcome to pcsx2hostfs v1.52\n" + +#ifdef DEBUG +#define dbgprintf(args...) printf(args) +#define dbgscr_printf(args...) scr_printf(args) +#else +#define dbgprintf(args...) do { } while(0) +#define dbgscr_printf(args...) do { } while(0) +#endif + +//#define BGCOLOR ((volatile unsigned long*)0x120000e0) +//#define GS_SET_BGCOLOR(R,G,B) *BGCOLOR = ((u64)(R)<< 0) | ((u64)(G)<< 8) | ((u64)(B)<< 16) + +#define IRX_BUFFER_BASE 0x1F80000 +int irx_buffer_addr = 0; + +//////////////////////////////////////////////////////////////////////// +// Globals +extern int userThreadID; + +// Argv name+path & just path +char elfName[256] __attribute__((aligned(16))); +char elfPath[256]; + +#ifndef BUILTIN_IRXS +char ioptrap_path[PKO_MAX_PATH]; +char pcsx2hostfs_path[PKO_MAX_PATH]; + +void *ioptrap_mod = NULL, *pcsx2hostfs_mod = NULL; +int ioptrap_size = 0, pcsx2hostfs_size = 0; +#else +extern unsigned char _binary_ioptrap_irx_start[], _binary_pcsx2hostfs_irx_start[]; +extern unsigned char _binary_ioptrap_irx_end[], _binary_pcsx2hostfs_irx_end[]; +static unsigned int _binary_ioptrap_irx_size, _binary_pcsx2hostfs_irx_size; +#endif + +char *imgcmd = "rom0:UDNL rom0:EELOADCNF"; + +// Flags for which type of boot +#define B_CD 1 +#define B_MC 2 +#define B_HOST 3 +#define B_CC 4 +#define B_UNKN 5 +int boot; + +//////////////////////////////////////////////////////////////////////// +// Prototypes +static void loadModules(void); +static void pkoLoadModule(char *path, int argc, char *argv); +static void getIpConfig(void); + +extern int pkoExecEE(pko_pkt_execee_req *cmd); + +//////////////////////////////////////////////////////////////////////// +#define IPCONF_MAX_LEN 1024 + +char if_conf[IPCONF_MAX_LEN] = ""; +char fExtraConfig[256]; +int load_extra_conf = 0; +int if_conf_len = 0; + +char ip[16] __attribute__((aligned(16))) = "192.168.0.10"; +char netmask[16] __attribute__((aligned(16))) = "255.255.255.0"; +char gw[16] __attribute__((aligned(16))) = "192.168.0.1"; + +//////////////////////////////////////////////////////////////////////// +// Parse network configuration from IPCONFIG.DAT +// Note: parsing really should be made more robust... + +static int getBufferValue(char* result, char* buffer, u32 buffer_size, char* field) +{ + u32 len = strlen(field); + u32 i = 0; + s32 j = 0; + char* start = 0; + char* end = 0; + + while(strncmp(&buffer[i], field, len) != 0) + { + i++; + if(i == buffer_size) return -1; + } + + // Look for # comment + j = i; + + while((j > 0) && (buffer[j] != '\n') && (buffer[j] !='\r')) + { + j--; + + if(buffer[j] == '#') + return -2; + } + + + while(buffer[i] != '=') + { + i++; + if(i == buffer_size) return -3; + } + i++; + + while(buffer[i] == ' ') + { + i++; + if(i == buffer_size) return -4; + } + i++; + + if((buffer[i] != '\r') && (buffer[i] != '\n') && (buffer[i] != ';')) + { + start = &buffer[i]; + } + else + { + return -5; + } + + while(buffer[i] != ';') + { + i++; + if(i == buffer_size) return -5; + } + + end = &buffer[i]; + + len = end - start; + + if(len > 256) return -6; + + strncpy(result, start, len); + + return 0; +} + +static void +getIpConfig(void) +{ + int fd; + int i; + int t; + int len; + char c; + char buf[IPCONF_MAX_LEN+256]; + static char path[256]; + + if (boot == B_CD) { + sprintf(path, "%s%s;1", elfPath, "IPCONFIG.DAT"); + } + else if (boot == B_CC) { + strcpy(path, "mc0:/BOOT/IPCONFIG.DAT"); + } + else { + sprintf(path, "%s%s", elfPath, "IPCONFIG.DAT"); + } + fd = fioOpen(path, O_RDONLY); + + if (fd < 0) + { + scr_printf("Could not find IPCONFIG.DAT, using defaults\n" + "Net config: %s %s %s\n", ip, netmask, gw); + // Set defaults + memset(if_conf, 0x00, IPCONF_MAX_LEN); + i = 0; + strncpy(&if_conf[i], ip, 15); + i += strlen(ip) + 1; + + strncpy(&if_conf[i], netmask, 15); + i += strlen(netmask) + 1; + + strncpy(&if_conf[i], gw, 15); + i += strlen(gw) + 1; + + if_conf_len = i; + dbgscr_printf("conf len %d\n", if_conf_len); + return; + } + + memset(if_conf, 0x00, IPCONF_MAX_LEN); + memset(buf, 0x00, IPCONF_MAX_LEN+256); + + len = fioRead(fd, buf, IPCONF_MAX_LEN + 256 - 1); // Let the last byte be '\0' + fioClose(fd); + + if (len < 0) { + dbgprintf("Error reading ipconfig.dat\n"); + return; + } + + dbgscr_printf("ipconfig: Read %d bytes\n", len); + + i = 0; + // Clear out spaces (and potential ending CR/LF) + while ((c = buf[i]) != '\0') { + if ((c == ' ') || (c == '\r') || (c == '\n')) + buf[i] = '\0'; + i++; + } + + scr_printf("Net config: "); + for (t = 0, i = 0; t < 3; t++) { + strncpy(&if_conf[i], &buf[i], 15); + scr_printf("%s ", &if_conf[i]); + i += strlen(&if_conf[i]) + 1; + } + scr_printf("\n"); + // get extra config filename + +#ifndef USE_CACHED_CFG + if(getBufferValue(fExtraConfig, buf, len, "EXTRACNF") == 0) + { + scr_printf("extra conf: %s\n", fExtraConfig); + + load_extra_conf = 1; + } + else + { + load_extra_conf = 0; + } +#else + load_extra_conf = 0; +#endif + + if_conf_len = i; +} + +void +getExtraConfig() +{ + int fd, size, ret; + char *buf, *ptr, *ptr2; + fd = fioOpen(fExtraConfig, O_RDONLY); + + if ( fd < 0 ) + { + scr_printf("failed to open extra conf file\n"); + return; + } + + size = fioLseek(fd, 0, SEEK_END); + fioLseek(fd, 0, SEEK_SET); + buf = malloc(size + 1); + ret = fioRead(fd, buf, size); + buf[size] = 0; + fioClose(fd); + ptr = buf; + ptr2 = buf; + while(ptr < buf+size) { + ptr2 = strstr(ptr, ";"); + if ( ptr2 == 0 ) { + break; + } + ptr[ptr2-ptr] = 0; + scr_printf("loading %s\n", ptr); + pkoLoadModule(ptr, 0, NULL); + ptr = ptr2+1; + } + free(buf); + return; +} + +//////////////////////////////////////////////////////////////////////// +// Wrapper to load irx module from disc/rom +static void +pkoLoadModule(char *path, int argc, char *argv) +{ + int ret; + + ret = SifLoadModule(path, argc, argv); + if (ret < 0) { + scr_printf("Could not load module %s: %d\n", path, ret); + SleepThread(); + } + dbgscr_printf("[%d] returned\n", ret); +} + +#ifndef BUILTIN_IRXS +/* Load a module into RAM. */ +void * modbuf_load(const char *filename, int *filesize) +{ + void *res = NULL; + int fd = -1, size; + + if ((fd = fioOpen(filename, O_RDONLY)) < 0) + goto out; + + if ((size = fioLseek(fd, 0, SEEK_END)) < 0) + goto out; + + fioLseek(fd, 0, SEEK_SET); + fioLseek(fd, 0, SEEK_SET); + + res = (void *)irx_buffer_addr; + irx_buffer_addr += size + 32 - (size % 16); + dbgscr_printf(" modbuf_load : %s , %d (0x%X)\n",filename,size,(int)res); + + if (fioRead(fd, res, size) != size) + res = NULL; + + if (filesize) + *filesize = size; + +out: + if (fd >= 0) + fioClose(fd); + + return res; +} + +static int loadHostModBuffers() +{ + if (irx_buffer_addr == 0) + { + irx_buffer_addr = IRX_BUFFER_BASE; + + getIpConfig(); + + if (!(ioptrap_mod = modbuf_load(ioptrap_path, &ioptrap_size))) + {return -1;} + + if (!(pcsx2hostfs_mod = modbuf_load(pcsx2hostfs_path, &pcsx2hostfs_size))) + return -1; + } + + else + { + dbgscr_printf("Using Cached Modules\n"); + } + return 0; +} +#endif + +//////////////////////////////////////////////////////////////////////// +// Load all the irx modules we need, according to 'boot mode' +static void +loadModules(void) +{ + int ret; +#ifdef USE_CACHED_CFG + int i,t; +#endif + + dbgscr_printf("loadModules \n"); + +#ifdef USE_CACHED_CFG + if(if_conf_len==0) + { +#endif + if ((boot == B_MC) || (boot == B_CC)) + { + pkoLoadModule("rom0:SIO2MAN", 0, NULL); + pkoLoadModule("rom0:MCMAN", 0, NULL); + pkoLoadModule("rom0:MCSERV", 0, NULL); + } +#ifdef USE_CACHED_CFG + return; + } +#endif + +#ifdef BUILTIN_IRXS + _binary_ioptrap_irx_size = _binary_ioptrap_irx_end - _binary_ioptrap_irx_start; + _binary_pcsx2hostfs_irx_size = _binary_pcsx2hostfs_irx_end - _binary_pcsx2hostfs_irx_start; + +#ifdef USE_CACHED_CFG + if(if_conf_len==0) + { + getIpConfig(); + } + else + { + i=0; + scr_printf("Net config: "); + for (t = 0, i = 0; t < 3; t++) { + scr_printf("%s ", &if_conf[i]); + i += strlen(&if_conf[i]) + 1; + } + scr_printf("\n"); + } + +#else + getIpConfig(); +#endif + + dbgscr_printf("Exec ioptrap module. (%x,%d) ", (unsigned int)_binary_ioptrap_irx_start, _binary_ioptrap_irx_size); + SifExecModuleBuffer(_binary_ioptrap_irx_start, _binary_ioptrap_irx_size, 0, NULL,&ret); + dbgscr_printf("[%d] returned\n", ret); + dbgscr_printf("Exec pcsx2hostfs module. (%x,%d) ", (unsigned int)_binary_pcsx2hostfs_irx_start, _binary_pcsx2hostfs_irx_size); + SifExecModuleBuffer(_binary_pcsx2hostfs_irx_start, _binary_pcsx2hostfs_irx_size, 0, NULL,&ret); + dbgscr_printf("[%d] returned\n", ret); + dbgscr_printf("All modules loaded on IOP.\n"); +#else + if (boot == B_HOST) { + + dbgscr_printf("Exec ioptrap module. (%x,%d) ", (u32)ioptrap_mod, ioptrap_size); + SifExecModuleBuffer(ioptrap_mod, ioptrap_size, 0, NULL,&ret); + dbgscr_printf("[%d] returned\n", ret); + dbgscr_printf("Exec pcsx2hostfs module. (%x,%d) ", (u32)pcsx2hostfs_mod, pcsx2hostfs_size); + SifExecModuleBuffer(pcsx2hostfs_mod, pcsx2hostfs_size, 0, NULL,&ret); + dbgscr_printf("[%d] returned\n", ret); + + + dbgscr_printf("All modules loaded on IOP.\n"); + } else { + getIpConfig(); + dbgscr_printf("Exec ioptrap module. "); + pkoLoadModule(ioptrap_path, 0, NULL); + dbgscr_printf("Exec pcsx2hostfs module. "); + pkoLoadModule(pcsx2hostfs_path, 0, NULL); + dbgscr_printf("All modules loaded on IOP. "); + } +#endif +} + +//////////////////////////////////////////////////////////////////////// +// C standard strrchr func.. +char *strrchr(const char *s, int i) +{ + const char *last = NULL; + char c = i; + + while (*s) { + if (*s == c) { + last = s; + } + s++; + } + + if (*s == c) { + last = s; + } + + return (char *) last; +} + +//////////////////////////////////////////////////////////////////////// +// Split path (argv[0]) at the last '/', '\' or ':' and initialise +// elfName (whole path & name to the elf, for example 'cdrom:\pukklink.elf') +// elfPath (path to where the elf was started, for example 'cdrom:\') +static void +setPathInfo(char *path) +{ + char *ptr; + + strncpy(elfName, path, 255); + strncpy(elfPath, path, 255); + elfName[255] = '\0'; + elfPath[255] = '\0'; + + + ptr = strrchr(elfPath, '/'); + if (ptr == NULL) { + ptr = strrchr(elfPath, '\\'); + if (ptr == NULL) { + ptr = strrchr(elfPath, ':'); + if (ptr == NULL) { + scr_printf("Did not find path (%s)!\n", path); + SleepThread(); + } + } + } + + ptr++; + *ptr = '\0'; + +#ifndef BUILTIN_IRXS + /* Paths to modules. */ + + sprintf(pcsx2hostfs_path, "%s%s", elfPath, "PS2LINK.IRX"); + sprintf(ioptrap_path, "%s%s", elfPath, "IOPTRAP.IRX"); + + if (boot == B_CD) { + strcat(ioptrap_path, ";1"); + strcat(pcsx2hostfs_path, ";1"); + } +#endif + + dbgscr_printf("path is %s\n", elfPath); +} + +//////////////////////////////////////////////////////////////////////// +// Clear user memory +void +wipeUserMem(void) +{ + int i; + // Whipe user mem + for (i = 0x100000; i < 0x2000000 ; i += 64) { + asm ( + "\tsq $0, 0(%0) \n" + "\tsq $0, 16(%0) \n" + "\tsq $0, 32(%0) \n" + "\tsq $0, 48(%0) \n" + :: "r" (i) ); + } +} + +//////////////////////////////////////////////////////////////////////// +// Clear user memory - Load High and Host version +void +wipeUserMemLoadHigh(void) +{ + int i; + // Whipe user mem, apart from last bit + for (i = 0x100000; i < 0x1F00000 ; i += 64) { + asm ( + "\tsq $0, 0(%0) \n" + "\tsq $0, 16(%0) \n" + "\tsq $0, 32(%0) \n" + "\tsq $0, 48(%0) \n" + :: "r" (i) ); + } +} + + +void +restartIOP() +{ + fioExit(); + SifExitIopHeap(); + SifLoadFileExit(); + SifExitRpc(); + + dbgscr_printf("reset iop\n"); + SifIopReset(imgcmd, 0); + while (SifIopSync()) ; + + dbgscr_printf("rpc init\n"); + SifInitRpc(0); + + scr_printf("Initializing...\n"); + sio_printf("Initializing...\n"); + sbv_patch_enable_lmb(); + sbv_patch_disable_prefix_check(); + +// SifLoadFileReset(); + dbgscr_printf("loading modules\n"); + loadModules(); +} + +#if HOOK_THREADS + +// we can spare 1k to allow a generous number of threads.. +#define MAX_MONITORED_THREADS 256 + +int _first_load = 1; + +int th_count; +int active_threads[MAX_MONITORED_THREADS]; + +void *addr_LoadExecPS2; +void *addr_CreateThread; +void *addr_DeleteThread; + +void InstallKernelHooks(void) +{ + // get the current address of each syscall we want to hook then replace the entry + // in the syscall table with that of our hook function. + + addr_LoadExecPS2 = GetSyscall(6); + SetSyscall(6, &Hook_LoadExecPS2); + + addr_CreateThread = GetSyscall(32); + SetSyscall(32, &Hook_CreateThread); + + addr_DeleteThread = GetSyscall(33); + SetSyscall(33, &Hook_DeleteThread); +} + +void RemoveKernelHooks(void) +{ + SetSyscall(6, addr_LoadExecPS2); + SetSyscall(32, addr_CreateThread); + SetSyscall(33, addr_DeleteThread); +} + +int Hook_LoadExecPS2(char *fname, int argc, char *argv[]) +{ + // kill any active threads + KillActiveThreads(); + + // remove our kernel hooks + RemoveKernelHooks(); + + // call the real LoadExecPS2 handler, this will never return + return(((int (*)(char *, int, char **)) (addr_LoadExecPS2))(fname, argc, argv)); +} + +int Hook_CreateThread(ee_thread_t *thread_p) +{ + int i; + for(i = 0; i < MAX_MONITORED_THREADS; i++) + { + if(active_threads[i] < 0) { break; } + } + + if(i >= MAX_MONITORED_THREADS) { return(-1); } + + // call the real CreateThread handler, saving the thread id in our list + return(active_threads[i] = ((int (*)(ee_thread_t *)) (addr_CreateThread))(thread_p)); +} + +int Hook_DeleteThread(int th_id) +{ + int i; + + for(i = 0; i < MAX_MONITORED_THREADS; i++) + { + if(active_threads[i] == th_id) + { + // remove the entry from the active thread list. + active_threads[i] = -1; + break; + } + } + + // call the real DeleteThread handler + return(((int (*)(int)) (addr_DeleteThread))(th_id)); +} + +// kill all threads created and not deleted since PS2LINK.ELF started except for the calling thread. +void KillActiveThreads(void) +{ + int my_id = GetThreadId(); + int i; + + for(i = 0; i < MAX_MONITORED_THREADS; i++) + { + if((active_threads[i] >= 0) && (active_threads[i] != my_id)) + { + TerminateThread(active_threads[i]); + DeleteThread(active_threads[i]); + active_threads[i] = -1; + } + } +} + +void ResetActiveThreads(void) +{ + int i; + for(i = 0; i < MAX_MONITORED_THREADS; i++) { active_threads[i] = -1; } +} +#endif + +extern void _start(void); +extern int _end; + +//////////////////////////////////////////////////////////////////////// +int +main(int argc, char *argv[]) +{ + // int ret; + char *bootPath; + + init_scr(); + scr_printf(WELCOME_STRING); +#ifdef _LOADHIGHVER + scr_printf("Highload version\n"); +#endif + +#ifdef SCREENSHOTS + scr_printf("Screenshot capable\n"); +#endif + + scr_printf("pcsx2hostfs loaded at 0x%08X-0x%08X\n", ((u32) _start) - 8, (u32) &_end); + + installExceptionHandlers(); + +#if HOOK_THREADS + if(_first_load) + { + _first_load = 0; + InstallKernelHooks(); + } + + ResetActiveThreads(); +#endif + + // argc == 0 usually means naplink.. + if (argc == 0) { + bootPath = "host:"; + } + // reload1 usually gives an argc > 60000 (yea, this is kinda a hack..) + else if (argc != 1) { + bootPath = "mc0:/BWLINUX/"; + } + else { + bootPath = argv[0]; + } + + SifInitRpc(0); + dbgscr_printf("Checking argv\n"); + boot = 0; + + setPathInfo(bootPath); + + if(!strncmp(bootPath, "mc", strlen("mc"))) { + // Booting from my mc + scr_printf("Booting from mc dir (%s)\n", bootPath); + boot = B_MC; + } + else if(!strncmp(bootPath, "host", strlen("host"))) { + // Host + scr_printf("Booting from host (%s)\n", bootPath); + boot = B_HOST; + } + else if(!strncmp(bootPath, "rom0:OSDSYS", strlen("rom0:OSDSYS"))) { + // From CC's firmware + scr_printf("Booting as CC firmware\n"); + boot = B_CC; + } + else { + // Unknown + scr_printf("Booting from unrecognized place %s\n", bootPath); + boot = B_UNKN; + } + +#ifdef USE_CACHED_CFG + if(if_conf_len==0) + { + scr_printf("Initial boot, will load config then reset\n"); + if(boot == B_MC || boot == B_CC) + restartIOP(); + + getIpConfig(); + + } + else + { + scr_printf("Using cached config\n"); + } +#endif + + // System initalisation +#ifndef BUILTIN_IRXS + if (boot == B_HOST) { + if (loadHostModBuffers() < 0) { + dbgscr_printf("Unable to load modules from host:!\n"); + SleepThread(); + } + } +#endif + + restartIOP(); + +// CLEARSPU seems to cause exceptions in the Multi_Thread_Manager module. +// I suspect this is caused by the interrupt handlers or some such. +/* + dbgscr_printf("clearing spu\n"); + if (SifLoadModule("rom0:CLEARSPU", 0, NULL)<0) + { + scr_printf("rom0:CLEARSPU failed\n"); + sio_printf("rom0:CLEARSPU failed\n"); + } +*/ + + // get extra config + if(load_extra_conf) + { + dbgscr_printf("getting extra config\n"); + getExtraConfig(); + } + + scr_printf("Ready\n"); + sio_printf("Ready\n"); + nprintf("Ready\n"); + + const char elfFile[PS2E_FIO_MAX_PATH]; + + if(argc<2 || strlen(argv[1]) == 0) + { + nprintf("No elf specified. Trying PS2E Hack.\n"); + + char* params_ptr = (char*)0x1FFFC00; + int* params_magic = params_ptr + 0; + int* params_argc = params_ptr + 4; + char* params_argv = params_ptr + 8; + + nprintf("Magic = %c%c%c%c, argc=%d, argv[0]=%s\n", params_ptr[0], params_ptr[1], params_ptr[2], params_ptr[3], *params_argc, params_argv ); + + if(params_ptr[0] == 'E' && params_ptr[1] == '2' && params_ptr[2] == 'S' && params_ptr[3] == 'P') + { + int i; + argc = *params_argc; + for(i=0;i<=argc;i++) + { + argv[i] = params_argv; + params_argv += strlen(params_argv)+1; + } + } + else + { + nprintf("No elf to run. Looping infinitely.\n"); + for(;;); + } + } + + strcpy(elfFile,"host0:"); + strcat(elfFile,argv[1]); + + nprintf("Loading elf '%s'...", elfFile); + + pko_pkt_execee_req cmd; + + cmd.argc = 1; + strcpy(cmd.argv, elfFile); + cmd.argv[strlen(cmd.argv)+1] = 0; // second null char to end argv + + pkoExecEE(&cmd); + +// SleepThread(); + ExitDeleteThread(); + return 0; +} diff --git a/pcsx2hostfs/ee/ps2regs.h b/pcsx2hostfs/ee/ps2regs.h index 02dc534547..0a1be224ff 100644 --- a/pcsx2hostfs/ee/ps2regs.h +++ b/pcsx2hostfs/ee/ps2regs.h @@ -1,286 +1,286 @@ -/********************************************************************* - * Copyright (C) 2003 Tord Lindstrom (pukko@home.se) - * This file is subject to the terms and conditions of the PS2Link License. - * See the file LICENSE in the main directory of this distribution for more - * details. - */ - -#ifndef PS2REGS_H -#define PS2REGS_H - -/* TIMERS */ -#define T0_COUNT *((volatile unsigned int *)0x10000000) -#define T0_MODE *((volatile unsigned short *)0x10000010) -#define T0_COMP *((volatile unsigned short *)0x10000020) -#define T0_HOLD *((volatile unsigned short *)0x10000030) - -#define T1_COUNT *((volatile unsigned int *)0x10000800) -#define T1_MODE *((volatile unsigned short *)0x10000810) -#define T1_COMP *((volatile unsigned short *)0x10000820) -#define T1_HOLD *((volatile unsigned short *)0x10000830) - -#define T2_COUNT *((volatile unsigned int *)0x10001000) -#define T2_MODE *((volatile unsigned short *)0x10001010) -#define T2_COMP *((volatile unsigned short *)0x10001020) - -#define T3_COUNT *((volatile unsigned int *)0x10001800) -#define T3_MODE *((volatile unsigned short *)0x10001810) -#define T3_COMP *((volatile unsigned short *)0x10001820) - -/* IPU */ -#define IPU_CMD *((volatile unsigned long *)0x10002000) -#define IPU_CTRL *((volatile unsigned int *)0x10002010) -#define IPU_BP *((volatile unsigned int *)0x10002020) -#define IPU_TOP *((volatile unsigned long *)0x10002030) - -/* GIF */ -#define GIF_CTRL *((volatile unsigned int *)0x10003000) -#define GIF_MODE *((volatile unsigned int *)0x10003010) -#define GIF_STAT *((volatile unsigned int *)0x10003020) - -#define GIF_TAG0 *((volatile unsigned int *)0x10003040) -#define GIF_TAG1 *((volatile unsigned int *)0x10003050) -#define GIF_TAG2 *((volatile unsigned int *)0x10003060) -#define GIF_TAG3 *((volatile unsigned int *)0x10003070) -#define GIF_CNT *((volatile unsigned int *)0x10003080) -#define GIF_P3CNT *((volatile unsigned int *)0x10003090) -#define GIF_P3TAG *((volatile unsigned int *)0x100030a0) - -/* VIF */ -#define VIF0_STAT *((volatile unsigned int *)0x10003800) -#define VIF0_FBRST *((volatile unsigned int *)0x10003810) -#define VIF0_ERR *((volatile unsigned int *)0x10003820) -#define VIF0_MARK *((volatile unsigned int *)0x10003830) -#define VIF0_CYCLE *((volatile unsigned int *)0x10003840) -#define VIF0_MODE *((volatile unsigned int *)0x10003850) -#define VIF0_NUM *((volatile unsigned int *)0x10003860) -#define VIF0_MASK *((volatile unsigned int *)0x10003870) -#define VIF0_CODE *((volatile unsigned int *)0x10003880) -#define VIF0_ITOPS *((volatile unsigned int *)0x10003890) - -#define VIF0_ITOP *((volatile unsigned int *)0x100038d0) - -#define VIF0_R0 *((volatile unsigned int *)0x10003900) -#define VIF0_R1 *((volatile unsigned int *)0x10003910) -#define VIF0_R2 *((volatile unsigned int *)0x10003920) -#define VIF0_R3 *((volatile unsigned int *)0x10003930) -#define VIF0_C0 *((volatile unsigned int *)0x10003940) -#define VIF0_C1 *((volatile unsigned int *)0x10003950) -#define VIF0_C2 *((volatile unsigned int *)0x10003960) -#define VIF0_C3 *((volatile unsigned int *)0x10003970) - -#define VIF1_STAT *((volatile unsigned int *)0x10003c00) -#define VIF1_FBRST *((volatile unsigned int *)0x10003c10) -#define VIF1_ERR *((volatile unsigned int *)0x10003c20) -#define VIF1_MARK *((volatile unsigned int *)0x10003c30) -#define VIF1_CYCLE *((volatile unsigned int *)0x10003c40) -#define VIF1_MODE *((volatile unsigned int *)0x10003c50) -#define VIF1_NUM *((volatile unsigned int *)0x10003c60) -#define VIF1_MASK *((volatile unsigned int *)0x10003c70) -#define VIF1_CODE *((volatile unsigned int *)0x10003c80) -#define VIF1_ITOPS *((volatile unsigned int *)0x10003c90) -#define VIF1_BASE *((volatile unsigned int *)0x10003ca0) -#define VIF1_OFST *((volatile unsigned int *)0x10003cb0) -#define VIF1_TOPS *((volatile unsigned int *)0x10003cc0) -#define VIF1_ITOP *((volatile unsigned int *)0x10003cd0) -#define VIF1_TOP *((volatile unsigned int *)0x10003ce0) - -#define VIF1_R0 *((volatile unsigned int *)0x10003d00) -#define VIF1_R1 *((volatile unsigned int *)0x10003d10) -#define VIF1_R2 *((volatile unsigned int *)0x10003d20) -#define VIF1_R3 *((volatile unsigned int *)0x10003d30) -#define VIF1_C0 *((volatile unsigned int *)0x10003d40) -#define VIF1_C1 *((volatile unsigned int *)0x10003d50) -#define VIF1_C2 *((volatile unsigned int *)0x10003d60) -#define VIF1_C3 *((volatile unsigned int *)0x10003d70) - -/* FIFO */ -//#define VIF0_FIFO(write) *((volatile unsigned int128 *)0x10004000) -//#define VIF1_FIFO(read/write) *((volatile unsigned int128 *)0x10005000) -//#define GIF_FIFO(write) *((volatile unsigned int128 *)0x10006000) -//#define IPU_out_FIFO(read) *((volatile unsigned int128 *)0x10007000) -//#define IPU_in_FIFO(write) *((volatile unsigned int128 *)0x10007010) - -/* DMAC */ -#define CHCR 0x00 -#define MADR 0x04 -#define QWC 0x08 -#define TADR 0x0C -#define ASR0 0x10 -#define ASR1 0x14 - -#define DMA0 ((volatile unsigned int *)0x10008000) -#define DMA1 ((volatile unsigned int *)0x10009000) -#define DMA2 ((volatile unsigned int *)0x1000a000) -#define DMA3 ((volatile unsigned int *)0x1000b000) -#define DMA4 ((volatile unsigned int *)0x1000b400) -#define DMA5 ((volatile unsigned int *)0x1000c000) -#define DMA6 ((volatile unsigned int *)0x1000c400) -#define DMA7 ((volatile unsigned int *)0x1000c800) -#define DMA8 ((volatile unsigned int *)0x1000d000) -#define DMA9 ((volatile unsigned int *)0x1000d400) - -#define D0_CHCR *((volatile unsigned int *)0x10008000) -#define D0_MADR *((volatile unsigned int *)0x10008010) -#define D0_QWC *((volatile unsigned int *)0x10008020) -#define D0_TADR *((volatile unsigned int *)0x10008030) -#define D0_ASR0 *((volatile unsigned int *)0x10008040) -#define D0_ASR1 *((volatile unsigned int *)0x10008050) - -#define D1_CHCR *((volatile unsigned int *)0x10009000) -#define D1_MADR *((volatile unsigned int *)0x10009010) -#define D1_QWC *((volatile unsigned int *)0x10009020) -#define D1_TADR *((volatile unsigned int *)0x10009030) -#define D1_ASR0 *((volatile unsigned int *)0x10009040) -#define D1_ASR1 *((volatile unsigned int *)0x10009050) - -#define D2_CHCR *((volatile unsigned int *)0x1000a000) -#define D2_MADR *((volatile unsigned int *)0x1000a010) -#define D2_QWC *((volatile unsigned int *)0x1000a020) -#define D2_TADR *((volatile unsigned int *)0x1000a030) -#define D2_ASR0 *((volatile unsigned int *)0x1000a040) -#define D2_ASR1 *((volatile unsigned int *)0x1000a050) - -#define D3_CHCR *((volatile unsigned int *)0x1000b000) -#define D3_MADR *((volatile unsigned int *)0x1000b010) -#define D3_QWC *((volatile unsigned int *)0x1000b020) - -#define D4_CHCR *((volatile unsigned int *)0x1000b400) -#define D4_MADR *((volatile unsigned int *)0x1000b410) -#define D4_QWC *((volatile unsigned int *)0x1000b420) -#define D4_TADR *((volatile unsigned int *)0x1000b430) - -#define D5_CHCR *((volatile unsigned int *)0x1000c000) -#define D5_MADR *((volatile unsigned int *)0x1000c010) -#define D5_QWC *((volatile unsigned int *)0x1000c020) - -#define D6_CHCR *((volatile unsigned int *)0x1000c400) -#define D6_MADR *((volatile unsigned int *)0x1000c410) -#define D6_QWC *((volatile unsigned int *)0x1000c420) - -#define D7_CHCR *((volatile unsigned int *)0x1000c800) -#define D7_MADR *((volatile unsigned int *)0x1000c810) -#define D7_QWC *((volatile unsigned int *)0x1000c820) - -#define D8_CHCR *((volatile unsigned int *)0x1000d000) -#define D8_MADR *((volatile unsigned int *)0x1000d010) -#define D8_QWC *((volatile unsigned int *)0x1000d020) - -#define D8_SADR *((volatile unsigned int *)0x1000d080) - -#define D9_CHCR *((volatile unsigned int *)0x1000d400) -#define D9_MADR *((volatile unsigned int *)0x1000d410) -#define D9_QWC *((volatile unsigned int *)0x1000d420) -#define D9_TADR *((volatile unsigned int *)0x1000d430) - -#define D9_SADR *((volatile unsigned int *)0x1000d480) - -/* DMAC */ -#define D_CTRL *((volatile unsigned int *)0x1000e000) -#define D_STAT *((volatile unsigned int *)0x1000e010) -#define D_PCR *((volatile unsigned int *)0x1000e020) -#define D_SQWC *((volatile unsigned int *)0x1000e030) -#define D_RBSR *((volatile unsigned int *)0x1000e040) -#define D_RBOR *((volatile unsigned int *)0x1000e050) -#define D_STADR *((volatile unsigned int *)0x1000e060) -/* INTC */ -//#define I_STAT 0x1000f000 -//#define I_MASK 0x1000f010 - -/* TOOL putchar */ -// .byte KPUTCHAR (0x1000f180) -/* SIF */ -// #define SB_SMFLG (0x1000f230) - -/* DMAC */ -#define D_ENABLER *((volatile unsigned int *)0x1000f520) -#define D_ENABLEW *((volatile unsigned int *)0x1000f590) - -/* VU Mem */ -#define VUMicroMem0 *((volatile unsigned int *)0x11000000) -#define VUMem0 *((volatile unsigned int *)0x11004000) -#define VUMicroMem1 *((volatile unsigned int *)0x11008000) -#define VUMem1 *((volatile unsigned int *)0x1100c000) - -/* GS Privileged */ -#define GS_PMODE *((volatile unsigned long *)0x12000000) -#define GS_SMODE1 *((volatile unsigned long *)0x12000010) -#define GS_SMODE2 *((volatile unsigned long *)0x12000020) -#define GS_SRFSH *((volatile unsigned long *)0x12000030) -#define GS_SYNCH1 *((volatile unsigned long *)0x12000040) -#define GS_SYNCH2 *((volatile unsigned long *)0x12000050) -#define GS_SYNCV *((volatile unsigned long *)0x12000060) -#define GS_DISPFB1 *((volatile unsigned long *)0x12000070) -#define GS_DISPLAY1 *((volatile unsigned long *)0x12000080) -#define GS_DISPFB2 *((volatile unsigned long *)0x12000090) -#define GS_DISPLAY2 *((volatile unsigned long *)0x120000a0) -#define GS_EXTBUF *((volatile unsigned long *)0x120000b0) -#define GS_EXTDATA *((volatile unsigned long *)0x120000c0) -#define GS_EXTWRITE *((volatile unsigned long *)0x120000d0) -#define GS_BGCOLOR *((volatile unsigned long *)0x120000e0) - -#define GS_CSR *((volatile unsigned long *)0x12001000) -#define GS_IMR *((volatile unsigned long *)0x12001010) - -#define GS_BUSDIR *((volatile unsigned long *)0x12001040) - -#define GS_SIGLBLID *((volatile unsigned long *)0x12001080) - -/* GS General */ -#define GS_PRIM 0x00 -#define GS_RGBAQ 0x01 -#define GS_ST 0x02 -#define GS_UV 0x03 -#define GS_XYZF2 0x04 -#define GS_XYZ2 0x05 -#define GS_TEX0_1 0x06 -#define GS_TEX0_2 0x07 -#define GS_CLAMP_1 0x08 -#define GS_CLAMP_2 0x09 -#define GS_FOG 0x0a -#define GS_XYZF3 0x0c -#define GS_XYZ3 0x0d -#define GS_AD 0x0e -#define GS_TEX1_1 0x14 -#define GS_TEX1_2 0x15 -#define GS_TEX2_1 0x16 -#define GS_TEX2_2 0x17 -#define GS_XYOFFSET_1 0x18 -#define GS_XYOFFSET_2 0x19 -#define GS_PRMODECONT 0x1a -#define GS_PRMODE 0x1b -#define GS_TEXCLUT 0x1c -#define GS_SCANMSK 0x22 -#define GS_MIPTBP1_1 0x34 -#define GS_MIPTBP1_2 0x35 -#define GS_MIPTBP2_1 0x36 -#define GS_MIPTBP2_2 0x37 -#define GS_TEXA 0x3b -#define GS_FOGCOL 0x3d -#define GS_TEXFLUSH 0x3f -#define GS_SCISSOR_1 0x40 -#define GS_SCISSOR_2 0x41 -#define GS_ALPHA_1 0x42 -#define GS_ALPHA_2 0x43 -#define GS_DIMX 0x44 -#define GS_DTHE 0x45 -#define GS_COLCLAMP 0x46 -#define GS_TEST_1 0x47 -#define GS_TEST_2 0x48 -#define GS_PABE 0x49 -#define GS_FBA_1 0x4a -#define GS_FBA_2 0x4b -#define GS_FRAME_1 0x4c -#define GS_FRAME_2 0x4d -#define GS_ZBUF_1 0x4e -#define GS_ZBUF_2 0x4f -#define GS_BITBLTBUF 0x50 -#define GS_TRXPOS 0x51 -#define GS_TRXREG 0x52 -#define GS_TRXDIR 0x53 -#define GS_HWREG 0x54 -#define GS_SIGNAL 0x60 -#define GS_FINISH 0x61 -#define GS_LABEL 0x62 - -#endif +/********************************************************************* + * Copyright (C) 2003 Tord Lindstrom (pukko@home.se) + * This file is subject to the terms and conditions of the PS2Link License. + * See the file LICENSE in the main directory of this distribution for more + * details. + */ + +#ifndef PS2REGS_H +#define PS2REGS_H + +/* TIMERS */ +#define T0_COUNT *((volatile unsigned int *)0x10000000) +#define T0_MODE *((volatile unsigned short *)0x10000010) +#define T0_COMP *((volatile unsigned short *)0x10000020) +#define T0_HOLD *((volatile unsigned short *)0x10000030) + +#define T1_COUNT *((volatile unsigned int *)0x10000800) +#define T1_MODE *((volatile unsigned short *)0x10000810) +#define T1_COMP *((volatile unsigned short *)0x10000820) +#define T1_HOLD *((volatile unsigned short *)0x10000830) + +#define T2_COUNT *((volatile unsigned int *)0x10001000) +#define T2_MODE *((volatile unsigned short *)0x10001010) +#define T2_COMP *((volatile unsigned short *)0x10001020) + +#define T3_COUNT *((volatile unsigned int *)0x10001800) +#define T3_MODE *((volatile unsigned short *)0x10001810) +#define T3_COMP *((volatile unsigned short *)0x10001820) + +/* IPU */ +#define IPU_CMD *((volatile unsigned long *)0x10002000) +#define IPU_CTRL *((volatile unsigned int *)0x10002010) +#define IPU_BP *((volatile unsigned int *)0x10002020) +#define IPU_TOP *((volatile unsigned long *)0x10002030) + +/* GIF */ +#define GIF_CTRL *((volatile unsigned int *)0x10003000) +#define GIF_MODE *((volatile unsigned int *)0x10003010) +#define GIF_STAT *((volatile unsigned int *)0x10003020) + +#define GIF_TAG0 *((volatile unsigned int *)0x10003040) +#define GIF_TAG1 *((volatile unsigned int *)0x10003050) +#define GIF_TAG2 *((volatile unsigned int *)0x10003060) +#define GIF_TAG3 *((volatile unsigned int *)0x10003070) +#define GIF_CNT *((volatile unsigned int *)0x10003080) +#define GIF_P3CNT *((volatile unsigned int *)0x10003090) +#define GIF_P3TAG *((volatile unsigned int *)0x100030a0) + +/* VIF */ +#define VIF0_STAT *((volatile unsigned int *)0x10003800) +#define VIF0_FBRST *((volatile unsigned int *)0x10003810) +#define VIF0_ERR *((volatile unsigned int *)0x10003820) +#define VIF0_MARK *((volatile unsigned int *)0x10003830) +#define VIF0_CYCLE *((volatile unsigned int *)0x10003840) +#define VIF0_MODE *((volatile unsigned int *)0x10003850) +#define VIF0_NUM *((volatile unsigned int *)0x10003860) +#define VIF0_MASK *((volatile unsigned int *)0x10003870) +#define VIF0_CODE *((volatile unsigned int *)0x10003880) +#define VIF0_ITOPS *((volatile unsigned int *)0x10003890) + +#define VIF0_ITOP *((volatile unsigned int *)0x100038d0) + +#define VIF0_R0 *((volatile unsigned int *)0x10003900) +#define VIF0_R1 *((volatile unsigned int *)0x10003910) +#define VIF0_R2 *((volatile unsigned int *)0x10003920) +#define VIF0_R3 *((volatile unsigned int *)0x10003930) +#define VIF0_C0 *((volatile unsigned int *)0x10003940) +#define VIF0_C1 *((volatile unsigned int *)0x10003950) +#define VIF0_C2 *((volatile unsigned int *)0x10003960) +#define VIF0_C3 *((volatile unsigned int *)0x10003970) + +#define VIF1_STAT *((volatile unsigned int *)0x10003c00) +#define VIF1_FBRST *((volatile unsigned int *)0x10003c10) +#define VIF1_ERR *((volatile unsigned int *)0x10003c20) +#define VIF1_MARK *((volatile unsigned int *)0x10003c30) +#define VIF1_CYCLE *((volatile unsigned int *)0x10003c40) +#define VIF1_MODE *((volatile unsigned int *)0x10003c50) +#define VIF1_NUM *((volatile unsigned int *)0x10003c60) +#define VIF1_MASK *((volatile unsigned int *)0x10003c70) +#define VIF1_CODE *((volatile unsigned int *)0x10003c80) +#define VIF1_ITOPS *((volatile unsigned int *)0x10003c90) +#define VIF1_BASE *((volatile unsigned int *)0x10003ca0) +#define VIF1_OFST *((volatile unsigned int *)0x10003cb0) +#define VIF1_TOPS *((volatile unsigned int *)0x10003cc0) +#define VIF1_ITOP *((volatile unsigned int *)0x10003cd0) +#define VIF1_TOP *((volatile unsigned int *)0x10003ce0) + +#define VIF1_R0 *((volatile unsigned int *)0x10003d00) +#define VIF1_R1 *((volatile unsigned int *)0x10003d10) +#define VIF1_R2 *((volatile unsigned int *)0x10003d20) +#define VIF1_R3 *((volatile unsigned int *)0x10003d30) +#define VIF1_C0 *((volatile unsigned int *)0x10003d40) +#define VIF1_C1 *((volatile unsigned int *)0x10003d50) +#define VIF1_C2 *((volatile unsigned int *)0x10003d60) +#define VIF1_C3 *((volatile unsigned int *)0x10003d70) + +/* FIFO */ +//#define VIF0_FIFO(write) *((volatile unsigned int128 *)0x10004000) +//#define VIF1_FIFO(read/write) *((volatile unsigned int128 *)0x10005000) +//#define GIF_FIFO(write) *((volatile unsigned int128 *)0x10006000) +//#define IPU_out_FIFO(read) *((volatile unsigned int128 *)0x10007000) +//#define IPU_in_FIFO(write) *((volatile unsigned int128 *)0x10007010) + +/* DMAC */ +#define CHCR 0x00 +#define MADR 0x04 +#define QWC 0x08 +#define TADR 0x0C +#define ASR0 0x10 +#define ASR1 0x14 + +#define DMA0 ((volatile unsigned int *)0x10008000) +#define DMA1 ((volatile unsigned int *)0x10009000) +#define DMA2 ((volatile unsigned int *)0x1000a000) +#define DMA3 ((volatile unsigned int *)0x1000b000) +#define DMA4 ((volatile unsigned int *)0x1000b400) +#define DMA5 ((volatile unsigned int *)0x1000c000) +#define DMA6 ((volatile unsigned int *)0x1000c400) +#define DMA7 ((volatile unsigned int *)0x1000c800) +#define DMA8 ((volatile unsigned int *)0x1000d000) +#define DMA9 ((volatile unsigned int *)0x1000d400) + +#define D0_CHCR *((volatile unsigned int *)0x10008000) +#define D0_MADR *((volatile unsigned int *)0x10008010) +#define D0_QWC *((volatile unsigned int *)0x10008020) +#define D0_TADR *((volatile unsigned int *)0x10008030) +#define D0_ASR0 *((volatile unsigned int *)0x10008040) +#define D0_ASR1 *((volatile unsigned int *)0x10008050) + +#define D1_CHCR *((volatile unsigned int *)0x10009000) +#define D1_MADR *((volatile unsigned int *)0x10009010) +#define D1_QWC *((volatile unsigned int *)0x10009020) +#define D1_TADR *((volatile unsigned int *)0x10009030) +#define D1_ASR0 *((volatile unsigned int *)0x10009040) +#define D1_ASR1 *((volatile unsigned int *)0x10009050) + +#define D2_CHCR *((volatile unsigned int *)0x1000a000) +#define D2_MADR *((volatile unsigned int *)0x1000a010) +#define D2_QWC *((volatile unsigned int *)0x1000a020) +#define D2_TADR *((volatile unsigned int *)0x1000a030) +#define D2_ASR0 *((volatile unsigned int *)0x1000a040) +#define D2_ASR1 *((volatile unsigned int *)0x1000a050) + +#define D3_CHCR *((volatile unsigned int *)0x1000b000) +#define D3_MADR *((volatile unsigned int *)0x1000b010) +#define D3_QWC *((volatile unsigned int *)0x1000b020) + +#define D4_CHCR *((volatile unsigned int *)0x1000b400) +#define D4_MADR *((volatile unsigned int *)0x1000b410) +#define D4_QWC *((volatile unsigned int *)0x1000b420) +#define D4_TADR *((volatile unsigned int *)0x1000b430) + +#define D5_CHCR *((volatile unsigned int *)0x1000c000) +#define D5_MADR *((volatile unsigned int *)0x1000c010) +#define D5_QWC *((volatile unsigned int *)0x1000c020) + +#define D6_CHCR *((volatile unsigned int *)0x1000c400) +#define D6_MADR *((volatile unsigned int *)0x1000c410) +#define D6_QWC *((volatile unsigned int *)0x1000c420) + +#define D7_CHCR *((volatile unsigned int *)0x1000c800) +#define D7_MADR *((volatile unsigned int *)0x1000c810) +#define D7_QWC *((volatile unsigned int *)0x1000c820) + +#define D8_CHCR *((volatile unsigned int *)0x1000d000) +#define D8_MADR *((volatile unsigned int *)0x1000d010) +#define D8_QWC *((volatile unsigned int *)0x1000d020) + +#define D8_SADR *((volatile unsigned int *)0x1000d080) + +#define D9_CHCR *((volatile unsigned int *)0x1000d400) +#define D9_MADR *((volatile unsigned int *)0x1000d410) +#define D9_QWC *((volatile unsigned int *)0x1000d420) +#define D9_TADR *((volatile unsigned int *)0x1000d430) + +#define D9_SADR *((volatile unsigned int *)0x1000d480) + +/* DMAC */ +#define D_CTRL *((volatile unsigned int *)0x1000e000) +#define D_STAT *((volatile unsigned int *)0x1000e010) +#define D_PCR *((volatile unsigned int *)0x1000e020) +#define D_SQWC *((volatile unsigned int *)0x1000e030) +#define D_RBSR *((volatile unsigned int *)0x1000e040) +#define D_RBOR *((volatile unsigned int *)0x1000e050) +#define D_STADR *((volatile unsigned int *)0x1000e060) +/* INTC */ +//#define I_STAT 0x1000f000 +//#define I_MASK 0x1000f010 + +/* TOOL putchar */ +// .byte KPUTCHAR (0x1000f180) +/* SIF */ +// #define SB_SMFLG (0x1000f230) + +/* DMAC */ +#define D_ENABLER *((volatile unsigned int *)0x1000f520) +#define D_ENABLEW *((volatile unsigned int *)0x1000f590) + +/* VU Mem */ +#define VUMicroMem0 *((volatile unsigned int *)0x11000000) +#define VUMem0 *((volatile unsigned int *)0x11004000) +#define VUMicroMem1 *((volatile unsigned int *)0x11008000) +#define VUMem1 *((volatile unsigned int *)0x1100c000) + +/* GS Privileged */ +#define GS_PMODE *((volatile unsigned long *)0x12000000) +#define GS_SMODE1 *((volatile unsigned long *)0x12000010) +#define GS_SMODE2 *((volatile unsigned long *)0x12000020) +#define GS_SRFSH *((volatile unsigned long *)0x12000030) +#define GS_SYNCH1 *((volatile unsigned long *)0x12000040) +#define GS_SYNCH2 *((volatile unsigned long *)0x12000050) +#define GS_SYNCV *((volatile unsigned long *)0x12000060) +#define GS_DISPFB1 *((volatile unsigned long *)0x12000070) +#define GS_DISPLAY1 *((volatile unsigned long *)0x12000080) +#define GS_DISPFB2 *((volatile unsigned long *)0x12000090) +#define GS_DISPLAY2 *((volatile unsigned long *)0x120000a0) +#define GS_EXTBUF *((volatile unsigned long *)0x120000b0) +#define GS_EXTDATA *((volatile unsigned long *)0x120000c0) +#define GS_EXTWRITE *((volatile unsigned long *)0x120000d0) +#define GS_BGCOLOR *((volatile unsigned long *)0x120000e0) + +#define GS_CSR *((volatile unsigned long *)0x12001000) +#define GS_IMR *((volatile unsigned long *)0x12001010) + +#define GS_BUSDIR *((volatile unsigned long *)0x12001040) + +#define GS_SIGLBLID *((volatile unsigned long *)0x12001080) + +/* GS General */ +#define GS_PRIM 0x00 +#define GS_RGBAQ 0x01 +#define GS_ST 0x02 +#define GS_UV 0x03 +#define GS_XYZF2 0x04 +#define GS_XYZ2 0x05 +#define GS_TEX0_1 0x06 +#define GS_TEX0_2 0x07 +#define GS_CLAMP_1 0x08 +#define GS_CLAMP_2 0x09 +#define GS_FOG 0x0a +#define GS_XYZF3 0x0c +#define GS_XYZ3 0x0d +#define GS_AD 0x0e +#define GS_TEX1_1 0x14 +#define GS_TEX1_2 0x15 +#define GS_TEX2_1 0x16 +#define GS_TEX2_2 0x17 +#define GS_XYOFFSET_1 0x18 +#define GS_XYOFFSET_2 0x19 +#define GS_PRMODECONT 0x1a +#define GS_PRMODE 0x1b +#define GS_TEXCLUT 0x1c +#define GS_SCANMSK 0x22 +#define GS_MIPTBP1_1 0x34 +#define GS_MIPTBP1_2 0x35 +#define GS_MIPTBP2_1 0x36 +#define GS_MIPTBP2_2 0x37 +#define GS_TEXA 0x3b +#define GS_FOGCOL 0x3d +#define GS_TEXFLUSH 0x3f +#define GS_SCISSOR_1 0x40 +#define GS_SCISSOR_2 0x41 +#define GS_ALPHA_1 0x42 +#define GS_ALPHA_2 0x43 +#define GS_DIMX 0x44 +#define GS_DTHE 0x45 +#define GS_COLCLAMP 0x46 +#define GS_TEST_1 0x47 +#define GS_TEST_2 0x48 +#define GS_PABE 0x49 +#define GS_FBA_1 0x4a +#define GS_FBA_2 0x4b +#define GS_FRAME_1 0x4c +#define GS_FRAME_2 0x4d +#define GS_ZBUF_1 0x4e +#define GS_ZBUF_2 0x4f +#define GS_BITBLTBUF 0x50 +#define GS_TRXPOS 0x51 +#define GS_TRXREG 0x52 +#define GS_TRXDIR 0x53 +#define GS_HWREG 0x54 +#define GS_SIGNAL 0x60 +#define GS_FINISH 0x61 +#define GS_LABEL 0x62 + +#endif diff --git a/pcsx2hostfs/ee/r5900_regs.h b/pcsx2hostfs/ee/r5900_regs.h index 79da322ca2..b0807ab9a5 100644 --- a/pcsx2hostfs/ee/r5900_regs.h +++ b/pcsx2hostfs/ee/r5900_regs.h @@ -1,73 +1,73 @@ -//------------------------------------------------------------------------ -// File: regs.h -// Author: Tony Saveski, t_saveski@yahoo.com -// Notes: Playstation 2 Register Definitions -//------------------------------------------------------------------------ -#ifndef R5900_REGS_H -#define R5900_REGS_H - -// MIPS CPU Registsers -#define zero $0 // Always 0 -#define at $1 // Assembler temporary -#define v0 $2 // Function return -#define v1 $3 // -#define a0 $4 // Function arguments -#define a1 $5 -#define a2 $6 -#define a3 $7 -#define t0 $8 // Temporaries. No need -#define t1 $9 // to preserve in your -#define t2 $10 // functions. -#define t3 $11 -#define t4 $12 -#define t5 $13 -#define t6 $14 -#define t7 $15 -#define s0 $16 // Saved Temporaries. -#define s1 $17 // Make sure to restore -#define s2 $18 // to original value -#define s3 $19 // if your function -#define s4 $20 // changes their value. -#define s5 $21 -#define s6 $22 -#define s7 $23 -#define t8 $24 // More Temporaries. -#define t9 $25 -#define k0 $26 // Reserved for Kernel -#define k1 $27 -#define gp $28 // Global Pointer -#define sp $29 // Stack Pointer -#define fp $30 // Frame Pointer -#define ra $31 // Function Return Address - -// COP0 -#define Index $0 // Index into the TLB array -#define Random $1 // Randomly generated index into the TLB array -#define EntryLo0 $2 // Low-order portion of the TLB entry for.. -#define EntryLo1 $3 // Low-order portion of the TLB entry for -#define Context $4 // Pointer to page table entry in memory -#define PageMask $5 // Control for variable page size in TLB entries -#define Wired $6 // Controls the number of fixed ("wired") TLB entries -#define BadVAddr $8 // Address for the most recent address-related exception -#define Count $9 // Processor cycle count -#define EntryHi $10 // High-order portion of the TLB entry -#define Compare $11 // Timer interrupt control -#define Status $12 // Processor status and control -#define Cause $13 // Cause of last general exception -#define EPC $14 // Program counter at last exception -#define PRId $15 // Processor identification and revision -#define Config $16 // Configuration register -#define LLAddr $17 // Load linked address -#define WatchLo $18 // Watchpoint address Section 6.25 on -#define WatchHi $19 // Watchpoint control -#define Debug $23 // EJTAG Debug register -#define DEPC $24 // Program counter at last EJTAG debug exception -#define PerfCnt $25 // Performance counter interface -#define ErrCtl $26 // Parity/ECC error control and status -#define CacheErr $27 // Cache parity error control and status -#define TagLo $28 // Low-order portion of cache tag interface -#define TagHi $29 // High-order portion of cache tag interface -#define ErrorPC $30 // Program counter at last error -#define DEASVE $31 // EJTAG debug exception save register - -#endif // R5900_REGS_H +//------------------------------------------------------------------------ +// File: regs.h +// Author: Tony Saveski, t_saveski@yahoo.com +// Notes: Playstation 2 Register Definitions +//------------------------------------------------------------------------ +#ifndef R5900_REGS_H +#define R5900_REGS_H + +// MIPS CPU Registsers +#define zero $0 // Always 0 +#define at $1 // Assembler temporary +#define v0 $2 // Function return +#define v1 $3 // +#define a0 $4 // Function arguments +#define a1 $5 +#define a2 $6 +#define a3 $7 +#define t0 $8 // Temporaries. No need +#define t1 $9 // to preserve in your +#define t2 $10 // functions. +#define t3 $11 +#define t4 $12 +#define t5 $13 +#define t6 $14 +#define t7 $15 +#define s0 $16 // Saved Temporaries. +#define s1 $17 // Make sure to restore +#define s2 $18 // to original value +#define s3 $19 // if your function +#define s4 $20 // changes their value. +#define s5 $21 +#define s6 $22 +#define s7 $23 +#define t8 $24 // More Temporaries. +#define t9 $25 +#define k0 $26 // Reserved for Kernel +#define k1 $27 +#define gp $28 // Global Pointer +#define sp $29 // Stack Pointer +#define fp $30 // Frame Pointer +#define ra $31 // Function Return Address + +// COP0 +#define Index $0 // Index into the TLB array +#define Random $1 // Randomly generated index into the TLB array +#define EntryLo0 $2 // Low-order portion of the TLB entry for.. +#define EntryLo1 $3 // Low-order portion of the TLB entry for +#define Context $4 // Pointer to page table entry in memory +#define PageMask $5 // Control for variable page size in TLB entries +#define Wired $6 // Controls the number of fixed ("wired") TLB entries +#define BadVAddr $8 // Address for the most recent address-related exception +#define Count $9 // Processor cycle count +#define EntryHi $10 // High-order portion of the TLB entry +#define Compare $11 // Timer interrupt control +#define Status $12 // Processor status and control +#define Cause $13 // Cause of last general exception +#define EPC $14 // Program counter at last exception +#define PRId $15 // Processor identification and revision +#define Config $16 // Configuration register +#define LLAddr $17 // Load linked address +#define WatchLo $18 // Watchpoint address Section 6.25 on +#define WatchHi $19 // Watchpoint control +#define Debug $23 // EJTAG Debug register +#define DEPC $24 // Program counter at last EJTAG debug exception +#define PerfCnt $25 // Performance counter interface +#define ErrCtl $26 // Parity/ECC error control and status +#define CacheErr $27 // Cache parity error control and status +#define TagLo $28 // Low-order portion of cache tag interface +#define TagHi $29 // High-order portion of cache tag interface +#define ErrorPC $30 // Program counter at last error +#define DEASVE $31 // EJTAG debug exception save register + +#endif // R5900_REGS_H diff --git a/pcsx2hostfs/include/byteorder.h b/pcsx2hostfs/include/byteorder.h index c1a35e0794..914573a256 100644 --- a/pcsx2hostfs/include/byteorder.h +++ b/pcsx2hostfs/include/byteorder.h @@ -1,42 +1,42 @@ -/********************************************************************* - * Copyright (C) 2003 Tord Lindstrom (pukko@home.se) - * This file is subject to the terms and conditions of the PS2Link License. - * See the file LICENSE in the main directory of this distribution for more - * details. - */ - -#ifndef BYTEORDER_H -#define BYTEORDER_H - -#include - -#ifdef BIG_ENDIAN -inline unsigned int ntohl(x) { return x; } -inline unsigned short ntohs(x) { return x; } -inline unsigned int ntohl(x) { return x; } -inline unsigned short ntohs(x) { return x; } -#else -// LITTLE_ENDIAN -inline unsigned int -htonl(unsigned int x) -{ - return ((x & 0xff) << 24 ) | - ((x & 0xff00) << 8 ) | - ((x & 0xff0000) >> 8 ) | - ((x & 0xff000000) >> 24 ); -} - -inline unsigned short -htons(unsigned short x) -{ - return ((x & 0xff) << 8 ) | ((x & 0xff00) >> 8 ); -} - -#define ntohl htonl -#define ntohs htons - -#endif - -#define IP4_ADDR(ipaddr, a,b,c,d) (ipaddr)->s_addr = htonl(((u32)(a & 0xff) << 24) | ((u32)(b & 0xff) << 16 ) | ((u32)(c & 0xff) << 8) | (u32)(d & 0xff)) - -#endif /* BYTEORDER_H */ +/********************************************************************* + * Copyright (C) 2003 Tord Lindstrom (pukko@home.se) + * This file is subject to the terms and conditions of the PS2Link License. + * See the file LICENSE in the main directory of this distribution for more + * details. + */ + +#ifndef BYTEORDER_H +#define BYTEORDER_H + +#include + +#ifdef BIG_ENDIAN +inline unsigned int ntohl(x) { return x; } +inline unsigned short ntohs(x) { return x; } +inline unsigned int ntohl(x) { return x; } +inline unsigned short ntohs(x) { return x; } +#else +// LITTLE_ENDIAN +inline unsigned int +htonl(unsigned int x) +{ + return ((x & 0xff) << 24 ) | + ((x & 0xff00) << 8 ) | + ((x & 0xff0000) >> 8 ) | + ((x & 0xff000000) >> 24 ); +} + +inline unsigned short +htons(unsigned short x) +{ + return ((x & 0xff) << 8 ) | ((x & 0xff00) >> 8 ); +} + +#define ntohl htonl +#define ntohs htons + +#endif + +#define IP4_ADDR(ipaddr, a,b,c,d) (ipaddr)->s_addr = htonl(((u32)(a & 0xff) << 24) | ((u32)(b & 0xff) << 16 ) | ((u32)(c & 0xff) << 8) | (u32)(d & 0xff)) + +#endif /* BYTEORDER_H */ diff --git a/pcsx2hostfs/include/hostlink.h b/pcsx2hostfs/include/hostlink.h index e9603b486c..05566630ec 100644 --- a/pcsx2hostfs/include/hostlink.h +++ b/pcsx2hostfs/include/hostlink.h @@ -1,216 +1,216 @@ -/********************************************************************* - * Copyright (C) 2003 Tord Lindstrom (pukko@home.se) - * This file is subject to the terms and conditions of the PS2Link License. - * See the file LICENSE in the main directory of this distribution for more - * details. - */ - -#define PS2E_FIO_OPEN_CMD 0xcc2e0101 -#define PS2E_FIO_CLOSE_CMD 0xcc2e0102 -#define PS2E_FIO_READ_CMD 0xcc2e0103 -#define PS2E_FIO_WRITE_CMD 0xcc2e0104 -#define PS2E_FIO_LSEEK_CMD 0xcc2e0105 -#define PS2E_FIO_OPENDIR_CMD 0xcc2e0106 -#define PS2E_FIO_CLOSEDIR_CMD 0xcc2e0107 -#define PS2E_FIO_READDIR_CMD 0xcc2e0108 -#define PS2E_FIO_REMOVE_CMD 0xcc2e0109 -#define PS2E_FIO_MKDIR_CMD 0xcc2e010a -#define PS2E_FIO_RMDIR_CMD 0xcc2e010b -#define PS2E_FIO_PRINTF_CMD 0xcc2e0201 - -#define PS2E_FIO_MAX_PATH 256 -#define PKO_MAX_PATH 256 - -// old stuff - -typedef struct -{ - unsigned int cmd; - unsigned short len; -} __attribute__((packed)) pko_pkt_hdr; - -typedef struct -{ - unsigned int cmd; - unsigned short len; - unsigned int retval; -} __attribute__((packed)) pko_pkt_file_rly; - -typedef struct -{ - unsigned int cmd; - unsigned short len; - int flags; - char path[PKO_MAX_PATH]; -} __attribute__((packed)) pko_pkt_open_req; - -typedef struct -{ - unsigned int cmd; - unsigned short len; - int fd; -} __attribute__((packed)) pko_pkt_close_req; - -typedef struct -{ - unsigned int cmd; - unsigned short len; - int fd; - int nbytes; -} __attribute__((packed)) pko_pkt_read_req; - -typedef struct -{ - unsigned int cmd; - unsigned short len; - int retval; - int nbytes; -} __attribute__((packed)) pko_pkt_read_rly; - -typedef struct -{ - unsigned int cmd; - unsigned short len; - int fd; - int nbytes; -} __attribute__((packed)) pko_pkt_write_req; - -typedef struct -{ - unsigned int cmd; - unsigned short len; - int fd; - int offset; - int whence; -} __attribute__((packed)) pko_pkt_lseek_req; - -typedef struct -{ - unsigned int cmd; - unsigned short len; - char name[PKO_MAX_PATH]; -} __attribute__((packed)) pko_pkt_remove_req; - -typedef struct -{ - unsigned int cmd; - unsigned short len; - int mode; - char name[PKO_MAX_PATH]; -} __attribute__((packed)) pko_pkt_mkdir_req; - -typedef struct -{ - unsigned int cmd; - unsigned short len; - char name[PKO_MAX_PATH]; -} __attribute__((packed)) pko_pkt_rmdir_req; - -typedef struct -{ - unsigned int cmd; - unsigned short len; - int fd; -} __attribute__((packed)) pko_pkt_dread_req; - -typedef struct -{ - unsigned int cmd; - unsigned short len; - int retval; -/* from io_common.h (fio_dirent_t) in ps2lib */ - unsigned int mode; - unsigned int attr; - unsigned int size; - unsigned char ctime[8]; - unsigned char atime[8]; - unsigned char mtime[8]; - unsigned int hisize; - char name[256]; -} __attribute__((packed)) pko_pkt_dread_rly; - -//// - -typedef struct -{ - unsigned int cmd; - unsigned short len; -} __attribute__((packed)) pko_pkt_reset_req; - -typedef struct -{ - unsigned int cmd; - unsigned short len; - int argc; - char argv[PKO_MAX_PATH]; -} __attribute__((packed)) pko_pkt_execee_req; - -typedef struct -{ - unsigned int cmd; - unsigned short len; - int argc; - char argv[PKO_MAX_PATH]; -} __attribute__((packed)) pko_pkt_execiop_req; - -typedef struct -{ - unsigned int cmd; - unsigned short len; - unsigned short size; - unsigned char file[PKO_MAX_PATH]; -} __attribute__((packed)) pko_pkt_gsexec_req; - -typedef struct -{ - unsigned int cmd; - unsigned short len; -} __attribute__((packed)) pko_pkt_poweroff_req; - -typedef struct -{ - unsigned int cmd; - unsigned short len; - int vpu; -} __attribute__((packed)) pko_pkt_start_vu; - -typedef struct -{ - unsigned int cmd; - unsigned short len; - int vpu; -} __attribute__((packed)) pko_pkt_stop_vu; - -typedef struct -{ - unsigned int cmd; - unsigned short len; - unsigned int offset; - unsigned int size; - char argv[PKO_MAX_PATH]; -} __attribute__((packed)) pko_pkt_mem_io; - -typedef struct { - unsigned int cmd; - unsigned short len; - int regs; - char argv[PKO_MAX_PATH]; -} __attribute__((packed)) pko_pkt_dump_regs; - -typedef struct { - unsigned int cmd; - unsigned short len; - unsigned int regs[79]; -} __attribute__((packed)) pko_pkt_send_regs; - -typedef struct { - unsigned int cmd; - unsigned short len; - unsigned int base; - unsigned int width; - unsigned int height; - unsigned short psm; -} __attribute__((packed)) pko_pkt_screenshot; - -#define PKO_MAX_WRITE_SEGMENT (1460 - sizeof(pko_pkt_write_req)) -#define PKO_MAX_READ_SEGMENT (1460 - sizeof(pko_pkt_read_rly)) +/********************************************************************* + * Copyright (C) 2003 Tord Lindstrom (pukko@home.se) + * This file is subject to the terms and conditions of the PS2Link License. + * See the file LICENSE in the main directory of this distribution for more + * details. + */ + +#define PS2E_FIO_OPEN_CMD 0xcc2e0101 +#define PS2E_FIO_CLOSE_CMD 0xcc2e0102 +#define PS2E_FIO_READ_CMD 0xcc2e0103 +#define PS2E_FIO_WRITE_CMD 0xcc2e0104 +#define PS2E_FIO_LSEEK_CMD 0xcc2e0105 +#define PS2E_FIO_OPENDIR_CMD 0xcc2e0106 +#define PS2E_FIO_CLOSEDIR_CMD 0xcc2e0107 +#define PS2E_FIO_READDIR_CMD 0xcc2e0108 +#define PS2E_FIO_REMOVE_CMD 0xcc2e0109 +#define PS2E_FIO_MKDIR_CMD 0xcc2e010a +#define PS2E_FIO_RMDIR_CMD 0xcc2e010b +#define PS2E_FIO_PRINTF_CMD 0xcc2e0201 + +#define PS2E_FIO_MAX_PATH 256 +#define PKO_MAX_PATH 256 + +// old stuff + +typedef struct +{ + unsigned int cmd; + unsigned short len; +} __attribute__((packed)) pko_pkt_hdr; + +typedef struct +{ + unsigned int cmd; + unsigned short len; + unsigned int retval; +} __attribute__((packed)) pko_pkt_file_rly; + +typedef struct +{ + unsigned int cmd; + unsigned short len; + int flags; + char path[PKO_MAX_PATH]; +} __attribute__((packed)) pko_pkt_open_req; + +typedef struct +{ + unsigned int cmd; + unsigned short len; + int fd; +} __attribute__((packed)) pko_pkt_close_req; + +typedef struct +{ + unsigned int cmd; + unsigned short len; + int fd; + int nbytes; +} __attribute__((packed)) pko_pkt_read_req; + +typedef struct +{ + unsigned int cmd; + unsigned short len; + int retval; + int nbytes; +} __attribute__((packed)) pko_pkt_read_rly; + +typedef struct +{ + unsigned int cmd; + unsigned short len; + int fd; + int nbytes; +} __attribute__((packed)) pko_pkt_write_req; + +typedef struct +{ + unsigned int cmd; + unsigned short len; + int fd; + int offset; + int whence; +} __attribute__((packed)) pko_pkt_lseek_req; + +typedef struct +{ + unsigned int cmd; + unsigned short len; + char name[PKO_MAX_PATH]; +} __attribute__((packed)) pko_pkt_remove_req; + +typedef struct +{ + unsigned int cmd; + unsigned short len; + int mode; + char name[PKO_MAX_PATH]; +} __attribute__((packed)) pko_pkt_mkdir_req; + +typedef struct +{ + unsigned int cmd; + unsigned short len; + char name[PKO_MAX_PATH]; +} __attribute__((packed)) pko_pkt_rmdir_req; + +typedef struct +{ + unsigned int cmd; + unsigned short len; + int fd; +} __attribute__((packed)) pko_pkt_dread_req; + +typedef struct +{ + unsigned int cmd; + unsigned short len; + int retval; +/* from io_common.h (fio_dirent_t) in ps2lib */ + unsigned int mode; + unsigned int attr; + unsigned int size; + unsigned char ctime[8]; + unsigned char atime[8]; + unsigned char mtime[8]; + unsigned int hisize; + char name[256]; +} __attribute__((packed)) pko_pkt_dread_rly; + +//// + +typedef struct +{ + unsigned int cmd; + unsigned short len; +} __attribute__((packed)) pko_pkt_reset_req; + +typedef struct +{ + unsigned int cmd; + unsigned short len; + int argc; + char argv[PKO_MAX_PATH]; +} __attribute__((packed)) pko_pkt_execee_req; + +typedef struct +{ + unsigned int cmd; + unsigned short len; + int argc; + char argv[PKO_MAX_PATH]; +} __attribute__((packed)) pko_pkt_execiop_req; + +typedef struct +{ + unsigned int cmd; + unsigned short len; + unsigned short size; + unsigned char file[PKO_MAX_PATH]; +} __attribute__((packed)) pko_pkt_gsexec_req; + +typedef struct +{ + unsigned int cmd; + unsigned short len; +} __attribute__((packed)) pko_pkt_poweroff_req; + +typedef struct +{ + unsigned int cmd; + unsigned short len; + int vpu; +} __attribute__((packed)) pko_pkt_start_vu; + +typedef struct +{ + unsigned int cmd; + unsigned short len; + int vpu; +} __attribute__((packed)) pko_pkt_stop_vu; + +typedef struct +{ + unsigned int cmd; + unsigned short len; + unsigned int offset; + unsigned int size; + char argv[PKO_MAX_PATH]; +} __attribute__((packed)) pko_pkt_mem_io; + +typedef struct { + unsigned int cmd; + unsigned short len; + int regs; + char argv[PKO_MAX_PATH]; +} __attribute__((packed)) pko_pkt_dump_regs; + +typedef struct { + unsigned int cmd; + unsigned short len; + unsigned int regs[79]; +} __attribute__((packed)) pko_pkt_send_regs; + +typedef struct { + unsigned int cmd; + unsigned short len; + unsigned int base; + unsigned int width; + unsigned int height; + unsigned short psm; +} __attribute__((packed)) pko_pkt_screenshot; + +#define PKO_MAX_WRITE_SEGMENT (1460 - sizeof(pko_pkt_write_req)) +#define PKO_MAX_READ_SEGMENT (1460 - sizeof(pko_pkt_read_rly)) diff --git a/pcsx2hostfs/iop/excepHandler.c b/pcsx2hostfs/iop/excepHandler.c index a3a7044da2..aa4c0b5262 100644 --- a/pcsx2hostfs/iop/excepHandler.c +++ b/pcsx2hostfs/iop/excepHandler.c @@ -1,119 +1,119 @@ -/********************************************************************* - * Copyright (C) 2004 Lukasz Bruun (mail@lukasz.dk) - * This file is subject to the terms and conditions of the PS2Link License. - * See the file LICENSE in the main directory of this distribution for more - * details. - */ - -#include -#include "irx_imports.h" -#include "hostlink.h" - -#define BUFFER_SIZE sizeof(exception_frame_t)+4+4+128 - -u32 excep_buffer[BUFFER_SIZE/4] __attribute__ ((aligned(16))); - -extern unsigned int pcsx2fioSendSifCmd(unsigned int cmd, void *src, unsigned int len); // lazy fix :) -extern int excepscrdump; - -#define PKO_RPC_IOPEXCEP 12 - -// taken from smod by mrbrown, only use one function, didn't wanna include another irx. - -/* Module info entry. */ -typedef struct _smod_mod_info { - struct _smod_mod_info *next; - u8 *name; - u16 version; - u16 newflags; /* For modload shipped with games. */ - u16 id; - u16 flags; /* I believe this is where flags are kept for BIOS versions. */ - u32 entry; /* _start */ - u32 gp; - u32 text_start; - u32 text_size; - u32 data_size; - u32 bss_size; - u32 unused1; - u32 unused2; -} smod_mod_info_t; - - -smod_mod_info_t *smod_get_next_mod(smod_mod_info_t *cur_mod) -{ - /* If cur_mod is 0, return the head of the list (IOP address 0x800). */ - if (!cur_mod) { - return (smod_mod_info_t *)0x800; - } else { - if (!cur_mod->next) - return 0; - else - return cur_mod->next; - } - return 0; -} - -char* ExceptionGetModuleName(u32 epc, u32* r_epc) -{ - smod_mod_info_t *mod_info = 0; - - while((mod_info = smod_get_next_mod(mod_info)) != 0) - { - if((epc >= mod_info->text_start) && (epc <= (mod_info->text_start+mod_info->text_size))) - { - if(r_epc) - *r_epc = epc - mod_info->text_start; - - return mod_info->name; - } - } - - return 0; -} - -static void excep_handler2(exception_frame_t *frame) -{ - u32 r_epc; // relative epc - char *module_name; - u32 len; - u32* buffer = excep_buffer; - - module_name = ExceptionGetModuleName(frame->epc, &r_epc); - - len = strlen(module_name); - - - buffer[0] = 0x0d02beba; // reverse engineering.. - buffer++; - - memcpy(buffer, frame, BUFFER_SIZE); - buffer+= (sizeof(exception_frame_t)/4); - - buffer[0] = r_epc; - buffer[1] = len; - buffer+=2; - memcpy(buffer, module_name, len+1); - - //pcsx2fioSendSifCmd(PKO_RPC_IOPEXCEP, excep_buffer, BUFFER_SIZE); -} - - -static void excep_handler(exception_type_t type, exception_frame_t *frame) -{ - excep_handler2(frame); // Don't know why this works, but keeps iop alive. -} - - -//////////////////////////////////////////////////////////////////////// -// Installs iop exception handlers for the 'usual' exceptions.. -void installExceptionHandlers(void) -{ - s32 i; - - for(i=1; i < 8; i++) - set_exception_handler(i, excep_handler); - - for(i=10; i < 13; i++) - set_exception_handler(i, excep_handler); - -} +/********************************************************************* + * Copyright (C) 2004 Lukasz Bruun (mail@lukasz.dk) + * This file is subject to the terms and conditions of the PS2Link License. + * See the file LICENSE in the main directory of this distribution for more + * details. + */ + +#include +#include "irx_imports.h" +#include "hostlink.h" + +#define BUFFER_SIZE sizeof(exception_frame_t)+4+4+128 + +u32 excep_buffer[BUFFER_SIZE/4] __attribute__ ((aligned(16))); + +extern unsigned int pcsx2fioSendSifCmd(unsigned int cmd, void *src, unsigned int len); // lazy fix :) +extern int excepscrdump; + +#define PKO_RPC_IOPEXCEP 12 + +// taken from smod by mrbrown, only use one function, didn't wanna include another irx. + +/* Module info entry. */ +typedef struct _smod_mod_info { + struct _smod_mod_info *next; + u8 *name; + u16 version; + u16 newflags; /* For modload shipped with games. */ + u16 id; + u16 flags; /* I believe this is where flags are kept for BIOS versions. */ + u32 entry; /* _start */ + u32 gp; + u32 text_start; + u32 text_size; + u32 data_size; + u32 bss_size; + u32 unused1; + u32 unused2; +} smod_mod_info_t; + + +smod_mod_info_t *smod_get_next_mod(smod_mod_info_t *cur_mod) +{ + /* If cur_mod is 0, return the head of the list (IOP address 0x800). */ + if (!cur_mod) { + return (smod_mod_info_t *)0x800; + } else { + if (!cur_mod->next) + return 0; + else + return cur_mod->next; + } + return 0; +} + +char* ExceptionGetModuleName(u32 epc, u32* r_epc) +{ + smod_mod_info_t *mod_info = 0; + + while((mod_info = smod_get_next_mod(mod_info)) != 0) + { + if((epc >= mod_info->text_start) && (epc <= (mod_info->text_start+mod_info->text_size))) + { + if(r_epc) + *r_epc = epc - mod_info->text_start; + + return mod_info->name; + } + } + + return 0; +} + +static void excep_handler2(exception_frame_t *frame) +{ + u32 r_epc; // relative epc + char *module_name; + u32 len; + u32* buffer = excep_buffer; + + module_name = ExceptionGetModuleName(frame->epc, &r_epc); + + len = strlen(module_name); + + + buffer[0] = 0x0d02beba; // reverse engineering.. + buffer++; + + memcpy(buffer, frame, BUFFER_SIZE); + buffer+= (sizeof(exception_frame_t)/4); + + buffer[0] = r_epc; + buffer[1] = len; + buffer+=2; + memcpy(buffer, module_name, len+1); + + //pcsx2fioSendSifCmd(PKO_RPC_IOPEXCEP, excep_buffer, BUFFER_SIZE); +} + + +static void excep_handler(exception_type_t type, exception_frame_t *frame) +{ + excep_handler2(frame); // Don't know why this works, but keeps iop alive. +} + + +//////////////////////////////////////////////////////////////////////// +// Installs iop exception handlers for the 'usual' exceptions.. +void installExceptionHandlers(void) +{ + s32 i; + + for(i=1; i < 8; i++) + set_exception_handler(i, excep_handler); + + for(i=10; i < 13; i++) + set_exception_handler(i, excep_handler); + +} diff --git a/pcsx2hostfs/iop/excepHandler.h b/pcsx2hostfs/iop/excepHandler.h index 603bff8a53..8a604882cc 100644 --- a/pcsx2hostfs/iop/excepHandler.h +++ b/pcsx2hostfs/iop/excepHandler.h @@ -1,13 +1,13 @@ -/********************************************************************* - * Copyright (C) 2003 Tord Lindstrom (pukko@home.se) - * This file is subject to the terms and conditions of the PS2Link License. - * See the file LICENSE in the main directory of this distribution for more - * details. - */ - -#ifndef _EXCEPTION_H_ -#define _EXCEPTION_H_ - -void installExceptionHandlers(void); - -#endif +/********************************************************************* + * Copyright (C) 2003 Tord Lindstrom (pukko@home.se) + * This file is subject to the terms and conditions of the PS2Link License. + * See the file LICENSE in the main directory of this distribution for more + * details. + */ + +#ifndef _EXCEPTION_H_ +#define _EXCEPTION_H_ + +void installExceptionHandlers(void); + +#endif diff --git a/pcsx2hostfs/iop/irx_imports.h b/pcsx2hostfs/iop/irx_imports.h index d40030c993..80cb381f88 100644 --- a/pcsx2hostfs/iop/irx_imports.h +++ b/pcsx2hostfs/iop/irx_imports.h @@ -1,28 +1,28 @@ -/* - * irx_imports.h - Defines all IRX imports. - * - * Copyright (c) 2003 Marcus R. Brown - * - * See the file LICENSE included with this distribution for licensing terms. - */ - -#ifndef IOP_IRX_IMPORTS_H -#define IOP_IRX_IMPORTS_H - -#include "irx.h" - - -/* Please keep these in alphabetical order! */ -#include "intrman.h" -#include "ioman.h" -#include "ioptrap.h" -#include "loadcore.h" -#include "modload.h" -#include "sifcmd.h" -#include "sifman.h" -#include "stdio.h" -#include "sysclib.h" -#include "thbase.h" -#include "thsemap.h" - -#endif /* IOP_IRX_IMPORTS_H */ +/* + * irx_imports.h - Defines all IRX imports. + * + * Copyright (c) 2003 Marcus R. Brown + * + * See the file LICENSE included with this distribution for licensing terms. + */ + +#ifndef IOP_IRX_IMPORTS_H +#define IOP_IRX_IMPORTS_H + +#include "irx.h" + + +/* Please keep these in alphabetical order! */ +#include "intrman.h" +#include "ioman.h" +#include "ioptrap.h" +#include "loadcore.h" +#include "modload.h" +#include "sifcmd.h" +#include "sifman.h" +#include "stdio.h" +#include "sysclib.h" +#include "thbase.h" +#include "thsemap.h" + +#endif /* IOP_IRX_IMPORTS_H */ diff --git a/pcsx2hostfs/iop/net_fio.c b/pcsx2hostfs/iop/net_fio.c index 9b9cb9665d..d863b989d6 100644 --- a/pcsx2hostfs/iop/net_fio.c +++ b/pcsx2hostfs/iop/net_fio.c @@ -1,230 +1,230 @@ -/********************************************************************* - * Copyright (C) 2003 Tord Lindstrom (pukko@home.se) - * Copyright (C) 2004 adresd (adresd_ps2dev@yahoo.com) - * This file is subject to the terms and conditions of the PS2Link License. - * See the file LICENSE in the main directory of this distribution for more - * details. - */ - -// Fu*k knows why net_fio & net_fsys are separated.. - -#include -#include -#include -#include -#include - -#include - -#include "net_fio.h" -#include "hostlink.h" - -#define PACKET_MAXSIZE 1024 -static int send_packet[PACKET_MAXSIZE] __attribute__((aligned(16))); - -#ifdef DEBUG -#define dbgprintf(args...) printf(args) -#else -#define dbgprintf(args...) do { } while(0) -#endif - -//---------------------------------------------------------------------- -// -#define PCSX2_FIO_BASE ((volatile unsigned int*)0x1d000800) -#define PCSX2_FIO_RDID (PCSX2_FIO_BASE + 0) // [f0] on read: returns the FIO magic number 'E2SP' -#define PCSX2_FIO_WCMD (PCSX2_FIO_BASE + 0) // [f0] on write: executes a command (usually "call function") -#define PCSX2_FIO_DLEN (PCSX2_FIO_BASE + 1) // [f4] write only: length of params data -#define PCSX2_FIO_DPTR (PCSX2_FIO_BASE + 2) // [f8] write only: mem address of params data -#define PCSX2_FIO_WFID (PCSX2_FIO_BASE + 3) // [fc] on write: function ID -#define PCSX2_FIO_RRET (PCSX2_FIO_BASE + 3) // [fc] on read: return value of the function - -#define PCSX2_FIO_CMD_CALL 1 - -int pcsx2fio_device_exists() -{ - return (*PCSX2_FIO_RDID == 'E2SP'); // PS2E -} - -int pcsx2fio_set_call(unsigned int func_id, void* params, int params_length) -{ - *PCSX2_FIO_DLEN = params_length; - *PCSX2_FIO_DPTR = (int)params; - *PCSX2_FIO_WFID = func_id; - *PCSX2_FIO_WCMD = PCSX2_FIO_CMD_CALL; - return *PCSX2_FIO_RRET; -} - -char *strcpy(char *dest, const char *src) -{ - while(*src) - { - *(dest++) = *(src++); - } - *(dest) = 0; - return dest; -} - -//---------------------------------------------------------------------- -// -void -pcsx2fio_close_fsys(void) -{ -} - -//---------------------------------------------------------------------- -// -int pcsx2fio_open_file(char *path, int flags) -{ - send_packet[0] = flags; - strcpy((char*)(send_packet+1),path); - - int length = strlen(path) + 4; - - int ret = pcsx2fio_set_call(PS2E_FIO_OPEN_CMD, send_packet, length); - - return ret; -} - - -//---------------------------------------------------------------------- -// -int pcsx2fio_close_file(int fd) -{ - send_packet[0] = fd; - - int length = 4; - - int ret = pcsx2fio_set_call(PS2E_FIO_CLOSE_CMD, send_packet, length); - - return ret; -} - -//---------------------------------------------------------------------- -// -int pcsx2fio_lseek_file(int fd, unsigned int offset, int whence) -{ - send_packet[0] = fd; - send_packet[1] = offset; - send_packet[2] = whence; - send_packet[3] = 0; - - int length = 12; - - int ret = pcsx2fio_set_call(PS2E_FIO_LSEEK_CMD, send_packet, length); - - return ret; -} - - -//---------------------------------------------------------------------- -// -int pcsx2fio_write_file(int fd, char *buf, int _length) -{ - send_packet[0] = fd; - send_packet[1] = (int)buf; - send_packet[2] = _length; - send_packet[3] = 0; - - int length = 12; - - int ret = pcsx2fio_set_call(PS2E_FIO_WRITE_CMD, send_packet, length); - - return ret; -} - -//---------------------------------------------------------------------- -// -int pcsx2fio_read_file(int fd, char *buf, int _length) -{ - send_packet[0] = fd; - send_packet[1] = (int)buf; - send_packet[2] = _length; - send_packet[3] = 0; - - int length = 12; - - int ret = pcsx2fio_set_call(PS2E_FIO_READ_CMD, send_packet, length); - - return ret; -} - -//---------------------------------------------------------------------- -// -int pcsx2fio_remove(char *name) -{ - strcpy((char*)(send_packet+0),name); - - int length = strlen(name); - - int ret = pcsx2fio_set_call(PS2E_FIO_REMOVE_CMD, send_packet, length); - - return ret; -} - -//---------------------------------------------------------------------- -// -int pcsx2fio_mkdir(char *name, int mode) -{ - send_packet[0] = mode; - strcpy((char*)(send_packet+1),name); - - int length = strlen(name) + 4; - - int ret = pcsx2fio_set_call(PS2E_FIO_MKDIR_CMD, send_packet, length); - - return ret; -} - -//---------------------------------------------------------------------- -// -int pcsx2fio_rmdir(char *name) -{ - strcpy((char*)(send_packet+0),name); - - int length = strlen(name); - - int ret = pcsx2fio_set_call(PS2E_FIO_RMDIR_CMD, send_packet, length); - - return ret; -} - -//---------------------------------------------------------------------- -// -int pcsx2fio_open_dir(char *path) -{ - strcpy((char*)(send_packet+0),path); - - int length = strlen(path); - - int ret = pcsx2fio_set_call(PS2E_FIO_OPENDIR_CMD, send_packet, length); - - return ret; -} - -//---------------------------------------------------------------------- -// -int pcsx2fio_read_dir(int fd, void *buf) -{ - send_packet[0] = fd; - send_packet[1] = (int)buf; - - int length = 8; - - int ret = pcsx2fio_set_call(PS2E_FIO_READDIR_CMD, send_packet, length); - - return ret; -} - - -//---------------------------------------------------------------------- -// -int pcsx2fio_close_dir(int fd) -{ - send_packet[0] = fd; - - int length = 4; - - int ret = pcsx2fio_set_call(PS2E_FIO_CLOSEDIR_CMD, send_packet, length); - - return ret; -} +/********************************************************************* + * Copyright (C) 2003 Tord Lindstrom (pukko@home.se) + * Copyright (C) 2004 adresd (adresd_ps2dev@yahoo.com) + * This file is subject to the terms and conditions of the PS2Link License. + * See the file LICENSE in the main directory of this distribution for more + * details. + */ + +// Fu*k knows why net_fio & net_fsys are separated.. + +#include +#include +#include +#include +#include + +#include + +#include "net_fio.h" +#include "hostlink.h" + +#define PACKET_MAXSIZE 1024 +static int send_packet[PACKET_MAXSIZE] __attribute__((aligned(16))); + +#ifdef DEBUG +#define dbgprintf(args...) printf(args) +#else +#define dbgprintf(args...) do { } while(0) +#endif + +//---------------------------------------------------------------------- +// +#define PCSX2_FIO_BASE ((volatile unsigned int*)0x1d000800) +#define PCSX2_FIO_RDID (PCSX2_FIO_BASE + 0) // [f0] on read: returns the FIO magic number 'E2SP' +#define PCSX2_FIO_WCMD (PCSX2_FIO_BASE + 0) // [f0] on write: executes a command (usually "call function") +#define PCSX2_FIO_DLEN (PCSX2_FIO_BASE + 1) // [f4] write only: length of params data +#define PCSX2_FIO_DPTR (PCSX2_FIO_BASE + 2) // [f8] write only: mem address of params data +#define PCSX2_FIO_WFID (PCSX2_FIO_BASE + 3) // [fc] on write: function ID +#define PCSX2_FIO_RRET (PCSX2_FIO_BASE + 3) // [fc] on read: return value of the function + +#define PCSX2_FIO_CMD_CALL 1 + +int pcsx2fio_device_exists() +{ + return (*PCSX2_FIO_RDID == 'E2SP'); // PS2E +} + +int pcsx2fio_set_call(unsigned int func_id, void* params, int params_length) +{ + *PCSX2_FIO_DLEN = params_length; + *PCSX2_FIO_DPTR = (int)params; + *PCSX2_FIO_WFID = func_id; + *PCSX2_FIO_WCMD = PCSX2_FIO_CMD_CALL; + return *PCSX2_FIO_RRET; +} + +char *strcpy(char *dest, const char *src) +{ + while(*src) + { + *(dest++) = *(src++); + } + *(dest) = 0; + return dest; +} + +//---------------------------------------------------------------------- +// +void +pcsx2fio_close_fsys(void) +{ +} + +//---------------------------------------------------------------------- +// +int pcsx2fio_open_file(char *path, int flags) +{ + send_packet[0] = flags; + strcpy((char*)(send_packet+1),path); + + int length = strlen(path) + 4; + + int ret = pcsx2fio_set_call(PS2E_FIO_OPEN_CMD, send_packet, length); + + return ret; +} + + +//---------------------------------------------------------------------- +// +int pcsx2fio_close_file(int fd) +{ + send_packet[0] = fd; + + int length = 4; + + int ret = pcsx2fio_set_call(PS2E_FIO_CLOSE_CMD, send_packet, length); + + return ret; +} + +//---------------------------------------------------------------------- +// +int pcsx2fio_lseek_file(int fd, unsigned int offset, int whence) +{ + send_packet[0] = fd; + send_packet[1] = offset; + send_packet[2] = whence; + send_packet[3] = 0; + + int length = 12; + + int ret = pcsx2fio_set_call(PS2E_FIO_LSEEK_CMD, send_packet, length); + + return ret; +} + + +//---------------------------------------------------------------------- +// +int pcsx2fio_write_file(int fd, char *buf, int _length) +{ + send_packet[0] = fd; + send_packet[1] = (int)buf; + send_packet[2] = _length; + send_packet[3] = 0; + + int length = 12; + + int ret = pcsx2fio_set_call(PS2E_FIO_WRITE_CMD, send_packet, length); + + return ret; +} + +//---------------------------------------------------------------------- +// +int pcsx2fio_read_file(int fd, char *buf, int _length) +{ + send_packet[0] = fd; + send_packet[1] = (int)buf; + send_packet[2] = _length; + send_packet[3] = 0; + + int length = 12; + + int ret = pcsx2fio_set_call(PS2E_FIO_READ_CMD, send_packet, length); + + return ret; +} + +//---------------------------------------------------------------------- +// +int pcsx2fio_remove(char *name) +{ + strcpy((char*)(send_packet+0),name); + + int length = strlen(name); + + int ret = pcsx2fio_set_call(PS2E_FIO_REMOVE_CMD, send_packet, length); + + return ret; +} + +//---------------------------------------------------------------------- +// +int pcsx2fio_mkdir(char *name, int mode) +{ + send_packet[0] = mode; + strcpy((char*)(send_packet+1),name); + + int length = strlen(name) + 4; + + int ret = pcsx2fio_set_call(PS2E_FIO_MKDIR_CMD, send_packet, length); + + return ret; +} + +//---------------------------------------------------------------------- +// +int pcsx2fio_rmdir(char *name) +{ + strcpy((char*)(send_packet+0),name); + + int length = strlen(name); + + int ret = pcsx2fio_set_call(PS2E_FIO_RMDIR_CMD, send_packet, length); + + return ret; +} + +//---------------------------------------------------------------------- +// +int pcsx2fio_open_dir(char *path) +{ + strcpy((char*)(send_packet+0),path); + + int length = strlen(path); + + int ret = pcsx2fio_set_call(PS2E_FIO_OPENDIR_CMD, send_packet, length); + + return ret; +} + +//---------------------------------------------------------------------- +// +int pcsx2fio_read_dir(int fd, void *buf) +{ + send_packet[0] = fd; + send_packet[1] = (int)buf; + + int length = 8; + + int ret = pcsx2fio_set_call(PS2E_FIO_READDIR_CMD, send_packet, length); + + return ret; +} + + +//---------------------------------------------------------------------- +// +int pcsx2fio_close_dir(int fd) +{ + send_packet[0] = fd; + + int length = 4; + + int ret = pcsx2fio_set_call(PS2E_FIO_CLOSEDIR_CMD, send_packet, length); + + return ret; +} diff --git a/pcsx2hostfs/iop/net_fio.h b/pcsx2hostfs/iop/net_fio.h index d136530ad9..49c08e4dc3 100644 --- a/pcsx2hostfs/iop/net_fio.h +++ b/pcsx2hostfs/iop/net_fio.h @@ -1,33 +1,33 @@ -/********************************************************************* - * Copyright (C) 2003 Tord Lindstrom (pukko@home.se) - * Copyright (C) 2004 adresd (adresd_ps2dev@yahoo.com) - * This file is subject to the terms and conditions of the PS2Link License. - * See the file LICENSE in the main directory of this distribution for more - * details. - */ - -#ifndef _NETFIO_H_ -#define _NETFIO_H_ - -int pcsx2fio_device_exists(); - -int pcsx2fio_set_call(unsigned int func_id, void* params, int params_length); - -int pcsx2fio_file_serv(void *arg); -int pcsx2fio_recv_bytes(int fd, char *buf, int bytes); -int pcsx2fio_accept_pkt(int fd, char *buf, int len, int pkt_type); -int pcsx2fio_open_file(char *path, int flags); -int pcsx2fio_close_file(int fd); -int pcsx2fio_read_file(int fd, char *buf, int length); -int pcsx2fio_write_file(int fd, char *buf, int length); -int pcsx2fio_lseek_file(int fd, unsigned int offset, int whence); -void pcsx2fio_close_socket(void); -void pcsx2fio_close_fsys(void); -int pcsx2fio_remove(char *name); -int pcsx2fio_mkdir(char *name, int mode); -int pcsx2fio_rmdir(char *name); -int pcsx2fio_open_dir(char *path); -int pcsx2fio_read_dir(int fd, void *buf); -int pcsx2fio_close_dir(int fd); - -#endif +/********************************************************************* + * Copyright (C) 2003 Tord Lindstrom (pukko@home.se) + * Copyright (C) 2004 adresd (adresd_ps2dev@yahoo.com) + * This file is subject to the terms and conditions of the PS2Link License. + * See the file LICENSE in the main directory of this distribution for more + * details. + */ + +#ifndef _NETFIO_H_ +#define _NETFIO_H_ + +int pcsx2fio_device_exists(); + +int pcsx2fio_set_call(unsigned int func_id, void* params, int params_length); + +int pcsx2fio_file_serv(void *arg); +int pcsx2fio_recv_bytes(int fd, char *buf, int bytes); +int pcsx2fio_accept_pkt(int fd, char *buf, int len, int pkt_type); +int pcsx2fio_open_file(char *path, int flags); +int pcsx2fio_close_file(int fd); +int pcsx2fio_read_file(int fd, char *buf, int length); +int pcsx2fio_write_file(int fd, char *buf, int length); +int pcsx2fio_lseek_file(int fd, unsigned int offset, int whence); +void pcsx2fio_close_socket(void); +void pcsx2fio_close_fsys(void); +int pcsx2fio_remove(char *name); +int pcsx2fio_mkdir(char *name, int mode); +int pcsx2fio_rmdir(char *name); +int pcsx2fio_open_dir(char *path); +int pcsx2fio_read_dir(int fd, void *buf); +int pcsx2fio_close_dir(int fd); + +#endif diff --git a/pcsx2hostfs/iop/net_fsys.c b/pcsx2hostfs/iop/net_fsys.c index 6941838ee7..bb4267b95a 100644 --- a/pcsx2hostfs/iop/net_fsys.c +++ b/pcsx2hostfs/iop/net_fsys.c @@ -1,306 +1,306 @@ -/********************************************************************* - * Copyright (C) 2003 Tord Lindstrom (pukko@home.se) - * Copyright (C) 2004 adresd (adresd_ps2dev@yahoo.com) - * This file is subject to the terms and conditions of the PS2Link License. - * See the file LICENSE in the main directory of this distribution for more - * details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "net_fio.h" - -#ifdef DEBUG -#define dbgprintf(args...) printf(args) -#else -#define dbgprintf(args...) do { } while(0) -#endif - -static char fsname[] = "host"; - -//////////////////////////////////////////////////////////////////////// -//static iop_device_t fsys_driver; - -/* File desc struct is probably larger than this, but we only - * need to access the word @ offset 0x0C (in which we put our identifier) - */ -struct filedesc_info -{ - int unkn0; - int unkn4; - int device_id; // the X in hostX - int own_fd; -}; - -//////////////////////////////////////////////////////////////////////// -/* We need(?) to protect the net access, so the server doesn't get - * confused if two processes calls a fsys func at the same time - */ -static int fsys_sema; -static int fsys_pid = 0; -//static iop_device_t fsys_driver; - -//////////////////////////////////////////////////////////////////////// -static int dummy5() -{ - printf("dummy function called\n"); - return -5; -} - -//////////////////////////////////////////////////////////////////////// -static void fsysInit(iop_device_t *driver) -{ - -} - -//////////////////////////////////////////////////////////////////////// -static int fsysDestroy(void) -{ - WaitSema(fsys_sema); - pcsx2fio_close_fsys(); - // ExitDeleteThread(fsys_pid); - SignalSema(fsys_sema); - DeleteSema(fsys_sema); - return 0; -} - - -//////////////////////////////////////////////////////////////////////// -static int fsysOpen( int fd, char *name, int mode) -{ - struct filedesc_info *fd_info; - int fsys_fd; - - dbgprintf("fsysOpen..\n"); - dbgprintf(" fd: %x, name: %s, mode: %d\n\n", fd, name, mode); - - fd_info = (struct filedesc_info *)fd; - - WaitSema(fsys_sema); - fsys_fd = pcsx2fio_open_file(name, mode); - SignalSema(fsys_sema); - fd_info->own_fd = fsys_fd; - - return fsys_fd; -} - - - -//////////////////////////////////////////////////////////////////////// -static int fsysClose( int fd) -{ - struct filedesc_info *fd_info; - int ret; - - dbgprintf("fsys_close..\n" - " fd: %x\n\n", fd); - - fd_info = (struct filedesc_info *)fd; - WaitSema(fsys_sema); - ret = pcsx2fio_close_file(fd_info->own_fd); - SignalSema(fsys_sema); - - return ret; -} - - - -//////////////////////////////////////////////////////////////////////// -static int fsysRead( int fd, char *buf, int size) -{ - struct filedesc_info *fd_info; - int ret; - - fd_info = (struct filedesc_info *)fd; - - dbgprintf("fsysRead..." - " fd: %x\n" - " bf: %x\n" - " sz: %d\n" - " ow: %d\n\n", fd, (int)buf, size, fd_info->own_fd); - - WaitSema(fsys_sema); - ret = pcsx2fio_read_file(fd_info->own_fd, buf, size); - SignalSema(fsys_sema); - - return ret; -} - - - - -//////////////////////////////////////////////////////////////////////// -static int fsysWrite( int fd, char *buf, int size) -{ - struct filedesc_info *fd_info; - int ret; - - dbgprintf("fsysWrite..." - " fd: %x\n", fd); - - fd_info = (struct filedesc_info *)fd; - WaitSema(fsys_sema); - ret = pcsx2fio_write_file(fd_info->own_fd, buf, size); - SignalSema(fsys_sema); - return ret; -} - - - -//////////////////////////////////////////////////////////////////////// -static int fsysLseek( int fd, unsigned int offset, int whence) -{ - struct filedesc_info *fd_info; - int ret; - - dbgprintf("fsysLseek..\n" - " fd: %x\n" - " of: %x\n" - " wh: %x\n\n", fd, offset, whence); - - fd_info = (struct filedesc_info *)fd; - WaitSema(fsys_sema); - ret = pcsx2fio_lseek_file(fd_info->own_fd, offset, whence); - SignalSema(fsys_sema); - return ret; -} - -//////////////////////////////////////////////////////////////////////// -static int fsysRemove(iop_file_t* file, char *name) -{ - int ret; - dbgprintf("fsysRemove..\n"); - dbgprintf(" name: %s\n\n", name); - - WaitSema(fsys_sema); - ret = pcsx2fio_remove(name); - SignalSema(fsys_sema); - - return ret; -} - -//////////////////////////////////////////////////////////////////////// -static int fsysMkdir(iop_file_t* file, char *name, int mode) -{ - int ret; - dbgprintf("fsysMkdir..\n"); - dbgprintf(" name: '%s'\n\n", name); - - WaitSema(fsys_sema); - ret = pcsx2fio_mkdir(name, mode); - SignalSema(fsys_sema); - - return ret; -} - -//////////////////////////////////////////////////////////////////////// -static int fsysRmdir(iop_file_t* file, char *name) -{ - int ret; - dbgprintf("fsysRmdir..\n"); - dbgprintf(" name: %s\n\n", name); - - WaitSema(fsys_sema); - ret = pcsx2fio_rmdir(name); - SignalSema(fsys_sema); - - return ret; -} - - -//////////////////////////////////////////////////////////////////////// -static int fsysDopen(int fd, char *name) -{ - struct filedesc_info *fd_info; - int fsys_fd; - - dbgprintf("fsysDopen..\n"); - dbgprintf(" fd: %x, name: %s\n\n", fd, name); - - fd_info = (struct filedesc_info *)fd; - - WaitSema(fsys_sema); - fsys_fd = pcsx2fio_open_dir(name); - SignalSema(fsys_sema); - fd_info->own_fd = fsys_fd; - - return fsys_fd; -} - -//////////////////////////////////////////////////////////////////////// -static int fsysDread(int fd, void *buf) -{ - struct filedesc_info *fd_info; - int ret; - - fd_info = (struct filedesc_info *)fd; - - dbgprintf("fsysDread..." - " fd: %x\n" - " bf: %x\n" - " ow: %d\n\n", fd, (int)buf, fd_info->own_fd); - - WaitSema(fsys_sema); - ret = pcsx2fio_read_dir(fd_info->own_fd, buf); - SignalSema(fsys_sema); - - return ret; - -} - -//////////////////////////////////////////////////////////////////////// -static int fsysDclose(int fd) -{ - struct filedesc_info *fd_info; - int ret; - - dbgprintf("fsys_dclose..\n" - " fd: %x\n\n", fd); - - fd_info = (struct filedesc_info *)fd; - WaitSema(fsys_sema); - ret = pcsx2fio_close_dir(fd_info->own_fd); - SignalSema(fsys_sema); - - return ret; -} - -iop_device_ops_t fsys_functarray = { (void *)fsysInit, (void *)fsysDestroy, (void *)dummy5, - (void *)fsysOpen, (void *)fsysClose, (void *)fsysRead, - (void *)fsysWrite, (void *)fsysLseek, (void *)dummy5, - (void *)fsysRemove, (void *)fsysMkdir, (void *)fsysRmdir, - (void *)fsysDopen, (void *)fsysDclose, (void *)fsysDread, - (void *)dummy5, (void *)dummy5 }; - -iop_device_t fsys_driver = { fsname, 16, 1, "fsys driver", - &fsys_functarray }; -//////////////////////////////////////////////////////////////////////// -// Entry point for mounting the file system -int fsysMount(void) -{ - iop_sema_t sema_info; - - sema_info.attr = 1; - sema_info.option = 0; - sema_info.initial = 1; - sema_info.max = 1; - fsys_sema = CreateSema(&sema_info); - - DelDrv(fsname); - AddDrv(&fsys_driver); - - return 0; -} - -int fsysUnmount(void) -{ - DelDrv(fsname); - return 0; -} +/********************************************************************* + * Copyright (C) 2003 Tord Lindstrom (pukko@home.se) + * Copyright (C) 2004 adresd (adresd_ps2dev@yahoo.com) + * This file is subject to the terms and conditions of the PS2Link License. + * See the file LICENSE in the main directory of this distribution for more + * details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "net_fio.h" + +#ifdef DEBUG +#define dbgprintf(args...) printf(args) +#else +#define dbgprintf(args...) do { } while(0) +#endif + +static char fsname[] = "host"; + +//////////////////////////////////////////////////////////////////////// +//static iop_device_t fsys_driver; + +/* File desc struct is probably larger than this, but we only + * need to access the word @ offset 0x0C (in which we put our identifier) + */ +struct filedesc_info +{ + int unkn0; + int unkn4; + int device_id; // the X in hostX + int own_fd; +}; + +//////////////////////////////////////////////////////////////////////// +/* We need(?) to protect the net access, so the server doesn't get + * confused if two processes calls a fsys func at the same time + */ +static int fsys_sema; +static int fsys_pid = 0; +//static iop_device_t fsys_driver; + +//////////////////////////////////////////////////////////////////////// +static int dummy5() +{ + printf("dummy function called\n"); + return -5; +} + +//////////////////////////////////////////////////////////////////////// +static void fsysInit(iop_device_t *driver) +{ + +} + +//////////////////////////////////////////////////////////////////////// +static int fsysDestroy(void) +{ + WaitSema(fsys_sema); + pcsx2fio_close_fsys(); + // ExitDeleteThread(fsys_pid); + SignalSema(fsys_sema); + DeleteSema(fsys_sema); + return 0; +} + + +//////////////////////////////////////////////////////////////////////// +static int fsysOpen( int fd, char *name, int mode) +{ + struct filedesc_info *fd_info; + int fsys_fd; + + dbgprintf("fsysOpen..\n"); + dbgprintf(" fd: %x, name: %s, mode: %d\n\n", fd, name, mode); + + fd_info = (struct filedesc_info *)fd; + + WaitSema(fsys_sema); + fsys_fd = pcsx2fio_open_file(name, mode); + SignalSema(fsys_sema); + fd_info->own_fd = fsys_fd; + + return fsys_fd; +} + + + +//////////////////////////////////////////////////////////////////////// +static int fsysClose( int fd) +{ + struct filedesc_info *fd_info; + int ret; + + dbgprintf("fsys_close..\n" + " fd: %x\n\n", fd); + + fd_info = (struct filedesc_info *)fd; + WaitSema(fsys_sema); + ret = pcsx2fio_close_file(fd_info->own_fd); + SignalSema(fsys_sema); + + return ret; +} + + + +//////////////////////////////////////////////////////////////////////// +static int fsysRead( int fd, char *buf, int size) +{ + struct filedesc_info *fd_info; + int ret; + + fd_info = (struct filedesc_info *)fd; + + dbgprintf("fsysRead..." + " fd: %x\n" + " bf: %x\n" + " sz: %d\n" + " ow: %d\n\n", fd, (int)buf, size, fd_info->own_fd); + + WaitSema(fsys_sema); + ret = pcsx2fio_read_file(fd_info->own_fd, buf, size); + SignalSema(fsys_sema); + + return ret; +} + + + + +//////////////////////////////////////////////////////////////////////// +static int fsysWrite( int fd, char *buf, int size) +{ + struct filedesc_info *fd_info; + int ret; + + dbgprintf("fsysWrite..." + " fd: %x\n", fd); + + fd_info = (struct filedesc_info *)fd; + WaitSema(fsys_sema); + ret = pcsx2fio_write_file(fd_info->own_fd, buf, size); + SignalSema(fsys_sema); + return ret; +} + + + +//////////////////////////////////////////////////////////////////////// +static int fsysLseek( int fd, unsigned int offset, int whence) +{ + struct filedesc_info *fd_info; + int ret; + + dbgprintf("fsysLseek..\n" + " fd: %x\n" + " of: %x\n" + " wh: %x\n\n", fd, offset, whence); + + fd_info = (struct filedesc_info *)fd; + WaitSema(fsys_sema); + ret = pcsx2fio_lseek_file(fd_info->own_fd, offset, whence); + SignalSema(fsys_sema); + return ret; +} + +//////////////////////////////////////////////////////////////////////// +static int fsysRemove(iop_file_t* file, char *name) +{ + int ret; + dbgprintf("fsysRemove..\n"); + dbgprintf(" name: %s\n\n", name); + + WaitSema(fsys_sema); + ret = pcsx2fio_remove(name); + SignalSema(fsys_sema); + + return ret; +} + +//////////////////////////////////////////////////////////////////////// +static int fsysMkdir(iop_file_t* file, char *name, int mode) +{ + int ret; + dbgprintf("fsysMkdir..\n"); + dbgprintf(" name: '%s'\n\n", name); + + WaitSema(fsys_sema); + ret = pcsx2fio_mkdir(name, mode); + SignalSema(fsys_sema); + + return ret; +} + +//////////////////////////////////////////////////////////////////////// +static int fsysRmdir(iop_file_t* file, char *name) +{ + int ret; + dbgprintf("fsysRmdir..\n"); + dbgprintf(" name: %s\n\n", name); + + WaitSema(fsys_sema); + ret = pcsx2fio_rmdir(name); + SignalSema(fsys_sema); + + return ret; +} + + +//////////////////////////////////////////////////////////////////////// +static int fsysDopen(int fd, char *name) +{ + struct filedesc_info *fd_info; + int fsys_fd; + + dbgprintf("fsysDopen..\n"); + dbgprintf(" fd: %x, name: %s\n\n", fd, name); + + fd_info = (struct filedesc_info *)fd; + + WaitSema(fsys_sema); + fsys_fd = pcsx2fio_open_dir(name); + SignalSema(fsys_sema); + fd_info->own_fd = fsys_fd; + + return fsys_fd; +} + +//////////////////////////////////////////////////////////////////////// +static int fsysDread(int fd, void *buf) +{ + struct filedesc_info *fd_info; + int ret; + + fd_info = (struct filedesc_info *)fd; + + dbgprintf("fsysDread..." + " fd: %x\n" + " bf: %x\n" + " ow: %d\n\n", fd, (int)buf, fd_info->own_fd); + + WaitSema(fsys_sema); + ret = pcsx2fio_read_dir(fd_info->own_fd, buf); + SignalSema(fsys_sema); + + return ret; + +} + +//////////////////////////////////////////////////////////////////////// +static int fsysDclose(int fd) +{ + struct filedesc_info *fd_info; + int ret; + + dbgprintf("fsys_dclose..\n" + " fd: %x\n\n", fd); + + fd_info = (struct filedesc_info *)fd; + WaitSema(fsys_sema); + ret = pcsx2fio_close_dir(fd_info->own_fd); + SignalSema(fsys_sema); + + return ret; +} + +iop_device_ops_t fsys_functarray = { (void *)fsysInit, (void *)fsysDestroy, (void *)dummy5, + (void *)fsysOpen, (void *)fsysClose, (void *)fsysRead, + (void *)fsysWrite, (void *)fsysLseek, (void *)dummy5, + (void *)fsysRemove, (void *)fsysMkdir, (void *)fsysRmdir, + (void *)fsysDopen, (void *)fsysDclose, (void *)fsysDread, + (void *)dummy5, (void *)dummy5 }; + +iop_device_t fsys_driver = { fsname, 16, 1, "fsys driver", + &fsys_functarray }; +//////////////////////////////////////////////////////////////////////// +// Entry point for mounting the file system +int fsysMount(void) +{ + iop_sema_t sema_info; + + sema_info.attr = 1; + sema_info.option = 0; + sema_info.initial = 1; + sema_info.max = 1; + fsys_sema = CreateSema(&sema_info); + + DelDrv(fsname); + AddDrv(&fsys_driver); + + return 0; +} + +int fsysUnmount(void) +{ + DelDrv(fsname); + return 0; +} diff --git a/pcsx2hostfs/iop/nprintf.c b/pcsx2hostfs/iop/nprintf.c index 449c15e898..989e9937ef 100644 --- a/pcsx2hostfs/iop/nprintf.c +++ b/pcsx2hostfs/iop/nprintf.c @@ -1,82 +1,82 @@ -/********************************************************************* - * Copyright (C) 2003 Tord Lindstrom (pukko@home.se) - * This file is subject to the terms and conditions of the PS2Link License. - * See the file LICENSE in the main directory of this distribution for more - * details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -//////////////////////////////////////////////////////////////////////// -#define NPM_PUTS 0x01 -#define RPC_NPM_USER 0x014d704e - -//////////////////////////////////////////////////////////////////////// -static void * -naplinkRpcHandler(int cmd, void *buffer, int size) -{ - // Only supports npmPrintf of course - switch(cmd) { - case NPM_PUTS: - printf(buffer); - break; - default: - printf("unknown npm rpc call\n"); - } - - return buffer; -} - -//////////////////////////////////////////////////////////////////////// -static SifRpcServerData_t server __attribute((aligned(16))); -static SifRpcDataQueue_t queue __attribute((aligned(16))); -static unsigned char rpc_buffer[512] __attribute((aligned(16))); - -static void -napThread(void *arg) -{ - int pid; - - SifInitRpc(0); - pid = GetThreadId(); - SifSetRpcQueue(&queue, pid); - SifRegisterRpc(&server, RPC_NPM_USER, naplinkRpcHandler, - rpc_buffer, 0, 0, &queue); - SifRpcLoop(&queue); // Never exits - ExitDeleteThread(); -} -//////////////////////////////////////////////////////////////////////// -int -naplinkRpcInit(void) -{ - struct _iop_thread th_attr; - int ret; - int pid; - - th_attr.attr = 0x02000000; - th_attr.option = 0; - th_attr.thread = napThread; - th_attr.stacksize = 0x800; - th_attr.priority = 79; - - pid = CreateThread(&th_attr); - if (pid < 0) { - printf("IOP: napRpc createThread failed %d\n", pid); - return -1; - } - - ret = StartThread(pid, 0); - if (ret < 0) { - printf("IOP: napRpc startThread failed %d\n", ret); - DeleteThread(pid); - return -1; - } - return 0; -} +/********************************************************************* + * Copyright (C) 2003 Tord Lindstrom (pukko@home.se) + * This file is subject to the terms and conditions of the PS2Link License. + * See the file LICENSE in the main directory of this distribution for more + * details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////////// +#define NPM_PUTS 0x01 +#define RPC_NPM_USER 0x014d704e + +//////////////////////////////////////////////////////////////////////// +static void * +naplinkRpcHandler(int cmd, void *buffer, int size) +{ + // Only supports npmPrintf of course + switch(cmd) { + case NPM_PUTS: + printf(buffer); + break; + default: + printf("unknown npm rpc call\n"); + } + + return buffer; +} + +//////////////////////////////////////////////////////////////////////// +static SifRpcServerData_t server __attribute((aligned(16))); +static SifRpcDataQueue_t queue __attribute((aligned(16))); +static unsigned char rpc_buffer[512] __attribute((aligned(16))); + +static void +napThread(void *arg) +{ + int pid; + + SifInitRpc(0); + pid = GetThreadId(); + SifSetRpcQueue(&queue, pid); + SifRegisterRpc(&server, RPC_NPM_USER, naplinkRpcHandler, + rpc_buffer, 0, 0, &queue); + SifRpcLoop(&queue); // Never exits + ExitDeleteThread(); +} +//////////////////////////////////////////////////////////////////////// +int +naplinkRpcInit(void) +{ + struct _iop_thread th_attr; + int ret; + int pid; + + th_attr.attr = 0x02000000; + th_attr.option = 0; + th_attr.thread = napThread; + th_attr.stacksize = 0x800; + th_attr.priority = 79; + + pid = CreateThread(&th_attr); + if (pid < 0) { + printf("IOP: napRpc createThread failed %d\n", pid); + return -1; + } + + ret = StartThread(pid, 0); + if (ret < 0) { + printf("IOP: napRpc startThread failed %d\n", ret); + DeleteThread(pid); + return -1; + } + return 0; +} diff --git a/pcsx2hostfs/iop/ps2link.c b/pcsx2hostfs/iop/ps2link.c index f556e3663a..c530dab709 100644 --- a/pcsx2hostfs/iop/ps2link.c +++ b/pcsx2hostfs/iop/ps2link.c @@ -1,55 +1,55 @@ -/********************************************************************* - * Copyright (C) 2003 Tord Lindstrom (pukko@home.se) - * This file is subject to the terms and conditions of the PS2Link License. - * See the file LICENSE in the main directory of this distribution for more - * details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include "excepHandler.h" - -// Entry points -extern int fsysMount(void); -extern int cmdHandlerInit(void); -extern int ttyMount(void); -extern int naplinkRpcInit(void); -//////////////////////////////////////////////////////////////////////// -// main -// start threads & init rpc & filesys -int -_start( int argc, char **argv) -{ - ttyWrite(NULL,"TEST",4); - - FlushDcache(); - CpuEnableIntr(0); - - SifInitRpc(0); - - pcsx2fio_device_exists(); - - if ((argc < 2) || (strncmp(argv[1], "-notty", 6))) { - ttyMount(); - // Oh well.. There's a bug in either smapif or lwip's etharp - // that thrashes udp msgs which are queued while waiting for arp - // request - // alas, this msg will probably not be displayed - printf("tty mounted\n"); - } - - fsysMount(); - printf("host: mounted\n"); - naplinkRpcInit(); - printf("Naplink thread started\n"); - - installExceptionHandlers(); - - return 0; -} - +/********************************************************************* + * Copyright (C) 2003 Tord Lindstrom (pukko@home.se) + * This file is subject to the terms and conditions of the PS2Link License. + * See the file LICENSE in the main directory of this distribution for more + * details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "excepHandler.h" + +// Entry points +extern int fsysMount(void); +extern int cmdHandlerInit(void); +extern int ttyMount(void); +extern int naplinkRpcInit(void); +//////////////////////////////////////////////////////////////////////// +// main +// start threads & init rpc & filesys +int +_start( int argc, char **argv) +{ + ttyWrite(NULL,"TEST",4); + + FlushDcache(); + CpuEnableIntr(0); + + SifInitRpc(0); + + pcsx2fio_device_exists(); + + if ((argc < 2) || (strncmp(argv[1], "-notty", 6))) { + ttyMount(); + // Oh well.. There's a bug in either smapif or lwip's etharp + // that thrashes udp msgs which are queued while waiting for arp + // request + // alas, this msg will probably not be displayed + printf("tty mounted\n"); + } + + fsysMount(); + printf("host: mounted\n"); + naplinkRpcInit(); + printf("Naplink thread started\n"); + + installExceptionHandlers(); + + return 0; +} + diff --git a/pcsx2hostfs/iop/tty.c b/pcsx2hostfs/iop/tty.c index a7364d84c2..1c3f0535a1 100644 --- a/pcsx2hostfs/iop/tty.c +++ b/pcsx2hostfs/iop/tty.c @@ -1,105 +1,105 @@ -/********************************************************************* - * Copyright (C) 2003 Tord Lindstrom (pukko@home.se) - * This file is subject to the terms and conditions of the PS2Link License. - * See the file LICENSE in the main directory of this distribution for more - * details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "net_fio.h" -#include "hostlink.h" - -//////////////////////////////////////////////////////////////////////// - -static int tty_socket = 0; -static int tty_sema = -1; - -static char ttyname[] = "tty"; - -//////////////////////////////////////////////////////////////////////// -static int dummy() -{ - return -5; -} - -//////////////////////////////////////////////////////////////////////// -static int dummy0() -{ - return 0; -} - -//////////////////////////////////////////////////////////////////////// -static int ttyInit(iop_device_t *driver) -{ - iop_sema_t sema_info; - - sema_info.attr = 0; - sema_info.initial = 1; /* Unlocked. */ - sema_info.max = 1; - if ((tty_sema = CreateSema(&sema_info)) < 0) - return -1; - - - if(!pcsx2fio_device_exists()) - return -1; - - return 1; -} - -//////////////////////////////////////////////////////////////////////// -static int ttyOpen( int fd, char *name, int mode) -{ - return 1; -} - -//////////////////////////////////////////////////////////////////////// -static int ttyClose( int fd) -{ - return 1; -} - - -//////////////////////////////////////////////////////////////////////// -int ttyWrite(iop_file_t *file, char *buf, int size) -{ - int res; - - WaitSema(tty_sema); - - res = pcsx2fio_set_call(PS2E_FIO_PRINTF_CMD,buf,size); - - SignalSema(tty_sema); - return res; -} - -iop_device_ops_t tty_functarray = { ttyInit, dummy0, (void *)dummy, - (void *)ttyOpen, (void *)ttyClose, (void *)dummy, - (void *)ttyWrite, (void *)dummy, (void *)dummy, - (void *)dummy, (void *)dummy, (void *)dummy, - (void *)dummy, (void *)dummy, (void *)dummy, - (void *)dummy, (void *)dummy }; - -iop_device_t tty_driver = { ttyname, 3, 1, "Fast TTY for pcsx2", - &tty_functarray }; - -//////////////////////////////////////////////////////////////////////// -// Entry point for mounting the file system -int ttyMount(void) -{ - close(0); - close(1); - DelDrv(ttyname); - AddDrv(&tty_driver); - if(open("tty00:", O_RDONLY) != 0) while(1); - if(open("tty00:", O_WRONLY) != 1) while(1); - - return 0; -} +/********************************************************************* + * Copyright (C) 2003 Tord Lindstrom (pukko@home.se) + * This file is subject to the terms and conditions of the PS2Link License. + * See the file LICENSE in the main directory of this distribution for more + * details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "net_fio.h" +#include "hostlink.h" + +//////////////////////////////////////////////////////////////////////// + +static int tty_socket = 0; +static int tty_sema = -1; + +static char ttyname[] = "tty"; + +//////////////////////////////////////////////////////////////////////// +static int dummy() +{ + return -5; +} + +//////////////////////////////////////////////////////////////////////// +static int dummy0() +{ + return 0; +} + +//////////////////////////////////////////////////////////////////////// +static int ttyInit(iop_device_t *driver) +{ + iop_sema_t sema_info; + + sema_info.attr = 0; + sema_info.initial = 1; /* Unlocked. */ + sema_info.max = 1; + if ((tty_sema = CreateSema(&sema_info)) < 0) + return -1; + + + if(!pcsx2fio_device_exists()) + return -1; + + return 1; +} + +//////////////////////////////////////////////////////////////////////// +static int ttyOpen( int fd, char *name, int mode) +{ + return 1; +} + +//////////////////////////////////////////////////////////////////////// +static int ttyClose( int fd) +{ + return 1; +} + + +//////////////////////////////////////////////////////////////////////// +int ttyWrite(iop_file_t *file, char *buf, int size) +{ + int res; + + WaitSema(tty_sema); + + res = pcsx2fio_set_call(PS2E_FIO_PRINTF_CMD,buf,size); + + SignalSema(tty_sema); + return res; +} + +iop_device_ops_t tty_functarray = { ttyInit, dummy0, (void *)dummy, + (void *)ttyOpen, (void *)ttyClose, (void *)dummy, + (void *)ttyWrite, (void *)dummy, (void *)dummy, + (void *)dummy, (void *)dummy, (void *)dummy, + (void *)dummy, (void *)dummy, (void *)dummy, + (void *)dummy, (void *)dummy }; + +iop_device_t tty_driver = { ttyname, 3, 1, "Fast TTY for pcsx2", + &tty_functarray }; + +//////////////////////////////////////////////////////////////////////// +// Entry point for mounting the file system +int ttyMount(void) +{ + close(0); + close(1); + DelDrv(ttyname); + AddDrv(&tty_driver); + if(open("tty00:", O_RDONLY) != 0) while(1); + if(open("tty00:", O_WRONLY) != 1) while(1); + + return 0; +} diff --git a/plugins/CDVDiso/src/Linux/Config.cpp b/plugins/CDVDiso/src/Linux/Config.cpp old mode 100755 new mode 100644 diff --git a/plugins/CDVDiso/src/Linux/Linux.cpp b/plugins/CDVDiso/src/Linux/Linux.cpp old mode 100755 new mode 100644 diff --git a/plugins/CDVDiso/src/Windows/Config.cpp b/plugins/CDVDiso/src/Windows/Config.cpp old mode 100755 new mode 100644 diff --git a/plugins/CDVDiso/src/Windows/Win32.cpp b/plugins/CDVDiso/src/Windows/Win32.cpp old mode 100755 new mode 100644 diff --git a/plugins/CDVDiso/src/libiso.cpp b/plugins/CDVDiso/src/libiso.cpp old mode 100755 new mode 100644 diff --git a/plugins/CDVDiso/src/mkiso/mkiso.cpp b/plugins/CDVDiso/src/mkiso/mkiso.cpp old mode 100755 new mode 100644 diff --git a/plugins/CDVDlinuz/Src/Linux/CD.c b/plugins/CDVDlinuz/Src/Linux/CD.c index c9918f29af..1e66cc8ab2 100644 --- a/plugins/CDVDlinuz/Src/Linux/CD.c +++ b/plugins/CDVDlinuz/Src/Linux/CD.c @@ -1,734 +1,734 @@ -/* CD.c - - * Copyright (C) 2002-2005 PCSX2 Team - - * - - * This program is free software; you can redistribute it and/or modify - - * it under the terms of the GNU General Public License as published by - - * the Free Software Foundation; either version 2 of the License, or - - * (at your option) any later version. - - * - - * This program 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 this program; if not, write to the Free Software - - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - * - - * PCSX2 members can be contacted through their website at www.pcsx2.net. - - */ - - - -#include // errno - -#include // NULL - -#include // strerror() - -#include // open() - -#include // ioctl() - -#include // open() - -#include // lseek(), open() - -#include // close(), lseek(), (sleep()) - - - -#include // CD/DVD based ioctl() and defines. - - - -#include "../convert.h" - -#include "logfile.h" - -#include "device.h" - -#include "CD.h" - - - - - -// Constants - -u8 *playstationcdname = "PLAYSTATION\0"; - -u8 *ps1name = "CD-XA001\0"; - - - -// CD-ROM temp storage structures (see linux/cdrom.h for details) - -struct cdrom_tochdr cdheader; - -struct cdrom_tocentry cdtrack; - -struct cdrom_subchnl subchannel; - -u8 cdtempbuffer[2352]; - - - -int cdmode; // mode of last CDVDreadTrack call (important for CDs) - - - - - -// Internal Functions - - - -void InitCDSectorInfo() { - - cdmode = -1; - -} // END InitSectorInfo(); - - - - - -// Function Calls from CDVD.c - - - -void InitCDInfo() { - - InitCDSectorInfo(); - -} // END InitDiscType() - - - -s32 CDreadTrack(u32 lsn, int mode, u8 *buffer) { - - s32 s32result; - - - -#ifdef VERBOSE_FUNCTION - - PrintLog("CDVD driver: CDreadTrack(%i)", lsn); - -#endif /* VERBOSE_FUNCTION */ - - - - s32result = 0; - - - - if(buffer == NULL) return(-1); - - - - // The CD way of figuring out where to read. - - LBAtoMSF(lsn, buffer); - - - - switch(mode) { - - case CDVD_MODE_2048: - - case CDVD_MODE_2328: - - case CDVD_MODE_2340: - - case CDVD_MODE_2352: - - errno = 4; // Interrupted system call... (simulated the first time) - - while(errno == 4) { - - errno = 0; - - s32result = ioctl(devicehandle, CDROMREADRAW, buffer); - - } // ENDWHILE- Continually being interrupted by the system... - - break; - - case CDVD_MODE_2368: // Unimplemented... as yet. - - default: - -#ifdef VERBOSE_WARNINGS - - PrintLog("CDVD driver: Unknown Mode %i", mode); - -#endif /* VERBOSE_WARNINGS */ - - return(-1); // Illegal Read Mode? Abort - - break; - - } // ENDSWITCH- Which read mode should we choose? - - if((s32result == -1) || (errno != 0)) { - -#ifdef VERBOSE_WARNINGS - - PrintLog("CDVD driver: Error reading CD: %i:%s", errno, strerror(errno)); - -#endif /* VERBOSE_WARNINGS */ - - InitCDSectorInfo(); - - return(-1); - - } // ENDIF- Trouble getting a track count? - - - - cdmode = mode; // Save mode for buffer positioning later. - - return(0); // Call accomplished - -} // END CDreadTrack() - - - -s32 CDgetBufferOffset() { - -#ifdef VERBOSE_FUNCTION - - PrintLog("CDVD driver: CDgetBufferOffset()"); - -#endif /* VERBOSE_FUNCTION */ - - - - switch(cdmode) { - - case CDVD_MODE_2048: - - return(0+24); - - case CDVD_MODE_2328: - - return(0+24); - - case CDVD_MODE_2340: - - return(0+12); - - case CDVD_MODE_2352: - - return(0+0); - - case CDVD_MODE_2368: // Unimplemented... as yet. - - default: - -#ifdef VERBOSE_WARNINGS - - PrintLog("CDVD driver: Unknown Mode %i", cdmode); - -#endif /* VERBOSE_WARNINGS */ - - return(0); // Not to worry. for now. - - } // ENDSWITCH- where should we put the buffer pointer? - -} // END CDgetBuffer() - - - -// I, personally, don't see the big deal with SubQ - -// However, sooner or later I'll incorporate it into the Cache Buffer system - -// (backward compatibility, and all that) - -s32 CDreadSubQ(u32 lsn, cdvdSubQ *subq) { - - int tempmode; - - s32 s32result; - - - - s32result = 0; - - - -#ifdef VERBOSE_FUNCTION - - PrintLog("CDVD driver: CDreadSubQ()"); - -#endif /* VERBOSE_FUNCTION */ - - - - tempmode = cdmode; - - if(tempmode == -1) tempmode = CDVD_MODE_2352; - - CDreadTrack(lsn, tempmode, cdtempbuffer); - - if((s32result == -1) || (errno != 0)) { - -#ifdef VERBOSE_WARNINGS - - PrintLog("CDVD driver: Error prepping CD SubQ: %i:%s", errno, strerror(errno)); - -#endif /* VERBOSE_WARNINGS */ - - return(s32result); - - } // ENDIF- Trouble? - - - - subchannel.cdsc_format = CDROM_MSF; - - s32result = ioctl(devicehandle, CDROMSUBCHNL, &subchannel); - - if((s32result == -1) || (errno != 0)) { - -#ifdef VERBOSE_WARNINGS - - PrintLog("CDVD driver: Error reading CD SubQ: %i:%s", errno, strerror(errno)); - -#endif /* VERBOSE_WARNINGS */ - - return(s32result); - - } // ENDIF- Trouble? - - - - if(subq != NULL) { - - subq->mode = subchannel.cdsc_adr; - - subq->ctrl = subchannel.cdsc_ctrl; - - subq->trackNum = subchannel.cdsc_trk; - - subq->trackIndex = subchannel.cdsc_ind; - - subq->trackM = subchannel.cdsc_reladdr.msf.minute; - - subq->trackS = subchannel.cdsc_reladdr.msf.second; - - subq->trackF = subchannel.cdsc_reladdr.msf.frame; - - subq->discM = subchannel.cdsc_absaddr.msf.minute; - - subq->discS = subchannel.cdsc_absaddr.msf.second; - - subq->discF = subchannel.cdsc_absaddr.msf.frame; - - } // ENDIF- Did the caller want all this data? - - - - return(0); - -} // END CDVDreadSubQ() - - - -s32 CDgetTN(cdvdTN *cdvdtn) { - -#ifdef VERBOSE_FUNCTION - - PrintLog("CDVD driver: CDgetTN()"); - -#endif /* VERBOSE_FUNCTION */ - - - - if(cdvdtn != NULL) { - - cdvdtn->strack = cdheader.cdth_trk0; - - cdvdtn->etrack = cdheader.cdth_trk1; - - } // ENDIF- programmer actually WANTS this info? - - - - return(0); // Call accomplished - -} // END CDVDgetTN() - - - -s32 CDgetTD(u8 newtrack, cdvdTD *cdvdtd) { - - u8 j; - - u16 k; - - char temptime[3]; - - - -#ifdef VERBOSE_FUNCTION - - PrintLog("CDVD driver: CDgetTD()"); - -#endif /* VERBOSE_FUNCTION */ - - - - j = newtrack; - - if(j == CDROM_LEADOUT) j = 0; - - - - if(j == 0) { - - k = 27; - - } else { - - k = j * 10 + 37; - - } // ENDIF- Where to start hunting for this number? - - - - if(cdvdtd != NULL) { - - cdvdtd->type = tocbuffer[j*10 + 30]; - - - - temptime[0] = BCDTOHEX(tocbuffer[k]); - - temptime[1] = BCDTOHEX(tocbuffer[k + 1]); - - temptime[2] = BCDTOHEX(tocbuffer[k + 2]); - - cdvdtd->lsn = MSFtoLBA(temptime); - - } // ENDIF- Does the caller REALLY want this data? - - - - return(0); // Call accomplished - -} // END CDVDgetTD() - - - -s32 CALLBACK CDgetDiskType(s32 ioctldisktype) { - - s32 offset; - - s32 s32result; - - int i; - - u8 j; - - int tempdisctype; - - - - offset = 0; - - errno = 0; - - i = 0; - - j = 0; - - tempdisctype = CDVD_TYPE_UNKNOWN; - - - -#ifdef VERBOSE_FUNCTION - - PrintLog("CDVD driver: CDgetDiskType()"); - -#endif /* VERBOSE_FUNCTION */ - - - - s32result = CDreadTrack(16, CDVD_MODE_2352, cdtempbuffer); - - if((s32result != 0) || (errno != 0)) { - - return(-1); - - } // ENDIF- Cannot read the CD's ISO9660 volume sector? Abort - - disctype = CDVD_TYPE_DETCTCD; - - - - switch(ioctldisktype) { - - case CDS_AUDIO: - -#ifdef VERBOSE_DISC_TYPE - - PrintLog("CDVD driver: Detected CDDA Audio disc."); - -#endif /* VERBOSE_DISC_TYPE */ - - tempdisctype = CDVD_TYPE_CDDA; - - tocbuffer[0] = 0x01; - - break; - - - - case CDS_DATA_1: - - case CDS_MIXED: - -#ifdef VERBOSE_DISC_TYPE - - PrintLog("CDVD driver: Detected CD disc."); - -#endif /* VERBOSE_DISC_TYPE */ - - tocbuffer[0] = 0x41; - - - - CDreadTrack(16, CDVD_MODE_2048, cdtempbuffer); - - offset = CDgetBufferOffset(); - - i = 0; - - while((*(playstationcdname + i) != 0) && - - (*(playstationcdname + i) == cdtempbuffer[offset + 8 + i])) i++; - - if(*(playstationcdname + i) == 0) { - - i = 0; - - while((*(ps1name + i) != 0) && - - (*(ps1name + i) == cdtempbuffer[offset + 1024 + i])) i++; - - if(*(ps1name + i) == 0) { - -#ifdef VERBOSE_DISC_TYPE - - PrintLog("CDVD driver: Detected Playstation CD disc."); - -#endif /* VERBOSE_DISC_TYPE */ - - tempdisctype = CDVD_TYPE_PSCD; - - } else { - -#ifdef VERBOSE_DISC_TYPE - - PrintLog("CDVD driver: Detected Playstation 2 CD disc."); - -#endif /* VERBOSE_DISC_TYPE */ - - tempdisctype = CDVD_TYPE_PS2CD; - - } // ENDIF- Did we find the CD ident? (For Playstation 1 CDs) - - } else { - - tempdisctype = CDVD_TYPE_UNKNOWN; - - } // ENDIF- Did we find the Playstation name? - - break; - - - - default: - - return(-1); - - } // ENDSWITCH- What has ioctl disc type come up with? - - - - // Collect TN data - - cdheader.cdth_trk0 = 0; - - cdheader.cdth_trk1 = 0; - - - - s32result = ioctl(devicehandle, CDROMREADTOCHDR, &cdheader); - - if((s32result == -1) || (errno != 0)) { - -#ifdef VERBOSE_WARNINGS - - PrintLog("CDVD driver: Error reading TN: (%i) %i:%s", - - s32result, errno, strerror(errno)); - -#endif /* VERBOSE_WARNINGS */ - - cdheader.cdth_trk0 = 1; - - cdheader.cdth_trk1 = 1; - - } // ENDIF- Failed to read in track count? Assume 1 track. - -#ifdef VERBOSE_DISC_INFO - - PrintLog("CDVD driver: Track Number Range: %i-%i", - - cdheader.cdth_trk0, cdheader.cdth_trk1); - -#endif /* VERBOSE_DISC_INFO */ - - tocbuffer[2] = 0xA0; - - tocbuffer[7] = HEXTOBCD(cdheader.cdth_trk0); - - tocbuffer[12] = 0xA1; - - tocbuffer[17] = HEXTOBCD(cdheader.cdth_trk1); - - - - // Collect disc TD data - - cdtrack.cdte_track = CDROM_LEADOUT; - - cdtrack.cdte_format = CDROM_LBA; - - s32result = ioctl(devicehandle, CDROMREADTOCENTRY, &cdtrack); - - if((s32result == -1) || (errno != 0)) { - -#ifdef VERBOSE_WARNINGS - - PrintLog("CDVD driver: Error reading TD for disc: (%i) %i:%s", - - s32result, errno, strerror(errno)); - -#endif /* VERBOSE_WARNINGS */ - - return(-1); - - } // ENDIF- Trouble getting a track count? - - - - LBAtoMSF(cdtrack.cdte_addr.lba, &tocbuffer[27]); - -#ifdef VERBOSE_DISC_INFO - - PrintLog("CDVD driver: Total Time: %i:%i", - - tocbuffer[27], tocbuffer[28]); - -#endif /* VERBOSE_DISC_INFO */ - - tocbuffer[27] = HEXTOBCD(tocbuffer[27]); - - tocbuffer[28] = HEXTOBCD(tocbuffer[28]); - - tocbuffer[29] = HEXTOBCD(tocbuffer[29]); - - - - // Collect track TD data - - for(j = cdheader.cdth_trk0; j <= cdheader.cdth_trk1; j++) { - - cdtrack.cdte_track = j; // j-1? - - cdtrack.cdte_format = CDROM_LBA; - - s32result = ioctl(devicehandle, CDROMREADTOCENTRY, &cdtrack); - - if((s32result == -1) || (errno != 0)) { - -#ifdef VERBOSE_WARNINGS - - PrintLog("CDVD driver: Error reading TD for track %i: (%i) %i:%s", - - j, s32result, errno, strerror(errno)); - -#endif /* VERBOSE_WARNINGS */ - - // No more here... - - - - } else { - - LBAtoMSF(cdtrack.cdte_addr.lba, &tocbuffer[j*10 + 37]); - -#ifdef VERBOSE_DISC_INFO - - PrintLog("CDVD driver: Track %i: Data Mode %i Disc Start Time:%i:%i.%i\n", - - j, - - cdtrack.cdte_datamode, - - tocbuffer[j*10+37], - - tocbuffer[j*10+38], - - tocbuffer[j*10+39]); - -#endif /* VERBOSE_DISC_INFO */ - - tocbuffer[j*10 + 30] = cdtrack.cdte_datamode; - - tocbuffer[j*10 + 32] = HEXTOBCD(j); - - tocbuffer[j*10 + 37] = HEXTOBCD(tocbuffer[j*10 + 37]); - - tocbuffer[j*10 + 38] = HEXTOBCD(tocbuffer[j*10 + 38]); - - tocbuffer[j*10 + 39] = HEXTOBCD(tocbuffer[j*10 + 39]); - - } // ENDIF- Trouble getting a track count? - - } // NEXT j- Reading each track's info in turn - - - - errno = 0; - - disctype = tempdisctype; // Trigger the fact we have the info (finally) - - return(disctype); - -} // END CDVDgetDiskType() - +/* CD.c + + * Copyright (C) 2002-2005 PCSX2 Team + + * + + * This program is free software; you can redistribute it and/or modify + + * it under the terms of the GNU General Public License as published by + + * the Free Software Foundation; either version 2 of the License, or + + * (at your option) any later version. + + * + + * This program 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 this program; if not, write to the Free Software + + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + * + + * PCSX2 members can be contacted through their website at www.pcsx2.net. + + */ + + + +#include // errno + +#include // NULL + +#include // strerror() + +#include // open() + +#include // ioctl() + +#include // open() + +#include // lseek(), open() + +#include // close(), lseek(), (sleep()) + + + +#include // CD/DVD based ioctl() and defines. + + + +#include "../convert.h" + +#include "logfile.h" + +#include "device.h" + +#include "CD.h" + + + + + +// Constants + +u8 *playstationcdname = "PLAYSTATION\0"; + +u8 *ps1name = "CD-XA001\0"; + + + +// CD-ROM temp storage structures (see linux/cdrom.h for details) + +struct cdrom_tochdr cdheader; + +struct cdrom_tocentry cdtrack; + +struct cdrom_subchnl subchannel; + +u8 cdtempbuffer[2352]; + + + +int cdmode; // mode of last CDVDreadTrack call (important for CDs) + + + + + +// Internal Functions + + + +void InitCDSectorInfo() { + + cdmode = -1; + +} // END InitSectorInfo(); + + + + + +// Function Calls from CDVD.c + + + +void InitCDInfo() { + + InitCDSectorInfo(); + +} // END InitDiscType() + + + +s32 CDreadTrack(u32 lsn, int mode, u8 *buffer) { + + s32 s32result; + + + +#ifdef VERBOSE_FUNCTION + + PrintLog("CDVD driver: CDreadTrack(%i)", lsn); + +#endif /* VERBOSE_FUNCTION */ + + + + s32result = 0; + + + + if(buffer == NULL) return(-1); + + + + // The CD way of figuring out where to read. + + LBAtoMSF(lsn, buffer); + + + + switch(mode) { + + case CDVD_MODE_2048: + + case CDVD_MODE_2328: + + case CDVD_MODE_2340: + + case CDVD_MODE_2352: + + errno = 4; // Interrupted system call... (simulated the first time) + + while(errno == 4) { + + errno = 0; + + s32result = ioctl(devicehandle, CDROMREADRAW, buffer); + + } // ENDWHILE- Continually being interrupted by the system... + + break; + + case CDVD_MODE_2368: // Unimplemented... as yet. + + default: + +#ifdef VERBOSE_WARNINGS + + PrintLog("CDVD driver: Unknown Mode %i", mode); + +#endif /* VERBOSE_WARNINGS */ + + return(-1); // Illegal Read Mode? Abort + + break; + + } // ENDSWITCH- Which read mode should we choose? + + if((s32result == -1) || (errno != 0)) { + +#ifdef VERBOSE_WARNINGS + + PrintLog("CDVD driver: Error reading CD: %i:%s", errno, strerror(errno)); + +#endif /* VERBOSE_WARNINGS */ + + InitCDSectorInfo(); + + return(-1); + + } // ENDIF- Trouble getting a track count? + + + + cdmode = mode; // Save mode for buffer positioning later. + + return(0); // Call accomplished + +} // END CDreadTrack() + + + +s32 CDgetBufferOffset() { + +#ifdef VERBOSE_FUNCTION + + PrintLog("CDVD driver: CDgetBufferOffset()"); + +#endif /* VERBOSE_FUNCTION */ + + + + switch(cdmode) { + + case CDVD_MODE_2048: + + return(0+24); + + case CDVD_MODE_2328: + + return(0+24); + + case CDVD_MODE_2340: + + return(0+12); + + case CDVD_MODE_2352: + + return(0+0); + + case CDVD_MODE_2368: // Unimplemented... as yet. + + default: + +#ifdef VERBOSE_WARNINGS + + PrintLog("CDVD driver: Unknown Mode %i", cdmode); + +#endif /* VERBOSE_WARNINGS */ + + return(0); // Not to worry. for now. + + } // ENDSWITCH- where should we put the buffer pointer? + +} // END CDgetBuffer() + + + +// I, personally, don't see the big deal with SubQ + +// However, sooner or later I'll incorporate it into the Cache Buffer system + +// (backward compatibility, and all that) + +s32 CDreadSubQ(u32 lsn, cdvdSubQ *subq) { + + int tempmode; + + s32 s32result; + + + + s32result = 0; + + + +#ifdef VERBOSE_FUNCTION + + PrintLog("CDVD driver: CDreadSubQ()"); + +#endif /* VERBOSE_FUNCTION */ + + + + tempmode = cdmode; + + if(tempmode == -1) tempmode = CDVD_MODE_2352; + + CDreadTrack(lsn, tempmode, cdtempbuffer); + + if((s32result == -1) || (errno != 0)) { + +#ifdef VERBOSE_WARNINGS + + PrintLog("CDVD driver: Error prepping CD SubQ: %i:%s", errno, strerror(errno)); + +#endif /* VERBOSE_WARNINGS */ + + return(s32result); + + } // ENDIF- Trouble? + + + + subchannel.cdsc_format = CDROM_MSF; + + s32result = ioctl(devicehandle, CDROMSUBCHNL, &subchannel); + + if((s32result == -1) || (errno != 0)) { + +#ifdef VERBOSE_WARNINGS + + PrintLog("CDVD driver: Error reading CD SubQ: %i:%s", errno, strerror(errno)); + +#endif /* VERBOSE_WARNINGS */ + + return(s32result); + + } // ENDIF- Trouble? + + + + if(subq != NULL) { + + subq->mode = subchannel.cdsc_adr; + + subq->ctrl = subchannel.cdsc_ctrl; + + subq->trackNum = subchannel.cdsc_trk; + + subq->trackIndex = subchannel.cdsc_ind; + + subq->trackM = subchannel.cdsc_reladdr.msf.minute; + + subq->trackS = subchannel.cdsc_reladdr.msf.second; + + subq->trackF = subchannel.cdsc_reladdr.msf.frame; + + subq->discM = subchannel.cdsc_absaddr.msf.minute; + + subq->discS = subchannel.cdsc_absaddr.msf.second; + + subq->discF = subchannel.cdsc_absaddr.msf.frame; + + } // ENDIF- Did the caller want all this data? + + + + return(0); + +} // END CDVDreadSubQ() + + + +s32 CDgetTN(cdvdTN *cdvdtn) { + +#ifdef VERBOSE_FUNCTION + + PrintLog("CDVD driver: CDgetTN()"); + +#endif /* VERBOSE_FUNCTION */ + + + + if(cdvdtn != NULL) { + + cdvdtn->strack = cdheader.cdth_trk0; + + cdvdtn->etrack = cdheader.cdth_trk1; + + } // ENDIF- programmer actually WANTS this info? + + + + return(0); // Call accomplished + +} // END CDVDgetTN() + + + +s32 CDgetTD(u8 newtrack, cdvdTD *cdvdtd) { + + u8 j; + + u16 k; + + char temptime[3]; + + + +#ifdef VERBOSE_FUNCTION + + PrintLog("CDVD driver: CDgetTD()"); + +#endif /* VERBOSE_FUNCTION */ + + + + j = newtrack; + + if(j == CDROM_LEADOUT) j = 0; + + + + if(j == 0) { + + k = 27; + + } else { + + k = j * 10 + 37; + + } // ENDIF- Where to start hunting for this number? + + + + if(cdvdtd != NULL) { + + cdvdtd->type = tocbuffer[j*10 + 30]; + + + + temptime[0] = BCDTOHEX(tocbuffer[k]); + + temptime[1] = BCDTOHEX(tocbuffer[k + 1]); + + temptime[2] = BCDTOHEX(tocbuffer[k + 2]); + + cdvdtd->lsn = MSFtoLBA(temptime); + + } // ENDIF- Does the caller REALLY want this data? + + + + return(0); // Call accomplished + +} // END CDVDgetTD() + + + +s32 CALLBACK CDgetDiskType(s32 ioctldisktype) { + + s32 offset; + + s32 s32result; + + int i; + + u8 j; + + int tempdisctype; + + + + offset = 0; + + errno = 0; + + i = 0; + + j = 0; + + tempdisctype = CDVD_TYPE_UNKNOWN; + + + +#ifdef VERBOSE_FUNCTION + + PrintLog("CDVD driver: CDgetDiskType()"); + +#endif /* VERBOSE_FUNCTION */ + + + + s32result = CDreadTrack(16, CDVD_MODE_2352, cdtempbuffer); + + if((s32result != 0) || (errno != 0)) { + + return(-1); + + } // ENDIF- Cannot read the CD's ISO9660 volume sector? Abort + + disctype = CDVD_TYPE_DETCTCD; + + + + switch(ioctldisktype) { + + case CDS_AUDIO: + +#ifdef VERBOSE_DISC_TYPE + + PrintLog("CDVD driver: Detected CDDA Audio disc."); + +#endif /* VERBOSE_DISC_TYPE */ + + tempdisctype = CDVD_TYPE_CDDA; + + tocbuffer[0] = 0x01; + + break; + + + + case CDS_DATA_1: + + case CDS_MIXED: + +#ifdef VERBOSE_DISC_TYPE + + PrintLog("CDVD driver: Detected CD disc."); + +#endif /* VERBOSE_DISC_TYPE */ + + tocbuffer[0] = 0x41; + + + + CDreadTrack(16, CDVD_MODE_2048, cdtempbuffer); + + offset = CDgetBufferOffset(); + + i = 0; + + while((*(playstationcdname + i) != 0) && + + (*(playstationcdname + i) == cdtempbuffer[offset + 8 + i])) i++; + + if(*(playstationcdname + i) == 0) { + + i = 0; + + while((*(ps1name + i) != 0) && + + (*(ps1name + i) == cdtempbuffer[offset + 1024 + i])) i++; + + if(*(ps1name + i) == 0) { + +#ifdef VERBOSE_DISC_TYPE + + PrintLog("CDVD driver: Detected Playstation CD disc."); + +#endif /* VERBOSE_DISC_TYPE */ + + tempdisctype = CDVD_TYPE_PSCD; + + } else { + +#ifdef VERBOSE_DISC_TYPE + + PrintLog("CDVD driver: Detected Playstation 2 CD disc."); + +#endif /* VERBOSE_DISC_TYPE */ + + tempdisctype = CDVD_TYPE_PS2CD; + + } // ENDIF- Did we find the CD ident? (For Playstation 1 CDs) + + } else { + + tempdisctype = CDVD_TYPE_UNKNOWN; + + } // ENDIF- Did we find the Playstation name? + + break; + + + + default: + + return(-1); + + } // ENDSWITCH- What has ioctl disc type come up with? + + + + // Collect TN data + + cdheader.cdth_trk0 = 0; + + cdheader.cdth_trk1 = 0; + + + + s32result = ioctl(devicehandle, CDROMREADTOCHDR, &cdheader); + + if((s32result == -1) || (errno != 0)) { + +#ifdef VERBOSE_WARNINGS + + PrintLog("CDVD driver: Error reading TN: (%i) %i:%s", + + s32result, errno, strerror(errno)); + +#endif /* VERBOSE_WARNINGS */ + + cdheader.cdth_trk0 = 1; + + cdheader.cdth_trk1 = 1; + + } // ENDIF- Failed to read in track count? Assume 1 track. + +#ifdef VERBOSE_DISC_INFO + + PrintLog("CDVD driver: Track Number Range: %i-%i", + + cdheader.cdth_trk0, cdheader.cdth_trk1); + +#endif /* VERBOSE_DISC_INFO */ + + tocbuffer[2] = 0xA0; + + tocbuffer[7] = HEXTOBCD(cdheader.cdth_trk0); + + tocbuffer[12] = 0xA1; + + tocbuffer[17] = HEXTOBCD(cdheader.cdth_trk1); + + + + // Collect disc TD data + + cdtrack.cdte_track = CDROM_LEADOUT; + + cdtrack.cdte_format = CDROM_LBA; + + s32result = ioctl(devicehandle, CDROMREADTOCENTRY, &cdtrack); + + if((s32result == -1) || (errno != 0)) { + +#ifdef VERBOSE_WARNINGS + + PrintLog("CDVD driver: Error reading TD for disc: (%i) %i:%s", + + s32result, errno, strerror(errno)); + +#endif /* VERBOSE_WARNINGS */ + + return(-1); + + } // ENDIF- Trouble getting a track count? + + + + LBAtoMSF(cdtrack.cdte_addr.lba, &tocbuffer[27]); + +#ifdef VERBOSE_DISC_INFO + + PrintLog("CDVD driver: Total Time: %i:%i", + + tocbuffer[27], tocbuffer[28]); + +#endif /* VERBOSE_DISC_INFO */ + + tocbuffer[27] = HEXTOBCD(tocbuffer[27]); + + tocbuffer[28] = HEXTOBCD(tocbuffer[28]); + + tocbuffer[29] = HEXTOBCD(tocbuffer[29]); + + + + // Collect track TD data + + for(j = cdheader.cdth_trk0; j <= cdheader.cdth_trk1; j++) { + + cdtrack.cdte_track = j; // j-1? + + cdtrack.cdte_format = CDROM_LBA; + + s32result = ioctl(devicehandle, CDROMREADTOCENTRY, &cdtrack); + + if((s32result == -1) || (errno != 0)) { + +#ifdef VERBOSE_WARNINGS + + PrintLog("CDVD driver: Error reading TD for track %i: (%i) %i:%s", + + j, s32result, errno, strerror(errno)); + +#endif /* VERBOSE_WARNINGS */ + + // No more here... + + + + } else { + + LBAtoMSF(cdtrack.cdte_addr.lba, &tocbuffer[j*10 + 37]); + +#ifdef VERBOSE_DISC_INFO + + PrintLog("CDVD driver: Track %i: Data Mode %i Disc Start Time:%i:%i.%i\n", + + j, + + cdtrack.cdte_datamode, + + tocbuffer[j*10+37], + + tocbuffer[j*10+38], + + tocbuffer[j*10+39]); + +#endif /* VERBOSE_DISC_INFO */ + + tocbuffer[j*10 + 30] = cdtrack.cdte_datamode; + + tocbuffer[j*10 + 32] = HEXTOBCD(j); + + tocbuffer[j*10 + 37] = HEXTOBCD(tocbuffer[j*10 + 37]); + + tocbuffer[j*10 + 38] = HEXTOBCD(tocbuffer[j*10 + 38]); + + tocbuffer[j*10 + 39] = HEXTOBCD(tocbuffer[j*10 + 39]); + + } // ENDIF- Trouble getting a track count? + + } // NEXT j- Reading each track's info in turn + + + + errno = 0; + + disctype = tempdisctype; // Trigger the fact we have the info (finally) + + return(disctype); + +} // END CDVDgetDiskType() + diff --git a/plugins/CDVDlinuz/Src/Linux/CDVDlinuz.c b/plugins/CDVDlinuz/Src/Linux/CDVDlinuz.c index 88f93bc804..ef901cd93e 100644 --- a/plugins/CDVDlinuz/Src/Linux/CDVDlinuz.c +++ b/plugins/CDVDlinuz/Src/Linux/CDVDlinuz.c @@ -1,368 +1,368 @@ -/* CDVDlinuz.c - * Copyright (C) 2002-2005 CDVDlinuz Team - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include // errno -#include // open() -#include // NULL -#include // printf() -#include // getenv(), system() -#include // strerror(), sprintf() -#include // ioctl() -#include // stat() -#include // stat() -#include // time_t, time(), struct timeval -#include // stat() - -#ifndef __LINUX__ -#ifdef __linux__ -#define __LINUX__ -#endif /* __linux__ */ -#endif /* No __LINUX__ */ - -#define CDVDdefs -#include "PS2Edefs.h" -// #include "PS2Etypes.h" - -#include "CDVDlinuz.h" - -#include "buffer.h" -#include "conf.h" -#include "logfile.h" -#include "CD.h" // InitCDInfo() -#include "DVD.h" // InitDVDInfo() -#include "device.h" - -#include "../version.h" - - -// Globals - -time_t lasttime; - - -// Interface Functions - -u32 CALLBACK PS2EgetLibType() { - return(PS2E_LT_CDVD); // Library Type CDVD -} // END PS2EgetLibType() - - -u32 CALLBACK PS2EgetLibVersion2(u32 type) { - return((version<<16)|(revision<<8)|build); -} // END PS2EgetLibVersion2() - - -char* CALLBACK PS2EgetLibName() { - return(libname); -} // END PS2EgetLibName() - - -s32 CALLBACK CDVDinit() { - errno = 0; - - InitLog(); - if(OpenLog() != 0) return(-1); - -#ifdef VERBOSE_FUNCTION - PrintLog("CDVD interface: CDVDinit()"); -#endif /* VERBOSE_FUNCTION */ - - InitConf(); - - devicehandle = -1; - devicecapability = 0; - lasttime = time(NULL); - - // Initialize DVD.c and CD.c as well - InitDisc(); - InitDVDInfo(); - InitCDInfo(); - - return(0); -} // END CDVDinit() - - -void CALLBACK CDVDshutdown() { -#ifdef VERBOSE_FUNCTION - PrintLog("CDVD interface: CDVDshutdown()"); -#endif /* VERBOSE_FUNCTION */ - - DeviceClose(); - CloseLog(); -} // END CDVDshutdown() - - -s32 CALLBACK CDVDopen(const char* pTitleFilename) { - s32 s32result; - -#ifdef VERBOSE_FUNCTION - PrintLog("CDVD interface: CDVDopen()"); -#endif /* VERBOSE_FUNCTION */ - - InitBuffer(); - - LoadConf(); - - errno = 0; - s32result = DeviceOpen(); - if(s32result != 0) return(s32result); - if(errno != 0) return(-1); - - return(0); -} // END CDVDopen(); - - -void CALLBACK CDVDclose() { -#ifdef VERBOSE_FUNCTION - PrintLog("CDVD interface: CDVDclose()"); -#endif /* VERBOSE_FUNCTION */ - - DeviceClose(); -} // END CDVDclose() - - -s32 CALLBACK CDVDreadTrack(u32 lsn, int mode) { - s32 s32result; - -#ifdef VERBOSE_FUNCTION - PrintLog("CDVD interface: CDVDreadTrack(%i)", lsn); -#endif /* VERBOSE_FUNCTION */ - - s32result = 0; - errno = 0; - - if(DiscInserted() == -1) return(-1); - - if(userbuffer < BUFFERMAX) { - if((bufferlist[userbuffer].lsn == lsn) && - (bufferlist[userbuffer].mode == mode)) { - return(0); - } // ENDIF- And it's the right one? - } // ENDIF- Are we already pointing at the buffer? - - userbuffer = FindListBuffer(lsn); - if(userbuffer < BUFFERMAX) { - if((bufferlist[userbuffer].lsn == lsn) && - (bufferlist[userbuffer].mode == mode)) { - return(0); - } // ENDIF- And it was the right one? - } // ENDIF- Was a buffer found in the cache? - - replacebuffer++; - if(replacebuffer >= BUFFERMAX) replacebuffer = 0; - userbuffer = replacebuffer; - - if(bufferlist[replacebuffer].upsort != 0xffff) { - RemoveListBuffer(replacebuffer); - } // ENDIF- Reference already in place? Remove it. - - s32result = DeviceReadTrack(lsn, mode, bufferlist[replacebuffer].buffer); - bufferlist[replacebuffer].lsn = lsn; - bufferlist[replacebuffer].mode = mode; - bufferlist[replacebuffer].offset = DeviceBufferOffset(); - - if((s32result != 0) || (errno != 0)) { - bufferlist[replacebuffer].mode = -1; // Error! flag buffer as such. - } else { - if((disctype != CDVD_TYPE_PS2DVD) && (disctype != CDVD_TYPE_DVDV)) { - if(mode == CDVD_MODE_2352) { - CDreadSubQ(lsn, &bufferlist[replacebuffer].subq); - errno = 0; - } // ENDIF- Read subq as well? - } // ENDIF- Read a DVD buffer or a CD buffer? - } // ENDIF-Read ok? Fill out rest of buffer info. - AddListBuffer(replacebuffer); - return(s32result); -} // END CDVDreadTrack() - - -u8* CALLBACK CDVDgetBuffer() { -#ifdef VERBOSE_FUNCTION - PrintLog("CDVD interface: CDVDgetBuffer()"); -#endif /* VERBOSE_FUNCTION */ - - if(DiscInserted() == -1) return(NULL); - - if(userbuffer == 0xffff) { -#ifdef VERBOSE_WARNINGS - PrintLog("CDVD interface: Not pointing to a buffer!"); -#endif /* VERBOSE_WARNINGS */ - return(NULL); // No buffer reference? - } // ENDIF- user buffer not pointing at anything? Abort - - if(bufferlist[userbuffer].mode < 0) { -#ifdef VERBOSE_WARNINGS - PrintLog("CDVD interface: Error in buffer!"); -#endif /* VERBOSE_WARNINGS */ - return(NULL); // Bad Sector? - } // ENDIF- Trouble reading physical sector? Tell them. - - return(bufferlist[userbuffer].buffer + bufferlist[userbuffer].offset); -} // END CDVDgetBuffer() - - -// Note: without the lsn, I could pull the SubQ data directly from -// the stored buffer (in buffer.h). Oh, well. -s32 CALLBACK CDVDreadSubQ(u32 lsn, cdvdSubQ *subq) { -#ifdef VERBOSE_FUNCTION - PrintLog("CDVD interface: CDVDreadSubQ(%i)", lsn); -#endif /* VERBOSE_FUNCTION */ - - if(DiscInserted() == -1) return(-1); - - // DVDs don't have SubQ data - if(disctype == CDVD_TYPE_PS2DVD) return(-1); - if(disctype == CDVD_TYPE_DVDV) return(-1); - - return(CDreadSubQ(lsn, subq)); -} // END CDVDreadSubQ() - - -s32 CALLBACK CDVDgetTN(cdvdTN *cdvdtn) { -#ifdef VERBOSE_FUNCTION - PrintLog("CDVD interface: CDVDgetTN()"); -#endif /* VERBOSE_FUNCTION */ - - if(DiscInserted() == -1) return(-1); - - if((disctype == CDVD_TYPE_PS2DVD) || (disctype == CDVD_TYPE_DVDV)) { - return(DVDgetTN(cdvdtn)); - } else { - return(CDgetTN(cdvdtn)); - } // ENDIF- Are we looking at a DVD? -} // END CDVDgetTN() - - -s32 CALLBACK CDVDgetTD(u8 newtrack, cdvdTD *cdvdtd) { -#ifdef VERBOSE_FUNCTION - PrintLog("CDVD interface: CDVDgetTD()"); -#endif /* VERBOSE_FUNCTION */ - - if(DiscInserted() == -1) return(-1); - - if((disctype == CDVD_TYPE_PS2DVD) || (disctype == CDVD_TYPE_DVDV)) { - return(DVDgetTD(newtrack, cdvdtd)); - } else { - return(CDgetTD(newtrack, cdvdtd)); - } // ENDIF- Are we looking at a DVD? -} // END CDVDgetTD() - - -s32 CALLBACK CDVDgetTOC(void *toc) { - // A structure to fill in, or at least some documentation on what - // the PS2 expects from this call would be more helpful than a - // "void *". - - union { - void *voidptr; - u8 *u8ptr; - } tocptr; - s32 i; - -#ifdef VERBOSE_FUNCTION - PrintLog("CDVD interface: CDVDgetTOC()"); -#endif /* VERBOSE_FUNCTION */ - - if(toc == NULL) return(-1); - if(DiscInserted() == -1) return(-1); - - tocptr.voidptr = toc; - for(i = 0; i < 1024; i++) *(tocptr.u8ptr + i) = tocbuffer[i]; - tocptr.voidptr = NULL; - - return(0); -} // END CDVDgetTOC() - - -s32 CALLBACK CDVDgetDiskType() { -#ifdef VERBOSE_FUNCTION - // Called way too often in boot part of bios to be left in. - // PrintLog("CDVD interface: CDVDgetDiskType()"); -#endif /* VERBOSE_FUNCTION */ - - if(lasttime != time(NULL)) { - lasttime = time(NULL); - DeviceTrayStatus(); - } // ENDIF- Has enough time passed between calls? - - return(disctype); -} // END CDVDgetDiskType() - - -s32 CALLBACK CDVDgetTrayStatus() { -#ifdef VERBOSE_FUNCTION - PrintLog("CDVD interface: CDVDgetTrayStatus()"); -#endif /* VERBOSE_FUNCTION */ - - if(lasttime != time(NULL)) { - lasttime = time(NULL); - DeviceTrayStatus(); - } // ENDIF- Has enough time passed between calls? - - return(traystatus); -} // END CDVDgetTrayStatus() - - -s32 CALLBACK CDVDctrlTrayOpen() { -#ifdef VERBOSE_FUNCTION - PrintLog("CDVD interface: CDVDctrlTrayOpen()"); -#endif /* VERBOSE_FUNCTION */ - - return(DeviceTrayOpen()); -} // END CDVDctrlTrayOpen() - - -s32 CALLBACK CDVDctrlTrayClose() { -#ifdef VERBOSE_FUNCTION - PrintLog("CDVD interface: CDVDctrlTrayClose()"); -#endif /* VERBOSE_FUNCTION */ - - return(DeviceTrayClose()); -} // END CDVDctrlTrayClose() - - -void CALLBACK CDVDconfigure() { - ExecCfg("configure"); -} // END CDVDconfigure() - - -void CALLBACK CDVDabout() { - ExecCfg("about"); -} // END CDVDabout() - - -s32 CALLBACK CDVDtest() { - s32 s32result; - - errno = 0; - - if(devicehandle != -1) { -#ifdef VERBOSE_WARNINGS - PrintLog("CDVD interface: Device already open"); -#endif /* VERBOSE_WARNINGS */ - return(0); - } // ENDIF- Is the CD/DVD already in use? That's fine. - -#ifdef VERBOSE_FUNCTION - PrintLog("CDVD driver: CDVDtest()"); -#endif /* VERBOSE_FUNCTION */ - - s32result = DeviceOpen(); - DeviceClose(); - return(s32result); -} // END CDVDtest() +/* CDVDlinuz.c + * Copyright (C) 2002-2005 CDVDlinuz Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include // errno +#include // open() +#include // NULL +#include // printf() +#include // getenv(), system() +#include // strerror(), sprintf() +#include // ioctl() +#include // stat() +#include // stat() +#include // time_t, time(), struct timeval +#include // stat() + +#ifndef __LINUX__ +#ifdef __linux__ +#define __LINUX__ +#endif /* __linux__ */ +#endif /* No __LINUX__ */ + +#define CDVDdefs +#include "PS2Edefs.h" +// #include "PS2Etypes.h" + +#include "CDVDlinuz.h" + +#include "buffer.h" +#include "conf.h" +#include "logfile.h" +#include "CD.h" // InitCDInfo() +#include "DVD.h" // InitDVDInfo() +#include "device.h" + +#include "../version.h" + + +// Globals + +time_t lasttime; + + +// Interface Functions + +u32 CALLBACK PS2EgetLibType() { + return(PS2E_LT_CDVD); // Library Type CDVD +} // END PS2EgetLibType() + + +u32 CALLBACK PS2EgetLibVersion2(u32 type) { + return((version<<16)|(revision<<8)|build); +} // END PS2EgetLibVersion2() + + +char* CALLBACK PS2EgetLibName() { + return(libname); +} // END PS2EgetLibName() + + +s32 CALLBACK CDVDinit() { + errno = 0; + + InitLog(); + if(OpenLog() != 0) return(-1); + +#ifdef VERBOSE_FUNCTION + PrintLog("CDVD interface: CDVDinit()"); +#endif /* VERBOSE_FUNCTION */ + + InitConf(); + + devicehandle = -1; + devicecapability = 0; + lasttime = time(NULL); + + // Initialize DVD.c and CD.c as well + InitDisc(); + InitDVDInfo(); + InitCDInfo(); + + return(0); +} // END CDVDinit() + + +void CALLBACK CDVDshutdown() { +#ifdef VERBOSE_FUNCTION + PrintLog("CDVD interface: CDVDshutdown()"); +#endif /* VERBOSE_FUNCTION */ + + DeviceClose(); + CloseLog(); +} // END CDVDshutdown() + + +s32 CALLBACK CDVDopen(const char* pTitleFilename) { + s32 s32result; + +#ifdef VERBOSE_FUNCTION + PrintLog("CDVD interface: CDVDopen()"); +#endif /* VERBOSE_FUNCTION */ + + InitBuffer(); + + LoadConf(); + + errno = 0; + s32result = DeviceOpen(); + if(s32result != 0) return(s32result); + if(errno != 0) return(-1); + + return(0); +} // END CDVDopen(); + + +void CALLBACK CDVDclose() { +#ifdef VERBOSE_FUNCTION + PrintLog("CDVD interface: CDVDclose()"); +#endif /* VERBOSE_FUNCTION */ + + DeviceClose(); +} // END CDVDclose() + + +s32 CALLBACK CDVDreadTrack(u32 lsn, int mode) { + s32 s32result; + +#ifdef VERBOSE_FUNCTION + PrintLog("CDVD interface: CDVDreadTrack(%i)", lsn); +#endif /* VERBOSE_FUNCTION */ + + s32result = 0; + errno = 0; + + if(DiscInserted() == -1) return(-1); + + if(userbuffer < BUFFERMAX) { + if((bufferlist[userbuffer].lsn == lsn) && + (bufferlist[userbuffer].mode == mode)) { + return(0); + } // ENDIF- And it's the right one? + } // ENDIF- Are we already pointing at the buffer? + + userbuffer = FindListBuffer(lsn); + if(userbuffer < BUFFERMAX) { + if((bufferlist[userbuffer].lsn == lsn) && + (bufferlist[userbuffer].mode == mode)) { + return(0); + } // ENDIF- And it was the right one? + } // ENDIF- Was a buffer found in the cache? + + replacebuffer++; + if(replacebuffer >= BUFFERMAX) replacebuffer = 0; + userbuffer = replacebuffer; + + if(bufferlist[replacebuffer].upsort != 0xffff) { + RemoveListBuffer(replacebuffer); + } // ENDIF- Reference already in place? Remove it. + + s32result = DeviceReadTrack(lsn, mode, bufferlist[replacebuffer].buffer); + bufferlist[replacebuffer].lsn = lsn; + bufferlist[replacebuffer].mode = mode; + bufferlist[replacebuffer].offset = DeviceBufferOffset(); + + if((s32result != 0) || (errno != 0)) { + bufferlist[replacebuffer].mode = -1; // Error! flag buffer as such. + } else { + if((disctype != CDVD_TYPE_PS2DVD) && (disctype != CDVD_TYPE_DVDV)) { + if(mode == CDVD_MODE_2352) { + CDreadSubQ(lsn, &bufferlist[replacebuffer].subq); + errno = 0; + } // ENDIF- Read subq as well? + } // ENDIF- Read a DVD buffer or a CD buffer? + } // ENDIF-Read ok? Fill out rest of buffer info. + AddListBuffer(replacebuffer); + return(s32result); +} // END CDVDreadTrack() + + +u8* CALLBACK CDVDgetBuffer() { +#ifdef VERBOSE_FUNCTION + PrintLog("CDVD interface: CDVDgetBuffer()"); +#endif /* VERBOSE_FUNCTION */ + + if(DiscInserted() == -1) return(NULL); + + if(userbuffer == 0xffff) { +#ifdef VERBOSE_WARNINGS + PrintLog("CDVD interface: Not pointing to a buffer!"); +#endif /* VERBOSE_WARNINGS */ + return(NULL); // No buffer reference? + } // ENDIF- user buffer not pointing at anything? Abort + + if(bufferlist[userbuffer].mode < 0) { +#ifdef VERBOSE_WARNINGS + PrintLog("CDVD interface: Error in buffer!"); +#endif /* VERBOSE_WARNINGS */ + return(NULL); // Bad Sector? + } // ENDIF- Trouble reading physical sector? Tell them. + + return(bufferlist[userbuffer].buffer + bufferlist[userbuffer].offset); +} // END CDVDgetBuffer() + + +// Note: without the lsn, I could pull the SubQ data directly from +// the stored buffer (in buffer.h). Oh, well. +s32 CALLBACK CDVDreadSubQ(u32 lsn, cdvdSubQ *subq) { +#ifdef VERBOSE_FUNCTION + PrintLog("CDVD interface: CDVDreadSubQ(%i)", lsn); +#endif /* VERBOSE_FUNCTION */ + + if(DiscInserted() == -1) return(-1); + + // DVDs don't have SubQ data + if(disctype == CDVD_TYPE_PS2DVD) return(-1); + if(disctype == CDVD_TYPE_DVDV) return(-1); + + return(CDreadSubQ(lsn, subq)); +} // END CDVDreadSubQ() + + +s32 CALLBACK CDVDgetTN(cdvdTN *cdvdtn) { +#ifdef VERBOSE_FUNCTION + PrintLog("CDVD interface: CDVDgetTN()"); +#endif /* VERBOSE_FUNCTION */ + + if(DiscInserted() == -1) return(-1); + + if((disctype == CDVD_TYPE_PS2DVD) || (disctype == CDVD_TYPE_DVDV)) { + return(DVDgetTN(cdvdtn)); + } else { + return(CDgetTN(cdvdtn)); + } // ENDIF- Are we looking at a DVD? +} // END CDVDgetTN() + + +s32 CALLBACK CDVDgetTD(u8 newtrack, cdvdTD *cdvdtd) { +#ifdef VERBOSE_FUNCTION + PrintLog("CDVD interface: CDVDgetTD()"); +#endif /* VERBOSE_FUNCTION */ + + if(DiscInserted() == -1) return(-1); + + if((disctype == CDVD_TYPE_PS2DVD) || (disctype == CDVD_TYPE_DVDV)) { + return(DVDgetTD(newtrack, cdvdtd)); + } else { + return(CDgetTD(newtrack, cdvdtd)); + } // ENDIF- Are we looking at a DVD? +} // END CDVDgetTD() + + +s32 CALLBACK CDVDgetTOC(void *toc) { + // A structure to fill in, or at least some documentation on what + // the PS2 expects from this call would be more helpful than a + // "void *". + + union { + void *voidptr; + u8 *u8ptr; + } tocptr; + s32 i; + +#ifdef VERBOSE_FUNCTION + PrintLog("CDVD interface: CDVDgetTOC()"); +#endif /* VERBOSE_FUNCTION */ + + if(toc == NULL) return(-1); + if(DiscInserted() == -1) return(-1); + + tocptr.voidptr = toc; + for(i = 0; i < 1024; i++) *(tocptr.u8ptr + i) = tocbuffer[i]; + tocptr.voidptr = NULL; + + return(0); +} // END CDVDgetTOC() + + +s32 CALLBACK CDVDgetDiskType() { +#ifdef VERBOSE_FUNCTION + // Called way too often in boot part of bios to be left in. + // PrintLog("CDVD interface: CDVDgetDiskType()"); +#endif /* VERBOSE_FUNCTION */ + + if(lasttime != time(NULL)) { + lasttime = time(NULL); + DeviceTrayStatus(); + } // ENDIF- Has enough time passed between calls? + + return(disctype); +} // END CDVDgetDiskType() + + +s32 CALLBACK CDVDgetTrayStatus() { +#ifdef VERBOSE_FUNCTION + PrintLog("CDVD interface: CDVDgetTrayStatus()"); +#endif /* VERBOSE_FUNCTION */ + + if(lasttime != time(NULL)) { + lasttime = time(NULL); + DeviceTrayStatus(); + } // ENDIF- Has enough time passed between calls? + + return(traystatus); +} // END CDVDgetTrayStatus() + + +s32 CALLBACK CDVDctrlTrayOpen() { +#ifdef VERBOSE_FUNCTION + PrintLog("CDVD interface: CDVDctrlTrayOpen()"); +#endif /* VERBOSE_FUNCTION */ + + return(DeviceTrayOpen()); +} // END CDVDctrlTrayOpen() + + +s32 CALLBACK CDVDctrlTrayClose() { +#ifdef VERBOSE_FUNCTION + PrintLog("CDVD interface: CDVDctrlTrayClose()"); +#endif /* VERBOSE_FUNCTION */ + + return(DeviceTrayClose()); +} // END CDVDctrlTrayClose() + + +void CALLBACK CDVDconfigure() { + ExecCfg("configure"); +} // END CDVDconfigure() + + +void CALLBACK CDVDabout() { + ExecCfg("about"); +} // END CDVDabout() + + +s32 CALLBACK CDVDtest() { + s32 s32result; + + errno = 0; + + if(devicehandle != -1) { +#ifdef VERBOSE_WARNINGS + PrintLog("CDVD interface: Device already open"); +#endif /* VERBOSE_WARNINGS */ + return(0); + } // ENDIF- Is the CD/DVD already in use? That's fine. + +#ifdef VERBOSE_FUNCTION + PrintLog("CDVD driver: CDVDtest()"); +#endif /* VERBOSE_FUNCTION */ + + s32result = DeviceOpen(); + DeviceClose(); + return(s32result); +} // END CDVDtest() diff --git a/plugins/CDVDlinuz/Src/Linux/aboutbox.c b/plugins/CDVDlinuz/Src/Linux/aboutbox.c index 1e29dc1900..357accf15a 100644 --- a/plugins/CDVDlinuz/Src/Linux/aboutbox.c +++ b/plugins/CDVDlinuz/Src/Linux/aboutbox.c @@ -1,212 +1,212 @@ -/* aboutbox.c - - * Copyright (C) 2002-2005 PCSX2 Team - - * - - * This program is free software; you can redistribute it and/or modify - - * it under the terms of the GNU General Public License as published by - - * the Free Software Foundation; either version 2 of the License, or - - * (at your option) any later version. - - * - - * This program 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 this program; if not, write to the Free Software - - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - * - - * PCSX2 members can be contacted through their website at www.pcsx2.net. - - */ - - - - - -#include // NULL - -#include // sprintf() - - - -#include // gtk_button_new_with_label() - -#include // gtk_container_add() - -#include // gtk_hbutton_box_new() - -#include // gtk_label_new() - -#include // gtk_init(), gtk_main(), gtk_main_quit() - -#include // gtk_vbox_new() - -#include // gtk_window_new() - - - -#include "version.h" - -#include "aboutbox.h" - - - - - -struct AboutBoxData aboutbox; - - - - - -gint AboutBoxCancelEvent(GtkWidget *widget, GdkEvent event, gpointer data) { - - if(aboutbox.window != NULL) { - - gtk_widget_destroy(aboutbox.window); - - aboutbox.window = NULL; - - } // ENDIF- Do we have an About Box still? - - - - gtk_main_quit(); - - return(TRUE); - -} // END AboutBoxCancelEvent() - - - - - -void AboutBoxDisplay() { - - GtkWidget *item; - - GtkWidget *container; - - GtkWidget *vbox1; - - char templine[256]; - - - - aboutbox.window = NULL; - - aboutbox.window = gtk_window_new(GTK_WINDOW_TOPLEVEL); - - gtk_container_set_border_width(GTK_CONTAINER(aboutbox.window), 5); - - gtk_window_set_title(GTK_WINDOW(aboutbox.window), "About CDVDlinuz"); - - gtk_window_set_position(GTK_WINDOW(aboutbox.window), GTK_WIN_POS_CENTER); - - gtk_window_set_modal(GTK_WINDOW(aboutbox.window), TRUE); - - gtk_window_set_resizable(GTK_WINDOW(aboutbox.window), FALSE); - - - - g_signal_connect(G_OBJECT(aboutbox.window), "delete_event", - - G_CALLBACK(AboutBoxCancelEvent), NULL); - - - - vbox1 = gtk_vbox_new(FALSE, 5); - - gtk_container_add(GTK_CONTAINER(aboutbox.window), vbox1); - - gtk_container_set_border_width(GTK_CONTAINER(vbox1), 5); - - gtk_widget_show(vbox1); - - - - sprintf(templine, "%s v%i.%i", libname, revision, build); - - item = gtk_label_new(templine); - - gtk_box_pack_start(GTK_BOX(vbox1), item, FALSE, FALSE, 0); - - gtk_widget_show(item); - - item = NULL; - - - - item = gtk_label_new("Current Author: efp"); - - gtk_box_pack_start(GTK_BOX(vbox1), item, FALSE, FALSE, 0); - - gtk_widget_show(item); - - item = NULL; - - - - item = gtk_label_new("Original code by: linuzappz & shadow"); - - gtk_box_pack_start(GTK_BOX(vbox1), item, FALSE, FALSE, 0); - - gtk_widget_show(item); - - item = NULL; - - - - container = gtk_hbutton_box_new(); - - gtk_box_pack_start(GTK_BOX(vbox1), container, TRUE, TRUE, 0); - - gtk_widget_show(container); - - - - item = gtk_button_new_with_label("Ok"); - - gtk_container_add(GTK_CONTAINER(container), item); - - GTK_WIDGET_SET_FLAGS(item, GTK_CAN_DEFAULT); - - gtk_widget_show(item); - - - - g_signal_connect(G_OBJECT(item), "clicked", - - G_CALLBACK(AboutBoxCancelEvent), NULL); - - item = NULL; - - container = NULL; - - vbox1 = NULL; - - - - gtk_widget_show(aboutbox.window); - - gtk_main(); - -} // END AboutDisplay() - +/* aboutbox.c + + * Copyright (C) 2002-2005 PCSX2 Team + + * + + * This program is free software; you can redistribute it and/or modify + + * it under the terms of the GNU General Public License as published by + + * the Free Software Foundation; either version 2 of the License, or + + * (at your option) any later version. + + * + + * This program 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 this program; if not, write to the Free Software + + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + * + + * PCSX2 members can be contacted through their website at www.pcsx2.net. + + */ + + + + + +#include // NULL + +#include // sprintf() + + + +#include // gtk_button_new_with_label() + +#include // gtk_container_add() + +#include // gtk_hbutton_box_new() + +#include // gtk_label_new() + +#include // gtk_init(), gtk_main(), gtk_main_quit() + +#include // gtk_vbox_new() + +#include // gtk_window_new() + + + +#include "version.h" + +#include "aboutbox.h" + + + + + +struct AboutBoxData aboutbox; + + + + + +gint AboutBoxCancelEvent(GtkWidget *widget, GdkEvent event, gpointer data) { + + if(aboutbox.window != NULL) { + + gtk_widget_destroy(aboutbox.window); + + aboutbox.window = NULL; + + } // ENDIF- Do we have an About Box still? + + + + gtk_main_quit(); + + return(TRUE); + +} // END AboutBoxCancelEvent() + + + + + +void AboutBoxDisplay() { + + GtkWidget *item; + + GtkWidget *container; + + GtkWidget *vbox1; + + char templine[256]; + + + + aboutbox.window = NULL; + + aboutbox.window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + + gtk_container_set_border_width(GTK_CONTAINER(aboutbox.window), 5); + + gtk_window_set_title(GTK_WINDOW(aboutbox.window), "About CDVDlinuz"); + + gtk_window_set_position(GTK_WINDOW(aboutbox.window), GTK_WIN_POS_CENTER); + + gtk_window_set_modal(GTK_WINDOW(aboutbox.window), TRUE); + + gtk_window_set_resizable(GTK_WINDOW(aboutbox.window), FALSE); + + + + g_signal_connect(G_OBJECT(aboutbox.window), "delete_event", + + G_CALLBACK(AboutBoxCancelEvent), NULL); + + + + vbox1 = gtk_vbox_new(FALSE, 5); + + gtk_container_add(GTK_CONTAINER(aboutbox.window), vbox1); + + gtk_container_set_border_width(GTK_CONTAINER(vbox1), 5); + + gtk_widget_show(vbox1); + + + + sprintf(templine, "%s v%i.%i", libname, revision, build); + + item = gtk_label_new(templine); + + gtk_box_pack_start(GTK_BOX(vbox1), item, FALSE, FALSE, 0); + + gtk_widget_show(item); + + item = NULL; + + + + item = gtk_label_new("Current Author: efp"); + + gtk_box_pack_start(GTK_BOX(vbox1), item, FALSE, FALSE, 0); + + gtk_widget_show(item); + + item = NULL; + + + + item = gtk_label_new("Original code by: linuzappz & shadow"); + + gtk_box_pack_start(GTK_BOX(vbox1), item, FALSE, FALSE, 0); + + gtk_widget_show(item); + + item = NULL; + + + + container = gtk_hbutton_box_new(); + + gtk_box_pack_start(GTK_BOX(vbox1), container, TRUE, TRUE, 0); + + gtk_widget_show(container); + + + + item = gtk_button_new_with_label("Ok"); + + gtk_container_add(GTK_CONTAINER(container), item); + + GTK_WIDGET_SET_FLAGS(item, GTK_CAN_DEFAULT); + + gtk_widget_show(item); + + + + g_signal_connect(G_OBJECT(item), "clicked", + + G_CALLBACK(AboutBoxCancelEvent), NULL); + + item = NULL; + + container = NULL; + + vbox1 = NULL; + + + + gtk_widget_show(aboutbox.window); + + gtk_main(); + +} // END AboutDisplay() + diff --git a/plugins/CDVDlinuz/Src/Linux/aboutbox.h b/plugins/CDVDlinuz/Src/Linux/aboutbox.h index feabf93790..875772f0c6 100644 --- a/plugins/CDVDlinuz/Src/Linux/aboutbox.h +++ b/plugins/CDVDlinuz/Src/Linux/aboutbox.h @@ -1,78 +1,78 @@ -/* aboutbox.h - - * Copyright (C) 2002-2005 PCSX2 Team - - * - - * This program is free software; you can redistribute it and/or modify - - * it under the terms of the GNU General Public License as published by - - * the Free Software Foundation; either version 2 of the License, or - - * (at your option) any later version. - - * - - * This program 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 this program; if not, write to the Free Software - - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - * - - * PCSX2 members can be contacted through their website at www.pcsx2.net. - - */ - - - - - -#ifndef ABOUTBOX_H - -#define ABOUTBOX_H - - - - - -#include - - - - - -struct AboutBoxData { - - GtkWidget *window; // GtkWindow - About Box - -}; - - - -extern struct AboutBoxData aboutbox; - - - - - -extern void AboutBoxDisplay(); - - - - - -#endif /* ABOUTBOX_H */ - +/* aboutbox.h + + * Copyright (C) 2002-2005 PCSX2 Team + + * + + * This program is free software; you can redistribute it and/or modify + + * it under the terms of the GNU General Public License as published by + + * the Free Software Foundation; either version 2 of the License, or + + * (at your option) any later version. + + * + + * This program 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 this program; if not, write to the Free Software + + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + * + + * PCSX2 members can be contacted through their website at www.pcsx2.net. + + */ + + + + + +#ifndef ABOUTBOX_H + +#define ABOUTBOX_H + + + + + +#include + + + + + +struct AboutBoxData { + + GtkWidget *window; // GtkWindow - About Box + +}; + + + +extern struct AboutBoxData aboutbox; + + + + + +extern void AboutBoxDisplay(); + + + + + +#endif /* ABOUTBOX_H */ + diff --git a/plugins/CDVDlinuz/Src/Linux/actualfile.c b/plugins/CDVDlinuz/Src/Linux/actualfile.c index 947cbe3a6c..07b47b3368 100644 --- a/plugins/CDVDlinuz/Src/Linux/actualfile.c +++ b/plugins/CDVDlinuz/Src/Linux/actualfile.c @@ -1,444 +1,444 @@ -/* actualfile.c - - * Copyright (C) 2002-2005 PCSX2 Team - - * - - * This program is free software; you can redistribute it and/or modify - - * it under the terms of the GNU General Public License as published by - - * the Free Software Foundation; either version 2 of the License, or - - * (at your option) any later version. - - * - - * This program 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 this program; if not, write to the Free Software - - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - * - - * PCSX2 members can be contacted through their website at www.pcsx2.net. - - */ - - - - - -#include // errno - -#include // open() - -#include // rename() - -#include // strerror() - -#include // stat64(), open(), fstat() - -#include // stat64(), open(), fstat(), lseek64() - -#include // stat64(), fstat(), lseek64(), read(), close(), write() - -// unlink() - - - -#include "logfile.h" - -#include "actualfile.h" - - - - - -int IsActualFile(const char *filename) { - - int retval; - - struct stat64 filestat; - - - -#ifdef VERBOSE_FUNCTION_ACTUALFILE - - PrintLog("CDVDiso file: IsActualFile(%s)", filename); - -#endif /* VERBOSE_FUNCTION_ACTUALFILE */ - - - - errno = 0; - - retval = stat64(filename, &filestat); - - if((retval < 0) || (errno != 0)) { - -#ifdef VERBOSE_WARNING_ACTUALFILE - - PrintLog("CDVDiso file: Error retrieving stats on %s", filename); - - PrintLog("CDVDiso file: %i:%s\n", errno, strerror(errno)); - -#endif /* VERBOSE_WARNING_ACTUALFILE */ - - return(-1); // Name doesn't exist. - - } // ENDIF- Trouble getting stat on a file? - - - - if(S_ISREG(filestat.st_mode) == 0) return(-2); // Not a regular file. - - return(0); // Yep, that's a file. - -} // END IsActualFile() - - - - - -void ActualFileDelete(const char *filename) { - -#ifdef VERBOSE_FUNCTION_ACTUALFILE - - PrintLog("CDVDiso file: ActualFileDelete(%s)", filename); - -#endif /* VERBOSE_FUNCTION_ACTUALFILE */ - - - - unlink(filename); - -} // END ActualFileDelete() - - - - - -void ActualFileRename(const char *origname, const char *newname) { - -#ifdef VERBOSE_FUNCTION_ACTUALFILE - - PrintLog("CDVDiso file: ActualFileRename(%s->%s)", origname, newname); - -#endif /* VERBOSE_FUNCTION_ACTUALFILE */ - - - - rename(origname, newname); - - return; - -} // END ActualFileRename() - - - - - -ACTUALHANDLE ActualFileOpenForRead(const char *filename) { - - int newhandle; - - - - if(filename == NULL) return(-1); - - - -#ifdef VERBOSE_FUNCTION_ACTUALFILE - - PrintLog("CDVDiso file: ActualFileOpenForRead(%s)", filename); - -#endif /* VERBOSE_FUNCTION_ACTUALFILE */ - - - - errno = 0; - - newhandle = open(filename, O_RDONLY | O_LARGEFILE); - - if((newhandle < 0) || (errno != 0)) { - -#ifdef VERBOSE_WARNING_ACTUALFILE - - PrintLog("CDVDiso file: Error opening file %s\n", filename); - - PrintLog("CDVDiso file: (%i) %i:%s\n", newhandle, errno, strerror(errno)); - -#endif /* VERBOSE_WARNING_ACTUALFILE */ - - return(-1); - - } // ENDIF- Error? Abort - - - - return(newhandle); - -} // END ActualFileOpenForRead() - - - - - -off64_t ActualFileSize(ACTUALHANDLE handle) { - - int retval; - - struct stat64 filestat; - - - -#ifdef VERBOSE_FUNCTION_ACTUALFILE - - PrintLog("CDVDiso file: ActualFileSize()\n"); - -#endif /* VERBOSE_FUNCTION_ACTUALFILE */ - - - - errno = 0; - - retval = fstat64(handle, &filestat); - - if((retval < 0) || (errno != 0)) return(-1); // Name doesn't exist. - - return(filestat.st_size); - -} // END ActualFileSize() - - - - - -int ActualFileSeek(ACTUALHANDLE handle, off64_t position) { - - off64_t moved; - - - - if(handle < 0) return(-1); - - if(position < 0) return(-1); // Maybe... position = 0? - - - -#ifdef VERBOSE_FUNCTION_ACTUALFILE - - PrintLog("CDVDiso file: ActualFileSeek(%lli)", position); - -#endif /* VERBOSE_FUNCTION_ACTUALFILE */ - - - - errno = 0; - - moved = lseek64(handle, position, SEEK_SET); - - if(errno != 0) { - -#ifdef VERBOSE_WARNING_ACTUALFILE - - PrintLog("CDVDiso file: Error on seek (%lli)", position); - - PrintLog("CDVDiso file: %i:%s\n", errno, strerror(errno)); - -#endif /* VERBOSE_WARNING_ACTUALFILE */ - - return(-1); - - } // ENDIF- Error? Abort - - - - return(0); - -} // END ActualFileSeek() - - - - - -int ActualFileRead(ACTUALHANDLE handle, int bytes, char *buffer) { - - int retval; - - - - if(handle == ACTUALHANDLENULL) return(-1); - - if(bytes < 1) return(-1); - - if(buffer == NULL) return(-1); - - - -#ifdef VERBOSE_FUNCTION_ACTUALFILE - - PrintLog("CDVDiso file: ActualFileRead(%i)", bytes); - -#endif /* VERBOSE_FUNCTION_ACTUALFILE */ - - - - errno = 0; - - retval = read(handle, buffer, bytes); - - if((retval < 0) || (errno != 0)) { - -#ifdef VERBOSE_WARNING_ACTUALFILE - - PrintLog("CDVDiso file: Error reading from file!"); - - PrintLog("CDVDiso file: %i:%s", errno, strerror(errno)); - -#endif /* VERBOSE_WARNING_ACTUALFILE */ - - // return(-1); - - } // ENDIF- Error? Abort - - - - return(retval); // Send back how many bytes read - -} // END ActualFileRead() - - - - - -void ActualFileClose(ACTUALHANDLE handle) { - - if(handle < 0) return; - - - -#ifdef VERBOSE_FUNCTION_ACTUALFILE - - PrintLog("CDVDiso file: ActualFileClose()"); - -#endif /* VERBOSE_FUNCTION_ACTUALFILE */ - - - - errno = 0; - - close(handle); - - return; - -} // END ActualFileClose() - - - - - -ACTUALHANDLE ActualFileOpenForWrite(const char *filename) { - - int newhandle; - - - - if(filename == NULL) return(-1); - - - -#ifdef VERBOSE_FUNCTION_ACTUALFILE - - PrintLog("CDVDiso file: ActualFileOpenForWrite(%s)", filename); - -#endif /* VERBOSE_FUNCTION_ACTUALFILE */ - - - - errno = 0; - - newhandle = open(filename, O_WRONLY | O_CREAT | O_LARGEFILE, 0644); - - if((newhandle < 0) || (errno != 0)) { - -#ifdef VERBOSE_WARNING_ACTUALFILE - - PrintLog("CDVDiso file: Error opening file %s", filename); - - PrintLog("CDVDiso file: (%i) %i:%s", newhandle, errno, strerror(errno)); - -#endif /* VERBOSE_WARNING_ACTUALFILE */ - - return(-1); - - } // ENDIF- Error? Abort - - - - return(newhandle); - -} // END ActualFileOpenForWrite() - - - - - -int ActualFileWrite(ACTUALHANDLE handle, int bytes, char *buffer) { - - int retval; - - - - if(handle < 0) return(-1); - - if(bytes < 1) return(-1); - - if(buffer == NULL) return(-1); - - - -#ifdef VERBOSE_FUNCTION_ACTUALFILE - - PrintLog("CDVDiso file: ActualFileWrite(%i)", bytes); - -#endif /* VERBOSE_FUNCTION_ACTUALFILE */ - - - - errno = 0; - - retval = write(handle, buffer, bytes); - - if((retval < 0) || (errno != 0)) { - -#ifdef VERBOSE_WARNING_ACTUALFILE - - PrintLog("CDVDiso file: Error writing to file!"); - - PrintLog("CDVDiso file: %i:%s", errno, strerror(errno)); - -#endif /* VERBOSE_WARNING_ACTUALFILE */ - - // return(-1); - - } // ENDIF- Error? Abort - - - - return(retval); // Send back how many bytes written - -} // END ActualFileWrite() - +/* actualfile.c + + * Copyright (C) 2002-2005 PCSX2 Team + + * + + * This program is free software; you can redistribute it and/or modify + + * it under the terms of the GNU General Public License as published by + + * the Free Software Foundation; either version 2 of the License, or + + * (at your option) any later version. + + * + + * This program 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 this program; if not, write to the Free Software + + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + * + + * PCSX2 members can be contacted through their website at www.pcsx2.net. + + */ + + + + + +#include // errno + +#include // open() + +#include // rename() + +#include // strerror() + +#include // stat64(), open(), fstat() + +#include // stat64(), open(), fstat(), lseek64() + +#include // stat64(), fstat(), lseek64(), read(), close(), write() + +// unlink() + + + +#include "logfile.h" + +#include "actualfile.h" + + + + + +int IsActualFile(const char *filename) { + + int retval; + + struct stat64 filestat; + + + +#ifdef VERBOSE_FUNCTION_ACTUALFILE + + PrintLog("CDVDiso file: IsActualFile(%s)", filename); + +#endif /* VERBOSE_FUNCTION_ACTUALFILE */ + + + + errno = 0; + + retval = stat64(filename, &filestat); + + if((retval < 0) || (errno != 0)) { + +#ifdef VERBOSE_WARNING_ACTUALFILE + + PrintLog("CDVDiso file: Error retrieving stats on %s", filename); + + PrintLog("CDVDiso file: %i:%s\n", errno, strerror(errno)); + +#endif /* VERBOSE_WARNING_ACTUALFILE */ + + return(-1); // Name doesn't exist. + + } // ENDIF- Trouble getting stat on a file? + + + + if(S_ISREG(filestat.st_mode) == 0) return(-2); // Not a regular file. + + return(0); // Yep, that's a file. + +} // END IsActualFile() + + + + + +void ActualFileDelete(const char *filename) { + +#ifdef VERBOSE_FUNCTION_ACTUALFILE + + PrintLog("CDVDiso file: ActualFileDelete(%s)", filename); + +#endif /* VERBOSE_FUNCTION_ACTUALFILE */ + + + + unlink(filename); + +} // END ActualFileDelete() + + + + + +void ActualFileRename(const char *origname, const char *newname) { + +#ifdef VERBOSE_FUNCTION_ACTUALFILE + + PrintLog("CDVDiso file: ActualFileRename(%s->%s)", origname, newname); + +#endif /* VERBOSE_FUNCTION_ACTUALFILE */ + + + + rename(origname, newname); + + return; + +} // END ActualFileRename() + + + + + +ACTUALHANDLE ActualFileOpenForRead(const char *filename) { + + int newhandle; + + + + if(filename == NULL) return(-1); + + + +#ifdef VERBOSE_FUNCTION_ACTUALFILE + + PrintLog("CDVDiso file: ActualFileOpenForRead(%s)", filename); + +#endif /* VERBOSE_FUNCTION_ACTUALFILE */ + + + + errno = 0; + + newhandle = open(filename, O_RDONLY | O_LARGEFILE); + + if((newhandle < 0) || (errno != 0)) { + +#ifdef VERBOSE_WARNING_ACTUALFILE + + PrintLog("CDVDiso file: Error opening file %s\n", filename); + + PrintLog("CDVDiso file: (%i) %i:%s\n", newhandle, errno, strerror(errno)); + +#endif /* VERBOSE_WARNING_ACTUALFILE */ + + return(-1); + + } // ENDIF- Error? Abort + + + + return(newhandle); + +} // END ActualFileOpenForRead() + + + + + +off64_t ActualFileSize(ACTUALHANDLE handle) { + + int retval; + + struct stat64 filestat; + + + +#ifdef VERBOSE_FUNCTION_ACTUALFILE + + PrintLog("CDVDiso file: ActualFileSize()\n"); + +#endif /* VERBOSE_FUNCTION_ACTUALFILE */ + + + + errno = 0; + + retval = fstat64(handle, &filestat); + + if((retval < 0) || (errno != 0)) return(-1); // Name doesn't exist. + + return(filestat.st_size); + +} // END ActualFileSize() + + + + + +int ActualFileSeek(ACTUALHANDLE handle, off64_t position) { + + off64_t moved; + + + + if(handle < 0) return(-1); + + if(position < 0) return(-1); // Maybe... position = 0? + + + +#ifdef VERBOSE_FUNCTION_ACTUALFILE + + PrintLog("CDVDiso file: ActualFileSeek(%lli)", position); + +#endif /* VERBOSE_FUNCTION_ACTUALFILE */ + + + + errno = 0; + + moved = lseek64(handle, position, SEEK_SET); + + if(errno != 0) { + +#ifdef VERBOSE_WARNING_ACTUALFILE + + PrintLog("CDVDiso file: Error on seek (%lli)", position); + + PrintLog("CDVDiso file: %i:%s\n", errno, strerror(errno)); + +#endif /* VERBOSE_WARNING_ACTUALFILE */ + + return(-1); + + } // ENDIF- Error? Abort + + + + return(0); + +} // END ActualFileSeek() + + + + + +int ActualFileRead(ACTUALHANDLE handle, int bytes, char *buffer) { + + int retval; + + + + if(handle == ACTUALHANDLENULL) return(-1); + + if(bytes < 1) return(-1); + + if(buffer == NULL) return(-1); + + + +#ifdef VERBOSE_FUNCTION_ACTUALFILE + + PrintLog("CDVDiso file: ActualFileRead(%i)", bytes); + +#endif /* VERBOSE_FUNCTION_ACTUALFILE */ + + + + errno = 0; + + retval = read(handle, buffer, bytes); + + if((retval < 0) || (errno != 0)) { + +#ifdef VERBOSE_WARNING_ACTUALFILE + + PrintLog("CDVDiso file: Error reading from file!"); + + PrintLog("CDVDiso file: %i:%s", errno, strerror(errno)); + +#endif /* VERBOSE_WARNING_ACTUALFILE */ + + // return(-1); + + } // ENDIF- Error? Abort + + + + return(retval); // Send back how many bytes read + +} // END ActualFileRead() + + + + + +void ActualFileClose(ACTUALHANDLE handle) { + + if(handle < 0) return; + + + +#ifdef VERBOSE_FUNCTION_ACTUALFILE + + PrintLog("CDVDiso file: ActualFileClose()"); + +#endif /* VERBOSE_FUNCTION_ACTUALFILE */ + + + + errno = 0; + + close(handle); + + return; + +} // END ActualFileClose() + + + + + +ACTUALHANDLE ActualFileOpenForWrite(const char *filename) { + + int newhandle; + + + + if(filename == NULL) return(-1); + + + +#ifdef VERBOSE_FUNCTION_ACTUALFILE + + PrintLog("CDVDiso file: ActualFileOpenForWrite(%s)", filename); + +#endif /* VERBOSE_FUNCTION_ACTUALFILE */ + + + + errno = 0; + + newhandle = open(filename, O_WRONLY | O_CREAT | O_LARGEFILE, 0644); + + if((newhandle < 0) || (errno != 0)) { + +#ifdef VERBOSE_WARNING_ACTUALFILE + + PrintLog("CDVDiso file: Error opening file %s", filename); + + PrintLog("CDVDiso file: (%i) %i:%s", newhandle, errno, strerror(errno)); + +#endif /* VERBOSE_WARNING_ACTUALFILE */ + + return(-1); + + } // ENDIF- Error? Abort + + + + return(newhandle); + +} // END ActualFileOpenForWrite() + + + + + +int ActualFileWrite(ACTUALHANDLE handle, int bytes, char *buffer) { + + int retval; + + + + if(handle < 0) return(-1); + + if(bytes < 1) return(-1); + + if(buffer == NULL) return(-1); + + + +#ifdef VERBOSE_FUNCTION_ACTUALFILE + + PrintLog("CDVDiso file: ActualFileWrite(%i)", bytes); + +#endif /* VERBOSE_FUNCTION_ACTUALFILE */ + + + + errno = 0; + + retval = write(handle, buffer, bytes); + + if((retval < 0) || (errno != 0)) { + +#ifdef VERBOSE_WARNING_ACTUALFILE + + PrintLog("CDVDiso file: Error writing to file!"); + + PrintLog("CDVDiso file: %i:%s", errno, strerror(errno)); + +#endif /* VERBOSE_WARNING_ACTUALFILE */ + + // return(-1); + + } // ENDIF- Error? Abort + + + + return(retval); // Send back how many bytes written + +} // END ActualFileWrite() + diff --git a/plugins/CDVDlinuz/Src/Linux/conf.c b/plugins/CDVDlinuz/Src/Linux/conf.c index 6183a871d1..c8b5e30a3a 100644 --- a/plugins/CDVDlinuz/Src/Linux/conf.c +++ b/plugins/CDVDlinuz/Src/Linux/conf.c @@ -1,184 +1,184 @@ -/* conf.c - * Copyright (C) 2002-2005 PCSX2 Team - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * PCSX2 members can be contacted through their website at www.pcsx2.net. - */ - -#include // errno -#include // NULL -#include // sprintf() -#include // getenv() -#include // strerror() -#include // mkdir(), stat() -#include // mkdir(), stat() -#include // stat() - -// #define CDVDdefs -// #include "../PS2Edefs.h" -#include "logfile.h" -#include "../ini.h" -#include "conf.h" - - -const char *cfgname[] = { \ - "./cfg/cfgCDVDlinuz", \ - "../cfg/cfgCDVDlinuz", \ - "./plugins/cfgCDVDlinuz", \ - "../plugins/cfgCDVDlinuz", \ - "./cfgCDVDlinuz", \ - "../cfgCDVDlinuz", \ - NULL }; - -const char *confnames[] = { "Device", NULL }; -const u8 defaultdevice[] = DEFAULT_DEVICE; -const char defaulthome[] = "../inis"; -const char defaultdirectory[] = ".PS2E"; -const char defaultfile[] = "CDVDlinuz.ini"; - -char confdirname[256]; -char conffilename[256]; - -CDVDconf conf; - - -void ExecCfg(char *arg) { - int nameptr; - struct stat filestat; - char templine[256]; - -#ifdef VERBOSE_FUNCTION_CONF - PrintLog("CDVDiso interface: ExecCfg(%s)", arg); -#endif /* VERBOSE FUNCTION_CONF */ - errno = 0; - nameptr = 0; - while((cfgname[nameptr] != NULL) && - (stat(cfgname[nameptr], &filestat) == -1)) nameptr++; - errno = 0; - - if(cfgname[nameptr] == NULL) { -#ifdef VERBOSE_FUNCTION_CONF - PrintLog("CDVDiso interface: Couldn't find configuration program!"); -#endif /* VERBOSE_FUNCTION_CONF */ - return; - } // ENDIF- Did not find the executable? - - sprintf(templine, "%s %s", cfgname[nameptr], arg); - system(templine); -} // END ExecCfg() - - -void InitConf() { - int i; - int pos; - char *envptr; - -#ifdef VERBOSE_FUNCTION_CONF - PrintLog("CDVD config: InitConf()"); -#endif /* VERBOSE_FUNCTION_CONF */ - - i = 0; - while((i < 255) && defaultdevice[i] != 0) { - conf.devicename[i] = defaultdevice[i]; - i++; - } // ENDWHILE- copying the default CD/DVD name in - conf.devicename[i] = 0; // 0-terminate the device name - - // Locating directory and file positions - pos = 0; - envptr = getenv("HOME"); - if(envptr == NULL) { - // = - i = 0; - while((pos < 253) && (defaulthome[i] != 0)) { - confdirname[pos] = defaulthome[i]; - conffilename[pos] = defaulthome[i]; - pos++; - i++; - } // NEXT- putting a default place to store configuration data - - } else { - // = / - i = 0; - while((pos < 253) && (*(envptr + i) != 0)) { - confdirname[pos] = *(envptr + i); - conffilename[pos] = *(envptr + i); - pos++; - i++; - } // ENDWHILE- copying home directory info in - - if(confdirname[pos-1] != '/') { - confdirname[pos] = '/'; - conffilename[pos] = '/'; - pos++; - } // ENDIF- No directory separator here? Add one. - - i = 0; - while((pos < 253) && (defaultdirectory[i] != 0)) { - confdirname[pos] = defaultdirectory[i]; - conffilename[pos] = defaultdirectory[i]; - pos++; - i++; - } // NEXT- putting a default place to store configuration data - } // ENDIF- No Home directory? - - confdirname[pos] = 0; // Directory reference finished - - // += / - if(conffilename[pos-1] != '/') { - conffilename[pos] = '/'; - pos++; - } // ENDIF- No directory separator here? Add one. - - i = 0; - while((pos < 253) && (defaultfile[i] != 0)) { - conffilename[pos] = defaultfile[i]; - pos++; - i++; - } // NEXT- putting a default place to store configuration data - - conffilename[pos] = 0; // File reference finished - -#ifdef VERBOSE_FUNCTION_CONF - PrintLog("CDVD config: Directory: %s\n", confdirname); - PrintLog("CDVD config: File: %s\n", conffilename); -#endif /* VERBOSE_FUNCTION_CONF */ -} // END InitConf() - - -void LoadConf() { - int retval; - -#ifdef VERBOSE_FUNCTION_CONF - PrintLog("CDVD config: LoadConf()\n"); -#endif /* VERBOSE_FUNCTION_CONF */ - - retval = INILoadString(conffilename, "Settings", "Device", conf.devicename); - if(retval < 0) { - sprintf(conf.devicename, "/dev/dvd"); - } // ENDIF- Couldn't find keyword? Fill in a default -} // END LoadConf() - - -void SaveConf() { -#ifdef VERBOSE_FUNCTION_CONF - PrintLog("CDVD config: SaveConf()\n"); -#endif /* VERBOSE_FUNCTION_CONF */ - - mkdir(confdirname, 0755); - - INISaveString(conffilename, "Settings", "Device", conf.devicename); -} // END SaveConf() +/* conf.c + * Copyright (C) 2002-2005 PCSX2 Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * PCSX2 members can be contacted through their website at www.pcsx2.net. + */ + +#include // errno +#include // NULL +#include // sprintf() +#include // getenv() +#include // strerror() +#include // mkdir(), stat() +#include // mkdir(), stat() +#include // stat() + +// #define CDVDdefs +// #include "../PS2Edefs.h" +#include "logfile.h" +#include "../ini.h" +#include "conf.h" + + +const char *cfgname[] = { \ + "./cfg/cfgCDVDlinuz", \ + "../cfg/cfgCDVDlinuz", \ + "./plugins/cfgCDVDlinuz", \ + "../plugins/cfgCDVDlinuz", \ + "./cfgCDVDlinuz", \ + "../cfgCDVDlinuz", \ + NULL }; + +const char *confnames[] = { "Device", NULL }; +const u8 defaultdevice[] = DEFAULT_DEVICE; +const char defaulthome[] = "../inis"; +const char defaultdirectory[] = ".PS2E"; +const char defaultfile[] = "CDVDlinuz.ini"; + +char confdirname[256]; +char conffilename[256]; + +CDVDconf conf; + + +void ExecCfg(char *arg) { + int nameptr; + struct stat filestat; + char templine[256]; + +#ifdef VERBOSE_FUNCTION_CONF + PrintLog("CDVDiso interface: ExecCfg(%s)", arg); +#endif /* VERBOSE FUNCTION_CONF */ + errno = 0; + nameptr = 0; + while((cfgname[nameptr] != NULL) && + (stat(cfgname[nameptr], &filestat) == -1)) nameptr++; + errno = 0; + + if(cfgname[nameptr] == NULL) { +#ifdef VERBOSE_FUNCTION_CONF + PrintLog("CDVDiso interface: Couldn't find configuration program!"); +#endif /* VERBOSE_FUNCTION_CONF */ + return; + } // ENDIF- Did not find the executable? + + sprintf(templine, "%s %s", cfgname[nameptr], arg); + system(templine); +} // END ExecCfg() + + +void InitConf() { + int i; + int pos; + char *envptr; + +#ifdef VERBOSE_FUNCTION_CONF + PrintLog("CDVD config: InitConf()"); +#endif /* VERBOSE_FUNCTION_CONF */ + + i = 0; + while((i < 255) && defaultdevice[i] != 0) { + conf.devicename[i] = defaultdevice[i]; + i++; + } // ENDWHILE- copying the default CD/DVD name in + conf.devicename[i] = 0; // 0-terminate the device name + + // Locating directory and file positions + pos = 0; + envptr = getenv("HOME"); + if(envptr == NULL) { + // = + i = 0; + while((pos < 253) && (defaulthome[i] != 0)) { + confdirname[pos] = defaulthome[i]; + conffilename[pos] = defaulthome[i]; + pos++; + i++; + } // NEXT- putting a default place to store configuration data + + } else { + // = / + i = 0; + while((pos < 253) && (*(envptr + i) != 0)) { + confdirname[pos] = *(envptr + i); + conffilename[pos] = *(envptr + i); + pos++; + i++; + } // ENDWHILE- copying home directory info in + + if(confdirname[pos-1] != '/') { + confdirname[pos] = '/'; + conffilename[pos] = '/'; + pos++; + } // ENDIF- No directory separator here? Add one. + + i = 0; + while((pos < 253) && (defaultdirectory[i] != 0)) { + confdirname[pos] = defaultdirectory[i]; + conffilename[pos] = defaultdirectory[i]; + pos++; + i++; + } // NEXT- putting a default place to store configuration data + } // ENDIF- No Home directory? + + confdirname[pos] = 0; // Directory reference finished + + // += / + if(conffilename[pos-1] != '/') { + conffilename[pos] = '/'; + pos++; + } // ENDIF- No directory separator here? Add one. + + i = 0; + while((pos < 253) && (defaultfile[i] != 0)) { + conffilename[pos] = defaultfile[i]; + pos++; + i++; + } // NEXT- putting a default place to store configuration data + + conffilename[pos] = 0; // File reference finished + +#ifdef VERBOSE_FUNCTION_CONF + PrintLog("CDVD config: Directory: %s\n", confdirname); + PrintLog("CDVD config: File: %s\n", conffilename); +#endif /* VERBOSE_FUNCTION_CONF */ +} // END InitConf() + + +void LoadConf() { + int retval; + +#ifdef VERBOSE_FUNCTION_CONF + PrintLog("CDVD config: LoadConf()\n"); +#endif /* VERBOSE_FUNCTION_CONF */ + + retval = INILoadString(conffilename, "Settings", "Device", conf.devicename); + if(retval < 0) { + sprintf(conf.devicename, "/dev/dvd"); + } // ENDIF- Couldn't find keyword? Fill in a default +} // END LoadConf() + + +void SaveConf() { +#ifdef VERBOSE_FUNCTION_CONF + PrintLog("CDVD config: SaveConf()\n"); +#endif /* VERBOSE_FUNCTION_CONF */ + + mkdir(confdirname, 0755); + + INISaveString(conffilename, "Settings", "Device", conf.devicename); +} // END SaveConf() diff --git a/plugins/CDVDlinuz/Src/Linux/interface.c b/plugins/CDVDlinuz/Src/Linux/interface.c index 006c623350..987fd761eb 100644 --- a/plugins/CDVDlinuz/Src/Linux/interface.c +++ b/plugins/CDVDlinuz/Src/Linux/interface.c @@ -1,114 +1,114 @@ -/* interface.c - - * Copyright (C) 2002-2005 PCSX2 Team - - * - - * This program is free software; you can redistribute it and/or modify - - * it under the terms of the GNU General Public License as published by - - * the Free Software Foundation; either version 2 of the License, or - - * (at your option) any later version. - - * - - * This program 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 this program; if not, write to the Free Software - - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - * - - * PCSX2 members can be contacted through their website at www.pcsx2.net. - - */ - - - - - -#include // NULL - -#include // sprintf() - -#include // strcmp() - - - -#include // gtk_init(), gtk_main(), gtk_main_quit() - -#include // gtk_widget_show_all() - - - -#include "logfile.h" - -#include "conf.h" - -#include "aboutbox.h" - -#include "mainbox.h" - - - - - -int main(int argc, char *argv[]) { - - if(argc != 2) return(1); - - - - gtk_init(NULL, NULL); - - - - if(!strcmp(argv[1], "about")) { - - AboutBoxDisplay(); - - return(0); - - - - } else if (!strcmp(argv[1], "configure")) { - - OpenLog(); - - InitConf(); - - LoadConf(); - - MainBoxDisplay(); - - - - gtk_widget_show_all(mainbox.window); - - gtk_main(); - - CloseLog(); - - return(0); - - } // ENDLONGIF- Which display would you like to see? - - - - return(1); // No Displays chosen? Abort! - -} // END main() - +/* interface.c + + * Copyright (C) 2002-2005 PCSX2 Team + + * + + * This program is free software; you can redistribute it and/or modify + + * it under the terms of the GNU General Public License as published by + + * the Free Software Foundation; either version 2 of the License, or + + * (at your option) any later version. + + * + + * This program 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 this program; if not, write to the Free Software + + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + * + + * PCSX2 members can be contacted through their website at www.pcsx2.net. + + */ + + + + + +#include // NULL + +#include // sprintf() + +#include // strcmp() + + + +#include // gtk_init(), gtk_main(), gtk_main_quit() + +#include // gtk_widget_show_all() + + + +#include "logfile.h" + +#include "conf.h" + +#include "aboutbox.h" + +#include "mainbox.h" + + + + + +int main(int argc, char *argv[]) { + + if(argc != 2) return(1); + + + + gtk_init(NULL, NULL); + + + + if(!strcmp(argv[1], "about")) { + + AboutBoxDisplay(); + + return(0); + + + + } else if (!strcmp(argv[1], "configure")) { + + OpenLog(); + + InitConf(); + + LoadConf(); + + MainBoxDisplay(); + + + + gtk_widget_show_all(mainbox.window); + + gtk_main(); + + CloseLog(); + + return(0); + + } // ENDLONGIF- Which display would you like to see? + + + + return(1); // No Displays chosen? Abort! + +} // END main() + diff --git a/plugins/CDVDlinuz/Src/Linux/logfile.c b/plugins/CDVDlinuz/Src/Linux/logfile.c index 8fe86e3c51..9c1a2348f5 100644 --- a/plugins/CDVDlinuz/Src/Linux/logfile.c +++ b/plugins/CDVDlinuz/Src/Linux/logfile.c @@ -1,180 +1,180 @@ -/* logfile.c - - * Copyright (C) 2002-2005 PCSX2 Team - - * - - * This program is free software; you can redistribute it and/or modify - - * it under the terms of the GNU General Public License as published by - - * the Free Software Foundation; either version 2 of the License, or - - * (at your option) any later version. - - * - - * This program 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 this program; if not, write to the Free Software - - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - * - - * PCSX2 members can be contacted through their website at www.pcsx2.net. - - */ - - - - - -#include // open - -#include // vsprintf() - -#include // va_start(), va_end(), vsprintf() - -#include // mkdir(), open() - -#include // mkdir(), open() - -#include // close(), write(), unlink() - - - -#include "logfile.h" - - - - - -int logfile; - -char logfiletemp[2048]; - - - - - -void InitLog() { - - // Token comment line - -#ifdef VERBOSE_LOGFILE - - mkdir("./logs", 0755); - - - - unlink("./logs/CDVDlog.txt"); - -#endif /* VERBOSE LOGFILE */ - -} // END InitLog(); - - - - - -int OpenLog() { - - // Token comment line - -#ifdef VERBOSE_LOGFILE - - logfile = -1; - - logfile = open("./logs/CDVDlog.txt", O_WRONLY | O_CREAT | O_APPEND, 0755); - - if(logfile == -1) return(-1); - -#endif /* VERBOSE LOGFILE */ - - - - return(0); - -} // END OpenLog(); - - - - - -void CloseLog() { - - // Token comment line - -#ifdef VERBOSE_LOGFILE - - if(logfile != -1) { - - close(logfile); - - logfile = -1; - - } // ENDIF- Is the log file actually open? Close it. - -#endif /* VERBOSE LOGFILE */ - -} // END CloseLog() - - - - - -void PrintLog(const char *fmt, ...) { - - // Token comment line - -#ifdef VERBOSE_LOGFILE - - va_list list; - - int len; - - - - if(logfile == -1) return; // Log file not open. - - - - va_start(list, fmt); - - vsprintf(logfiletemp, fmt, list); - - va_end(list); - - - - len = 0; - - while((len < 2048) && (logfiletemp[len] != 0)) len++; - - if((len > 0) && (logfiletemp[len-1] == '\n')) len--; - - if((len > 0) && (logfiletemp[len-1] == '\r')) len--; - - logfiletemp[len] = 0; // Slice off the last "\r\n"... - - - - write(logfile, logfiletemp, len); - - write(logfile, "\r\n", 2); // ... and write out your own. - -#endif /* VERBOSE LOGFILE */ - -} // END PrintLog() - +/* logfile.c + + * Copyright (C) 2002-2005 PCSX2 Team + + * + + * This program is free software; you can redistribute it and/or modify + + * it under the terms of the GNU General Public License as published by + + * the Free Software Foundation; either version 2 of the License, or + + * (at your option) any later version. + + * + + * This program 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 this program; if not, write to the Free Software + + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + * + + * PCSX2 members can be contacted through their website at www.pcsx2.net. + + */ + + + + + +#include // open + +#include // vsprintf() + +#include // va_start(), va_end(), vsprintf() + +#include // mkdir(), open() + +#include // mkdir(), open() + +#include // close(), write(), unlink() + + + +#include "logfile.h" + + + + + +int logfile; + +char logfiletemp[2048]; + + + + + +void InitLog() { + + // Token comment line + +#ifdef VERBOSE_LOGFILE + + mkdir("./logs", 0755); + + + + unlink("./logs/CDVDlog.txt"); + +#endif /* VERBOSE LOGFILE */ + +} // END InitLog(); + + + + + +int OpenLog() { + + // Token comment line + +#ifdef VERBOSE_LOGFILE + + logfile = -1; + + logfile = open("./logs/CDVDlog.txt", O_WRONLY | O_CREAT | O_APPEND, 0755); + + if(logfile == -1) return(-1); + +#endif /* VERBOSE LOGFILE */ + + + + return(0); + +} // END OpenLog(); + + + + + +void CloseLog() { + + // Token comment line + +#ifdef VERBOSE_LOGFILE + + if(logfile != -1) { + + close(logfile); + + logfile = -1; + + } // ENDIF- Is the log file actually open? Close it. + +#endif /* VERBOSE LOGFILE */ + +} // END CloseLog() + + + + + +void PrintLog(const char *fmt, ...) { + + // Token comment line + +#ifdef VERBOSE_LOGFILE + + va_list list; + + int len; + + + + if(logfile == -1) return; // Log file not open. + + + + va_start(list, fmt); + + vsprintf(logfiletemp, fmt, list); + + va_end(list); + + + + len = 0; + + while((len < 2048) && (logfiletemp[len] != 0)) len++; + + if((len > 0) && (logfiletemp[len-1] == '\n')) len--; + + if((len > 0) && (logfiletemp[len-1] == '\r')) len--; + + logfiletemp[len] = 0; // Slice off the last "\r\n"... + + + + write(logfile, logfiletemp, len); + + write(logfile, "\r\n", 2); // ... and write out your own. + +#endif /* VERBOSE LOGFILE */ + +} // END PrintLog() + diff --git a/plugins/CDVDlinuz/Src/Linux/mainbox.c b/plugins/CDVDlinuz/Src/Linux/mainbox.c index 89e39f1ddf..95aadc41f3 100644 --- a/plugins/CDVDlinuz/Src/Linux/mainbox.c +++ b/plugins/CDVDlinuz/Src/Linux/mainbox.c @@ -1,374 +1,374 @@ -/* mainbox.c - - * Copyright (C) 2002-2005 PCSX2 Team - - * - - * This program is free software; you can redistribute it and/or modify - - * it under the terms of the GNU General Public License as published by - - * the Free Software Foundation; either version 2 of the License, or - - * (at your option) any later version. - - * - - * This program 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 this program; if not, write to the Free Software - - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - * - - * PCSX2 members can be contacted through their website at www.pcsx2.net. - - */ - - - - - -#include // NULL - -#include // sprintf() - -#include // strcpy() - -#include // stat() - -#include // stat() - -#include // stat() - - - -#include // gtk_button_new_with_label() - -#include // gtk_container_add() - -#include // gtk_entry_new() - -#include // gtk_hbutton_box_new() - -#include // gtk_hbox_new() - -#include // gtk_label_new() - -#include // gtk_init(), gtk_main(), gtk_main_quit() - -#include // gtk_vbox_new() - -#include // gtk_window_new() - - - -#include "conf.h" - -// #include "logfile.h" - -#include "device.h" // DeviceOpen(), DeviceClose() - -#include "mainbox.h" - - - - - -struct MainBoxData mainbox; - - - - - -void MainBoxDestroy() { - - if(mainbox.window != NULL) { - - gtk_widget_destroy(mainbox.window); - - mainbox.window = NULL; - - mainbox.device = NULL; - - mainbox.desc = NULL; - - } // ENDIF- Do we have a Main Window still? - -} // END MainBoxDestroy() - - - - - -void MainBoxUnfocus() { - - gtk_widget_set_sensitive(mainbox.device, FALSE); - - gtk_window_iconify(GTK_WINDOW(mainbox.window)); - -} // END MainBoxUnfocus() - - - - - -gint MainBoxDeviceEvent(GtkWidget *widget, GdkEvent event, gpointer data) { - - struct stat filestat; - - int retval; - - - - retval = stat(gtk_entry_get_text(GTK_ENTRY(mainbox.device)), &filestat); - - if(retval == -1) { - - gtk_label_set_text(GTK_LABEL(mainbox.desc), "Device Type: ---"); - - return(TRUE); - - } // ENDIF- Not a name of any sort? - - - - if(S_ISDIR(filestat.st_mode) != 0) { - - gtk_label_set_text(GTK_LABEL(mainbox.desc), "Device Type: Not a device"); - - return(TRUE); - - } // ENDIF- Not a regular file? - - - - gtk_label_set_text(GTK_LABEL(mainbox.desc), "Device Type: Device Likely"); - - return(TRUE); - -} // END MainBoxFileEvent() - - - - - -void MainBoxRefocus() { - - GdkEvent event; - - - - MainBoxDeviceEvent(NULL, event, NULL); - - - - gtk_widget_set_sensitive(mainbox.device, TRUE); - - gtk_window_set_focus(GTK_WINDOW(mainbox.window), mainbox.device); - - gtk_window_deiconify(GTK_WINDOW(mainbox.window)); - -} // END MainBoxRefocus() - - - - - -gint MainBoxCancelEvent(GtkWidget *widget, GdkEvent event, gpointer data) { - - MainBoxDestroy(); - - - - gtk_main_quit(); - - return(TRUE); - -} // END MainBoxCancelEvent() - - - - - -gint MainBoxOKEvent(GtkWidget *widget, GdkEvent event, gpointer data) { - - const char *tempdevice; - - int retval; - - - - MainBoxUnfocus(); - - - - tempdevice = gtk_entry_get_text(GTK_ENTRY(mainbox.device)); - - strcpy(conf.devicename, tempdevice); // Temporarily put in new device name - - tempdevice = NULL; - - if(*(conf.devicename) != 0) { - - retval = DeviceOpen(); // Test by opening the device. - - DeviceClose(); // Failed or not, close it. - - if(retval != 0) { - - MainBoxRefocus(); - - return(TRUE); - - } // ENDIF- Not an ISO file? Message and Stop here. - - } // ENDIF- Is there an ISO file to check out? - - - - SaveConf(); - - - - MainBoxCancelEvent(widget, event, data); - - return(TRUE); - -} // END MainBoxOKEvent() - - - - - -void MainBoxDisplay() { - - GtkWidget *item; - - GtkWidget *hbox1; - - GtkWidget *vbox1; - - - - mainbox.window = gtk_window_new(GTK_WINDOW_TOPLEVEL); - - gtk_container_set_border_width(GTK_CONTAINER(mainbox.window), 5); - - gtk_window_set_title(GTK_WINDOW(mainbox.window), "CDVDlinuz Configuration"); - - gtk_window_set_position(GTK_WINDOW(mainbox.window), GTK_WIN_POS_CENTER); - - - - g_signal_connect(G_OBJECT(mainbox.window), "delete_event", - - G_CALLBACK(MainBoxCancelEvent), NULL); - - - - vbox1 = gtk_vbox_new(FALSE, 5); - - gtk_container_add(GTK_CONTAINER(mainbox.window), vbox1); - - gtk_container_set_border_width(GTK_CONTAINER(vbox1), 5); - - gtk_widget_show(vbox1); - - - - hbox1 = gtk_hbox_new(FALSE, 10); - - gtk_box_pack_start(GTK_BOX(vbox1), hbox1, TRUE, TRUE, 0); - - gtk_widget_show(hbox1); - - - - item = gtk_label_new("CD/DVD Device:"); - - gtk_box_pack_start(GTK_BOX(hbox1), item, FALSE, FALSE, 0); - - gtk_widget_show(item); - - item = NULL; - - - - mainbox.device = gtk_entry_new(); - - gtk_box_pack_start(GTK_BOX(hbox1), mainbox.device, TRUE, TRUE, 0); - - gtk_widget_show(mainbox.device); - - g_signal_connect(G_OBJECT(mainbox.device), "changed", - - G_CALLBACK(MainBoxDeviceEvent), NULL); - - hbox1 = NULL; - - - - mainbox.desc = gtk_label_new("File Type: ---"); - - gtk_box_pack_start(GTK_BOX(vbox1), mainbox.desc, FALSE, FALSE, 0); - - gtk_widget_show(mainbox.desc); - - - - hbox1 = gtk_hbutton_box_new(); - - gtk_box_pack_start(GTK_BOX(vbox1), hbox1, TRUE, TRUE, 0); - - gtk_widget_show(hbox1); - - - - item = gtk_button_new_with_label("Ok"); - - gtk_box_pack_start(GTK_BOX(hbox1), item, TRUE, TRUE, 0); - - gtk_widget_show(item); - - g_signal_connect(G_OBJECT(item), "clicked", - - G_CALLBACK(MainBoxOKEvent), NULL); - - - - item = gtk_button_new_with_label("Cancel"); - - gtk_box_pack_start(GTK_BOX(hbox1), item, TRUE, TRUE, 0); - - gtk_widget_show(item); - - g_signal_connect(G_OBJECT(item), "clicked", - - G_CALLBACK(MainBoxCancelEvent), NULL); - - item = NULL; - - hbox1 = NULL; - - vbox1 = NULL; - - - - // We held off setting the name until now... so description would show. - - gtk_entry_set_text(GTK_ENTRY(mainbox.device), conf.devicename); - -} // END MainBoxDisplay() - +/* mainbox.c + + * Copyright (C) 2002-2005 PCSX2 Team + + * + + * This program is free software; you can redistribute it and/or modify + + * it under the terms of the GNU General Public License as published by + + * the Free Software Foundation; either version 2 of the License, or + + * (at your option) any later version. + + * + + * This program 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 this program; if not, write to the Free Software + + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + * + + * PCSX2 members can be contacted through their website at www.pcsx2.net. + + */ + + + + + +#include // NULL + +#include // sprintf() + +#include // strcpy() + +#include // stat() + +#include // stat() + +#include // stat() + + + +#include // gtk_button_new_with_label() + +#include // gtk_container_add() + +#include // gtk_entry_new() + +#include // gtk_hbutton_box_new() + +#include // gtk_hbox_new() + +#include // gtk_label_new() + +#include // gtk_init(), gtk_main(), gtk_main_quit() + +#include // gtk_vbox_new() + +#include // gtk_window_new() + + + +#include "conf.h" + +// #include "logfile.h" + +#include "device.h" // DeviceOpen(), DeviceClose() + +#include "mainbox.h" + + + + + +struct MainBoxData mainbox; + + + + + +void MainBoxDestroy() { + + if(mainbox.window != NULL) { + + gtk_widget_destroy(mainbox.window); + + mainbox.window = NULL; + + mainbox.device = NULL; + + mainbox.desc = NULL; + + } // ENDIF- Do we have a Main Window still? + +} // END MainBoxDestroy() + + + + + +void MainBoxUnfocus() { + + gtk_widget_set_sensitive(mainbox.device, FALSE); + + gtk_window_iconify(GTK_WINDOW(mainbox.window)); + +} // END MainBoxUnfocus() + + + + + +gint MainBoxDeviceEvent(GtkWidget *widget, GdkEvent event, gpointer data) { + + struct stat filestat; + + int retval; + + + + retval = stat(gtk_entry_get_text(GTK_ENTRY(mainbox.device)), &filestat); + + if(retval == -1) { + + gtk_label_set_text(GTK_LABEL(mainbox.desc), "Device Type: ---"); + + return(TRUE); + + } // ENDIF- Not a name of any sort? + + + + if(S_ISDIR(filestat.st_mode) != 0) { + + gtk_label_set_text(GTK_LABEL(mainbox.desc), "Device Type: Not a device"); + + return(TRUE); + + } // ENDIF- Not a regular file? + + + + gtk_label_set_text(GTK_LABEL(mainbox.desc), "Device Type: Device Likely"); + + return(TRUE); + +} // END MainBoxFileEvent() + + + + + +void MainBoxRefocus() { + + GdkEvent event; + + + + MainBoxDeviceEvent(NULL, event, NULL); + + + + gtk_widget_set_sensitive(mainbox.device, TRUE); + + gtk_window_set_focus(GTK_WINDOW(mainbox.window), mainbox.device); + + gtk_window_deiconify(GTK_WINDOW(mainbox.window)); + +} // END MainBoxRefocus() + + + + + +gint MainBoxCancelEvent(GtkWidget *widget, GdkEvent event, gpointer data) { + + MainBoxDestroy(); + + + + gtk_main_quit(); + + return(TRUE); + +} // END MainBoxCancelEvent() + + + + + +gint MainBoxOKEvent(GtkWidget *widget, GdkEvent event, gpointer data) { + + const char *tempdevice; + + int retval; + + + + MainBoxUnfocus(); + + + + tempdevice = gtk_entry_get_text(GTK_ENTRY(mainbox.device)); + + strcpy(conf.devicename, tempdevice); // Temporarily put in new device name + + tempdevice = NULL; + + if(*(conf.devicename) != 0) { + + retval = DeviceOpen(); // Test by opening the device. + + DeviceClose(); // Failed or not, close it. + + if(retval != 0) { + + MainBoxRefocus(); + + return(TRUE); + + } // ENDIF- Not an ISO file? Message and Stop here. + + } // ENDIF- Is there an ISO file to check out? + + + + SaveConf(); + + + + MainBoxCancelEvent(widget, event, data); + + return(TRUE); + +} // END MainBoxOKEvent() + + + + + +void MainBoxDisplay() { + + GtkWidget *item; + + GtkWidget *hbox1; + + GtkWidget *vbox1; + + + + mainbox.window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + + gtk_container_set_border_width(GTK_CONTAINER(mainbox.window), 5); + + gtk_window_set_title(GTK_WINDOW(mainbox.window), "CDVDlinuz Configuration"); + + gtk_window_set_position(GTK_WINDOW(mainbox.window), GTK_WIN_POS_CENTER); + + + + g_signal_connect(G_OBJECT(mainbox.window), "delete_event", + + G_CALLBACK(MainBoxCancelEvent), NULL); + + + + vbox1 = gtk_vbox_new(FALSE, 5); + + gtk_container_add(GTK_CONTAINER(mainbox.window), vbox1); + + gtk_container_set_border_width(GTK_CONTAINER(vbox1), 5); + + gtk_widget_show(vbox1); + + + + hbox1 = gtk_hbox_new(FALSE, 10); + + gtk_box_pack_start(GTK_BOX(vbox1), hbox1, TRUE, TRUE, 0); + + gtk_widget_show(hbox1); + + + + item = gtk_label_new("CD/DVD Device:"); + + gtk_box_pack_start(GTK_BOX(hbox1), item, FALSE, FALSE, 0); + + gtk_widget_show(item); + + item = NULL; + + + + mainbox.device = gtk_entry_new(); + + gtk_box_pack_start(GTK_BOX(hbox1), mainbox.device, TRUE, TRUE, 0); + + gtk_widget_show(mainbox.device); + + g_signal_connect(G_OBJECT(mainbox.device), "changed", + + G_CALLBACK(MainBoxDeviceEvent), NULL); + + hbox1 = NULL; + + + + mainbox.desc = gtk_label_new("File Type: ---"); + + gtk_box_pack_start(GTK_BOX(vbox1), mainbox.desc, FALSE, FALSE, 0); + + gtk_widget_show(mainbox.desc); + + + + hbox1 = gtk_hbutton_box_new(); + + gtk_box_pack_start(GTK_BOX(vbox1), hbox1, TRUE, TRUE, 0); + + gtk_widget_show(hbox1); + + + + item = gtk_button_new_with_label("Ok"); + + gtk_box_pack_start(GTK_BOX(hbox1), item, TRUE, TRUE, 0); + + gtk_widget_show(item); + + g_signal_connect(G_OBJECT(item), "clicked", + + G_CALLBACK(MainBoxOKEvent), NULL); + + + + item = gtk_button_new_with_label("Cancel"); + + gtk_box_pack_start(GTK_BOX(hbox1), item, TRUE, TRUE, 0); + + gtk_widget_show(item); + + g_signal_connect(G_OBJECT(item), "clicked", + + G_CALLBACK(MainBoxCancelEvent), NULL); + + item = NULL; + + hbox1 = NULL; + + vbox1 = NULL; + + + + // We held off setting the name until now... so description would show. + + gtk_entry_set_text(GTK_ENTRY(mainbox.device), conf.devicename); + +} // END MainBoxDisplay() + diff --git a/plugins/CDVDlinuz/Src/Win32/CD.c b/plugins/CDVDlinuz/Src/Win32/CD.c index d70edb089b..feae4b7678 100644 --- a/plugins/CDVDlinuz/Src/Win32/CD.c +++ b/plugins/CDVDlinuz/Src/Win32/CD.c @@ -1,774 +1,774 @@ -/* CD.c - - * Copyright (C) 2002-2005 PCSX2 Team - - * - - * This program is free software; you can redistribute it and/or modify - - * it under the terms of the GNU General Public License as published by - - * the Free Software Foundation; either version 2 of the License, or - - * (at your option) any later version. - - * - - * This program 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 this program; if not, write to the Free Software - - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - * - - * PCSX2 members can be contacted through their website at www.pcsx2.net. - - */ - - - - - -#include - -#include // IOCTL_CDROM... - - - -#include // off64_t - - - -#define CDVDdefs - -#include "PS2Edefs.h" - - - -#include "logfile.h" - -#include "../convert.h" // MSFtoLBA(), HEXTOBCD() - -#include "actualfile.h" - -#include "device.h" // tocbuffer[], FinishCommand() - -#include "CD.h" - - - - - -int actualcdmode; // -1=ReadFile, 0=YellowMode2, 1=XAForm2, 2=CDDA - -DWORD cdblocksize; // 2048 to 2352 - -int cdoffset; // 0, 8, 16, or 24 - -int cdmode; - - - - - -void InitCDInfo() { - - actualcdmode = -1; - - cdblocksize = 2048; - - cdmode = -1; - -} // END InitCDInfo() - - - - - -s32 CDreadTrackPass(u32 lsn, int mode, u8 *buffer) { - - RAW_READ_INFO rawinfo; - - BOOL boolresult; - - DWORD byteswritten; - - DWORD errcode; - - LARGE_INTEGER targetpos; - - int i; - - - - if((actualcdmode < -1) || (actualcdmode > 2)) return(-1); - - - -#ifdef VERBOSE_FUNCTION_DEVICE - - PrintLog("CDVDlinuz CD: CDreadTrack(%llu, %i)", lsn, actualcdmode); - -#endif /* VERBOSE_FUNCTION_DEVICE */ - - - - if(actualcdmode >= 0) { - - rawinfo.DiskOffset.QuadPart = lsn * 2048; // Yes, 2048. - - rawinfo.SectorCount = 1; - - rawinfo.TrackMode = actualcdmode; - - boolresult = DeviceIoControl(devicehandle, - - IOCTL_CDROM_RAW_READ, - - &rawinfo, - - sizeof(RAW_READ_INFO), - - buffer, - - 2352, - - &byteswritten, - - &waitevent); - - errcode = FinishCommand(boolresult); - - - - if(errcode != 0) { - -#ifdef VERBOSE_WARNING_DEVICE - - PrintLog("CDVDlinuz CD: Couldn't read a sector raw!"); - - PrintError("CDVDlinuz CD", errcode); - -#endif /* VERBOSE_WARNING_DEVICE */ - - return(-1); - - } // ENDIF- Couldn't read a raw sector? Maybe not a CD. - - - - } else { - - targetpos.QuadPart = lsn * 2048; - - waitevent.Offset = targetpos.LowPart; - - waitevent.OffsetHigh = targetpos.HighPart; - - - - boolresult = ReadFile(devicehandle, - - buffer + 24, - - 2048, - - &byteswritten, - - &waitevent); - - errcode = FinishCommand(boolresult); - - - - if(errcode != 0) { - -#ifdef VERBOSE_WARNING_DEVICE - - PrintLog("CDVDlinuz CD: Couldn't read a cooked sector!"); - - PrintError("CDVDlinuz CD", errcode); - -#endif /* VERBOSE_WARNING_DEVICE */ - - return(-1); - - } // ENDIF- Trouble with seek? Report it. - - - - for(i = 0; i < 24; i++) *(buffer + i) = 0; - - for(i = 24 + 2048; i < 2352; i++) *(buffer + i) = 0; - - } // ENDIF- Could we read a raw sector? Read another one! - - - - if(boolresult == FALSE) { - - boolresult = GetOverlappedResult(devicehandle, - - &waitevent, - - &byteswritten, - - FALSE); - - } // ENDIF- Did the initial call not complete? Get byteswritten for - - // the completed call. - - - - if(byteswritten < 2048) { - -#ifdef VERBOSE_WARNING_DEVICE - - errcode = GetLastError(); - - PrintLog("CDVDlinuz CD: Short block! only got %u out of %u bytes", - - byteswritten, cdblocksize); - - PrintError("CDVDlinuz CD", errcode); - -#endif /* VERBOSE_WARNING_DEVICE */ - - return(-1); - - } // ENDIF- Couldn't read a raw sector? Maybe not a CD. - - - - cdmode = mode; - - cdblocksize = byteswritten; - - return(0); - -} // END CDreadTrackPass() - - - - - -s32 CDreadTrack(u32 lsn, int mode, u8 *buffer) { - - int retval; - - int lastmode; - - int i; - - - - retval = CDreadTrackPass(lsn, mode, buffer); - - if(retval >= 0) return(retval); - - - -#ifdef VERBOSE_WARNING_DEVICE - - PrintLog("CDVDlinuz CD: Same mode doesn't work. Scanning..."); - -#endif /* VERBOSE_WARNING_DEVICE */ - - - - lastmode = actualcdmode; - - actualcdmode = 2; - - while(actualcdmode >= -1) { - - retval = CDreadTrackPass(lsn, mode, buffer); - - if(retval >= 0) return(retval); - - actualcdmode--; - - } // ENDWHILE- Searching each mode for a way to read the sector - - actualcdmode = lastmode; // None worked? Go back to first mode. - - - -#ifdef VERBOSE_WARNING_DEVICE - - PrintLog("CDVDlinuz CD: No modes work. Failing sector!"); - -#endif /* VERBOSE_WARNING_DEVICE */ - - - - for(i = 0; i < 2352; i++) *(buffer + i) = 0; - - return(-1); - -} // END CDreadTrack() - - - - - -s32 CDgetBufferOffset() { - -#ifdef VERBOSE_FUNCTION_DEVICE - - PrintLog("CDVD CD: CDgetBufferOffset()"); - -#endif /* VERBOSE_FUNCTION_DEVICE */ - - - - // Send a whole CDDA record in? - - if((actualcdmode == CDDA) && (cdmode == CDVD_MODE_2352)) return(0); - - - - // Otherwise, send the start of the data block in... - - return(cdoffset); - -} // END CDgetBufferOffset() - - - - - -s32 CDreadSubQ() { - - return(-1); - -} // END CDreadSubQ() - - - - - -s32 CDgetTN(cdvdTN *cdvdtn) { - - cdvdtn->strack = BCDTOHEX(tocbuffer[7]); - - cdvdtn->etrack = BCDTOHEX(tocbuffer[17]); - - return(0); - -} // END CDgetTN() - - - - - -s32 CDgetTD(u8 newtrack, cdvdTD *cdvdtd) { - - u8 actualtrack; - - int pos; - - char temptime[3]; - - - -#ifdef VERBOSE_FUNCTION_DEVICE - - PrintLog("CDVDlinuz CD: CDgetTD()"); - -#endif /* VERBOSE_FUNCTION_DEVICE */ - - - - actualtrack = newtrack; - - if(actualtrack == 0xaa) actualtrack = 0; - - - - if(actualtrack == 0) { - - cdvdtd->type = 0; - - temptime[0] = BCDTOHEX(tocbuffer[27]); - - temptime[1] = BCDTOHEX(tocbuffer[28]); - - temptime[2] = BCDTOHEX(tocbuffer[29]); - - cdvdtd->lsn = MSFtoLBA(temptime); - - } else { - - pos = actualtrack * 10; - - pos += 30; - - cdvdtd->type = tocbuffer[pos]; - - temptime[0] = BCDTOHEX(tocbuffer[pos + 7]); - - temptime[1] = BCDTOHEX(tocbuffer[pos + 8]); - - temptime[2] = BCDTOHEX(tocbuffer[pos + 9]); - - cdvdtd->lsn = MSFtoLBA(temptime); - - } // ENDIF- Whole disc? (or single track?) - - - - return(0); - -} // END CDgetTD() - - - - - -s32 CDgetDiskType() { - - CDROM_TOC cdinfo; - - BOOL boolresult; - - int retval; - - DWORD byteswritten; - - DWORD errcode; - - u8 iso9660name[] = "CD001\0"; - - u8 playstationname[] = "PLAYSTATION\0"; - - u8 ps1name[] = "CD-XA001\0"; - - u8 tempbuffer[2448]; - - int tempdisctype; - - int i; - - int pos; - - unsigned long volumesize; - - - -#ifdef VERBOSE_FUNCTION_DEVICE - - PrintLog("CDVDlinuz CD: CDgetDiskType()"); - -#endif /* VERBOSE_FUNCTION_DEVICE */ - - - - tempdisctype = CDVD_TYPE_UNKNOWN; - - - - actualcdmode = 2; - - retval = CDreadTrack(16, CDVD_MODE_2048, tempbuffer); - - if(retval < 0) { - - disctype = tempdisctype; - - return(-1); - - } // ENDIF- Couldn't read the directory sector? Abort. - - - - disctype = CDVD_TYPE_DETCTCD; - - tempdisctype = CDVD_TYPE_CDDA; - - - - cdoffset = 0; - - i = 0; - - while((cdoffset <= 24) && (iso9660name[i] != 0)) { - - i = 0; - - while((iso9660name[i] != 0) && - - (iso9660name[i] == tempbuffer[cdoffset + 1 + i])) i++; - - if(iso9660name[i] != 0) cdoffset += 8; - - } // ENDWHILE- Trying to find a working offset for a ISO9660 record. - - - - if(iso9660name[i] != 0) { - -#ifdef VERBOSE_DISC_TYPE - - PrintLog("Detected a CDDA (Music CD)."); - -#endif /* VERBOSE_DISC_TYPE */ - - tempdisctype = CDVD_TYPE_CDDA; - - tocbuffer[0] = 0x01; - - - - } else { - - tocbuffer[0] = 0x41; - - i = 0; - - while((playstationname[i] != 0) && - - (playstationname[i] == tempbuffer[cdoffset + 8 + i])) i++; - - if(playstationname[i] != 0) { - -#ifdef VERBOSE_DISC_TYPE - - PrintLog("Detected a non-Playstation CD."); - -#endif /* VERBOSE_DISC_TYPE */ - - tempdisctype = CDVD_TYPE_UNKNOWN; - - - - } else { - - i = 0; - - while((ps1name[i] != 0) && - - (ps1name[i] == tempbuffer[cdoffset + 1024 + i])) i++; - - if(ps1name[i] != 0) { - -#ifdef VERBOSE_DISC_TYPE - - PrintLog("Detected a Playstation 2 CD."); - -#endif /* VERBOSE_DISC_TYPE */ - - tempdisctype = CDVD_TYPE_PS2CD; - - } else { - -#ifdef VERBOSE_DISC_TYPE - - PrintLog("Detected a Playstation CD."); - -#endif /* VERBOSE_DISC_TYPE */ - - tempdisctype = CDVD_TYPE_PSCD; - - } // ENDIF- Is this not a PlayStation Disc? - - } // ENDIF- Is this not a PlayStation Disc? - - } // ENDIF- Is this not an ISO9660 CD? (a CDDA, in other words?) - - - - // Build the Fake TOC - - tocbuffer[2] = 0xA0; - - tocbuffer[7] = HEXTOBCD(1); // Starting Track - - tocbuffer[12] = 0xA1; - - tocbuffer[17] = HEXTOBCD(1); // Ending Track - - - - volumesize = tempbuffer[84]; // Volume size (big endian) - - volumesize *= 256; - - volumesize += tempbuffer[85]; - - volumesize *= 256; - - volumesize += tempbuffer[86]; - - volumesize *= 256; - - volumesize += tempbuffer[87]; - -#ifdef VERBOSE_DISC_INFO - - if(tempdisctype != CDVD_TYPE_CDDA) { - - PrintLog("CDVDlinuz CD: ISO9660 size %llu", volumesize); - - } // ENDIF- Data CD? Print size in blocks. - -#endif /* VERBOSE_DISC_INFO */ - - - - LBAtoMSF(volumesize, &tocbuffer[27]); - - tocbuffer[27] = HEXTOBCD(tocbuffer[27]); - - tocbuffer[28] = HEXTOBCD(tocbuffer[28]); - - tocbuffer[29] = HEXTOBCD(tocbuffer[29]); - - - - tocbuffer[40] = 0x02; // Data Mode - - tocbuffer[42] = 0x01; // Track # - - LBAtoMSF(0, &tocbuffer[47]); - - tocbuffer[47] = HEXTOBCD(tocbuffer[47]); - - tocbuffer[48] = HEXTOBCD(tocbuffer[48]); - - tocbuffer[49] = HEXTOBCD(tocbuffer[49]); - - - - // Can we get the REAL TOC? - - boolresult = DeviceIoControl(devicehandle, - - IOCTL_CDROM_READ_TOC, - - NULL, - - 0, - - &cdinfo, - - sizeof(CDROM_TOC), - - &byteswritten, - - NULL); - - - - if(boolresult == FALSE) { - -#ifdef VERBOSE_WARNING_DEVICE - - errcode = GetLastError(); - - PrintLog("CDVDlinuz CD: Can't get TOC!"); - - PrintError("CDVDlinuz CD", errcode); - -#endif /* VERBOSE_WARNING_DEVICE */ - - disctype = tempdisctype; - - return(disctype); - - } // ENDIF- Can't read the TOC? Accept the fake TOC then. - - - - // Fill in the pieces of the REAL TOC. - -#ifdef VERBOSE_DISC_INFO - - PrintLog("CDVDlinuz CD: TOC First Track: %u Last Track: %u", - - cdinfo.FirstTrack, cdinfo.LastTrack); - -#endif /* VERBOSE_DISC_INFO */ - - tocbuffer[7] = HEXTOBCD(cdinfo.FirstTrack); - - tocbuffer[17] = HEXTOBCD(cdinfo.LastTrack); - - - - // for(i = cdinfo.FirstTrack; i <= cdinfo.LastTrack; i++) { - - for(i = 0; i <= cdinfo.LastTrack - cdinfo.FirstTrack; i++) { - -#ifdef VERBOSE_DISC_INFO - - PrintLog("CDVDlinuz CD: TOC Track %u Disc Size: %u:%u.%u Data Mode %u", - - cdinfo.TrackData[i].TrackNumber, - - cdinfo.TrackData[i].Address[1] * 1, - - cdinfo.TrackData[i].Address[2] * 1, - - cdinfo.TrackData[i].Address[3] * 1, - - cdinfo.TrackData[i].Control * 1); - -#endif /* VERBOSE_DISC_INFO */ - - pos = i * 10 + 40; - - tocbuffer[pos] = cdinfo.TrackData[i].Control; - - tocbuffer[pos + 2] = HEXTOBCD(i + 1); - - tocbuffer[pos + 7] = HEXTOBCD(cdinfo.TrackData[i].Address[1]); - - tocbuffer[pos + 8] = HEXTOBCD(cdinfo.TrackData[i].Address[2]); - - tocbuffer[pos + 9] = HEXTOBCD(cdinfo.TrackData[i].Address[3]); - - } // NEXT i- Transferring Track data to the PS2 TOC - - - - - -#ifdef VERBOSE_DISC_INFO - - PrintLog("CDVDlinuz CD: TOC Disc Size: %u:%u.%u", - - cdinfo.TrackData[i].Address[1] * 1, - - cdinfo.TrackData[i].Address[2] * 1, - - cdinfo.TrackData[i].Address[3] * 1); - -#endif /* VERBOSE_DISC_INFO */ - - tocbuffer[27] = HEXTOBCD(cdinfo.TrackData[i].Address[1]); - - tocbuffer[28] = HEXTOBCD(cdinfo.TrackData[i].Address[2]); - - tocbuffer[29] = HEXTOBCD(cdinfo.TrackData[i].Address[3]); - - - - disctype = tempdisctype; - - return(disctype); - -} // END CDgetDiskType() - +/* CD.c + + * Copyright (C) 2002-2005 PCSX2 Team + + * + + * This program is free software; you can redistribute it and/or modify + + * it under the terms of the GNU General Public License as published by + + * the Free Software Foundation; either version 2 of the License, or + + * (at your option) any later version. + + * + + * This program 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 this program; if not, write to the Free Software + + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + * + + * PCSX2 members can be contacted through their website at www.pcsx2.net. + + */ + + + + + +#include + +#include // IOCTL_CDROM... + + + +#include // off64_t + + + +#define CDVDdefs + +#include "PS2Edefs.h" + + + +#include "logfile.h" + +#include "../convert.h" // MSFtoLBA(), HEXTOBCD() + +#include "actualfile.h" + +#include "device.h" // tocbuffer[], FinishCommand() + +#include "CD.h" + + + + + +int actualcdmode; // -1=ReadFile, 0=YellowMode2, 1=XAForm2, 2=CDDA + +DWORD cdblocksize; // 2048 to 2352 + +int cdoffset; // 0, 8, 16, or 24 + +int cdmode; + + + + + +void InitCDInfo() { + + actualcdmode = -1; + + cdblocksize = 2048; + + cdmode = -1; + +} // END InitCDInfo() + + + + + +s32 CDreadTrackPass(u32 lsn, int mode, u8 *buffer) { + + RAW_READ_INFO rawinfo; + + BOOL boolresult; + + DWORD byteswritten; + + DWORD errcode; + + LARGE_INTEGER targetpos; + + int i; + + + + if((actualcdmode < -1) || (actualcdmode > 2)) return(-1); + + + +#ifdef VERBOSE_FUNCTION_DEVICE + + PrintLog("CDVDlinuz CD: CDreadTrack(%llu, %i)", lsn, actualcdmode); + +#endif /* VERBOSE_FUNCTION_DEVICE */ + + + + if(actualcdmode >= 0) { + + rawinfo.DiskOffset.QuadPart = lsn * 2048; // Yes, 2048. + + rawinfo.SectorCount = 1; + + rawinfo.TrackMode = actualcdmode; + + boolresult = DeviceIoControl(devicehandle, + + IOCTL_CDROM_RAW_READ, + + &rawinfo, + + sizeof(RAW_READ_INFO), + + buffer, + + 2352, + + &byteswritten, + + &waitevent); + + errcode = FinishCommand(boolresult); + + + + if(errcode != 0) { + +#ifdef VERBOSE_WARNING_DEVICE + + PrintLog("CDVDlinuz CD: Couldn't read a sector raw!"); + + PrintError("CDVDlinuz CD", errcode); + +#endif /* VERBOSE_WARNING_DEVICE */ + + return(-1); + + } // ENDIF- Couldn't read a raw sector? Maybe not a CD. + + + + } else { + + targetpos.QuadPart = lsn * 2048; + + waitevent.Offset = targetpos.LowPart; + + waitevent.OffsetHigh = targetpos.HighPart; + + + + boolresult = ReadFile(devicehandle, + + buffer + 24, + + 2048, + + &byteswritten, + + &waitevent); + + errcode = FinishCommand(boolresult); + + + + if(errcode != 0) { + +#ifdef VERBOSE_WARNING_DEVICE + + PrintLog("CDVDlinuz CD: Couldn't read a cooked sector!"); + + PrintError("CDVDlinuz CD", errcode); + +#endif /* VERBOSE_WARNING_DEVICE */ + + return(-1); + + } // ENDIF- Trouble with seek? Report it. + + + + for(i = 0; i < 24; i++) *(buffer + i) = 0; + + for(i = 24 + 2048; i < 2352; i++) *(buffer + i) = 0; + + } // ENDIF- Could we read a raw sector? Read another one! + + + + if(boolresult == FALSE) { + + boolresult = GetOverlappedResult(devicehandle, + + &waitevent, + + &byteswritten, + + FALSE); + + } // ENDIF- Did the initial call not complete? Get byteswritten for + + // the completed call. + + + + if(byteswritten < 2048) { + +#ifdef VERBOSE_WARNING_DEVICE + + errcode = GetLastError(); + + PrintLog("CDVDlinuz CD: Short block! only got %u out of %u bytes", + + byteswritten, cdblocksize); + + PrintError("CDVDlinuz CD", errcode); + +#endif /* VERBOSE_WARNING_DEVICE */ + + return(-1); + + } // ENDIF- Couldn't read a raw sector? Maybe not a CD. + + + + cdmode = mode; + + cdblocksize = byteswritten; + + return(0); + +} // END CDreadTrackPass() + + + + + +s32 CDreadTrack(u32 lsn, int mode, u8 *buffer) { + + int retval; + + int lastmode; + + int i; + + + + retval = CDreadTrackPass(lsn, mode, buffer); + + if(retval >= 0) return(retval); + + + +#ifdef VERBOSE_WARNING_DEVICE + + PrintLog("CDVDlinuz CD: Same mode doesn't work. Scanning..."); + +#endif /* VERBOSE_WARNING_DEVICE */ + + + + lastmode = actualcdmode; + + actualcdmode = 2; + + while(actualcdmode >= -1) { + + retval = CDreadTrackPass(lsn, mode, buffer); + + if(retval >= 0) return(retval); + + actualcdmode--; + + } // ENDWHILE- Searching each mode for a way to read the sector + + actualcdmode = lastmode; // None worked? Go back to first mode. + + + +#ifdef VERBOSE_WARNING_DEVICE + + PrintLog("CDVDlinuz CD: No modes work. Failing sector!"); + +#endif /* VERBOSE_WARNING_DEVICE */ + + + + for(i = 0; i < 2352; i++) *(buffer + i) = 0; + + return(-1); + +} // END CDreadTrack() + + + + + +s32 CDgetBufferOffset() { + +#ifdef VERBOSE_FUNCTION_DEVICE + + PrintLog("CDVD CD: CDgetBufferOffset()"); + +#endif /* VERBOSE_FUNCTION_DEVICE */ + + + + // Send a whole CDDA record in? + + if((actualcdmode == CDDA) && (cdmode == CDVD_MODE_2352)) return(0); + + + + // Otherwise, send the start of the data block in... + + return(cdoffset); + +} // END CDgetBufferOffset() + + + + + +s32 CDreadSubQ() { + + return(-1); + +} // END CDreadSubQ() + + + + + +s32 CDgetTN(cdvdTN *cdvdtn) { + + cdvdtn->strack = BCDTOHEX(tocbuffer[7]); + + cdvdtn->etrack = BCDTOHEX(tocbuffer[17]); + + return(0); + +} // END CDgetTN() + + + + + +s32 CDgetTD(u8 newtrack, cdvdTD *cdvdtd) { + + u8 actualtrack; + + int pos; + + char temptime[3]; + + + +#ifdef VERBOSE_FUNCTION_DEVICE + + PrintLog("CDVDlinuz CD: CDgetTD()"); + +#endif /* VERBOSE_FUNCTION_DEVICE */ + + + + actualtrack = newtrack; + + if(actualtrack == 0xaa) actualtrack = 0; + + + + if(actualtrack == 0) { + + cdvdtd->type = 0; + + temptime[0] = BCDTOHEX(tocbuffer[27]); + + temptime[1] = BCDTOHEX(tocbuffer[28]); + + temptime[2] = BCDTOHEX(tocbuffer[29]); + + cdvdtd->lsn = MSFtoLBA(temptime); + + } else { + + pos = actualtrack * 10; + + pos += 30; + + cdvdtd->type = tocbuffer[pos]; + + temptime[0] = BCDTOHEX(tocbuffer[pos + 7]); + + temptime[1] = BCDTOHEX(tocbuffer[pos + 8]); + + temptime[2] = BCDTOHEX(tocbuffer[pos + 9]); + + cdvdtd->lsn = MSFtoLBA(temptime); + + } // ENDIF- Whole disc? (or single track?) + + + + return(0); + +} // END CDgetTD() + + + + + +s32 CDgetDiskType() { + + CDROM_TOC cdinfo; + + BOOL boolresult; + + int retval; + + DWORD byteswritten; + + DWORD errcode; + + u8 iso9660name[] = "CD001\0"; + + u8 playstationname[] = "PLAYSTATION\0"; + + u8 ps1name[] = "CD-XA001\0"; + + u8 tempbuffer[2448]; + + int tempdisctype; + + int i; + + int pos; + + unsigned long volumesize; + + + +#ifdef VERBOSE_FUNCTION_DEVICE + + PrintLog("CDVDlinuz CD: CDgetDiskType()"); + +#endif /* VERBOSE_FUNCTION_DEVICE */ + + + + tempdisctype = CDVD_TYPE_UNKNOWN; + + + + actualcdmode = 2; + + retval = CDreadTrack(16, CDVD_MODE_2048, tempbuffer); + + if(retval < 0) { + + disctype = tempdisctype; + + return(-1); + + } // ENDIF- Couldn't read the directory sector? Abort. + + + + disctype = CDVD_TYPE_DETCTCD; + + tempdisctype = CDVD_TYPE_CDDA; + + + + cdoffset = 0; + + i = 0; + + while((cdoffset <= 24) && (iso9660name[i] != 0)) { + + i = 0; + + while((iso9660name[i] != 0) && + + (iso9660name[i] == tempbuffer[cdoffset + 1 + i])) i++; + + if(iso9660name[i] != 0) cdoffset += 8; + + } // ENDWHILE- Trying to find a working offset for a ISO9660 record. + + + + if(iso9660name[i] != 0) { + +#ifdef VERBOSE_DISC_TYPE + + PrintLog("Detected a CDDA (Music CD)."); + +#endif /* VERBOSE_DISC_TYPE */ + + tempdisctype = CDVD_TYPE_CDDA; + + tocbuffer[0] = 0x01; + + + + } else { + + tocbuffer[0] = 0x41; + + i = 0; + + while((playstationname[i] != 0) && + + (playstationname[i] == tempbuffer[cdoffset + 8 + i])) i++; + + if(playstationname[i] != 0) { + +#ifdef VERBOSE_DISC_TYPE + + PrintLog("Detected a non-Playstation CD."); + +#endif /* VERBOSE_DISC_TYPE */ + + tempdisctype = CDVD_TYPE_UNKNOWN; + + + + } else { + + i = 0; + + while((ps1name[i] != 0) && + + (ps1name[i] == tempbuffer[cdoffset + 1024 + i])) i++; + + if(ps1name[i] != 0) { + +#ifdef VERBOSE_DISC_TYPE + + PrintLog("Detected a Playstation 2 CD."); + +#endif /* VERBOSE_DISC_TYPE */ + + tempdisctype = CDVD_TYPE_PS2CD; + + } else { + +#ifdef VERBOSE_DISC_TYPE + + PrintLog("Detected a Playstation CD."); + +#endif /* VERBOSE_DISC_TYPE */ + + tempdisctype = CDVD_TYPE_PSCD; + + } // ENDIF- Is this not a PlayStation Disc? + + } // ENDIF- Is this not a PlayStation Disc? + + } // ENDIF- Is this not an ISO9660 CD? (a CDDA, in other words?) + + + + // Build the Fake TOC + + tocbuffer[2] = 0xA0; + + tocbuffer[7] = HEXTOBCD(1); // Starting Track + + tocbuffer[12] = 0xA1; + + tocbuffer[17] = HEXTOBCD(1); // Ending Track + + + + volumesize = tempbuffer[84]; // Volume size (big endian) + + volumesize *= 256; + + volumesize += tempbuffer[85]; + + volumesize *= 256; + + volumesize += tempbuffer[86]; + + volumesize *= 256; + + volumesize += tempbuffer[87]; + +#ifdef VERBOSE_DISC_INFO + + if(tempdisctype != CDVD_TYPE_CDDA) { + + PrintLog("CDVDlinuz CD: ISO9660 size %llu", volumesize); + + } // ENDIF- Data CD? Print size in blocks. + +#endif /* VERBOSE_DISC_INFO */ + + + + LBAtoMSF(volumesize, &tocbuffer[27]); + + tocbuffer[27] = HEXTOBCD(tocbuffer[27]); + + tocbuffer[28] = HEXTOBCD(tocbuffer[28]); + + tocbuffer[29] = HEXTOBCD(tocbuffer[29]); + + + + tocbuffer[40] = 0x02; // Data Mode + + tocbuffer[42] = 0x01; // Track # + + LBAtoMSF(0, &tocbuffer[47]); + + tocbuffer[47] = HEXTOBCD(tocbuffer[47]); + + tocbuffer[48] = HEXTOBCD(tocbuffer[48]); + + tocbuffer[49] = HEXTOBCD(tocbuffer[49]); + + + + // Can we get the REAL TOC? + + boolresult = DeviceIoControl(devicehandle, + + IOCTL_CDROM_READ_TOC, + + NULL, + + 0, + + &cdinfo, + + sizeof(CDROM_TOC), + + &byteswritten, + + NULL); + + + + if(boolresult == FALSE) { + +#ifdef VERBOSE_WARNING_DEVICE + + errcode = GetLastError(); + + PrintLog("CDVDlinuz CD: Can't get TOC!"); + + PrintError("CDVDlinuz CD", errcode); + +#endif /* VERBOSE_WARNING_DEVICE */ + + disctype = tempdisctype; + + return(disctype); + + } // ENDIF- Can't read the TOC? Accept the fake TOC then. + + + + // Fill in the pieces of the REAL TOC. + +#ifdef VERBOSE_DISC_INFO + + PrintLog("CDVDlinuz CD: TOC First Track: %u Last Track: %u", + + cdinfo.FirstTrack, cdinfo.LastTrack); + +#endif /* VERBOSE_DISC_INFO */ + + tocbuffer[7] = HEXTOBCD(cdinfo.FirstTrack); + + tocbuffer[17] = HEXTOBCD(cdinfo.LastTrack); + + + + // for(i = cdinfo.FirstTrack; i <= cdinfo.LastTrack; i++) { + + for(i = 0; i <= cdinfo.LastTrack - cdinfo.FirstTrack; i++) { + +#ifdef VERBOSE_DISC_INFO + + PrintLog("CDVDlinuz CD: TOC Track %u Disc Size: %u:%u.%u Data Mode %u", + + cdinfo.TrackData[i].TrackNumber, + + cdinfo.TrackData[i].Address[1] * 1, + + cdinfo.TrackData[i].Address[2] * 1, + + cdinfo.TrackData[i].Address[3] * 1, + + cdinfo.TrackData[i].Control * 1); + +#endif /* VERBOSE_DISC_INFO */ + + pos = i * 10 + 40; + + tocbuffer[pos] = cdinfo.TrackData[i].Control; + + tocbuffer[pos + 2] = HEXTOBCD(i + 1); + + tocbuffer[pos + 7] = HEXTOBCD(cdinfo.TrackData[i].Address[1]); + + tocbuffer[pos + 8] = HEXTOBCD(cdinfo.TrackData[i].Address[2]); + + tocbuffer[pos + 9] = HEXTOBCD(cdinfo.TrackData[i].Address[3]); + + } // NEXT i- Transferring Track data to the PS2 TOC + + + + + +#ifdef VERBOSE_DISC_INFO + + PrintLog("CDVDlinuz CD: TOC Disc Size: %u:%u.%u", + + cdinfo.TrackData[i].Address[1] * 1, + + cdinfo.TrackData[i].Address[2] * 1, + + cdinfo.TrackData[i].Address[3] * 1); + +#endif /* VERBOSE_DISC_INFO */ + + tocbuffer[27] = HEXTOBCD(cdinfo.TrackData[i].Address[1]); + + tocbuffer[28] = HEXTOBCD(cdinfo.TrackData[i].Address[2]); + + tocbuffer[29] = HEXTOBCD(cdinfo.TrackData[i].Address[3]); + + + + disctype = tempdisctype; + + return(disctype); + +} // END CDgetDiskType() + diff --git a/plugins/CDVDlinuz/Src/Win32/CD.h b/plugins/CDVDlinuz/Src/Win32/CD.h index 2049fe764d..68cd260f2e 100644 --- a/plugins/CDVDlinuz/Src/Win32/CD.h +++ b/plugins/CDVDlinuz/Src/Win32/CD.h @@ -1,88 +1,88 @@ -/* CD.h - - * Copyright (C) 2002-2005 PCSX2 Team - - * - - * This program is free software; you can redistribute it and/or modify - - * it under the terms of the GNU General Public License as published by - - * the Free Software Foundation; either version 2 of the License, or - - * (at your option) any later version. - - * - - * This program 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 this program; if not, write to the Free Software - - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - * - - * PCSX2 members can be contacted through their website at www.pcsx2.net. - - */ - - - - - -#ifndef CD_H - -#define CD_H - - - - - -#include // DWORD - - - -#define CDVDdefs - -#include "PS2Edefs.h" - - - - - -extern DWORD cdblocksize; - - - - - -extern void InitCDInfo(); - -extern s32 CDreadTrack(u32 lsn, int mode, u8 *buffer); - -extern s32 CDgetBufferOffset(); - -extern s32 CDreadSubQ(); - -extern s32 CDgetTN(cdvdTN *cdvdtn); - -extern s32 CDgetTD(u8 newtrack, cdvdTD *cdvdtd); - -extern s32 CDgetDiskType(); - - - - - -#endif /* CD_H */ - +/* CD.h + + * Copyright (C) 2002-2005 PCSX2 Team + + * + + * This program is free software; you can redistribute it and/or modify + + * it under the terms of the GNU General Public License as published by + + * the Free Software Foundation; either version 2 of the License, or + + * (at your option) any later version. + + * + + * This program 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 this program; if not, write to the Free Software + + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + * + + * PCSX2 members can be contacted through their website at www.pcsx2.net. + + */ + + + + + +#ifndef CD_H + +#define CD_H + + + + + +#include // DWORD + + + +#define CDVDdefs + +#include "PS2Edefs.h" + + + + + +extern DWORD cdblocksize; + + + + + +extern void InitCDInfo(); + +extern s32 CDreadTrack(u32 lsn, int mode, u8 *buffer); + +extern s32 CDgetBufferOffset(); + +extern s32 CDreadSubQ(); + +extern s32 CDgetTN(cdvdTN *cdvdtn); + +extern s32 CDgetTD(u8 newtrack, cdvdTD *cdvdtd); + +extern s32 CDgetDiskType(); + + + + + +#endif /* CD_H */ + diff --git a/plugins/CDVDlinuz/Src/Win32/CDVDlinuz.c b/plugins/CDVDlinuz/Src/Win32/CDVDlinuz.c index b106575d8b..938f362219 100644 --- a/plugins/CDVDlinuz/Src/Win32/CDVDlinuz.c +++ b/plugins/CDVDlinuz/Src/Win32/CDVDlinuz.c @@ -1,868 +1,868 @@ -/* CDVDlinuz.c - - * Copyright (C) 2002-2005 PCSX2 Team - - * - - * This program is free software; you can redistribute it and/or modify - - * it under the terms of the GNU General Public License as published by - - * the Free Software Foundation; either version 2 of the License, or - - * (at your option) any later version. - - * - - * This program 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 this program; if not, write to the Free Software - - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - * - - * PCSX2 members can be contacted through their website at www.pcsx2.net. - - */ - - - - - -#include // BOOL, CALLBACK, APIENTRY - -#include // NULL - - - -#include // time(), time_t - - - -#define CDVDdefs - -#include "../PS2Edefs.h" - - - -#include "device.h" - -#include "CD.h" - -#include "DVD.h" - -#include "../buffer.h" - -#include "../convert.h" - -#include "conf.h" - -#include "logfile.h" - -#include "../version.h" - -#include "screens.h" - -#include "mainbox.h" // Initialize mainboxwindow - -#include "CDVDlinuz.h" - - - - - -HINSTANCE progmodule; - - - - - -BOOL APIENTRY DllMain(HANDLE hModule, - - DWORD param, - - LPVOID reserved) { - - - - - - switch(param) { - - case DLL_PROCESS_ATTACH: - - progmodule = hModule; - - // mainboxwindow = NULL; - - return(TRUE); - - break; - - case DLL_PROCESS_DETACH: - - // CDVDshutdown(); - - return(TRUE); - - break; - - case DLL_THREAD_ATTACH: - - return(TRUE); - - break; - - case DLL_THREAD_DETACH: - - return(TRUE); - - break; - - } // ENDSWITCH param- What does the OS want with us? - - - - return(FALSE); // Wasn't on list? Wasn't handled. - -} // END DllMain() - - - - - -char* CALLBACK PS2EgetLibName() { - - return(libname); - -} // END PS2EgetLibName() - - - - - -u32 CALLBACK PS2EgetLibType() { - - return(PS2E_LT_CDVD); - -} // END PS2getLibType() - - - - - -u32 CALLBACK PS2EgetLibVersion2(u32 type) { - - return((version << 16) | (revision << 8) | build); - -} // END PS2EgetLibVersion2() - - - - - -s32 CALLBACK CDVDinit() { - - InitLog(); - - if(OpenLog() != 0) return(-1); // Couldn't open Log File? Abort. - - - -#ifdef VERBOSE_FUNCTION_INTERFACE - - PrintLog("CDVDlinuz interface: CDVDinit()"); - -#endif /* VERBOSE_FUNCTION_INTERFACE */ - - - - InitConf(); - - - - DeviceInit(); - - InitCDInfo(); - - InitDVDInfo(); - - - - mainboxwindow = NULL; - - - - return(0); - -} // END CDVDinit() - - - - - -void CALLBACK CDVDshutdown() { - -#ifdef VERBOSE_FUNCTION_INTERFACE - - PrintLog("CDVDlinuz interface: CDVDshutdown()"); - -#endif /* VERBOSE_FUNCTION_INTERFACE */ - - - - DeviceClose(); - - - - // Close Windows as well? (Just in case) - - - - CloseLog(); - -} // END CDVDshutdown() - - - - - -s32 CALLBACK CDVDopen() { - - int retval; - - - -#ifdef VERBOSE_FUNCTION_INTERFACE - - PrintLog("CDVDlinuz interface: CDVDopen()"); - -#endif /* VERBOSE_FUNCTION_INTERFACE */ - - - - InitBuffer(); - - - - LoadConf(); - - - - retval = DeviceOpen(); - - if(retval == 0) { - - DeviceTrayStatus(); - - } // ENDIF- Did we open the device? Poll for media. - - return(retval); - -} // END CDVDopen() - - - - - -void CALLBACK CDVDclose() { - -#ifdef VERBOSE_FUNCTION_INTERFACE - - PrintLog("CDVDlinuz interface: CDVDclose()"); - -#endif /* VERBOSE_FUNCTION_INTERFACE */ - - DeviceClose(); - -} // END CDVDclose() - - - - - -s32 CALLBACK CDVDreadSubQ(u32 lsn, cdvdSubQ* subq) { - - char temptime[3]; - - int i; - - int pos; - - u32 tracklsn; - - - -#ifdef VERBOSE_FUNCTION_INTERFACE - - PrintLog("CDVDlinuz interface: CDVDreadSubQ()"); - -#endif /* VERBOSE_FUNCTION_INTERFACE */ - - - - if(DiscInserted() != 0) return(-1); - - - - if((disctype == CDVD_TYPE_PS2DVD) || (disctype == CDVD_TYPE_DVDV)) { - - return(-1); // DVDs don't have SubQ data - - } // ENDIF- Trying to get a SubQ from a DVD? - - - - // fake it - - i = BCDTOHEX(tocbuffer[7]); - - pos = i * 10; - - pos += 30; - - temptime[0] = BCDTOHEX(tocbuffer[pos + 7]); - - temptime[1] = BCDTOHEX(tocbuffer[pos + 8]); - - temptime[2] = BCDTOHEX(tocbuffer[pos + 9]); - - tracklsn = MSFtoLBA(temptime); - - while((i < BCDTOHEX(tocbuffer[17])) && (tracklsn < lsn)) { - - i++; - - pos = i * 10; - - pos += 30; - - temptime[0] = BCDTOHEX(tocbuffer[pos + 7]); - - temptime[1] = BCDTOHEX(tocbuffer[pos + 8]); - - temptime[2] = BCDTOHEX(tocbuffer[pos + 9]); - - tracklsn = MSFtoLBA(temptime); - - } // ENDIF- Loop through tracks searching for lsn track - - i--; - - - - subq->ctrl = 4; - - subq->mode = 1; - - subq->trackNum = HEXTOBCD(i); - - subq->trackIndex = HEXTOBCD(i); - - - - LBAtoMSF(lsn - tracklsn, temptime); - - subq->trackM = HEXTOBCD(temptime[0]); - - subq->trackS = HEXTOBCD(temptime[1]); - - subq->trackF = HEXTOBCD(temptime[2]); - - - - subq->pad = 0; - - - - // lba_to_msf(lsn + (2*75), &min, &sec, &frm); - - LBAtoMSF(lsn, temptime); - - subq->discM = HEXTOBCD(temptime[0]); - - subq->discS = HEXTOBCD(temptime[1]); - - subq->discF = HEXTOBCD(temptime[2]); - - return(0); - -} // END CDVDreadSubQ() - - - - - -s32 CALLBACK CDVDgetTN(cdvdTN *Buffer) { - - - -#ifdef VERBOSE_FUNCTION_INTERFACE - - PrintLog("CDVDlinuz interface: CDVDgetTN()"); - -#endif /* VERBOSE_FUNCTION_INTERFACE */ - - - - if(DiscInserted() != 0) return(-1); - - - - if((disctype == CDVD_TYPE_PS2DVD) || (disctype == CDVD_TYPE_DVDV)) { - - Buffer->strack = 1; - - Buffer->etrack = 1; - - } else { - - Buffer->strack = BCDTOHEX(tocbuffer[7]); - - Buffer->etrack = BCDTOHEX(tocbuffer[17]); - - } // ENDIF- Retrieve track info from a DVD? (or a CD?) - - - - return(0); - -} // END CDVDgetTN() - - - - - -s32 CALLBACK CDVDgetTD(u8 track, cdvdTD *buffer) { - -#ifdef VERBOSE_FUNCTION_INTERFACE - - PrintLog("CDVDlinuz interface: CDVDgetTD()"); - -#endif /* VERBOSE_FUNCTION_INTERFACE */ - - - - return(DeviceGetTD(track, buffer)); - -} // END CDVDgetTD() - - - - - -s32 CALLBACK CDVDgetTOC(void* toc) { - - int i; - - - -#ifdef VERBOSE_FUNCTION_INTERFACE - - PrintLog("CDVDlinuz interface: CDVDgetTOC()"); - -#endif /* VERBOSE_FUNCTION_INTERFACE */ - - - - if(DiscInserted() != 0) return(-1); - - - - for(i = 0; i < 2048; i++) *(((char *) toc) + i) = tocbuffer[i]; - - return(0); - -} // END CDVDgetTOC() - - - - - -s32 CALLBACK CDVDreadTrack(u32 lsn, int mode) { - - int retval; - - - -#ifdef VERBOSE_FUNCTION_INTERFACE - - PrintLog("CDVDlinuz interface: CDVDreadTrack(%u)", lsn); - -#endif /* VERBOSE_FUNCTION_INTERFACE */ - - - - if(DiscInserted() == -1) return(-1); - - - - if(userbuffer < BUFFERMAX) { - - if((bufferlist[userbuffer].lsn == lsn) && - - (bufferlist[userbuffer].mode == mode)) { - - return(0); - - } // ENDIF- And it's the right one? - - } // ENDIF- Are we already pointing at the buffer? - - - - userbuffer = FindListBuffer(lsn); - - if(userbuffer < BUFFERMAX) { - - if((bufferlist[userbuffer].lsn == lsn) && - - (bufferlist[userbuffer].mode == mode)) { - - return(0); - - } // ENDIF- And it was the right one? - - } // ENDIF- Was a buffer found in the cache? - - - - replacebuffer++; - - if(replacebuffer >= BUFFERMAX) replacebuffer = 0; - - userbuffer = replacebuffer; - - - - if(bufferlist[replacebuffer].upsort != 0xffff) { - - RemoveListBuffer(replacebuffer); - - } // ENDIF- Reference already in place? Remove it. - - - - retval = DeviceReadTrack(lsn, mode, bufferlist[replacebuffer].buffer); - - bufferlist[replacebuffer].lsn = lsn; - - bufferlist[replacebuffer].mode = mode; - - bufferlist[replacebuffer].offset = DeviceBufferOffset(); - - - - if(retval != 0) { - - bufferlist[replacebuffer].mode = -1; // Error! flag buffer as such. - - } else { - - if((disctype != CDVD_TYPE_PS2DVD) && (disctype != CDVD_TYPE_DVDV)) { - - if(mode == CDVD_MODE_2352) { - - CDreadSubQ(lsn, &bufferlist[replacebuffer].subq); - - } // ENDIF- Read subq as well? - - } // ENDIF- Read a DVD buffer or a CD buffer? - - } // ENDIF-Read ok? Fill out rest of buffer info. - - AddListBuffer(replacebuffer); - - return(retval); - -} // END CDVDreadTrack() - - - - - -u8* CALLBACK CDVDgetBuffer() { - -#ifdef VERBOSE_FUNCTION_INTERFACE - - PrintLog("CDVDlinuz interface: CDVDgetBuffer()"); - -#endif /* VERBOSE_FUNCTION_INTERFACE */ - - - - if(DiscInserted() == -1) return(NULL); - - - - if(userbuffer == 0xffff) { - -#ifdef VERBOSE_WARNINGS - - PrintLog("CDVDlinuz interface: Not pointing to a buffer!"); - -#endif /* VERBOSE_WARNINGS */ - - return(NULL); // No buffer reference? - - } // ENDIF- user buffer not pointing at anything? Abort - - - - if(bufferlist[userbuffer].mode < 0) { - -#ifdef VERBOSE_WARNINGS - - PrintLog("CDVDlinuz interface: Error retrieving sector (ReadTrack call)"); - -#endif /* VERBOSE_WARNINGS */ - - return(NULL); // Bad Sector? - - } // ENDIF- Trouble reading physical sector? Tell them. - - - - return(bufferlist[userbuffer].buffer + bufferlist[userbuffer].offset); - -} // END CDVDgetBuffer() - - - - - -s32 CALLBACK CDVDgetDiskType() { - -#ifdef VERBOSE_FUNCTION_INTERFACE - - PrintLog("CDVDlinuz interface: CDVDgetDiskType()"); - -#endif /* VERBOSE_FUNCTION_INTERFACE */ - - - - if(lasttime != time(NULL)) { - - lasttime = time(NULL); - - DeviceTrayStatus(); - - } // ENDIF- Has enough time passed between calls? - - - - return(disctype); - -} // END CDVDgetDiskType() - - - - - -s32 CALLBACK CDVDgetTrayStatus() { - -#ifdef VERBOSE_FUNCTION_INTERFACE - - PrintLog("CDVDlinuz interface: CDVDgetTrayStatus()"); - -#endif /* VERBOSE_FUNCTION_INTERFACE */ - - - - if(lasttime != time(NULL)) { - - lasttime = time(NULL); - - DeviceTrayStatus(); - - } // ENDIF- Has enough time passed between calls? - - - - return(traystatus); - -} // END CDVDgetTrayStatus() - - - - - -s32 CALLBACK CDVDctrlTrayOpen() { - -#ifdef VERBOSE_FUNCTION_INTERFACE - - PrintLog("CDVDlinuz interface: CDVDctrlTrayOpen()"); - -#endif /* VERBOSE_FUNCTION_INTERFACE */ - - - - return(DeviceTrayOpen()); - -} // END CDVDctrlTrayOpen() - - - - - -s32 CALLBACK CDVDctrlTrayClose() { - -#ifdef VERBOSE_FUNCTION_INTERFACE - - PrintLog("CDVDlinuz interface: CDVDctrlTrayClose()"); - -#endif /* VERBOSE_FUNCTION_INTERFACE */ - - - - return(DeviceTrayClose()); - -} // END CDVDctrlTrayClose() - - - - - -s32 CALLBACK CDVDtest() { - - int retval; - - - - errno = 0; - - - - if(devicehandle != NULL) { - -#ifdef VERBOSE_WARNINGS - - PrintLog("CDVDlinuz interface: Device already open"); - -#endif /* VERBOSE_WARNINGS */ - - return(0); - - } // ENDIF- Is the CD/DVD already in use? That's fine. - - - -#ifdef VERBOSE_FUNCTION_INTERFACE - - PrintLog("CDVDlinuz interface: CDVDtest()"); - -#endif /* VERBOSE_FUNCTION_INTERFACE */ - - - - retval = DeviceOpen(); - - DeviceClose(); - - return(retval); - -} // END CDVDtest() - - - - - -void CALLBACK CDVDconfigure() { - - HWND lastwindow; - - - - lastwindow = GetActiveWindow(); - - DialogBox(progmodule, - - MAKEINTRESOURCE(DLG_0200), - - lastwindow, - - (DLGPROC)MainBoxCallback); - - SetActiveWindow(lastwindow); - - lastwindow = NULL; - - return; - -} // END CDVDconfigure() - - - - - -BOOL CALLBACK AboutCallback(HWND window, UINT msg, WPARAM param, LPARAM param2) { - - switch(msg) { - - case WM_COMMAND: - - switch(LOWORD(param)) { - - case IDC_0104: // "Ok" Button - - EndDialog(window, FALSE); - - return(TRUE); - - break; - - } // ENDSWITCH param- Which Windows Message Command? - - - - case WM_CLOSE: - - EndDialog(window, FALSE); - - return(TRUE); - - break; - - } // ENDSWITCH msg- what message has been sent to this window? - - - - return(FALSE); // Not a recognisable message. Pass it back to the OS. - -} // END AboutCallback() - - - -void CALLBACK CDVDabout() { - - HWND lastwindow; - - - - lastwindow = GetActiveWindow(); - - DialogBox(progmodule, - - MAKEINTRESOURCE(DLG_0100), - - lastwindow, - - (DLGPROC)AboutCallback); - - SetActiveWindow(lastwindow); - - return; - -} // END CDVDabout() - +/* CDVDlinuz.c + + * Copyright (C) 2002-2005 PCSX2 Team + + * + + * This program is free software; you can redistribute it and/or modify + + * it under the terms of the GNU General Public License as published by + + * the Free Software Foundation; either version 2 of the License, or + + * (at your option) any later version. + + * + + * This program 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 this program; if not, write to the Free Software + + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + * + + * PCSX2 members can be contacted through their website at www.pcsx2.net. + + */ + + + + + +#include // BOOL, CALLBACK, APIENTRY + +#include // NULL + + + +#include // time(), time_t + + + +#define CDVDdefs + +#include "../PS2Edefs.h" + + + +#include "device.h" + +#include "CD.h" + +#include "DVD.h" + +#include "../buffer.h" + +#include "../convert.h" + +#include "conf.h" + +#include "logfile.h" + +#include "../version.h" + +#include "screens.h" + +#include "mainbox.h" // Initialize mainboxwindow + +#include "CDVDlinuz.h" + + + + + +HINSTANCE progmodule; + + + + + +BOOL APIENTRY DllMain(HANDLE hModule, + + DWORD param, + + LPVOID reserved) { + + + + + + switch(param) { + + case DLL_PROCESS_ATTACH: + + progmodule = hModule; + + // mainboxwindow = NULL; + + return(TRUE); + + break; + + case DLL_PROCESS_DETACH: + + // CDVDshutdown(); + + return(TRUE); + + break; + + case DLL_THREAD_ATTACH: + + return(TRUE); + + break; + + case DLL_THREAD_DETACH: + + return(TRUE); + + break; + + } // ENDSWITCH param- What does the OS want with us? + + + + return(FALSE); // Wasn't on list? Wasn't handled. + +} // END DllMain() + + + + + +char* CALLBACK PS2EgetLibName() { + + return(libname); + +} // END PS2EgetLibName() + + + + + +u32 CALLBACK PS2EgetLibType() { + + return(PS2E_LT_CDVD); + +} // END PS2getLibType() + + + + + +u32 CALLBACK PS2EgetLibVersion2(u32 type) { + + return((version << 16) | (revision << 8) | build); + +} // END PS2EgetLibVersion2() + + + + + +s32 CALLBACK CDVDinit() { + + InitLog(); + + if(OpenLog() != 0) return(-1); // Couldn't open Log File? Abort. + + + +#ifdef VERBOSE_FUNCTION_INTERFACE + + PrintLog("CDVDlinuz interface: CDVDinit()"); + +#endif /* VERBOSE_FUNCTION_INTERFACE */ + + + + InitConf(); + + + + DeviceInit(); + + InitCDInfo(); + + InitDVDInfo(); + + + + mainboxwindow = NULL; + + + + return(0); + +} // END CDVDinit() + + + + + +void CALLBACK CDVDshutdown() { + +#ifdef VERBOSE_FUNCTION_INTERFACE + + PrintLog("CDVDlinuz interface: CDVDshutdown()"); + +#endif /* VERBOSE_FUNCTION_INTERFACE */ + + + + DeviceClose(); + + + + // Close Windows as well? (Just in case) + + + + CloseLog(); + +} // END CDVDshutdown() + + + + + +s32 CALLBACK CDVDopen() { + + int retval; + + + +#ifdef VERBOSE_FUNCTION_INTERFACE + + PrintLog("CDVDlinuz interface: CDVDopen()"); + +#endif /* VERBOSE_FUNCTION_INTERFACE */ + + + + InitBuffer(); + + + + LoadConf(); + + + + retval = DeviceOpen(); + + if(retval == 0) { + + DeviceTrayStatus(); + + } // ENDIF- Did we open the device? Poll for media. + + return(retval); + +} // END CDVDopen() + + + + + +void CALLBACK CDVDclose() { + +#ifdef VERBOSE_FUNCTION_INTERFACE + + PrintLog("CDVDlinuz interface: CDVDclose()"); + +#endif /* VERBOSE_FUNCTION_INTERFACE */ + + DeviceClose(); + +} // END CDVDclose() + + + + + +s32 CALLBACK CDVDreadSubQ(u32 lsn, cdvdSubQ* subq) { + + char temptime[3]; + + int i; + + int pos; + + u32 tracklsn; + + + +#ifdef VERBOSE_FUNCTION_INTERFACE + + PrintLog("CDVDlinuz interface: CDVDreadSubQ()"); + +#endif /* VERBOSE_FUNCTION_INTERFACE */ + + + + if(DiscInserted() != 0) return(-1); + + + + if((disctype == CDVD_TYPE_PS2DVD) || (disctype == CDVD_TYPE_DVDV)) { + + return(-1); // DVDs don't have SubQ data + + } // ENDIF- Trying to get a SubQ from a DVD? + + + + // fake it + + i = BCDTOHEX(tocbuffer[7]); + + pos = i * 10; + + pos += 30; + + temptime[0] = BCDTOHEX(tocbuffer[pos + 7]); + + temptime[1] = BCDTOHEX(tocbuffer[pos + 8]); + + temptime[2] = BCDTOHEX(tocbuffer[pos + 9]); + + tracklsn = MSFtoLBA(temptime); + + while((i < BCDTOHEX(tocbuffer[17])) && (tracklsn < lsn)) { + + i++; + + pos = i * 10; + + pos += 30; + + temptime[0] = BCDTOHEX(tocbuffer[pos + 7]); + + temptime[1] = BCDTOHEX(tocbuffer[pos + 8]); + + temptime[2] = BCDTOHEX(tocbuffer[pos + 9]); + + tracklsn = MSFtoLBA(temptime); + + } // ENDIF- Loop through tracks searching for lsn track + + i--; + + + + subq->ctrl = 4; + + subq->mode = 1; + + subq->trackNum = HEXTOBCD(i); + + subq->trackIndex = HEXTOBCD(i); + + + + LBAtoMSF(lsn - tracklsn, temptime); + + subq->trackM = HEXTOBCD(temptime[0]); + + subq->trackS = HEXTOBCD(temptime[1]); + + subq->trackF = HEXTOBCD(temptime[2]); + + + + subq->pad = 0; + + + + // lba_to_msf(lsn + (2*75), &min, &sec, &frm); + + LBAtoMSF(lsn, temptime); + + subq->discM = HEXTOBCD(temptime[0]); + + subq->discS = HEXTOBCD(temptime[1]); + + subq->discF = HEXTOBCD(temptime[2]); + + return(0); + +} // END CDVDreadSubQ() + + + + + +s32 CALLBACK CDVDgetTN(cdvdTN *Buffer) { + + + +#ifdef VERBOSE_FUNCTION_INTERFACE + + PrintLog("CDVDlinuz interface: CDVDgetTN()"); + +#endif /* VERBOSE_FUNCTION_INTERFACE */ + + + + if(DiscInserted() != 0) return(-1); + + + + if((disctype == CDVD_TYPE_PS2DVD) || (disctype == CDVD_TYPE_DVDV)) { + + Buffer->strack = 1; + + Buffer->etrack = 1; + + } else { + + Buffer->strack = BCDTOHEX(tocbuffer[7]); + + Buffer->etrack = BCDTOHEX(tocbuffer[17]); + + } // ENDIF- Retrieve track info from a DVD? (or a CD?) + + + + return(0); + +} // END CDVDgetTN() + + + + + +s32 CALLBACK CDVDgetTD(u8 track, cdvdTD *buffer) { + +#ifdef VERBOSE_FUNCTION_INTERFACE + + PrintLog("CDVDlinuz interface: CDVDgetTD()"); + +#endif /* VERBOSE_FUNCTION_INTERFACE */ + + + + return(DeviceGetTD(track, buffer)); + +} // END CDVDgetTD() + + + + + +s32 CALLBACK CDVDgetTOC(void* toc) { + + int i; + + + +#ifdef VERBOSE_FUNCTION_INTERFACE + + PrintLog("CDVDlinuz interface: CDVDgetTOC()"); + +#endif /* VERBOSE_FUNCTION_INTERFACE */ + + + + if(DiscInserted() != 0) return(-1); + + + + for(i = 0; i < 2048; i++) *(((char *) toc) + i) = tocbuffer[i]; + + return(0); + +} // END CDVDgetTOC() + + + + + +s32 CALLBACK CDVDreadTrack(u32 lsn, int mode) { + + int retval; + + + +#ifdef VERBOSE_FUNCTION_INTERFACE + + PrintLog("CDVDlinuz interface: CDVDreadTrack(%u)", lsn); + +#endif /* VERBOSE_FUNCTION_INTERFACE */ + + + + if(DiscInserted() == -1) return(-1); + + + + if(userbuffer < BUFFERMAX) { + + if((bufferlist[userbuffer].lsn == lsn) && + + (bufferlist[userbuffer].mode == mode)) { + + return(0); + + } // ENDIF- And it's the right one? + + } // ENDIF- Are we already pointing at the buffer? + + + + userbuffer = FindListBuffer(lsn); + + if(userbuffer < BUFFERMAX) { + + if((bufferlist[userbuffer].lsn == lsn) && + + (bufferlist[userbuffer].mode == mode)) { + + return(0); + + } // ENDIF- And it was the right one? + + } // ENDIF- Was a buffer found in the cache? + + + + replacebuffer++; + + if(replacebuffer >= BUFFERMAX) replacebuffer = 0; + + userbuffer = replacebuffer; + + + + if(bufferlist[replacebuffer].upsort != 0xffff) { + + RemoveListBuffer(replacebuffer); + + } // ENDIF- Reference already in place? Remove it. + + + + retval = DeviceReadTrack(lsn, mode, bufferlist[replacebuffer].buffer); + + bufferlist[replacebuffer].lsn = lsn; + + bufferlist[replacebuffer].mode = mode; + + bufferlist[replacebuffer].offset = DeviceBufferOffset(); + + + + if(retval != 0) { + + bufferlist[replacebuffer].mode = -1; // Error! flag buffer as such. + + } else { + + if((disctype != CDVD_TYPE_PS2DVD) && (disctype != CDVD_TYPE_DVDV)) { + + if(mode == CDVD_MODE_2352) { + + CDreadSubQ(lsn, &bufferlist[replacebuffer].subq); + + } // ENDIF- Read subq as well? + + } // ENDIF- Read a DVD buffer or a CD buffer? + + } // ENDIF-Read ok? Fill out rest of buffer info. + + AddListBuffer(replacebuffer); + + return(retval); + +} // END CDVDreadTrack() + + + + + +u8* CALLBACK CDVDgetBuffer() { + +#ifdef VERBOSE_FUNCTION_INTERFACE + + PrintLog("CDVDlinuz interface: CDVDgetBuffer()"); + +#endif /* VERBOSE_FUNCTION_INTERFACE */ + + + + if(DiscInserted() == -1) return(NULL); + + + + if(userbuffer == 0xffff) { + +#ifdef VERBOSE_WARNINGS + + PrintLog("CDVDlinuz interface: Not pointing to a buffer!"); + +#endif /* VERBOSE_WARNINGS */ + + return(NULL); // No buffer reference? + + } // ENDIF- user buffer not pointing at anything? Abort + + + + if(bufferlist[userbuffer].mode < 0) { + +#ifdef VERBOSE_WARNINGS + + PrintLog("CDVDlinuz interface: Error retrieving sector (ReadTrack call)"); + +#endif /* VERBOSE_WARNINGS */ + + return(NULL); // Bad Sector? + + } // ENDIF- Trouble reading physical sector? Tell them. + + + + return(bufferlist[userbuffer].buffer + bufferlist[userbuffer].offset); + +} // END CDVDgetBuffer() + + + + + +s32 CALLBACK CDVDgetDiskType() { + +#ifdef VERBOSE_FUNCTION_INTERFACE + + PrintLog("CDVDlinuz interface: CDVDgetDiskType()"); + +#endif /* VERBOSE_FUNCTION_INTERFACE */ + + + + if(lasttime != time(NULL)) { + + lasttime = time(NULL); + + DeviceTrayStatus(); + + } // ENDIF- Has enough time passed between calls? + + + + return(disctype); + +} // END CDVDgetDiskType() + + + + + +s32 CALLBACK CDVDgetTrayStatus() { + +#ifdef VERBOSE_FUNCTION_INTERFACE + + PrintLog("CDVDlinuz interface: CDVDgetTrayStatus()"); + +#endif /* VERBOSE_FUNCTION_INTERFACE */ + + + + if(lasttime != time(NULL)) { + + lasttime = time(NULL); + + DeviceTrayStatus(); + + } // ENDIF- Has enough time passed between calls? + + + + return(traystatus); + +} // END CDVDgetTrayStatus() + + + + + +s32 CALLBACK CDVDctrlTrayOpen() { + +#ifdef VERBOSE_FUNCTION_INTERFACE + + PrintLog("CDVDlinuz interface: CDVDctrlTrayOpen()"); + +#endif /* VERBOSE_FUNCTION_INTERFACE */ + + + + return(DeviceTrayOpen()); + +} // END CDVDctrlTrayOpen() + + + + + +s32 CALLBACK CDVDctrlTrayClose() { + +#ifdef VERBOSE_FUNCTION_INTERFACE + + PrintLog("CDVDlinuz interface: CDVDctrlTrayClose()"); + +#endif /* VERBOSE_FUNCTION_INTERFACE */ + + + + return(DeviceTrayClose()); + +} // END CDVDctrlTrayClose() + + + + + +s32 CALLBACK CDVDtest() { + + int retval; + + + + errno = 0; + + + + if(devicehandle != NULL) { + +#ifdef VERBOSE_WARNINGS + + PrintLog("CDVDlinuz interface: Device already open"); + +#endif /* VERBOSE_WARNINGS */ + + return(0); + + } // ENDIF- Is the CD/DVD already in use? That's fine. + + + +#ifdef VERBOSE_FUNCTION_INTERFACE + + PrintLog("CDVDlinuz interface: CDVDtest()"); + +#endif /* VERBOSE_FUNCTION_INTERFACE */ + + + + retval = DeviceOpen(); + + DeviceClose(); + + return(retval); + +} // END CDVDtest() + + + + + +void CALLBACK CDVDconfigure() { + + HWND lastwindow; + + + + lastwindow = GetActiveWindow(); + + DialogBox(progmodule, + + MAKEINTRESOURCE(DLG_0200), + + lastwindow, + + (DLGPROC)MainBoxCallback); + + SetActiveWindow(lastwindow); + + lastwindow = NULL; + + return; + +} // END CDVDconfigure() + + + + + +BOOL CALLBACK AboutCallback(HWND window, UINT msg, WPARAM param, LPARAM param2) { + + switch(msg) { + + case WM_COMMAND: + + switch(LOWORD(param)) { + + case IDC_0104: // "Ok" Button + + EndDialog(window, FALSE); + + return(TRUE); + + break; + + } // ENDSWITCH param- Which Windows Message Command? + + + + case WM_CLOSE: + + EndDialog(window, FALSE); + + return(TRUE); + + break; + + } // ENDSWITCH msg- what message has been sent to this window? + + + + return(FALSE); // Not a recognisable message. Pass it back to the OS. + +} // END AboutCallback() + + + +void CALLBACK CDVDabout() { + + HWND lastwindow; + + + + lastwindow = GetActiveWindow(); + + DialogBox(progmodule, + + MAKEINTRESOURCE(DLG_0100), + + lastwindow, + + (DLGPROC)AboutCallback); + + SetActiveWindow(lastwindow); + + return; + +} // END CDVDabout() + diff --git a/plugins/CDVDlinuz/Src/Win32/CDVDlinuz.h b/plugins/CDVDlinuz/Src/Win32/CDVDlinuz.h index 0d55d3bda6..beacc02d95 100644 --- a/plugins/CDVDlinuz/Src/Win32/CDVDlinuz.h +++ b/plugins/CDVDlinuz/Src/Win32/CDVDlinuz.h @@ -1,80 +1,80 @@ -/* CDVDlinuz.h - - * Copyright (C) 2002-2005 PCSX2 Team - - * - - * This program is free software; you can redistribute it and/or modify - - * it under the terms of the GNU General Public License as published by - - * the Free Software Foundation; either version 2 of the License, or - - * (at your option) any later version. - - * - - * This program 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 this program; if not, write to the Free Software - - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - * - - * PCSX2 members can be contacted through their website at www.pcsx2.net. - - */ - - - - - -#ifndef CDVDLINUZ_H - -#define CDVDLINUZ_H - - - - - -#include // HINSTANCE - - - - - -// #define VERBOSE_WARNINGS - -// #define VERBOSE_FUNCTION_INTERFACE - -#define VERBOSE_DISC_INFO - -#define VERBOSE_DISC_TYPE - - - -#define READ_AHEAD_BUFFERS 32 - - - - - -extern HINSTANCE progmodule; - - - - - -#endif /* CDVDLINUZ_H */ - +/* CDVDlinuz.h + + * Copyright (C) 2002-2005 PCSX2 Team + + * + + * This program is free software; you can redistribute it and/or modify + + * it under the terms of the GNU General Public License as published by + + * the Free Software Foundation; either version 2 of the License, or + + * (at your option) any later version. + + * + + * This program 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 this program; if not, write to the Free Software + + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + * + + * PCSX2 members can be contacted through their website at www.pcsx2.net. + + */ + + + + + +#ifndef CDVDLINUZ_H + +#define CDVDLINUZ_H + + + + + +#include // HINSTANCE + + + + + +// #define VERBOSE_WARNINGS + +// #define VERBOSE_FUNCTION_INTERFACE + +#define VERBOSE_DISC_INFO + +#define VERBOSE_DISC_TYPE + + + +#define READ_AHEAD_BUFFERS 32 + + + + + +extern HINSTANCE progmodule; + + + + + +#endif /* CDVDLINUZ_H */ + diff --git a/plugins/CDVDlinuz/Src/Win32/DVD.c b/plugins/CDVDlinuz/Src/Win32/DVD.c index b69affde76..79280604ad 100644 --- a/plugins/CDVDlinuz/Src/Win32/DVD.c +++ b/plugins/CDVDlinuz/Src/Win32/DVD.c @@ -1,796 +1,796 @@ -/* DVD.c - - * Copyright (C) 2002-2005 PCSX2 Team - - * - - * This program is free software; you can redistribute it and/or modify - - * it under the terms of the GNU General Public License as published by - - * the Free Software Foundation; either version 2 of the License, or - - * (at your option) any later version. - - * - - * This program 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 this program; if not, write to the Free Software - - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - * - - * PCSX2 members can be contacted through their website at www.pcsx2.net. - - */ - - - - - -#include - -#include // IOCTL_DVD... - - - -#include // off64_t - - - -#define CDVDdefs - -#include "PS2Edefs.h" - - - -#include "logfile.h" - -#include "device.h" // FinishCommand() - - - - - -struct { - - DVD_DESCRIPTOR_HEADER h; - - DVD_LAYER_DESCRIPTOR d; - -} layer; - -// DVD_LAYER_DESCRIPTOR layer; - -// DVD_COPYRIGHT_DESCRIPTOR copyright; - -// DVD_DISK_KEY_DESCRIPTOR disckey; - -// DVD_BCA_DESCRIPTOR bca; - -// DVD_MANUFACTURER_DESCRIPTOR manufact; - - - - - -void InitDVDInfo() { - - layer.d.EndDataSector = 0; - -} // END InitDVDInfo() - - - - - -s32 DVDGetStructures() { - - DVD_SESSION_ID sessionid; - - DVD_READ_STRUCTURE request; - - DWORD byteswritten; - - BOOL boolresult; - - DWORD errcode; - - s32 retval; - - - -#ifdef VERBOSE_FUNCTION_DEVICE - - PrintLog("CDVDlinuz DVD: DVDgetStructures()"); - -#endif /* VERBOSE_FUNCTION_DEVICE */ - - - - boolresult = DeviceIoControl(devicehandle, - - IOCTL_DVD_START_SESSION, - - NULL, - - 0, - - &sessionid, - - sizeof(DVD_SESSION_ID), - - &byteswritten, - - &waitevent); - - errcode = FinishCommand(boolresult); - - if(errcode != 0) { - -#ifdef VERBOSE_WARNING_DEVICE - - PrintLog("CDVDlinuz DVD: Couldn't start session!"); - - PrintError("CDVDlinuz DVD", errcode); - -#endif /* VERBOSE_WARNING_DEVICE */ - - return(-1); - - } // ENDIF- Couldn't start a user session on the DVD drive? Fail. - - - - request.BlockByteOffset.QuadPart = 0; - - request.Format = DvdPhysicalDescriptor; - - request.SessionId = sessionid; - - request.LayerNumber = 0; - - retval = 0; - - boolresult = DeviceIoControl(devicehandle, - - IOCTL_DVD_READ_STRUCTURE, - - &request, - - sizeof(DVD_READ_STRUCTURE), - - &layer, - - sizeof(layer), - - &byteswritten, - - &waitevent); - - errcode = FinishCommand(boolresult); - - if(errcode != 0) { - -#ifdef VERBOSE_WARNING_DEVICE - - PrintLog("CDVDlinuz DVD: Couldn't get layer data!"); - - PrintError("CDVDlinuz DVD", errcode); - -#endif /* VERBOSE_WARNING_DEVICE */ - - retval = -1; - - } // ENDIF- Couldn't get layer data? (Including DVD size) Abort. - - - -#ifdef VERBOSE_DISC_INFO - - switch(layer.d.BookType) { - - case 0: - - PrintLog("CDVDlinuz DVD: Book Type: DVD-ROM"); - - break; - - case 1: - - PrintLog("CDVDlinuz DVD: Book Type: DVD-RAM"); - - break; - - case 2: - - PrintLog("CDVDlinuz DVD: Book Type: DVD-R"); - - break; - - case 3: - - PrintLog("CDVDlinuz DVD: Book Type: DVD-RW"); - - break; - - case 9: - - PrintLog("CDVDlinuz DVD: Book Type: DVD+RW"); - - break; - - default: - - PrintLog("CDVDlinuz DVD: Book Type: Unknown (%i)", layer.d.BookType); - - break; - - } // ENDSWITCH- Displaying the Book Type - - PrintLog("CDVDlinuz DVD: Book Version %i", layer.d.BookVersion); - - switch(layer.d.MinimumRate) { - - case 0: - - PrintLog("CDVDlinuz DVD: Use Minimum Rate for: DVD-ROM"); - - break; - - case 1: - - PrintLog("CDVDlinuz DVD: Use Minimum Rate for: DVD-RAM"); - - break; - - case 2: - - PrintLog("CDVDlinuz DVD: Use Minimum Rate for: DVD-R"); - - break; - - case 3: - - PrintLog("CDVDlinuz DVD: Use Minimum Rate for: DVD-RW"); - - break; - - case 9: - - PrintLog("CDVDlinuz DVD: Use Minimum Rate for: DVD+RW"); - - break; - - default: - - PrintLog("CDVDlinuz DVD: Use Minimum Rate for: Unknown (%i)", layer.d.MinimumRate); - - break; - - } // ENDSWITCH- Displaying the Minimum (Spin?) Rate - - switch(layer.d.DiskSize) { - - case 0: - - PrintLog("CDVDlinuz DVD: Physical Disk Size: 120mm"); - - break; - - case 1: - - PrintLog("CDVDlinuz DVD: Physical Disk Size: 80mm"); - - break; - - default: - - PrintLog("CDVDlinuz DVD: Physical Disk Size: Unknown (%i)", layer.d.DiskSize); - - break; - - } // ENDSWITCH- What's the Disk Size? - - switch(layer.d.LayerType) { - - case 1: - - PrintLog("CDVDlinuz DVD: Layer Type: Read-Only"); - - break; - - case 2: - - PrintLog("CDVDlinuz DVD: Layer Type: Recordable"); - - break; - - case 4: - - PrintLog("CDVDlinuz DVD: Layer Type: Rewritable"); - - break; - - default: - - PrintLog("CDVDlinuz DVD: Layer Type: Unknown (%i)", layer.d.LayerType); - - break; - - } // ENDSWITCH- Displaying the Layer Type - - switch(layer.d.TrackPath) { - - case 0: - - PrintLog("CDVDlinuz DVD: Track Path: PTP"); - - break; - - case 1: - - PrintLog("CDVDlinuz DVD: Track Path: OTP"); - - break; - - default: - - PrintLog("CDVDlinuz DVD: Track Path: Unknown (%i)", layer.d.TrackPath); - - break; - - } // ENDSWITCH- What's Track Path Layout? - - PrintLog("CDVDlinuz DVD: Number of Layers: %i", layer.d.NumberOfLayers + 1); - - switch(layer.d.TrackDensity) { - - case 0: - - PrintLog("CDVDlinuz DVD: Track Density: .74 m/track"); - - break; - - case 1: - - PrintLog("CDVDlinuz DVD: Track Density: .8 m/track"); - - break; - - case 2: - - PrintLog("CDVDlinuz DVD: Track Density: .615 m/track"); - - break; - - default: - - PrintLog("CDVDlinuz DVD: Track Density: Unknown (%i)", layer.d.TrackDensity); - - break; - - } // ENDSWITCH- Displaying the Layer Type - - switch(layer.d.LinearDensity) { - - case 0: - - PrintLog("CDVDlinuz DVD: Linear Density: .267 m/bit"); - - break; - - case 1: - - PrintLog("CDVDlinuz DVD: Linear Density: .293 m/bit"); - - break; - - case 2: - - PrintLog("CDVDlinuz DVD: Linear Density: .409 to .435 m/bit"); - - break; - - case 4: - - PrintLog("CDVDlinuz DVD: Linear Density: .280 to .291 m/bit"); - - break; - - case 8: - - PrintLog("CDVDlinuz DVD: Linear Density: .353 m/bit"); - - break; - - default: - - PrintLog("CDVDlinuz DVD: Linear Density: Unknown (%i)", layer.d.LinearDensity); - - break; - - } // ENDSWITCH- Displaying the Book Type - - if(layer.d.StartingDataSector == 0x30000) { - - PrintLog("CDVDlinuz DVD: Starting Sector: %lu (DVD-ROM, DVD-R, DVD-RW)", - - layer.d.StartingDataSector); - - } else if(layer.d.StartingDataSector == 0x31000) { - - PrintLog("CDVDlinuz DVD: Starting Sector: %lu (DVD-RAM, DVD+RW)", - - layer.d.StartingDataSector); - - } else { - - PrintLog("CDVDlinuz DVD: Starting Sector: %lu", layer.d.StartingDataSector); - - } // ENDLONGIF- What does the starting sector tell us? - - PrintLog("CDVDlinuz DVD: End of Layer 0: %lu", layer.d.EndLayerZeroSector); - - PrintLog("CDVDlinuz DVD: Ending Sector: %lu", layer.d.EndDataSector); - - if(layer.d.BCAFlag != 0) PrintLog("CDVDlinuz DVD: BCA data present"); - -#endif /* VERBOSE_DISC_INFO */ - - - - boolresult = DeviceIoControl(devicehandle, - - IOCTL_DVD_END_SESSION, - - &sessionid, - - sizeof(DVD_SESSION_ID), - - NULL, - - 0, - - &byteswritten, - - &waitevent); - - errcode = FinishCommand(boolresult); - - if(errcode != 0) { - -#ifdef VERBOSE_WARNING_DEVICE - - PrintLog("CDVDlinuz DVD: Couldn't end the session!"); - - PrintError("CDVDlinuz DVD", errcode); - -#endif /* VERBOSE_WARNING_DEVICE */ - - } // ENDIF- Couldn't end the user session? Report it. - - - - return(retval); - -} // END DVDGetStructures() - - - - - -s32 DVDreadTrack(u32 lsn, int mode, u8 *buffer) { - - LARGE_INTEGER targetpos; - - DWORD byteswritten; - - BOOL boolresult; - - DWORD errcode; - - - -#ifdef VERBOSE_FUNCTION_DEVICE - - PrintLog("CDVDlinuz DVD: DVDreadTrack(%lu)", lsn); - -#endif /* VERBOSE_FUNCTION_DEVICE */ - - - - targetpos.QuadPart = lsn * 2048; - - waitevent.Offset = targetpos.LowPart; - - waitevent.OffsetHigh = targetpos.HighPart; - - - - boolresult = ReadFile(devicehandle, - - buffer, - - 2048, - - &byteswritten, - - &waitevent); - - errcode = FinishCommand(boolresult); - - - - if(errcode != 0) { - -#ifdef VERBOSE_WARNING_DEVICE - - PrintLog("CDVDlinuz DVD: Couldn't read sector!"); - - PrintError("CDVDlinuz DVD", errcode); - -#endif /* VERBOSE_WARNING_DEVICE */ - - return(-1); - - } // ENDIF- Trouble with the command? Report it. - - - - if(boolresult == FALSE) { - - boolresult = GetOverlappedResult(devicehandle, - - &waitevent, - - &byteswritten, - - FALSE); - - } // ENDIF- Did the initial call not complete? Get byteswritten for - - // the completed call. - - - - if(byteswritten < 2048) { - -#ifdef VERBOSE_WARNING_DEVICE - - errcode = GetLastError(); - - PrintLog("CDVDlinuz CD: Short block! only got %u out of %u bytes", - - byteswritten, 2048); - - PrintError("CDVDlinuz CD", errcode); - -#endif /* VERBOSE_WARNING_DEVICE */ - - return(-1); - - } // ENDIF- Didn't get enough bytes? Report and Abort! - - - - return(0); - -} // END DVDreadTrack() - - - - - -s32 DVDgetTN(cdvdTN *cdvdtn) { - -#ifdef VERBOSE_FUNCTION_DEVICE - - PrintLog("CDVDlinuz DVD: DVDgetTN()"); - -#endif /* VERBOSE_FUNCTION_DEVICE */ - - - - if(cdvdtn != NULL) { - - cdvdtn->strack = 1; - - cdvdtn->etrack = 1; - - } // ENDIF- Does the user really want this data? - - return(0); - -} // END DVDgetTN() - - - - - -s32 DVDgetTD(u8 newtrack, cdvdTD *cdvdtd) { - -#ifdef VERBOSE_FUNCTION_DEVICE - - PrintLog("CDVDlinuz DVD: DVDgetTD()"); - -#endif /* VERBOSE_FUNCTION_DEVICE */ - - - - if((newtrack >= 2) && (newtrack != 0xAA)) return(-1); // Wrong track - - - - if(cdvdtd != NULL) { - - cdvdtd->type = 0; - - cdvdtd->lsn = layer.d.EndDataSector - layer.d.StartingDataSector + 1; - - } // ENDIF- Does the user really want this data? - - return(0); - -} // END DVDgetTD() - - - - - -s32 DVDgetDiskType() { - - char playstationname[] = "PLAYSTATION\0"; - - int retval; - - s32 tempdisctype; - - char tempbuffer[2048]; - - int i; - - - -#ifdef VERBOSE_FUNCTION_DEVICE - - PrintLog("CDVDlinuz DVD: DVDgetDiskType()"); - -#endif /* VERBOSE_FUNCTION_DEVICE */ - - - - retval = DVDGetStructures(); - - if(retval < 0) return(-1); // Can't get DVD structures? Not a DVD then. - - if(layer.d.EndDataSector == 0) return(-1); // Missing info? Abort. - - - - retval = DVDreadTrack(16, CDVD_MODE_2048, tempbuffer); - - if(retval < 0) { - - return(-1); - - } // ENDIF- Couldn't read the ISO9660 volume track? Fail. - - - - tempdisctype = CDVD_TYPE_UNKNOWN; - - if(layer.d.NumberOfLayers == 0) { - -#ifdef VERBOSE_DISC_INFO - - PrintLog("CDVDlinuz DVD: Found Single-Sided DVD."); - -#endif /* VERBOSE_DISC_INFO */ - - disctype = CDVD_TYPE_DETCTDVDS; - - } else { - -#ifdef VERBOSE_DISC_INFO - - PrintLog("CDVDlinuz DVD: Found Dual-Sided DVD."); - -#endif /* VERBOSE_DISC_INFO */ - - disctype = CDVD_TYPE_DETCTDVDD; - - } // ENDIF- Are we looking at a single layer DVD? (NumberOfLayers + 1) - - - - i = 0; - - while((playstationname[i] != 0) && - - (playstationname[i] == tempbuffer[8 + i])) i++; - - if(playstationname[i] == 0) { - -#ifdef VERBOSE_DISC_TYPE - - PrintLog("CDVDlinuz DVD: Found Playstation 2 DVD."); - -#endif /* VERBOSE_DISC_TYPE */ - - tempdisctype = CDVD_TYPE_PS2DVD; - - } else { - -#ifdef VERBOSE_DISC_TYPE - - PrintLog("CDVDlinuz DVD: Guessing it's a Video DVD."); - -#endif /* VERBOSE_DISC_TYPE */ - - tempdisctype = CDVD_TYPE_DVDV; - - } // ENDIF- Is this a playstation disc? - - - - for(i = 0; i < 2048; i++) tocbuffer[i] = 0; - - - - if(layer.d.NumberOfLayers == 0) { - - tocbuffer[0] = 0x04; - - tocbuffer[4] = 0x86; - - tocbuffer[5] = 0x72; - - } else { - - tocbuffer[0] = 0x24; - - tocbuffer[4] = 0x41; - - tocbuffer[5] = 0x95; - - } // ENDIF- Are we looking at a single layer DVD? (NumberOfLayers + 1) - - - - tocbuffer[1] = 0x02; - - tocbuffer[2] = 0xF2; - - tocbuffer[3] = 0x00; - - - - tocbuffer[16] = 0x00; - - tocbuffer[17] = 0x03; - - tocbuffer[18] = 0x00; - - tocbuffer[19] = 0x00; - - - - disctype = tempdisctype; - - return(disctype); - -} // END DVDgetDiskType() - +/* DVD.c + + * Copyright (C) 2002-2005 PCSX2 Team + + * + + * This program is free software; you can redistribute it and/or modify + + * it under the terms of the GNU General Public License as published by + + * the Free Software Foundation; either version 2 of the License, or + + * (at your option) any later version. + + * + + * This program 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 this program; if not, write to the Free Software + + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + * + + * PCSX2 members can be contacted through their website at www.pcsx2.net. + + */ + + + + + +#include + +#include // IOCTL_DVD... + + + +#include // off64_t + + + +#define CDVDdefs + +#include "PS2Edefs.h" + + + +#include "logfile.h" + +#include "device.h" // FinishCommand() + + + + + +struct { + + DVD_DESCRIPTOR_HEADER h; + + DVD_LAYER_DESCRIPTOR d; + +} layer; + +// DVD_LAYER_DESCRIPTOR layer; + +// DVD_COPYRIGHT_DESCRIPTOR copyright; + +// DVD_DISK_KEY_DESCRIPTOR disckey; + +// DVD_BCA_DESCRIPTOR bca; + +// DVD_MANUFACTURER_DESCRIPTOR manufact; + + + + + +void InitDVDInfo() { + + layer.d.EndDataSector = 0; + +} // END InitDVDInfo() + + + + + +s32 DVDGetStructures() { + + DVD_SESSION_ID sessionid; + + DVD_READ_STRUCTURE request; + + DWORD byteswritten; + + BOOL boolresult; + + DWORD errcode; + + s32 retval; + + + +#ifdef VERBOSE_FUNCTION_DEVICE + + PrintLog("CDVDlinuz DVD: DVDgetStructures()"); + +#endif /* VERBOSE_FUNCTION_DEVICE */ + + + + boolresult = DeviceIoControl(devicehandle, + + IOCTL_DVD_START_SESSION, + + NULL, + + 0, + + &sessionid, + + sizeof(DVD_SESSION_ID), + + &byteswritten, + + &waitevent); + + errcode = FinishCommand(boolresult); + + if(errcode != 0) { + +#ifdef VERBOSE_WARNING_DEVICE + + PrintLog("CDVDlinuz DVD: Couldn't start session!"); + + PrintError("CDVDlinuz DVD", errcode); + +#endif /* VERBOSE_WARNING_DEVICE */ + + return(-1); + + } // ENDIF- Couldn't start a user session on the DVD drive? Fail. + + + + request.BlockByteOffset.QuadPart = 0; + + request.Format = DvdPhysicalDescriptor; + + request.SessionId = sessionid; + + request.LayerNumber = 0; + + retval = 0; + + boolresult = DeviceIoControl(devicehandle, + + IOCTL_DVD_READ_STRUCTURE, + + &request, + + sizeof(DVD_READ_STRUCTURE), + + &layer, + + sizeof(layer), + + &byteswritten, + + &waitevent); + + errcode = FinishCommand(boolresult); + + if(errcode != 0) { + +#ifdef VERBOSE_WARNING_DEVICE + + PrintLog("CDVDlinuz DVD: Couldn't get layer data!"); + + PrintError("CDVDlinuz DVD", errcode); + +#endif /* VERBOSE_WARNING_DEVICE */ + + retval = -1; + + } // ENDIF- Couldn't get layer data? (Including DVD size) Abort. + + + +#ifdef VERBOSE_DISC_INFO + + switch(layer.d.BookType) { + + case 0: + + PrintLog("CDVDlinuz DVD: Book Type: DVD-ROM"); + + break; + + case 1: + + PrintLog("CDVDlinuz DVD: Book Type: DVD-RAM"); + + break; + + case 2: + + PrintLog("CDVDlinuz DVD: Book Type: DVD-R"); + + break; + + case 3: + + PrintLog("CDVDlinuz DVD: Book Type: DVD-RW"); + + break; + + case 9: + + PrintLog("CDVDlinuz DVD: Book Type: DVD+RW"); + + break; + + default: + + PrintLog("CDVDlinuz DVD: Book Type: Unknown (%i)", layer.d.BookType); + + break; + + } // ENDSWITCH- Displaying the Book Type + + PrintLog("CDVDlinuz DVD: Book Version %i", layer.d.BookVersion); + + switch(layer.d.MinimumRate) { + + case 0: + + PrintLog("CDVDlinuz DVD: Use Minimum Rate for: DVD-ROM"); + + break; + + case 1: + + PrintLog("CDVDlinuz DVD: Use Minimum Rate for: DVD-RAM"); + + break; + + case 2: + + PrintLog("CDVDlinuz DVD: Use Minimum Rate for: DVD-R"); + + break; + + case 3: + + PrintLog("CDVDlinuz DVD: Use Minimum Rate for: DVD-RW"); + + break; + + case 9: + + PrintLog("CDVDlinuz DVD: Use Minimum Rate for: DVD+RW"); + + break; + + default: + + PrintLog("CDVDlinuz DVD: Use Minimum Rate for: Unknown (%i)", layer.d.MinimumRate); + + break; + + } // ENDSWITCH- Displaying the Minimum (Spin?) Rate + + switch(layer.d.DiskSize) { + + case 0: + + PrintLog("CDVDlinuz DVD: Physical Disk Size: 120mm"); + + break; + + case 1: + + PrintLog("CDVDlinuz DVD: Physical Disk Size: 80mm"); + + break; + + default: + + PrintLog("CDVDlinuz DVD: Physical Disk Size: Unknown (%i)", layer.d.DiskSize); + + break; + + } // ENDSWITCH- What's the Disk Size? + + switch(layer.d.LayerType) { + + case 1: + + PrintLog("CDVDlinuz DVD: Layer Type: Read-Only"); + + break; + + case 2: + + PrintLog("CDVDlinuz DVD: Layer Type: Recordable"); + + break; + + case 4: + + PrintLog("CDVDlinuz DVD: Layer Type: Rewritable"); + + break; + + default: + + PrintLog("CDVDlinuz DVD: Layer Type: Unknown (%i)", layer.d.LayerType); + + break; + + } // ENDSWITCH- Displaying the Layer Type + + switch(layer.d.TrackPath) { + + case 0: + + PrintLog("CDVDlinuz DVD: Track Path: PTP"); + + break; + + case 1: + + PrintLog("CDVDlinuz DVD: Track Path: OTP"); + + break; + + default: + + PrintLog("CDVDlinuz DVD: Track Path: Unknown (%i)", layer.d.TrackPath); + + break; + + } // ENDSWITCH- What's Track Path Layout? + + PrintLog("CDVDlinuz DVD: Number of Layers: %i", layer.d.NumberOfLayers + 1); + + switch(layer.d.TrackDensity) { + + case 0: + + PrintLog("CDVDlinuz DVD: Track Density: .74 m/track"); + + break; + + case 1: + + PrintLog("CDVDlinuz DVD: Track Density: .8 m/track"); + + break; + + case 2: + + PrintLog("CDVDlinuz DVD: Track Density: .615 m/track"); + + break; + + default: + + PrintLog("CDVDlinuz DVD: Track Density: Unknown (%i)", layer.d.TrackDensity); + + break; + + } // ENDSWITCH- Displaying the Layer Type + + switch(layer.d.LinearDensity) { + + case 0: + + PrintLog("CDVDlinuz DVD: Linear Density: .267 m/bit"); + + break; + + case 1: + + PrintLog("CDVDlinuz DVD: Linear Density: .293 m/bit"); + + break; + + case 2: + + PrintLog("CDVDlinuz DVD: Linear Density: .409 to .435 m/bit"); + + break; + + case 4: + + PrintLog("CDVDlinuz DVD: Linear Density: .280 to .291 m/bit"); + + break; + + case 8: + + PrintLog("CDVDlinuz DVD: Linear Density: .353 m/bit"); + + break; + + default: + + PrintLog("CDVDlinuz DVD: Linear Density: Unknown (%i)", layer.d.LinearDensity); + + break; + + } // ENDSWITCH- Displaying the Book Type + + if(layer.d.StartingDataSector == 0x30000) { + + PrintLog("CDVDlinuz DVD: Starting Sector: %lu (DVD-ROM, DVD-R, DVD-RW)", + + layer.d.StartingDataSector); + + } else if(layer.d.StartingDataSector == 0x31000) { + + PrintLog("CDVDlinuz DVD: Starting Sector: %lu (DVD-RAM, DVD+RW)", + + layer.d.StartingDataSector); + + } else { + + PrintLog("CDVDlinuz DVD: Starting Sector: %lu", layer.d.StartingDataSector); + + } // ENDLONGIF- What does the starting sector tell us? + + PrintLog("CDVDlinuz DVD: End of Layer 0: %lu", layer.d.EndLayerZeroSector); + + PrintLog("CDVDlinuz DVD: Ending Sector: %lu", layer.d.EndDataSector); + + if(layer.d.BCAFlag != 0) PrintLog("CDVDlinuz DVD: BCA data present"); + +#endif /* VERBOSE_DISC_INFO */ + + + + boolresult = DeviceIoControl(devicehandle, + + IOCTL_DVD_END_SESSION, + + &sessionid, + + sizeof(DVD_SESSION_ID), + + NULL, + + 0, + + &byteswritten, + + &waitevent); + + errcode = FinishCommand(boolresult); + + if(errcode != 0) { + +#ifdef VERBOSE_WARNING_DEVICE + + PrintLog("CDVDlinuz DVD: Couldn't end the session!"); + + PrintError("CDVDlinuz DVD", errcode); + +#endif /* VERBOSE_WARNING_DEVICE */ + + } // ENDIF- Couldn't end the user session? Report it. + + + + return(retval); + +} // END DVDGetStructures() + + + + + +s32 DVDreadTrack(u32 lsn, int mode, u8 *buffer) { + + LARGE_INTEGER targetpos; + + DWORD byteswritten; + + BOOL boolresult; + + DWORD errcode; + + + +#ifdef VERBOSE_FUNCTION_DEVICE + + PrintLog("CDVDlinuz DVD: DVDreadTrack(%lu)", lsn); + +#endif /* VERBOSE_FUNCTION_DEVICE */ + + + + targetpos.QuadPart = lsn * 2048; + + waitevent.Offset = targetpos.LowPart; + + waitevent.OffsetHigh = targetpos.HighPart; + + + + boolresult = ReadFile(devicehandle, + + buffer, + + 2048, + + &byteswritten, + + &waitevent); + + errcode = FinishCommand(boolresult); + + + + if(errcode != 0) { + +#ifdef VERBOSE_WARNING_DEVICE + + PrintLog("CDVDlinuz DVD: Couldn't read sector!"); + + PrintError("CDVDlinuz DVD", errcode); + +#endif /* VERBOSE_WARNING_DEVICE */ + + return(-1); + + } // ENDIF- Trouble with the command? Report it. + + + + if(boolresult == FALSE) { + + boolresult = GetOverlappedResult(devicehandle, + + &waitevent, + + &byteswritten, + + FALSE); + + } // ENDIF- Did the initial call not complete? Get byteswritten for + + // the completed call. + + + + if(byteswritten < 2048) { + +#ifdef VERBOSE_WARNING_DEVICE + + errcode = GetLastError(); + + PrintLog("CDVDlinuz CD: Short block! only got %u out of %u bytes", + + byteswritten, 2048); + + PrintError("CDVDlinuz CD", errcode); + +#endif /* VERBOSE_WARNING_DEVICE */ + + return(-1); + + } // ENDIF- Didn't get enough bytes? Report and Abort! + + + + return(0); + +} // END DVDreadTrack() + + + + + +s32 DVDgetTN(cdvdTN *cdvdtn) { + +#ifdef VERBOSE_FUNCTION_DEVICE + + PrintLog("CDVDlinuz DVD: DVDgetTN()"); + +#endif /* VERBOSE_FUNCTION_DEVICE */ + + + + if(cdvdtn != NULL) { + + cdvdtn->strack = 1; + + cdvdtn->etrack = 1; + + } // ENDIF- Does the user really want this data? + + return(0); + +} // END DVDgetTN() + + + + + +s32 DVDgetTD(u8 newtrack, cdvdTD *cdvdtd) { + +#ifdef VERBOSE_FUNCTION_DEVICE + + PrintLog("CDVDlinuz DVD: DVDgetTD()"); + +#endif /* VERBOSE_FUNCTION_DEVICE */ + + + + if((newtrack >= 2) && (newtrack != 0xAA)) return(-1); // Wrong track + + + + if(cdvdtd != NULL) { + + cdvdtd->type = 0; + + cdvdtd->lsn = layer.d.EndDataSector - layer.d.StartingDataSector + 1; + + } // ENDIF- Does the user really want this data? + + return(0); + +} // END DVDgetTD() + + + + + +s32 DVDgetDiskType() { + + char playstationname[] = "PLAYSTATION\0"; + + int retval; + + s32 tempdisctype; + + char tempbuffer[2048]; + + int i; + + + +#ifdef VERBOSE_FUNCTION_DEVICE + + PrintLog("CDVDlinuz DVD: DVDgetDiskType()"); + +#endif /* VERBOSE_FUNCTION_DEVICE */ + + + + retval = DVDGetStructures(); + + if(retval < 0) return(-1); // Can't get DVD structures? Not a DVD then. + + if(layer.d.EndDataSector == 0) return(-1); // Missing info? Abort. + + + + retval = DVDreadTrack(16, CDVD_MODE_2048, tempbuffer); + + if(retval < 0) { + + return(-1); + + } // ENDIF- Couldn't read the ISO9660 volume track? Fail. + + + + tempdisctype = CDVD_TYPE_UNKNOWN; + + if(layer.d.NumberOfLayers == 0) { + +#ifdef VERBOSE_DISC_INFO + + PrintLog("CDVDlinuz DVD: Found Single-Sided DVD."); + +#endif /* VERBOSE_DISC_INFO */ + + disctype = CDVD_TYPE_DETCTDVDS; + + } else { + +#ifdef VERBOSE_DISC_INFO + + PrintLog("CDVDlinuz DVD: Found Dual-Sided DVD."); + +#endif /* VERBOSE_DISC_INFO */ + + disctype = CDVD_TYPE_DETCTDVDD; + + } // ENDIF- Are we looking at a single layer DVD? (NumberOfLayers + 1) + + + + i = 0; + + while((playstationname[i] != 0) && + + (playstationname[i] == tempbuffer[8 + i])) i++; + + if(playstationname[i] == 0) { + +#ifdef VERBOSE_DISC_TYPE + + PrintLog("CDVDlinuz DVD: Found Playstation 2 DVD."); + +#endif /* VERBOSE_DISC_TYPE */ + + tempdisctype = CDVD_TYPE_PS2DVD; + + } else { + +#ifdef VERBOSE_DISC_TYPE + + PrintLog("CDVDlinuz DVD: Guessing it's a Video DVD."); + +#endif /* VERBOSE_DISC_TYPE */ + + tempdisctype = CDVD_TYPE_DVDV; + + } // ENDIF- Is this a playstation disc? + + + + for(i = 0; i < 2048; i++) tocbuffer[i] = 0; + + + + if(layer.d.NumberOfLayers == 0) { + + tocbuffer[0] = 0x04; + + tocbuffer[4] = 0x86; + + tocbuffer[5] = 0x72; + + } else { + + tocbuffer[0] = 0x24; + + tocbuffer[4] = 0x41; + + tocbuffer[5] = 0x95; + + } // ENDIF- Are we looking at a single layer DVD? (NumberOfLayers + 1) + + + + tocbuffer[1] = 0x02; + + tocbuffer[2] = 0xF2; + + tocbuffer[3] = 0x00; + + + + tocbuffer[16] = 0x00; + + tocbuffer[17] = 0x03; + + tocbuffer[18] = 0x00; + + tocbuffer[19] = 0x00; + + + + disctype = tempdisctype; + + return(disctype); + +} // END DVDgetDiskType() + diff --git a/plugins/CDVDlinuz/Src/Win32/actualfile.c b/plugins/CDVDlinuz/Src/Win32/actualfile.c index 7212d1e1c1..864bf91717 100644 --- a/plugins/CDVDlinuz/Src/Win32/actualfile.c +++ b/plugins/CDVDlinuz/Src/Win32/actualfile.c @@ -1,506 +1,506 @@ -/* actualfile.c - - * Copyright (C) 2002-2005 PCSX2 Team - - * - - * This program is free software; you can redistribute it and/or modify - - * it under the terms of the GNU General Public License as published by - - * the Free Software Foundation; either version 2 of the License, or - - * (at your option) any later version. - - * - - * This program 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 this program; if not, write to the Free Software - - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - * - - * PCSX2 members can be contacted through their website at www.pcsx2.net. - - */ - - - - - -#include - - - -#include // NULL - - - -#include "logfile.h" - -#include "actualfile.h" - - - - - -int IsActualFile(const char *filename) { - - DWORD retval; - - - - if(filename == NULL) return(-1); - - - -#ifdef VERBOSE_FUNCTION_ACTUALFILE - - PrintLog("CDVDiso file: IsActualFile(%s)", filename); - -#endif /* VERBOSE_FUNCTION_ACTUALFILE */ - - - - retval = GetFileAttributes(filename); - - if(retval == INVALID_FILE_ATTRIBUTES) return(-1); // Name doesn't exist. - - if((retval & FILE_ATTRIBUTE_DIRECTORY) != 0) return(-2); - - - - return(0); // Yep, that's a file. - -} // END IsActualFile() - - - - - -void ActualFileDelete(const char *filename) { - -#ifdef VERBOSE_FUNCTION_ACTUALFILE - - PrintLog("CDVDiso file: ActualFileDelete(%s)", filename); - -#endif /* VERBOSE_FUNCTION_ACTUALFILE */ - - - - DeleteFile(filename); - -} // END ActualFileDelete() - - - - - -void ActualFileRename(const char *origname, const char *newname) { - -#ifdef VERBOSE_FUNCTION_ACTUALFILE - - PrintLog("CDVDiso file: ActualFileDelete(%s->%s)", origname, newname); - -#endif /* VERBOSE_FUNCTION_ACTUALFILE */ - - - - MoveFile(origname, newname); - - return; - -} // END ActualFileRename() - - - - - -ACTUALHANDLE ActualFileOpenForRead(const char *filename) { - - HANDLE newhandle; - - - - if(filename == NULL) return(NULL); - - - -#ifdef VERBOSE_FUNCTION_ACTUALFILE - - PrintLog("CDVDiso file: ActualFileOpenForRead(%s)", filename); - -#endif /* VERBOSE_FUNCTION_ACTUALFILE */ - - - - newhandle = CreateFile(filename, - - GENERIC_READ, - - FILE_SHARE_READ, - - NULL, - - OPEN_EXISTING, - - FILE_FLAG_RANDOM_ACCESS, - - NULL); - - if(newhandle == INVALID_HANDLE_VALUE) { - -#ifdef VERBOSE_WARNING_ACTUALFILE - - PrintLog("CDVDiso file: Error opening file %s", filename); - -#endif /* VERBOSE_WARNING_ACTUALFILE */ - - return(NULL); - - } // ENDIF- Error? Abort - - - - return(newhandle); - -} // END ActualFileOpenForRead() - - - - - -off64_t ActualFileSize(ACTUALHANDLE handle) { - - int retval; - - BY_HANDLE_FILE_INFORMATION info; - - off64_t retsize; - - - - if(handle == NULL) return(-1); - - if(handle == INVALID_HANDLE_VALUE) return(-1); - - - -#ifdef VERBOSE_FUNCTION_ACTUALFILE - - PrintLog("CDVDiso file: ActualFileSize()"); - -#endif /* VERBOSE_FUNCTION_ACTUALFILE */ - - - - retval = GetFileInformationByHandle(handle, &info); - - if(retval == 0) return(-1); // Handle doesn't exist... - - - - retsize = info.nFileSizeHigh; - - retsize *= 0x10000; - - retsize *= 0x10000; - - retsize += info.nFileSizeLow; - - return(retsize); - -} // END ActualFileSize() - - - - - -int ActualFileSeek(ACTUALHANDLE handle, off64_t position) { - - // int retval; - - LARGE_INTEGER realpos; - - DWORD errcode; - - - - if(handle == NULL) return(-1); - - if(handle == INVALID_HANDLE_VALUE) return(-1); - - if(position < 0) return(-1); - - - -#ifdef VERBOSE_FUNCTION_ACTUALFILE - - PrintLog("CDVDiso file: ActualFileSeek(%llu)", position); - -#endif /* VERBOSE_FUNCTION_ACTUALFILE */ - - - - realpos.QuadPart = position; - -////// WinXP code for seek - -// retval = SetFilePointerEx(handle, - -// realpos, - -// NULL, - -// FILE_BEGIN); - -// if(retval == 0) { - - - -////// Win98 code for seek - - realpos.LowPart = SetFilePointer(handle, - - realpos.LowPart, - - &realpos.HighPart, - - FILE_BEGIN); - - errcode = GetLastError(); - - if((realpos.LowPart == 0xFFFFFFFF) && (errcode != NO_ERROR)) { - - - -#ifdef VERBOSE_WARNING_ACTUALFILE - - PrintLog("CDVDiso file: Error on seek (%llu)", position); - - PrintError("CDVDiso file", errcode); - -#endif /* VERBOSE_WARNING_ACTUALFILE */ - - return(-1); - - } // ENDIF- Error? Abort - - - - return(0); - -} // END ActualFileSeek() - - - - - -int ActualFileRead(ACTUALHANDLE handle, int bytes, char *buffer) { - - int retval; - - DWORD bytesread; - -#ifdef VERBOSE_WARNING_ACTUALFILE - - DWORD errcode; - -#endif /* VERBOSE_WARNING_ACTUALFILE */ - - - - if(handle == NULL) return(-1); - - if(handle == INVALID_HANDLE_VALUE) return(-1); - - if(bytes < 1) return(-1); - - if(buffer == NULL) return(-1); - - - -#ifdef VERBOSE_FUNCTION_ACTUALFILE - - PrintLog("CDVDiso file: ActualFileRead(%i)", bytes); - -#endif /* VERBOSE_FUNCTION_ACTUALFILE */ - - - - retval = ReadFile(handle, buffer, bytes, &bytesread, NULL); - - if(retval == 0) { - -#ifdef VERBOSE_WARNING_ACTUALFILE - - errcode = GetLastError(); - - PrintLog("CDVDiso file: Error reading from file"); - - PrintError("CDVDiso file", errcode); - -#endif /* VERBOSE_WARNING_ACTUALFILE */ - - return(-1); - - } // ENDIF- Error? Abort - - if(bytesread < bytes) { - -#ifdef VERBOSE_WARNING_ACTUALFILE - - PrintLog("CDVDiso file: Short Block! Only read %i out of %i bytes", bytesread, bytes); - -#endif /* VERBOSE_WARNING_ACTUALFILE */ - - } // ENDIF- Error? Abort - - - - return(bytesread); // Send back how many bytes read - -} // END ActualFileRead() - - - - - -void ActualFileClose(ACTUALHANDLE handle) { - - if(handle == NULL) return; - - if(handle == INVALID_HANDLE_VALUE) return; - - - -#ifdef VERBOSE_FUNCTION_ACTUALFILE - - PrintLog("CDVDiso file: ActualFileClose()"); - -#endif /* VERBOSE_FUNCTION_ACTUALFILE */ - - - - CloseHandle(handle); - - return; - -} // END ActualFileClose() - - - - - -ACTUALHANDLE ActualFileOpenForWrite(const char *filename) { - - HANDLE newhandle; - - - - if(filename == NULL) return(NULL); - - - -#ifdef VERBOSE_FUNCTION_ACTUALFILE - - PrintLog("CDVDiso file: ActualFileOpenForWrite(%s)", filename); - -#endif /* VERBOSE_FUNCTION_ACTUALFILE */ - - - - newhandle = CreateFile(filename, - - GENERIC_WRITE, - - 0, - - NULL, - - CREATE_ALWAYS, - - FILE_FLAG_SEQUENTIAL_SCAN, - - NULL); - - if(newhandle == INVALID_HANDLE_VALUE) { - -#ifdef VERBOSE_WARNING_ACTUALFILE - - PrintLog("CDVDiso file: Error opening file %s", filename); - -#endif /* VERBOSE_WARNING_ACTUALFILE */ - - return(NULL); - - } // ENDIF- Error? Abort - - - - return(newhandle); - -} // END ActualFileOpenForWrite() - - - - - -int ActualFileWrite(ACTUALHANDLE handle, int bytes, char *buffer) { - - int retval; - - DWORD byteswritten; - - - - if(handle == NULL) return(-1); - - if(handle == INVALID_HANDLE_VALUE) return(-1); - - if(bytes < 1) return(-1); - - if(buffer == NULL) return(-1); - - - -#ifdef VERBOSE_FUNCTION_ACTUALFILE - - PrintLog("CDVDiso file: ActualFileWrite(%i)", bytes); - -#endif /* VERBOSE_FUNCTION_ACTUALFILE */ - - - - retval = WriteFile(handle, buffer, bytes, &byteswritten, NULL); - - if(retval == 0) { - -#ifdef VERBOSE_WARNING_ACTUALFILE - - PrintLog("CDVDiso file: Error writing to file!"); - -#endif /* VERBOSE_WARNING_ACTUALFILE */ - - // return(-1); - - } // ENDIF- Error? Abort - - - - return(byteswritten); // Send back how many bytes written - -} // END ActualFileWrite() - +/* actualfile.c + + * Copyright (C) 2002-2005 PCSX2 Team + + * + + * This program is free software; you can redistribute it and/or modify + + * it under the terms of the GNU General Public License as published by + + * the Free Software Foundation; either version 2 of the License, or + + * (at your option) any later version. + + * + + * This program 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 this program; if not, write to the Free Software + + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + * + + * PCSX2 members can be contacted through their website at www.pcsx2.net. + + */ + + + + + +#include + + + +#include // NULL + + + +#include "logfile.h" + +#include "actualfile.h" + + + + + +int IsActualFile(const char *filename) { + + DWORD retval; + + + + if(filename == NULL) return(-1); + + + +#ifdef VERBOSE_FUNCTION_ACTUALFILE + + PrintLog("CDVDiso file: IsActualFile(%s)", filename); + +#endif /* VERBOSE_FUNCTION_ACTUALFILE */ + + + + retval = GetFileAttributes(filename); + + if(retval == INVALID_FILE_ATTRIBUTES) return(-1); // Name doesn't exist. + + if((retval & FILE_ATTRIBUTE_DIRECTORY) != 0) return(-2); + + + + return(0); // Yep, that's a file. + +} // END IsActualFile() + + + + + +void ActualFileDelete(const char *filename) { + +#ifdef VERBOSE_FUNCTION_ACTUALFILE + + PrintLog("CDVDiso file: ActualFileDelete(%s)", filename); + +#endif /* VERBOSE_FUNCTION_ACTUALFILE */ + + + + DeleteFile(filename); + +} // END ActualFileDelete() + + + + + +void ActualFileRename(const char *origname, const char *newname) { + +#ifdef VERBOSE_FUNCTION_ACTUALFILE + + PrintLog("CDVDiso file: ActualFileDelete(%s->%s)", origname, newname); + +#endif /* VERBOSE_FUNCTION_ACTUALFILE */ + + + + MoveFile(origname, newname); + + return; + +} // END ActualFileRename() + + + + + +ACTUALHANDLE ActualFileOpenForRead(const char *filename) { + + HANDLE newhandle; + + + + if(filename == NULL) return(NULL); + + + +#ifdef VERBOSE_FUNCTION_ACTUALFILE + + PrintLog("CDVDiso file: ActualFileOpenForRead(%s)", filename); + +#endif /* VERBOSE_FUNCTION_ACTUALFILE */ + + + + newhandle = CreateFile(filename, + + GENERIC_READ, + + FILE_SHARE_READ, + + NULL, + + OPEN_EXISTING, + + FILE_FLAG_RANDOM_ACCESS, + + NULL); + + if(newhandle == INVALID_HANDLE_VALUE) { + +#ifdef VERBOSE_WARNING_ACTUALFILE + + PrintLog("CDVDiso file: Error opening file %s", filename); + +#endif /* VERBOSE_WARNING_ACTUALFILE */ + + return(NULL); + + } // ENDIF- Error? Abort + + + + return(newhandle); + +} // END ActualFileOpenForRead() + + + + + +off64_t ActualFileSize(ACTUALHANDLE handle) { + + int retval; + + BY_HANDLE_FILE_INFORMATION info; + + off64_t retsize; + + + + if(handle == NULL) return(-1); + + if(handle == INVALID_HANDLE_VALUE) return(-1); + + + +#ifdef VERBOSE_FUNCTION_ACTUALFILE + + PrintLog("CDVDiso file: ActualFileSize()"); + +#endif /* VERBOSE_FUNCTION_ACTUALFILE */ + + + + retval = GetFileInformationByHandle(handle, &info); + + if(retval == 0) return(-1); // Handle doesn't exist... + + + + retsize = info.nFileSizeHigh; + + retsize *= 0x10000; + + retsize *= 0x10000; + + retsize += info.nFileSizeLow; + + return(retsize); + +} // END ActualFileSize() + + + + + +int ActualFileSeek(ACTUALHANDLE handle, off64_t position) { + + // int retval; + + LARGE_INTEGER realpos; + + DWORD errcode; + + + + if(handle == NULL) return(-1); + + if(handle == INVALID_HANDLE_VALUE) return(-1); + + if(position < 0) return(-1); + + + +#ifdef VERBOSE_FUNCTION_ACTUALFILE + + PrintLog("CDVDiso file: ActualFileSeek(%llu)", position); + +#endif /* VERBOSE_FUNCTION_ACTUALFILE */ + + + + realpos.QuadPart = position; + +////// WinXP code for seek + +// retval = SetFilePointerEx(handle, + +// realpos, + +// NULL, + +// FILE_BEGIN); + +// if(retval == 0) { + + + +////// Win98 code for seek + + realpos.LowPart = SetFilePointer(handle, + + realpos.LowPart, + + &realpos.HighPart, + + FILE_BEGIN); + + errcode = GetLastError(); + + if((realpos.LowPart == 0xFFFFFFFF) && (errcode != NO_ERROR)) { + + + +#ifdef VERBOSE_WARNING_ACTUALFILE + + PrintLog("CDVDiso file: Error on seek (%llu)", position); + + PrintError("CDVDiso file", errcode); + +#endif /* VERBOSE_WARNING_ACTUALFILE */ + + return(-1); + + } // ENDIF- Error? Abort + + + + return(0); + +} // END ActualFileSeek() + + + + + +int ActualFileRead(ACTUALHANDLE handle, int bytes, char *buffer) { + + int retval; + + DWORD bytesread; + +#ifdef VERBOSE_WARNING_ACTUALFILE + + DWORD errcode; + +#endif /* VERBOSE_WARNING_ACTUALFILE */ + + + + if(handle == NULL) return(-1); + + if(handle == INVALID_HANDLE_VALUE) return(-1); + + if(bytes < 1) return(-1); + + if(buffer == NULL) return(-1); + + + +#ifdef VERBOSE_FUNCTION_ACTUALFILE + + PrintLog("CDVDiso file: ActualFileRead(%i)", bytes); + +#endif /* VERBOSE_FUNCTION_ACTUALFILE */ + + + + retval = ReadFile(handle, buffer, bytes, &bytesread, NULL); + + if(retval == 0) { + +#ifdef VERBOSE_WARNING_ACTUALFILE + + errcode = GetLastError(); + + PrintLog("CDVDiso file: Error reading from file"); + + PrintError("CDVDiso file", errcode); + +#endif /* VERBOSE_WARNING_ACTUALFILE */ + + return(-1); + + } // ENDIF- Error? Abort + + if(bytesread < bytes) { + +#ifdef VERBOSE_WARNING_ACTUALFILE + + PrintLog("CDVDiso file: Short Block! Only read %i out of %i bytes", bytesread, bytes); + +#endif /* VERBOSE_WARNING_ACTUALFILE */ + + } // ENDIF- Error? Abort + + + + return(bytesread); // Send back how many bytes read + +} // END ActualFileRead() + + + + + +void ActualFileClose(ACTUALHANDLE handle) { + + if(handle == NULL) return; + + if(handle == INVALID_HANDLE_VALUE) return; + + + +#ifdef VERBOSE_FUNCTION_ACTUALFILE + + PrintLog("CDVDiso file: ActualFileClose()"); + +#endif /* VERBOSE_FUNCTION_ACTUALFILE */ + + + + CloseHandle(handle); + + return; + +} // END ActualFileClose() + + + + + +ACTUALHANDLE ActualFileOpenForWrite(const char *filename) { + + HANDLE newhandle; + + + + if(filename == NULL) return(NULL); + + + +#ifdef VERBOSE_FUNCTION_ACTUALFILE + + PrintLog("CDVDiso file: ActualFileOpenForWrite(%s)", filename); + +#endif /* VERBOSE_FUNCTION_ACTUALFILE */ + + + + newhandle = CreateFile(filename, + + GENERIC_WRITE, + + 0, + + NULL, + + CREATE_ALWAYS, + + FILE_FLAG_SEQUENTIAL_SCAN, + + NULL); + + if(newhandle == INVALID_HANDLE_VALUE) { + +#ifdef VERBOSE_WARNING_ACTUALFILE + + PrintLog("CDVDiso file: Error opening file %s", filename); + +#endif /* VERBOSE_WARNING_ACTUALFILE */ + + return(NULL); + + } // ENDIF- Error? Abort + + + + return(newhandle); + +} // END ActualFileOpenForWrite() + + + + + +int ActualFileWrite(ACTUALHANDLE handle, int bytes, char *buffer) { + + int retval; + + DWORD byteswritten; + + + + if(handle == NULL) return(-1); + + if(handle == INVALID_HANDLE_VALUE) return(-1); + + if(bytes < 1) return(-1); + + if(buffer == NULL) return(-1); + + + +#ifdef VERBOSE_FUNCTION_ACTUALFILE + + PrintLog("CDVDiso file: ActualFileWrite(%i)", bytes); + +#endif /* VERBOSE_FUNCTION_ACTUALFILE */ + + + + retval = WriteFile(handle, buffer, bytes, &byteswritten, NULL); + + if(retval == 0) { + +#ifdef VERBOSE_WARNING_ACTUALFILE + + PrintLog("CDVDiso file: Error writing to file!"); + +#endif /* VERBOSE_WARNING_ACTUALFILE */ + + // return(-1); + + } // ENDIF- Error? Abort + + + + return(byteswritten); // Send back how many bytes written + +} // END ActualFileWrite() + diff --git a/plugins/CDVDlinuz/Src/Win32/actualfile.h b/plugins/CDVDlinuz/Src/Win32/actualfile.h index 63053f440d..f91258c7f6 100644 --- a/plugins/CDVDlinuz/Src/Win32/actualfile.h +++ b/plugins/CDVDlinuz/Src/Win32/actualfile.h @@ -1,104 +1,104 @@ -/* actualfile.h - - * Copyright (C) 2002-2005 PCSX2 Team - - * - - * This program is free software; you can redistribute it and/or modify - - * it under the terms of the GNU General Public License as published by - - * the Free Software Foundation; either version 2 of the License, or - - * (at your option) any later version. - - * - - * This program 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 this program; if not, write to the Free Software - - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - * - - * PCSX2 members can be contacted through their website at www.pcsx2.net. - - */ - - - - - -#ifndef ACTUALFILE_H - -#define ACTUALFILE_H - - - - - -#include - - - -#include // off64_t - - - - - -#define ACTUALHANDLE HANDLE - -#define ACTUALHANDLENULL NULL - - - -// #define VERBOSE_FUNCTION_ACTUALFILE - -// #define VERBOSE_WARNING_ACTUALFILE - - - - - -extern int IsActualFile(const char *filename); - -extern void ActualFileDelete(const char *filename); - -extern void ActualFileRename(const char *origname, const char *newname); - - - -extern ACTUALHANDLE ActualFileOpenForRead(const char *filename); - -extern off64_t ActualFileSize(ACTUALHANDLE handle); - -extern int ActualFileSeek(ACTUALHANDLE handle, off64_t position); - -extern int ActualFileRead(ACTUALHANDLE handle, int bytes, char *buffer); - -extern void ActualFileClose(ACTUALHANDLE handle); - - - -extern ACTUALHANDLE ActualFileOpenForWrite(const char *filename); - -extern int ActualFileWrite(ACTUALHANDLE handle, int bytes, char *buffer); - - - - - -#endif /* ACTUALFILE_H */ - +/* actualfile.h + + * Copyright (C) 2002-2005 PCSX2 Team + + * + + * This program is free software; you can redistribute it and/or modify + + * it under the terms of the GNU General Public License as published by + + * the Free Software Foundation; either version 2 of the License, or + + * (at your option) any later version. + + * + + * This program 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 this program; if not, write to the Free Software + + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + * + + * PCSX2 members can be contacted through their website at www.pcsx2.net. + + */ + + + + + +#ifndef ACTUALFILE_H + +#define ACTUALFILE_H + + + + + +#include + + + +#include // off64_t + + + + + +#define ACTUALHANDLE HANDLE + +#define ACTUALHANDLENULL NULL + + + +// #define VERBOSE_FUNCTION_ACTUALFILE + +// #define VERBOSE_WARNING_ACTUALFILE + + + + + +extern int IsActualFile(const char *filename); + +extern void ActualFileDelete(const char *filename); + +extern void ActualFileRename(const char *origname, const char *newname); + + + +extern ACTUALHANDLE ActualFileOpenForRead(const char *filename); + +extern off64_t ActualFileSize(ACTUALHANDLE handle); + +extern int ActualFileSeek(ACTUALHANDLE handle, off64_t position); + +extern int ActualFileRead(ACTUALHANDLE handle, int bytes, char *buffer); + +extern void ActualFileClose(ACTUALHANDLE handle); + + + +extern ACTUALHANDLE ActualFileOpenForWrite(const char *filename); + +extern int ActualFileWrite(ACTUALHANDLE handle, int bytes, char *buffer); + + + + + +#endif /* ACTUALFILE_H */ + diff --git a/plugins/CDVDlinuz/Src/Win32/conf.c b/plugins/CDVDlinuz/Src/Win32/conf.c index 4cc04c5aa6..6ac741f877 100644 --- a/plugins/CDVDlinuz/Src/Win32/conf.c +++ b/plugins/CDVDlinuz/Src/Win32/conf.c @@ -1,350 +1,350 @@ -/* conf.c - - * Copyright (C) 2002-2005 PCSX2 Team - - * - - * This program is free software; you can redistribute it and/or modify - - * it under the terms of the GNU General Public License as published by - - * the Free Software Foundation; either version 2 of the License, or - - * (at your option) any later version. - - * - - * This program 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 this program; if not, write to the Free Software - - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - * - - * PCSX2 members can be contacted through their website at www.pcsx2.net. - - */ - - - -#include // errno - -#include // NULL - -#include // sprintf() - -#include // getenv() - -#include // strerror() - -#include // mkdir(), stat() - -#include // mkdir(), stat(), fork() - -#include // stat(), fork(), execlp() - - - -#include // CreateProcess() - - - -// #define CDVDdefs - -// #include "../PS2Edefs.h" - -#include "../PS2Etypes.h" // u8 - -#include "logfile.h" - -#include "../ini.h" - -#include "conf.h" - - - - - -const char *confnames[] = { "Device", NULL }; - -const u8 defaultdevice[] = DEFAULT_DEVICE; - -const char defaulthome[] = "inis"; - -const char defaultdirectory[] = "HideMe.PS2E"; - -const char defaultfile[] = "CDVDlinuz.ini"; - - - -char confdirname[256]; - -char conffilename[256]; - - - -CDVDconf conf; - - - - - -void InitConf() { - - DWORD retval; - - int i; - - int pos; - - char *envptr; - - - -#ifdef VERBOSE_FUNCTION_CONF - - PrintLog("CDVD config: InitConf()"); - -#endif /* VERBOSE_FUNCTION_CONF */ - - - - i = 0; - - while((i < 255) && defaultdevice[i] != 0) { - - conf.devicename[i] = defaultdevice[i]; - - i++; - - } // ENDWHILE- copying the default CD/DVD name in - - conf.devicename[i] = 0; // 0-terminate the device name - - - - // Locating directory and file positions - - pos = 0; - - envptr = NULL; - - // envptr = getenv("HOME"); - - if(envptr == NULL) { - - // = - - retval = GetCurrentDirectory(253, confdirname); - - if(retval > 0) { - - pos = retval; - - } else { - - pos = 2; - - confdirname[0] = '.'; - - confdirname[1] = '\\'; - - } // ENDIF- Did we retrieve a directory reference? - - - - i = 0; - - while(i < pos) { - - conffilename[i] = confdirname[i]; - - i++; - - } // ENDWHILE- Copying dir info (so far) into file info - - - - if(confdirname[pos-1] != '\\') { - - confdirname[pos] = '\\'; - - conffilename[pos] = '\\'; - - pos++; - - } // ENDIF- No directory separator here? Add one. - - - - i = 0; - - while((pos < 253) && (defaulthome[i] != 0)) { - - confdirname[pos] = defaulthome[i]; - - conffilename[pos] = defaulthome[i]; - - pos++; - - i++; - - } // ENDWHILE- putting an offset where to store ini data - - - - } else { - - // = / - - i = 0; - - while((pos < 253) && (*(envptr + i) != 0)) { - - confdirname[pos] = *(envptr + i); - - conffilename[pos] = *(envptr + i); - - pos++; - - i++; - - } // ENDWHILE- copying home directory info in - - - - if(confdirname[pos-1] != '\\') { - - confdirname[pos] = '\\'; - - conffilename[pos] = '\\'; - - pos++; - - } // ENDIF- No directory separator here? Add one. - - - - i = 0; - - while((pos < 253) && (defaultdirectory[i] != 0)) { - - confdirname[pos] = defaultdirectory[i]; - - conffilename[pos] = defaultdirectory[i]; - - pos++; - - i++; - - } // NEXT- putting a default place to store configuration data - - } // ENDIF- No Home directory? - - - - confdirname[pos] = 0; // Directory reference finished - - - - // += / - - if(conffilename[pos-1] != '\\') { - - conffilename[pos] = '\\'; - - pos++; - - } // ENDIF- No directory separator here? Add one. - - - - i = 0; - - while((pos < 253) && (defaultfile[i] != 0)) { - - conffilename[pos] = defaultfile[i]; - - pos++; - - i++; - - } // NEXT- putting a default place to store configuration data - - - - conffilename[pos] = 0; // File reference finished - - - -#ifdef VERBOSE_FUNCTION_CONF - - PrintLog("CDVD config: Directory: %s", confdirname); - - PrintLog("CDVD config: File: %s", conffilename); - -#endif /* VERBOSE_FUNCTION_CONF */ - -} // END InitConf() - - - - - -void LoadConf() { - - int retval; - - - -#ifdef VERBOSE_FUNCTION_CONF - - PrintLog("CDVD config: LoadConf()"); - -#endif /* VERBOSE_FUNCTION_CONF */ - - - - retval = INILoadString(conffilename, "Settings", "Device", conf.devicename); - - if(retval < 0) { - - sprintf(conf.devicename, "D:"); - - } // ENDIF- Couldn't find keyword? Fill in a default - -} // END LoadConf() - - - - - -void SaveConf() { - -#ifdef VERBOSE_FUNCTION_CONF - - PrintLog("CDVD config: SaveConf()"); - -#endif /* VERBOSE_FUNCTION_CONF */ - - - - mkdir(confdirname); - - - - INISaveString(conffilename, "Settings", "Device", conf.devicename); - -} // END SaveConf() - +/* conf.c + + * Copyright (C) 2002-2005 PCSX2 Team + + * + + * This program is free software; you can redistribute it and/or modify + + * it under the terms of the GNU General Public License as published by + + * the Free Software Foundation; either version 2 of the License, or + + * (at your option) any later version. + + * + + * This program 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 this program; if not, write to the Free Software + + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + * + + * PCSX2 members can be contacted through their website at www.pcsx2.net. + + */ + + + +#include // errno + +#include // NULL + +#include // sprintf() + +#include // getenv() + +#include // strerror() + +#include // mkdir(), stat() + +#include // mkdir(), stat(), fork() + +#include // stat(), fork(), execlp() + + + +#include // CreateProcess() + + + +// #define CDVDdefs + +// #include "../PS2Edefs.h" + +#include "../PS2Etypes.h" // u8 + +#include "logfile.h" + +#include "../ini.h" + +#include "conf.h" + + + + + +const char *confnames[] = { "Device", NULL }; + +const u8 defaultdevice[] = DEFAULT_DEVICE; + +const char defaulthome[] = "inis"; + +const char defaultdirectory[] = "HideMe.PS2E"; + +const char defaultfile[] = "CDVDlinuz.ini"; + + + +char confdirname[256]; + +char conffilename[256]; + + + +CDVDconf conf; + + + + + +void InitConf() { + + DWORD retval; + + int i; + + int pos; + + char *envptr; + + + +#ifdef VERBOSE_FUNCTION_CONF + + PrintLog("CDVD config: InitConf()"); + +#endif /* VERBOSE_FUNCTION_CONF */ + + + + i = 0; + + while((i < 255) && defaultdevice[i] != 0) { + + conf.devicename[i] = defaultdevice[i]; + + i++; + + } // ENDWHILE- copying the default CD/DVD name in + + conf.devicename[i] = 0; // 0-terminate the device name + + + + // Locating directory and file positions + + pos = 0; + + envptr = NULL; + + // envptr = getenv("HOME"); + + if(envptr == NULL) { + + // = + + retval = GetCurrentDirectory(253, confdirname); + + if(retval > 0) { + + pos = retval; + + } else { + + pos = 2; + + confdirname[0] = '.'; + + confdirname[1] = '\\'; + + } // ENDIF- Did we retrieve a directory reference? + + + + i = 0; + + while(i < pos) { + + conffilename[i] = confdirname[i]; + + i++; + + } // ENDWHILE- Copying dir info (so far) into file info + + + + if(confdirname[pos-1] != '\\') { + + confdirname[pos] = '\\'; + + conffilename[pos] = '\\'; + + pos++; + + } // ENDIF- No directory separator here? Add one. + + + + i = 0; + + while((pos < 253) && (defaulthome[i] != 0)) { + + confdirname[pos] = defaulthome[i]; + + conffilename[pos] = defaulthome[i]; + + pos++; + + i++; + + } // ENDWHILE- putting an offset where to store ini data + + + + } else { + + // = / + + i = 0; + + while((pos < 253) && (*(envptr + i) != 0)) { + + confdirname[pos] = *(envptr + i); + + conffilename[pos] = *(envptr + i); + + pos++; + + i++; + + } // ENDWHILE- copying home directory info in + + + + if(confdirname[pos-1] != '\\') { + + confdirname[pos] = '\\'; + + conffilename[pos] = '\\'; + + pos++; + + } // ENDIF- No directory separator here? Add one. + + + + i = 0; + + while((pos < 253) && (defaultdirectory[i] != 0)) { + + confdirname[pos] = defaultdirectory[i]; + + conffilename[pos] = defaultdirectory[i]; + + pos++; + + i++; + + } // NEXT- putting a default place to store configuration data + + } // ENDIF- No Home directory? + + + + confdirname[pos] = 0; // Directory reference finished + + + + // += / + + if(conffilename[pos-1] != '\\') { + + conffilename[pos] = '\\'; + + pos++; + + } // ENDIF- No directory separator here? Add one. + + + + i = 0; + + while((pos < 253) && (defaultfile[i] != 0)) { + + conffilename[pos] = defaultfile[i]; + + pos++; + + i++; + + } // NEXT- putting a default place to store configuration data + + + + conffilename[pos] = 0; // File reference finished + + + +#ifdef VERBOSE_FUNCTION_CONF + + PrintLog("CDVD config: Directory: %s", confdirname); + + PrintLog("CDVD config: File: %s", conffilename); + +#endif /* VERBOSE_FUNCTION_CONF */ + +} // END InitConf() + + + + + +void LoadConf() { + + int retval; + + + +#ifdef VERBOSE_FUNCTION_CONF + + PrintLog("CDVD config: LoadConf()"); + +#endif /* VERBOSE_FUNCTION_CONF */ + + + + retval = INILoadString(conffilename, "Settings", "Device", conf.devicename); + + if(retval < 0) { + + sprintf(conf.devicename, "D:"); + + } // ENDIF- Couldn't find keyword? Fill in a default + +} // END LoadConf() + + + + + +void SaveConf() { + +#ifdef VERBOSE_FUNCTION_CONF + + PrintLog("CDVD config: SaveConf()"); + +#endif /* VERBOSE_FUNCTION_CONF */ + + + + mkdir(confdirname); + + + + INISaveString(conffilename, "Settings", "Device", conf.devicename); + +} // END SaveConf() + diff --git a/plugins/CDVDlinuz/Src/Win32/device.c b/plugins/CDVDlinuz/Src/Win32/device.c index cb5e6100db..0aebc12f19 100644 --- a/plugins/CDVDlinuz/Src/Win32/device.c +++ b/plugins/CDVDlinuz/Src/Win32/device.c @@ -1,1166 +1,1166 @@ -/* device.c - - * Copyright (C) 2002-2005 PCSX2 Team - - * - - * This program is free software; you can redistribute it and/or modify - - * it under the terms of the GNU General Public License as published by - - * the Free Software Foundation; either version 2 of the License, or - - * (at your option) any later version. - - * - - * This program 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 this program; if not, write to the Free Software - - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - * - - * PCSX2 members can be contacted through their website at www.pcsx2.net. - - */ - - - - - -#include - -#include // IOCTL_CDROM..., IOCTL_STORAGE... - -#include // IOCTL_DISK... - -#include // sprintf() - -#include // time_t - - - -#define CDVDdefs - -#include "PS2Edefs.h" - - - -#include "logfile.h" - -#include "conf.h" - -#include "CD.h" - -#include "DVD.h" - -#include "device.h" - - - - - -HANDLE devicehandle; - -OVERLAPPED waitevent; - - - -time_t lasttime; - -s32 traystatus; - -int traystatusmethod; - -s32 disctype; - -char tocbuffer[2048]; - - - - - -void DeviceInit() { - - devicehandle = NULL; - - waitevent.hEvent = NULL; - - waitevent.Internal = 0; - - waitevent.InternalHigh = 0; - - waitevent.Offset = 0; - - waitevent.OffsetHigh = 0; - - lasttime = 0; - - - - InitDisc(); - -} // END DeviceInit() - - - - - -void InitDisc() { - - int i; - - - - InitCDInfo(); - - InitDVDInfo(); - - traystatus = CDVD_TRAY_OPEN; - - traystatusmethod = 0; // Poll all until one works - - disctype = CDVD_TYPE_NODISC; - - for(i = 0; i < 2048; i++) tocbuffer[i] = 0; - -} // END InitDisc() - - - - - -s32 DiscInserted() { - - if(traystatus != CDVD_TRAY_CLOSE) return(-1); - - - - if(disctype == CDVD_TYPE_ILLEGAL) return(-1); - - // if(disctype == CDVD_TYPE_UNKNOWN) return(-1); // Hmm. Let this one through? - - if(disctype == CDVD_TYPE_DETCTDVDD) return(-1); - - if(disctype == CDVD_TYPE_DETCTDVDS) return(-1); - - if(disctype == CDVD_TYPE_DETCTCD) return(-1); - - if(disctype == CDVD_TYPE_DETCT) return(-1); - - if(disctype == CDVD_TYPE_NODISC) return(-1); - - - -#ifdef VERBOSE_FUNCTION_DEVICE - - PrintLog("CDVDlinuz device: DiscInserted()"); - -#endif /* VERBOSE_FUNCTION_DEVICE */ - - - - return(0); - -} // END DiscInserted() - - - - - -// Returns errcode (or 0 if successful) - -DWORD FinishCommand(BOOL boolresult) { - - DWORD errcode; - - DWORD waitcode; - - - -#ifdef VERBOSE_FUNCTION_DEVICE - - PrintLog("CDVDlinuz device: FinishCommand()"); - -#endif /* VERBOSE_FUNCTION_DEVICE */ - - - - if(boolresult == TRUE) { - - ResetEvent(waitevent.hEvent); - - return(0); - - } // ENDIF- Device is ready? Say so. - - - - errcode = GetLastError(); - - if(errcode == ERROR_IO_PENDING) { - -#ifdef VERBOSE_FUNCTION_DEVICE - - PrintLog("CDVDlinuz device: Waiting for completion."); - -#endif /* VERBOSE_FUNCTION_DEVICE */ - - waitcode = WaitForSingleObject(waitevent.hEvent, 10 * 1000); // 10 sec wait - - if((waitcode == WAIT_FAILED) || (waitcode == WAIT_ABANDONED)) { - - errcode = GetLastError(); - - } else if(waitcode == WAIT_TIMEOUT) { - - errcode = 21; - - CancelIo(devicehandle); // Speculative Line - - } else { - - ResetEvent(waitevent.hEvent); - - return(0); // Success! - - } // ENDIF- Trouble waiting? (Or doesn't finish in 5 seconds?) - - } // ENDIF- Should we wait for the call to finish? - - - - ResetEvent(waitevent.hEvent); - - return(errcode); - -} // END DeviceCommand() - - - - - -s32 DeviceOpen() { - - char tempname[256]; - - UINT drivetype; - - DWORD errcode; - - - - if(conf.devicename[0] == 0) return(-1); - - - - if(devicehandle != NULL) return(0); - - - -#ifdef VERBOSE_FUNCTION_DEVICE - - PrintLog("CDVDlinuz device: DeviceOpen()"); - -#endif /* VERBOSE_FUNCTION_DEVICE */ - - - - // InitConf(); - - // LoadConf(); // Should be done at least once before this call - - - - // Root Directory reference - - if(conf.devicename[1] == 0) { - - sprintf(tempname, "%s:\\", conf.devicename); - - } else if((conf.devicename[1] == ':') && (conf.devicename[2] == 0)) { - - sprintf(tempname, "%s\\", conf.devicename); - - } else { - - sprintf(tempname, "%s", conf.devicename); - - } // ENDIF- Not a single drive letter? (or a letter/colon?) Copy the name in. - - - - drivetype = GetDriveType(tempname); - - if(drivetype != DRIVE_CDROM) { - -#ifdef VERBOSE_WARNING_DEVICE - - PrintLog("CDVDlinuz device: Not a CD-ROM!"); - - PrintLog("CDVDlinuz device: (Came back: %u)", drivetype); - - errcode = GetLastError(); - - if(errcode > 0) PrintError("CDVDlinuz device", errcode); - -#endif /* VERBOSE_WARNING_DEVICE */ - - return(-1); - - } // ENDIF- Not a CD-ROM? Say so! - - // Hmm. Do we want to include DRIVE_REMOVABLE... just in case? - - - - // Device Reference - - if(conf.devicename[1] == 0) { - - sprintf(tempname, "\\\\.\\%s:", conf.devicename); - - } else if((conf.devicename[1] == ':') && (conf.devicename[2] == 0)) { - - sprintf(tempname, "\\\\.\\%s", conf.devicename); - - } else { - - sprintf(tempname, "%s", conf.devicename); - - } // ENDIF- Not a single drive letter? (or a letter/colon?) Copy the name in. - - - - devicehandle = CreateFile(tempname, - - GENERIC_READ, - - FILE_SHARE_READ, - - NULL, - - OPEN_EXISTING, - - FILE_FLAG_OVERLAPPED | FILE_FLAG_SEQUENTIAL_SCAN, - - NULL); - - - - if(devicehandle == INVALID_HANDLE_VALUE) { - -#ifdef VERBOSE_WARNING_DEVICE - - PrintLog("CDVDlinuz device: Couldn't open device read-only! Read-Write perhaps?"); - - errcode = GetLastError(); - - PrintError("CDVDlinuz device", errcode); - -#endif /* VERBOSE_WARNING_DEVICE */ - - devicehandle = CreateFile(tempname, - - GENERIC_READ | GENERIC_WRITE, - - FILE_SHARE_READ | FILE_SHARE_WRITE, - - NULL, - - OPEN_EXISTING, - - FILE_FLAG_OVERLAPPED | FILE_FLAG_SEQUENTIAL_SCAN, - - NULL); - - } // ENDIF- Couldn't open for read? Try read/write (for those drives that insist) - - - - if(devicehandle == INVALID_HANDLE_VALUE) { - -#ifdef VERBOSE_WARNING_DEVICE - - PrintLog("CDVDlinuz device: Couldn't open device!"); - - errcode = GetLastError(); - - PrintError("CDVDlinuz device", errcode); - -#endif /* VERBOSE_WARNING_DEVICE */ - - devicehandle = NULL; - - return(-1); - - } // ENDIF- Couldn't open that way either? Abort. - - - - waitevent.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); - - if(waitevent.hEvent == INVALID_HANDLE_VALUE) { - -#ifdef VERBOSE_WARNING_DEVICE - - PrintLog("CDVDlinuz device: Couldn't open event handler!"); - - errcode = GetLastError(); - - PrintError("CDVDlinuz device", errcode); - -#endif /* VERBOSE_WARNING_DEVICE */ - - waitevent.hEvent = NULL; - - CloseHandle(devicehandle); - - devicehandle = NULL; - - } // ENDIF- Couldn't create an "Wait for I/O" handle? Abort. - - - - // More here... DeviceIoControl? for Drive Capabilities - - // DEVICE_CAPABILITIES? - - - - ////// Should be done just after the first DeviceOpen(); - - // InitDisc(); // ? - - // DeviceTrayStatus(); - - - - return(0); - -} // END DeviceOpen() - - - - - -void DeviceClose() { - - if(devicehandle == NULL) return; - - - - if(devicehandle == INVALID_HANDLE_VALUE) { - - devicehandle = NULL; - - return; - - } // ENDIF- Bad value? Just clear the value. - - - -#ifdef VERBOSE_FUNCTION_DEVICE - - PrintLog("CDVDlinuz device: DeviceClose()"); - -#endif /* VERBOSE_FUNCTION_DEVICE */ - - - - if(waitevent.hEvent != NULL) { - - if(waitevent.hEvent != INVALID_HANDLE_VALUE) { - - CancelIo(devicehandle); - - CloseHandle(waitevent.hEvent); - - } // ENDIF- Is this handle actually open? - - waitevent.hEvent = NULL; - - waitevent.Offset = 0; - - waitevent.OffsetHigh = 0; - - } // ENDIF- Reset the event handle? - - - - CloseHandle(devicehandle); - - devicehandle = NULL; - - return; - -} // END DeviceClose() - - - - - -s32 DeviceReadTrack(u32 lsn, int mode, u8 *buffer) { - - if(DiscInserted() == -1) return(-1); - - - - if((disctype == CDVD_TYPE_PS2DVD) || (disctype == CDVD_TYPE_DVDV)) { - - return(DVDreadTrack(lsn, mode, buffer)); - - } else { - - return(CDreadTrack(lsn, mode, buffer)); - - } // ENDIF- Is this a DVD? - -} // END DeviceReadTrack() - - - - - -s32 DeviceBufferOffset() { - - if(DiscInserted() == -1) return(-1); - - - - if((disctype == CDVD_TYPE_PS2DVD) || (disctype == CDVD_TYPE_DVDV)) { - - return(0); - - } else { - - return(CDgetBufferOffset()); - - } // ENDIF- Is this a DVD? - - - - return(-1); - -} // END DeviceBufferOffset() - - - - - -s32 DeviceGetTD(u8 track, cdvdTD *cdvdtd) { - - if(DiscInserted() == -1) return(-1); - - - - if((disctype == CDVD_TYPE_PS2DVD) || (disctype == CDVD_TYPE_DVDV)) { - - return(DVDgetTD(track, cdvdtd)); - - } else { - - return(CDgetTD(track, cdvdtd)); - - } // ENDIF- Is this a DVD? - - - - return(-1); - -} // END DeviceGetTD() - - - - - -s32 DeviceGetDiskType() { - - s32 s32result; - - - - if(devicehandle == NULL) return(-1); - - if(devicehandle == INVALID_HANDLE_VALUE) return(-1); - - - - if(traystatus == CDVD_TRAY_OPEN) return(disctype); - - - - if(disctype != CDVD_TYPE_NODISC) return(disctype); - - - -#ifdef VERBOSE_FUNCTION_DEVICE - - PrintLog("CDVDlinuz device: DeviceGetDiskType()"); - -#endif /* VERBOSE_FUNCTION_DEVICE */ - - - - disctype = CDVD_TYPE_DETCT; - - - - s32result = DVDgetDiskType(); - - if(s32result != -1) return(disctype); - - - - s32result = CDgetDiskType(); - - if(s32result != -1) return(disctype); - - - - disctype = CDVD_TYPE_UNKNOWN; - - return(disctype); - -} // END DeviceGetDiskType() - - - - - -BOOL DeviceTrayStatusStorage() { - - BOOL boolresult; - - DWORD byteswritten; - - DWORD errcode; - - - - boolresult = DeviceIoControl(devicehandle, - - IOCTL_STORAGE_CHECK_VERIFY, - - NULL, - - 0, - - NULL, - - 0, - - &byteswritten, - - NULL); - - errcode = FinishCommand(boolresult); - - - - if(errcode == 0) return(TRUE); - - if(errcode == 21) return(FALSE); // Device not ready? (Valid error) - - - -#ifdef VERBOSE_WARNING_DEVICE - - PrintLog("CDVDlinuz device: Trouble detecting drive status (STORAGE)!"); - - PrintError("CDVDlinuz device", errcode); - -#endif /* VERBOSE_WARNING_DEVICE */ - - return(FALSE); - -} // END DeviceTrayStatusStorage() - - - - - -BOOL DeviceTrayStatusCDRom() { - - BOOL boolresult; - - DWORD byteswritten; - - DWORD errcode; - - - - boolresult = DeviceIoControl(devicehandle, - - IOCTL_CDROM_CHECK_VERIFY, - - NULL, - - 0, - - NULL, - - 0, - - &byteswritten, - - NULL); - - errcode = FinishCommand(boolresult); - - - - if(errcode == 0) return(TRUE); - - if(errcode == 21) return(FALSE); // Device not ready? (Valid error) - - - -#ifdef VERBOSE_WARNING_DEVICE - - PrintLog("CDVDlinuz device: Trouble detecting drive status (CDROM)!"); - - PrintError("CDVDlinuz device", errcode); - -#endif /* VERBOSE_WARNING_DEVICE */ - - return(FALSE); - -} // END DeviceTrayStatusCDRom() - - - - - -BOOL DeviceTrayStatusDisk() { - - BOOL boolresult; - - DWORD byteswritten; - - DWORD errcode; - - - - boolresult = DeviceIoControl(devicehandle, - - IOCTL_DISK_CHECK_VERIFY, - - NULL, - - 0, - - NULL, - - 0, - - &byteswritten, - - NULL); - - errcode = FinishCommand(boolresult); - - - - if(errcode == 0) return(TRUE); - - if(errcode == 21) return(FALSE); // Device not ready? (Valid error) - - - -#ifdef VERBOSE_WARNING_DEVICE - - PrintLog("CDVDlinuz device: Trouble detecting drive status (DISK)!"); - - PrintError("CDVDlinuz device", errcode); - -#endif /* VERBOSE_WARNING_DEVICE */ - - return(FALSE); - -} // END DeviceTrayStatusDisk() - - - - - -s32 DeviceTrayStatus() { - - BOOL boolresult; - - - - if(devicehandle == NULL) return(-1); - - if(devicehandle == INVALID_HANDLE_VALUE) return(-1); - - - -#ifdef VERBOSE_FUNCTION_DEVICE - - PrintLog("CDVDlinuz device: DeviceTrayStatus()"); - -#endif /* VERBOSE_FUNCTION_DEVICE */ - - - - switch(traystatusmethod) { - - case 1: - - boolresult = DeviceTrayStatusStorage(); - - break; - - case 2: - - boolresult = DeviceTrayStatusCDRom(); - - break; - - case 3: - - boolresult = DeviceTrayStatusDisk(); - - break; - - default: - - boolresult = FALSE; - - break; - - } // ENDSWITCH traystatusmethod- One method already working? Try it again. - - - - if(boolresult == FALSE) { - - traystatusmethod = 0; - - boolresult = DeviceTrayStatusStorage(); - - if(boolresult == TRUE) { - - traystatusmethod = 1; - - } else { - - boolresult = DeviceTrayStatusCDRom(); - - if(boolresult == TRUE) { - - traystatusmethod = 2; - - } else { - - boolresult = DeviceTrayStatusDisk(); - - if(boolresult == TRUE) traystatusmethod = 3; - - } // ENDIF- Did we succeed with CDRom? - - } // ENDIF- Did we succeed with Storage? - - } // Single call to already working test just failed? Test them all. - - - - if(boolresult == FALSE) { - - if(traystatus == CDVD_TRAY_CLOSE) { - -#ifdef VERBOSE_FUNCTION_DEVICE - - PrintLog("CDVDlinuz device: Tray just opened!"); - -#endif /* VERBOSE_FUNCTION_DEVICE */ - - traystatus = CDVD_TRAY_OPEN; - - DeviceClose(); - - DeviceOpen(); - - InitDisc(); - - } // ENDIF- Just opened? clear disc info - - return(traystatus); - - } // ENDIF- Still failed? Assume no disc in drive then. - - - - if(traystatus == CDVD_TRAY_OPEN) { - - traystatus = CDVD_TRAY_CLOSE; - -#ifdef VERBOSE_FUNCTION_DEVICE - - PrintLog("CDVDlinuz device: Tray just closed!"); - -#endif /* VERBOSE_FUNCTION_DEVICE */ - - DeviceGetDiskType(); - - return(traystatus); - - } // ENDIF- Just closed? Get disc information - - - - return(traystatus); - -} // END DeviceTrayStatus() - - - - - -s32 DeviceTrayOpen() { - - BOOL boolresult; - - DWORD byteswritten; - - DWORD errcode; - - - - if(devicehandle == NULL) return(-1); - - if(devicehandle == INVALID_HANDLE_VALUE) return(-1); - - - - if(traystatus == CDVD_TRAY_OPEN) return(0); - - - -#ifdef VERBOSE_FUNCTION_DEVICE - - PrintLog("CDVDlinuz device: DeviceOpenTray()"); - -#endif /* VERBOSE_FUNCTION_DEVICE */ - - - - boolresult = DeviceIoControl(devicehandle, - - IOCTL_STORAGE_EJECT_MEDIA, - - NULL, - - 0, - - NULL, - - 0, - - &byteswritten, - - &waitevent); - - errcode = FinishCommand(boolresult); - - - - if(errcode != 0) { - -#ifdef VERBOSE_WARNING_DEVICE - - PrintLog("CDVDlinuz device: Couldn't signal media to eject! (STORAGE)"); - - PrintError("CDVDlinuz device", errcode); - -#endif /* VERBOSE_WARNING_DEVICE */ - - - -// boolresult = DeviceIoControl(devicehandle, - -// IOCTL_DISK_EJECT_MEDIA, - -// NULL, - -// 0, - -// NULL, - -// 0, - -// &byteswritten, - -// NULL); - -// } // ENDIF- Storage Call failed? Try Disk call. - - - -// if(boolresult == FALSE) { - -// #ifdef VERBOSE_WARNING_DEVICE - -// PrintLog("CDVDlinuz device: Couldn't signal media to eject! (DISK)"); - -// PrintError("CDVDlinuz device", errcode); - -// #endif /* VERBOSE_WARNING_DEVICE */ - - return(-1); - - } // ENDIF- Disk Call failed as well? Give it up. - - - - return(0); - -} // END DeviceTrayOpen() - - - - - -s32 DeviceTrayClose() { - - BOOL boolresult; - - DWORD byteswritten; - - DWORD errcode; - - - - if(devicehandle == NULL) return(-1); - - if(devicehandle == INVALID_HANDLE_VALUE) return(-1); - - - - if(traystatus == CDVD_TRAY_CLOSE) return(0); - - - -#ifdef VERBOSE_FUNCTION_DEVICE - - PrintLog("CDVDlinuz device: DeviceCloseTray()"); - -#endif /* VERBOSE_FUNCTION_DEVICE */ - - - - boolresult = DeviceIoControl(devicehandle, - - IOCTL_STORAGE_LOAD_MEDIA, - - NULL, - - 0, - - NULL, - - 0, - - &byteswritten, - - NULL); - - errcode = FinishCommand(boolresult); - - - - if(errcode != 0) { - -#ifdef VERBOSE_WARNING_DEVICE - - PrintLog("CDVDlinuz device: Couldn't signal media to load! (STORAGE)"); - - PrintError("CDVDlinuz device", errcode); - -#endif /* VERBOSE_WARNING_DEVICE */ - -// boolresult = DeviceIoControl(devicehandle, - -// IOCTL_CDROM_LOAD_MEDIA, - -// NULL, - -// 0, - -// NULL, - -// 0, - -// &byteswritten, - -// NULL); - -// } // ENDIF- Storage call failed. CDRom call? - - - -// if(boolresult == FALSE) { - -// errcode = GetLastError(); - -// #ifdef VERBOSE_WARNING_DEVICE - -// PrintLog("CDVDlinuz device: Couldn't signal media to load! (CDROM)"); - -// PrintError("CDVDlinuz device", errcode); - -// #endif /* VERBOSE_WARNING_DEVICE */ - -// boolresult = DeviceIoControl(devicehandle, - -// IOCTL_DISK_LOAD_MEDIA, - -// NULL, - -// 0, - -// NULL, - -// 0, - -// &byteswritten, - -// NULL); - -// } // ENDIF- CDRom call failed. Disk call? - - - -// if(boolresult == FALSE) { - -// #ifdef VERBOSE_WARNING_DEVICE - -// PrintLog("CDVDlinuz device: Couldn't signal media to load! (DISK)"); - -// PrintError("CDVDlinuz device", errcode); - -// #endif /* VERBOSE_WARNING_DEVICE */ - - return(-1); - - } // ENDIF- Media not available? - - - - return(0); - -} // END DeviceTrayClose() - +/* device.c + + * Copyright (C) 2002-2005 PCSX2 Team + + * + + * This program is free software; you can redistribute it and/or modify + + * it under the terms of the GNU General Public License as published by + + * the Free Software Foundation; either version 2 of the License, or + + * (at your option) any later version. + + * + + * This program 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 this program; if not, write to the Free Software + + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + * + + * PCSX2 members can be contacted through their website at www.pcsx2.net. + + */ + + + + + +#include + +#include // IOCTL_CDROM..., IOCTL_STORAGE... + +#include // IOCTL_DISK... + +#include // sprintf() + +#include // time_t + + + +#define CDVDdefs + +#include "PS2Edefs.h" + + + +#include "logfile.h" + +#include "conf.h" + +#include "CD.h" + +#include "DVD.h" + +#include "device.h" + + + + + +HANDLE devicehandle; + +OVERLAPPED waitevent; + + + +time_t lasttime; + +s32 traystatus; + +int traystatusmethod; + +s32 disctype; + +char tocbuffer[2048]; + + + + + +void DeviceInit() { + + devicehandle = NULL; + + waitevent.hEvent = NULL; + + waitevent.Internal = 0; + + waitevent.InternalHigh = 0; + + waitevent.Offset = 0; + + waitevent.OffsetHigh = 0; + + lasttime = 0; + + + + InitDisc(); + +} // END DeviceInit() + + + + + +void InitDisc() { + + int i; + + + + InitCDInfo(); + + InitDVDInfo(); + + traystatus = CDVD_TRAY_OPEN; + + traystatusmethod = 0; // Poll all until one works + + disctype = CDVD_TYPE_NODISC; + + for(i = 0; i < 2048; i++) tocbuffer[i] = 0; + +} // END InitDisc() + + + + + +s32 DiscInserted() { + + if(traystatus != CDVD_TRAY_CLOSE) return(-1); + + + + if(disctype == CDVD_TYPE_ILLEGAL) return(-1); + + // if(disctype == CDVD_TYPE_UNKNOWN) return(-1); // Hmm. Let this one through? + + if(disctype == CDVD_TYPE_DETCTDVDD) return(-1); + + if(disctype == CDVD_TYPE_DETCTDVDS) return(-1); + + if(disctype == CDVD_TYPE_DETCTCD) return(-1); + + if(disctype == CDVD_TYPE_DETCT) return(-1); + + if(disctype == CDVD_TYPE_NODISC) return(-1); + + + +#ifdef VERBOSE_FUNCTION_DEVICE + + PrintLog("CDVDlinuz device: DiscInserted()"); + +#endif /* VERBOSE_FUNCTION_DEVICE */ + + + + return(0); + +} // END DiscInserted() + + + + + +// Returns errcode (or 0 if successful) + +DWORD FinishCommand(BOOL boolresult) { + + DWORD errcode; + + DWORD waitcode; + + + +#ifdef VERBOSE_FUNCTION_DEVICE + + PrintLog("CDVDlinuz device: FinishCommand()"); + +#endif /* VERBOSE_FUNCTION_DEVICE */ + + + + if(boolresult == TRUE) { + + ResetEvent(waitevent.hEvent); + + return(0); + + } // ENDIF- Device is ready? Say so. + + + + errcode = GetLastError(); + + if(errcode == ERROR_IO_PENDING) { + +#ifdef VERBOSE_FUNCTION_DEVICE + + PrintLog("CDVDlinuz device: Waiting for completion."); + +#endif /* VERBOSE_FUNCTION_DEVICE */ + + waitcode = WaitForSingleObject(waitevent.hEvent, 10 * 1000); // 10 sec wait + + if((waitcode == WAIT_FAILED) || (waitcode == WAIT_ABANDONED)) { + + errcode = GetLastError(); + + } else if(waitcode == WAIT_TIMEOUT) { + + errcode = 21; + + CancelIo(devicehandle); // Speculative Line + + } else { + + ResetEvent(waitevent.hEvent); + + return(0); // Success! + + } // ENDIF- Trouble waiting? (Or doesn't finish in 5 seconds?) + + } // ENDIF- Should we wait for the call to finish? + + + + ResetEvent(waitevent.hEvent); + + return(errcode); + +} // END DeviceCommand() + + + + + +s32 DeviceOpen() { + + char tempname[256]; + + UINT drivetype; + + DWORD errcode; + + + + if(conf.devicename[0] == 0) return(-1); + + + + if(devicehandle != NULL) return(0); + + + +#ifdef VERBOSE_FUNCTION_DEVICE + + PrintLog("CDVDlinuz device: DeviceOpen()"); + +#endif /* VERBOSE_FUNCTION_DEVICE */ + + + + // InitConf(); + + // LoadConf(); // Should be done at least once before this call + + + + // Root Directory reference + + if(conf.devicename[1] == 0) { + + sprintf(tempname, "%s:\\", conf.devicename); + + } else if((conf.devicename[1] == ':') && (conf.devicename[2] == 0)) { + + sprintf(tempname, "%s\\", conf.devicename); + + } else { + + sprintf(tempname, "%s", conf.devicename); + + } // ENDIF- Not a single drive letter? (or a letter/colon?) Copy the name in. + + + + drivetype = GetDriveType(tempname); + + if(drivetype != DRIVE_CDROM) { + +#ifdef VERBOSE_WARNING_DEVICE + + PrintLog("CDVDlinuz device: Not a CD-ROM!"); + + PrintLog("CDVDlinuz device: (Came back: %u)", drivetype); + + errcode = GetLastError(); + + if(errcode > 0) PrintError("CDVDlinuz device", errcode); + +#endif /* VERBOSE_WARNING_DEVICE */ + + return(-1); + + } // ENDIF- Not a CD-ROM? Say so! + + // Hmm. Do we want to include DRIVE_REMOVABLE... just in case? + + + + // Device Reference + + if(conf.devicename[1] == 0) { + + sprintf(tempname, "\\\\.\\%s:", conf.devicename); + + } else if((conf.devicename[1] == ':') && (conf.devicename[2] == 0)) { + + sprintf(tempname, "\\\\.\\%s", conf.devicename); + + } else { + + sprintf(tempname, "%s", conf.devicename); + + } // ENDIF- Not a single drive letter? (or a letter/colon?) Copy the name in. + + + + devicehandle = CreateFile(tempname, + + GENERIC_READ, + + FILE_SHARE_READ, + + NULL, + + OPEN_EXISTING, + + FILE_FLAG_OVERLAPPED | FILE_FLAG_SEQUENTIAL_SCAN, + + NULL); + + + + if(devicehandle == INVALID_HANDLE_VALUE) { + +#ifdef VERBOSE_WARNING_DEVICE + + PrintLog("CDVDlinuz device: Couldn't open device read-only! Read-Write perhaps?"); + + errcode = GetLastError(); + + PrintError("CDVDlinuz device", errcode); + +#endif /* VERBOSE_WARNING_DEVICE */ + + devicehandle = CreateFile(tempname, + + GENERIC_READ | GENERIC_WRITE, + + FILE_SHARE_READ | FILE_SHARE_WRITE, + + NULL, + + OPEN_EXISTING, + + FILE_FLAG_OVERLAPPED | FILE_FLAG_SEQUENTIAL_SCAN, + + NULL); + + } // ENDIF- Couldn't open for read? Try read/write (for those drives that insist) + + + + if(devicehandle == INVALID_HANDLE_VALUE) { + +#ifdef VERBOSE_WARNING_DEVICE + + PrintLog("CDVDlinuz device: Couldn't open device!"); + + errcode = GetLastError(); + + PrintError("CDVDlinuz device", errcode); + +#endif /* VERBOSE_WARNING_DEVICE */ + + devicehandle = NULL; + + return(-1); + + } // ENDIF- Couldn't open that way either? Abort. + + + + waitevent.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + + if(waitevent.hEvent == INVALID_HANDLE_VALUE) { + +#ifdef VERBOSE_WARNING_DEVICE + + PrintLog("CDVDlinuz device: Couldn't open event handler!"); + + errcode = GetLastError(); + + PrintError("CDVDlinuz device", errcode); + +#endif /* VERBOSE_WARNING_DEVICE */ + + waitevent.hEvent = NULL; + + CloseHandle(devicehandle); + + devicehandle = NULL; + + } // ENDIF- Couldn't create an "Wait for I/O" handle? Abort. + + + + // More here... DeviceIoControl? for Drive Capabilities + + // DEVICE_CAPABILITIES? + + + + ////// Should be done just after the first DeviceOpen(); + + // InitDisc(); // ? + + // DeviceTrayStatus(); + + + + return(0); + +} // END DeviceOpen() + + + + + +void DeviceClose() { + + if(devicehandle == NULL) return; + + + + if(devicehandle == INVALID_HANDLE_VALUE) { + + devicehandle = NULL; + + return; + + } // ENDIF- Bad value? Just clear the value. + + + +#ifdef VERBOSE_FUNCTION_DEVICE + + PrintLog("CDVDlinuz device: DeviceClose()"); + +#endif /* VERBOSE_FUNCTION_DEVICE */ + + + + if(waitevent.hEvent != NULL) { + + if(waitevent.hEvent != INVALID_HANDLE_VALUE) { + + CancelIo(devicehandle); + + CloseHandle(waitevent.hEvent); + + } // ENDIF- Is this handle actually open? + + waitevent.hEvent = NULL; + + waitevent.Offset = 0; + + waitevent.OffsetHigh = 0; + + } // ENDIF- Reset the event handle? + + + + CloseHandle(devicehandle); + + devicehandle = NULL; + + return; + +} // END DeviceClose() + + + + + +s32 DeviceReadTrack(u32 lsn, int mode, u8 *buffer) { + + if(DiscInserted() == -1) return(-1); + + + + if((disctype == CDVD_TYPE_PS2DVD) || (disctype == CDVD_TYPE_DVDV)) { + + return(DVDreadTrack(lsn, mode, buffer)); + + } else { + + return(CDreadTrack(lsn, mode, buffer)); + + } // ENDIF- Is this a DVD? + +} // END DeviceReadTrack() + + + + + +s32 DeviceBufferOffset() { + + if(DiscInserted() == -1) return(-1); + + + + if((disctype == CDVD_TYPE_PS2DVD) || (disctype == CDVD_TYPE_DVDV)) { + + return(0); + + } else { + + return(CDgetBufferOffset()); + + } // ENDIF- Is this a DVD? + + + + return(-1); + +} // END DeviceBufferOffset() + + + + + +s32 DeviceGetTD(u8 track, cdvdTD *cdvdtd) { + + if(DiscInserted() == -1) return(-1); + + + + if((disctype == CDVD_TYPE_PS2DVD) || (disctype == CDVD_TYPE_DVDV)) { + + return(DVDgetTD(track, cdvdtd)); + + } else { + + return(CDgetTD(track, cdvdtd)); + + } // ENDIF- Is this a DVD? + + + + return(-1); + +} // END DeviceGetTD() + + + + + +s32 DeviceGetDiskType() { + + s32 s32result; + + + + if(devicehandle == NULL) return(-1); + + if(devicehandle == INVALID_HANDLE_VALUE) return(-1); + + + + if(traystatus == CDVD_TRAY_OPEN) return(disctype); + + + + if(disctype != CDVD_TYPE_NODISC) return(disctype); + + + +#ifdef VERBOSE_FUNCTION_DEVICE + + PrintLog("CDVDlinuz device: DeviceGetDiskType()"); + +#endif /* VERBOSE_FUNCTION_DEVICE */ + + + + disctype = CDVD_TYPE_DETCT; + + + + s32result = DVDgetDiskType(); + + if(s32result != -1) return(disctype); + + + + s32result = CDgetDiskType(); + + if(s32result != -1) return(disctype); + + + + disctype = CDVD_TYPE_UNKNOWN; + + return(disctype); + +} // END DeviceGetDiskType() + + + + + +BOOL DeviceTrayStatusStorage() { + + BOOL boolresult; + + DWORD byteswritten; + + DWORD errcode; + + + + boolresult = DeviceIoControl(devicehandle, + + IOCTL_STORAGE_CHECK_VERIFY, + + NULL, + + 0, + + NULL, + + 0, + + &byteswritten, + + NULL); + + errcode = FinishCommand(boolresult); + + + + if(errcode == 0) return(TRUE); + + if(errcode == 21) return(FALSE); // Device not ready? (Valid error) + + + +#ifdef VERBOSE_WARNING_DEVICE + + PrintLog("CDVDlinuz device: Trouble detecting drive status (STORAGE)!"); + + PrintError("CDVDlinuz device", errcode); + +#endif /* VERBOSE_WARNING_DEVICE */ + + return(FALSE); + +} // END DeviceTrayStatusStorage() + + + + + +BOOL DeviceTrayStatusCDRom() { + + BOOL boolresult; + + DWORD byteswritten; + + DWORD errcode; + + + + boolresult = DeviceIoControl(devicehandle, + + IOCTL_CDROM_CHECK_VERIFY, + + NULL, + + 0, + + NULL, + + 0, + + &byteswritten, + + NULL); + + errcode = FinishCommand(boolresult); + + + + if(errcode == 0) return(TRUE); + + if(errcode == 21) return(FALSE); // Device not ready? (Valid error) + + + +#ifdef VERBOSE_WARNING_DEVICE + + PrintLog("CDVDlinuz device: Trouble detecting drive status (CDROM)!"); + + PrintError("CDVDlinuz device", errcode); + +#endif /* VERBOSE_WARNING_DEVICE */ + + return(FALSE); + +} // END DeviceTrayStatusCDRom() + + + + + +BOOL DeviceTrayStatusDisk() { + + BOOL boolresult; + + DWORD byteswritten; + + DWORD errcode; + + + + boolresult = DeviceIoControl(devicehandle, + + IOCTL_DISK_CHECK_VERIFY, + + NULL, + + 0, + + NULL, + + 0, + + &byteswritten, + + NULL); + + errcode = FinishCommand(boolresult); + + + + if(errcode == 0) return(TRUE); + + if(errcode == 21) return(FALSE); // Device not ready? (Valid error) + + + +#ifdef VERBOSE_WARNING_DEVICE + + PrintLog("CDVDlinuz device: Trouble detecting drive status (DISK)!"); + + PrintError("CDVDlinuz device", errcode); + +#endif /* VERBOSE_WARNING_DEVICE */ + + return(FALSE); + +} // END DeviceTrayStatusDisk() + + + + + +s32 DeviceTrayStatus() { + + BOOL boolresult; + + + + if(devicehandle == NULL) return(-1); + + if(devicehandle == INVALID_HANDLE_VALUE) return(-1); + + + +#ifdef VERBOSE_FUNCTION_DEVICE + + PrintLog("CDVDlinuz device: DeviceTrayStatus()"); + +#endif /* VERBOSE_FUNCTION_DEVICE */ + + + + switch(traystatusmethod) { + + case 1: + + boolresult = DeviceTrayStatusStorage(); + + break; + + case 2: + + boolresult = DeviceTrayStatusCDRom(); + + break; + + case 3: + + boolresult = DeviceTrayStatusDisk(); + + break; + + default: + + boolresult = FALSE; + + break; + + } // ENDSWITCH traystatusmethod- One method already working? Try it again. + + + + if(boolresult == FALSE) { + + traystatusmethod = 0; + + boolresult = DeviceTrayStatusStorage(); + + if(boolresult == TRUE) { + + traystatusmethod = 1; + + } else { + + boolresult = DeviceTrayStatusCDRom(); + + if(boolresult == TRUE) { + + traystatusmethod = 2; + + } else { + + boolresult = DeviceTrayStatusDisk(); + + if(boolresult == TRUE) traystatusmethod = 3; + + } // ENDIF- Did we succeed with CDRom? + + } // ENDIF- Did we succeed with Storage? + + } // Single call to already working test just failed? Test them all. + + + + if(boolresult == FALSE) { + + if(traystatus == CDVD_TRAY_CLOSE) { + +#ifdef VERBOSE_FUNCTION_DEVICE + + PrintLog("CDVDlinuz device: Tray just opened!"); + +#endif /* VERBOSE_FUNCTION_DEVICE */ + + traystatus = CDVD_TRAY_OPEN; + + DeviceClose(); + + DeviceOpen(); + + InitDisc(); + + } // ENDIF- Just opened? clear disc info + + return(traystatus); + + } // ENDIF- Still failed? Assume no disc in drive then. + + + + if(traystatus == CDVD_TRAY_OPEN) { + + traystatus = CDVD_TRAY_CLOSE; + +#ifdef VERBOSE_FUNCTION_DEVICE + + PrintLog("CDVDlinuz device: Tray just closed!"); + +#endif /* VERBOSE_FUNCTION_DEVICE */ + + DeviceGetDiskType(); + + return(traystatus); + + } // ENDIF- Just closed? Get disc information + + + + return(traystatus); + +} // END DeviceTrayStatus() + + + + + +s32 DeviceTrayOpen() { + + BOOL boolresult; + + DWORD byteswritten; + + DWORD errcode; + + + + if(devicehandle == NULL) return(-1); + + if(devicehandle == INVALID_HANDLE_VALUE) return(-1); + + + + if(traystatus == CDVD_TRAY_OPEN) return(0); + + + +#ifdef VERBOSE_FUNCTION_DEVICE + + PrintLog("CDVDlinuz device: DeviceOpenTray()"); + +#endif /* VERBOSE_FUNCTION_DEVICE */ + + + + boolresult = DeviceIoControl(devicehandle, + + IOCTL_STORAGE_EJECT_MEDIA, + + NULL, + + 0, + + NULL, + + 0, + + &byteswritten, + + &waitevent); + + errcode = FinishCommand(boolresult); + + + + if(errcode != 0) { + +#ifdef VERBOSE_WARNING_DEVICE + + PrintLog("CDVDlinuz device: Couldn't signal media to eject! (STORAGE)"); + + PrintError("CDVDlinuz device", errcode); + +#endif /* VERBOSE_WARNING_DEVICE */ + + + +// boolresult = DeviceIoControl(devicehandle, + +// IOCTL_DISK_EJECT_MEDIA, + +// NULL, + +// 0, + +// NULL, + +// 0, + +// &byteswritten, + +// NULL); + +// } // ENDIF- Storage Call failed? Try Disk call. + + + +// if(boolresult == FALSE) { + +// #ifdef VERBOSE_WARNING_DEVICE + +// PrintLog("CDVDlinuz device: Couldn't signal media to eject! (DISK)"); + +// PrintError("CDVDlinuz device", errcode); + +// #endif /* VERBOSE_WARNING_DEVICE */ + + return(-1); + + } // ENDIF- Disk Call failed as well? Give it up. + + + + return(0); + +} // END DeviceTrayOpen() + + + + + +s32 DeviceTrayClose() { + + BOOL boolresult; + + DWORD byteswritten; + + DWORD errcode; + + + + if(devicehandle == NULL) return(-1); + + if(devicehandle == INVALID_HANDLE_VALUE) return(-1); + + + + if(traystatus == CDVD_TRAY_CLOSE) return(0); + + + +#ifdef VERBOSE_FUNCTION_DEVICE + + PrintLog("CDVDlinuz device: DeviceCloseTray()"); + +#endif /* VERBOSE_FUNCTION_DEVICE */ + + + + boolresult = DeviceIoControl(devicehandle, + + IOCTL_STORAGE_LOAD_MEDIA, + + NULL, + + 0, + + NULL, + + 0, + + &byteswritten, + + NULL); + + errcode = FinishCommand(boolresult); + + + + if(errcode != 0) { + +#ifdef VERBOSE_WARNING_DEVICE + + PrintLog("CDVDlinuz device: Couldn't signal media to load! (STORAGE)"); + + PrintError("CDVDlinuz device", errcode); + +#endif /* VERBOSE_WARNING_DEVICE */ + +// boolresult = DeviceIoControl(devicehandle, + +// IOCTL_CDROM_LOAD_MEDIA, + +// NULL, + +// 0, + +// NULL, + +// 0, + +// &byteswritten, + +// NULL); + +// } // ENDIF- Storage call failed. CDRom call? + + + +// if(boolresult == FALSE) { + +// errcode = GetLastError(); + +// #ifdef VERBOSE_WARNING_DEVICE + +// PrintLog("CDVDlinuz device: Couldn't signal media to load! (CDROM)"); + +// PrintError("CDVDlinuz device", errcode); + +// #endif /* VERBOSE_WARNING_DEVICE */ + +// boolresult = DeviceIoControl(devicehandle, + +// IOCTL_DISK_LOAD_MEDIA, + +// NULL, + +// 0, + +// NULL, + +// 0, + +// &byteswritten, + +// NULL); + +// } // ENDIF- CDRom call failed. Disk call? + + + +// if(boolresult == FALSE) { + +// #ifdef VERBOSE_WARNING_DEVICE + +// PrintLog("CDVDlinuz device: Couldn't signal media to load! (DISK)"); + +// PrintError("CDVDlinuz device", errcode); + +// #endif /* VERBOSE_WARNING_DEVICE */ + + return(-1); + + } // ENDIF- Media not available? + + + + return(0); + +} // END DeviceTrayClose() + diff --git a/plugins/CDVDlinuz/Src/Win32/logfile.c b/plugins/CDVDlinuz/Src/Win32/logfile.c index 8d1226e298..8dc1a6561a 100644 --- a/plugins/CDVDlinuz/Src/Win32/logfile.c +++ b/plugins/CDVDlinuz/Src/Win32/logfile.c @@ -1,238 +1,238 @@ -/* logfile.c - - * Copyright (C) 2002-2005 PCSX2 Team - - * - - * This program is free software; you can redistribute it and/or modify - - * it under the terms of the GNU General Public License as published by - - * the Free Software Foundation; either version 2 of the License, or - - * (at your option) any later version. - - * - - * This program 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 this program; if not, write to the Free Software - - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - * - - * PCSX2 members can be contacted through their website at www.pcsx2.net. - - */ - - - - - -#include - - - -// #include // open() - -// #include // mkdir() - -#include // NULL - -#include // vsprintf() - -#include // va_start(), va_end(), vsprintf() - -// #include // open() - -// #include // open() - - - -#include "logfile.h" - - - - - -HANDLE logfile; - -char logfiletemp[2048]; - - - - - -void InitLog() { - - // Token comment line - -#ifdef VERBOSE_LOGFILE - - CreateDirectory("logs", NULL); - - - - DeleteFile("logs\\CDVDlog.txt"); - - logfile = NULL; - -#endif /* VERBOSE LOGFILE */ - -} // END InitLog(); - - - - - -int OpenLog() { - - // Token comment line - -#ifdef VERBOSE_LOGFILE - - logfile = CreateFile("logs\\CDVDlog.txt", - - GENERIC_WRITE, - - 0, - - NULL, - - CREATE_ALWAYS, - - FILE_FLAG_SEQUENTIAL_SCAN, - - NULL); - - if(logfile == INVALID_HANDLE_VALUE) { - - logfile = NULL; - - return(-1); - - } // ENDIF- Failed to open? Say so. - -#endif /* VERBOSE LOGFILE */ - - - - return(0); - -} // END OpenLog(); - - - - - -void CloseLog() { - - // Token comment line - -#ifdef VERBOSE_LOGFILE - - if(logfile != NULL) { - - CloseHandle(logfile); - - logfile = NULL; - - } // ENDIF- Is the log file actually open? Close it. - -#endif /* VERBOSE LOGFILE */ - -} // END CloseLog() - - - - - -void PrintLog(const char *fmt, ...) { - - DWORD byteswritten; - - - - // Token comment line - -#ifdef VERBOSE_LOGFILE - - va_list list; - - int len; - - - - if(logfile == NULL) return; // Log file not open... yet. - - - - va_start(list, fmt); - - vsprintf(logfiletemp, fmt, list); - - va_end(list); - - - - len = 0; - - while((len < 2048) && (logfiletemp[len] != 0)) len++; - - if((len > 0) && (logfiletemp[len-1] == '\n')) len--; - - if((len > 0) && (logfiletemp[len-1] == '\r')) len--; - - logfiletemp[len] = 0; // Slice off the last "\r\n"... - - - - WriteFile(logfile, logfiletemp, len, &byteswritten, NULL); - - WriteFile(logfile, "\r\n", 2, &byteswritten, NULL); - -#endif /* VERBOSE LOGFILE */ - -} // END PrintLog() - - - -void PrintError(const char *header, DWORD errcode) { - -#ifdef VERBOSE_LOGFILE - - TCHAR errmsg[256]; - - - - FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | 80, - - NULL, - - errcode, - - 0, - - errmsg, - - 256, - - NULL); - - PrintLog("%s: (%u) %s", header, errcode, errmsg); - -#endif /* VERBOSE_WARNING_DEVICE */ - -} // END PrintError() - +/* logfile.c + + * Copyright (C) 2002-2005 PCSX2 Team + + * + + * This program is free software; you can redistribute it and/or modify + + * it under the terms of the GNU General Public License as published by + + * the Free Software Foundation; either version 2 of the License, or + + * (at your option) any later version. + + * + + * This program 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 this program; if not, write to the Free Software + + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + * + + * PCSX2 members can be contacted through their website at www.pcsx2.net. + + */ + + + + + +#include + + + +// #include // open() + +// #include // mkdir() + +#include // NULL + +#include // vsprintf() + +#include // va_start(), va_end(), vsprintf() + +// #include // open() + +// #include // open() + + + +#include "logfile.h" + + + + + +HANDLE logfile; + +char logfiletemp[2048]; + + + + + +void InitLog() { + + // Token comment line + +#ifdef VERBOSE_LOGFILE + + CreateDirectory("logs", NULL); + + + + DeleteFile("logs\\CDVDlog.txt"); + + logfile = NULL; + +#endif /* VERBOSE LOGFILE */ + +} // END InitLog(); + + + + + +int OpenLog() { + + // Token comment line + +#ifdef VERBOSE_LOGFILE + + logfile = CreateFile("logs\\CDVDlog.txt", + + GENERIC_WRITE, + + 0, + + NULL, + + CREATE_ALWAYS, + + FILE_FLAG_SEQUENTIAL_SCAN, + + NULL); + + if(logfile == INVALID_HANDLE_VALUE) { + + logfile = NULL; + + return(-1); + + } // ENDIF- Failed to open? Say so. + +#endif /* VERBOSE LOGFILE */ + + + + return(0); + +} // END OpenLog(); + + + + + +void CloseLog() { + + // Token comment line + +#ifdef VERBOSE_LOGFILE + + if(logfile != NULL) { + + CloseHandle(logfile); + + logfile = NULL; + + } // ENDIF- Is the log file actually open? Close it. + +#endif /* VERBOSE LOGFILE */ + +} // END CloseLog() + + + + + +void PrintLog(const char *fmt, ...) { + + DWORD byteswritten; + + + + // Token comment line + +#ifdef VERBOSE_LOGFILE + + va_list list; + + int len; + + + + if(logfile == NULL) return; // Log file not open... yet. + + + + va_start(list, fmt); + + vsprintf(logfiletemp, fmt, list); + + va_end(list); + + + + len = 0; + + while((len < 2048) && (logfiletemp[len] != 0)) len++; + + if((len > 0) && (logfiletemp[len-1] == '\n')) len--; + + if((len > 0) && (logfiletemp[len-1] == '\r')) len--; + + logfiletemp[len] = 0; // Slice off the last "\r\n"... + + + + WriteFile(logfile, logfiletemp, len, &byteswritten, NULL); + + WriteFile(logfile, "\r\n", 2, &byteswritten, NULL); + +#endif /* VERBOSE LOGFILE */ + +} // END PrintLog() + + + +void PrintError(const char *header, DWORD errcode) { + +#ifdef VERBOSE_LOGFILE + + TCHAR errmsg[256]; + + + + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | 80, + + NULL, + + errcode, + + 0, + + errmsg, + + 256, + + NULL); + + PrintLog("%s: (%u) %s", header, errcode, errmsg); + +#endif /* VERBOSE_WARNING_DEVICE */ + +} // END PrintError() + diff --git a/plugins/CDVDlinuz/Src/Win32/mainbox.c b/plugins/CDVDlinuz/Src/Win32/mainbox.c index 91298702e0..5920f9710a 100644 --- a/plugins/CDVDlinuz/Src/Win32/mainbox.c +++ b/plugins/CDVDlinuz/Src/Win32/mainbox.c @@ -1,346 +1,346 @@ -/* mainbox.c - - * Copyright (C) 2002-2005 PCSX2 Team - - * - - * This program is free software; you can redistribute it and/or modify - - * it under the terms of the GNU General Public License as published by - - * the Free Software Foundation; either version 2 of the License, or - - * (at your option) any later version. - - * - - * This program 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 this program; if not, write to the Free Software - - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - * - - * PCSX2 members can be contacted through their website at www.pcsx2.net. - - */ - - - - - -#include - -// #include // Button_ - -#include // NULL - - - -#include // sprintf() - -#include // strcpy() - -#include // stat() - -#include // stat() - -#include // stat() - - - -#include "conf.h" - -#include "device.h" - -// #include "imagetype.h" // imagedata[].name - -#include "screens.h" // DLG_, IDC_ - -#include "CDVDlinuz.h" // progmodule - -#include "mainbox.h" - - - - - -HWND mainboxwindow; - - - - - -void MainBoxDestroy() { - - if(mainboxwindow != NULL) { - - EndDialog(mainboxwindow, FALSE); - - mainboxwindow = NULL; - - } // ENDIF- Do we have a Main Window still? - -} // END MainBoxDestroy() - - - - - -void MainBoxUnfocus() { - - // EnableWindow(?) - - // gtk_widget_set_sensitive(mainbox.device, FALSE); - - ShowWindow(mainboxwindow, SW_HIDE); - -} // END MainBoxUnfocus() - - - - - -void MainBoxDeviceEvent() { - - char tempdevice[256]; - - struct stat filestat; - - int returnval; - - - - GetDlgItemText(mainboxwindow, IDC_0202, tempdevice, 256); - - returnval = stat(tempdevice, &filestat); - - if(returnval == -1) { - - SetDlgItemText(mainboxwindow, IDC_0203, "Device Type: ---"); - - return; - - } // ENDIF- Not a name of any sort? - - - - if(S_ISDIR(filestat.st_mode) != 0) { - - SetDlgItemText(mainboxwindow, IDC_0203, "Device Type: Not a device"); - - return; - - } // ENDIF- Not a regular file? - - - - SetDlgItemText(mainboxwindow, IDC_0203, "Device Type: Device Likely"); - - return; - -} // END MainBoxFileEvent() - - - - - -void MainBoxRefocus() { - - MainBoxDeviceEvent(); - - - - // gtk_widget_set_sensitive(mainbox.device, TRUE); - - // gtk_window_set_focus(GTK_WINDOW(mainbox.window), mainbox.device); - - // ShowWindow(mainboxwindow, SW_RESTORE); // and/or, SW_SHOW? SW_SHOWNORMAL? - - ShowWindow(mainboxwindow, SW_SHOW); - - SetActiveWindow(mainboxwindow); - -} // END MainBoxRefocus() - - - - - -void MainBoxCancelEvent() { - - MainBoxDestroy(); - - return; - -} // END MainBoxCancelEvent() - - - - - -void MainBoxOKEvent() { - - int retval; - - - - MainBoxUnfocus(); - - - - GetDlgItemText(mainboxwindow, IDC_0202, conf.devicename, 256); - - retval = DeviceOpen(); - - DeviceClose(); - - if(retval != 0) { - - MainBoxRefocus(); - - MessageBox(mainboxwindow, - - "Could not open the device", - - "CDVDlinuz Message", - - MB_OK | MB_ICONWARNING | MB_SETFOREGROUND); - - return; - - } // ENDIF- Trouble opening device? Abort here. - - - - SaveConf(); - - - - MainBoxCancelEvent(); - - return; - -} // END MainBoxOKEvent() - - - - - -void MainBoxDisplay() { - - InitConf(); // Man, am I boiling mad! CDVDinit() should have been called first! - - LoadConf(); - - - - // Adjust window position? - - - - // We held off setting the name until now... so description would show. - - SetDlgItemText(mainboxwindow, IDC_0202, conf.devicename); - - - - // First Time - Show the window - - ShowWindow(mainboxwindow, SW_SHOWNORMAL); - -} // END MainBoxDisplay() - - - - - -BOOL CALLBACK MainBoxCallback(HWND window, - - UINT msg, - - WPARAM param, - - LPARAM param2) { - - switch(msg) { - - case WM_INITDIALOG: - - mainboxwindow = window; - - MainBoxDisplay(); // In this case, final touches to this window. - - return(FALSE); // And let Windows display this window. - - break; - - - - case WM_CLOSE: // The "X" in the upper right corner was hit. - - MainBoxCancelEvent(); - - return(TRUE); - - break; - - - - case WM_COMMAND: - - // Do we wish to capture 'ENTER/RETURN' and/or 'ESC' here? - - - - switch(LOWORD(param)) { - - case IDC_0202: // Devicename Edit Box - - MainBoxDeviceEvent(); // Describe the File's type... - - return(FALSE); // Let Windows handle the actual 'edit' processing... - - break; - - - - case IDC_0204: // "Ok" Button - - MainBoxOKEvent(); - - return(TRUE); - - break; - - - - case IDC_0205: // "Cancel" Button - - MainBoxCancelEvent(); - - return(TRUE); - - break; - - } // ENDSWITCH param- Which object got the message? - - } // ENDSWITCH msg- what message has been sent to this window? - - - - return(FALSE); // Not a recognized message? Tell Windows to handle it. - -} // END MainBoxEventLoop() - +/* mainbox.c + + * Copyright (C) 2002-2005 PCSX2 Team + + * + + * This program is free software; you can redistribute it and/or modify + + * it under the terms of the GNU General Public License as published by + + * the Free Software Foundation; either version 2 of the License, or + + * (at your option) any later version. + + * + + * This program 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 this program; if not, write to the Free Software + + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + * + + * PCSX2 members can be contacted through their website at www.pcsx2.net. + + */ + + + + + +#include + +// #include // Button_ + +#include // NULL + + + +#include // sprintf() + +#include // strcpy() + +#include // stat() + +#include // stat() + +#include // stat() + + + +#include "conf.h" + +#include "device.h" + +// #include "imagetype.h" // imagedata[].name + +#include "screens.h" // DLG_, IDC_ + +#include "CDVDlinuz.h" // progmodule + +#include "mainbox.h" + + + + + +HWND mainboxwindow; + + + + + +void MainBoxDestroy() { + + if(mainboxwindow != NULL) { + + EndDialog(mainboxwindow, FALSE); + + mainboxwindow = NULL; + + } // ENDIF- Do we have a Main Window still? + +} // END MainBoxDestroy() + + + + + +void MainBoxUnfocus() { + + // EnableWindow(?) + + // gtk_widget_set_sensitive(mainbox.device, FALSE); + + ShowWindow(mainboxwindow, SW_HIDE); + +} // END MainBoxUnfocus() + + + + + +void MainBoxDeviceEvent() { + + char tempdevice[256]; + + struct stat filestat; + + int returnval; + + + + GetDlgItemText(mainboxwindow, IDC_0202, tempdevice, 256); + + returnval = stat(tempdevice, &filestat); + + if(returnval == -1) { + + SetDlgItemText(mainboxwindow, IDC_0203, "Device Type: ---"); + + return; + + } // ENDIF- Not a name of any sort? + + + + if(S_ISDIR(filestat.st_mode) != 0) { + + SetDlgItemText(mainboxwindow, IDC_0203, "Device Type: Not a device"); + + return; + + } // ENDIF- Not a regular file? + + + + SetDlgItemText(mainboxwindow, IDC_0203, "Device Type: Device Likely"); + + return; + +} // END MainBoxFileEvent() + + + + + +void MainBoxRefocus() { + + MainBoxDeviceEvent(); + + + + // gtk_widget_set_sensitive(mainbox.device, TRUE); + + // gtk_window_set_focus(GTK_WINDOW(mainbox.window), mainbox.device); + + // ShowWindow(mainboxwindow, SW_RESTORE); // and/or, SW_SHOW? SW_SHOWNORMAL? + + ShowWindow(mainboxwindow, SW_SHOW); + + SetActiveWindow(mainboxwindow); + +} // END MainBoxRefocus() + + + + + +void MainBoxCancelEvent() { + + MainBoxDestroy(); + + return; + +} // END MainBoxCancelEvent() + + + + + +void MainBoxOKEvent() { + + int retval; + + + + MainBoxUnfocus(); + + + + GetDlgItemText(mainboxwindow, IDC_0202, conf.devicename, 256); + + retval = DeviceOpen(); + + DeviceClose(); + + if(retval != 0) { + + MainBoxRefocus(); + + MessageBox(mainboxwindow, + + "Could not open the device", + + "CDVDlinuz Message", + + MB_OK | MB_ICONWARNING | MB_SETFOREGROUND); + + return; + + } // ENDIF- Trouble opening device? Abort here. + + + + SaveConf(); + + + + MainBoxCancelEvent(); + + return; + +} // END MainBoxOKEvent() + + + + + +void MainBoxDisplay() { + + InitConf(); // Man, am I boiling mad! CDVDinit() should have been called first! + + LoadConf(); + + + + // Adjust window position? + + + + // We held off setting the name until now... so description would show. + + SetDlgItemText(mainboxwindow, IDC_0202, conf.devicename); + + + + // First Time - Show the window + + ShowWindow(mainboxwindow, SW_SHOWNORMAL); + +} // END MainBoxDisplay() + + + + + +BOOL CALLBACK MainBoxCallback(HWND window, + + UINT msg, + + WPARAM param, + + LPARAM param2) { + + switch(msg) { + + case WM_INITDIALOG: + + mainboxwindow = window; + + MainBoxDisplay(); // In this case, final touches to this window. + + return(FALSE); // And let Windows display this window. + + break; + + + + case WM_CLOSE: // The "X" in the upper right corner was hit. + + MainBoxCancelEvent(); + + return(TRUE); + + break; + + + + case WM_COMMAND: + + // Do we wish to capture 'ENTER/RETURN' and/or 'ESC' here? + + + + switch(LOWORD(param)) { + + case IDC_0202: // Devicename Edit Box + + MainBoxDeviceEvent(); // Describe the File's type... + + return(FALSE); // Let Windows handle the actual 'edit' processing... + + break; + + + + case IDC_0204: // "Ok" Button + + MainBoxOKEvent(); + + return(TRUE); + + break; + + + + case IDC_0205: // "Cancel" Button + + MainBoxCancelEvent(); + + return(TRUE); + + break; + + } // ENDSWITCH param- Which object got the message? + + } // ENDSWITCH msg- what message has been sent to this window? + + + + return(FALSE); // Not a recognized message? Tell Windows to handle it. + +} // END MainBoxEventLoop() + diff --git a/plugins/CDVDlinuz/Src/Win32/screens.h b/plugins/CDVDlinuz/Src/Win32/screens.h index 3d12ffad47..0dff5630e3 100644 --- a/plugins/CDVDlinuz/Src/Win32/screens.h +++ b/plugins/CDVDlinuz/Src/Win32/screens.h @@ -1,18 +1,18 @@ -/* Weditres generated include file. Do NOT edit */ - -#include - -#define DLG_0100 100 - -#define IDC_0104 104 - -#define DLG_0200 200 - -#define IDC_0202 202 - -#define IDC_0203 203 - -#define IDC_0204 204 - -#define IDC_0205 205 - +/* Weditres generated include file. Do NOT edit */ + +#include + +#define DLG_0100 100 + +#define IDC_0104 104 + +#define DLG_0200 200 + +#define IDC_0202 202 + +#define IDC_0203 203 + +#define IDC_0204 204 + +#define IDC_0205 205 + diff --git a/plugins/CDVDlinuz/Src/buffer.c b/plugins/CDVDlinuz/Src/buffer.c index d308e96c3a..ccb781d5e5 100644 --- a/plugins/CDVDlinuz/Src/buffer.c +++ b/plugins/CDVDlinuz/Src/buffer.c @@ -1,1050 +1,1050 @@ -/* buffer.c - - * Copyright (C) 2002-2005 CDVDlinuz Team - - * - - * This program is free software; you can redistribute it and/or modify - - * it under the terms of the GNU General Public License as published by - - * the Free Software Foundation; either version 2 of the License, or - - * (at your option) any later version. - - * - - * This program 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 this program; if not, write to the Free Software - - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - */ - - - -#include // errno - -#include // NULL - -#include // printf() - -#include // strerror() - - - -#ifndef __LINUX__ - -#ifdef __linux__ - -#define __LINUX__ - -#endif /* __linux__ */ - -#endif /* No __LINUX__ */ - - - -#define CDVDdefs - -#include "PS2Edefs.h" - - - -#include "logfile.h" - - - -#include "buffer.h" - - - - - -// Globals - - - -struct BufferSortEmpty { - - u16 sortptr; - -}; - - - -struct BufferSort { - - u16 upptr; - - u16 uppos; - - - - u32 mask; - - u32 divisor; - - - - struct { - - u8 isdata; - - u16 ptr; - - } lower[256]; - - u16 ptrcount; - - u16 firstptr; - -}; - - - -struct BufferSortEmpty buffersortempty[BUFFERMAX]; - -u16 buffersortemptystart; - -u16 buffersortemptyend; - - - -struct BufferSort buffersort[BUFFERMAX]; - -u8 buffersortstartisdata; - -u16 buffersortstart; - - - -struct BufferList bufferlist[BUFFERMAX]; - -u16 userbuffer; - -u16 replacebuffer; - - - - - -void InitBuffer() { - - u16 i; - - u16 j; - - - -#ifdef VERBOSE_FUNCTION_BUFFER - - PrintLog("CDVD buffer: InitBuffer()"); - -#endif /* VERBOSE_FUNCTION_BUFFER */ - - - - buffersortemptystart = 0; - - buffersortemptyend = 0; - - for(i = 0; i < BUFFERMAX; i++) { - - buffersortempty[i].sortptr = i; - - } // NEXT i- Saying all buffersort[] entries are open. - - - - buffersortstart = 0xffff; - - buffersortstartisdata = 2; - - for(i = 0; i < BUFFERMAX; i++) { - - for(j = 0; j < 256; j++) buffersort[i].lower[j].isdata = 2; - - // buffersort[i].ptrcount = 0; - - } // NEXT i- Saying all buffersort[] entries are open. - - - - for(i = 0; i < BUFFERMAX; i++) { - - bufferlist[i].upsort = 0xffff; - - } // NEXT i- Clearing out the buffer pointers - - userbuffer = 0xffff; - - replacebuffer = BUFFERMAX - 1; - -} // END InitBuffer(); - - - - - -u16 AllocSortBuffer() { - - u16 newbuffer; - - - -#ifdef VERBOSE_FUNCTION_BUFFER - - PrintLog("CDVD buffer: AllocSortBuffer()"); - -#endif /* VERBOSE_FUNCTION_BUFFER */ - - - - newbuffer = buffersortempty[buffersortemptyend].sortptr; - - buffersortempty[buffersortemptyend].sortptr = BUFFERMAX; - - buffersortemptyend++; - - if(buffersortemptyend > BUFFERMAX - 1) buffersortemptyend = 0; - - - -#ifdef VERBOSE_FUNCTION_BUFFER - - PrintLog("CDVD buffer: Sort Buffer %u", newbuffer); - -#endif /* VERBOSE_FUNCTION_BUFFER */ - - - -#ifdef VERBOSE_WARNINGS_BUFFER - - if(buffersortemptyend == buffersortemptystart) { - - PrintLog("Completely out of Sort Buffers to allocate now!"); - - } // ENDIF- Out of Sort Buffers? Say so! - -#endif /* VERBOSE_WARNINGS_BUFFER */ - - - - return(newbuffer); - -} // END AllocSortBuffer() - - - - - -void ReleaseSortBuffer(u16 oldbuffer) { - -#ifdef VERBOSE_FUNCTION_BUFFER - - PrintLog("CDVD buffer: ReleaseSortBuffer(%u)", oldbuffer); - -#endif /* VERBOSE_FUNCTION_BUFFER */ - - - - buffersortempty[buffersortemptystart].sortptr = oldbuffer; - - buffersortemptystart++; - - if(buffersortemptystart > BUFFERMAX - 1) buffersortemptystart = 0; - -} // END ReleaseSortBuffer() - - - - - -// Returns either index in buffersort... or closest insertion point. - -// Make lsn == buffersort[int].lsn test for exact match - -u16 FindListBuffer(u32 lsn) { - - u16 current; - - u8 isdata; - - u32 index; - - - -#ifdef VERBOSE_FUNCTION_BUFFER - - PrintLog("CDVD buffer: FindListBuffer(%u)", lsn); - -#endif /* VERBOSE_FUNCTION_BUFFER */ - - - - if(buffersortstart == 0xffff) return(0xffff); // Buffer empty? Exit - - - - if(buffersortstartisdata == 1) { - - if(bufferlist[buffersortstart].lsn != lsn) return(0xffff); - - return(buffersortstart); - - } // ENDIF- Only one Record in Buffer? - - - -#ifdef VERBOSE_FUNCTION_BUFFER - - PrintLog("CDVD buffer: Searching..."); - -#endif /* VERBOSE_FUNCTION_BUFFER */ - - - - current = buffersortstart; - - isdata = 0; - - while(isdata == 0) { - - index = lsn; - - index &= buffersort[current].mask; - - index /= buffersort[current].divisor; - - isdata = buffersort[current].lower[index].isdata; - - current = buffersort[current].lower[index].ptr; - - } // ENDWHILE- still haven't found data - - - - if(isdata == 2) return(0xffff); // Pointing at empty entry? - - if(bufferlist[current].lsn != lsn) return(0xffff); // LSNs don't match? - - - -#ifdef VERBOSE_FUNCTION_BUFFER - - PrintLog("CDVD buffer: Found."); - -#endif /* VERBOSE_FUNCTION_BUFFER */ - - return(current); - -} // END FindListBuffer() - - - - - -// Removes buffer from the buffersort list - -// bufnum = The bufferlist pointer - -void RemoveListBuffer(u16 bufnum) { - - u16 current; - - u16 currentpos; - - - - u16 upperlink; - - u16 upperindex; - - - - u16 lowerlink; - - u8 lowerisdata; - - - - u32 index; - - - -#ifdef VERBOSE_FUNCTION_BUFFER - - PrintLog("CDVD buffer: RemoveListBuffer(%u)", bufnum); - -#endif /* VERBOSE_FUNCTION_BUFFER */ - - - - if(bufferlist[bufnum].upsort == 0xffff) return; // No link to break. - - - - current = bufferlist[bufnum].upsort; - - currentpos = bufferlist[bufnum].uppos; - - bufferlist[bufnum].upsort = 0xffff; - - - - if(current == 0xfffe) { - - buffersortstart = 0xffff; - - buffersortstartisdata = 2; - -#ifdef VERBOSE_FUNCTION_BUFFER - - PrintLog("CDVD buffer: Buffer emptied"); - -#endif /* VERBOSE_FUNCTION_BUFFER */ - - return; - - } // ENDIF- Last link broken... empty buffer now. - - - - lowerlink = 0xffff; - - lowerisdata = 2; - - - - // Remove Lower Pointer - - buffersort[current].lower[currentpos].isdata = 2; - - if(currentpos == buffersort[current].firstptr) { - - index = currentpos + 1; - - while((index < 256) && (buffersort[current].lower[index].isdata == 2)) index++; - - buffersort[current].firstptr = index; - - } // ENDIF- Do we need to move firstptr to an active entry? - - buffersort[current].ptrcount--; - -#ifdef VERBOSE_FUNCTION_BUFFER - - PrintLog("CDVD buffer: Pointer count for sort buffer %u: %u", - - current, buffersort[current].ptrcount); - -#endif /* VERBOSE_FUNCTION_BUFFER */ - - if(buffersort[current].ptrcount > 1) return; // Still 2+ branches left - - - - // Find Lower Link - - index = buffersort[current].firstptr; - - lowerlink = buffersort[current].lower[index].ptr; - - lowerisdata = buffersort[current].lower[index].isdata; - - buffersort[current].lower[index].isdata = 2; - - - - // Find and Break Upper Link - - upperlink = buffersort[current].upptr; - - upperindex = buffersort[current].uppos; - - - - if(upperlink == 0xffff) { - - buffersortstart = lowerlink; - - buffersortstartisdata = lowerisdata; - - } else { - - buffersort[upperlink].lower[upperindex].ptr = lowerlink; - - buffersort[upperlink].lower[upperindex].isdata = lowerisdata; - - } // ENDIF- Did we hit the top of the web? - - - - // Break Lower Link - - if(lowerisdata == 1) { - - if(upperlink == 0xffff) { - - bufferlist[lowerlink].upsort = 0xfffe; - -#ifdef VERBOSE_FUNCTION_BUFFER - - PrintLog("CDVD buffer: Buffer down to 1 record now"); - -#endif /* VERBOSE_FUNCTION_BUFFER */ - - } else { - - bufferlist[lowerlink].upsort = upperlink; - - bufferlist[lowerlink].uppos = upperindex; - - } // ENDIF- Is this the last active buffersort? - - } else { - - buffersort[lowerlink].upptr = upperlink; - - buffersort[lowerlink].uppos = upperindex; - - } // ENDIF- Was this a BufferList link? - - - - // Cleanup in aisle 5.... - - ReleaseSortBuffer(current); - - return; - -} // END RemoveListBuffer() - - - - - -// Adds buffer to the buffersort list - -// bufnum = The bufferlist pointer - -void AddListBuffer(u16 bufnum) { - - u32 newmask; - - u32 newdivisor; - - - - u16 newbuffer; - - u32 newvalue; - - - - u16 current; - - u32 currentvalue; - - u8 currentisdata; - - - - u16 prevbuffer; - - u32 prevvalue; - - - - u16 comparebuffer; - - u8 compareisdata; - - u32 comparevalue; - - - -#ifdef VERBOSE_FUNCTION_BUFFER - - PrintLog("CDVD buffer: AddListBuffer(%u)", bufnum); - -#endif /* VERBOSE_FUNCTION_BUFFER */ - - - - // Already in list? Oh, re-sorting? Got it covered. - - if(bufferlist[bufnum].upsort != 0xffff) RemoveListBuffer(bufnum); - - - - if(buffersortstartisdata == 2) { - - buffersortstart = bufnum; - - buffersortstartisdata = 1; - - bufferlist[bufnum].upsort = 0xfffe; - -#ifdef VERBOSE_FUNCTION_BUFFER - - PrintLog("CDVD buffer: Buffer up to 1 record now"); - -#endif /* VERBOSE_FUNCTION_BUFFER */ - - return; - - } // ENDIF- Empty list? Set for just 1 entry. - - - - if(buffersortstartisdata == 1) { - - newmask = 0xff000000; - - newdivisor = 0x01000000; - - newvalue = (bufferlist[bufnum].lsn & newmask) / newdivisor; - - currentvalue = (bufferlist[buffersortstart].lsn & newmask) / newdivisor; - - while((newdivisor != 0x00000001) && (newvalue == currentvalue)) { - - newmask /= 0x0100; - - newdivisor /= 0x0100; - - newvalue = (bufferlist[bufnum].lsn & newmask) / newdivisor; - - currentvalue = (bufferlist[buffersortstart].lsn & newmask) / newdivisor; - - } // ENDWHILE- trying to find a difference between the LSNs - - - - if(newvalue == currentvalue) { - - bufferlist[buffersortstart].upsort = 0xffff; - - bufferlist[bufnum].upsort = 0xfffe; - - buffersortstart = bufnum; - - return; - - - - } else { - - newbuffer = AllocSortBuffer(); - - buffersort[newbuffer].upptr = 0xffff; - - buffersort[newbuffer].mask = newmask; - - buffersort[newbuffer].divisor = newdivisor; - - buffersort[newbuffer].lower[currentvalue].isdata = 1; - - buffersort[newbuffer].lower[currentvalue].ptr = buffersortstart; - - buffersort[newbuffer].lower[newvalue].isdata = 1; - - buffersort[newbuffer].lower[newvalue].ptr = bufnum; - - buffersort[newbuffer].ptrcount = 2; - - buffersort[newbuffer].firstptr = currentvalue; - - if(newvalue < buffersort[newbuffer].firstptr) - - buffersort[newbuffer].firstptr = newvalue; - - - - bufferlist[buffersortstart].upsort = newbuffer; - - bufferlist[buffersortstart].uppos = currentvalue; - - buffersortstart = newbuffer; - - buffersortstartisdata = 0; - - - - bufferlist[bufnum].upsort = newbuffer; - - bufferlist[bufnum].uppos = newvalue; - -#ifdef VERBOSE_FUNCTION_BUFFER - - PrintLog("CDVD buffer: Buffer up to 2 records now"); - -#endif /* VERBOSE_FUNCTION_BUFFER */ - - return; - - } // ENDIF- Same LSN? Shift pointer in response. Else, add a Sort Record. - - } // ENDIF- Only 1 record? Check if we need a Sort Record. - - - - newmask = 0xff000000; - - newdivisor = 0x01000000; - - prevbuffer = 0xffff; - - prevvalue = 0; - - current = buffersortstart; - - currentisdata = 0; - - while(currentisdata == 0) { - - newvalue = (bufferlist[bufnum].lsn & newmask) / newdivisor; - - - - if(buffersort[current].mask != newmask) { - - comparebuffer = current; - - compareisdata = 0; - - while(compareisdata == 0) { - - comparevalue = buffersort[comparebuffer].firstptr; - - compareisdata = buffersort[comparebuffer].lower[comparevalue].isdata; - - comparebuffer = buffersort[comparebuffer].lower[comparevalue].ptr; - - } // ENDWHILE- looking for an another buffer to compare to... - - - - comparevalue = (bufferlist[comparebuffer].lsn & newmask) / newdivisor; - - if(newvalue != comparevalue) { - - // Add buffersort here (comparevalue/newvalue) - - newbuffer = AllocSortBuffer(); - - buffersort[newbuffer].upptr = buffersort[current].upptr; - - buffersort[newbuffer].uppos = buffersort[current].uppos; - - buffersort[newbuffer].mask = newmask; - - buffersort[newbuffer].divisor = newdivisor; - - buffersort[newbuffer].lower[comparevalue].isdata = 0; - - buffersort[newbuffer].lower[comparevalue].ptr = current; - - buffersort[newbuffer].lower[newvalue].isdata = 1; - - buffersort[newbuffer].lower[newvalue].ptr = bufnum; - - buffersort[newbuffer].ptrcount = 2; - - buffersort[newbuffer].firstptr = comparevalue; - - if(newvalue < buffersort[newbuffer].firstptr) - - buffersort[newbuffer].firstptr = newvalue; - - - - if(buffersort[newbuffer].upptr == 0xffff) { - - buffersortstart = newbuffer; - - } else { - - buffersort[prevbuffer].lower[prevvalue].isdata = 0; - - buffersort[prevbuffer].lower[prevvalue].ptr = newbuffer; - - if(prevvalue < buffersort[prevbuffer].firstptr) - - buffersort[prevbuffer].firstptr = prevvalue; - - } // ENDIF- Do we need to adjust to buffersortstart connection? - - buffersort[current].upptr = newbuffer; - - buffersort[current].uppos = comparevalue; - - bufferlist[bufnum].upsort = newbuffer; - - bufferlist[bufnum].uppos = newvalue; - - return; - - } // ENDIF- Don't match? Add a buffersort here to say that! - - - - compareisdata = 0; - - newmask /= 0x0100; - - newdivisor /= 0x0100; - - - - } else { - - currentisdata = buffersort[current].lower[newvalue].isdata; - - prevbuffer = current; - - prevvalue = newvalue; - - if(currentisdata == 0) { - - current = buffersort[current].lower[newvalue].ptr; - - newmask /= 0x0100; - - newdivisor /= 0x0100; - - if(newdivisor == 0) { - -#ifdef VERBOSE_WARNINGS_BUFFER - - PrintLog("CDVD buffer: Mask/Divisor at 0! Index corruption! (detected in search)"); - -#endif /* VERBOSE_WARNINGS_BUFFER */ - - return; - - } // ENDIF- The Mask went too far! Abort! (Sanity Check) - - } // ENDIF- Do we have to go through another level of sort data? - - } // ENDIF- We don't have a comparison on this byte? - - } // ENDWHILE- looking for his level... - - - - if(buffersort[current].lower[newvalue].isdata == 2) { - - buffersort[current].lower[newvalue].isdata = 1; - - buffersort[current].lower[newvalue].ptr = bufnum; - - buffersort[current].ptrcount++; - - if(newvalue < buffersort[current].firstptr) - - buffersort[current].firstptr = newvalue; - - bufferlist[bufnum].upsort = current; - - bufferlist[bufnum].uppos = newvalue; - - return; - - } // ENDIF- an empty slot? Fill it in. - - - - comparebuffer = buffersort[current].lower[newvalue].ptr; - - if(bufferlist[bufnum].lsn == bufferlist[comparebuffer].lsn) { - - buffersort[current].lower[newvalue].ptr = bufnum; - - bufferlist[bufnum].upsort = current; - - bufferlist[bufnum].uppos = newvalue; - - bufferlist[comparebuffer].upsort = 0xffff; - - return; - - } // ENDIF- Same LSN? Replace! - - - - // Calc new position based on new separation markers... - - newmask /= 0x0100; - - newdivisor /= 0x0100; - - if(newdivisor == 0) { - -#ifdef VERBOSE_WARNINGS_BUFFER - - PrintLog("CDVD buffer: Mask/Divisor at 0! Index corruption! (bottom add initial)"); - -#endif /* VERBOSE_WARNINGS_BUFFER */ - - return; - - } // ENDIF- The Mask went too far! Abort! (Sanity Check) - - newvalue = (bufferlist[bufnum].lsn & newmask) / newdivisor; - - comparevalue = (bufferlist[comparebuffer].lsn & newmask) / newdivisor; - - while((newmask != 0x000000ff) && (comparevalue == newvalue)) { - - newmask /= 0x0100; - - newdivisor /= 0x0100; - - if(newdivisor == 0) { - -#ifdef VERBOSE_WARNINGS_BUFFER - - PrintLog("CDVD buffer: Mask/Divisor at 0! Index corruption! (bottom add loop)"); - -#endif /* VERBOSE_WARNINGS_BUFFER */ - - return; - - } // ENDIF- The Mask went too far! Abort! (Sanity Check) - - newvalue = (bufferlist[bufnum].lsn & newmask) / newdivisor; - - comparevalue = (bufferlist[comparebuffer].lsn & newmask) / newdivisor; - - } // ENDWHILE- continuing to scan for difference between the two numbers - - - - newbuffer = AllocSortBuffer(); - - buffersort[newbuffer].upptr = prevbuffer; - - buffersort[newbuffer].uppos = prevvalue; - - buffersort[newbuffer].mask = newmask; - - buffersort[newbuffer].divisor = newdivisor; - - buffersort[newbuffer].lower[comparevalue].isdata = 1; - - buffersort[newbuffer].lower[comparevalue].ptr = comparebuffer; - - buffersort[newbuffer].lower[newvalue].isdata = 1; - - buffersort[newbuffer].lower[newvalue].ptr = bufnum; - - buffersort[newbuffer].ptrcount = 2; - - buffersort[newbuffer].firstptr = comparevalue; - - if(newvalue < buffersort[newbuffer].firstptr) - - buffersort[newbuffer].firstptr = newvalue; - - - - buffersort[prevbuffer].lower[prevvalue].isdata = 0; - - buffersort[prevbuffer].lower[prevvalue].ptr = newbuffer; - - bufferlist[comparebuffer].upsort = newbuffer; - - bufferlist[comparebuffer].uppos = comparevalue; - - bufferlist[bufnum].upsort = newbuffer; - - bufferlist[bufnum].uppos = newvalue; - -} // END AddListBuffer() - - - -#ifdef VERBOSE_WARNINGS_BUFFER - -void PrintSortBuffers() { - - u16 h; - - u16 i; - - u16 j; - - - - printf("CDVD buffer: Sort Buffer Dump\n"); - - printf("CDVD buffer: Top Pointer: isdata %u ptr %u\n", - - buffersortstartisdata, buffersortstart); - - for(i = 0; i < BUFFERMAX; i++) { - - - - h = 0; - - while((h < BUFFERMAX) && (buffersortempty[h].sortptr != i)) h++; - - - - if(h == BUFFERMAX) { - - printf("CDVD buffer: Sort Buffer:%u Mask:%x Divisor:%x\n", - - i, buffersort[i].mask, buffersort[i].divisor); - - printf("CDVD buffer: Up Ptr:%u Up Pos:%u First Down Ptr:%u Ptr Count: %u\n", - - buffersort[i].upptr, - - buffersort[i].uppos, - - buffersort[i].firstptr, - - buffersort[i].ptrcount); - - printf("CDVD buffer: "); - - for(j = 0; j < 256; j++) { - - if(buffersort[i].lower[j].isdata == 1) printf(" D"); - - if(buffersort[i].lower[j].isdata == 0) printf(" L"); - - if(buffersort[i].lower[j].isdata < 2) { - - printf("%u:%u", j, buffersort[i].lower[j].ptr); - - } // ENDIF- We have active data? Print it. - - } // NEXT j- Scanning lower 256 pointers for active ones... - - printf("\n"); - - } // ENDIF- Not found in inactive list? Must be active! Print it. - - } // NEXT i- looking at all the Allocated Buffers... - -} // END PrintSortBuffers() - -#endif /* VERBOSE_WARNINGS_BUFFER */ - +/* buffer.c + + * Copyright (C) 2002-2005 CDVDlinuz Team + + * + + * This program is free software; you can redistribute it and/or modify + + * it under the terms of the GNU General Public License as published by + + * the Free Software Foundation; either version 2 of the License, or + + * (at your option) any later version. + + * + + * This program 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 this program; if not, write to the Free Software + + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + + + +#include // errno + +#include // NULL + +#include // printf() + +#include // strerror() + + + +#ifndef __LINUX__ + +#ifdef __linux__ + +#define __LINUX__ + +#endif /* __linux__ */ + +#endif /* No __LINUX__ */ + + + +#define CDVDdefs + +#include "PS2Edefs.h" + + + +#include "logfile.h" + + + +#include "buffer.h" + + + + + +// Globals + + + +struct BufferSortEmpty { + + u16 sortptr; + +}; + + + +struct BufferSort { + + u16 upptr; + + u16 uppos; + + + + u32 mask; + + u32 divisor; + + + + struct { + + u8 isdata; + + u16 ptr; + + } lower[256]; + + u16 ptrcount; + + u16 firstptr; + +}; + + + +struct BufferSortEmpty buffersortempty[BUFFERMAX]; + +u16 buffersortemptystart; + +u16 buffersortemptyend; + + + +struct BufferSort buffersort[BUFFERMAX]; + +u8 buffersortstartisdata; + +u16 buffersortstart; + + + +struct BufferList bufferlist[BUFFERMAX]; + +u16 userbuffer; + +u16 replacebuffer; + + + + + +void InitBuffer() { + + u16 i; + + u16 j; + + + +#ifdef VERBOSE_FUNCTION_BUFFER + + PrintLog("CDVD buffer: InitBuffer()"); + +#endif /* VERBOSE_FUNCTION_BUFFER */ + + + + buffersortemptystart = 0; + + buffersortemptyend = 0; + + for(i = 0; i < BUFFERMAX; i++) { + + buffersortempty[i].sortptr = i; + + } // NEXT i- Saying all buffersort[] entries are open. + + + + buffersortstart = 0xffff; + + buffersortstartisdata = 2; + + for(i = 0; i < BUFFERMAX; i++) { + + for(j = 0; j < 256; j++) buffersort[i].lower[j].isdata = 2; + + // buffersort[i].ptrcount = 0; + + } // NEXT i- Saying all buffersort[] entries are open. + + + + for(i = 0; i < BUFFERMAX; i++) { + + bufferlist[i].upsort = 0xffff; + + } // NEXT i- Clearing out the buffer pointers + + userbuffer = 0xffff; + + replacebuffer = BUFFERMAX - 1; + +} // END InitBuffer(); + + + + + +u16 AllocSortBuffer() { + + u16 newbuffer; + + + +#ifdef VERBOSE_FUNCTION_BUFFER + + PrintLog("CDVD buffer: AllocSortBuffer()"); + +#endif /* VERBOSE_FUNCTION_BUFFER */ + + + + newbuffer = buffersortempty[buffersortemptyend].sortptr; + + buffersortempty[buffersortemptyend].sortptr = BUFFERMAX; + + buffersortemptyend++; + + if(buffersortemptyend > BUFFERMAX - 1) buffersortemptyend = 0; + + + +#ifdef VERBOSE_FUNCTION_BUFFER + + PrintLog("CDVD buffer: Sort Buffer %u", newbuffer); + +#endif /* VERBOSE_FUNCTION_BUFFER */ + + + +#ifdef VERBOSE_WARNINGS_BUFFER + + if(buffersortemptyend == buffersortemptystart) { + + PrintLog("Completely out of Sort Buffers to allocate now!"); + + } // ENDIF- Out of Sort Buffers? Say so! + +#endif /* VERBOSE_WARNINGS_BUFFER */ + + + + return(newbuffer); + +} // END AllocSortBuffer() + + + + + +void ReleaseSortBuffer(u16 oldbuffer) { + +#ifdef VERBOSE_FUNCTION_BUFFER + + PrintLog("CDVD buffer: ReleaseSortBuffer(%u)", oldbuffer); + +#endif /* VERBOSE_FUNCTION_BUFFER */ + + + + buffersortempty[buffersortemptystart].sortptr = oldbuffer; + + buffersortemptystart++; + + if(buffersortemptystart > BUFFERMAX - 1) buffersortemptystart = 0; + +} // END ReleaseSortBuffer() + + + + + +// Returns either index in buffersort... or closest insertion point. + +// Make lsn == buffersort[int].lsn test for exact match + +u16 FindListBuffer(u32 lsn) { + + u16 current; + + u8 isdata; + + u32 index; + + + +#ifdef VERBOSE_FUNCTION_BUFFER + + PrintLog("CDVD buffer: FindListBuffer(%u)", lsn); + +#endif /* VERBOSE_FUNCTION_BUFFER */ + + + + if(buffersortstart == 0xffff) return(0xffff); // Buffer empty? Exit + + + + if(buffersortstartisdata == 1) { + + if(bufferlist[buffersortstart].lsn != lsn) return(0xffff); + + return(buffersortstart); + + } // ENDIF- Only one Record in Buffer? + + + +#ifdef VERBOSE_FUNCTION_BUFFER + + PrintLog("CDVD buffer: Searching..."); + +#endif /* VERBOSE_FUNCTION_BUFFER */ + + + + current = buffersortstart; + + isdata = 0; + + while(isdata == 0) { + + index = lsn; + + index &= buffersort[current].mask; + + index /= buffersort[current].divisor; + + isdata = buffersort[current].lower[index].isdata; + + current = buffersort[current].lower[index].ptr; + + } // ENDWHILE- still haven't found data + + + + if(isdata == 2) return(0xffff); // Pointing at empty entry? + + if(bufferlist[current].lsn != lsn) return(0xffff); // LSNs don't match? + + + +#ifdef VERBOSE_FUNCTION_BUFFER + + PrintLog("CDVD buffer: Found."); + +#endif /* VERBOSE_FUNCTION_BUFFER */ + + return(current); + +} // END FindListBuffer() + + + + + +// Removes buffer from the buffersort list + +// bufnum = The bufferlist pointer + +void RemoveListBuffer(u16 bufnum) { + + u16 current; + + u16 currentpos; + + + + u16 upperlink; + + u16 upperindex; + + + + u16 lowerlink; + + u8 lowerisdata; + + + + u32 index; + + + +#ifdef VERBOSE_FUNCTION_BUFFER + + PrintLog("CDVD buffer: RemoveListBuffer(%u)", bufnum); + +#endif /* VERBOSE_FUNCTION_BUFFER */ + + + + if(bufferlist[bufnum].upsort == 0xffff) return; // No link to break. + + + + current = bufferlist[bufnum].upsort; + + currentpos = bufferlist[bufnum].uppos; + + bufferlist[bufnum].upsort = 0xffff; + + + + if(current == 0xfffe) { + + buffersortstart = 0xffff; + + buffersortstartisdata = 2; + +#ifdef VERBOSE_FUNCTION_BUFFER + + PrintLog("CDVD buffer: Buffer emptied"); + +#endif /* VERBOSE_FUNCTION_BUFFER */ + + return; + + } // ENDIF- Last link broken... empty buffer now. + + + + lowerlink = 0xffff; + + lowerisdata = 2; + + + + // Remove Lower Pointer + + buffersort[current].lower[currentpos].isdata = 2; + + if(currentpos == buffersort[current].firstptr) { + + index = currentpos + 1; + + while((index < 256) && (buffersort[current].lower[index].isdata == 2)) index++; + + buffersort[current].firstptr = index; + + } // ENDIF- Do we need to move firstptr to an active entry? + + buffersort[current].ptrcount--; + +#ifdef VERBOSE_FUNCTION_BUFFER + + PrintLog("CDVD buffer: Pointer count for sort buffer %u: %u", + + current, buffersort[current].ptrcount); + +#endif /* VERBOSE_FUNCTION_BUFFER */ + + if(buffersort[current].ptrcount > 1) return; // Still 2+ branches left + + + + // Find Lower Link + + index = buffersort[current].firstptr; + + lowerlink = buffersort[current].lower[index].ptr; + + lowerisdata = buffersort[current].lower[index].isdata; + + buffersort[current].lower[index].isdata = 2; + + + + // Find and Break Upper Link + + upperlink = buffersort[current].upptr; + + upperindex = buffersort[current].uppos; + + + + if(upperlink == 0xffff) { + + buffersortstart = lowerlink; + + buffersortstartisdata = lowerisdata; + + } else { + + buffersort[upperlink].lower[upperindex].ptr = lowerlink; + + buffersort[upperlink].lower[upperindex].isdata = lowerisdata; + + } // ENDIF- Did we hit the top of the web? + + + + // Break Lower Link + + if(lowerisdata == 1) { + + if(upperlink == 0xffff) { + + bufferlist[lowerlink].upsort = 0xfffe; + +#ifdef VERBOSE_FUNCTION_BUFFER + + PrintLog("CDVD buffer: Buffer down to 1 record now"); + +#endif /* VERBOSE_FUNCTION_BUFFER */ + + } else { + + bufferlist[lowerlink].upsort = upperlink; + + bufferlist[lowerlink].uppos = upperindex; + + } // ENDIF- Is this the last active buffersort? + + } else { + + buffersort[lowerlink].upptr = upperlink; + + buffersort[lowerlink].uppos = upperindex; + + } // ENDIF- Was this a BufferList link? + + + + // Cleanup in aisle 5.... + + ReleaseSortBuffer(current); + + return; + +} // END RemoveListBuffer() + + + + + +// Adds buffer to the buffersort list + +// bufnum = The bufferlist pointer + +void AddListBuffer(u16 bufnum) { + + u32 newmask; + + u32 newdivisor; + + + + u16 newbuffer; + + u32 newvalue; + + + + u16 current; + + u32 currentvalue; + + u8 currentisdata; + + + + u16 prevbuffer; + + u32 prevvalue; + + + + u16 comparebuffer; + + u8 compareisdata; + + u32 comparevalue; + + + +#ifdef VERBOSE_FUNCTION_BUFFER + + PrintLog("CDVD buffer: AddListBuffer(%u)", bufnum); + +#endif /* VERBOSE_FUNCTION_BUFFER */ + + + + // Already in list? Oh, re-sorting? Got it covered. + + if(bufferlist[bufnum].upsort != 0xffff) RemoveListBuffer(bufnum); + + + + if(buffersortstartisdata == 2) { + + buffersortstart = bufnum; + + buffersortstartisdata = 1; + + bufferlist[bufnum].upsort = 0xfffe; + +#ifdef VERBOSE_FUNCTION_BUFFER + + PrintLog("CDVD buffer: Buffer up to 1 record now"); + +#endif /* VERBOSE_FUNCTION_BUFFER */ + + return; + + } // ENDIF- Empty list? Set for just 1 entry. + + + + if(buffersortstartisdata == 1) { + + newmask = 0xff000000; + + newdivisor = 0x01000000; + + newvalue = (bufferlist[bufnum].lsn & newmask) / newdivisor; + + currentvalue = (bufferlist[buffersortstart].lsn & newmask) / newdivisor; + + while((newdivisor != 0x00000001) && (newvalue == currentvalue)) { + + newmask /= 0x0100; + + newdivisor /= 0x0100; + + newvalue = (bufferlist[bufnum].lsn & newmask) / newdivisor; + + currentvalue = (bufferlist[buffersortstart].lsn & newmask) / newdivisor; + + } // ENDWHILE- trying to find a difference between the LSNs + + + + if(newvalue == currentvalue) { + + bufferlist[buffersortstart].upsort = 0xffff; + + bufferlist[bufnum].upsort = 0xfffe; + + buffersortstart = bufnum; + + return; + + + + } else { + + newbuffer = AllocSortBuffer(); + + buffersort[newbuffer].upptr = 0xffff; + + buffersort[newbuffer].mask = newmask; + + buffersort[newbuffer].divisor = newdivisor; + + buffersort[newbuffer].lower[currentvalue].isdata = 1; + + buffersort[newbuffer].lower[currentvalue].ptr = buffersortstart; + + buffersort[newbuffer].lower[newvalue].isdata = 1; + + buffersort[newbuffer].lower[newvalue].ptr = bufnum; + + buffersort[newbuffer].ptrcount = 2; + + buffersort[newbuffer].firstptr = currentvalue; + + if(newvalue < buffersort[newbuffer].firstptr) + + buffersort[newbuffer].firstptr = newvalue; + + + + bufferlist[buffersortstart].upsort = newbuffer; + + bufferlist[buffersortstart].uppos = currentvalue; + + buffersortstart = newbuffer; + + buffersortstartisdata = 0; + + + + bufferlist[bufnum].upsort = newbuffer; + + bufferlist[bufnum].uppos = newvalue; + +#ifdef VERBOSE_FUNCTION_BUFFER + + PrintLog("CDVD buffer: Buffer up to 2 records now"); + +#endif /* VERBOSE_FUNCTION_BUFFER */ + + return; + + } // ENDIF- Same LSN? Shift pointer in response. Else, add a Sort Record. + + } // ENDIF- Only 1 record? Check if we need a Sort Record. + + + + newmask = 0xff000000; + + newdivisor = 0x01000000; + + prevbuffer = 0xffff; + + prevvalue = 0; + + current = buffersortstart; + + currentisdata = 0; + + while(currentisdata == 0) { + + newvalue = (bufferlist[bufnum].lsn & newmask) / newdivisor; + + + + if(buffersort[current].mask != newmask) { + + comparebuffer = current; + + compareisdata = 0; + + while(compareisdata == 0) { + + comparevalue = buffersort[comparebuffer].firstptr; + + compareisdata = buffersort[comparebuffer].lower[comparevalue].isdata; + + comparebuffer = buffersort[comparebuffer].lower[comparevalue].ptr; + + } // ENDWHILE- looking for an another buffer to compare to... + + + + comparevalue = (bufferlist[comparebuffer].lsn & newmask) / newdivisor; + + if(newvalue != comparevalue) { + + // Add buffersort here (comparevalue/newvalue) + + newbuffer = AllocSortBuffer(); + + buffersort[newbuffer].upptr = buffersort[current].upptr; + + buffersort[newbuffer].uppos = buffersort[current].uppos; + + buffersort[newbuffer].mask = newmask; + + buffersort[newbuffer].divisor = newdivisor; + + buffersort[newbuffer].lower[comparevalue].isdata = 0; + + buffersort[newbuffer].lower[comparevalue].ptr = current; + + buffersort[newbuffer].lower[newvalue].isdata = 1; + + buffersort[newbuffer].lower[newvalue].ptr = bufnum; + + buffersort[newbuffer].ptrcount = 2; + + buffersort[newbuffer].firstptr = comparevalue; + + if(newvalue < buffersort[newbuffer].firstptr) + + buffersort[newbuffer].firstptr = newvalue; + + + + if(buffersort[newbuffer].upptr == 0xffff) { + + buffersortstart = newbuffer; + + } else { + + buffersort[prevbuffer].lower[prevvalue].isdata = 0; + + buffersort[prevbuffer].lower[prevvalue].ptr = newbuffer; + + if(prevvalue < buffersort[prevbuffer].firstptr) + + buffersort[prevbuffer].firstptr = prevvalue; + + } // ENDIF- Do we need to adjust to buffersortstart connection? + + buffersort[current].upptr = newbuffer; + + buffersort[current].uppos = comparevalue; + + bufferlist[bufnum].upsort = newbuffer; + + bufferlist[bufnum].uppos = newvalue; + + return; + + } // ENDIF- Don't match? Add a buffersort here to say that! + + + + compareisdata = 0; + + newmask /= 0x0100; + + newdivisor /= 0x0100; + + + + } else { + + currentisdata = buffersort[current].lower[newvalue].isdata; + + prevbuffer = current; + + prevvalue = newvalue; + + if(currentisdata == 0) { + + current = buffersort[current].lower[newvalue].ptr; + + newmask /= 0x0100; + + newdivisor /= 0x0100; + + if(newdivisor == 0) { + +#ifdef VERBOSE_WARNINGS_BUFFER + + PrintLog("CDVD buffer: Mask/Divisor at 0! Index corruption! (detected in search)"); + +#endif /* VERBOSE_WARNINGS_BUFFER */ + + return; + + } // ENDIF- The Mask went too far! Abort! (Sanity Check) + + } // ENDIF- Do we have to go through another level of sort data? + + } // ENDIF- We don't have a comparison on this byte? + + } // ENDWHILE- looking for his level... + + + + if(buffersort[current].lower[newvalue].isdata == 2) { + + buffersort[current].lower[newvalue].isdata = 1; + + buffersort[current].lower[newvalue].ptr = bufnum; + + buffersort[current].ptrcount++; + + if(newvalue < buffersort[current].firstptr) + + buffersort[current].firstptr = newvalue; + + bufferlist[bufnum].upsort = current; + + bufferlist[bufnum].uppos = newvalue; + + return; + + } // ENDIF- an empty slot? Fill it in. + + + + comparebuffer = buffersort[current].lower[newvalue].ptr; + + if(bufferlist[bufnum].lsn == bufferlist[comparebuffer].lsn) { + + buffersort[current].lower[newvalue].ptr = bufnum; + + bufferlist[bufnum].upsort = current; + + bufferlist[bufnum].uppos = newvalue; + + bufferlist[comparebuffer].upsort = 0xffff; + + return; + + } // ENDIF- Same LSN? Replace! + + + + // Calc new position based on new separation markers... + + newmask /= 0x0100; + + newdivisor /= 0x0100; + + if(newdivisor == 0) { + +#ifdef VERBOSE_WARNINGS_BUFFER + + PrintLog("CDVD buffer: Mask/Divisor at 0! Index corruption! (bottom add initial)"); + +#endif /* VERBOSE_WARNINGS_BUFFER */ + + return; + + } // ENDIF- The Mask went too far! Abort! (Sanity Check) + + newvalue = (bufferlist[bufnum].lsn & newmask) / newdivisor; + + comparevalue = (bufferlist[comparebuffer].lsn & newmask) / newdivisor; + + while((newmask != 0x000000ff) && (comparevalue == newvalue)) { + + newmask /= 0x0100; + + newdivisor /= 0x0100; + + if(newdivisor == 0) { + +#ifdef VERBOSE_WARNINGS_BUFFER + + PrintLog("CDVD buffer: Mask/Divisor at 0! Index corruption! (bottom add loop)"); + +#endif /* VERBOSE_WARNINGS_BUFFER */ + + return; + + } // ENDIF- The Mask went too far! Abort! (Sanity Check) + + newvalue = (bufferlist[bufnum].lsn & newmask) / newdivisor; + + comparevalue = (bufferlist[comparebuffer].lsn & newmask) / newdivisor; + + } // ENDWHILE- continuing to scan for difference between the two numbers + + + + newbuffer = AllocSortBuffer(); + + buffersort[newbuffer].upptr = prevbuffer; + + buffersort[newbuffer].uppos = prevvalue; + + buffersort[newbuffer].mask = newmask; + + buffersort[newbuffer].divisor = newdivisor; + + buffersort[newbuffer].lower[comparevalue].isdata = 1; + + buffersort[newbuffer].lower[comparevalue].ptr = comparebuffer; + + buffersort[newbuffer].lower[newvalue].isdata = 1; + + buffersort[newbuffer].lower[newvalue].ptr = bufnum; + + buffersort[newbuffer].ptrcount = 2; + + buffersort[newbuffer].firstptr = comparevalue; + + if(newvalue < buffersort[newbuffer].firstptr) + + buffersort[newbuffer].firstptr = newvalue; + + + + buffersort[prevbuffer].lower[prevvalue].isdata = 0; + + buffersort[prevbuffer].lower[prevvalue].ptr = newbuffer; + + bufferlist[comparebuffer].upsort = newbuffer; + + bufferlist[comparebuffer].uppos = comparevalue; + + bufferlist[bufnum].upsort = newbuffer; + + bufferlist[bufnum].uppos = newvalue; + +} // END AddListBuffer() + + + +#ifdef VERBOSE_WARNINGS_BUFFER + +void PrintSortBuffers() { + + u16 h; + + u16 i; + + u16 j; + + + + printf("CDVD buffer: Sort Buffer Dump\n"); + + printf("CDVD buffer: Top Pointer: isdata %u ptr %u\n", + + buffersortstartisdata, buffersortstart); + + for(i = 0; i < BUFFERMAX; i++) { + + + + h = 0; + + while((h < BUFFERMAX) && (buffersortempty[h].sortptr != i)) h++; + + + + if(h == BUFFERMAX) { + + printf("CDVD buffer: Sort Buffer:%u Mask:%x Divisor:%x\n", + + i, buffersort[i].mask, buffersort[i].divisor); + + printf("CDVD buffer: Up Ptr:%u Up Pos:%u First Down Ptr:%u Ptr Count: %u\n", + + buffersort[i].upptr, + + buffersort[i].uppos, + + buffersort[i].firstptr, + + buffersort[i].ptrcount); + + printf("CDVD buffer: "); + + for(j = 0; j < 256; j++) { + + if(buffersort[i].lower[j].isdata == 1) printf(" D"); + + if(buffersort[i].lower[j].isdata == 0) printf(" L"); + + if(buffersort[i].lower[j].isdata < 2) { + + printf("%u:%u", j, buffersort[i].lower[j].ptr); + + } // ENDIF- We have active data? Print it. + + } // NEXT j- Scanning lower 256 pointers for active ones... + + printf("\n"); + + } // ENDIF- Not found in inactive list? Must be active! Print it. + + } // NEXT i- looking at all the Allocated Buffers... + +} // END PrintSortBuffers() + +#endif /* VERBOSE_WARNINGS_BUFFER */ + diff --git a/plugins/CDVDlinuz/Src/buffer.h b/plugins/CDVDlinuz/Src/buffer.h index 3b2ec4d003..e3edb90da9 100644 --- a/plugins/CDVDlinuz/Src/buffer.h +++ b/plugins/CDVDlinuz/Src/buffer.h @@ -1,134 +1,134 @@ -/* buffer.h - - * Copyright (C) 2002-2005 CDVDlinuz Team - - * - - * This program is free software; you can redistribute it and/or modify - - * it under the terms of the GNU General Public License as published by - - * the Free Software Foundation; either version 2 of the License, or - - * (at your option) any later version. - - * - - * This program 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 this program; if not, write to the Free Software - - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - */ - - - -#ifndef __BUFFER_H__ - -#define __BUFFER_H__ - - - - - -#define CDVDdefs - -#include "PS2Edefs.h" - - - - - -// #define VERBOSE_FUNCTION_BUFFER - -// #define VERBOSE_WARNINGS_BUFFER - - - -// Remember, each buffer set is about 5k (packed. might be 4x that in-memory) - -// Minimum: 16 Maximum: 32760 - -#define BUFFERMAX 256 - - - - - -// Buffer Structures - - - -struct BufferList { - - u16 upsort; // Don't alter this variable - - u16 uppos; // Don't alter this variable - - - - u32 lsn; - - int mode; // -1 means error - - u8 buffer[2368]; - - u8 offset; - - cdvdSubQ subq; - -}; - - - - - -// Exported Variables - - - -extern struct BufferList bufferlist[]; - -extern u16 userbuffer; - -extern u16 replacebuffer; - - - - - -// Exported Functions - - - -extern void InitBuffer(); - -extern u16 FindListBuffer(u32 lsn); - -extern void RemoveListBuffer(u16 oldbuffer); - -extern void AddListBuffer(u16 newbuffer); - -#ifdef VERBOSE_WARNINGS_BUFFER - -extern void PrintSortBuffers(); - -#endif /* VERBOSE_WARNINGS_BUFFER */ - - - - - -#endif /* __BUFFER_H__ */ - +/* buffer.h + + * Copyright (C) 2002-2005 CDVDlinuz Team + + * + + * This program is free software; you can redistribute it and/or modify + + * it under the terms of the GNU General Public License as published by + + * the Free Software Foundation; either version 2 of the License, or + + * (at your option) any later version. + + * + + * This program 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 this program; if not, write to the Free Software + + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + + + +#ifndef __BUFFER_H__ + +#define __BUFFER_H__ + + + + + +#define CDVDdefs + +#include "PS2Edefs.h" + + + + + +// #define VERBOSE_FUNCTION_BUFFER + +// #define VERBOSE_WARNINGS_BUFFER + + + +// Remember, each buffer set is about 5k (packed. might be 4x that in-memory) + +// Minimum: 16 Maximum: 32760 + +#define BUFFERMAX 256 + + + + + +// Buffer Structures + + + +struct BufferList { + + u16 upsort; // Don't alter this variable + + u16 uppos; // Don't alter this variable + + + + u32 lsn; + + int mode; // -1 means error + + u8 buffer[2368]; + + u8 offset; + + cdvdSubQ subq; + +}; + + + + + +// Exported Variables + + + +extern struct BufferList bufferlist[]; + +extern u16 userbuffer; + +extern u16 replacebuffer; + + + + + +// Exported Functions + + + +extern void InitBuffer(); + +extern u16 FindListBuffer(u32 lsn); + +extern void RemoveListBuffer(u16 oldbuffer); + +extern void AddListBuffer(u16 newbuffer); + +#ifdef VERBOSE_WARNINGS_BUFFER + +extern void PrintSortBuffers(); + +#endif /* VERBOSE_WARNINGS_BUFFER */ + + + + + +#endif /* __BUFFER_H__ */ + diff --git a/plugins/CDVDlinuz/Src/convert.c b/plugins/CDVDlinuz/Src/convert.c index 7579673933..9a1fe12aef 100644 --- a/plugins/CDVDlinuz/Src/convert.c +++ b/plugins/CDVDlinuz/Src/convert.c @@ -1,236 +1,236 @@ -/* convert.c - - * Copyright (C) 2002-2005 PCSX2 Team - - * - - * This program is free software; you can redistribute it and/or modify - - * it under the terms of the GNU General Public License as published by - - * the Free Software Foundation; either version 2 of the License, or - - * (at your option) any later version. - - * - - * This program 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 this program; if not, write to the Free Software - - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - * - - * PCSX2 members can be contacted through their website at www.pcsx2.net. - - */ - - - - - -#include - -#include - - - -#include "convert.h" - - - - - -off64_t ConvertEndianOffset(off64_t number) { - -#ifndef CONVERTLITTLEENDIAN - - union { - - off64_t n; - - char c[sizeof(off64_t)]; - - } oldnumber, newnumber; - - int i; - - - - oldnumber.n = number; - - for(i = 0; i < sizeof(off64_t); i++) - - newnumber.c[i] = oldnumber.c[sizeof(off64_t) - 1 - i]; - - return(newnumber.n); - -#else - - return(number); - -#endif /* CONVERTLITTLEENDIAN */ - -} // END ConvertEndianOffset() - - - - - -unsigned int ConvertEndianUInt(unsigned int number) { - -#ifndef CONVERTLITTLEENDIAN - - union { - - unsigned int n; - - char c[sizeof(unsigned int)]; - - } oldnumber, newnumber; - - int i; - - - - oldnumber.n = number; - - for(i = 0; i < sizeof(unsigned int); i++) - - newnumber.c[i] = oldnumber.c[sizeof(unsigned int) - 1 - i]; - - return(newnumber.n); - -#else - - return(number); - -#endif /* CONVERTLITTLEENDIAN */ - -} // END ConvertEndianUInt() - - - - - -unsigned short ConvertEndianUShort(unsigned short number) { - -#ifndef CONVERTLITTLEENDIAN - - union { - - unsigned short n; - - char c[sizeof(unsigned short)]; - - } oldnumber, newnumber; - - int i; - - - - oldnumber.n = number; - - for(i = 0; i < sizeof(unsigned short); i++) - - newnumber.c[i] = oldnumber.c[sizeof(unsigned short) - 1 - i]; - - return(newnumber.n); - -#else - - return(number); - -#endif /* CONVERTLITTLEENDIAN */ - -} // END ConvertEndianUShort() - - - - - -// Note: deposits M/S/F data in buffer[0]/[1]/[2] respectively. - -void LBAtoMSF(unsigned long lsn, char *buffer) { - - unsigned long templsn; - - - - if(lsn >= 0xFFFFFFFF - 150) { - - *(buffer + 2) = 75-1; - - *(buffer + 1) = 60-1; - - *(buffer) = 100-1; - - } // ENDIF- Out of range? - - - - templsn = lsn; - - templsn += 150; // 2 second offset (75 Frames * 2 Seconds) - - *(buffer + 2) = templsn % 75; // Remainder in frames - - templsn -= *(buffer + 2); - - templsn /= 75; - - *(buffer + 1) = templsn % 60; // Remainder in seconds - - templsn -= *(buffer + 1); - - templsn /= 60; - - *(buffer) = templsn; // Leftover quotient in minutes - -} // END LBAtoMSF() - - - - - -unsigned long MSFtoLBA(char *buffer) { - - unsigned long templsn; - - - - if(buffer == NULL) return(0xFFFFFFFF); - - - - templsn = *(buffer); // Minutes - - templsn *= 60; - - templsn += *(buffer + 1); // Seconds - - templsn *= 75; - - templsn += *(buffer + 2); // Frames - - if(templsn < 150) return(0xFFFFFFFF); - - templsn -= 150; // Offset - - - - return(templsn); - -} // END MSFtoLBA() - +/* convert.c + + * Copyright (C) 2002-2005 PCSX2 Team + + * + + * This program is free software; you can redistribute it and/or modify + + * it under the terms of the GNU General Public License as published by + + * the Free Software Foundation; either version 2 of the License, or + + * (at your option) any later version. + + * + + * This program 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 this program; if not, write to the Free Software + + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + * + + * PCSX2 members can be contacted through their website at www.pcsx2.net. + + */ + + + + + +#include + +#include + + + +#include "convert.h" + + + + + +off64_t ConvertEndianOffset(off64_t number) { + +#ifndef CONVERTLITTLEENDIAN + + union { + + off64_t n; + + char c[sizeof(off64_t)]; + + } oldnumber, newnumber; + + int i; + + + + oldnumber.n = number; + + for(i = 0; i < sizeof(off64_t); i++) + + newnumber.c[i] = oldnumber.c[sizeof(off64_t) - 1 - i]; + + return(newnumber.n); + +#else + + return(number); + +#endif /* CONVERTLITTLEENDIAN */ + +} // END ConvertEndianOffset() + + + + + +unsigned int ConvertEndianUInt(unsigned int number) { + +#ifndef CONVERTLITTLEENDIAN + + union { + + unsigned int n; + + char c[sizeof(unsigned int)]; + + } oldnumber, newnumber; + + int i; + + + + oldnumber.n = number; + + for(i = 0; i < sizeof(unsigned int); i++) + + newnumber.c[i] = oldnumber.c[sizeof(unsigned int) - 1 - i]; + + return(newnumber.n); + +#else + + return(number); + +#endif /* CONVERTLITTLEENDIAN */ + +} // END ConvertEndianUInt() + + + + + +unsigned short ConvertEndianUShort(unsigned short number) { + +#ifndef CONVERTLITTLEENDIAN + + union { + + unsigned short n; + + char c[sizeof(unsigned short)]; + + } oldnumber, newnumber; + + int i; + + + + oldnumber.n = number; + + for(i = 0; i < sizeof(unsigned short); i++) + + newnumber.c[i] = oldnumber.c[sizeof(unsigned short) - 1 - i]; + + return(newnumber.n); + +#else + + return(number); + +#endif /* CONVERTLITTLEENDIAN */ + +} // END ConvertEndianUShort() + + + + + +// Note: deposits M/S/F data in buffer[0]/[1]/[2] respectively. + +void LBAtoMSF(unsigned long lsn, char *buffer) { + + unsigned long templsn; + + + + if(lsn >= 0xFFFFFFFF - 150) { + + *(buffer + 2) = 75-1; + + *(buffer + 1) = 60-1; + + *(buffer) = 100-1; + + } // ENDIF- Out of range? + + + + templsn = lsn; + + templsn += 150; // 2 second offset (75 Frames * 2 Seconds) + + *(buffer + 2) = templsn % 75; // Remainder in frames + + templsn -= *(buffer + 2); + + templsn /= 75; + + *(buffer + 1) = templsn % 60; // Remainder in seconds + + templsn -= *(buffer + 1); + + templsn /= 60; + + *(buffer) = templsn; // Leftover quotient in minutes + +} // END LBAtoMSF() + + + + + +unsigned long MSFtoLBA(char *buffer) { + + unsigned long templsn; + + + + if(buffer == NULL) return(0xFFFFFFFF); + + + + templsn = *(buffer); // Minutes + + templsn *= 60; + + templsn += *(buffer + 1); // Seconds + + templsn *= 75; + + templsn += *(buffer + 2); // Frames + + if(templsn < 150) return(0xFFFFFFFF); + + templsn -= 150; // Offset + + + + return(templsn); + +} // END MSFtoLBA() + diff --git a/plugins/CDVDlinuz/Src/ini.c b/plugins/CDVDlinuz/Src/ini.c index f1bc58746f..2eb85b9a65 100644 --- a/plugins/CDVDlinuz/Src/ini.c +++ b/plugins/CDVDlinuz/Src/ini.c @@ -1,1378 +1,1378 @@ -/* ini.c - - * Copyright (C) 2002-2005 PCSX2 Team - - * - - * This program is free software; you can redistribute it and/or modify - - * it under the terms of the GNU General Public License as published by - - * the Free Software Foundation; either version 2 of the License, or - - * (at your option) any later version. - - * - - * This program 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 this program; if not, write to the Free Software - - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - * - - * PCSX2 members can be contacted through their website at www.pcsx2.net. - - */ - - - - - -#include // NULL - -#include // sprintf() - - - -#include "logfile.h" - -#include "actualfile.h" - -#include "ini.h" - - - - - -const char INIext[] = ".ini"; - -const char INInewext[] = ".new"; - - - - - -// Returns: position where new extensions should be added. - -int INIRemoveExt(char *argname, char *tempname) { - - int i; - - int j; - - int k; - - - -#ifdef VERBOSE_FUNCTION_INI - - PrintLog("CDVDiso ini: RemoveExt(%s)", argname); - -#endif /* VERBOSE_FUNCTION_INI */ - - - - i = 0; - - while((i <= INIMAXLEN) && (*(argname + i) != 0)) { - - *(tempname + i) = *(argname + i); - - i++; - - } // ENDWHILE- Copying the argument name into a temporary area; - - *(tempname + i) = 0; // And 0-terminate - - k = i; - - k--; - - - - j = 0; - - while((j <= INIMAXLEN) && (INIext[j] != 0)) j++; - - j--; - - - - while((j >= 0) && (*(tempname + k) == INIext[j])) { - - k--; - - j--; - - } // ENDWHILE- Comparing the ending characters to the INI ext. - - if(j < 0) { - - k++; - - i = k; - - *(tempname + i) = 0; // 0-terminate, cutting off ".ini" - - } // ENDIF- Do we have a match? Then remove the end chars. - - - - return(i); - -} // END INIRemoveExt() - - - - - -void INIAddInExt(char *tempname, int temppos) { - - int i; - - - -#ifdef VERBOSE_FUNCTION_INI - - PrintLog("CDVDiso ini: AddInExt(%s, %i)", tempname, temppos); - -#endif /* VERBOSE_FUNCTION_INI */ - - - - i = 0; - - while((i + temppos < INIMAXLEN) && (INIext[i] != 0)) { - - *(tempname + temppos + i) = INIext[i]; - - i++; - - } // ENDWHILE- Attaching extenstion to filename - - *(tempname + temppos + i) = 0; // And 0-terminate - -} // END INIAddInExt() - - - - - -void INIAddOutExt(char *tempname, int temppos) { - - int i; - - - -#ifdef VERBOSE_FUNCTION_INI - - PrintLog("CDVDiso ini: AddOutExt(%s, %i)", tempname, temppos); - -#endif /* VERBOSE_FUNCTION_INI */ - - - - i = 0; - - while((i + temppos < INIMAXLEN) && (INInewext[i] != 0)) { - - *(tempname + temppos + i) = INInewext[i]; - - i++; - - } // ENDWHILE- Attaching extenstion to filename - - *(tempname + temppos + i) = 0; // And 0-terminate - -} // END INIAddInExt() - - - - - -// Returns number of bytes read to get line (0 means end-of-file) - -int INIReadLine(ACTUALHANDLE infile, char *buffer) { - - int charcount; - - int i; - - char tempin[2]; - - int retflag; - - int retval; - - - -#ifdef VERBOSE_FUNCTION_INI - - PrintLog("CDVDiso ini: ReadLine()"); - -#endif /* VERBOSE_FUNCTION_INI */ - - - - charcount = 0; - - i = 0; - - tempin[1] = 0; - - retflag = 0; - - - - while((i < INIMAXLEN) && (retflag < 2)) { - - retval = ActualFileRead(infile, 1, tempin); - - charcount++; - - if(retval != 1) { - - retflag = 2; - - charcount--; - - - - } else if(tempin[0] == '\n') { - - retflag = 2; - - - - } else if(tempin[0] >= ' ') { - - *(buffer + i) = tempin[0]; - - i++; - - } // ENDLONGIF- How do we react to the next character? - - } // ENDWHILE- Loading up on characters until an End-of-Line appears - - *(buffer + i) = 0; // And 0-terminate - - - -#ifdef VERBOSE_FUNCTION_INI - - PrintLog("CDVDiso ini: Line: %s", buffer); - -#endif /* VERBOSE_FUNCTION_INI */ - - - - return(charcount); - -} // END INIReadLine() - -// Note: Do we need to back-skip a char if something other \n follows \r? - - - - - -// Returns: number of bytes to get to start of section (or -1) - -int INIFindSection(ACTUALHANDLE infile, char *section) { - - int charcount; - - int i; - - int retflag; - - int retval; - - char scanbuffer[INIMAXLEN+1]; - - - -#ifdef VERBOSE_FUNCTION_INI - - PrintLog("CDVDiso ini: FindSection(%s)", section); - -#endif /* VERBOSE_FUNCTION_INI */ - - - - charcount = 0; - - retflag = 0; - - - - while(retflag == 0) { - - retval = INIReadLine(infile, scanbuffer); - - if(retval == 0) return(-1); // EOF? Stop here. - - - - if(scanbuffer[0] == '[') { - - i = 0; - - while((i < INIMAXLEN) && - - (*(section + i) != 0) && - - (*(section + i) == scanbuffer[i + 1])) i++; - - if((i < INIMAXLEN - 2) && (*(section + i) == 0)) { - - if((scanbuffer[i + 1] == ']') && (scanbuffer[i + 2] == 0)) { - - retflag = 1; - - } // ENDIF- End marks look good? Return successful. - - } // ENDIF- Do we have a section match? - - } // ENDIF- Does this look like a section header? - - - - if(retflag == 0) charcount += retval; - - } // ENDWHILE- Scanning lines for the correct [Section] header. - - - - return(charcount); - -} // END INIFindSection() - - - - - -// Returns: number of bytes to get to start of keyword (or -1) - -int INIFindKeyword(ACTUALHANDLE infile, char *keyword, char *buffer) { - - int charcount; - - int i; - - int j; - - int retflag; - - int retval; - - char scanbuffer[INIMAXLEN+1]; - - - -#ifdef VERBOSE_FUNCTION_INI - - PrintLog("CDVDiso ini: FindKeyword(%s)", keyword); - -#endif /* VERBOSE_FUNCTION_INI */ - - - - charcount = 0; - - retflag = 0; - - - - while(retflag == 0) { - - retval = INIReadLine(infile, scanbuffer); - - if(retval == 0) return(-1); // EOF? Stop here. - - if(scanbuffer[0] == '[') return(-1); // New section? Stop here. - - - - i = 0; - - while((i < INIMAXLEN) && - - (*(keyword + i) != 0) && - - (*(keyword + i) == scanbuffer[i])) i++; - - if((i < INIMAXLEN - 2) && (*(keyword + i) == 0)) { - - if(scanbuffer[i] == '=') { - - retflag = 1; - - if(buffer != NULL) { - - i++; - - j = 0; - - while((i < INIMAXLEN) && (scanbuffer[i] != 0)) { - - *(buffer + j) = scanbuffer[i]; - - i++; - - j++; - - } // ENDWHILE- Copying the value out to the outbound buffer. - - *(buffer + j) = 0; // And 0-terminate. - - } // ENDIF- Return the value as well? - - } // ENDIF- End marks look good? Return successful. - - } // ENDIF- Do we have a section match? - - - - if(retflag == 0) charcount += retval; - - } // ENDWHILE- Scanning lines for the correct [Section] header. - - - -#ifdef VERBOSE_FUNCTION_INI - - PrintLog("CDVDiso ini: Value: %s", buffer); - -#endif /* VERBOSE_FUNCTION_INI */ - - - - return(charcount); - -} // END INIFindKeyWord() - - - - - -// Returns: number of bytes left to write... (from charcount back) - -int INICopy(ACTUALHANDLE infile, ACTUALHANDLE outfile, int charcount) { - - char buffer[4096]; - - int i; - - int chunk; - - int retval; - - - -#ifdef VERBOSE_FUNCTION_INI - - PrintLog("CDVDiso ini: Copy(%i)", charcount); - -#endif /* VERBOSE_FUNCTION_INI */ - - - - i = charcount; - - chunk = 4096; - - if(i < chunk) chunk = i; - - while(chunk > 0) { - - retval = ActualFileRead(infile, chunk, buffer); - - if(retval <= 0) return(i); // Trouble? Stop here. - - if(retval < chunk) chunk = retval; // Short block? Note it. - - - - retval = ActualFileWrite(outfile, chunk, buffer); - - if(retval <= 0) return(i); // Trouble? Stop here. - - i -= retval; - - if(retval < chunk) return(i); // Short block written? Stop here. - - - - chunk = 4096; - - if(i < chunk) chunk = i; - - } // ENDWHILE- Copying a section of file across, one chunk at a time. - - - - return(0); - -} // END INICopyToPos() - - - - - -int INISaveString(char *file, char *section, char *keyword, char *value) { - - char inname[INIMAXLEN+1]; - - char outname[INIMAXLEN+1]; - - int filepos; - - ACTUALHANDLE infile; - - ACTUALHANDLE outfile; - - int i; - - int retval; - - char templine[INIMAXLEN+1]; - - - - if(file == NULL) return(-1); - - if(section == NULL) return(-1); - - if(keyword == NULL) return(-1); - - if(value == NULL) return(-1); - - - -#ifdef VERBOSE_FUNCTION_INI - - PrintLog("CDVDiso ini: SaveString(%s, %s, %s, %s)", - - file, section, keyword, value); - -#endif /* VERBOSE_FUNCTION_INI */ - - - - filepos = INIRemoveExt(file, inname); - - for(i = 0; i <= filepos; i++) outname[i] = inname[i]; - - INIAddInExt(inname, filepos); - - INIAddOutExt(outname, filepos); - - - - filepos = 0; - - infile = ActualFileOpenForRead(inname); - - if(infile == ACTUALHANDLENULL) { - -#ifdef VERBOSE_FUNCTION_INI - - PrintLog("CDVDiso ini: creating new file"); - -#endif /* VERBOSE_FUNCTION_INI */ - - outfile = ActualFileOpenForWrite(inname); - - if(outfile == ACTUALHANDLENULL) return(-1); // Just a bad name? Abort. - - - - sprintf(templine, "[%s]\r\n", section); - - i = 0; - - while((i < INIMAXLEN) && (templine[i] != 0)) i++; - - retval = ActualFileWrite(outfile, i, templine); - - if(retval < i) { - - ActualFileClose(outfile); - - outfile = ACTUALHANDLENULL; - - ActualFileDelete(inname); - - return(-1); - - } // ENDIF- Trouble writing it out? Abort. - - - - sprintf(templine, "%s=%s\r\n", keyword, value); - - i = 0; - - while((i < INIMAXLEN) && (templine[i] != 0)) i++; - - retval = ActualFileWrite(outfile, i, templine); - - ActualFileClose(outfile); - - outfile = ACTUALHANDLENULL; - - if(retval < i) { - - ActualFileDelete(inname); - - return(-1); - - } // ENDIF- Trouble writing it out? Abort. - - return(0); - - } // ENDIF- No input file? Create a brand new .ini file then. - - - - retval = INIFindSection(infile, section); - - if(retval < 0) { - -#ifdef VERBOSE_FUNCTION_INI - - PrintLog("CDVDiso ini: creating new section"); - -#endif /* VERBOSE_FUNCTION_INI */ - - outfile = ActualFileOpenForWrite(outname); - - if(outfile == ACTUALHANDLENULL) { - - ActualFileClose(infile); - - infile = ACTUALHANDLENULL; - - return(-1); - - } // ENDIF- Couldn't open a temp file? Abort - - - - ActualFileSeek(infile, 0); // Move ini to beginning of file... - - INICopy(infile, outfile, 0x0FFFFFFF); // Copy the whole file out... - - - - sprintf(templine, "\r\n[%s]\r\n", section); - - i = 0; - - while((i < INIMAXLEN) && (templine[i] != 0)) i++; - - retval = ActualFileWrite(outfile, i, templine); - - if(retval < i) { - - ActualFileClose(infile); - - infile = ACTUALHANDLENULL; - - ActualFileClose(outfile); - - outfile = ACTUALHANDLENULL; - - ActualFileDelete(outname); - - return(-1); - - } // ENDIF- Trouble writing it out? Abort. - - - - sprintf(templine, "%s=%s\r\n", keyword, value); - - i = 0; - - while((i < INIMAXLEN) && (templine[i] != 0)) i++; - - retval = ActualFileWrite(outfile, i, templine); - - ActualFileClose(infile); - - infile = ACTUALHANDLENULL; - - ActualFileClose(outfile); - - outfile = ACTUALHANDLENULL; - - if(retval < i) { - - ActualFileDelete(outname); - - return(-1); - - } // ENDIF- Trouble writing it out? Abort. - - - - ActualFileDelete(inname); - - ActualFileRename(outname, inname); - - return(0); - - } // ENDIF- Couldn't find the section? Make a new one! - - - - filepos = retval; - - ActualFileSeek(infile, filepos); - - filepos += INIReadLine(infile, templine); // Get section line's byte count - - - - retval = INIFindKeyword(infile, keyword, NULL); - - if(retval < 0) { - -#ifdef VERBOSE_FUNCTION_INI - - PrintLog("CDVDiso ini: creating new keyword"); - -#endif /* VERBOSE_FUNCTION_INI */ - - ActualFileSeek(infile, filepos); - - retval = INIReadLine(infile, templine); - - i = 0; - - while((i < INIMAXLEN) && (templine[i] != 0) && (templine[i] != '=')) i++; - - while((retval > 0) && (templine[i] == '=')) { - - filepos += retval; - - retval = INIReadLine(infile, templine); - - i = 0; - - while((i < INIMAXLEN) && (templine[i] != 0) && (templine[i] != '=')) i++; - - } // ENDWHILE- skimming to the bottom of the section - - - - outfile = ActualFileOpenForWrite(outname); - - if(outfile == ACTUALHANDLENULL) { - - ActualFileClose(infile); - - infile = ACTUALHANDLENULL; - - return(-1); - - } // ENDIF- Couldn't open a temp file? Abort - - - - ActualFileSeek(infile, 0); - - retval = INICopy(infile, outfile, filepos); - - if(retval > 0) { - - ActualFileClose(infile); - - infile = ACTUALHANDLENULL; - - ActualFileClose(outfile); - - outfile = ACTUALHANDLENULL; - - ActualFileDelete(outname); - - return(-1); - - } // ENDIF- Trouble writing everything up to keyword? Abort. - - - - sprintf(templine, "%s=%s\r\n", keyword, value); - - i = 0; - - while((i < INIMAXLEN) && (templine[i] != 0)) i++; - - retval = ActualFileWrite(outfile, i, templine); - - if(retval < i) { - - ActualFileClose(infile); - - infile = ACTUALHANDLENULL; - - ActualFileClose(outfile); - - outfile = ACTUALHANDLENULL; - - ActualFileDelete(outname); - - return(-1); - - } // ENDIF- Trouble writing it out? Abort. - - - - } else { - -#ifdef VERBOSE_FUNCTION_INI - - PrintLog("CDVDiso ini: replacing keyword"); - -#endif /* VERBOSE_FUNCTION_INI */ - - filepos += retval; // Position just before old version of keyword - - - - outfile = ActualFileOpenForWrite(outname); - - if(outfile == ACTUALHANDLENULL) { - - ActualFileClose(infile); - - infile = ACTUALHANDLENULL; - - return(-1); - - } // ENDIF- Couldn't open a temp file? Abort - - - - ActualFileSeek(infile, 0); - - retval = INICopy(infile, outfile, filepos); - - if(retval > 0) { - - ActualFileClose(infile); - - infile = ACTUALHANDLENULL; - - ActualFileClose(outfile); - - outfile = ACTUALHANDLENULL; - - ActualFileDelete(outname); - - return(-1); - - } // ENDIF- Trouble writing everything up to keyword? Abort. - - - - INIReadLine(infile, templine); // Read past old keyword/value... - - - - // Replace with new value - - sprintf(templine, "%s=%s\r\n", keyword, value); - - i = 0; - - while((i < INIMAXLEN) && (templine[i] != 0)) i++; - - retval = ActualFileWrite(outfile, i, templine); - - if(retval < i) { - - ActualFileClose(infile); - - infile = ACTUALHANDLENULL; - - ActualFileClose(outfile); - - outfile = ACTUALHANDLENULL; - - ActualFileDelete(outname); - - return(-1); - - } // ENDIF- Trouble writing it out? Abort. - - } // ENDIF- Need to add a new keyword? - - - - INICopy(infile, outfile, 0xFFFFFFF); // Write out rest of file - - ActualFileClose(infile); - - infile = ACTUALHANDLENULL; - - ActualFileClose(outfile); - - outfile = ACTUALHANDLENULL; - - ActualFileDelete(inname); - - ActualFileRename(outname, inname); - - return(0); - -} // END INISaveString() - - - - - -int INILoadString(char *file, char *section, char *keyword, char *buffer) { - - char inname[INIMAXLEN+1]; - - int filepos; - - ACTUALHANDLE infile; - - int retval; - - - - if(file == NULL) return(-1); - - if(section == NULL) return(-1); - - if(keyword == NULL) return(-1); - - if(buffer == NULL) return(-1); - - - -#ifdef VERBOSE_FUNCTION_INI - - PrintLog("CDVDiso ini: LoadString(%s, %s, %s)", - - file, section, keyword); - -#endif /* VERBOSE_FUNCTION_INI */ - - - - filepos = INIRemoveExt(file, inname); - - INIAddInExt(inname, filepos); - - - - filepos = 0; - - infile = ActualFileOpenForRead(inname); - - if(infile == ACTUALHANDLENULL) return(-1); - - - - retval = INIFindSection(infile, section); - - if(retval < 0) { - - ActualFileClose(infile); - - infile = ACTUALHANDLENULL; - - return(-1); - - } // ENDIF- Didn't find it? Abort. - - - - retval = INIFindKeyword(infile, keyword, buffer); - - if(retval < 0) { - - ActualFileClose(infile); - - infile = ACTUALHANDLENULL; - - return(-1); - - } // ENDIF- Didn't find it? Abort. - - - - ActualFileClose(infile); - - infile = ACTUALHANDLENULL; - - return(0); - -} // END INILoadString() - - - - - -int INIRemove(char *file, char *section, char *keyword) { - - char inname[INIMAXLEN+1]; - - char outname[INIMAXLEN+1]; - - int filepos; - - ACTUALHANDLE infile; - - ACTUALHANDLE outfile; - - char templine[INIMAXLEN+1]; - - int i; - - int retval; - - - - if(file == NULL) return(-1); - - if(section == NULL) return(-1); - - - -#ifdef VERBOSE_FUNCTION_INI - - PrintLog("CDVDiso ini: Remove(%s, %s, %s)", - - file, section, keyword); - -#endif /* VERBOSE_FUNCTION_INI */ - - - - filepos = INIRemoveExt(file, inname); - - for(i = 0; i <= filepos; i++) outname[i] = inname[i]; - - INIAddInExt(inname, filepos); - - INIAddOutExt(outname, filepos); - - - - infile = ActualFileOpenForRead(inname); - - if(infile == ACTUALHANDLENULL) return(-1); - - - - retval = INIFindSection(infile, section); - - if(retval == -1) { - - ActualFileClose(infile); - - infile = ACTUALHANDLENULL; - - return(-1); - - } // ENDIF- Couldn't even find the section? Abort - - - - filepos = retval; - - if(keyword == NULL) { - -#ifdef VERBOSE_FUNCTION_INI - - PrintLog("CDVDiso ini: removing section"); - -#endif /* VERBOSE_FUNCTION_INI */ - - outfile = ActualFileOpenForWrite(outname); - - if(outfile == ACTUALHANDLENULL) { - - ActualFileClose(infile); - - infile = ACTUALHANDLENULL; - - return(-1); - - } // ENDIF- Couldn't open a temp file? Abort - - - - ActualFileSeek(infile, 0); - - retval = INICopy(infile, outfile, filepos); - - if(retval > 0) { - - ActualFileClose(infile); - - infile = ACTUALHANDLENULL; - - ActualFileClose(outfile); - - outfile = ACTUALHANDLENULL; - - ActualFileDelete(outname); - - return(-1); - - } // ENDIF- Trouble writing everything up to the section? Abort. - - - - templine[0] = 0; - - retval = 1; - - while((retval > 0) && (templine[0] != '[')) { - - retval = INIReadLine(infile, templine); - - } // ENDWHILE- Read to the start of the next section... or EOF. - - - - if(templine[0] == '[') { - - i = 0; - - while((i < INIMAXLEN) && (templine[i] != 0)) i++; - - retval = ActualFileWrite(outfile, i, templine); - - if(retval < i) { - - ActualFileClose(infile); - - infile = ACTUALHANDLENULL; - - ActualFileClose(outfile); - - outfile = ACTUALHANDLENULL; - - ActualFileDelete(outname); - - return(-1); - - } // ENDIF- Trouble writing it out? Abort. - - } // ENDIF- Are there other sections after this one? Save them then. - - - - } else { - - filepos = retval; - - ActualFileSeek(infile, filepos); - - filepos += INIReadLine(infile, templine); // Get section line's byte count - - - - retval = INIFindKeyword(infile, keyword, NULL); - - if(retval == -1) { - - ActualFileClose(infile); - - infile = ACTUALHANDLENULL; - - return(-1); - - } // ENDIF- Couldn't find the keyword? Abort - - filepos += retval; - - - -#ifdef VERBOSE_FUNCTION_INI - - PrintLog("CDVDiso ini: removing keyword"); - -#endif /* VERBOSE_FUNCTION_INI */ - - outfile = ActualFileOpenForWrite(outname); - - if(outfile == ACTUALHANDLENULL) { - - ActualFileClose(infile); - - infile = ACTUALHANDLENULL; - - return(-1); - - } // ENDIF- Couldn't open a temp file? Abort - - - - ActualFileSeek(infile, 0); - - retval = INICopy(infile, outfile, filepos); - - if(retval > 0) { - - ActualFileClose(infile); - - infile = ACTUALHANDLENULL; - - ActualFileClose(outfile); - - outfile = ACTUALHANDLENULL; - - ActualFileDelete(outname); - - return(-1); - - } // ENDIF- Trouble writing everything up to keyword? Abort. - - - - INIReadLine(infile, templine); // Read (and discard) the keyword line - - } // ENDIF- Wipe out the whole section? Or just a keyword? - - - - INICopy(infile, outfile, 0xFFFFFFF); // Write out rest of file - - ActualFileClose(infile); - - infile = ACTUALHANDLENULL; - - ActualFileClose(outfile); - - outfile = ACTUALHANDLENULL; - - ActualFileDelete(inname); - - ActualFileRename(outname, inname); - - return(0); - -} // END INIRemove() - - - - - -int INISaveUInt(char *file, char *section, char *keyword, unsigned int value) { - - char numvalue[INIMAXLEN+1]; - - - - sprintf(numvalue, "%u", value); - - return(INISaveString(file, section, keyword, numvalue)); - -} // END INISaveUInt() - - - - - -int INILoadUInt(char *file, char *section, char *keyword, unsigned int *buffer) { - - char numvalue[INIMAXLEN+1]; - - int retval; - - unsigned int value; - - // unsigned int sign; // Not needed in unsigned numbers - - int pos; - - - - if(buffer == NULL) return(-1); - - *(buffer) = 0; - - - - retval = INILoadString(file, section, keyword, numvalue); - - if(retval < 0) return(retval); - - - - value = 0; - - // sign = 1; // Start positive - - pos = 0; - - - - // Note: skip leading spaces? (Shouldn't have to, I hope) - - - - // if(numvalue[pos] == '-') { - - // pos++; - - // sign = -1; - - // } // ENDIF- Negative sign check - - - - while((pos < INIMAXLEN) && (numvalue[pos] != 0)) { - - if(value > (0xFFFFFFFF / 10)) return(-1); // Overflow? - - - - if((numvalue[pos] >= '0') && (numvalue[pos] <= '9')) { - - value *= 10; - - value += numvalue[pos] - '0'; - - pos++; - - } else { - - numvalue[pos] = 0; - - } // ENDIF- Add a digit in? Or stop searching for digits? - - } // ENDWHILE- Adding digits of info to our ever-increasing value - - - - // value *= sign - - *(buffer) = value; - - return(0); - -} // END INILoadUInt() - +/* ini.c + + * Copyright (C) 2002-2005 PCSX2 Team + + * + + * This program is free software; you can redistribute it and/or modify + + * it under the terms of the GNU General Public License as published by + + * the Free Software Foundation; either version 2 of the License, or + + * (at your option) any later version. + + * + + * This program 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 this program; if not, write to the Free Software + + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + * + + * PCSX2 members can be contacted through their website at www.pcsx2.net. + + */ + + + + + +#include // NULL + +#include // sprintf() + + + +#include "logfile.h" + +#include "actualfile.h" + +#include "ini.h" + + + + + +const char INIext[] = ".ini"; + +const char INInewext[] = ".new"; + + + + + +// Returns: position where new extensions should be added. + +int INIRemoveExt(char *argname, char *tempname) { + + int i; + + int j; + + int k; + + + +#ifdef VERBOSE_FUNCTION_INI + + PrintLog("CDVDiso ini: RemoveExt(%s)", argname); + +#endif /* VERBOSE_FUNCTION_INI */ + + + + i = 0; + + while((i <= INIMAXLEN) && (*(argname + i) != 0)) { + + *(tempname + i) = *(argname + i); + + i++; + + } // ENDWHILE- Copying the argument name into a temporary area; + + *(tempname + i) = 0; // And 0-terminate + + k = i; + + k--; + + + + j = 0; + + while((j <= INIMAXLEN) && (INIext[j] != 0)) j++; + + j--; + + + + while((j >= 0) && (*(tempname + k) == INIext[j])) { + + k--; + + j--; + + } // ENDWHILE- Comparing the ending characters to the INI ext. + + if(j < 0) { + + k++; + + i = k; + + *(tempname + i) = 0; // 0-terminate, cutting off ".ini" + + } // ENDIF- Do we have a match? Then remove the end chars. + + + + return(i); + +} // END INIRemoveExt() + + + + + +void INIAddInExt(char *tempname, int temppos) { + + int i; + + + +#ifdef VERBOSE_FUNCTION_INI + + PrintLog("CDVDiso ini: AddInExt(%s, %i)", tempname, temppos); + +#endif /* VERBOSE_FUNCTION_INI */ + + + + i = 0; + + while((i + temppos < INIMAXLEN) && (INIext[i] != 0)) { + + *(tempname + temppos + i) = INIext[i]; + + i++; + + } // ENDWHILE- Attaching extenstion to filename + + *(tempname + temppos + i) = 0; // And 0-terminate + +} // END INIAddInExt() + + + + + +void INIAddOutExt(char *tempname, int temppos) { + + int i; + + + +#ifdef VERBOSE_FUNCTION_INI + + PrintLog("CDVDiso ini: AddOutExt(%s, %i)", tempname, temppos); + +#endif /* VERBOSE_FUNCTION_INI */ + + + + i = 0; + + while((i + temppos < INIMAXLEN) && (INInewext[i] != 0)) { + + *(tempname + temppos + i) = INInewext[i]; + + i++; + + } // ENDWHILE- Attaching extenstion to filename + + *(tempname + temppos + i) = 0; // And 0-terminate + +} // END INIAddInExt() + + + + + +// Returns number of bytes read to get line (0 means end-of-file) + +int INIReadLine(ACTUALHANDLE infile, char *buffer) { + + int charcount; + + int i; + + char tempin[2]; + + int retflag; + + int retval; + + + +#ifdef VERBOSE_FUNCTION_INI + + PrintLog("CDVDiso ini: ReadLine()"); + +#endif /* VERBOSE_FUNCTION_INI */ + + + + charcount = 0; + + i = 0; + + tempin[1] = 0; + + retflag = 0; + + + + while((i < INIMAXLEN) && (retflag < 2)) { + + retval = ActualFileRead(infile, 1, tempin); + + charcount++; + + if(retval != 1) { + + retflag = 2; + + charcount--; + + + + } else if(tempin[0] == '\n') { + + retflag = 2; + + + + } else if(tempin[0] >= ' ') { + + *(buffer + i) = tempin[0]; + + i++; + + } // ENDLONGIF- How do we react to the next character? + + } // ENDWHILE- Loading up on characters until an End-of-Line appears + + *(buffer + i) = 0; // And 0-terminate + + + +#ifdef VERBOSE_FUNCTION_INI + + PrintLog("CDVDiso ini: Line: %s", buffer); + +#endif /* VERBOSE_FUNCTION_INI */ + + + + return(charcount); + +} // END INIReadLine() + +// Note: Do we need to back-skip a char if something other \n follows \r? + + + + + +// Returns: number of bytes to get to start of section (or -1) + +int INIFindSection(ACTUALHANDLE infile, char *section) { + + int charcount; + + int i; + + int retflag; + + int retval; + + char scanbuffer[INIMAXLEN+1]; + + + +#ifdef VERBOSE_FUNCTION_INI + + PrintLog("CDVDiso ini: FindSection(%s)", section); + +#endif /* VERBOSE_FUNCTION_INI */ + + + + charcount = 0; + + retflag = 0; + + + + while(retflag == 0) { + + retval = INIReadLine(infile, scanbuffer); + + if(retval == 0) return(-1); // EOF? Stop here. + + + + if(scanbuffer[0] == '[') { + + i = 0; + + while((i < INIMAXLEN) && + + (*(section + i) != 0) && + + (*(section + i) == scanbuffer[i + 1])) i++; + + if((i < INIMAXLEN - 2) && (*(section + i) == 0)) { + + if((scanbuffer[i + 1] == ']') && (scanbuffer[i + 2] == 0)) { + + retflag = 1; + + } // ENDIF- End marks look good? Return successful. + + } // ENDIF- Do we have a section match? + + } // ENDIF- Does this look like a section header? + + + + if(retflag == 0) charcount += retval; + + } // ENDWHILE- Scanning lines for the correct [Section] header. + + + + return(charcount); + +} // END INIFindSection() + + + + + +// Returns: number of bytes to get to start of keyword (or -1) + +int INIFindKeyword(ACTUALHANDLE infile, char *keyword, char *buffer) { + + int charcount; + + int i; + + int j; + + int retflag; + + int retval; + + char scanbuffer[INIMAXLEN+1]; + + + +#ifdef VERBOSE_FUNCTION_INI + + PrintLog("CDVDiso ini: FindKeyword(%s)", keyword); + +#endif /* VERBOSE_FUNCTION_INI */ + + + + charcount = 0; + + retflag = 0; + + + + while(retflag == 0) { + + retval = INIReadLine(infile, scanbuffer); + + if(retval == 0) return(-1); // EOF? Stop here. + + if(scanbuffer[0] == '[') return(-1); // New section? Stop here. + + + + i = 0; + + while((i < INIMAXLEN) && + + (*(keyword + i) != 0) && + + (*(keyword + i) == scanbuffer[i])) i++; + + if((i < INIMAXLEN - 2) && (*(keyword + i) == 0)) { + + if(scanbuffer[i] == '=') { + + retflag = 1; + + if(buffer != NULL) { + + i++; + + j = 0; + + while((i < INIMAXLEN) && (scanbuffer[i] != 0)) { + + *(buffer + j) = scanbuffer[i]; + + i++; + + j++; + + } // ENDWHILE- Copying the value out to the outbound buffer. + + *(buffer + j) = 0; // And 0-terminate. + + } // ENDIF- Return the value as well? + + } // ENDIF- End marks look good? Return successful. + + } // ENDIF- Do we have a section match? + + + + if(retflag == 0) charcount += retval; + + } // ENDWHILE- Scanning lines for the correct [Section] header. + + + +#ifdef VERBOSE_FUNCTION_INI + + PrintLog("CDVDiso ini: Value: %s", buffer); + +#endif /* VERBOSE_FUNCTION_INI */ + + + + return(charcount); + +} // END INIFindKeyWord() + + + + + +// Returns: number of bytes left to write... (from charcount back) + +int INICopy(ACTUALHANDLE infile, ACTUALHANDLE outfile, int charcount) { + + char buffer[4096]; + + int i; + + int chunk; + + int retval; + + + +#ifdef VERBOSE_FUNCTION_INI + + PrintLog("CDVDiso ini: Copy(%i)", charcount); + +#endif /* VERBOSE_FUNCTION_INI */ + + + + i = charcount; + + chunk = 4096; + + if(i < chunk) chunk = i; + + while(chunk > 0) { + + retval = ActualFileRead(infile, chunk, buffer); + + if(retval <= 0) return(i); // Trouble? Stop here. + + if(retval < chunk) chunk = retval; // Short block? Note it. + + + + retval = ActualFileWrite(outfile, chunk, buffer); + + if(retval <= 0) return(i); // Trouble? Stop here. + + i -= retval; + + if(retval < chunk) return(i); // Short block written? Stop here. + + + + chunk = 4096; + + if(i < chunk) chunk = i; + + } // ENDWHILE- Copying a section of file across, one chunk at a time. + + + + return(0); + +} // END INICopyToPos() + + + + + +int INISaveString(char *file, char *section, char *keyword, char *value) { + + char inname[INIMAXLEN+1]; + + char outname[INIMAXLEN+1]; + + int filepos; + + ACTUALHANDLE infile; + + ACTUALHANDLE outfile; + + int i; + + int retval; + + char templine[INIMAXLEN+1]; + + + + if(file == NULL) return(-1); + + if(section == NULL) return(-1); + + if(keyword == NULL) return(-1); + + if(value == NULL) return(-1); + + + +#ifdef VERBOSE_FUNCTION_INI + + PrintLog("CDVDiso ini: SaveString(%s, %s, %s, %s)", + + file, section, keyword, value); + +#endif /* VERBOSE_FUNCTION_INI */ + + + + filepos = INIRemoveExt(file, inname); + + for(i = 0; i <= filepos; i++) outname[i] = inname[i]; + + INIAddInExt(inname, filepos); + + INIAddOutExt(outname, filepos); + + + + filepos = 0; + + infile = ActualFileOpenForRead(inname); + + if(infile == ACTUALHANDLENULL) { + +#ifdef VERBOSE_FUNCTION_INI + + PrintLog("CDVDiso ini: creating new file"); + +#endif /* VERBOSE_FUNCTION_INI */ + + outfile = ActualFileOpenForWrite(inname); + + if(outfile == ACTUALHANDLENULL) return(-1); // Just a bad name? Abort. + + + + sprintf(templine, "[%s]\r\n", section); + + i = 0; + + while((i < INIMAXLEN) && (templine[i] != 0)) i++; + + retval = ActualFileWrite(outfile, i, templine); + + if(retval < i) { + + ActualFileClose(outfile); + + outfile = ACTUALHANDLENULL; + + ActualFileDelete(inname); + + return(-1); + + } // ENDIF- Trouble writing it out? Abort. + + + + sprintf(templine, "%s=%s\r\n", keyword, value); + + i = 0; + + while((i < INIMAXLEN) && (templine[i] != 0)) i++; + + retval = ActualFileWrite(outfile, i, templine); + + ActualFileClose(outfile); + + outfile = ACTUALHANDLENULL; + + if(retval < i) { + + ActualFileDelete(inname); + + return(-1); + + } // ENDIF- Trouble writing it out? Abort. + + return(0); + + } // ENDIF- No input file? Create a brand new .ini file then. + + + + retval = INIFindSection(infile, section); + + if(retval < 0) { + +#ifdef VERBOSE_FUNCTION_INI + + PrintLog("CDVDiso ini: creating new section"); + +#endif /* VERBOSE_FUNCTION_INI */ + + outfile = ActualFileOpenForWrite(outname); + + if(outfile == ACTUALHANDLENULL) { + + ActualFileClose(infile); + + infile = ACTUALHANDLENULL; + + return(-1); + + } // ENDIF- Couldn't open a temp file? Abort + + + + ActualFileSeek(infile, 0); // Move ini to beginning of file... + + INICopy(infile, outfile, 0x0FFFFFFF); // Copy the whole file out... + + + + sprintf(templine, "\r\n[%s]\r\n", section); + + i = 0; + + while((i < INIMAXLEN) && (templine[i] != 0)) i++; + + retval = ActualFileWrite(outfile, i, templine); + + if(retval < i) { + + ActualFileClose(infile); + + infile = ACTUALHANDLENULL; + + ActualFileClose(outfile); + + outfile = ACTUALHANDLENULL; + + ActualFileDelete(outname); + + return(-1); + + } // ENDIF- Trouble writing it out? Abort. + + + + sprintf(templine, "%s=%s\r\n", keyword, value); + + i = 0; + + while((i < INIMAXLEN) && (templine[i] != 0)) i++; + + retval = ActualFileWrite(outfile, i, templine); + + ActualFileClose(infile); + + infile = ACTUALHANDLENULL; + + ActualFileClose(outfile); + + outfile = ACTUALHANDLENULL; + + if(retval < i) { + + ActualFileDelete(outname); + + return(-1); + + } // ENDIF- Trouble writing it out? Abort. + + + + ActualFileDelete(inname); + + ActualFileRename(outname, inname); + + return(0); + + } // ENDIF- Couldn't find the section? Make a new one! + + + + filepos = retval; + + ActualFileSeek(infile, filepos); + + filepos += INIReadLine(infile, templine); // Get section line's byte count + + + + retval = INIFindKeyword(infile, keyword, NULL); + + if(retval < 0) { + +#ifdef VERBOSE_FUNCTION_INI + + PrintLog("CDVDiso ini: creating new keyword"); + +#endif /* VERBOSE_FUNCTION_INI */ + + ActualFileSeek(infile, filepos); + + retval = INIReadLine(infile, templine); + + i = 0; + + while((i < INIMAXLEN) && (templine[i] != 0) && (templine[i] != '=')) i++; + + while((retval > 0) && (templine[i] == '=')) { + + filepos += retval; + + retval = INIReadLine(infile, templine); + + i = 0; + + while((i < INIMAXLEN) && (templine[i] != 0) && (templine[i] != '=')) i++; + + } // ENDWHILE- skimming to the bottom of the section + + + + outfile = ActualFileOpenForWrite(outname); + + if(outfile == ACTUALHANDLENULL) { + + ActualFileClose(infile); + + infile = ACTUALHANDLENULL; + + return(-1); + + } // ENDIF- Couldn't open a temp file? Abort + + + + ActualFileSeek(infile, 0); + + retval = INICopy(infile, outfile, filepos); + + if(retval > 0) { + + ActualFileClose(infile); + + infile = ACTUALHANDLENULL; + + ActualFileClose(outfile); + + outfile = ACTUALHANDLENULL; + + ActualFileDelete(outname); + + return(-1); + + } // ENDIF- Trouble writing everything up to keyword? Abort. + + + + sprintf(templine, "%s=%s\r\n", keyword, value); + + i = 0; + + while((i < INIMAXLEN) && (templine[i] != 0)) i++; + + retval = ActualFileWrite(outfile, i, templine); + + if(retval < i) { + + ActualFileClose(infile); + + infile = ACTUALHANDLENULL; + + ActualFileClose(outfile); + + outfile = ACTUALHANDLENULL; + + ActualFileDelete(outname); + + return(-1); + + } // ENDIF- Trouble writing it out? Abort. + + + + } else { + +#ifdef VERBOSE_FUNCTION_INI + + PrintLog("CDVDiso ini: replacing keyword"); + +#endif /* VERBOSE_FUNCTION_INI */ + + filepos += retval; // Position just before old version of keyword + + + + outfile = ActualFileOpenForWrite(outname); + + if(outfile == ACTUALHANDLENULL) { + + ActualFileClose(infile); + + infile = ACTUALHANDLENULL; + + return(-1); + + } // ENDIF- Couldn't open a temp file? Abort + + + + ActualFileSeek(infile, 0); + + retval = INICopy(infile, outfile, filepos); + + if(retval > 0) { + + ActualFileClose(infile); + + infile = ACTUALHANDLENULL; + + ActualFileClose(outfile); + + outfile = ACTUALHANDLENULL; + + ActualFileDelete(outname); + + return(-1); + + } // ENDIF- Trouble writing everything up to keyword? Abort. + + + + INIReadLine(infile, templine); // Read past old keyword/value... + + + + // Replace with new value + + sprintf(templine, "%s=%s\r\n", keyword, value); + + i = 0; + + while((i < INIMAXLEN) && (templine[i] != 0)) i++; + + retval = ActualFileWrite(outfile, i, templine); + + if(retval < i) { + + ActualFileClose(infile); + + infile = ACTUALHANDLENULL; + + ActualFileClose(outfile); + + outfile = ACTUALHANDLENULL; + + ActualFileDelete(outname); + + return(-1); + + } // ENDIF- Trouble writing it out? Abort. + + } // ENDIF- Need to add a new keyword? + + + + INICopy(infile, outfile, 0xFFFFFFF); // Write out rest of file + + ActualFileClose(infile); + + infile = ACTUALHANDLENULL; + + ActualFileClose(outfile); + + outfile = ACTUALHANDLENULL; + + ActualFileDelete(inname); + + ActualFileRename(outname, inname); + + return(0); + +} // END INISaveString() + + + + + +int INILoadString(char *file, char *section, char *keyword, char *buffer) { + + char inname[INIMAXLEN+1]; + + int filepos; + + ACTUALHANDLE infile; + + int retval; + + + + if(file == NULL) return(-1); + + if(section == NULL) return(-1); + + if(keyword == NULL) return(-1); + + if(buffer == NULL) return(-1); + + + +#ifdef VERBOSE_FUNCTION_INI + + PrintLog("CDVDiso ini: LoadString(%s, %s, %s)", + + file, section, keyword); + +#endif /* VERBOSE_FUNCTION_INI */ + + + + filepos = INIRemoveExt(file, inname); + + INIAddInExt(inname, filepos); + + + + filepos = 0; + + infile = ActualFileOpenForRead(inname); + + if(infile == ACTUALHANDLENULL) return(-1); + + + + retval = INIFindSection(infile, section); + + if(retval < 0) { + + ActualFileClose(infile); + + infile = ACTUALHANDLENULL; + + return(-1); + + } // ENDIF- Didn't find it? Abort. + + + + retval = INIFindKeyword(infile, keyword, buffer); + + if(retval < 0) { + + ActualFileClose(infile); + + infile = ACTUALHANDLENULL; + + return(-1); + + } // ENDIF- Didn't find it? Abort. + + + + ActualFileClose(infile); + + infile = ACTUALHANDLENULL; + + return(0); + +} // END INILoadString() + + + + + +int INIRemove(char *file, char *section, char *keyword) { + + char inname[INIMAXLEN+1]; + + char outname[INIMAXLEN+1]; + + int filepos; + + ACTUALHANDLE infile; + + ACTUALHANDLE outfile; + + char templine[INIMAXLEN+1]; + + int i; + + int retval; + + + + if(file == NULL) return(-1); + + if(section == NULL) return(-1); + + + +#ifdef VERBOSE_FUNCTION_INI + + PrintLog("CDVDiso ini: Remove(%s, %s, %s)", + + file, section, keyword); + +#endif /* VERBOSE_FUNCTION_INI */ + + + + filepos = INIRemoveExt(file, inname); + + for(i = 0; i <= filepos; i++) outname[i] = inname[i]; + + INIAddInExt(inname, filepos); + + INIAddOutExt(outname, filepos); + + + + infile = ActualFileOpenForRead(inname); + + if(infile == ACTUALHANDLENULL) return(-1); + + + + retval = INIFindSection(infile, section); + + if(retval == -1) { + + ActualFileClose(infile); + + infile = ACTUALHANDLENULL; + + return(-1); + + } // ENDIF- Couldn't even find the section? Abort + + + + filepos = retval; + + if(keyword == NULL) { + +#ifdef VERBOSE_FUNCTION_INI + + PrintLog("CDVDiso ini: removing section"); + +#endif /* VERBOSE_FUNCTION_INI */ + + outfile = ActualFileOpenForWrite(outname); + + if(outfile == ACTUALHANDLENULL) { + + ActualFileClose(infile); + + infile = ACTUALHANDLENULL; + + return(-1); + + } // ENDIF- Couldn't open a temp file? Abort + + + + ActualFileSeek(infile, 0); + + retval = INICopy(infile, outfile, filepos); + + if(retval > 0) { + + ActualFileClose(infile); + + infile = ACTUALHANDLENULL; + + ActualFileClose(outfile); + + outfile = ACTUALHANDLENULL; + + ActualFileDelete(outname); + + return(-1); + + } // ENDIF- Trouble writing everything up to the section? Abort. + + + + templine[0] = 0; + + retval = 1; + + while((retval > 0) && (templine[0] != '[')) { + + retval = INIReadLine(infile, templine); + + } // ENDWHILE- Read to the start of the next section... or EOF. + + + + if(templine[0] == '[') { + + i = 0; + + while((i < INIMAXLEN) && (templine[i] != 0)) i++; + + retval = ActualFileWrite(outfile, i, templine); + + if(retval < i) { + + ActualFileClose(infile); + + infile = ACTUALHANDLENULL; + + ActualFileClose(outfile); + + outfile = ACTUALHANDLENULL; + + ActualFileDelete(outname); + + return(-1); + + } // ENDIF- Trouble writing it out? Abort. + + } // ENDIF- Are there other sections after this one? Save them then. + + + + } else { + + filepos = retval; + + ActualFileSeek(infile, filepos); + + filepos += INIReadLine(infile, templine); // Get section line's byte count + + + + retval = INIFindKeyword(infile, keyword, NULL); + + if(retval == -1) { + + ActualFileClose(infile); + + infile = ACTUALHANDLENULL; + + return(-1); + + } // ENDIF- Couldn't find the keyword? Abort + + filepos += retval; + + + +#ifdef VERBOSE_FUNCTION_INI + + PrintLog("CDVDiso ini: removing keyword"); + +#endif /* VERBOSE_FUNCTION_INI */ + + outfile = ActualFileOpenForWrite(outname); + + if(outfile == ACTUALHANDLENULL) { + + ActualFileClose(infile); + + infile = ACTUALHANDLENULL; + + return(-1); + + } // ENDIF- Couldn't open a temp file? Abort + + + + ActualFileSeek(infile, 0); + + retval = INICopy(infile, outfile, filepos); + + if(retval > 0) { + + ActualFileClose(infile); + + infile = ACTUALHANDLENULL; + + ActualFileClose(outfile); + + outfile = ACTUALHANDLENULL; + + ActualFileDelete(outname); + + return(-1); + + } // ENDIF- Trouble writing everything up to keyword? Abort. + + + + INIReadLine(infile, templine); // Read (and discard) the keyword line + + } // ENDIF- Wipe out the whole section? Or just a keyword? + + + + INICopy(infile, outfile, 0xFFFFFFF); // Write out rest of file + + ActualFileClose(infile); + + infile = ACTUALHANDLENULL; + + ActualFileClose(outfile); + + outfile = ACTUALHANDLENULL; + + ActualFileDelete(inname); + + ActualFileRename(outname, inname); + + return(0); + +} // END INIRemove() + + + + + +int INISaveUInt(char *file, char *section, char *keyword, unsigned int value) { + + char numvalue[INIMAXLEN+1]; + + + + sprintf(numvalue, "%u", value); + + return(INISaveString(file, section, keyword, numvalue)); + +} // END INISaveUInt() + + + + + +int INILoadUInt(char *file, char *section, char *keyword, unsigned int *buffer) { + + char numvalue[INIMAXLEN+1]; + + int retval; + + unsigned int value; + + // unsigned int sign; // Not needed in unsigned numbers + + int pos; + + + + if(buffer == NULL) return(-1); + + *(buffer) = 0; + + + + retval = INILoadString(file, section, keyword, numvalue); + + if(retval < 0) return(retval); + + + + value = 0; + + // sign = 1; // Start positive + + pos = 0; + + + + // Note: skip leading spaces? (Shouldn't have to, I hope) + + + + // if(numvalue[pos] == '-') { + + // pos++; + + // sign = -1; + + // } // ENDIF- Negative sign check + + + + while((pos < INIMAXLEN) && (numvalue[pos] != 0)) { + + if(value > (0xFFFFFFFF / 10)) return(-1); // Overflow? + + + + if((numvalue[pos] >= '0') && (numvalue[pos] <= '9')) { + + value *= 10; + + value += numvalue[pos] - '0'; + + pos++; + + } else { + + numvalue[pos] = 0; + + } // ENDIF- Add a digit in? Or stop searching for digits? + + } // ENDWHILE- Adding digits of info to our ever-increasing value + + + + // value *= sign + + *(buffer) = value; + + return(0); + +} // END INILoadUInt() + diff --git a/plugins/CDVDlinuz/Src/version.c b/plugins/CDVDlinuz/Src/version.c index f765058b8b..f4c68e5172 100644 --- a/plugins/CDVDlinuz/Src/version.c +++ b/plugins/CDVDlinuz/Src/version.c @@ -1,72 +1,72 @@ -/* version.c - - * Copyright (C) 2002-2005 PCSX2 Team - - * - - * This program is free software; you can redistribute it and/or modify - - * it under the terms of the GNU General Public License as published by - - * the Free Software Foundation; either version 2 of the License, or - - * (at your option) any later version. - - * - - * This program 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 this program; if not, write to the Free Software - - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - * - - * PCSX2 members can be contacted through their website at www.pcsx2.net. - - */ - - - - - -#ifndef __LINUX__ - -#ifdef __linux__ - -#define __LINUX__ - -#endif /* __linux__ */ - -#endif /* No __LINUX__ */ - - - -#define CDVDdefs - -#include "PS2Edefs.h" - - - - - -char *libname = "EFP polling CDVD Driver"; - - - -const unsigned char version = PS2E_CDVD_VERSION; - -const unsigned char revision = 0; - -const unsigned char build = 4; - +/* version.c + + * Copyright (C) 2002-2005 PCSX2 Team + + * + + * This program is free software; you can redistribute it and/or modify + + * it under the terms of the GNU General Public License as published by + + * the Free Software Foundation; either version 2 of the License, or + + * (at your option) any later version. + + * + + * This program 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 this program; if not, write to the Free Software + + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + * + + * PCSX2 members can be contacted through their website at www.pcsx2.net. + + */ + + + + + +#ifndef __LINUX__ + +#ifdef __linux__ + +#define __LINUX__ + +#endif /* __linux__ */ + +#endif /* No __LINUX__ */ + + + +#define CDVDdefs + +#include "PS2Edefs.h" + + + + + +char *libname = "EFP polling CDVD Driver"; + + + +const unsigned char version = PS2E_CDVD_VERSION; + +const unsigned char revision = 0; + +const unsigned char build = 4; + diff --git a/plugins/CDVDolio/CDVDDialog.cpp b/plugins/CDVDolio/CDVDDialog.cpp index b83f02a28c..765bfd8bf5 100644 --- a/plugins/CDVDolio/CDVDDialog.cpp +++ b/plugins/CDVDolio/CDVDDialog.cpp @@ -1,177 +1,177 @@ -/* - * Copyright (C) 2007-2009 Gabest - * http://www.gabest.org - * - * This Program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This Program 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 GNU Make; see the file COPYING. If not, write to - * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - * http://www.gnu.org/copyleft/gpl.html - * - */ - -#include "StdAfx.h" -#include "CDVD.h" -#include "CDVDDialog.h" - -CDVDDialog::CDVDDialog(UINT id) - : m_id(id) - , m_hWnd(NULL) -{ -} - -INT_PTR CDVDDialog::DoModal() -{ - return DialogBoxParam(theApp.GetModuleHandle(), MAKEINTRESOURCE(m_id), NULL, DialogProc, (LPARAM)this); -} - -INT_PTR CALLBACK CDVDDialog::DialogProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) -{ - CDVDDialog* dlg = NULL; - - if(message == WM_INITDIALOG) - { - dlg = (CDVDDialog*)lParam; - SetWindowLongPtr(hWnd, GWL_USERDATA, (LONG_PTR)dlg); - dlg->m_hWnd = hWnd; - - MONITORINFO mi; - mi.cbSize = sizeof(mi); - GetMonitorInfo(MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST), &mi); - - RECT r; - GetWindowRect(hWnd, &r); - - int x = (mi.rcWork.left + mi.rcWork.right - (r.right - r.left)) / 2; - int y = (mi.rcWork.top + mi.rcWork.bottom - (r.bottom - r.top)) / 2; - - SetWindowPos(hWnd, NULL, x, y, -1, -1, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); - - dlg->OnInit(); - - return true; - } - - dlg = (CDVDDialog*)GetWindowLongPtr(hWnd, GWL_USERDATA); - - return dlg != NULL ? dlg->OnMessage(message, wParam, lParam) : FALSE; -} - -bool CDVDDialog::OnMessage(UINT message, WPARAM wParam, LPARAM lParam) -{ - return message == WM_COMMAND ? OnCommand((HWND)lParam, LOWORD(wParam), HIWORD(wParam)) : false; -} - -bool CDVDDialog::OnCommand(HWND hWnd, UINT id, UINT code) -{ - if(id == IDOK || id == IDCANCEL) - { - EndDialog(m_hWnd, id); - - return true; - } - - return false; -} - -string CDVDDialog::GetText(UINT id) -{ - string s; - - char* buff = NULL; - - for(int size = 256, limit = 65536; size < limit; size <<= 1) - { - buff = new char[size]; - - if(GetDlgItemText(m_hWnd, id, buff, size)) - { - s = buff; - size = limit; - } - - delete [] buff; - } - - return s; -} - -int CDVDDialog::GetTextAsInt(UINT id) -{ - return atoi(GetText(id).c_str()); -} - -void CDVDDialog::SetText(UINT id, const char* str) -{ - SetDlgItemText(m_hWnd, id, str); -} - -void CDVDDialog::SetTextAsInt(UINT id, int i) -{ - char buff[32] = {0}; - itoa(i, buff, 10); - SetText(id, buff); -} - -void CDVDDialog::ComboBoxInit(UINT id, const CDVDSetting* settings, int count, uint32 selid, uint32 maxid) -{ - HWND hWnd = GetDlgItem(m_hWnd, id); - - SendMessage(hWnd, CB_RESETCONTENT, 0, 0); - - for(int i = 0; i < count; i++) - { - if(settings[i].id <= maxid) - { - string str = settings[i].name; - - if(!settings[i].note.empty()) - { - str = str + " (" + settings[i].note + ")"; - } - - ComboBoxAppend(id, str.c_str(), (LPARAM)settings[i].id, settings[i].id == selid); - } - } -} - -int CDVDDialog::ComboBoxAppend(UINT id, const char* str, LPARAM data, bool select) -{ - HWND hWnd = GetDlgItem(m_hWnd, id); - - int item = (int)SendMessage(hWnd, CB_ADDSTRING, 0, (LPARAM)str); - - SendMessage(hWnd, CB_SETITEMDATA, item, (LPARAM)data); - - if(select) - { - SendMessage(hWnd, CB_SETCURSEL, item, 0); - } - - return item; -} - -bool CDVDDialog::ComboBoxGetSelData(UINT id, INT_PTR& data) -{ - HWND hWnd = GetDlgItem(m_hWnd, id); - - int item = SendMessage(hWnd, CB_GETCURSEL, 0, 0); - - if(item >= 0) - { - data = SendMessage(hWnd, CB_GETITEMDATA, item, 0); - - return true; - } - - return false; -} +/* + * Copyright (C) 2007-2009 Gabest + * http://www.gabest.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program 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 GNU Make; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "StdAfx.h" +#include "CDVD.h" +#include "CDVDDialog.h" + +CDVDDialog::CDVDDialog(UINT id) + : m_id(id) + , m_hWnd(NULL) +{ +} + +INT_PTR CDVDDialog::DoModal() +{ + return DialogBoxParam(theApp.GetModuleHandle(), MAKEINTRESOURCE(m_id), NULL, DialogProc, (LPARAM)this); +} + +INT_PTR CALLBACK CDVDDialog::DialogProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + CDVDDialog* dlg = NULL; + + if(message == WM_INITDIALOG) + { + dlg = (CDVDDialog*)lParam; + SetWindowLongPtr(hWnd, GWL_USERDATA, (LONG_PTR)dlg); + dlg->m_hWnd = hWnd; + + MONITORINFO mi; + mi.cbSize = sizeof(mi); + GetMonitorInfo(MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST), &mi); + + RECT r; + GetWindowRect(hWnd, &r); + + int x = (mi.rcWork.left + mi.rcWork.right - (r.right - r.left)) / 2; + int y = (mi.rcWork.top + mi.rcWork.bottom - (r.bottom - r.top)) / 2; + + SetWindowPos(hWnd, NULL, x, y, -1, -1, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); + + dlg->OnInit(); + + return true; + } + + dlg = (CDVDDialog*)GetWindowLongPtr(hWnd, GWL_USERDATA); + + return dlg != NULL ? dlg->OnMessage(message, wParam, lParam) : FALSE; +} + +bool CDVDDialog::OnMessage(UINT message, WPARAM wParam, LPARAM lParam) +{ + return message == WM_COMMAND ? OnCommand((HWND)lParam, LOWORD(wParam), HIWORD(wParam)) : false; +} + +bool CDVDDialog::OnCommand(HWND hWnd, UINT id, UINT code) +{ + if(id == IDOK || id == IDCANCEL) + { + EndDialog(m_hWnd, id); + + return true; + } + + return false; +} + +string CDVDDialog::GetText(UINT id) +{ + string s; + + char* buff = NULL; + + for(int size = 256, limit = 65536; size < limit; size <<= 1) + { + buff = new char[size]; + + if(GetDlgItemText(m_hWnd, id, buff, size)) + { + s = buff; + size = limit; + } + + delete [] buff; + } + + return s; +} + +int CDVDDialog::GetTextAsInt(UINT id) +{ + return atoi(GetText(id).c_str()); +} + +void CDVDDialog::SetText(UINT id, const char* str) +{ + SetDlgItemText(m_hWnd, id, str); +} + +void CDVDDialog::SetTextAsInt(UINT id, int i) +{ + char buff[32] = {0}; + itoa(i, buff, 10); + SetText(id, buff); +} + +void CDVDDialog::ComboBoxInit(UINT id, const CDVDSetting* settings, int count, uint32 selid, uint32 maxid) +{ + HWND hWnd = GetDlgItem(m_hWnd, id); + + SendMessage(hWnd, CB_RESETCONTENT, 0, 0); + + for(int i = 0; i < count; i++) + { + if(settings[i].id <= maxid) + { + string str = settings[i].name; + + if(!settings[i].note.empty()) + { + str = str + " (" + settings[i].note + ")"; + } + + ComboBoxAppend(id, str.c_str(), (LPARAM)settings[i].id, settings[i].id == selid); + } + } +} + +int CDVDDialog::ComboBoxAppend(UINT id, const char* str, LPARAM data, bool select) +{ + HWND hWnd = GetDlgItem(m_hWnd, id); + + int item = (int)SendMessage(hWnd, CB_ADDSTRING, 0, (LPARAM)str); + + SendMessage(hWnd, CB_SETITEMDATA, item, (LPARAM)data); + + if(select) + { + SendMessage(hWnd, CB_SETCURSEL, item, 0); + } + + return item; +} + +bool CDVDDialog::ComboBoxGetSelData(UINT id, INT_PTR& data) +{ + HWND hWnd = GetDlgItem(m_hWnd, id); + + int item = SendMessage(hWnd, CB_GETCURSEL, 0, 0); + + if(item >= 0) + { + data = SendMessage(hWnd, CB_GETITEMDATA, item, 0); + + return true; + } + + return false; +} diff --git a/plugins/CDVDolio/CDVDDialog.h b/plugins/CDVDolio/CDVDDialog.h index 0abd02d4df..90e27f415d 100644 --- a/plugins/CDVDolio/CDVDDialog.h +++ b/plugins/CDVDolio/CDVDDialog.h @@ -1,59 +1,59 @@ -/* - * Copyright (C) 2007-2009 Gabest - * http://www.gabest.org - * - * This Program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This Program 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 GNU Make; see the file COPYING. If not, write to - * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - * http://www.gnu.org/copyleft/gpl.html - * - */ - -#pragma once - -struct CDVDSetting -{ - uint32 id; - string name; - string note; -}; - -class CDVDDialog -{ - int m_id; - - static INT_PTR CALLBACK DialogProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); - -protected: - HWND m_hWnd; - - virtual void OnInit() {} - virtual bool OnMessage(UINT message, WPARAM wParam, LPARAM lParam); - virtual bool OnCommand(HWND hWnd, UINT id, UINT code); - -public: - CDVDDialog (UINT id); - virtual ~CDVDDialog () {} - - INT_PTR DoModal(); - - string GetText(UINT id); - int GetTextAsInt(UINT id); - - void SetText(UINT id, const char* str); - void SetTextAsInt(UINT id, int i); - - void ComboBoxInit(UINT id, const CDVDSetting* settings, int count, uint32 selid, uint32 maxid = ~0); - int ComboBoxAppend(UINT id, const char* str, LPARAM data = 0, bool select = false); - bool ComboBoxGetSelData(UINT id, INT_PTR& data); -}; +/* + * Copyright (C) 2007-2009 Gabest + * http://www.gabest.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program 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 GNU Make; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#pragma once + +struct CDVDSetting +{ + uint32 id; + string name; + string note; +}; + +class CDVDDialog +{ + int m_id; + + static INT_PTR CALLBACK DialogProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); + +protected: + HWND m_hWnd; + + virtual void OnInit() {} + virtual bool OnMessage(UINT message, WPARAM wParam, LPARAM lParam); + virtual bool OnCommand(HWND hWnd, UINT id, UINT code); + +public: + CDVDDialog (UINT id); + virtual ~CDVDDialog () {} + + INT_PTR DoModal(); + + string GetText(UINT id); + int GetTextAsInt(UINT id); + + void SetText(UINT id, const char* str); + void SetTextAsInt(UINT id, int i); + + void ComboBoxInit(UINT id, const CDVDSetting* settings, int count, uint32 selid, uint32 maxid = ~0); + int ComboBoxAppend(UINT id, const char* str, LPARAM data = 0, bool select = false); + bool ComboBoxGetSelData(UINT id, INT_PTR& data); +}; diff --git a/plugins/CDVDpeops/libiso.h b/plugins/CDVDpeops/libiso.h index 640cbf3322..2423aecb94 100644 --- a/plugins/CDVDpeops/libiso.h +++ b/plugins/CDVDpeops/libiso.h @@ -1,59 +1,59 @@ -#ifndef __LIBISO_H__ -#define __LIBISO_H__ - -#ifdef __MSCW32__ -#pragma warning(disable:4018) -#endif - -#define CDVDdefs -#include "PS2Etypes.h" -#include "PS2Edefs.h" - -#define ISOTYPE_ILLEGAL 0 -#define ISOTYPE_CD 1 -#define ISOTYPE_DVD 2 -#define ISOTYPE_AUDIO 3 - -#define ISOFLAGS_Z 0x1 -#define ISOFLAGS_Z2 0x2 -#define ISOFLAGS_BLOCKDUMP 0x4 - -#define CD_FRAMESIZE_RAW 2352 -#define DATA_SIZE (CD_FRAMESIZE_RAW-12) - -#define itob(i) ((i)/10*16 + (i)%10) /* u_char to BCD */ -#define btoi(b) ((b)/16*10 + (b)%16) /* BCD to u_char */ - -typedef struct { - char filename[256]; - u32 type; - u32 flags; - u32 offset; - u32 blockofs; - u32 blocksize; - u32 blocks; - void *handle; - void *htable; - char *Ztable; - u32 *dtable; - int dtablesize; - char buffer[CD_FRAMESIZE_RAW * 10]; -} isoFile; - - -isoFile *isoOpen(const char *filename); -isoFile *isoCreate(const char *filename, int mode); -int isoSetFormat(isoFile *iso, int blockofs, int blocksize, int blocks); -int isoDetect(isoFile *iso); -int isoReadBlock(isoFile *iso, char *dst, int lsn); -int isoWriteBlock(isoFile *iso, char *src, int lsn); -void isoClose(isoFile *iso); - -void *_openfile(const char *filename, int flags); -u64 _tellfile(void *handle); -int _seekfile(void *handle, u64 offset, int whence); -int _readfile(void *handle, void *dst, int size); -int _writefile(void *handle, void *src, int size); -void _closefile(void *handle); - -#endif /* __LIBISO_H__ */ +#ifndef __LIBISO_H__ +#define __LIBISO_H__ + +#ifdef __MSCW32__ +#pragma warning(disable:4018) +#endif + +#define CDVDdefs +#include "PS2Etypes.h" +#include "PS2Edefs.h" + +#define ISOTYPE_ILLEGAL 0 +#define ISOTYPE_CD 1 +#define ISOTYPE_DVD 2 +#define ISOTYPE_AUDIO 3 + +#define ISOFLAGS_Z 0x1 +#define ISOFLAGS_Z2 0x2 +#define ISOFLAGS_BLOCKDUMP 0x4 + +#define CD_FRAMESIZE_RAW 2352 +#define DATA_SIZE (CD_FRAMESIZE_RAW-12) + +#define itob(i) ((i)/10*16 + (i)%10) /* u_char to BCD */ +#define btoi(b) ((b)/16*10 + (b)%16) /* BCD to u_char */ + +typedef struct { + char filename[256]; + u32 type; + u32 flags; + u32 offset; + u32 blockofs; + u32 blocksize; + u32 blocks; + void *handle; + void *htable; + char *Ztable; + u32 *dtable; + int dtablesize; + char buffer[CD_FRAMESIZE_RAW * 10]; +} isoFile; + + +isoFile *isoOpen(const char *filename); +isoFile *isoCreate(const char *filename, int mode); +int isoSetFormat(isoFile *iso, int blockofs, int blocksize, int blocks); +int isoDetect(isoFile *iso); +int isoReadBlock(isoFile *iso, char *dst, int lsn); +int isoWriteBlock(isoFile *iso, char *src, int lsn); +void isoClose(isoFile *iso); + +void *_openfile(const char *filename, int flags); +u64 _tellfile(void *handle); +int _seekfile(void *handle, u64 offset, int whence); +int _readfile(void *handle, void *dst, int size); +int _writefile(void *handle, void *src, int size); +void _closefile(void *handle); + +#endif /* __LIBISO_H__ */ diff --git a/plugins/FWnull/Windows/Config.cpp b/plugins/FWnull/Windows/Config.cpp index 62db91dd61..aee13009b9 100644 --- a/plugins/FWnull/Windows/Config.cpp +++ b/plugins/FWnull/Windows/Config.cpp @@ -1,51 +1,51 @@ -#include - -#include "../FW.h" - -extern HINSTANCE hInst; -void SaveConfig() -{ - - Config *Conf1 = &conf; - char *szTemp; - char szIniFile[256], szValue[256]; - - GetModuleFileName(GetModuleHandle((LPCSTR)hInst), szIniFile, 256); - szTemp = strrchr(szIniFile, '\\'); - - if(!szTemp) return; - strcpy(szTemp, "\\inis\\fwnull.ini"); - sprintf(szValue,"%u",Conf1->Log); - WritePrivateProfileString("Interface", "Logging",szValue,szIniFile); - -} - -void LoadConfig() { - FILE *fp; - - - Config *Conf1 = &conf; - char *szTemp; - char szIniFile[256], szValue[256]; - - GetModuleFileName(GetModuleHandle((LPCSTR)hInst), szIniFile, 256); - szTemp = strrchr(szIniFile, '\\'); - - if(!szTemp) return ; - strcpy(szTemp, "\\inis\\fwnull.ini"); - fp=fopen("inis\\fwnull.ini","rt");//check if firewirenull.ini really exists - if (!fp) - { - CreateDirectory("inis",NULL); - memset(&conf, 0, sizeof(conf)); - conf.Log = 0;//default value - SaveConfig();//save and return - return ; - } - fclose(fp); - GetPrivateProfileString("Interface", "Logging", NULL, szValue, 20, szIniFile); - Conf1->Log = strtoul(szValue, NULL, 10); - return ; - -} - +#include + +#include "../FW.h" + +extern HINSTANCE hInst; +void SaveConfig() +{ + + Config *Conf1 = &conf; + char *szTemp; + char szIniFile[256], szValue[256]; + + GetModuleFileName(GetModuleHandle((LPCSTR)hInst), szIniFile, 256); + szTemp = strrchr(szIniFile, '\\'); + + if(!szTemp) return; + strcpy(szTemp, "\\inis\\fwnull.ini"); + sprintf(szValue,"%u",Conf1->Log); + WritePrivateProfileString("Interface", "Logging",szValue,szIniFile); + +} + +void LoadConfig() { + FILE *fp; + + + Config *Conf1 = &conf; + char *szTemp; + char szIniFile[256], szValue[256]; + + GetModuleFileName(GetModuleHandle((LPCSTR)hInst), szIniFile, 256); + szTemp = strrchr(szIniFile, '\\'); + + if(!szTemp) return ; + strcpy(szTemp, "\\inis\\fwnull.ini"); + fp=fopen("inis\\fwnull.ini","rt");//check if firewirenull.ini really exists + if (!fp) + { + CreateDirectory("inis",NULL); + memset(&conf, 0, sizeof(conf)); + conf.Log = 0;//default value + SaveConfig();//save and return + return ; + } + fclose(fp); + GetPrivateProfileString("Interface", "Logging", NULL, szValue, 20, szIniFile); + Conf1->Log = strtoul(szValue, NULL, 10); + return ; + +} + diff --git a/plugins/FWnull/Windows/Win32.cpp b/plugins/FWnull/Windows/Win32.cpp index 80a47c50f6..3506132300 100644 --- a/plugins/FWnull/Windows/Win32.cpp +++ b/plugins/FWnull/Windows/Win32.cpp @@ -1,81 +1,81 @@ -#include -#include -#include - - -#include "resource.h" -#include "../FW.h" - -HINSTANCE hInst; - -void SysMessage(char *fmt, ...) { - va_list list; - char tmp[512]; - - va_start(list,fmt); - vsprintf(tmp,fmt,list); - va_end(list); - MessageBox(GetActiveWindow(), tmp, "FW Plugin Msg", MB_SETFOREGROUND | MB_OK); -} - -BOOL CALLBACK ConfigureDlgProc(HWND hW, UINT uMsg, WPARAM wParam, LPARAM lParam) { - - switch(uMsg) { - case WM_INITDIALOG: - LoadConfig(); - if (conf.Log) CheckDlgButton(hW, IDC_LOGGING, TRUE); - return TRUE; - - case WM_COMMAND: - switch(LOWORD(wParam)) { - case IDCANCEL: - EndDialog(hW, TRUE); - return TRUE; - case IDOK: - if (IsDlgButtonChecked(hW, IDC_LOGGING)) - conf.Log = 1; - else conf.Log = 0; - SaveConfig(); - EndDialog(hW, FALSE); - return TRUE; - } - } - return FALSE; -} - -BOOL CALLBACK AboutDlgProc(HWND hW, UINT uMsg, WPARAM wParam, LPARAM lParam) { - switch(uMsg) { - case WM_INITDIALOG: - return TRUE; - - case WM_COMMAND: - switch(LOWORD(wParam)) { - case IDOK: - EndDialog(hW, FALSE); - return TRUE; - } - } - return FALSE; -} - -void CALLBACK FWconfigure() { - DialogBox(hInst, - MAKEINTRESOURCE(IDD_CONFIG), - GetActiveWindow(), - (DLGPROC)ConfigureDlgProc); -} - -void CALLBACK FWabout() { - DialogBox(hInst, - MAKEINTRESOURCE(IDD_ABOUT), - GetActiveWindow(), - (DLGPROC)AboutDlgProc); -} - -BOOL APIENTRY DllMain(HANDLE hModule, // DLL INIT - DWORD dwReason, - LPVOID lpReserved) { - hInst = (HINSTANCE)hModule; - return TRUE; // very quick :) -} - +#include +#include +#include + + +#include "resource.h" +#include "../FW.h" + +HINSTANCE hInst; + +void SysMessage(char *fmt, ...) { + va_list list; + char tmp[512]; + + va_start(list,fmt); + vsprintf(tmp,fmt,list); + va_end(list); + MessageBox(GetActiveWindow(), tmp, "FW Plugin Msg", MB_SETFOREGROUND | MB_OK); +} + +BOOL CALLBACK ConfigureDlgProc(HWND hW, UINT uMsg, WPARAM wParam, LPARAM lParam) { + + switch(uMsg) { + case WM_INITDIALOG: + LoadConfig(); + if (conf.Log) CheckDlgButton(hW, IDC_LOGGING, TRUE); + return TRUE; + + case WM_COMMAND: + switch(LOWORD(wParam)) { + case IDCANCEL: + EndDialog(hW, TRUE); + return TRUE; + case IDOK: + if (IsDlgButtonChecked(hW, IDC_LOGGING)) + conf.Log = 1; + else conf.Log = 0; + SaveConfig(); + EndDialog(hW, FALSE); + return TRUE; + } + } + return FALSE; +} + +BOOL CALLBACK AboutDlgProc(HWND hW, UINT uMsg, WPARAM wParam, LPARAM lParam) { + switch(uMsg) { + case WM_INITDIALOG: + return TRUE; + + case WM_COMMAND: + switch(LOWORD(wParam)) { + case IDOK: + EndDialog(hW, FALSE); + return TRUE; + } + } + return FALSE; +} + +void CALLBACK FWconfigure() { + DialogBox(hInst, + MAKEINTRESOURCE(IDD_CONFIG), + GetActiveWindow(), + (DLGPROC)ConfigureDlgProc); +} + +void CALLBACK FWabout() { + DialogBox(hInst, + MAKEINTRESOURCE(IDD_ABOUT), + GetActiveWindow(), + (DLGPROC)AboutDlgProc); +} + +BOOL APIENTRY DllMain(HANDLE hModule, // DLL INIT + DWORD dwReason, + LPVOID lpReserved) { + hInst = (HINSTANCE)hModule; + return TRUE; // very quick :) +} + diff --git a/plugins/GSdx/GSCodeBuffer.cpp b/plugins/GSdx/GSCodeBuffer.cpp index 83b0f45f61..78e08da67e 100644 --- a/plugins/GSdx/GSCodeBuffer.cpp +++ b/plugins/GSdx/GSCodeBuffer.cpp @@ -1,71 +1,71 @@ -/* - * Copyright (C) 2007-2009 Gabest - * http://www.gabest.org - * - * This Program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This Program 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 GNU Make; see the file COPYING. If not, write to - * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - * http://www.gnu.org/copyleft/gpl.html - * - */ - -#include "StdAfx.h" -#include "GSCodeBuffer.h" - -GSCodeBuffer::GSCodeBuffer(size_t blocksize) - : m_ptr(NULL) - , m_blocksize(blocksize) - , m_pos(0) - , m_reserved(0) -{ -} - -GSCodeBuffer::~GSCodeBuffer() -{ - for(list::iterator i = m_buffers.begin(); i != m_buffers.end(); i++) - { - VirtualFree(*i, 0, MEM_RELEASE); - } -} - -void* GSCodeBuffer::GetBuffer(size_t size) -{ - ASSERT(size < m_blocksize); - ASSERT(m_reserved == 0); - - size = (size + 15) & ~15; - - if(m_ptr == NULL || m_pos + size > m_blocksize) - { - m_ptr = (uint8*)VirtualAlloc(NULL, m_blocksize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); - - m_pos = 0; - - m_buffers.push_back(m_ptr); - } - - uint8* ptr = &m_ptr[m_pos]; - - m_reserved = size; - - return ptr; -} - -void GSCodeBuffer::ReleaseBuffer(size_t size) -{ - ASSERT(size <= m_reserved); - - m_pos = ((m_pos + size) + 15) & ~15; - - m_reserved = 0; +/* + * Copyright (C) 2007-2009 Gabest + * http://www.gabest.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program 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 GNU Make; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "StdAfx.h" +#include "GSCodeBuffer.h" + +GSCodeBuffer::GSCodeBuffer(size_t blocksize) + : m_ptr(NULL) + , m_blocksize(blocksize) + , m_pos(0) + , m_reserved(0) +{ +} + +GSCodeBuffer::~GSCodeBuffer() +{ + for(list::iterator i = m_buffers.begin(); i != m_buffers.end(); i++) + { + VirtualFree(*i, 0, MEM_RELEASE); + } +} + +void* GSCodeBuffer::GetBuffer(size_t size) +{ + ASSERT(size < m_blocksize); + ASSERT(m_reserved == 0); + + size = (size + 15) & ~15; + + if(m_ptr == NULL || m_pos + size > m_blocksize) + { + m_ptr = (uint8*)VirtualAlloc(NULL, m_blocksize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); + + m_pos = 0; + + m_buffers.push_back(m_ptr); + } + + uint8* ptr = &m_ptr[m_pos]; + + m_reserved = size; + + return ptr; +} + +void GSCodeBuffer::ReleaseBuffer(size_t size) +{ + ASSERT(size <= m_reserved); + + m_pos = ((m_pos + size) + 15) & ~15; + + m_reserved = 0; } \ No newline at end of file diff --git a/plugins/GSdx/GSCodeBuffer.h b/plugins/GSdx/GSCodeBuffer.h index badaed81a8..ed1a197e9a 100644 --- a/plugins/GSdx/GSCodeBuffer.h +++ b/plugins/GSdx/GSCodeBuffer.h @@ -1,37 +1,37 @@ -/* - * Copyright (C) 2007-2009 Gabest - * http://www.gabest.org - * - * This Program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This Program 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 GNU Make; see the file COPYING. If not, write to - * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - * http://www.gnu.org/copyleft/gpl.html - * - */ - -#pragma once - -class GSCodeBuffer -{ - list m_buffers; - size_t m_blocksize; - size_t m_pos, m_reserved; - uint8* m_ptr; - -public: - GSCodeBuffer(size_t blocksize = 4096 * 64); // 256k - virtual ~GSCodeBuffer(); - - void* GetBuffer(size_t size); - void ReleaseBuffer(size_t size); +/* + * Copyright (C) 2007-2009 Gabest + * http://www.gabest.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program 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 GNU Make; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#pragma once + +class GSCodeBuffer +{ + list m_buffers; + size_t m_blocksize; + size_t m_pos, m_reserved; + uint8* m_ptr; + +public: + GSCodeBuffer(size_t blocksize = 4096 * 64); // 256k + virtual ~GSCodeBuffer(); + + void* GetBuffer(size_t size); + void ReleaseBuffer(size_t size); }; \ No newline at end of file diff --git a/plugins/GSdx/GSDevice11.h b/plugins/GSdx/GSDevice11.h index 78a008f3e5..057cacfe9e 100644 --- a/plugins/GSdx/GSDevice11.h +++ b/plugins/GSdx/GSDevice11.h @@ -1,180 +1,180 @@ -/* - * Copyright (C) 2007-2009 Gabest - * http://www.gabest.org - * - * This Program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This Program 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 GNU Make; see the file COPYING. If not, write to - * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - * http://www.gnu.org/copyleft/gpl.html - * - */ - -#pragma once - -#include "GSDeviceDX.h" -#include "GSTexture11.h" - -struct GSVertexShader11 -{ - CComPtr vs; - CComPtr il; -}; - -class GSDevice11 : public GSDeviceDX -{ - GSTexture* Create(int type, int w, int h, bool msaa, int format); - - void DoMerge(GSTexture* st[2], GSVector4* sr, GSVector4* dr, GSTexture* dt, bool slbg, bool mmod, const GSVector4& c); - void DoInterlace(GSTexture* st, GSTexture* dt, int shader, bool linear, float yoffset = 0); - - // - - CComPtr m_dev; - CComPtr m_ctx; - CComPtr m_swapchain; - CComPtr m_vb; - CComPtr m_vb_old; - - struct - { - ID3D11Buffer* vb; - size_t vb_stride; - ID3D11InputLayout* layout; - D3D11_PRIMITIVE_TOPOLOGY topology; - ID3D11VertexShader* vs; - ID3D11Buffer* vs_cb; - ID3D11GeometryShader* gs; - ID3D11ShaderResourceView* ps_srv[2]; - ID3D11PixelShader* ps; - ID3D11Buffer* ps_cb; - ID3D11SamplerState* ps_ss[2]; - GSVector2i viewport; - GSVector4i scissor; - ID3D11DepthStencilState* dss; - uint8 sref; - ID3D11BlendState* bs; - float bf; - ID3D11RenderTargetView* rtv; - ID3D11DepthStencilView* dsv; - } m_state; - -public: // TODO - CComPtr m_rs; - - struct - { - CComPtr il; - CComPtr vs; - CComPtr ps[7]; - CComPtr ln; - CComPtr pt; - CComPtr dss; - CComPtr bs; - } m_convert; - - struct - { - CComPtr ps[2]; - CComPtr cb; - CComPtr bs; - } m_merge; - - struct - { - CComPtr ps[4]; - CComPtr cb; - } m_interlace; - - struct - { - CComPtr dss; - CComPtr bs; - } m_date; - - void SetupDATE(GSTexture* rt, GSTexture* ds, const GSVertexPT1 (&iaVertices)[4], bool datm); - - // Shaders... - - hash_map m_vs; - CComPtr m_vs_cb; - hash_map > m_gs; - hash_map > m_ps; - CComPtr m_ps_cb; - hash_map > m_ps_ss; - CComPtr m_palette_ss; - hash_map > m_om_dss; - hash_map > m_om_bs; - - VSConstantBuffer m_vs_cb_cache; - PSConstantBuffer m_ps_cb_cache; - -public: - GSDevice11(); - virtual ~GSDevice11(); - - bool Create(GSWnd* wnd); - bool CreateTextureFX(); - bool Reset(int w, int h); - void Flip(); - - void SetExclusive(bool isExcl); - - void DrawPrimitive(); - - void ClearRenderTarget(GSTexture* t, const GSVector4& c); - void ClearRenderTarget(GSTexture* t, uint32 c); - void ClearDepth(GSTexture* t, float c); - void ClearStencil(GSTexture* t, uint8 c); - - GSTexture* CreateRenderTarget(int w, int h, bool msaa, int format = 0); - GSTexture* CreateDepthStencil(int w, int h, bool msaa, int format = 0); - GSTexture* CreateTexture(int w, int h, int format = 0); - GSTexture* CreateOffscreen(int w, int h, int format = 0); - - GSTexture* Resolve(GSTexture* t); - - GSTexture* CopyOffscreen(GSTexture* src, const GSVector4& sr, int w, int h, int format = 0); - - void CopyRect(GSTexture* st, GSTexture* dt, const GSVector4i& r); - - void StretchRect(GSTexture* st, const GSVector4& sr, GSTexture* dt, const GSVector4& dr, int shader = 0, bool linear = true); - void StretchRect(GSTexture* st, const GSVector4& sr, GSTexture* dt, const GSVector4& dr, ID3D11PixelShader* ps, ID3D11Buffer* ps_cb, bool linear = true); - void StretchRect(GSTexture* st, const GSVector4& sr, GSTexture* dt, const GSVector4& dr, ID3D11PixelShader* ps, ID3D11Buffer* ps_cb, ID3D11BlendState* bs, bool linear = true); - - void IASetVertexBuffer(const void* vertices, size_t stride, size_t count); - void IASetVertexBuffer(ID3D11Buffer* vb, size_t stride); - void IASetInputLayout(ID3D11InputLayout* layout); - void IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY topology); - void VSSetShader(ID3D11VertexShader* vs, ID3D11Buffer* vs_cb); - void GSSetShader(ID3D11GeometryShader* gs); - void PSSetShaderResources(GSTexture* sr0, GSTexture* sr1); - void PSSetShader(ID3D11PixelShader* ps, ID3D11Buffer* ps_cb); - void PSSetSamplerState(ID3D11SamplerState* ss0, ID3D11SamplerState* ss1); - void OMSetDepthStencilState(ID3D11DepthStencilState* dss, uint8 sref); - void OMSetBlendState(ID3D11BlendState* bs, float bf); - void OMSetRenderTargets(GSTexture* rt, GSTexture* ds, const GSVector4i* scissor = NULL); - - void SetupIA(const void* vertices, int count, int prim); - void SetupVS(VSSelector sel, const VSConstantBuffer* cb); - void SetupGS(GSSelector sel); - void SetupPS(PSSelector sel, const PSConstantBuffer* cb, PSSamplerSelector ssel); - void SetupOM(OMDepthStencilSelector dssel, OMBlendSelector bsel, uint8 afix); - - ID3D11Device* operator->() {return m_dev;} - operator ID3D11Device*() {return m_dev;} - operator ID3D11DeviceContext*() {return m_ctx;} - - HRESULT CompileShader(uint32 id, const string& entry, D3D11_SHADER_MACRO* macro, ID3D11VertexShader** vs, D3D11_INPUT_ELEMENT_DESC* layout, int count, ID3D11InputLayout** il); - HRESULT CompileShader(uint32 id, const string& entry, D3D11_SHADER_MACRO* macro, ID3D11GeometryShader** gs); - HRESULT CompileShader(uint32 id, const string& entry, D3D11_SHADER_MACRO* macro, ID3D11PixelShader** ps); -}; +/* + * Copyright (C) 2007-2009 Gabest + * http://www.gabest.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program 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 GNU Make; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#pragma once + +#include "GSDeviceDX.h" +#include "GSTexture11.h" + +struct GSVertexShader11 +{ + CComPtr vs; + CComPtr il; +}; + +class GSDevice11 : public GSDeviceDX +{ + GSTexture* Create(int type, int w, int h, bool msaa, int format); + + void DoMerge(GSTexture* st[2], GSVector4* sr, GSVector4* dr, GSTexture* dt, bool slbg, bool mmod, const GSVector4& c); + void DoInterlace(GSTexture* st, GSTexture* dt, int shader, bool linear, float yoffset = 0); + + // + + CComPtr m_dev; + CComPtr m_ctx; + CComPtr m_swapchain; + CComPtr m_vb; + CComPtr m_vb_old; + + struct + { + ID3D11Buffer* vb; + size_t vb_stride; + ID3D11InputLayout* layout; + D3D11_PRIMITIVE_TOPOLOGY topology; + ID3D11VertexShader* vs; + ID3D11Buffer* vs_cb; + ID3D11GeometryShader* gs; + ID3D11ShaderResourceView* ps_srv[2]; + ID3D11PixelShader* ps; + ID3D11Buffer* ps_cb; + ID3D11SamplerState* ps_ss[2]; + GSVector2i viewport; + GSVector4i scissor; + ID3D11DepthStencilState* dss; + uint8 sref; + ID3D11BlendState* bs; + float bf; + ID3D11RenderTargetView* rtv; + ID3D11DepthStencilView* dsv; + } m_state; + +public: // TODO + CComPtr m_rs; + + struct + { + CComPtr il; + CComPtr vs; + CComPtr ps[7]; + CComPtr ln; + CComPtr pt; + CComPtr dss; + CComPtr bs; + } m_convert; + + struct + { + CComPtr ps[2]; + CComPtr cb; + CComPtr bs; + } m_merge; + + struct + { + CComPtr ps[4]; + CComPtr cb; + } m_interlace; + + struct + { + CComPtr dss; + CComPtr bs; + } m_date; + + void SetupDATE(GSTexture* rt, GSTexture* ds, const GSVertexPT1 (&iaVertices)[4], bool datm); + + // Shaders... + + hash_map m_vs; + CComPtr m_vs_cb; + hash_map > m_gs; + hash_map > m_ps; + CComPtr m_ps_cb; + hash_map > m_ps_ss; + CComPtr m_palette_ss; + hash_map > m_om_dss; + hash_map > m_om_bs; + + VSConstantBuffer m_vs_cb_cache; + PSConstantBuffer m_ps_cb_cache; + +public: + GSDevice11(); + virtual ~GSDevice11(); + + bool Create(GSWnd* wnd); + bool CreateTextureFX(); + bool Reset(int w, int h); + void Flip(); + + void SetExclusive(bool isExcl); + + void DrawPrimitive(); + + void ClearRenderTarget(GSTexture* t, const GSVector4& c); + void ClearRenderTarget(GSTexture* t, uint32 c); + void ClearDepth(GSTexture* t, float c); + void ClearStencil(GSTexture* t, uint8 c); + + GSTexture* CreateRenderTarget(int w, int h, bool msaa, int format = 0); + GSTexture* CreateDepthStencil(int w, int h, bool msaa, int format = 0); + GSTexture* CreateTexture(int w, int h, int format = 0); + GSTexture* CreateOffscreen(int w, int h, int format = 0); + + GSTexture* Resolve(GSTexture* t); + + GSTexture* CopyOffscreen(GSTexture* src, const GSVector4& sr, int w, int h, int format = 0); + + void CopyRect(GSTexture* st, GSTexture* dt, const GSVector4i& r); + + void StretchRect(GSTexture* st, const GSVector4& sr, GSTexture* dt, const GSVector4& dr, int shader = 0, bool linear = true); + void StretchRect(GSTexture* st, const GSVector4& sr, GSTexture* dt, const GSVector4& dr, ID3D11PixelShader* ps, ID3D11Buffer* ps_cb, bool linear = true); + void StretchRect(GSTexture* st, const GSVector4& sr, GSTexture* dt, const GSVector4& dr, ID3D11PixelShader* ps, ID3D11Buffer* ps_cb, ID3D11BlendState* bs, bool linear = true); + + void IASetVertexBuffer(const void* vertices, size_t stride, size_t count); + void IASetVertexBuffer(ID3D11Buffer* vb, size_t stride); + void IASetInputLayout(ID3D11InputLayout* layout); + void IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY topology); + void VSSetShader(ID3D11VertexShader* vs, ID3D11Buffer* vs_cb); + void GSSetShader(ID3D11GeometryShader* gs); + void PSSetShaderResources(GSTexture* sr0, GSTexture* sr1); + void PSSetShader(ID3D11PixelShader* ps, ID3D11Buffer* ps_cb); + void PSSetSamplerState(ID3D11SamplerState* ss0, ID3D11SamplerState* ss1); + void OMSetDepthStencilState(ID3D11DepthStencilState* dss, uint8 sref); + void OMSetBlendState(ID3D11BlendState* bs, float bf); + void OMSetRenderTargets(GSTexture* rt, GSTexture* ds, const GSVector4i* scissor = NULL); + + void SetupIA(const void* vertices, int count, int prim); + void SetupVS(VSSelector sel, const VSConstantBuffer* cb); + void SetupGS(GSSelector sel); + void SetupPS(PSSelector sel, const PSConstantBuffer* cb, PSSamplerSelector ssel); + void SetupOM(OMDepthStencilSelector dssel, OMBlendSelector bsel, uint8 afix); + + ID3D11Device* operator->() {return m_dev;} + operator ID3D11Device*() {return m_dev;} + operator ID3D11DeviceContext*() {return m_ctx;} + + HRESULT CompileShader(uint32 id, const string& entry, D3D11_SHADER_MACRO* macro, ID3D11VertexShader** vs, D3D11_INPUT_ELEMENT_DESC* layout, int count, ID3D11InputLayout** il); + HRESULT CompileShader(uint32 id, const string& entry, D3D11_SHADER_MACRO* macro, ID3D11GeometryShader** gs); + HRESULT CompileShader(uint32 id, const string& entry, D3D11_SHADER_MACRO* macro, ID3D11PixelShader** ps); +}; diff --git a/plugins/GSdx/GSDialog.cpp b/plugins/GSdx/GSDialog.cpp index 50244c4a79..56ebc54051 100644 --- a/plugins/GSdx/GSDialog.cpp +++ b/plugins/GSdx/GSDialog.cpp @@ -1,178 +1,178 @@ -/* - * Copyright (C) 2007-2009 Gabest - * http://www.gabest.org - * - * This Program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This Program 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 GNU Make; see the file COPYING. If not, write to - * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - * http://www.gnu.org/copyleft/gpl.html - * - */ - -#include "StdAfx.h" -#include "GSdx.h" -#include "GSDialog.h" -#include "GSVector.h" - -GSDialog::GSDialog(UINT id) - : m_id(id) - , m_hWnd(NULL) -{ -} - -INT_PTR GSDialog::DoModal() -{ - return DialogBoxParam(theApp.GetModuleHandle(), MAKEINTRESOURCE(m_id), GetActiveWindow(), DialogProc, (LPARAM)this); -} - -INT_PTR CALLBACK GSDialog::DialogProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) -{ - GSDialog* dlg = NULL; - - if(message == WM_INITDIALOG) - { - dlg = (GSDialog*)lParam; - SetWindowLongPtr(hWnd, GWL_USERDATA, (LONG_PTR)dlg); - dlg->m_hWnd = hWnd; - - MONITORINFO mi; - mi.cbSize = sizeof(mi); - GetMonitorInfo(MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST), &mi); - - GSVector4i r; - GetWindowRect(hWnd, r); - - int x = (mi.rcWork.left + mi.rcWork.right - r.width()) / 2; - int y = (mi.rcWork.top + mi.rcWork.bottom - r.height()) / 2; - - SetWindowPos(hWnd, NULL, x, y, -1, -1, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); - - dlg->OnInit(); - - return true; - } - - dlg = (GSDialog*)GetWindowLongPtr(hWnd, GWL_USERDATA); - - return dlg != NULL ? dlg->OnMessage(message, wParam, lParam) : FALSE; -} - -bool GSDialog::OnMessage(UINT message, WPARAM wParam, LPARAM lParam) -{ - return message == WM_COMMAND ? OnCommand((HWND)lParam, LOWORD(wParam), HIWORD(wParam)) : false; -} - -bool GSDialog::OnCommand(HWND hWnd, UINT id, UINT code) -{ - if(id == IDOK || id == IDCANCEL) - { - EndDialog(m_hWnd, id); - - return true; - } - - return false; -} - -string GSDialog::GetText(UINT id) -{ - string s; - - char* buff = NULL; - - for(int size = 256, limit = 65536; size < limit; size <<= 1) - { - buff = new char[size]; - - if(GetDlgItemText(m_hWnd, id, buff, size)) - { - s = buff; - size = limit; - } - - delete [] buff; - } - - return s; -} - -int GSDialog::GetTextAsInt(UINT id) -{ - return atoi(GetText(id).c_str()); -} - -void GSDialog::SetText(UINT id, const char* str) -{ - SetDlgItemText(m_hWnd, id, str); -} - -void GSDialog::SetTextAsInt(UINT id, int i) -{ - char buff[32] = {0}; - itoa(i, buff, 10); - SetText(id, buff); -} - -void GSDialog::ComboBoxInit(UINT id, const GSSetting* settings, int count, uint32 selid, uint32 maxid) -{ - HWND hWnd = GetDlgItem(m_hWnd, id); - - SendMessage(hWnd, CB_RESETCONTENT, 0, 0); - - for(int i = 0; i < count; i++) - { - if(settings[i].id <= maxid) - { - string str = settings[i].name; - - if(settings[i].note != NULL) - { - str = str + " (" + settings[i].note + ")"; - } - - ComboBoxAppend(id, str.c_str(), (LPARAM)settings[i].id, settings[i].id == selid); - } - } -} - -int GSDialog::ComboBoxAppend(UINT id, const char* str, LPARAM data, bool select) -{ - HWND hWnd = GetDlgItem(m_hWnd, id); - - int item = (int)SendMessage(hWnd, CB_ADDSTRING, 0, (LPARAM)str); - - SendMessage(hWnd, CB_SETITEMDATA, item, (LPARAM)data); - - if(select) - { - SendMessage(hWnd, CB_SETCURSEL, item, 0); - } - - return item; -} - -bool GSDialog::ComboBoxGetSelData(UINT id, INT_PTR& data) -{ - HWND hWnd = GetDlgItem(m_hWnd, id); - - int item = SendMessage(hWnd, CB_GETCURSEL, 0, 0); - - if(item >= 0) - { - data = SendMessage(hWnd, CB_GETITEMDATA, item, 0); - - return true; - } - - return false; -} +/* + * Copyright (C) 2007-2009 Gabest + * http://www.gabest.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program 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 GNU Make; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "StdAfx.h" +#include "GSdx.h" +#include "GSDialog.h" +#include "GSVector.h" + +GSDialog::GSDialog(UINT id) + : m_id(id) + , m_hWnd(NULL) +{ +} + +INT_PTR GSDialog::DoModal() +{ + return DialogBoxParam(theApp.GetModuleHandle(), MAKEINTRESOURCE(m_id), GetActiveWindow(), DialogProc, (LPARAM)this); +} + +INT_PTR CALLBACK GSDialog::DialogProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + GSDialog* dlg = NULL; + + if(message == WM_INITDIALOG) + { + dlg = (GSDialog*)lParam; + SetWindowLongPtr(hWnd, GWL_USERDATA, (LONG_PTR)dlg); + dlg->m_hWnd = hWnd; + + MONITORINFO mi; + mi.cbSize = sizeof(mi); + GetMonitorInfo(MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST), &mi); + + GSVector4i r; + GetWindowRect(hWnd, r); + + int x = (mi.rcWork.left + mi.rcWork.right - r.width()) / 2; + int y = (mi.rcWork.top + mi.rcWork.bottom - r.height()) / 2; + + SetWindowPos(hWnd, NULL, x, y, -1, -1, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); + + dlg->OnInit(); + + return true; + } + + dlg = (GSDialog*)GetWindowLongPtr(hWnd, GWL_USERDATA); + + return dlg != NULL ? dlg->OnMessage(message, wParam, lParam) : FALSE; +} + +bool GSDialog::OnMessage(UINT message, WPARAM wParam, LPARAM lParam) +{ + return message == WM_COMMAND ? OnCommand((HWND)lParam, LOWORD(wParam), HIWORD(wParam)) : false; +} + +bool GSDialog::OnCommand(HWND hWnd, UINT id, UINT code) +{ + if(id == IDOK || id == IDCANCEL) + { + EndDialog(m_hWnd, id); + + return true; + } + + return false; +} + +string GSDialog::GetText(UINT id) +{ + string s; + + char* buff = NULL; + + for(int size = 256, limit = 65536; size < limit; size <<= 1) + { + buff = new char[size]; + + if(GetDlgItemText(m_hWnd, id, buff, size)) + { + s = buff; + size = limit; + } + + delete [] buff; + } + + return s; +} + +int GSDialog::GetTextAsInt(UINT id) +{ + return atoi(GetText(id).c_str()); +} + +void GSDialog::SetText(UINT id, const char* str) +{ + SetDlgItemText(m_hWnd, id, str); +} + +void GSDialog::SetTextAsInt(UINT id, int i) +{ + char buff[32] = {0}; + itoa(i, buff, 10); + SetText(id, buff); +} + +void GSDialog::ComboBoxInit(UINT id, const GSSetting* settings, int count, uint32 selid, uint32 maxid) +{ + HWND hWnd = GetDlgItem(m_hWnd, id); + + SendMessage(hWnd, CB_RESETCONTENT, 0, 0); + + for(int i = 0; i < count; i++) + { + if(settings[i].id <= maxid) + { + string str = settings[i].name; + + if(settings[i].note != NULL) + { + str = str + " (" + settings[i].note + ")"; + } + + ComboBoxAppend(id, str.c_str(), (LPARAM)settings[i].id, settings[i].id == selid); + } + } +} + +int GSDialog::ComboBoxAppend(UINT id, const char* str, LPARAM data, bool select) +{ + HWND hWnd = GetDlgItem(m_hWnd, id); + + int item = (int)SendMessage(hWnd, CB_ADDSTRING, 0, (LPARAM)str); + + SendMessage(hWnd, CB_SETITEMDATA, item, (LPARAM)data); + + if(select) + { + SendMessage(hWnd, CB_SETCURSEL, item, 0); + } + + return item; +} + +bool GSDialog::ComboBoxGetSelData(UINT id, INT_PTR& data) +{ + HWND hWnd = GetDlgItem(m_hWnd, id); + + int item = SendMessage(hWnd, CB_GETCURSEL, 0, 0); + + if(item >= 0) + { + data = SendMessage(hWnd, CB_GETITEMDATA, item, 0); + + return true; + } + + return false; +} diff --git a/plugins/GSdx/GSDialog.h b/plugins/GSdx/GSDialog.h index 0f1b913569..e08447c51b 100644 --- a/plugins/GSdx/GSDialog.h +++ b/plugins/GSdx/GSDialog.h @@ -1,54 +1,54 @@ -/* - * Copyright (C) 2007-2009 Gabest - * http://www.gabest.org - * - * This Program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This Program 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 GNU Make; see the file COPYING. If not, write to - * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - * http://www.gnu.org/copyleft/gpl.html - * - */ - -#pragma once - -#include "GSSetting.h" - -class GSDialog -{ - int m_id; - - static INT_PTR CALLBACK DialogProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); - -protected: - HWND m_hWnd; - - virtual void OnInit() {} - virtual bool OnMessage(UINT message, WPARAM wParam, LPARAM lParam); - virtual bool OnCommand(HWND hWnd, UINT id, UINT code); - -public: - GSDialog(UINT id); - virtual ~GSDialog() {} - - INT_PTR DoModal(); - - string GetText(UINT id); - int GetTextAsInt(UINT id); - - void SetText(UINT id, const char* str); - void SetTextAsInt(UINT id, int i); - - void ComboBoxInit(UINT id, const GSSetting* settings, int count, uint32 selid, uint32 maxid = ~0); - int ComboBoxAppend(UINT id, const char* str, LPARAM data = 0, bool select = false); - bool ComboBoxGetSelData(UINT id, INT_PTR& data); -}; +/* + * Copyright (C) 2007-2009 Gabest + * http://www.gabest.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program 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 GNU Make; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#pragma once + +#include "GSSetting.h" + +class GSDialog +{ + int m_id; + + static INT_PTR CALLBACK DialogProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); + +protected: + HWND m_hWnd; + + virtual void OnInit() {} + virtual bool OnMessage(UINT message, WPARAM wParam, LPARAM lParam); + virtual bool OnCommand(HWND hWnd, UINT id, UINT code); + +public: + GSDialog(UINT id); + virtual ~GSDialog() {} + + INT_PTR DoModal(); + + string GetText(UINT id); + int GetTextAsInt(UINT id); + + void SetText(UINT id, const char* str); + void SetTextAsInt(UINT id, int i); + + void ComboBoxInit(UINT id, const GSSetting* settings, int count, uint32 selid, uint32 maxid = ~0); + int ComboBoxAppend(UINT id, const char* str, LPARAM data = 0, bool select = false); + bool ComboBoxGetSelData(UINT id, INT_PTR& data); +}; diff --git a/plugins/GSdx/GSDrawScanlineCodeGenerator.cpp b/plugins/GSdx/GSDrawScanlineCodeGenerator.cpp index cb1d003a72..ac0aa8e821 100644 --- a/plugins/GSdx/GSDrawScanlineCodeGenerator.cpp +++ b/plugins/GSdx/GSDrawScanlineCodeGenerator.cpp @@ -1,2181 +1,2181 @@ -/* - * Copyright (C) 2007-2009 Gabest - * http://www.gabest.org - * - * This Program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This Program 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 GNU Make; see the file COPYING. If not, write to - * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - * http://www.gnu.org/copyleft/gpl.html - * - */ - -// TODO: x64 (use the extra regs to avoid spills of zs, zd, uf, vf, rb, ga and keep a few constants in the last two like aref or afix) -// TODO: for edges doing 4 pixels is wasteful (needed memory access * 4) - -#include "StdAfx.h" -#include "GSDrawScanlineCodeGenerator.h" - -GSDrawScanlineCodeGenerator::GSDrawScanlineCodeGenerator(GSScanlineEnvironment& env, uint64 key, void* ptr, size_t maxsize) - : CodeGenerator(maxsize, ptr) - , m_env(env) -{ - #if _M_AMD64 - #error TODO - #endif - - m_sel.key = key; - - Generate(); -} - -void GSDrawScanlineCodeGenerator::Generate() -{ - push(ebx); - push(esi); - push(edi); - push(ebp); - - const int params = 16; - - Init(params); - - if(!m_sel.edge) - { - align(16); - } - -L("loop"); - - // ecx = steps - // esi = fzbr - // edi = fzbc - // xmm0 = z/zi - // xmm2 = u (tme) - // xmm3 = v (tme) - // xmm5 = rb (!tme) - // xmm6 = ga (!tme) - // xmm7 = test - - bool tme = m_sel.tfx != TFX_NONE; - - TestZ(tme ? xmm5 : xmm2, tme ? xmm6 : xmm3); - - // ecx = steps - // esi = fzbr - // edi = fzbc - // - xmm0 - // xmm2 = u (tme) - // xmm3 = v (tme) - // xmm5 = rb (!tme) - // xmm6 = ga (!tme) - // xmm7 = test - - SampleTexture(); - - // ecx = steps - // esi = fzbr - // edi = fzbc - // ebp = za - // - xmm2 - // - xmm3 - // - xmm4 - // xmm5 = rb - // xmm6 = ga - // xmm7 = test - - AlphaTFX(); - - // ecx = steps - // esi = fzbr - // edi = fzbc - // ebp = za - // xmm2 = gaf (TFX_HIGHLIGHT || TFX_HIGHLIGHT2 && !tcc) - // xmm5 = rb - // xmm6 = ga - // xmm7 = test - - if(m_sel.fwrite) - { - movdqa(xmm3, xmmword[&m_env.fm]); - } - - if(m_sel.zwrite) - { - movdqa(xmm4, xmmword[&m_env.zm]); - } - - // ecx = steps - // esi = fzbr - // edi = fzbc - // ebp = za - // xmm2 = gaf (TFX_HIGHLIGHT || TFX_HIGHLIGHT2 && !tcc) - // xmm3 = fm - // xmm4 = zm - // xmm5 = rb - // xmm6 = ga - // xmm7 = test - - TestAlpha(); - - // ecx = steps - // esi = fzbr - // edi = fzbc - // ebp = za - // xmm2 = gaf (TFX_HIGHLIGHT || TFX_HIGHLIGHT2 && !tcc) - // xmm3 = fm - // xmm4 = zm - // xmm5 = rb - // xmm6 = ga - // xmm7 = test - - ColorTFX(); - - // ecx = steps - // esi = fzbr - // edi = fzbc - // ebp = za - // xmm3 = fm - // xmm4 = zm - // xmm5 = rb - // xmm6 = ga - // xmm7 = test - - Fog(); - - // ecx = steps - // esi = fzbr - // edi = fzbc - // ebp = za - // xmm3 = fm - // xmm4 = zm - // xmm5 = rb - // xmm6 = ga - // xmm7 = test - - ReadFrame(); - - // ecx = steps - // esi = fzbr - // edi = fzbc - // ebp = za - // xmm2 = fd - // xmm3 = fm - // xmm4 = zm - // xmm5 = rb - // xmm6 = ga - // xmm7 = test - - TestDestAlpha(); - - // fm |= test; - // zm |= test; - - if(m_sel.fwrite) - { - por(xmm3, xmm7); - } - - if(m_sel.zwrite) - { - por(xmm4, xmm7); - } - - // int fzm = ~(fm == GSVector4i::xffffffff()).ps32(zm == GSVector4i::xffffffff()).mask(); - - pcmpeqd(xmm1, xmm1); - - if(m_sel.fwrite && m_sel.zwrite) - { - movdqa(xmm0, xmm1); - pcmpeqd(xmm1, xmm3); - pcmpeqd(xmm0, xmm4); - packssdw(xmm1, xmm0); - } - else if(m_sel.fwrite) - { - pcmpeqd(xmm1, xmm3); - packssdw(xmm1, xmm1); - } - else if(m_sel.zwrite) - { - pcmpeqd(xmm1, xmm4); - packssdw(xmm1, xmm1); - } - - pmovmskb(edx, xmm1); - not(edx); - - // ebx = fa - // ecx = steps - // edx = fzm - // esi = fzbr - // edi = fzbc - // ebp = za - // xmm2 = fd - // xmm3 = fm - // xmm4 = zm - // xmm5 = rb - // xmm6 = ga - - WriteZBuf(); - - // ebx = fa - // ecx = steps - // edx = fzm - // esi = fzbr - // edi = fzbc - // - ebp - // xmm2 = fd - // xmm3 = fm - // - xmm4 - // xmm5 = rb - // xmm6 = ga - - AlphaBlend(); - - // ebx = fa - // ecx = steps - // edx = fzm - // esi = fzbr - // edi = fzbc - // xmm2 = fd - // xmm3 = fm - // xmm5 = rb - // xmm6 = ga - - WriteFrame(params); - -L("step"); - - // if(steps <= 0) break; - - if(!m_sel.edge) - { - test(ecx, ecx); - jle("exit", T_NEAR); - - Step(); - - jmp("loop", T_NEAR); - } - -L("exit"); - - pop(ebp); - pop(edi); - pop(esi); - pop(ebx); - - ret(8); -} - -void GSDrawScanlineCodeGenerator::Init(int params) -{ - const int _top = params + 4; - const int _v = params + 8; - - // int skip = left & 3; - - mov(ebx, edx); - and(edx, 3); - - // left -= skip; - - sub(ebx, edx); - - // int steps = right - left - 4; - - sub(ecx, ebx); - sub(ecx, 4); - - // GSVector4i test = m_test[skip] | m_test[7 + (steps & (steps >> 31))]; - - shl(edx, 4); - - movdqa(xmm7, xmmword[edx + (size_t)&m_test[0]]); - - mov(eax, ecx); - sar(eax, 31); - and(eax, ecx); - shl(eax, 4); - - por(xmm7, xmmword[eax + (size_t)&m_test[7]]); - - // GSVector2i* fza_base = &m_env.fzbr[top]; - - mov(esi, dword[esp + _top]); - lea(esi, ptr[esi * 8]); - add(esi, dword[&m_env.fzbr]); - - // GSVector2i* fza_offset = &m_env.fzbc[left >> 2]; - - lea(edi, ptr[ebx * 2]); - add(edi, dword[&m_env.fzbc]); - - if(!m_sel.sprite && (m_sel.fwrite && m_sel.fge || m_sel.zb) || m_sel.fb && (m_sel.edge || m_sel.tfx != TFX_NONE || m_sel.iip)) - { - // edx = &m_env.d[skip] - - shl(edx, 4); - lea(edx, ptr[edx + (size_t)m_env.d]); - - // ebx = &v - - mov(ebx, dword[esp + _v]); - } - - if(!m_sel.sprite) - { - if(m_sel.fwrite && m_sel.fge || m_sel.zb) - { - movaps(xmm0, xmmword[ebx + 16]); // v.p - - if(m_sel.fwrite && m_sel.fge) - { - // f = GSVector4i(vp).zzzzh().zzzz().add16(m_env.d[skip].f); - - cvttps2dq(xmm1, xmm0); - pshufhw(xmm1, xmm1, _MM_SHUFFLE(2, 2, 2, 2)); - pshufd(xmm1, xmm1, _MM_SHUFFLE(2, 2, 2, 2)); - paddw(xmm1, xmmword[edx + 16 * 6]); - - movdqa(xmmword[&m_env.temp.f], xmm1); - } - - if(m_sel.zb) - { - // z = vp.zzzz() + m_env.d[skip].z; - - shufps(xmm0, xmm0, _MM_SHUFFLE(2, 2, 2, 2)); - addps(xmm0, xmmword[edx]); - - movaps(xmmword[&m_env.temp.z], xmm0); - } - } - } - else - { - if(m_sel.ztest) - { - movdqa(xmm0, xmmword[&m_env.p.z]); - } - } - - if(m_sel.fb) - { - if(m_sel.edge || m_sel.tfx != TFX_NONE) - { - movaps(xmm4, xmmword[ebx + 32]); // v.t - } - - if(m_sel.edge) - { - pshufhw(xmm3, xmm4, _MM_SHUFFLE(2, 2, 2, 2)); - pshufd(xmm3, xmm3, _MM_SHUFFLE(3, 3, 3, 3)); - psrlw(xmm3, 9); - - movdqa(xmmword[&m_env.temp.cov], xmm3); - } - - if(m_sel.tfx != TFX_NONE) - { - if(m_sel.fst) - { - // GSVector4i vti(vt); - - cvttps2dq(xmm4, xmm4); - - // si = vti.xxxx() + m_env.d[skip].si; - // ti = vti.yyyy(); if(!sprite) ti += m_env.d[skip].ti; - - pshufd(xmm2, xmm4, _MM_SHUFFLE(0, 0, 0, 0)); - pshufd(xmm3, xmm4, _MM_SHUFFLE(1, 1, 1, 1)); - - paddd(xmm2, xmmword[edx + 16 * 7]); - - if(!m_sel.sprite) - { - paddd(xmm3, xmmword[edx + 16 * 8]); - } - else - { - if(m_sel.ltf) - { - movdqa(xmm4, xmm3); - pshuflw(xmm4, xmm4, _MM_SHUFFLE(2, 2, 0, 0)); - pshufhw(xmm4, xmm4, _MM_SHUFFLE(2, 2, 0, 0)); - psrlw(xmm4, 1); - movdqa(xmmword[&m_env.temp.vf], xmm4); - } - } - - movdqa(xmmword[&m_env.temp.s], xmm2); - movdqa(xmmword[&m_env.temp.t], xmm3); - } - else - { - // s = vt.xxxx() + m_env.d[skip].s; - // t = vt.yyyy() + m_env.d[skip].t; - // q = vt.zzzz() + m_env.d[skip].q; - - movaps(xmm2, xmm4); - movaps(xmm3, xmm4); - - shufps(xmm2, xmm2, _MM_SHUFFLE(0, 0, 0, 0)); - shufps(xmm3, xmm3, _MM_SHUFFLE(1, 1, 1, 1)); - shufps(xmm4, xmm4, _MM_SHUFFLE(2, 2, 2, 2)); - - addps(xmm2, xmmword[edx + 16 * 1]); - addps(xmm3, xmmword[edx + 16 * 2]); - addps(xmm4, xmmword[edx + 16 * 3]); - - movaps(xmmword[&m_env.temp.s], xmm2); - movaps(xmmword[&m_env.temp.t], xmm3); - movaps(xmmword[&m_env.temp.q], xmm4); - - rcpps(xmm4, xmm4); - mulps(xmm2, xmm4); - mulps(xmm3, xmm4); - } - } - - if(!(m_sel.tfx == TFX_DECAL && m_sel.tcc)) - { - if(m_sel.iip) - { - // GSVector4i vc = GSVector4i(v.c); - - cvttps2dq(xmm6, xmmword[ebx]); // v.c - - // vc = vc.upl16(vc.zwxy()); - - pshufd(xmm5, xmm6, _MM_SHUFFLE(1, 0, 3, 2)); - punpcklwd(xmm6, xmm5); - - // rb = vc.xxxx().add16(m_env.d[skip].rb); - // ga = vc.zzzz().add16(m_env.d[skip].ga); - - pshufd(xmm5, xmm6, _MM_SHUFFLE(0, 0, 0, 0)); - pshufd(xmm6, xmm6, _MM_SHUFFLE(2, 2, 2, 2)); - - paddw(xmm5, xmmword[edx + 16 * 4]); - paddw(xmm6, xmmword[edx + 16 * 5]); - - movdqa(xmmword[&m_env.temp.rb], xmm5); - movdqa(xmmword[&m_env.temp.ga], xmm6); - } - else - { - if(m_sel.tfx == TFX_NONE) - { - movdqa(xmm5, xmmword[&m_env.c.rb]); - movdqa(xmm6, xmmword[&m_env.c.ga]); - } - } - } - } -} - -void GSDrawScanlineCodeGenerator::Step() -{ - // steps -= 4; - - sub(ecx, 4); - - // fza_offset++; - - add(edi, 8); - - if(!m_sel.sprite) - { - // z += m_env.d4.z; - - if(m_sel.zb) - { - movaps(xmm0, xmmword[&m_env.temp.z]); - addps(xmm0, xmmword[&m_env.d4.z]); - movaps(xmmword[&m_env.temp.z], xmm0); - } - - // f = f.add16(m_env.d4.f); - - if(m_sel.fwrite && m_sel.fge) - { - movdqa(xmm1, xmmword[&m_env.temp.f]); - paddw(xmm1, xmmword[&m_env.d4.f]); - movdqa(xmmword[&m_env.temp.f], xmm1); - } - } - else - { - if(m_sel.ztest) - { - movdqa(xmm0, xmmword[&m_env.p.z]); - } - } - - if(m_sel.fb) - { - if(m_sel.tfx != TFX_NONE) - { - if(m_sel.fst) - { - // GSVector4i st = m_env.d4.st; - - // si += st.xxxx(); - // if(!sprite) ti += st.yyyy(); - - movdqa(xmm4, xmmword[&m_env.d4.st]); - - pshufd(xmm2, xmm4, _MM_SHUFFLE(0, 0, 0, 0)); - paddd(xmm2, xmmword[&m_env.temp.s]); - movdqa(xmmword[&m_env.temp.s], xmm2); - - if(!m_sel.sprite) - { - pshufd(xmm3, xmm4, _MM_SHUFFLE(1, 1, 1, 1)); - paddd(xmm3, xmmword[&m_env.temp.t]); - movdqa(xmmword[&m_env.temp.t], xmm3); - } - else - { - movdqa(xmm3, xmmword[&m_env.temp.t]); - } - } - else - { - // GSVector4 stq = m_env.d4.stq; - - // s += stq.xxxx(); - // t += stq.yyyy(); - // q += stq.zzzz(); - - movaps(xmm2, xmmword[&m_env.d4.stq]); - movaps(xmm3, xmm2); - movaps(xmm4, xmm2); - - shufps(xmm2, xmm2, _MM_SHUFFLE(0, 0, 0, 0)); - shufps(xmm3, xmm3, _MM_SHUFFLE(1, 1, 1, 1)); - shufps(xmm4, xmm4, _MM_SHUFFLE(2, 2, 2, 2)); - - addps(xmm2, xmmword[&m_env.temp.s]); - addps(xmm3, xmmword[&m_env.temp.t]); - addps(xmm4, xmmword[&m_env.temp.q]); - - movaps(xmmword[&m_env.temp.s], xmm2); - movaps(xmmword[&m_env.temp.t], xmm3); - movaps(xmmword[&m_env.temp.q], xmm4); - - rcpps(xmm4, xmm4); - mulps(xmm2, xmm4); - mulps(xmm3, xmm4); - } - } - - if(!(m_sel.tfx == TFX_DECAL && m_sel.tcc)) - { - if(m_sel.iip) - { - // GSVector4i c = m_env.d4.c; - - // rb = rb.add16(c.xxxx()); - // ga = ga.add16(c.yyyy()); - - movdqa(xmm7, xmmword[&m_env.d4.c]); - - pshufd(xmm5, xmm7, _MM_SHUFFLE(0, 0, 0, 0)); - pshufd(xmm6, xmm7, _MM_SHUFFLE(1, 1, 1, 1)); - - paddw(xmm5, xmmword[&m_env.temp.rb]); - paddw(xmm6, xmmword[&m_env.temp.ga]); - - movdqa(xmmword[&m_env.temp.rb], xmm5); - movdqa(xmmword[&m_env.temp.ga], xmm6); - } - else - { - if(m_sel.tfx == TFX_NONE) - { - movdqa(xmm5, xmmword[&m_env.c.rb]); - movdqa(xmm6, xmmword[&m_env.c.ga]); - } - } - } - } - - // test = m_test[7 + (steps & (steps >> 31))]; - - mov(edx, ecx); - sar(edx, 31); - and(edx, ecx); - shl(edx, 4); - - movdqa(xmm7, xmmword[edx + (size_t)&m_test[7]]); -} - -void GSDrawScanlineCodeGenerator::TestZ(const Xmm& temp1, const Xmm& temp2) -{ - if(!m_sel.zb) - { - return; - } - - // int za = fza_base.y + fza_offset->y; - - mov(ebp, dword[esi + 4]); - add(ebp, dword[edi + 4]); - - // GSVector4i zs = zi; - - if(!m_sel.sprite) - { - if(m_sel.zoverflow) - { - // zs = (GSVector4i(z * 0.5f) << 1) | (GSVector4i(z) & GSVector4i::x00000001()); - - static float half = 0.5f; - - movss(temp1, dword[&half]); - shufps(temp1, temp1, _MM_SHUFFLE(0, 0, 0, 0)); - mulps(temp1, xmm0); - cvttps2dq(temp1, temp1); - pslld(temp1, 1); - - cvttps2dq(xmm0, xmm0); - pcmpeqd(temp2, temp2); - psrld(temp2, 31); - pand(xmm0, temp2); - - por(xmm0, temp1); - } - else - { - // zs = GSVector4i(z); - - cvttps2dq(xmm0, xmm0); - } - - if(m_sel.zwrite) - { - movdqa(xmmword[&m_env.temp.zs], xmm0); - } - } - - if(m_sel.ztest) - { - ReadPixel(xmm1, ebp); - - if(m_sel.zwrite && m_sel.zpsm < 2) - { - movdqa(xmmword[&m_env.temp.zd], xmm1); - } - - // zd &= 0xffffffff >> m_sel.zpsm * 8; - - if(m_sel.zpsm) - { - pslld(xmm1, m_sel.zpsm * 8); - psrld(xmm1, m_sel.zpsm * 8); - } - - if(m_sel.zoverflow || m_sel.zpsm == 0) - { - // GSVector4i o = GSVector4i::x80000000(); - - pcmpeqd(xmm4, xmm4); - pslld(xmm4, 31); - - // GSVector4i zso = zs - o; - - psubd(xmm0, xmm4); - - // GSVector4i zdo = zd - o; - - psubd(xmm1, xmm4); - } - - switch(m_sel.ztst) - { - case ZTST_GEQUAL: - // test |= zso < zdo; // ~(zso >= zdo) - pcmpgtd(xmm1, xmm0); - por(xmm7, xmm1); - break; - - case ZTST_GREATER: // TODO: tidus hair and chocobo wings only appear fully when this is tested as ZTST_GEQUAL - // test |= zso <= zdo; // ~(zso > zdo) - pcmpgtd(xmm0, xmm1); - pcmpeqd(xmm4, xmm4); - pxor(xmm0, xmm4); - por(xmm7, xmm0); - break; - } - - alltrue(); - } -} - -void GSDrawScanlineCodeGenerator::SampleTexture() -{ - if(!m_sel.fb || m_sel.tfx == TFX_NONE) - { - return; - } - - mov(ebx, dword[&m_env.tex]); - - // ebx = tex - - if(!m_sel.fst) - { - // TODO: move these into Init/Step too? - - cvttps2dq(xmm2, xmm2); - cvttps2dq(xmm3, xmm3); - - if(m_sel.ltf) - { - // u -= 0x8000; - // v -= 0x8000; - - mov(eax, 0x8000); - movd(xmm4, eax); - pshufd(xmm4, xmm4, _MM_SHUFFLE(0, 0, 0, 0)); - psubd(xmm2, xmm4); - psubd(xmm3, xmm4); - } - } - - // xmm2 = u - // xmm3 = v - - if(m_sel.ltf) - { - // GSVector4i uf = u.xxzzlh().srl16(1); - - movdqa(xmm0, xmm2); - pshuflw(xmm0, xmm0, _MM_SHUFFLE(2, 2, 0, 0)); - pshufhw(xmm0, xmm0, _MM_SHUFFLE(2, 2, 0, 0)); - psrlw(xmm0, 1); - movdqa(xmmword[&m_env.temp.uf], xmm0); - - if(!m_sel.sprite) - { - // GSVector4i vf = v.xxzzlh().srl16(1); - - movdqa(xmm1, xmm3); - pshuflw(xmm1, xmm1, _MM_SHUFFLE(2, 2, 0, 0)); - pshufhw(xmm1, xmm1, _MM_SHUFFLE(2, 2, 0, 0)); - psrlw(xmm1, 1); - movdqa(xmmword[&m_env.temp.vf], xmm1); - } - } - - // GSVector4i uv0 = u.sra32(16).ps32(v.sra32(16)); - - psrad(xmm2, 16); - psrad(xmm3, 16); - packssdw(xmm2, xmm3); - - if(m_sel.ltf) - { - // GSVector4i uv1 = uv0.add16(GSVector4i::x0001()); - - movdqa(xmm3, xmm2); - pcmpeqd(xmm1, xmm1); - psrlw(xmm1, 15); - paddw(xmm3, xmm1); - - // uv0 = Wrap(uv0); - // uv1 = Wrap(uv1); - - Wrap(xmm2, xmm3); - } - else - { - // uv0 = Wrap(uv0); - - Wrap(xmm2); - } - - // xmm2 = uv0 - // xmm3 = uv1 (ltf) - // xmm0, xmm1, xmm4, xmm5, xmm6 = free - // xmm7 = used - - // GSVector4i y0 = uv0.uph16() << tw; - // GSVector4i x0 = uv0.upl16(); - - pxor(xmm0, xmm0); - movd(xmm1, ptr[&m_env.tw]); - - movdqa(xmm4, xmm2); - punpckhwd(xmm2, xmm0); - punpcklwd(xmm4, xmm0); - pslld(xmm2, xmm1); - - // xmm0 = 0 - // xmm1 = tw - // xmm2 = y0 - // xmm3 = uv1 (ltf) - // xmm4 = x0 - // xmm5, xmm6 = free - // xmm7 = used - - if(m_sel.ltf) - { - // GSVector4i y1 = uv1.uph16() << tw; - // GSVector4i x1 = uv1.upl16(); - - movdqa(xmm6, xmm3); - punpckhwd(xmm3, xmm0); - punpcklwd(xmm6, xmm0); - pslld(xmm3, xmm1); - - // xmm2 = y0 - // xmm3 = y1 - // xmm4 = x0 - // xmm6 = x1 - // xmm0, xmm5, xmm6 = free - // xmm7 = used - - // GSVector4i addr00 = y0 + x0; - // GSVector4i addr01 = y0 + x1; - // GSVector4i addr10 = y1 + x0; - // GSVector4i addr11 = y1 + x1; - - movdqa(xmm5, xmm2); - paddd(xmm5, xmm4); - paddd(xmm2, xmm6); - - movdqa(xmm0, xmm3); - paddd(xmm0, xmm4); - paddd(xmm3, xmm6); - - // xmm5 = addr00 - // xmm2 = addr01 - // xmm0 = addr10 - // xmm3 = addr11 - // xmm1, xmm4, xmm6 = free - // xmm7 = used - - // c00 = addr00.gather32_32((const uint32/uint8*)tex[, clut]); - // c01 = addr01.gather32_32((const uint32/uint8*)tex[, clut]); - // c10 = addr10.gather32_32((const uint32/uint8*)tex[, clut]); - // c11 = addr11.gather32_32((const uint32/uint8*)tex[, clut]); - - ReadTexel(xmm6, xmm5, xmm1, xmm4); - - // xmm2, xmm5, xmm1 = free - - ReadTexel(xmm4, xmm2, xmm5, xmm1); - - // xmm0, xmm2, xmm5 = free - - ReadTexel(xmm1, xmm0, xmm2, xmm5); - - // xmm3, xmm0, xmm2 = free - - ReadTexel(xmm5, xmm3, xmm0, xmm2); - - // xmm6 = c00 - // xmm4 = c01 - // xmm1 = c10 - // xmm5 = c11 - // xmm0, xmm2, xmm3 = free - // xmm7 = used - - movdqa(xmm0, xmmword[&m_env.temp.uf]); - - // GSVector4i rb00 = c00 & mask; - // GSVector4i ga00 = (c00 >> 8) & mask; - - movdqa(xmm2, xmm6); - psllw(xmm2, 8); - psrlw(xmm2, 8); - psrlw(xmm6, 8); - - // GSVector4i rb01 = c01 & mask; - // GSVector4i ga01 = (c01 >> 8) & mask; - - movdqa(xmm3, xmm4); - psllw(xmm3, 8); - psrlw(xmm3, 8); - psrlw(xmm4, 8); - - // xmm0 = uf - // xmm2 = rb00 - // xmm3 = rb01 - // xmm6 = ga00 - // xmm4 = ga01 - // xmm1 = c10 - // xmm5 = c11 - // xmm7 = used - - // rb00 = rb00.lerp16<0>(rb01, uf); - // ga00 = ga00.lerp16<0>(ga01, uf); - - lerp16<0>(xmm3, xmm2, xmm0); - lerp16<0>(xmm4, xmm6, xmm0); - - // xmm0 = uf - // xmm3 = rb00 - // xmm4 = ga00 - // xmm1 = c10 - // xmm5 = c11 - // xmm2, xmm6 = free - // xmm7 = used - - // GSVector4i rb10 = c10 & mask; - // GSVector4i ga10 = (c10 >> 8) & mask; - - movdqa(xmm2, xmm1); - psllw(xmm1, 8); - psrlw(xmm1, 8); - psrlw(xmm2, 8); - - // GSVector4i rb11 = c11 & mask; - // GSVector4i ga11 = (c11 >> 8) & mask; - - movdqa(xmm6, xmm5); - psllw(xmm5, 8); - psrlw(xmm5, 8); - psrlw(xmm6, 8); - - // xmm0 = uf - // xmm3 = rb00 - // xmm4 = ga00 - // xmm1 = rb10 - // xmm5 = rb11 - // xmm2 = ga10 - // xmm6 = ga11 - // xmm7 = used - - // rb10 = rb10.lerp16<0>(rb11, uf); - // ga10 = ga10.lerp16<0>(ga11, uf); - - lerp16<0>(xmm5, xmm1, xmm0); - lerp16<0>(xmm6, xmm2, xmm0); - - // xmm3 = rb00 - // xmm4 = ga00 - // xmm5 = rb10 - // xmm6 = ga10 - // xmm0, xmm1, xmm2 = free - // xmm7 = used - - // rb00 = rb00.lerp16<0>(rb10, vf); - // ga00 = ga00.lerp16<0>(ga10, vf); - - movdqa(xmm0, xmmword[&m_env.temp.vf]); - - lerp16<0>(xmm5, xmm3, xmm0); - lerp16<0>(xmm6, xmm4, xmm0); - } - else - { - // GSVector4i addr00 = y0 + x0; - - paddd(xmm2, xmm4); - - // c00 = addr00.gather32_32((const uint32/uint8*)tex[, clut]); - - ReadTexel(xmm5, xmm2, xmm0, xmm1); - - // GSVector4i mask = GSVector4i::x00ff(); - - // c[0] = c00 & mask; - // c[1] = (c00 >> 8) & mask; - - movdqa(xmm6, xmm5); - - psllw(xmm5, 8); - psrlw(xmm5, 8); - psrlw(xmm6, 8); - } -} - -void GSDrawScanlineCodeGenerator::Wrap(const Xmm& uv) -{ - // xmm0, xmm1, xmm4, xmm5, xmm6 = free - - int wms_clamp = ((m_sel.wms + 1) >> 1) & 1; - int wmt_clamp = ((m_sel.wmt + 1) >> 1) & 1; - - int region = ((m_sel.wms | m_sel.wmt) >> 1) & 1; - - if(wms_clamp == wmt_clamp) - { - if(wms_clamp) - { - if(region) - { - pmaxsw(uv, xmmword[&m_env.t.min]); - } - else - { - pxor(xmm0, xmm0); - pmaxsw(uv, xmm0); - } - - pminsw(uv, xmmword[&m_env.t.max]); - } - else - { - pand(uv, xmmword[&m_env.t.min]); - - if(region) - { - por(uv, xmmword[&m_env.t.max]); - } - } - } - else - { - movdqa(xmm1, uv); - - movdqa(xmm4, xmmword[&m_env.t.min]); - movdqa(xmm5, xmmword[&m_env.t.max]); - - // GSVector4i clamp = t.sat_i16(m_env.t.min, m_env.t.max); - - pmaxsw(uv, xmm4); - pminsw(uv, xmm5); - - // GSVector4i repeat = (t & m_env.t.min) | m_env.t.max; - - pand(xmm1, xmm4); - - if(region) - { - por(xmm1, xmm5); - } - - // clamp.blend8(repeat, m_env.t.mask); - - movdqa(xmm0, xmmword[&m_env.t.mask]); - blend8(uv, xmm1); - } -} - -void GSDrawScanlineCodeGenerator::Wrap(const Xmm& uv0, const Xmm& uv1) -{ - // xmm0, xmm1, xmm4, xmm5, xmm6 = free - - int wms_clamp = ((m_sel.wms + 1) >> 1) & 1; - int wmt_clamp = ((m_sel.wmt + 1) >> 1) & 1; - - int region = ((m_sel.wms | m_sel.wmt) >> 1) & 1; - - if(wms_clamp == wmt_clamp) - { - if(wms_clamp) - { - if(region) - { - movdqa(xmm4, xmmword[&m_env.t.min]); - - pmaxsw(uv0, xmm4); - pmaxsw(uv1, xmm4); - } - else - { - pxor(xmm0, xmm0); - pmaxsw(uv0, xmm0); - pmaxsw(uv1, xmm0); - } - - movdqa(xmm5, xmmword[&m_env.t.max]); - - pminsw(uv0, xmm5); - pminsw(uv1, xmm5); - } - else - { - movdqa(xmm4, xmmword[&m_env.t.min]); - - pand(uv0, xmm4); - pand(uv1, xmm4); - - if(region) - { - movdqa(xmm5, xmmword[&m_env.t.max]); - - por(uv0, xmm5); - por(uv1, xmm5); - } - } - } - else - { - movdqa(xmm1, uv0); - movdqa(xmm6, uv1); - - movdqa(xmm4, xmmword[&m_env.t.min]); - movdqa(xmm5, xmmword[&m_env.t.max]); - - // GSVector4i clamp = t.sat_i16(m_env.t.min, m_env.t.max); - - pmaxsw(uv0, xmm4); - pmaxsw(uv1, xmm4); - pminsw(uv0, xmm5); - pminsw(uv1, xmm5); - - // GSVector4i repeat = (t & m_env.t.min) | m_env.t.max; - - pand(xmm1, xmm4); - pand(xmm6, xmm4); - - if(region) - { - por(xmm1, xmm5); - por(xmm6, xmm5); - } - - // clamp.blend8(repeat, m_env.t.mask); - - if(m_cpu.has(util::Cpu::tSSE41)) - { - movdqa(xmm0, xmmword[&m_env.t.mask]); - - pblendvb(uv0, xmm1); - pblendvb(uv1, xmm6); - } - else - { - movdqa(xmm0, xmmword[&m_env.t.invmask]); - movdqa(xmm4, xmm0); - - pand(uv0, xmm0); - pandn(xmm0, xmm1); - por(uv0, xmm0); - - pand(uv1, xmm4); - pandn(xmm4, xmm6); - por(uv1, xmm4); - } - } -} - -void GSDrawScanlineCodeGenerator::AlphaTFX() -{ - if(!m_sel.fb) - { - return; - } - - switch(m_sel.tfx) - { - case TFX_MODULATE: - - // GSVector4i ga = iip ? gaf : m_env.c.ga; - - movdqa(xmm4, xmmword[m_sel.iip ? &m_env.temp.ga : &m_env.c.ga]); - - // gat = gat.modulate16<1>(ga).clamp8(); - - modulate16<1>(xmm6, xmm4); - - clamp16(xmm6, xmm3); - - // if(!tcc) gat = gat.mix16(ga.srl16(7)); - - if(!m_sel.tcc) - { - psrlw(xmm4, 7); - - mix16(xmm6, xmm4, xmm3); - } - - break; - - case TFX_DECAL: - - // if(!tcc) gat = gat.mix16(ga.srl16(7)); - - if(!m_sel.tcc) - { - // GSVector4i ga = iip ? gaf : m_env.c.ga; - - movdqa(xmm4, xmmword[m_sel.iip ? &m_env.temp.ga : &m_env.c.ga]); - - psrlw(xmm4, 7); - - mix16(xmm6, xmm4, xmm3); - } - - break; - - case TFX_HIGHLIGHT: - - // GSVector4i ga = iip ? gaf : m_env.c.ga; - - movdqa(xmm4, xmmword[m_sel.iip ? &m_env.temp.ga : &m_env.c.ga]); - movdqa(xmm2, xmm4); - - // gat = gat.mix16(!tcc ? ga.srl16(7) : gat.addus8(ga.srl16(7))); - - psrlw(xmm4, 7); - - if(m_sel.tcc) - { - paddusb(xmm4, xmm6); - } - - mix16(xmm6, xmm4, xmm3); - - break; - - case TFX_HIGHLIGHT2: - - // if(!tcc) gat = gat.mix16(ga.srl16(7)); - - if(!m_sel.tcc) - { - // GSVector4i ga = iip ? gaf : m_env.c.ga; - - movdqa(xmm4, xmmword[m_sel.iip ? &m_env.temp.ga : &m_env.c.ga]); - movdqa(xmm2, xmm4); - - psrlw(xmm4, 7); - - mix16(xmm6, xmm4, xmm3); - } - - break; - - case TFX_NONE: - - // gat = iip ? ga.srl16(7) : ga; - - if(m_sel.iip) - { - psrlw(xmm6, 7); - } - - break; - } - - if(m_sel.aa1) - { - // gs_user figure 3-2: anti-aliasing after tfx, before tests, modifies alpha - - // FIXME: bios config screen cubes - - if(!m_sel.abe) - { - // a = cov - - if(m_sel.edge) - { - movdqa(xmm0, xmmword[&m_env.temp.cov]); - } - else - { - pcmpeqd(xmm0, xmm0); - psllw(xmm0, 15); - psrlw(xmm0, 8); - } - - mix16(xmm6, xmm0, xmm1); - } - else - { - // a = a == 0x80 ? cov : a - - pcmpeqd(xmm0, xmm0); - psllw(xmm0, 15); - psrlw(xmm0, 8); - - if(m_sel.edge) - { - movdqa(xmm1, xmmword[&m_env.temp.cov]); - } - else - { - movdqa(xmm1, xmm0); - } - - pcmpeqw(xmm0, xmm6); - psrld(xmm0, 16); - pslld(xmm0, 16); - - blend8(xmm6, xmm1); - } - } -} - -void GSDrawScanlineCodeGenerator::TestAlpha() -{ - switch(m_sel.afail) - { - case AFAIL_FB_ONLY: - if(!m_sel.zwrite) return; - break; - - case AFAIL_ZB_ONLY: - if(!m_sel.fwrite) return; - break; - - case AFAIL_RGB_ONLY: - if(!m_sel.zwrite && m_sel.fpsm == 1) return; - break; - } - - switch(m_sel.atst) - { - case ATST_NEVER: - // t = GSVector4i::xffffffff(); - pcmpeqd(xmm1, xmm1); - break; - - case ATST_ALWAYS: - return; - - case ATST_LESS: - case ATST_LEQUAL: - // t = (ga >> 16) > m_env.aref; - movdqa(xmm1, xmm6); - psrld(xmm1, 16); - pcmpgtd(xmm1, xmmword[&m_env.aref]); - break; - - case ATST_EQUAL: - // t = (ga >> 16) != m_env.aref; - movdqa(xmm1, xmm6); - psrld(xmm1, 16); - pcmpeqd(xmm1, xmmword[&m_env.aref]); - pcmpeqd(xmm0, xmm0); - pxor(xmm1, xmm0); - break; - - case ATST_GEQUAL: - case ATST_GREATER: - // t = (ga >> 16) < m_env.aref; - movdqa(xmm0, xmm6); - psrld(xmm0, 16); - movdqa(xmm1, xmmword[&m_env.aref]); - pcmpgtd(xmm1, xmm0); - break; - - case ATST_NOTEQUAL: - // t = (ga >> 16) == m_env.aref; - movdqa(xmm1, xmm6); - psrld(xmm1, 16); - pcmpeqd(xmm1, xmmword[&m_env.aref]); - break; - } - - switch(m_sel.afail) - { - case AFAIL_KEEP: - // test |= t; - por(xmm7, xmm1); - alltrue(); - break; - - case AFAIL_FB_ONLY: - // zm |= t; - por(xmm4, xmm1); - break; - - case AFAIL_ZB_ONLY: - // fm |= t; - por(xmm3, xmm1); - break; - - case AFAIL_RGB_ONLY: - // zm |= t; - por(xmm4, xmm1); - // fm |= t & GSVector4i::xff000000(); - psrld(xmm1, 24); - pslld(xmm1, 24); - por(xmm3, xmm1); - break; - } -} - -void GSDrawScanlineCodeGenerator::ColorTFX() -{ - if(!m_sel.fwrite) - { - return; - } - - switch(m_sel.tfx) - { - case TFX_MODULATE: - - // GSVector4i rb = iip ? rbf : m_env.c.rb; - - // rbt = rbt.modulate16<1>(rb).clamp8(); - - modulate16<1>(xmm5, xmmword[m_sel.iip ? &m_env.temp.rb : &m_env.c.rb]); - - clamp16(xmm5, xmm1); - - break; - - case TFX_DECAL: - - break; - - case TFX_HIGHLIGHT: - case TFX_HIGHLIGHT2: - - if(m_sel.tfx == TFX_HIGHLIGHT2 && m_sel.tcc) - { - // GSVector4i ga = iip ? gaf : m_env.c.ga; - - movdqa(xmm2, xmmword[m_sel.iip ? &m_env.temp.ga : &m_env.c.ga]); - } - - // gat = gat.modulate16<1>(ga).add16(af).clamp8().mix16(gat); - - movdqa(xmm1, xmm6); - - modulate16<1>(xmm6, xmm2); - - pshuflw(xmm2, xmm2, _MM_SHUFFLE(3, 3, 1, 1)); - pshufhw(xmm2, xmm2, _MM_SHUFFLE(3, 3, 1, 1)); - psrlw(xmm2, 7); - - paddw(xmm6, xmm2); - - clamp16(xmm6, xmm0); - - mix16(xmm6, xmm1, xmm0); - - // GSVector4i rb = iip ? rbf : m_env.c.rb; - - // rbt = rbt.modulate16<1>(rb).add16(af).clamp8(); - - modulate16<1>(xmm5, xmmword[m_sel.iip ? &m_env.temp.rb : &m_env.c.rb]); - - paddw(xmm5, xmm2); - - clamp16(xmm5, xmm0); - - break; - - case TFX_NONE: - - // rbt = iip ? rb.srl16(7) : rb; - - if(m_sel.iip) - { - psrlw(xmm5, 7); - } - - break; - } -} - -void GSDrawScanlineCodeGenerator::Fog() -{ - if(!m_sel.fwrite || !m_sel.fge) - { - return; - } - - // rb = m_env.frb.lerp16<0>(rb, f); - // ga = m_env.fga.lerp16<0>(ga, f).mix16(ga); - - movdqa(xmm0, xmmword[!m_sel.sprite ? &m_env.temp.f : &m_env.p.f]); - movdqa(xmm1, xmm6); - - movdqa(xmm2, xmmword[&m_env.frb]); - lerp16<0>(xmm5, xmm2, xmm0); - - movdqa(xmm2, xmmword[&m_env.fga]); - lerp16<0>(xmm6, xmm2, xmm0); - - mix16(xmm6, xmm1, xmm0); -} - -void GSDrawScanlineCodeGenerator::ReadFrame() -{ - if(!m_sel.fb) - { - return; - } - - // int fa = fza_base.x + fza_offset->x; - - mov(ebx, dword[esi]); - add(ebx, dword[edi]); - - if(!m_sel.rfb) - { - return; - } - - ReadPixel(xmm2, ebx); -} - -void GSDrawScanlineCodeGenerator::TestDestAlpha() -{ - if(!m_sel.date || m_sel.fpsm != 0 && m_sel.fpsm != 2) - { - return; - } - - // test |= ((fd [<< 16]) ^ m_env.datm).sra32(31); - - movdqa(xmm1, xmm2); - - if(m_sel.datm) - { - if(m_sel.fpsm == 2) - { - pxor(xmm0, xmm0); - psrld(xmm1, 15); - pcmpeqd(xmm1, xmm0); - } - else - { - pcmpeqd(xmm0, xmm0); - pxor(xmm1, xmm0); - psrad(xmm1, 31); - } - } - else - { - if(m_sel.fpsm == 2) - { - pslld(xmm1, 16); - } - - psrad(xmm1, 31); - } - - por(xmm7, xmm1); - - alltrue(); -} - -void GSDrawScanlineCodeGenerator::WriteZBuf() -{ - if(!m_sel.zwrite) - { - return; - } - - movdqa(xmm1, xmmword[!m_sel.sprite ? &m_env.temp.zs : &m_env.p.z]); - - bool fast = false; - - if(m_sel.ztest && m_sel.zpsm < 2) - { - // zs = zs.blend8(zd, zm); - - movdqa(xmm0, xmm4); - movdqa(xmm7, xmmword[&m_env.temp.zd]); - blend8(xmm1, xmm7); - - fast = true; - } - - WritePixel(xmm1, xmm0, ebp, dh, fast, m_sel.zpsm); -} - -void GSDrawScanlineCodeGenerator::AlphaBlend() -{ - if(!m_sel.fwrite) - { - return; - } - - if(m_sel.abe == 0 && m_sel.aa1 == 0) - { - return; - } - - if((m_sel.aba != m_sel.abb) && (m_sel.aba == 1 || m_sel.abb == 1 || m_sel.abc == 1) || m_sel.abd == 1) - { - switch(m_sel.fpsm) - { - case 0: - case 1: - - // c[2] = fd & mask; - // c[3] = (fd >> 8) & mask; - - movdqa(xmm0, xmm2); - movdqa(xmm1, xmm2); - - psllw(xmm0, 8); - psrlw(xmm0, 8); - psrlw(xmm1, 8); - - break; - - case 2: - - // c[2] = ((fd & 0x7c00) << 9) | ((fd & 0x001f) << 3); - // c[3] = ((fd & 0x8000) << 8) | ((fd & 0x03e0) >> 2); - - movdqa(xmm0, xmm2); - movdqa(xmm1, xmm2); - movdqa(xmm4, xmm2); - - pcmpeqd(xmm7, xmm7); - psrld(xmm7, 27); // 0x0000001f - pand(xmm0, xmm7); - pslld(xmm0, 3); - - pslld(xmm7, 10); // 0x00007c00 - pand(xmm4, xmm7); - pslld(xmm4, 9); - - por(xmm0, xmm4); - - movdqa(xmm4, xmm1); - - psrld(xmm7, 5); // 0x000003e0 - pand(xmm1, xmm7); - psrld(xmm1, 2); - - psllw(xmm7, 10); // 0x00008000 - pand(xmm4, xmm7); - pslld(xmm4, 8); - - por(xmm1, xmm4); - - break; - } - } - - // xmm5, xmm6 = src rb, ga - // xmm0, xmm1 = dst rb, ga - // xmm2, xmm3 = used - // xmm4, xmm7 = free - - if(m_sel.pabe || (m_sel.aba != m_sel.abb) && (m_sel.abb == 0 || m_sel.abd == 0)) - { - movdqa(xmm4, xmm5); - } - - if(m_sel.aba != m_sel.abb) - { - // rb = c[aba * 2 + 0]; - - switch(m_sel.aba) - { - case 0: break; - case 1: movdqa(xmm5, xmm0); break; - case 2: pxor(xmm5, xmm5); break; - } - - // rb = rb.sub16(c[abb * 2 + 0]); - - switch(m_sel.abb) - { - case 0: psubw(xmm5, xmm4); break; - case 1: psubw(xmm5, xmm0); break; - case 2: break; - } - - if(!(m_sel.fpsm == 1 && m_sel.abc == 1)) - { - // GSVector4i a = abc < 2 ? c[abc * 2 + 1].yywwlh().sll16(7) : m_env.afix; - - switch(m_sel.abc) - { - case 0: - case 1: - movdqa(xmm7, m_sel.abc ? xmm1 : xmm6); - pshuflw(xmm7, xmm7, _MM_SHUFFLE(3, 3, 1, 1)); - pshufhw(xmm7, xmm7, _MM_SHUFFLE(3, 3, 1, 1)); - psllw(xmm7, 7); - break; - case 2: - movdqa(xmm7, xmmword[&m_env.afix]); - break; - } - - // rb = rb.modulate16<1>(a); - - modulate16<1>(xmm5, xmm7); - } - - // rb = rb.add16(c[abd * 2 + 0]); - - switch(m_sel.abd) - { - case 0: paddw(xmm5, xmm4); break; - case 1: paddw(xmm5, xmm0); break; - case 2: break; - } - } - else - { - // rb = c[abd * 2 + 0]; - - switch(m_sel.abd) - { - case 0: break; - case 1: movdqa(xmm5, xmm0); break; - case 2: pxor(xmm5, xmm5); break; - } - } - - if(m_sel.pabe) - { - // mask = (c[1] << 8).sra32(31); - - movdqa(xmm0, xmm6); - pslld(xmm0, 8); - psrad(xmm0, 31); - - // rb = c[0].blend8(rb, mask); - - blend8r(xmm5, xmm4); - } - - // xmm6 = src ga - // xmm1 = dst ga - // xmm5 = rb - // xmm7 = a - // xmm2, xmm3 = used - // xmm0, xmm4 = free - - movdqa(xmm4, xmm6); - - if(m_sel.aba != m_sel.abb) - { - // ga = c[aba * 2 + 1]; - - switch(m_sel.aba) - { - case 0: break; - case 1: movdqa(xmm6, xmm1); break; - case 2: pxor(xmm6, xmm6); break; - } - - // ga = ga.sub16(c[abeb * 2 + 1]); - - switch(m_sel.abb) - { - case 0: psubw(xmm6, xmm4); break; - case 1: psubw(xmm6, xmm1); break; - case 2: break; - } - - if(!(m_sel.fpsm == 1 && m_sel.abc == 1)) - { - // ga = ga.modulate16<1>(a); - - modulate16<1>(xmm6, xmm7); - } - - // ga = ga.add16(c[abd * 2 + 1]); - - switch(m_sel.abd) - { - case 0: paddw(xmm6, xmm4); break; - case 1: paddw(xmm6, xmm1); break; - case 2: break; - } - } - else - { - // ga = c[abd * 2 + 1]; - - switch(m_sel.abd) - { - case 0: break; - case 1: movdqa(xmm6, xmm1); break; - case 2: pxor(xmm6, xmm6); break; - } - } - - // xmm4 = src ga - // xmm5 = rb - // xmm6 = ga - // xmm2, xmm3 = used - // xmm0, xmm1, xmm7 = free - - if(m_sel.pabe) - { - if(!m_cpu.has(util::Cpu::tSSE41)) - { - // doh, previous blend8r overwrote xmm0 (sse41 uses pblendvb) - - movdqa(xmm0, xmm4); - pslld(xmm0, 8); - psrad(xmm0, 31); - } - - psrld(xmm0, 16); // zero out high words to select the source alpha in blend (so it also does mix16) - - // ga = c[1].blend8(ga, mask).mix16(c[1]); - - blend8r(xmm6, xmm4); - } - else - { - if(m_sel.fpsm != 1) // TODO: fm == 0xffxxxxxx - { - mix16(xmm6, xmm4, xmm7); - } - } -} - -void GSDrawScanlineCodeGenerator::WriteFrame(int params) -{ - const int _top = params + 4; - - if(!m_sel.fwrite) - { - return; - } - - if(m_sel.colclamp == 0) - { - // c[0] &= 0x000000ff; - // c[1] &= 0x000000ff; - - pcmpeqd(xmm7, xmm7); - psrlw(xmm7, 8); - pand(xmm5, xmm7); - pand(xmm6, xmm7); - } - - if(m_sel.fpsm == 2 && m_sel.dthe) - { - mov(eax, dword[esp + _top]); - and(eax, 3); - shl(eax, 5); - paddw(xmm5, xmmword[eax + (size_t)&m_env.dimx[0]]); - paddw(xmm6, xmmword[eax + (size_t)&m_env.dimx[1]]); - } - - // GSVector4i fs = c[0].upl16(c[1]).pu16(c[0].uph16(c[1])); - - movdqa(xmm7, xmm5); - punpcklwd(xmm5, xmm6); - punpckhwd(xmm7, xmm6); - packuswb(xmm5, xmm7); - - if(m_sel.fba && m_sel.fpsm != 1) - { - // fs |= 0x80000000; - - pcmpeqd(xmm7, xmm7); - pslld(xmm7, 31); - por(xmm5, xmm7); - } - - if(m_sel.fpsm == 2) - { - // GSVector4i rb = fs & 0x00f800f8; - // GSVector4i ga = fs & 0x8000f800; - - mov(eax, 0x00f800f8); - movd(xmm6, eax); - pshufd(xmm6, xmm6, _MM_SHUFFLE(0, 0, 0, 0)); - - mov(eax, 0x8000f800); - movd(xmm7, eax); - pshufd(xmm7, xmm7, _MM_SHUFFLE(0, 0, 0, 0)); - - movdqa(xmm4, xmm5); - pand(xmm4, xmm6); - pand(xmm5, xmm7); - - // fs = (ga >> 16) | (rb >> 9) | (ga >> 6) | (rb >> 3); - - movdqa(xmm6, xmm4); - movdqa(xmm7, xmm5); - - psrld(xmm4, 3); - psrld(xmm6, 9); - psrld(xmm5, 6); - psrld(xmm7, 16); - - por(xmm5, xmm4); - por(xmm7, xmm6); - por(xmm5, xmm7); - } - - if(m_sel.rfb) - { - // fs = fs.blend(fd, fm); - - blend(xmm5, xmm2, xmm3); // TODO: could be skipped in certain cases, depending on fpsm and fm - } - - bool fast = m_sel.rfb && m_sel.fpsm < 2; - - WritePixel(xmm5, xmm0, ebx, dl, fast, m_sel.fpsm); -} - -void GSDrawScanlineCodeGenerator::ReadPixel(const Xmm& dst, const Reg32& addr) -{ - movq(dst, qword[addr * 2 + (size_t)m_env.vm]); - movhps(dst, qword[addr * 2 + (size_t)m_env.vm + 8 * 2]); -} - -void GSDrawScanlineCodeGenerator::WritePixel(const Xmm& src, const Xmm& temp, const Reg32& addr, const Reg8& mask, bool fast, int psm) -{ - if(fast) - { - // if(fzm & 0x0f) GSVector4i::storel(&vm16[addr + 0], fs); - // if(fzm & 0xf0) GSVector4i::storeh(&vm16[addr + 8], fs); - - test(mask, 0x0f); - je("@f"); - movq(qword[addr * 2 + (size_t)m_env.vm], src); - L("@@"); - - test(mask, 0xf0); - je("@f"); - movhps(qword[addr * 2 + (size_t)m_env.vm + 8 * 2], src); - L("@@"); - } - else - { - // if(fzm & 0x03) WritePixel(fpsm, &vm16[addr + 0], fs.extract32<0>()); - // if(fzm & 0x0c) WritePixel(fpsm, &vm16[addr + 2], fs.extract32<1>()); - // if(fzm & 0x30) WritePixel(fpsm, &vm16[addr + 8], fs.extract32<2>()); - // if(fzm & 0xc0) WritePixel(fpsm, &vm16[addr + 10], fs.extract32<3>()); - - test(mask, 0x03); - je("@f"); - WritePixel(src, temp, addr, 0, psm); - L("@@"); - - test(mask, 0x0c); - je("@f"); - WritePixel(src, temp, addr, 1, psm); - L("@@"); - - test(mask, 0x30); - je("@f"); - WritePixel(src, temp, addr, 2, psm); - L("@@"); - - test(mask, 0xc0); - je("@f"); - WritePixel(src, temp, addr, 3, psm); - L("@@"); - } -} - -void GSDrawScanlineCodeGenerator::WritePixel(const Xmm& src, const Xmm& temp, const Reg32& addr, uint8 i, int psm) -{ - static const int offsets[4] = {0, 2, 8, 10}; - - Address dst = ptr[addr * 2 + (size_t)m_env.vm + offsets[i] * 2]; - - if(m_cpu.has(util::Cpu::tSSE41)) - { - switch(psm) - { - case 0: - if(i == 0) movd(dst, src); - else pextrd(dst, src, i); - break; - case 1: - if(i == 0) movd(eax, src); - else pextrd(eax, src, i); - xor(eax, dst); - and(eax, 0xffffff); - xor(dst, eax); - break; - case 2: - pextrw(eax, src, i * 2); - mov(dst, ax); - break; - } - } - else - { - switch(psm) - { - case 0: - if(i == 0) movd(dst, src); - else {pshufd(temp, src, _MM_SHUFFLE(i, i, i, i)); movd(dst, temp);} - break; - case 1: - if(i == 0) movd(eax, src); - else {pshufd(temp, src, _MM_SHUFFLE(i, i, i, i)); movd(eax, temp);} - xor(eax, dst); - and(eax, 0xffffff); - xor(dst, eax); - break; - case 2: - pextrw(eax, src, i * 2); - mov(dst, ax); - break; - } - } -} - -void GSDrawScanlineCodeGenerator::ReadTexel(const Xmm& dst, const Xmm& addr, const Xmm& temp1, const Xmm& temp2) -{ - if(m_cpu.has(util::Cpu::tSSE41)) - { - ReadTexel(dst, addr, 0); - ReadTexel(dst, addr, 1); - ReadTexel(dst, addr, 2); - ReadTexel(dst, addr, 3); - } - else - { - ReadTexel(dst, addr, 0); - psrldq(addr, 4); // shuffle instead? (1 2 3 0 ~ rotation) - ReadTexel(temp1, addr, 0); - psrldq(addr, 4); - punpckldq(dst, temp1); - - ReadTexel(temp1, addr, 0); - psrldq(addr, 4); - ReadTexel(temp2, addr, 0); - // psrldq(addr, 4); - punpckldq(temp1, temp2); - - punpcklqdq(dst, temp1); - } -} - -void GSDrawScanlineCodeGenerator::ReadTexel(const Xmm& dst, const Xmm& addr, uint8 i) -{ - if(!m_cpu.has(util::Cpu::tSSE41) && i > 0) - { - ASSERT(0); - } - - if(i == 0) movd(eax, addr); - else pextrd(eax, addr, i); - - if(m_sel.tlu) movzx(eax, byte[ebx + eax]); - - const Address& src = m_sel.tlu ? ptr[eax * 4 + (size_t)m_env.clut] : ptr[ebx + eax * 4]; - - if(i == 0) movd(dst, src); - else pinsrd(dst, src, i); -} - -template -void GSDrawScanlineCodeGenerator::modulate16(const Xmm& a, const Operand& f) -{ - if(shift == 0 && m_cpu.has(util::Cpu::tSSSE3)) - { - pmulhrsw(a, f); - } - else - { - psllw(a, shift + 1); - pmulhw(a, f); - } -} - -template -void GSDrawScanlineCodeGenerator::lerp16(const Xmm& a, const Xmm& b, const Xmm& f) -{ - psubw(a, b); - modulate16(a, f); - paddw(a, b); -} - -void GSDrawScanlineCodeGenerator::mix16(const Xmm& a, const Xmm& b, const Xmm& temp) -{ - if(m_cpu.has(util::Cpu::tSSE41)) - { - pblendw(a, b, 0xaa); - } - else - { - pcmpeqd(temp, temp); - psrld(temp, 16); - pand(a, temp); - pandn(temp, b); - por(a, temp); - } -} - -void GSDrawScanlineCodeGenerator::clamp16(const Xmm& a, const Xmm& temp) -{ - packuswb(a, a); - - if(m_cpu.has(util::Cpu::tSSE41)) - { - pmovzxbw(a, a); - } - else - { - pxor(temp, temp); - punpcklbw(a, temp); - } -} - -void GSDrawScanlineCodeGenerator::alltrue() -{ - pmovmskb(eax, xmm7); - cmp(eax, 0xffff); - je("step", T_NEAR); -} - -void GSDrawScanlineCodeGenerator::blend8(const Xmm& a, const Xmm& b) -{ - if(m_cpu.has(util::Cpu::tSSE41)) - { - pblendvb(a, b); - } - else - { - blend(a, b, xmm0); - } -} - -void GSDrawScanlineCodeGenerator::blend(const Xmm& a, const Xmm& b, const Xmm& mask) -{ - pand(b, mask); - pandn(mask, a); - por(b, mask); - movdqa(a, b); -} - -void GSDrawScanlineCodeGenerator::blend8r(const Xmm& b, const Xmm& a) -{ - if(m_cpu.has(util::Cpu::tSSE41)) - { - pblendvb(a, b); - movdqa(b, a); - } - else - { - blendr(b, a, xmm0); - } -} - -void GSDrawScanlineCodeGenerator::blendr(const Xmm& b, const Xmm& a, const Xmm& mask) -{ - pand(b, mask); - pandn(mask, a); - por(b, mask); -} - -const GSVector4i GSDrawScanlineCodeGenerator::m_test[8] = -{ - GSVector4i::zero(), - GSVector4i(0xffffffff, 0x00000000, 0x00000000, 0x00000000), - GSVector4i(0xffffffff, 0xffffffff, 0x00000000, 0x00000000), - GSVector4i(0xffffffff, 0xffffffff, 0xffffffff, 0x00000000), - GSVector4i(0x00000000, 0xffffffff, 0xffffffff, 0xffffffff), - GSVector4i(0x00000000, 0x00000000, 0xffffffff, 0xffffffff), - GSVector4i(0x00000000, 0x00000000, 0x00000000, 0xffffffff), - GSVector4i::zero(), -}; +/* + * Copyright (C) 2007-2009 Gabest + * http://www.gabest.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program 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 GNU Make; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +// TODO: x64 (use the extra regs to avoid spills of zs, zd, uf, vf, rb, ga and keep a few constants in the last two like aref or afix) +// TODO: for edges doing 4 pixels is wasteful (needed memory access * 4) + +#include "StdAfx.h" +#include "GSDrawScanlineCodeGenerator.h" + +GSDrawScanlineCodeGenerator::GSDrawScanlineCodeGenerator(GSScanlineEnvironment& env, uint64 key, void* ptr, size_t maxsize) + : CodeGenerator(maxsize, ptr) + , m_env(env) +{ + #if _M_AMD64 + #error TODO + #endif + + m_sel.key = key; + + Generate(); +} + +void GSDrawScanlineCodeGenerator::Generate() +{ + push(ebx); + push(esi); + push(edi); + push(ebp); + + const int params = 16; + + Init(params); + + if(!m_sel.edge) + { + align(16); + } + +L("loop"); + + // ecx = steps + // esi = fzbr + // edi = fzbc + // xmm0 = z/zi + // xmm2 = u (tme) + // xmm3 = v (tme) + // xmm5 = rb (!tme) + // xmm6 = ga (!tme) + // xmm7 = test + + bool tme = m_sel.tfx != TFX_NONE; + + TestZ(tme ? xmm5 : xmm2, tme ? xmm6 : xmm3); + + // ecx = steps + // esi = fzbr + // edi = fzbc + // - xmm0 + // xmm2 = u (tme) + // xmm3 = v (tme) + // xmm5 = rb (!tme) + // xmm6 = ga (!tme) + // xmm7 = test + + SampleTexture(); + + // ecx = steps + // esi = fzbr + // edi = fzbc + // ebp = za + // - xmm2 + // - xmm3 + // - xmm4 + // xmm5 = rb + // xmm6 = ga + // xmm7 = test + + AlphaTFX(); + + // ecx = steps + // esi = fzbr + // edi = fzbc + // ebp = za + // xmm2 = gaf (TFX_HIGHLIGHT || TFX_HIGHLIGHT2 && !tcc) + // xmm5 = rb + // xmm6 = ga + // xmm7 = test + + if(m_sel.fwrite) + { + movdqa(xmm3, xmmword[&m_env.fm]); + } + + if(m_sel.zwrite) + { + movdqa(xmm4, xmmword[&m_env.zm]); + } + + // ecx = steps + // esi = fzbr + // edi = fzbc + // ebp = za + // xmm2 = gaf (TFX_HIGHLIGHT || TFX_HIGHLIGHT2 && !tcc) + // xmm3 = fm + // xmm4 = zm + // xmm5 = rb + // xmm6 = ga + // xmm7 = test + + TestAlpha(); + + // ecx = steps + // esi = fzbr + // edi = fzbc + // ebp = za + // xmm2 = gaf (TFX_HIGHLIGHT || TFX_HIGHLIGHT2 && !tcc) + // xmm3 = fm + // xmm4 = zm + // xmm5 = rb + // xmm6 = ga + // xmm7 = test + + ColorTFX(); + + // ecx = steps + // esi = fzbr + // edi = fzbc + // ebp = za + // xmm3 = fm + // xmm4 = zm + // xmm5 = rb + // xmm6 = ga + // xmm7 = test + + Fog(); + + // ecx = steps + // esi = fzbr + // edi = fzbc + // ebp = za + // xmm3 = fm + // xmm4 = zm + // xmm5 = rb + // xmm6 = ga + // xmm7 = test + + ReadFrame(); + + // ecx = steps + // esi = fzbr + // edi = fzbc + // ebp = za + // xmm2 = fd + // xmm3 = fm + // xmm4 = zm + // xmm5 = rb + // xmm6 = ga + // xmm7 = test + + TestDestAlpha(); + + // fm |= test; + // zm |= test; + + if(m_sel.fwrite) + { + por(xmm3, xmm7); + } + + if(m_sel.zwrite) + { + por(xmm4, xmm7); + } + + // int fzm = ~(fm == GSVector4i::xffffffff()).ps32(zm == GSVector4i::xffffffff()).mask(); + + pcmpeqd(xmm1, xmm1); + + if(m_sel.fwrite && m_sel.zwrite) + { + movdqa(xmm0, xmm1); + pcmpeqd(xmm1, xmm3); + pcmpeqd(xmm0, xmm4); + packssdw(xmm1, xmm0); + } + else if(m_sel.fwrite) + { + pcmpeqd(xmm1, xmm3); + packssdw(xmm1, xmm1); + } + else if(m_sel.zwrite) + { + pcmpeqd(xmm1, xmm4); + packssdw(xmm1, xmm1); + } + + pmovmskb(edx, xmm1); + not(edx); + + // ebx = fa + // ecx = steps + // edx = fzm + // esi = fzbr + // edi = fzbc + // ebp = za + // xmm2 = fd + // xmm3 = fm + // xmm4 = zm + // xmm5 = rb + // xmm6 = ga + + WriteZBuf(); + + // ebx = fa + // ecx = steps + // edx = fzm + // esi = fzbr + // edi = fzbc + // - ebp + // xmm2 = fd + // xmm3 = fm + // - xmm4 + // xmm5 = rb + // xmm6 = ga + + AlphaBlend(); + + // ebx = fa + // ecx = steps + // edx = fzm + // esi = fzbr + // edi = fzbc + // xmm2 = fd + // xmm3 = fm + // xmm5 = rb + // xmm6 = ga + + WriteFrame(params); + +L("step"); + + // if(steps <= 0) break; + + if(!m_sel.edge) + { + test(ecx, ecx); + jle("exit", T_NEAR); + + Step(); + + jmp("loop", T_NEAR); + } + +L("exit"); + + pop(ebp); + pop(edi); + pop(esi); + pop(ebx); + + ret(8); +} + +void GSDrawScanlineCodeGenerator::Init(int params) +{ + const int _top = params + 4; + const int _v = params + 8; + + // int skip = left & 3; + + mov(ebx, edx); + and(edx, 3); + + // left -= skip; + + sub(ebx, edx); + + // int steps = right - left - 4; + + sub(ecx, ebx); + sub(ecx, 4); + + // GSVector4i test = m_test[skip] | m_test[7 + (steps & (steps >> 31))]; + + shl(edx, 4); + + movdqa(xmm7, xmmword[edx + (size_t)&m_test[0]]); + + mov(eax, ecx); + sar(eax, 31); + and(eax, ecx); + shl(eax, 4); + + por(xmm7, xmmword[eax + (size_t)&m_test[7]]); + + // GSVector2i* fza_base = &m_env.fzbr[top]; + + mov(esi, dword[esp + _top]); + lea(esi, ptr[esi * 8]); + add(esi, dword[&m_env.fzbr]); + + // GSVector2i* fza_offset = &m_env.fzbc[left >> 2]; + + lea(edi, ptr[ebx * 2]); + add(edi, dword[&m_env.fzbc]); + + if(!m_sel.sprite && (m_sel.fwrite && m_sel.fge || m_sel.zb) || m_sel.fb && (m_sel.edge || m_sel.tfx != TFX_NONE || m_sel.iip)) + { + // edx = &m_env.d[skip] + + shl(edx, 4); + lea(edx, ptr[edx + (size_t)m_env.d]); + + // ebx = &v + + mov(ebx, dword[esp + _v]); + } + + if(!m_sel.sprite) + { + if(m_sel.fwrite && m_sel.fge || m_sel.zb) + { + movaps(xmm0, xmmword[ebx + 16]); // v.p + + if(m_sel.fwrite && m_sel.fge) + { + // f = GSVector4i(vp).zzzzh().zzzz().add16(m_env.d[skip].f); + + cvttps2dq(xmm1, xmm0); + pshufhw(xmm1, xmm1, _MM_SHUFFLE(2, 2, 2, 2)); + pshufd(xmm1, xmm1, _MM_SHUFFLE(2, 2, 2, 2)); + paddw(xmm1, xmmword[edx + 16 * 6]); + + movdqa(xmmword[&m_env.temp.f], xmm1); + } + + if(m_sel.zb) + { + // z = vp.zzzz() + m_env.d[skip].z; + + shufps(xmm0, xmm0, _MM_SHUFFLE(2, 2, 2, 2)); + addps(xmm0, xmmword[edx]); + + movaps(xmmword[&m_env.temp.z], xmm0); + } + } + } + else + { + if(m_sel.ztest) + { + movdqa(xmm0, xmmword[&m_env.p.z]); + } + } + + if(m_sel.fb) + { + if(m_sel.edge || m_sel.tfx != TFX_NONE) + { + movaps(xmm4, xmmword[ebx + 32]); // v.t + } + + if(m_sel.edge) + { + pshufhw(xmm3, xmm4, _MM_SHUFFLE(2, 2, 2, 2)); + pshufd(xmm3, xmm3, _MM_SHUFFLE(3, 3, 3, 3)); + psrlw(xmm3, 9); + + movdqa(xmmword[&m_env.temp.cov], xmm3); + } + + if(m_sel.tfx != TFX_NONE) + { + if(m_sel.fst) + { + // GSVector4i vti(vt); + + cvttps2dq(xmm4, xmm4); + + // si = vti.xxxx() + m_env.d[skip].si; + // ti = vti.yyyy(); if(!sprite) ti += m_env.d[skip].ti; + + pshufd(xmm2, xmm4, _MM_SHUFFLE(0, 0, 0, 0)); + pshufd(xmm3, xmm4, _MM_SHUFFLE(1, 1, 1, 1)); + + paddd(xmm2, xmmword[edx + 16 * 7]); + + if(!m_sel.sprite) + { + paddd(xmm3, xmmword[edx + 16 * 8]); + } + else + { + if(m_sel.ltf) + { + movdqa(xmm4, xmm3); + pshuflw(xmm4, xmm4, _MM_SHUFFLE(2, 2, 0, 0)); + pshufhw(xmm4, xmm4, _MM_SHUFFLE(2, 2, 0, 0)); + psrlw(xmm4, 1); + movdqa(xmmword[&m_env.temp.vf], xmm4); + } + } + + movdqa(xmmword[&m_env.temp.s], xmm2); + movdqa(xmmword[&m_env.temp.t], xmm3); + } + else + { + // s = vt.xxxx() + m_env.d[skip].s; + // t = vt.yyyy() + m_env.d[skip].t; + // q = vt.zzzz() + m_env.d[skip].q; + + movaps(xmm2, xmm4); + movaps(xmm3, xmm4); + + shufps(xmm2, xmm2, _MM_SHUFFLE(0, 0, 0, 0)); + shufps(xmm3, xmm3, _MM_SHUFFLE(1, 1, 1, 1)); + shufps(xmm4, xmm4, _MM_SHUFFLE(2, 2, 2, 2)); + + addps(xmm2, xmmword[edx + 16 * 1]); + addps(xmm3, xmmword[edx + 16 * 2]); + addps(xmm4, xmmword[edx + 16 * 3]); + + movaps(xmmword[&m_env.temp.s], xmm2); + movaps(xmmword[&m_env.temp.t], xmm3); + movaps(xmmword[&m_env.temp.q], xmm4); + + rcpps(xmm4, xmm4); + mulps(xmm2, xmm4); + mulps(xmm3, xmm4); + } + } + + if(!(m_sel.tfx == TFX_DECAL && m_sel.tcc)) + { + if(m_sel.iip) + { + // GSVector4i vc = GSVector4i(v.c); + + cvttps2dq(xmm6, xmmword[ebx]); // v.c + + // vc = vc.upl16(vc.zwxy()); + + pshufd(xmm5, xmm6, _MM_SHUFFLE(1, 0, 3, 2)); + punpcklwd(xmm6, xmm5); + + // rb = vc.xxxx().add16(m_env.d[skip].rb); + // ga = vc.zzzz().add16(m_env.d[skip].ga); + + pshufd(xmm5, xmm6, _MM_SHUFFLE(0, 0, 0, 0)); + pshufd(xmm6, xmm6, _MM_SHUFFLE(2, 2, 2, 2)); + + paddw(xmm5, xmmword[edx + 16 * 4]); + paddw(xmm6, xmmword[edx + 16 * 5]); + + movdqa(xmmword[&m_env.temp.rb], xmm5); + movdqa(xmmword[&m_env.temp.ga], xmm6); + } + else + { + if(m_sel.tfx == TFX_NONE) + { + movdqa(xmm5, xmmword[&m_env.c.rb]); + movdqa(xmm6, xmmword[&m_env.c.ga]); + } + } + } + } +} + +void GSDrawScanlineCodeGenerator::Step() +{ + // steps -= 4; + + sub(ecx, 4); + + // fza_offset++; + + add(edi, 8); + + if(!m_sel.sprite) + { + // z += m_env.d4.z; + + if(m_sel.zb) + { + movaps(xmm0, xmmword[&m_env.temp.z]); + addps(xmm0, xmmword[&m_env.d4.z]); + movaps(xmmword[&m_env.temp.z], xmm0); + } + + // f = f.add16(m_env.d4.f); + + if(m_sel.fwrite && m_sel.fge) + { + movdqa(xmm1, xmmword[&m_env.temp.f]); + paddw(xmm1, xmmword[&m_env.d4.f]); + movdqa(xmmword[&m_env.temp.f], xmm1); + } + } + else + { + if(m_sel.ztest) + { + movdqa(xmm0, xmmword[&m_env.p.z]); + } + } + + if(m_sel.fb) + { + if(m_sel.tfx != TFX_NONE) + { + if(m_sel.fst) + { + // GSVector4i st = m_env.d4.st; + + // si += st.xxxx(); + // if(!sprite) ti += st.yyyy(); + + movdqa(xmm4, xmmword[&m_env.d4.st]); + + pshufd(xmm2, xmm4, _MM_SHUFFLE(0, 0, 0, 0)); + paddd(xmm2, xmmword[&m_env.temp.s]); + movdqa(xmmword[&m_env.temp.s], xmm2); + + if(!m_sel.sprite) + { + pshufd(xmm3, xmm4, _MM_SHUFFLE(1, 1, 1, 1)); + paddd(xmm3, xmmword[&m_env.temp.t]); + movdqa(xmmword[&m_env.temp.t], xmm3); + } + else + { + movdqa(xmm3, xmmword[&m_env.temp.t]); + } + } + else + { + // GSVector4 stq = m_env.d4.stq; + + // s += stq.xxxx(); + // t += stq.yyyy(); + // q += stq.zzzz(); + + movaps(xmm2, xmmword[&m_env.d4.stq]); + movaps(xmm3, xmm2); + movaps(xmm4, xmm2); + + shufps(xmm2, xmm2, _MM_SHUFFLE(0, 0, 0, 0)); + shufps(xmm3, xmm3, _MM_SHUFFLE(1, 1, 1, 1)); + shufps(xmm4, xmm4, _MM_SHUFFLE(2, 2, 2, 2)); + + addps(xmm2, xmmword[&m_env.temp.s]); + addps(xmm3, xmmword[&m_env.temp.t]); + addps(xmm4, xmmword[&m_env.temp.q]); + + movaps(xmmword[&m_env.temp.s], xmm2); + movaps(xmmword[&m_env.temp.t], xmm3); + movaps(xmmword[&m_env.temp.q], xmm4); + + rcpps(xmm4, xmm4); + mulps(xmm2, xmm4); + mulps(xmm3, xmm4); + } + } + + if(!(m_sel.tfx == TFX_DECAL && m_sel.tcc)) + { + if(m_sel.iip) + { + // GSVector4i c = m_env.d4.c; + + // rb = rb.add16(c.xxxx()); + // ga = ga.add16(c.yyyy()); + + movdqa(xmm7, xmmword[&m_env.d4.c]); + + pshufd(xmm5, xmm7, _MM_SHUFFLE(0, 0, 0, 0)); + pshufd(xmm6, xmm7, _MM_SHUFFLE(1, 1, 1, 1)); + + paddw(xmm5, xmmword[&m_env.temp.rb]); + paddw(xmm6, xmmword[&m_env.temp.ga]); + + movdqa(xmmword[&m_env.temp.rb], xmm5); + movdqa(xmmword[&m_env.temp.ga], xmm6); + } + else + { + if(m_sel.tfx == TFX_NONE) + { + movdqa(xmm5, xmmword[&m_env.c.rb]); + movdqa(xmm6, xmmword[&m_env.c.ga]); + } + } + } + } + + // test = m_test[7 + (steps & (steps >> 31))]; + + mov(edx, ecx); + sar(edx, 31); + and(edx, ecx); + shl(edx, 4); + + movdqa(xmm7, xmmword[edx + (size_t)&m_test[7]]); +} + +void GSDrawScanlineCodeGenerator::TestZ(const Xmm& temp1, const Xmm& temp2) +{ + if(!m_sel.zb) + { + return; + } + + // int za = fza_base.y + fza_offset->y; + + mov(ebp, dword[esi + 4]); + add(ebp, dword[edi + 4]); + + // GSVector4i zs = zi; + + if(!m_sel.sprite) + { + if(m_sel.zoverflow) + { + // zs = (GSVector4i(z * 0.5f) << 1) | (GSVector4i(z) & GSVector4i::x00000001()); + + static float half = 0.5f; + + movss(temp1, dword[&half]); + shufps(temp1, temp1, _MM_SHUFFLE(0, 0, 0, 0)); + mulps(temp1, xmm0); + cvttps2dq(temp1, temp1); + pslld(temp1, 1); + + cvttps2dq(xmm0, xmm0); + pcmpeqd(temp2, temp2); + psrld(temp2, 31); + pand(xmm0, temp2); + + por(xmm0, temp1); + } + else + { + // zs = GSVector4i(z); + + cvttps2dq(xmm0, xmm0); + } + + if(m_sel.zwrite) + { + movdqa(xmmword[&m_env.temp.zs], xmm0); + } + } + + if(m_sel.ztest) + { + ReadPixel(xmm1, ebp); + + if(m_sel.zwrite && m_sel.zpsm < 2) + { + movdqa(xmmword[&m_env.temp.zd], xmm1); + } + + // zd &= 0xffffffff >> m_sel.zpsm * 8; + + if(m_sel.zpsm) + { + pslld(xmm1, m_sel.zpsm * 8); + psrld(xmm1, m_sel.zpsm * 8); + } + + if(m_sel.zoverflow || m_sel.zpsm == 0) + { + // GSVector4i o = GSVector4i::x80000000(); + + pcmpeqd(xmm4, xmm4); + pslld(xmm4, 31); + + // GSVector4i zso = zs - o; + + psubd(xmm0, xmm4); + + // GSVector4i zdo = zd - o; + + psubd(xmm1, xmm4); + } + + switch(m_sel.ztst) + { + case ZTST_GEQUAL: + // test |= zso < zdo; // ~(zso >= zdo) + pcmpgtd(xmm1, xmm0); + por(xmm7, xmm1); + break; + + case ZTST_GREATER: // TODO: tidus hair and chocobo wings only appear fully when this is tested as ZTST_GEQUAL + // test |= zso <= zdo; // ~(zso > zdo) + pcmpgtd(xmm0, xmm1); + pcmpeqd(xmm4, xmm4); + pxor(xmm0, xmm4); + por(xmm7, xmm0); + break; + } + + alltrue(); + } +} + +void GSDrawScanlineCodeGenerator::SampleTexture() +{ + if(!m_sel.fb || m_sel.tfx == TFX_NONE) + { + return; + } + + mov(ebx, dword[&m_env.tex]); + + // ebx = tex + + if(!m_sel.fst) + { + // TODO: move these into Init/Step too? + + cvttps2dq(xmm2, xmm2); + cvttps2dq(xmm3, xmm3); + + if(m_sel.ltf) + { + // u -= 0x8000; + // v -= 0x8000; + + mov(eax, 0x8000); + movd(xmm4, eax); + pshufd(xmm4, xmm4, _MM_SHUFFLE(0, 0, 0, 0)); + psubd(xmm2, xmm4); + psubd(xmm3, xmm4); + } + } + + // xmm2 = u + // xmm3 = v + + if(m_sel.ltf) + { + // GSVector4i uf = u.xxzzlh().srl16(1); + + movdqa(xmm0, xmm2); + pshuflw(xmm0, xmm0, _MM_SHUFFLE(2, 2, 0, 0)); + pshufhw(xmm0, xmm0, _MM_SHUFFLE(2, 2, 0, 0)); + psrlw(xmm0, 1); + movdqa(xmmword[&m_env.temp.uf], xmm0); + + if(!m_sel.sprite) + { + // GSVector4i vf = v.xxzzlh().srl16(1); + + movdqa(xmm1, xmm3); + pshuflw(xmm1, xmm1, _MM_SHUFFLE(2, 2, 0, 0)); + pshufhw(xmm1, xmm1, _MM_SHUFFLE(2, 2, 0, 0)); + psrlw(xmm1, 1); + movdqa(xmmword[&m_env.temp.vf], xmm1); + } + } + + // GSVector4i uv0 = u.sra32(16).ps32(v.sra32(16)); + + psrad(xmm2, 16); + psrad(xmm3, 16); + packssdw(xmm2, xmm3); + + if(m_sel.ltf) + { + // GSVector4i uv1 = uv0.add16(GSVector4i::x0001()); + + movdqa(xmm3, xmm2); + pcmpeqd(xmm1, xmm1); + psrlw(xmm1, 15); + paddw(xmm3, xmm1); + + // uv0 = Wrap(uv0); + // uv1 = Wrap(uv1); + + Wrap(xmm2, xmm3); + } + else + { + // uv0 = Wrap(uv0); + + Wrap(xmm2); + } + + // xmm2 = uv0 + // xmm3 = uv1 (ltf) + // xmm0, xmm1, xmm4, xmm5, xmm6 = free + // xmm7 = used + + // GSVector4i y0 = uv0.uph16() << tw; + // GSVector4i x0 = uv0.upl16(); + + pxor(xmm0, xmm0); + movd(xmm1, ptr[&m_env.tw]); + + movdqa(xmm4, xmm2); + punpckhwd(xmm2, xmm0); + punpcklwd(xmm4, xmm0); + pslld(xmm2, xmm1); + + // xmm0 = 0 + // xmm1 = tw + // xmm2 = y0 + // xmm3 = uv1 (ltf) + // xmm4 = x0 + // xmm5, xmm6 = free + // xmm7 = used + + if(m_sel.ltf) + { + // GSVector4i y1 = uv1.uph16() << tw; + // GSVector4i x1 = uv1.upl16(); + + movdqa(xmm6, xmm3); + punpckhwd(xmm3, xmm0); + punpcklwd(xmm6, xmm0); + pslld(xmm3, xmm1); + + // xmm2 = y0 + // xmm3 = y1 + // xmm4 = x0 + // xmm6 = x1 + // xmm0, xmm5, xmm6 = free + // xmm7 = used + + // GSVector4i addr00 = y0 + x0; + // GSVector4i addr01 = y0 + x1; + // GSVector4i addr10 = y1 + x0; + // GSVector4i addr11 = y1 + x1; + + movdqa(xmm5, xmm2); + paddd(xmm5, xmm4); + paddd(xmm2, xmm6); + + movdqa(xmm0, xmm3); + paddd(xmm0, xmm4); + paddd(xmm3, xmm6); + + // xmm5 = addr00 + // xmm2 = addr01 + // xmm0 = addr10 + // xmm3 = addr11 + // xmm1, xmm4, xmm6 = free + // xmm7 = used + + // c00 = addr00.gather32_32((const uint32/uint8*)tex[, clut]); + // c01 = addr01.gather32_32((const uint32/uint8*)tex[, clut]); + // c10 = addr10.gather32_32((const uint32/uint8*)tex[, clut]); + // c11 = addr11.gather32_32((const uint32/uint8*)tex[, clut]); + + ReadTexel(xmm6, xmm5, xmm1, xmm4); + + // xmm2, xmm5, xmm1 = free + + ReadTexel(xmm4, xmm2, xmm5, xmm1); + + // xmm0, xmm2, xmm5 = free + + ReadTexel(xmm1, xmm0, xmm2, xmm5); + + // xmm3, xmm0, xmm2 = free + + ReadTexel(xmm5, xmm3, xmm0, xmm2); + + // xmm6 = c00 + // xmm4 = c01 + // xmm1 = c10 + // xmm5 = c11 + // xmm0, xmm2, xmm3 = free + // xmm7 = used + + movdqa(xmm0, xmmword[&m_env.temp.uf]); + + // GSVector4i rb00 = c00 & mask; + // GSVector4i ga00 = (c00 >> 8) & mask; + + movdqa(xmm2, xmm6); + psllw(xmm2, 8); + psrlw(xmm2, 8); + psrlw(xmm6, 8); + + // GSVector4i rb01 = c01 & mask; + // GSVector4i ga01 = (c01 >> 8) & mask; + + movdqa(xmm3, xmm4); + psllw(xmm3, 8); + psrlw(xmm3, 8); + psrlw(xmm4, 8); + + // xmm0 = uf + // xmm2 = rb00 + // xmm3 = rb01 + // xmm6 = ga00 + // xmm4 = ga01 + // xmm1 = c10 + // xmm5 = c11 + // xmm7 = used + + // rb00 = rb00.lerp16<0>(rb01, uf); + // ga00 = ga00.lerp16<0>(ga01, uf); + + lerp16<0>(xmm3, xmm2, xmm0); + lerp16<0>(xmm4, xmm6, xmm0); + + // xmm0 = uf + // xmm3 = rb00 + // xmm4 = ga00 + // xmm1 = c10 + // xmm5 = c11 + // xmm2, xmm6 = free + // xmm7 = used + + // GSVector4i rb10 = c10 & mask; + // GSVector4i ga10 = (c10 >> 8) & mask; + + movdqa(xmm2, xmm1); + psllw(xmm1, 8); + psrlw(xmm1, 8); + psrlw(xmm2, 8); + + // GSVector4i rb11 = c11 & mask; + // GSVector4i ga11 = (c11 >> 8) & mask; + + movdqa(xmm6, xmm5); + psllw(xmm5, 8); + psrlw(xmm5, 8); + psrlw(xmm6, 8); + + // xmm0 = uf + // xmm3 = rb00 + // xmm4 = ga00 + // xmm1 = rb10 + // xmm5 = rb11 + // xmm2 = ga10 + // xmm6 = ga11 + // xmm7 = used + + // rb10 = rb10.lerp16<0>(rb11, uf); + // ga10 = ga10.lerp16<0>(ga11, uf); + + lerp16<0>(xmm5, xmm1, xmm0); + lerp16<0>(xmm6, xmm2, xmm0); + + // xmm3 = rb00 + // xmm4 = ga00 + // xmm5 = rb10 + // xmm6 = ga10 + // xmm0, xmm1, xmm2 = free + // xmm7 = used + + // rb00 = rb00.lerp16<0>(rb10, vf); + // ga00 = ga00.lerp16<0>(ga10, vf); + + movdqa(xmm0, xmmword[&m_env.temp.vf]); + + lerp16<0>(xmm5, xmm3, xmm0); + lerp16<0>(xmm6, xmm4, xmm0); + } + else + { + // GSVector4i addr00 = y0 + x0; + + paddd(xmm2, xmm4); + + // c00 = addr00.gather32_32((const uint32/uint8*)tex[, clut]); + + ReadTexel(xmm5, xmm2, xmm0, xmm1); + + // GSVector4i mask = GSVector4i::x00ff(); + + // c[0] = c00 & mask; + // c[1] = (c00 >> 8) & mask; + + movdqa(xmm6, xmm5); + + psllw(xmm5, 8); + psrlw(xmm5, 8); + psrlw(xmm6, 8); + } +} + +void GSDrawScanlineCodeGenerator::Wrap(const Xmm& uv) +{ + // xmm0, xmm1, xmm4, xmm5, xmm6 = free + + int wms_clamp = ((m_sel.wms + 1) >> 1) & 1; + int wmt_clamp = ((m_sel.wmt + 1) >> 1) & 1; + + int region = ((m_sel.wms | m_sel.wmt) >> 1) & 1; + + if(wms_clamp == wmt_clamp) + { + if(wms_clamp) + { + if(region) + { + pmaxsw(uv, xmmword[&m_env.t.min]); + } + else + { + pxor(xmm0, xmm0); + pmaxsw(uv, xmm0); + } + + pminsw(uv, xmmword[&m_env.t.max]); + } + else + { + pand(uv, xmmword[&m_env.t.min]); + + if(region) + { + por(uv, xmmword[&m_env.t.max]); + } + } + } + else + { + movdqa(xmm1, uv); + + movdqa(xmm4, xmmword[&m_env.t.min]); + movdqa(xmm5, xmmword[&m_env.t.max]); + + // GSVector4i clamp = t.sat_i16(m_env.t.min, m_env.t.max); + + pmaxsw(uv, xmm4); + pminsw(uv, xmm5); + + // GSVector4i repeat = (t & m_env.t.min) | m_env.t.max; + + pand(xmm1, xmm4); + + if(region) + { + por(xmm1, xmm5); + } + + // clamp.blend8(repeat, m_env.t.mask); + + movdqa(xmm0, xmmword[&m_env.t.mask]); + blend8(uv, xmm1); + } +} + +void GSDrawScanlineCodeGenerator::Wrap(const Xmm& uv0, const Xmm& uv1) +{ + // xmm0, xmm1, xmm4, xmm5, xmm6 = free + + int wms_clamp = ((m_sel.wms + 1) >> 1) & 1; + int wmt_clamp = ((m_sel.wmt + 1) >> 1) & 1; + + int region = ((m_sel.wms | m_sel.wmt) >> 1) & 1; + + if(wms_clamp == wmt_clamp) + { + if(wms_clamp) + { + if(region) + { + movdqa(xmm4, xmmword[&m_env.t.min]); + + pmaxsw(uv0, xmm4); + pmaxsw(uv1, xmm4); + } + else + { + pxor(xmm0, xmm0); + pmaxsw(uv0, xmm0); + pmaxsw(uv1, xmm0); + } + + movdqa(xmm5, xmmword[&m_env.t.max]); + + pminsw(uv0, xmm5); + pminsw(uv1, xmm5); + } + else + { + movdqa(xmm4, xmmword[&m_env.t.min]); + + pand(uv0, xmm4); + pand(uv1, xmm4); + + if(region) + { + movdqa(xmm5, xmmword[&m_env.t.max]); + + por(uv0, xmm5); + por(uv1, xmm5); + } + } + } + else + { + movdqa(xmm1, uv0); + movdqa(xmm6, uv1); + + movdqa(xmm4, xmmword[&m_env.t.min]); + movdqa(xmm5, xmmword[&m_env.t.max]); + + // GSVector4i clamp = t.sat_i16(m_env.t.min, m_env.t.max); + + pmaxsw(uv0, xmm4); + pmaxsw(uv1, xmm4); + pminsw(uv0, xmm5); + pminsw(uv1, xmm5); + + // GSVector4i repeat = (t & m_env.t.min) | m_env.t.max; + + pand(xmm1, xmm4); + pand(xmm6, xmm4); + + if(region) + { + por(xmm1, xmm5); + por(xmm6, xmm5); + } + + // clamp.blend8(repeat, m_env.t.mask); + + if(m_cpu.has(util::Cpu::tSSE41)) + { + movdqa(xmm0, xmmword[&m_env.t.mask]); + + pblendvb(uv0, xmm1); + pblendvb(uv1, xmm6); + } + else + { + movdqa(xmm0, xmmword[&m_env.t.invmask]); + movdqa(xmm4, xmm0); + + pand(uv0, xmm0); + pandn(xmm0, xmm1); + por(uv0, xmm0); + + pand(uv1, xmm4); + pandn(xmm4, xmm6); + por(uv1, xmm4); + } + } +} + +void GSDrawScanlineCodeGenerator::AlphaTFX() +{ + if(!m_sel.fb) + { + return; + } + + switch(m_sel.tfx) + { + case TFX_MODULATE: + + // GSVector4i ga = iip ? gaf : m_env.c.ga; + + movdqa(xmm4, xmmword[m_sel.iip ? &m_env.temp.ga : &m_env.c.ga]); + + // gat = gat.modulate16<1>(ga).clamp8(); + + modulate16<1>(xmm6, xmm4); + + clamp16(xmm6, xmm3); + + // if(!tcc) gat = gat.mix16(ga.srl16(7)); + + if(!m_sel.tcc) + { + psrlw(xmm4, 7); + + mix16(xmm6, xmm4, xmm3); + } + + break; + + case TFX_DECAL: + + // if(!tcc) gat = gat.mix16(ga.srl16(7)); + + if(!m_sel.tcc) + { + // GSVector4i ga = iip ? gaf : m_env.c.ga; + + movdqa(xmm4, xmmword[m_sel.iip ? &m_env.temp.ga : &m_env.c.ga]); + + psrlw(xmm4, 7); + + mix16(xmm6, xmm4, xmm3); + } + + break; + + case TFX_HIGHLIGHT: + + // GSVector4i ga = iip ? gaf : m_env.c.ga; + + movdqa(xmm4, xmmword[m_sel.iip ? &m_env.temp.ga : &m_env.c.ga]); + movdqa(xmm2, xmm4); + + // gat = gat.mix16(!tcc ? ga.srl16(7) : gat.addus8(ga.srl16(7))); + + psrlw(xmm4, 7); + + if(m_sel.tcc) + { + paddusb(xmm4, xmm6); + } + + mix16(xmm6, xmm4, xmm3); + + break; + + case TFX_HIGHLIGHT2: + + // if(!tcc) gat = gat.mix16(ga.srl16(7)); + + if(!m_sel.tcc) + { + // GSVector4i ga = iip ? gaf : m_env.c.ga; + + movdqa(xmm4, xmmword[m_sel.iip ? &m_env.temp.ga : &m_env.c.ga]); + movdqa(xmm2, xmm4); + + psrlw(xmm4, 7); + + mix16(xmm6, xmm4, xmm3); + } + + break; + + case TFX_NONE: + + // gat = iip ? ga.srl16(7) : ga; + + if(m_sel.iip) + { + psrlw(xmm6, 7); + } + + break; + } + + if(m_sel.aa1) + { + // gs_user figure 3-2: anti-aliasing after tfx, before tests, modifies alpha + + // FIXME: bios config screen cubes + + if(!m_sel.abe) + { + // a = cov + + if(m_sel.edge) + { + movdqa(xmm0, xmmword[&m_env.temp.cov]); + } + else + { + pcmpeqd(xmm0, xmm0); + psllw(xmm0, 15); + psrlw(xmm0, 8); + } + + mix16(xmm6, xmm0, xmm1); + } + else + { + // a = a == 0x80 ? cov : a + + pcmpeqd(xmm0, xmm0); + psllw(xmm0, 15); + psrlw(xmm0, 8); + + if(m_sel.edge) + { + movdqa(xmm1, xmmword[&m_env.temp.cov]); + } + else + { + movdqa(xmm1, xmm0); + } + + pcmpeqw(xmm0, xmm6); + psrld(xmm0, 16); + pslld(xmm0, 16); + + blend8(xmm6, xmm1); + } + } +} + +void GSDrawScanlineCodeGenerator::TestAlpha() +{ + switch(m_sel.afail) + { + case AFAIL_FB_ONLY: + if(!m_sel.zwrite) return; + break; + + case AFAIL_ZB_ONLY: + if(!m_sel.fwrite) return; + break; + + case AFAIL_RGB_ONLY: + if(!m_sel.zwrite && m_sel.fpsm == 1) return; + break; + } + + switch(m_sel.atst) + { + case ATST_NEVER: + // t = GSVector4i::xffffffff(); + pcmpeqd(xmm1, xmm1); + break; + + case ATST_ALWAYS: + return; + + case ATST_LESS: + case ATST_LEQUAL: + // t = (ga >> 16) > m_env.aref; + movdqa(xmm1, xmm6); + psrld(xmm1, 16); + pcmpgtd(xmm1, xmmword[&m_env.aref]); + break; + + case ATST_EQUAL: + // t = (ga >> 16) != m_env.aref; + movdqa(xmm1, xmm6); + psrld(xmm1, 16); + pcmpeqd(xmm1, xmmword[&m_env.aref]); + pcmpeqd(xmm0, xmm0); + pxor(xmm1, xmm0); + break; + + case ATST_GEQUAL: + case ATST_GREATER: + // t = (ga >> 16) < m_env.aref; + movdqa(xmm0, xmm6); + psrld(xmm0, 16); + movdqa(xmm1, xmmword[&m_env.aref]); + pcmpgtd(xmm1, xmm0); + break; + + case ATST_NOTEQUAL: + // t = (ga >> 16) == m_env.aref; + movdqa(xmm1, xmm6); + psrld(xmm1, 16); + pcmpeqd(xmm1, xmmword[&m_env.aref]); + break; + } + + switch(m_sel.afail) + { + case AFAIL_KEEP: + // test |= t; + por(xmm7, xmm1); + alltrue(); + break; + + case AFAIL_FB_ONLY: + // zm |= t; + por(xmm4, xmm1); + break; + + case AFAIL_ZB_ONLY: + // fm |= t; + por(xmm3, xmm1); + break; + + case AFAIL_RGB_ONLY: + // zm |= t; + por(xmm4, xmm1); + // fm |= t & GSVector4i::xff000000(); + psrld(xmm1, 24); + pslld(xmm1, 24); + por(xmm3, xmm1); + break; + } +} + +void GSDrawScanlineCodeGenerator::ColorTFX() +{ + if(!m_sel.fwrite) + { + return; + } + + switch(m_sel.tfx) + { + case TFX_MODULATE: + + // GSVector4i rb = iip ? rbf : m_env.c.rb; + + // rbt = rbt.modulate16<1>(rb).clamp8(); + + modulate16<1>(xmm5, xmmword[m_sel.iip ? &m_env.temp.rb : &m_env.c.rb]); + + clamp16(xmm5, xmm1); + + break; + + case TFX_DECAL: + + break; + + case TFX_HIGHLIGHT: + case TFX_HIGHLIGHT2: + + if(m_sel.tfx == TFX_HIGHLIGHT2 && m_sel.tcc) + { + // GSVector4i ga = iip ? gaf : m_env.c.ga; + + movdqa(xmm2, xmmword[m_sel.iip ? &m_env.temp.ga : &m_env.c.ga]); + } + + // gat = gat.modulate16<1>(ga).add16(af).clamp8().mix16(gat); + + movdqa(xmm1, xmm6); + + modulate16<1>(xmm6, xmm2); + + pshuflw(xmm2, xmm2, _MM_SHUFFLE(3, 3, 1, 1)); + pshufhw(xmm2, xmm2, _MM_SHUFFLE(3, 3, 1, 1)); + psrlw(xmm2, 7); + + paddw(xmm6, xmm2); + + clamp16(xmm6, xmm0); + + mix16(xmm6, xmm1, xmm0); + + // GSVector4i rb = iip ? rbf : m_env.c.rb; + + // rbt = rbt.modulate16<1>(rb).add16(af).clamp8(); + + modulate16<1>(xmm5, xmmword[m_sel.iip ? &m_env.temp.rb : &m_env.c.rb]); + + paddw(xmm5, xmm2); + + clamp16(xmm5, xmm0); + + break; + + case TFX_NONE: + + // rbt = iip ? rb.srl16(7) : rb; + + if(m_sel.iip) + { + psrlw(xmm5, 7); + } + + break; + } +} + +void GSDrawScanlineCodeGenerator::Fog() +{ + if(!m_sel.fwrite || !m_sel.fge) + { + return; + } + + // rb = m_env.frb.lerp16<0>(rb, f); + // ga = m_env.fga.lerp16<0>(ga, f).mix16(ga); + + movdqa(xmm0, xmmword[!m_sel.sprite ? &m_env.temp.f : &m_env.p.f]); + movdqa(xmm1, xmm6); + + movdqa(xmm2, xmmword[&m_env.frb]); + lerp16<0>(xmm5, xmm2, xmm0); + + movdqa(xmm2, xmmword[&m_env.fga]); + lerp16<0>(xmm6, xmm2, xmm0); + + mix16(xmm6, xmm1, xmm0); +} + +void GSDrawScanlineCodeGenerator::ReadFrame() +{ + if(!m_sel.fb) + { + return; + } + + // int fa = fza_base.x + fza_offset->x; + + mov(ebx, dword[esi]); + add(ebx, dword[edi]); + + if(!m_sel.rfb) + { + return; + } + + ReadPixel(xmm2, ebx); +} + +void GSDrawScanlineCodeGenerator::TestDestAlpha() +{ + if(!m_sel.date || m_sel.fpsm != 0 && m_sel.fpsm != 2) + { + return; + } + + // test |= ((fd [<< 16]) ^ m_env.datm).sra32(31); + + movdqa(xmm1, xmm2); + + if(m_sel.datm) + { + if(m_sel.fpsm == 2) + { + pxor(xmm0, xmm0); + psrld(xmm1, 15); + pcmpeqd(xmm1, xmm0); + } + else + { + pcmpeqd(xmm0, xmm0); + pxor(xmm1, xmm0); + psrad(xmm1, 31); + } + } + else + { + if(m_sel.fpsm == 2) + { + pslld(xmm1, 16); + } + + psrad(xmm1, 31); + } + + por(xmm7, xmm1); + + alltrue(); +} + +void GSDrawScanlineCodeGenerator::WriteZBuf() +{ + if(!m_sel.zwrite) + { + return; + } + + movdqa(xmm1, xmmword[!m_sel.sprite ? &m_env.temp.zs : &m_env.p.z]); + + bool fast = false; + + if(m_sel.ztest && m_sel.zpsm < 2) + { + // zs = zs.blend8(zd, zm); + + movdqa(xmm0, xmm4); + movdqa(xmm7, xmmword[&m_env.temp.zd]); + blend8(xmm1, xmm7); + + fast = true; + } + + WritePixel(xmm1, xmm0, ebp, dh, fast, m_sel.zpsm); +} + +void GSDrawScanlineCodeGenerator::AlphaBlend() +{ + if(!m_sel.fwrite) + { + return; + } + + if(m_sel.abe == 0 && m_sel.aa1 == 0) + { + return; + } + + if((m_sel.aba != m_sel.abb) && (m_sel.aba == 1 || m_sel.abb == 1 || m_sel.abc == 1) || m_sel.abd == 1) + { + switch(m_sel.fpsm) + { + case 0: + case 1: + + // c[2] = fd & mask; + // c[3] = (fd >> 8) & mask; + + movdqa(xmm0, xmm2); + movdqa(xmm1, xmm2); + + psllw(xmm0, 8); + psrlw(xmm0, 8); + psrlw(xmm1, 8); + + break; + + case 2: + + // c[2] = ((fd & 0x7c00) << 9) | ((fd & 0x001f) << 3); + // c[3] = ((fd & 0x8000) << 8) | ((fd & 0x03e0) >> 2); + + movdqa(xmm0, xmm2); + movdqa(xmm1, xmm2); + movdqa(xmm4, xmm2); + + pcmpeqd(xmm7, xmm7); + psrld(xmm7, 27); // 0x0000001f + pand(xmm0, xmm7); + pslld(xmm0, 3); + + pslld(xmm7, 10); // 0x00007c00 + pand(xmm4, xmm7); + pslld(xmm4, 9); + + por(xmm0, xmm4); + + movdqa(xmm4, xmm1); + + psrld(xmm7, 5); // 0x000003e0 + pand(xmm1, xmm7); + psrld(xmm1, 2); + + psllw(xmm7, 10); // 0x00008000 + pand(xmm4, xmm7); + pslld(xmm4, 8); + + por(xmm1, xmm4); + + break; + } + } + + // xmm5, xmm6 = src rb, ga + // xmm0, xmm1 = dst rb, ga + // xmm2, xmm3 = used + // xmm4, xmm7 = free + + if(m_sel.pabe || (m_sel.aba != m_sel.abb) && (m_sel.abb == 0 || m_sel.abd == 0)) + { + movdqa(xmm4, xmm5); + } + + if(m_sel.aba != m_sel.abb) + { + // rb = c[aba * 2 + 0]; + + switch(m_sel.aba) + { + case 0: break; + case 1: movdqa(xmm5, xmm0); break; + case 2: pxor(xmm5, xmm5); break; + } + + // rb = rb.sub16(c[abb * 2 + 0]); + + switch(m_sel.abb) + { + case 0: psubw(xmm5, xmm4); break; + case 1: psubw(xmm5, xmm0); break; + case 2: break; + } + + if(!(m_sel.fpsm == 1 && m_sel.abc == 1)) + { + // GSVector4i a = abc < 2 ? c[abc * 2 + 1].yywwlh().sll16(7) : m_env.afix; + + switch(m_sel.abc) + { + case 0: + case 1: + movdqa(xmm7, m_sel.abc ? xmm1 : xmm6); + pshuflw(xmm7, xmm7, _MM_SHUFFLE(3, 3, 1, 1)); + pshufhw(xmm7, xmm7, _MM_SHUFFLE(3, 3, 1, 1)); + psllw(xmm7, 7); + break; + case 2: + movdqa(xmm7, xmmword[&m_env.afix]); + break; + } + + // rb = rb.modulate16<1>(a); + + modulate16<1>(xmm5, xmm7); + } + + // rb = rb.add16(c[abd * 2 + 0]); + + switch(m_sel.abd) + { + case 0: paddw(xmm5, xmm4); break; + case 1: paddw(xmm5, xmm0); break; + case 2: break; + } + } + else + { + // rb = c[abd * 2 + 0]; + + switch(m_sel.abd) + { + case 0: break; + case 1: movdqa(xmm5, xmm0); break; + case 2: pxor(xmm5, xmm5); break; + } + } + + if(m_sel.pabe) + { + // mask = (c[1] << 8).sra32(31); + + movdqa(xmm0, xmm6); + pslld(xmm0, 8); + psrad(xmm0, 31); + + // rb = c[0].blend8(rb, mask); + + blend8r(xmm5, xmm4); + } + + // xmm6 = src ga + // xmm1 = dst ga + // xmm5 = rb + // xmm7 = a + // xmm2, xmm3 = used + // xmm0, xmm4 = free + + movdqa(xmm4, xmm6); + + if(m_sel.aba != m_sel.abb) + { + // ga = c[aba * 2 + 1]; + + switch(m_sel.aba) + { + case 0: break; + case 1: movdqa(xmm6, xmm1); break; + case 2: pxor(xmm6, xmm6); break; + } + + // ga = ga.sub16(c[abeb * 2 + 1]); + + switch(m_sel.abb) + { + case 0: psubw(xmm6, xmm4); break; + case 1: psubw(xmm6, xmm1); break; + case 2: break; + } + + if(!(m_sel.fpsm == 1 && m_sel.abc == 1)) + { + // ga = ga.modulate16<1>(a); + + modulate16<1>(xmm6, xmm7); + } + + // ga = ga.add16(c[abd * 2 + 1]); + + switch(m_sel.abd) + { + case 0: paddw(xmm6, xmm4); break; + case 1: paddw(xmm6, xmm1); break; + case 2: break; + } + } + else + { + // ga = c[abd * 2 + 1]; + + switch(m_sel.abd) + { + case 0: break; + case 1: movdqa(xmm6, xmm1); break; + case 2: pxor(xmm6, xmm6); break; + } + } + + // xmm4 = src ga + // xmm5 = rb + // xmm6 = ga + // xmm2, xmm3 = used + // xmm0, xmm1, xmm7 = free + + if(m_sel.pabe) + { + if(!m_cpu.has(util::Cpu::tSSE41)) + { + // doh, previous blend8r overwrote xmm0 (sse41 uses pblendvb) + + movdqa(xmm0, xmm4); + pslld(xmm0, 8); + psrad(xmm0, 31); + } + + psrld(xmm0, 16); // zero out high words to select the source alpha in blend (so it also does mix16) + + // ga = c[1].blend8(ga, mask).mix16(c[1]); + + blend8r(xmm6, xmm4); + } + else + { + if(m_sel.fpsm != 1) // TODO: fm == 0xffxxxxxx + { + mix16(xmm6, xmm4, xmm7); + } + } +} + +void GSDrawScanlineCodeGenerator::WriteFrame(int params) +{ + const int _top = params + 4; + + if(!m_sel.fwrite) + { + return; + } + + if(m_sel.colclamp == 0) + { + // c[0] &= 0x000000ff; + // c[1] &= 0x000000ff; + + pcmpeqd(xmm7, xmm7); + psrlw(xmm7, 8); + pand(xmm5, xmm7); + pand(xmm6, xmm7); + } + + if(m_sel.fpsm == 2 && m_sel.dthe) + { + mov(eax, dword[esp + _top]); + and(eax, 3); + shl(eax, 5); + paddw(xmm5, xmmword[eax + (size_t)&m_env.dimx[0]]); + paddw(xmm6, xmmword[eax + (size_t)&m_env.dimx[1]]); + } + + // GSVector4i fs = c[0].upl16(c[1]).pu16(c[0].uph16(c[1])); + + movdqa(xmm7, xmm5); + punpcklwd(xmm5, xmm6); + punpckhwd(xmm7, xmm6); + packuswb(xmm5, xmm7); + + if(m_sel.fba && m_sel.fpsm != 1) + { + // fs |= 0x80000000; + + pcmpeqd(xmm7, xmm7); + pslld(xmm7, 31); + por(xmm5, xmm7); + } + + if(m_sel.fpsm == 2) + { + // GSVector4i rb = fs & 0x00f800f8; + // GSVector4i ga = fs & 0x8000f800; + + mov(eax, 0x00f800f8); + movd(xmm6, eax); + pshufd(xmm6, xmm6, _MM_SHUFFLE(0, 0, 0, 0)); + + mov(eax, 0x8000f800); + movd(xmm7, eax); + pshufd(xmm7, xmm7, _MM_SHUFFLE(0, 0, 0, 0)); + + movdqa(xmm4, xmm5); + pand(xmm4, xmm6); + pand(xmm5, xmm7); + + // fs = (ga >> 16) | (rb >> 9) | (ga >> 6) | (rb >> 3); + + movdqa(xmm6, xmm4); + movdqa(xmm7, xmm5); + + psrld(xmm4, 3); + psrld(xmm6, 9); + psrld(xmm5, 6); + psrld(xmm7, 16); + + por(xmm5, xmm4); + por(xmm7, xmm6); + por(xmm5, xmm7); + } + + if(m_sel.rfb) + { + // fs = fs.blend(fd, fm); + + blend(xmm5, xmm2, xmm3); // TODO: could be skipped in certain cases, depending on fpsm and fm + } + + bool fast = m_sel.rfb && m_sel.fpsm < 2; + + WritePixel(xmm5, xmm0, ebx, dl, fast, m_sel.fpsm); +} + +void GSDrawScanlineCodeGenerator::ReadPixel(const Xmm& dst, const Reg32& addr) +{ + movq(dst, qword[addr * 2 + (size_t)m_env.vm]); + movhps(dst, qword[addr * 2 + (size_t)m_env.vm + 8 * 2]); +} + +void GSDrawScanlineCodeGenerator::WritePixel(const Xmm& src, const Xmm& temp, const Reg32& addr, const Reg8& mask, bool fast, int psm) +{ + if(fast) + { + // if(fzm & 0x0f) GSVector4i::storel(&vm16[addr + 0], fs); + // if(fzm & 0xf0) GSVector4i::storeh(&vm16[addr + 8], fs); + + test(mask, 0x0f); + je("@f"); + movq(qword[addr * 2 + (size_t)m_env.vm], src); + L("@@"); + + test(mask, 0xf0); + je("@f"); + movhps(qword[addr * 2 + (size_t)m_env.vm + 8 * 2], src); + L("@@"); + } + else + { + // if(fzm & 0x03) WritePixel(fpsm, &vm16[addr + 0], fs.extract32<0>()); + // if(fzm & 0x0c) WritePixel(fpsm, &vm16[addr + 2], fs.extract32<1>()); + // if(fzm & 0x30) WritePixel(fpsm, &vm16[addr + 8], fs.extract32<2>()); + // if(fzm & 0xc0) WritePixel(fpsm, &vm16[addr + 10], fs.extract32<3>()); + + test(mask, 0x03); + je("@f"); + WritePixel(src, temp, addr, 0, psm); + L("@@"); + + test(mask, 0x0c); + je("@f"); + WritePixel(src, temp, addr, 1, psm); + L("@@"); + + test(mask, 0x30); + je("@f"); + WritePixel(src, temp, addr, 2, psm); + L("@@"); + + test(mask, 0xc0); + je("@f"); + WritePixel(src, temp, addr, 3, psm); + L("@@"); + } +} + +void GSDrawScanlineCodeGenerator::WritePixel(const Xmm& src, const Xmm& temp, const Reg32& addr, uint8 i, int psm) +{ + static const int offsets[4] = {0, 2, 8, 10}; + + Address dst = ptr[addr * 2 + (size_t)m_env.vm + offsets[i] * 2]; + + if(m_cpu.has(util::Cpu::tSSE41)) + { + switch(psm) + { + case 0: + if(i == 0) movd(dst, src); + else pextrd(dst, src, i); + break; + case 1: + if(i == 0) movd(eax, src); + else pextrd(eax, src, i); + xor(eax, dst); + and(eax, 0xffffff); + xor(dst, eax); + break; + case 2: + pextrw(eax, src, i * 2); + mov(dst, ax); + break; + } + } + else + { + switch(psm) + { + case 0: + if(i == 0) movd(dst, src); + else {pshufd(temp, src, _MM_SHUFFLE(i, i, i, i)); movd(dst, temp);} + break; + case 1: + if(i == 0) movd(eax, src); + else {pshufd(temp, src, _MM_SHUFFLE(i, i, i, i)); movd(eax, temp);} + xor(eax, dst); + and(eax, 0xffffff); + xor(dst, eax); + break; + case 2: + pextrw(eax, src, i * 2); + mov(dst, ax); + break; + } + } +} + +void GSDrawScanlineCodeGenerator::ReadTexel(const Xmm& dst, const Xmm& addr, const Xmm& temp1, const Xmm& temp2) +{ + if(m_cpu.has(util::Cpu::tSSE41)) + { + ReadTexel(dst, addr, 0); + ReadTexel(dst, addr, 1); + ReadTexel(dst, addr, 2); + ReadTexel(dst, addr, 3); + } + else + { + ReadTexel(dst, addr, 0); + psrldq(addr, 4); // shuffle instead? (1 2 3 0 ~ rotation) + ReadTexel(temp1, addr, 0); + psrldq(addr, 4); + punpckldq(dst, temp1); + + ReadTexel(temp1, addr, 0); + psrldq(addr, 4); + ReadTexel(temp2, addr, 0); + // psrldq(addr, 4); + punpckldq(temp1, temp2); + + punpcklqdq(dst, temp1); + } +} + +void GSDrawScanlineCodeGenerator::ReadTexel(const Xmm& dst, const Xmm& addr, uint8 i) +{ + if(!m_cpu.has(util::Cpu::tSSE41) && i > 0) + { + ASSERT(0); + } + + if(i == 0) movd(eax, addr); + else pextrd(eax, addr, i); + + if(m_sel.tlu) movzx(eax, byte[ebx + eax]); + + const Address& src = m_sel.tlu ? ptr[eax * 4 + (size_t)m_env.clut] : ptr[ebx + eax * 4]; + + if(i == 0) movd(dst, src); + else pinsrd(dst, src, i); +} + +template +void GSDrawScanlineCodeGenerator::modulate16(const Xmm& a, const Operand& f) +{ + if(shift == 0 && m_cpu.has(util::Cpu::tSSSE3)) + { + pmulhrsw(a, f); + } + else + { + psllw(a, shift + 1); + pmulhw(a, f); + } +} + +template +void GSDrawScanlineCodeGenerator::lerp16(const Xmm& a, const Xmm& b, const Xmm& f) +{ + psubw(a, b); + modulate16(a, f); + paddw(a, b); +} + +void GSDrawScanlineCodeGenerator::mix16(const Xmm& a, const Xmm& b, const Xmm& temp) +{ + if(m_cpu.has(util::Cpu::tSSE41)) + { + pblendw(a, b, 0xaa); + } + else + { + pcmpeqd(temp, temp); + psrld(temp, 16); + pand(a, temp); + pandn(temp, b); + por(a, temp); + } +} + +void GSDrawScanlineCodeGenerator::clamp16(const Xmm& a, const Xmm& temp) +{ + packuswb(a, a); + + if(m_cpu.has(util::Cpu::tSSE41)) + { + pmovzxbw(a, a); + } + else + { + pxor(temp, temp); + punpcklbw(a, temp); + } +} + +void GSDrawScanlineCodeGenerator::alltrue() +{ + pmovmskb(eax, xmm7); + cmp(eax, 0xffff); + je("step", T_NEAR); +} + +void GSDrawScanlineCodeGenerator::blend8(const Xmm& a, const Xmm& b) +{ + if(m_cpu.has(util::Cpu::tSSE41)) + { + pblendvb(a, b); + } + else + { + blend(a, b, xmm0); + } +} + +void GSDrawScanlineCodeGenerator::blend(const Xmm& a, const Xmm& b, const Xmm& mask) +{ + pand(b, mask); + pandn(mask, a); + por(b, mask); + movdqa(a, b); +} + +void GSDrawScanlineCodeGenerator::blend8r(const Xmm& b, const Xmm& a) +{ + if(m_cpu.has(util::Cpu::tSSE41)) + { + pblendvb(a, b); + movdqa(b, a); + } + else + { + blendr(b, a, xmm0); + } +} + +void GSDrawScanlineCodeGenerator::blendr(const Xmm& b, const Xmm& a, const Xmm& mask) +{ + pand(b, mask); + pandn(mask, a); + por(b, mask); +} + +const GSVector4i GSDrawScanlineCodeGenerator::m_test[8] = +{ + GSVector4i::zero(), + GSVector4i(0xffffffff, 0x00000000, 0x00000000, 0x00000000), + GSVector4i(0xffffffff, 0xffffffff, 0x00000000, 0x00000000), + GSVector4i(0xffffffff, 0xffffffff, 0xffffffff, 0x00000000), + GSVector4i(0x00000000, 0xffffffff, 0xffffffff, 0xffffffff), + GSVector4i(0x00000000, 0x00000000, 0xffffffff, 0xffffffff), + GSVector4i(0x00000000, 0x00000000, 0x00000000, 0xffffffff), + GSVector4i::zero(), +}; diff --git a/plugins/GSdx/GSDrawScanlineCodeGenerator.h b/plugins/GSdx/GSDrawScanlineCodeGenerator.h index a131f8c678..fdf6755bf6 100644 --- a/plugins/GSdx/GSDrawScanlineCodeGenerator.h +++ b/plugins/GSdx/GSDrawScanlineCodeGenerator.h @@ -1,77 +1,77 @@ -/* - * Copyright (C) 2007-2009 Gabest - * http://www.gabest.org - * - * This Program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This Program 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 GNU Make; see the file COPYING. If not, write to - * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - * http://www.gnu.org/copyleft/gpl.html - * - */ - -#pragma once - -#include "GSScanlineEnvironment.h" -#include "xbyak/xbyak.h" -#include "xbyak/xbyak_util.h" - -using namespace Xbyak; - -class GSDrawScanlineCodeGenerator : public CodeGenerator -{ - void operator = (const GSDrawScanlineCodeGenerator&); - - static const GSVector4i m_test[8]; - - util::Cpu m_cpu; - - GSScanlineEnvironment& m_env; - GSScanlineSelector m_sel; - - void Generate(); - - void Init(int params); - void Step(); - void TestZ(const Xmm& temp1, const Xmm& temp2); - void SampleTexture(); - void Wrap(const Xmm& uv0); - void Wrap(const Xmm& uv0, const Xmm& uv1); - void AlphaTFX(); - void TestAlpha(); - void ColorTFX(); - void Fog(); - void ReadFrame(); - void TestDestAlpha(); - void WriteZBuf(); - void AlphaBlend(); - void WriteFrame(int params); - - void ReadPixel(const Xmm& dst, const Reg32& addr); - void WritePixel(const Xmm& src, const Xmm& temp, const Reg32& addr, const Reg8& mask, bool fast, int psm); - void WritePixel(const Xmm& src, const Xmm& temp, const Reg32& addr, uint8 i, int psm); - void ReadTexel(const Xmm& dst, const Xmm& addr, const Xmm& temp1, const Xmm& temp2); - void ReadTexel(const Xmm& dst, const Xmm& addr, uint8 i); - - template void modulate16(const Xmm& a, const Operand& f); - template void lerp16(const Xmm& a, const Xmm& b, const Xmm& f); - void mix16(const Xmm& a, const Xmm& b, const Xmm& temp); - void clamp16(const Xmm& a, const Xmm& temp); - void alltrue(); - void blend8(const Xmm& a, const Xmm& b); - void blend(const Xmm& a, const Xmm& b, const Xmm& mask); - void blend8r(const Xmm& b, const Xmm& a); - void blendr(const Xmm& b, const Xmm& a, const Xmm& mask); - -public: - GSDrawScanlineCodeGenerator(GSScanlineEnvironment& env, uint64 key, void* ptr, size_t maxsize); +/* + * Copyright (C) 2007-2009 Gabest + * http://www.gabest.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program 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 GNU Make; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#pragma once + +#include "GSScanlineEnvironment.h" +#include "xbyak/xbyak.h" +#include "xbyak/xbyak_util.h" + +using namespace Xbyak; + +class GSDrawScanlineCodeGenerator : public CodeGenerator +{ + void operator = (const GSDrawScanlineCodeGenerator&); + + static const GSVector4i m_test[8]; + + util::Cpu m_cpu; + + GSScanlineEnvironment& m_env; + GSScanlineSelector m_sel; + + void Generate(); + + void Init(int params); + void Step(); + void TestZ(const Xmm& temp1, const Xmm& temp2); + void SampleTexture(); + void Wrap(const Xmm& uv0); + void Wrap(const Xmm& uv0, const Xmm& uv1); + void AlphaTFX(); + void TestAlpha(); + void ColorTFX(); + void Fog(); + void ReadFrame(); + void TestDestAlpha(); + void WriteZBuf(); + void AlphaBlend(); + void WriteFrame(int params); + + void ReadPixel(const Xmm& dst, const Reg32& addr); + void WritePixel(const Xmm& src, const Xmm& temp, const Reg32& addr, const Reg8& mask, bool fast, int psm); + void WritePixel(const Xmm& src, const Xmm& temp, const Reg32& addr, uint8 i, int psm); + void ReadTexel(const Xmm& dst, const Xmm& addr, const Xmm& temp1, const Xmm& temp2); + void ReadTexel(const Xmm& dst, const Xmm& addr, uint8 i); + + template void modulate16(const Xmm& a, const Operand& f); + template void lerp16(const Xmm& a, const Xmm& b, const Xmm& f); + void mix16(const Xmm& a, const Xmm& b, const Xmm& temp); + void clamp16(const Xmm& a, const Xmm& temp); + void alltrue(); + void blend8(const Xmm& a, const Xmm& b); + void blend(const Xmm& a, const Xmm& b, const Xmm& mask); + void blend8r(const Xmm& b, const Xmm& a); + void blendr(const Xmm& b, const Xmm& a, const Xmm& mask); + +public: + GSDrawScanlineCodeGenerator(GSScanlineEnvironment& env, uint64 key, void* ptr, size_t maxsize); }; \ No newline at end of file diff --git a/plugins/GSdx/GSRendererDX.cpp b/plugins/GSdx/GSRendererDX.cpp index 77f6bfd198..dc5e32dacd 100644 --- a/plugins/GSdx/GSRendererDX.cpp +++ b/plugins/GSdx/GSRendererDX.cpp @@ -1,23 +1,23 @@ -/* - * Copyright (C) 2007-2009 Gabest - * http://www.gabest.org - * - * This Program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This Program 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 GNU Make; see the file COPYING. If not, write to - * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - * http://www.gnu.org/copyleft/gpl.html - * - */ - -#include "stdafx.h" +/* + * Copyright (C) 2007-2009 Gabest + * http://www.gabest.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program 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 GNU Make; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "stdafx.h" #include "GSRendererDX.h" \ No newline at end of file diff --git a/plugins/GSdx/GSRendererDX.h b/plugins/GSdx/GSRendererDX.h index 33fdcb4dd3..f17a40d7d3 100644 --- a/plugins/GSdx/GSRendererDX.h +++ b/plugins/GSdx/GSRendererDX.h @@ -1,420 +1,420 @@ -/* - * Copyright (C) 2007-2009 Gabest - * http://www.gabest.org - * - * This Program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This Program 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 GNU Make; see the file COPYING. If not, write to - * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - * http://www.gnu.org/copyleft/gpl.html - * - */ - -#pragma once - -#include "GSRendererHW.h" - -template -class GSRendererDX : public GSRendererHW -{ - GSVector2 m_pixelcenter; - bool m_logz; - bool m_fba; - //bool UserHacks_HalfPixelOffset; - bool UserHacks_AlphaHack; - -protected: - int m_topology; - - virtual void UpdateFBA(GSTexture* rt) {} - -public: - GSRendererDX(GSTextureCache* tc, const GSVector2& pixelcenter = GSVector2(0, 0)) - : GSRendererHW(tc) - , m_pixelcenter(pixelcenter) - , m_topology(-1) - { - m_logz = !!theApp.GetConfig("logz", 0); - m_fba = !!theApp.GetConfig("fba", 1); - //UserHacks_HalfPixelOffset = !!theApp.GetConfig("UserHacks_HalfPixelOffset", 0); - UserHacks_AlphaHack = !!theApp.GetConfig("UserHacks_AlphaHack", 0); - } - - virtual ~GSRendererDX() - { - } - - bool CreateDevice(GSDevice* dev) - { - if(!__super::CreateDevice(dev)) - return false; - - return true; - } - - __forceinline void Draw(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* tex) - { - GSDrawingEnvironment& env = m_env; - GSDrawingContext* context = m_context; - - assert(m_dev != NULL); - - GSDeviceDX& dev = (GSDeviceDX&)*m_dev; - - // - if(m_context->TEST.DATE) - { - const GSVector2i& size = rt->GetSize(); - - GSVector4 s = GSVector4(rt->GetScale().x / size.x, rt->GetScale().y / size.y); - GSVector4 o = GSVector4(-1.0f, 1.0f); - - GSVector4 src = ((m_vt.m_min.p.xyxy(m_vt.m_max.p) + o.xxyy()) * s.xyxy()).sat(o.zzyy()); - GSVector4 dst = src * 2.0f + o.xxxx(); - - GSVertexPT1 vertices[] = - { - {GSVector4(dst.x, -dst.y, 0.5f, 1.0f), GSVector2(src.x, src.y)}, - {GSVector4(dst.z, -dst.y, 0.5f, 1.0f), GSVector2(src.z, src.y)}, - {GSVector4(dst.x, -dst.w, 0.5f, 1.0f), GSVector2(src.x, src.w)}, - {GSVector4(dst.z, -dst.w, 0.5f, 1.0f), GSVector2(src.z, src.w)}, - }; - - dev.SetupDATE(rt, ds, vertices, m_context->TEST.DATM ); - } - - // - - dev.BeginScene(); - - // om - - GSDeviceDX::OMDepthStencilSelector om_dssel; - - if(context->TEST.ZTE) - { - om_dssel.ztst = context->TEST.ZTST; - om_dssel.zwe = !context->ZBUF.ZMSK; - } - else - { - om_dssel.ztst = ZTST_ALWAYS; - } - - if(context->FRAME.PSM != PSM_PSMCT24) - { - om_dssel.date = context->TEST.DATE; - } - - if(m_fba) - { - om_dssel.fba = context->FBA.FBA; - } - - GSDeviceDX::OMBlendSelector om_bsel; - - if(!IsOpaque()) - { - om_bsel.abe = PRIM->ABE || PRIM->AA1 && m_vt.m_primclass == GS_LINE_CLASS; - - om_bsel.a = context->ALPHA.A; - om_bsel.b = context->ALPHA.B; - om_bsel.c = context->ALPHA.C; - om_bsel.d = context->ALPHA.D; - - if(env.PABE.PABE) - { - if(om_bsel.a == 0 && om_bsel.b == 1 && om_bsel.c == 0 && om_bsel.d == 1) - { - // this works because with PABE alpha blending is on when alpha >= 0x80, but since the pixel shader - // cannot output anything over 0x80 (== 1.0) blending with 0x80 or turning it off gives the same result - - om_bsel.abe = 0; - } - else - { - //Breath of Fire Dragon Quarter triggers this in battles. Graphics are fine though. - ASSERT(0); - } - } - } - - om_bsel.wrgba = ~GSVector4i::load((int)context->FRAME.FBMSK).eq8(GSVector4i::xffffffff()).mask(); - - // vs - - GSDeviceDX::VSSelector vs_sel; - - vs_sel.tme = PRIM->TME; - vs_sel.fst = PRIM->FST; - vs_sel.logz = m_logz ? 1 : 0; - - if(om_dssel.ztst >= ZTST_ALWAYS && om_dssel.zwe) - { - if(context->ZBUF.PSM == PSM_PSMZ24) - { - if(m_vt.m_max.p.z > 0xffffff) - { - ASSERT(m_vt.m_min.p.z > 0xffffff); - // Fixme :Following conditional fixes some dialog frame in Wild Arms 3, but may not be what was intended. - if (m_vt.m_min.p.z > 0xffffff) - { - vs_sel.bppz = 1; - om_dssel.ztst = ZTST_ALWAYS; - } - } - } - else if(context->ZBUF.PSM == PSM_PSMZ16 || context->ZBUF.PSM == PSM_PSMZ16S) - { - if(m_vt.m_max.p.z > 0xffff) - { - ASSERT(m_vt.m_min.p.z > 0xffff); // sfex capcom logo - // Fixme : Same as above, I guess. - if (m_vt.m_min.p.z > 0xffff) - { - vs_sel.bppz = 2; - om_dssel.ztst = ZTST_ALWAYS; - } - } - } - } - - GSDeviceDX::VSConstantBuffer vs_cb; - - float sx = 2.0f * rt->GetScale().x / (rt->GetWidth() << 4); - float sy = 2.0f * rt->GetScale().y / (rt->GetHeight() << 4); - float ox = (float)(int)context->XYOFFSET.OFX; - float oy = (float)(int)context->XYOFFSET.OFY; - float ox2 = 2.0f * m_pixelcenter.x / rt->GetWidth(); - float oy2 = 2.0f * m_pixelcenter.y / rt->GetHeight(); - - //This hack subtracts around half a pixel from OFX and OFY. (Cannot do this directly, - //because DX10 and DX9 have a different pixel center.) - // - //The resulting shifted output aligns better with common blending / corona / blurring effects, - //but introduces a few bad pixels on the edges. - - // Edit: Moved to CreateSource() in GSTextureCache.cpp - //if (UserHacks_HalfPixelOffset == true) - //{ - // //DX9 has pixelcenter set to 0.0, so give it some value here - // if (m_pixelcenter.x == 0 && m_pixelcenter.y == 0) { ox2 = oy2 = -0.00035f; } - // - // if (ox != 0) { ox2 *= upscale_Multiplier(); } - // if (oy != 0) { oy2 *= upscale_Multiplier(); } - //} - - vs_cb.VertexScale = GSVector4(sx, -sy, 1.0f / UINT_MAX, 0.0f); - vs_cb.VertexOffset = GSVector4(ox * sx + ox2 + 1, -(oy * sy + oy2 + 1), 0.0f, -1.0f); - // gs - - GSDeviceDX::GSSelector gs_sel; - - gs_sel.iip = PRIM->IIP; - gs_sel.prim = m_vt.m_primclass; - - // ps - - GSDeviceDX::PSSelector ps_sel; - GSDeviceDX::PSSamplerSelector ps_ssel; - GSDeviceDX::PSConstantBuffer ps_cb; - - if (env.COLCLAMP.CLAMP == 0) { - ps_sel.colclip = 1; - } - - ps_sel.clr1 = om_bsel.IsCLR1(); - ps_sel.fba = context->FBA.FBA; - ps_sel.aout = context->FRAME.PSM == PSM_PSMCT16 || context->FRAME.PSM == PSM_PSMCT16S || (context->FRAME.FBMSK & 0xff000000) == 0x7f000000 ? 1 : 0; - if (UserHacks_AlphaHack) ps_sel.aout = 1; - if(PRIM->FGE) - { - ps_sel.fog = 1; - - ps_cb.FogColor_AREF = GSVector4(env.FOGCOL.u32[0]) / 255; - } - - if(context->TEST.ATE) - { - ps_sel.atst = context->TEST.ATST; - - switch(ps_sel.atst) - { - case ATST_LESS: - ps_cb.FogColor_AREF.a = (float)((int)context->TEST.AREF - 1); - break; - case ATST_GREATER: - ps_cb.FogColor_AREF.a = (float)((int)context->TEST.AREF + 1); - break; - default: - ps_cb.FogColor_AREF.a = (float)(int)context->TEST.AREF; - break; - } - } - else - { - ps_sel.atst = ATST_ALWAYS; - } - - if(tex) - { - ps_sel.wms = context->CLAMP.WMS; - ps_sel.wmt = context->CLAMP.WMT; - ps_sel.fmt = tex->m_fmt; - ps_sel.aem = env.TEXA.AEM; - ps_sel.tfx = context->TEX0.TFX; - ps_sel.tcc = context->TEX0.TCC; - ps_sel.ltf = m_filter == 2 ? IsLinear() : m_filter; - ps_sel.rt = tex->m_target; - - int w = tex->m_texture->GetWidth(); - int h = tex->m_texture->GetHeight(); - - int tw = (int)(1 << context->TEX0.TW); - int th = (int)(1 << context->TEX0.TH); - - GSVector4 WH(tw, th, w, h); - - if(PRIM->FST) - { - vs_cb.TextureScale = GSVector4(1.0f / 16) / WH.xyxy(); - //Maybe better? - //vs_cb.TextureScale = GSVector4(1.0f / 16) * GSVector4(tex->m_texture->GetScale()).xyxy() / WH.zwzw(); - ps_sel.fst = 1; - } - - ps_cb.WH = WH; - ps_cb.HalfTexel = GSVector4(-0.5f, 0.5f).xxyy() / WH.zwzw(); - ps_cb.MskFix = GSVector4i(context->CLAMP.MINU, context->CLAMP.MINV, context->CLAMP.MAXU, context->CLAMP.MAXV); - - GSVector4 clamp(ps_cb.MskFix); - GSVector4 ta(env.TEXA & GSVector4i::x000000ff()); - - ps_cb.MinMax = clamp / WH.xyxy(); - ps_cb.MinF_TA = (clamp + 0.5f).xyxy(ta) / WH.xyxy(GSVector4(255, 255)); - - ps_ssel.tau = (context->CLAMP.WMS + 3) >> 1; - ps_ssel.tav = (context->CLAMP.WMT + 3) >> 1; - ps_ssel.ltf = ps_sel.ltf; - } - else - { - ps_sel.tfx = 4; - } - - // rs - - GSVector4i scissor = GSVector4i(GSVector4(rt->GetScale()).xyxy() * context->scissor.in).rintersect(GSVector4i(rt->GetSize()).zwxy()); - - dev.OMSetRenderTargets(rt, ds, &scissor); - dev.PSSetShaderResources(tex ? tex->m_texture : NULL, tex ? tex->m_palette : NULL); - - uint8 afix = context->ALPHA.FIX; - - dev.SetupOM(om_dssel, om_bsel, afix); - dev.SetupIA(m_vertices, m_count, m_topology); - dev.SetupVS(vs_sel, &vs_cb); - dev.SetupGS(gs_sel); - dev.SetupPS(ps_sel, &ps_cb, ps_ssel); - - // draw - - if(context->TEST.DoFirstPass()) - { - dev.DrawPrimitive(); - - if (env.COLCLAMP.CLAMP == 0) - { - GSDeviceDX::OMBlendSelector om_bselneg(om_bsel); - GSDeviceDX::PSSelector ps_selneg(ps_sel); - - om_bselneg.negative = 1; - ps_selneg.colclip = 2; - - dev.SetupOM(om_dssel, om_bselneg, afix); - dev.SetupPS(ps_selneg, &ps_cb, ps_ssel); - - dev.DrawPrimitive(); - } - } - - if(context->TEST.DoSecondPass()) - { - ASSERT(!env.PABE.PABE); - - static const uint32 iatst[] = {1, 0, 5, 6, 7, 2, 3, 4}; - - ps_sel.atst = iatst[ps_sel.atst]; - - switch(ps_sel.atst) - { - case ATST_LESS: - ps_cb.FogColor_AREF.a = (float)((int)context->TEST.AREF - 1); - break; - case ATST_GREATER: - ps_cb.FogColor_AREF.a = (float)((int)context->TEST.AREF + 1); - break; - default: - ps_cb.FogColor_AREF.a = (float)(int)context->TEST.AREF; - break; - } - - dev.SetupPS(ps_sel, &ps_cb, ps_ssel); - - bool z = om_dssel.zwe; - bool r = om_bsel.wr; - bool g = om_bsel.wg; - bool b = om_bsel.wb; - bool a = om_bsel.wa; - - switch(context->TEST.AFAIL) - { - case 0: z = r = g = b = a = false; break; // none - case 1: z = false; break; // rgba - case 2: r = g = b = a = false; break; // z - case 3: z = a = false; break; // rgb - default: __assume(0); - } - - if(z || r || g || b || a) - { - om_dssel.zwe = z; - om_bsel.wr = r; - om_bsel.wg = g; - om_bsel.wb = b; - om_bsel.wa = a; - - dev.SetupOM(om_dssel, om_bsel, afix); - - dev.DrawPrimitive(); - - if (env.COLCLAMP.CLAMP == 0) - { - GSDeviceDX::OMBlendSelector om_bselneg(om_bsel); - GSDeviceDX::PSSelector ps_selneg(ps_sel); - - om_bselneg.negative = 1; - ps_selneg.colclip = 2; - - dev.SetupOM(om_dssel, om_bselneg, afix); - dev.SetupPS(ps_selneg, &ps_cb, ps_ssel); - - dev.DrawPrimitive(); - } - } - } - - dev.EndScene(); - - if(om_dssel.fba) UpdateFBA(rt); - } +/* + * Copyright (C) 2007-2009 Gabest + * http://www.gabest.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program 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 GNU Make; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#pragma once + +#include "GSRendererHW.h" + +template +class GSRendererDX : public GSRendererHW +{ + GSVector2 m_pixelcenter; + bool m_logz; + bool m_fba; + //bool UserHacks_HalfPixelOffset; + bool UserHacks_AlphaHack; + +protected: + int m_topology; + + virtual void UpdateFBA(GSTexture* rt) {} + +public: + GSRendererDX(GSTextureCache* tc, const GSVector2& pixelcenter = GSVector2(0, 0)) + : GSRendererHW(tc) + , m_pixelcenter(pixelcenter) + , m_topology(-1) + { + m_logz = !!theApp.GetConfig("logz", 0); + m_fba = !!theApp.GetConfig("fba", 1); + //UserHacks_HalfPixelOffset = !!theApp.GetConfig("UserHacks_HalfPixelOffset", 0); + UserHacks_AlphaHack = !!theApp.GetConfig("UserHacks_AlphaHack", 0); + } + + virtual ~GSRendererDX() + { + } + + bool CreateDevice(GSDevice* dev) + { + if(!__super::CreateDevice(dev)) + return false; + + return true; + } + + __forceinline void Draw(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* tex) + { + GSDrawingEnvironment& env = m_env; + GSDrawingContext* context = m_context; + + assert(m_dev != NULL); + + GSDeviceDX& dev = (GSDeviceDX&)*m_dev; + + // + if(m_context->TEST.DATE) + { + const GSVector2i& size = rt->GetSize(); + + GSVector4 s = GSVector4(rt->GetScale().x / size.x, rt->GetScale().y / size.y); + GSVector4 o = GSVector4(-1.0f, 1.0f); + + GSVector4 src = ((m_vt.m_min.p.xyxy(m_vt.m_max.p) + o.xxyy()) * s.xyxy()).sat(o.zzyy()); + GSVector4 dst = src * 2.0f + o.xxxx(); + + GSVertexPT1 vertices[] = + { + {GSVector4(dst.x, -dst.y, 0.5f, 1.0f), GSVector2(src.x, src.y)}, + {GSVector4(dst.z, -dst.y, 0.5f, 1.0f), GSVector2(src.z, src.y)}, + {GSVector4(dst.x, -dst.w, 0.5f, 1.0f), GSVector2(src.x, src.w)}, + {GSVector4(dst.z, -dst.w, 0.5f, 1.0f), GSVector2(src.z, src.w)}, + }; + + dev.SetupDATE(rt, ds, vertices, m_context->TEST.DATM ); + } + + // + + dev.BeginScene(); + + // om + + GSDeviceDX::OMDepthStencilSelector om_dssel; + + if(context->TEST.ZTE) + { + om_dssel.ztst = context->TEST.ZTST; + om_dssel.zwe = !context->ZBUF.ZMSK; + } + else + { + om_dssel.ztst = ZTST_ALWAYS; + } + + if(context->FRAME.PSM != PSM_PSMCT24) + { + om_dssel.date = context->TEST.DATE; + } + + if(m_fba) + { + om_dssel.fba = context->FBA.FBA; + } + + GSDeviceDX::OMBlendSelector om_bsel; + + if(!IsOpaque()) + { + om_bsel.abe = PRIM->ABE || PRIM->AA1 && m_vt.m_primclass == GS_LINE_CLASS; + + om_bsel.a = context->ALPHA.A; + om_bsel.b = context->ALPHA.B; + om_bsel.c = context->ALPHA.C; + om_bsel.d = context->ALPHA.D; + + if(env.PABE.PABE) + { + if(om_bsel.a == 0 && om_bsel.b == 1 && om_bsel.c == 0 && om_bsel.d == 1) + { + // this works because with PABE alpha blending is on when alpha >= 0x80, but since the pixel shader + // cannot output anything over 0x80 (== 1.0) blending with 0x80 or turning it off gives the same result + + om_bsel.abe = 0; + } + else + { + //Breath of Fire Dragon Quarter triggers this in battles. Graphics are fine though. + ASSERT(0); + } + } + } + + om_bsel.wrgba = ~GSVector4i::load((int)context->FRAME.FBMSK).eq8(GSVector4i::xffffffff()).mask(); + + // vs + + GSDeviceDX::VSSelector vs_sel; + + vs_sel.tme = PRIM->TME; + vs_sel.fst = PRIM->FST; + vs_sel.logz = m_logz ? 1 : 0; + + if(om_dssel.ztst >= ZTST_ALWAYS && om_dssel.zwe) + { + if(context->ZBUF.PSM == PSM_PSMZ24) + { + if(m_vt.m_max.p.z > 0xffffff) + { + ASSERT(m_vt.m_min.p.z > 0xffffff); + // Fixme :Following conditional fixes some dialog frame in Wild Arms 3, but may not be what was intended. + if (m_vt.m_min.p.z > 0xffffff) + { + vs_sel.bppz = 1; + om_dssel.ztst = ZTST_ALWAYS; + } + } + } + else if(context->ZBUF.PSM == PSM_PSMZ16 || context->ZBUF.PSM == PSM_PSMZ16S) + { + if(m_vt.m_max.p.z > 0xffff) + { + ASSERT(m_vt.m_min.p.z > 0xffff); // sfex capcom logo + // Fixme : Same as above, I guess. + if (m_vt.m_min.p.z > 0xffff) + { + vs_sel.bppz = 2; + om_dssel.ztst = ZTST_ALWAYS; + } + } + } + } + + GSDeviceDX::VSConstantBuffer vs_cb; + + float sx = 2.0f * rt->GetScale().x / (rt->GetWidth() << 4); + float sy = 2.0f * rt->GetScale().y / (rt->GetHeight() << 4); + float ox = (float)(int)context->XYOFFSET.OFX; + float oy = (float)(int)context->XYOFFSET.OFY; + float ox2 = 2.0f * m_pixelcenter.x / rt->GetWidth(); + float oy2 = 2.0f * m_pixelcenter.y / rt->GetHeight(); + + //This hack subtracts around half a pixel from OFX and OFY. (Cannot do this directly, + //because DX10 and DX9 have a different pixel center.) + // + //The resulting shifted output aligns better with common blending / corona / blurring effects, + //but introduces a few bad pixels on the edges. + + // Edit: Moved to CreateSource() in GSTextureCache.cpp + //if (UserHacks_HalfPixelOffset == true) + //{ + // //DX9 has pixelcenter set to 0.0, so give it some value here + // if (m_pixelcenter.x == 0 && m_pixelcenter.y == 0) { ox2 = oy2 = -0.00035f; } + // + // if (ox != 0) { ox2 *= upscale_Multiplier(); } + // if (oy != 0) { oy2 *= upscale_Multiplier(); } + //} + + vs_cb.VertexScale = GSVector4(sx, -sy, 1.0f / UINT_MAX, 0.0f); + vs_cb.VertexOffset = GSVector4(ox * sx + ox2 + 1, -(oy * sy + oy2 + 1), 0.0f, -1.0f); + // gs + + GSDeviceDX::GSSelector gs_sel; + + gs_sel.iip = PRIM->IIP; + gs_sel.prim = m_vt.m_primclass; + + // ps + + GSDeviceDX::PSSelector ps_sel; + GSDeviceDX::PSSamplerSelector ps_ssel; + GSDeviceDX::PSConstantBuffer ps_cb; + + if (env.COLCLAMP.CLAMP == 0) { + ps_sel.colclip = 1; + } + + ps_sel.clr1 = om_bsel.IsCLR1(); + ps_sel.fba = context->FBA.FBA; + ps_sel.aout = context->FRAME.PSM == PSM_PSMCT16 || context->FRAME.PSM == PSM_PSMCT16S || (context->FRAME.FBMSK & 0xff000000) == 0x7f000000 ? 1 : 0; + if (UserHacks_AlphaHack) ps_sel.aout = 1; + if(PRIM->FGE) + { + ps_sel.fog = 1; + + ps_cb.FogColor_AREF = GSVector4(env.FOGCOL.u32[0]) / 255; + } + + if(context->TEST.ATE) + { + ps_sel.atst = context->TEST.ATST; + + switch(ps_sel.atst) + { + case ATST_LESS: + ps_cb.FogColor_AREF.a = (float)((int)context->TEST.AREF - 1); + break; + case ATST_GREATER: + ps_cb.FogColor_AREF.a = (float)((int)context->TEST.AREF + 1); + break; + default: + ps_cb.FogColor_AREF.a = (float)(int)context->TEST.AREF; + break; + } + } + else + { + ps_sel.atst = ATST_ALWAYS; + } + + if(tex) + { + ps_sel.wms = context->CLAMP.WMS; + ps_sel.wmt = context->CLAMP.WMT; + ps_sel.fmt = tex->m_fmt; + ps_sel.aem = env.TEXA.AEM; + ps_sel.tfx = context->TEX0.TFX; + ps_sel.tcc = context->TEX0.TCC; + ps_sel.ltf = m_filter == 2 ? IsLinear() : m_filter; + ps_sel.rt = tex->m_target; + + int w = tex->m_texture->GetWidth(); + int h = tex->m_texture->GetHeight(); + + int tw = (int)(1 << context->TEX0.TW); + int th = (int)(1 << context->TEX0.TH); + + GSVector4 WH(tw, th, w, h); + + if(PRIM->FST) + { + vs_cb.TextureScale = GSVector4(1.0f / 16) / WH.xyxy(); + //Maybe better? + //vs_cb.TextureScale = GSVector4(1.0f / 16) * GSVector4(tex->m_texture->GetScale()).xyxy() / WH.zwzw(); + ps_sel.fst = 1; + } + + ps_cb.WH = WH; + ps_cb.HalfTexel = GSVector4(-0.5f, 0.5f).xxyy() / WH.zwzw(); + ps_cb.MskFix = GSVector4i(context->CLAMP.MINU, context->CLAMP.MINV, context->CLAMP.MAXU, context->CLAMP.MAXV); + + GSVector4 clamp(ps_cb.MskFix); + GSVector4 ta(env.TEXA & GSVector4i::x000000ff()); + + ps_cb.MinMax = clamp / WH.xyxy(); + ps_cb.MinF_TA = (clamp + 0.5f).xyxy(ta) / WH.xyxy(GSVector4(255, 255)); + + ps_ssel.tau = (context->CLAMP.WMS + 3) >> 1; + ps_ssel.tav = (context->CLAMP.WMT + 3) >> 1; + ps_ssel.ltf = ps_sel.ltf; + } + else + { + ps_sel.tfx = 4; + } + + // rs + + GSVector4i scissor = GSVector4i(GSVector4(rt->GetScale()).xyxy() * context->scissor.in).rintersect(GSVector4i(rt->GetSize()).zwxy()); + + dev.OMSetRenderTargets(rt, ds, &scissor); + dev.PSSetShaderResources(tex ? tex->m_texture : NULL, tex ? tex->m_palette : NULL); + + uint8 afix = context->ALPHA.FIX; + + dev.SetupOM(om_dssel, om_bsel, afix); + dev.SetupIA(m_vertices, m_count, m_topology); + dev.SetupVS(vs_sel, &vs_cb); + dev.SetupGS(gs_sel); + dev.SetupPS(ps_sel, &ps_cb, ps_ssel); + + // draw + + if(context->TEST.DoFirstPass()) + { + dev.DrawPrimitive(); + + if (env.COLCLAMP.CLAMP == 0) + { + GSDeviceDX::OMBlendSelector om_bselneg(om_bsel); + GSDeviceDX::PSSelector ps_selneg(ps_sel); + + om_bselneg.negative = 1; + ps_selneg.colclip = 2; + + dev.SetupOM(om_dssel, om_bselneg, afix); + dev.SetupPS(ps_selneg, &ps_cb, ps_ssel); + + dev.DrawPrimitive(); + } + } + + if(context->TEST.DoSecondPass()) + { + ASSERT(!env.PABE.PABE); + + static const uint32 iatst[] = {1, 0, 5, 6, 7, 2, 3, 4}; + + ps_sel.atst = iatst[ps_sel.atst]; + + switch(ps_sel.atst) + { + case ATST_LESS: + ps_cb.FogColor_AREF.a = (float)((int)context->TEST.AREF - 1); + break; + case ATST_GREATER: + ps_cb.FogColor_AREF.a = (float)((int)context->TEST.AREF + 1); + break; + default: + ps_cb.FogColor_AREF.a = (float)(int)context->TEST.AREF; + break; + } + + dev.SetupPS(ps_sel, &ps_cb, ps_ssel); + + bool z = om_dssel.zwe; + bool r = om_bsel.wr; + bool g = om_bsel.wg; + bool b = om_bsel.wb; + bool a = om_bsel.wa; + + switch(context->TEST.AFAIL) + { + case 0: z = r = g = b = a = false; break; // none + case 1: z = false; break; // rgba + case 2: r = g = b = a = false; break; // z + case 3: z = a = false; break; // rgb + default: __assume(0); + } + + if(z || r || g || b || a) + { + om_dssel.zwe = z; + om_bsel.wr = r; + om_bsel.wg = g; + om_bsel.wb = b; + om_bsel.wa = a; + + dev.SetupOM(om_dssel, om_bsel, afix); + + dev.DrawPrimitive(); + + if (env.COLCLAMP.CLAMP == 0) + { + GSDeviceDX::OMBlendSelector om_bselneg(om_bsel); + GSDeviceDX::PSSelector ps_selneg(ps_sel); + + om_bselneg.negative = 1; + ps_selneg.colclip = 2; + + dev.SetupOM(om_dssel, om_bselneg, afix); + dev.SetupPS(ps_selneg, &ps_cb, ps_ssel); + + dev.DrawPrimitive(); + } + } + } + + dev.EndScene(); + + if(om_dssel.fba) UpdateFBA(rt); + } }; \ No newline at end of file diff --git a/plugins/GSdx/GSRendererDX11.cpp b/plugins/GSdx/GSRendererDX11.cpp index 9be3c5ec75..0b844d5ac3 100644 --- a/plugins/GSdx/GSRendererDX11.cpp +++ b/plugins/GSdx/GSRendererDX11.cpp @@ -1,223 +1,223 @@ -/* - * Copyright (C) 2007-2009 Gabest - * http://www.gabest.org - * - * This Program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This Program 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 GNU Make; see the file COPYING. If not, write to - * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - * http://www.gnu.org/copyleft/gpl.html - * - */ - -#include "stdafx.h" -#include "GSRendererDX11.h" -#include "GSCrc.h" -#include "resource.h" - -GSRendererDX11::GSRendererDX11() - : GSRendererDX(new GSTextureCache11(this), GSVector2(-0.5f, -0.5f)) -{ - InitVertexKick(); -} - -bool GSRendererDX11::CreateDevice(GSDevice* dev) -{ - if(!__super::CreateDevice(dev)) - return false; - - return true; -} - -template -void GSRendererDX11::VertexKick(bool skip) -{ - GSVertexHW11& dst = m_vl.AddTail(); - - dst.vi[0] = m_v.vi[0]; - dst.vi[1] = m_v.vi[1]; - -#ifdef USE_UPSCALE_HACKS - if(tme && fst) - { - //GSVector4::storel(&dst.ST, m_v.GetUV()); - int Udiff = 0; - int Vdiff = 0; - int Uadjust = 0; - int Vadjust = 0; - int multiplier = upscale_Multiplier(); - - if (multiplier > 1) { - - Udiff = m_v.UV.U & 4095; - Vdiff = m_v.UV.V & 4095; - if (Udiff != 0){ - if (Udiff >= 4080) {/*printf("U+ %d %d\n", Udiff, m_v.UV.U);*/ Uadjust = -1; } - else if (Udiff <= 16) {/*printf("U- %d %d\n", Udiff, m_v.UV.U);*/ Uadjust = 1; } - } - if (Vdiff != 0){ - if (Vdiff >= 4080) {/*printf("V+ %d %d\n", Vdiff, m_v.UV.V);*/ Vadjust = -1; } - else if (Vdiff <= 16) {/*printf("V- %d %d\n", Vdiff, m_v.UV.V);*/ Vadjust = 1; } - } - - Udiff = m_v.UV.U & 255; - Vdiff = m_v.UV.V & 255; - if (Udiff != 0){ - if (Udiff >= 248) { Uadjust = -1; } - else if (Udiff <= 8) { Uadjust = 1; } - } - - if (Vdiff != 0){ - if (Vdiff >= 248) { Vadjust = -1; } - else if (Vdiff <= 8) { Vadjust = 1; } - } - - Udiff = m_v.UV.U & 15; - Vdiff = m_v.UV.V & 15; - if (Udiff != 0){ - if (Udiff >= 15) { Uadjust = -1; } - else if (Udiff <= 1) { Uadjust = 1; } - } - - if (Vdiff != 0){ - if (Vdiff >= 15) { Vadjust = -1; } - else if (Vdiff <= 1) { Vadjust = 1; } - } - } - dst.ST.S = (float)m_v.UV.U - Uadjust; - dst.ST.T = (float)m_v.UV.V - Vadjust; - } - else if (tme) - { - // Wip :p - //dst.XYZ.X += 5; - //dst.XYZ.Y += 5; - } -#else - if(tme && fst) - { - GSVector4::storel(&dst.ST, m_v.GetUV()); - } -#endif - - int count = 0; - - if(GSVertexHW11* v = DrawingKick(skip, count)) - { - GSVector4i scissor = m_context->scissor.dx10; - - GSVector4i pmin, pmax; - - #if _M_SSE >= 0x401 - - GSVector4i v0, v1, v2; - - switch(prim) - { - case GS_POINTLIST: - v0 = GSVector4i::load((int)v[0].p.xy).upl16(); - pmin = v0; - pmax = v0; - break; - case GS_LINELIST: - case GS_LINESTRIP: - case GS_SPRITE: - v0 = GSVector4i::load((int)v[0].p.xy); - v1 = GSVector4i::load((int)v[1].p.xy); - pmin = v0.min_u16(v1).upl16(); - pmax = v0.max_u16(v1).upl16(); - break; - case GS_TRIANGLELIST: - case GS_TRIANGLESTRIP: - case GS_TRIANGLEFAN: - v0 = GSVector4i::load((int)v[0].p.xy); - v1 = GSVector4i::load((int)v[1].p.xy); - v2 = GSVector4i::load((int)v[2].p.xy); - pmin = v0.min_u16(v1).min_u16(v2).upl16(); - pmax = v0.max_u16(v1).max_u16(v2).upl16(); - break; - } - - #else - - switch(prim) - { - case GS_POINTLIST: - pmin.x = v[0].p.x; - pmin.y = v[0].p.y; - pmax.x = v[0].p.x; - pmax.y = v[0].p.y; - break; - case GS_LINELIST: - case GS_LINESTRIP: - case GS_SPRITE: - pmin.x = std::min(v[0].p.x, v[1].p.x); - pmin.y = std::min(v[0].p.y, v[1].p.y); - pmax.x = std::max(v[0].p.x, v[1].p.x); - pmax.y = std::max(v[0].p.y, v[1].p.y); - break; - case GS_TRIANGLELIST: - case GS_TRIANGLESTRIP: - case GS_TRIANGLEFAN: - pmin.x = std::min(std::min(v[0].p.x, v[1].p.x), v[2].p.x); - pmin.y = std::min(std::min(v[0].p.y, v[1].p.y), v[2].p.y); - pmax.x = std::max(std::max(v[0].p.x, v[1].p.x), v[2].p.x); - pmax.y = std::max(std::max(v[0].p.y, v[1].p.y), v[2].p.y); - break; - } - - #endif - - GSVector4i test = (pmax < scissor) | (pmin > scissor.zwxy()); - - switch(prim) - { - case GS_TRIANGLELIST: - case GS_TRIANGLESTRIP: - case GS_TRIANGLEFAN: - case GS_SPRITE: - test |= pmin == pmax; - break; - } - - if(test.mask() & 0xff) - { - return; - } - - m_count += count; - } -} - -void GSRendererDX11::Draw(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* tex) -{ - switch(m_vt.m_primclass) - { - case GS_POINT_CLASS: - m_topology = D3D11_PRIMITIVE_TOPOLOGY_POINTLIST; - m_perfmon.Put(GSPerfMon::Prim, m_count); - break; - case GS_LINE_CLASS: - case GS_SPRITE_CLASS: - m_topology = D3D11_PRIMITIVE_TOPOLOGY_LINELIST; - m_perfmon.Put(GSPerfMon::Prim, m_count / 2); - break; - case GS_TRIANGLE_CLASS: - m_topology = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST; - m_perfmon.Put(GSPerfMon::Prim, m_count / 3); - break; - default: - __assume(0); - } - - __super::Draw(rt, ds, tex); -} +/* + * Copyright (C) 2007-2009 Gabest + * http://www.gabest.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program 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 GNU Make; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "stdafx.h" +#include "GSRendererDX11.h" +#include "GSCrc.h" +#include "resource.h" + +GSRendererDX11::GSRendererDX11() + : GSRendererDX(new GSTextureCache11(this), GSVector2(-0.5f, -0.5f)) +{ + InitVertexKick(); +} + +bool GSRendererDX11::CreateDevice(GSDevice* dev) +{ + if(!__super::CreateDevice(dev)) + return false; + + return true; +} + +template +void GSRendererDX11::VertexKick(bool skip) +{ + GSVertexHW11& dst = m_vl.AddTail(); + + dst.vi[0] = m_v.vi[0]; + dst.vi[1] = m_v.vi[1]; + +#ifdef USE_UPSCALE_HACKS + if(tme && fst) + { + //GSVector4::storel(&dst.ST, m_v.GetUV()); + int Udiff = 0; + int Vdiff = 0; + int Uadjust = 0; + int Vadjust = 0; + int multiplier = upscale_Multiplier(); + + if (multiplier > 1) { + + Udiff = m_v.UV.U & 4095; + Vdiff = m_v.UV.V & 4095; + if (Udiff != 0){ + if (Udiff >= 4080) {/*printf("U+ %d %d\n", Udiff, m_v.UV.U);*/ Uadjust = -1; } + else if (Udiff <= 16) {/*printf("U- %d %d\n", Udiff, m_v.UV.U);*/ Uadjust = 1; } + } + if (Vdiff != 0){ + if (Vdiff >= 4080) {/*printf("V+ %d %d\n", Vdiff, m_v.UV.V);*/ Vadjust = -1; } + else if (Vdiff <= 16) {/*printf("V- %d %d\n", Vdiff, m_v.UV.V);*/ Vadjust = 1; } + } + + Udiff = m_v.UV.U & 255; + Vdiff = m_v.UV.V & 255; + if (Udiff != 0){ + if (Udiff >= 248) { Uadjust = -1; } + else if (Udiff <= 8) { Uadjust = 1; } + } + + if (Vdiff != 0){ + if (Vdiff >= 248) { Vadjust = -1; } + else if (Vdiff <= 8) { Vadjust = 1; } + } + + Udiff = m_v.UV.U & 15; + Vdiff = m_v.UV.V & 15; + if (Udiff != 0){ + if (Udiff >= 15) { Uadjust = -1; } + else if (Udiff <= 1) { Uadjust = 1; } + } + + if (Vdiff != 0){ + if (Vdiff >= 15) { Vadjust = -1; } + else if (Vdiff <= 1) { Vadjust = 1; } + } + } + dst.ST.S = (float)m_v.UV.U - Uadjust; + dst.ST.T = (float)m_v.UV.V - Vadjust; + } + else if (tme) + { + // Wip :p + //dst.XYZ.X += 5; + //dst.XYZ.Y += 5; + } +#else + if(tme && fst) + { + GSVector4::storel(&dst.ST, m_v.GetUV()); + } +#endif + + int count = 0; + + if(GSVertexHW11* v = DrawingKick(skip, count)) + { + GSVector4i scissor = m_context->scissor.dx10; + + GSVector4i pmin, pmax; + + #if _M_SSE >= 0x401 + + GSVector4i v0, v1, v2; + + switch(prim) + { + case GS_POINTLIST: + v0 = GSVector4i::load((int)v[0].p.xy).upl16(); + pmin = v0; + pmax = v0; + break; + case GS_LINELIST: + case GS_LINESTRIP: + case GS_SPRITE: + v0 = GSVector4i::load((int)v[0].p.xy); + v1 = GSVector4i::load((int)v[1].p.xy); + pmin = v0.min_u16(v1).upl16(); + pmax = v0.max_u16(v1).upl16(); + break; + case GS_TRIANGLELIST: + case GS_TRIANGLESTRIP: + case GS_TRIANGLEFAN: + v0 = GSVector4i::load((int)v[0].p.xy); + v1 = GSVector4i::load((int)v[1].p.xy); + v2 = GSVector4i::load((int)v[2].p.xy); + pmin = v0.min_u16(v1).min_u16(v2).upl16(); + pmax = v0.max_u16(v1).max_u16(v2).upl16(); + break; + } + + #else + + switch(prim) + { + case GS_POINTLIST: + pmin.x = v[0].p.x; + pmin.y = v[0].p.y; + pmax.x = v[0].p.x; + pmax.y = v[0].p.y; + break; + case GS_LINELIST: + case GS_LINESTRIP: + case GS_SPRITE: + pmin.x = std::min(v[0].p.x, v[1].p.x); + pmin.y = std::min(v[0].p.y, v[1].p.y); + pmax.x = std::max(v[0].p.x, v[1].p.x); + pmax.y = std::max(v[0].p.y, v[1].p.y); + break; + case GS_TRIANGLELIST: + case GS_TRIANGLESTRIP: + case GS_TRIANGLEFAN: + pmin.x = std::min(std::min(v[0].p.x, v[1].p.x), v[2].p.x); + pmin.y = std::min(std::min(v[0].p.y, v[1].p.y), v[2].p.y); + pmax.x = std::max(std::max(v[0].p.x, v[1].p.x), v[2].p.x); + pmax.y = std::max(std::max(v[0].p.y, v[1].p.y), v[2].p.y); + break; + } + + #endif + + GSVector4i test = (pmax < scissor) | (pmin > scissor.zwxy()); + + switch(prim) + { + case GS_TRIANGLELIST: + case GS_TRIANGLESTRIP: + case GS_TRIANGLEFAN: + case GS_SPRITE: + test |= pmin == pmax; + break; + } + + if(test.mask() & 0xff) + { + return; + } + + m_count += count; + } +} + +void GSRendererDX11::Draw(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* tex) +{ + switch(m_vt.m_primclass) + { + case GS_POINT_CLASS: + m_topology = D3D11_PRIMITIVE_TOPOLOGY_POINTLIST; + m_perfmon.Put(GSPerfMon::Prim, m_count); + break; + case GS_LINE_CLASS: + case GS_SPRITE_CLASS: + m_topology = D3D11_PRIMITIVE_TOPOLOGY_LINELIST; + m_perfmon.Put(GSPerfMon::Prim, m_count / 2); + break; + case GS_TRIANGLE_CLASS: + m_topology = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST; + m_perfmon.Put(GSPerfMon::Prim, m_count / 3); + break; + default: + __assume(0); + } + + __super::Draw(rt, ds, tex); +} diff --git a/plugins/GSdx/GSRendererDX11.h b/plugins/GSdx/GSRendererDX11.h index 0f564a88cc..125d60a735 100644 --- a/plugins/GSdx/GSRendererDX11.h +++ b/plugins/GSdx/GSRendererDX11.h @@ -1,40 +1,40 @@ -/* - * Copyright (C) 2007-2009 Gabest - * http://www.gabest.org - * - * This Program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This Program 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 GNU Make; see the file COPYING. If not, write to - * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - * http://www.gnu.org/copyleft/gpl.html - * - */ - -#pragma once - -#include "GSRendererDX.h" -#include "GSVertexHW.h" -#include "GSTextureCache11.h" - -class GSRendererDX11 : public GSRendererDX -{ -protected: - void Draw(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* tex); - -public: - GSRendererDX11(); - virtual ~GSRendererDX11() {} - - bool CreateDevice(GSDevice* dev); - - template void VertexKick(bool skip); +/* + * Copyright (C) 2007-2009 Gabest + * http://www.gabest.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program 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 GNU Make; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#pragma once + +#include "GSRendererDX.h" +#include "GSVertexHW.h" +#include "GSTextureCache11.h" + +class GSRendererDX11 : public GSRendererDX +{ +protected: + void Draw(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* tex); + +public: + GSRendererDX11(); + virtual ~GSRendererDX11() {} + + bool CreateDevice(GSDevice* dev); + + template void VertexKick(bool skip); }; \ No newline at end of file diff --git a/plugins/GSdx/GSScanlineEnvironment.h b/plugins/GSdx/GSScanlineEnvironment.h index 0775b083ba..2a4fb7ba2e 100644 --- a/plugins/GSdx/GSScanlineEnvironment.h +++ b/plugins/GSdx/GSScanlineEnvironment.h @@ -1,145 +1,145 @@ -/* - * Copyright (C) 2007-2009 Gabest - * http://www.gabest.org - * - * This Program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This Program 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 GNU Make; see the file COPYING. If not, write to - * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - * http://www.gnu.org/copyleft/gpl.html - * - */ - -#pragma once - -#include "GSLocalMemory.h" -#include "GSVector.h" - -union GSScanlineSelector -{ - struct - { - uint32 fpsm:2; // 0 - uint32 zpsm:2; // 2 - uint32 ztst:2; // 4 (0: off, 1: write, 2: test (ge), 3: test (g)) - uint32 atst:3; // 6 - uint32 afail:2; // 9 - uint32 iip:1; // 11 - uint32 tfx:3; // 12 - uint32 tcc:1; // 15 - uint32 fst:1; // 16 - uint32 ltf:1; // 17 - uint32 tlu:1; // 18 - uint32 fge:1; // 19 - uint32 date:1; // 20 - uint32 abe:1; // 21 - uint32 aba:2; // 22 - uint32 abb:2; // 24 - uint32 abc:2; // 26 - uint32 abd:2; // 28 - uint32 pabe:1; // 30 - uint32 aa1:1; // 31 - - uint32 fwrite:1; // 32 - uint32 ftest:1; // 33 - uint32 rfb:1; // 34 - uint32 zwrite:1; // 35 - uint32 ztest:1; // 36 - uint32 zoverflow:1; // 37 (z max >= 0x80000000) - uint32 wms:2; // 38 - uint32 wmt:2; // 40 - uint32 datm:1; // 42 - uint32 colclamp:1; // 43 - uint32 fba:1; // 44 - uint32 dthe:1; // 45 - uint32 sprite:1; // 46 - uint32 edge:1; // 47 - }; - - struct - { - uint32 _pad1:22; - uint32 ababcd:8; - uint32 _pad2:2; - uint32 fb:2; - uint32 _pad3:1; - uint32 zb:2; - }; - - struct - { - uint32 lo; - uint32 hi; - }; - - uint64 key; - - operator uint32() {return lo;} - operator uint64() {return key;} - - bool IsSolidRect() - { - return sprite - && iip == 0 - && tfx == TFX_NONE - && abe == 0 - && ztst <= 1 - && atst <= 1 - && date == 0 - && fge == 0; - } -}; - -__aligned16 struct GSScanlineParam -{ - GSScanlineSelector sel; - - void* vm; - const void* tex; - const uint32* clut; - uint32 tw; - - GSOffset* fbo; - GSOffset* zbo; - GSPixelOffset4* fzbo; - - uint32 fm, zm; -}; - -__aligned16 struct GSScanlineEnvironment -{ - void* vm; - const void* tex; - const uint32* clut; - uint32 tw; - - int* fbr; - int* zbr; - int* fbc; - int* zbc; - GSVector2i* fzbr; - GSVector2i* fzbc; - - GSVector4i* dimx; - - GSVector4i fm, zm; - struct {GSVector4i min, max, mask, invmask;} t; // [u] x 4 [v] x 4 - GSVector4i aref; - GSVector4i afix; - GSVector4i frb, fga; - - struct {GSVector4 z, s, t, q; GSVector4i rb, ga, f, si, ti, _pad[7];} d[4]; - struct {GSVector4 z, stq; GSVector4i c, f, st;} d4; - struct {GSVector4i rb, ga;} c; - struct {GSVector4i z, f;} p; - struct {GSVector4i z, f, s, t, q, rb, ga, zs, zd, uf, vf, cov;} temp; -}; +/* + * Copyright (C) 2007-2009 Gabest + * http://www.gabest.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program 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 GNU Make; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#pragma once + +#include "GSLocalMemory.h" +#include "GSVector.h" + +union GSScanlineSelector +{ + struct + { + uint32 fpsm:2; // 0 + uint32 zpsm:2; // 2 + uint32 ztst:2; // 4 (0: off, 1: write, 2: test (ge), 3: test (g)) + uint32 atst:3; // 6 + uint32 afail:2; // 9 + uint32 iip:1; // 11 + uint32 tfx:3; // 12 + uint32 tcc:1; // 15 + uint32 fst:1; // 16 + uint32 ltf:1; // 17 + uint32 tlu:1; // 18 + uint32 fge:1; // 19 + uint32 date:1; // 20 + uint32 abe:1; // 21 + uint32 aba:2; // 22 + uint32 abb:2; // 24 + uint32 abc:2; // 26 + uint32 abd:2; // 28 + uint32 pabe:1; // 30 + uint32 aa1:1; // 31 + + uint32 fwrite:1; // 32 + uint32 ftest:1; // 33 + uint32 rfb:1; // 34 + uint32 zwrite:1; // 35 + uint32 ztest:1; // 36 + uint32 zoverflow:1; // 37 (z max >= 0x80000000) + uint32 wms:2; // 38 + uint32 wmt:2; // 40 + uint32 datm:1; // 42 + uint32 colclamp:1; // 43 + uint32 fba:1; // 44 + uint32 dthe:1; // 45 + uint32 sprite:1; // 46 + uint32 edge:1; // 47 + }; + + struct + { + uint32 _pad1:22; + uint32 ababcd:8; + uint32 _pad2:2; + uint32 fb:2; + uint32 _pad3:1; + uint32 zb:2; + }; + + struct + { + uint32 lo; + uint32 hi; + }; + + uint64 key; + + operator uint32() {return lo;} + operator uint64() {return key;} + + bool IsSolidRect() + { + return sprite + && iip == 0 + && tfx == TFX_NONE + && abe == 0 + && ztst <= 1 + && atst <= 1 + && date == 0 + && fge == 0; + } +}; + +__aligned16 struct GSScanlineParam +{ + GSScanlineSelector sel; + + void* vm; + const void* tex; + const uint32* clut; + uint32 tw; + + GSOffset* fbo; + GSOffset* zbo; + GSPixelOffset4* fzbo; + + uint32 fm, zm; +}; + +__aligned16 struct GSScanlineEnvironment +{ + void* vm; + const void* tex; + const uint32* clut; + uint32 tw; + + int* fbr; + int* zbr; + int* fbc; + int* zbc; + GSVector2i* fzbr; + GSVector2i* fzbc; + + GSVector4i* dimx; + + GSVector4i fm, zm; + struct {GSVector4i min, max, mask, invmask;} t; // [u] x 4 [v] x 4 + GSVector4i aref; + GSVector4i afix; + GSVector4i frb, fga; + + struct {GSVector4 z, s, t, q; GSVector4i rb, ga, f, si, ti, _pad[7];} d[4]; + struct {GSVector4 z, stq; GSVector4i c, f, st;} d4; + struct {GSVector4i rb, ga;} c; + struct {GSVector4i z, f;} p; + struct {GSVector4i z, f, s, t, q, rb, ga, zs, zd, uf, vf, cov;} temp; +}; diff --git a/plugins/GSdx/GSSetupPrimCodeGenerator.cpp b/plugins/GSdx/GSSetupPrimCodeGenerator.cpp index 3efc752076..8bfa457828 100644 --- a/plugins/GSdx/GSSetupPrimCodeGenerator.cpp +++ b/plugins/GSdx/GSSetupPrimCodeGenerator.cpp @@ -1,377 +1,377 @@ -/* - * Copyright (C) 2007-2009 Gabest - * http://www.gabest.org - * - * This Program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This Program 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 GNU Make; see the file COPYING. If not, write to - * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - * http://www.gnu.org/copyleft/gpl.html - * - */ - -// TODO: x64 - -#include "StdAfx.h" -#include "GSSetupPrimCodeGenerator.h" - -GSSetupPrimCodeGenerator::GSSetupPrimCodeGenerator(GSScanlineEnvironment& env, uint64 key, void* ptr, size_t maxsize) - : CodeGenerator(maxsize, ptr) - , m_env(env) -{ - m_sel.key = key; - - m_en.z = m_sel.zb ? 1 : 0; - m_en.f = m_sel.fb && m_sel.fge ? 1 : 0; - m_en.t = m_sel.fb && m_sel.tfx != TFX_NONE ? 1 : 0; - m_en.c = m_sel.fb && !(m_sel.tfx == TFX_DECAL && m_sel.tcc) ? 1 : 0; - - #if _M_AMD64 - #error TODO - #endif - - Generate(); -} - -void GSSetupPrimCodeGenerator::Generate() -{ - if((m_en.z || m_en.f) && !m_sel.sprite || m_en.t || m_en.c && m_sel.iip) - { - for(int i = 0; i < 5; i++) - { - movaps(Xmm(3 + i), xmmword[&m_shift[i]]); - } - } - - Depth(); - - Texture(); - - Color(); - - ret(); -} - -void GSSetupPrimCodeGenerator::Depth() -{ - if(!m_en.z && !m_en.f) - { - return; - } - - if(!m_sel.sprite) - { - // GSVector4 t = dscan.p; - - movaps(xmm0, xmmword[edx + 16]); - - if(m_en.f) - { - // GSVector4 df = p.wwww(); - - movaps(xmm1, xmm0); - shufps(xmm1, xmm1, _MM_SHUFFLE(3, 3, 3, 3)); - - // m_env.d4.f = GSVector4i(df * 4.0f).xxzzlh(); - - movaps(xmm2, xmm1); - mulps(xmm2, xmm3); - cvttps2dq(xmm2, xmm2); - pshuflw(xmm2, xmm2, _MM_SHUFFLE(2, 2, 0, 0)); - pshufhw(xmm2, xmm2, _MM_SHUFFLE(2, 2, 0, 0)); - movdqa(xmmword[&m_env.d4.f], xmm2); - - for(int i = 0; i < 4; i++) - { - // m_env.d[i].f = GSVector4i(df * m_shift[i]).xxzzlh(); - - movaps(xmm2, xmm1); - mulps(xmm2, Xmm(4 + i)); - cvttps2dq(xmm2, xmm2); - pshuflw(xmm2, xmm2, _MM_SHUFFLE(2, 2, 0, 0)); - pshufhw(xmm2, xmm2, _MM_SHUFFLE(2, 2, 0, 0)); - movdqa(xmmword[&m_env.d[i].f], xmm2); - } - } - - if(m_en.z) - { - // GSVector4 dz = p.zzzz(); - - shufps(xmm0, xmm0, _MM_SHUFFLE(2, 2, 2, 2)); - - // m_env.d4.z = dz * 4.0f; - - movaps(xmm1, xmm0); - mulps(xmm1, xmm3); - movdqa(xmmword[&m_env.d4.z], xmm1); - - for(int i = 0; i < 4; i++) - { - // m_env.d[i].z = dz * m_shift[i]; - - movaps(xmm1, xmm0); - mulps(xmm1, Xmm(4 + i)); - movdqa(xmmword[&m_env.d[i].z], xmm1); - } - } - } - else - { - // GSVector4 p = vertices[0].p; - - movaps(xmm0, xmmword[ecx + 16]); - - if(m_en.f) - { - // m_env.p.f = GSVector4i(p).zzzzh().zzzz(); - - movaps(xmm1, xmm0); - cvttps2dq(xmm1, xmm1); - pshufhw(xmm1, xmm1, _MM_SHUFFLE(2, 2, 2, 2)); - pshufd(xmm1, xmm1, _MM_SHUFFLE(2, 2, 2, 2)); - movdqa(xmmword[&m_env.p.f], xmm1); - } - - if(m_en.z) - { - // GSVector4 z = p.zzzz(); - - shufps(xmm0, xmm0, _MM_SHUFFLE(2, 2, 2, 2)); - - if(m_sel.zoverflow) - { - // m_env.p.z = (GSVector4i(z * 0.5f) << 1) | (GSVector4i(z) & GSVector4i::x00000001()); - - static const float half = 0.5f; - - movss(xmm1, dword[&half]); - shufps(xmm1, xmm1, _MM_SHUFFLE(0, 0, 0, 0)); - mulps(xmm1, xmm0); - cvttps2dq(xmm1, xmm1); - pslld(xmm1, 1); - - cvttps2dq(xmm0, xmm0); - pcmpeqd(xmm2, xmm2); - psrld(xmm2, 31); - pand(xmm0, xmm2); - - por(xmm0, xmm1); - } - else - { - // m_env.p.z = GSVector4i(z); - - cvttps2dq(xmm0, xmm0); - } - - movdqa(xmmword[&m_env.p.z], xmm0); - } - } -} - -void GSSetupPrimCodeGenerator::Texture() -{ - if(!m_en.t) - { - return; - } - - // GSVector4 t = dscan.t; - - movaps(xmm0, xmmword[edx + 32]); - - movaps(xmm1, xmm0); - mulps(xmm1, xmm3); - - if(m_sel.fst) - { - // m_env.d4.st = GSVector4i(t * 4.0f); - - cvttps2dq(xmm1, xmm1); - movdqa(xmmword[&m_env.d4.st], xmm1); - } - else - { - // m_env.d4.stq = t * 4.0f; - - movaps(xmmword[&m_env.d4.stq], xmm1); - } - - for(int j = 0, k = m_sel.fst ? 2 : 3; j < k; j++) - { - // GSVector4 ds = t.xxxx(); - // GSVector4 dt = t.yyyy(); - // GSVector4 dq = t.zzzz(); - - movaps(xmm1, xmm0); - shufps(xmm1, xmm1, (uint8)_MM_SHUFFLE(j, j, j, j)); - - for(int i = 0; i < 4; i++) - { - // GSVector4 v = ds/dt * m_shift[i]; - - movaps(xmm2, xmm1); - mulps(xmm2, Xmm(4 + i)); - - if(m_sel.fst) - { - // m_env.d[i].si/ti = GSVector4i(v); - - cvttps2dq(xmm2, xmm2); - - switch(j) - { - case 0: movdqa(xmmword[&m_env.d[i].si], xmm2); break; - case 1: movdqa(xmmword[&m_env.d[i].ti], xmm2); break; - } - } - else - { - // m_env.d[i].s/t/q = v; - - switch(j) - { - case 0: movaps(xmmword[&m_env.d[i].s], xmm2); break; - case 1: movaps(xmmword[&m_env.d[i].t], xmm2); break; - case 2: movaps(xmmword[&m_env.d[i].q], xmm2); break; - } - } - } - } -} - -void GSSetupPrimCodeGenerator::Color() -{ - if(!m_en.c) - { - return; - } - - if(m_sel.iip) - { - // GSVector4 c = dscan.c; - - movaps(xmm0, xmmword[edx]); - movaps(xmm1, xmm0); - - // m_env.d4.c = GSVector4i(c * 4.0f).xzyw().ps32(); - - movaps(xmm2, xmm0); - mulps(xmm2, xmm3); - cvttps2dq(xmm2, xmm2); - pshufd(xmm2, xmm2, _MM_SHUFFLE(3, 1, 2, 0)); - packssdw(xmm2, xmm2); - movdqa(xmmword[&m_env.d4.c], xmm2); - - // xmm3 is not needed anymore - - // GSVector4 dr = c.xxxx(); - // GSVector4 db = c.zzzz(); - - shufps(xmm0, xmm0, _MM_SHUFFLE(0, 0, 0, 0)); - shufps(xmm1, xmm1, _MM_SHUFFLE(2, 2, 2, 2)); - - for(int i = 0; i < 4; i++) - { - // GSVector4i r = GSVector4i(dr * m_shift[i]).ps32(); - - movaps(xmm2, xmm0); - mulps(xmm2, Xmm(4 + i)); - cvttps2dq(xmm2, xmm2); - packssdw(xmm2, xmm2); - - // GSVector4i b = GSVector4i(db * m_shift[i]).ps32(); - - movaps(xmm3, xmm1); - mulps(xmm3, Xmm(4 + i)); - cvttps2dq(xmm3, xmm3); - packssdw(xmm3, xmm3); - - // m_env.d[i].rb = r.upl16(b); - - punpcklwd(xmm2, xmm3); - movdqa(xmmword[&m_env.d[i].rb], xmm2); - } - - // GSVector4 c = dscan.c; - - movaps(xmm0, xmmword[edx]); // not enough regs, have to reload it - movaps(xmm1, xmm0); - - // GSVector4 dg = c.yyyy(); - // GSVector4 da = c.wwww(); - - shufps(xmm0, xmm0, _MM_SHUFFLE(1, 1, 1, 1)); - shufps(xmm1, xmm1, _MM_SHUFFLE(3, 3, 3, 3)); - - for(int i = 0; i < 4; i++) - { - // GSVector4i g = GSVector4i(dg * m_shift[i]).ps32(); - - movaps(xmm2, xmm0); - mulps(xmm2, Xmm(4 + i)); - cvttps2dq(xmm2, xmm2); - packssdw(xmm2, xmm2); - - // GSVector4i a = GSVector4i(da * m_shift[i]).ps32(); - - movaps(xmm3, xmm1); - mulps(xmm3, Xmm(4 + i)); - cvttps2dq(xmm3, xmm3); - packssdw(xmm3, xmm3); - - // m_env.d[i].ga = g.upl16(a); - - punpcklwd(xmm2, xmm3); - movdqa(xmmword[&m_env.d[i].ga], xmm2); - } - } - else - { - // GSVector4i c = GSVector4i(vertices[0].c); - - movaps(xmm0, xmmword[ecx]); - cvttps2dq(xmm0, xmm0); - - // c = c.upl16(c.zwxy()); - - movdqa(xmm1, xmm0); - pshufd(xmm1, xmm1, _MM_SHUFFLE(1, 0, 3, 2)); - punpcklwd(xmm0, xmm1); - - // if(!tme) c = c.srl16(7); - - if(m_sel.tfx == TFX_NONE) - { - psrlw(xmm0, 7); - } - - // m_env.c.rb = c.xxxx(); - // m_env.c.ga = c.zzzz(); - - movdqa(xmm1, xmm0); - pshufd(xmm0, xmm0, _MM_SHUFFLE(0, 0, 0, 0)); - pshufd(xmm1, xmm1, _MM_SHUFFLE(2, 2, 2, 2)); - movdqa(xmmword[&m_env.c.rb], xmm0); - movdqa(xmmword[&m_env.c.ga], xmm1); - } -} - -const GSVector4 GSSetupPrimCodeGenerator::m_shift[5] = -{ - GSVector4(4.0f, 4.0f, 4.0f, 4.0f), - GSVector4(0.0f, 1.0f, 2.0f, 3.0f), - GSVector4(-1.0f, 0.0f, 1.0f, 2.0f), - GSVector4(-2.0f, -1.0f, 0.0f, 1.0f), - GSVector4(-3.0f, -2.0f, -1.0f, 0.0f), +/* + * Copyright (C) 2007-2009 Gabest + * http://www.gabest.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program 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 GNU Make; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +// TODO: x64 + +#include "StdAfx.h" +#include "GSSetupPrimCodeGenerator.h" + +GSSetupPrimCodeGenerator::GSSetupPrimCodeGenerator(GSScanlineEnvironment& env, uint64 key, void* ptr, size_t maxsize) + : CodeGenerator(maxsize, ptr) + , m_env(env) +{ + m_sel.key = key; + + m_en.z = m_sel.zb ? 1 : 0; + m_en.f = m_sel.fb && m_sel.fge ? 1 : 0; + m_en.t = m_sel.fb && m_sel.tfx != TFX_NONE ? 1 : 0; + m_en.c = m_sel.fb && !(m_sel.tfx == TFX_DECAL && m_sel.tcc) ? 1 : 0; + + #if _M_AMD64 + #error TODO + #endif + + Generate(); +} + +void GSSetupPrimCodeGenerator::Generate() +{ + if((m_en.z || m_en.f) && !m_sel.sprite || m_en.t || m_en.c && m_sel.iip) + { + for(int i = 0; i < 5; i++) + { + movaps(Xmm(3 + i), xmmword[&m_shift[i]]); + } + } + + Depth(); + + Texture(); + + Color(); + + ret(); +} + +void GSSetupPrimCodeGenerator::Depth() +{ + if(!m_en.z && !m_en.f) + { + return; + } + + if(!m_sel.sprite) + { + // GSVector4 t = dscan.p; + + movaps(xmm0, xmmword[edx + 16]); + + if(m_en.f) + { + // GSVector4 df = p.wwww(); + + movaps(xmm1, xmm0); + shufps(xmm1, xmm1, _MM_SHUFFLE(3, 3, 3, 3)); + + // m_env.d4.f = GSVector4i(df * 4.0f).xxzzlh(); + + movaps(xmm2, xmm1); + mulps(xmm2, xmm3); + cvttps2dq(xmm2, xmm2); + pshuflw(xmm2, xmm2, _MM_SHUFFLE(2, 2, 0, 0)); + pshufhw(xmm2, xmm2, _MM_SHUFFLE(2, 2, 0, 0)); + movdqa(xmmword[&m_env.d4.f], xmm2); + + for(int i = 0; i < 4; i++) + { + // m_env.d[i].f = GSVector4i(df * m_shift[i]).xxzzlh(); + + movaps(xmm2, xmm1); + mulps(xmm2, Xmm(4 + i)); + cvttps2dq(xmm2, xmm2); + pshuflw(xmm2, xmm2, _MM_SHUFFLE(2, 2, 0, 0)); + pshufhw(xmm2, xmm2, _MM_SHUFFLE(2, 2, 0, 0)); + movdqa(xmmword[&m_env.d[i].f], xmm2); + } + } + + if(m_en.z) + { + // GSVector4 dz = p.zzzz(); + + shufps(xmm0, xmm0, _MM_SHUFFLE(2, 2, 2, 2)); + + // m_env.d4.z = dz * 4.0f; + + movaps(xmm1, xmm0); + mulps(xmm1, xmm3); + movdqa(xmmword[&m_env.d4.z], xmm1); + + for(int i = 0; i < 4; i++) + { + // m_env.d[i].z = dz * m_shift[i]; + + movaps(xmm1, xmm0); + mulps(xmm1, Xmm(4 + i)); + movdqa(xmmword[&m_env.d[i].z], xmm1); + } + } + } + else + { + // GSVector4 p = vertices[0].p; + + movaps(xmm0, xmmword[ecx + 16]); + + if(m_en.f) + { + // m_env.p.f = GSVector4i(p).zzzzh().zzzz(); + + movaps(xmm1, xmm0); + cvttps2dq(xmm1, xmm1); + pshufhw(xmm1, xmm1, _MM_SHUFFLE(2, 2, 2, 2)); + pshufd(xmm1, xmm1, _MM_SHUFFLE(2, 2, 2, 2)); + movdqa(xmmword[&m_env.p.f], xmm1); + } + + if(m_en.z) + { + // GSVector4 z = p.zzzz(); + + shufps(xmm0, xmm0, _MM_SHUFFLE(2, 2, 2, 2)); + + if(m_sel.zoverflow) + { + // m_env.p.z = (GSVector4i(z * 0.5f) << 1) | (GSVector4i(z) & GSVector4i::x00000001()); + + static const float half = 0.5f; + + movss(xmm1, dword[&half]); + shufps(xmm1, xmm1, _MM_SHUFFLE(0, 0, 0, 0)); + mulps(xmm1, xmm0); + cvttps2dq(xmm1, xmm1); + pslld(xmm1, 1); + + cvttps2dq(xmm0, xmm0); + pcmpeqd(xmm2, xmm2); + psrld(xmm2, 31); + pand(xmm0, xmm2); + + por(xmm0, xmm1); + } + else + { + // m_env.p.z = GSVector4i(z); + + cvttps2dq(xmm0, xmm0); + } + + movdqa(xmmword[&m_env.p.z], xmm0); + } + } +} + +void GSSetupPrimCodeGenerator::Texture() +{ + if(!m_en.t) + { + return; + } + + // GSVector4 t = dscan.t; + + movaps(xmm0, xmmword[edx + 32]); + + movaps(xmm1, xmm0); + mulps(xmm1, xmm3); + + if(m_sel.fst) + { + // m_env.d4.st = GSVector4i(t * 4.0f); + + cvttps2dq(xmm1, xmm1); + movdqa(xmmword[&m_env.d4.st], xmm1); + } + else + { + // m_env.d4.stq = t * 4.0f; + + movaps(xmmword[&m_env.d4.stq], xmm1); + } + + for(int j = 0, k = m_sel.fst ? 2 : 3; j < k; j++) + { + // GSVector4 ds = t.xxxx(); + // GSVector4 dt = t.yyyy(); + // GSVector4 dq = t.zzzz(); + + movaps(xmm1, xmm0); + shufps(xmm1, xmm1, (uint8)_MM_SHUFFLE(j, j, j, j)); + + for(int i = 0; i < 4; i++) + { + // GSVector4 v = ds/dt * m_shift[i]; + + movaps(xmm2, xmm1); + mulps(xmm2, Xmm(4 + i)); + + if(m_sel.fst) + { + // m_env.d[i].si/ti = GSVector4i(v); + + cvttps2dq(xmm2, xmm2); + + switch(j) + { + case 0: movdqa(xmmword[&m_env.d[i].si], xmm2); break; + case 1: movdqa(xmmword[&m_env.d[i].ti], xmm2); break; + } + } + else + { + // m_env.d[i].s/t/q = v; + + switch(j) + { + case 0: movaps(xmmword[&m_env.d[i].s], xmm2); break; + case 1: movaps(xmmword[&m_env.d[i].t], xmm2); break; + case 2: movaps(xmmword[&m_env.d[i].q], xmm2); break; + } + } + } + } +} + +void GSSetupPrimCodeGenerator::Color() +{ + if(!m_en.c) + { + return; + } + + if(m_sel.iip) + { + // GSVector4 c = dscan.c; + + movaps(xmm0, xmmword[edx]); + movaps(xmm1, xmm0); + + // m_env.d4.c = GSVector4i(c * 4.0f).xzyw().ps32(); + + movaps(xmm2, xmm0); + mulps(xmm2, xmm3); + cvttps2dq(xmm2, xmm2); + pshufd(xmm2, xmm2, _MM_SHUFFLE(3, 1, 2, 0)); + packssdw(xmm2, xmm2); + movdqa(xmmword[&m_env.d4.c], xmm2); + + // xmm3 is not needed anymore + + // GSVector4 dr = c.xxxx(); + // GSVector4 db = c.zzzz(); + + shufps(xmm0, xmm0, _MM_SHUFFLE(0, 0, 0, 0)); + shufps(xmm1, xmm1, _MM_SHUFFLE(2, 2, 2, 2)); + + for(int i = 0; i < 4; i++) + { + // GSVector4i r = GSVector4i(dr * m_shift[i]).ps32(); + + movaps(xmm2, xmm0); + mulps(xmm2, Xmm(4 + i)); + cvttps2dq(xmm2, xmm2); + packssdw(xmm2, xmm2); + + // GSVector4i b = GSVector4i(db * m_shift[i]).ps32(); + + movaps(xmm3, xmm1); + mulps(xmm3, Xmm(4 + i)); + cvttps2dq(xmm3, xmm3); + packssdw(xmm3, xmm3); + + // m_env.d[i].rb = r.upl16(b); + + punpcklwd(xmm2, xmm3); + movdqa(xmmword[&m_env.d[i].rb], xmm2); + } + + // GSVector4 c = dscan.c; + + movaps(xmm0, xmmword[edx]); // not enough regs, have to reload it + movaps(xmm1, xmm0); + + // GSVector4 dg = c.yyyy(); + // GSVector4 da = c.wwww(); + + shufps(xmm0, xmm0, _MM_SHUFFLE(1, 1, 1, 1)); + shufps(xmm1, xmm1, _MM_SHUFFLE(3, 3, 3, 3)); + + for(int i = 0; i < 4; i++) + { + // GSVector4i g = GSVector4i(dg * m_shift[i]).ps32(); + + movaps(xmm2, xmm0); + mulps(xmm2, Xmm(4 + i)); + cvttps2dq(xmm2, xmm2); + packssdw(xmm2, xmm2); + + // GSVector4i a = GSVector4i(da * m_shift[i]).ps32(); + + movaps(xmm3, xmm1); + mulps(xmm3, Xmm(4 + i)); + cvttps2dq(xmm3, xmm3); + packssdw(xmm3, xmm3); + + // m_env.d[i].ga = g.upl16(a); + + punpcklwd(xmm2, xmm3); + movdqa(xmmword[&m_env.d[i].ga], xmm2); + } + } + else + { + // GSVector4i c = GSVector4i(vertices[0].c); + + movaps(xmm0, xmmword[ecx]); + cvttps2dq(xmm0, xmm0); + + // c = c.upl16(c.zwxy()); + + movdqa(xmm1, xmm0); + pshufd(xmm1, xmm1, _MM_SHUFFLE(1, 0, 3, 2)); + punpcklwd(xmm0, xmm1); + + // if(!tme) c = c.srl16(7); + + if(m_sel.tfx == TFX_NONE) + { + psrlw(xmm0, 7); + } + + // m_env.c.rb = c.xxxx(); + // m_env.c.ga = c.zzzz(); + + movdqa(xmm1, xmm0); + pshufd(xmm0, xmm0, _MM_SHUFFLE(0, 0, 0, 0)); + pshufd(xmm1, xmm1, _MM_SHUFFLE(2, 2, 2, 2)); + movdqa(xmmword[&m_env.c.rb], xmm0); + movdqa(xmmword[&m_env.c.ga], xmm1); + } +} + +const GSVector4 GSSetupPrimCodeGenerator::m_shift[5] = +{ + GSVector4(4.0f, 4.0f, 4.0f, 4.0f), + GSVector4(0.0f, 1.0f, 2.0f, 3.0f), + GSVector4(-1.0f, 0.0f, 1.0f, 2.0f), + GSVector4(-2.0f, -1.0f, 0.0f, 1.0f), + GSVector4(-3.0f, -2.0f, -1.0f, 0.0f), }; \ No newline at end of file diff --git a/plugins/GSdx/GSSetupPrimCodeGenerator.h b/plugins/GSdx/GSSetupPrimCodeGenerator.h index af7dd116b8..548ef4312f 100644 --- a/plugins/GSdx/GSSetupPrimCodeGenerator.h +++ b/plugins/GSdx/GSSetupPrimCodeGenerator.h @@ -1,51 +1,51 @@ -/* - * Copyright (C) 2007-2009 Gabest - * http://www.gabest.org - * - * This Program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This Program 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 GNU Make; see the file COPYING. If not, write to - * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - * http://www.gnu.org/copyleft/gpl.html - * - */ - -#pragma once - -#include "GSScanlineEnvironment.h" -#include "xbyak/xbyak.h" -#include "xbyak/xbyak_util.h" - -using namespace Xbyak; - -class GSSetupPrimCodeGenerator : public CodeGenerator -{ - void operator = (const GSSetupPrimCodeGenerator&); - - static const GSVector4 m_shift[5]; - - util::Cpu m_cpu; - - GSScanlineEnvironment& m_env; - GSScanlineSelector m_sel; - - struct {uint32 z:1, f:1, t:1, c:1;} m_en; - - void Generate(); - - void Depth(); - void Texture(); - void Color(); - -public: - GSSetupPrimCodeGenerator(GSScanlineEnvironment& env, uint64 key, void* ptr, size_t maxsize); +/* + * Copyright (C) 2007-2009 Gabest + * http://www.gabest.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program 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 GNU Make; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#pragma once + +#include "GSScanlineEnvironment.h" +#include "xbyak/xbyak.h" +#include "xbyak/xbyak_util.h" + +using namespace Xbyak; + +class GSSetupPrimCodeGenerator : public CodeGenerator +{ + void operator = (const GSSetupPrimCodeGenerator&); + + static const GSVector4 m_shift[5]; + + util::Cpu m_cpu; + + GSScanlineEnvironment& m_env; + GSScanlineSelector m_sel; + + struct {uint32 z:1, f:1, t:1, c:1;} m_en; + + void Generate(); + + void Depth(); + void Texture(); + void Color(); + +public: + GSSetupPrimCodeGenerator(GSScanlineEnvironment& env, uint64 key, void* ptr, size_t maxsize); }; \ No newline at end of file diff --git a/plugins/GSdx/GSTexture11.cpp b/plugins/GSdx/GSTexture11.cpp index 286aec3b70..14774bbcfa 100644 --- a/plugins/GSdx/GSTexture11.cpp +++ b/plugins/GSdx/GSTexture11.cpp @@ -1,190 +1,190 @@ -/* - * Copyright (C) 2007-2009 Gabest - * http://www.gabest.org - * - * This Program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This Program 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 GNU Make; see the file COPYING. If not, write to - * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - * http://www.gnu.org/copyleft/gpl.html - * - */ - -#include "stdafx.h" -#include "GSTexture11.h" - -GSTexture11::GSTexture11(ID3D11Texture2D* texture) - : m_texture(texture) -{ - ASSERT(m_texture); - - m_texture->GetDevice(&m_dev); - m_texture->GetDesc(&m_desc); - - m_dev->GetImmediateContext(&m_ctx); - - m_size.x = (int)m_desc.Width; - m_size.y = (int)m_desc.Height; - - if(m_desc.BindFlags & D3D11_BIND_RENDER_TARGET) m_type = RenderTarget; - else if(m_desc.BindFlags & D3D11_BIND_DEPTH_STENCIL) m_type = DepthStencil; - else if(m_desc.BindFlags & D3D11_BIND_SHADER_RESOURCE) m_type = Texture; - else if(m_desc.Usage == D3D11_USAGE_STAGING) m_type = Offscreen; - - m_format = (int)m_desc.Format; - - m_msaa = m_desc.SampleDesc.Count > 1; -} - -bool GSTexture11::Update(const GSVector4i& r, const void* data, int pitch) -{ - if(m_dev && m_texture) - { - D3D11_BOX box = {r.left, r.top, 0, r.right, r.bottom, 1}; - - m_ctx->UpdateSubresource(m_texture, 0, &box, data, pitch, 0); - - return true; - } - - return false; -} - -bool GSTexture11::Map(GSMap& m, const GSVector4i* r) -{ - if(r != NULL) - { - // ASSERT(0); // not implemented - - return false; - } - - if(m_texture && m_desc.Usage == D3D11_USAGE_STAGING) - { - D3D11_MAPPED_SUBRESOURCE map; - - if(SUCCEEDED(m_ctx->Map(m_texture, 0, D3D11_MAP_READ_WRITE, 0, &map))) - { - m.bits = (uint8*)map.pData; - m.pitch = (int)map.RowPitch; - - return true; - } - } - - return false; -} - -void GSTexture11::Unmap() -{ - if(m_texture) - { - m_ctx->Unmap(m_texture, 0); - } -} - -bool GSTexture11::Save(const string& fn, bool dds) -{ - CComPtr res; - - if(m_desc.BindFlags & D3D11_BIND_DEPTH_STENCIL) - { - HRESULT hr; - - D3D11_TEXTURE2D_DESC desc; - - memset(&desc, 0, sizeof(desc)); - - m_texture->GetDesc(&desc); - - desc.Usage = D3D11_USAGE_STAGING; - desc.BindFlags = 0; - desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; - - CComPtr src, dst; - - hr = m_dev->CreateTexture2D(&desc, NULL, &src); - - m_ctx->CopyResource(src, m_texture); - - desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; - desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; - - hr = m_dev->CreateTexture2D(&desc, NULL, &dst); - - D3D11_MAPPED_SUBRESOURCE sm, dm; - - hr = m_ctx->Map(src, 0, D3D11_MAP_READ, 0, &sm); - hr = m_ctx->Map(dst, 0, D3D11_MAP_WRITE, 0, &dm); - - uint8* s = (uint8*)sm.pData; - uint8* d = (uint8*)dm.pData; - - for(uint32 y = 0; y < desc.Height; y++, s += sm.RowPitch, d += dm.RowPitch) - { - for(uint32 x = 0; x < desc.Width; x++) - { - ((uint32*)d)[x] = (uint32)(((float*)s)[x*2] * UINT_MAX); - } - } - - m_ctx->Unmap(src, 0); - m_ctx->Unmap(dst, 0); - - res = dst; - } - else - { - res = m_texture; - } - - return SUCCEEDED(D3DX11SaveTextureToFile(m_ctx, res, dds ? D3DX11_IFF_DDS : D3DX11_IFF_BMP, fn.c_str())); -} - -GSTexture11::operator ID3D11Texture2D*() -{ - return m_texture; -} - -GSTexture11::operator ID3D11ShaderResourceView*() -{ - if(!m_srv && m_dev && m_texture) - { - ASSERT(!m_msaa); - - m_dev->CreateShaderResourceView(m_texture, NULL, &m_srv); - } - - return m_srv; -} - -GSTexture11::operator ID3D11RenderTargetView*() -{ - ASSERT(m_dev); - - if(!m_rtv && m_dev && m_texture) - { - m_dev->CreateRenderTargetView(m_texture, NULL, &m_rtv); - } - - return m_rtv; -} - -GSTexture11::operator ID3D11DepthStencilView*() -{ - if(!m_dsv && m_dev && m_texture) - { - m_dev->CreateDepthStencilView(m_texture, NULL, &m_dsv); - } - - return m_dsv; -} +/* + * Copyright (C) 2007-2009 Gabest + * http://www.gabest.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program 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 GNU Make; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "stdafx.h" +#include "GSTexture11.h" + +GSTexture11::GSTexture11(ID3D11Texture2D* texture) + : m_texture(texture) +{ + ASSERT(m_texture); + + m_texture->GetDevice(&m_dev); + m_texture->GetDesc(&m_desc); + + m_dev->GetImmediateContext(&m_ctx); + + m_size.x = (int)m_desc.Width; + m_size.y = (int)m_desc.Height; + + if(m_desc.BindFlags & D3D11_BIND_RENDER_TARGET) m_type = RenderTarget; + else if(m_desc.BindFlags & D3D11_BIND_DEPTH_STENCIL) m_type = DepthStencil; + else if(m_desc.BindFlags & D3D11_BIND_SHADER_RESOURCE) m_type = Texture; + else if(m_desc.Usage == D3D11_USAGE_STAGING) m_type = Offscreen; + + m_format = (int)m_desc.Format; + + m_msaa = m_desc.SampleDesc.Count > 1; +} + +bool GSTexture11::Update(const GSVector4i& r, const void* data, int pitch) +{ + if(m_dev && m_texture) + { + D3D11_BOX box = {r.left, r.top, 0, r.right, r.bottom, 1}; + + m_ctx->UpdateSubresource(m_texture, 0, &box, data, pitch, 0); + + return true; + } + + return false; +} + +bool GSTexture11::Map(GSMap& m, const GSVector4i* r) +{ + if(r != NULL) + { + // ASSERT(0); // not implemented + + return false; + } + + if(m_texture && m_desc.Usage == D3D11_USAGE_STAGING) + { + D3D11_MAPPED_SUBRESOURCE map; + + if(SUCCEEDED(m_ctx->Map(m_texture, 0, D3D11_MAP_READ_WRITE, 0, &map))) + { + m.bits = (uint8*)map.pData; + m.pitch = (int)map.RowPitch; + + return true; + } + } + + return false; +} + +void GSTexture11::Unmap() +{ + if(m_texture) + { + m_ctx->Unmap(m_texture, 0); + } +} + +bool GSTexture11::Save(const string& fn, bool dds) +{ + CComPtr res; + + if(m_desc.BindFlags & D3D11_BIND_DEPTH_STENCIL) + { + HRESULT hr; + + D3D11_TEXTURE2D_DESC desc; + + memset(&desc, 0, sizeof(desc)); + + m_texture->GetDesc(&desc); + + desc.Usage = D3D11_USAGE_STAGING; + desc.BindFlags = 0; + desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + + CComPtr src, dst; + + hr = m_dev->CreateTexture2D(&desc, NULL, &src); + + m_ctx->CopyResource(src, m_texture); + + desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + + hr = m_dev->CreateTexture2D(&desc, NULL, &dst); + + D3D11_MAPPED_SUBRESOURCE sm, dm; + + hr = m_ctx->Map(src, 0, D3D11_MAP_READ, 0, &sm); + hr = m_ctx->Map(dst, 0, D3D11_MAP_WRITE, 0, &dm); + + uint8* s = (uint8*)sm.pData; + uint8* d = (uint8*)dm.pData; + + for(uint32 y = 0; y < desc.Height; y++, s += sm.RowPitch, d += dm.RowPitch) + { + for(uint32 x = 0; x < desc.Width; x++) + { + ((uint32*)d)[x] = (uint32)(((float*)s)[x*2] * UINT_MAX); + } + } + + m_ctx->Unmap(src, 0); + m_ctx->Unmap(dst, 0); + + res = dst; + } + else + { + res = m_texture; + } + + return SUCCEEDED(D3DX11SaveTextureToFile(m_ctx, res, dds ? D3DX11_IFF_DDS : D3DX11_IFF_BMP, fn.c_str())); +} + +GSTexture11::operator ID3D11Texture2D*() +{ + return m_texture; +} + +GSTexture11::operator ID3D11ShaderResourceView*() +{ + if(!m_srv && m_dev && m_texture) + { + ASSERT(!m_msaa); + + m_dev->CreateShaderResourceView(m_texture, NULL, &m_srv); + } + + return m_srv; +} + +GSTexture11::operator ID3D11RenderTargetView*() +{ + ASSERT(m_dev); + + if(!m_rtv && m_dev && m_texture) + { + m_dev->CreateRenderTargetView(m_texture, NULL, &m_rtv); + } + + return m_rtv; +} + +GSTexture11::operator ID3D11DepthStencilView*() +{ + if(!m_dsv && m_dev && m_texture) + { + m_dev->CreateDepthStencilView(m_texture, NULL, &m_dsv); + } + + return m_dsv; +} diff --git a/plugins/GSdx/GSTexture11.h b/plugins/GSdx/GSTexture11.h index 8aad1258bd..b4f266d8b7 100644 --- a/plugins/GSdx/GSTexture11.h +++ b/plugins/GSdx/GSTexture11.h @@ -1,48 +1,48 @@ -/* - * Copyright (C) 2007-2009 Gabest - * http://www.gabest.org - * - * This Program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This Program 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 GNU Make; see the file COPYING. If not, write to - * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - * http://www.gnu.org/copyleft/gpl.html - * - */ - -#pragma once - -#include "GSTexture.h" - -class GSTexture11 : public GSTexture -{ - CComPtr m_dev; - CComPtr m_ctx; - CComPtr m_texture; - D3D11_TEXTURE2D_DESC m_desc; - CComPtr m_srv; - CComPtr m_rtv; - CComPtr m_dsv; - -public: - explicit GSTexture11(ID3D11Texture2D* texture); - - bool Update(const GSVector4i& r, const void* data, int pitch); - bool Map(GSMap& m, const GSVector4i* r); - void Unmap(); - bool Save(const string& fn, bool dds = false); - - operator ID3D11Texture2D*(); - operator ID3D11ShaderResourceView*(); - operator ID3D11RenderTargetView*(); - operator ID3D11DepthStencilView*(); -}; +/* + * Copyright (C) 2007-2009 Gabest + * http://www.gabest.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program 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 GNU Make; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#pragma once + +#include "GSTexture.h" + +class GSTexture11 : public GSTexture +{ + CComPtr m_dev; + CComPtr m_ctx; + CComPtr m_texture; + D3D11_TEXTURE2D_DESC m_desc; + CComPtr m_srv; + CComPtr m_rtv; + CComPtr m_dsv; + +public: + explicit GSTexture11(ID3D11Texture2D* texture); + + bool Update(const GSVector4i& r, const void* data, int pitch); + bool Map(GSMap& m, const GSVector4i* r); + void Unmap(); + bool Save(const string& fn, bool dds = false); + + operator ID3D11Texture2D*(); + operator ID3D11ShaderResourceView*(); + operator ID3D11RenderTargetView*(); + operator ID3D11DepthStencilView*(); +}; diff --git a/plugins/GSdx/GSTextureCache11.cpp b/plugins/GSdx/GSTextureCache11.cpp index 9fd664d026..fe598b5dc1 100644 --- a/plugins/GSdx/GSTextureCache11.cpp +++ b/plugins/GSdx/GSTextureCache11.cpp @@ -1,99 +1,99 @@ -/* - * Copyright (C) 2007-2009 Gabest - * http://www.gabest.org - * - * This Program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This Program 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 GNU Make; see the file COPYING. If not, write to - * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - * http://www.gnu.org/copyleft/gpl.html - * - */ - -#include "StdAfx.h" -#include "GSTextureCache11.h" - -// GSTextureCache11 - -GSTextureCache11::GSTextureCache11(GSRenderer* r) - : GSTextureCache(r) -{ -} - -void GSTextureCache11::Read(Target* t, const GSVector4i& r) -{ - if(t->m_type != RenderTarget) - { - // TODO - - return; - } - - const GIFRegTEX0& TEX0 = t->m_TEX0; - - if(TEX0.PSM != PSM_PSMCT32 - && TEX0.PSM != PSM_PSMCT24 - && TEX0.PSM != PSM_PSMCT16 - && TEX0.PSM != PSM_PSMCT16S) - { - //ASSERT(0); - - return; - } - - if(!t->m_dirty.empty()) - { - return; - } - - // printf("GSRenderTarget::Read %d,%d - %d,%d (%08x)\n", r.left, r.top, r.right, r.bottom, TEX0.TBP0); - - int w = r.width(); - int h = r.height(); - - GSVector4 src = GSVector4(r) * GSVector4(t->m_texture->GetScale()).xyxy() / GSVector4(t->m_texture->GetSize()).xyxy(); - - DXGI_FORMAT format = TEX0.PSM == PSM_PSMCT16 || TEX0.PSM == PSM_PSMCT16S ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R8G8B8A8_UNORM; - - if(GSTexture* offscreen = m_renderer->m_dev->CopyOffscreen(t->m_texture, src, w, h, format)) - { - GSTexture::GSMap m; - - if(offscreen->Map(m)) - { - // TODO: block level write - - GSOffset* o = m_renderer->m_mem.GetOffset(TEX0.TBP0, TEX0.TBW, TEX0.PSM); - - switch(TEX0.PSM) - { - case PSM_PSMCT32: - m_renderer->m_mem.WritePixel32(m.bits, m.pitch, o, r); - break; - case PSM_PSMCT24: - m_renderer->m_mem.WritePixel24(m.bits, m.pitch, o, r); - break; - case PSM_PSMCT16: - case PSM_PSMCT16S: - m_renderer->m_mem.WritePixel16(m.bits, m.pitch, o, r); - break; - default: - ASSERT(0); - } - - offscreen->Unmap(); - } - - m_renderer->m_dev->Recycle(offscreen); - } -} - +/* + * Copyright (C) 2007-2009 Gabest + * http://www.gabest.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program 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 GNU Make; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "StdAfx.h" +#include "GSTextureCache11.h" + +// GSTextureCache11 + +GSTextureCache11::GSTextureCache11(GSRenderer* r) + : GSTextureCache(r) +{ +} + +void GSTextureCache11::Read(Target* t, const GSVector4i& r) +{ + if(t->m_type != RenderTarget) + { + // TODO + + return; + } + + const GIFRegTEX0& TEX0 = t->m_TEX0; + + if(TEX0.PSM != PSM_PSMCT32 + && TEX0.PSM != PSM_PSMCT24 + && TEX0.PSM != PSM_PSMCT16 + && TEX0.PSM != PSM_PSMCT16S) + { + //ASSERT(0); + + return; + } + + if(!t->m_dirty.empty()) + { + return; + } + + // printf("GSRenderTarget::Read %d,%d - %d,%d (%08x)\n", r.left, r.top, r.right, r.bottom, TEX0.TBP0); + + int w = r.width(); + int h = r.height(); + + GSVector4 src = GSVector4(r) * GSVector4(t->m_texture->GetScale()).xyxy() / GSVector4(t->m_texture->GetSize()).xyxy(); + + DXGI_FORMAT format = TEX0.PSM == PSM_PSMCT16 || TEX0.PSM == PSM_PSMCT16S ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R8G8B8A8_UNORM; + + if(GSTexture* offscreen = m_renderer->m_dev->CopyOffscreen(t->m_texture, src, w, h, format)) + { + GSTexture::GSMap m; + + if(offscreen->Map(m)) + { + // TODO: block level write + + GSOffset* o = m_renderer->m_mem.GetOffset(TEX0.TBP0, TEX0.TBW, TEX0.PSM); + + switch(TEX0.PSM) + { + case PSM_PSMCT32: + m_renderer->m_mem.WritePixel32(m.bits, m.pitch, o, r); + break; + case PSM_PSMCT24: + m_renderer->m_mem.WritePixel24(m.bits, m.pitch, o, r); + break; + case PSM_PSMCT16: + case PSM_PSMCT16S: + m_renderer->m_mem.WritePixel16(m.bits, m.pitch, o, r); + break; + default: + ASSERT(0); + } + + offscreen->Unmap(); + } + + m_renderer->m_dev->Recycle(offscreen); + } +} + diff --git a/plugins/GSdx/GSTextureCache11.h b/plugins/GSdx/GSTextureCache11.h index f4da837984..4900955d08 100644 --- a/plugins/GSdx/GSTextureCache11.h +++ b/plugins/GSdx/GSTextureCache11.h @@ -1,36 +1,36 @@ -/* - * Copyright (C) 2007-2009 Gabest - * http://www.gabest.org - * - * This Program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This Program 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 GNU Make; see the file COPYING. If not, write to - * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - * http://www.gnu.org/copyleft/gpl.html - * - */ - -#pragma once - -#include "GSTextureCache.h" -#include "GSDevice11.h" - -class GSTextureCache11 : public GSTextureCache -{ -protected: - int Get8bitFormat() {return DXGI_FORMAT_A8_UNORM;} - - void Read(Target* t, const GSVector4i& r); - -public: - GSTextureCache11(GSRenderer* r); -}; +/* + * Copyright (C) 2007-2009 Gabest + * http://www.gabest.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program 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 GNU Make; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#pragma once + +#include "GSTextureCache.h" +#include "GSDevice11.h" + +class GSTextureCache11 : public GSTextureCache +{ +protected: + int Get8bitFormat() {return DXGI_FORMAT_A8_UNORM;} + + void Read(Target* t, const GSVector4i& r); + +public: + GSTextureCache11(GSRenderer* r); +}; diff --git a/plugins/GSdx/GSTextureFX.cpp b/plugins/GSdx/GSTextureFX.cpp index f243c41679..ce9a21197e 100644 --- a/plugins/GSdx/GSTextureFX.cpp +++ b/plugins/GSdx/GSTextureFX.cpp @@ -1,36 +1,36 @@ -/* - * Copyright (C) 2007-2009 Gabest - * http://www.gabest.org - * - * This Program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This Program 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 GNU Make; see the file COPYING. If not, write to - * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - * http://www.gnu.org/copyleft/gpl.html - * - */ - -#include "stdafx.h" -#include "GSTextureFX.h" - -GSTextureFX::GSTextureFX() - : m_dev(NULL) -{ -} - -bool GSTextureFX::Create(GSDevice* dev) -{ - m_dev = dev; - - return true; -} - +/* + * Copyright (C) 2007-2009 Gabest + * http://www.gabest.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program 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 GNU Make; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "stdafx.h" +#include "GSTextureFX.h" + +GSTextureFX::GSTextureFX() + : m_dev(NULL) +{ +} + +bool GSTextureFX::Create(GSDevice* dev) +{ + m_dev = dev; + + return true; +} + diff --git a/plugins/GSdx/GSTextureFX11.cpp b/plugins/GSdx/GSTextureFX11.cpp index 7330042b4e..fc3c244e16 100644 --- a/plugins/GSdx/GSTextureFX11.cpp +++ b/plugins/GSdx/GSTextureFX11.cpp @@ -1,394 +1,394 @@ -/* - * Copyright (C) 2007-2009 Gabest - * http://www.gabest.org - * - * This Program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This Program 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 GNU Make; see the file COPYING. If not, write to - * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - * http://www.gnu.org/copyleft/gpl.html - * - */ - -#include "stdafx.h" -#include "GSDevice11.h" -#include "resource.h" -#include "GSTables.h" - -bool GSDevice11::CreateTextureFX() -{ - HRESULT hr; - - D3D11_BUFFER_DESC bd; - - memset(&bd, 0, sizeof(bd)); - - bd.ByteWidth = sizeof(VSConstantBuffer); - bd.Usage = D3D11_USAGE_DEFAULT; - bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER; - - hr = m_dev->CreateBuffer(&bd, NULL, &m_vs_cb); - - if(FAILED(hr)) return false; - - memset(&bd, 0, sizeof(bd)); - - bd.ByteWidth = sizeof(PSConstantBuffer); - bd.Usage = D3D11_USAGE_DEFAULT; - bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER; - - hr = m_dev->CreateBuffer(&bd, NULL, &m_ps_cb); - - if(FAILED(hr)) return false; - - D3D11_SAMPLER_DESC sd; - - memset(&sd, 0, sizeof(sd)); - - sd.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT; - sd.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP; - sd.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP; - sd.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP; - sd.MaxLOD = FLT_MAX; - sd.MaxAnisotropy = 16; - sd.ComparisonFunc = D3D11_COMPARISON_NEVER; - - hr = m_dev->CreateSamplerState(&sd, &m_palette_ss); - - if(FAILED(hr)) return false; - - // create layout - - VSSelector sel; - VSConstantBuffer cb; - - SetupVS(sel, &cb); - - // - - return true; -} - -void GSDevice11::SetupIA(const void* vertices, int count, int prim) -{ - IASetVertexBuffer(vertices, sizeof(GSVertexHW11), count); - IASetPrimitiveTopology((D3D11_PRIMITIVE_TOPOLOGY)prim); -} - -void GSDevice11::SetupVS(VSSelector sel, const VSConstantBuffer* cb) -{ - hash_map::const_iterator i = m_vs.find(sel); - - if(i == m_vs.end()) - { - string str[3]; - - str[0] = format("%d", sel.bppz); - str[1] = format("%d", sel.tme); - str[2] = format("%d", sel.fst); - - D3D11_SHADER_MACRO macro[] = - { - {"VS_BPPZ", str[0].c_str()}, - {"VS_TME", str[1].c_str()}, - {"VS_FST", str[2].c_str()}, - {NULL, NULL}, - }; - - D3D11_INPUT_ELEMENT_DESC layout[] = - { - {"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0}, - {"COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 8, D3D11_INPUT_PER_VERTEX_DATA, 0}, - {"TEXCOORD", 1, DXGI_FORMAT_R32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0}, - {"POSITION", 0, DXGI_FORMAT_R16G16_UINT, 0, 16, D3D11_INPUT_PER_VERTEX_DATA, 0}, - {"POSITION", 1, DXGI_FORMAT_R32_UINT, 0, 20, D3D11_INPUT_PER_VERTEX_DATA, 0}, - {"COLOR", 1, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 28, D3D11_INPUT_PER_VERTEX_DATA, 0}, - }; - - GSVertexShader11 vs; - - CompileShader(IDR_TFX_FX, "vs_main", macro, &vs.vs, layout, countof(layout), &vs.il); - - m_vs[sel] = vs; - - i = m_vs.find(sel); - } - - if(m_vs_cb_cache.Update(cb)) - { - ID3D11DeviceContext* ctx = m_ctx; - - ctx->UpdateSubresource(m_vs_cb, 0, NULL, cb, 0, 0); - } - - VSSetShader(i->second.vs, m_vs_cb); - IASetInputLayout(i->second.il); -} - -void GSDevice11::SetupGS(GSSelector sel) -{ - ID3D11GeometryShader* gs = NULL; - - if(sel.prim > 0 && (sel.iip == 0 || sel.prim == 3)) // geometry shader works in every case, but not needed - { - hash_map >::const_iterator i = m_gs.find(sel); - - if(i != m_gs.end()) - { - gs = i->second; - } - else - { - string str[2]; - - str[0] = format("%d", sel.iip); - str[1] = format("%d", sel.prim); - - D3D11_SHADER_MACRO macro[] = - { - {"GS_IIP", str[0].c_str()}, - {"GS_PRIM", str[1].c_str()}, - {NULL, NULL}, - }; - - CompileShader(IDR_TFX_FX, "gs_main", macro, &gs); - - m_gs[sel] = gs; - } - } - - GSSetShader(gs); -} - -void GSDevice11::SetupPS(PSSelector sel, const PSConstantBuffer* cb, PSSamplerSelector ssel) -{ - hash_map >::const_iterator i = m_ps.find(sel); - - if(i == m_ps.end()) - { - string str[14]; - - str[0] = format("%d", sel.fst); - str[1] = format("%d", sel.wms); - str[2] = format("%d", sel.wmt); - str[3] = format("%d", sel.fmt); - str[4] = format("%d", sel.aem); - str[5] = format("%d", sel.tfx); - str[6] = format("%d", sel.tcc); - str[7] = format("%d", sel.atst); - str[8] = format("%d", sel.fog); - str[9] = format("%d", sel.clr1); - str[10] = format("%d", sel.fba); - str[11] = format("%d", sel.aout); - str[12] = format("%d", sel.ltf); - str[13] = format("%d", sel.colclip); - - D3D11_SHADER_MACRO macro[] = - { - {"PS_FST", str[0].c_str()}, - {"PS_WMS", str[1].c_str()}, - {"PS_WMT", str[2].c_str()}, - {"PS_FMT", str[3].c_str()}, - {"PS_AEM", str[4].c_str()}, - {"PS_TFX", str[5].c_str()}, - {"PS_TCC", str[6].c_str()}, - {"PS_ATST", str[7].c_str()}, - {"PS_FOG", str[8].c_str()}, - {"PS_CLR1", str[9].c_str()}, - {"PS_FBA", str[10].c_str()}, - {"PS_AOUT", str[11].c_str()}, - {"PS_LTF", str[12].c_str()}, - {"PS_COLCLIP", str[13].c_str()}, - {NULL, NULL}, - }; - - CComPtr ps; - - CompileShader(IDR_TFX_FX, "ps_main", macro, &ps); - - m_ps[sel] = ps; - - i = m_ps.find(sel); - } - - if(m_ps_cb_cache.Update(cb)) - { - ID3D11DeviceContext* ctx = m_ctx; - - ctx->UpdateSubresource(m_ps_cb, 0, NULL, cb, 0, 0); - } - - PSSetShader(i->second, m_ps_cb); - - ID3D11SamplerState* ss0 = NULL; - ID3D11SamplerState* ss1 = NULL; - - if(sel.tfx != 4) - { - if(!(sel.fmt < 3 && sel.wms < 3 && sel.wmt < 3)) - { - ssel.ltf = 0; - } - - hash_map >::const_iterator i = m_ps_ss.find(ssel); - - if(i != m_ps_ss.end()) - { - ss0 = i->second; - } - else - { - D3D11_SAMPLER_DESC sd; - - memset(&sd, 0, sizeof(sd)); - - sd.Filter = ssel.ltf ? D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT : D3D11_FILTER_MIN_MAG_MIP_POINT; - - sd.AddressU = ssel.tau ? D3D11_TEXTURE_ADDRESS_WRAP : D3D11_TEXTURE_ADDRESS_CLAMP; - sd.AddressV = ssel.tav ? D3D11_TEXTURE_ADDRESS_WRAP : D3D11_TEXTURE_ADDRESS_CLAMP; - sd.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP; - - sd.MaxLOD = FLT_MAX; - sd.MaxAnisotropy = 16; - sd.ComparisonFunc = D3D11_COMPARISON_NEVER; - - m_dev->CreateSamplerState(&sd, &ss0); - - m_ps_ss[ssel] = ss0; - } - - if(sel.fmt >= 3) - { - ss1 = m_palette_ss; - } - } - - PSSetSamplerState(ss0, ss1); -} - -void GSDevice11::SetupOM(OMDepthStencilSelector dssel, OMBlendSelector bsel, uint8 afix) -{ - hash_map >::const_iterator i = m_om_dss.find(dssel); - - if(i == m_om_dss.end()) - { - D3D11_DEPTH_STENCIL_DESC dsd; - - memset(&dsd, 0, sizeof(dsd)); - - if(dssel.date) - { - dsd.StencilEnable = true; - dsd.StencilReadMask = 1; - dsd.StencilWriteMask = 1; - dsd.FrontFace.StencilFunc = D3D11_COMPARISON_EQUAL; - dsd.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP; - dsd.FrontFace.StencilFailOp = D3D11_STENCIL_OP_KEEP; - dsd.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP; - dsd.BackFace.StencilFunc = D3D11_COMPARISON_EQUAL; - dsd.BackFace.StencilPassOp = D3D11_STENCIL_OP_KEEP; - dsd.BackFace.StencilFailOp = D3D11_STENCIL_OP_KEEP; - dsd.BackFace.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP; - } - - if(dssel.ztst != ZTST_ALWAYS || dssel.zwe) - { - static const D3D11_COMPARISON_FUNC ztst[] = - { - D3D11_COMPARISON_NEVER, - D3D11_COMPARISON_ALWAYS, - D3D11_COMPARISON_GREATER_EQUAL, - D3D11_COMPARISON_GREATER - }; - - dsd.DepthEnable = true; - dsd.DepthWriteMask = dssel.zwe ? D3D11_DEPTH_WRITE_MASK_ALL : D3D11_DEPTH_WRITE_MASK_ZERO; - dsd.DepthFunc = ztst[dssel.ztst]; - } - - CComPtr dss; - - m_dev->CreateDepthStencilState(&dsd, &dss); - - m_om_dss[dssel] = dss; - - i = m_om_dss.find(dssel); - } - - OMSetDepthStencilState(i->second, 1); - - hash_map >::const_iterator j = m_om_bs.find(bsel); - - if(j == m_om_bs.end()) - { - D3D11_BLEND_DESC bd; - - memset(&bd, 0, sizeof(bd)); - - bd.RenderTarget[0].BlendEnable = bsel.abe; - - if(bsel.abe) - { - int i = ((bsel.a * 3 + bsel.b) * 3 + bsel.c) * 3 + bsel.d; - - bd.RenderTarget[0].BlendOp = (D3D11_BLEND_OP)blendMapD3D9[i].op; - bd.RenderTarget[0].SrcBlend = (D3D11_BLEND)blendMapD3D9[i].src; - bd.RenderTarget[0].DestBlend = (D3D11_BLEND)blendMapD3D9[i].dst; - bd.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD; - bd.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE; - bd.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO; - - // SRC* -> SRC1* - // Yes, this casting mess really is needed. I want to go back to C - if (bd.RenderTarget[0].SrcBlend >= 3 && bd.RenderTarget[0].SrcBlend <= 6) - bd.RenderTarget[0].SrcBlend = (D3D11_BLEND)((int)bd.RenderTarget[0].SrcBlend + 13); - if (bd.RenderTarget[0].DestBlend >= 3 && bd.RenderTarget[0].DestBlend <= 6) - bd.RenderTarget[0].DestBlend = (D3D11_BLEND)((int)bd.RenderTarget[0].DestBlend + 13); - - // Not very good but I don't wanna write another 81 row table - if (bsel.negative) { - if (bd.RenderTarget[0].BlendOp == D3D11_BLEND_OP_ADD) - bd.RenderTarget[0].BlendOp = D3D11_BLEND_OP_REV_SUBTRACT; - else if (bd.RenderTarget[0].BlendOp == D3D11_BLEND_OP_REV_SUBTRACT) - bd.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD; - else - ; // god knows, best just not to mess with it for now - } - - if(blendMapD3D9[i].bogus == 1) - { - (bsel.a == 0 ? bd.RenderTarget[0].SrcBlend : bd.RenderTarget[0].DestBlend) = D3D11_BLEND_ONE; - - const string afixstr = format("%d >> 7", afix); - const char *col[3] = {"Cs", "Cd", "0"}; - const char *alpha[3] = {"As", "Ad", afixstr.c_str()}; - printf("Impossible blend for D3D: (%s - %s) * %s + %s\n", - col[bsel.a], col[bsel.b], alpha[bsel.c], col[bsel.d]); - } - } - - if(bsel.wr) bd.RenderTarget[0].RenderTargetWriteMask |= D3D11_COLOR_WRITE_ENABLE_RED; - if(bsel.wg) bd.RenderTarget[0].RenderTargetWriteMask |= D3D11_COLOR_WRITE_ENABLE_GREEN; - if(bsel.wb) bd.RenderTarget[0].RenderTargetWriteMask |= D3D11_COLOR_WRITE_ENABLE_BLUE; - if(bsel.wa) bd.RenderTarget[0].RenderTargetWriteMask |= D3D11_COLOR_WRITE_ENABLE_ALPHA; - - CComPtr bs; - - m_dev->CreateBlendState(&bd, &bs); - - m_om_bs[bsel] = bs; - - j = m_om_bs.find(bsel); - } - - OMSetBlendState(j->second, (float)(int)afix / 0x80); -} +/* + * Copyright (C) 2007-2009 Gabest + * http://www.gabest.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program 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 GNU Make; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "stdafx.h" +#include "GSDevice11.h" +#include "resource.h" +#include "GSTables.h" + +bool GSDevice11::CreateTextureFX() +{ + HRESULT hr; + + D3D11_BUFFER_DESC bd; + + memset(&bd, 0, sizeof(bd)); + + bd.ByteWidth = sizeof(VSConstantBuffer); + bd.Usage = D3D11_USAGE_DEFAULT; + bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER; + + hr = m_dev->CreateBuffer(&bd, NULL, &m_vs_cb); + + if(FAILED(hr)) return false; + + memset(&bd, 0, sizeof(bd)); + + bd.ByteWidth = sizeof(PSConstantBuffer); + bd.Usage = D3D11_USAGE_DEFAULT; + bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER; + + hr = m_dev->CreateBuffer(&bd, NULL, &m_ps_cb); + + if(FAILED(hr)) return false; + + D3D11_SAMPLER_DESC sd; + + memset(&sd, 0, sizeof(sd)); + + sd.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT; + sd.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP; + sd.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP; + sd.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP; + sd.MaxLOD = FLT_MAX; + sd.MaxAnisotropy = 16; + sd.ComparisonFunc = D3D11_COMPARISON_NEVER; + + hr = m_dev->CreateSamplerState(&sd, &m_palette_ss); + + if(FAILED(hr)) return false; + + // create layout + + VSSelector sel; + VSConstantBuffer cb; + + SetupVS(sel, &cb); + + // + + return true; +} + +void GSDevice11::SetupIA(const void* vertices, int count, int prim) +{ + IASetVertexBuffer(vertices, sizeof(GSVertexHW11), count); + IASetPrimitiveTopology((D3D11_PRIMITIVE_TOPOLOGY)prim); +} + +void GSDevice11::SetupVS(VSSelector sel, const VSConstantBuffer* cb) +{ + hash_map::const_iterator i = m_vs.find(sel); + + if(i == m_vs.end()) + { + string str[3]; + + str[0] = format("%d", sel.bppz); + str[1] = format("%d", sel.tme); + str[2] = format("%d", sel.fst); + + D3D11_SHADER_MACRO macro[] = + { + {"VS_BPPZ", str[0].c_str()}, + {"VS_TME", str[1].c_str()}, + {"VS_FST", str[2].c_str()}, + {NULL, NULL}, + }; + + D3D11_INPUT_ELEMENT_DESC layout[] = + { + {"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0}, + {"COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 8, D3D11_INPUT_PER_VERTEX_DATA, 0}, + {"TEXCOORD", 1, DXGI_FORMAT_R32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0}, + {"POSITION", 0, DXGI_FORMAT_R16G16_UINT, 0, 16, D3D11_INPUT_PER_VERTEX_DATA, 0}, + {"POSITION", 1, DXGI_FORMAT_R32_UINT, 0, 20, D3D11_INPUT_PER_VERTEX_DATA, 0}, + {"COLOR", 1, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 28, D3D11_INPUT_PER_VERTEX_DATA, 0}, + }; + + GSVertexShader11 vs; + + CompileShader(IDR_TFX_FX, "vs_main", macro, &vs.vs, layout, countof(layout), &vs.il); + + m_vs[sel] = vs; + + i = m_vs.find(sel); + } + + if(m_vs_cb_cache.Update(cb)) + { + ID3D11DeviceContext* ctx = m_ctx; + + ctx->UpdateSubresource(m_vs_cb, 0, NULL, cb, 0, 0); + } + + VSSetShader(i->second.vs, m_vs_cb); + IASetInputLayout(i->second.il); +} + +void GSDevice11::SetupGS(GSSelector sel) +{ + ID3D11GeometryShader* gs = NULL; + + if(sel.prim > 0 && (sel.iip == 0 || sel.prim == 3)) // geometry shader works in every case, but not needed + { + hash_map >::const_iterator i = m_gs.find(sel); + + if(i != m_gs.end()) + { + gs = i->second; + } + else + { + string str[2]; + + str[0] = format("%d", sel.iip); + str[1] = format("%d", sel.prim); + + D3D11_SHADER_MACRO macro[] = + { + {"GS_IIP", str[0].c_str()}, + {"GS_PRIM", str[1].c_str()}, + {NULL, NULL}, + }; + + CompileShader(IDR_TFX_FX, "gs_main", macro, &gs); + + m_gs[sel] = gs; + } + } + + GSSetShader(gs); +} + +void GSDevice11::SetupPS(PSSelector sel, const PSConstantBuffer* cb, PSSamplerSelector ssel) +{ + hash_map >::const_iterator i = m_ps.find(sel); + + if(i == m_ps.end()) + { + string str[14]; + + str[0] = format("%d", sel.fst); + str[1] = format("%d", sel.wms); + str[2] = format("%d", sel.wmt); + str[3] = format("%d", sel.fmt); + str[4] = format("%d", sel.aem); + str[5] = format("%d", sel.tfx); + str[6] = format("%d", sel.tcc); + str[7] = format("%d", sel.atst); + str[8] = format("%d", sel.fog); + str[9] = format("%d", sel.clr1); + str[10] = format("%d", sel.fba); + str[11] = format("%d", sel.aout); + str[12] = format("%d", sel.ltf); + str[13] = format("%d", sel.colclip); + + D3D11_SHADER_MACRO macro[] = + { + {"PS_FST", str[0].c_str()}, + {"PS_WMS", str[1].c_str()}, + {"PS_WMT", str[2].c_str()}, + {"PS_FMT", str[3].c_str()}, + {"PS_AEM", str[4].c_str()}, + {"PS_TFX", str[5].c_str()}, + {"PS_TCC", str[6].c_str()}, + {"PS_ATST", str[7].c_str()}, + {"PS_FOG", str[8].c_str()}, + {"PS_CLR1", str[9].c_str()}, + {"PS_FBA", str[10].c_str()}, + {"PS_AOUT", str[11].c_str()}, + {"PS_LTF", str[12].c_str()}, + {"PS_COLCLIP", str[13].c_str()}, + {NULL, NULL}, + }; + + CComPtr ps; + + CompileShader(IDR_TFX_FX, "ps_main", macro, &ps); + + m_ps[sel] = ps; + + i = m_ps.find(sel); + } + + if(m_ps_cb_cache.Update(cb)) + { + ID3D11DeviceContext* ctx = m_ctx; + + ctx->UpdateSubresource(m_ps_cb, 0, NULL, cb, 0, 0); + } + + PSSetShader(i->second, m_ps_cb); + + ID3D11SamplerState* ss0 = NULL; + ID3D11SamplerState* ss1 = NULL; + + if(sel.tfx != 4) + { + if(!(sel.fmt < 3 && sel.wms < 3 && sel.wmt < 3)) + { + ssel.ltf = 0; + } + + hash_map >::const_iterator i = m_ps_ss.find(ssel); + + if(i != m_ps_ss.end()) + { + ss0 = i->second; + } + else + { + D3D11_SAMPLER_DESC sd; + + memset(&sd, 0, sizeof(sd)); + + sd.Filter = ssel.ltf ? D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT : D3D11_FILTER_MIN_MAG_MIP_POINT; + + sd.AddressU = ssel.tau ? D3D11_TEXTURE_ADDRESS_WRAP : D3D11_TEXTURE_ADDRESS_CLAMP; + sd.AddressV = ssel.tav ? D3D11_TEXTURE_ADDRESS_WRAP : D3D11_TEXTURE_ADDRESS_CLAMP; + sd.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP; + + sd.MaxLOD = FLT_MAX; + sd.MaxAnisotropy = 16; + sd.ComparisonFunc = D3D11_COMPARISON_NEVER; + + m_dev->CreateSamplerState(&sd, &ss0); + + m_ps_ss[ssel] = ss0; + } + + if(sel.fmt >= 3) + { + ss1 = m_palette_ss; + } + } + + PSSetSamplerState(ss0, ss1); +} + +void GSDevice11::SetupOM(OMDepthStencilSelector dssel, OMBlendSelector bsel, uint8 afix) +{ + hash_map >::const_iterator i = m_om_dss.find(dssel); + + if(i == m_om_dss.end()) + { + D3D11_DEPTH_STENCIL_DESC dsd; + + memset(&dsd, 0, sizeof(dsd)); + + if(dssel.date) + { + dsd.StencilEnable = true; + dsd.StencilReadMask = 1; + dsd.StencilWriteMask = 1; + dsd.FrontFace.StencilFunc = D3D11_COMPARISON_EQUAL; + dsd.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP; + dsd.FrontFace.StencilFailOp = D3D11_STENCIL_OP_KEEP; + dsd.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP; + dsd.BackFace.StencilFunc = D3D11_COMPARISON_EQUAL; + dsd.BackFace.StencilPassOp = D3D11_STENCIL_OP_KEEP; + dsd.BackFace.StencilFailOp = D3D11_STENCIL_OP_KEEP; + dsd.BackFace.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP; + } + + if(dssel.ztst != ZTST_ALWAYS || dssel.zwe) + { + static const D3D11_COMPARISON_FUNC ztst[] = + { + D3D11_COMPARISON_NEVER, + D3D11_COMPARISON_ALWAYS, + D3D11_COMPARISON_GREATER_EQUAL, + D3D11_COMPARISON_GREATER + }; + + dsd.DepthEnable = true; + dsd.DepthWriteMask = dssel.zwe ? D3D11_DEPTH_WRITE_MASK_ALL : D3D11_DEPTH_WRITE_MASK_ZERO; + dsd.DepthFunc = ztst[dssel.ztst]; + } + + CComPtr dss; + + m_dev->CreateDepthStencilState(&dsd, &dss); + + m_om_dss[dssel] = dss; + + i = m_om_dss.find(dssel); + } + + OMSetDepthStencilState(i->second, 1); + + hash_map >::const_iterator j = m_om_bs.find(bsel); + + if(j == m_om_bs.end()) + { + D3D11_BLEND_DESC bd; + + memset(&bd, 0, sizeof(bd)); + + bd.RenderTarget[0].BlendEnable = bsel.abe; + + if(bsel.abe) + { + int i = ((bsel.a * 3 + bsel.b) * 3 + bsel.c) * 3 + bsel.d; + + bd.RenderTarget[0].BlendOp = (D3D11_BLEND_OP)blendMapD3D9[i].op; + bd.RenderTarget[0].SrcBlend = (D3D11_BLEND)blendMapD3D9[i].src; + bd.RenderTarget[0].DestBlend = (D3D11_BLEND)blendMapD3D9[i].dst; + bd.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD; + bd.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE; + bd.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO; + + // SRC* -> SRC1* + // Yes, this casting mess really is needed. I want to go back to C + if (bd.RenderTarget[0].SrcBlend >= 3 && bd.RenderTarget[0].SrcBlend <= 6) + bd.RenderTarget[0].SrcBlend = (D3D11_BLEND)((int)bd.RenderTarget[0].SrcBlend + 13); + if (bd.RenderTarget[0].DestBlend >= 3 && bd.RenderTarget[0].DestBlend <= 6) + bd.RenderTarget[0].DestBlend = (D3D11_BLEND)((int)bd.RenderTarget[0].DestBlend + 13); + + // Not very good but I don't wanna write another 81 row table + if (bsel.negative) { + if (bd.RenderTarget[0].BlendOp == D3D11_BLEND_OP_ADD) + bd.RenderTarget[0].BlendOp = D3D11_BLEND_OP_REV_SUBTRACT; + else if (bd.RenderTarget[0].BlendOp == D3D11_BLEND_OP_REV_SUBTRACT) + bd.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD; + else + ; // god knows, best just not to mess with it for now + } + + if(blendMapD3D9[i].bogus == 1) + { + (bsel.a == 0 ? bd.RenderTarget[0].SrcBlend : bd.RenderTarget[0].DestBlend) = D3D11_BLEND_ONE; + + const string afixstr = format("%d >> 7", afix); + const char *col[3] = {"Cs", "Cd", "0"}; + const char *alpha[3] = {"As", "Ad", afixstr.c_str()}; + printf("Impossible blend for D3D: (%s - %s) * %s + %s\n", + col[bsel.a], col[bsel.b], alpha[bsel.c], col[bsel.d]); + } + } + + if(bsel.wr) bd.RenderTarget[0].RenderTargetWriteMask |= D3D11_COLOR_WRITE_ENABLE_RED; + if(bsel.wg) bd.RenderTarget[0].RenderTargetWriteMask |= D3D11_COLOR_WRITE_ENABLE_GREEN; + if(bsel.wb) bd.RenderTarget[0].RenderTargetWriteMask |= D3D11_COLOR_WRITE_ENABLE_BLUE; + if(bsel.wa) bd.RenderTarget[0].RenderTargetWriteMask |= D3D11_COLOR_WRITE_ENABLE_ALPHA; + + CComPtr bs; + + m_dev->CreateBlendState(&bd, &bs); + + m_om_bs[bsel] = bs; + + j = m_om_bs.find(bsel); + } + + OMSetBlendState(j->second, (float)(int)afix / 0x80); +} diff --git a/plugins/GSdx/GSThread.cpp b/plugins/GSdx/GSThread.cpp index 8b64fdce55..f4b19e4243 100644 --- a/plugins/GSdx/GSThread.cpp +++ b/plugins/GSdx/GSThread.cpp @@ -1,63 +1,63 @@ -/* - * Copyright (C) 2007-2009 Gabest - * http://www.gabest.org - * - * This Program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This Program 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 GNU Make; see the file COPYING. If not, write to - * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - * http://www.gnu.org/copyleft/gpl.html - * - */ - -#include "StdAfx.h" -#include "GSThread.h" - -GSThread::GSThread() - : m_ThreadId(0) - , m_hThread(NULL) -{ -} - -GSThread::~GSThread() -{ - CloseThread(); -} - -DWORD WINAPI GSThread::StaticThreadProc(LPVOID lpParam) -{ - ((GSThread*)lpParam)->ThreadProc(); - - return 0; -} - -void GSThread::CreateThread() -{ - m_hThread = ::CreateThread(NULL, 0, StaticThreadProc, (LPVOID)this, 0, &m_ThreadId); -} - -void GSThread::CloseThread() -{ - if(m_hThread != NULL) - { - if(WaitForSingleObject(m_hThread, 5000) != WAIT_OBJECT_0) - { - TerminateThread(m_hThread, 1); - } - - CloseHandle(m_hThread); - - m_hThread = NULL; - m_ThreadId = 0; - } -} - +/* + * Copyright (C) 2007-2009 Gabest + * http://www.gabest.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program 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 GNU Make; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "StdAfx.h" +#include "GSThread.h" + +GSThread::GSThread() + : m_ThreadId(0) + , m_hThread(NULL) +{ +} + +GSThread::~GSThread() +{ + CloseThread(); +} + +DWORD WINAPI GSThread::StaticThreadProc(LPVOID lpParam) +{ + ((GSThread*)lpParam)->ThreadProc(); + + return 0; +} + +void GSThread::CreateThread() +{ + m_hThread = ::CreateThread(NULL, 0, StaticThreadProc, (LPVOID)this, 0, &m_ThreadId); +} + +void GSThread::CloseThread() +{ + if(m_hThread != NULL) + { + if(WaitForSingleObject(m_hThread, 5000) != WAIT_OBJECT_0) + { + TerminateThread(m_hThread, 1); + } + + CloseHandle(m_hThread); + + m_hThread = NULL; + m_ThreadId = 0; + } +} + diff --git a/plugins/GSdx/GSThread.h b/plugins/GSdx/GSThread.h index 79dca94f9e..2a2be1b0c5 100644 --- a/plugins/GSdx/GSThread.h +++ b/plugins/GSdx/GSThread.h @@ -1,42 +1,42 @@ -/* - * Copyright (C) 2007-2009 Gabest - * http://www.gabest.org - * - * This Program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This Program 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 GNU Make; see the file COPYING. If not, write to - * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - * http://www.gnu.org/copyleft/gpl.html - * - */ - -#pragma once - -class GSThread -{ - // TODO: linux - - DWORD m_ThreadId; - HANDLE m_hThread; - - static DWORD WINAPI StaticThreadProc(LPVOID lpParam); - -protected: - virtual void ThreadProc() = 0; - - void CreateThread(); - void CloseThread(); - -public: - GSThread(); - virtual ~GSThread(); -}; +/* + * Copyright (C) 2007-2009 Gabest + * http://www.gabest.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program 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 GNU Make; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#pragma once + +class GSThread +{ + // TODO: linux + + DWORD m_ThreadId; + HANDLE m_hThread; + + static DWORD WINAPI StaticThreadProc(LPVOID lpParam); + +protected: + virtual void ThreadProc() = 0; + + void CreateThread(); + void CloseThread(); + +public: + GSThread(); + virtual ~GSThread(); +}; diff --git a/plugins/GSdx/GSVertexSW.cpp b/plugins/GSdx/GSVertexSW.cpp index 0706fa00f8..ea5dbf29c9 100644 --- a/plugins/GSdx/GSVertexSW.cpp +++ b/plugins/GSdx/GSVertexSW.cpp @@ -1,25 +1,25 @@ -/* - * Copyright (C) 2007-2009 Gabest - * http://www.gabest.org - * - * This Program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This Program 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 GNU Make; see the file COPYING. If not, write to - * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - * http://www.gnu.org/copyleft/gpl.html - * - */ - -#pragma once - -#include "stdafx.h" -#include "GSVertexSW.h" +/* + * Copyright (C) 2007-2009 Gabest + * http://www.gabest.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program 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 GNU Make; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#pragma once + +#include "stdafx.h" +#include "GSVertexSW.h" diff --git a/plugins/GSdx/GSVertexTrace.cpp b/plugins/GSdx/GSVertexTrace.cpp index ebbdd1fe46..57f89117ba 100644 --- a/plugins/GSdx/GSVertexTrace.cpp +++ b/plugins/GSdx/GSVertexTrace.cpp @@ -1,674 +1,674 @@ -/* - * Copyright (C) 2007-2009 Gabest - * http://www.gabest.org - * - * This Program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This Program 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 GNU Make; see the file COPYING. If not, write to - * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - * http://www.gnu.org/copyleft/gpl.html - * - */ - -#pragma once - -#include "stdafx.h" -#include "GSVertexTrace.h" -#include "GSUtil.h" -#include "GSState.h" - -GSVertexTrace::GSVertexTrace(const GSState* state) - : m_state(state) -{ -} - -uint32 GSVertexTrace::Hash(GS_PRIM_CLASS primclass) -{ - m_primclass = primclass; - - uint32 hash = m_primclass | (m_state->PRIM->IIP << 2) | (m_state->PRIM->TME << 3) | (m_state->PRIM->FST << 4); - - if(!(m_state->PRIM->TME && m_state->m_context->TEX0.TFX == TFX_DECAL && m_state->m_context->TEX0.TCC)) - { - hash |= 1 << 5; - } - - return hash; -} - -void GSVertexTrace::Update(const GSVertexSW* v, int count, GS_PRIM_CLASS primclass) -{ - m_map_sw[Hash(primclass)](v, count, m_min, m_max); - - m_eq.value = (m_min.c == m_max.c).mask() | ((m_min.p == m_max.p).mask() << 16) | ((m_min.t == m_max.t).mask() << 20); - - m_alpha.valid = false; -} - -void GSVertexTrace::Update(const GSVertexHW9* v, int count, GS_PRIM_CLASS primclass) -{ - m_map_hw9[Hash(primclass)](v, count, m_min, m_max); - - const GSDrawingContext* context = m_state->m_context; - - GSVector4 o(context->XYOFFSET); - GSVector4 s(1.0f / 16, 1.0f / 16, 1.0f, 1.0f); - - m_min.p = (m_min.p - o) * s; - m_max.p = (m_max.p - o) * s; - - if(m_state->PRIM->TME) - { - if(m_state->PRIM->FST) - { - s = GSVector4(1 << (16 - 4), 1).xxyy(); - } - else - { - s = GSVector4(0x10000 << context->TEX0.TW, 0x10000 << context->TEX0.TH, 1, 1); - } - - m_min.t *= s; - m_max.t *= s; - } - - m_eq.value = (m_min.c == m_max.c).mask() | ((m_min.p == m_max.p).mask() << 16) | ((m_min.t == m_max.t).mask() << 20); - - m_alpha.valid = false; -} - -void GSVertexTrace::Update(const GSVertexHW11* v, int count, GS_PRIM_CLASS primclass) -{ - m_map_hw11[Hash(primclass)](v, count, m_min, m_max); - - const GSDrawingContext* context = m_state->m_context; - - GSVector4 o(context->XYOFFSET); - GSVector4 s(1.0f / 16, 1.0f / 16, 2.0f, 1.0f); - - m_min.p = (m_min.p - o) * s; - m_max.p = (m_max.p - o) * s; - - if(m_state->PRIM->TME) - { - if(m_state->PRIM->FST) - { - s = GSVector4(1 << (16 - 4), 1).xxyy(); - } - else - { - s = GSVector4(0x10000 << context->TEX0.TW, 0x10000 << context->TEX0.TH, 1, 1); - } - - m_min.t *= s; - m_max.t *= s; - } - - m_eq.value = (m_min.c == m_max.c).mask() | ((m_min.p == m_max.p).mask() << 16) | ((m_min.t == m_max.t).mask() << 20); - - m_alpha.valid = false; -} - -using namespace Xbyak; - -GSVertexTrace::CGSW::CGSW(uint32 key, void* ptr, size_t maxsize) - : CodeGenerator(maxsize, ptr) -{ - #if _M_AMD64 - #error TODO - #endif - - const int params = 0; - - uint32 primclass = (key >> 0) & 3; - uint32 iip = (key >> 2) & 1; - uint32 tme = (key >> 3) & 1; - uint32 fst = (key >> 4) & 1; - uint32 color = (key >> 5) & 1; - - int n = 1; - - switch(primclass) - { - case GS_POINT_CLASS: - n = 1; - break; - case GS_LINE_CLASS: - case GS_SPRITE_CLASS: - n = 2; - break; - case GS_TRIANGLE_CLASS: - n = 3; - break; - } - - const int _v = params + 4; - const int _count = params + 8; - const int _min = params + 12; - const int _max = params + 16; - - // - - static const float fmin = -FLT_MAX; - static const float fmax = FLT_MAX; - - movss(xmm0, xmmword[&fmax]); - shufps(xmm0, xmm0, _MM_SHUFFLE(0, 0, 0, 0)); - - movss(xmm1, xmmword[&fmin]); - shufps(xmm1, xmm1, _MM_SHUFFLE(0, 0, 0, 0)); - - if(color) - { - // min.c = FLT_MAX; - // max.c = -FLT_MAX; - - movaps(xmm2, xmm0); - movaps(xmm3, xmm1); - } - - // min.p = FLT_MAX; - // max.p = -FLT_MAX; - - movaps(xmm4, xmm0); - movaps(xmm5, xmm1); - - if(tme) - { - // min.t = FLT_MAX; - // max.t = -FLT_MAX; - - movaps(xmm6, xmm0); - movaps(xmm7, xmm1); - } - - // for(int i = 0; i < count; i += step) { - - mov(edx, dword[esp + _v]); - mov(ecx, dword[esp + _count]); - - align(16); - -L("loop"); - - if(tme && !fst && primclass == GS_SPRITE_CLASS) - { - movaps(xmm1, xmmword[edx + 1 * sizeof(GSVertexSW) + 32]); - shufps(xmm1, xmm1, _MM_SHUFFLE(2, 2, 2, 2)); - } - - for(int j = 0; j < n; j++) - { - if(color && (iip || j == n - 1)) - { - // min.c = min.c.minv(v[i + j].c); - // max.c = max.c.maxv(v[i + j].c); - - movaps(xmm0, xmmword[edx + j * sizeof(GSVertexSW)]); - - minps(xmm2, xmm0); - maxps(xmm3, xmm0); - } - - // min.p = min.p.minv(v[i + j].p); - // max.p = max.p.maxv(v[i + j].p); - - movaps(xmm0, xmmword[edx + j * sizeof(GSVertexSW) + 16]); - - minps(xmm4, xmm0); - maxps(xmm5, xmm0); - - if(tme) - { - // min.t = min.t.minv(v[i + j].t); - // max.t = max.t.maxv(v[i + j].t); - - movaps(xmm0, xmmword[edx + j * sizeof(GSVertexSW) + 32]); - - if(!fst) - { - if(primclass != GS_SPRITE_CLASS) - { - movaps(xmm1, xmm0); - shufps(xmm1, xmm1, _MM_SHUFFLE(2, 2, 2, 2)); - } - - divps(xmm0, xmm1); - shufps(xmm0, xmm1, _MM_SHUFFLE(3, 2, 1, 0)); - } - - minps(xmm6, xmm0); - maxps(xmm7, xmm0); - } - } - - add(edx, n * sizeof(GSVertexSW)); - sub(ecx, n); - - jg("loop"); - - // } - - mov(eax, dword[esp + _min]); - mov(edx, dword[esp + _max]); - - if(color) - { - cvttps2dq(xmm2, xmm2); - psrld(xmm2, 7); - movaps(xmmword[eax], xmm2); - - cvttps2dq(xmm3, xmm3); - psrld(xmm3, 7); - movaps(xmmword[edx], xmm3); - } - - movaps(xmmword[eax + 16], xmm4); - movaps(xmmword[edx + 16], xmm5); - - if(tme) - { - movaps(xmmword[eax + 32], xmm6); - movaps(xmmword[edx + 32], xmm7); - } - - ret(); -} - -GSVertexTrace::CGHW9::CGHW9(uint32 key, void* ptr, size_t maxsize) - : CodeGenerator(maxsize, ptr) -{ - #if _M_AMD64 - #error TODO - #endif - - const int params = 0; - - uint32 primclass = (key >> 0) & 3; - uint32 iip = (key >> 2) & 1; - uint32 tme = (key >> 3) & 1; - uint32 fst = (key >> 4) & 1; - uint32 color = (key >> 5) & 1; - - int n = 1; - - switch(primclass) - { - case GS_POINT_CLASS: - n = 1; - break; - case GS_LINE_CLASS: - n = 2; - break; - case GS_TRIANGLE_CLASS: - n = 3; - break; - case GS_SPRITE_CLASS: - n = 6; - break; - } - - const int _v = params + 4; - const int _count = params + 8; - const int _min = params + 12; - const int _max = params + 16; - - // - - static const float fmin = -FLT_MAX; - static const float fmax = FLT_MAX; - - movss(xmm0, xmmword[&fmax]); - shufps(xmm0, xmm0, _MM_SHUFFLE(0, 0, 0, 0)); - - movss(xmm1, xmmword[&fmin]); - shufps(xmm1, xmm1, _MM_SHUFFLE(0, 0, 0, 0)); - - if(color) - { - // min.c = 0xffffffff; - // max.c = 0; - - pcmpeqd(xmm2, xmm2); - pxor(xmm3, xmm3); - } - - // min.p = FLT_MAX; - // max.p = -FLT_MAX; - - movaps(xmm4, xmm0); - movaps(xmm5, xmm1); - - if(tme) - { - // min.t = FLT_MAX; - // max.t = -FLT_MAX; - - movaps(xmm6, xmm0); - movaps(xmm7, xmm1); - } - - // for(int i = 0; i < count; i += step) { - - mov(edx, dword[esp + _v]); - mov(ecx, dword[esp + _count]); - - align(16); - -L("loop"); - - if(tme && !fst && primclass == GS_SPRITE_CLASS) - { - movaps(xmm1, xmmword[edx + 5 * sizeof(GSVertexHW9) + 16]); - shufps(xmm1, xmm1, _MM_SHUFFLE(3, 3, 3, 3)); - } - - for(int j = 0; j < n; j++) - { - // min.p = min.p.minv(v[i + j].p); - // max.p = max.p.maxv(v[i + j].p); - - movaps(xmm0, xmmword[edx + j * sizeof(GSVertexHW9) + 16]); - - minps(xmm4, xmm0); - maxps(xmm5, xmm0); - - if(tme && !fst && primclass != GS_SPRITE_CLASS) - { - movaps(xmm1, xmm0); - shufps(xmm1, xmm1, _MM_SHUFFLE(3, 3, 3, 3)); - } - - if(color && (iip || j == n - 1) || tme) - { - movaps(xmm0, xmmword[edx + j * sizeof(GSVertexHW9)]); - } - - if(color && (iip || j == n - 1)) - { - // min.c = min.c.min_u8(v[i + j].c); - // max.c = max.c.min_u8(v[i + j].c); - - pminub(xmm2, xmm0); - pmaxub(xmm3, xmm0); - } - - if(tme) - { - shufps(xmm0, xmm0, _MM_SHUFFLE(1, 0, 1, 0)); // avoid FP assist, high part is integral - - if(!fst) - { - // t /= p.wwww(); - - divps(xmm0, xmm1); - } - - // min.t = min.t.minv(v[i + j].t); - // max.t = max.t.maxv(v[i + j].t); - - minps(xmm6, xmm0); - maxps(xmm7, xmm0); - } - } - - add(edx, n * sizeof(GSVertexHW9)); - sub(ecx, n); - - jg("loop"); - - // } - - mov(eax, dword[esp + _min]); - mov(edx, dword[esp + _max]); - - if(color) - { - // m_min.c = cmin.zzzz().u8to32(); - // m_max.c = cmax.zzzz().u8to32(); - - if(m_cpu.has(util::Cpu::tSSE41)) - { - pshufd(xmm2, xmm2, _MM_SHUFFLE(2, 2, 2, 2)); - pmovzxbd(xmm2, xmm2); - - pshufd(xmm3, xmm3, _MM_SHUFFLE(2, 2, 2, 2)); - pmovzxbd(xmm3, xmm3); - } - else - { - pxor(xmm0, xmm0); - - punpckhbw(xmm2, xmm0); - punpcklwd(xmm2, xmm0); - - punpckhbw(xmm3, xmm0); - punpcklwd(xmm3, xmm0); - } - - movaps(xmmword[eax], xmm2); - movaps(xmmword[edx], xmm3); - } - - // m_min.p = pmin; - // m_max.p = pmax; - - movaps(xmmword[eax + 16], xmm4); - movaps(xmmword[edx + 16], xmm5); - - if(tme) - { - // m_min.t = tmin.xyww(pmin); - // m_max.t = tmax.xyww(pmax); - - shufps(xmm6, xmm4, _MM_SHUFFLE(3, 3, 1, 0)); - shufps(xmm7, xmm5, _MM_SHUFFLE(3, 3, 1, 0)); - - movaps(xmmword[eax + 32], xmm6); - movaps(xmmword[edx + 32], xmm7); - } - - ret(); -} - -GSVertexTrace::CGHW11::CGHW11(uint32 key, void* ptr, size_t maxsize) - : CodeGenerator(maxsize, ptr) -{ - #if _M_AMD64 - #error TODO - #endif - - const int params = 0; - - uint32 primclass = (key >> 0) & 3; - uint32 iip = (key >> 2) & 1; - uint32 tme = (key >> 3) & 1; - uint32 fst = (key >> 4) & 1; - uint32 color = (key >> 5) & 1; - - int n = 1; - - switch(primclass) - { - case GS_POINT_CLASS: - n = 1; - break; - case GS_LINE_CLASS: - case GS_SPRITE_CLASS: - n = 2; - break; - case GS_TRIANGLE_CLASS: - n = 3; - break; - } - - const int _v = params + 4; - const int _count = params + 8; - const int _min = params + 12; - const int _max = params + 16; - - // - - static const float fmin = -FLT_MAX; - static const float fmax = FLT_MAX; - - movss(xmm0, xmmword[&fmax]); - shufps(xmm0, xmm0, _MM_SHUFFLE(0, 0, 0, 0)); - - movss(xmm1, xmmword[&fmin]); - shufps(xmm1, xmm1, _MM_SHUFFLE(0, 0, 0, 0)); - - if(color) - { - // min.c = 0xffffffff; - // max.c = 0; - - pcmpeqd(xmm2, xmm2); - pxor(xmm3, xmm3); - } - - // min.p = FLT_MAX; - // max.p = -FLT_MAX; - - movaps(xmm4, xmm0); - movaps(xmm5, xmm1); - - if(tme) - { - // min.t = FLT_MAX; - // max.t = -FLT_MAX; - - movaps(xmm6, xmm0); - movaps(xmm7, xmm1); - } - - // for(int i = 0; i < count; i += step) { - - mov(edx, dword[esp + _v]); - mov(ecx, dword[esp + _count]); - - align(16); - -L("loop"); - - for(int j = 0; j < n; j++) - { - if(color && (iip || j == n - 1) || tme) - { - movaps(xmm0, xmmword[edx + j * sizeof(GSVertexHW11)]); - } - - if(color && (iip || j == n - 1)) - { - pminub(xmm2, xmm0); - pmaxub(xmm3, xmm0); - } - - if(tme) - { - if(!fst) - { - movaps(xmm1, xmm0); - } - - shufps(xmm0, xmm0, _MM_SHUFFLE(3, 3, 1, 0)); // avoid FP assist, third dword is integral - - if(!fst) - { - shufps(xmm1, xmm1, _MM_SHUFFLE(3, 3, 3, 3)); - divps(xmm0, xmm1); - shufps(xmm0, xmm1, _MM_SHUFFLE(3, 3, 1, 0)); // restore q - } - - minps(xmm6, xmm0); - maxps(xmm7, xmm0); - } - - movdqa(xmm0, xmmword[edx + j * sizeof(GSVertexHW11) + 16]); - - if(m_cpu.has(util::Cpu::tSSE41)) - { - pmovzxwd(xmm1, xmm0); - } - else - { - movdqa(xmm1, xmm0); - punpcklwd(xmm1, xmm1); - psrld(xmm1, 16); - } - - psrld(xmm0, 1); - punpcklqdq(xmm1, xmm0); - cvtdq2ps(xmm1, xmm1); - - minps(xmm4, xmm1); - maxps(xmm5, xmm1); - } - - add(edx, n * sizeof(GSVertexHW11)); - sub(ecx, n); - - jg("loop"); - - // } - - mov(eax, dword[esp + _min]); - mov(edx, dword[esp + _max]); - - if(color) - { - // m_min.c = cmin.zzzz().u8to32(); - // m_max.c = cmax.zzzz().u8to32(); - - if(m_cpu.has(util::Cpu::tSSE41)) - { - pshufd(xmm2, xmm2, _MM_SHUFFLE(2, 2, 2, 2)); - pmovzxbd(xmm2, xmm2); - - pshufd(xmm3, xmm3, _MM_SHUFFLE(2, 2, 2, 2)); - pmovzxbd(xmm3, xmm3); - } - else - { - pxor(xmm0, xmm0); - - punpckhbw(xmm2, xmm0); - punpcklwd(xmm2, xmm0); - - punpckhbw(xmm3, xmm0); - punpcklwd(xmm3, xmm0); - } - - movaps(xmmword[eax], xmm2); - movaps(xmmword[edx], xmm3); - } - - // m_min.p = pmin.xyww(); - // m_max.p = pmax.xyww(); - - shufps(xmm4, xmm4, _MM_SHUFFLE(3, 3, 1, 0)); - shufps(xmm5, xmm5, _MM_SHUFFLE(3, 3, 1, 0)); - - movaps(xmmword[eax + 16], xmm4); - movaps(xmmword[edx + 16], xmm5); - - if(tme) - { - // m_min.t = tmin; - // m_max.t = tmax; - - movaps(xmmword[eax + 32], xmm6); - movaps(xmmword[edx + 32], xmm7); - } - - ret(); +/* + * Copyright (C) 2007-2009 Gabest + * http://www.gabest.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program 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 GNU Make; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#pragma once + +#include "stdafx.h" +#include "GSVertexTrace.h" +#include "GSUtil.h" +#include "GSState.h" + +GSVertexTrace::GSVertexTrace(const GSState* state) + : m_state(state) +{ +} + +uint32 GSVertexTrace::Hash(GS_PRIM_CLASS primclass) +{ + m_primclass = primclass; + + uint32 hash = m_primclass | (m_state->PRIM->IIP << 2) | (m_state->PRIM->TME << 3) | (m_state->PRIM->FST << 4); + + if(!(m_state->PRIM->TME && m_state->m_context->TEX0.TFX == TFX_DECAL && m_state->m_context->TEX0.TCC)) + { + hash |= 1 << 5; + } + + return hash; +} + +void GSVertexTrace::Update(const GSVertexSW* v, int count, GS_PRIM_CLASS primclass) +{ + m_map_sw[Hash(primclass)](v, count, m_min, m_max); + + m_eq.value = (m_min.c == m_max.c).mask() | ((m_min.p == m_max.p).mask() << 16) | ((m_min.t == m_max.t).mask() << 20); + + m_alpha.valid = false; +} + +void GSVertexTrace::Update(const GSVertexHW9* v, int count, GS_PRIM_CLASS primclass) +{ + m_map_hw9[Hash(primclass)](v, count, m_min, m_max); + + const GSDrawingContext* context = m_state->m_context; + + GSVector4 o(context->XYOFFSET); + GSVector4 s(1.0f / 16, 1.0f / 16, 1.0f, 1.0f); + + m_min.p = (m_min.p - o) * s; + m_max.p = (m_max.p - o) * s; + + if(m_state->PRIM->TME) + { + if(m_state->PRIM->FST) + { + s = GSVector4(1 << (16 - 4), 1).xxyy(); + } + else + { + s = GSVector4(0x10000 << context->TEX0.TW, 0x10000 << context->TEX0.TH, 1, 1); + } + + m_min.t *= s; + m_max.t *= s; + } + + m_eq.value = (m_min.c == m_max.c).mask() | ((m_min.p == m_max.p).mask() << 16) | ((m_min.t == m_max.t).mask() << 20); + + m_alpha.valid = false; +} + +void GSVertexTrace::Update(const GSVertexHW11* v, int count, GS_PRIM_CLASS primclass) +{ + m_map_hw11[Hash(primclass)](v, count, m_min, m_max); + + const GSDrawingContext* context = m_state->m_context; + + GSVector4 o(context->XYOFFSET); + GSVector4 s(1.0f / 16, 1.0f / 16, 2.0f, 1.0f); + + m_min.p = (m_min.p - o) * s; + m_max.p = (m_max.p - o) * s; + + if(m_state->PRIM->TME) + { + if(m_state->PRIM->FST) + { + s = GSVector4(1 << (16 - 4), 1).xxyy(); + } + else + { + s = GSVector4(0x10000 << context->TEX0.TW, 0x10000 << context->TEX0.TH, 1, 1); + } + + m_min.t *= s; + m_max.t *= s; + } + + m_eq.value = (m_min.c == m_max.c).mask() | ((m_min.p == m_max.p).mask() << 16) | ((m_min.t == m_max.t).mask() << 20); + + m_alpha.valid = false; +} + +using namespace Xbyak; + +GSVertexTrace::CGSW::CGSW(uint32 key, void* ptr, size_t maxsize) + : CodeGenerator(maxsize, ptr) +{ + #if _M_AMD64 + #error TODO + #endif + + const int params = 0; + + uint32 primclass = (key >> 0) & 3; + uint32 iip = (key >> 2) & 1; + uint32 tme = (key >> 3) & 1; + uint32 fst = (key >> 4) & 1; + uint32 color = (key >> 5) & 1; + + int n = 1; + + switch(primclass) + { + case GS_POINT_CLASS: + n = 1; + break; + case GS_LINE_CLASS: + case GS_SPRITE_CLASS: + n = 2; + break; + case GS_TRIANGLE_CLASS: + n = 3; + break; + } + + const int _v = params + 4; + const int _count = params + 8; + const int _min = params + 12; + const int _max = params + 16; + + // + + static const float fmin = -FLT_MAX; + static const float fmax = FLT_MAX; + + movss(xmm0, xmmword[&fmax]); + shufps(xmm0, xmm0, _MM_SHUFFLE(0, 0, 0, 0)); + + movss(xmm1, xmmword[&fmin]); + shufps(xmm1, xmm1, _MM_SHUFFLE(0, 0, 0, 0)); + + if(color) + { + // min.c = FLT_MAX; + // max.c = -FLT_MAX; + + movaps(xmm2, xmm0); + movaps(xmm3, xmm1); + } + + // min.p = FLT_MAX; + // max.p = -FLT_MAX; + + movaps(xmm4, xmm0); + movaps(xmm5, xmm1); + + if(tme) + { + // min.t = FLT_MAX; + // max.t = -FLT_MAX; + + movaps(xmm6, xmm0); + movaps(xmm7, xmm1); + } + + // for(int i = 0; i < count; i += step) { + + mov(edx, dword[esp + _v]); + mov(ecx, dword[esp + _count]); + + align(16); + +L("loop"); + + if(tme && !fst && primclass == GS_SPRITE_CLASS) + { + movaps(xmm1, xmmword[edx + 1 * sizeof(GSVertexSW) + 32]); + shufps(xmm1, xmm1, _MM_SHUFFLE(2, 2, 2, 2)); + } + + for(int j = 0; j < n; j++) + { + if(color && (iip || j == n - 1)) + { + // min.c = min.c.minv(v[i + j].c); + // max.c = max.c.maxv(v[i + j].c); + + movaps(xmm0, xmmword[edx + j * sizeof(GSVertexSW)]); + + minps(xmm2, xmm0); + maxps(xmm3, xmm0); + } + + // min.p = min.p.minv(v[i + j].p); + // max.p = max.p.maxv(v[i + j].p); + + movaps(xmm0, xmmword[edx + j * sizeof(GSVertexSW) + 16]); + + minps(xmm4, xmm0); + maxps(xmm5, xmm0); + + if(tme) + { + // min.t = min.t.minv(v[i + j].t); + // max.t = max.t.maxv(v[i + j].t); + + movaps(xmm0, xmmword[edx + j * sizeof(GSVertexSW) + 32]); + + if(!fst) + { + if(primclass != GS_SPRITE_CLASS) + { + movaps(xmm1, xmm0); + shufps(xmm1, xmm1, _MM_SHUFFLE(2, 2, 2, 2)); + } + + divps(xmm0, xmm1); + shufps(xmm0, xmm1, _MM_SHUFFLE(3, 2, 1, 0)); + } + + minps(xmm6, xmm0); + maxps(xmm7, xmm0); + } + } + + add(edx, n * sizeof(GSVertexSW)); + sub(ecx, n); + + jg("loop"); + + // } + + mov(eax, dword[esp + _min]); + mov(edx, dword[esp + _max]); + + if(color) + { + cvttps2dq(xmm2, xmm2); + psrld(xmm2, 7); + movaps(xmmword[eax], xmm2); + + cvttps2dq(xmm3, xmm3); + psrld(xmm3, 7); + movaps(xmmword[edx], xmm3); + } + + movaps(xmmword[eax + 16], xmm4); + movaps(xmmword[edx + 16], xmm5); + + if(tme) + { + movaps(xmmword[eax + 32], xmm6); + movaps(xmmword[edx + 32], xmm7); + } + + ret(); +} + +GSVertexTrace::CGHW9::CGHW9(uint32 key, void* ptr, size_t maxsize) + : CodeGenerator(maxsize, ptr) +{ + #if _M_AMD64 + #error TODO + #endif + + const int params = 0; + + uint32 primclass = (key >> 0) & 3; + uint32 iip = (key >> 2) & 1; + uint32 tme = (key >> 3) & 1; + uint32 fst = (key >> 4) & 1; + uint32 color = (key >> 5) & 1; + + int n = 1; + + switch(primclass) + { + case GS_POINT_CLASS: + n = 1; + break; + case GS_LINE_CLASS: + n = 2; + break; + case GS_TRIANGLE_CLASS: + n = 3; + break; + case GS_SPRITE_CLASS: + n = 6; + break; + } + + const int _v = params + 4; + const int _count = params + 8; + const int _min = params + 12; + const int _max = params + 16; + + // + + static const float fmin = -FLT_MAX; + static const float fmax = FLT_MAX; + + movss(xmm0, xmmword[&fmax]); + shufps(xmm0, xmm0, _MM_SHUFFLE(0, 0, 0, 0)); + + movss(xmm1, xmmword[&fmin]); + shufps(xmm1, xmm1, _MM_SHUFFLE(0, 0, 0, 0)); + + if(color) + { + // min.c = 0xffffffff; + // max.c = 0; + + pcmpeqd(xmm2, xmm2); + pxor(xmm3, xmm3); + } + + // min.p = FLT_MAX; + // max.p = -FLT_MAX; + + movaps(xmm4, xmm0); + movaps(xmm5, xmm1); + + if(tme) + { + // min.t = FLT_MAX; + // max.t = -FLT_MAX; + + movaps(xmm6, xmm0); + movaps(xmm7, xmm1); + } + + // for(int i = 0; i < count; i += step) { + + mov(edx, dword[esp + _v]); + mov(ecx, dword[esp + _count]); + + align(16); + +L("loop"); + + if(tme && !fst && primclass == GS_SPRITE_CLASS) + { + movaps(xmm1, xmmword[edx + 5 * sizeof(GSVertexHW9) + 16]); + shufps(xmm1, xmm1, _MM_SHUFFLE(3, 3, 3, 3)); + } + + for(int j = 0; j < n; j++) + { + // min.p = min.p.minv(v[i + j].p); + // max.p = max.p.maxv(v[i + j].p); + + movaps(xmm0, xmmword[edx + j * sizeof(GSVertexHW9) + 16]); + + minps(xmm4, xmm0); + maxps(xmm5, xmm0); + + if(tme && !fst && primclass != GS_SPRITE_CLASS) + { + movaps(xmm1, xmm0); + shufps(xmm1, xmm1, _MM_SHUFFLE(3, 3, 3, 3)); + } + + if(color && (iip || j == n - 1) || tme) + { + movaps(xmm0, xmmword[edx + j * sizeof(GSVertexHW9)]); + } + + if(color && (iip || j == n - 1)) + { + // min.c = min.c.min_u8(v[i + j].c); + // max.c = max.c.min_u8(v[i + j].c); + + pminub(xmm2, xmm0); + pmaxub(xmm3, xmm0); + } + + if(tme) + { + shufps(xmm0, xmm0, _MM_SHUFFLE(1, 0, 1, 0)); // avoid FP assist, high part is integral + + if(!fst) + { + // t /= p.wwww(); + + divps(xmm0, xmm1); + } + + // min.t = min.t.minv(v[i + j].t); + // max.t = max.t.maxv(v[i + j].t); + + minps(xmm6, xmm0); + maxps(xmm7, xmm0); + } + } + + add(edx, n * sizeof(GSVertexHW9)); + sub(ecx, n); + + jg("loop"); + + // } + + mov(eax, dword[esp + _min]); + mov(edx, dword[esp + _max]); + + if(color) + { + // m_min.c = cmin.zzzz().u8to32(); + // m_max.c = cmax.zzzz().u8to32(); + + if(m_cpu.has(util::Cpu::tSSE41)) + { + pshufd(xmm2, xmm2, _MM_SHUFFLE(2, 2, 2, 2)); + pmovzxbd(xmm2, xmm2); + + pshufd(xmm3, xmm3, _MM_SHUFFLE(2, 2, 2, 2)); + pmovzxbd(xmm3, xmm3); + } + else + { + pxor(xmm0, xmm0); + + punpckhbw(xmm2, xmm0); + punpcklwd(xmm2, xmm0); + + punpckhbw(xmm3, xmm0); + punpcklwd(xmm3, xmm0); + } + + movaps(xmmword[eax], xmm2); + movaps(xmmword[edx], xmm3); + } + + // m_min.p = pmin; + // m_max.p = pmax; + + movaps(xmmword[eax + 16], xmm4); + movaps(xmmword[edx + 16], xmm5); + + if(tme) + { + // m_min.t = tmin.xyww(pmin); + // m_max.t = tmax.xyww(pmax); + + shufps(xmm6, xmm4, _MM_SHUFFLE(3, 3, 1, 0)); + shufps(xmm7, xmm5, _MM_SHUFFLE(3, 3, 1, 0)); + + movaps(xmmword[eax + 32], xmm6); + movaps(xmmword[edx + 32], xmm7); + } + + ret(); +} + +GSVertexTrace::CGHW11::CGHW11(uint32 key, void* ptr, size_t maxsize) + : CodeGenerator(maxsize, ptr) +{ + #if _M_AMD64 + #error TODO + #endif + + const int params = 0; + + uint32 primclass = (key >> 0) & 3; + uint32 iip = (key >> 2) & 1; + uint32 tme = (key >> 3) & 1; + uint32 fst = (key >> 4) & 1; + uint32 color = (key >> 5) & 1; + + int n = 1; + + switch(primclass) + { + case GS_POINT_CLASS: + n = 1; + break; + case GS_LINE_CLASS: + case GS_SPRITE_CLASS: + n = 2; + break; + case GS_TRIANGLE_CLASS: + n = 3; + break; + } + + const int _v = params + 4; + const int _count = params + 8; + const int _min = params + 12; + const int _max = params + 16; + + // + + static const float fmin = -FLT_MAX; + static const float fmax = FLT_MAX; + + movss(xmm0, xmmword[&fmax]); + shufps(xmm0, xmm0, _MM_SHUFFLE(0, 0, 0, 0)); + + movss(xmm1, xmmword[&fmin]); + shufps(xmm1, xmm1, _MM_SHUFFLE(0, 0, 0, 0)); + + if(color) + { + // min.c = 0xffffffff; + // max.c = 0; + + pcmpeqd(xmm2, xmm2); + pxor(xmm3, xmm3); + } + + // min.p = FLT_MAX; + // max.p = -FLT_MAX; + + movaps(xmm4, xmm0); + movaps(xmm5, xmm1); + + if(tme) + { + // min.t = FLT_MAX; + // max.t = -FLT_MAX; + + movaps(xmm6, xmm0); + movaps(xmm7, xmm1); + } + + // for(int i = 0; i < count; i += step) { + + mov(edx, dword[esp + _v]); + mov(ecx, dword[esp + _count]); + + align(16); + +L("loop"); + + for(int j = 0; j < n; j++) + { + if(color && (iip || j == n - 1) || tme) + { + movaps(xmm0, xmmword[edx + j * sizeof(GSVertexHW11)]); + } + + if(color && (iip || j == n - 1)) + { + pminub(xmm2, xmm0); + pmaxub(xmm3, xmm0); + } + + if(tme) + { + if(!fst) + { + movaps(xmm1, xmm0); + } + + shufps(xmm0, xmm0, _MM_SHUFFLE(3, 3, 1, 0)); // avoid FP assist, third dword is integral + + if(!fst) + { + shufps(xmm1, xmm1, _MM_SHUFFLE(3, 3, 3, 3)); + divps(xmm0, xmm1); + shufps(xmm0, xmm1, _MM_SHUFFLE(3, 3, 1, 0)); // restore q + } + + minps(xmm6, xmm0); + maxps(xmm7, xmm0); + } + + movdqa(xmm0, xmmword[edx + j * sizeof(GSVertexHW11) + 16]); + + if(m_cpu.has(util::Cpu::tSSE41)) + { + pmovzxwd(xmm1, xmm0); + } + else + { + movdqa(xmm1, xmm0); + punpcklwd(xmm1, xmm1); + psrld(xmm1, 16); + } + + psrld(xmm0, 1); + punpcklqdq(xmm1, xmm0); + cvtdq2ps(xmm1, xmm1); + + minps(xmm4, xmm1); + maxps(xmm5, xmm1); + } + + add(edx, n * sizeof(GSVertexHW11)); + sub(ecx, n); + + jg("loop"); + + // } + + mov(eax, dword[esp + _min]); + mov(edx, dword[esp + _max]); + + if(color) + { + // m_min.c = cmin.zzzz().u8to32(); + // m_max.c = cmax.zzzz().u8to32(); + + if(m_cpu.has(util::Cpu::tSSE41)) + { + pshufd(xmm2, xmm2, _MM_SHUFFLE(2, 2, 2, 2)); + pmovzxbd(xmm2, xmm2); + + pshufd(xmm3, xmm3, _MM_SHUFFLE(2, 2, 2, 2)); + pmovzxbd(xmm3, xmm3); + } + else + { + pxor(xmm0, xmm0); + + punpckhbw(xmm2, xmm0); + punpcklwd(xmm2, xmm0); + + punpckhbw(xmm3, xmm0); + punpcklwd(xmm3, xmm0); + } + + movaps(xmmword[eax], xmm2); + movaps(xmmword[edx], xmm3); + } + + // m_min.p = pmin.xyww(); + // m_max.p = pmax.xyww(); + + shufps(xmm4, xmm4, _MM_SHUFFLE(3, 3, 1, 0)); + shufps(xmm5, xmm5, _MM_SHUFFLE(3, 3, 1, 0)); + + movaps(xmmword[eax + 16], xmm4); + movaps(xmmword[edx + 16], xmm5); + + if(tme) + { + // m_min.t = tmin; + // m_max.t = tmax; + + movaps(xmmword[eax + 32], xmm6); + movaps(xmmword[edx + 32], xmm7); + } + + ret(); } \ No newline at end of file diff --git a/plugins/GSdx/GSVertexTrace.h b/plugins/GSdx/GSVertexTrace.h index 0f372fe8bc..4ae30596aa 100644 --- a/plugins/GSdx/GSVertexTrace.h +++ b/plugins/GSdx/GSVertexTrace.h @@ -1,110 +1,110 @@ -/* - * Copyright (C) 2007-2009 Gabest - * http://www.gabest.org - * - * This Program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This Program 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 GNU Make; see the file COPYING. If not, write to - * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - * http://www.gnu.org/copyleft/gpl.html - * - */ - -#pragma once - -#include "GSDrawingContext.h" -#include "GSVertex.h" -#include "GSVertexSW.h" -#include "GSVertexHW.h" -#include "GSFunctionMap.h" -#include "xbyak/xbyak.h" -#include "xbyak/xbyak_util.h" - -class GSState; - -__aligned16 class GSVertexTrace -{ - struct Vertex {GSVector4i c; GSVector4 p, t;}; - struct VertexAlpha {int min, max; bool valid;}; - - typedef void (*VertexTracePtr)(const void* v, int count, Vertex& min, Vertex& max); - - class CGSW : public Xbyak::CodeGenerator - { - public: - CGSW(uint32 key, void* ptr, size_t maxsize); - }; - - class GSVertexTraceMapSW : public GSCodeGeneratorFunctionMap - { - public: - GSVertexTraceMapSW() : GSCodeGeneratorFunctionMap("VertexTraceSW") {} - CGSW* Create(uint32 key, void* ptr, size_t maxsize) {return new CGSW(key, ptr, maxsize);} - }; - - class CGHW9 : public Xbyak::CodeGenerator - { - Xbyak::util::Cpu m_cpu; - - public: - CGHW9(uint32 key, void* ptr, size_t maxsize); - }; - - class GSVertexTraceMapHW9 : public GSCodeGeneratorFunctionMap - { - public: - GSVertexTraceMapHW9() : GSCodeGeneratorFunctionMap("VertexTraceHW9") {} - CGHW9* Create(uint32 key, void* ptr, size_t maxsize) {return new CGHW9(key, ptr, maxsize);} - }; - - class CGHW11 : public Xbyak::CodeGenerator - { - Xbyak::util::Cpu m_cpu; - - public: - CGHW11(uint32 key, void* ptr, size_t maxsize); - }; - - class GSVertexTraceMapHW11 : public GSCodeGeneratorFunctionMap - { - public: - GSVertexTraceMapHW11() : GSCodeGeneratorFunctionMap("VertexTraceHW11") {} - CGHW11* Create(uint32 key, void* ptr, size_t maxsize) {return new CGHW11(key, ptr, maxsize);} - }; - - GSVertexTraceMapSW m_map_sw; - GSVertexTraceMapHW9 m_map_hw9; - GSVertexTraceMapHW11 m_map_hw11; - - uint32 Hash(GS_PRIM_CLASS primclass); - - const GSState* m_state; - -public: - GS_PRIM_CLASS m_primclass; - Vertex m_min, m_max; // t.xy * 0x10000 - VertexAlpha m_alpha; // source alpha range after tfx, GSRenderer::GetAlphaMinMax() updates it - - union - { - uint32 value; - struct {uint32 r:4, g:4, b:4, a:4, x:1, y:1, z:1, f:1, s:1, t:1, q:1, _pad:1;}; - struct {uint32 rgba:16, xyzf:4, stq:4;}; - } m_eq; - - GSVertexTrace(const GSState* state); - - void Update(const GSVertexSW* v, int count, GS_PRIM_CLASS primclass); - void Update(const GSVertexHW9* v, int count, GS_PRIM_CLASS primclass); - void Update(const GSVertexHW11* v, int count, GS_PRIM_CLASS primclass); - void Update(const GSVertexNull* v, int count, GS_PRIM_CLASS primclass) {} -}; +/* + * Copyright (C) 2007-2009 Gabest + * http://www.gabest.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program 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 GNU Make; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#pragma once + +#include "GSDrawingContext.h" +#include "GSVertex.h" +#include "GSVertexSW.h" +#include "GSVertexHW.h" +#include "GSFunctionMap.h" +#include "xbyak/xbyak.h" +#include "xbyak/xbyak_util.h" + +class GSState; + +__aligned16 class GSVertexTrace +{ + struct Vertex {GSVector4i c; GSVector4 p, t;}; + struct VertexAlpha {int min, max; bool valid;}; + + typedef void (*VertexTracePtr)(const void* v, int count, Vertex& min, Vertex& max); + + class CGSW : public Xbyak::CodeGenerator + { + public: + CGSW(uint32 key, void* ptr, size_t maxsize); + }; + + class GSVertexTraceMapSW : public GSCodeGeneratorFunctionMap + { + public: + GSVertexTraceMapSW() : GSCodeGeneratorFunctionMap("VertexTraceSW") {} + CGSW* Create(uint32 key, void* ptr, size_t maxsize) {return new CGSW(key, ptr, maxsize);} + }; + + class CGHW9 : public Xbyak::CodeGenerator + { + Xbyak::util::Cpu m_cpu; + + public: + CGHW9(uint32 key, void* ptr, size_t maxsize); + }; + + class GSVertexTraceMapHW9 : public GSCodeGeneratorFunctionMap + { + public: + GSVertexTraceMapHW9() : GSCodeGeneratorFunctionMap("VertexTraceHW9") {} + CGHW9* Create(uint32 key, void* ptr, size_t maxsize) {return new CGHW9(key, ptr, maxsize);} + }; + + class CGHW11 : public Xbyak::CodeGenerator + { + Xbyak::util::Cpu m_cpu; + + public: + CGHW11(uint32 key, void* ptr, size_t maxsize); + }; + + class GSVertexTraceMapHW11 : public GSCodeGeneratorFunctionMap + { + public: + GSVertexTraceMapHW11() : GSCodeGeneratorFunctionMap("VertexTraceHW11") {} + CGHW11* Create(uint32 key, void* ptr, size_t maxsize) {return new CGHW11(key, ptr, maxsize);} + }; + + GSVertexTraceMapSW m_map_sw; + GSVertexTraceMapHW9 m_map_hw9; + GSVertexTraceMapHW11 m_map_hw11; + + uint32 Hash(GS_PRIM_CLASS primclass); + + const GSState* m_state; + +public: + GS_PRIM_CLASS m_primclass; + Vertex m_min, m_max; // t.xy * 0x10000 + VertexAlpha m_alpha; // source alpha range after tfx, GSRenderer::GetAlphaMinMax() updates it + + union + { + uint32 value; + struct {uint32 r:4, g:4, b:4, a:4, x:1, y:1, z:1, f:1, s:1, t:1, q:1, _pad:1;}; + struct {uint32 rgba:16, xyzf:4, stq:4;}; + } m_eq; + + GSVertexTrace(const GSState* state); + + void Update(const GSVertexSW* v, int count, GS_PRIM_CLASS primclass); + void Update(const GSVertexHW9* v, int count, GS_PRIM_CLASS primclass); + void Update(const GSVertexHW11* v, int count, GS_PRIM_CLASS primclass); + void Update(const GSVertexNull* v, int count, GS_PRIM_CLASS primclass) {} +}; diff --git a/plugins/GSdx/vtune/JITProfiling.h b/plugins/GSdx/vtune/JITProfiling.h index bd1f2c243d..856756b97e 100644 --- a/plugins/GSdx/vtune/JITProfiling.h +++ b/plugins/GSdx/vtune/JITProfiling.h @@ -1,154 +1,154 @@ -/************************************************************************* - * - * INTEL CORPORATION PROPRIETARY INFORMATION - * This software is supplied under the terms of a license agreement or - * nondisclosure agreement with Intel Corporation and may not be copied - * or disclosed except in accordance with the terms of that agreement. - * - * Copyright(c) 2008 Intel Corporation. All Rights Reserved. - * - ************************************************************************/ -#ifndef __JITPROFILING_H__ -#define __JITPROFILING_H__ - -/*************************************** - * Various constants used by functions * - ***************************************/ - - // event notification - typedef enum iJIT_jvm_event - { - - // shutdown - iJVM_EVENT_TYPE_SHUTDOWN = 2, // Program exiting - // EventSpecificData NA - - - // JIT profiling - iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED=13, // issued after method code jitted - // into memory but before code is executed - // EventSpecificData is an iJIT_Method_Load - - iJVM_EVENT_TYPE_METHOD_UNLOAD_START, // issued before unload. Method code will no - // longer be executed, but code and info - // are still in memory. The VTune profiler - // may capture method code only at this point. - // EventSpecificData is iJIT_Method_Id - - // Method Profiling - - // method name, Id and stack is supplied - iJVM_EVENT_TYPE_ENTER_NIDS = 19, // issued when a method is about to be entered - // EventSpecificData is iJIT_Method_NIDS - - // method name, Id and stack is supplied - iJVM_EVENT_TYPE_LEAVE_NIDS, // issued when a method is about to be left - // EventSpecificData is iJIT_Method_NIDS - - } iJIT_JVM_EVENT; - - typedef enum _iJIT_ModeFlags - { - iJIT_NO_NOTIFICATIONS = 0x0000, // No need to Notify VTune, Since VTune is not running. - - iJIT_BE_NOTIFY_ON_LOAD = 0x0001, // when turned on the jit must call iJIT_NotifyEvent( iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED, ...) for all the method already jitted. - iJIT_BE_NOTIFY_ON_UNLOAD = 0x0002, // when turned on the jit must call iJIT_NotifyEvent( iJVM_EVENT_TYPE_METHOD_UNLOAD_FINISHED, ...) for all the method that are unloaded - - iJIT_BE_NOTIFY_ON_METHOD_ENTRY = 0x0004, // when turned on the jit must instrument all the currently jited code with calls on method entries - iJIT_BE_NOTIFY_ON_METHOD_EXIT = 0x0008, // when turned on the jit must instrument all the currently jited code with calls on method exit - - } iJIT_ModeFlags; - - - // Flags used by iJIT_IsProfilingActive() -typedef enum _iJIT_IsProfilingActiveFlags -{ - iJIT_NOTHING_RUNNING = 0x0000, // No profiler is running. Currently not used. - iJIT_SAMPLING_ON = 0x0001, // Sampling is running. This is the default value returned by iJIT_IsProfilingActive() - iJIT_CALLGRAPH_ON = 0x0002 // Call Graph is running - -} iJIT_IsProfilingActiveFlags; - - - // Enumerator for the environment of methods -typedef enum _iJDEnvironmentType -{ - iJDE_JittingAPI = 2 - -} iJDEnvironmentType; - -/********************************** - * Data structures for the events * - **********************************/ - - // structure for the events: - // iJVM_EVENT_TYPE_METHOD_UNLOAD_START - -typedef struct _iJIT_Method_Id -{ - unsigned int method_id; // Id of the method (same as the one passed in the iJIT_Method_Load struct - -} *piJIT_Method_Id, iJIT_Method_Id; - - - // structure for the events: - // iJVM_EVENT_TYPE_ENTER_NIDS, - // iJVM_EVENT_TYPE_LEAVE_NIDS, - // iJVM_EVENT_TYPE_EXCEPTION_OCCURRED_NIDS - -typedef struct _iJIT_Method_NIDS -{ - unsigned int method_id; // unique method ID - unsigned int stack_id; // NOTE: no need to fill this field, it's filled by VTune - char* method_name; // method name (just the method, without the class) - -} *piJIT_Method_NIDS, iJIT_Method_NIDS; - - // structures for the events: - // iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED - -typedef struct _LineNumberInfo -{ - unsigned int Offset; // x86 Offset from the begining of the method - unsigned int LineNumber; // source line number from the begining of the source file. - -} *pLineNumberInfo, LineNumberInfo; - -typedef struct _iJIT_Method_Load -{ - unsigned int method_id; // unique method ID - can be any unique value, (except 0 - 999) - char* method_name; // method name (can be with or without the class and signature, in any case the class name will be added to it) - void* method_load_address; // virtual address of that method - This determines the method range for the iJVM_EVENT_TYPE_ENTER/LEAVE_METHOD_ADDR events - unsigned int method_size; // Size in memory - Must be exact - unsigned int line_number_size; // Line Table size in number of entries - Zero if none - pLineNumberInfo line_number_table; // Pointer to the begining of the line numbers info array - unsigned int class_id; // unique class ID - char* class_file_name; // class file name - char* source_file_name; // source file name - void* user_data; // bits supplied by the user for saving in the JIT file... - unsigned int user_data_size; // the size of the user data buffer - iJDEnvironmentType env; // NOTE: no need to fill this field, it's filled by VTune - -} *piJIT_Method_Load, iJIT_Method_Load; - - - -//API Functions -#ifdef __cplusplus -extern "C" { -#endif - -typedef void (*iJIT_ModeChangedEx)(void *UserData, iJIT_ModeFlags Flags); // called when the settings are changed with new settings - -int iJIT_NotifyEvent(iJIT_JVM_EVENT event_type, void *EventSpecificData); -void iJIT_RegisterCallbackEx(void *userdata, iJIT_ModeChangedEx NewModeCallBackFuncEx); // The new mode call back routine -iJIT_IsProfilingActiveFlags iJIT_IsProfilingActive(); -void FinalizeThread(); -void FinalizeProcess(); -unsigned int iJIT_GetNewMethodID(); - -#ifdef __cplusplus -} -#endif - -#endif //__JITPROFILING_H__ +/************************************************************************* + * + * INTEL CORPORATION PROPRIETARY INFORMATION + * This software is supplied under the terms of a license agreement or + * nondisclosure agreement with Intel Corporation and may not be copied + * or disclosed except in accordance with the terms of that agreement. + * + * Copyright(c) 2008 Intel Corporation. All Rights Reserved. + * + ************************************************************************/ +#ifndef __JITPROFILING_H__ +#define __JITPROFILING_H__ + +/*************************************** + * Various constants used by functions * + ***************************************/ + + // event notification + typedef enum iJIT_jvm_event + { + + // shutdown + iJVM_EVENT_TYPE_SHUTDOWN = 2, // Program exiting + // EventSpecificData NA + + + // JIT profiling + iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED=13, // issued after method code jitted + // into memory but before code is executed + // EventSpecificData is an iJIT_Method_Load + + iJVM_EVENT_TYPE_METHOD_UNLOAD_START, // issued before unload. Method code will no + // longer be executed, but code and info + // are still in memory. The VTune profiler + // may capture method code only at this point. + // EventSpecificData is iJIT_Method_Id + + // Method Profiling + + // method name, Id and stack is supplied + iJVM_EVENT_TYPE_ENTER_NIDS = 19, // issued when a method is about to be entered + // EventSpecificData is iJIT_Method_NIDS + + // method name, Id and stack is supplied + iJVM_EVENT_TYPE_LEAVE_NIDS, // issued when a method is about to be left + // EventSpecificData is iJIT_Method_NIDS + + } iJIT_JVM_EVENT; + + typedef enum _iJIT_ModeFlags + { + iJIT_NO_NOTIFICATIONS = 0x0000, // No need to Notify VTune, Since VTune is not running. + + iJIT_BE_NOTIFY_ON_LOAD = 0x0001, // when turned on the jit must call iJIT_NotifyEvent( iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED, ...) for all the method already jitted. + iJIT_BE_NOTIFY_ON_UNLOAD = 0x0002, // when turned on the jit must call iJIT_NotifyEvent( iJVM_EVENT_TYPE_METHOD_UNLOAD_FINISHED, ...) for all the method that are unloaded + + iJIT_BE_NOTIFY_ON_METHOD_ENTRY = 0x0004, // when turned on the jit must instrument all the currently jited code with calls on method entries + iJIT_BE_NOTIFY_ON_METHOD_EXIT = 0x0008, // when turned on the jit must instrument all the currently jited code with calls on method exit + + } iJIT_ModeFlags; + + + // Flags used by iJIT_IsProfilingActive() +typedef enum _iJIT_IsProfilingActiveFlags +{ + iJIT_NOTHING_RUNNING = 0x0000, // No profiler is running. Currently not used. + iJIT_SAMPLING_ON = 0x0001, // Sampling is running. This is the default value returned by iJIT_IsProfilingActive() + iJIT_CALLGRAPH_ON = 0x0002 // Call Graph is running + +} iJIT_IsProfilingActiveFlags; + + + // Enumerator for the environment of methods +typedef enum _iJDEnvironmentType +{ + iJDE_JittingAPI = 2 + +} iJDEnvironmentType; + +/********************************** + * Data structures for the events * + **********************************/ + + // structure for the events: + // iJVM_EVENT_TYPE_METHOD_UNLOAD_START + +typedef struct _iJIT_Method_Id +{ + unsigned int method_id; // Id of the method (same as the one passed in the iJIT_Method_Load struct + +} *piJIT_Method_Id, iJIT_Method_Id; + + + // structure for the events: + // iJVM_EVENT_TYPE_ENTER_NIDS, + // iJVM_EVENT_TYPE_LEAVE_NIDS, + // iJVM_EVENT_TYPE_EXCEPTION_OCCURRED_NIDS + +typedef struct _iJIT_Method_NIDS +{ + unsigned int method_id; // unique method ID + unsigned int stack_id; // NOTE: no need to fill this field, it's filled by VTune + char* method_name; // method name (just the method, without the class) + +} *piJIT_Method_NIDS, iJIT_Method_NIDS; + + // structures for the events: + // iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED + +typedef struct _LineNumberInfo +{ + unsigned int Offset; // x86 Offset from the begining of the method + unsigned int LineNumber; // source line number from the begining of the source file. + +} *pLineNumberInfo, LineNumberInfo; + +typedef struct _iJIT_Method_Load +{ + unsigned int method_id; // unique method ID - can be any unique value, (except 0 - 999) + char* method_name; // method name (can be with or without the class and signature, in any case the class name will be added to it) + void* method_load_address; // virtual address of that method - This determines the method range for the iJVM_EVENT_TYPE_ENTER/LEAVE_METHOD_ADDR events + unsigned int method_size; // Size in memory - Must be exact + unsigned int line_number_size; // Line Table size in number of entries - Zero if none + pLineNumberInfo line_number_table; // Pointer to the begining of the line numbers info array + unsigned int class_id; // unique class ID + char* class_file_name; // class file name + char* source_file_name; // source file name + void* user_data; // bits supplied by the user for saving in the JIT file... + unsigned int user_data_size; // the size of the user data buffer + iJDEnvironmentType env; // NOTE: no need to fill this field, it's filled by VTune + +} *piJIT_Method_Load, iJIT_Method_Load; + + + +//API Functions +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (*iJIT_ModeChangedEx)(void *UserData, iJIT_ModeFlags Flags); // called when the settings are changed with new settings + +int iJIT_NotifyEvent(iJIT_JVM_EVENT event_type, void *EventSpecificData); +void iJIT_RegisterCallbackEx(void *userdata, iJIT_ModeChangedEx NewModeCallBackFuncEx); // The new mode call back routine +iJIT_IsProfilingActiveFlags iJIT_IsProfilingActive(); +void FinalizeThread(); +void FinalizeProcess(); +unsigned int iJIT_GetNewMethodID(); + +#ifdef __cplusplus +} +#endif + +#endif //__JITPROFILING_H__ diff --git a/plugins/GSdx/xbyak/xbyak.h b/plugins/GSdx/xbyak/xbyak.h index 564cca6262..8ca512b078 100644 --- a/plugins/GSdx/xbyak/xbyak.h +++ b/plugins/GSdx/xbyak/xbyak.h @@ -1,1338 +1,1338 @@ -#ifndef XBYAK_H_ -#define XBYAK_H_ -/*! - @file xbyak.h - @brief Xbyak ; JIT assembler for x86(IA32)/x64 by C++ - @author herumi - @version $Revision: 1.157 $ - @url http://homepage1.nifty.com/herumi/soft/xbyak.html - @date $Date: 2008/12/30 04:53:11 $ - @note modified new BSD license - http://www.opensource.org/licenses/bsd-license.php -*/ - -#include // for debug print -#include -#include -#include -#ifdef __GNUC__ -#include -#include -#endif - -#ifdef __x86_64__ - #define XBYAK64_GCC -#elif defined(_WIN64) - #define XBYAK64_WIN -#endif -#if !defined(XBYAK64) && !defined(XBYAK32) - #if defined(XBYAK64_GCC) || defined(XBYAK64_WIN) - #define XBYAK64 - #else - #define XBYAK32 - #endif -#endif - -#ifdef _MSC_VER - #pragma warning(push) - #pragma warning(disable : 4514) /* remove inline function */ - #pragma warning(disable : 4786) /* identifier is too long */ - #pragma warning(disable : 4503) /* name is too long */ - #pragma warning(disable : 4127) /* constant expresison */ - #if (_MSC_VER <= 1200) - #ifndef for - #define for if(0);else for - #pragma warning(disable : 4127) /* condition is constant(for "if" trick) */ - #endif - #endif - #include -#endif - -#ifndef NUM_OF_ARRAY -// template -// size_t num_of_array(const T (&)[N]) { return N; } - #define NUM_OF_ARRAY(x) (sizeof(x) / sizeof(*x)) -#endif - -namespace Xbyak { - -#include "xbyak_bin2hex.h" - -enum { - DEFAULT_MAX_CODE_SIZE = 2048, - VERSION = 0x2070, /* 0xABCD = A.BC(D) */ -}; -/* -#ifndef MIE_DEFINED_UINT32 - #define MIE_DEFINED_UINT32 - #ifdef _MSC_VER - typedef unsigned __int64 uint64; - #else - typedef unsigned long long uint64; - #endif - typedef unsigned int uint32; - typedef unsigned short uint16; - typedef unsigned char uint8; - #ifndef MIE_ALIGN - #ifdef _MSC_VER - #define MIE_ALIGN(x) __declspec(align(x)) - #else - #define MIE_ALIGN(x) __attribute__((aligned(x))) - #endif - #endif -#endif -*/ -enum Error { - ERR_NONE = 0, - ERR_BAD_ADDRESSING, - ERR_CODE_IS_TOO_BIG, - ERR_BAD_SCALE, - ERR_ESP_CANT_BE_INDEX, - ERR_BAD_COMBINATION, - ERR_BAD_SIZE_OF_REGISTER, - ERR_IMM_IS_TOO_BIG, - ERR_BAD_ALIGN, - ERR_LABEL_IS_REDEFINED, - ERR_LABEL_IS_TOO_FAR, - ERR_LABEL_IS_NOT_FOUND, - ERR_CODE_ISNOT_COPYABLE, - ERR_BAD_PARAMETER, - ERR_CANT_PROTECT, - ERR_CANT_USE_64BIT_DISP, - ERR_OFFSET_IS_TOO_BIG, - ERR_MEM_SIZE_IS_NOT_SPECIFIED, - ERR_INTERNAL -}; - -static inline const char *ConvertErrorToString(Error err) -{ - static const char errTbl[][40] = { - "none", - "bad addressing", - "code is too big", - "bad scale", - "esp can't be index", - "bad combination", - "bad size of register", - "imm is too big", - "bad align", - "label is redefined", - "label is too far", - "label is not found", - "code is not copyable", - "bad parameter", - "can't protect", - "can't use 64bit disp(use (void*))", - "offset is too big", - "MEM size is not specified", - "internal error", - }; - if (err < 0 || err > ERR_INTERNAL) return 0; - return errTbl[err]; -} - -namespace inner { - -enum { debug = 1 }; - -static inline uint32 GetPtrDist(const void *p1, const void *p2 = 0) -{ - uint64 diff = static_cast(p1) - static_cast(p2); -#ifdef XBYAK64 - if (0x7FFFFFFFULL < diff && diff < 0xFFFFFFFF80000000ULL) throw ERR_OFFSET_IS_TOO_BIG; -#endif - return static_cast(diff); -} - -static inline bool IsInDisp8(uint32 x) { return 0xFFFFFF80 <= x || x <= 0x7F; } - -} - -class Operand { -private: - const uint8 idx_; - const uint8 kind_; - const uint8 bit_; - const uint8 ext8bit_; // 1 if spl/bpl/sil/dil, otherwise 0 - void operator=(Operand&); -public: - enum Kind { - NONE = 0, - MEM = 1 << 1, - IMM = 1 << 2, - REG = 1 << 3, - MMX = 1 << 4, - XMM = 1 << 5, - FPU = 1 << 6 - }; - enum Code { -#ifdef XBYAK64 - RAX = 0, RCX, RDX, RBX, RSP, RBP, RSI, RDI, R8, R9, R10, R11, R12, R13, R14, R15, - R8D = 8, R9D, R10D, R11D, R12D, R13D, R14D, R15D, - R8W = 8, R9W, R10W, R11W, R12W, R13W, R14W, R15W, - R8B = 8, R9B, R10B, R11B, R12B, R13B, R14B, R15B, - SPL = 4, BPL, SIL, DIL, -#endif - EAX = 0, ECX, EDX, EBX, ESP, EBP, ESI, EDI, - AX = 0, CX, DX, BX, SP, BP, SI, DI, - AL = 0, CL, DL, BL, AH, CH, DH, BH - }; - Operand() : idx_(0), kind_(0), bit_(0), ext8bit_(0) { } - Operand(int idx, Kind kind, int bit, int ext8bit = 0) - : idx_(static_cast(idx)) - , kind_(static_cast(kind)) - , bit_(static_cast(bit)) - , ext8bit_(static_cast(ext8bit)) - { - assert((bit_ & (bit_ - 1)) == 0); // bit must be power of two - } - Kind getKind() const { return static_cast(kind_); } - int getIdx() const { return idx_; } - bool isNone() const { return kind_ == 0; } - bool isMMX() const { return is(MMX); } - bool isXMM() const { return is(XMM); } - bool isREG(int bit = 0) const { return is(REG, bit); } - bool isMEM(int bit = 0) const { return is(MEM, bit); } - bool isExt8bit() const { return ext8bit_ != 0; } - Operand changeBit(int bit) const { return Operand(idx_, static_cast(kind_), bit, ext8bit_); } - // any bit is accetable if bit == 0 - bool is(int kind, uint32 bit = 0) const - { - return (kind_ & kind) && (bit == 0 || (bit_ & bit)); // cf. you can set (8|16) - } - bool isBit(uint32 bit) const { return (bit_ & bit) != 0; } - uint32 getBit() const { return bit_; } - const char *toString() const - { - if (kind_ == REG) { - if (ext8bit_) { - static const char tbl[4][4] = { "spl", "bpl", "sil", "dil" }; - return tbl[idx_ - 4]; - } - static const char tbl[4][16][5] = { - { "al", "cl", "dl", "bl", "ah", "ch", "dh", "bh", "r8b", "r9b", "r10b", "r11b", "r12b", "r13b", "r14b", "r15b" }, - { "ax", "cx", "dx", "bx", "sp", "bp", "si", "di", "r8w", "r9w", "r10w", "r11w", "r12w", "r13w", "r14w", "r15w" }, - { "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi", "r8d", "r9d", "r10d", "r11d", "r12d", "r13d", "r14d", "r15d" }, - { "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15" }, - }; - return tbl[bit_ == 8 ? 0 : bit_ == 16 ? 1 : bit_ == 32 ? 2 : 3][idx_]; - } else if (isMMX()) { - static const char tbl[8][4] = { "mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7" }; - return tbl[idx_]; - } else if (isXMM()) { - static const char tbl[16][5] = { "xm0", "xm1", "xm2", "xm3", "xm4", "xm5", "xm6", "xm7", "xm8", "xm9", "xm10", "xm11", "xm12", "xm13", "xm14", "xm15" }; - return tbl[idx_]; - } - throw ERR_INTERNAL; - } -}; - -class Reg : public Operand { - void operator=(const Reg&); -public: - Reg() { } - Reg(int idx, Kind kind, int bit = 0, int ext8bit = 0) : Operand(idx, kind, bit, ext8bit) { } - // reg = this - uint8 getRex(const Reg& index = Reg(), const Reg& base = Reg()) const - { - if ((!isExt8bit() && !index.isExt8bit() && !base.isExt8bit()) && (getIdx() | index.getIdx() | base.getIdx()) < 8) return 0; - return uint8(0x40 | ((getIdx() >> 3) << 2)| ((index.getIdx() >> 3) << 1) | (base.getIdx() >> 3)); - } -}; - -class Reg8 : public Reg { - void operator=(const Reg8&); -public: - explicit Reg8(int idx, int ext8bit = 0) : Reg(idx, Operand::REG, 8, ext8bit) { } -}; - -class Reg16 : public Reg { - void operator=(const Reg16&); -public: - explicit Reg16(int idx) : Reg(idx, Operand::REG, 16) { } -}; - -class Mmx : public Reg { - void operator=(const Mmx&); -public: - explicit Mmx(int idx, Kind kind = Operand::MMX, int bit = 64) : Reg(idx, kind, bit) { } -}; - -class Xmm : public Mmx { - void operator=(const Xmm&); -public: - explicit Xmm(int idx) : Mmx(idx, Operand::XMM, 128) { } -}; - -// register for addressing(32bit or 64bit) -class Reg32e : public Reg { -public: - // [base_(this) + index_ * scale_ + disp_] - const Reg index_; - const int scale_; // 0(index is none), 1, 2, 4, 8 - const uint32 disp_; -private: - friend class Address; - friend Reg32e operator+(const Reg32e& a, const Reg32e& b) - { - if (a.scale_ == 0) { - if (b.scale_ == 0) { // base + base - if (b.getIdx() == Operand::ESP) { // [reg + esp] => [esp + reg] - return Reg32e(b, a, 1, a.disp_ + b.disp_); - } else { - return Reg32e(a, b, 1, a.disp_ + b.disp_); - } - } else if (b.isNone()) { // base + index - return Reg32e(a, b.index_, b.scale_, a.disp_ + b.disp_); - } - } - throw ERR_BAD_ADDRESSING; - } - friend Reg32e operator*(const Reg32e& r, int scale) - { - if (r.scale_ == 0) { - if (scale == 1) { - return r; - } else if (scale == 2 || scale == 4 || scale == 8) { - return Reg32e(Reg(), r, scale, r.disp_); - } - } - throw ERR_BAD_SCALE; - } - friend Reg32e operator+(const Reg32e& r, unsigned int disp) - { - return Reg32e(r, r.index_, r.scale_, r.disp_ + disp); - } - friend Reg32e operator-(const Reg32e& r, unsigned int disp) - { - return operator+(r, -static_cast(disp)); - } - void operator=(const Reg32e&); // don't call -public: - explicit Reg32e(int idx, int bit) - : Reg(idx, REG, bit) - , index_() - , scale_(0) - , disp_(0) - { - } - Reg32e(const Reg& base, const Reg& index, int scale, unsigned int disp) - : Reg(base) - , index_(index) - , scale_(scale) - , disp_(disp) - { - if (scale != 0 && scale != 1 && scale != 2 && scale != 4 && scale != 8) throw ERR_BAD_SCALE; - if (!base.isNone() && !index.isNone() && base.getBit() != index.getBit()) throw ERR_BAD_COMBINATION; - if (index.getIdx() == Operand::ESP) throw ERR_ESP_CANT_BE_INDEX; - } - Reg32e optimize() const // select smaller size - { - // [reg * 2] => [reg + reg] - if (isNone() && !index_.isNone() && scale_ == 2) { - const Reg index(index_.getIdx(), Operand::REG, index_.getBit()); - return Reg32e(index, index, 1, disp_); - } - return *this; - } -}; - -struct Reg32 : public Reg32e { - explicit Reg32(int idx) : Reg32e(idx, 32) {} -private: - void operator=(const Reg32&); -}; -#ifdef XBYAK64 -struct Reg64 : public Reg32e { - explicit Reg64(int idx) : Reg32e(idx, 64) {} -private: - void operator=(const Reg64&); -}; -struct RegRip { - uint32 disp_; - RegRip(unsigned int disp = 0) : disp_(disp) {} - friend const RegRip operator+(const RegRip& r, unsigned int disp) { - return RegRip(r.disp_ + disp); - } - friend const RegRip operator-(const RegRip& r, unsigned int disp) { - return RegRip(r.disp_ - disp); - } -}; -#endif - -class CodeArray { - enum { - ALIGN_SIZE = 16, - MAX_FIXED_BUF_SIZE = 8 - }; - enum Type { - FIXED_BUF, // use buf_(non alignment, non protect) - USER_BUF, // use userPtr(non alignment, non protect) - ALLOC_BUF // use new(alignment, protect) - }; - void operator=(const CodeArray&); - Type type_; - uint8 *const allocPtr_; // for ALLOC_BUF - uint8 buf_[MAX_FIXED_BUF_SIZE]; // for FIXED_BUF -protected: - const size_t maxSize_; - uint8 *const top_; - size_t size_; -public: - CodeArray(size_t maxSize = MAX_FIXED_BUF_SIZE, void *userPtr = 0) - : type_(userPtr ? USER_BUF : maxSize <= MAX_FIXED_BUF_SIZE ? FIXED_BUF : ALLOC_BUF) - , allocPtr_(type_ == ALLOC_BUF ? new uint8[maxSize + ALIGN_SIZE] : 0) - , maxSize_(maxSize) - , top_(type_ == ALLOC_BUF ? getAlignedAddress(allocPtr_) : type_ == USER_BUF ? reinterpret_cast(userPtr) : buf_) - , size_(0) - { - if (type_ == ALLOC_BUF && !protect(top_, maxSize, true)) { -// fprintf(stderr, "can't protect (addr=%p, size=%u, canExec=%d)\n", addr, size, canExec); - throw ERR_CANT_PROTECT; - } - } - virtual ~CodeArray() - { - if (type_ == ALLOC_BUF) { - protect(top_, maxSize_, false); - delete[] allocPtr_; - } - } - CodeArray(const CodeArray& rhs) - : type_(rhs.type_) - , allocPtr_(0) - , maxSize_(rhs.maxSize_) - , top_(buf_) - , size_(rhs.size_) - { - if (type_ != FIXED_BUF) throw ERR_CODE_ISNOT_COPYABLE; - for (size_t i = 0; i < size_; i++) top_[i] = rhs.top_[i]; - } - void db(int code) - { - if (size_ >= maxSize_) throw ERR_CODE_IS_TOO_BIG; - top_[size_++] = static_cast(code); - } - void db(const uint8 *code, int codeSize) - { - for (int i = 0; i < codeSize; i++) db(code[i]); - } - void db(uint64 code, int codeSize) - { - if (codeSize > 8) throw ERR_BAD_PARAMETER; - for (int i = 0; i < codeSize; i++) db(static_cast(code >> (i * 8))); - } - void dw(uint32 code) { db(code, 2); } - void dd(uint32 code) { db(code, 4); } - const uint8 *getCode() const { return top_; } - const uint8 *getCurr() const { return &top_[size_]; } - size_t getSize() const { return size_; } - void dump() const - { - const uint8 *p = getCode(); - size_t bufSize = getSize(); - size_t remain = bufSize; - for (int i = 0; i < 4; i++) { - size_t disp = 16; - if (remain < 16) { - disp = remain; - } - for (size_t j = 0; j < 16; j++) { - if (j < disp) { - printf("%02X", p[i * 16 + j]); - } - } - putchar('\n'); - remain -= disp; - if (remain <= 0) { - break; - } - } - } - /* - @param data [in] address of jmp data - @param disp [in] offset from the next of jmp - @param isShort [in] true if short jmp - */ - void rewrite(uint8 *data, uint32 disp, bool isShort) - { - if (isShort) { - data[0] = static_cast(disp); - } else { - data[0] = static_cast(disp); - data[1] = static_cast(disp >> 8); - data[2] = static_cast(disp >> 16); - data[3] = static_cast(disp >> 24); - } - } - /** - change exec permission of memory - @param addr [in] buffer address - @param size [in] buffer size - @param canExec [in] true(enable to exec), false(disable to exec) - @return true(success), false(failure) - */ - static inline bool protect(const void *addr, size_t size, bool canExec) - { -#ifdef __GNUC__ - size_t pageSize = sysconf(_SC_PAGESIZE); - size_t iaddr = reinterpret_cast(addr); - size_t roundAddr = iaddr & ~(pageSize - static_cast(1)); - int mode = PROT_READ | PROT_WRITE | (canExec ? PROT_EXEC : 0); - return mprotect(reinterpret_cast(roundAddr), size + (iaddr - roundAddr), mode) == 0; -#elif defined(_WIN32) - DWORD oldProtect; - return VirtualProtect(const_cast(addr), size, canExec ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE, &oldProtect) != 0; -#else - return true; -#endif - } - /** - get aligned memory pointer - @param addr [in] address - @param alingedSize [in] power of two - @return aligned addr by alingedSize - */ - static inline uint8 *getAlignedAddress(uint8 *addr, size_t alignedSize = ALIGN_SIZE) - { - return reinterpret_cast((reinterpret_cast(addr) + alignedSize - 1) & ~(alignedSize - static_cast(1))); - } -}; - -class Address : public Operand, public CodeArray { - void operator=(const Address&); - uint64 disp_; - bool isOnlyDisp_; - bool is64bitDisp_; - uint8 rex_; -public: - const bool is32bit_; - Address(uint32 sizeBit, bool isOnlyDisp, uint64 disp, bool is32bit, bool is64bitDisp = false) - : Operand(0, MEM, sizeBit) - , CodeArray(6) // 6 = 1(ModRM) + 1(SIB) + 4(disp) - , disp_(disp) - , isOnlyDisp_(isOnlyDisp) - , is64bitDisp_(is64bitDisp) - , rex_(0) - , is32bit_(is32bit) - { - } - bool isOnlyDisp() const { return isOnlyDisp_; } // for mov eax - uint64 getDisp() const { return disp_; } - uint8 getRex() const { return rex_; } - bool is64bitDisp() const { return is64bitDisp_; } // for moffset -#ifdef XBYAK64 - void setRex(uint8 rex) { rex_ = rex; } -#else - void setRex(uint8) { } -#endif -}; - -class AddressFrame { -private: - void operator=(const AddressFrame&); -public: - const uint32 bit_; - explicit AddressFrame(uint32 bit) : bit_(bit) { } - Address operator[](const void *disp) const - { - Reg32e r(Reg(), Reg(), 0, inner::GetPtrDist(disp)); - return operator[](r); - } -#ifdef XBYAK64 - Address operator[](uint64 disp) const - { - return Address(64, true, disp, false, true); - } - Address operator[](const RegRip& addr) const - { - Address frame(64, true, addr.disp_, false); - frame.db(B00000101); - frame.dd(addr.disp_); - return frame; - } -#endif - Address operator[](const Reg32e& in) const - { - const Reg32e& r = in.optimize(); - Address frame(bit_, (r.isNone() && r.index_.isNone()), r.disp_, r.isBit(32) || r.index_.isBit(32)); - enum { - mod00 = 0, mod01 = 1, mod10 = 2 - }; - int mod; - if (r.isNone() || ((r.getIdx() & 7) != Operand::EBP && r.disp_ == 0)) { - mod = mod00; - } else if (inner::IsInDisp8(r.disp_)) { - mod = mod01; - } else { - mod = mod10; - } - const int base = r.isNone() ? Operand::EBP : (r.getIdx() & 7); - /* ModR/M = [2:3:3] = [Mod:reg/code:R/M] */ - bool hasSIB = !r.index_.isNone() || (r.getIdx() & 7) == Operand::ESP; -#ifdef XBYAK64 - if (r.isNone() && r.index_.isNone()) hasSIB = true; -#endif - if (!hasSIB) { - frame.db((mod << 6) | base); - } else { - frame.db((mod << 6) | Operand::ESP); - /* SIB = [2:3:3] = [SS:index:base(=rm)] */ - int index = r.index_.isNone() ? Operand::ESP : (r.index_.getIdx() & 7); - int ss = (r.scale_ == 8) ? 3 : (r.scale_ == 4) ? 2 : (r.scale_ == 2) ? 1 : 0; - frame.db((ss << 6) | (index << 3) | base); - } - if (mod == mod01) { - frame.db(r.disp_); - } else if (mod == mod10 || (mod == mod00 && r.isNone())) { - frame.dd(r.disp_); - } - frame.setRex(Reg().getRex(r.index_, r)); - return frame; - } -}; - -struct JmpLabel { - uint8 *endOfJmp; /* end address of jmp */ - bool isShort; -}; - -class Label { - CodeArray *base_; - int anonymousCount_; // for @@, @f, @b - int localCount_; // for .*** - typedef std::map DefinedList; - typedef std::multimap UndefinedList; - DefinedList definedList_; - UndefinedList undefinedList_; - - /* - @@ --> @@. - @b --> @@. - @f --> @@. - .*** -> .***. - */ - std::string convertLabel(const char *label) const - { - std::string newLabel(label); - if (newLabel == "@f" || newLabel == "@F") { - newLabel = std::string("@@") + toStr(anonymousCount_ + 1); - } else if (newLabel == "@b" || newLabel == "@B") { - newLabel = std::string("@@") + toStr(anonymousCount_); - } else if (*label == '.') { - newLabel += toStr(localCount_); - } - return newLabel; - } -public: - Label() - : base_(0) - , anonymousCount_(0) - , localCount_(0) - { - } - void incLocalCount() { localCount_++; } - void decLocalCount() { localCount_--; } - void set(CodeArray *base) - { - base_ = base; - } - void define(const char *label, const uint8 *address) - { - std::string newLabel(label); - if (newLabel == "@@") { - newLabel += toStr(++anonymousCount_); - } else if (*label == '.') { - newLabel += toStr(localCount_); - } - label = newLabel.c_str(); - // add label - DefinedList::value_type item(label, address); - std::pair ret = definedList_.insert(item); - if (!ret.second) throw ERR_LABEL_IS_REDEFINED; - // search undefined label - for (;;) { - UndefinedList::iterator itr = undefinedList_.find(label); - if (itr == undefinedList_.end()) break; - const JmpLabel *jmp = &itr->second; - uint32 disp = inner::GetPtrDist(address, jmp->endOfJmp); - if (jmp->isShort && !inner::IsInDisp8(disp)) throw ERR_LABEL_IS_TOO_FAR; - uint8 *data = jmp->endOfJmp - (jmp->isShort ? 1 : 4); - base_->rewrite(data, disp, jmp->isShort); - undefinedList_.erase(itr); - } - } - const uint8 *getAddress(const char *label) const - { - std::string newLabel = convertLabel(label); - DefinedList::const_iterator itr = definedList_.find(newLabel); - if (itr != definedList_.end()) { - return itr->second; - } else { - return 0; - } - } - void addUndefinedLabel(const char *label, const JmpLabel& jmp) - { - std::string newLabel = convertLabel(label); - undefinedList_.insert(UndefinedList::value_type(newLabel, jmp)); - } - bool hasUndefinedLabel() const - { - if (inner::debug) { - for (UndefinedList::const_iterator i = undefinedList_.begin(); i != undefinedList_.end(); ++i) { - fprintf(stderr, "undefined label:%s\n", i->first.c_str()); - } - } - return !undefinedList_.empty(); - } - static inline std::string toStr(int num) - { - char buf[16]; - static const char fmt[] = ".%08x"; -#ifdef _WIN32 - #if _MSC_VER < 1400 - _snprintf(buf, sizeof(buf), fmt, num); - #else - _snprintf_s(buf, sizeof(buf), fmt, num); - #endif -#else - snprintf(buf, sizeof(buf), fmt, num); -#endif - return buf; - } -}; - -class CodeGenerator : public CodeArray { -protected: - enum LabelType { - T_SHORT, - T_NEAR, - T_AUTO // T_SHORT if possible - }; -private: - CodeGenerator operator=(const CodeGenerator&); // don't call -#ifdef XBYAK64 - enum { i32e = 32 | 64, BIT = 64 }; -#else - enum { i32e = 32, BIT = 32 }; -#endif - // (XMM, XMM|MEM) - static inline bool isXMM_XMMorMEM(const Operand& op1, const Operand& op2) - { - return op1.isXMM() && (op2.isXMM() || op2.isMEM()); - } - // (MMX, MMX|MEM) or (XMM, XMM|MEM) - static inline bool isXMMorMMX_MEM(const Operand& op1, const Operand& op2) - { - return (op1.isMMX() && (op2.isMMX() || op2.isMEM())) || isXMM_XMMorMEM(op1, op2); - } - // (XMM, MMX|MEM) - static inline bool isXMM_MMXorMEM(const Operand& op1, const Operand& op2) - { - return op1.isXMM() && (op2.isMMX() || op2.isMEM()); - } - // (MMX, XMM|MEM) - static inline bool isMMX_XMMorMEM(const Operand& op1, const Operand& op2) - { - return op1.isMMX() && (op2.isXMM() || op2.isMEM()); - } - // (XMM, REG32|MEM) - static inline bool isXMM_REG32orMEM(const Operand& op1, const Operand& op2) - { - return op1.isXMM() && (op2.isREG(i32e) || op2.isMEM()); - } - // (REG32, XMM|MEM) - static inline bool isREG32_XMMorMEM(const Operand& op1, const Operand& op2) - { - return op1.isREG(i32e) && (op2.isXMM() || op2.isMEM()); - } - void if16bit(const Operand& reg1, const Operand& reg2) - { - // except movsx(16bit, 32/64bit) - if ((reg1.isBit(16) && !reg2.isBit(i32e)) || (reg2.isBit(16) && !reg1.isBit(i32e))) db(0x66); - } - void rexAddr(const Address& addr, const Reg& reg = Reg()) - { -#ifdef XBYAK64 - if (addr.is32bit_) db(0x67); -#endif - if16bit(reg, addr); - uint32 rex = addr.getRex() | reg.getRex(); - if (reg.isREG(64)) rex |= 0x48; - if (rex) db(rex); - } - void rex(const Operand& op1, const Operand& op2 = Operand()) - { - if (op1.isMEM()) { - rexAddr(static_cast(op1), static_cast(op2)); - } else if (op2.isMEM()) { - rexAddr(static_cast(op2), static_cast(op1)); - } else { - const Reg& reg1 = static_cast(op1); - const Reg& reg2 = static_cast(op2); - // ModRM(reg, base); - if16bit(reg1, reg2); - uint8 rex = reg2.getRex(Reg(), reg1); - if (reg1.isREG(64) || reg2.isREG(64)) rex |= 0x48; - if (rex) db(rex); - } - } - Label label_; - bool isInDisp16(uint32 x) const { return 0xFFFF8000 <= x || x <= 0x7FFF; } - uint8 getModRM(int mod, int r1, int r2) const { return static_cast((mod << 6) | ((r1 & 7) << 3) | (r2 & 7)); } - void opModR(const Reg& reg1, const Reg& reg2, int code0, int code1 = NONE, int code2 = NONE) - { - rex(reg2, reg1); - db(code0 | (reg1.isBit(8) ? 0 : 1)); if (code1 != NONE) db(code1); if (code2 != NONE) db(code2); - db(getModRM(3, reg1.getIdx(), reg2.getIdx())); - } - void opModM(const Address& addr, const Reg& reg, int code0, int code1 = NONE, int code2 = NONE) - { - if (addr.is64bitDisp()) throw ERR_CANT_USE_64BIT_DISP; - rex(addr, reg); - db(code0 | (reg.isBit(8) ? 0 : 1)); if (code1 != NONE) db(code1); if (code2 != NONE) db(code2); - uint8 t = *addr.getCode(); - assert((t & ~0xC7) == 0); /* 0b11000111 */ - db(t | ((reg.getIdx() & 7) << 3)); // update reg field - db(addr.getCode() + 1, static_cast(addr.getSize()) - 1); - } - void opJmp(const char *label, LabelType type, uint8 shortCode, uint8 longCode, uint8 longPref) - { - const uint8 *address = label_.getAddress(label); - if (address) { /* label exists */ - opJmp(address, type, shortCode, longCode, longPref); - } else { - const int shortHeaderSize = 1; - const int shortJmpSize = shortHeaderSize + 1; /* +1 means 8-bit displacement */ - const int longHeaderSize = longPref ? 2 : 1; - const int longJmpSize = longHeaderSize + 4; /* +4 means 32-bit displacement */ - uint8 *top = const_cast(getCurr()); - bool isShort = (type != T_NEAR); - JmpLabel jmp; - jmp.endOfJmp = top + (isShort ? shortJmpSize : longJmpSize); - jmp.isShort = isShort; - if (isShort) { - db(shortCode); - db(0); - } else { - if (longPref) db(longPref); - db(longCode); - dd(0); - } - label_.addUndefinedLabel(label, jmp); - } - } - void opJmp(const void *addr, LabelType type, uint8 shortCode, uint8 longCode, uint8 longPref) - { - const int shortHeaderSize = 1; - const int shortJmpSize = shortHeaderSize + 1; /* +1 means 8-bit displacement */ - const int longHeaderSize = longPref ? 2 : 1; - const int longJmpSize = longHeaderSize + 4; /* +4 means 32-bit displacement */ - - uint8 *top = const_cast(getCurr()); - uint32 disp = inner::GetPtrDist(addr, top); - if (type != T_NEAR && inner::IsInDisp8(disp - shortJmpSize)) { - db(shortCode); - db(0); - rewrite(top + shortHeaderSize, disp - shortJmpSize, true); - } else { - if (type == T_SHORT) throw ERR_LABEL_IS_TOO_FAR; - if (longPref) db(longPref); - db(longCode); - dd(0); - rewrite(top + longHeaderSize, disp - longJmpSize, false); - } - } - /* preCode is for SSSE3/SSE4 */ - void opGen(const Operand& reg, const Operand& op, int code, int pref, bool isValid(const Operand&, const Operand&), int imm8 = NONE, int preCode = NONE) - { - if (isValid && !isValid(reg, op)) throw ERR_BAD_COMBINATION; - if (pref != NONE) db(pref); - if (op.isMEM()) { - opModM(static_cast(op), static_cast(reg), 0x0F, preCode, code); - } else { - opModR(static_cast(reg), static_cast(op), 0x0F, preCode, code); - } - if (imm8 != NONE) db(imm8); - } - void opMMX_IMM(const Mmx& mmx, int imm8, int code, int ext) - { - if (mmx.isXMM()) db(0x66); - opModR(Reg32(ext), mmx, 0x0F, code); - db(imm8); - } - void opMMX(const Mmx& mmx, const Operand& op, int code, int pref = 0x66, int imm8 = NONE, int preCode = NONE) - { - pref = mmx.isXMM() ? pref : NONE; - opGen(mmx, op, code, pref, isXMMorMMX_MEM, imm8, preCode); - } - void opMovXMM(const Operand& op1, const Operand& op2, int code, int pref) - { - if (pref != NONE) db(pref); - if (op1.isXMM() && op2.isMEM()) { - opModM(static_cast(op2), static_cast(op1), 0x0F, code); - } else if (op1.isMEM() && op2.isXMM()) { - opModM(static_cast(op1), static_cast(op2), 0x0F, code | 1); - } else { - throw ERR_BAD_COMBINATION; - } - } - void opExt(const Operand& op, const Mmx& mmx, int code, int imm, bool hasMMX2 = false) - { - if (hasMMX2 && op.isREG(i32e)) { /* pextrw is special */ - if (mmx.isXMM()) db(0x66); - opModR(static_cast(op), mmx, 0x0F, B11000101); db(imm); - } else { - opGen(mmx, op, code, 0x66, isXMM_REG32orMEM, imm, B00111010); - } - } - void opR_ModM(const Operand& op, int bit, uint8 mod, int ext, int code0, int code1 = NONE, int code2 = NONE) - { - if (op.isREG(bit)) { - rex(op); - db(code0 | (op.isBit(8) ? 0 : 1)); if (code1 != NONE) db(code1); if (code2 != NONE) db(code2); - db(getModRM(mod, ext, op.getIdx())); - } else if (op.isMEM()) { - opModM(static_cast(op), Reg(ext, Operand::REG, op.getBit()), code0, code1, code2); - } else { - throw ERR_BAD_COMBINATION; - } - } - void opShift(const Operand& op, int imm, int ext) - { - verifyMemHasSize(op); - opR_ModM(op, 0, 3, ext, (B11000000 | ((imm == 1 ? 1 : 0) << 4))); - if (imm != 1) db(imm); - } - void opShift(const Operand& op, const Reg8& cl, int ext) - { - if (cl.getIdx() != Operand::CL) throw ERR_BAD_COMBINATION; - opR_ModM(op, 0, 3, ext, B11010010); - } - void opModRM(const Operand& op1, const Operand& op2, bool condR, bool condM, int code0, int code1 = NONE, int code2 = NONE) - { - if (condR) { - opModR(static_cast(op1), static_cast(op2), code0, code1, code2); - } else if (condM) { - opModM(static_cast(op2), static_cast(op1), code0, code1, code2); - } else { - throw ERR_BAD_COMBINATION; - } - } - void opShxd(const Operand& op, const Reg& reg, uint8 imm, int code, const Reg8 *cl = 0) - { - if (cl && cl->getIdx() != Operand::CL) throw ERR_BAD_COMBINATION; - opModRM(reg, op, (op.isREG(16 | i32e) && op.getBit() == reg.getBit()), op.isMEM() && (reg.isREG(16 | i32e)), 0x0F, code | (cl ? 1 : 0)); - if (!cl) db(imm); - } - // (REG, REG|MEM), (MEM, REG) - void opRM_RM(const Operand& op1, const Operand& op2, int code) - { - if (op1.isREG() && op2.isMEM()) { - opModM(static_cast(op2), static_cast(op1), code | 2); - } else { - opModRM(op2, op1, op1.isREG() && op1.getKind() == op2.getKind(), op1.isMEM() && op2.isREG(), code); - } - } - // (REG|MEM, IMM) - void opRM_I(const Operand& op, uint32 imm, int code, int ext) - { - verifyMemHasSize(op); - uint32 immBit = inner::IsInDisp8(imm) ? 8 : isInDisp16(imm) ? 16 : 32; - if (op.getBit() < immBit) throw ERR_IMM_IS_TOO_BIG; - if (op.isREG()) { - if (immBit == 16 && op.isBit(32)) immBit = 32; /* don't use MEM16 if 32bit mode */ - } - if (op.isREG() && op.getIdx() == 0 && (op.getBit() == immBit || (op.isBit(64) && immBit == 32))) { // rax, eax, ax, al - rex(op); - db(code | 4 | (immBit == 8 ? 0 : 1)); - } else { - int tmp = (op.getBit() > immBit && 32 > immBit) ? 2 : 0; - opR_ModM(op, 0, 3, ext, B10000000 | tmp); - } - db(imm, immBit / 8); - } - void opIncDec(const Operand& op, int code, int ext) - { -#ifndef XBYAK64 - if (op.isREG() && !op.isBit(8)) { - rex(op); db(code | op.getIdx()); - return; - } -#endif - code = B11111110; - if (op.isREG()) { - opModR(Reg(ext, Operand::REG, op.getBit()), static_cast(op), code); - } else if (op.isMEM() && op.getBit() > 0) { - opModM(static_cast(op), Reg(ext, Operand::REG, op.getBit()), code); - } else { - throw ERR_BAD_COMBINATION; - } - } - void opPushPop(const Operand& op, int code, int ext, int alt) - { - if (op.isREG()) { -#ifdef XBYAK64 - if (op.isBit(16)) db(0x66); - if (static_cast(op).getIdx() >= 8) db(0x41); -#else - rex(op); -#endif - db(alt | (op.getIdx() & 7)); - } else if (op.isMEM()) { - opModM(static_cast(op), Reg(ext, Operand::REG, op.getBit()), code); - } else { - throw ERR_BAD_COMBINATION; - } - } - void verifyMemHasSize(const Operand& op) const - { - if (op.isMEM() && op.getBit() == 0) throw ERR_MEM_SIZE_IS_NOT_SPECIFIED; - } -protected: - unsigned int getVersion() const { return VERSION; } - using CodeArray::db; - const Mmx mm0, mm1, mm2, mm3, mm4, mm5, mm6, mm7; - const Xmm xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7; - const Xmm &xm0, &xm1, &xm2, &xm3, &xm4, &xm5, &xm6, &xm7; - const Reg32 eax, ecx, edx, ebx, esp, ebp, esi, edi; - const Reg16 ax, cx, dx, bx, sp, bp, si, di; - const Reg8 al, cl, dl, bl, ah, ch, dh, bh; - const AddressFrame ptr, byte, word, dword, qword, xmmword; -#ifdef XBYAK64 - const Reg64 rax, rcx, rdx, rbx, rsp, rbp, rsi, rdi, r8, r9, r10, r11, r12, r13, r14, r15; - const Reg32 r8d, r9d, r10d, r11d, r12d, r13d, r14d, r15d; - const Reg16 r8w, r9w, r10w, r11w, r12w, r13w, r14w, r15w; - const Reg8 r8b, r9b, r10b, r11b, r12b, r13b, r14b, r15b; - const Reg8 spl, bpl, sil, dil; - const Xmm xmm8, xmm9, xmm10, xmm11, xmm12, xmm13, xmm14, xmm15; - const Xmm &xm8, &xm9, &xm10, &xm11, &xm12, &xm13, &xm14, &xm15; - const RegRip rip; -#endif - - void L(const char *label) - { - label_.define(label, getCurr()); - } - void inLocalLabel() { label_.incLocalCount(); } - void outLocalLabel() { label_.decLocalCount(); } - void jmp(const char *label, LabelType type = T_AUTO) - { - opJmp(label, type, B11101011, B11101001, 0); - } - void jmp(const void *addr, LabelType type = T_AUTO) - { - opJmp(addr, type, B11101011, B11101001, 0); - } - void jmp(const Operand& op) - { - opR_ModM(op, i32e, 3, 4, 0xFF); - } - // (REG|MEM, REG) - void test(const Operand& op, const Reg& reg) - { - opModRM(reg, op, op.isREG() && (op.getKind() == reg.getKind()), op.isMEM(), B10000100); - } - // (REG|MEM, IMM) - void test(const Operand& op, uint32 imm) - { - verifyMemHasSize(op); - if (op.isREG() && op.getIdx() == 0) { // al, ax, eax - rex(op); - db(B10101000 | (op.isBit(8) ? 0 : 1)); - } else { - opR_ModM(op, 0, 3, 0, B11110110); - } - int size = op.getBit() / 8; if (size > 4) size = 4; - db(imm, size); - } - void ret(int imm = 0) - { - if (imm) { - db(B11000010); dw(imm); - } else { - db(B11000011); - } - } - // (REG16|REG32, REG16|REG32|MEM) - void imul(const Reg& reg, const Operand& op) - { - opModRM(reg, op, op.isREG() && (reg.getKind() == op.getKind()), op.isMEM(), 0x0F, B10101111); - } - void imul(const Reg& reg, const Operand& op, int imm) - { - int s = inner::IsInDisp8(imm) ? 1 : 0; - opModRM(reg, op, op.isREG() && (reg.getKind() == op.getKind()), op.isMEM(), B01101001 | (s << 1)); - int size = s ? 1 : reg.isREG(16) ? 2 : 4; - db(imm, size); - } - void pop(const Operand& op) - { - opPushPop(op, B10001111, 0, B01011000); - } - void push(const Operand& op) - { - opPushPop(op, B11111111, 6, B01010000); - } - void push(const AddressFrame& af, uint32 imm) - { - if (af.bit_ == 8 && inner::IsInDisp8(imm)) { - db(B01101010); db(imm); - } else if (af.bit_ == 16 && isInDisp16(imm)) { - db(0x66); db(B01101000); dw(imm); - } else { - db(B01101000); dd(imm); - } - } - /* use "push(word, 4)" if you want "push word 4" */ - void push(uint32 imm) - { - if (inner::IsInDisp8(imm)) { - push(byte, imm); - } else { - push(dword, imm); - } - } - void bswap(const Reg32e& reg) - { - opModR(Reg32(1), reg, 0x0F); - } - void mov(const Operand& reg1, const Operand& reg2) - { - const Reg *reg = 0; - const Address *addr = 0; - uint8 code = 0; - if (reg1.isREG() && reg1.getIdx() == 0 && reg2.isMEM()) { // mov eax|ax|al, [disp] - reg = &static_cast(reg1); - addr= &static_cast(reg2); - code = B10100000; - } else - if (reg1.isMEM() && reg2.isREG() && reg2.getIdx() == 0) { // mov [disp], eax|ax|al - reg = &static_cast(reg2); - addr= &static_cast(reg1); - code = B10100010; - } -#ifdef XBYAK64 - if (addr && addr->is64bitDisp()) { - if (code) { - rex(*reg); - db(reg1.isREG(8) ? 0xA0 : reg1.isREG() ? 0xA1 : reg2.isREG(8) ? 0xA2 : 0xA3); - db(addr->getDisp(), 8); - } else { - throw ERR_BAD_COMBINATION; - } - } else -#else - if (code && addr->isOnlyDisp()) { - rex(*reg, *addr); - db(code | (reg->isBit(8) ? 0 : 1)); - dd(static_cast(addr->getDisp())); - } else -#endif - { - opRM_RM(reg1, reg2, B10001000); - } - } - void mov(const Operand& op, uint64 imm) - { - verifyMemHasSize(op); - if (op.isREG()) { - int w = op.isBit(8) ? 0 : 1; - rex(op); db(B10110000 | (w << 3) | (op.getIdx() & 7)); - } else if (op.isMEM()) { - opModM(static_cast(op), Reg(0, Operand::REG, op.getBit()), B11000110); - } else { - throw ERR_BAD_COMBINATION; - } - db(imm, op.getBit() / 8); - } - void opMovxx(const Reg& reg, const Operand& op, uint8 code) - { - int w = op.isBit(16); - bool cond = reg.isREG() && (reg.getBit() > op.getBit()); - opModRM(reg, op, cond && op.isREG(), cond && op.isMEM(), 0x0F, code | w); - } - void cmpxchg8b(const Address& addr) { opModM(addr, Reg32(1), 0x0F, B11000111); } -#ifdef XBYAK64 - void cmpxchg16b(const Address& addr) { opModM(addr, Reg64(1), 0x0F, B11000111); } -#endif - void xadd(const Operand& op, const Reg& reg) - { - opModRM(reg, op, (op.isREG() && reg.isREG() && op.getBit() == reg.getBit()), op.isMEM(), 0x0F, B11000000 | (reg.isBit(8) ? 0 : 1)); - } - void xchg(const Operand& op1, const Operand& op2) - { - const Operand *p1 = &op1, *p2 = &op2; - if (p1->isMEM() || (p2->isREG(16 | i32e) && p2->getIdx() == 0)) { - p1 = &op2; p2 = &op1; - } - if (p1->isMEM()) throw ERR_BAD_COMBINATION; - if (p2->isREG() && (p1->isREG(16 | i32e) && p1->getIdx() == 0) -#ifdef XBYAK64 - && (p2->getIdx() != 0 || !p1->isREG(32)) -#endif - ) { - rex(*p2, *p1); db(0x90 | (p2->getIdx() & 7)); - return; - } - opModRM(*p1, *p2, (p1->isREG() && p2->isREG() && (p1->getBit() == p2->getBit())), p2->isMEM(), B10000110 | (p1->isBit(8) ? 0 : 1)); - } - void call(const char *label) - { - opJmp(label, T_NEAR, 0, B10011010, 0); - } - void call(const void *addr) - { - opJmp(addr, T_NEAR, 0, B11101000, 0); - } - void call(const Operand& op) - { - opR_ModM(op, 16 | i32e, 3, 2, B11111111); - } - // special case - void movd(const Address& addr, const Mmx& mmx) - { - opModM(addr, Reg(mmx.getIdx(), Operand::REG, mmx.getBit() / 8), 0x0F, B01111110); - } - void movd(const Reg32& reg, const Mmx& mmx) - { - if (mmx.isXMM()) db(0x66); - opModR(mmx, reg, 0x0F, B01111110); - } - void movd(const Mmx& mmx, const Address& addr) - { - ASSERT(!addr.isBit(32)); // don't use dword ptr, bogus, won't output 0x66 for xmm dest op - opModM(addr, Reg(mmx.getIdx(), Operand::REG, mmx.getBit() / 8), 0x0F, B01101110); - } - void movd(const Mmx& mmx, const Reg32& reg) - { - if (mmx.isXMM()) db(0x66); - opModR(mmx, reg, 0x0F, B01101110); - } - void movq2dq(const Xmm& xmm, const Mmx& mmx) - { - db(0xF3); opModR(xmm, mmx, 0x0F, B11010110); - } - void movdq2q(const Mmx& mmx, const Xmm& xmm) - { - db(0xF2); opModR(mmx, xmm, 0x0F, B11010110); - } - void movq(const Mmx& mmx, const Operand& op) - { - if (mmx.isXMM()) db(0xF3); - opModRM(mmx, op, (mmx.getKind() == op.getKind()), op.isMEM(), 0x0F, mmx.isXMM() ? B01111110 : B01101111); - } - void movq(const Address& addr, const Mmx& mmx) - { - opModM(addr, Reg(mmx.getIdx(), Operand::REG, mmx.getBit() / 8), 0x0F, mmx.isXMM() ? B11010110 : B01111111); - } - // MMX2 : pextrw : reg, mmx/xmm, imm - // SSE4 : pextrw, pextrb, pextrd, extractps : reg/mem, mmx/xmm, imm - void pextrw(const Operand& op, const Mmx& xmm, uint8 imm) { opExt(op, xmm, 0x15, imm, true); } - void pextrb(const Operand& op, const Xmm& xmm, uint8 imm) { opExt(op, xmm, 0x14, imm); } - void pextrd(const Operand& op, const Xmm& xmm, uint8 imm) { opExt(op, xmm, 0x16, imm); } - void extractps(const Operand& op, const Xmm& xmm, uint8 imm) { opExt(op, xmm, 0x17, imm); } - void pinsrw(const Mmx& mmx, const Operand& op, int imm) - { - if (!op.isREG(32) && !op.isMEM()) throw ERR_BAD_COMBINATION; - opGen(mmx, op, B11000100, mmx.isXMM() ? 0x66 : NONE, 0, imm); - } - void insertps(const Xmm& xmm, const Operand& op, uint8 imm) { opGen(xmm, op, 0x21, 0x66, isXMM_XMMorMEM, imm, B00111010); } - void pinsrb(const Xmm& xmm, const Operand& op, uint8 imm) { opGen(xmm, op, 0x20, 0x66, isXMM_REG32orMEM, imm, B00111010); } - void pinsrd(const Xmm& xmm, const Operand& op, uint8 imm) { opGen(xmm, op, 0x22, 0x66, isXMM_REG32orMEM, imm, B00111010); } - - void pmovmskb(const Reg32e& reg, const Mmx& mmx) - { - if (mmx.isXMM()) db(0x66); - opModR(reg, mmx, 0x0F, B11010111); - } - void maskmovq(const Mmx& reg1, const Mmx& reg2) - { - if (!reg1.isMMX() || !reg2.isMMX()) throw ERR_BAD_COMBINATION; - opModR(reg1, reg2, 0x0F, B11110111); - } - void lea(const Reg32e& reg, const Address& addr) { opModM(addr, reg, B10001101); } - - void movmskps(const Reg32e& reg, const Xmm& xmm) { opModR(reg, xmm, 0x0F, B01010000); } - void movmskpd(const Reg32e& reg, const Xmm& xmm) { db(0x66); movmskps(reg, xmm); } - void movntps(const Address& addr, const Xmm& xmm) { opModM(addr, Mmx(xmm.getIdx()), 0x0F, B00101011); } - void movntdqa(const Xmm& xmm, const Address& addr) { db(0x66); opModM(addr, xmm, 0x0F, 0x38, 0x2A); } - void lddqu(const Xmm& xmm, const Address& addr) { db(0xF2); opModM(addr, xmm, 0x0F, B11110000); } - void movnti(const Address& addr, const Reg32e& reg) { opModM(addr, reg, 0x0F, B11000011); } - void movntq(const Address& addr, const Mmx& mmx) - { - if (!mmx.isMMX()) throw ERR_BAD_COMBINATION; - opModM(addr, mmx, 0x0F, B11100111); - } - void popcnt(const Reg& reg, const Operand& op) - { - bool is16bit = reg.isREG(16) && (op.isREG(16) || op.isMEM()); - if (!is16bit && !(reg.isREG(i32e) && (op.isREG(i32e) || op.isMEM()))) throw ERR_BAD_COMBINATION; - if (is16bit) db(0x66); - db(0xF3); opModRM(Reg(reg.getIdx(), Operand::REG, i32e == 32 ? 32 : reg.getBit()), op, op.isREG(), true, 0x0F, 0xB8); - } - void crc32(const Reg32e& reg, const Operand& op) - { - if (reg.isBit(32) && op.isBit(16)) db(0x66); - db(0xF2); - opModRM(reg, op, op.isREG(), op.isMEM(), 0x0F, 0x38, 0xF0 | (op.isBit(8) ? 0 : 1)); - } -public: - enum { NONE = 256 }; - CodeGenerator(size_t maxSize = DEFAULT_MAX_CODE_SIZE, void *userPtr = 0) - : CodeArray(maxSize, userPtr) - , mm0(0), mm1(1), mm2(2), mm3(3), mm4(4), mm5(5), mm6(6), mm7(7) - , xmm0(0), xmm1(1), xmm2(2), xmm3(3), xmm4(4), xmm5(5), xmm6(6), xmm7(7) - , xm0(xmm0), xm1(xmm1), xm2(xmm2), xm3(xmm3), xm4(xmm4), xm5(xmm5), xm6(xmm6), xm7(xmm7) // for my convenience - , eax(Operand::EAX), ecx(Operand::ECX), edx(Operand::EDX), ebx(Operand::EBX), esp(Operand::ESP), ebp(Operand::EBP), esi(Operand::ESI), edi(Operand::EDI) - , ax(Operand::EAX), cx(Operand::ECX), dx(Operand::EDX), bx(Operand::EBX), sp(Operand::ESP), bp(Operand::EBP), si(Operand::ESI), di(Operand::EDI) - , al(Operand::AL), cl(Operand::CL), dl(Operand::DL), bl(Operand::BL), ah(Operand::AH), ch(Operand::CH), dh(Operand::DH), bh(Operand::BH) - , ptr(0), byte(8), word(16), dword(32), qword(64), xmmword(128) -#ifdef XBYAK64 - , rax(Operand::RAX), rcx(Operand::RCX), rdx(Operand::RDX), rbx(Operand::RBX), rsp(Operand::RSP), rbp(Operand::RBP), rsi(Operand::RSI), rdi(Operand::RDI), r8(Operand::R8), r9(Operand::R9), r10(Operand::R10), r11(Operand::R11), r12(Operand::R12), r13(Operand::R13), r14(Operand::R14), r15(Operand::R15) - , r8d(Operand::R8D), r9d(Operand::R9D), r10d(Operand::R10D), r11d(Operand::R11D), r12d(Operand::R12D), r13d(Operand::R13D), r14d(Operand::R14D), r15d(Operand::R15D) - , r8w(Operand::R8W), r9w(Operand::R9W), r10w(Operand::R10W), r11w(Operand::R11W), r12w(Operand::R12W), r13w(Operand::R13W), r14w(Operand::R14W), r15w(Operand::R15W) - , r8b(Operand::R8B), r9b(Operand::R9B), r10b(Operand::R10B), r11b(Operand::R11B), r12b(Operand::R12B), r13b(Operand::R13B), r14b(Operand::R14B), r15b(Operand::R15B) - , spl(Operand::SPL, 1), bpl(Operand::BPL, 1), sil(Operand::SIL, 1), dil(Operand::DIL, 1) - , xmm8(8), xmm9(9), xmm10(10), xmm11(11), xmm12(12), xmm13(13), xmm14(14), xmm15(15) - , xm8(xmm8), xm9(xmm9), xm10(xmm10), xm11(xmm11), xm12(xmm12), xm13(xmm13), xm14(xmm14), xm15(xmm15) // for my convenience - , rip() -#endif - { - label_.set(this); - } - bool hasUndefinedLabel() const { return label_.hasUndefinedLabel(); } - const uint8 *getCode() const - { - assert(!hasUndefinedLabel()); -// if (hasUndefinedLabel()) throw ERR_LABEL_IS_NOT_FOUND; - return top_; - } -#ifdef TEST_NM - void dump(bool doClear = true) - { - CodeArray::dump(); - if (doClear) size_ = 0; - } -#endif - -#ifndef XBYAK_DONT_READ_LIST -#include "xbyak_mnemonic.h" - void align(int x = 16) - { - if (x != 4 && x != 8 && x != 16 && x != 32) throw ERR_BAD_ALIGN; - while (inner::GetPtrDist(getCurr()) % x) { - nop(); - } - } -#endif -}; - -#ifdef _MSC_VER - #pragma warning(pop) -#endif - -} // end of namespace - -#endif // XBYAK_H_ +#ifndef XBYAK_H_ +#define XBYAK_H_ +/*! + @file xbyak.h + @brief Xbyak ; JIT assembler for x86(IA32)/x64 by C++ + @author herumi + @version $Revision: 1.157 $ + @url http://homepage1.nifty.com/herumi/soft/xbyak.html + @date $Date: 2008/12/30 04:53:11 $ + @note modified new BSD license + http://www.opensource.org/licenses/bsd-license.php +*/ + +#include // for debug print +#include +#include +#include +#ifdef __GNUC__ +#include +#include +#endif + +#ifdef __x86_64__ + #define XBYAK64_GCC +#elif defined(_WIN64) + #define XBYAK64_WIN +#endif +#if !defined(XBYAK64) && !defined(XBYAK32) + #if defined(XBYAK64_GCC) || defined(XBYAK64_WIN) + #define XBYAK64 + #else + #define XBYAK32 + #endif +#endif + +#ifdef _MSC_VER + #pragma warning(push) + #pragma warning(disable : 4514) /* remove inline function */ + #pragma warning(disable : 4786) /* identifier is too long */ + #pragma warning(disable : 4503) /* name is too long */ + #pragma warning(disable : 4127) /* constant expresison */ + #if (_MSC_VER <= 1200) + #ifndef for + #define for if(0);else for + #pragma warning(disable : 4127) /* condition is constant(for "if" trick) */ + #endif + #endif + #include +#endif + +#ifndef NUM_OF_ARRAY +// template +// size_t num_of_array(const T (&)[N]) { return N; } + #define NUM_OF_ARRAY(x) (sizeof(x) / sizeof(*x)) +#endif + +namespace Xbyak { + +#include "xbyak_bin2hex.h" + +enum { + DEFAULT_MAX_CODE_SIZE = 2048, + VERSION = 0x2070, /* 0xABCD = A.BC(D) */ +}; +/* +#ifndef MIE_DEFINED_UINT32 + #define MIE_DEFINED_UINT32 + #ifdef _MSC_VER + typedef unsigned __int64 uint64; + #else + typedef unsigned long long uint64; + #endif + typedef unsigned int uint32; + typedef unsigned short uint16; + typedef unsigned char uint8; + #ifndef MIE_ALIGN + #ifdef _MSC_VER + #define MIE_ALIGN(x) __declspec(align(x)) + #else + #define MIE_ALIGN(x) __attribute__((aligned(x))) + #endif + #endif +#endif +*/ +enum Error { + ERR_NONE = 0, + ERR_BAD_ADDRESSING, + ERR_CODE_IS_TOO_BIG, + ERR_BAD_SCALE, + ERR_ESP_CANT_BE_INDEX, + ERR_BAD_COMBINATION, + ERR_BAD_SIZE_OF_REGISTER, + ERR_IMM_IS_TOO_BIG, + ERR_BAD_ALIGN, + ERR_LABEL_IS_REDEFINED, + ERR_LABEL_IS_TOO_FAR, + ERR_LABEL_IS_NOT_FOUND, + ERR_CODE_ISNOT_COPYABLE, + ERR_BAD_PARAMETER, + ERR_CANT_PROTECT, + ERR_CANT_USE_64BIT_DISP, + ERR_OFFSET_IS_TOO_BIG, + ERR_MEM_SIZE_IS_NOT_SPECIFIED, + ERR_INTERNAL +}; + +static inline const char *ConvertErrorToString(Error err) +{ + static const char errTbl[][40] = { + "none", + "bad addressing", + "code is too big", + "bad scale", + "esp can't be index", + "bad combination", + "bad size of register", + "imm is too big", + "bad align", + "label is redefined", + "label is too far", + "label is not found", + "code is not copyable", + "bad parameter", + "can't protect", + "can't use 64bit disp(use (void*))", + "offset is too big", + "MEM size is not specified", + "internal error", + }; + if (err < 0 || err > ERR_INTERNAL) return 0; + return errTbl[err]; +} + +namespace inner { + +enum { debug = 1 }; + +static inline uint32 GetPtrDist(const void *p1, const void *p2 = 0) +{ + uint64 diff = static_cast(p1) - static_cast(p2); +#ifdef XBYAK64 + if (0x7FFFFFFFULL < diff && diff < 0xFFFFFFFF80000000ULL) throw ERR_OFFSET_IS_TOO_BIG; +#endif + return static_cast(diff); +} + +static inline bool IsInDisp8(uint32 x) { return 0xFFFFFF80 <= x || x <= 0x7F; } + +} + +class Operand { +private: + const uint8 idx_; + const uint8 kind_; + const uint8 bit_; + const uint8 ext8bit_; // 1 if spl/bpl/sil/dil, otherwise 0 + void operator=(Operand&); +public: + enum Kind { + NONE = 0, + MEM = 1 << 1, + IMM = 1 << 2, + REG = 1 << 3, + MMX = 1 << 4, + XMM = 1 << 5, + FPU = 1 << 6 + }; + enum Code { +#ifdef XBYAK64 + RAX = 0, RCX, RDX, RBX, RSP, RBP, RSI, RDI, R8, R9, R10, R11, R12, R13, R14, R15, + R8D = 8, R9D, R10D, R11D, R12D, R13D, R14D, R15D, + R8W = 8, R9W, R10W, R11W, R12W, R13W, R14W, R15W, + R8B = 8, R9B, R10B, R11B, R12B, R13B, R14B, R15B, + SPL = 4, BPL, SIL, DIL, +#endif + EAX = 0, ECX, EDX, EBX, ESP, EBP, ESI, EDI, + AX = 0, CX, DX, BX, SP, BP, SI, DI, + AL = 0, CL, DL, BL, AH, CH, DH, BH + }; + Operand() : idx_(0), kind_(0), bit_(0), ext8bit_(0) { } + Operand(int idx, Kind kind, int bit, int ext8bit = 0) + : idx_(static_cast(idx)) + , kind_(static_cast(kind)) + , bit_(static_cast(bit)) + , ext8bit_(static_cast(ext8bit)) + { + assert((bit_ & (bit_ - 1)) == 0); // bit must be power of two + } + Kind getKind() const { return static_cast(kind_); } + int getIdx() const { return idx_; } + bool isNone() const { return kind_ == 0; } + bool isMMX() const { return is(MMX); } + bool isXMM() const { return is(XMM); } + bool isREG(int bit = 0) const { return is(REG, bit); } + bool isMEM(int bit = 0) const { return is(MEM, bit); } + bool isExt8bit() const { return ext8bit_ != 0; } + Operand changeBit(int bit) const { return Operand(idx_, static_cast(kind_), bit, ext8bit_); } + // any bit is accetable if bit == 0 + bool is(int kind, uint32 bit = 0) const + { + return (kind_ & kind) && (bit == 0 || (bit_ & bit)); // cf. you can set (8|16) + } + bool isBit(uint32 bit) const { return (bit_ & bit) != 0; } + uint32 getBit() const { return bit_; } + const char *toString() const + { + if (kind_ == REG) { + if (ext8bit_) { + static const char tbl[4][4] = { "spl", "bpl", "sil", "dil" }; + return tbl[idx_ - 4]; + } + static const char tbl[4][16][5] = { + { "al", "cl", "dl", "bl", "ah", "ch", "dh", "bh", "r8b", "r9b", "r10b", "r11b", "r12b", "r13b", "r14b", "r15b" }, + { "ax", "cx", "dx", "bx", "sp", "bp", "si", "di", "r8w", "r9w", "r10w", "r11w", "r12w", "r13w", "r14w", "r15w" }, + { "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi", "r8d", "r9d", "r10d", "r11d", "r12d", "r13d", "r14d", "r15d" }, + { "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15" }, + }; + return tbl[bit_ == 8 ? 0 : bit_ == 16 ? 1 : bit_ == 32 ? 2 : 3][idx_]; + } else if (isMMX()) { + static const char tbl[8][4] = { "mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7" }; + return tbl[idx_]; + } else if (isXMM()) { + static const char tbl[16][5] = { "xm0", "xm1", "xm2", "xm3", "xm4", "xm5", "xm6", "xm7", "xm8", "xm9", "xm10", "xm11", "xm12", "xm13", "xm14", "xm15" }; + return tbl[idx_]; + } + throw ERR_INTERNAL; + } +}; + +class Reg : public Operand { + void operator=(const Reg&); +public: + Reg() { } + Reg(int idx, Kind kind, int bit = 0, int ext8bit = 0) : Operand(idx, kind, bit, ext8bit) { } + // reg = this + uint8 getRex(const Reg& index = Reg(), const Reg& base = Reg()) const + { + if ((!isExt8bit() && !index.isExt8bit() && !base.isExt8bit()) && (getIdx() | index.getIdx() | base.getIdx()) < 8) return 0; + return uint8(0x40 | ((getIdx() >> 3) << 2)| ((index.getIdx() >> 3) << 1) | (base.getIdx() >> 3)); + } +}; + +class Reg8 : public Reg { + void operator=(const Reg8&); +public: + explicit Reg8(int idx, int ext8bit = 0) : Reg(idx, Operand::REG, 8, ext8bit) { } +}; + +class Reg16 : public Reg { + void operator=(const Reg16&); +public: + explicit Reg16(int idx) : Reg(idx, Operand::REG, 16) { } +}; + +class Mmx : public Reg { + void operator=(const Mmx&); +public: + explicit Mmx(int idx, Kind kind = Operand::MMX, int bit = 64) : Reg(idx, kind, bit) { } +}; + +class Xmm : public Mmx { + void operator=(const Xmm&); +public: + explicit Xmm(int idx) : Mmx(idx, Operand::XMM, 128) { } +}; + +// register for addressing(32bit or 64bit) +class Reg32e : public Reg { +public: + // [base_(this) + index_ * scale_ + disp_] + const Reg index_; + const int scale_; // 0(index is none), 1, 2, 4, 8 + const uint32 disp_; +private: + friend class Address; + friend Reg32e operator+(const Reg32e& a, const Reg32e& b) + { + if (a.scale_ == 0) { + if (b.scale_ == 0) { // base + base + if (b.getIdx() == Operand::ESP) { // [reg + esp] => [esp + reg] + return Reg32e(b, a, 1, a.disp_ + b.disp_); + } else { + return Reg32e(a, b, 1, a.disp_ + b.disp_); + } + } else if (b.isNone()) { // base + index + return Reg32e(a, b.index_, b.scale_, a.disp_ + b.disp_); + } + } + throw ERR_BAD_ADDRESSING; + } + friend Reg32e operator*(const Reg32e& r, int scale) + { + if (r.scale_ == 0) { + if (scale == 1) { + return r; + } else if (scale == 2 || scale == 4 || scale == 8) { + return Reg32e(Reg(), r, scale, r.disp_); + } + } + throw ERR_BAD_SCALE; + } + friend Reg32e operator+(const Reg32e& r, unsigned int disp) + { + return Reg32e(r, r.index_, r.scale_, r.disp_ + disp); + } + friend Reg32e operator-(const Reg32e& r, unsigned int disp) + { + return operator+(r, -static_cast(disp)); + } + void operator=(const Reg32e&); // don't call +public: + explicit Reg32e(int idx, int bit) + : Reg(idx, REG, bit) + , index_() + , scale_(0) + , disp_(0) + { + } + Reg32e(const Reg& base, const Reg& index, int scale, unsigned int disp) + : Reg(base) + , index_(index) + , scale_(scale) + , disp_(disp) + { + if (scale != 0 && scale != 1 && scale != 2 && scale != 4 && scale != 8) throw ERR_BAD_SCALE; + if (!base.isNone() && !index.isNone() && base.getBit() != index.getBit()) throw ERR_BAD_COMBINATION; + if (index.getIdx() == Operand::ESP) throw ERR_ESP_CANT_BE_INDEX; + } + Reg32e optimize() const // select smaller size + { + // [reg * 2] => [reg + reg] + if (isNone() && !index_.isNone() && scale_ == 2) { + const Reg index(index_.getIdx(), Operand::REG, index_.getBit()); + return Reg32e(index, index, 1, disp_); + } + return *this; + } +}; + +struct Reg32 : public Reg32e { + explicit Reg32(int idx) : Reg32e(idx, 32) {} +private: + void operator=(const Reg32&); +}; +#ifdef XBYAK64 +struct Reg64 : public Reg32e { + explicit Reg64(int idx) : Reg32e(idx, 64) {} +private: + void operator=(const Reg64&); +}; +struct RegRip { + uint32 disp_; + RegRip(unsigned int disp = 0) : disp_(disp) {} + friend const RegRip operator+(const RegRip& r, unsigned int disp) { + return RegRip(r.disp_ + disp); + } + friend const RegRip operator-(const RegRip& r, unsigned int disp) { + return RegRip(r.disp_ - disp); + } +}; +#endif + +class CodeArray { + enum { + ALIGN_SIZE = 16, + MAX_FIXED_BUF_SIZE = 8 + }; + enum Type { + FIXED_BUF, // use buf_(non alignment, non protect) + USER_BUF, // use userPtr(non alignment, non protect) + ALLOC_BUF // use new(alignment, protect) + }; + void operator=(const CodeArray&); + Type type_; + uint8 *const allocPtr_; // for ALLOC_BUF + uint8 buf_[MAX_FIXED_BUF_SIZE]; // for FIXED_BUF +protected: + const size_t maxSize_; + uint8 *const top_; + size_t size_; +public: + CodeArray(size_t maxSize = MAX_FIXED_BUF_SIZE, void *userPtr = 0) + : type_(userPtr ? USER_BUF : maxSize <= MAX_FIXED_BUF_SIZE ? FIXED_BUF : ALLOC_BUF) + , allocPtr_(type_ == ALLOC_BUF ? new uint8[maxSize + ALIGN_SIZE] : 0) + , maxSize_(maxSize) + , top_(type_ == ALLOC_BUF ? getAlignedAddress(allocPtr_) : type_ == USER_BUF ? reinterpret_cast(userPtr) : buf_) + , size_(0) + { + if (type_ == ALLOC_BUF && !protect(top_, maxSize, true)) { +// fprintf(stderr, "can't protect (addr=%p, size=%u, canExec=%d)\n", addr, size, canExec); + throw ERR_CANT_PROTECT; + } + } + virtual ~CodeArray() + { + if (type_ == ALLOC_BUF) { + protect(top_, maxSize_, false); + delete[] allocPtr_; + } + } + CodeArray(const CodeArray& rhs) + : type_(rhs.type_) + , allocPtr_(0) + , maxSize_(rhs.maxSize_) + , top_(buf_) + , size_(rhs.size_) + { + if (type_ != FIXED_BUF) throw ERR_CODE_ISNOT_COPYABLE; + for (size_t i = 0; i < size_; i++) top_[i] = rhs.top_[i]; + } + void db(int code) + { + if (size_ >= maxSize_) throw ERR_CODE_IS_TOO_BIG; + top_[size_++] = static_cast(code); + } + void db(const uint8 *code, int codeSize) + { + for (int i = 0; i < codeSize; i++) db(code[i]); + } + void db(uint64 code, int codeSize) + { + if (codeSize > 8) throw ERR_BAD_PARAMETER; + for (int i = 0; i < codeSize; i++) db(static_cast(code >> (i * 8))); + } + void dw(uint32 code) { db(code, 2); } + void dd(uint32 code) { db(code, 4); } + const uint8 *getCode() const { return top_; } + const uint8 *getCurr() const { return &top_[size_]; } + size_t getSize() const { return size_; } + void dump() const + { + const uint8 *p = getCode(); + size_t bufSize = getSize(); + size_t remain = bufSize; + for (int i = 0; i < 4; i++) { + size_t disp = 16; + if (remain < 16) { + disp = remain; + } + for (size_t j = 0; j < 16; j++) { + if (j < disp) { + printf("%02X", p[i * 16 + j]); + } + } + putchar('\n'); + remain -= disp; + if (remain <= 0) { + break; + } + } + } + /* + @param data [in] address of jmp data + @param disp [in] offset from the next of jmp + @param isShort [in] true if short jmp + */ + void rewrite(uint8 *data, uint32 disp, bool isShort) + { + if (isShort) { + data[0] = static_cast(disp); + } else { + data[0] = static_cast(disp); + data[1] = static_cast(disp >> 8); + data[2] = static_cast(disp >> 16); + data[3] = static_cast(disp >> 24); + } + } + /** + change exec permission of memory + @param addr [in] buffer address + @param size [in] buffer size + @param canExec [in] true(enable to exec), false(disable to exec) + @return true(success), false(failure) + */ + static inline bool protect(const void *addr, size_t size, bool canExec) + { +#ifdef __GNUC__ + size_t pageSize = sysconf(_SC_PAGESIZE); + size_t iaddr = reinterpret_cast(addr); + size_t roundAddr = iaddr & ~(pageSize - static_cast(1)); + int mode = PROT_READ | PROT_WRITE | (canExec ? PROT_EXEC : 0); + return mprotect(reinterpret_cast(roundAddr), size + (iaddr - roundAddr), mode) == 0; +#elif defined(_WIN32) + DWORD oldProtect; + return VirtualProtect(const_cast(addr), size, canExec ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE, &oldProtect) != 0; +#else + return true; +#endif + } + /** + get aligned memory pointer + @param addr [in] address + @param alingedSize [in] power of two + @return aligned addr by alingedSize + */ + static inline uint8 *getAlignedAddress(uint8 *addr, size_t alignedSize = ALIGN_SIZE) + { + return reinterpret_cast((reinterpret_cast(addr) + alignedSize - 1) & ~(alignedSize - static_cast(1))); + } +}; + +class Address : public Operand, public CodeArray { + void operator=(const Address&); + uint64 disp_; + bool isOnlyDisp_; + bool is64bitDisp_; + uint8 rex_; +public: + const bool is32bit_; + Address(uint32 sizeBit, bool isOnlyDisp, uint64 disp, bool is32bit, bool is64bitDisp = false) + : Operand(0, MEM, sizeBit) + , CodeArray(6) // 6 = 1(ModRM) + 1(SIB) + 4(disp) + , disp_(disp) + , isOnlyDisp_(isOnlyDisp) + , is64bitDisp_(is64bitDisp) + , rex_(0) + , is32bit_(is32bit) + { + } + bool isOnlyDisp() const { return isOnlyDisp_; } // for mov eax + uint64 getDisp() const { return disp_; } + uint8 getRex() const { return rex_; } + bool is64bitDisp() const { return is64bitDisp_; } // for moffset +#ifdef XBYAK64 + void setRex(uint8 rex) { rex_ = rex; } +#else + void setRex(uint8) { } +#endif +}; + +class AddressFrame { +private: + void operator=(const AddressFrame&); +public: + const uint32 bit_; + explicit AddressFrame(uint32 bit) : bit_(bit) { } + Address operator[](const void *disp) const + { + Reg32e r(Reg(), Reg(), 0, inner::GetPtrDist(disp)); + return operator[](r); + } +#ifdef XBYAK64 + Address operator[](uint64 disp) const + { + return Address(64, true, disp, false, true); + } + Address operator[](const RegRip& addr) const + { + Address frame(64, true, addr.disp_, false); + frame.db(B00000101); + frame.dd(addr.disp_); + return frame; + } +#endif + Address operator[](const Reg32e& in) const + { + const Reg32e& r = in.optimize(); + Address frame(bit_, (r.isNone() && r.index_.isNone()), r.disp_, r.isBit(32) || r.index_.isBit(32)); + enum { + mod00 = 0, mod01 = 1, mod10 = 2 + }; + int mod; + if (r.isNone() || ((r.getIdx() & 7) != Operand::EBP && r.disp_ == 0)) { + mod = mod00; + } else if (inner::IsInDisp8(r.disp_)) { + mod = mod01; + } else { + mod = mod10; + } + const int base = r.isNone() ? Operand::EBP : (r.getIdx() & 7); + /* ModR/M = [2:3:3] = [Mod:reg/code:R/M] */ + bool hasSIB = !r.index_.isNone() || (r.getIdx() & 7) == Operand::ESP; +#ifdef XBYAK64 + if (r.isNone() && r.index_.isNone()) hasSIB = true; +#endif + if (!hasSIB) { + frame.db((mod << 6) | base); + } else { + frame.db((mod << 6) | Operand::ESP); + /* SIB = [2:3:3] = [SS:index:base(=rm)] */ + int index = r.index_.isNone() ? Operand::ESP : (r.index_.getIdx() & 7); + int ss = (r.scale_ == 8) ? 3 : (r.scale_ == 4) ? 2 : (r.scale_ == 2) ? 1 : 0; + frame.db((ss << 6) | (index << 3) | base); + } + if (mod == mod01) { + frame.db(r.disp_); + } else if (mod == mod10 || (mod == mod00 && r.isNone())) { + frame.dd(r.disp_); + } + frame.setRex(Reg().getRex(r.index_, r)); + return frame; + } +}; + +struct JmpLabel { + uint8 *endOfJmp; /* end address of jmp */ + bool isShort; +}; + +class Label { + CodeArray *base_; + int anonymousCount_; // for @@, @f, @b + int localCount_; // for .*** + typedef std::map DefinedList; + typedef std::multimap UndefinedList; + DefinedList definedList_; + UndefinedList undefinedList_; + + /* + @@ --> @@. + @b --> @@. + @f --> @@. + .*** -> .***. + */ + std::string convertLabel(const char *label) const + { + std::string newLabel(label); + if (newLabel == "@f" || newLabel == "@F") { + newLabel = std::string("@@") + toStr(anonymousCount_ + 1); + } else if (newLabel == "@b" || newLabel == "@B") { + newLabel = std::string("@@") + toStr(anonymousCount_); + } else if (*label == '.') { + newLabel += toStr(localCount_); + } + return newLabel; + } +public: + Label() + : base_(0) + , anonymousCount_(0) + , localCount_(0) + { + } + void incLocalCount() { localCount_++; } + void decLocalCount() { localCount_--; } + void set(CodeArray *base) + { + base_ = base; + } + void define(const char *label, const uint8 *address) + { + std::string newLabel(label); + if (newLabel == "@@") { + newLabel += toStr(++anonymousCount_); + } else if (*label == '.') { + newLabel += toStr(localCount_); + } + label = newLabel.c_str(); + // add label + DefinedList::value_type item(label, address); + std::pair ret = definedList_.insert(item); + if (!ret.second) throw ERR_LABEL_IS_REDEFINED; + // search undefined label + for (;;) { + UndefinedList::iterator itr = undefinedList_.find(label); + if (itr == undefinedList_.end()) break; + const JmpLabel *jmp = &itr->second; + uint32 disp = inner::GetPtrDist(address, jmp->endOfJmp); + if (jmp->isShort && !inner::IsInDisp8(disp)) throw ERR_LABEL_IS_TOO_FAR; + uint8 *data = jmp->endOfJmp - (jmp->isShort ? 1 : 4); + base_->rewrite(data, disp, jmp->isShort); + undefinedList_.erase(itr); + } + } + const uint8 *getAddress(const char *label) const + { + std::string newLabel = convertLabel(label); + DefinedList::const_iterator itr = definedList_.find(newLabel); + if (itr != definedList_.end()) { + return itr->second; + } else { + return 0; + } + } + void addUndefinedLabel(const char *label, const JmpLabel& jmp) + { + std::string newLabel = convertLabel(label); + undefinedList_.insert(UndefinedList::value_type(newLabel, jmp)); + } + bool hasUndefinedLabel() const + { + if (inner::debug) { + for (UndefinedList::const_iterator i = undefinedList_.begin(); i != undefinedList_.end(); ++i) { + fprintf(stderr, "undefined label:%s\n", i->first.c_str()); + } + } + return !undefinedList_.empty(); + } + static inline std::string toStr(int num) + { + char buf[16]; + static const char fmt[] = ".%08x"; +#ifdef _WIN32 + #if _MSC_VER < 1400 + _snprintf(buf, sizeof(buf), fmt, num); + #else + _snprintf_s(buf, sizeof(buf), fmt, num); + #endif +#else + snprintf(buf, sizeof(buf), fmt, num); +#endif + return buf; + } +}; + +class CodeGenerator : public CodeArray { +protected: + enum LabelType { + T_SHORT, + T_NEAR, + T_AUTO // T_SHORT if possible + }; +private: + CodeGenerator operator=(const CodeGenerator&); // don't call +#ifdef XBYAK64 + enum { i32e = 32 | 64, BIT = 64 }; +#else + enum { i32e = 32, BIT = 32 }; +#endif + // (XMM, XMM|MEM) + static inline bool isXMM_XMMorMEM(const Operand& op1, const Operand& op2) + { + return op1.isXMM() && (op2.isXMM() || op2.isMEM()); + } + // (MMX, MMX|MEM) or (XMM, XMM|MEM) + static inline bool isXMMorMMX_MEM(const Operand& op1, const Operand& op2) + { + return (op1.isMMX() && (op2.isMMX() || op2.isMEM())) || isXMM_XMMorMEM(op1, op2); + } + // (XMM, MMX|MEM) + static inline bool isXMM_MMXorMEM(const Operand& op1, const Operand& op2) + { + return op1.isXMM() && (op2.isMMX() || op2.isMEM()); + } + // (MMX, XMM|MEM) + static inline bool isMMX_XMMorMEM(const Operand& op1, const Operand& op2) + { + return op1.isMMX() && (op2.isXMM() || op2.isMEM()); + } + // (XMM, REG32|MEM) + static inline bool isXMM_REG32orMEM(const Operand& op1, const Operand& op2) + { + return op1.isXMM() && (op2.isREG(i32e) || op2.isMEM()); + } + // (REG32, XMM|MEM) + static inline bool isREG32_XMMorMEM(const Operand& op1, const Operand& op2) + { + return op1.isREG(i32e) && (op2.isXMM() || op2.isMEM()); + } + void if16bit(const Operand& reg1, const Operand& reg2) + { + // except movsx(16bit, 32/64bit) + if ((reg1.isBit(16) && !reg2.isBit(i32e)) || (reg2.isBit(16) && !reg1.isBit(i32e))) db(0x66); + } + void rexAddr(const Address& addr, const Reg& reg = Reg()) + { +#ifdef XBYAK64 + if (addr.is32bit_) db(0x67); +#endif + if16bit(reg, addr); + uint32 rex = addr.getRex() | reg.getRex(); + if (reg.isREG(64)) rex |= 0x48; + if (rex) db(rex); + } + void rex(const Operand& op1, const Operand& op2 = Operand()) + { + if (op1.isMEM()) { + rexAddr(static_cast(op1), static_cast(op2)); + } else if (op2.isMEM()) { + rexAddr(static_cast(op2), static_cast(op1)); + } else { + const Reg& reg1 = static_cast(op1); + const Reg& reg2 = static_cast(op2); + // ModRM(reg, base); + if16bit(reg1, reg2); + uint8 rex = reg2.getRex(Reg(), reg1); + if (reg1.isREG(64) || reg2.isREG(64)) rex |= 0x48; + if (rex) db(rex); + } + } + Label label_; + bool isInDisp16(uint32 x) const { return 0xFFFF8000 <= x || x <= 0x7FFF; } + uint8 getModRM(int mod, int r1, int r2) const { return static_cast((mod << 6) | ((r1 & 7) << 3) | (r2 & 7)); } + void opModR(const Reg& reg1, const Reg& reg2, int code0, int code1 = NONE, int code2 = NONE) + { + rex(reg2, reg1); + db(code0 | (reg1.isBit(8) ? 0 : 1)); if (code1 != NONE) db(code1); if (code2 != NONE) db(code2); + db(getModRM(3, reg1.getIdx(), reg2.getIdx())); + } + void opModM(const Address& addr, const Reg& reg, int code0, int code1 = NONE, int code2 = NONE) + { + if (addr.is64bitDisp()) throw ERR_CANT_USE_64BIT_DISP; + rex(addr, reg); + db(code0 | (reg.isBit(8) ? 0 : 1)); if (code1 != NONE) db(code1); if (code2 != NONE) db(code2); + uint8 t = *addr.getCode(); + assert((t & ~0xC7) == 0); /* 0b11000111 */ + db(t | ((reg.getIdx() & 7) << 3)); // update reg field + db(addr.getCode() + 1, static_cast(addr.getSize()) - 1); + } + void opJmp(const char *label, LabelType type, uint8 shortCode, uint8 longCode, uint8 longPref) + { + const uint8 *address = label_.getAddress(label); + if (address) { /* label exists */ + opJmp(address, type, shortCode, longCode, longPref); + } else { + const int shortHeaderSize = 1; + const int shortJmpSize = shortHeaderSize + 1; /* +1 means 8-bit displacement */ + const int longHeaderSize = longPref ? 2 : 1; + const int longJmpSize = longHeaderSize + 4; /* +4 means 32-bit displacement */ + uint8 *top = const_cast(getCurr()); + bool isShort = (type != T_NEAR); + JmpLabel jmp; + jmp.endOfJmp = top + (isShort ? shortJmpSize : longJmpSize); + jmp.isShort = isShort; + if (isShort) { + db(shortCode); + db(0); + } else { + if (longPref) db(longPref); + db(longCode); + dd(0); + } + label_.addUndefinedLabel(label, jmp); + } + } + void opJmp(const void *addr, LabelType type, uint8 shortCode, uint8 longCode, uint8 longPref) + { + const int shortHeaderSize = 1; + const int shortJmpSize = shortHeaderSize + 1; /* +1 means 8-bit displacement */ + const int longHeaderSize = longPref ? 2 : 1; + const int longJmpSize = longHeaderSize + 4; /* +4 means 32-bit displacement */ + + uint8 *top = const_cast(getCurr()); + uint32 disp = inner::GetPtrDist(addr, top); + if (type != T_NEAR && inner::IsInDisp8(disp - shortJmpSize)) { + db(shortCode); + db(0); + rewrite(top + shortHeaderSize, disp - shortJmpSize, true); + } else { + if (type == T_SHORT) throw ERR_LABEL_IS_TOO_FAR; + if (longPref) db(longPref); + db(longCode); + dd(0); + rewrite(top + longHeaderSize, disp - longJmpSize, false); + } + } + /* preCode is for SSSE3/SSE4 */ + void opGen(const Operand& reg, const Operand& op, int code, int pref, bool isValid(const Operand&, const Operand&), int imm8 = NONE, int preCode = NONE) + { + if (isValid && !isValid(reg, op)) throw ERR_BAD_COMBINATION; + if (pref != NONE) db(pref); + if (op.isMEM()) { + opModM(static_cast(op), static_cast(reg), 0x0F, preCode, code); + } else { + opModR(static_cast(reg), static_cast(op), 0x0F, preCode, code); + } + if (imm8 != NONE) db(imm8); + } + void opMMX_IMM(const Mmx& mmx, int imm8, int code, int ext) + { + if (mmx.isXMM()) db(0x66); + opModR(Reg32(ext), mmx, 0x0F, code); + db(imm8); + } + void opMMX(const Mmx& mmx, const Operand& op, int code, int pref = 0x66, int imm8 = NONE, int preCode = NONE) + { + pref = mmx.isXMM() ? pref : NONE; + opGen(mmx, op, code, pref, isXMMorMMX_MEM, imm8, preCode); + } + void opMovXMM(const Operand& op1, const Operand& op2, int code, int pref) + { + if (pref != NONE) db(pref); + if (op1.isXMM() && op2.isMEM()) { + opModM(static_cast(op2), static_cast(op1), 0x0F, code); + } else if (op1.isMEM() && op2.isXMM()) { + opModM(static_cast(op1), static_cast(op2), 0x0F, code | 1); + } else { + throw ERR_BAD_COMBINATION; + } + } + void opExt(const Operand& op, const Mmx& mmx, int code, int imm, bool hasMMX2 = false) + { + if (hasMMX2 && op.isREG(i32e)) { /* pextrw is special */ + if (mmx.isXMM()) db(0x66); + opModR(static_cast(op), mmx, 0x0F, B11000101); db(imm); + } else { + opGen(mmx, op, code, 0x66, isXMM_REG32orMEM, imm, B00111010); + } + } + void opR_ModM(const Operand& op, int bit, uint8 mod, int ext, int code0, int code1 = NONE, int code2 = NONE) + { + if (op.isREG(bit)) { + rex(op); + db(code0 | (op.isBit(8) ? 0 : 1)); if (code1 != NONE) db(code1); if (code2 != NONE) db(code2); + db(getModRM(mod, ext, op.getIdx())); + } else if (op.isMEM()) { + opModM(static_cast(op), Reg(ext, Operand::REG, op.getBit()), code0, code1, code2); + } else { + throw ERR_BAD_COMBINATION; + } + } + void opShift(const Operand& op, int imm, int ext) + { + verifyMemHasSize(op); + opR_ModM(op, 0, 3, ext, (B11000000 | ((imm == 1 ? 1 : 0) << 4))); + if (imm != 1) db(imm); + } + void opShift(const Operand& op, const Reg8& cl, int ext) + { + if (cl.getIdx() != Operand::CL) throw ERR_BAD_COMBINATION; + opR_ModM(op, 0, 3, ext, B11010010); + } + void opModRM(const Operand& op1, const Operand& op2, bool condR, bool condM, int code0, int code1 = NONE, int code2 = NONE) + { + if (condR) { + opModR(static_cast(op1), static_cast(op2), code0, code1, code2); + } else if (condM) { + opModM(static_cast(op2), static_cast(op1), code0, code1, code2); + } else { + throw ERR_BAD_COMBINATION; + } + } + void opShxd(const Operand& op, const Reg& reg, uint8 imm, int code, const Reg8 *cl = 0) + { + if (cl && cl->getIdx() != Operand::CL) throw ERR_BAD_COMBINATION; + opModRM(reg, op, (op.isREG(16 | i32e) && op.getBit() == reg.getBit()), op.isMEM() && (reg.isREG(16 | i32e)), 0x0F, code | (cl ? 1 : 0)); + if (!cl) db(imm); + } + // (REG, REG|MEM), (MEM, REG) + void opRM_RM(const Operand& op1, const Operand& op2, int code) + { + if (op1.isREG() && op2.isMEM()) { + opModM(static_cast(op2), static_cast(op1), code | 2); + } else { + opModRM(op2, op1, op1.isREG() && op1.getKind() == op2.getKind(), op1.isMEM() && op2.isREG(), code); + } + } + // (REG|MEM, IMM) + void opRM_I(const Operand& op, uint32 imm, int code, int ext) + { + verifyMemHasSize(op); + uint32 immBit = inner::IsInDisp8(imm) ? 8 : isInDisp16(imm) ? 16 : 32; + if (op.getBit() < immBit) throw ERR_IMM_IS_TOO_BIG; + if (op.isREG()) { + if (immBit == 16 && op.isBit(32)) immBit = 32; /* don't use MEM16 if 32bit mode */ + } + if (op.isREG() && op.getIdx() == 0 && (op.getBit() == immBit || (op.isBit(64) && immBit == 32))) { // rax, eax, ax, al + rex(op); + db(code | 4 | (immBit == 8 ? 0 : 1)); + } else { + int tmp = (op.getBit() > immBit && 32 > immBit) ? 2 : 0; + opR_ModM(op, 0, 3, ext, B10000000 | tmp); + } + db(imm, immBit / 8); + } + void opIncDec(const Operand& op, int code, int ext) + { +#ifndef XBYAK64 + if (op.isREG() && !op.isBit(8)) { + rex(op); db(code | op.getIdx()); + return; + } +#endif + code = B11111110; + if (op.isREG()) { + opModR(Reg(ext, Operand::REG, op.getBit()), static_cast(op), code); + } else if (op.isMEM() && op.getBit() > 0) { + opModM(static_cast(op), Reg(ext, Operand::REG, op.getBit()), code); + } else { + throw ERR_BAD_COMBINATION; + } + } + void opPushPop(const Operand& op, int code, int ext, int alt) + { + if (op.isREG()) { +#ifdef XBYAK64 + if (op.isBit(16)) db(0x66); + if (static_cast(op).getIdx() >= 8) db(0x41); +#else + rex(op); +#endif + db(alt | (op.getIdx() & 7)); + } else if (op.isMEM()) { + opModM(static_cast(op), Reg(ext, Operand::REG, op.getBit()), code); + } else { + throw ERR_BAD_COMBINATION; + } + } + void verifyMemHasSize(const Operand& op) const + { + if (op.isMEM() && op.getBit() == 0) throw ERR_MEM_SIZE_IS_NOT_SPECIFIED; + } +protected: + unsigned int getVersion() const { return VERSION; } + using CodeArray::db; + const Mmx mm0, mm1, mm2, mm3, mm4, mm5, mm6, mm7; + const Xmm xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7; + const Xmm &xm0, &xm1, &xm2, &xm3, &xm4, &xm5, &xm6, &xm7; + const Reg32 eax, ecx, edx, ebx, esp, ebp, esi, edi; + const Reg16 ax, cx, dx, bx, sp, bp, si, di; + const Reg8 al, cl, dl, bl, ah, ch, dh, bh; + const AddressFrame ptr, byte, word, dword, qword, xmmword; +#ifdef XBYAK64 + const Reg64 rax, rcx, rdx, rbx, rsp, rbp, rsi, rdi, r8, r9, r10, r11, r12, r13, r14, r15; + const Reg32 r8d, r9d, r10d, r11d, r12d, r13d, r14d, r15d; + const Reg16 r8w, r9w, r10w, r11w, r12w, r13w, r14w, r15w; + const Reg8 r8b, r9b, r10b, r11b, r12b, r13b, r14b, r15b; + const Reg8 spl, bpl, sil, dil; + const Xmm xmm8, xmm9, xmm10, xmm11, xmm12, xmm13, xmm14, xmm15; + const Xmm &xm8, &xm9, &xm10, &xm11, &xm12, &xm13, &xm14, &xm15; + const RegRip rip; +#endif + + void L(const char *label) + { + label_.define(label, getCurr()); + } + void inLocalLabel() { label_.incLocalCount(); } + void outLocalLabel() { label_.decLocalCount(); } + void jmp(const char *label, LabelType type = T_AUTO) + { + opJmp(label, type, B11101011, B11101001, 0); + } + void jmp(const void *addr, LabelType type = T_AUTO) + { + opJmp(addr, type, B11101011, B11101001, 0); + } + void jmp(const Operand& op) + { + opR_ModM(op, i32e, 3, 4, 0xFF); + } + // (REG|MEM, REG) + void test(const Operand& op, const Reg& reg) + { + opModRM(reg, op, op.isREG() && (op.getKind() == reg.getKind()), op.isMEM(), B10000100); + } + // (REG|MEM, IMM) + void test(const Operand& op, uint32 imm) + { + verifyMemHasSize(op); + if (op.isREG() && op.getIdx() == 0) { // al, ax, eax + rex(op); + db(B10101000 | (op.isBit(8) ? 0 : 1)); + } else { + opR_ModM(op, 0, 3, 0, B11110110); + } + int size = op.getBit() / 8; if (size > 4) size = 4; + db(imm, size); + } + void ret(int imm = 0) + { + if (imm) { + db(B11000010); dw(imm); + } else { + db(B11000011); + } + } + // (REG16|REG32, REG16|REG32|MEM) + void imul(const Reg& reg, const Operand& op) + { + opModRM(reg, op, op.isREG() && (reg.getKind() == op.getKind()), op.isMEM(), 0x0F, B10101111); + } + void imul(const Reg& reg, const Operand& op, int imm) + { + int s = inner::IsInDisp8(imm) ? 1 : 0; + opModRM(reg, op, op.isREG() && (reg.getKind() == op.getKind()), op.isMEM(), B01101001 | (s << 1)); + int size = s ? 1 : reg.isREG(16) ? 2 : 4; + db(imm, size); + } + void pop(const Operand& op) + { + opPushPop(op, B10001111, 0, B01011000); + } + void push(const Operand& op) + { + opPushPop(op, B11111111, 6, B01010000); + } + void push(const AddressFrame& af, uint32 imm) + { + if (af.bit_ == 8 && inner::IsInDisp8(imm)) { + db(B01101010); db(imm); + } else if (af.bit_ == 16 && isInDisp16(imm)) { + db(0x66); db(B01101000); dw(imm); + } else { + db(B01101000); dd(imm); + } + } + /* use "push(word, 4)" if you want "push word 4" */ + void push(uint32 imm) + { + if (inner::IsInDisp8(imm)) { + push(byte, imm); + } else { + push(dword, imm); + } + } + void bswap(const Reg32e& reg) + { + opModR(Reg32(1), reg, 0x0F); + } + void mov(const Operand& reg1, const Operand& reg2) + { + const Reg *reg = 0; + const Address *addr = 0; + uint8 code = 0; + if (reg1.isREG() && reg1.getIdx() == 0 && reg2.isMEM()) { // mov eax|ax|al, [disp] + reg = &static_cast(reg1); + addr= &static_cast(reg2); + code = B10100000; + } else + if (reg1.isMEM() && reg2.isREG() && reg2.getIdx() == 0) { // mov [disp], eax|ax|al + reg = &static_cast(reg2); + addr= &static_cast(reg1); + code = B10100010; + } +#ifdef XBYAK64 + if (addr && addr->is64bitDisp()) { + if (code) { + rex(*reg); + db(reg1.isREG(8) ? 0xA0 : reg1.isREG() ? 0xA1 : reg2.isREG(8) ? 0xA2 : 0xA3); + db(addr->getDisp(), 8); + } else { + throw ERR_BAD_COMBINATION; + } + } else +#else + if (code && addr->isOnlyDisp()) { + rex(*reg, *addr); + db(code | (reg->isBit(8) ? 0 : 1)); + dd(static_cast(addr->getDisp())); + } else +#endif + { + opRM_RM(reg1, reg2, B10001000); + } + } + void mov(const Operand& op, uint64 imm) + { + verifyMemHasSize(op); + if (op.isREG()) { + int w = op.isBit(8) ? 0 : 1; + rex(op); db(B10110000 | (w << 3) | (op.getIdx() & 7)); + } else if (op.isMEM()) { + opModM(static_cast(op), Reg(0, Operand::REG, op.getBit()), B11000110); + } else { + throw ERR_BAD_COMBINATION; + } + db(imm, op.getBit() / 8); + } + void opMovxx(const Reg& reg, const Operand& op, uint8 code) + { + int w = op.isBit(16); + bool cond = reg.isREG() && (reg.getBit() > op.getBit()); + opModRM(reg, op, cond && op.isREG(), cond && op.isMEM(), 0x0F, code | w); + } + void cmpxchg8b(const Address& addr) { opModM(addr, Reg32(1), 0x0F, B11000111); } +#ifdef XBYAK64 + void cmpxchg16b(const Address& addr) { opModM(addr, Reg64(1), 0x0F, B11000111); } +#endif + void xadd(const Operand& op, const Reg& reg) + { + opModRM(reg, op, (op.isREG() && reg.isREG() && op.getBit() == reg.getBit()), op.isMEM(), 0x0F, B11000000 | (reg.isBit(8) ? 0 : 1)); + } + void xchg(const Operand& op1, const Operand& op2) + { + const Operand *p1 = &op1, *p2 = &op2; + if (p1->isMEM() || (p2->isREG(16 | i32e) && p2->getIdx() == 0)) { + p1 = &op2; p2 = &op1; + } + if (p1->isMEM()) throw ERR_BAD_COMBINATION; + if (p2->isREG() && (p1->isREG(16 | i32e) && p1->getIdx() == 0) +#ifdef XBYAK64 + && (p2->getIdx() != 0 || !p1->isREG(32)) +#endif + ) { + rex(*p2, *p1); db(0x90 | (p2->getIdx() & 7)); + return; + } + opModRM(*p1, *p2, (p1->isREG() && p2->isREG() && (p1->getBit() == p2->getBit())), p2->isMEM(), B10000110 | (p1->isBit(8) ? 0 : 1)); + } + void call(const char *label) + { + opJmp(label, T_NEAR, 0, B10011010, 0); + } + void call(const void *addr) + { + opJmp(addr, T_NEAR, 0, B11101000, 0); + } + void call(const Operand& op) + { + opR_ModM(op, 16 | i32e, 3, 2, B11111111); + } + // special case + void movd(const Address& addr, const Mmx& mmx) + { + opModM(addr, Reg(mmx.getIdx(), Operand::REG, mmx.getBit() / 8), 0x0F, B01111110); + } + void movd(const Reg32& reg, const Mmx& mmx) + { + if (mmx.isXMM()) db(0x66); + opModR(mmx, reg, 0x0F, B01111110); + } + void movd(const Mmx& mmx, const Address& addr) + { + ASSERT(!addr.isBit(32)); // don't use dword ptr, bogus, won't output 0x66 for xmm dest op + opModM(addr, Reg(mmx.getIdx(), Operand::REG, mmx.getBit() / 8), 0x0F, B01101110); + } + void movd(const Mmx& mmx, const Reg32& reg) + { + if (mmx.isXMM()) db(0x66); + opModR(mmx, reg, 0x0F, B01101110); + } + void movq2dq(const Xmm& xmm, const Mmx& mmx) + { + db(0xF3); opModR(xmm, mmx, 0x0F, B11010110); + } + void movdq2q(const Mmx& mmx, const Xmm& xmm) + { + db(0xF2); opModR(mmx, xmm, 0x0F, B11010110); + } + void movq(const Mmx& mmx, const Operand& op) + { + if (mmx.isXMM()) db(0xF3); + opModRM(mmx, op, (mmx.getKind() == op.getKind()), op.isMEM(), 0x0F, mmx.isXMM() ? B01111110 : B01101111); + } + void movq(const Address& addr, const Mmx& mmx) + { + opModM(addr, Reg(mmx.getIdx(), Operand::REG, mmx.getBit() / 8), 0x0F, mmx.isXMM() ? B11010110 : B01111111); + } + // MMX2 : pextrw : reg, mmx/xmm, imm + // SSE4 : pextrw, pextrb, pextrd, extractps : reg/mem, mmx/xmm, imm + void pextrw(const Operand& op, const Mmx& xmm, uint8 imm) { opExt(op, xmm, 0x15, imm, true); } + void pextrb(const Operand& op, const Xmm& xmm, uint8 imm) { opExt(op, xmm, 0x14, imm); } + void pextrd(const Operand& op, const Xmm& xmm, uint8 imm) { opExt(op, xmm, 0x16, imm); } + void extractps(const Operand& op, const Xmm& xmm, uint8 imm) { opExt(op, xmm, 0x17, imm); } + void pinsrw(const Mmx& mmx, const Operand& op, int imm) + { + if (!op.isREG(32) && !op.isMEM()) throw ERR_BAD_COMBINATION; + opGen(mmx, op, B11000100, mmx.isXMM() ? 0x66 : NONE, 0, imm); + } + void insertps(const Xmm& xmm, const Operand& op, uint8 imm) { opGen(xmm, op, 0x21, 0x66, isXMM_XMMorMEM, imm, B00111010); } + void pinsrb(const Xmm& xmm, const Operand& op, uint8 imm) { opGen(xmm, op, 0x20, 0x66, isXMM_REG32orMEM, imm, B00111010); } + void pinsrd(const Xmm& xmm, const Operand& op, uint8 imm) { opGen(xmm, op, 0x22, 0x66, isXMM_REG32orMEM, imm, B00111010); } + + void pmovmskb(const Reg32e& reg, const Mmx& mmx) + { + if (mmx.isXMM()) db(0x66); + opModR(reg, mmx, 0x0F, B11010111); + } + void maskmovq(const Mmx& reg1, const Mmx& reg2) + { + if (!reg1.isMMX() || !reg2.isMMX()) throw ERR_BAD_COMBINATION; + opModR(reg1, reg2, 0x0F, B11110111); + } + void lea(const Reg32e& reg, const Address& addr) { opModM(addr, reg, B10001101); } + + void movmskps(const Reg32e& reg, const Xmm& xmm) { opModR(reg, xmm, 0x0F, B01010000); } + void movmskpd(const Reg32e& reg, const Xmm& xmm) { db(0x66); movmskps(reg, xmm); } + void movntps(const Address& addr, const Xmm& xmm) { opModM(addr, Mmx(xmm.getIdx()), 0x0F, B00101011); } + void movntdqa(const Xmm& xmm, const Address& addr) { db(0x66); opModM(addr, xmm, 0x0F, 0x38, 0x2A); } + void lddqu(const Xmm& xmm, const Address& addr) { db(0xF2); opModM(addr, xmm, 0x0F, B11110000); } + void movnti(const Address& addr, const Reg32e& reg) { opModM(addr, reg, 0x0F, B11000011); } + void movntq(const Address& addr, const Mmx& mmx) + { + if (!mmx.isMMX()) throw ERR_BAD_COMBINATION; + opModM(addr, mmx, 0x0F, B11100111); + } + void popcnt(const Reg& reg, const Operand& op) + { + bool is16bit = reg.isREG(16) && (op.isREG(16) || op.isMEM()); + if (!is16bit && !(reg.isREG(i32e) && (op.isREG(i32e) || op.isMEM()))) throw ERR_BAD_COMBINATION; + if (is16bit) db(0x66); + db(0xF3); opModRM(Reg(reg.getIdx(), Operand::REG, i32e == 32 ? 32 : reg.getBit()), op, op.isREG(), true, 0x0F, 0xB8); + } + void crc32(const Reg32e& reg, const Operand& op) + { + if (reg.isBit(32) && op.isBit(16)) db(0x66); + db(0xF2); + opModRM(reg, op, op.isREG(), op.isMEM(), 0x0F, 0x38, 0xF0 | (op.isBit(8) ? 0 : 1)); + } +public: + enum { NONE = 256 }; + CodeGenerator(size_t maxSize = DEFAULT_MAX_CODE_SIZE, void *userPtr = 0) + : CodeArray(maxSize, userPtr) + , mm0(0), mm1(1), mm2(2), mm3(3), mm4(4), mm5(5), mm6(6), mm7(7) + , xmm0(0), xmm1(1), xmm2(2), xmm3(3), xmm4(4), xmm5(5), xmm6(6), xmm7(7) + , xm0(xmm0), xm1(xmm1), xm2(xmm2), xm3(xmm3), xm4(xmm4), xm5(xmm5), xm6(xmm6), xm7(xmm7) // for my convenience + , eax(Operand::EAX), ecx(Operand::ECX), edx(Operand::EDX), ebx(Operand::EBX), esp(Operand::ESP), ebp(Operand::EBP), esi(Operand::ESI), edi(Operand::EDI) + , ax(Operand::EAX), cx(Operand::ECX), dx(Operand::EDX), bx(Operand::EBX), sp(Operand::ESP), bp(Operand::EBP), si(Operand::ESI), di(Operand::EDI) + , al(Operand::AL), cl(Operand::CL), dl(Operand::DL), bl(Operand::BL), ah(Operand::AH), ch(Operand::CH), dh(Operand::DH), bh(Operand::BH) + , ptr(0), byte(8), word(16), dword(32), qword(64), xmmword(128) +#ifdef XBYAK64 + , rax(Operand::RAX), rcx(Operand::RCX), rdx(Operand::RDX), rbx(Operand::RBX), rsp(Operand::RSP), rbp(Operand::RBP), rsi(Operand::RSI), rdi(Operand::RDI), r8(Operand::R8), r9(Operand::R9), r10(Operand::R10), r11(Operand::R11), r12(Operand::R12), r13(Operand::R13), r14(Operand::R14), r15(Operand::R15) + , r8d(Operand::R8D), r9d(Operand::R9D), r10d(Operand::R10D), r11d(Operand::R11D), r12d(Operand::R12D), r13d(Operand::R13D), r14d(Operand::R14D), r15d(Operand::R15D) + , r8w(Operand::R8W), r9w(Operand::R9W), r10w(Operand::R10W), r11w(Operand::R11W), r12w(Operand::R12W), r13w(Operand::R13W), r14w(Operand::R14W), r15w(Operand::R15W) + , r8b(Operand::R8B), r9b(Operand::R9B), r10b(Operand::R10B), r11b(Operand::R11B), r12b(Operand::R12B), r13b(Operand::R13B), r14b(Operand::R14B), r15b(Operand::R15B) + , spl(Operand::SPL, 1), bpl(Operand::BPL, 1), sil(Operand::SIL, 1), dil(Operand::DIL, 1) + , xmm8(8), xmm9(9), xmm10(10), xmm11(11), xmm12(12), xmm13(13), xmm14(14), xmm15(15) + , xm8(xmm8), xm9(xmm9), xm10(xmm10), xm11(xmm11), xm12(xmm12), xm13(xmm13), xm14(xmm14), xm15(xmm15) // for my convenience + , rip() +#endif + { + label_.set(this); + } + bool hasUndefinedLabel() const { return label_.hasUndefinedLabel(); } + const uint8 *getCode() const + { + assert(!hasUndefinedLabel()); +// if (hasUndefinedLabel()) throw ERR_LABEL_IS_NOT_FOUND; + return top_; + } +#ifdef TEST_NM + void dump(bool doClear = true) + { + CodeArray::dump(); + if (doClear) size_ = 0; + } +#endif + +#ifndef XBYAK_DONT_READ_LIST +#include "xbyak_mnemonic.h" + void align(int x = 16) + { + if (x != 4 && x != 8 && x != 16 && x != 32) throw ERR_BAD_ALIGN; + while (inner::GetPtrDist(getCurr()) % x) { + nop(); + } + } +#endif +}; + +#ifdef _MSC_VER + #pragma warning(pop) +#endif + +} // end of namespace + +#endif // XBYAK_H_ diff --git a/plugins/GSdx/xbyak/xbyak_bin2hex.h b/plugins/GSdx/xbyak/xbyak_bin2hex.h index 8c75029af3..69ecdbfedc 100644 --- a/plugins/GSdx/xbyak/xbyak_bin2hex.h +++ b/plugins/GSdx/xbyak/xbyak_bin2hex.h @@ -1,258 +1,258 @@ -enum { - B00000000= 0, - B00000001= 1, - B00000010= 2, - B00000011= 3, - B00000100= 4, - B00000101= 5, - B00000110= 6, - B00000111= 7, - B00001000= 8, - B00001001= 9, - B00001010= 10, - B00001011= 11, - B00001100= 12, - B00001101= 13, - B00001110= 14, - B00001111= 15, - B00010000= 16, - B00010001= 17, - B00010010= 18, - B00010011= 19, - B00010100= 20, - B00010101= 21, - B00010110= 22, - B00010111= 23, - B00011000= 24, - B00011001= 25, - B00011010= 26, - B00011011= 27, - B00011100= 28, - B00011101= 29, - B00011110= 30, - B00011111= 31, - B00100000= 32, - B00100001= 33, - B00100010= 34, - B00100011= 35, - B00100100= 36, - B00100101= 37, - B00100110= 38, - B00100111= 39, - B00101000= 40, - B00101001= 41, - B00101010= 42, - B00101011= 43, - B00101100= 44, - B00101101= 45, - B00101110= 46, - B00101111= 47, - B00110000= 48, - B00110001= 49, - B00110010= 50, - B00110011= 51, - B00110100= 52, - B00110101= 53, - B00110110= 54, - B00110111= 55, - B00111000= 56, - B00111001= 57, - B00111010= 58, - B00111011= 59, - B00111100= 60, - B00111101= 61, - B00111110= 62, - B00111111= 63, - B01000000= 64, - B01000001= 65, - B01000010= 66, - B01000011= 67, - B01000100= 68, - B01000101= 69, - B01000110= 70, - B01000111= 71, - B01001000= 72, - B01001001= 73, - B01001010= 74, - B01001011= 75, - B01001100= 76, - B01001101= 77, - B01001110= 78, - B01001111= 79, - B01010000= 80, - B01010001= 81, - B01010010= 82, - B01010011= 83, - B01010100= 84, - B01010101= 85, - B01010110= 86, - B01010111= 87, - B01011000= 88, - B01011001= 89, - B01011010= 90, - B01011011= 91, - B01011100= 92, - B01011101= 93, - B01011110= 94, - B01011111= 95, - B01100000= 96, - B01100001= 97, - B01100010= 98, - B01100011= 99, - B01100100= 100, - B01100101= 101, - B01100110= 102, - B01100111= 103, - B01101000= 104, - B01101001= 105, - B01101010= 106, - B01101011= 107, - B01101100= 108, - B01101101= 109, - B01101110= 110, - B01101111= 111, - B01110000= 112, - B01110001= 113, - B01110010= 114, - B01110011= 115, - B01110100= 116, - B01110101= 117, - B01110110= 118, - B01110111= 119, - B01111000= 120, - B01111001= 121, - B01111010= 122, - B01111011= 123, - B01111100= 124, - B01111101= 125, - B01111110= 126, - B01111111= 127, - B10000000= 128, - B10000001= 129, - B10000010= 130, - B10000011= 131, - B10000100= 132, - B10000101= 133, - B10000110= 134, - B10000111= 135, - B10001000= 136, - B10001001= 137, - B10001010= 138, - B10001011= 139, - B10001100= 140, - B10001101= 141, - B10001110= 142, - B10001111= 143, - B10010000= 144, - B10010001= 145, - B10010010= 146, - B10010011= 147, - B10010100= 148, - B10010101= 149, - B10010110= 150, - B10010111= 151, - B10011000= 152, - B10011001= 153, - B10011010= 154, - B10011011= 155, - B10011100= 156, - B10011101= 157, - B10011110= 158, - B10011111= 159, - B10100000= 160, - B10100001= 161, - B10100010= 162, - B10100011= 163, - B10100100= 164, - B10100101= 165, - B10100110= 166, - B10100111= 167, - B10101000= 168, - B10101001= 169, - B10101010= 170, - B10101011= 171, - B10101100= 172, - B10101101= 173, - B10101110= 174, - B10101111= 175, - B10110000= 176, - B10110001= 177, - B10110010= 178, - B10110011= 179, - B10110100= 180, - B10110101= 181, - B10110110= 182, - B10110111= 183, - B10111000= 184, - B10111001= 185, - B10111010= 186, - B10111011= 187, - B10111100= 188, - B10111101= 189, - B10111110= 190, - B10111111= 191, - B11000000= 192, - B11000001= 193, - B11000010= 194, - B11000011= 195, - B11000100= 196, - B11000101= 197, - B11000110= 198, - B11000111= 199, - B11001000= 200, - B11001001= 201, - B11001010= 202, - B11001011= 203, - B11001100= 204, - B11001101= 205, - B11001110= 206, - B11001111= 207, - B11010000= 208, - B11010001= 209, - B11010010= 210, - B11010011= 211, - B11010100= 212, - B11010101= 213, - B11010110= 214, - B11010111= 215, - B11011000= 216, - B11011001= 217, - B11011010= 218, - B11011011= 219, - B11011100= 220, - B11011101= 221, - B11011110= 222, - B11011111= 223, - B11100000= 224, - B11100001= 225, - B11100010= 226, - B11100011= 227, - B11100100= 228, - B11100101= 229, - B11100110= 230, - B11100111= 231, - B11101000= 232, - B11101001= 233, - B11101010= 234, - B11101011= 235, - B11101100= 236, - B11101101= 237, - B11101110= 238, - B11101111= 239, - B11110000= 240, - B11110001= 241, - B11110010= 242, - B11110011= 243, - B11110100= 244, - B11110101= 245, - B11110110= 246, - B11110111= 247, - B11111000= 248, - B11111001= 249, - B11111010= 250, - B11111011= 251, - B11111100= 252, - B11111101= 253, - B11111110= 254, - B11111111= 255 -}; +enum { + B00000000= 0, + B00000001= 1, + B00000010= 2, + B00000011= 3, + B00000100= 4, + B00000101= 5, + B00000110= 6, + B00000111= 7, + B00001000= 8, + B00001001= 9, + B00001010= 10, + B00001011= 11, + B00001100= 12, + B00001101= 13, + B00001110= 14, + B00001111= 15, + B00010000= 16, + B00010001= 17, + B00010010= 18, + B00010011= 19, + B00010100= 20, + B00010101= 21, + B00010110= 22, + B00010111= 23, + B00011000= 24, + B00011001= 25, + B00011010= 26, + B00011011= 27, + B00011100= 28, + B00011101= 29, + B00011110= 30, + B00011111= 31, + B00100000= 32, + B00100001= 33, + B00100010= 34, + B00100011= 35, + B00100100= 36, + B00100101= 37, + B00100110= 38, + B00100111= 39, + B00101000= 40, + B00101001= 41, + B00101010= 42, + B00101011= 43, + B00101100= 44, + B00101101= 45, + B00101110= 46, + B00101111= 47, + B00110000= 48, + B00110001= 49, + B00110010= 50, + B00110011= 51, + B00110100= 52, + B00110101= 53, + B00110110= 54, + B00110111= 55, + B00111000= 56, + B00111001= 57, + B00111010= 58, + B00111011= 59, + B00111100= 60, + B00111101= 61, + B00111110= 62, + B00111111= 63, + B01000000= 64, + B01000001= 65, + B01000010= 66, + B01000011= 67, + B01000100= 68, + B01000101= 69, + B01000110= 70, + B01000111= 71, + B01001000= 72, + B01001001= 73, + B01001010= 74, + B01001011= 75, + B01001100= 76, + B01001101= 77, + B01001110= 78, + B01001111= 79, + B01010000= 80, + B01010001= 81, + B01010010= 82, + B01010011= 83, + B01010100= 84, + B01010101= 85, + B01010110= 86, + B01010111= 87, + B01011000= 88, + B01011001= 89, + B01011010= 90, + B01011011= 91, + B01011100= 92, + B01011101= 93, + B01011110= 94, + B01011111= 95, + B01100000= 96, + B01100001= 97, + B01100010= 98, + B01100011= 99, + B01100100= 100, + B01100101= 101, + B01100110= 102, + B01100111= 103, + B01101000= 104, + B01101001= 105, + B01101010= 106, + B01101011= 107, + B01101100= 108, + B01101101= 109, + B01101110= 110, + B01101111= 111, + B01110000= 112, + B01110001= 113, + B01110010= 114, + B01110011= 115, + B01110100= 116, + B01110101= 117, + B01110110= 118, + B01110111= 119, + B01111000= 120, + B01111001= 121, + B01111010= 122, + B01111011= 123, + B01111100= 124, + B01111101= 125, + B01111110= 126, + B01111111= 127, + B10000000= 128, + B10000001= 129, + B10000010= 130, + B10000011= 131, + B10000100= 132, + B10000101= 133, + B10000110= 134, + B10000111= 135, + B10001000= 136, + B10001001= 137, + B10001010= 138, + B10001011= 139, + B10001100= 140, + B10001101= 141, + B10001110= 142, + B10001111= 143, + B10010000= 144, + B10010001= 145, + B10010010= 146, + B10010011= 147, + B10010100= 148, + B10010101= 149, + B10010110= 150, + B10010111= 151, + B10011000= 152, + B10011001= 153, + B10011010= 154, + B10011011= 155, + B10011100= 156, + B10011101= 157, + B10011110= 158, + B10011111= 159, + B10100000= 160, + B10100001= 161, + B10100010= 162, + B10100011= 163, + B10100100= 164, + B10100101= 165, + B10100110= 166, + B10100111= 167, + B10101000= 168, + B10101001= 169, + B10101010= 170, + B10101011= 171, + B10101100= 172, + B10101101= 173, + B10101110= 174, + B10101111= 175, + B10110000= 176, + B10110001= 177, + B10110010= 178, + B10110011= 179, + B10110100= 180, + B10110101= 181, + B10110110= 182, + B10110111= 183, + B10111000= 184, + B10111001= 185, + B10111010= 186, + B10111011= 187, + B10111100= 188, + B10111101= 189, + B10111110= 190, + B10111111= 191, + B11000000= 192, + B11000001= 193, + B11000010= 194, + B11000011= 195, + B11000100= 196, + B11000101= 197, + B11000110= 198, + B11000111= 199, + B11001000= 200, + B11001001= 201, + B11001010= 202, + B11001011= 203, + B11001100= 204, + B11001101= 205, + B11001110= 206, + B11001111= 207, + B11010000= 208, + B11010001= 209, + B11010010= 210, + B11010011= 211, + B11010100= 212, + B11010101= 213, + B11010110= 214, + B11010111= 215, + B11011000= 216, + B11011001= 217, + B11011010= 218, + B11011011= 219, + B11011100= 220, + B11011101= 221, + B11011110= 222, + B11011111= 223, + B11100000= 224, + B11100001= 225, + B11100010= 226, + B11100011= 227, + B11100100= 228, + B11100101= 229, + B11100110= 230, + B11100111= 231, + B11101000= 232, + B11101001= 233, + B11101010= 234, + B11101011= 235, + B11101100= 236, + B11101101= 237, + B11101110= 238, + B11101111= 239, + B11110000= 240, + B11110001= 241, + B11110010= 242, + B11110011= 243, + B11110100= 244, + B11110101= 245, + B11110110= 246, + B11110111= 247, + B11111000= 248, + B11111001= 249, + B11111010= 250, + B11111011= 251, + B11111100= 252, + B11111101= 253, + B11111110= 254, + B11111111= 255 +}; diff --git a/plugins/GSdx/xbyak/xbyak_mnemonic.h b/plugins/GSdx/xbyak/xbyak_mnemonic.h index 2ae7a4ee19..e82d422b35 100644 --- a/plugins/GSdx/xbyak/xbyak_mnemonic.h +++ b/plugins/GSdx/xbyak/xbyak_mnemonic.h @@ -1,429 +1,429 @@ -const char *getVersionString() const { return "2.07"; } -void packssdw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x6B); } -void packsswb(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x63); } -void packuswb(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x67); } -void pand(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xDB); } -void pandn(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xDF); } -void pmaddwd(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xF5); } -void pmulhuw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xE4); } -void pmulhw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xE5); } -void pmullw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xD5); } -void por(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xEB); } -void punpckhbw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x68); } -void punpckhwd(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x69); } -void punpckhdq(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x6A); } -void punpcklbw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x60); } -void punpcklwd(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x61); } -void punpckldq(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x62); } -void pxor(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xEF); } -void pavgb(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xE0); } -void pavgw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xE3); } -void pmaxsw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xEE); } -void pmaxub(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xDE); } -void pminsw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xEA); } -void pminub(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xDA); } -void psadbw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xF6); } -void paddq(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xD4); } -void pmuludq(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xF4); } -void psubq(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xFB); } -void paddb(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xFC); } -void paddw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xFD); } -void paddd(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xFE); } -void paddsb(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xEC); } -void paddsw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xED); } -void paddusb(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xDC); } -void paddusw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xDD); } -void pcmpeqb(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x74); } -void pcmpeqw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x75); } -void pcmpeqd(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x76); } -void pcmpgtb(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x64); } -void pcmpgtw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x65); } -void pcmpgtd(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x66); } -void psllw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xF1); } -void pslld(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xF2); } -void psllq(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xF3); } -void psraw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xE1); } -void psrad(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xE2); } -void psrlw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xD1); } -void psrld(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xD2); } -void psrlq(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xD3); } -void psubb(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xF8); } -void psubw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xF9); } -void psubd(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xFA); } -void psubsb(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xE8); } -void psubsw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xE9); } -void psubusb(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xD8); } -void psubusw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xD9); } -void psllw(const Mmx& mmx, int imm8) { opMMX_IMM(mmx, imm8, 0x71, 6); } -void pslld(const Mmx& mmx, int imm8) { opMMX_IMM(mmx, imm8, 0x72, 6); } -void psllq(const Mmx& mmx, int imm8) { opMMX_IMM(mmx, imm8, 0x73, 6); } -void psraw(const Mmx& mmx, int imm8) { opMMX_IMM(mmx, imm8, 0x71, 4); } -void psrad(const Mmx& mmx, int imm8) { opMMX_IMM(mmx, imm8, 0x72, 4); } -void psrlw(const Mmx& mmx, int imm8) { opMMX_IMM(mmx, imm8, 0x71, 2); } -void psrld(const Mmx& mmx, int imm8) { opMMX_IMM(mmx, imm8, 0x72, 2); } -void psrlq(const Mmx& mmx, int imm8) { opMMX_IMM(mmx, imm8, 0x73, 2); } -void pslldq(const Xmm& xmm, int imm8) { opMMX_IMM(xmm, imm8, 0x73, 7); } -void psrldq(const Xmm& xmm, int imm8) { opMMX_IMM(xmm, imm8, 0x73, 3); } -void pshufw(const Mmx& mmx, const Operand& op, uint8 imm8) { opMMX(mmx, op, 0x70, 0x00, imm8); } -void pshuflw(const Mmx& mmx, const Operand& op, uint8 imm8) { opMMX(mmx, op, 0x70, 0xF2, imm8); } -void pshufhw(const Mmx& mmx, const Operand& op, uint8 imm8) { opMMX(mmx, op, 0x70, 0xF3, imm8); } -void pshufd(const Mmx& mmx, const Operand& op, uint8 imm8) { opMMX(mmx, op, 0x70, 0x66, imm8); } -void movdqa(const Xmm& xmm, const Operand& op) { opMMX(xmm, op, 0x6F, 0x66); } -void movdqa(const Address& addr, const Xmm& xmm) { db(0x66); opModM(addr, xmm, 0x0F, 0x7F); } -void movdqu(const Xmm& xmm, const Operand& op) { opMMX(xmm, op, 0x6F, 0xF3); } -void movdqu(const Address& addr, const Xmm& xmm) { db(0xF3); opModM(addr, xmm, 0x0F, 0x7F); } -void movaps(const Xmm& xmm, const Operand& op) { opMMX(xmm, op, 0x28, 0x100); } -void movaps(const Address& addr, const Xmm& xmm) { opModM(addr, xmm, 0x0F, 0x29); } -void movss(const Xmm& xmm, const Operand& op) { opMMX(xmm, op, 0x10, 0xF3); } -void movss(const Address& addr, const Xmm& xmm) { db(0xF3); opModM(addr, xmm, 0x0F, 0x11); } -void movups(const Xmm& xmm, const Operand& op) { opMMX(xmm, op, 0x10, 0x100); } -void movups(const Address& addr, const Xmm& xmm) { opModM(addr, xmm, 0x0F, 0x11); } -void movapd(const Xmm& xmm, const Operand& op) { opMMX(xmm, op, 0x28, 0x66); } -void movapd(const Address& addr, const Xmm& xmm) { db(0x66); opModM(addr, xmm, 0x0F, 0x29); } -void movsd(const Xmm& xmm, const Operand& op) { opMMX(xmm, op, 0x10, 0xF2); } -void movsd(const Address& addr, const Xmm& xmm) { db(0xF2); opModM(addr, xmm, 0x0F, 0x11); } -void movupd(const Xmm& xmm, const Operand& op) { opMMX(xmm, op, 0x10, 0x66); } -void movupd(const Address& addr, const Xmm& xmm) { db(0x66); opModM(addr, xmm, 0x0F, 0x11); } -void addps(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x58, 0x100, isXMM_XMMorMEM); } -void addss(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x58, 0xF3, isXMM_XMMorMEM); } -void addpd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x58, 0x66, isXMM_XMMorMEM); } -void addsd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x58, 0xF2, isXMM_XMMorMEM); } -void andnps(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x55, 0x100, isXMM_XMMorMEM); } -void andnpd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x55, 0x66, isXMM_XMMorMEM); } -void andps(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x54, 0x100, isXMM_XMMorMEM); } -void andpd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x54, 0x66, isXMM_XMMorMEM); } -void cmpps(const Xmm& xmm, const Operand& op, uint8 imm8) { opGen(xmm, op, 0xC2, 0x100, isXMM_XMMorMEM, imm8); } -void cmpss(const Xmm& xmm, const Operand& op, uint8 imm8) { opGen(xmm, op, 0xC2, 0xF3, isXMM_XMMorMEM, imm8); } -void cmppd(const Xmm& xmm, const Operand& op, uint8 imm8) { opGen(xmm, op, 0xC2, 0x66, isXMM_XMMorMEM, imm8); } -void cmpsd(const Xmm& xmm, const Operand& op, uint8 imm8) { opGen(xmm, op, 0xC2, 0xF2, isXMM_XMMorMEM, imm8); } -void divps(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5E, 0x100, isXMM_XMMorMEM); } -void divss(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5E, 0xF3, isXMM_XMMorMEM); } -void divpd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5E, 0x66, isXMM_XMMorMEM); } -void divsd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5E, 0xF2, isXMM_XMMorMEM); } -void maxps(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5F, 0x100, isXMM_XMMorMEM); } -void maxss(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5F, 0xF3, isXMM_XMMorMEM); } -void maxpd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5F, 0x66, isXMM_XMMorMEM); } -void maxsd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5F, 0xF2, isXMM_XMMorMEM); } -void minps(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5D, 0x100, isXMM_XMMorMEM); } -void minss(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5D, 0xF3, isXMM_XMMorMEM); } -void minpd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5D, 0x66, isXMM_XMMorMEM); } -void minsd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5D, 0xF2, isXMM_XMMorMEM); } -void mulps(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x59, 0x100, isXMM_XMMorMEM); } -void mulss(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x59, 0xF3, isXMM_XMMorMEM); } -void mulpd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x59, 0x66, isXMM_XMMorMEM); } -void mulsd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x59, 0xF2, isXMM_XMMorMEM); } -void orps(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x56, 0x100, isXMM_XMMorMEM); } -void orpd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x56, 0x66, isXMM_XMMorMEM); } -void rcpps(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x53, 0x100, isXMM_XMMorMEM); } -void rcpss(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x53, 0xF3, isXMM_XMMorMEM); } -void rsqrtps(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x52, 0x100, isXMM_XMMorMEM); } -void rsqrtss(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x52, 0xF3, isXMM_XMMorMEM); } -void shufps(const Xmm& xmm, const Operand& op, uint8 imm8) { opGen(xmm, op, 0xC6, 0x100, isXMM_XMMorMEM, imm8); } -void shufpd(const Xmm& xmm, const Operand& op, uint8 imm8) { opGen(xmm, op, 0xC6, 0x66, isXMM_XMMorMEM, imm8); } -void sqrtps(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x51, 0x100, isXMM_XMMorMEM); } -void sqrtss(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x51, 0xF3, isXMM_XMMorMEM); } -void sqrtpd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x51, 0x66, isXMM_XMMorMEM); } -void sqrtsd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x51, 0xF2, isXMM_XMMorMEM); } -void subps(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5C, 0x100, isXMM_XMMorMEM); } -void subss(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5C, 0xF3, isXMM_XMMorMEM); } -void subpd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5C, 0x66, isXMM_XMMorMEM); } -void subsd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5C, 0xF2, isXMM_XMMorMEM); } -void unpckhps(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x15, 0x100, isXMM_XMMorMEM); } -void unpckhpd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x15, 0x66, isXMM_XMMorMEM); } -void unpcklps(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x14, 0x100, isXMM_XMMorMEM); } -void unpcklpd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x14, 0x66, isXMM_XMMorMEM); } -void xorps(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x57, 0x100, isXMM_XMMorMEM); } -void xorpd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x57, 0x66, isXMM_XMMorMEM); } -void maskmovdqu(const Xmm& reg1, const Xmm& reg2) { db(0x66); opModR(reg1, reg2, 0x0F, 0xF7); } -void movhlps(const Xmm& reg1, const Xmm& reg2) { opModR(reg1, reg2, 0x0F, 0x12); } -void movlhps(const Xmm& reg1, const Xmm& reg2) { opModR(reg1, reg2, 0x0F, 0x16); } -void punpckhqdq(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x6D, 0x66, isXMM_XMMorMEM); } -void punpcklqdq(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x6C, 0x66, isXMM_XMMorMEM); } -void comiss(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x2F, 0x100, isXMM_XMMorMEM); } -void ucomiss(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x2E, 0x100, isXMM_XMMorMEM); } -void comisd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x2F, 0x66, isXMM_XMMorMEM); } -void ucomisd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x2E, 0x66, isXMM_XMMorMEM); } -void cvtpd2ps(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5A, 0x66, isXMM_XMMorMEM); } -void cvtps2pd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5A, 0x100, isXMM_XMMorMEM); } -void cvtsd2ss(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5A, 0xF2, isXMM_XMMorMEM); } -void cvtss2sd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5A, 0xF3, isXMM_XMMorMEM); } -void cvtpd2dq(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0xE6, 0xF2, isXMM_XMMorMEM); } -void cvttpd2dq(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0xE6, 0x66, isXMM_XMMorMEM); } -void cvtdq2pd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0xE6, 0xF3, isXMM_XMMorMEM); } -void cvtps2dq(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5B, 0x66, isXMM_XMMorMEM); } -void cvttps2dq(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5B, 0xF3, isXMM_XMMorMEM); } -void cvtdq2ps(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5B, 0x100, isXMM_XMMorMEM); } -void addsubpd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0xD0, 0x66, isXMM_XMMorMEM); } -void addsubps(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0xD0, 0xF2, isXMM_XMMorMEM); } -void haddpd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x7C, 0x66, isXMM_XMMorMEM); } -void haddps(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x7C, 0xF2, isXMM_XMMorMEM); } -void hsubpd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x7D, 0x66, isXMM_XMMorMEM); } -void hsubps(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x7D, 0xF2, isXMM_XMMorMEM); } -void movddup(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x12, 0xF2, isXMM_XMMorMEM); } -void movshdup(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x16, 0xF3, isXMM_XMMorMEM); } -void movsldup(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x12, 0xF3, isXMM_XMMorMEM); } -void cvtpi2ps(const Operand& reg, const Operand& op) { opGen(reg, op, 0x2A, 0x100, isXMM_MMXorMEM); } -void cvtps2pi(const Operand& reg, const Operand& op) { opGen(reg, op, 0x2D, 0x100, isMMX_XMMorMEM); } -void cvtsi2ss(const Operand& reg, const Operand& op) { opGen(reg, op, 0x2A, 0xF3, isXMM_REG32orMEM); } -void cvtss2si(const Operand& reg, const Operand& op) { opGen(reg, op, 0x2D, 0xF3, isREG32_XMMorMEM); } -void cvttps2pi(const Operand& reg, const Operand& op) { opGen(reg, op, 0x2C, 0x100, isMMX_XMMorMEM); } -void cvttss2si(const Operand& reg, const Operand& op) { opGen(reg, op, 0x2C, 0xF3, isREG32_XMMorMEM); } -void cvtpi2pd(const Operand& reg, const Operand& op) { opGen(reg, op, 0x2A, 0x66, isXMM_MMXorMEM); } -void cvtpd2pi(const Operand& reg, const Operand& op) { opGen(reg, op, 0x2D, 0x66, isMMX_XMMorMEM); } -void cvtsi2sd(const Operand& reg, const Operand& op) { opGen(reg, op, 0x2A, 0xF2, isXMM_REG32orMEM); } -void cvtsd2si(const Operand& reg, const Operand& op) { opGen(reg, op, 0x2D, 0xF2, isREG32_XMMorMEM); } -void cvttpd2pi(const Operand& reg, const Operand& op) { opGen(reg, op, 0x2C, 0x66, isMMX_XMMorMEM); } -void cvttsd2si(const Operand& reg, const Operand& op) { opGen(reg, op, 0x2C, 0xF2, isREG32_XMMorMEM); } -void prefetcht0(const Address& addr) { opModM(addr, Reg32(1), 0x0F, B00011000); } -void prefetcht1(const Address& addr) { opModM(addr, Reg32(2), 0x0F, B00011000); } -void prefetcht2(const Address& addr) { opModM(addr, Reg32(3), 0x0F, B00011000); } -void prefetchnta(const Address& addr) { opModM(addr, Reg32(0), 0x0F, B00011000); } -void movhps(const Operand& op1, const Operand& op2) { opMovXMM(op1, op2, 0x16, 0x100); } -void movlps(const Operand& op1, const Operand& op2) { opMovXMM(op1, op2, 0x12, 0x100); } -void movhpd(const Operand& op1, const Operand& op2) { opMovXMM(op1, op2, 0x16, 0x66); } -void movlpd(const Operand& op1, const Operand& op2) { opMovXMM(op1, op2, 0x12, 0x66); } -void cmovo(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 0); } -void jo(const char *label, LabelType type = T_AUTO) { opJmp(label, type, 0x70, 0x80, 0x0F); } -void seto(const Operand& op) { opR_ModM(op, 8, 3, 0, 0x0F, B10010000 | 0); } -void cmovno(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 1); } -void jno(const char *label, LabelType type = T_AUTO) { opJmp(label, type, 0x71, 0x81, 0x0F); } -void setno(const Operand& op) { opR_ModM(op, 8, 3, 0, 0x0F, B10010000 | 1); } -void cmovb(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 2); } -void jb(const char *label, LabelType type = T_AUTO) { opJmp(label, type, 0x72, 0x82, 0x0F); } -void setb(const Operand& op) { opR_ModM(op, 8, 3, 0, 0x0F, B10010000 | 2); } -void cmovnae(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 2); } -void jnae(const char *label, LabelType type = T_AUTO) { opJmp(label, type, 0x72, 0x82, 0x0F); } -void setnae(const Operand& op) { opR_ModM(op, 8, 3, 0, 0x0F, B10010000 | 2); } -void cmovnb(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 3); } -void jnb(const char *label, LabelType type = T_AUTO) { opJmp(label, type, 0x73, 0x83, 0x0F); } -void setnb(const Operand& op) { opR_ModM(op, 8, 3, 0, 0x0F, B10010000 | 3); } -void cmovae(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 3); } -void jae(const char *label, LabelType type = T_AUTO) { opJmp(label, type, 0x73, 0x83, 0x0F); } -void setae(const Operand& op) { opR_ModM(op, 8, 3, 0, 0x0F, B10010000 | 3); } -void cmove(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 4); } -void je(const char *label, LabelType type = T_AUTO) { opJmp(label, type, 0x74, 0x84, 0x0F); } -void sete(const Operand& op) { opR_ModM(op, 8, 3, 0, 0x0F, B10010000 | 4); } -void cmovz(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 4); } -void jz(const char *label, LabelType type = T_AUTO) { opJmp(label, type, 0x74, 0x84, 0x0F); } -void setz(const Operand& op) { opR_ModM(op, 8, 3, 0, 0x0F, B10010000 | 4); } -void cmovne(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 5); } -void jne(const char *label, LabelType type = T_AUTO) { opJmp(label, type, 0x75, 0x85, 0x0F); } -void setne(const Operand& op) { opR_ModM(op, 8, 3, 0, 0x0F, B10010000 | 5); } -void cmovnz(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 5); } -void jnz(const char *label, LabelType type = T_AUTO) { opJmp(label, type, 0x75, 0x85, 0x0F); } -void setnz(const Operand& op) { opR_ModM(op, 8, 3, 0, 0x0F, B10010000 | 5); } -void cmovbe(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 6); } -void jbe(const char *label, LabelType type = T_AUTO) { opJmp(label, type, 0x76, 0x86, 0x0F); } -void setbe(const Operand& op) { opR_ModM(op, 8, 3, 0, 0x0F, B10010000 | 6); } -void cmovna(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 6); } -void jna(const char *label, LabelType type = T_AUTO) { opJmp(label, type, 0x76, 0x86, 0x0F); } -void setna(const Operand& op) { opR_ModM(op, 8, 3, 0, 0x0F, B10010000 | 6); } -void cmovnbe(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 7); } -void jnbe(const char *label, LabelType type = T_AUTO) { opJmp(label, type, 0x77, 0x87, 0x0F); } -void setnbe(const Operand& op) { opR_ModM(op, 8, 3, 0, 0x0F, B10010000 | 7); } -void cmova(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 7); } -void ja(const char *label, LabelType type = T_AUTO) { opJmp(label, type, 0x77, 0x87, 0x0F); } -void seta(const Operand& op) { opR_ModM(op, 8, 3, 0, 0x0F, B10010000 | 7); } -void cmovs(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 8); } -void js(const char *label, LabelType type = T_AUTO) { opJmp(label, type, 0x78, 0x88, 0x0F); } -void sets(const Operand& op) { opR_ModM(op, 8, 3, 0, 0x0F, B10010000 | 8); } -void cmovns(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 9); } -void jns(const char *label, LabelType type = T_AUTO) { opJmp(label, type, 0x79, 0x89, 0x0F); } -void setns(const Operand& op) { opR_ModM(op, 8, 3, 0, 0x0F, B10010000 | 9); } -void cmovp(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 10); } -void jp(const char *label, LabelType type = T_AUTO) { opJmp(label, type, 0x7A, 0x8A, 0x0F); } -void setp(const Operand& op) { opR_ModM(op, 8, 3, 0, 0x0F, B10010000 | 10); } -void cmovpe(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 10); } -void jpe(const char *label, LabelType type = T_AUTO) { opJmp(label, type, 0x7A, 0x8A, 0x0F); } -void setpe(const Operand& op) { opR_ModM(op, 8, 3, 0, 0x0F, B10010000 | 10); } -void cmovnp(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 11); } -void jnp(const char *label, LabelType type = T_AUTO) { opJmp(label, type, 0x7B, 0x8B, 0x0F); } -void setnp(const Operand& op) { opR_ModM(op, 8, 3, 0, 0x0F, B10010000 | 11); } -void cmovpo(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 11); } -void jpo(const char *label, LabelType type = T_AUTO) { opJmp(label, type, 0x7B, 0x8B, 0x0F); } -void setpo(const Operand& op) { opR_ModM(op, 8, 3, 0, 0x0F, B10010000 | 11); } -void cmovl(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 12); } -void jl(const char *label, LabelType type = T_AUTO) { opJmp(label, type, 0x7C, 0x8C, 0x0F); } -void setl(const Operand& op) { opR_ModM(op, 8, 3, 0, 0x0F, B10010000 | 12); } -void cmovnge(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 12); } -void jnge(const char *label, LabelType type = T_AUTO) { opJmp(label, type, 0x7C, 0x8C, 0x0F); } -void setnge(const Operand& op) { opR_ModM(op, 8, 3, 0, 0x0F, B10010000 | 12); } -void cmovnl(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 13); } -void jnl(const char *label, LabelType type = T_AUTO) { opJmp(label, type, 0x7D, 0x8D, 0x0F); } -void setnl(const Operand& op) { opR_ModM(op, 8, 3, 0, 0x0F, B10010000 | 13); } -void cmovge(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 13); } -void jge(const char *label, LabelType type = T_AUTO) { opJmp(label, type, 0x7D, 0x8D, 0x0F); } -void setge(const Operand& op) { opR_ModM(op, 8, 3, 0, 0x0F, B10010000 | 13); } -void cmovle(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 14); } -void jle(const char *label, LabelType type = T_AUTO) { opJmp(label, type, 0x7E, 0x8E, 0x0F); } -void setle(const Operand& op) { opR_ModM(op, 8, 3, 0, 0x0F, B10010000 | 14); } -void cmovng(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 14); } -void jng(const char *label, LabelType type = T_AUTO) { opJmp(label, type, 0x7E, 0x8E, 0x0F); } -void setng(const Operand& op) { opR_ModM(op, 8, 3, 0, 0x0F, B10010000 | 14); } -void cmovnle(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 15); } -void jnle(const char *label, LabelType type = T_AUTO) { opJmp(label, type, 0x7F, 0x8F, 0x0F); } -void setnle(const Operand& op) { opR_ModM(op, 8, 3, 0, 0x0F, B10010000 | 15); } -void cmovg(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 15); } -void jg(const char *label, LabelType type = T_AUTO) { opJmp(label, type, 0x7F, 0x8F, 0x0F); } -void setg(const Operand& op) { opR_ModM(op, 8, 3, 0, 0x0F, B10010000 | 15); } -#ifdef XBYAK64 -void cdqe() { db(0x48); db(0x98); } -#else -void aaa() { db(0x37); } -void aad() { db(0xD5); db(0x0A); } -void aam() { db(0xD4); db(0x0A); } -void aas() { db(0x3F); } -void daa() { db(0x27); } -void das() { db(0x2F); } -void popad() { db(0x61); } -void popfd() { db(0x9D); } -void pusha() { db(0x60); } -void pushad() { db(0x60); } -void pushfd() { db(0x9C); } -void popa() { db(0x61); } -#endif -void cbw() { db(0x66); db(0x98); } -void cdq() { db(0x99); } -void clc() { db(0xF8); } -void cld() { db(0xFC); } -void cli() { db(0xFA); } -void cmc() { db(0xF5); } -void cpuid() { db(0x0F); db(0xA2); } -void cwd() { db(0x66); db(0x99); } -void cwde() { db(0x98); } -void lahf() { db(0x9F); } -void lock() { db(0xF0); } -void nop() { db(0x90); } -void sahf() { db(0x9E); } -void stc() { db(0xF9); } -void std() { db(0xFD); } -void sti() { db(0xFB); } -void emms() { db(0x0F); db(0x77); } -void pause() { db(0xF3); db(0x90); } -void sfence() { db(0x0F); db(0xAE); db(0xF8); } -void lfence() { db(0x0F); db(0xAE); db(0xE8); } -void mfence() { db(0x0F); db(0xAE); db(0xF0); } -void monitor() { db(0x0F); db(0x01); db(0xC8); } -void mwait() { db(0x0F); db(0x01); db(0xC9); } -void rdmsr() { db(0x0F); db(0x32); } -void rdpmc() { db(0x0F); db(0x33); } -void rdtsc() { db(0x0F); db(0x31); } -void wait() { db(0x9B); } -void wbinvd() { db(0x0F); db(0x09); } -void wrmsr() { db(0x0F); db(0x30); } -void xlatb() { db(0xD7); } -void popf() { db(0x9D); } -void pushf() { db(0x9C); } -void adc(const Operand& op1, const Operand& op2) { opRM_RM(op1, op2, 0x10); } -void adc(const Operand& op, uint32 imm) { opRM_I(op, imm, 0x10, 2); } -void add(const Operand& op1, const Operand& op2) { opRM_RM(op1, op2, 0x00); } -void add(const Operand& op, uint32 imm) { opRM_I(op, imm, 0x00, 0); } -void and(const Operand& op1, const Operand& op2) { opRM_RM(op1, op2, 0x20); } -void and(const Operand& op, uint32 imm) { opRM_I(op, imm, 0x20, 4); } -void cmp(const Operand& op1, const Operand& op2) { opRM_RM(op1, op2, 0x38); } -void cmp(const Operand& op, uint32 imm) { opRM_I(op, imm, 0x38, 7); } -void or(const Operand& op1, const Operand& op2) { opRM_RM(op1, op2, 0x08); } -void or(const Operand& op, uint32 imm) { opRM_I(op, imm, 0x08, 1); } -void sbb(const Operand& op1, const Operand& op2) { opRM_RM(op1, op2, 0x18); } -void sbb(const Operand& op, uint32 imm) { opRM_I(op, imm, 0x18, 3); } -void sub(const Operand& op1, const Operand& op2) { opRM_RM(op1, op2, 0x28); } -void sub(const Operand& op, uint32 imm) { opRM_I(op, imm, 0x28, 5); } -void xor(const Operand& op1, const Operand& op2) { opRM_RM(op1, op2, 0x30); } -void xor(const Operand& op, uint32 imm) { opRM_I(op, imm, 0x30, 6); } -void dec(const Operand& op) { opIncDec(op, 0x48, 1); } -void inc(const Operand& op) { opIncDec(op, 0x40, 0); } -void div(const Operand& op) { opR_ModM(op, 0, 3, 6, 0xF6); } -void idiv(const Operand& op) { opR_ModM(op, 0, 3, 7, 0xF6); } -void imul(const Operand& op) { opR_ModM(op, 0, 3, 5, 0xF6); } -void mul(const Operand& op) { opR_ModM(op, 0, 3, 4, 0xF6); } -void neg(const Operand& op) { opR_ModM(op, 0, 3, 3, 0xF6); } -void not(const Operand& op) { opR_ModM(op, 0, 3, 2, 0xF6); } -void rcl(const Operand& op, int imm) { opShift(op, imm, 2); } -void rcl(const Operand& op, const Reg8& cl) { opShift(op, cl, 2); } -void rcr(const Operand& op, int imm) { opShift(op, imm, 3); } -void rcr(const Operand& op, const Reg8& cl) { opShift(op, cl, 3); } -void rol(const Operand& op, int imm) { opShift(op, imm, 0); } -void rol(const Operand& op, const Reg8& cl) { opShift(op, cl, 0); } -void ror(const Operand& op, int imm) { opShift(op, imm, 1); } -void ror(const Operand& op, const Reg8& cl) { opShift(op, cl, 1); } -void sar(const Operand& op, int imm) { opShift(op, imm, 7); } -void sar(const Operand& op, const Reg8& cl) { opShift(op, cl, 7); } -void shl(const Operand& op, int imm) { opShift(op, imm, 4); } -void shl(const Operand& op, const Reg8& cl) { opShift(op, cl, 4); } -void shr(const Operand& op, int imm) { opShift(op, imm, 5); } -void shr(const Operand& op, const Reg8& cl) { opShift(op, cl, 5); } -void sal(const Operand& op, int imm) { opShift(op, imm, 4); } -void sal(const Operand& op, const Reg8& cl) { opShift(op, cl, 4); } -void shld(const Operand& op, const Reg& reg, uint8 imm) { opShxd(op, reg, imm, 0xA4); } -void shld(const Operand& op, const Reg& reg, const Reg8& cl) { opShxd(op, reg, 0, 0xA4, &cl); } -void shrd(const Operand& op, const Reg& reg, uint8 imm) { opShxd(op, reg, imm, 0xAC); } -void shrd(const Operand& op, const Reg& reg, const Reg8& cl) { opShxd(op, reg, 0, 0xAC, &cl); } -void bsf(const Reg®, const Operand& op) { opModRM(reg, op, op.isREG(16 | i32e), op.isMEM(), 0x0F, 0xBC); } -void bsr(const Reg®, const Operand& op) { opModRM(reg, op, op.isREG(16 | i32e), op.isMEM(), 0x0F, 0xBD); } -void pshufb(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x00, 0x66, 256, 0x38); } -void phaddw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x01, 0x66, 256, 0x38); } -void phaddd(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x02, 0x66, 256, 0x38); } -void phaddsw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x03, 0x66, 256, 0x38); } -void pmaddubsw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x04, 0x66, 256, 0x38); } -void phsubw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x05, 0x66, 256, 0x38); } -void phsubd(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x06, 0x66, 256, 0x38); } -void phsubsw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x07, 0x66, 256, 0x38); } -void psignb(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x08, 0x66, 256, 0x38); } -void psignw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x09, 0x66, 256, 0x38); } -void psignd(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x0A, 0x66, 256, 0x38); } -void pmulhrsw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x0B, 0x66, 256, 0x38); } -void pabsb(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x1C, 0x66, 256, 0x38); } -void pabsw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x1D, 0x66, 256, 0x38); } -void pabsd(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x1E, 0x66, 256, 0x38); } -void palignr(const Mmx& mmx, const Operand& op, int imm) { opMMX(mmx, op, 0x0f, 0x66, static_cast(imm), 0x3a); } -void blendvpd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x15, 0x66, isXMM_XMMorMEM, 256, 0x38); } -void blendvps(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x14, 0x66, isXMM_XMMorMEM, 256, 0x38); } -void packusdw(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x2B, 0x66, isXMM_XMMorMEM, 256, 0x38); } -void pblendvb(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x10, 0x66, isXMM_XMMorMEM, 256, 0x38); } -void pcmpeqq(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x29, 0x66, isXMM_XMMorMEM, 256, 0x38); } -void ptest(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x17, 0x66, isXMM_XMMorMEM, 256, 0x38); } -void pmovsxbw(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x20, 0x66, isXMM_XMMorMEM, 256, 0x38); } -void pmovsxbd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x21, 0x66, isXMM_XMMorMEM, 256, 0x38); } -void pmovsxbq(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x22, 0x66, isXMM_XMMorMEM, 256, 0x38); } -void pmovsxwd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x23, 0x66, isXMM_XMMorMEM, 256, 0x38); } -void pmovsxwq(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x24, 0x66, isXMM_XMMorMEM, 256, 0x38); } -void pmovsxdq(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x25, 0x66, isXMM_XMMorMEM, 256, 0x38); } -void pmovzxbw(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x30, 0x66, isXMM_XMMorMEM, 256, 0x38); } -void pmovzxbd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x31, 0x66, isXMM_XMMorMEM, 256, 0x38); } -void pmovzxbq(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x32, 0x66, isXMM_XMMorMEM, 256, 0x38); } -void pmovzxwd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x33, 0x66, isXMM_XMMorMEM, 256, 0x38); } -void pmovzxwq(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x34, 0x66, isXMM_XMMorMEM, 256, 0x38); } -void pmovzxdq(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x35, 0x66, isXMM_XMMorMEM, 256, 0x38); } -void pminsb(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x38, 0x66, isXMM_XMMorMEM, 256, 0x38); } -void pminsd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x39, 0x66, isXMM_XMMorMEM, 256, 0x38); } -void pminuw(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x3A, 0x66, isXMM_XMMorMEM, 256, 0x38); } -void pminud(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x3B, 0x66, isXMM_XMMorMEM, 256, 0x38); } -void pmaxsb(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x3C, 0x66, isXMM_XMMorMEM, 256, 0x38); } -void pmaxsd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x3D, 0x66, isXMM_XMMorMEM, 256, 0x38); } -void pmaxuw(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x3E, 0x66, isXMM_XMMorMEM, 256, 0x38); } -void pmaxud(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x3F, 0x66, isXMM_XMMorMEM, 256, 0x38); } -void pmuldq(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x28, 0x66, isXMM_XMMorMEM, 256, 0x38); } -void pmulld(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x40, 0x66, isXMM_XMMorMEM, 256, 0x38); } -void phminposuw(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x41, 0x66, isXMM_XMMorMEM, 256, 0x38); } -void pcmpgtq(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x37, 0x66, isXMM_XMMorMEM, 256, 0x38); } -void blendpd(const Xmm& xmm, const Operand& op, int imm) { opGen(xmm, op, 0x0D, 0x66, isXMM_XMMorMEM, static_cast(imm), 0x3A); } -void blendps(const Xmm& xmm, const Operand& op, int imm) { opGen(xmm, op, 0x0C, 0x66, isXMM_XMMorMEM, static_cast(imm), 0x3A); } -void dppd(const Xmm& xmm, const Operand& op, int imm) { opGen(xmm, op, 0x41, 0x66, isXMM_XMMorMEM, static_cast(imm), 0x3A); } -void dpps(const Xmm& xmm, const Operand& op, int imm) { opGen(xmm, op, 0x40, 0x66, isXMM_XMMorMEM, static_cast(imm), 0x3A); } -void mpsadbw(const Xmm& xmm, const Operand& op, int imm) { opGen(xmm, op, 0x42, 0x66, isXMM_XMMorMEM, static_cast(imm), 0x3A); } -void pblendw(const Xmm& xmm, const Operand& op, int imm) { opGen(xmm, op, 0x0E, 0x66, isXMM_XMMorMEM, static_cast(imm), 0x3A); } -void roundps(const Xmm& xmm, const Operand& op, int imm) { opGen(xmm, op, 0x08, 0x66, isXMM_XMMorMEM, static_cast(imm), 0x3A); } -void roundpd(const Xmm& xmm, const Operand& op, int imm) { opGen(xmm, op, 0x09, 0x66, isXMM_XMMorMEM, static_cast(imm), 0x3A); } -void roundss(const Xmm& xmm, const Operand& op, int imm) { opGen(xmm, op, 0x0A, 0x66, isXMM_XMMorMEM, static_cast(imm), 0x3A); } -void roundsd(const Xmm& xmm, const Operand& op, int imm) { opGen(xmm, op, 0x0B, 0x66, isXMM_XMMorMEM, static_cast(imm), 0x3A); } -void pcmpestrm(const Xmm& xmm, const Operand& op, int imm) { opGen(xmm, op, 0x60, 0x66, isXMM_XMMorMEM, static_cast(imm), 0x3A); } -void pcmpestri(const Xmm& xmm, const Operand& op, int imm) { opGen(xmm, op, 0x61, 0x66, isXMM_XMMorMEM, static_cast(imm), 0x3A); } -void pcmpistrm(const Xmm& xmm, const Operand& op, int imm) { opGen(xmm, op, 0x62, 0x66, isXMM_XMMorMEM, static_cast(imm), 0x3A); } -void pcmpistri(const Xmm& xmm, const Operand& op, int imm) { opGen(xmm, op, 0x63, 0x66, isXMM_XMMorMEM, static_cast(imm), 0x3A); } -void ldmxcsr(const Address& addr) { opModM(addr, Reg32(2), 0x0F, 0xAE); } -void stmxcsr(const Address& addr) { opModM(addr, Reg32(3), 0x0F, 0xAE); } -void clflush(const Address& addr) { opModM(addr, Reg32(7), 0x0F, 0xAE); } -void movntpd(const Address& addr, const Xmm& reg) { opModM(addr, Reg16(reg.getIdx()), 0x0F, 0x2B); } -void movntdq(const Address& addr, const Xmm& reg) { opModM(addr, Reg16(reg.getIdx()), 0x0F, 0xE7); } -void movsx(const Reg& reg, const Operand& op) { opMovxx(reg, op, 0xBE); } -void movzx(const Reg& reg, const Operand& op) { opMovxx(reg, op, 0xB6); } +const char *getVersionString() const { return "2.07"; } +void packssdw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x6B); } +void packsswb(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x63); } +void packuswb(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x67); } +void pand(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xDB); } +void pandn(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xDF); } +void pmaddwd(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xF5); } +void pmulhuw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xE4); } +void pmulhw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xE5); } +void pmullw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xD5); } +void por(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xEB); } +void punpckhbw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x68); } +void punpckhwd(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x69); } +void punpckhdq(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x6A); } +void punpcklbw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x60); } +void punpcklwd(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x61); } +void punpckldq(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x62); } +void pxor(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xEF); } +void pavgb(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xE0); } +void pavgw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xE3); } +void pmaxsw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xEE); } +void pmaxub(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xDE); } +void pminsw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xEA); } +void pminub(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xDA); } +void psadbw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xF6); } +void paddq(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xD4); } +void pmuludq(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xF4); } +void psubq(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xFB); } +void paddb(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xFC); } +void paddw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xFD); } +void paddd(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xFE); } +void paddsb(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xEC); } +void paddsw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xED); } +void paddusb(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xDC); } +void paddusw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xDD); } +void pcmpeqb(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x74); } +void pcmpeqw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x75); } +void pcmpeqd(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x76); } +void pcmpgtb(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x64); } +void pcmpgtw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x65); } +void pcmpgtd(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x66); } +void psllw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xF1); } +void pslld(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xF2); } +void psllq(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xF3); } +void psraw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xE1); } +void psrad(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xE2); } +void psrlw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xD1); } +void psrld(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xD2); } +void psrlq(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xD3); } +void psubb(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xF8); } +void psubw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xF9); } +void psubd(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xFA); } +void psubsb(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xE8); } +void psubsw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xE9); } +void psubusb(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xD8); } +void psubusw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xD9); } +void psllw(const Mmx& mmx, int imm8) { opMMX_IMM(mmx, imm8, 0x71, 6); } +void pslld(const Mmx& mmx, int imm8) { opMMX_IMM(mmx, imm8, 0x72, 6); } +void psllq(const Mmx& mmx, int imm8) { opMMX_IMM(mmx, imm8, 0x73, 6); } +void psraw(const Mmx& mmx, int imm8) { opMMX_IMM(mmx, imm8, 0x71, 4); } +void psrad(const Mmx& mmx, int imm8) { opMMX_IMM(mmx, imm8, 0x72, 4); } +void psrlw(const Mmx& mmx, int imm8) { opMMX_IMM(mmx, imm8, 0x71, 2); } +void psrld(const Mmx& mmx, int imm8) { opMMX_IMM(mmx, imm8, 0x72, 2); } +void psrlq(const Mmx& mmx, int imm8) { opMMX_IMM(mmx, imm8, 0x73, 2); } +void pslldq(const Xmm& xmm, int imm8) { opMMX_IMM(xmm, imm8, 0x73, 7); } +void psrldq(const Xmm& xmm, int imm8) { opMMX_IMM(xmm, imm8, 0x73, 3); } +void pshufw(const Mmx& mmx, const Operand& op, uint8 imm8) { opMMX(mmx, op, 0x70, 0x00, imm8); } +void pshuflw(const Mmx& mmx, const Operand& op, uint8 imm8) { opMMX(mmx, op, 0x70, 0xF2, imm8); } +void pshufhw(const Mmx& mmx, const Operand& op, uint8 imm8) { opMMX(mmx, op, 0x70, 0xF3, imm8); } +void pshufd(const Mmx& mmx, const Operand& op, uint8 imm8) { opMMX(mmx, op, 0x70, 0x66, imm8); } +void movdqa(const Xmm& xmm, const Operand& op) { opMMX(xmm, op, 0x6F, 0x66); } +void movdqa(const Address& addr, const Xmm& xmm) { db(0x66); opModM(addr, xmm, 0x0F, 0x7F); } +void movdqu(const Xmm& xmm, const Operand& op) { opMMX(xmm, op, 0x6F, 0xF3); } +void movdqu(const Address& addr, const Xmm& xmm) { db(0xF3); opModM(addr, xmm, 0x0F, 0x7F); } +void movaps(const Xmm& xmm, const Operand& op) { opMMX(xmm, op, 0x28, 0x100); } +void movaps(const Address& addr, const Xmm& xmm) { opModM(addr, xmm, 0x0F, 0x29); } +void movss(const Xmm& xmm, const Operand& op) { opMMX(xmm, op, 0x10, 0xF3); } +void movss(const Address& addr, const Xmm& xmm) { db(0xF3); opModM(addr, xmm, 0x0F, 0x11); } +void movups(const Xmm& xmm, const Operand& op) { opMMX(xmm, op, 0x10, 0x100); } +void movups(const Address& addr, const Xmm& xmm) { opModM(addr, xmm, 0x0F, 0x11); } +void movapd(const Xmm& xmm, const Operand& op) { opMMX(xmm, op, 0x28, 0x66); } +void movapd(const Address& addr, const Xmm& xmm) { db(0x66); opModM(addr, xmm, 0x0F, 0x29); } +void movsd(const Xmm& xmm, const Operand& op) { opMMX(xmm, op, 0x10, 0xF2); } +void movsd(const Address& addr, const Xmm& xmm) { db(0xF2); opModM(addr, xmm, 0x0F, 0x11); } +void movupd(const Xmm& xmm, const Operand& op) { opMMX(xmm, op, 0x10, 0x66); } +void movupd(const Address& addr, const Xmm& xmm) { db(0x66); opModM(addr, xmm, 0x0F, 0x11); } +void addps(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x58, 0x100, isXMM_XMMorMEM); } +void addss(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x58, 0xF3, isXMM_XMMorMEM); } +void addpd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x58, 0x66, isXMM_XMMorMEM); } +void addsd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x58, 0xF2, isXMM_XMMorMEM); } +void andnps(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x55, 0x100, isXMM_XMMorMEM); } +void andnpd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x55, 0x66, isXMM_XMMorMEM); } +void andps(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x54, 0x100, isXMM_XMMorMEM); } +void andpd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x54, 0x66, isXMM_XMMorMEM); } +void cmpps(const Xmm& xmm, const Operand& op, uint8 imm8) { opGen(xmm, op, 0xC2, 0x100, isXMM_XMMorMEM, imm8); } +void cmpss(const Xmm& xmm, const Operand& op, uint8 imm8) { opGen(xmm, op, 0xC2, 0xF3, isXMM_XMMorMEM, imm8); } +void cmppd(const Xmm& xmm, const Operand& op, uint8 imm8) { opGen(xmm, op, 0xC2, 0x66, isXMM_XMMorMEM, imm8); } +void cmpsd(const Xmm& xmm, const Operand& op, uint8 imm8) { opGen(xmm, op, 0xC2, 0xF2, isXMM_XMMorMEM, imm8); } +void divps(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5E, 0x100, isXMM_XMMorMEM); } +void divss(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5E, 0xF3, isXMM_XMMorMEM); } +void divpd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5E, 0x66, isXMM_XMMorMEM); } +void divsd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5E, 0xF2, isXMM_XMMorMEM); } +void maxps(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5F, 0x100, isXMM_XMMorMEM); } +void maxss(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5F, 0xF3, isXMM_XMMorMEM); } +void maxpd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5F, 0x66, isXMM_XMMorMEM); } +void maxsd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5F, 0xF2, isXMM_XMMorMEM); } +void minps(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5D, 0x100, isXMM_XMMorMEM); } +void minss(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5D, 0xF3, isXMM_XMMorMEM); } +void minpd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5D, 0x66, isXMM_XMMorMEM); } +void minsd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5D, 0xF2, isXMM_XMMorMEM); } +void mulps(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x59, 0x100, isXMM_XMMorMEM); } +void mulss(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x59, 0xF3, isXMM_XMMorMEM); } +void mulpd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x59, 0x66, isXMM_XMMorMEM); } +void mulsd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x59, 0xF2, isXMM_XMMorMEM); } +void orps(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x56, 0x100, isXMM_XMMorMEM); } +void orpd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x56, 0x66, isXMM_XMMorMEM); } +void rcpps(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x53, 0x100, isXMM_XMMorMEM); } +void rcpss(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x53, 0xF3, isXMM_XMMorMEM); } +void rsqrtps(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x52, 0x100, isXMM_XMMorMEM); } +void rsqrtss(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x52, 0xF3, isXMM_XMMorMEM); } +void shufps(const Xmm& xmm, const Operand& op, uint8 imm8) { opGen(xmm, op, 0xC6, 0x100, isXMM_XMMorMEM, imm8); } +void shufpd(const Xmm& xmm, const Operand& op, uint8 imm8) { opGen(xmm, op, 0xC6, 0x66, isXMM_XMMorMEM, imm8); } +void sqrtps(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x51, 0x100, isXMM_XMMorMEM); } +void sqrtss(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x51, 0xF3, isXMM_XMMorMEM); } +void sqrtpd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x51, 0x66, isXMM_XMMorMEM); } +void sqrtsd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x51, 0xF2, isXMM_XMMorMEM); } +void subps(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5C, 0x100, isXMM_XMMorMEM); } +void subss(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5C, 0xF3, isXMM_XMMorMEM); } +void subpd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5C, 0x66, isXMM_XMMorMEM); } +void subsd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5C, 0xF2, isXMM_XMMorMEM); } +void unpckhps(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x15, 0x100, isXMM_XMMorMEM); } +void unpckhpd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x15, 0x66, isXMM_XMMorMEM); } +void unpcklps(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x14, 0x100, isXMM_XMMorMEM); } +void unpcklpd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x14, 0x66, isXMM_XMMorMEM); } +void xorps(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x57, 0x100, isXMM_XMMorMEM); } +void xorpd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x57, 0x66, isXMM_XMMorMEM); } +void maskmovdqu(const Xmm& reg1, const Xmm& reg2) { db(0x66); opModR(reg1, reg2, 0x0F, 0xF7); } +void movhlps(const Xmm& reg1, const Xmm& reg2) { opModR(reg1, reg2, 0x0F, 0x12); } +void movlhps(const Xmm& reg1, const Xmm& reg2) { opModR(reg1, reg2, 0x0F, 0x16); } +void punpckhqdq(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x6D, 0x66, isXMM_XMMorMEM); } +void punpcklqdq(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x6C, 0x66, isXMM_XMMorMEM); } +void comiss(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x2F, 0x100, isXMM_XMMorMEM); } +void ucomiss(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x2E, 0x100, isXMM_XMMorMEM); } +void comisd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x2F, 0x66, isXMM_XMMorMEM); } +void ucomisd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x2E, 0x66, isXMM_XMMorMEM); } +void cvtpd2ps(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5A, 0x66, isXMM_XMMorMEM); } +void cvtps2pd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5A, 0x100, isXMM_XMMorMEM); } +void cvtsd2ss(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5A, 0xF2, isXMM_XMMorMEM); } +void cvtss2sd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5A, 0xF3, isXMM_XMMorMEM); } +void cvtpd2dq(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0xE6, 0xF2, isXMM_XMMorMEM); } +void cvttpd2dq(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0xE6, 0x66, isXMM_XMMorMEM); } +void cvtdq2pd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0xE6, 0xF3, isXMM_XMMorMEM); } +void cvtps2dq(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5B, 0x66, isXMM_XMMorMEM); } +void cvttps2dq(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5B, 0xF3, isXMM_XMMorMEM); } +void cvtdq2ps(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5B, 0x100, isXMM_XMMorMEM); } +void addsubpd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0xD0, 0x66, isXMM_XMMorMEM); } +void addsubps(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0xD0, 0xF2, isXMM_XMMorMEM); } +void haddpd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x7C, 0x66, isXMM_XMMorMEM); } +void haddps(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x7C, 0xF2, isXMM_XMMorMEM); } +void hsubpd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x7D, 0x66, isXMM_XMMorMEM); } +void hsubps(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x7D, 0xF2, isXMM_XMMorMEM); } +void movddup(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x12, 0xF2, isXMM_XMMorMEM); } +void movshdup(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x16, 0xF3, isXMM_XMMorMEM); } +void movsldup(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x12, 0xF3, isXMM_XMMorMEM); } +void cvtpi2ps(const Operand& reg, const Operand& op) { opGen(reg, op, 0x2A, 0x100, isXMM_MMXorMEM); } +void cvtps2pi(const Operand& reg, const Operand& op) { opGen(reg, op, 0x2D, 0x100, isMMX_XMMorMEM); } +void cvtsi2ss(const Operand& reg, const Operand& op) { opGen(reg, op, 0x2A, 0xF3, isXMM_REG32orMEM); } +void cvtss2si(const Operand& reg, const Operand& op) { opGen(reg, op, 0x2D, 0xF3, isREG32_XMMorMEM); } +void cvttps2pi(const Operand& reg, const Operand& op) { opGen(reg, op, 0x2C, 0x100, isMMX_XMMorMEM); } +void cvttss2si(const Operand& reg, const Operand& op) { opGen(reg, op, 0x2C, 0xF3, isREG32_XMMorMEM); } +void cvtpi2pd(const Operand& reg, const Operand& op) { opGen(reg, op, 0x2A, 0x66, isXMM_MMXorMEM); } +void cvtpd2pi(const Operand& reg, const Operand& op) { opGen(reg, op, 0x2D, 0x66, isMMX_XMMorMEM); } +void cvtsi2sd(const Operand& reg, const Operand& op) { opGen(reg, op, 0x2A, 0xF2, isXMM_REG32orMEM); } +void cvtsd2si(const Operand& reg, const Operand& op) { opGen(reg, op, 0x2D, 0xF2, isREG32_XMMorMEM); } +void cvttpd2pi(const Operand& reg, const Operand& op) { opGen(reg, op, 0x2C, 0x66, isMMX_XMMorMEM); } +void cvttsd2si(const Operand& reg, const Operand& op) { opGen(reg, op, 0x2C, 0xF2, isREG32_XMMorMEM); } +void prefetcht0(const Address& addr) { opModM(addr, Reg32(1), 0x0F, B00011000); } +void prefetcht1(const Address& addr) { opModM(addr, Reg32(2), 0x0F, B00011000); } +void prefetcht2(const Address& addr) { opModM(addr, Reg32(3), 0x0F, B00011000); } +void prefetchnta(const Address& addr) { opModM(addr, Reg32(0), 0x0F, B00011000); } +void movhps(const Operand& op1, const Operand& op2) { opMovXMM(op1, op2, 0x16, 0x100); } +void movlps(const Operand& op1, const Operand& op2) { opMovXMM(op1, op2, 0x12, 0x100); } +void movhpd(const Operand& op1, const Operand& op2) { opMovXMM(op1, op2, 0x16, 0x66); } +void movlpd(const Operand& op1, const Operand& op2) { opMovXMM(op1, op2, 0x12, 0x66); } +void cmovo(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 0); } +void jo(const char *label, LabelType type = T_AUTO) { opJmp(label, type, 0x70, 0x80, 0x0F); } +void seto(const Operand& op) { opR_ModM(op, 8, 3, 0, 0x0F, B10010000 | 0); } +void cmovno(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 1); } +void jno(const char *label, LabelType type = T_AUTO) { opJmp(label, type, 0x71, 0x81, 0x0F); } +void setno(const Operand& op) { opR_ModM(op, 8, 3, 0, 0x0F, B10010000 | 1); } +void cmovb(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 2); } +void jb(const char *label, LabelType type = T_AUTO) { opJmp(label, type, 0x72, 0x82, 0x0F); } +void setb(const Operand& op) { opR_ModM(op, 8, 3, 0, 0x0F, B10010000 | 2); } +void cmovnae(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 2); } +void jnae(const char *label, LabelType type = T_AUTO) { opJmp(label, type, 0x72, 0x82, 0x0F); } +void setnae(const Operand& op) { opR_ModM(op, 8, 3, 0, 0x0F, B10010000 | 2); } +void cmovnb(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 3); } +void jnb(const char *label, LabelType type = T_AUTO) { opJmp(label, type, 0x73, 0x83, 0x0F); } +void setnb(const Operand& op) { opR_ModM(op, 8, 3, 0, 0x0F, B10010000 | 3); } +void cmovae(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 3); } +void jae(const char *label, LabelType type = T_AUTO) { opJmp(label, type, 0x73, 0x83, 0x0F); } +void setae(const Operand& op) { opR_ModM(op, 8, 3, 0, 0x0F, B10010000 | 3); } +void cmove(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 4); } +void je(const char *label, LabelType type = T_AUTO) { opJmp(label, type, 0x74, 0x84, 0x0F); } +void sete(const Operand& op) { opR_ModM(op, 8, 3, 0, 0x0F, B10010000 | 4); } +void cmovz(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 4); } +void jz(const char *label, LabelType type = T_AUTO) { opJmp(label, type, 0x74, 0x84, 0x0F); } +void setz(const Operand& op) { opR_ModM(op, 8, 3, 0, 0x0F, B10010000 | 4); } +void cmovne(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 5); } +void jne(const char *label, LabelType type = T_AUTO) { opJmp(label, type, 0x75, 0x85, 0x0F); } +void setne(const Operand& op) { opR_ModM(op, 8, 3, 0, 0x0F, B10010000 | 5); } +void cmovnz(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 5); } +void jnz(const char *label, LabelType type = T_AUTO) { opJmp(label, type, 0x75, 0x85, 0x0F); } +void setnz(const Operand& op) { opR_ModM(op, 8, 3, 0, 0x0F, B10010000 | 5); } +void cmovbe(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 6); } +void jbe(const char *label, LabelType type = T_AUTO) { opJmp(label, type, 0x76, 0x86, 0x0F); } +void setbe(const Operand& op) { opR_ModM(op, 8, 3, 0, 0x0F, B10010000 | 6); } +void cmovna(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 6); } +void jna(const char *label, LabelType type = T_AUTO) { opJmp(label, type, 0x76, 0x86, 0x0F); } +void setna(const Operand& op) { opR_ModM(op, 8, 3, 0, 0x0F, B10010000 | 6); } +void cmovnbe(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 7); } +void jnbe(const char *label, LabelType type = T_AUTO) { opJmp(label, type, 0x77, 0x87, 0x0F); } +void setnbe(const Operand& op) { opR_ModM(op, 8, 3, 0, 0x0F, B10010000 | 7); } +void cmova(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 7); } +void ja(const char *label, LabelType type = T_AUTO) { opJmp(label, type, 0x77, 0x87, 0x0F); } +void seta(const Operand& op) { opR_ModM(op, 8, 3, 0, 0x0F, B10010000 | 7); } +void cmovs(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 8); } +void js(const char *label, LabelType type = T_AUTO) { opJmp(label, type, 0x78, 0x88, 0x0F); } +void sets(const Operand& op) { opR_ModM(op, 8, 3, 0, 0x0F, B10010000 | 8); } +void cmovns(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 9); } +void jns(const char *label, LabelType type = T_AUTO) { opJmp(label, type, 0x79, 0x89, 0x0F); } +void setns(const Operand& op) { opR_ModM(op, 8, 3, 0, 0x0F, B10010000 | 9); } +void cmovp(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 10); } +void jp(const char *label, LabelType type = T_AUTO) { opJmp(label, type, 0x7A, 0x8A, 0x0F); } +void setp(const Operand& op) { opR_ModM(op, 8, 3, 0, 0x0F, B10010000 | 10); } +void cmovpe(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 10); } +void jpe(const char *label, LabelType type = T_AUTO) { opJmp(label, type, 0x7A, 0x8A, 0x0F); } +void setpe(const Operand& op) { opR_ModM(op, 8, 3, 0, 0x0F, B10010000 | 10); } +void cmovnp(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 11); } +void jnp(const char *label, LabelType type = T_AUTO) { opJmp(label, type, 0x7B, 0x8B, 0x0F); } +void setnp(const Operand& op) { opR_ModM(op, 8, 3, 0, 0x0F, B10010000 | 11); } +void cmovpo(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 11); } +void jpo(const char *label, LabelType type = T_AUTO) { opJmp(label, type, 0x7B, 0x8B, 0x0F); } +void setpo(const Operand& op) { opR_ModM(op, 8, 3, 0, 0x0F, B10010000 | 11); } +void cmovl(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 12); } +void jl(const char *label, LabelType type = T_AUTO) { opJmp(label, type, 0x7C, 0x8C, 0x0F); } +void setl(const Operand& op) { opR_ModM(op, 8, 3, 0, 0x0F, B10010000 | 12); } +void cmovnge(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 12); } +void jnge(const char *label, LabelType type = T_AUTO) { opJmp(label, type, 0x7C, 0x8C, 0x0F); } +void setnge(const Operand& op) { opR_ModM(op, 8, 3, 0, 0x0F, B10010000 | 12); } +void cmovnl(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 13); } +void jnl(const char *label, LabelType type = T_AUTO) { opJmp(label, type, 0x7D, 0x8D, 0x0F); } +void setnl(const Operand& op) { opR_ModM(op, 8, 3, 0, 0x0F, B10010000 | 13); } +void cmovge(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 13); } +void jge(const char *label, LabelType type = T_AUTO) { opJmp(label, type, 0x7D, 0x8D, 0x0F); } +void setge(const Operand& op) { opR_ModM(op, 8, 3, 0, 0x0F, B10010000 | 13); } +void cmovle(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 14); } +void jle(const char *label, LabelType type = T_AUTO) { opJmp(label, type, 0x7E, 0x8E, 0x0F); } +void setle(const Operand& op) { opR_ModM(op, 8, 3, 0, 0x0F, B10010000 | 14); } +void cmovng(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 14); } +void jng(const char *label, LabelType type = T_AUTO) { opJmp(label, type, 0x7E, 0x8E, 0x0F); } +void setng(const Operand& op) { opR_ModM(op, 8, 3, 0, 0x0F, B10010000 | 14); } +void cmovnle(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 15); } +void jnle(const char *label, LabelType type = T_AUTO) { opJmp(label, type, 0x7F, 0x8F, 0x0F); } +void setnle(const Operand& op) { opR_ModM(op, 8, 3, 0, 0x0F, B10010000 | 15); } +void cmovg(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 15); } +void jg(const char *label, LabelType type = T_AUTO) { opJmp(label, type, 0x7F, 0x8F, 0x0F); } +void setg(const Operand& op) { opR_ModM(op, 8, 3, 0, 0x0F, B10010000 | 15); } +#ifdef XBYAK64 +void cdqe() { db(0x48); db(0x98); } +#else +void aaa() { db(0x37); } +void aad() { db(0xD5); db(0x0A); } +void aam() { db(0xD4); db(0x0A); } +void aas() { db(0x3F); } +void daa() { db(0x27); } +void das() { db(0x2F); } +void popad() { db(0x61); } +void popfd() { db(0x9D); } +void pusha() { db(0x60); } +void pushad() { db(0x60); } +void pushfd() { db(0x9C); } +void popa() { db(0x61); } +#endif +void cbw() { db(0x66); db(0x98); } +void cdq() { db(0x99); } +void clc() { db(0xF8); } +void cld() { db(0xFC); } +void cli() { db(0xFA); } +void cmc() { db(0xF5); } +void cpuid() { db(0x0F); db(0xA2); } +void cwd() { db(0x66); db(0x99); } +void cwde() { db(0x98); } +void lahf() { db(0x9F); } +void lock() { db(0xF0); } +void nop() { db(0x90); } +void sahf() { db(0x9E); } +void stc() { db(0xF9); } +void std() { db(0xFD); } +void sti() { db(0xFB); } +void emms() { db(0x0F); db(0x77); } +void pause() { db(0xF3); db(0x90); } +void sfence() { db(0x0F); db(0xAE); db(0xF8); } +void lfence() { db(0x0F); db(0xAE); db(0xE8); } +void mfence() { db(0x0F); db(0xAE); db(0xF0); } +void monitor() { db(0x0F); db(0x01); db(0xC8); } +void mwait() { db(0x0F); db(0x01); db(0xC9); } +void rdmsr() { db(0x0F); db(0x32); } +void rdpmc() { db(0x0F); db(0x33); } +void rdtsc() { db(0x0F); db(0x31); } +void wait() { db(0x9B); } +void wbinvd() { db(0x0F); db(0x09); } +void wrmsr() { db(0x0F); db(0x30); } +void xlatb() { db(0xD7); } +void popf() { db(0x9D); } +void pushf() { db(0x9C); } +void adc(const Operand& op1, const Operand& op2) { opRM_RM(op1, op2, 0x10); } +void adc(const Operand& op, uint32 imm) { opRM_I(op, imm, 0x10, 2); } +void add(const Operand& op1, const Operand& op2) { opRM_RM(op1, op2, 0x00); } +void add(const Operand& op, uint32 imm) { opRM_I(op, imm, 0x00, 0); } +void and(const Operand& op1, const Operand& op2) { opRM_RM(op1, op2, 0x20); } +void and(const Operand& op, uint32 imm) { opRM_I(op, imm, 0x20, 4); } +void cmp(const Operand& op1, const Operand& op2) { opRM_RM(op1, op2, 0x38); } +void cmp(const Operand& op, uint32 imm) { opRM_I(op, imm, 0x38, 7); } +void or(const Operand& op1, const Operand& op2) { opRM_RM(op1, op2, 0x08); } +void or(const Operand& op, uint32 imm) { opRM_I(op, imm, 0x08, 1); } +void sbb(const Operand& op1, const Operand& op2) { opRM_RM(op1, op2, 0x18); } +void sbb(const Operand& op, uint32 imm) { opRM_I(op, imm, 0x18, 3); } +void sub(const Operand& op1, const Operand& op2) { opRM_RM(op1, op2, 0x28); } +void sub(const Operand& op, uint32 imm) { opRM_I(op, imm, 0x28, 5); } +void xor(const Operand& op1, const Operand& op2) { opRM_RM(op1, op2, 0x30); } +void xor(const Operand& op, uint32 imm) { opRM_I(op, imm, 0x30, 6); } +void dec(const Operand& op) { opIncDec(op, 0x48, 1); } +void inc(const Operand& op) { opIncDec(op, 0x40, 0); } +void div(const Operand& op) { opR_ModM(op, 0, 3, 6, 0xF6); } +void idiv(const Operand& op) { opR_ModM(op, 0, 3, 7, 0xF6); } +void imul(const Operand& op) { opR_ModM(op, 0, 3, 5, 0xF6); } +void mul(const Operand& op) { opR_ModM(op, 0, 3, 4, 0xF6); } +void neg(const Operand& op) { opR_ModM(op, 0, 3, 3, 0xF6); } +void not(const Operand& op) { opR_ModM(op, 0, 3, 2, 0xF6); } +void rcl(const Operand& op, int imm) { opShift(op, imm, 2); } +void rcl(const Operand& op, const Reg8& cl) { opShift(op, cl, 2); } +void rcr(const Operand& op, int imm) { opShift(op, imm, 3); } +void rcr(const Operand& op, const Reg8& cl) { opShift(op, cl, 3); } +void rol(const Operand& op, int imm) { opShift(op, imm, 0); } +void rol(const Operand& op, const Reg8& cl) { opShift(op, cl, 0); } +void ror(const Operand& op, int imm) { opShift(op, imm, 1); } +void ror(const Operand& op, const Reg8& cl) { opShift(op, cl, 1); } +void sar(const Operand& op, int imm) { opShift(op, imm, 7); } +void sar(const Operand& op, const Reg8& cl) { opShift(op, cl, 7); } +void shl(const Operand& op, int imm) { opShift(op, imm, 4); } +void shl(const Operand& op, const Reg8& cl) { opShift(op, cl, 4); } +void shr(const Operand& op, int imm) { opShift(op, imm, 5); } +void shr(const Operand& op, const Reg8& cl) { opShift(op, cl, 5); } +void sal(const Operand& op, int imm) { opShift(op, imm, 4); } +void sal(const Operand& op, const Reg8& cl) { opShift(op, cl, 4); } +void shld(const Operand& op, const Reg& reg, uint8 imm) { opShxd(op, reg, imm, 0xA4); } +void shld(const Operand& op, const Reg& reg, const Reg8& cl) { opShxd(op, reg, 0, 0xA4, &cl); } +void shrd(const Operand& op, const Reg& reg, uint8 imm) { opShxd(op, reg, imm, 0xAC); } +void shrd(const Operand& op, const Reg& reg, const Reg8& cl) { opShxd(op, reg, 0, 0xAC, &cl); } +void bsf(const Reg®, const Operand& op) { opModRM(reg, op, op.isREG(16 | i32e), op.isMEM(), 0x0F, 0xBC); } +void bsr(const Reg®, const Operand& op) { opModRM(reg, op, op.isREG(16 | i32e), op.isMEM(), 0x0F, 0xBD); } +void pshufb(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x00, 0x66, 256, 0x38); } +void phaddw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x01, 0x66, 256, 0x38); } +void phaddd(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x02, 0x66, 256, 0x38); } +void phaddsw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x03, 0x66, 256, 0x38); } +void pmaddubsw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x04, 0x66, 256, 0x38); } +void phsubw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x05, 0x66, 256, 0x38); } +void phsubd(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x06, 0x66, 256, 0x38); } +void phsubsw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x07, 0x66, 256, 0x38); } +void psignb(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x08, 0x66, 256, 0x38); } +void psignw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x09, 0x66, 256, 0x38); } +void psignd(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x0A, 0x66, 256, 0x38); } +void pmulhrsw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x0B, 0x66, 256, 0x38); } +void pabsb(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x1C, 0x66, 256, 0x38); } +void pabsw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x1D, 0x66, 256, 0x38); } +void pabsd(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x1E, 0x66, 256, 0x38); } +void palignr(const Mmx& mmx, const Operand& op, int imm) { opMMX(mmx, op, 0x0f, 0x66, static_cast(imm), 0x3a); } +void blendvpd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x15, 0x66, isXMM_XMMorMEM, 256, 0x38); } +void blendvps(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x14, 0x66, isXMM_XMMorMEM, 256, 0x38); } +void packusdw(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x2B, 0x66, isXMM_XMMorMEM, 256, 0x38); } +void pblendvb(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x10, 0x66, isXMM_XMMorMEM, 256, 0x38); } +void pcmpeqq(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x29, 0x66, isXMM_XMMorMEM, 256, 0x38); } +void ptest(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x17, 0x66, isXMM_XMMorMEM, 256, 0x38); } +void pmovsxbw(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x20, 0x66, isXMM_XMMorMEM, 256, 0x38); } +void pmovsxbd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x21, 0x66, isXMM_XMMorMEM, 256, 0x38); } +void pmovsxbq(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x22, 0x66, isXMM_XMMorMEM, 256, 0x38); } +void pmovsxwd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x23, 0x66, isXMM_XMMorMEM, 256, 0x38); } +void pmovsxwq(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x24, 0x66, isXMM_XMMorMEM, 256, 0x38); } +void pmovsxdq(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x25, 0x66, isXMM_XMMorMEM, 256, 0x38); } +void pmovzxbw(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x30, 0x66, isXMM_XMMorMEM, 256, 0x38); } +void pmovzxbd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x31, 0x66, isXMM_XMMorMEM, 256, 0x38); } +void pmovzxbq(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x32, 0x66, isXMM_XMMorMEM, 256, 0x38); } +void pmovzxwd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x33, 0x66, isXMM_XMMorMEM, 256, 0x38); } +void pmovzxwq(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x34, 0x66, isXMM_XMMorMEM, 256, 0x38); } +void pmovzxdq(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x35, 0x66, isXMM_XMMorMEM, 256, 0x38); } +void pminsb(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x38, 0x66, isXMM_XMMorMEM, 256, 0x38); } +void pminsd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x39, 0x66, isXMM_XMMorMEM, 256, 0x38); } +void pminuw(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x3A, 0x66, isXMM_XMMorMEM, 256, 0x38); } +void pminud(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x3B, 0x66, isXMM_XMMorMEM, 256, 0x38); } +void pmaxsb(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x3C, 0x66, isXMM_XMMorMEM, 256, 0x38); } +void pmaxsd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x3D, 0x66, isXMM_XMMorMEM, 256, 0x38); } +void pmaxuw(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x3E, 0x66, isXMM_XMMorMEM, 256, 0x38); } +void pmaxud(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x3F, 0x66, isXMM_XMMorMEM, 256, 0x38); } +void pmuldq(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x28, 0x66, isXMM_XMMorMEM, 256, 0x38); } +void pmulld(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x40, 0x66, isXMM_XMMorMEM, 256, 0x38); } +void phminposuw(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x41, 0x66, isXMM_XMMorMEM, 256, 0x38); } +void pcmpgtq(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x37, 0x66, isXMM_XMMorMEM, 256, 0x38); } +void blendpd(const Xmm& xmm, const Operand& op, int imm) { opGen(xmm, op, 0x0D, 0x66, isXMM_XMMorMEM, static_cast(imm), 0x3A); } +void blendps(const Xmm& xmm, const Operand& op, int imm) { opGen(xmm, op, 0x0C, 0x66, isXMM_XMMorMEM, static_cast(imm), 0x3A); } +void dppd(const Xmm& xmm, const Operand& op, int imm) { opGen(xmm, op, 0x41, 0x66, isXMM_XMMorMEM, static_cast(imm), 0x3A); } +void dpps(const Xmm& xmm, const Operand& op, int imm) { opGen(xmm, op, 0x40, 0x66, isXMM_XMMorMEM, static_cast(imm), 0x3A); } +void mpsadbw(const Xmm& xmm, const Operand& op, int imm) { opGen(xmm, op, 0x42, 0x66, isXMM_XMMorMEM, static_cast(imm), 0x3A); } +void pblendw(const Xmm& xmm, const Operand& op, int imm) { opGen(xmm, op, 0x0E, 0x66, isXMM_XMMorMEM, static_cast(imm), 0x3A); } +void roundps(const Xmm& xmm, const Operand& op, int imm) { opGen(xmm, op, 0x08, 0x66, isXMM_XMMorMEM, static_cast(imm), 0x3A); } +void roundpd(const Xmm& xmm, const Operand& op, int imm) { opGen(xmm, op, 0x09, 0x66, isXMM_XMMorMEM, static_cast(imm), 0x3A); } +void roundss(const Xmm& xmm, const Operand& op, int imm) { opGen(xmm, op, 0x0A, 0x66, isXMM_XMMorMEM, static_cast(imm), 0x3A); } +void roundsd(const Xmm& xmm, const Operand& op, int imm) { opGen(xmm, op, 0x0B, 0x66, isXMM_XMMorMEM, static_cast(imm), 0x3A); } +void pcmpestrm(const Xmm& xmm, const Operand& op, int imm) { opGen(xmm, op, 0x60, 0x66, isXMM_XMMorMEM, static_cast(imm), 0x3A); } +void pcmpestri(const Xmm& xmm, const Operand& op, int imm) { opGen(xmm, op, 0x61, 0x66, isXMM_XMMorMEM, static_cast(imm), 0x3A); } +void pcmpistrm(const Xmm& xmm, const Operand& op, int imm) { opGen(xmm, op, 0x62, 0x66, isXMM_XMMorMEM, static_cast(imm), 0x3A); } +void pcmpistri(const Xmm& xmm, const Operand& op, int imm) { opGen(xmm, op, 0x63, 0x66, isXMM_XMMorMEM, static_cast(imm), 0x3A); } +void ldmxcsr(const Address& addr) { opModM(addr, Reg32(2), 0x0F, 0xAE); } +void stmxcsr(const Address& addr) { opModM(addr, Reg32(3), 0x0F, 0xAE); } +void clflush(const Address& addr) { opModM(addr, Reg32(7), 0x0F, 0xAE); } +void movntpd(const Address& addr, const Xmm& reg) { opModM(addr, Reg16(reg.getIdx()), 0x0F, 0x2B); } +void movntdq(const Address& addr, const Xmm& reg) { opModM(addr, Reg16(reg.getIdx()), 0x0F, 0xE7); } +void movsx(const Reg& reg, const Operand& op) { opMovxx(reg, op, 0xBE); } +void movzx(const Reg& reg, const Operand& op) { opMovxx(reg, op, 0xB6); } diff --git a/plugins/GSdx/xbyak/xbyak_util.h b/plugins/GSdx/xbyak/xbyak_util.h index da0ceb54f0..ba091687d0 100644 --- a/plugins/GSdx/xbyak/xbyak_util.h +++ b/plugins/GSdx/xbyak/xbyak_util.h @@ -1,185 +1,185 @@ -#ifndef XBYAK_XBYAK_UTIL_H_ -#define XBYAK_XBYAK_UTIL_H_ - -/** - utility class for Xbyak - @note this header is under construction -*/ - -#ifdef _WIN32 - #if (_MSC_VER < 1400) && defined(XBYAK32) - static inline __declspec(naked) void __cpuid(int[4], int) - { - __asm { - push ebx - push esi - mov eax, dword ptr [esp + 4 * 2 + 8] // eaxIn - cpuid - mov esi, dword ptr [esp + 4 * 2 + 4] // data - mov dword ptr [esi], eax - mov dword ptr [esi + 4], ebx - mov dword ptr [esi + 8], ecx - mov dword ptr [esi + 12], edx - pop esi - pop ebx - ret - } - } - #else - #include // for __cpuid - #endif -#else - #if __GNUC_PREREQ(4, 3) - #include - #else - #define __cpuid(eaxIn, a, b, c, d) __asm__("cpuid\n" : "=a"(a), "=b"(b), "=c"(c), "=d"(d) : "0"(eaxIn)) - #endif -#endif - -namespace Xbyak { namespace util { - -/** - CPU detection class -*/ -class Cpu { - unsigned int type_; -public: - static inline void getCpuid(unsigned int eaxIn, unsigned int data[4]) - { -#ifdef _WIN32 - __cpuid(reinterpret_cast(data), eaxIn); -#else - __cpuid(eaxIn, data[0], data[1], data[2], data[3]); -#endif - } - enum Type { - NONE = 0, - tMMX = 1 << 0, - tMMX2 = 1 << 1, - tCMOV = 1 << 2, - tSSE = 1 << 3, - tSSE2 = 1 << 4, - tSSE3 = 1 << 5, - tSSSE3 = 1 << 6, - tSSE41 = 1 << 7, - tSSE42 = 1 << 8, - tPOPCNT = 1 << 9, - - t3DN = 1 << 16, - tE3DN = 1 << 17, - tSSE4a = 1 << 18, - tSSE5 = 1 << 11, - - tINTEL = 1 << 24, - tAMD = 1 << 25 - }; - Cpu() - : type_(NONE) - { - unsigned int data[4]; - getCpuid(0, data); - static const char intel[] = "ntel"; - static const char amd[] = "cAMD"; - if (data[2] == *reinterpret_cast(amd)) { - type_ |= tAMD; - getCpuid(0x80000001, data); - if (data[3] & (1 << 31)) type_ |= t3DN; - if (data[3] & (1 << 15)) type_ |= tCMOV; - if (data[3] & (1 << 30)) type_ |= tE3DN; - if (data[3] & (1 << 22)) type_ |= tMMX2; - } - if (data[2] == *reinterpret_cast(intel)) { - type_ |= tINTEL; - } - getCpuid(1, data); - if (data[2] & (1 << 0)) type_ |= tSSE3; - if (data[2] & (1 << 9)) type_ |= tSSSE3; - if (data[2] & (1 << 19)) type_ |= tSSE41; - if (data[2] & (1 << 20)) type_ |= tSSE42; - if (data[2] & (1 << 23)) type_ |= tPOPCNT; - - if (data[3] & (1 << 15)) type_ |= tCMOV; - if (data[3] & (1 << 23)) type_ |= tMMX; - if (data[3] & (1 << 25)) type_ |= tMMX2 | tSSE; - if (data[3] & (1 << 26)) type_ |= tSSE2; - } - bool has(Type type) const - { - return (type & type_) != 0; - } -}; - -#ifdef XBYAK32 - -namespace local { -#ifdef _WIN32 - #define XBYAK_LOCAL_DEFINE_SET_EIP_TO_REG(x) static inline __declspec(naked) void set_eip_to_ ## x() { \ - __asm { mov x, dword ptr [esp] } __asm { ret } \ - } -#else - #define XBYAK_LOCAL_DEFINE_SET_EIP_TO_REG(x) static inline void set_eip_to_ ## x() { \ - __asm__ volatile("movl (%esp), %" #x); \ - } -#endif - -XBYAK_LOCAL_DEFINE_SET_EIP_TO_REG(eax) -XBYAK_LOCAL_DEFINE_SET_EIP_TO_REG(ecx) -XBYAK_LOCAL_DEFINE_SET_EIP_TO_REG(edx) -XBYAK_LOCAL_DEFINE_SET_EIP_TO_REG(ebx) -XBYAK_LOCAL_DEFINE_SET_EIP_TO_REG(esi) -XBYAK_LOCAL_DEFINE_SET_EIP_TO_REG(edi) -XBYAK_LOCAL_DEFINE_SET_EIP_TO_REG(ebp) - -#undef XBYAK_LOCAL_DEFINE_SET_EIP_TO_REG -} // end of local - -template -struct EnableSetEip : public Gen { - EnableSetEip(size_t maxSize = DEFAULT_MAX_CODE_SIZE, void *userPtr = 0) - : Gen(maxSize, userPtr) - { - } - /** - get pid to out register - @note out = eax or ecx or edx - */ - void setEipTo(const Xbyak::Reg32& out) - { -#if 0 - Gen::call(Gen::getCurr() + 5); - Gen::pop(out); -#else - int idx = out.getIdx(); - switch (idx) { - case Xbyak::Operand::EAX: - Gen::call((void*)local::set_eip_to_eax); - break; - case Xbyak::Operand::ECX: - Gen::call((void*)local::set_eip_to_ecx); - break; - case Xbyak::Operand::EDX: - Gen::call((void*)local::set_eip_to_edx); - break; - case Xbyak::Operand::EBX: - Gen::call((void*)local::set_eip_to_ebx); - break; - case Xbyak::Operand::ESI: - Gen::call((void*)local::set_eip_to_esi); - break; - case Xbyak::Operand::EDI: - Gen::call((void*)local::set_eip_to_edi); - break; - case Xbyak::Operand::EBP: - Gen::call((void*)local::set_eip_to_ebp); - break; - default: - assert(0); - } -#endif - } -}; -#endif - -} } // end of util -#endif - +#ifndef XBYAK_XBYAK_UTIL_H_ +#define XBYAK_XBYAK_UTIL_H_ + +/** + utility class for Xbyak + @note this header is under construction +*/ + +#ifdef _WIN32 + #if (_MSC_VER < 1400) && defined(XBYAK32) + static inline __declspec(naked) void __cpuid(int[4], int) + { + __asm { + push ebx + push esi + mov eax, dword ptr [esp + 4 * 2 + 8] // eaxIn + cpuid + mov esi, dword ptr [esp + 4 * 2 + 4] // data + mov dword ptr [esi], eax + mov dword ptr [esi + 4], ebx + mov dword ptr [esi + 8], ecx + mov dword ptr [esi + 12], edx + pop esi + pop ebx + ret + } + } + #else + #include // for __cpuid + #endif +#else + #if __GNUC_PREREQ(4, 3) + #include + #else + #define __cpuid(eaxIn, a, b, c, d) __asm__("cpuid\n" : "=a"(a), "=b"(b), "=c"(c), "=d"(d) : "0"(eaxIn)) + #endif +#endif + +namespace Xbyak { namespace util { + +/** + CPU detection class +*/ +class Cpu { + unsigned int type_; +public: + static inline void getCpuid(unsigned int eaxIn, unsigned int data[4]) + { +#ifdef _WIN32 + __cpuid(reinterpret_cast(data), eaxIn); +#else + __cpuid(eaxIn, data[0], data[1], data[2], data[3]); +#endif + } + enum Type { + NONE = 0, + tMMX = 1 << 0, + tMMX2 = 1 << 1, + tCMOV = 1 << 2, + tSSE = 1 << 3, + tSSE2 = 1 << 4, + tSSE3 = 1 << 5, + tSSSE3 = 1 << 6, + tSSE41 = 1 << 7, + tSSE42 = 1 << 8, + tPOPCNT = 1 << 9, + + t3DN = 1 << 16, + tE3DN = 1 << 17, + tSSE4a = 1 << 18, + tSSE5 = 1 << 11, + + tINTEL = 1 << 24, + tAMD = 1 << 25 + }; + Cpu() + : type_(NONE) + { + unsigned int data[4]; + getCpuid(0, data); + static const char intel[] = "ntel"; + static const char amd[] = "cAMD"; + if (data[2] == *reinterpret_cast(amd)) { + type_ |= tAMD; + getCpuid(0x80000001, data); + if (data[3] & (1 << 31)) type_ |= t3DN; + if (data[3] & (1 << 15)) type_ |= tCMOV; + if (data[3] & (1 << 30)) type_ |= tE3DN; + if (data[3] & (1 << 22)) type_ |= tMMX2; + } + if (data[2] == *reinterpret_cast(intel)) { + type_ |= tINTEL; + } + getCpuid(1, data); + if (data[2] & (1 << 0)) type_ |= tSSE3; + if (data[2] & (1 << 9)) type_ |= tSSSE3; + if (data[2] & (1 << 19)) type_ |= tSSE41; + if (data[2] & (1 << 20)) type_ |= tSSE42; + if (data[2] & (1 << 23)) type_ |= tPOPCNT; + + if (data[3] & (1 << 15)) type_ |= tCMOV; + if (data[3] & (1 << 23)) type_ |= tMMX; + if (data[3] & (1 << 25)) type_ |= tMMX2 | tSSE; + if (data[3] & (1 << 26)) type_ |= tSSE2; + } + bool has(Type type) const + { + return (type & type_) != 0; + } +}; + +#ifdef XBYAK32 + +namespace local { +#ifdef _WIN32 + #define XBYAK_LOCAL_DEFINE_SET_EIP_TO_REG(x) static inline __declspec(naked) void set_eip_to_ ## x() { \ + __asm { mov x, dword ptr [esp] } __asm { ret } \ + } +#else + #define XBYAK_LOCAL_DEFINE_SET_EIP_TO_REG(x) static inline void set_eip_to_ ## x() { \ + __asm__ volatile("movl (%esp), %" #x); \ + } +#endif + +XBYAK_LOCAL_DEFINE_SET_EIP_TO_REG(eax) +XBYAK_LOCAL_DEFINE_SET_EIP_TO_REG(ecx) +XBYAK_LOCAL_DEFINE_SET_EIP_TO_REG(edx) +XBYAK_LOCAL_DEFINE_SET_EIP_TO_REG(ebx) +XBYAK_LOCAL_DEFINE_SET_EIP_TO_REG(esi) +XBYAK_LOCAL_DEFINE_SET_EIP_TO_REG(edi) +XBYAK_LOCAL_DEFINE_SET_EIP_TO_REG(ebp) + +#undef XBYAK_LOCAL_DEFINE_SET_EIP_TO_REG +} // end of local + +template +struct EnableSetEip : public Gen { + EnableSetEip(size_t maxSize = DEFAULT_MAX_CODE_SIZE, void *userPtr = 0) + : Gen(maxSize, userPtr) + { + } + /** + get pid to out register + @note out = eax or ecx or edx + */ + void setEipTo(const Xbyak::Reg32& out) + { +#if 0 + Gen::call(Gen::getCurr() + 5); + Gen::pop(out); +#else + int idx = out.getIdx(); + switch (idx) { + case Xbyak::Operand::EAX: + Gen::call((void*)local::set_eip_to_eax); + break; + case Xbyak::Operand::ECX: + Gen::call((void*)local::set_eip_to_ecx); + break; + case Xbyak::Operand::EDX: + Gen::call((void*)local::set_eip_to_edx); + break; + case Xbyak::Operand::EBX: + Gen::call((void*)local::set_eip_to_ebx); + break; + case Xbyak::Operand::ESI: + Gen::call((void*)local::set_eip_to_esi); + break; + case Xbyak::Operand::EDI: + Gen::call((void*)local::set_eip_to_edi); + break; + case Xbyak::Operand::EBP: + Gen::call((void*)local::set_eip_to_ebp); + break; + default: + assert(0); + } +#endif + } +}; +#endif + +} } // end of util +#endif + diff --git a/plugins/GSnull/Linux/GSLinux.cpp b/plugins/GSnull/Linux/GSLinux.cpp index 5abcf08ff0..72af62cbce 100644 --- a/plugins/GSnull/Linux/GSLinux.cpp +++ b/plugins/GSnull/Linux/GSLinux.cpp @@ -1,37 +1,37 @@ -/* GSnull - * Copyright (C) 2002-2009 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 "GS.h" -#include "GSLinux.h" - -Display *display; +/* GSnull + * Copyright (C) 2002-2009 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 "GS.h" +#include "GSLinux.h" + +Display *display; int screen; -GtkScrolledWindow *win; - -int GSOpenWindow(void *pDsp, char *Title) -{ - display = XOpenDisplay(0); - screen = DefaultScreen(display); - - if (pDsp != NULL) - *(Display**)pDsp = display; - else - return -1; - - return 0; -} +GtkScrolledWindow *win; + +int GSOpenWindow(void *pDsp, char *Title) +{ + display = XOpenDisplay(0); + screen = DefaultScreen(display); + + if (pDsp != NULL) + *(Display**)pDsp = display; + else + return -1; + + return 0; +} int GSOpenWindow2(void *pDsp, u32 flags) { @@ -43,75 +43,75 @@ int GSOpenWindow2(void *pDsp, u32 flags) return 0; } - -void GSCloseWindow() -{ - XCloseDisplay(display); -} - -void GSProcessMessages() -{ - if ( GSKeyEvent ) - { - int myKeyEvent = GSKeyEvent; - bool myShift = GSShift; - GSKeyEvent = 0; - - switch ( myKeyEvent ) - { - case XK_F5: - OnKeyboardF5(myShift); - break; - case XK_F6: - OnKeyboardF6(myShift); - break; - case XK_F7: - OnKeyboardF7(myShift); - break; - case XK_F9: - OnKeyboardF9(myShift); - break; - } - } -} - - -void HandleKeyEvent(keyEvent *ev) -{ - switch(ev->evt) - { - case KEYPRESS: - switch(ev->key) - { - case XK_F5: - case XK_F6: - case XK_F7: - case XK_F9: - GSKeyEvent = ev->key ; - break; - case XK_Escape: - break; - case XK_Shift_L: - case XK_Shift_R: - GSShift = true; - break; - case XK_Alt_L: - case XK_Alt_R: - GSAlt = true; - break; - } - break; - case KEYRELEASE: - switch(ev->key) - { - case XK_Shift_L: - case XK_Shift_R: - GSShift = false; - break; - case XK_Alt_L: - case XK_Alt_R: - GSAlt = false; - break; - } - } -} + +void GSCloseWindow() +{ + XCloseDisplay(display); +} + +void GSProcessMessages() +{ + if ( GSKeyEvent ) + { + int myKeyEvent = GSKeyEvent; + bool myShift = GSShift; + GSKeyEvent = 0; + + switch ( myKeyEvent ) + { + case XK_F5: + OnKeyboardF5(myShift); + break; + case XK_F6: + OnKeyboardF6(myShift); + break; + case XK_F7: + OnKeyboardF7(myShift); + break; + case XK_F9: + OnKeyboardF9(myShift); + break; + } + } +} + + +void HandleKeyEvent(keyEvent *ev) +{ + switch(ev->evt) + { + case KEYPRESS: + switch(ev->key) + { + case XK_F5: + case XK_F6: + case XK_F7: + case XK_F9: + GSKeyEvent = ev->key ; + break; + case XK_Escape: + break; + case XK_Shift_L: + case XK_Shift_R: + GSShift = true; + break; + case XK_Alt_L: + case XK_Alt_R: + GSAlt = true; + break; + } + break; + case KEYRELEASE: + switch(ev->key) + { + case XK_Shift_L: + case XK_Shift_R: + GSShift = false; + break; + case XK_Alt_L: + case XK_Alt_R: + GSAlt = false; + break; + } + } +} diff --git a/plugins/GSnull/Windows/GSwin.h b/plugins/GSnull/Windows/GSwin.h index 4069f6919c..8ebc1ff4a7 100644 --- a/plugins/GSnull/Windows/GSwin.h +++ b/plugins/GSnull/Windows/GSwin.h @@ -1,25 +1,25 @@ -/* GSnull - * Copyright (C) 2002-2009 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 . - */ - -#pragma once - -#include -#include - -extern int GSOpenWindow(void *pDsp, char *Title); -extern void GSCloseWindow(); -extern void GSProcessMessages(); -extern void HandleKeyEvent(keyEvent *ev); - +/* GSnull + * Copyright (C) 2002-2009 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 . + */ + +#pragma once + +#include +#include + +extern int GSOpenWindow(void *pDsp, char *Title); +extern void GSCloseWindow(); +extern void GSProcessMessages(); +extern void HandleKeyEvent(keyEvent *ev); + diff --git a/plugins/LilyPad/Config.cpp b/plugins/LilyPad/Config.cpp index dcb7c7db52..ea159a5b09 100644 --- a/plugins/LilyPad/Config.cpp +++ b/plugins/LilyPad/Config.cpp @@ -1,2079 +1,2079 @@ -#include "Global.h" - -#include "resource.h" -#include "InputManager.h" -#include "Config.h" - -#include "Diagnostics.h" -#include "DeviceEnumerator.h" -#include "KeyboardQueue.h" -#include "WndProcEater.h" -#include "DualShock3.h" - -// Needed to know if raw input is available. It requires XP or higher. -#include "RawInput.h" - -GeneralConfig config; - -// 1 if running inside a PS2 emulator. Set to 1 on any -// of the PS2-specific functions (PS2EgetLibVersion2, PS2EgetLibType). -// Only affects if I allow read input in GS thread to be set. -u8 ps2e = 0; - -HWND hWndProp = 0; - -int selected = 0; - -// Older versions of PCSX2 don't always create the ini dir on startup, so LilyPad does it -// for it. But if PCSX2 sets the ini path with a call to setSettingsDir, then it means -// we shouldn't make our own. -bool createIniDir = true; - -HWND hWnds[2][4]; -HWND hWndGeneral = 0; - -struct GeneralSettingsBool { - wchar_t *name; - unsigned int ControlId; - u8 defaultValue; -}; - -// Ties together config data structure, config files, and general config -// dialog. -const GeneralSettingsBool BoolOptionsInfo[] = { - {L"Force Cursor Hide", IDC_FORCE_HIDE, 0}, - {L"Mouse Unfocus", IDC_MOUSE_UNFOCUS, 1}, - {L"Background", IDC_BACKGROUND, 1}, - {L"Multiple Bindings", IDC_MULTIPLE_BINDING, 0}, - - {L"DirectInput Game Devices", IDC_G_DI, 1}, - {L"XInput", IDC_G_XI, 1}, - {L"DualShock 3", IDC_G_DS3, 0}, - - {L"Multitap 1", IDC_MULTITAP1, 0}, - {L"Multitap 2", IDC_MULTITAP2, 0}, - - {L"Escape Fullscreen Hack", IDC_ESCAPE_FULLSCREEN_HACK, 1}, - {L"Disable Screen Saver", IDC_DISABLE_SCREENSAVER, 1}, - {L"Logging", IDC_DEBUG_FILE, 0}, - - {L"Save State in Title", IDC_SAVE_STATE_TITLE, 1}, - {L"GH2", IDC_GH2_HACK, 0}, - - {L"Vista Volume", IDC_VISTA_VOLUME, 1}, -}; - -void Populate(int port, int slot); - -void SetupLogSlider(HWND hWndSlider) { - SendMessage(hWndSlider, TBM_SETRANGEMIN, 0, 1); - SendMessage(hWndSlider, TBM_SETPAGESIZE, 0, 1<<13); - SendMessage(hWndSlider, TBM_SETRANGEMAX, 1, 1<<22); - SendMessage(hWndSlider, TBM_SETTIC, 0, 1<<21); - SendMessage(hWndSlider, TBM_SETTIC, 0, 1<<20); - SendMessage(hWndSlider, TBM_SETTIC, 0, 3<<20); -} - -int GetLogSliderVal(HWND hWnd, int id) { - HWND hWndSlider = GetDlgItem(hWnd, id); - int val = SendMessage(hWndSlider, TBM_GETPOS, 0, 0); - if (val <= (1<<21)) { - val = (val+16) >> 5; - } - else { - double v = (val - (1<<21))/(double)((1<<22)- (1<<21)); - double max = log((double)(1<<23)); - double min = log((double)BASE_SENSITIVITY); - v = v*(max-min)+min; - // exp is not implemented in ntdll, so use pow instead. - // More than accurate enough for my needs. - // v = exp(v); - v = pow(2.7182818284590451, v); - if (v > (1<<23)) v = (1<<23); - val = (int)floor(v+0.5); - } - if (!val) val = 1; - if (IsDlgButtonChecked(hWnd, id+1) == BST_CHECKED) { - val = -val; - } - return val; -} - -void SetLogSliderVal(HWND hWnd, int id, HWND hWndText, int val) { - HWND hWndSlider = GetDlgItem(hWnd, id); - int sliderPos = 0; - wchar_t temp[30]; - EnableWindow(hWndSlider, val != 0); - EnableWindow(hWndText, val != 0); - EnableWindow(GetDlgItem(hWnd, id+1), val != 0); - if (!val) val = BASE_SENSITIVITY; - CheckDlgButton(hWnd, id+1, BST_CHECKED * (val<0)); - if (val < 0) { - val = -val; - } - if (val <= BASE_SENSITIVITY) { - sliderPos = val<<5; - } - else { - double max = log((double)(1<<23)); - double min = log((double)BASE_SENSITIVITY); - double v = log((double)(val)); - v = (v-min)/(max-min); - v = ((1<<22)- (1<<21)) *v + (1<<21); - // _ftol - sliderPos = (int)(floor(v+0.5)); - } - SendMessage(hWndSlider, TBM_SETPOS, 1, sliderPos); - int val2 = (int)(1000*(__int64)val/BASE_SENSITIVITY); - wsprintfW(temp, L"%i.%03i", val2/1000, val2%1000); - SetWindowTextW(hWndText, temp); -} - -void RefreshEnabledDevices(int updateDeviceList) { - // Clears all device state. - static int lastXInputState = -1; - if (updateDeviceList || lastXInputState != config.gameApis.xInput) { - EnumDevices(config.gameApis.xInput); - lastXInputState = config.gameApis.xInput; - } - - for (int i=0; inumDevices; i++) { - Device *dev = dm->devices[i]; - - if (!dev->attached && dev->displayName[0] != '[') { - wchar_t *newName = (wchar_t*) malloc(sizeof(wchar_t) * (wcslen(dev->displayName) + 12)); - wsprintfW(newName, L"[Detached] %s", dev->displayName); - free(dev->displayName); - dev->displayName = newName; - } - - if ((dev->type == KEYBOARD && dev->api == IGNORE_KEYBOARD) || - (dev->type == KEYBOARD && dev->api == config.keyboardApi) || - (dev->type == MOUSE && dev->api == config.mouseApi) || - (dev->type == OTHER && - ((dev->api == DI && config.gameApis.directInput) || - (dev->api == DS3 && config.gameApis.dualShock3) || - (dev->api == XINPUT && config.gameApis.xInput)))) { - if (config.gameApis.dualShock3 && dev->api == DI && dev->displayName && - !wcsicmp(dev->displayName, L"DX PLAYSTATION(R)3 Controller")) { - dm->DisableDevice(i); - } - else { - dm->EnableDevice(i); - } - } - else { - dm->DisableDevice(i); - } - } -} - -// Disables/enables devices as necessary. Also updates diagnostic list -// and pad1/pad2 bindings lists, depending on params. Also updates device -// list, if indicated. - -// Must be called at some point when entering config mode, as I disable -// (non-keyboard/mice) devices with no active bindings when the emulator's running. -void RefreshEnabledDevicesAndDisplay(int updateDeviceList = 0, HWND hWnd = 0, int populate=0) { - RefreshEnabledDevices(updateDeviceList); - if (hWnd) { - HWND hWndList = GetDlgItem(hWnd, IDC_LIST); - ListView_SetExtendedListViewStyleEx(hWndList, LVS_EX_DOUBLEBUFFER, LVS_EX_DOUBLEBUFFER); - int count = ListView_GetItemCount(hWndList); - LVITEM item; - item.iItem = 0; - item.iSubItem = 0; - item.mask = LVIF_TEXT | LVIF_PARAM; - for (int j=0; jnumDevices; j++) { - if (dm->devices[j]->enabled && dm->devices[j]->api != IGNORE_KEYBOARD) { - item.lParam = j; - item.pszText = dm->devices[j]->displayName; - if (count > 0) { - ListView_SetItem(hWndList, &item); - count --; - } - else { - ListView_InsertItem(hWndList, &item); - } - item.iItem++; - } - } - // This way, won't scroll list to start. - while(count > 0) { - ListView_DeleteItem(hWndList, item.iItem); - count --; - } - } - if (populate) { - for (int i=0; i<8; i++) { - Populate(i&1, i>>1); - } - } -} - -wchar_t *GetCommandStringW(u8 command, int port, int slot) { - static wchar_t temp[34]; - if (command >= 0x20 && command <= 0x27) { - if (config.padConfigs[port][slot].type == GuitarPad && (command == 0x20 || command == 0x22)) { - HWND hWnd = GetDlgItem(hWnds[port][slot], 0x10F0+command); - int res = GetWindowTextW(hWnd, temp, 20); - if ((unsigned int)res-1 <= 18) return temp; - } - static wchar_t stick[2] = {'L', 'R'}; - static wchar_t *dir[] = { - L"Up", L"Right", - L"Down", L"Left"}; - wsprintfW(temp, L"%c-Stick %s", stick[(command-0x20)/4], dir[command & 3]); - return temp; - } - /* Get text from the buttons. */ - if (command >= 0x0C && command <=0x28) { - HWND hWnd = GetDlgItem(hWnds[port][slot], 0x10F0+command); - if (!hWnd) { - wchar_t *strings[] = { - L"Lock Buttons", - L"Lock Input", - L"Lock Direction", - L"Mouse", - L"Select", - L"L3", - L"R3", - L"Start", - L"Up", - L"Right", - L"Down", - L"Left", - L"L2", - L"R2", - L"L1", - L"R1", - L"Triangle", - L"Circle", - L"Square", - L"Cross", - L"L-Stick Up", - L"L-Stick Right", - L"L-Stick Down", - L"L-Stick Left", - L"R-Stick Up", - L"R-Stick Right", - L"R-Stick Down", - L"R-Stick Left", - L"Analog", - }; - return strings[command - 0xC]; - } - int res = GetWindowTextW(hWnd, temp, 20); - if ((unsigned int)res-1 <= 18) return temp; - } - else if (command == 0x7F) { - return L"Ignore Key"; - } - return L""; -} - -static wchar_t iniFile[MAX_PATH*2] = L"inis\\LilyPad.ini"; - -void CALLBACK PADsetSettingsDir( const char *dir ) -{ - // emulator assures a trailing slash/backslash (yay!) - swprintf_s( iniFile, L"%S", (dir==NULL) ? "inis\\" : dir ); - wcscat(iniFile, L"LilyPad.ini"); - - createIniDir = false; -} - -int GetBinding(int port, int slot, int index, Device *&dev, Binding *&b, ForceFeedbackBinding *&ffb); -int BindCommand(Device *dev, unsigned int uid, unsigned int port, unsigned int slot, int command, int sensitivity, int turbo, int deadZone); - -int CreateEffectBinding(Device *dev, wchar_t *effectName, unsigned int port, unsigned int slot, unsigned int motor, ForceFeedbackBinding **binding); - -void SelChanged(int port, int slot) { - HWND hWnd = hWnds[port][slot]; - if (!hWnd) return; - HWND hWndTemp, hWndList = GetDlgItem(hWnd, IDC_LIST); - int j, i = ListView_GetSelectedCount(hWndList); - wchar_t *devName = L"N/A"; - wchar_t *key = L"N/A"; - wchar_t *command = L"N/A"; - // Second value is now turbo. - int turbo = -1; - int sensitivity = 0; - int deadZone = 0; - int nonButtons = 0; - // Set if sensitivity != 0, but need to disable flip anyways. - // Only used to relative axes. - int disableFlip = 0; - wchar_t temp[4][1000]; - Device *dev; - int bFound = 0; - int ffbFound = 0; - ForceFeedbackBinding *ffb = 0; - Binding *b = 0; - if (i >= 1) { - int index = -1; - int flipped = 0; - while (1) { - index = ListView_GetNextItem(hWndList, index, LVNI_SELECTED); - if (index < 0) break; - LVITEMW item; - item.iItem = index; - item.mask = LVIF_TEXT; - item.pszText = temp[3]; - for (j=0; j<3; j++) { - item.iSubItem = j; - item.cchTextMax = sizeof(temp[0])/sizeof(temp[3][0]); - if (!ListView_GetItem(hWndList, &item)) break; - if (!bFound && !ffbFound) - wcscpy(temp[j], temp[3]); - else if (wcsicmp(temp[j], temp[3])) { - int q = 0; - while (temp[j][q] == temp[3][q]) q++; - if (q && temp[j][q-1] == ' ' && temp[j][q] && temp[j][q+1] == 0) q--; - if (j == 1) { - // Really ugly, but merges labels for multiple directions for same axis. - if ((temp[j][q] == 0 || (temp[j][q] == ' ' && temp[j][q+2] == 0)) && - (temp[3][q] == 0 || (temp[3][q] == ' ' && temp[3][q+2] == 0))) { - temp[j][q] = 0; - continue; - } - } - // Merge different directions for same stick. - else if (j == 2 && q > 4) { - temp[j][q] = 0; - continue; - } - wcscpy(temp[j], L"*"); - } - } - if (j == 3) { - devName = temp[0]; - key = temp[1]; - command = temp[2]; - if (GetBinding(port, slot, index, dev, b, ffb)) { - if (b) { - bFound ++; - VirtualControl *control = &dev->virtualControls[b->controlIndex]; - // Ignore - if (b->command != 0x7F) { - // Only relative axes can't have negative sensitivity. - if (((control->uid >> 16) & 0xFF) == RELAXIS) { - disableFlip = 1; - } - turbo += b->turbo; - if (b->sensitivity < 0) { - flipped ++; - sensitivity -= b->sensitivity; - } - else { - sensitivity += b->sensitivity; - } - if (((control->uid >> 16)&0xFF) != PSHBTN && ((control->uid >> 16)&0xFF) != TGLBTN) { - deadZone += b->deadZone; - nonButtons++; - } - } - else disableFlip = 1; - } - else ffbFound++; - } - } - } - if ((bFound && ffbFound) || ffbFound > 1) { - ffb = 0; - turbo = -1; - deadZone = 0; - sensitivity = 0; - disableFlip = 1; - bFound = ffbFound = 0; - } - else if (bFound) { - turbo++; - sensitivity /= bFound; - if (nonButtons) { - deadZone /= nonButtons; - } - if (bFound > 1) disableFlip = 1; - else if (flipped) { - sensitivity = -sensitivity; - } - } - } - - for (i=IDC_SLIDER1; i= IDC_FF_AXIS1_ENABLED) { - int index = (i - IDC_FF_AXIS1_ENABLED) / 16; - if (index >= dev->numFFAxes) { - enable = 0; - } - else { - int type = i - index*16; - AxisEffectInfo *info = 0; - if (dev->numFFAxes > index) { - info = ffb->axes+index; - } - if (type == IDC_FF_AXIS1) { - // takes care of IDC_FF_AXIS1_FLIP as well. - SetupLogSlider(hWndTemp); - int force = 0; - if (info) { - force = info->force; - } - SetLogSliderVal(hWnd, i, GetDlgItem(hWnd, i-IDC_FF_AXIS1+IDC_FF_AXIS1_SCALE), force); - } - else if (type == IDC_FF_AXIS1_ENABLED) { - CheckDlgButton(hWnd, i, BST_CHECKED * (info->force!=0)); - wsprintfW(temp[0], L"Axis %i", index+1); - if (dev->ffAxes[index].displayName && wcslen(dev->ffAxes[index].displayName) < 50) { - wchar_t *end = wcschr(temp[0], 0); - wsprintfW(end, L": %s", dev->ffAxes[index].displayName); - } - SetWindowText(hWndTemp, temp[0]); - } - } - } - ShowWindow(hWndTemp, enable); - } - } - if (!ffb) { - SetLogSliderVal(hWnd, IDC_SLIDER1, GetDlgItem(hWnd, IDC_AXIS_SENSITIVITY1), sensitivity); - SetLogSliderVal(hWnd, IDC_SLIDER_DEADZONE, GetDlgItem(hWnd, IDC_AXIS_DEADZONE), deadZone); - - if (disableFlip) EnableWindow(GetDlgItem(hWnd, IDC_FLIP1), 0); - - EnableWindow(GetDlgItem(hWnd, IDC_TURBO), turbo >= 0); - if (turbo > 0 && turbo < bFound) { - SendMessage(GetDlgItem(hWnd, IDC_TURBO), BM_SETSTYLE, BS_AUTO3STATE, 0); - CheckDlgButton(hWnd, IDC_TURBO, BST_INDETERMINATE); - } - else { - SendMessage(GetDlgItem(hWnd, IDC_TURBO), BM_SETSTYLE, BS_AUTOCHECKBOX, 0); - CheckDlgButton(hWnd, IDC_TURBO, BST_CHECKED * (bFound && turbo == bFound)); - } - HWND hWndCombo = GetDlgItem(hWnd, IDC_AXIS_DIRECTION); - int enableCombo = 0; - SendMessage(hWndCombo, CB_RESETCONTENT, 0, 0); - if (b && bFound == 1) { - VirtualControl *control = &dev->virtualControls[b->controlIndex]; - unsigned int uid = control->uid; - if (((uid>>16) & 0xFF) == ABSAXIS) { - enableCombo = 1; - wchar_t *endings[3] = {L" -", L" +", L""}; - wchar_t *string = temp[3]; - wcscpy(string, key); - wchar_t *end = wcschr(string, 0); - int sel = 2; - if (!(uid & UID_AXIS)) { - end[-2] = 0; - sel = (end[-1] == '+'); - end -= 2; - } - for (int i=0; i<3; i++) { - wcscpy(end, endings[i]); - SendMessage(hWndCombo, CB_ADDSTRING, 0, (LPARAM) string); - if (i == sel) - SendMessage(hWndCombo, CB_SETCURSEL, i, 0); - } - } - } - EnableWindow(hWndCombo, enableCombo); - if (!enableCombo) { - SendMessage(hWndCombo, CB_ADDSTRING, 0, (LPARAM) key); - SendMessage(hWndCombo, CB_SELECTSTRING, i, (LPARAM) key); - } - - SetDlgItemText(hWnd, IDC_AXIS_DEVICE1, devName); - SetDlgItemText(hWnd, IDC_AXIS_CONTROL1, command); - } - else { - wchar_t temp2[2000]; - wsprintfW(temp2, L"%s / %s", devName, command); - SetDlgItemText(hWnd, ID_FF, temp2); - - hWndTemp = GetDlgItem(hWnd, IDC_FF_EFFECT); - SendMessage(hWndTemp, CB_RESETCONTENT, 0, 0); - for (i=0; inumFFEffectTypes; i++) { - SendMessage(hWndTemp, CB_INSERTSTRING, i, (LPARAM)dev->ffEffectTypes[i].displayName); - } - SendMessage(hWndTemp, CB_SETCURSEL, ffb->effectIndex, 0); - EnableWindow(hWndTemp, dev->numFFEffectTypes>1); - } -} - -void UnselectAll(HWND hWnd) { - int i = ListView_GetSelectedCount(hWnd); - while (i-- > 0) { - int index = ListView_GetNextItem(hWnd, -1, LVNI_SELECTED); - ListView_SetItemState(hWnd, index, 0, LVIS_SELECTED); - } -} - - -int GetItemIndex(int port, int slot, Device *dev, ForceFeedbackBinding *binding) { - int count = 0; - for (int i = 0; inumDevices; i++) { - Device *dev2 = dm->devices[i]; - if (!dev2->enabled) continue; - if (dev2 != dev) { - count += dev2->pads[port][slot].numBindings + dev2->pads[port][slot].numFFBindings; - continue; - } - return count += dev2->pads[port][slot].numBindings + (binding - dev2->pads[port][slot].ffBindings); - } - return -1; -} -int GetItemIndex(int port, int slot, Device *dev, Binding *binding) { - int count = 0; - for (int i = 0; inumDevices; i++) { - Device *dev2 = dm->devices[i]; - if (!dev2->enabled) continue; - if (dev2 != dev) { - count += dev2->pads[port][slot].numBindings + dev2->pads[port][slot].numFFBindings; - continue; - } - return count += binding - dev->pads[port][slot].bindings; - } - return -1; -} - -// Doesn't check if already displayed. -int ListBoundCommand(int port, int slot, Device *dev, Binding *b) { - if (!hWnds[port][slot]) return -1; - HWND hWndList = GetDlgItem(hWnds[port][slot], IDC_LIST); - int index = -1; - if (hWndList) { - index = GetItemIndex(port, slot, dev, b); - if (index >= 0) { - LVITEM item; - item.mask = LVIF_TEXT; - item.pszText = dev->displayName; - item.iItem = index; - item.iSubItem = 0; - SendMessage(hWndList, LVM_INSERTITEM, 0, (LPARAM)&item); - item.mask = LVIF_TEXT; - item.iSubItem = 1; - item.pszText = dev->GetVirtualControlName(&dev->virtualControls[b->controlIndex]); - SendMessage(hWndList, LVM_SETITEM, 0, (LPARAM)&item); - item.iSubItem = 2; - item.pszText = GetCommandStringW(b->command, port, slot); - SendMessage(hWndList, LVM_SETITEM, 0, (LPARAM)&item); - } - } - return index; -} - -int ListBoundEffect(int port, int slot, Device *dev, ForceFeedbackBinding *b) { - if (!hWnds[port][slot]) return -1; - HWND hWndList = GetDlgItem(hWnds[port][slot], IDC_LIST); - int index = -1; - if (hWndList) { - index = GetItemIndex(port, slot, dev, b); - if (index >= 0) { - LVITEM item; - item.mask = LVIF_TEXT; - item.pszText = dev->displayName; - item.iItem = index; - item.iSubItem = 0; - SendMessage(hWndList, LVM_INSERTITEM, 0, (LPARAM)&item); - item.mask = LVIF_TEXT; - item.iSubItem = 1; - item.pszText = dev->ffEffectTypes[b->effectIndex].displayName; - SendMessage(hWndList, LVM_SETITEM, 0, (LPARAM)&item); - item.iSubItem = 2; - wchar_t *ps2Motors[2] = {L"Big Motor", L"Small Motor"}; - item.pszText = ps2Motors[b->motor]; - SendMessage(hWndList, LVM_SETITEM, 0, (LPARAM)&item); - } - } - return index; -} - -// Only for use with control bindings. Affects all highlighted bindings. -void ChangeValue(int port, int slot, int *newSensitivity, int *newTurbo, int *newDeadZone) { - if (!hWnds[port][slot]) return; - HWND hWndList = GetDlgItem(hWnds[port][slot], IDC_LIST); - int count = ListView_GetSelectedCount(hWndList); - if (count < 1) return; - int index = -1; - while (1) { - index = ListView_GetNextItem(hWndList, index, LVNI_SELECTED); - if (index < 0) break; - Device *dev; - Binding *b; - ForceFeedbackBinding *ffb; - if (!GetBinding(port, slot, index, dev, b, ffb) || ffb) return; - if (newSensitivity) { - // Don't change flip state when modifying multiple controls. - if (count > 1 && b->sensitivity < 0) - b->sensitivity = -*newSensitivity; - else - b->sensitivity = *newSensitivity; - } - if (newDeadZone) { - if (b->deadZone) { - b->deadZone = *newDeadZone; - } - } - if (newTurbo) { - b->turbo = *newTurbo; - } - } - PropSheet_Changed(hWndProp, hWnds[port][slot]); - SelChanged(port, slot); -} - -// Only for use with effect bindings. -void ChangeEffect(int port, int slot, int id, int *newForce, unsigned int *newEffectType) { - if (!hWnds[port][slot]) return; - HWND hWndList = GetDlgItem(hWnds[port][slot], IDC_LIST); - int i = ListView_GetSelectedCount(hWndList); - if (i != 1) return; - int index = ListView_GetNextItem(hWndList, -1, LVNI_SELECTED); - Device *dev; - Binding *b; - ForceFeedbackBinding *ffb; - if (!GetBinding(port, slot, index, dev, b, ffb) || b) return; - if (newForce) { - unsigned int axisIndex = (id - IDC_FF_AXIS1_ENABLED)/16; - if (axisIndex < (unsigned int)dev->numFFAxes) { - ffb->axes[axisIndex].force = *newForce; - } - } - if (newEffectType && *newEffectType < (unsigned int)dev->numFFEffectTypes) { - ffb->effectIndex = *newEffectType; - ListView_DeleteItem(hWndList, index); - index = ListBoundEffect(port, slot, dev, ffb); - ListView_SetItemState(hWndList, index, LVIS_SELECTED, LVIS_SELECTED); - } - PropSheet_Changed(hWndProp, hWnds[port][slot]); - SelChanged(port, slot); -} - - - -void Populate(int port, int slot) { - if (!hWnds[port][slot]) return; - HWND hWnd = GetDlgItem(hWnds[port][slot], IDC_LIST); - ListView_DeleteAllItems(hWnd); - int i, j; - - int multipleBinding = config.multipleBinding; - config.multipleBinding = 1; - for (j=0; jnumDevices; j++) { - Device *dev = dm->devices[j]; - if (!dev->enabled) continue; - for (i=0; ipads[port][slot].numBindings; i++) { - ListBoundCommand(port, slot, dev, dev->pads[port][slot].bindings+i); - } - for (i=0; ipads[port][slot].numFFBindings; i++) { - ListBoundEffect(port, slot, dev, dev->pads[port][slot].ffBindings+i); - } - } - config.multipleBinding = multipleBinding; - - hWnd = GetDlgItem(hWnds[port][slot], IDC_FORCEFEEDBACK); - SendMessage(hWnd, CB_RESETCONTENT, 0, 0); - int added = 0; - for (i=0; inumDevices; i++) { - Device *dev = dm->devices[i]; - if (dev->enabled && dev->numFFAxes && dev->numFFEffectTypes) { - SendMessage(hWnd, CB_INSERTSTRING, added, (LPARAM)dev->displayName); - SendMessage(hWnd, CB_SETITEMDATA, added, i); - added++; - } - } - SendMessage(hWnd, CB_SETCURSEL, 0, 0); - EnableWindow(hWnd, added!=0); - EnableWindow(GetDlgItem(hWnds[port][slot], ID_BIG_MOTOR), added!=0); - EnableWindow(GetDlgItem(hWnds[port][slot], ID_SMALL_MOTOR), added!=0); - - SelChanged(port, slot); -} - -int WritePrivateProfileInt(wchar_t *s1, wchar_t *s2, int v, wchar_t *ini) { - wchar_t temp[100]; - _itow(v, temp, 10); - return WritePrivateProfileStringW(s1, s2, temp, ini); -} - -void SetVolume(int volume) { - if (volume > 100) volume = 100; - if (volume < 0) volume = 0; - config.volume = volume; - unsigned int val = 0xFFFF * volume/100; - val = val | (val<<16); - for (int i=waveOutGetNumDevs()-1; i>=0; i--) { - waveOutSetVolume((HWAVEOUT)i, val); - } - WritePrivateProfileInt(L"General Settings", L"Volume", config.volume, iniFile); -} - -int SaveSettings(wchar_t *file=0) { - - // Need this either way for saving path. - if (!file) { - file = iniFile; - } - else { - wchar_t *c = wcsrchr(file, '\\'); - if (*c) { - *c = 0; - wcscpy(config.lastSaveConfigPath, file); - wcscpy(config.lastSaveConfigFileName, c+1); - *c = '\\'; - } - } - DeleteFileW(file); - - WritePrivateProfileStringW(L"General Settings", L"Last Config Path", config.lastSaveConfigPath, iniFile); - WritePrivateProfileStringW(L"General Settings", L"Last Config Name", config.lastSaveConfigFileName, iniFile); - - // Just check first, last, and all pad bindings. Should be more than enough. No real need to check - // config path. - int noError = 1; - - for (int i=0; inumDevices; i++) { - wchar_t id[50]; - wchar_t temp[50], temp2[1000]; - wsprintfW(id, L"Device %i", i); - Device *dev = dm->devices[i]; - wchar_t *name = dev->displayName; - while (name[0] == '[') { - wchar_t *name2 = wcschr(name, ']'); - if (!name2) break; - name = name2+1; - while (iswspace(name[0])) name++; - } - WritePrivateProfileStringW(id, L"Display Name", name, file); - WritePrivateProfileStringW(id, L"Instance ID", dev->instanceID, file); - if (dev->productID) { - WritePrivateProfileStringW(id, L"Product ID", dev->productID, file); - } - WritePrivateProfileInt(id, L"API", dev->api, file); - WritePrivateProfileInt(id, L"Type", dev->type, file); - int ffBindingCount = 0; - int bindingCount = 0; - for (int port=0; port<2; port++) { - for (int slot=0; slot<4; slot++) { - for (int j=0; jpads[port][slot].numBindings; j++) { - Binding *b = dev->pads[port][slot].bindings+j; - VirtualControl *c = &dev->virtualControls[b->controlIndex]; - wsprintfW(temp, L"Binding %i", bindingCount++); - wsprintfW(temp2, L"0x%08X, %i, %i, %i, %i, %i, %i", c->uid, port, b->command, b->sensitivity, b->turbo, slot, b->deadZone); - noError &= WritePrivateProfileStringW(id, temp, temp2, file); - } - for (int j=0; jpads[port][slot].numFFBindings; j++) { - ForceFeedbackBinding *b = dev->pads[port][slot].ffBindings+j; - ForceFeedbackEffectType *eff = &dev->ffEffectTypes[b->effectIndex]; - wsprintfW(temp, L"FF Binding %i", ffBindingCount++); - wsprintfW(temp2, L"%s %i, %i, %i", eff->effectID, port, b->motor, slot); - for (int k=0; knumFFAxes; k++) { - ForceFeedbackAxis *axis = dev->ffAxes + k; - AxisEffectInfo *info = b->axes + k; - wsprintfW(wcschr(temp2,0), L", %i, %i", axis->id, info->force); - } - noError &= WritePrivateProfileStringW(id, temp, temp2, file); - } - } - } - } - if (!noError) { - MessageBoxA(hWndProp, "Unable to save settings. Make sure the disk is not full or write protected, the file isn't write protected, and that the app has permissions to write to the directory. On Vista, try running in administrator mode.", "Error Writing Configuration File", MB_OK | MB_ICONERROR); - } - return !noError; -} - -u8 GetPrivateProfileBool(wchar_t *s1, wchar_t *s2, int def, wchar_t *ini) { - return (0!=GetPrivateProfileIntW(s1, s2, def, ini)); -} - -int LoadSettings(int force, wchar_t *file) { - if (dm && !force) return 0; - - if( createIniDir ) - { - CreateDirectory(L"inis", 0); - createIniDir = false; - } - - // Could just do ClearDevices() instead, but if I ever add any extra stuff, - // this will still work. - UnloadConfigs(); - dm = new InputDeviceManager(); - - if (!file) { - file = iniFile; - GetPrivateProfileStringW(L"General Settings", L"Last Config Path", L"inis", config.lastSaveConfigPath, sizeof(config.lastSaveConfigPath), file); - GetPrivateProfileStringW(L"General Settings", L"Last Config Name", L"LilyPad.lily", config.lastSaveConfigFileName, sizeof(config.lastSaveConfigFileName), file); - } - else { - wchar_t *c = wcsrchr(file, '\\'); - if (c) { - *c = 0; - wcscpy(config.lastSaveConfigPath, file); - wcscpy(config.lastSaveConfigFileName, c+1); - *c = '\\'; - WritePrivateProfileStringW(L"General Settings", L"Last Config Path", config.lastSaveConfigPath, iniFile); - WritePrivateProfileStringW(L"General Settings", L"Last Config Name", config.lastSaveConfigFileName, iniFile); - } - } - - for (int i=0; i= 100) break; - continue; - } - wchar_t *id2 = 0; - if (GetPrivateProfileStringW(id, L"Product ID", 0, temp4, 1000, file) && temp4[0]) - id2 = temp4; - - int api = GetPrivateProfileIntW(id, L"API", 0, file); - int type = GetPrivateProfileIntW(id, L"Type", 0, file); - if (!api || !type) continue; - - Device *dev = new Device((DeviceAPI)api, (DeviceType)type, temp2, temp3, id2); - dev->attached = 0; - dm->AddDevice(dev); - int j = 0; - int last = 0; - while (1) { - wsprintfW(temp, L"Binding %i", j++); - if (!GetPrivateProfileStringW(id, temp, 0, temp2, 1000, file)) { - if (j >= 100) { - if (!last) break; - last = 0; - } - continue; - } - last = 1; - unsigned int uid; - int port, command, sensitivity, turbo, slot = 0, deadZone = 0; - int w = 0; - char string[1000]; - while (temp2[w]) { - string[w] = (char)temp2[w]; - w++; - } - string[w] = 0; - int len = sscanf(string, " %i , %i , %i , %i , %i , %i , %i", &uid, &port, &command, &sensitivity, &turbo, &slot, &deadZone); - if (len >= 5 && type) { - VirtualControl *c = dev->GetVirtualControl(uid); - if (!c) c = dev->AddVirtualControl(uid, -1); - if (c) { - BindCommand(dev, uid, port, slot, command, sensitivity, turbo, deadZone); - } - } - } - j = 0; - while (1) { - wsprintfW(temp, L"FF Binding %i", j++); - if (!GetPrivateProfileStringW(id, temp, 0, temp2, 1000, file)) { - if (j >= 10) { - if (!last) break; - last = 0; - } - continue; - } - last = 1; - int port, slot, motor; - int w = 0; - char string[1000]; - char effect[1000]; - while (temp2[w]) { - string[w] = (char)temp2[w]; - w++; - } - string[w] = 0; - // wcstok not in ntdll. More effore than its worth to shave off - // whitespace without it. - if (sscanf(string, " %s %i , %i , %i", effect, &port, &motor, &slot) == 4) { - char *s = strchr(strchr(strchr(string, ',')+1, ',')+1, ','); - if (!s) continue; - s++; - w = 0; - while (effect[w]) { - temp2[w] = effect[w]; - w++; - } - temp2[w] = 0; - ForceFeedbackEffectType *eff = dev->GetForcefeedbackEffect(temp2); - if (!eff) { - // At the moment, don't record effect types. - // Only used internally, anyways, so not an issue. - dev->AddFFEffectType(temp2, temp2, EFFECT_CONSTANT); - // eff = &dev->ffEffectTypes[dev->numFFEffectTypes-1]; - } - ForceFeedbackBinding *b; - CreateEffectBinding(dev, temp2, port, slot, motor, &b); - if (b) { - while (1) { - int axisID = atoi(s); - if (!(s = strchr(s, ','))) break; - s++; - int force = atoi(s); - int i; - for (i=0; inumFFAxes; i++) { - if (axisID == dev->ffAxes[i].id) break; - } - if (i == dev->numFFAxes) { - dev->AddFFAxis(L"?", axisID); - } - b->axes[i].force = force; - if (!(s = strchr(s, ','))) break; - s++; - } - } - } - } - } - config.multipleBinding = multipleBinding; - - RefreshEnabledDevicesAndDisplay(1); - - return 0; -} - -inline int GetPort(HWND hWnd, int *slot) { - for (int i=0; i>1] == hWnd) { - *slot = i>>1; - return i&1; - } - } - *slot = 0; - return 0; -} - -void Diagnostics(HWND hWnd) { - HWND hWndList = GetDlgItem(hWnd, IDC_LIST); - if (!hWndList) return; - int index = ListView_GetNextItem(hWndList, -1, LVNI_SELECTED); - if (index < 0) return; - LVITEM item; - memset(&item, 0, sizeof(item)); - item.mask = LVIF_PARAM; - item.iSubItem = 0; - item.iItem = index; - if (!ListView_GetItem(hWndList, &item)) return; - Diagnose(item.lParam, hWnd); - RefreshEnabledDevicesAndDisplay(0, hWnd, 1); -} - -int GetBinding(int port, int slot, int index, Device *&dev, Binding *&b, ForceFeedbackBinding *&ffb) { - ffb = 0; - b = 0; - for (int i = 0; inumDevices; i++) { - dev = dm->devices[i]; - if (!dev->enabled) continue; - if (index < dev->pads[port][slot].numBindings) { - b = dev->pads[port][slot].bindings + index; - return 1; - } - index -= dev->pads[port][slot].numBindings; - - if (index < dev->pads[port][slot].numFFBindings) { - ffb = dev->pads[port][slot].ffBindings + index; - return 1; - } - index -= dev->pads[port][slot].numFFBindings; - } - return 0; -} - -// Only used when deleting things from ListView. Will remove from listview if needed. -void DeleteBinding(int port, int slot, Device *dev, Binding *b) { - if (dev->enabled && hWnds[port][slot]) { - int count = GetItemIndex(port, slot, dev, b); - if (count >= 0) { - HWND hWndList = GetDlgItem(hWnds[port][slot], IDC_LIST); - if (hWndList) { - ListView_DeleteItem(hWndList, count); - } - } - } - Binding *bindings = dev->pads[port][slot].bindings; - int i = b - bindings; - memmove(bindings+i, bindings+i+1, sizeof(Binding) * (dev->pads[port][slot].numBindings - i - 1)); - dev->pads[port][slot].numBindings--; -} - -void DeleteBinding(int port, int slot, Device *dev, ForceFeedbackBinding *b) { - if (dev->enabled && hWnds[port][slot]) { - int count = GetItemIndex(port, slot, dev, b); - if (count >= 0) { - HWND hWndList = GetDlgItem(hWnds[port][slot], IDC_LIST); - if (hWndList) { - ListView_DeleteItem(hWndList, count); - } - } - } - ForceFeedbackBinding *bindings = dev->pads[port][slot].ffBindings; - int i = b - bindings; - memmove(bindings+i, bindings+i+1, sizeof(Binding) * (dev->pads[port][slot].numFFBindings - i - 1)); - dev->pads[port][slot].numFFBindings--; -} - -int DeleteByIndex(int port, int slot, int index) { - ForceFeedbackBinding *ffb; - Binding *b; - Device *dev; - if (GetBinding(port, slot, index, dev, b, ffb)) { - if (b) { - DeleteBinding(port, slot, dev, b); - } - else { - DeleteBinding(port, slot, dev, ffb); - } - return 1; - } - return 0; -} - -int DeleteSelected(int port, int slot) { - if (!hWnds[port][slot]) return 0; - HWND hWnd = GetDlgItem(hWnds[port][slot], IDC_LIST); - int changes = 0; - while (1) { - int index = ListView_GetNextItem(hWnd, -1, LVNI_SELECTED); - if (index < 0) break; - changes += DeleteByIndex(port, slot, index); - } - //ShowScrollBar(hWnd, SB_VERT, 1); - return changes; -} - -int CreateEffectBinding(Device *dev, wchar_t *effectID, unsigned int port, unsigned int slot, unsigned int motor, ForceFeedbackBinding **binding) { - // Checks needed because I use this directly when loading bindings. - // Note: dev->numFFAxes *can* be 0, for loading from file. - *binding = 0; - if (port > 1 || slot>3 || motor > 1 || !dev->numFFEffectTypes) { - return -1; - } - ForceFeedbackEffectType *eff = 0; - if (effectID) { - eff = dev->GetForcefeedbackEffect(effectID); - } - if (!eff) { - eff = dev->ffEffectTypes; - } - if (!eff) { - return -1; - } - int effectIndex = eff - dev->ffEffectTypes; - dev->pads[port][slot].ffBindings = (ForceFeedbackBinding*) realloc(dev->pads[port][slot].ffBindings, (dev->pads[port][slot].numFFBindings+1) * sizeof(ForceFeedbackBinding)); - int newIndex = dev->pads[port][slot].numFFBindings; - while (newIndex && dev->pads[port][slot].ffBindings[newIndex-1].motor >= motor) { - dev->pads[port][slot].ffBindings[newIndex] = dev->pads[port][slot].ffBindings[newIndex-1]; - newIndex--; - } - ForceFeedbackBinding *b = dev->pads[port][slot].ffBindings + newIndex; - b->axes = (AxisEffectInfo*) calloc(dev->numFFAxes, sizeof(AxisEffectInfo)); - b->motor = motor; - b->effectIndex = effectIndex; - dev->pads[port][slot].numFFBindings++; - if (binding) *binding = b; - return ListBoundEffect(port, slot, dev, b); -} - -int BindCommand(Device *dev, unsigned int uid, unsigned int port, unsigned int slot, int command, int sensitivity, int turbo, int deadZone) { - // Checks needed because I use this directly when loading bindings. - if (port > 1 || slot>3) { - return -1; - } - if (!sensitivity) sensitivity = BASE_SENSITIVITY; - if ((uid>>16) & (PSHBTN|TGLBTN)) { - deadZone = 0; - } - else if (!deadZone) { - if ((uid>>16) & PRESSURE_BTN) { - deadZone = 1; - } - else { - deadZone = DEFAULT_DEADZONE; - } - } - // Relative axes can have negative sensitivity. - else if (((uid>>16) & 0xFF) == RELAXIS) { - sensitivity = abs(sensitivity); - } - VirtualControl *c = dev->GetVirtualControl(uid); - if (!c) return -1; - // Add before deleting. Means I won't scroll up one line when scrolled down to bottom. - int controlIndex = c - dev->virtualControls; - int index = 0; - PadBindings *p = dev->pads[port]+slot; - p->bindings = (Binding*) realloc(p->bindings, (p->numBindings+1) * sizeof(Binding)); - for (index = p->numBindings; index > 0; index--) { - if (p->bindings[index-1].controlIndex < controlIndex) break; - p->bindings[index] = p->bindings[index-1]; - } - Binding *b = p->bindings+index; - p->numBindings++; - b->command = command; - b->controlIndex = controlIndex; - b->turbo = turbo; - b->sensitivity = sensitivity; - b->deadZone = deadZone; - // Where it appears in listview. - int count = ListBoundCommand(port, slot, dev, b); - - int newBindingIndex = index; - index = 0; - while (index < p->numBindings) { - if (index == newBindingIndex) { - index ++; - continue; - } - b = p->bindings + index; - int nuke = 0; - if (config.multipleBinding) { - if (b->controlIndex == controlIndex && b->command == command) - nuke = 1; - } - else { - int uid2 = dev->virtualControls[b->controlIndex].uid; - if (b->controlIndex == controlIndex || (!((uid2^uid) & 0xFFFFFF) && ((uid|uid2) & (UID_POV | UID_AXIS)))) - nuke = 1; - } - if (!nuke) { - index++; - continue; - } - if (index < newBindingIndex) { - newBindingIndex--; - count --; - } - DeleteBinding(port, slot, dev, b); - } - if (!config.multipleBinding) { - for (int port2=0; port2<2; port2++) { - for (int slot2=0; slot2<4; slot2++) { - if (port2==port && slot2 == slot) continue; - PadBindings *p = dev->pads[port2]+slot2; - for (int i=0; i < p->numBindings; i++) { - Binding *b = p->bindings+i; - int uid2 = dev->virtualControls[b->controlIndex].uid; - if (b->controlIndex == controlIndex || (!((uid2^uid) & 0xFFFFFF) && ((uid|uid2) & (UID_POV | UID_AXIS)))) { - DeleteBinding(port2, slot2, dev, b); - i--; - } - } - } - } - } - - return count; -} - -// Does nothing, but makes sure I'm overriding the dialog's window proc, to block -// default key handling. -ExtraWndProcResult DoNothingWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *output) { - return CONTINUE_BLISSFULLY; -} - -void EndBinding(HWND hWnd) { - if (selected) { - KillTimer(hWnd, 1); - - int needRefreshDevices = 0; - // If binding ignore keyboard, this will disable it and enable other devices. - // 0xFF is used for testing force feedback. I disable all other devices first - // just so I don't needlessly steal the mouse. - if (selected == 0x7F || selected == 0xFF) { - needRefreshDevices = 1; - } - selected = 0; - - dm->ReleaseInput(); - ClearKeyQueue(); - hWndButtonProc.Release(); - - // Safest to do this last. - if (needRefreshDevices) { - RefreshEnabledDevices(); - } - } -} - - -INT_PTR CALLBACK DialogProc(HWND hWnd, unsigned int msg, WPARAM wParam, LPARAM lParam) { - int index = (hWnd == PropSheet_IndexToHwnd(hWndProp, 1)); - int slot; - int port = GetPort(hWnd, &slot); - HWND hWndList = GetDlgItem(hWnd, IDC_LIST); - switch (msg) { - case WM_INITDIALOG: - { - ListView_SetExtendedListViewStyleEx(hWndList, LVS_EX_DOUBLEBUFFER, LVS_EX_DOUBLEBUFFER); - LVCOLUMN c; - c.mask = LVCF_TEXT | LVCF_WIDTH; - c.cx = 101; - c.pszText = L"Device"; - ListView_InsertColumn(hWndList, 0, &c); - c.cx = 70; - c.pszText = L"PC Control"; - ListView_InsertColumn(hWndList, 1, &c); - c.cx = 84; - c.pszText = L"PS2 Control"; - ListView_InsertColumn(hWndList, 2, &c); - selected = 0; - port = (int)((PROPSHEETPAGE *)lParam)->lParam & 1; - slot = (int)((PROPSHEETPAGE *)lParam)->lParam >> 1; - hWnds[port][slot] = hWnd; - SendMessage(hWndList, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, LVS_EX_FULLROWSELECT); - SetupLogSlider(GetDlgItem(hWnd, IDC_SLIDER1)); - SetupLogSlider(GetDlgItem(hWnd, IDC_SLIDER_DEADZONE)); - if (port || slot) - EnableWindow(GetDlgItem(hWnd, ID_IGNORE), 0); - - Populate(port, slot); - } - break; - case WM_DEVICECHANGE: - if (wParam == DBT_DEVNODES_CHANGED) { - EndBinding(hWnd); - RefreshEnabledDevicesAndDisplay(1, hWndGeneral, 1); - } - break; - case WM_TIMER: - // ignore generic timer callback and handle the hwnd-specific one which comes later - if(hWnd == 0) return 0; - - if (!selected || selected == 0xFF) { - // !selected is mostly for device added/removed when binding. - selected = 0xFF; - EndBinding(hWnd); - } - else { - unsigned int uid; - int value; - - // The old code re-bound our button hWndProcEater to GetDlgItem(hWnd, selected). But at best hWnd - // was null (WM_TIMER is passed twice, once with a null parameter, and a second time that is hWnd - // specific), and at worst 'selected' is a post-processed code "based" on cmd, so GetDlgItem - // *always* returned NULL anyway. This resulted in hWndButton being null, which meant Device code - // used hWnd instead. This may have caused odd behavior since the callbacks were still all eaten - // by the initial GetDlgItem(hWnd, cmd) selection made when the timer was initialized. - - InitInfo info = {selected==0x7F, 1, hWndProp, &hWndButtonProc}; - Device *dev = dm->GetActiveDevice(&info, &uid, &index, &value); - if (dev) { - int command = selected; - // Good idea to do this first, as BindCommand modifies the ListView, which will - // call it anyways, which is a bit funky. - EndBinding(hWnd); - UnselectAll(hWndList); - int index = -1; - if (command == 0x7F && dev->api == IGNORE_KEYBOARD) { - index = BindCommand(dev, uid, 0, 0, command, BASE_SENSITIVITY, 0, 0); - } - else if (command < 0x30) { - index = BindCommand(dev, uid, port, slot, command, BASE_SENSITIVITY, 0, 0); - } - if (index >= 0) { - PropSheet_Changed(hWndProp, hWnds[port][slot]); - ListView_SetItemState(hWndList, index, LVIS_SELECTED, LVIS_SELECTED); - ListView_EnsureVisible(hWndList, index, 0); - } - } - } - break; - case WM_SYSKEYDOWN: - case WM_KEYDOWN: - EndBinding(hWnd); - break; - case PSM_CANCELTOCLOSE: - // Load(); - break; - case WM_ACTIVATE: - EndBinding(hWnd); - break; - case WM_NOTIFY: - { - PSHNOTIFY* n = (PSHNOTIFY*) lParam; - if (n->hdr.hwndFrom == hWndProp) { - switch(n->hdr.code) { - case PSN_QUERYCANCEL: - case PSN_KILLACTIVE: - EndBinding(hWnd); - return 0; - case PSN_SETACTIVE: - return 0; - case PSN_APPLY: - SetWindowLong(hWnd, DWL_MSGRESULT, PSNRET_NOERROR); - return 1; - } - break; - } - else if (n->hdr.idFrom == IDC_LIST) { - static int NeedUpdate = 0; - if (n->hdr.code == LVN_KEYDOWN) { - NMLVKEYDOWN *key = (NMLVKEYDOWN *) n; - if (key->wVKey == VK_DELETE || - key->wVKey == VK_BACK) { - - if (DeleteSelected(port, slot)) - PropSheet_Changed(hWndProp, hWnds[0]); - } - } - // Update sensitivity and motor/binding display on redraw - // rather than on itemchanged. This reduces blinking, as - // I get 3 LVN_ITEMCHANGED messages, and first is sent before - // the new item is set as being selected. - else if (n->hdr.code == LVN_ITEMCHANGED) { - NeedUpdate = 1; - } - else if (n->hdr.code == NM_CUSTOMDRAW && NeedUpdate) { - NeedUpdate = 0; - SelChanged(port, slot); - } - } - } - break; - case WM_HSCROLL: - { - int id = GetDlgCtrlID((HWND)lParam); - int val = GetLogSliderVal(hWnd, id); - if (id == IDC_SLIDER1) { - ChangeValue(port, slot, &val, 0, 0); - } - else if (id == IDC_SLIDER_DEADZONE) { - ChangeValue(port, slot, 0, 0, &val); - } - else { - ChangeEffect(port, slot, id, &val, 0); - } - } - break; - case WM_COMMAND: - if (HIWORD(wParam)==CBN_SELCHANGE && LOWORD(wParam) == IDC_AXIS_DIRECTION) { - int index = ListView_GetNextItem(hWndList, -1, LVNI_SELECTED); - if (index >= 0) { - int cbsel = SendMessage((HWND)lParam, CB_GETCURSEL, 0, 0); - if (cbsel >= 0) { - ForceFeedbackBinding *ffb; - Binding *b; - Device *dev; - if (GetBinding(port, slot, index, dev, b, ffb)) { - const static unsigned int axisUIDs[3] = {UID_AXIS_NEG, UID_AXIS_POS, UID_AXIS}; - int uid = dev->virtualControls[b->controlIndex].uid; - uid = (uid&0x00FFFFFF) | axisUIDs[cbsel]; - Binding backup = *b; - DeleteSelected(port, slot); - int index = BindCommand(dev, uid, port, slot, backup.command, backup.sensitivity, backup.turbo, backup.deadZone); - ListView_SetItemState(hWndList, index, LVIS_SELECTED, LVIS_SELECTED); - PropSheet_Changed(hWndProp, hWnd); - } - } - } - } - else if (HIWORD(wParam)==CBN_SELCHANGE && LOWORD(wParam) == IDC_FF_EFFECT) { - int typeIndex = SendMessage((HWND)lParam, CB_GETCURSEL, 0, 0); - if (typeIndex >= 0) - ChangeEffect(port, slot, 0, 0, (unsigned int*)&typeIndex); - } - else if (HIWORD(wParam)==BN_CLICKED) { - EndBinding(hWnd); - int cmd = LOWORD(wParam); - if (cmd == ID_DELETE) { - if (DeleteSelected(port, slot)) - PropSheet_Changed(hWndProp, hWnd); - } - else if (cmd == ID_CLEAR) { - while (DeleteByIndex(port, slot, 0)) PropSheet_Changed(hWndProp, hWnd); - } - else if (cmd == ID_BIG_MOTOR || cmd == ID_SMALL_MOTOR) { - int i = (int)SendMessage(GetDlgItem(hWnd, IDC_FORCEFEEDBACK), CB_GETCURSEL, 0, 0); - if (i >= 0) { - unsigned int index = (unsigned int)SendMessage(GetDlgItem(hWnd, IDC_FORCEFEEDBACK), CB_GETITEMDATA, i, 0); - if (index < (unsigned int) dm->numDevices) { - Device *dev = dm->devices[index]; - ForceFeedbackBinding *b; - wchar_t *effectID = 0; - if (dev->api == DI) { - // Constant effect. - if (cmd == ID_BIG_MOTOR) effectID = L"13541C20-8E33-11D0-9AD0-00A0C9A06E35"; - // Square. - else effectID = L"13541C22-8E33-11D0-9AD0-00A0C9A06E35"; - } - int count = CreateEffectBinding(dev, effectID, port, slot, cmd-ID_BIG_MOTOR, &b); - if (b) { - int needSet = 1; - if (dev->api == XINPUT && dev->numFFAxes == 2) { - needSet = 0; - if (cmd == ID_BIG_MOTOR) { - b->axes[0].force = BASE_SENSITIVITY; - } - else { - b->axes[1].force = BASE_SENSITIVITY; - } - } - else if (dev->api == DS3 && dev->numFFAxes == 2) { - needSet = 0; - if (cmd == ID_BIG_MOTOR) { - b->axes[0].force = BASE_SENSITIVITY; - } - else { - b->axes[1].force = BASE_SENSITIVITY; - } - } - else if (dev->api == DI) { - int bigIndex=0, littleIndex=0; - int j; - for (j=0; jnumFFAxes; j++) { - // DI object instance. 0 is x-axis, 1 is y-axis. - int instance = (dev->ffAxes[j].id>>8)&0xFFFF; - if (instance == 0) { - bigIndex = j; - } - else if (instance == 1) { - littleIndex = j; - } - } - needSet = 0; - if (cmd == ID_BIG_MOTOR) { - b->axes[bigIndex].force = BASE_SENSITIVITY; - b->axes[littleIndex].force = 1; - } - else { - b->axes[bigIndex].force = 1; - b->axes[littleIndex].force = BASE_SENSITIVITY; - } - } - if (needSet) { - for (int j=0; j<2 && j numFFAxes; j++) { - b->axes[j].force = BASE_SENSITIVITY; - } - } - UnselectAll(hWndList); - ListView_SetItemState(hWndList, count, LVIS_SELECTED, LVIS_SELECTED); - ListView_EnsureVisible(hWndList, count, 0); - } - PropSheet_Changed(hWndProp, hWnd); - } - } - } - else if (cmd == ID_CONTROLS) { - UnselectAll(hWndList); - } - else if (cmd == ID_TEST) { - // Just in case... - if (selected) break; - Device *dev; - Binding *b; - ForceFeedbackBinding *ffb = 0; - int selIndex = ListView_GetNextItem(hWndList, -1, LVNI_SELECTED); - if (selIndex >= 0) { - if (GetBinding(port, slot, selIndex, dev, b, ffb)) { - selected = 0xFF; - hWndButtonProc.SetWndHandle(GetDlgItem(hWnd, cmd)); - InitInfo info = {0, 1, hWndProp, &hWndButtonProc}; - for (int i=0; inumDevices; i++) { - if (dm->devices[i] != dev) { - dm->DisableDevice(i); - } - } - dm->Update(&info); - dm->PostRead(); - dev->SetEffect(ffb, 255); - Sleep(200); - dm->Update(&info); - SetTimer(hWnd, 1, 3000, 0); - } - } - } - else if ((cmd >= ID_LOCK_BUTTONS && cmd <= ID_ANALOG) || cmd == ID_IGNORE) {// || cmd == ID_FORCE_FEEDBACK) { - // Messes up things, unfortunately. - // End binding on a bunch of notification messages, and - // this will send a bunch. - // UnselectAll(hWndList); - EndBinding(hWnd); - if (cmd != ID_IGNORE) { - selected = cmd-(ID_SELECT-0x10); - } - else { - selected = 0x7F; - for (int i=0; inumDevices; i++) { - if (dm->devices[i]->api != IGNORE_KEYBOARD) { - dm->DisableDevice(i); - } - else { - dm->EnableDevice(i); - } - } - } - - hWndButtonProc.SetWndHandle(GetDlgItem(hWnd, cmd)); - hWndButtonProc.Eat(DoNothingWndProc, 0); - InitInfo info = {selected==0x7F, 1, hWndProp, &hWndButtonProc}; - int w = timeGetTime(); - dm->Update(&info); - dm->PostRead(); - // Workaround for things that return 0 on first poll and something else ever after. - Sleep(80); - dm->Update(&info); - dm->PostRead(); - SetTimer(hWnd, 1, 30, 0); - } - if (cmd == IDC_TURBO) { - // Don't allow setting it back to indeterminate. - SendMessage(GetDlgItem(hWnd, IDC_TURBO), BM_SETSTYLE, BS_AUTOCHECKBOX, 0); - int turbo = (IsDlgButtonChecked(hWnd, IDC_TURBO) == BST_CHECKED); - ChangeValue(port, slot, 0, &turbo, 0); - } - else if (cmd == IDC_FLIP1) { - int val = GetLogSliderVal(hWnd, IDC_SLIDER1); - ChangeValue(port, slot, &val, 0, 0); - } - else if (cmd >= IDC_FF_AXIS1_ENABLED && cmd < IDC_FF_AXIS8_ENABLED + 16) { - int index = (cmd - IDC_FF_AXIS1_ENABLED)/16; - int val = GetLogSliderVal(hWnd, 16*index + IDC_FF_AXIS1); - if (IsDlgButtonChecked(hWnd, 16*index + IDC_FF_AXIS1_ENABLED) != BST_CHECKED) { - val = 0; - } - ChangeEffect(port, slot, cmd, &val, 0); - } - } - break; - default: - break; - } - return 0; -} - -// Returns 0 if pad doesn't exist due to mtap settings, as a convenience. -int GetPadString(wchar_t *string, unsigned int port, unsigned int slot) { - if (!slot && !config.multitap[port]) { - wsprintfW(string, L"Pad %i", port+1); - } - else { - wsprintfW(string, L"Pad %i%c", port+1, 'A'+slot); - if (!config.multitap[port]) return 0; - } - return 1; -} - -void UpdatePadPages() { - HPROPSHEETPAGE pages[10]; - int count = 0; - memset(hWnds, 0, sizeof(hWnds)); - int slot = 0; - for (int port=0; port<2; port++) { - for (int slot=0; slot<4; slot++) { - if (config.padConfigs[port][slot].type == DisabledPad) continue; - wchar_t title[20]; - if (!GetPadString(title, port, slot)) continue; - - PROPSHEETPAGE psp; - ZeroMemory(&psp, sizeof(psp)); - psp.dwSize = sizeof(psp); - psp.dwFlags = PSP_USETITLE | PSP_PREMATURE; - psp.hInstance = hInst; - psp.pfnDlgProc = DialogProc; - psp.lParam = port | (slot<<1); - psp.pszTitle = title; - if (config.padConfigs[port][slot].type != GuitarPad) - psp.pszTemplate = MAKEINTRESOURCE(IDD_CONFIG); - else - psp.pszTemplate = MAKEINTRESOURCE(IDD_CONFIG_GUITAR); - pages[count] = CreatePropertySheetPage(&psp); - if (pages[count]) count++; - } - } - while (SendMessage(hWndProp, PSM_INDEXTOPAGE, 1, 0)) { - PropSheet_RemovePage(hWndProp, 1, 0); - } - for (int i=0; i= 2 + 3*(config.multitap[0]+config.multitap[1])) { - *port = 0; - *slot = 0; - return 0; - } - if (index < 1 + 3*config.multitap[0]) { - *port = 0; - *slot = index; - } - else { - *port = 1; - *slot = index-1-3*config.multitap[0]; - } - return 1; -} - -void UpdatePadList(HWND hWnd) { - static u8 recurse = 0; - if (recurse) return; - recurse = 1; - HWND hWndList = GetDlgItem(hWnd, IDC_PAD_LIST); - HWND hWndCombo = GetDlgItem(hWnd, IDC_PAD_TYPE); - HWND hWndAnalog = GetDlgItem(hWnd, IDC_ANALOG_START1); - int slot; - int port; - int index = 0; - wchar_t *padTypes[] = {L"Unplugged", L"Dualshock 2", L"Guitar"}; - for (port=0; port<2; port++) { - for (slot = 0; slot<4; slot++) { - wchar_t text[20]; - if (!GetPadString(text, port, slot)) continue; - LVITEM item; - item.iItem = index; - item.iSubItem = 0; - item.mask = LVIF_TEXT; - item.pszText = text; - if (SendMessage(hWndList, LVM_GETITEMCOUNT, 0, 0) <= index) { - ListView_InsertItem(hWndList, &item); - } - else { - ListView_SetItem(hWndList, &item); - } - - item.iSubItem = 1; - if (2 < (unsigned int)config.padConfigs[port][slot].type) config.padConfigs[port][slot].type = Dualshock2Pad; - item.pszText = padTypes[config.padConfigs[port][slot].type]; - //if (!slot && !config.padConfigs[port][slot].type) - // item.pszText = L"Unplugged (Kinda)"; - - ListView_SetItem(hWndList, &item); - - item.iSubItem = 2; - int count = 0; - for (int i = 0; inumDevices; i++) { - Device *dev = dm->devices[i]; - if (!dev->enabled) continue; - count += dev->pads[port][slot].numBindings + dev->pads[port][slot].numFFBindings; - } - wsprintf(text, L"%i", count); - item.pszText = text; - ListView_SetItem(hWndList, &item); - index++; - } - } - while (ListView_DeleteItem(hWndList, index)); - int sel = ListView_GetNextItem(hWndList, -1, LVNI_SELECTED); - - int enable; - if (!ListIndexToPortAndSlot(sel, &port, &slot)) { - enable = 0; - SendMessage(hWndCombo, CB_SETCURSEL, -1, 0); - CheckDlgButton(hWnd, IDC_ANALOG_START1, BST_UNCHECKED); - } - else { - enable = 1; - SendMessage(hWndCombo, CB_SETCURSEL, config.padConfigs[port][slot].type, 0); - CheckDlgButton(hWnd, IDC_ANALOG_START1, BST_CHECKED*config.padConfigs[port][slot].autoAnalog); - } - EnableWindow(hWndCombo, enable); - EnableWindow(hWndAnalog, enable); - //ListView_SetExtendedListViewStyleEx(hWndList, LVS_EX_DOUBLEBUFFER|LVS_EX_ONECLICKACTIVATE, LVS_EX_DOUBLEBUFFER|LVS_EX_ONECLICKACTIVATE); - recurse = 0; -} - -INT_PTR CALLBACK GeneralDialogProc(HWND hWnd, unsigned int msg, WPARAM wParam, LPARAM lParam) { - int i; - HWND hWndList = GetDlgItem(hWnd, IDC_PAD_LIST); - switch (msg) { - case WM_INITDIALOG: - { - HWND hWndCombo = GetDlgItem(hWnd, IDC_PAD_TYPE); - if (SendMessage(hWndCombo, CB_GETCOUNT, 0, 0) == 0) { - LVCOLUMN c; - c.mask = LVCF_TEXT | LVCF_WIDTH; - c.cx = 50; - c.pszText = L"Pad"; - ListView_InsertColumn(hWndList, 0, &c); - c.cx = 120; - c.pszText = L"Type"; - ListView_InsertColumn(hWndList, 1, &c); - c.cx = 70; - c.pszText = L"Bindings"; - ListView_InsertColumn(hWndList, 2, &c); - selected = 0; - ListView_SetExtendedListViewStyleEx(hWndList, LVS_EX_FULLROWSELECT|LVS_EX_DOUBLEBUFFER, LVS_EX_FULLROWSELECT|LVS_EX_DOUBLEBUFFER); - SendMessage(hWndList, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, LVS_EX_FULLROWSELECT); - SendMessage(hWndCombo, CB_ADDSTRING, 0, (LPARAM) L"Unplugged"); - SendMessage(hWndCombo, CB_ADDSTRING, 0, (LPARAM) L"Dualshock 2"); - SendMessage(hWndCombo, CB_ADDSTRING, 0, (LPARAM) L"Guitar"); - } - } - UpdatePadPages(); - hWndGeneral = hWnd; - RefreshEnabledDevicesAndDisplay(0, hWnd, 0); - UpdatePadList(hWnd); - - if (!DualShock3Possible()) { - config.gameApis.dualShock3 = 0; - EnableWindow(GetDlgItem(hWnd, IDC_G_DS3), 0); - } - - for (int j=0; j>1)); - - if (config.osVersion < 6) EnableWindow(GetDlgItem(hWnd, IDC_VISTA_VOLUME), 0); - - - if (config.keyboardApi < 0 || config.keyboardApi > 3) config.keyboardApi = NO_API; - CheckRadioButton(hWnd, IDC_KB_DISABLE, IDC_KB_RAW, IDC_KB_DISABLE + config.keyboardApi); - if (config.mouseApi < 0 || config.mouseApi > 3) config.mouseApi = NO_API; - CheckRadioButton(hWnd, IDC_M_DISABLE, IDC_M_RAW, IDC_M_DISABLE + config.mouseApi); - - if (!InitializeRawInput()) { - EnableWindow(GetDlgItem(hWnd, IDC_KB_RAW), 0); - EnableWindow(GetDlgItem(hWnd, IDC_M_RAW), 0); - } - break; - case WM_DEVICECHANGE: - if (wParam == DBT_DEVNODES_CHANGED) { - RefreshEnabledDevicesAndDisplay(1, hWndGeneral, 1); - UpdatePadList(hWnd); - } - break; - case WM_COMMAND: - if (LOWORD(wParam) == IDC_PAD_TYPE) { - if (HIWORD(wParam) == CBN_SELCHANGE) { - HWND hWndCombo = GetDlgItem(hWnd, IDC_PAD_TYPE); - int index = ListView_GetNextItem(hWndList, -1, LVNI_SELECTED); - int sel = SendMessage(hWndCombo, CB_GETCURSEL, 0, 0); - int port, slot; - if (sel < 0 || !ListIndexToPortAndSlot(index, &port, &slot)) break; - if (sel != config.padConfigs[port][slot].type) { - config.padConfigs[port][slot].type = (PadType)sel; - UpdatePadList(hWnd); - UpdatePadPages(); - RefreshEnabledDevicesAndDisplay(0, hWnd, 1); - PropSheet_Changed(hWndProp, hWnd); - } - } - } - else if (HIWORD(wParam)==BN_CLICKED && (LOWORD(wParam) == ID_LOAD || LOWORD(wParam) == ID_SAVE)) { - OPENFILENAMEW ofn; - memset (&ofn, 0, sizeof(ofn)); - ofn.lStructSize = sizeof(ofn); - ofn.hwndOwner = hWnd; - ofn.lpstrFilter = L"LilyPad Config Files\0*.lily\0All Files\0*.*\0\0"; - wchar_t file[MAX_PATH+1]; - ofn.lpstrFile = file; - ofn.nMaxFile = MAX_PATH; - wcscpy(file, config.lastSaveConfigFileName); - ofn.lpstrInitialDir = config.lastSaveConfigPath; - ofn.Flags = OFN_DONTADDTORECENT | OFN_LONGNAMES | OFN_NOCHANGEDIR; - if (LOWORD(wParam) == ID_LOAD) { - ofn.lpstrTitle = L"Load LilyPad Configuration"; - ofn.Flags |= OFN_FILEMUSTEXIST; - if (GetOpenFileNameW(&ofn)) { - LoadSettings(1, ofn.lpstrFile); - GeneralDialogProc(hWnd, WM_INITDIALOG, 0, 0); - PropSheet_Changed(hWndProp, hWnd); - } - } - else { - ofn.lpstrTitle = L"Save LilyPad Configuration"; - ofn.Flags |= OFN_OVERWRITEPROMPT; - if (GetSaveFileNameW(&ofn)) { - if (SaveSettings(ofn.lpstrFile) == -1) { - MessageBox(hWnd, L"Save fail", L"Couldn't save file.", MB_OK); - } - } - } - break; - } - - else if (HIWORD(wParam)==BN_CLICKED && LOWORD(wParam) == ID_TEST) { - Diagnostics(hWnd); - RefreshEnabledDevices(); - } - else if (HIWORD(wParam)==BN_CLICKED && LOWORD(wParam) == ID_REFRESH) { - RefreshEnabledDevicesAndDisplay(1, hWnd, 1); - UpdatePadList(hWnd); - } - else if (HIWORD(wParam)==BN_CLICKED && LOWORD(wParam) == IDC_ANALOG_START1) { - int index = ListView_GetNextItem(hWndList, -1, LVNI_SELECTED); - int port, slot; - if (!ListIndexToPortAndSlot(index, &port, &slot)) break; - config.padConfigs[port][slot].autoAnalog = (IsDlgButtonChecked(hWnd, IDC_ANALOG_START1) == BST_CHECKED); - PropSheet_Changed(hWndProp, hWnd); - } - else { - int t = IDC_CLOSE_HACK1; - int test = LOWORD(wParam); - if (test == IDC_CLOSE_HACK1) { - CheckDlgButton(hWnd, IDC_CLOSE_HACK2, BST_UNCHECKED); - } - else if (test == IDC_CLOSE_HACK2) { - CheckDlgButton(hWnd, IDC_CLOSE_HACK1, BST_UNCHECKED); - } - - int mtap = config.multitap[0] + 2*config.multitap[1]; - int vistaVol = config.vistaVolume; - - for (int j=0; jhdr.hwndFrom == hWndProp) { - switch(n->hdr.code) { - case PSN_QUERYCANCEL: - case PSN_KILLACTIVE: - EndBinding(hWnd); - return 0; - case PSN_SETACTIVE: - //selected = 0; - UpdatePadList(hWnd); - return 0; - case PSN_APPLY: - selected = 0; - if (SaveSettings()) { - SetWindowLong(hWnd, DWL_MSGRESULT, PSNRET_INVALID_NOCHANGEPAGE); - return 0; - } - SetWindowLong(hWnd, DWL_MSGRESULT, PSNRET_NOERROR); - return 1; - } - } - else if (n->hdr.idFrom == IDC_LIST && n->hdr.code == NM_DBLCLK) { - Diagnostics(hWnd); - } - else if (n->hdr.idFrom == IDC_PAD_LIST) { - if (n->hdr.code == LVN_ITEMCHANGED) { - UpdatePadList(hWnd); - } - if (n->hdr.code == NM_RCLICK) { - UpdatePadList(hWnd); - int index = ListView_GetNextItem(hWndList, -1, LVNI_SELECTED); - int port1, slot1, port2, slot2; - if (!ListIndexToPortAndSlot(index, &port1, &slot1)) break; - HMENU hMenu = CreatePopupMenu(); - if (!hMenu) break; - MENUITEMINFOW info; - for (port2=1; port2>=0; port2--) { - for (slot2 = 3; slot2>=0; slot2--) { - wchar_t text[40]; - wchar_t pad[20]; - if (!GetPadString(pad, port2, slot2)) continue; - info.cbSize = sizeof(info); - info.fMask = MIIM_STRING | MIIM_ID; - info.dwTypeData = text; - if (port2 == port1 && slot2 == slot1) { - int index = GetMenuItemCount(hMenu); - wsprintfW(text, L"Clear %s Bindings", pad); - info.wID = -1; - InsertMenuItemW(hMenu, index, 1, &info); - info.fMask = MIIM_TYPE; - info.fType = MFT_SEPARATOR; - InsertMenuItemW(hMenu, index, 1, &info); - } - else { - info.wID = port2+2*slot2+1; - wsprintfW(text, L"Swap with %s", pad); - InsertMenuItemW(hMenu, 0, 1, &info); - } - } - } - POINT pos; - GetCursorPos(&pos); - short res = TrackPopupMenuEx(hMenu, TPM_NONOTIFY|TPM_RETURNCMD, pos.x, pos.y, hWndProp, 0); - DestroyMenu(hMenu); - if (!res) break; - if (res > 0) { - res--; - slot2 = res / 2; - port2 = res&1; - PadConfig padCfgTemp = config.padConfigs[port1][slot1]; - config.padConfigs[port1][slot1] = config.padConfigs[port2][slot2]; - config.padConfigs[port2][slot2] = padCfgTemp; - for (int i=0; inumDevices; i++) { - if (dm->devices[i]->type == IGNORE) continue; - PadBindings bindings = dm->devices[i]->pads[port1][slot1]; - dm->devices[i]->pads[port1][slot1] = dm->devices[i]->pads[port2][slot2]; - dm->devices[i]->pads[port2][slot2] = bindings; - } - } - else { - for (int i=0; inumDevices; i++) { - if (dm->devices[i]->type == IGNORE) continue; - free(dm->devices[i]->pads[port1][slot1].bindings); - for (int j=0; jdevices[i]->pads[port1][slot1].numFFBindings; j++) { - free(dm->devices[i]->pads[port1][slot1].ffBindings[j].axes); - } - free(dm->devices[i]->pads[port1][slot1].ffBindings); - memset(&dm->devices[i]->pads[port1][slot1], 0, sizeof(dm->devices[i]->pads[port1][slot1])); - } - } - UpdatePadPages(); - UpdatePadList(hWnd); - PropSheet_Changed(hWndProp, hWnd); - } - } - } - break; - default: - break; - } - return 0; -} - -int CALLBACK PropSheetProc(HWND hWnd, UINT msg, LPARAM lParam) { - if (hWnd) hWndProp = hWnd; - return 0; -} - -void Configure() { - // Can end up here without PADinit() being called first. - LoadSettings(); - // Can also end up here after running emulator a bit, and possibly - // disabling some devices due to focus changes, or releasing mouse. - RefreshEnabledDevices(0); - - memset(hWnds, 0, sizeof(hWnds)); - - PROPSHEETPAGE psp; - ZeroMemory(&psp, sizeof(psp)); - psp.dwSize = sizeof(psp); - psp.dwFlags = PSP_USETITLE | PSP_PREMATURE; - psp.hInstance = hInst; - psp.pfnDlgProc = GeneralDialogProc; - psp.pszTitle = L"General"; - psp.pszTemplate = MAKEINTRESOURCE(IDD_GENERAL); - HPROPSHEETPAGE page = CreatePropertySheetPage(&psp); - if (!page) return; - - PROPSHEETHEADER psh; - ZeroMemory(&psh, sizeof(psh)); - psh.dwFlags = PSH_DEFAULT | PSH_USECALLBACK | PSH_NOCONTEXTHELP; - psh.dwSize = sizeof(PROPSHEETHEADER); - psh.pfnCallback = PropSheetProc; - psh.hwndParent = GetActiveWindow(); - psh.nPages = 1; - psh.phpage = &page; - wchar_t title[200]; - GetNameAndVersionString(title); - wcscat(title, L" Settings"); - psh.pszCaption = title; - PropertySheet(&psh); - LoadSettings(1); - memset(hWnds, 0, sizeof(hWnds)); -} - -void UnloadConfigs() { - if (dm) { - delete dm; - dm = 0; - } -} +#include "Global.h" + +#include "resource.h" +#include "InputManager.h" +#include "Config.h" + +#include "Diagnostics.h" +#include "DeviceEnumerator.h" +#include "KeyboardQueue.h" +#include "WndProcEater.h" +#include "DualShock3.h" + +// Needed to know if raw input is available. It requires XP or higher. +#include "RawInput.h" + +GeneralConfig config; + +// 1 if running inside a PS2 emulator. Set to 1 on any +// of the PS2-specific functions (PS2EgetLibVersion2, PS2EgetLibType). +// Only affects if I allow read input in GS thread to be set. +u8 ps2e = 0; + +HWND hWndProp = 0; + +int selected = 0; + +// Older versions of PCSX2 don't always create the ini dir on startup, so LilyPad does it +// for it. But if PCSX2 sets the ini path with a call to setSettingsDir, then it means +// we shouldn't make our own. +bool createIniDir = true; + +HWND hWnds[2][4]; +HWND hWndGeneral = 0; + +struct GeneralSettingsBool { + wchar_t *name; + unsigned int ControlId; + u8 defaultValue; +}; + +// Ties together config data structure, config files, and general config +// dialog. +const GeneralSettingsBool BoolOptionsInfo[] = { + {L"Force Cursor Hide", IDC_FORCE_HIDE, 0}, + {L"Mouse Unfocus", IDC_MOUSE_UNFOCUS, 1}, + {L"Background", IDC_BACKGROUND, 1}, + {L"Multiple Bindings", IDC_MULTIPLE_BINDING, 0}, + + {L"DirectInput Game Devices", IDC_G_DI, 1}, + {L"XInput", IDC_G_XI, 1}, + {L"DualShock 3", IDC_G_DS3, 0}, + + {L"Multitap 1", IDC_MULTITAP1, 0}, + {L"Multitap 2", IDC_MULTITAP2, 0}, + + {L"Escape Fullscreen Hack", IDC_ESCAPE_FULLSCREEN_HACK, 1}, + {L"Disable Screen Saver", IDC_DISABLE_SCREENSAVER, 1}, + {L"Logging", IDC_DEBUG_FILE, 0}, + + {L"Save State in Title", IDC_SAVE_STATE_TITLE, 1}, + {L"GH2", IDC_GH2_HACK, 0}, + + {L"Vista Volume", IDC_VISTA_VOLUME, 1}, +}; + +void Populate(int port, int slot); + +void SetupLogSlider(HWND hWndSlider) { + SendMessage(hWndSlider, TBM_SETRANGEMIN, 0, 1); + SendMessage(hWndSlider, TBM_SETPAGESIZE, 0, 1<<13); + SendMessage(hWndSlider, TBM_SETRANGEMAX, 1, 1<<22); + SendMessage(hWndSlider, TBM_SETTIC, 0, 1<<21); + SendMessage(hWndSlider, TBM_SETTIC, 0, 1<<20); + SendMessage(hWndSlider, TBM_SETTIC, 0, 3<<20); +} + +int GetLogSliderVal(HWND hWnd, int id) { + HWND hWndSlider = GetDlgItem(hWnd, id); + int val = SendMessage(hWndSlider, TBM_GETPOS, 0, 0); + if (val <= (1<<21)) { + val = (val+16) >> 5; + } + else { + double v = (val - (1<<21))/(double)((1<<22)- (1<<21)); + double max = log((double)(1<<23)); + double min = log((double)BASE_SENSITIVITY); + v = v*(max-min)+min; + // exp is not implemented in ntdll, so use pow instead. + // More than accurate enough for my needs. + // v = exp(v); + v = pow(2.7182818284590451, v); + if (v > (1<<23)) v = (1<<23); + val = (int)floor(v+0.5); + } + if (!val) val = 1; + if (IsDlgButtonChecked(hWnd, id+1) == BST_CHECKED) { + val = -val; + } + return val; +} + +void SetLogSliderVal(HWND hWnd, int id, HWND hWndText, int val) { + HWND hWndSlider = GetDlgItem(hWnd, id); + int sliderPos = 0; + wchar_t temp[30]; + EnableWindow(hWndSlider, val != 0); + EnableWindow(hWndText, val != 0); + EnableWindow(GetDlgItem(hWnd, id+1), val != 0); + if (!val) val = BASE_SENSITIVITY; + CheckDlgButton(hWnd, id+1, BST_CHECKED * (val<0)); + if (val < 0) { + val = -val; + } + if (val <= BASE_SENSITIVITY) { + sliderPos = val<<5; + } + else { + double max = log((double)(1<<23)); + double min = log((double)BASE_SENSITIVITY); + double v = log((double)(val)); + v = (v-min)/(max-min); + v = ((1<<22)- (1<<21)) *v + (1<<21); + // _ftol + sliderPos = (int)(floor(v+0.5)); + } + SendMessage(hWndSlider, TBM_SETPOS, 1, sliderPos); + int val2 = (int)(1000*(__int64)val/BASE_SENSITIVITY); + wsprintfW(temp, L"%i.%03i", val2/1000, val2%1000); + SetWindowTextW(hWndText, temp); +} + +void RefreshEnabledDevices(int updateDeviceList) { + // Clears all device state. + static int lastXInputState = -1; + if (updateDeviceList || lastXInputState != config.gameApis.xInput) { + EnumDevices(config.gameApis.xInput); + lastXInputState = config.gameApis.xInput; + } + + for (int i=0; inumDevices; i++) { + Device *dev = dm->devices[i]; + + if (!dev->attached && dev->displayName[0] != '[') { + wchar_t *newName = (wchar_t*) malloc(sizeof(wchar_t) * (wcslen(dev->displayName) + 12)); + wsprintfW(newName, L"[Detached] %s", dev->displayName); + free(dev->displayName); + dev->displayName = newName; + } + + if ((dev->type == KEYBOARD && dev->api == IGNORE_KEYBOARD) || + (dev->type == KEYBOARD && dev->api == config.keyboardApi) || + (dev->type == MOUSE && dev->api == config.mouseApi) || + (dev->type == OTHER && + ((dev->api == DI && config.gameApis.directInput) || + (dev->api == DS3 && config.gameApis.dualShock3) || + (dev->api == XINPUT && config.gameApis.xInput)))) { + if (config.gameApis.dualShock3 && dev->api == DI && dev->displayName && + !wcsicmp(dev->displayName, L"DX PLAYSTATION(R)3 Controller")) { + dm->DisableDevice(i); + } + else { + dm->EnableDevice(i); + } + } + else { + dm->DisableDevice(i); + } + } +} + +// Disables/enables devices as necessary. Also updates diagnostic list +// and pad1/pad2 bindings lists, depending on params. Also updates device +// list, if indicated. + +// Must be called at some point when entering config mode, as I disable +// (non-keyboard/mice) devices with no active bindings when the emulator's running. +void RefreshEnabledDevicesAndDisplay(int updateDeviceList = 0, HWND hWnd = 0, int populate=0) { + RefreshEnabledDevices(updateDeviceList); + if (hWnd) { + HWND hWndList = GetDlgItem(hWnd, IDC_LIST); + ListView_SetExtendedListViewStyleEx(hWndList, LVS_EX_DOUBLEBUFFER, LVS_EX_DOUBLEBUFFER); + int count = ListView_GetItemCount(hWndList); + LVITEM item; + item.iItem = 0; + item.iSubItem = 0; + item.mask = LVIF_TEXT | LVIF_PARAM; + for (int j=0; jnumDevices; j++) { + if (dm->devices[j]->enabled && dm->devices[j]->api != IGNORE_KEYBOARD) { + item.lParam = j; + item.pszText = dm->devices[j]->displayName; + if (count > 0) { + ListView_SetItem(hWndList, &item); + count --; + } + else { + ListView_InsertItem(hWndList, &item); + } + item.iItem++; + } + } + // This way, won't scroll list to start. + while(count > 0) { + ListView_DeleteItem(hWndList, item.iItem); + count --; + } + } + if (populate) { + for (int i=0; i<8; i++) { + Populate(i&1, i>>1); + } + } +} + +wchar_t *GetCommandStringW(u8 command, int port, int slot) { + static wchar_t temp[34]; + if (command >= 0x20 && command <= 0x27) { + if (config.padConfigs[port][slot].type == GuitarPad && (command == 0x20 || command == 0x22)) { + HWND hWnd = GetDlgItem(hWnds[port][slot], 0x10F0+command); + int res = GetWindowTextW(hWnd, temp, 20); + if ((unsigned int)res-1 <= 18) return temp; + } + static wchar_t stick[2] = {'L', 'R'}; + static wchar_t *dir[] = { + L"Up", L"Right", + L"Down", L"Left"}; + wsprintfW(temp, L"%c-Stick %s", stick[(command-0x20)/4], dir[command & 3]); + return temp; + } + /* Get text from the buttons. */ + if (command >= 0x0C && command <=0x28) { + HWND hWnd = GetDlgItem(hWnds[port][slot], 0x10F0+command); + if (!hWnd) { + wchar_t *strings[] = { + L"Lock Buttons", + L"Lock Input", + L"Lock Direction", + L"Mouse", + L"Select", + L"L3", + L"R3", + L"Start", + L"Up", + L"Right", + L"Down", + L"Left", + L"L2", + L"R2", + L"L1", + L"R1", + L"Triangle", + L"Circle", + L"Square", + L"Cross", + L"L-Stick Up", + L"L-Stick Right", + L"L-Stick Down", + L"L-Stick Left", + L"R-Stick Up", + L"R-Stick Right", + L"R-Stick Down", + L"R-Stick Left", + L"Analog", + }; + return strings[command - 0xC]; + } + int res = GetWindowTextW(hWnd, temp, 20); + if ((unsigned int)res-1 <= 18) return temp; + } + else if (command == 0x7F) { + return L"Ignore Key"; + } + return L""; +} + +static wchar_t iniFile[MAX_PATH*2] = L"inis\\LilyPad.ini"; + +void CALLBACK PADsetSettingsDir( const char *dir ) +{ + // emulator assures a trailing slash/backslash (yay!) + swprintf_s( iniFile, L"%S", (dir==NULL) ? "inis\\" : dir ); + wcscat(iniFile, L"LilyPad.ini"); + + createIniDir = false; +} + +int GetBinding(int port, int slot, int index, Device *&dev, Binding *&b, ForceFeedbackBinding *&ffb); +int BindCommand(Device *dev, unsigned int uid, unsigned int port, unsigned int slot, int command, int sensitivity, int turbo, int deadZone); + +int CreateEffectBinding(Device *dev, wchar_t *effectName, unsigned int port, unsigned int slot, unsigned int motor, ForceFeedbackBinding **binding); + +void SelChanged(int port, int slot) { + HWND hWnd = hWnds[port][slot]; + if (!hWnd) return; + HWND hWndTemp, hWndList = GetDlgItem(hWnd, IDC_LIST); + int j, i = ListView_GetSelectedCount(hWndList); + wchar_t *devName = L"N/A"; + wchar_t *key = L"N/A"; + wchar_t *command = L"N/A"; + // Second value is now turbo. + int turbo = -1; + int sensitivity = 0; + int deadZone = 0; + int nonButtons = 0; + // Set if sensitivity != 0, but need to disable flip anyways. + // Only used to relative axes. + int disableFlip = 0; + wchar_t temp[4][1000]; + Device *dev; + int bFound = 0; + int ffbFound = 0; + ForceFeedbackBinding *ffb = 0; + Binding *b = 0; + if (i >= 1) { + int index = -1; + int flipped = 0; + while (1) { + index = ListView_GetNextItem(hWndList, index, LVNI_SELECTED); + if (index < 0) break; + LVITEMW item; + item.iItem = index; + item.mask = LVIF_TEXT; + item.pszText = temp[3]; + for (j=0; j<3; j++) { + item.iSubItem = j; + item.cchTextMax = sizeof(temp[0])/sizeof(temp[3][0]); + if (!ListView_GetItem(hWndList, &item)) break; + if (!bFound && !ffbFound) + wcscpy(temp[j], temp[3]); + else if (wcsicmp(temp[j], temp[3])) { + int q = 0; + while (temp[j][q] == temp[3][q]) q++; + if (q && temp[j][q-1] == ' ' && temp[j][q] && temp[j][q+1] == 0) q--; + if (j == 1) { + // Really ugly, but merges labels for multiple directions for same axis. + if ((temp[j][q] == 0 || (temp[j][q] == ' ' && temp[j][q+2] == 0)) && + (temp[3][q] == 0 || (temp[3][q] == ' ' && temp[3][q+2] == 0))) { + temp[j][q] = 0; + continue; + } + } + // Merge different directions for same stick. + else if (j == 2 && q > 4) { + temp[j][q] = 0; + continue; + } + wcscpy(temp[j], L"*"); + } + } + if (j == 3) { + devName = temp[0]; + key = temp[1]; + command = temp[2]; + if (GetBinding(port, slot, index, dev, b, ffb)) { + if (b) { + bFound ++; + VirtualControl *control = &dev->virtualControls[b->controlIndex]; + // Ignore + if (b->command != 0x7F) { + // Only relative axes can't have negative sensitivity. + if (((control->uid >> 16) & 0xFF) == RELAXIS) { + disableFlip = 1; + } + turbo += b->turbo; + if (b->sensitivity < 0) { + flipped ++; + sensitivity -= b->sensitivity; + } + else { + sensitivity += b->sensitivity; + } + if (((control->uid >> 16)&0xFF) != PSHBTN && ((control->uid >> 16)&0xFF) != TGLBTN) { + deadZone += b->deadZone; + nonButtons++; + } + } + else disableFlip = 1; + } + else ffbFound++; + } + } + } + if ((bFound && ffbFound) || ffbFound > 1) { + ffb = 0; + turbo = -1; + deadZone = 0; + sensitivity = 0; + disableFlip = 1; + bFound = ffbFound = 0; + } + else if (bFound) { + turbo++; + sensitivity /= bFound; + if (nonButtons) { + deadZone /= nonButtons; + } + if (bFound > 1) disableFlip = 1; + else if (flipped) { + sensitivity = -sensitivity; + } + } + } + + for (i=IDC_SLIDER1; i= IDC_FF_AXIS1_ENABLED) { + int index = (i - IDC_FF_AXIS1_ENABLED) / 16; + if (index >= dev->numFFAxes) { + enable = 0; + } + else { + int type = i - index*16; + AxisEffectInfo *info = 0; + if (dev->numFFAxes > index) { + info = ffb->axes+index; + } + if (type == IDC_FF_AXIS1) { + // takes care of IDC_FF_AXIS1_FLIP as well. + SetupLogSlider(hWndTemp); + int force = 0; + if (info) { + force = info->force; + } + SetLogSliderVal(hWnd, i, GetDlgItem(hWnd, i-IDC_FF_AXIS1+IDC_FF_AXIS1_SCALE), force); + } + else if (type == IDC_FF_AXIS1_ENABLED) { + CheckDlgButton(hWnd, i, BST_CHECKED * (info->force!=0)); + wsprintfW(temp[0], L"Axis %i", index+1); + if (dev->ffAxes[index].displayName && wcslen(dev->ffAxes[index].displayName) < 50) { + wchar_t *end = wcschr(temp[0], 0); + wsprintfW(end, L": %s", dev->ffAxes[index].displayName); + } + SetWindowText(hWndTemp, temp[0]); + } + } + } + ShowWindow(hWndTemp, enable); + } + } + if (!ffb) { + SetLogSliderVal(hWnd, IDC_SLIDER1, GetDlgItem(hWnd, IDC_AXIS_SENSITIVITY1), sensitivity); + SetLogSliderVal(hWnd, IDC_SLIDER_DEADZONE, GetDlgItem(hWnd, IDC_AXIS_DEADZONE), deadZone); + + if (disableFlip) EnableWindow(GetDlgItem(hWnd, IDC_FLIP1), 0); + + EnableWindow(GetDlgItem(hWnd, IDC_TURBO), turbo >= 0); + if (turbo > 0 && turbo < bFound) { + SendMessage(GetDlgItem(hWnd, IDC_TURBO), BM_SETSTYLE, BS_AUTO3STATE, 0); + CheckDlgButton(hWnd, IDC_TURBO, BST_INDETERMINATE); + } + else { + SendMessage(GetDlgItem(hWnd, IDC_TURBO), BM_SETSTYLE, BS_AUTOCHECKBOX, 0); + CheckDlgButton(hWnd, IDC_TURBO, BST_CHECKED * (bFound && turbo == bFound)); + } + HWND hWndCombo = GetDlgItem(hWnd, IDC_AXIS_DIRECTION); + int enableCombo = 0; + SendMessage(hWndCombo, CB_RESETCONTENT, 0, 0); + if (b && bFound == 1) { + VirtualControl *control = &dev->virtualControls[b->controlIndex]; + unsigned int uid = control->uid; + if (((uid>>16) & 0xFF) == ABSAXIS) { + enableCombo = 1; + wchar_t *endings[3] = {L" -", L" +", L""}; + wchar_t *string = temp[3]; + wcscpy(string, key); + wchar_t *end = wcschr(string, 0); + int sel = 2; + if (!(uid & UID_AXIS)) { + end[-2] = 0; + sel = (end[-1] == '+'); + end -= 2; + } + for (int i=0; i<3; i++) { + wcscpy(end, endings[i]); + SendMessage(hWndCombo, CB_ADDSTRING, 0, (LPARAM) string); + if (i == sel) + SendMessage(hWndCombo, CB_SETCURSEL, i, 0); + } + } + } + EnableWindow(hWndCombo, enableCombo); + if (!enableCombo) { + SendMessage(hWndCombo, CB_ADDSTRING, 0, (LPARAM) key); + SendMessage(hWndCombo, CB_SELECTSTRING, i, (LPARAM) key); + } + + SetDlgItemText(hWnd, IDC_AXIS_DEVICE1, devName); + SetDlgItemText(hWnd, IDC_AXIS_CONTROL1, command); + } + else { + wchar_t temp2[2000]; + wsprintfW(temp2, L"%s / %s", devName, command); + SetDlgItemText(hWnd, ID_FF, temp2); + + hWndTemp = GetDlgItem(hWnd, IDC_FF_EFFECT); + SendMessage(hWndTemp, CB_RESETCONTENT, 0, 0); + for (i=0; inumFFEffectTypes; i++) { + SendMessage(hWndTemp, CB_INSERTSTRING, i, (LPARAM)dev->ffEffectTypes[i].displayName); + } + SendMessage(hWndTemp, CB_SETCURSEL, ffb->effectIndex, 0); + EnableWindow(hWndTemp, dev->numFFEffectTypes>1); + } +} + +void UnselectAll(HWND hWnd) { + int i = ListView_GetSelectedCount(hWnd); + while (i-- > 0) { + int index = ListView_GetNextItem(hWnd, -1, LVNI_SELECTED); + ListView_SetItemState(hWnd, index, 0, LVIS_SELECTED); + } +} + + +int GetItemIndex(int port, int slot, Device *dev, ForceFeedbackBinding *binding) { + int count = 0; + for (int i = 0; inumDevices; i++) { + Device *dev2 = dm->devices[i]; + if (!dev2->enabled) continue; + if (dev2 != dev) { + count += dev2->pads[port][slot].numBindings + dev2->pads[port][slot].numFFBindings; + continue; + } + return count += dev2->pads[port][slot].numBindings + (binding - dev2->pads[port][slot].ffBindings); + } + return -1; +} +int GetItemIndex(int port, int slot, Device *dev, Binding *binding) { + int count = 0; + for (int i = 0; inumDevices; i++) { + Device *dev2 = dm->devices[i]; + if (!dev2->enabled) continue; + if (dev2 != dev) { + count += dev2->pads[port][slot].numBindings + dev2->pads[port][slot].numFFBindings; + continue; + } + return count += binding - dev->pads[port][slot].bindings; + } + return -1; +} + +// Doesn't check if already displayed. +int ListBoundCommand(int port, int slot, Device *dev, Binding *b) { + if (!hWnds[port][slot]) return -1; + HWND hWndList = GetDlgItem(hWnds[port][slot], IDC_LIST); + int index = -1; + if (hWndList) { + index = GetItemIndex(port, slot, dev, b); + if (index >= 0) { + LVITEM item; + item.mask = LVIF_TEXT; + item.pszText = dev->displayName; + item.iItem = index; + item.iSubItem = 0; + SendMessage(hWndList, LVM_INSERTITEM, 0, (LPARAM)&item); + item.mask = LVIF_TEXT; + item.iSubItem = 1; + item.pszText = dev->GetVirtualControlName(&dev->virtualControls[b->controlIndex]); + SendMessage(hWndList, LVM_SETITEM, 0, (LPARAM)&item); + item.iSubItem = 2; + item.pszText = GetCommandStringW(b->command, port, slot); + SendMessage(hWndList, LVM_SETITEM, 0, (LPARAM)&item); + } + } + return index; +} + +int ListBoundEffect(int port, int slot, Device *dev, ForceFeedbackBinding *b) { + if (!hWnds[port][slot]) return -1; + HWND hWndList = GetDlgItem(hWnds[port][slot], IDC_LIST); + int index = -1; + if (hWndList) { + index = GetItemIndex(port, slot, dev, b); + if (index >= 0) { + LVITEM item; + item.mask = LVIF_TEXT; + item.pszText = dev->displayName; + item.iItem = index; + item.iSubItem = 0; + SendMessage(hWndList, LVM_INSERTITEM, 0, (LPARAM)&item); + item.mask = LVIF_TEXT; + item.iSubItem = 1; + item.pszText = dev->ffEffectTypes[b->effectIndex].displayName; + SendMessage(hWndList, LVM_SETITEM, 0, (LPARAM)&item); + item.iSubItem = 2; + wchar_t *ps2Motors[2] = {L"Big Motor", L"Small Motor"}; + item.pszText = ps2Motors[b->motor]; + SendMessage(hWndList, LVM_SETITEM, 0, (LPARAM)&item); + } + } + return index; +} + +// Only for use with control bindings. Affects all highlighted bindings. +void ChangeValue(int port, int slot, int *newSensitivity, int *newTurbo, int *newDeadZone) { + if (!hWnds[port][slot]) return; + HWND hWndList = GetDlgItem(hWnds[port][slot], IDC_LIST); + int count = ListView_GetSelectedCount(hWndList); + if (count < 1) return; + int index = -1; + while (1) { + index = ListView_GetNextItem(hWndList, index, LVNI_SELECTED); + if (index < 0) break; + Device *dev; + Binding *b; + ForceFeedbackBinding *ffb; + if (!GetBinding(port, slot, index, dev, b, ffb) || ffb) return; + if (newSensitivity) { + // Don't change flip state when modifying multiple controls. + if (count > 1 && b->sensitivity < 0) + b->sensitivity = -*newSensitivity; + else + b->sensitivity = *newSensitivity; + } + if (newDeadZone) { + if (b->deadZone) { + b->deadZone = *newDeadZone; + } + } + if (newTurbo) { + b->turbo = *newTurbo; + } + } + PropSheet_Changed(hWndProp, hWnds[port][slot]); + SelChanged(port, slot); +} + +// Only for use with effect bindings. +void ChangeEffect(int port, int slot, int id, int *newForce, unsigned int *newEffectType) { + if (!hWnds[port][slot]) return; + HWND hWndList = GetDlgItem(hWnds[port][slot], IDC_LIST); + int i = ListView_GetSelectedCount(hWndList); + if (i != 1) return; + int index = ListView_GetNextItem(hWndList, -1, LVNI_SELECTED); + Device *dev; + Binding *b; + ForceFeedbackBinding *ffb; + if (!GetBinding(port, slot, index, dev, b, ffb) || b) return; + if (newForce) { + unsigned int axisIndex = (id - IDC_FF_AXIS1_ENABLED)/16; + if (axisIndex < (unsigned int)dev->numFFAxes) { + ffb->axes[axisIndex].force = *newForce; + } + } + if (newEffectType && *newEffectType < (unsigned int)dev->numFFEffectTypes) { + ffb->effectIndex = *newEffectType; + ListView_DeleteItem(hWndList, index); + index = ListBoundEffect(port, slot, dev, ffb); + ListView_SetItemState(hWndList, index, LVIS_SELECTED, LVIS_SELECTED); + } + PropSheet_Changed(hWndProp, hWnds[port][slot]); + SelChanged(port, slot); +} + + + +void Populate(int port, int slot) { + if (!hWnds[port][slot]) return; + HWND hWnd = GetDlgItem(hWnds[port][slot], IDC_LIST); + ListView_DeleteAllItems(hWnd); + int i, j; + + int multipleBinding = config.multipleBinding; + config.multipleBinding = 1; + for (j=0; jnumDevices; j++) { + Device *dev = dm->devices[j]; + if (!dev->enabled) continue; + for (i=0; ipads[port][slot].numBindings; i++) { + ListBoundCommand(port, slot, dev, dev->pads[port][slot].bindings+i); + } + for (i=0; ipads[port][slot].numFFBindings; i++) { + ListBoundEffect(port, slot, dev, dev->pads[port][slot].ffBindings+i); + } + } + config.multipleBinding = multipleBinding; + + hWnd = GetDlgItem(hWnds[port][slot], IDC_FORCEFEEDBACK); + SendMessage(hWnd, CB_RESETCONTENT, 0, 0); + int added = 0; + for (i=0; inumDevices; i++) { + Device *dev = dm->devices[i]; + if (dev->enabled && dev->numFFAxes && dev->numFFEffectTypes) { + SendMessage(hWnd, CB_INSERTSTRING, added, (LPARAM)dev->displayName); + SendMessage(hWnd, CB_SETITEMDATA, added, i); + added++; + } + } + SendMessage(hWnd, CB_SETCURSEL, 0, 0); + EnableWindow(hWnd, added!=0); + EnableWindow(GetDlgItem(hWnds[port][slot], ID_BIG_MOTOR), added!=0); + EnableWindow(GetDlgItem(hWnds[port][slot], ID_SMALL_MOTOR), added!=0); + + SelChanged(port, slot); +} + +int WritePrivateProfileInt(wchar_t *s1, wchar_t *s2, int v, wchar_t *ini) { + wchar_t temp[100]; + _itow(v, temp, 10); + return WritePrivateProfileStringW(s1, s2, temp, ini); +} + +void SetVolume(int volume) { + if (volume > 100) volume = 100; + if (volume < 0) volume = 0; + config.volume = volume; + unsigned int val = 0xFFFF * volume/100; + val = val | (val<<16); + for (int i=waveOutGetNumDevs()-1; i>=0; i--) { + waveOutSetVolume((HWAVEOUT)i, val); + } + WritePrivateProfileInt(L"General Settings", L"Volume", config.volume, iniFile); +} + +int SaveSettings(wchar_t *file=0) { + + // Need this either way for saving path. + if (!file) { + file = iniFile; + } + else { + wchar_t *c = wcsrchr(file, '\\'); + if (*c) { + *c = 0; + wcscpy(config.lastSaveConfigPath, file); + wcscpy(config.lastSaveConfigFileName, c+1); + *c = '\\'; + } + } + DeleteFileW(file); + + WritePrivateProfileStringW(L"General Settings", L"Last Config Path", config.lastSaveConfigPath, iniFile); + WritePrivateProfileStringW(L"General Settings", L"Last Config Name", config.lastSaveConfigFileName, iniFile); + + // Just check first, last, and all pad bindings. Should be more than enough. No real need to check + // config path. + int noError = 1; + + for (int i=0; inumDevices; i++) { + wchar_t id[50]; + wchar_t temp[50], temp2[1000]; + wsprintfW(id, L"Device %i", i); + Device *dev = dm->devices[i]; + wchar_t *name = dev->displayName; + while (name[0] == '[') { + wchar_t *name2 = wcschr(name, ']'); + if (!name2) break; + name = name2+1; + while (iswspace(name[0])) name++; + } + WritePrivateProfileStringW(id, L"Display Name", name, file); + WritePrivateProfileStringW(id, L"Instance ID", dev->instanceID, file); + if (dev->productID) { + WritePrivateProfileStringW(id, L"Product ID", dev->productID, file); + } + WritePrivateProfileInt(id, L"API", dev->api, file); + WritePrivateProfileInt(id, L"Type", dev->type, file); + int ffBindingCount = 0; + int bindingCount = 0; + for (int port=0; port<2; port++) { + for (int slot=0; slot<4; slot++) { + for (int j=0; jpads[port][slot].numBindings; j++) { + Binding *b = dev->pads[port][slot].bindings+j; + VirtualControl *c = &dev->virtualControls[b->controlIndex]; + wsprintfW(temp, L"Binding %i", bindingCount++); + wsprintfW(temp2, L"0x%08X, %i, %i, %i, %i, %i, %i", c->uid, port, b->command, b->sensitivity, b->turbo, slot, b->deadZone); + noError &= WritePrivateProfileStringW(id, temp, temp2, file); + } + for (int j=0; jpads[port][slot].numFFBindings; j++) { + ForceFeedbackBinding *b = dev->pads[port][slot].ffBindings+j; + ForceFeedbackEffectType *eff = &dev->ffEffectTypes[b->effectIndex]; + wsprintfW(temp, L"FF Binding %i", ffBindingCount++); + wsprintfW(temp2, L"%s %i, %i, %i", eff->effectID, port, b->motor, slot); + for (int k=0; knumFFAxes; k++) { + ForceFeedbackAxis *axis = dev->ffAxes + k; + AxisEffectInfo *info = b->axes + k; + wsprintfW(wcschr(temp2,0), L", %i, %i", axis->id, info->force); + } + noError &= WritePrivateProfileStringW(id, temp, temp2, file); + } + } + } + } + if (!noError) { + MessageBoxA(hWndProp, "Unable to save settings. Make sure the disk is not full or write protected, the file isn't write protected, and that the app has permissions to write to the directory. On Vista, try running in administrator mode.", "Error Writing Configuration File", MB_OK | MB_ICONERROR); + } + return !noError; +} + +u8 GetPrivateProfileBool(wchar_t *s1, wchar_t *s2, int def, wchar_t *ini) { + return (0!=GetPrivateProfileIntW(s1, s2, def, ini)); +} + +int LoadSettings(int force, wchar_t *file) { + if (dm && !force) return 0; + + if( createIniDir ) + { + CreateDirectory(L"inis", 0); + createIniDir = false; + } + + // Could just do ClearDevices() instead, but if I ever add any extra stuff, + // this will still work. + UnloadConfigs(); + dm = new InputDeviceManager(); + + if (!file) { + file = iniFile; + GetPrivateProfileStringW(L"General Settings", L"Last Config Path", L"inis", config.lastSaveConfigPath, sizeof(config.lastSaveConfigPath), file); + GetPrivateProfileStringW(L"General Settings", L"Last Config Name", L"LilyPad.lily", config.lastSaveConfigFileName, sizeof(config.lastSaveConfigFileName), file); + } + else { + wchar_t *c = wcsrchr(file, '\\'); + if (c) { + *c = 0; + wcscpy(config.lastSaveConfigPath, file); + wcscpy(config.lastSaveConfigFileName, c+1); + *c = '\\'; + WritePrivateProfileStringW(L"General Settings", L"Last Config Path", config.lastSaveConfigPath, iniFile); + WritePrivateProfileStringW(L"General Settings", L"Last Config Name", config.lastSaveConfigFileName, iniFile); + } + } + + for (int i=0; i= 100) break; + continue; + } + wchar_t *id2 = 0; + if (GetPrivateProfileStringW(id, L"Product ID", 0, temp4, 1000, file) && temp4[0]) + id2 = temp4; + + int api = GetPrivateProfileIntW(id, L"API", 0, file); + int type = GetPrivateProfileIntW(id, L"Type", 0, file); + if (!api || !type) continue; + + Device *dev = new Device((DeviceAPI)api, (DeviceType)type, temp2, temp3, id2); + dev->attached = 0; + dm->AddDevice(dev); + int j = 0; + int last = 0; + while (1) { + wsprintfW(temp, L"Binding %i", j++); + if (!GetPrivateProfileStringW(id, temp, 0, temp2, 1000, file)) { + if (j >= 100) { + if (!last) break; + last = 0; + } + continue; + } + last = 1; + unsigned int uid; + int port, command, sensitivity, turbo, slot = 0, deadZone = 0; + int w = 0; + char string[1000]; + while (temp2[w]) { + string[w] = (char)temp2[w]; + w++; + } + string[w] = 0; + int len = sscanf(string, " %i , %i , %i , %i , %i , %i , %i", &uid, &port, &command, &sensitivity, &turbo, &slot, &deadZone); + if (len >= 5 && type) { + VirtualControl *c = dev->GetVirtualControl(uid); + if (!c) c = dev->AddVirtualControl(uid, -1); + if (c) { + BindCommand(dev, uid, port, slot, command, sensitivity, turbo, deadZone); + } + } + } + j = 0; + while (1) { + wsprintfW(temp, L"FF Binding %i", j++); + if (!GetPrivateProfileStringW(id, temp, 0, temp2, 1000, file)) { + if (j >= 10) { + if (!last) break; + last = 0; + } + continue; + } + last = 1; + int port, slot, motor; + int w = 0; + char string[1000]; + char effect[1000]; + while (temp2[w]) { + string[w] = (char)temp2[w]; + w++; + } + string[w] = 0; + // wcstok not in ntdll. More effore than its worth to shave off + // whitespace without it. + if (sscanf(string, " %s %i , %i , %i", effect, &port, &motor, &slot) == 4) { + char *s = strchr(strchr(strchr(string, ',')+1, ',')+1, ','); + if (!s) continue; + s++; + w = 0; + while (effect[w]) { + temp2[w] = effect[w]; + w++; + } + temp2[w] = 0; + ForceFeedbackEffectType *eff = dev->GetForcefeedbackEffect(temp2); + if (!eff) { + // At the moment, don't record effect types. + // Only used internally, anyways, so not an issue. + dev->AddFFEffectType(temp2, temp2, EFFECT_CONSTANT); + // eff = &dev->ffEffectTypes[dev->numFFEffectTypes-1]; + } + ForceFeedbackBinding *b; + CreateEffectBinding(dev, temp2, port, slot, motor, &b); + if (b) { + while (1) { + int axisID = atoi(s); + if (!(s = strchr(s, ','))) break; + s++; + int force = atoi(s); + int i; + for (i=0; inumFFAxes; i++) { + if (axisID == dev->ffAxes[i].id) break; + } + if (i == dev->numFFAxes) { + dev->AddFFAxis(L"?", axisID); + } + b->axes[i].force = force; + if (!(s = strchr(s, ','))) break; + s++; + } + } + } + } + } + config.multipleBinding = multipleBinding; + + RefreshEnabledDevicesAndDisplay(1); + + return 0; +} + +inline int GetPort(HWND hWnd, int *slot) { + for (int i=0; i>1] == hWnd) { + *slot = i>>1; + return i&1; + } + } + *slot = 0; + return 0; +} + +void Diagnostics(HWND hWnd) { + HWND hWndList = GetDlgItem(hWnd, IDC_LIST); + if (!hWndList) return; + int index = ListView_GetNextItem(hWndList, -1, LVNI_SELECTED); + if (index < 0) return; + LVITEM item; + memset(&item, 0, sizeof(item)); + item.mask = LVIF_PARAM; + item.iSubItem = 0; + item.iItem = index; + if (!ListView_GetItem(hWndList, &item)) return; + Diagnose(item.lParam, hWnd); + RefreshEnabledDevicesAndDisplay(0, hWnd, 1); +} + +int GetBinding(int port, int slot, int index, Device *&dev, Binding *&b, ForceFeedbackBinding *&ffb) { + ffb = 0; + b = 0; + for (int i = 0; inumDevices; i++) { + dev = dm->devices[i]; + if (!dev->enabled) continue; + if (index < dev->pads[port][slot].numBindings) { + b = dev->pads[port][slot].bindings + index; + return 1; + } + index -= dev->pads[port][slot].numBindings; + + if (index < dev->pads[port][slot].numFFBindings) { + ffb = dev->pads[port][slot].ffBindings + index; + return 1; + } + index -= dev->pads[port][slot].numFFBindings; + } + return 0; +} + +// Only used when deleting things from ListView. Will remove from listview if needed. +void DeleteBinding(int port, int slot, Device *dev, Binding *b) { + if (dev->enabled && hWnds[port][slot]) { + int count = GetItemIndex(port, slot, dev, b); + if (count >= 0) { + HWND hWndList = GetDlgItem(hWnds[port][slot], IDC_LIST); + if (hWndList) { + ListView_DeleteItem(hWndList, count); + } + } + } + Binding *bindings = dev->pads[port][slot].bindings; + int i = b - bindings; + memmove(bindings+i, bindings+i+1, sizeof(Binding) * (dev->pads[port][slot].numBindings - i - 1)); + dev->pads[port][slot].numBindings--; +} + +void DeleteBinding(int port, int slot, Device *dev, ForceFeedbackBinding *b) { + if (dev->enabled && hWnds[port][slot]) { + int count = GetItemIndex(port, slot, dev, b); + if (count >= 0) { + HWND hWndList = GetDlgItem(hWnds[port][slot], IDC_LIST); + if (hWndList) { + ListView_DeleteItem(hWndList, count); + } + } + } + ForceFeedbackBinding *bindings = dev->pads[port][slot].ffBindings; + int i = b - bindings; + memmove(bindings+i, bindings+i+1, sizeof(Binding) * (dev->pads[port][slot].numFFBindings - i - 1)); + dev->pads[port][slot].numFFBindings--; +} + +int DeleteByIndex(int port, int slot, int index) { + ForceFeedbackBinding *ffb; + Binding *b; + Device *dev; + if (GetBinding(port, slot, index, dev, b, ffb)) { + if (b) { + DeleteBinding(port, slot, dev, b); + } + else { + DeleteBinding(port, slot, dev, ffb); + } + return 1; + } + return 0; +} + +int DeleteSelected(int port, int slot) { + if (!hWnds[port][slot]) return 0; + HWND hWnd = GetDlgItem(hWnds[port][slot], IDC_LIST); + int changes = 0; + while (1) { + int index = ListView_GetNextItem(hWnd, -1, LVNI_SELECTED); + if (index < 0) break; + changes += DeleteByIndex(port, slot, index); + } + //ShowScrollBar(hWnd, SB_VERT, 1); + return changes; +} + +int CreateEffectBinding(Device *dev, wchar_t *effectID, unsigned int port, unsigned int slot, unsigned int motor, ForceFeedbackBinding **binding) { + // Checks needed because I use this directly when loading bindings. + // Note: dev->numFFAxes *can* be 0, for loading from file. + *binding = 0; + if (port > 1 || slot>3 || motor > 1 || !dev->numFFEffectTypes) { + return -1; + } + ForceFeedbackEffectType *eff = 0; + if (effectID) { + eff = dev->GetForcefeedbackEffect(effectID); + } + if (!eff) { + eff = dev->ffEffectTypes; + } + if (!eff) { + return -1; + } + int effectIndex = eff - dev->ffEffectTypes; + dev->pads[port][slot].ffBindings = (ForceFeedbackBinding*) realloc(dev->pads[port][slot].ffBindings, (dev->pads[port][slot].numFFBindings+1) * sizeof(ForceFeedbackBinding)); + int newIndex = dev->pads[port][slot].numFFBindings; + while (newIndex && dev->pads[port][slot].ffBindings[newIndex-1].motor >= motor) { + dev->pads[port][slot].ffBindings[newIndex] = dev->pads[port][slot].ffBindings[newIndex-1]; + newIndex--; + } + ForceFeedbackBinding *b = dev->pads[port][slot].ffBindings + newIndex; + b->axes = (AxisEffectInfo*) calloc(dev->numFFAxes, sizeof(AxisEffectInfo)); + b->motor = motor; + b->effectIndex = effectIndex; + dev->pads[port][slot].numFFBindings++; + if (binding) *binding = b; + return ListBoundEffect(port, slot, dev, b); +} + +int BindCommand(Device *dev, unsigned int uid, unsigned int port, unsigned int slot, int command, int sensitivity, int turbo, int deadZone) { + // Checks needed because I use this directly when loading bindings. + if (port > 1 || slot>3) { + return -1; + } + if (!sensitivity) sensitivity = BASE_SENSITIVITY; + if ((uid>>16) & (PSHBTN|TGLBTN)) { + deadZone = 0; + } + else if (!deadZone) { + if ((uid>>16) & PRESSURE_BTN) { + deadZone = 1; + } + else { + deadZone = DEFAULT_DEADZONE; + } + } + // Relative axes can have negative sensitivity. + else if (((uid>>16) & 0xFF) == RELAXIS) { + sensitivity = abs(sensitivity); + } + VirtualControl *c = dev->GetVirtualControl(uid); + if (!c) return -1; + // Add before deleting. Means I won't scroll up one line when scrolled down to bottom. + int controlIndex = c - dev->virtualControls; + int index = 0; + PadBindings *p = dev->pads[port]+slot; + p->bindings = (Binding*) realloc(p->bindings, (p->numBindings+1) * sizeof(Binding)); + for (index = p->numBindings; index > 0; index--) { + if (p->bindings[index-1].controlIndex < controlIndex) break; + p->bindings[index] = p->bindings[index-1]; + } + Binding *b = p->bindings+index; + p->numBindings++; + b->command = command; + b->controlIndex = controlIndex; + b->turbo = turbo; + b->sensitivity = sensitivity; + b->deadZone = deadZone; + // Where it appears in listview. + int count = ListBoundCommand(port, slot, dev, b); + + int newBindingIndex = index; + index = 0; + while (index < p->numBindings) { + if (index == newBindingIndex) { + index ++; + continue; + } + b = p->bindings + index; + int nuke = 0; + if (config.multipleBinding) { + if (b->controlIndex == controlIndex && b->command == command) + nuke = 1; + } + else { + int uid2 = dev->virtualControls[b->controlIndex].uid; + if (b->controlIndex == controlIndex || (!((uid2^uid) & 0xFFFFFF) && ((uid|uid2) & (UID_POV | UID_AXIS)))) + nuke = 1; + } + if (!nuke) { + index++; + continue; + } + if (index < newBindingIndex) { + newBindingIndex--; + count --; + } + DeleteBinding(port, slot, dev, b); + } + if (!config.multipleBinding) { + for (int port2=0; port2<2; port2++) { + for (int slot2=0; slot2<4; slot2++) { + if (port2==port && slot2 == slot) continue; + PadBindings *p = dev->pads[port2]+slot2; + for (int i=0; i < p->numBindings; i++) { + Binding *b = p->bindings+i; + int uid2 = dev->virtualControls[b->controlIndex].uid; + if (b->controlIndex == controlIndex || (!((uid2^uid) & 0xFFFFFF) && ((uid|uid2) & (UID_POV | UID_AXIS)))) { + DeleteBinding(port2, slot2, dev, b); + i--; + } + } + } + } + } + + return count; +} + +// Does nothing, but makes sure I'm overriding the dialog's window proc, to block +// default key handling. +ExtraWndProcResult DoNothingWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *output) { + return CONTINUE_BLISSFULLY; +} + +void EndBinding(HWND hWnd) { + if (selected) { + KillTimer(hWnd, 1); + + int needRefreshDevices = 0; + // If binding ignore keyboard, this will disable it and enable other devices. + // 0xFF is used for testing force feedback. I disable all other devices first + // just so I don't needlessly steal the mouse. + if (selected == 0x7F || selected == 0xFF) { + needRefreshDevices = 1; + } + selected = 0; + + dm->ReleaseInput(); + ClearKeyQueue(); + hWndButtonProc.Release(); + + // Safest to do this last. + if (needRefreshDevices) { + RefreshEnabledDevices(); + } + } +} + + +INT_PTR CALLBACK DialogProc(HWND hWnd, unsigned int msg, WPARAM wParam, LPARAM lParam) { + int index = (hWnd == PropSheet_IndexToHwnd(hWndProp, 1)); + int slot; + int port = GetPort(hWnd, &slot); + HWND hWndList = GetDlgItem(hWnd, IDC_LIST); + switch (msg) { + case WM_INITDIALOG: + { + ListView_SetExtendedListViewStyleEx(hWndList, LVS_EX_DOUBLEBUFFER, LVS_EX_DOUBLEBUFFER); + LVCOLUMN c; + c.mask = LVCF_TEXT | LVCF_WIDTH; + c.cx = 101; + c.pszText = L"Device"; + ListView_InsertColumn(hWndList, 0, &c); + c.cx = 70; + c.pszText = L"PC Control"; + ListView_InsertColumn(hWndList, 1, &c); + c.cx = 84; + c.pszText = L"PS2 Control"; + ListView_InsertColumn(hWndList, 2, &c); + selected = 0; + port = (int)((PROPSHEETPAGE *)lParam)->lParam & 1; + slot = (int)((PROPSHEETPAGE *)lParam)->lParam >> 1; + hWnds[port][slot] = hWnd; + SendMessage(hWndList, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, LVS_EX_FULLROWSELECT); + SetupLogSlider(GetDlgItem(hWnd, IDC_SLIDER1)); + SetupLogSlider(GetDlgItem(hWnd, IDC_SLIDER_DEADZONE)); + if (port || slot) + EnableWindow(GetDlgItem(hWnd, ID_IGNORE), 0); + + Populate(port, slot); + } + break; + case WM_DEVICECHANGE: + if (wParam == DBT_DEVNODES_CHANGED) { + EndBinding(hWnd); + RefreshEnabledDevicesAndDisplay(1, hWndGeneral, 1); + } + break; + case WM_TIMER: + // ignore generic timer callback and handle the hwnd-specific one which comes later + if(hWnd == 0) return 0; + + if (!selected || selected == 0xFF) { + // !selected is mostly for device added/removed when binding. + selected = 0xFF; + EndBinding(hWnd); + } + else { + unsigned int uid; + int value; + + // The old code re-bound our button hWndProcEater to GetDlgItem(hWnd, selected). But at best hWnd + // was null (WM_TIMER is passed twice, once with a null parameter, and a second time that is hWnd + // specific), and at worst 'selected' is a post-processed code "based" on cmd, so GetDlgItem + // *always* returned NULL anyway. This resulted in hWndButton being null, which meant Device code + // used hWnd instead. This may have caused odd behavior since the callbacks were still all eaten + // by the initial GetDlgItem(hWnd, cmd) selection made when the timer was initialized. + + InitInfo info = {selected==0x7F, 1, hWndProp, &hWndButtonProc}; + Device *dev = dm->GetActiveDevice(&info, &uid, &index, &value); + if (dev) { + int command = selected; + // Good idea to do this first, as BindCommand modifies the ListView, which will + // call it anyways, which is a bit funky. + EndBinding(hWnd); + UnselectAll(hWndList); + int index = -1; + if (command == 0x7F && dev->api == IGNORE_KEYBOARD) { + index = BindCommand(dev, uid, 0, 0, command, BASE_SENSITIVITY, 0, 0); + } + else if (command < 0x30) { + index = BindCommand(dev, uid, port, slot, command, BASE_SENSITIVITY, 0, 0); + } + if (index >= 0) { + PropSheet_Changed(hWndProp, hWnds[port][slot]); + ListView_SetItemState(hWndList, index, LVIS_SELECTED, LVIS_SELECTED); + ListView_EnsureVisible(hWndList, index, 0); + } + } + } + break; + case WM_SYSKEYDOWN: + case WM_KEYDOWN: + EndBinding(hWnd); + break; + case PSM_CANCELTOCLOSE: + // Load(); + break; + case WM_ACTIVATE: + EndBinding(hWnd); + break; + case WM_NOTIFY: + { + PSHNOTIFY* n = (PSHNOTIFY*) lParam; + if (n->hdr.hwndFrom == hWndProp) { + switch(n->hdr.code) { + case PSN_QUERYCANCEL: + case PSN_KILLACTIVE: + EndBinding(hWnd); + return 0; + case PSN_SETACTIVE: + return 0; + case PSN_APPLY: + SetWindowLong(hWnd, DWL_MSGRESULT, PSNRET_NOERROR); + return 1; + } + break; + } + else if (n->hdr.idFrom == IDC_LIST) { + static int NeedUpdate = 0; + if (n->hdr.code == LVN_KEYDOWN) { + NMLVKEYDOWN *key = (NMLVKEYDOWN *) n; + if (key->wVKey == VK_DELETE || + key->wVKey == VK_BACK) { + + if (DeleteSelected(port, slot)) + PropSheet_Changed(hWndProp, hWnds[0]); + } + } + // Update sensitivity and motor/binding display on redraw + // rather than on itemchanged. This reduces blinking, as + // I get 3 LVN_ITEMCHANGED messages, and first is sent before + // the new item is set as being selected. + else if (n->hdr.code == LVN_ITEMCHANGED) { + NeedUpdate = 1; + } + else if (n->hdr.code == NM_CUSTOMDRAW && NeedUpdate) { + NeedUpdate = 0; + SelChanged(port, slot); + } + } + } + break; + case WM_HSCROLL: + { + int id = GetDlgCtrlID((HWND)lParam); + int val = GetLogSliderVal(hWnd, id); + if (id == IDC_SLIDER1) { + ChangeValue(port, slot, &val, 0, 0); + } + else if (id == IDC_SLIDER_DEADZONE) { + ChangeValue(port, slot, 0, 0, &val); + } + else { + ChangeEffect(port, slot, id, &val, 0); + } + } + break; + case WM_COMMAND: + if (HIWORD(wParam)==CBN_SELCHANGE && LOWORD(wParam) == IDC_AXIS_DIRECTION) { + int index = ListView_GetNextItem(hWndList, -1, LVNI_SELECTED); + if (index >= 0) { + int cbsel = SendMessage((HWND)lParam, CB_GETCURSEL, 0, 0); + if (cbsel >= 0) { + ForceFeedbackBinding *ffb; + Binding *b; + Device *dev; + if (GetBinding(port, slot, index, dev, b, ffb)) { + const static unsigned int axisUIDs[3] = {UID_AXIS_NEG, UID_AXIS_POS, UID_AXIS}; + int uid = dev->virtualControls[b->controlIndex].uid; + uid = (uid&0x00FFFFFF) | axisUIDs[cbsel]; + Binding backup = *b; + DeleteSelected(port, slot); + int index = BindCommand(dev, uid, port, slot, backup.command, backup.sensitivity, backup.turbo, backup.deadZone); + ListView_SetItemState(hWndList, index, LVIS_SELECTED, LVIS_SELECTED); + PropSheet_Changed(hWndProp, hWnd); + } + } + } + } + else if (HIWORD(wParam)==CBN_SELCHANGE && LOWORD(wParam) == IDC_FF_EFFECT) { + int typeIndex = SendMessage((HWND)lParam, CB_GETCURSEL, 0, 0); + if (typeIndex >= 0) + ChangeEffect(port, slot, 0, 0, (unsigned int*)&typeIndex); + } + else if (HIWORD(wParam)==BN_CLICKED) { + EndBinding(hWnd); + int cmd = LOWORD(wParam); + if (cmd == ID_DELETE) { + if (DeleteSelected(port, slot)) + PropSheet_Changed(hWndProp, hWnd); + } + else if (cmd == ID_CLEAR) { + while (DeleteByIndex(port, slot, 0)) PropSheet_Changed(hWndProp, hWnd); + } + else if (cmd == ID_BIG_MOTOR || cmd == ID_SMALL_MOTOR) { + int i = (int)SendMessage(GetDlgItem(hWnd, IDC_FORCEFEEDBACK), CB_GETCURSEL, 0, 0); + if (i >= 0) { + unsigned int index = (unsigned int)SendMessage(GetDlgItem(hWnd, IDC_FORCEFEEDBACK), CB_GETITEMDATA, i, 0); + if (index < (unsigned int) dm->numDevices) { + Device *dev = dm->devices[index]; + ForceFeedbackBinding *b; + wchar_t *effectID = 0; + if (dev->api == DI) { + // Constant effect. + if (cmd == ID_BIG_MOTOR) effectID = L"13541C20-8E33-11D0-9AD0-00A0C9A06E35"; + // Square. + else effectID = L"13541C22-8E33-11D0-9AD0-00A0C9A06E35"; + } + int count = CreateEffectBinding(dev, effectID, port, slot, cmd-ID_BIG_MOTOR, &b); + if (b) { + int needSet = 1; + if (dev->api == XINPUT && dev->numFFAxes == 2) { + needSet = 0; + if (cmd == ID_BIG_MOTOR) { + b->axes[0].force = BASE_SENSITIVITY; + } + else { + b->axes[1].force = BASE_SENSITIVITY; + } + } + else if (dev->api == DS3 && dev->numFFAxes == 2) { + needSet = 0; + if (cmd == ID_BIG_MOTOR) { + b->axes[0].force = BASE_SENSITIVITY; + } + else { + b->axes[1].force = BASE_SENSITIVITY; + } + } + else if (dev->api == DI) { + int bigIndex=0, littleIndex=0; + int j; + for (j=0; jnumFFAxes; j++) { + // DI object instance. 0 is x-axis, 1 is y-axis. + int instance = (dev->ffAxes[j].id>>8)&0xFFFF; + if (instance == 0) { + bigIndex = j; + } + else if (instance == 1) { + littleIndex = j; + } + } + needSet = 0; + if (cmd == ID_BIG_MOTOR) { + b->axes[bigIndex].force = BASE_SENSITIVITY; + b->axes[littleIndex].force = 1; + } + else { + b->axes[bigIndex].force = 1; + b->axes[littleIndex].force = BASE_SENSITIVITY; + } + } + if (needSet) { + for (int j=0; j<2 && j numFFAxes; j++) { + b->axes[j].force = BASE_SENSITIVITY; + } + } + UnselectAll(hWndList); + ListView_SetItemState(hWndList, count, LVIS_SELECTED, LVIS_SELECTED); + ListView_EnsureVisible(hWndList, count, 0); + } + PropSheet_Changed(hWndProp, hWnd); + } + } + } + else if (cmd == ID_CONTROLS) { + UnselectAll(hWndList); + } + else if (cmd == ID_TEST) { + // Just in case... + if (selected) break; + Device *dev; + Binding *b; + ForceFeedbackBinding *ffb = 0; + int selIndex = ListView_GetNextItem(hWndList, -1, LVNI_SELECTED); + if (selIndex >= 0) { + if (GetBinding(port, slot, selIndex, dev, b, ffb)) { + selected = 0xFF; + hWndButtonProc.SetWndHandle(GetDlgItem(hWnd, cmd)); + InitInfo info = {0, 1, hWndProp, &hWndButtonProc}; + for (int i=0; inumDevices; i++) { + if (dm->devices[i] != dev) { + dm->DisableDevice(i); + } + } + dm->Update(&info); + dm->PostRead(); + dev->SetEffect(ffb, 255); + Sleep(200); + dm->Update(&info); + SetTimer(hWnd, 1, 3000, 0); + } + } + } + else if ((cmd >= ID_LOCK_BUTTONS && cmd <= ID_ANALOG) || cmd == ID_IGNORE) {// || cmd == ID_FORCE_FEEDBACK) { + // Messes up things, unfortunately. + // End binding on a bunch of notification messages, and + // this will send a bunch. + // UnselectAll(hWndList); + EndBinding(hWnd); + if (cmd != ID_IGNORE) { + selected = cmd-(ID_SELECT-0x10); + } + else { + selected = 0x7F; + for (int i=0; inumDevices; i++) { + if (dm->devices[i]->api != IGNORE_KEYBOARD) { + dm->DisableDevice(i); + } + else { + dm->EnableDevice(i); + } + } + } + + hWndButtonProc.SetWndHandle(GetDlgItem(hWnd, cmd)); + hWndButtonProc.Eat(DoNothingWndProc, 0); + InitInfo info = {selected==0x7F, 1, hWndProp, &hWndButtonProc}; + int w = timeGetTime(); + dm->Update(&info); + dm->PostRead(); + // Workaround for things that return 0 on first poll and something else ever after. + Sleep(80); + dm->Update(&info); + dm->PostRead(); + SetTimer(hWnd, 1, 30, 0); + } + if (cmd == IDC_TURBO) { + // Don't allow setting it back to indeterminate. + SendMessage(GetDlgItem(hWnd, IDC_TURBO), BM_SETSTYLE, BS_AUTOCHECKBOX, 0); + int turbo = (IsDlgButtonChecked(hWnd, IDC_TURBO) == BST_CHECKED); + ChangeValue(port, slot, 0, &turbo, 0); + } + else if (cmd == IDC_FLIP1) { + int val = GetLogSliderVal(hWnd, IDC_SLIDER1); + ChangeValue(port, slot, &val, 0, 0); + } + else if (cmd >= IDC_FF_AXIS1_ENABLED && cmd < IDC_FF_AXIS8_ENABLED + 16) { + int index = (cmd - IDC_FF_AXIS1_ENABLED)/16; + int val = GetLogSliderVal(hWnd, 16*index + IDC_FF_AXIS1); + if (IsDlgButtonChecked(hWnd, 16*index + IDC_FF_AXIS1_ENABLED) != BST_CHECKED) { + val = 0; + } + ChangeEffect(port, slot, cmd, &val, 0); + } + } + break; + default: + break; + } + return 0; +} + +// Returns 0 if pad doesn't exist due to mtap settings, as a convenience. +int GetPadString(wchar_t *string, unsigned int port, unsigned int slot) { + if (!slot && !config.multitap[port]) { + wsprintfW(string, L"Pad %i", port+1); + } + else { + wsprintfW(string, L"Pad %i%c", port+1, 'A'+slot); + if (!config.multitap[port]) return 0; + } + return 1; +} + +void UpdatePadPages() { + HPROPSHEETPAGE pages[10]; + int count = 0; + memset(hWnds, 0, sizeof(hWnds)); + int slot = 0; + for (int port=0; port<2; port++) { + for (int slot=0; slot<4; slot++) { + if (config.padConfigs[port][slot].type == DisabledPad) continue; + wchar_t title[20]; + if (!GetPadString(title, port, slot)) continue; + + PROPSHEETPAGE psp; + ZeroMemory(&psp, sizeof(psp)); + psp.dwSize = sizeof(psp); + psp.dwFlags = PSP_USETITLE | PSP_PREMATURE; + psp.hInstance = hInst; + psp.pfnDlgProc = DialogProc; + psp.lParam = port | (slot<<1); + psp.pszTitle = title; + if (config.padConfigs[port][slot].type != GuitarPad) + psp.pszTemplate = MAKEINTRESOURCE(IDD_CONFIG); + else + psp.pszTemplate = MAKEINTRESOURCE(IDD_CONFIG_GUITAR); + pages[count] = CreatePropertySheetPage(&psp); + if (pages[count]) count++; + } + } + while (SendMessage(hWndProp, PSM_INDEXTOPAGE, 1, 0)) { + PropSheet_RemovePage(hWndProp, 1, 0); + } + for (int i=0; i= 2 + 3*(config.multitap[0]+config.multitap[1])) { + *port = 0; + *slot = 0; + return 0; + } + if (index < 1 + 3*config.multitap[0]) { + *port = 0; + *slot = index; + } + else { + *port = 1; + *slot = index-1-3*config.multitap[0]; + } + return 1; +} + +void UpdatePadList(HWND hWnd) { + static u8 recurse = 0; + if (recurse) return; + recurse = 1; + HWND hWndList = GetDlgItem(hWnd, IDC_PAD_LIST); + HWND hWndCombo = GetDlgItem(hWnd, IDC_PAD_TYPE); + HWND hWndAnalog = GetDlgItem(hWnd, IDC_ANALOG_START1); + int slot; + int port; + int index = 0; + wchar_t *padTypes[] = {L"Unplugged", L"Dualshock 2", L"Guitar"}; + for (port=0; port<2; port++) { + for (slot = 0; slot<4; slot++) { + wchar_t text[20]; + if (!GetPadString(text, port, slot)) continue; + LVITEM item; + item.iItem = index; + item.iSubItem = 0; + item.mask = LVIF_TEXT; + item.pszText = text; + if (SendMessage(hWndList, LVM_GETITEMCOUNT, 0, 0) <= index) { + ListView_InsertItem(hWndList, &item); + } + else { + ListView_SetItem(hWndList, &item); + } + + item.iSubItem = 1; + if (2 < (unsigned int)config.padConfigs[port][slot].type) config.padConfigs[port][slot].type = Dualshock2Pad; + item.pszText = padTypes[config.padConfigs[port][slot].type]; + //if (!slot && !config.padConfigs[port][slot].type) + // item.pszText = L"Unplugged (Kinda)"; + + ListView_SetItem(hWndList, &item); + + item.iSubItem = 2; + int count = 0; + for (int i = 0; inumDevices; i++) { + Device *dev = dm->devices[i]; + if (!dev->enabled) continue; + count += dev->pads[port][slot].numBindings + dev->pads[port][slot].numFFBindings; + } + wsprintf(text, L"%i", count); + item.pszText = text; + ListView_SetItem(hWndList, &item); + index++; + } + } + while (ListView_DeleteItem(hWndList, index)); + int sel = ListView_GetNextItem(hWndList, -1, LVNI_SELECTED); + + int enable; + if (!ListIndexToPortAndSlot(sel, &port, &slot)) { + enable = 0; + SendMessage(hWndCombo, CB_SETCURSEL, -1, 0); + CheckDlgButton(hWnd, IDC_ANALOG_START1, BST_UNCHECKED); + } + else { + enable = 1; + SendMessage(hWndCombo, CB_SETCURSEL, config.padConfigs[port][slot].type, 0); + CheckDlgButton(hWnd, IDC_ANALOG_START1, BST_CHECKED*config.padConfigs[port][slot].autoAnalog); + } + EnableWindow(hWndCombo, enable); + EnableWindow(hWndAnalog, enable); + //ListView_SetExtendedListViewStyleEx(hWndList, LVS_EX_DOUBLEBUFFER|LVS_EX_ONECLICKACTIVATE, LVS_EX_DOUBLEBUFFER|LVS_EX_ONECLICKACTIVATE); + recurse = 0; +} + +INT_PTR CALLBACK GeneralDialogProc(HWND hWnd, unsigned int msg, WPARAM wParam, LPARAM lParam) { + int i; + HWND hWndList = GetDlgItem(hWnd, IDC_PAD_LIST); + switch (msg) { + case WM_INITDIALOG: + { + HWND hWndCombo = GetDlgItem(hWnd, IDC_PAD_TYPE); + if (SendMessage(hWndCombo, CB_GETCOUNT, 0, 0) == 0) { + LVCOLUMN c; + c.mask = LVCF_TEXT | LVCF_WIDTH; + c.cx = 50; + c.pszText = L"Pad"; + ListView_InsertColumn(hWndList, 0, &c); + c.cx = 120; + c.pszText = L"Type"; + ListView_InsertColumn(hWndList, 1, &c); + c.cx = 70; + c.pszText = L"Bindings"; + ListView_InsertColumn(hWndList, 2, &c); + selected = 0; + ListView_SetExtendedListViewStyleEx(hWndList, LVS_EX_FULLROWSELECT|LVS_EX_DOUBLEBUFFER, LVS_EX_FULLROWSELECT|LVS_EX_DOUBLEBUFFER); + SendMessage(hWndList, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, LVS_EX_FULLROWSELECT); + SendMessage(hWndCombo, CB_ADDSTRING, 0, (LPARAM) L"Unplugged"); + SendMessage(hWndCombo, CB_ADDSTRING, 0, (LPARAM) L"Dualshock 2"); + SendMessage(hWndCombo, CB_ADDSTRING, 0, (LPARAM) L"Guitar"); + } + } + UpdatePadPages(); + hWndGeneral = hWnd; + RefreshEnabledDevicesAndDisplay(0, hWnd, 0); + UpdatePadList(hWnd); + + if (!DualShock3Possible()) { + config.gameApis.dualShock3 = 0; + EnableWindow(GetDlgItem(hWnd, IDC_G_DS3), 0); + } + + for (int j=0; j>1)); + + if (config.osVersion < 6) EnableWindow(GetDlgItem(hWnd, IDC_VISTA_VOLUME), 0); + + + if (config.keyboardApi < 0 || config.keyboardApi > 3) config.keyboardApi = NO_API; + CheckRadioButton(hWnd, IDC_KB_DISABLE, IDC_KB_RAW, IDC_KB_DISABLE + config.keyboardApi); + if (config.mouseApi < 0 || config.mouseApi > 3) config.mouseApi = NO_API; + CheckRadioButton(hWnd, IDC_M_DISABLE, IDC_M_RAW, IDC_M_DISABLE + config.mouseApi); + + if (!InitializeRawInput()) { + EnableWindow(GetDlgItem(hWnd, IDC_KB_RAW), 0); + EnableWindow(GetDlgItem(hWnd, IDC_M_RAW), 0); + } + break; + case WM_DEVICECHANGE: + if (wParam == DBT_DEVNODES_CHANGED) { + RefreshEnabledDevicesAndDisplay(1, hWndGeneral, 1); + UpdatePadList(hWnd); + } + break; + case WM_COMMAND: + if (LOWORD(wParam) == IDC_PAD_TYPE) { + if (HIWORD(wParam) == CBN_SELCHANGE) { + HWND hWndCombo = GetDlgItem(hWnd, IDC_PAD_TYPE); + int index = ListView_GetNextItem(hWndList, -1, LVNI_SELECTED); + int sel = SendMessage(hWndCombo, CB_GETCURSEL, 0, 0); + int port, slot; + if (sel < 0 || !ListIndexToPortAndSlot(index, &port, &slot)) break; + if (sel != config.padConfigs[port][slot].type) { + config.padConfigs[port][slot].type = (PadType)sel; + UpdatePadList(hWnd); + UpdatePadPages(); + RefreshEnabledDevicesAndDisplay(0, hWnd, 1); + PropSheet_Changed(hWndProp, hWnd); + } + } + } + else if (HIWORD(wParam)==BN_CLICKED && (LOWORD(wParam) == ID_LOAD || LOWORD(wParam) == ID_SAVE)) { + OPENFILENAMEW ofn; + memset (&ofn, 0, sizeof(ofn)); + ofn.lStructSize = sizeof(ofn); + ofn.hwndOwner = hWnd; + ofn.lpstrFilter = L"LilyPad Config Files\0*.lily\0All Files\0*.*\0\0"; + wchar_t file[MAX_PATH+1]; + ofn.lpstrFile = file; + ofn.nMaxFile = MAX_PATH; + wcscpy(file, config.lastSaveConfigFileName); + ofn.lpstrInitialDir = config.lastSaveConfigPath; + ofn.Flags = OFN_DONTADDTORECENT | OFN_LONGNAMES | OFN_NOCHANGEDIR; + if (LOWORD(wParam) == ID_LOAD) { + ofn.lpstrTitle = L"Load LilyPad Configuration"; + ofn.Flags |= OFN_FILEMUSTEXIST; + if (GetOpenFileNameW(&ofn)) { + LoadSettings(1, ofn.lpstrFile); + GeneralDialogProc(hWnd, WM_INITDIALOG, 0, 0); + PropSheet_Changed(hWndProp, hWnd); + } + } + else { + ofn.lpstrTitle = L"Save LilyPad Configuration"; + ofn.Flags |= OFN_OVERWRITEPROMPT; + if (GetSaveFileNameW(&ofn)) { + if (SaveSettings(ofn.lpstrFile) == -1) { + MessageBox(hWnd, L"Save fail", L"Couldn't save file.", MB_OK); + } + } + } + break; + } + + else if (HIWORD(wParam)==BN_CLICKED && LOWORD(wParam) == ID_TEST) { + Diagnostics(hWnd); + RefreshEnabledDevices(); + } + else if (HIWORD(wParam)==BN_CLICKED && LOWORD(wParam) == ID_REFRESH) { + RefreshEnabledDevicesAndDisplay(1, hWnd, 1); + UpdatePadList(hWnd); + } + else if (HIWORD(wParam)==BN_CLICKED && LOWORD(wParam) == IDC_ANALOG_START1) { + int index = ListView_GetNextItem(hWndList, -1, LVNI_SELECTED); + int port, slot; + if (!ListIndexToPortAndSlot(index, &port, &slot)) break; + config.padConfigs[port][slot].autoAnalog = (IsDlgButtonChecked(hWnd, IDC_ANALOG_START1) == BST_CHECKED); + PropSheet_Changed(hWndProp, hWnd); + } + else { + int t = IDC_CLOSE_HACK1; + int test = LOWORD(wParam); + if (test == IDC_CLOSE_HACK1) { + CheckDlgButton(hWnd, IDC_CLOSE_HACK2, BST_UNCHECKED); + } + else if (test == IDC_CLOSE_HACK2) { + CheckDlgButton(hWnd, IDC_CLOSE_HACK1, BST_UNCHECKED); + } + + int mtap = config.multitap[0] + 2*config.multitap[1]; + int vistaVol = config.vistaVolume; + + for (int j=0; jhdr.hwndFrom == hWndProp) { + switch(n->hdr.code) { + case PSN_QUERYCANCEL: + case PSN_KILLACTIVE: + EndBinding(hWnd); + return 0; + case PSN_SETACTIVE: + //selected = 0; + UpdatePadList(hWnd); + return 0; + case PSN_APPLY: + selected = 0; + if (SaveSettings()) { + SetWindowLong(hWnd, DWL_MSGRESULT, PSNRET_INVALID_NOCHANGEPAGE); + return 0; + } + SetWindowLong(hWnd, DWL_MSGRESULT, PSNRET_NOERROR); + return 1; + } + } + else if (n->hdr.idFrom == IDC_LIST && n->hdr.code == NM_DBLCLK) { + Diagnostics(hWnd); + } + else if (n->hdr.idFrom == IDC_PAD_LIST) { + if (n->hdr.code == LVN_ITEMCHANGED) { + UpdatePadList(hWnd); + } + if (n->hdr.code == NM_RCLICK) { + UpdatePadList(hWnd); + int index = ListView_GetNextItem(hWndList, -1, LVNI_SELECTED); + int port1, slot1, port2, slot2; + if (!ListIndexToPortAndSlot(index, &port1, &slot1)) break; + HMENU hMenu = CreatePopupMenu(); + if (!hMenu) break; + MENUITEMINFOW info; + for (port2=1; port2>=0; port2--) { + for (slot2 = 3; slot2>=0; slot2--) { + wchar_t text[40]; + wchar_t pad[20]; + if (!GetPadString(pad, port2, slot2)) continue; + info.cbSize = sizeof(info); + info.fMask = MIIM_STRING | MIIM_ID; + info.dwTypeData = text; + if (port2 == port1 && slot2 == slot1) { + int index = GetMenuItemCount(hMenu); + wsprintfW(text, L"Clear %s Bindings", pad); + info.wID = -1; + InsertMenuItemW(hMenu, index, 1, &info); + info.fMask = MIIM_TYPE; + info.fType = MFT_SEPARATOR; + InsertMenuItemW(hMenu, index, 1, &info); + } + else { + info.wID = port2+2*slot2+1; + wsprintfW(text, L"Swap with %s", pad); + InsertMenuItemW(hMenu, 0, 1, &info); + } + } + } + POINT pos; + GetCursorPos(&pos); + short res = TrackPopupMenuEx(hMenu, TPM_NONOTIFY|TPM_RETURNCMD, pos.x, pos.y, hWndProp, 0); + DestroyMenu(hMenu); + if (!res) break; + if (res > 0) { + res--; + slot2 = res / 2; + port2 = res&1; + PadConfig padCfgTemp = config.padConfigs[port1][slot1]; + config.padConfigs[port1][slot1] = config.padConfigs[port2][slot2]; + config.padConfigs[port2][slot2] = padCfgTemp; + for (int i=0; inumDevices; i++) { + if (dm->devices[i]->type == IGNORE) continue; + PadBindings bindings = dm->devices[i]->pads[port1][slot1]; + dm->devices[i]->pads[port1][slot1] = dm->devices[i]->pads[port2][slot2]; + dm->devices[i]->pads[port2][slot2] = bindings; + } + } + else { + for (int i=0; inumDevices; i++) { + if (dm->devices[i]->type == IGNORE) continue; + free(dm->devices[i]->pads[port1][slot1].bindings); + for (int j=0; jdevices[i]->pads[port1][slot1].numFFBindings; j++) { + free(dm->devices[i]->pads[port1][slot1].ffBindings[j].axes); + } + free(dm->devices[i]->pads[port1][slot1].ffBindings); + memset(&dm->devices[i]->pads[port1][slot1], 0, sizeof(dm->devices[i]->pads[port1][slot1])); + } + } + UpdatePadPages(); + UpdatePadList(hWnd); + PropSheet_Changed(hWndProp, hWnd); + } + } + } + break; + default: + break; + } + return 0; +} + +int CALLBACK PropSheetProc(HWND hWnd, UINT msg, LPARAM lParam) { + if (hWnd) hWndProp = hWnd; + return 0; +} + +void Configure() { + // Can end up here without PADinit() being called first. + LoadSettings(); + // Can also end up here after running emulator a bit, and possibly + // disabling some devices due to focus changes, or releasing mouse. + RefreshEnabledDevices(0); + + memset(hWnds, 0, sizeof(hWnds)); + + PROPSHEETPAGE psp; + ZeroMemory(&psp, sizeof(psp)); + psp.dwSize = sizeof(psp); + psp.dwFlags = PSP_USETITLE | PSP_PREMATURE; + psp.hInstance = hInst; + psp.pfnDlgProc = GeneralDialogProc; + psp.pszTitle = L"General"; + psp.pszTemplate = MAKEINTRESOURCE(IDD_GENERAL); + HPROPSHEETPAGE page = CreatePropertySheetPage(&psp); + if (!page) return; + + PROPSHEETHEADER psh; + ZeroMemory(&psh, sizeof(psh)); + psh.dwFlags = PSH_DEFAULT | PSH_USECALLBACK | PSH_NOCONTEXTHELP; + psh.dwSize = sizeof(PROPSHEETHEADER); + psh.pfnCallback = PropSheetProc; + psh.hwndParent = GetActiveWindow(); + psh.nPages = 1; + psh.phpage = &page; + wchar_t title[200]; + GetNameAndVersionString(title); + wcscat(title, L" Settings"); + psh.pszCaption = title; + PropertySheet(&psh); + LoadSettings(1); + memset(hWnds, 0, sizeof(hWnds)); +} + +void UnloadConfigs() { + if (dm) { + delete dm; + dm = 0; + } +} diff --git a/plugins/LilyPad/Config.h b/plugins/LilyPad/Config.h index fcfbc30104..948cce590b 100644 --- a/plugins/LilyPad/Config.h +++ b/plugins/LilyPad/Config.h @@ -1,79 +1,79 @@ -#ifndef CONFIG_H -#define CONFIG_H - -extern u8 ps2e; - -enum PadType { - DisabledPad, - Dualshock2Pad, - GuitarPad -}; - -struct PadConfig { - PadType type; - u8 autoAnalog; -}; - -struct GeneralConfig { -public: - PadConfig padConfigs[2][4]; - - u8 closeHacks; - - DeviceAPI keyboardApi; - DeviceAPI mouseApi; - - // Derived value, calculated by GetInput(). - u8 ignoreKeys; - - union { - struct { - u8 forceHide; - u8 mouseUnfocus; - u8 background; - u8 multipleBinding; - - struct { - u8 directInput; - u8 xInput; - u8 dualShock3; - } gameApis; - - u8 multitap[2]; - - u8 escapeFullscreenHack; - u8 disableScreenSaver; - u8 debug; - - u8 saveStateTitle; - u8 GH2; - - u8 vistaVolume; - }; - u8 bools[1]; - }; - - int volume; - - // Unlike the others, not a changeable value. - DWORD osVersion; - - wchar_t lastSaveConfigPath[MAX_PATH+1]; - wchar_t lastSaveConfigFileName[MAX_PATH+1]; -}; - -extern GeneralConfig config; - -void UnloadConfigs(); - -void AddIgnore(LPARAM k); - -void SetVolume(int volume); - -int LoadSettings(int force = 0, wchar_t *file = 0); - -// Refreshes the set of enabled devices. -void RefreshEnabledDevices(int updateDeviceList = 0); - -void Configure(); -#endif +#ifndef CONFIG_H +#define CONFIG_H + +extern u8 ps2e; + +enum PadType { + DisabledPad, + Dualshock2Pad, + GuitarPad +}; + +struct PadConfig { + PadType type; + u8 autoAnalog; +}; + +struct GeneralConfig { +public: + PadConfig padConfigs[2][4]; + + u8 closeHacks; + + DeviceAPI keyboardApi; + DeviceAPI mouseApi; + + // Derived value, calculated by GetInput(). + u8 ignoreKeys; + + union { + struct { + u8 forceHide; + u8 mouseUnfocus; + u8 background; + u8 multipleBinding; + + struct { + u8 directInput; + u8 xInput; + u8 dualShock3; + } gameApis; + + u8 multitap[2]; + + u8 escapeFullscreenHack; + u8 disableScreenSaver; + u8 debug; + + u8 saveStateTitle; + u8 GH2; + + u8 vistaVolume; + }; + u8 bools[1]; + }; + + int volume; + + // Unlike the others, not a changeable value. + DWORD osVersion; + + wchar_t lastSaveConfigPath[MAX_PATH+1]; + wchar_t lastSaveConfigFileName[MAX_PATH+1]; +}; + +extern GeneralConfig config; + +void UnloadConfigs(); + +void AddIgnore(LPARAM k); + +void SetVolume(int volume); + +int LoadSettings(int force = 0, wchar_t *file = 0); + +// Refreshes the set of enabled devices. +void RefreshEnabledDevices(int updateDeviceList = 0); + +void Configure(); +#endif diff --git a/plugins/LilyPad/DeviceEnumerator.cpp b/plugins/LilyPad/DeviceEnumerator.cpp index 0565cd64cd..d1ecdb890c 100644 --- a/plugins/LilyPad/DeviceEnumerator.cpp +++ b/plugins/LilyPad/DeviceEnumerator.cpp @@ -1,29 +1,29 @@ -#include "Global.h" -#include "InputManager.h" - -#include "DeviceEnumerator.h" -#include "WindowsMessaging.h" -#include "DirectInput.h" -#include "KeyboardHook.h" -#include "RawInput.h" -#include "XInput.h" -#include "HidDevice.h" -#include "DualShock3.h" - -void EnumDevices(int hideDXXinput) { - // Needed for enumeration of some device types. - dm->ReleaseInput(); - InputDeviceManager *oldDm = dm; - dm = new InputDeviceManager(); - - EnumHookDevices(); - EnumWindowsMessagingDevices(); - EnumRawInputDevices(); - EnumDualShock3s(); - EnumXInputDevices(); - EnumDirectInputDevices(hideDXXinput); - - dm->CopyBindings(oldDm->numDevices, oldDm->devices); - - delete oldDm; -} +#include "Global.h" +#include "InputManager.h" + +#include "DeviceEnumerator.h" +#include "WindowsMessaging.h" +#include "DirectInput.h" +#include "KeyboardHook.h" +#include "RawInput.h" +#include "XInput.h" +#include "HidDevice.h" +#include "DualShock3.h" + +void EnumDevices(int hideDXXinput) { + // Needed for enumeration of some device types. + dm->ReleaseInput(); + InputDeviceManager *oldDm = dm; + dm = new InputDeviceManager(); + + EnumHookDevices(); + EnumWindowsMessagingDevices(); + EnumRawInputDevices(); + EnumDualShock3s(); + EnumXInputDevices(); + EnumDirectInputDevices(hideDXXinput); + + dm->CopyBindings(oldDm->numDevices, oldDm->devices); + + delete oldDm; +} diff --git a/plugins/LilyPad/DeviceEnumerator.h b/plugins/LilyPad/DeviceEnumerator.h index b24c8701b9..5dea33eed7 100644 --- a/plugins/LilyPad/DeviceEnumerator.h +++ b/plugins/LilyPad/DeviceEnumerator.h @@ -1,2 +1,2 @@ -void EnumDevices(int hideDXXinput); - +void EnumDevices(int hideDXXinput); + diff --git a/plugins/LilyPad/Diagnostics.cpp b/plugins/LilyPad/Diagnostics.cpp index 2d1fe3a73c..df2adb4fc1 100644 --- a/plugins/LilyPad/Diagnostics.cpp +++ b/plugins/LilyPad/Diagnostics.cpp @@ -1,128 +1,128 @@ -#include "Global.h" -#include "DeviceEnumerator.h" -#include "KeyboardQueue.h" - -#include "resource.h" -#include "InputManager.h" -#include "WndProcEater.h" - -Device *dev; - -INT_PTR CALLBACK DiagDialog(HWND hWnd, unsigned int uMsg, WPARAM wParam, LPARAM lParam) { - int i; - HWND hWndList = GetDlgItem(hWnd, IDC_LIST); - static int fullRefresh; - if (dev) { - switch (uMsg) { - case WM_INITDIALOG: - { - fullRefresh = 1; - SetWindowText(hWnd, dev->displayName); - LVCOLUMNW c; - c.mask = LVCF_TEXT | LVCF_WIDTH; - c.cx = 151; - c.pszText = L"Control"; - ListView_InsertColumn(hWndList, 0, &c); - c.cx = 90; - c.pszText = L"Value"; - ListView_InsertColumn(hWndList, 1, &c); - ListView_DeleteAllItems(hWndList); - LVITEM item; - item.mask = LVIF_TEXT; - item.iSubItem = 0; - for (i=0; inumVirtualControls; i++) { - item.pszText = dev->GetVirtualControlName(dev->virtualControls+i); - item.iItem = i; - ListView_InsertItem(hWndList, &item); - } - SetTimer(hWnd, 1, 200, 0); - } - //break; - case WM_TIMER: - { - hWndButtonProc.SetWndHandle(hWndList); - InitInfo info = {0, 1, hWnd, &hWndButtonProc}; - dm->Update(&info); - LVITEMW item; - item.mask = LVIF_TEXT; - item.iSubItem = 1; - //ShowWindow(hWndList, 0); - //LockWindowUpdate(hWndList); - if (!dev->active) { - item.pszText = L"?"; - for (i=0; inumVirtualControls; i++) { - item.iItem = i; - ListView_SetItem(hWndList, &item); - } - fullRefresh = 1; - } - else { - for (i=0; inumVirtualControls; i++) { - if (fullRefresh || dev->virtualControlState[i] != dev->oldVirtualControlState[i]) { - - VirtualControl *c = dev->virtualControls + i; - wchar_t temp[50]; - int val = dev->virtualControlState[i]; - if (c->uid & (UID_POV)) { - wsprintfW(temp, L"%i", val); - } - else { - wchar_t *sign = L""; - if (val < 0) { - sign = L"-"; - val = -val; - } - if ((c->uid& UID_AXIS) && val) { - val = val; - } - val = (int)floor(0.5 + val * 1000.0 / (double)FULLY_DOWN); - wsprintfW(temp, L"%s%i.%03i", sign, val/1000, val%1000); - } - item.pszText = temp; - item.iItem = i; - ListView_SetItem(hWndList, &item); - } - } - dm->PostRead(); - fullRefresh = 0; - } - //LockWindowUpdate(0); - //ShowWindow(hWndList, 1); - //UpdateWindow(hWnd); - } - break; - case WM_NOTIFY: - { - NMLVKEYDOWN *n = (NMLVKEYDOWN *) lParam; - // Don't always get the notification when testing DirectInput non-keyboard devices. - // Don't get it (Or want it) when testing keyboards. - if (n->hdr.idFrom != IDC_LIST || n->hdr.code != LVN_KEYDOWN || n->wVKey != VK_ESCAPE) break; - } - case WM_ACTIVATE: - if (uMsg == WM_ACTIVATE && wParam != WA_INACTIVE) break; - break; - case WM_CLOSE: - KillTimer(hWnd, 1); - dm->ReleaseInput(); - // Prevents reaching this branch again. - dev = 0; - EndDialog(hWnd, 1); - break; - default: - break; - } - } - return 0; -} - -void Diagnose(int id, HWND hWnd) { - // init = 0; - dev = dm->devices[id]; - for (int i=0; inumDevices; i++) { - if (i != id) dm->DisableDevice(i); - // Shouldn't be needed. - else dm->EnableDevice(i); - } - DialogBox(hInst, MAKEINTRESOURCE(IDD_DIAG), hWnd, DiagDialog); - ClearKeyQueue(); -} +#include "Global.h" +#include "DeviceEnumerator.h" +#include "KeyboardQueue.h" + +#include "resource.h" +#include "InputManager.h" +#include "WndProcEater.h" + +Device *dev; + +INT_PTR CALLBACK DiagDialog(HWND hWnd, unsigned int uMsg, WPARAM wParam, LPARAM lParam) { + int i; + HWND hWndList = GetDlgItem(hWnd, IDC_LIST); + static int fullRefresh; + if (dev) { + switch (uMsg) { + case WM_INITDIALOG: + { + fullRefresh = 1; + SetWindowText(hWnd, dev->displayName); + LVCOLUMNW c; + c.mask = LVCF_TEXT | LVCF_WIDTH; + c.cx = 151; + c.pszText = L"Control"; + ListView_InsertColumn(hWndList, 0, &c); + c.cx = 90; + c.pszText = L"Value"; + ListView_InsertColumn(hWndList, 1, &c); + ListView_DeleteAllItems(hWndList); + LVITEM item; + item.mask = LVIF_TEXT; + item.iSubItem = 0; + for (i=0; inumVirtualControls; i++) { + item.pszText = dev->GetVirtualControlName(dev->virtualControls+i); + item.iItem = i; + ListView_InsertItem(hWndList, &item); + } + SetTimer(hWnd, 1, 200, 0); + } + //break; + case WM_TIMER: + { + hWndButtonProc.SetWndHandle(hWndList); + InitInfo info = {0, 1, hWnd, &hWndButtonProc}; + dm->Update(&info); + LVITEMW item; + item.mask = LVIF_TEXT; + item.iSubItem = 1; + //ShowWindow(hWndList, 0); + //LockWindowUpdate(hWndList); + if (!dev->active) { + item.pszText = L"?"; + for (i=0; inumVirtualControls; i++) { + item.iItem = i; + ListView_SetItem(hWndList, &item); + } + fullRefresh = 1; + } + else { + for (i=0; inumVirtualControls; i++) { + if (fullRefresh || dev->virtualControlState[i] != dev->oldVirtualControlState[i]) { + + VirtualControl *c = dev->virtualControls + i; + wchar_t temp[50]; + int val = dev->virtualControlState[i]; + if (c->uid & (UID_POV)) { + wsprintfW(temp, L"%i", val); + } + else { + wchar_t *sign = L""; + if (val < 0) { + sign = L"-"; + val = -val; + } + if ((c->uid& UID_AXIS) && val) { + val = val; + } + val = (int)floor(0.5 + val * 1000.0 / (double)FULLY_DOWN); + wsprintfW(temp, L"%s%i.%03i", sign, val/1000, val%1000); + } + item.pszText = temp; + item.iItem = i; + ListView_SetItem(hWndList, &item); + } + } + dm->PostRead(); + fullRefresh = 0; + } + //LockWindowUpdate(0); + //ShowWindow(hWndList, 1); + //UpdateWindow(hWnd); + } + break; + case WM_NOTIFY: + { + NMLVKEYDOWN *n = (NMLVKEYDOWN *) lParam; + // Don't always get the notification when testing DirectInput non-keyboard devices. + // Don't get it (Or want it) when testing keyboards. + if (n->hdr.idFrom != IDC_LIST || n->hdr.code != LVN_KEYDOWN || n->wVKey != VK_ESCAPE) break; + } + case WM_ACTIVATE: + if (uMsg == WM_ACTIVATE && wParam != WA_INACTIVE) break; + break; + case WM_CLOSE: + KillTimer(hWnd, 1); + dm->ReleaseInput(); + // Prevents reaching this branch again. + dev = 0; + EndDialog(hWnd, 1); + break; + default: + break; + } + } + return 0; +} + +void Diagnose(int id, HWND hWnd) { + // init = 0; + dev = dm->devices[id]; + for (int i=0; inumDevices; i++) { + if (i != id) dm->DisableDevice(i); + // Shouldn't be needed. + else dm->EnableDevice(i); + } + DialogBox(hInst, MAKEINTRESOURCE(IDD_DIAG), hWnd, DiagDialog); + ClearKeyQueue(); +} diff --git a/plugins/LilyPad/Diagnostics.h b/plugins/LilyPad/Diagnostics.h index 2007d9666b..4966b63a88 100644 --- a/plugins/LilyPad/Diagnostics.h +++ b/plugins/LilyPad/Diagnostics.h @@ -1,6 +1,6 @@ -#ifndef DIAGNOSTICS_H -#define DIAGNOSTICS_H - -void Diagnose(int id, HWND hWnd); - -#endif +#ifndef DIAGNOSTICS_H +#define DIAGNOSTICS_H + +void Diagnose(int id, HWND hWnd); + +#endif diff --git a/plugins/LilyPad/DirectInput.cpp b/plugins/LilyPad/DirectInput.cpp index 1efbb5634c..a978577b56 100644 --- a/plugins/LilyPad/DirectInput.cpp +++ b/plugins/LilyPad/DirectInput.cpp @@ -1,622 +1,622 @@ -#include "Global.h" -#include -#include "InputManager.h" - -#include "VKey.h" -#include "DirectInput.h" -#include "DeviceEnumerator.h" -#include "PS2Etypes.h" - -// All for getting GUIDs of XInput devices.... -#include -#include -// MS's code imports wmsstd.h, thus requiring the entire windows -// media SDK also be installed for a simple macro. This is -// simpler and less silly. -#ifndef SAFE_RELEASE -#define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } } -#endif - -// Aka htons, without the winsock dependency. -inline static u16 flipShort(u16 s) { - return (s>>8) | (s<<8); -} - -// Aka htonl, without the winsock dependency. -inline static u32 flipLong(u32 l) { - return (((u32)flipShort((u16)l))<<16) | flipShort((u16)(l>>16)); -} - -static void GUIDtoString(wchar_t *data, const GUID *pg) { - wsprintfW(data, L"%08X-%04X-%04X-%04X-%04X%08X", - pg->Data1, (u32)pg->Data2, (u32)pg->Data3, - flipShort(((u16*)pg->Data4)[0]), - flipShort(((u16*)pg->Data4)[1]), - flipLong(((u32*)pg->Data4)[1])); -} - -struct DirectInput8Data { - IDirectInput8* lpDI8; - int refCount; - int deviceCount; -}; - -DirectInput8Data di8d = {0,0,0}; - -IDirectInput8* GetDirectInput() { - if (!di8d.lpDI8) { - if (FAILED(DirectInput8Create(hInst, 0x800, IID_IDirectInput8, (void**) &di8d.lpDI8, 0))) return 0; - } - di8d.refCount++; - return di8d.lpDI8; -} -void ReleaseDirectInput() { - if (di8d.refCount) { - di8d.refCount--; - if (!di8d.refCount) { - di8d.lpDI8->Release(); - di8d.lpDI8 = 0; - } - } -} - -static int StringToGUID(GUID *pg, wchar_t *dataw) { - char data[100]; - if (wcslen(dataw) > 50) return 0; - int w = 0; - while (dataw[w]) { - data[w] = (char) dataw[w]; - w++; - } - data[w] = 0; - u32 temp[5]; - sscanf(data, "%08X-%04X-%04X-%04X-%04X%08X", - &pg->Data1, temp, temp+1, - temp+2, temp+3, temp+4); - pg->Data2 = (u16) temp[0]; - pg->Data3 = (u16) temp[1]; - ((u16*)pg->Data4)[0] = flipShort((u16)temp[2]); - ((u16*)pg->Data4)[1] = flipShort((u16)temp[3]); - ((u32*)pg->Data4)[1] = flipLong(temp[4]); - return 1; -} - - -struct DI8Effect { - IDirectInputEffect *die; - int scale; -}; - -BOOL CALLBACK EnumDeviceObjectsCallback (LPCDIDEVICEOBJECTINSTANCE lpddoi, LPVOID pvRef); -BOOL CALLBACK EnumEffectsCallback(LPCDIEFFECTINFOW pdei, LPVOID pvRef); -class DirectInputDevice : public Device { -public: - DI8Effect *diEffects; - - IDirectInputDevice8 *did; - GUID guidInstance; - DirectInputDevice(DeviceType type, IDirectInputDevice8* did, wchar_t *displayName, wchar_t *instanceID, wchar_t *productID, GUID guid) : Device(DI, type, displayName, instanceID, productID) { - diEffects = 0; - guidInstance = guid; - this->did = 0; - did->EnumEffects(EnumEffectsCallback, this, DIEFT_ALL); - did->EnumObjects(EnumDeviceObjectsCallback, this, DIDFT_ALL); - did->Release(); - } - - void SetEffect(ForceFeedbackBinding *binding, unsigned char force) { - int index = 0; - if (!diEffects) { - return; - } - for (int port=0; port<2; port++) { - for (int slot=0; slot<4; slot++) { - unsigned int diff = binding - pads[port][slot].ffBindings; - if (diff < (unsigned int)pads[port][slot].numFFBindings) { - index += diff; - port = 2; - break; - } - index += pads[port][slot].numFFBindings; - } - } - IDirectInputEffect *die = diEffects[index].die; - if (die) { - DIEFFECT dieffect; - memset(&dieffect, 0, sizeof(dieffect)); - union { - DIPERIODIC periodic; - DIRAMPFORCE ramp; - DICONSTANTFORCE constant; - }; - - dieffect.dwSize = sizeof(dieffect); - dieffect.lpvTypeSpecificParams = &periodic; - int magnitude = abs((int)((force*10000*(__int64)diEffects[index].scale)/BASE_SENSITIVITY/255)); - if (magnitude > 10000) magnitude = 10000; - int type = ffEffectTypes[binding->effectIndex].type; - if (type == EFFECT_CONSTANT) { - dieffect.cbTypeSpecificParams = sizeof(DICONSTANTFORCE); - constant.lMagnitude = magnitude; - } - else if (type == EFFECT_PERIODIC) { - dieffect.cbTypeSpecificParams = sizeof(DIPERIODIC); - periodic.dwMagnitude = 0; - periodic.lOffset = magnitude; - periodic.dwPhase = 0; - periodic.dwPeriod = 2000000; - } - else if (type == EFFECT_RAMP) { - dieffect.cbTypeSpecificParams = sizeof(DIRAMPFORCE); - ramp.lEnd = ramp.lStart = magnitude; - } - dieffect.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTIDS; - dieffect.dwDuration = 2000000; - die->SetParameters(&dieffect, DIEP_TYPESPECIFICPARAMS | DIEP_START); - } - } - - int Activate(InitInfo *initInfo) { - int i; - IDirectInput8 *di8 = GetDirectInput(); - Deactivate(); - if (!di8) return 0; - if (DI_OK != di8->CreateDevice(guidInstance, &did, 0)) { - ReleaseDirectInput(); - did = 0; - return 0; - } - - { - DIOBJECTDATAFORMAT *formats = (DIOBJECTDATAFORMAT*)calloc(numPhysicalControls, sizeof(DIOBJECTDATAFORMAT)); - for (i=0; iSetDataFormat(&format); - for (i=0; iSetProperty(DIPROP_RANGE, &prop.diph); - - // May do something like this again, if there's any need. - /* - if (FAILED(DI->did->SetProperty(DIPROP_RANGE, &prop.diph))) { - if (FAILED(DI->did->GetProperty(DIPROP_RANGE, &prop.diph))) { - // ???? - DI->objects[i].min = prop.lMin; - DI->objects[i].max = prop.lMax; - continue; - } - } - DI->objects[i].min = prop.lMin; - DI->objects[i].max = prop.lMax; - //*/ - } - } - free(formats); - } - // Note: Have to use hWndTop to properly hide cursor for mouse device. - if (type == OTHER) { - did->SetCooperativeLevel(initInfo->hWndTop, DISCL_BACKGROUND | DISCL_EXCLUSIVE); - } - else if (type == KEYBOARD) { - did->SetCooperativeLevel(initInfo->hWndTop, DISCL_FOREGROUND); - } - else { - did->SetCooperativeLevel(initInfo->hWndTop, DISCL_FOREGROUND | DISCL_EXCLUSIVE); - } - if (did->Acquire() != DI_OK) { - did->Release(); - did = 0; - ReleaseDirectInput(); - return 0; - } - AllocState(); - int count = GetFFBindingCount(); - diEffects = (DI8Effect*) calloc(count, sizeof(DI8Effect)); - i = 0; - for (int port=0; port<2; port++) { - for (int slot=0; slot<4; slot++) { - int subIndex = i; - for (int j=0; jeffectIndex; - GUID guid; - if (!StringToGUID(&guid, eff->effectID)) continue; - - DIEFFECT dieffect; - memset(&dieffect, 0, sizeof(dieffect)); - dieffect.dwSize = sizeof(dieffect); - dieffect.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTIDS; - dieffect.dwDuration = 1000000; - dieffect.dwGain = 10000; - dieffect.dwTriggerButton = DIEB_NOTRIGGER; - union { - DIPERIODIC pediodic; - DIRAMPFORCE ramp; - DICONSTANTFORCE constant; - } stuff = {0,0,0,0}; - - if (eff->type == EFFECT_CONSTANT) { - dieffect.cbTypeSpecificParams = sizeof(DICONSTANTFORCE); - } - else if (eff->type == EFFECT_PERIODIC) { - dieffect.cbTypeSpecificParams = sizeof(DIPERIODIC); - } - else if (eff->type == EFFECT_RAMP) { - dieffect.cbTypeSpecificParams = sizeof(DIRAMPFORCE); - } - dieffect.lpvTypeSpecificParams = &stuff; - - int maxForce = 0; - int numAxes = 0; - int *axes = (int*) malloc(sizeof(int) * 3 * numFFAxes); - DWORD *axisIDs = (DWORD*)(axes + numFFAxes); - LONG *dirList = (LONG*)(axisIDs + numFFAxes); - dieffect.rgdwAxes = axisIDs; - dieffect.rglDirection = dirList; - for (int k=0; kaxes[k].force) { - int force = abs(b->axes[k].force); - if (force > maxForce) { - maxForce = force; - } - axes[numAxes] = k; - axisIDs[numAxes] = ffAxes[k].id; - dirList[numAxes] = b->axes[k].force; - numAxes++; - } - } - if (!numAxes) { - free(axes); - continue; - } - dieffect.cAxes = numAxes; - diEffects[i].scale = maxForce; - if (!SUCCEEDED(did->CreateEffect(guid, &dieffect, &diEffects[i].die, 0))) { - diEffects[i].die = 0; - diEffects[i].scale = 0; - } - - free(axes); - axes = 0; - i++; - } - } - } - active = 1; - return 1; - } - - int Update() { - if (!active) return 0; - if (numPhysicalControls) { - HRESULT res = did->Poll(); - // ?? - if ((res != DI_OK && res != DI_NOEFFECT) || - DI_OK != did->GetDeviceState(4*numPhysicalControls, physicalControlState)) { - Deactivate(); - return 0; - } - for (int i=0; iStop(); - diEffects[i].die->Release(); - } - } - free(diEffects); - diEffects = 0; - } - if (active) { - did->Unacquire(); - did->Release(); - ReleaseDirectInput(); - did = 0; - active = 0; - } - } - - ~DirectInputDevice() { - } -}; - -BOOL CALLBACK EnumEffectsCallback(LPCDIEFFECTINFOW pdei, LPVOID pvRef) { - DirectInputDevice * did = (DirectInputDevice*) pvRef; - EffectType type; - int diType = DIEFT_GETTYPE(pdei->dwEffType); - if (diType == DIEFT_CONSTANTFORCE) { - type = EFFECT_CONSTANT; - } - else if (diType == DIEFT_RAMPFORCE) { - type = EFFECT_RAMP; - } - else if (diType == DIEFT_PERIODIC) { - type = EFFECT_PERIODIC; - } - else { - return DIENUM_CONTINUE; - } - wchar_t guidString[50]; - GUIDtoString(guidString, &pdei->guid); - did->AddFFEffectType(pdei->tszName, guidString, type); - - return DIENUM_CONTINUE; -} - -BOOL CALLBACK EnumDeviceObjectsCallback (LPCDIDEVICEOBJECTINSTANCE lpddoi, LPVOID pvRef) { - DirectInputDevice * did = (DirectInputDevice*) pvRef; - if (lpddoi->dwType & DIDFT_FFACTUATOR) { - did->AddFFAxis(lpddoi->tszName, lpddoi->dwType); - } - - ControlType type; - - if (lpddoi->dwType & DIDFT_POV) - type = POV; - else if (lpddoi->dwType & DIDFT_ABSAXIS) - type = ABSAXIS; - else if (lpddoi->dwType & DIDFT_RELAXIS) - type = RELAXIS; - else if (lpddoi->dwType & DIDFT_PSHBUTTON) - type = PSHBTN; - else if (lpddoi->dwType & DIDFT_TGLBUTTON) - type = TGLBTN; - else - return DIENUM_CONTINUE; - // If too many objects, ignore extra buttons. - if ((did->numPhysicalControls > 255 && DIDFT_GETINSTANCE(lpddoi->dwType) > 255) && (type & (DIDFT_PSHBUTTON | DIDFT_TGLBUTTON))) { - int i; - for (i = did->numPhysicalControls-1; i>did->numPhysicalControls-4; i--) { - if (!lpddoi->tszName[0]) break; - const wchar_t *s1 = lpddoi->tszName; - const wchar_t *s2 = did->physicalControls[i].name; - while (*s1 && *s1 == *s2) { - s1++; - s2++; - } - // If perfect match with one of last 4 names, break. - if (!*s1 && !*s2) break; - - while (s1 != lpddoi->tszName && (s1[-1] >= '0' && s1[-1] <= '9')) s1--; - int check = 0; - while (*s1 >= '0' && *s1 <= '9') { - check = check*10 + *s1 - '0'; - s1++; - } - while (*s2 >= '0' && *s2 <= '9') { - s2++; - } - // If perfect match other than final number > 30, then break. - // takes care of "button xx" case without causing issues with F keys. - if (!*s1 && !*s2 && check > 30) break; - } - if (i != did->numPhysicalControls-4) { - return DIENUM_CONTINUE; - } - } - - int vkey = 0; - if (lpddoi->tszName[0] && did->type == KEYBOARD) { - for (u32 i = 0; i<256; i++) { - wchar_t *t = GetVKStringW((u8)i); - if (!wcsicmp(lpddoi->tszName, t)) { - vkey = i; - break; - } - } - } - did->AddPhysicalControl(type, DIDFT_GETINSTANCE(lpddoi->dwType), vkey, lpddoi->tszName); - return DIENUM_CONTINUE; -} - -// Evil code from MS's site. If only they'd just made a way to get -// an XInput device's GUID directly in the first place... -BOOL IsXInputDevice( const GUID* pGuidProductFromDirectInput ) -{ - IWbemLocator* pIWbemLocator = NULL; - IEnumWbemClassObject* pEnumDevices = NULL; - IWbemClassObject* pDevices[20] = {0}; - IWbemServices* pIWbemServices = NULL; - BSTR bstrNamespace = NULL; - BSTR bstrDeviceID = NULL; - BSTR bstrClassName = NULL; - DWORD uReturned = 0; - bool bIsXinputDevice= false; - UINT iDevice = 0; - VARIANT var; - HRESULT hr; - - // CoInit if needed - hr = CoInitialize(NULL); - bool bCleanupCOM = SUCCEEDED(hr); - - // Create WMI - hr = CoCreateInstance( __uuidof(WbemLocator), - NULL, - CLSCTX_INPROC_SERVER, - __uuidof(IWbemLocator), - (LPVOID*) &pIWbemLocator); - if( FAILED(hr) || pIWbemLocator == NULL ) - goto LCleanup; - - bstrNamespace = SysAllocString( L"\\\\.\\root\\cimv2" );if( bstrNamespace == NULL ) goto LCleanup; - bstrClassName = SysAllocString( L"Win32_PNPEntity" ); if( bstrClassName == NULL ) goto LCleanup; - bstrDeviceID = SysAllocString( L"DeviceID" ); if( bstrDeviceID == NULL ) goto LCleanup; - - // Connect to WMI - hr = pIWbemLocator->ConnectServer( bstrNamespace, NULL, NULL, 0L, - 0L, NULL, NULL, &pIWbemServices ); - if( FAILED(hr) || pIWbemServices == NULL ) - goto LCleanup; - - // Switch security level to IMPERSONATE. - CoSetProxyBlanket( pIWbemServices, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, - RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE ); - - hr = pIWbemServices->CreateInstanceEnum( bstrClassName, 0, NULL, &pEnumDevices ); - if( FAILED(hr) || pEnumDevices == NULL ) - goto LCleanup; - - // Loop over all devices - for( ;; ) - { - // Get 20 at a time - hr = pEnumDevices->Next( 10000, 20, pDevices, &uReturned ); - if( FAILED(hr) ) - goto LCleanup; - if( uReturned == 0 ) - break; - - for( iDevice=0; iDeviceGet( bstrDeviceID, 0L, &var, NULL, NULL ); - if( SUCCEEDED( hr ) && var.vt == VT_BSTR && var.bstrVal != NULL ) - { - // Check if the device ID contains "IG_". If it does, then it's an XInput device - // This information can not be found from DirectInput - if( wcsstr( var.bstrVal, L"IG_" ) ) - { - // If it does, then get the VID/PID from var.bstrVal - DWORD dwPid = 0, dwVid = 0; - WCHAR* strVid = wcsstr( var.bstrVal, L"VID_" ); - if (strVid) { - dwVid = wcstoul(strVid+4, 0, 16); - } - WCHAR* strPid = wcsstr( var.bstrVal, L"PID_" ); - if (strPid) { - dwPid = wcstoul(strPid+4, 0, 16); - } - - // Compare the VID/PID to the DInput device - DWORD dwVidPid = MAKELONG( dwVid, dwPid ); - if( dwVidPid == pGuidProductFromDirectInput->Data1 ) - { - bIsXinputDevice = true; - goto LCleanup; - } - } - } - SAFE_RELEASE( pDevices[iDevice] ); - } - } - -LCleanup: - if(bstrNamespace) - SysFreeString(bstrNamespace); - if(bstrDeviceID) - SysFreeString(bstrDeviceID); - if(bstrClassName) - SysFreeString(bstrClassName); - for( iDevice=0; iDevice<20; iDevice++ ) - SAFE_RELEASE( pDevices[iDevice] ); - SAFE_RELEASE( pEnumDevices ); - SAFE_RELEASE( pIWbemLocator ); - SAFE_RELEASE( pIWbemServices ); - - if( bCleanupCOM ) - CoUninitialize(); - - return bIsXinputDevice; -} - - -struct DeviceEnumInfo { - IDirectInput8 *di8; - int ignoreXInput; -}; - -BOOL CALLBACK EnumCallback (LPCDIDEVICEINSTANCE lpddi, LPVOID pvRef) { - IDirectInput8* di8 = ((DeviceEnumInfo*)pvRef)->di8; - const wchar_t *name; - wchar_t temp[40]; - //if (((DeviceEnumInfo*)pvRef)->ignoreXInput && lpddi-> - if (lpddi->tszInstanceName[0]) { - name = lpddi->tszInstanceName; - } - else if (lpddi->tszProductName[0]) { - name = lpddi->tszProductName; - } - else { - wsprintfW (temp, L"Device %i", di8d.deviceCount); - name = temp; - } - di8d.deviceCount++; - wchar_t *fullName = (wchar_t *) malloc((wcslen(name) + 4) * sizeof(wchar_t)); - wsprintf(fullName, L"DX %s", name); - wchar_t instanceID[100]; - wchar_t productID[100]; - GUIDtoString(instanceID, &lpddi->guidInstance); - GUIDtoString(productID, &lpddi->guidProduct); - DeviceType type = OTHER; - if ((lpddi->dwDevType & 0xFF) == DI8DEVTYPE_KEYBOARD) { - type = KEYBOARD; - } - else if ((lpddi->dwDevType & 0xFF) == DI8DEVTYPE_MOUSE) { - type = MOUSE; - } - IDirectInputDevice8 *did; - if (DI_OK == di8->CreateDevice(lpddi->guidInstance, &did, 0)) { - DirectInputDevice *dev = new DirectInputDevice(type, did, fullName, instanceID, productID, lpddi->guidInstance); - if (dev->numPhysicalControls || dev->numFFAxes) { - dm->AddDevice(dev); - } - else { - delete dev; - } - } - free(fullName); - return DIENUM_CONTINUE; -} - -void EnumDirectInputDevices(int ignoreXInput) { - DeviceEnumInfo enumInfo; - enumInfo.di8 = GetDirectInput(); - if (!enumInfo.di8) return; - enumInfo.ignoreXInput = ignoreXInput; - di8d.deviceCount = 0; - enumInfo.di8->EnumDevices(DI8DEVCLASS_ALL, EnumCallback, &enumInfo, DIEDFL_ATTACHEDONLY); - ReleaseDirectInput(); -} - +#include "Global.h" +#include +#include "InputManager.h" + +#include "VKey.h" +#include "DirectInput.h" +#include "DeviceEnumerator.h" +#include "PS2Etypes.h" + +// All for getting GUIDs of XInput devices.... +#include +#include +// MS's code imports wmsstd.h, thus requiring the entire windows +// media SDK also be installed for a simple macro. This is +// simpler and less silly. +#ifndef SAFE_RELEASE +#define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } } +#endif + +// Aka htons, without the winsock dependency. +inline static u16 flipShort(u16 s) { + return (s>>8) | (s<<8); +} + +// Aka htonl, without the winsock dependency. +inline static u32 flipLong(u32 l) { + return (((u32)flipShort((u16)l))<<16) | flipShort((u16)(l>>16)); +} + +static void GUIDtoString(wchar_t *data, const GUID *pg) { + wsprintfW(data, L"%08X-%04X-%04X-%04X-%04X%08X", + pg->Data1, (u32)pg->Data2, (u32)pg->Data3, + flipShort(((u16*)pg->Data4)[0]), + flipShort(((u16*)pg->Data4)[1]), + flipLong(((u32*)pg->Data4)[1])); +} + +struct DirectInput8Data { + IDirectInput8* lpDI8; + int refCount; + int deviceCount; +}; + +DirectInput8Data di8d = {0,0,0}; + +IDirectInput8* GetDirectInput() { + if (!di8d.lpDI8) { + if (FAILED(DirectInput8Create(hInst, 0x800, IID_IDirectInput8, (void**) &di8d.lpDI8, 0))) return 0; + } + di8d.refCount++; + return di8d.lpDI8; +} +void ReleaseDirectInput() { + if (di8d.refCount) { + di8d.refCount--; + if (!di8d.refCount) { + di8d.lpDI8->Release(); + di8d.lpDI8 = 0; + } + } +} + +static int StringToGUID(GUID *pg, wchar_t *dataw) { + char data[100]; + if (wcslen(dataw) > 50) return 0; + int w = 0; + while (dataw[w]) { + data[w] = (char) dataw[w]; + w++; + } + data[w] = 0; + u32 temp[5]; + sscanf(data, "%08X-%04X-%04X-%04X-%04X%08X", + &pg->Data1, temp, temp+1, + temp+2, temp+3, temp+4); + pg->Data2 = (u16) temp[0]; + pg->Data3 = (u16) temp[1]; + ((u16*)pg->Data4)[0] = flipShort((u16)temp[2]); + ((u16*)pg->Data4)[1] = flipShort((u16)temp[3]); + ((u32*)pg->Data4)[1] = flipLong(temp[4]); + return 1; +} + + +struct DI8Effect { + IDirectInputEffect *die; + int scale; +}; + +BOOL CALLBACK EnumDeviceObjectsCallback (LPCDIDEVICEOBJECTINSTANCE lpddoi, LPVOID pvRef); +BOOL CALLBACK EnumEffectsCallback(LPCDIEFFECTINFOW pdei, LPVOID pvRef); +class DirectInputDevice : public Device { +public: + DI8Effect *diEffects; + + IDirectInputDevice8 *did; + GUID guidInstance; + DirectInputDevice(DeviceType type, IDirectInputDevice8* did, wchar_t *displayName, wchar_t *instanceID, wchar_t *productID, GUID guid) : Device(DI, type, displayName, instanceID, productID) { + diEffects = 0; + guidInstance = guid; + this->did = 0; + did->EnumEffects(EnumEffectsCallback, this, DIEFT_ALL); + did->EnumObjects(EnumDeviceObjectsCallback, this, DIDFT_ALL); + did->Release(); + } + + void SetEffect(ForceFeedbackBinding *binding, unsigned char force) { + int index = 0; + if (!diEffects) { + return; + } + for (int port=0; port<2; port++) { + for (int slot=0; slot<4; slot++) { + unsigned int diff = binding - pads[port][slot].ffBindings; + if (diff < (unsigned int)pads[port][slot].numFFBindings) { + index += diff; + port = 2; + break; + } + index += pads[port][slot].numFFBindings; + } + } + IDirectInputEffect *die = diEffects[index].die; + if (die) { + DIEFFECT dieffect; + memset(&dieffect, 0, sizeof(dieffect)); + union { + DIPERIODIC periodic; + DIRAMPFORCE ramp; + DICONSTANTFORCE constant; + }; + + dieffect.dwSize = sizeof(dieffect); + dieffect.lpvTypeSpecificParams = &periodic; + int magnitude = abs((int)((force*10000*(__int64)diEffects[index].scale)/BASE_SENSITIVITY/255)); + if (magnitude > 10000) magnitude = 10000; + int type = ffEffectTypes[binding->effectIndex].type; + if (type == EFFECT_CONSTANT) { + dieffect.cbTypeSpecificParams = sizeof(DICONSTANTFORCE); + constant.lMagnitude = magnitude; + } + else if (type == EFFECT_PERIODIC) { + dieffect.cbTypeSpecificParams = sizeof(DIPERIODIC); + periodic.dwMagnitude = 0; + periodic.lOffset = magnitude; + periodic.dwPhase = 0; + periodic.dwPeriod = 2000000; + } + else if (type == EFFECT_RAMP) { + dieffect.cbTypeSpecificParams = sizeof(DIRAMPFORCE); + ramp.lEnd = ramp.lStart = magnitude; + } + dieffect.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTIDS; + dieffect.dwDuration = 2000000; + die->SetParameters(&dieffect, DIEP_TYPESPECIFICPARAMS | DIEP_START); + } + } + + int Activate(InitInfo *initInfo) { + int i; + IDirectInput8 *di8 = GetDirectInput(); + Deactivate(); + if (!di8) return 0; + if (DI_OK != di8->CreateDevice(guidInstance, &did, 0)) { + ReleaseDirectInput(); + did = 0; + return 0; + } + + { + DIOBJECTDATAFORMAT *formats = (DIOBJECTDATAFORMAT*)calloc(numPhysicalControls, sizeof(DIOBJECTDATAFORMAT)); + for (i=0; iSetDataFormat(&format); + for (i=0; iSetProperty(DIPROP_RANGE, &prop.diph); + + // May do something like this again, if there's any need. + /* + if (FAILED(DI->did->SetProperty(DIPROP_RANGE, &prop.diph))) { + if (FAILED(DI->did->GetProperty(DIPROP_RANGE, &prop.diph))) { + // ???? + DI->objects[i].min = prop.lMin; + DI->objects[i].max = prop.lMax; + continue; + } + } + DI->objects[i].min = prop.lMin; + DI->objects[i].max = prop.lMax; + //*/ + } + } + free(formats); + } + // Note: Have to use hWndTop to properly hide cursor for mouse device. + if (type == OTHER) { + did->SetCooperativeLevel(initInfo->hWndTop, DISCL_BACKGROUND | DISCL_EXCLUSIVE); + } + else if (type == KEYBOARD) { + did->SetCooperativeLevel(initInfo->hWndTop, DISCL_FOREGROUND); + } + else { + did->SetCooperativeLevel(initInfo->hWndTop, DISCL_FOREGROUND | DISCL_EXCLUSIVE); + } + if (did->Acquire() != DI_OK) { + did->Release(); + did = 0; + ReleaseDirectInput(); + return 0; + } + AllocState(); + int count = GetFFBindingCount(); + diEffects = (DI8Effect*) calloc(count, sizeof(DI8Effect)); + i = 0; + for (int port=0; port<2; port++) { + for (int slot=0; slot<4; slot++) { + int subIndex = i; + for (int j=0; jeffectIndex; + GUID guid; + if (!StringToGUID(&guid, eff->effectID)) continue; + + DIEFFECT dieffect; + memset(&dieffect, 0, sizeof(dieffect)); + dieffect.dwSize = sizeof(dieffect); + dieffect.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTIDS; + dieffect.dwDuration = 1000000; + dieffect.dwGain = 10000; + dieffect.dwTriggerButton = DIEB_NOTRIGGER; + union { + DIPERIODIC pediodic; + DIRAMPFORCE ramp; + DICONSTANTFORCE constant; + } stuff = {0,0,0,0}; + + if (eff->type == EFFECT_CONSTANT) { + dieffect.cbTypeSpecificParams = sizeof(DICONSTANTFORCE); + } + else if (eff->type == EFFECT_PERIODIC) { + dieffect.cbTypeSpecificParams = sizeof(DIPERIODIC); + } + else if (eff->type == EFFECT_RAMP) { + dieffect.cbTypeSpecificParams = sizeof(DIRAMPFORCE); + } + dieffect.lpvTypeSpecificParams = &stuff; + + int maxForce = 0; + int numAxes = 0; + int *axes = (int*) malloc(sizeof(int) * 3 * numFFAxes); + DWORD *axisIDs = (DWORD*)(axes + numFFAxes); + LONG *dirList = (LONG*)(axisIDs + numFFAxes); + dieffect.rgdwAxes = axisIDs; + dieffect.rglDirection = dirList; + for (int k=0; kaxes[k].force) { + int force = abs(b->axes[k].force); + if (force > maxForce) { + maxForce = force; + } + axes[numAxes] = k; + axisIDs[numAxes] = ffAxes[k].id; + dirList[numAxes] = b->axes[k].force; + numAxes++; + } + } + if (!numAxes) { + free(axes); + continue; + } + dieffect.cAxes = numAxes; + diEffects[i].scale = maxForce; + if (!SUCCEEDED(did->CreateEffect(guid, &dieffect, &diEffects[i].die, 0))) { + diEffects[i].die = 0; + diEffects[i].scale = 0; + } + + free(axes); + axes = 0; + i++; + } + } + } + active = 1; + return 1; + } + + int Update() { + if (!active) return 0; + if (numPhysicalControls) { + HRESULT res = did->Poll(); + // ?? + if ((res != DI_OK && res != DI_NOEFFECT) || + DI_OK != did->GetDeviceState(4*numPhysicalControls, physicalControlState)) { + Deactivate(); + return 0; + } + for (int i=0; iStop(); + diEffects[i].die->Release(); + } + } + free(diEffects); + diEffects = 0; + } + if (active) { + did->Unacquire(); + did->Release(); + ReleaseDirectInput(); + did = 0; + active = 0; + } + } + + ~DirectInputDevice() { + } +}; + +BOOL CALLBACK EnumEffectsCallback(LPCDIEFFECTINFOW pdei, LPVOID pvRef) { + DirectInputDevice * did = (DirectInputDevice*) pvRef; + EffectType type; + int diType = DIEFT_GETTYPE(pdei->dwEffType); + if (diType == DIEFT_CONSTANTFORCE) { + type = EFFECT_CONSTANT; + } + else if (diType == DIEFT_RAMPFORCE) { + type = EFFECT_RAMP; + } + else if (diType == DIEFT_PERIODIC) { + type = EFFECT_PERIODIC; + } + else { + return DIENUM_CONTINUE; + } + wchar_t guidString[50]; + GUIDtoString(guidString, &pdei->guid); + did->AddFFEffectType(pdei->tszName, guidString, type); + + return DIENUM_CONTINUE; +} + +BOOL CALLBACK EnumDeviceObjectsCallback (LPCDIDEVICEOBJECTINSTANCE lpddoi, LPVOID pvRef) { + DirectInputDevice * did = (DirectInputDevice*) pvRef; + if (lpddoi->dwType & DIDFT_FFACTUATOR) { + did->AddFFAxis(lpddoi->tszName, lpddoi->dwType); + } + + ControlType type; + + if (lpddoi->dwType & DIDFT_POV) + type = POV; + else if (lpddoi->dwType & DIDFT_ABSAXIS) + type = ABSAXIS; + else if (lpddoi->dwType & DIDFT_RELAXIS) + type = RELAXIS; + else if (lpddoi->dwType & DIDFT_PSHBUTTON) + type = PSHBTN; + else if (lpddoi->dwType & DIDFT_TGLBUTTON) + type = TGLBTN; + else + return DIENUM_CONTINUE; + // If too many objects, ignore extra buttons. + if ((did->numPhysicalControls > 255 && DIDFT_GETINSTANCE(lpddoi->dwType) > 255) && (type & (DIDFT_PSHBUTTON | DIDFT_TGLBUTTON))) { + int i; + for (i = did->numPhysicalControls-1; i>did->numPhysicalControls-4; i--) { + if (!lpddoi->tszName[0]) break; + const wchar_t *s1 = lpddoi->tszName; + const wchar_t *s2 = did->physicalControls[i].name; + while (*s1 && *s1 == *s2) { + s1++; + s2++; + } + // If perfect match with one of last 4 names, break. + if (!*s1 && !*s2) break; + + while (s1 != lpddoi->tszName && (s1[-1] >= '0' && s1[-1] <= '9')) s1--; + int check = 0; + while (*s1 >= '0' && *s1 <= '9') { + check = check*10 + *s1 - '0'; + s1++; + } + while (*s2 >= '0' && *s2 <= '9') { + s2++; + } + // If perfect match other than final number > 30, then break. + // takes care of "button xx" case without causing issues with F keys. + if (!*s1 && !*s2 && check > 30) break; + } + if (i != did->numPhysicalControls-4) { + return DIENUM_CONTINUE; + } + } + + int vkey = 0; + if (lpddoi->tszName[0] && did->type == KEYBOARD) { + for (u32 i = 0; i<256; i++) { + wchar_t *t = GetVKStringW((u8)i); + if (!wcsicmp(lpddoi->tszName, t)) { + vkey = i; + break; + } + } + } + did->AddPhysicalControl(type, DIDFT_GETINSTANCE(lpddoi->dwType), vkey, lpddoi->tszName); + return DIENUM_CONTINUE; +} + +// Evil code from MS's site. If only they'd just made a way to get +// an XInput device's GUID directly in the first place... +BOOL IsXInputDevice( const GUID* pGuidProductFromDirectInput ) +{ + IWbemLocator* pIWbemLocator = NULL; + IEnumWbemClassObject* pEnumDevices = NULL; + IWbemClassObject* pDevices[20] = {0}; + IWbemServices* pIWbemServices = NULL; + BSTR bstrNamespace = NULL; + BSTR bstrDeviceID = NULL; + BSTR bstrClassName = NULL; + DWORD uReturned = 0; + bool bIsXinputDevice= false; + UINT iDevice = 0; + VARIANT var; + HRESULT hr; + + // CoInit if needed + hr = CoInitialize(NULL); + bool bCleanupCOM = SUCCEEDED(hr); + + // Create WMI + hr = CoCreateInstance( __uuidof(WbemLocator), + NULL, + CLSCTX_INPROC_SERVER, + __uuidof(IWbemLocator), + (LPVOID*) &pIWbemLocator); + if( FAILED(hr) || pIWbemLocator == NULL ) + goto LCleanup; + + bstrNamespace = SysAllocString( L"\\\\.\\root\\cimv2" );if( bstrNamespace == NULL ) goto LCleanup; + bstrClassName = SysAllocString( L"Win32_PNPEntity" ); if( bstrClassName == NULL ) goto LCleanup; + bstrDeviceID = SysAllocString( L"DeviceID" ); if( bstrDeviceID == NULL ) goto LCleanup; + + // Connect to WMI + hr = pIWbemLocator->ConnectServer( bstrNamespace, NULL, NULL, 0L, + 0L, NULL, NULL, &pIWbemServices ); + if( FAILED(hr) || pIWbemServices == NULL ) + goto LCleanup; + + // Switch security level to IMPERSONATE. + CoSetProxyBlanket( pIWbemServices, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, + RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE ); + + hr = pIWbemServices->CreateInstanceEnum( bstrClassName, 0, NULL, &pEnumDevices ); + if( FAILED(hr) || pEnumDevices == NULL ) + goto LCleanup; + + // Loop over all devices + for( ;; ) + { + // Get 20 at a time + hr = pEnumDevices->Next( 10000, 20, pDevices, &uReturned ); + if( FAILED(hr) ) + goto LCleanup; + if( uReturned == 0 ) + break; + + for( iDevice=0; iDeviceGet( bstrDeviceID, 0L, &var, NULL, NULL ); + if( SUCCEEDED( hr ) && var.vt == VT_BSTR && var.bstrVal != NULL ) + { + // Check if the device ID contains "IG_". If it does, then it's an XInput device + // This information can not be found from DirectInput + if( wcsstr( var.bstrVal, L"IG_" ) ) + { + // If it does, then get the VID/PID from var.bstrVal + DWORD dwPid = 0, dwVid = 0; + WCHAR* strVid = wcsstr( var.bstrVal, L"VID_" ); + if (strVid) { + dwVid = wcstoul(strVid+4, 0, 16); + } + WCHAR* strPid = wcsstr( var.bstrVal, L"PID_" ); + if (strPid) { + dwPid = wcstoul(strPid+4, 0, 16); + } + + // Compare the VID/PID to the DInput device + DWORD dwVidPid = MAKELONG( dwVid, dwPid ); + if( dwVidPid == pGuidProductFromDirectInput->Data1 ) + { + bIsXinputDevice = true; + goto LCleanup; + } + } + } + SAFE_RELEASE( pDevices[iDevice] ); + } + } + +LCleanup: + if(bstrNamespace) + SysFreeString(bstrNamespace); + if(bstrDeviceID) + SysFreeString(bstrDeviceID); + if(bstrClassName) + SysFreeString(bstrClassName); + for( iDevice=0; iDevice<20; iDevice++ ) + SAFE_RELEASE( pDevices[iDevice] ); + SAFE_RELEASE( pEnumDevices ); + SAFE_RELEASE( pIWbemLocator ); + SAFE_RELEASE( pIWbemServices ); + + if( bCleanupCOM ) + CoUninitialize(); + + return bIsXinputDevice; +} + + +struct DeviceEnumInfo { + IDirectInput8 *di8; + int ignoreXInput; +}; + +BOOL CALLBACK EnumCallback (LPCDIDEVICEINSTANCE lpddi, LPVOID pvRef) { + IDirectInput8* di8 = ((DeviceEnumInfo*)pvRef)->di8; + const wchar_t *name; + wchar_t temp[40]; + //if (((DeviceEnumInfo*)pvRef)->ignoreXInput && lpddi-> + if (lpddi->tszInstanceName[0]) { + name = lpddi->tszInstanceName; + } + else if (lpddi->tszProductName[0]) { + name = lpddi->tszProductName; + } + else { + wsprintfW (temp, L"Device %i", di8d.deviceCount); + name = temp; + } + di8d.deviceCount++; + wchar_t *fullName = (wchar_t *) malloc((wcslen(name) + 4) * sizeof(wchar_t)); + wsprintf(fullName, L"DX %s", name); + wchar_t instanceID[100]; + wchar_t productID[100]; + GUIDtoString(instanceID, &lpddi->guidInstance); + GUIDtoString(productID, &lpddi->guidProduct); + DeviceType type = OTHER; + if ((lpddi->dwDevType & 0xFF) == DI8DEVTYPE_KEYBOARD) { + type = KEYBOARD; + } + else if ((lpddi->dwDevType & 0xFF) == DI8DEVTYPE_MOUSE) { + type = MOUSE; + } + IDirectInputDevice8 *did; + if (DI_OK == di8->CreateDevice(lpddi->guidInstance, &did, 0)) { + DirectInputDevice *dev = new DirectInputDevice(type, did, fullName, instanceID, productID, lpddi->guidInstance); + if (dev->numPhysicalControls || dev->numFFAxes) { + dm->AddDevice(dev); + } + else { + delete dev; + } + } + free(fullName); + return DIENUM_CONTINUE; +} + +void EnumDirectInputDevices(int ignoreXInput) { + DeviceEnumInfo enumInfo; + enumInfo.di8 = GetDirectInput(); + if (!enumInfo.di8) return; + enumInfo.ignoreXInput = ignoreXInput; + di8d.deviceCount = 0; + enumInfo.di8->EnumDevices(DI8DEVCLASS_ALL, EnumCallback, &enumInfo, DIEDFL_ATTACHEDONLY); + ReleaseDirectInput(); +} + diff --git a/plugins/LilyPad/DirectInput.h b/plugins/LilyPad/DirectInput.h index e206067020..b71338d032 100644 --- a/plugins/LilyPad/DirectInput.h +++ b/plugins/LilyPad/DirectInput.h @@ -1 +1 @@ -void EnumDirectInputDevices(int ignoreXInput); +void EnumDirectInputDevices(int ignoreXInput); diff --git a/plugins/LilyPad/DualShock3.cpp b/plugins/LilyPad/DualShock3.cpp index ec6707862e..baf929d680 100644 --- a/plugins/LilyPad/DualShock3.cpp +++ b/plugins/LilyPad/DualShock3.cpp @@ -1,452 +1,452 @@ -#include "Global.h" -#include "InputManager.h" - -#include "usb.h" -#include "HidDevice.h" - - -#define VID 0x054c -#define PID 0x0268 - -// Unresponsive period required before calling DS3Check(). -#define DEVICE_CHECK_DELAY 2000 -// Unresponsive period required before calling DS3Enum(). Note that enum is always called on first check. -#define DEVICE_ENUM_DELAY 10000 - -// Delay between when DS3Check() and DS3Enum() actually do stuff. -#define DOUBLE_CHECK_DELAY 1000 -#define DOUBLE_ENUM_DELAY 20000 - -// Send at least one message every 3 seconds - basically just makes sure the right light(s) are on. -// Not really necessary. -#define UPDATE_INTERVAL 3000 - -unsigned int lastDS3Check = 0; -unsigned int lastDS3Enum = 0; - -typedef void (__cdecl *_usb_init)(void); -typedef int (__cdecl *_usb_close)(usb_dev_handle *dev); -typedef int (__cdecl *_usb_get_string_simple)(usb_dev_handle *dev, int index, char *buf, size_t buflen); -typedef usb_dev_handle *(__cdecl *_usb_open)(struct usb_device *dev); -typedef int (__cdecl *_usb_find_busses)(void); -typedef int (__cdecl *_usb_find_devices)(void); -typedef struct usb_bus *(__cdecl *_usb_get_busses)(void); -typedef usb_dev_handle *(__cdecl *_usb_open)(struct usb_device *dev); -typedef int (__cdecl *_usb_control_msg)(usb_dev_handle *dev, int requesttype, int request, int value, int index, char *bytes, int size, int timeout); - -_usb_init pusb_init; -_usb_close pusb_close; -_usb_get_string_simple pusb_get_string_simple; -_usb_open pusb_open; -_usb_find_busses pusb_find_busses; -_usb_find_devices pusb_find_devices; -_usb_get_busses pusb_get_busses; -_usb_control_msg pusb_control_msg; - -HMODULE hModLibusb = 0; - -void UninitLibUsb() { - if (hModLibusb) { - FreeLibrary(hModLibusb); - hModLibusb = 0; - } -} - -void TryInitDS3(usb_device *dev) { - while (dev) { - if (dev->descriptor.idVendor == VID && dev->descriptor.idProduct == PID) { - usb_dev_handle *handle = pusb_open(dev); - if (handle) { - char junk[20]; - // This looks like HidD_GetFeature with a feature report id of 0xF2 to me and a length of 17. - // That doesn't work, however, and 17 is shorter than the report length. - pusb_control_msg(handle, 0xa1, 1, 0x03f2, dev->config->interface->altsetting->bInterfaceNumber, junk, 17, 1000); - pusb_close(handle); - } - } - if (dev->num_children) { - for (int i=0; inum_children; i++) { - TryInitDS3(dev->children[i]); - } - } - dev = dev->next; - } -} - -void DS3Enum(unsigned int time) { - if (time - lastDS3Enum < DOUBLE_ENUM_DELAY) { - return; - } - lastDS3Enum = time; - pusb_find_busses(); - pusb_find_devices(); -} - -void DS3Check(unsigned int time) { - if (time - lastDS3Check < DOUBLE_CHECK_DELAY) { - return; - } - if (!lastDS3Check) { - DS3Enum(time); - } - lastDS3Check = time; - - usb_bus *bus = pusb_get_busses(); - while (bus) { - TryInitDS3(bus->devices); - bus = bus->next; - } -} - -int InitLibUsb() { - if (hModLibusb) { - return 1; - } - hModLibusb = LoadLibraryA("C:\\windows\\system32\\libusb0.dll"); - if (hModLibusb) { - if ((pusb_init = (_usb_init) GetProcAddress(hModLibusb, "usb_init")) && - (pusb_close = (_usb_close) GetProcAddress(hModLibusb, "usb_close")) && - (pusb_get_string_simple = (_usb_get_string_simple) GetProcAddress(hModLibusb, "usb_get_string_simple")) && - (pusb_open = (_usb_open) GetProcAddress(hModLibusb, "usb_open")) && - (pusb_find_busses = (_usb_find_busses) GetProcAddress(hModLibusb, "usb_find_busses")) && - (pusb_find_devices = (_usb_find_devices) GetProcAddress(hModLibusb, "usb_find_devices")) && - (pusb_get_busses = (_usb_get_busses) GetProcAddress(hModLibusb, "usb_get_busses")) && - (pusb_control_msg = (_usb_control_msg) GetProcAddress(hModLibusb, "usb_control_msg"))) { - pusb_init(); - return 1; - } - UninitLibUsb(); - } - return 0; -} - -int DualShock3Possible() { - return InitLibUsb(); -} - -#include - -struct MotorState { - unsigned char duration; - unsigned char force; -}; - -struct LightState { - // 0xFF makes it stay on. - unsigned char duration; - // Have to make one or the other non-zero to turn on light. - unsigned char dunno[2]; - // 0 is fully lit. - unsigned char dimness; - // Have to make non-zero to turn on light. - unsigned char on; -}; - -// Data sent to DS3 to set state. -struct DS3Command { - unsigned char id; - unsigned char unsure; - // Small is first, then big. - MotorState motors[2]; - unsigned char noClue[4]; - // 2 is pad 1 light, 4 is pad 2, 8 is pad 3, 16 is pad 4. No clue about the others. - unsigned char lightFlags; - // Lights are in reverse order. pad 1 is last. - LightState lights[4]; - unsigned char dunno[18]; -}; - -#include - -int CharToAxis(unsigned char c) { - int v = (int)c + ((unsigned int)c >> 7); - return ((c-128) * FULLY_DOWN)>>7; -} - -int CharToButton(unsigned char c) { - int v = (int)c + ((unsigned int)c >> 7); - return (v * FULLY_DOWN)>>8; -} - -class DualShock3Device : public Device { - // Cached last vibration values by pad and motor. - // Need this, as only one value is changed at a time. - int ps2Vibration[2][4][2]; - int vibration[2]; -public: - int index; - HANDLE hFile; - DS3Command sendState; - unsigned char getState[49]; - OVERLAPPED readop; - OVERLAPPED writeop; - int writeCount; - int lastWrite; - - unsigned int dataLastReceived; - - int writeQueued; - int writing; - - int StartRead() { - int res = ReadFile(hFile, &getState, sizeof(getState), 0, &readop); - return (res || GetLastError() == ERROR_IO_PENDING); - } - - void QueueWrite() { - // max of 2 queued writes allowed, one for either motor. - if (writeQueued < 2) { - writeQueued++; - StartWrite(); - } - } - - int StartWrite() { - if (!writing && writeQueued) { - lastWrite = GetTickCount(); - writing++; - writeQueued--; - sendState.motors[0].duration = 0x50; - sendState.motors[1].duration = 0x50; - - int bigForce = vibration[0] * 256/FULLY_DOWN; - if (bigForce > 255) bigForce = 255; - sendState.motors[1].force = (unsigned char) bigForce; - sendState.motors[0].force = (unsigned char) (vibration[1] >= FULLY_DOWN/2); - // Can't seem to have them both non-zero at once. - if (sendState.motors[writeCount&1].force) { - sendState.motors[(writeCount&1)^1].force = 0; - sendState.motors[(writeCount&1)^1].duration = 0; - } - - writeCount++; - int res = WriteFile(hFile, &sendState, sizeof(sendState), 0, &writeop); - return (res || GetLastError() == ERROR_IO_PENDING); - } - return 1; - } - - DualShock3Device(int index, wchar_t *name, wchar_t *path) : Device(DS3, OTHER, name, path, L"DualShock 3") { - writeCount = 0; - writing = 0; - writeQueued = 0; - memset(&readop, 0, sizeof(readop)); - memset(&writeop, 0, sizeof(writeop)); - memset(&sendState, 0, sizeof(sendState)); - sendState.id = 1; - int temp = (index&4); - sendState.lightFlags = (1 << (temp+1)); - sendState.lights[3-temp].duration = 0xFF; - sendState.lights[3-temp].dunno[0] = 1; - sendState.lights[3-temp].on = 1; - memset(ps2Vibration, 0, sizeof(ps2Vibration)); - vibration[0] = vibration[1] = 0; - this->index = index; - int i; - for (i=0; i<16; i++) { - if (i != 14 && i != 15 && i != 8 && i != 9) { - AddPhysicalControl(PRESSURE_BTN, i, 0); - } - else { - AddPhysicalControl(PSHBTN, i, 0); - } - } - for (; i<23; i++) { - AddPhysicalControl(ABSAXIS, i, 0); - } - AddFFAxis(L"Big Motor", 0); - AddFFAxis(L"Small Motor", 1); - AddFFEffectType(L"Constant Effect", L"Constant", EFFECT_CONSTANT); - hFile = INVALID_HANDLE_VALUE; - } - - wchar_t *GetPhysicalControlName(PhysicalControl *c) { - const static wchar_t *names[] = { - L"Square", - L"Cross", - L"Circle", - L"Triangle", - L"R1", - L"L1", - L"R2", - L"L2", - L"R3", - L"L3", - L"Left", - L"Down", - L"Right", - L"Up", - L"Start", - L"Select", - L"L-Stick X", - L"L-Stick Y", - L"R-Stick X", - L"R-Stick Y", - L"Left/Right Tilt", - L"Forward/Back Tilt", - L"???", - }; - unsigned int i = (unsigned int) (c - physicalControls); - if (i < sizeof(names)/sizeof(names[0])) { - return (wchar_t*)names[i]; - } - return Device::GetPhysicalControlName(c); - } - - int Activate(InitInfo *initInfo) { - if (active) Deactivate(); - // Give grace period before get mad. - lastWrite = dataLastReceived = GetTickCount(); - readop.hEvent = CreateEvent(0, 0, 0, 0); - writeop.hEvent = CreateEvent(0, 0, 0, 0); - hFile = CreateFileW(instanceID, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0); - if (!readop.hEvent || !writeop.hEvent || hFile == INVALID_HANDLE_VALUE || - !StartRead()) { - Deactivate(); - return 0; - } - active = 1; - AllocState(); - return 1; - } - - int Update() { - if (!active) return 0; - HANDLE h[2] = { - readop.hEvent, - writeop.hEvent - }; - unsigned int time = GetTickCount(); - if (time - lastWrite > UPDATE_INTERVAL) { - QueueWrite(); - } - while (1) { - DWORD res = WaitForMultipleObjects(2, h, 0, 0); - if (res == WAIT_OBJECT_0) { - dataLastReceived = time; - if (!StartRead()) { - Deactivate(); - return 0; - } - - physicalControlState[0] = CharToButton(getState[25]); - physicalControlState[1] = CharToButton(getState[24]); - physicalControlState[2] = CharToButton(getState[23]); - physicalControlState[3] = CharToButton(getState[22]); - physicalControlState[4] = CharToButton(getState[21]); - physicalControlState[5] = CharToButton(getState[20]); - physicalControlState[6] = CharToButton(getState[19]); - physicalControlState[7] = CharToButton(getState[18]); - physicalControlState[10] = CharToButton(getState[17]); - physicalControlState[11] = CharToButton(getState[16]); - physicalControlState[12] = CharToButton(getState[15]); - physicalControlState[13] = CharToButton(getState[14]); - physicalControlState[8] = ((getState[2]&4)/4) * FULLY_DOWN; - physicalControlState[9] = ((getState[2]&2)/2) * FULLY_DOWN; - physicalControlState[15] = ((getState[2]&1)/1) * FULLY_DOWN; - physicalControlState[14] = ((getState[2]&8)/8) * FULLY_DOWN; - physicalControlState[16] = CharToAxis(getState[6]); - physicalControlState[17] = CharToAxis(getState[7]); - physicalControlState[18] = CharToAxis(getState[8]); - physicalControlState[19] = CharToAxis(getState[9]); - physicalControlState[20] = CharToAxis(getState[42]+128); - physicalControlState[21] = CharToAxis(getState[44]+128); - physicalControlState[22] = CharToAxis(getState[46]+128); - continue; - } - else if (res == WAIT_OBJECT_0+1) { - writing = 0; - if (!writeQueued && (vibration[0] | vibration[1])) { - QueueWrite(); - } - if (!StartWrite()) { - Deactivate(); - return 0; - } - } - else { - if (time-dataLastReceived >= DEVICE_CHECK_DELAY) { - if (time-dataLastReceived >= DEVICE_ENUM_DELAY) { - DS3Enum(time); - } - DS3Check(time); - QueueWrite(); - } - } - break; - } - return 1; - } - - void SetEffects(unsigned char port, unsigned int slot, unsigned char motor, unsigned char force) { - ps2Vibration[port][slot][motor] = force; - vibration[0] = vibration[1] = 0; - for (int p=0; p<2; p++) { - for (int s=0; s<4; s++) { - for (int i=0; iaxes[0].force * (__int64)ps2Vibration[p][s][ffb->motor]) / 255); - vibration[1] += (int)((ffb->axes[1].force * (__int64)ps2Vibration[p][s][ffb->motor]) / 255); - } - } - } - // Make sure at least 2 writes are queued, to update both motors. - QueueWrite(); - QueueWrite(); - } - - void SetEffect(ForceFeedbackBinding *binding, unsigned char force) { - PadBindings pBackup = pads[0][0]; - pads[0][0].ffBindings = binding; - pads[0][0].numFFBindings = 1; - SetEffects(0, 0, binding->motor, 255); - pads[0][0] = pBackup; - } - - void Deactivate() { - if (hFile != INVALID_HANDLE_VALUE) { - CancelIo(hFile); - CloseHandle(hFile); - hFile = INVALID_HANDLE_VALUE; - } - if (readop.hEvent) { - CloseHandle(readop.hEvent); - } - if (writeop.hEvent) { - CloseHandle(writeop.hEvent); - } - writing = 0; - writeQueued = 0; - memset(ps2Vibration, 0, sizeof(ps2Vibration)); - vibration[0] = vibration[1] = 0; - - FreeState(); - active = 0; - } - - ~DualShock3Device() { - } -}; - -void EnumDualShock3s() { - if (!InitLibUsb()) return; - - HidDeviceInfo *foundDevs = 0; - - int numDevs = FindHids(&foundDevs, VID, PID); - if (!numDevs) return; - int index = 0; - for (int i=0; iAddDevice(new DualShock3Device(index, temp, foundDevs[i].path)); - index++; - } - free(foundDevs[i].path); - } - free(foundDevs); -} +#include "Global.h" +#include "InputManager.h" + +#include "usb.h" +#include "HidDevice.h" + + +#define VID 0x054c +#define PID 0x0268 + +// Unresponsive period required before calling DS3Check(). +#define DEVICE_CHECK_DELAY 2000 +// Unresponsive period required before calling DS3Enum(). Note that enum is always called on first check. +#define DEVICE_ENUM_DELAY 10000 + +// Delay between when DS3Check() and DS3Enum() actually do stuff. +#define DOUBLE_CHECK_DELAY 1000 +#define DOUBLE_ENUM_DELAY 20000 + +// Send at least one message every 3 seconds - basically just makes sure the right light(s) are on. +// Not really necessary. +#define UPDATE_INTERVAL 3000 + +unsigned int lastDS3Check = 0; +unsigned int lastDS3Enum = 0; + +typedef void (__cdecl *_usb_init)(void); +typedef int (__cdecl *_usb_close)(usb_dev_handle *dev); +typedef int (__cdecl *_usb_get_string_simple)(usb_dev_handle *dev, int index, char *buf, size_t buflen); +typedef usb_dev_handle *(__cdecl *_usb_open)(struct usb_device *dev); +typedef int (__cdecl *_usb_find_busses)(void); +typedef int (__cdecl *_usb_find_devices)(void); +typedef struct usb_bus *(__cdecl *_usb_get_busses)(void); +typedef usb_dev_handle *(__cdecl *_usb_open)(struct usb_device *dev); +typedef int (__cdecl *_usb_control_msg)(usb_dev_handle *dev, int requesttype, int request, int value, int index, char *bytes, int size, int timeout); + +_usb_init pusb_init; +_usb_close pusb_close; +_usb_get_string_simple pusb_get_string_simple; +_usb_open pusb_open; +_usb_find_busses pusb_find_busses; +_usb_find_devices pusb_find_devices; +_usb_get_busses pusb_get_busses; +_usb_control_msg pusb_control_msg; + +HMODULE hModLibusb = 0; + +void UninitLibUsb() { + if (hModLibusb) { + FreeLibrary(hModLibusb); + hModLibusb = 0; + } +} + +void TryInitDS3(usb_device *dev) { + while (dev) { + if (dev->descriptor.idVendor == VID && dev->descriptor.idProduct == PID) { + usb_dev_handle *handle = pusb_open(dev); + if (handle) { + char junk[20]; + // This looks like HidD_GetFeature with a feature report id of 0xF2 to me and a length of 17. + // That doesn't work, however, and 17 is shorter than the report length. + pusb_control_msg(handle, 0xa1, 1, 0x03f2, dev->config->interface->altsetting->bInterfaceNumber, junk, 17, 1000); + pusb_close(handle); + } + } + if (dev->num_children) { + for (int i=0; inum_children; i++) { + TryInitDS3(dev->children[i]); + } + } + dev = dev->next; + } +} + +void DS3Enum(unsigned int time) { + if (time - lastDS3Enum < DOUBLE_ENUM_DELAY) { + return; + } + lastDS3Enum = time; + pusb_find_busses(); + pusb_find_devices(); +} + +void DS3Check(unsigned int time) { + if (time - lastDS3Check < DOUBLE_CHECK_DELAY) { + return; + } + if (!lastDS3Check) { + DS3Enum(time); + } + lastDS3Check = time; + + usb_bus *bus = pusb_get_busses(); + while (bus) { + TryInitDS3(bus->devices); + bus = bus->next; + } +} + +int InitLibUsb() { + if (hModLibusb) { + return 1; + } + hModLibusb = LoadLibraryA("C:\\windows\\system32\\libusb0.dll"); + if (hModLibusb) { + if ((pusb_init = (_usb_init) GetProcAddress(hModLibusb, "usb_init")) && + (pusb_close = (_usb_close) GetProcAddress(hModLibusb, "usb_close")) && + (pusb_get_string_simple = (_usb_get_string_simple) GetProcAddress(hModLibusb, "usb_get_string_simple")) && + (pusb_open = (_usb_open) GetProcAddress(hModLibusb, "usb_open")) && + (pusb_find_busses = (_usb_find_busses) GetProcAddress(hModLibusb, "usb_find_busses")) && + (pusb_find_devices = (_usb_find_devices) GetProcAddress(hModLibusb, "usb_find_devices")) && + (pusb_get_busses = (_usb_get_busses) GetProcAddress(hModLibusb, "usb_get_busses")) && + (pusb_control_msg = (_usb_control_msg) GetProcAddress(hModLibusb, "usb_control_msg"))) { + pusb_init(); + return 1; + } + UninitLibUsb(); + } + return 0; +} + +int DualShock3Possible() { + return InitLibUsb(); +} + +#include + +struct MotorState { + unsigned char duration; + unsigned char force; +}; + +struct LightState { + // 0xFF makes it stay on. + unsigned char duration; + // Have to make one or the other non-zero to turn on light. + unsigned char dunno[2]; + // 0 is fully lit. + unsigned char dimness; + // Have to make non-zero to turn on light. + unsigned char on; +}; + +// Data sent to DS3 to set state. +struct DS3Command { + unsigned char id; + unsigned char unsure; + // Small is first, then big. + MotorState motors[2]; + unsigned char noClue[4]; + // 2 is pad 1 light, 4 is pad 2, 8 is pad 3, 16 is pad 4. No clue about the others. + unsigned char lightFlags; + // Lights are in reverse order. pad 1 is last. + LightState lights[4]; + unsigned char dunno[18]; +}; + +#include + +int CharToAxis(unsigned char c) { + int v = (int)c + ((unsigned int)c >> 7); + return ((c-128) * FULLY_DOWN)>>7; +} + +int CharToButton(unsigned char c) { + int v = (int)c + ((unsigned int)c >> 7); + return (v * FULLY_DOWN)>>8; +} + +class DualShock3Device : public Device { + // Cached last vibration values by pad and motor. + // Need this, as only one value is changed at a time. + int ps2Vibration[2][4][2]; + int vibration[2]; +public: + int index; + HANDLE hFile; + DS3Command sendState; + unsigned char getState[49]; + OVERLAPPED readop; + OVERLAPPED writeop; + int writeCount; + int lastWrite; + + unsigned int dataLastReceived; + + int writeQueued; + int writing; + + int StartRead() { + int res = ReadFile(hFile, &getState, sizeof(getState), 0, &readop); + return (res || GetLastError() == ERROR_IO_PENDING); + } + + void QueueWrite() { + // max of 2 queued writes allowed, one for either motor. + if (writeQueued < 2) { + writeQueued++; + StartWrite(); + } + } + + int StartWrite() { + if (!writing && writeQueued) { + lastWrite = GetTickCount(); + writing++; + writeQueued--; + sendState.motors[0].duration = 0x50; + sendState.motors[1].duration = 0x50; + + int bigForce = vibration[0] * 256/FULLY_DOWN; + if (bigForce > 255) bigForce = 255; + sendState.motors[1].force = (unsigned char) bigForce; + sendState.motors[0].force = (unsigned char) (vibration[1] >= FULLY_DOWN/2); + // Can't seem to have them both non-zero at once. + if (sendState.motors[writeCount&1].force) { + sendState.motors[(writeCount&1)^1].force = 0; + sendState.motors[(writeCount&1)^1].duration = 0; + } + + writeCount++; + int res = WriteFile(hFile, &sendState, sizeof(sendState), 0, &writeop); + return (res || GetLastError() == ERROR_IO_PENDING); + } + return 1; + } + + DualShock3Device(int index, wchar_t *name, wchar_t *path) : Device(DS3, OTHER, name, path, L"DualShock 3") { + writeCount = 0; + writing = 0; + writeQueued = 0; + memset(&readop, 0, sizeof(readop)); + memset(&writeop, 0, sizeof(writeop)); + memset(&sendState, 0, sizeof(sendState)); + sendState.id = 1; + int temp = (index&4); + sendState.lightFlags = (1 << (temp+1)); + sendState.lights[3-temp].duration = 0xFF; + sendState.lights[3-temp].dunno[0] = 1; + sendState.lights[3-temp].on = 1; + memset(ps2Vibration, 0, sizeof(ps2Vibration)); + vibration[0] = vibration[1] = 0; + this->index = index; + int i; + for (i=0; i<16; i++) { + if (i != 14 && i != 15 && i != 8 && i != 9) { + AddPhysicalControl(PRESSURE_BTN, i, 0); + } + else { + AddPhysicalControl(PSHBTN, i, 0); + } + } + for (; i<23; i++) { + AddPhysicalControl(ABSAXIS, i, 0); + } + AddFFAxis(L"Big Motor", 0); + AddFFAxis(L"Small Motor", 1); + AddFFEffectType(L"Constant Effect", L"Constant", EFFECT_CONSTANT); + hFile = INVALID_HANDLE_VALUE; + } + + wchar_t *GetPhysicalControlName(PhysicalControl *c) { + const static wchar_t *names[] = { + L"Square", + L"Cross", + L"Circle", + L"Triangle", + L"R1", + L"L1", + L"R2", + L"L2", + L"R3", + L"L3", + L"Left", + L"Down", + L"Right", + L"Up", + L"Start", + L"Select", + L"L-Stick X", + L"L-Stick Y", + L"R-Stick X", + L"R-Stick Y", + L"Left/Right Tilt", + L"Forward/Back Tilt", + L"???", + }; + unsigned int i = (unsigned int) (c - physicalControls); + if (i < sizeof(names)/sizeof(names[0])) { + return (wchar_t*)names[i]; + } + return Device::GetPhysicalControlName(c); + } + + int Activate(InitInfo *initInfo) { + if (active) Deactivate(); + // Give grace period before get mad. + lastWrite = dataLastReceived = GetTickCount(); + readop.hEvent = CreateEvent(0, 0, 0, 0); + writeop.hEvent = CreateEvent(0, 0, 0, 0); + hFile = CreateFileW(instanceID, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0); + if (!readop.hEvent || !writeop.hEvent || hFile == INVALID_HANDLE_VALUE || + !StartRead()) { + Deactivate(); + return 0; + } + active = 1; + AllocState(); + return 1; + } + + int Update() { + if (!active) return 0; + HANDLE h[2] = { + readop.hEvent, + writeop.hEvent + }; + unsigned int time = GetTickCount(); + if (time - lastWrite > UPDATE_INTERVAL) { + QueueWrite(); + } + while (1) { + DWORD res = WaitForMultipleObjects(2, h, 0, 0); + if (res == WAIT_OBJECT_0) { + dataLastReceived = time; + if (!StartRead()) { + Deactivate(); + return 0; + } + + physicalControlState[0] = CharToButton(getState[25]); + physicalControlState[1] = CharToButton(getState[24]); + physicalControlState[2] = CharToButton(getState[23]); + physicalControlState[3] = CharToButton(getState[22]); + physicalControlState[4] = CharToButton(getState[21]); + physicalControlState[5] = CharToButton(getState[20]); + physicalControlState[6] = CharToButton(getState[19]); + physicalControlState[7] = CharToButton(getState[18]); + physicalControlState[10] = CharToButton(getState[17]); + physicalControlState[11] = CharToButton(getState[16]); + physicalControlState[12] = CharToButton(getState[15]); + physicalControlState[13] = CharToButton(getState[14]); + physicalControlState[8] = ((getState[2]&4)/4) * FULLY_DOWN; + physicalControlState[9] = ((getState[2]&2)/2) * FULLY_DOWN; + physicalControlState[15] = ((getState[2]&1)/1) * FULLY_DOWN; + physicalControlState[14] = ((getState[2]&8)/8) * FULLY_DOWN; + physicalControlState[16] = CharToAxis(getState[6]); + physicalControlState[17] = CharToAxis(getState[7]); + physicalControlState[18] = CharToAxis(getState[8]); + physicalControlState[19] = CharToAxis(getState[9]); + physicalControlState[20] = CharToAxis(getState[42]+128); + physicalControlState[21] = CharToAxis(getState[44]+128); + physicalControlState[22] = CharToAxis(getState[46]+128); + continue; + } + else if (res == WAIT_OBJECT_0+1) { + writing = 0; + if (!writeQueued && (vibration[0] | vibration[1])) { + QueueWrite(); + } + if (!StartWrite()) { + Deactivate(); + return 0; + } + } + else { + if (time-dataLastReceived >= DEVICE_CHECK_DELAY) { + if (time-dataLastReceived >= DEVICE_ENUM_DELAY) { + DS3Enum(time); + } + DS3Check(time); + QueueWrite(); + } + } + break; + } + return 1; + } + + void SetEffects(unsigned char port, unsigned int slot, unsigned char motor, unsigned char force) { + ps2Vibration[port][slot][motor] = force; + vibration[0] = vibration[1] = 0; + for (int p=0; p<2; p++) { + for (int s=0; s<4; s++) { + for (int i=0; iaxes[0].force * (__int64)ps2Vibration[p][s][ffb->motor]) / 255); + vibration[1] += (int)((ffb->axes[1].force * (__int64)ps2Vibration[p][s][ffb->motor]) / 255); + } + } + } + // Make sure at least 2 writes are queued, to update both motors. + QueueWrite(); + QueueWrite(); + } + + void SetEffect(ForceFeedbackBinding *binding, unsigned char force) { + PadBindings pBackup = pads[0][0]; + pads[0][0].ffBindings = binding; + pads[0][0].numFFBindings = 1; + SetEffects(0, 0, binding->motor, 255); + pads[0][0] = pBackup; + } + + void Deactivate() { + if (hFile != INVALID_HANDLE_VALUE) { + CancelIo(hFile); + CloseHandle(hFile); + hFile = INVALID_HANDLE_VALUE; + } + if (readop.hEvent) { + CloseHandle(readop.hEvent); + } + if (writeop.hEvent) { + CloseHandle(writeop.hEvent); + } + writing = 0; + writeQueued = 0; + memset(ps2Vibration, 0, sizeof(ps2Vibration)); + vibration[0] = vibration[1] = 0; + + FreeState(); + active = 0; + } + + ~DualShock3Device() { + } +}; + +void EnumDualShock3s() { + if (!InitLibUsb()) return; + + HidDeviceInfo *foundDevs = 0; + + int numDevs = FindHids(&foundDevs, VID, PID); + if (!numDevs) return; + int index = 0; + for (int i=0; iAddDevice(new DualShock3Device(index, temp, foundDevs[i].path)); + index++; + } + free(foundDevs[i].path); + } + free(foundDevs); +} diff --git a/plugins/LilyPad/DualShock3.h b/plugins/LilyPad/DualShock3.h index 1c46774d34..099dbb2f51 100644 --- a/plugins/LilyPad/DualShock3.h +++ b/plugins/LilyPad/DualShock3.h @@ -1,6 +1,6 @@ -int DualShock3Possible(); -void EnumDualShock3s(); - -// May move elsewhere in the future. -int InitLibUsb(); -void UninitLibUsb(); +int DualShock3Possible(); +void EnumDualShock3s(); + +// May move elsewhere in the future. +int InitLibUsb(); +void UninitLibUsb(); diff --git a/plugins/LilyPad/Global.cpp b/plugins/LilyPad/Global.cpp index a57d59275b..680ff56cfb 100644 --- a/plugins/LilyPad/Global.cpp +++ b/plugins/LilyPad/Global.cpp @@ -1 +1 @@ -#include "Global.h" +#include "Global.h" diff --git a/plugins/LilyPad/Global.h b/plugins/LilyPad/Global.h index 233041a05e..ba8f9e6581 100644 --- a/plugins/LilyPad/Global.h +++ b/plugins/LilyPad/Global.h @@ -1,147 +1,147 @@ -// Includes Windows.h, has inlined versions of memory allocation and -// string comparison functions needed to avoid using CRT. This reduces -// dll size by over 100k while avoiding any dependencies on updated CRT dlls. -#pragma once - -#define DIRECTINPUT_VERSION 0x0800 - -#ifdef NO_CRT -#define _CRT_ALLOCATION_DEFINED - -inline void * malloc(size_t size); -inline void * calloc(size_t num, size_t size); -inline void free(void * mem); -inline void * realloc(void *mem, size_t size); -#endif - -#define UNICODE - -#ifndef _CRT_SECURE_NO_DEPRECATE -#define _CRT_SECURE_NO_DEPRECATE -#endif - -#ifdef _MSC_VER -#define EXPORT_C_(type) extern "C" __declspec(dllexport) type CALLBACK -#else -#define EXPORT_C_(type) extern "C" type -#endif - -// Actually works with 0x0400, but need 0x500 to get XBUTTON defines, -// 0x501 to get raw input structures, and 0x0600 to get WM_MOUSEHWHEEL. -#define WINVER 0x0600 -#define _WIN32_WINNT WINVER -#define __MSCW32__ - - -#include - -#ifdef PCSX2_DEBUG -#define _CRTDBG_MAPALLOC -#define _CRTDBG_MAP_ALLOC -#include -#include -#else -#include -#endif - -#include -#include -#include - -#include -// Only needed for DBT_DEVNODES_CHANGED -#include - -#include "PS2Edefs.h" - -extern HINSTANCE hInst; -// Needed for config screen -void GetNameAndVersionString(wchar_t *out); - -typedef struct { - unsigned char controllerType; - unsigned short buttonStatus; - unsigned char rightJoyX, rightJoyY, leftJoyX, leftJoyY; - unsigned char moveX, moveY; - unsigned char reserved[91]; -} PadDataS; - -EXPORT_C_(void) PADupdate(int pad); -EXPORT_C_(u32) PS2EgetLibType(void); -EXPORT_C_(u32) PS2EgetLibVersion2(u32 type); -EXPORT_C_(char*) PSEgetLibName(); -EXPORT_C_(char*) PS2EgetLibName(void); -EXPORT_C_(void) PADshutdown(); -EXPORT_C_(s32) PADinit(u32 flags); -EXPORT_C_(s32) PADopen(void *pDsp); -EXPORT_C_(void) PADclose(); -EXPORT_C_(u8) PADstartPoll(int pad); -EXPORT_C_(u8) PADpoll(u8 value); -EXPORT_C_(u32) PADquery(); -EXPORT_C_(void) PADabout(); -EXPORT_C_(s32) PADtest(); -EXPORT_C_(keyEvent*) PADkeyEvent(); -EXPORT_C_(u32) PADreadPort1 (PadDataS* pads); -EXPORT_C_(u32) PADreadPort2 (PadDataS* pads); -EXPORT_C_(u32) PSEgetLibType(); -EXPORT_C_(u32) PSEgetLibVersion(); -EXPORT_C_(void) PADconfigure(); -EXPORT_C_(s32) PADfreeze(int mode, freezeData *data); -EXPORT_C_(s32) PADsetSlot(u8 port, u8 slot); -EXPORT_C_(s32) PADqueryMtap(u8 port); - -#ifdef NO_CRT - -#define wcsdup MyWcsdup -#define wcsicmp MyWcsicmp - -inline void * malloc(size_t size) { - return HeapAlloc(GetProcessHeap(), 0, size); -} - -inline void * calloc(size_t num, size_t size) { - size *= num; - void *out = malloc(size); - if (out) memset(out, 0, size); - return out; -} - -inline void free(void * mem) { - if (mem) HeapFree(GetProcessHeap(), 0, mem); -} - -inline void * realloc(void *mem, size_t size) { - if (!mem) { - return malloc(size); - } - - if (!size) { - free(mem); - return 0; - } - return HeapReAlloc(GetProcessHeap(), 0, mem, size); -} - -inline void * __cdecl operator new(size_t lSize) { - return HeapAlloc(GetProcessHeap(), 0, lSize); -} - -inline void __cdecl operator delete(void *pBlock) { - HeapFree(GetProcessHeap(), 0, pBlock); -} - -inline wchar_t * __cdecl wcsdup(const wchar_t *in) { - size_t size = sizeof(wchar_t) * (1+wcslen(in)); - wchar_t *out = (wchar_t*) malloc(size); - if (out) - memcpy(out, in, size); - return out; -} - -inline int __cdecl wcsicmp(const wchar_t *s1, const wchar_t *s2) { - int res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, -1, s2, -1); - if (res) return res-2; - return res; -} - -#endif +// Includes Windows.h, has inlined versions of memory allocation and +// string comparison functions needed to avoid using CRT. This reduces +// dll size by over 100k while avoiding any dependencies on updated CRT dlls. +#pragma once + +#define DIRECTINPUT_VERSION 0x0800 + +#ifdef NO_CRT +#define _CRT_ALLOCATION_DEFINED + +inline void * malloc(size_t size); +inline void * calloc(size_t num, size_t size); +inline void free(void * mem); +inline void * realloc(void *mem, size_t size); +#endif + +#define UNICODE + +#ifndef _CRT_SECURE_NO_DEPRECATE +#define _CRT_SECURE_NO_DEPRECATE +#endif + +#ifdef _MSC_VER +#define EXPORT_C_(type) extern "C" __declspec(dllexport) type CALLBACK +#else +#define EXPORT_C_(type) extern "C" type +#endif + +// Actually works with 0x0400, but need 0x500 to get XBUTTON defines, +// 0x501 to get raw input structures, and 0x0600 to get WM_MOUSEHWHEEL. +#define WINVER 0x0600 +#define _WIN32_WINNT WINVER +#define __MSCW32__ + + +#include + +#ifdef PCSX2_DEBUG +#define _CRTDBG_MAPALLOC +#define _CRTDBG_MAP_ALLOC +#include +#include +#else +#include +#endif + +#include +#include +#include + +#include +// Only needed for DBT_DEVNODES_CHANGED +#include + +#include "PS2Edefs.h" + +extern HINSTANCE hInst; +// Needed for config screen +void GetNameAndVersionString(wchar_t *out); + +typedef struct { + unsigned char controllerType; + unsigned short buttonStatus; + unsigned char rightJoyX, rightJoyY, leftJoyX, leftJoyY; + unsigned char moveX, moveY; + unsigned char reserved[91]; +} PadDataS; + +EXPORT_C_(void) PADupdate(int pad); +EXPORT_C_(u32) PS2EgetLibType(void); +EXPORT_C_(u32) PS2EgetLibVersion2(u32 type); +EXPORT_C_(char*) PSEgetLibName(); +EXPORT_C_(char*) PS2EgetLibName(void); +EXPORT_C_(void) PADshutdown(); +EXPORT_C_(s32) PADinit(u32 flags); +EXPORT_C_(s32) PADopen(void *pDsp); +EXPORT_C_(void) PADclose(); +EXPORT_C_(u8) PADstartPoll(int pad); +EXPORT_C_(u8) PADpoll(u8 value); +EXPORT_C_(u32) PADquery(); +EXPORT_C_(void) PADabout(); +EXPORT_C_(s32) PADtest(); +EXPORT_C_(keyEvent*) PADkeyEvent(); +EXPORT_C_(u32) PADreadPort1 (PadDataS* pads); +EXPORT_C_(u32) PADreadPort2 (PadDataS* pads); +EXPORT_C_(u32) PSEgetLibType(); +EXPORT_C_(u32) PSEgetLibVersion(); +EXPORT_C_(void) PADconfigure(); +EXPORT_C_(s32) PADfreeze(int mode, freezeData *data); +EXPORT_C_(s32) PADsetSlot(u8 port, u8 slot); +EXPORT_C_(s32) PADqueryMtap(u8 port); + +#ifdef NO_CRT + +#define wcsdup MyWcsdup +#define wcsicmp MyWcsicmp + +inline void * malloc(size_t size) { + return HeapAlloc(GetProcessHeap(), 0, size); +} + +inline void * calloc(size_t num, size_t size) { + size *= num; + void *out = malloc(size); + if (out) memset(out, 0, size); + return out; +} + +inline void free(void * mem) { + if (mem) HeapFree(GetProcessHeap(), 0, mem); +} + +inline void * realloc(void *mem, size_t size) { + if (!mem) { + return malloc(size); + } + + if (!size) { + free(mem); + return 0; + } + return HeapReAlloc(GetProcessHeap(), 0, mem, size); +} + +inline void * __cdecl operator new(size_t lSize) { + return HeapAlloc(GetProcessHeap(), 0, lSize); +} + +inline void __cdecl operator delete(void *pBlock) { + HeapFree(GetProcessHeap(), 0, pBlock); +} + +inline wchar_t * __cdecl wcsdup(const wchar_t *in) { + size_t size = sizeof(wchar_t) * (1+wcslen(in)); + wchar_t *out = (wchar_t*) malloc(size); + if (out) + memcpy(out, in, size); + return out; +} + +inline int __cdecl wcsicmp(const wchar_t *s1, const wchar_t *s2) { + int res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, -1, s2, -1); + if (res) return res-2; + return res; +} + +#endif diff --git a/plugins/LilyPad/HidDevice.cpp b/plugins/LilyPad/HidDevice.cpp index 55df5d34cb..ea36167c73 100644 --- a/plugins/LilyPad/HidDevice.cpp +++ b/plugins/LilyPad/HidDevice.cpp @@ -1,117 +1,117 @@ -#include "Global.h" -#include "HidDevice.h" -#include - -// Tons of annoying junk to avoid the hid dependencies, so no one has to download the -// DDK to compile. -#define HIDP_STATUS_SUCCESS 0x110000 - -struct HIDD_ATTRIBUTES { - ULONG Size; - - USHORT VendorID; - USHORT ProductID; - USHORT VersionNumber; -}; -struct HIDP_PREPARSED_DATA; - -typedef BOOLEAN (__stdcall *_HidD_GetAttributes) (HANDLE HidDeviceObject, HIDD_ATTRIBUTES *Attributes); -typedef void (__stdcall *_HidD_GetHidGuid) (GUID* HidGuid); -typedef BOOLEAN (__stdcall *_HidD_GetPreparsedData) (HANDLE HidDeviceObject, HIDP_PREPARSED_DATA **PreparsedData); -typedef NTSTATUS (__stdcall *_HidP_GetCaps) (HIDP_PREPARSED_DATA* PreparsedData, HIDP_CAPS *caps); -typedef BOOLEAN (__stdcall *_HidD_FreePreparsedData) (HIDP_PREPARSED_DATA *PreparsedData); -typedef BOOLEAN (__stdcall *_HidD_GetFeature) (HANDLE HidDeviceObject, PVOID ReportBuffer, ULONG ReportBufferLength); -typedef BOOLEAN (__stdcall *_HidD_SetFeature) (HANDLE HidDeviceObject, PVOID ReportBuffer, ULONG ReportBufferLength); - -GUID GUID_DEVINTERFACE_HID; -_HidD_GetHidGuid pHidD_GetHidGuid; -_HidD_GetAttributes pHidD_GetAttributes; -_HidD_GetPreparsedData pHidD_GetPreparsedData; -_HidP_GetCaps pHidP_GetCaps; -_HidD_FreePreparsedData pHidD_FreePreparsedData; -_HidD_GetFeature pHidD_GetFeature; -_HidD_SetFeature pHidD_SetFeature; - -HMODULE hModHid = 0; - -int InitHid() { - if (hModHid) { - return 1; - } - hModHid = LoadLibraryA("hid.dll"); - if (hModHid) { - if ((pHidD_GetHidGuid = (_HidD_GetHidGuid) GetProcAddress(hModHid, "HidD_GetHidGuid")) && - (pHidD_GetAttributes = (_HidD_GetAttributes) GetProcAddress(hModHid, "HidD_GetAttributes")) && - (pHidD_GetPreparsedData = (_HidD_GetPreparsedData) GetProcAddress(hModHid, "HidD_GetPreparsedData")) && - (pHidP_GetCaps = (_HidP_GetCaps) GetProcAddress(hModHid, "HidP_GetCaps")) && - (pHidD_FreePreparsedData = (_HidD_FreePreparsedData) GetProcAddress(hModHid, "HidD_FreePreparsedData")) && - (pHidD_GetFeature = (_HidD_GetFeature) GetProcAddress(hModHid, "HidD_GetFeature")) && - (pHidD_SetFeature = (_HidD_SetFeature) GetProcAddress(hModHid, "HidD_SetFeature"))) { - pHidD_GetHidGuid(&GUID_DEVINTERFACE_HID); - return 1; - } - UninitHid(); - } - return 0; -} - -int FindHids(HidDeviceInfo **foundDevs, int vid, int pid) { - if (!InitHid()) return 0; - int numFoundDevs = 0; - *foundDevs = 0; - HDEVINFO hdev = SetupDiGetClassDevsW(&GUID_DEVINTERFACE_HID, 0, 0, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); - if (hdev != INVALID_HANDLE_VALUE) { - SP_DEVICE_INTERFACE_DATA devInterfaceData; - devInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); - for (int i=0; SetupDiEnumDeviceInterfaces(hdev, 0, &GUID_DEVINTERFACE_HID, i, &devInterfaceData); i++) { - - DWORD size = 0; - SetupDiGetDeviceInterfaceDetail(hdev, &devInterfaceData, 0, 0, &size, 0); - if (GetLastError() != ERROR_INSUFFICIENT_BUFFER || !size) continue; - SP_DEVICE_INTERFACE_DETAIL_DATA *devInterfaceDetails = (SP_DEVICE_INTERFACE_DETAIL_DATA *) malloc(size); - if (!devInterfaceDetails) continue; - - devInterfaceDetails->cbSize = sizeof(*devInterfaceDetails); - SP_DEVINFO_DATA devInfoData; - devInfoData.cbSize = sizeof(SP_DEVINFO_DATA); - - if (!SetupDiGetDeviceInterfaceDetail(hdev, &devInterfaceData, devInterfaceDetails, size, &size, &devInfoData)) continue; - - HANDLE hfile = CreateFileW(devInterfaceDetails->DevicePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0); - if (hfile != INVALID_HANDLE_VALUE) { - HIDD_ATTRIBUTES attributes; - attributes.Size = sizeof(attributes); - if (pHidD_GetAttributes(hfile, &attributes)) { - if (attributes.VendorID == vid && attributes.ProductID == pid) { - HIDP_PREPARSED_DATA *pData; - HIDP_CAPS caps; - if (pHidD_GetPreparsedData(hfile, &pData)) { - if (HIDP_STATUS_SUCCESS == pHidP_GetCaps(pData, &caps)) { - if (numFoundDevs % 32 == 0) { - *foundDevs = (HidDeviceInfo*) realloc(*foundDevs, sizeof(HidDeviceInfo) * (32 + numFoundDevs)); - } - HidDeviceInfo *dev = &foundDevs[0][numFoundDevs++]; - dev->caps = caps; - dev->vid = attributes.VendorID; - dev->pid = attributes.ProductID; - dev->path = wcsdup(devInterfaceDetails->DevicePath); - } - pHidD_FreePreparsedData(pData); - } - } - } - CloseHandle(hfile); - } - free(devInterfaceDetails); - } - SetupDiDestroyDeviceInfoList(hdev); - } - return numFoundDevs; -} - -void UninitHid() { - if (hModHid) { - FreeLibrary(hModHid); - hModHid = 0; - } -} +#include "Global.h" +#include "HidDevice.h" +#include + +// Tons of annoying junk to avoid the hid dependencies, so no one has to download the +// DDK to compile. +#define HIDP_STATUS_SUCCESS 0x110000 + +struct HIDD_ATTRIBUTES { + ULONG Size; + + USHORT VendorID; + USHORT ProductID; + USHORT VersionNumber; +}; +struct HIDP_PREPARSED_DATA; + +typedef BOOLEAN (__stdcall *_HidD_GetAttributes) (HANDLE HidDeviceObject, HIDD_ATTRIBUTES *Attributes); +typedef void (__stdcall *_HidD_GetHidGuid) (GUID* HidGuid); +typedef BOOLEAN (__stdcall *_HidD_GetPreparsedData) (HANDLE HidDeviceObject, HIDP_PREPARSED_DATA **PreparsedData); +typedef NTSTATUS (__stdcall *_HidP_GetCaps) (HIDP_PREPARSED_DATA* PreparsedData, HIDP_CAPS *caps); +typedef BOOLEAN (__stdcall *_HidD_FreePreparsedData) (HIDP_PREPARSED_DATA *PreparsedData); +typedef BOOLEAN (__stdcall *_HidD_GetFeature) (HANDLE HidDeviceObject, PVOID ReportBuffer, ULONG ReportBufferLength); +typedef BOOLEAN (__stdcall *_HidD_SetFeature) (HANDLE HidDeviceObject, PVOID ReportBuffer, ULONG ReportBufferLength); + +GUID GUID_DEVINTERFACE_HID; +_HidD_GetHidGuid pHidD_GetHidGuid; +_HidD_GetAttributes pHidD_GetAttributes; +_HidD_GetPreparsedData pHidD_GetPreparsedData; +_HidP_GetCaps pHidP_GetCaps; +_HidD_FreePreparsedData pHidD_FreePreparsedData; +_HidD_GetFeature pHidD_GetFeature; +_HidD_SetFeature pHidD_SetFeature; + +HMODULE hModHid = 0; + +int InitHid() { + if (hModHid) { + return 1; + } + hModHid = LoadLibraryA("hid.dll"); + if (hModHid) { + if ((pHidD_GetHidGuid = (_HidD_GetHidGuid) GetProcAddress(hModHid, "HidD_GetHidGuid")) && + (pHidD_GetAttributes = (_HidD_GetAttributes) GetProcAddress(hModHid, "HidD_GetAttributes")) && + (pHidD_GetPreparsedData = (_HidD_GetPreparsedData) GetProcAddress(hModHid, "HidD_GetPreparsedData")) && + (pHidP_GetCaps = (_HidP_GetCaps) GetProcAddress(hModHid, "HidP_GetCaps")) && + (pHidD_FreePreparsedData = (_HidD_FreePreparsedData) GetProcAddress(hModHid, "HidD_FreePreparsedData")) && + (pHidD_GetFeature = (_HidD_GetFeature) GetProcAddress(hModHid, "HidD_GetFeature")) && + (pHidD_SetFeature = (_HidD_SetFeature) GetProcAddress(hModHid, "HidD_SetFeature"))) { + pHidD_GetHidGuid(&GUID_DEVINTERFACE_HID); + return 1; + } + UninitHid(); + } + return 0; +} + +int FindHids(HidDeviceInfo **foundDevs, int vid, int pid) { + if (!InitHid()) return 0; + int numFoundDevs = 0; + *foundDevs = 0; + HDEVINFO hdev = SetupDiGetClassDevsW(&GUID_DEVINTERFACE_HID, 0, 0, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); + if (hdev != INVALID_HANDLE_VALUE) { + SP_DEVICE_INTERFACE_DATA devInterfaceData; + devInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); + for (int i=0; SetupDiEnumDeviceInterfaces(hdev, 0, &GUID_DEVINTERFACE_HID, i, &devInterfaceData); i++) { + + DWORD size = 0; + SetupDiGetDeviceInterfaceDetail(hdev, &devInterfaceData, 0, 0, &size, 0); + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER || !size) continue; + SP_DEVICE_INTERFACE_DETAIL_DATA *devInterfaceDetails = (SP_DEVICE_INTERFACE_DETAIL_DATA *) malloc(size); + if (!devInterfaceDetails) continue; + + devInterfaceDetails->cbSize = sizeof(*devInterfaceDetails); + SP_DEVINFO_DATA devInfoData; + devInfoData.cbSize = sizeof(SP_DEVINFO_DATA); + + if (!SetupDiGetDeviceInterfaceDetail(hdev, &devInterfaceData, devInterfaceDetails, size, &size, &devInfoData)) continue; + + HANDLE hfile = CreateFileW(devInterfaceDetails->DevicePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0); + if (hfile != INVALID_HANDLE_VALUE) { + HIDD_ATTRIBUTES attributes; + attributes.Size = sizeof(attributes); + if (pHidD_GetAttributes(hfile, &attributes)) { + if (attributes.VendorID == vid && attributes.ProductID == pid) { + HIDP_PREPARSED_DATA *pData; + HIDP_CAPS caps; + if (pHidD_GetPreparsedData(hfile, &pData)) { + if (HIDP_STATUS_SUCCESS == pHidP_GetCaps(pData, &caps)) { + if (numFoundDevs % 32 == 0) { + *foundDevs = (HidDeviceInfo*) realloc(*foundDevs, sizeof(HidDeviceInfo) * (32 + numFoundDevs)); + } + HidDeviceInfo *dev = &foundDevs[0][numFoundDevs++]; + dev->caps = caps; + dev->vid = attributes.VendorID; + dev->pid = attributes.ProductID; + dev->path = wcsdup(devInterfaceDetails->DevicePath); + } + pHidD_FreePreparsedData(pData); + } + } + } + CloseHandle(hfile); + } + free(devInterfaceDetails); + } + SetupDiDestroyDeviceInfoList(hdev); + } + return numFoundDevs; +} + +void UninitHid() { + if (hModHid) { + FreeLibrary(hModHid); + hModHid = 0; + } +} diff --git a/plugins/LilyPad/HidDevice.h b/plugins/LilyPad/HidDevice.h index 376d1694c8..78bb63380b 100644 --- a/plugins/LilyPad/HidDevice.h +++ b/plugins/LilyPad/HidDevice.h @@ -1,40 +1,40 @@ -#ifndef HID_DEVICE_H -#define HID_DEVICE_H - -int InitHid(); - -typedef USHORT USAGE; -struct HIDP_CAPS { - USAGE Usage; - USAGE UsagePage; - USHORT InputReportByteLength; - USHORT OutputReportByteLength; - USHORT FeatureReportByteLength; - USHORT Reserved[17]; - - USHORT NumberLinkCollectionNodes; - - USHORT NumberInputButtonCaps; - USHORT NumberInputValueCaps; - USHORT NumberInputDataIndices; - - USHORT NumberOutputButtonCaps; - USHORT NumberOutputValueCaps; - USHORT NumberOutputDataIndices; - - USHORT NumberFeatureButtonCaps; - USHORT NumberFeatureValueCaps; - USHORT NumberFeatureDataIndices; -}; - -struct HidDeviceInfo { - HIDP_CAPS caps; - wchar_t *path; - unsigned short vid; - unsigned short pid; -}; - -void UninitHid(); -int FindHids(HidDeviceInfo **foundDevs, int vid, int pid); - -#endif +#ifndef HID_DEVICE_H +#define HID_DEVICE_H + +int InitHid(); + +typedef USHORT USAGE; +struct HIDP_CAPS { + USAGE Usage; + USAGE UsagePage; + USHORT InputReportByteLength; + USHORT OutputReportByteLength; + USHORT FeatureReportByteLength; + USHORT Reserved[17]; + + USHORT NumberLinkCollectionNodes; + + USHORT NumberInputButtonCaps; + USHORT NumberInputValueCaps; + USHORT NumberInputDataIndices; + + USHORT NumberOutputButtonCaps; + USHORT NumberOutputValueCaps; + USHORT NumberOutputDataIndices; + + USHORT NumberFeatureButtonCaps; + USHORT NumberFeatureValueCaps; + USHORT NumberFeatureDataIndices; +}; + +struct HidDeviceInfo { + HIDP_CAPS caps; + wchar_t *path; + unsigned short vid; + unsigned short pid; +}; + +void UninitHid(); +int FindHids(HidDeviceInfo **foundDevs, int vid, int pid); + +#endif diff --git a/plugins/LilyPad/InputManager.cpp b/plugins/LilyPad/InputManager.cpp index 7d14385e5c..48a5b4610d 100644 --- a/plugins/LilyPad/InputManager.cpp +++ b/plugins/LilyPad/InputManager.cpp @@ -1,600 +1,600 @@ -#include "Global.h" -#include "InputManager.h" -#include "KeyboardQueue.h" - -InputDeviceManager *dm = 0; - -InputDeviceManager::InputDeviceManager() { - memset(this, 0, sizeof(*this)); -} - -void InputDeviceManager::ClearDevices() { - for (int i=0; iapi = api; - type = d; - this->displayName = wcsdup(displayName); - if (instanceID) - this->instanceID = wcsdup(instanceID); - else - this->instanceID = wcsdup(displayName); - this->productID = 0; - if (productID) - this->productID = wcsdup(productID); - active = 0; - attached = 1; - enabled = 0; - - hWndProc = 0; - - virtualControls = 0; - numVirtualControls = 0; - virtualControlState = 0; - oldVirtualControlState = 0; - - physicalControls = 0; - numPhysicalControls = 0; - physicalControlState = 0; - - ffEffectTypes = 0; - numFFEffectTypes = 0; - ffAxes = 0; - numFFAxes = 0; -} - -void Device::FreeState() { - if (virtualControlState) free(virtualControlState); - virtualControlState = 0; - oldVirtualControlState = 0; - physicalControlState = 0; -} - -Device::~Device() { - Deactivate(); - // Generally called by deactivate, but just in case... - FreeState(); - int i; - for (int port=0; port<2; port++) { - for (int slot=0; slot<4; slot++) { - free(pads[port][slot].bindings); - for (i=0; i=0; i--) { - if (physicalControls[i].name) free(physicalControls[i].name); - } - free(physicalControls); - - free(displayName); - free(instanceID); - free(productID); - if (ffAxes) { - for (i=0; iaxes = (AxisEffectInfo*) realloc(b->axes, sizeof(AxisEffectInfo) * (numFFAxes)); - memset(b->axes + (numFFAxes-1), 0, sizeof(AxisEffectInfo)); - bindingsExist = 1; - } - } - } - // Generally the case when not loading a binding file. - if (!bindingsExist) { - int i = numFFAxes-1; - ForceFeedbackAxis temp = ffAxes[i]; - while (i && temp.id < ffAxes[i-1].id) { - ffAxes[i] = ffAxes[i-1]; - i--; - } - ffAxes[i] = temp; - } -} - -void Device::AllocState() { - FreeState(); - virtualControlState = (int*) calloc(numVirtualControls + numVirtualControls + numPhysicalControls, sizeof(int)); - oldVirtualControlState = virtualControlState + numVirtualControls; - physicalControlState = oldVirtualControlState + numVirtualControls; -} - -void Device::FlipState() { - memcpy(oldVirtualControlState, virtualControlState, sizeof(int)*numVirtualControls); -} - -void Device::PostRead() { - FlipState(); -} - -void Device::CalcVirtualState() { - for (int i=0; ibaseVirtualControlIndex; - int val = physicalControlState[i]; - if (c->type & BUTTON) { - virtualControlState[index] = val; - // DirectInput keyboard events only. - if (this->api == DI && this->type == KEYBOARD) { - if (!(virtualControlState[index]>>15) != !(oldVirtualControlState[index]>>15) && c->vkey) { - // Check for alt-F4 to avoid toggling skip mode incorrectly. - if (c->vkey == VK_F4) { - int i; - for (i=0; i>15)) event = KEYRELEASE; - QueueKeyEvent(c->vkey, event); - } - } - } - else if (c->type & ABSAXIS) { - virtualControlState[index] = (val + FULLY_DOWN)/2; - // Positive. Overkill. - virtualControlState[index+1] = (val & ~(val>>31)); - // Negative - virtualControlState[index+2] = (-val & (val>>31)); - } - else if (c->type & RELAXIS) { - int delta = val - oldVirtualControlState[index]; - virtualControlState[index] = val; - // Positive - virtualControlState[index+1] = (delta & ~(delta>>31)); - // Negative - virtualControlState[index+2] = (-delta & (delta>>31)); - } - else if (c->type & POV) { - virtualControlState[index] = val; - int iSouth = 0; - int iEast = 0; - if ((unsigned int)val <= 37000) { - double angle = val * (3.141592653589793/18000.0); - double East = sin(angle); - double South = -cos(angle); - // Normalize so greatest direction is 1. - double mul = FULLY_DOWN / max(fabs(South), fabs(East)); - iEast = (int) floor(East * mul + 0.5); - iSouth = (int) floor(South * mul + 0.5); - } - // N - virtualControlState[index+1] = (-iSouth & (iSouth>>31)); - // S - virtualControlState[index+3] = (iSouth & ~(iSouth>>31)); - // E - virtualControlState[index+2] = (iEast & ~(iEast>>31)); - // W - virtualControlState[index+4] = (-iEast & (iEast>>31)); - } - } -} - -VirtualControl *Device::GetVirtualControl(unsigned int uid) { - for (int i=0; iuid = uid; - c->physicalControlIndex = physicalControlIndex; - - numVirtualControls++; - return c; -} - -PhysicalControl *Device::AddPhysicalControl(ControlType type, unsigned short id, unsigned short vkey, const wchar_t *name) { - // Not really necessary, as always call AllocState when activated, but doesn't hurt. - FreeState(); - - if (numPhysicalControls % 16 == 0) { - physicalControls = (PhysicalControl*) realloc(physicalControls, sizeof(PhysicalControl)*(numPhysicalControls+16)); - } - PhysicalControl *control = physicalControls + numPhysicalControls; - - memset(control, 0, sizeof(PhysicalControl)); - control->type = type; - control->id = id; - if (name) control->name = wcsdup(name); - control->baseVirtualControlIndex = numVirtualControls; - unsigned int uid = id | (type<<16); - if (type & BUTTON) { - AddVirtualControl(uid, numPhysicalControls); - control->vkey = vkey; - } - else if (type & AXIS) { - AddVirtualControl(uid | UID_AXIS, numPhysicalControls); - AddVirtualControl(uid | UID_AXIS_POS, numPhysicalControls); - AddVirtualControl(uid | UID_AXIS_NEG, numPhysicalControls); - } - else if (type & POV) { - AddVirtualControl(uid | UID_POV, numPhysicalControls); - AddVirtualControl(uid | UID_POV_N, numPhysicalControls); - AddVirtualControl(uid | UID_POV_E, numPhysicalControls); - AddVirtualControl(uid | UID_POV_S, numPhysicalControls); - AddVirtualControl(uid | UID_POV_W, numPhysicalControls); - } - numPhysicalControls++; - return control; -} - -void Device::SetEffects(unsigned char port, unsigned int slot, unsigned char motor, unsigned char force) { - for (int i=0; imotor == motor) { - SetEffect(binding, force); - } - } -} - -wchar_t *GetDefaultControlName(unsigned short id, int type) { - static wchar_t name[20]; - if (type & BUTTON) { - wsprintfW(name, L"Button %i", id); - } - else if (type & AXIS) { - wsprintfW(name, L"Axis %i", id); - } - else if (type & POV) { - wsprintfW(name, L"POV %i", id); - } - else return L"Unknown"; - return name; -} - -wchar_t *Device::GetVirtualControlName(VirtualControl *control) { - static wchar_t temp[100]; - wchar_t *baseName = 0; - if (control->physicalControlIndex >= 0) { - baseName = physicalControls[control->physicalControlIndex].name; - if (!baseName) baseName = GetPhysicalControlName(&physicalControls[control->physicalControlIndex]); - } - unsigned int uid = control->uid; - if (!baseName) { - baseName = GetDefaultControlName(uid & 0xFFFF, (uid >> 16)& 0x1F); - } - uid &= 0xFF000000; - int len = (int)wcslen(baseName); - if (len > 99) len = 99; - memcpy(temp, baseName, len*sizeof(wchar_t)); - temp[len] = 0; - if (uid) { - if (len > 95) len = 95; - wchar_t *out = temp+len; - if (uid == UID_AXIS_POS) { - wcscpy(out, L" +"); - } - else if (uid == UID_AXIS_NEG) { - wcscpy(out, L" -"); - } - else if (uid == UID_POV_N) { - wcscpy(out, L" N"); - } - else if (uid == UID_POV_E) { - wcscpy(out, L" E"); - } - else if (uid == UID_POV_S) { - wcscpy(out, L" S"); - } - else if (uid == UID_POV_W) { - wcscpy(out, L" W"); - } - } - return temp; -} - -wchar_t *Device::GetPhysicalControlName(PhysicalControl *control) { - if (control->name) return control->name; - return GetDefaultControlName(control->id, control->type); -} - -void InputDeviceManager::AddDevice(Device *d) { - devices = (Device**) realloc(devices, sizeof(Device*) * (numDevices+1)); - devices[numDevices++] = d; -} - -void InputDeviceManager::Update(InitInfo *info) { - for (int i=0; ienabled) { - if (!devices[i]->active) { - if (!devices[i]->Activate(info) || !devices[i]->Update()) continue; - devices[i]->CalcVirtualState(); - devices[i]->PostRead(); - } - if (devices[i]->Update()) - devices[i]->CalcVirtualState(); - } - } -} - -void InputDeviceManager::PostRead() { - for (int i=0; iactive) - devices[i]->PostRead(); - } -} - -Device *InputDeviceManager::GetActiveDevice(InitInfo *info, unsigned int *uid, int *index, int *value) { - int i, j; - Update(info); - int bestDiff = FULLY_DOWN/2; - Device *bestDevice = 0; - for (i=0; iactive) { - for (j=0; jnumVirtualControls; j++) { - if (devices[i]->virtualControlState[j] == devices[i]->oldVirtualControlState[j]) continue; - if (devices[i]->virtualControls[j].uid & UID_POV) continue; - // Fix for releasing button used to click on bind button - if (!((devices[i]->virtualControls[j].uid>>16) & (POV|RELAXIS|ABSAXIS))) { - if (abs(devices[i]->oldVirtualControlState[j]) > abs(devices[i]->virtualControlState[j])) { - devices[i]->oldVirtualControlState[j] = 0; - } - } - int diff = abs(devices[i]->virtualControlState[j] - devices[i]->oldVirtualControlState[j]); - // Make it require a bit more work to bind relative axes. - if (((devices[i]->virtualControls[j].uid>>16) & 0xFF) == RELAXIS) { - diff = diff/4+1; - } - // Less pressure needed to bind DS3 buttons. - if (devices[i]->api == DS3 && (((devices[i]->virtualControls[j].uid>>16) & 0xFF) & BUTTON)) { - diff *= 4; - } - if (diff > bestDiff) { - if (devices[i]->virtualControls[j].uid & UID_AXIS) { - if ((((devices[i]->virtualControls[j].uid>>16)&0xFF) != ABSAXIS)) continue; - // Very picky when binding entire axes. Prefer binding half-axes. - if (!((devices[i]->oldVirtualControlState[j] < FULLY_DOWN/32 && devices[i]->virtualControlState[j] > FULLY_DOWN/8) || - (devices[i]->oldVirtualControlState[j] > 31*FULLY_DOWN/32 && devices[i]->virtualControlState[j] < 7*FULLY_DOWN/8))) { - continue; - } - devices[i]->virtualControls[j].uid = devices[i]->virtualControls[j].uid; - } - else if ((((devices[i]->virtualControls[j].uid>>16)&0xFF) == ABSAXIS)) { - if (devices[i]->oldVirtualControlState[j] > 15*FULLY_DOWN/16) - continue; - } - bestDiff = diff; - *uid = devices[i]->virtualControls[j].uid; - *index = j; - bestDevice = devices[i]; - if (value) { - if ((devices[i]->virtualControls[j].uid>>16)& RELAXIS) { - *value = devices[i]->virtualControlState[j] - devices[i]->oldVirtualControlState[j]; - } - else { - *value = devices[i]->virtualControlState[j]; - } - } - } - } - } - } - // Don't call when binding. - // PostRead(); - return bestDevice; -} - -void InputDeviceManager::ReleaseInput() { - for (int i=0; iactive) devices[i]->Deactivate(); - } -} - -void InputDeviceManager::EnableDevices(DeviceType type, DeviceAPI api) { - for (int i=0; iapi == api && devices[i]->type == type) { - EnableDevice(i); - } - } -} - -void InputDeviceManager::DisableAllDevices() { - for (int i=0; ienabled = 0; - if (devices[index]->active) { - devices[index]->Deactivate(); - } -} - -ForceFeedbackEffectType *Device::GetForcefeedbackEffect(wchar_t *id) { - for (int i=0; ipads[port][slot].numBindings + old->pads[port][slot].numFFBindings) { - // Means that there are bindings. - oldMatches[i] = -1; - } - } - } - } - // Loops through ids looking for match, from most specific to most general. - for (int id=0; id<3; id++) { - for (i=0; i= 0) continue; - for (j=0; j= 0) { - continue; - } - wchar_t *id1 = devices[j]->IDs[id]; - wchar_t *id2 = oldDevices[i]->IDs[id]; - if (!id1 || !id2) { - continue; - } - if (!wcsicmp(id1, id2)) { - matches[j] = i; - oldMatches[i] = j; - break; - } - } - } - } - - for (i=0; iapi, old->type, old->displayName, old->instanceID, old->productID); - dev->attached = 0; - AddDevice(dev); - for (j=0; jnumVirtualControls; j++) { - VirtualControl *c = old->virtualControls+j; - dev->AddVirtualControl(c->uid, -1); - } - for (j=0; jnumFFEffectTypes; j++) { - ForceFeedbackEffectType * effect = old->ffEffectTypes + j; - dev->AddFFEffectType(effect->displayName, effect->effectID, effect->type); - } - for (j=0; jnumFFAxes; j++) { - ForceFeedbackAxis * axis = old->ffAxes + j; - dev->AddFFAxis(axis->displayName, axis->id); - } - // Just steal the old bindings directly when there's no matching device. - // Indices will be the same. - memcpy(dev->pads, old->pads, sizeof(old->pads)); - memset(old->pads, 0, sizeof(old->pads)); - } - else { - dev = devices[oldMatches[i]]; - for (port=0; port<2; port++) { - for (slot=0; slot<4; slot++) { - if (old->pads[port][slot].numBindings) { - dev->pads[port][slot].bindings = (Binding*) malloc(old->pads[port][slot].numBindings * sizeof(Binding)); - for (int j=0; jpads[port][slot].numBindings; j++) { - Binding *bo = old->pads[port][slot].bindings + j; - Binding *bn = dev->pads[port][slot].bindings + dev->pads[port][slot].numBindings; - VirtualControl *cn = dev->GetVirtualControl(old->virtualControls[bo->controlIndex].uid); - if (cn) { - *bn = *bo; - bn->controlIndex = cn - dev->virtualControls; - dev->pads[port][slot].numBindings++; - } - } - } - if (old->pads[port][slot].numFFBindings) { - dev->pads[port][slot].ffBindings = (ForceFeedbackBinding*) malloc(old->pads[port][slot].numFFBindings * sizeof(ForceFeedbackBinding)); - for (int j=0; jpads[port][slot].numFFBindings; j++) { - ForceFeedbackBinding *bo = old->pads[port][slot].ffBindings + j; - ForceFeedbackBinding *bn = dev->pads[port][slot].ffBindings + dev->pads[port][slot].numFFBindings; - ForceFeedbackEffectType *en = dev->GetForcefeedbackEffect(old->ffEffectTypes[bo->effectIndex].effectID); - if (en) { - *bn = *bo; - bn->effectIndex = en - dev->ffEffectTypes; - bn->axes = (AxisEffectInfo*)calloc(dev->numFFAxes, sizeof(AxisEffectInfo)); - for (int k=0; knumFFAxes; k++) { - ForceFeedbackAxis *newAxis = dev->GetForceFeedbackAxis(old->ffAxes[k].id); - if (newAxis) { - bn->axes[newAxis - dev->ffAxes] = bo->axes[k]; - } - } - dev->pads[port][slot].numFFBindings++; - } - } - } - } - } - } - } - free(oldMatches); - free(matches); -} - -void InputDeviceManager::SetEffect(unsigned char port, unsigned int slot, unsigned char motor, unsigned char force) { - for (int i=0; ienabled && dev->numFFEffectTypes) { - dev->SetEffects(port, slot, motor, force); - } - } -} - +#include "Global.h" +#include "InputManager.h" +#include "KeyboardQueue.h" + +InputDeviceManager *dm = 0; + +InputDeviceManager::InputDeviceManager() { + memset(this, 0, sizeof(*this)); +} + +void InputDeviceManager::ClearDevices() { + for (int i=0; iapi = api; + type = d; + this->displayName = wcsdup(displayName); + if (instanceID) + this->instanceID = wcsdup(instanceID); + else + this->instanceID = wcsdup(displayName); + this->productID = 0; + if (productID) + this->productID = wcsdup(productID); + active = 0; + attached = 1; + enabled = 0; + + hWndProc = 0; + + virtualControls = 0; + numVirtualControls = 0; + virtualControlState = 0; + oldVirtualControlState = 0; + + physicalControls = 0; + numPhysicalControls = 0; + physicalControlState = 0; + + ffEffectTypes = 0; + numFFEffectTypes = 0; + ffAxes = 0; + numFFAxes = 0; +} + +void Device::FreeState() { + if (virtualControlState) free(virtualControlState); + virtualControlState = 0; + oldVirtualControlState = 0; + physicalControlState = 0; +} + +Device::~Device() { + Deactivate(); + // Generally called by deactivate, but just in case... + FreeState(); + int i; + for (int port=0; port<2; port++) { + for (int slot=0; slot<4; slot++) { + free(pads[port][slot].bindings); + for (i=0; i=0; i--) { + if (physicalControls[i].name) free(physicalControls[i].name); + } + free(physicalControls); + + free(displayName); + free(instanceID); + free(productID); + if (ffAxes) { + for (i=0; iaxes = (AxisEffectInfo*) realloc(b->axes, sizeof(AxisEffectInfo) * (numFFAxes)); + memset(b->axes + (numFFAxes-1), 0, sizeof(AxisEffectInfo)); + bindingsExist = 1; + } + } + } + // Generally the case when not loading a binding file. + if (!bindingsExist) { + int i = numFFAxes-1; + ForceFeedbackAxis temp = ffAxes[i]; + while (i && temp.id < ffAxes[i-1].id) { + ffAxes[i] = ffAxes[i-1]; + i--; + } + ffAxes[i] = temp; + } +} + +void Device::AllocState() { + FreeState(); + virtualControlState = (int*) calloc(numVirtualControls + numVirtualControls + numPhysicalControls, sizeof(int)); + oldVirtualControlState = virtualControlState + numVirtualControls; + physicalControlState = oldVirtualControlState + numVirtualControls; +} + +void Device::FlipState() { + memcpy(oldVirtualControlState, virtualControlState, sizeof(int)*numVirtualControls); +} + +void Device::PostRead() { + FlipState(); +} + +void Device::CalcVirtualState() { + for (int i=0; ibaseVirtualControlIndex; + int val = physicalControlState[i]; + if (c->type & BUTTON) { + virtualControlState[index] = val; + // DirectInput keyboard events only. + if (this->api == DI && this->type == KEYBOARD) { + if (!(virtualControlState[index]>>15) != !(oldVirtualControlState[index]>>15) && c->vkey) { + // Check for alt-F4 to avoid toggling skip mode incorrectly. + if (c->vkey == VK_F4) { + int i; + for (i=0; i>15)) event = KEYRELEASE; + QueueKeyEvent(c->vkey, event); + } + } + } + else if (c->type & ABSAXIS) { + virtualControlState[index] = (val + FULLY_DOWN)/2; + // Positive. Overkill. + virtualControlState[index+1] = (val & ~(val>>31)); + // Negative + virtualControlState[index+2] = (-val & (val>>31)); + } + else if (c->type & RELAXIS) { + int delta = val - oldVirtualControlState[index]; + virtualControlState[index] = val; + // Positive + virtualControlState[index+1] = (delta & ~(delta>>31)); + // Negative + virtualControlState[index+2] = (-delta & (delta>>31)); + } + else if (c->type & POV) { + virtualControlState[index] = val; + int iSouth = 0; + int iEast = 0; + if ((unsigned int)val <= 37000) { + double angle = val * (3.141592653589793/18000.0); + double East = sin(angle); + double South = -cos(angle); + // Normalize so greatest direction is 1. + double mul = FULLY_DOWN / max(fabs(South), fabs(East)); + iEast = (int) floor(East * mul + 0.5); + iSouth = (int) floor(South * mul + 0.5); + } + // N + virtualControlState[index+1] = (-iSouth & (iSouth>>31)); + // S + virtualControlState[index+3] = (iSouth & ~(iSouth>>31)); + // E + virtualControlState[index+2] = (iEast & ~(iEast>>31)); + // W + virtualControlState[index+4] = (-iEast & (iEast>>31)); + } + } +} + +VirtualControl *Device::GetVirtualControl(unsigned int uid) { + for (int i=0; iuid = uid; + c->physicalControlIndex = physicalControlIndex; + + numVirtualControls++; + return c; +} + +PhysicalControl *Device::AddPhysicalControl(ControlType type, unsigned short id, unsigned short vkey, const wchar_t *name) { + // Not really necessary, as always call AllocState when activated, but doesn't hurt. + FreeState(); + + if (numPhysicalControls % 16 == 0) { + physicalControls = (PhysicalControl*) realloc(physicalControls, sizeof(PhysicalControl)*(numPhysicalControls+16)); + } + PhysicalControl *control = physicalControls + numPhysicalControls; + + memset(control, 0, sizeof(PhysicalControl)); + control->type = type; + control->id = id; + if (name) control->name = wcsdup(name); + control->baseVirtualControlIndex = numVirtualControls; + unsigned int uid = id | (type<<16); + if (type & BUTTON) { + AddVirtualControl(uid, numPhysicalControls); + control->vkey = vkey; + } + else if (type & AXIS) { + AddVirtualControl(uid | UID_AXIS, numPhysicalControls); + AddVirtualControl(uid | UID_AXIS_POS, numPhysicalControls); + AddVirtualControl(uid | UID_AXIS_NEG, numPhysicalControls); + } + else if (type & POV) { + AddVirtualControl(uid | UID_POV, numPhysicalControls); + AddVirtualControl(uid | UID_POV_N, numPhysicalControls); + AddVirtualControl(uid | UID_POV_E, numPhysicalControls); + AddVirtualControl(uid | UID_POV_S, numPhysicalControls); + AddVirtualControl(uid | UID_POV_W, numPhysicalControls); + } + numPhysicalControls++; + return control; +} + +void Device::SetEffects(unsigned char port, unsigned int slot, unsigned char motor, unsigned char force) { + for (int i=0; imotor == motor) { + SetEffect(binding, force); + } + } +} + +wchar_t *GetDefaultControlName(unsigned short id, int type) { + static wchar_t name[20]; + if (type & BUTTON) { + wsprintfW(name, L"Button %i", id); + } + else if (type & AXIS) { + wsprintfW(name, L"Axis %i", id); + } + else if (type & POV) { + wsprintfW(name, L"POV %i", id); + } + else return L"Unknown"; + return name; +} + +wchar_t *Device::GetVirtualControlName(VirtualControl *control) { + static wchar_t temp[100]; + wchar_t *baseName = 0; + if (control->physicalControlIndex >= 0) { + baseName = physicalControls[control->physicalControlIndex].name; + if (!baseName) baseName = GetPhysicalControlName(&physicalControls[control->physicalControlIndex]); + } + unsigned int uid = control->uid; + if (!baseName) { + baseName = GetDefaultControlName(uid & 0xFFFF, (uid >> 16)& 0x1F); + } + uid &= 0xFF000000; + int len = (int)wcslen(baseName); + if (len > 99) len = 99; + memcpy(temp, baseName, len*sizeof(wchar_t)); + temp[len] = 0; + if (uid) { + if (len > 95) len = 95; + wchar_t *out = temp+len; + if (uid == UID_AXIS_POS) { + wcscpy(out, L" +"); + } + else if (uid == UID_AXIS_NEG) { + wcscpy(out, L" -"); + } + else if (uid == UID_POV_N) { + wcscpy(out, L" N"); + } + else if (uid == UID_POV_E) { + wcscpy(out, L" E"); + } + else if (uid == UID_POV_S) { + wcscpy(out, L" S"); + } + else if (uid == UID_POV_W) { + wcscpy(out, L" W"); + } + } + return temp; +} + +wchar_t *Device::GetPhysicalControlName(PhysicalControl *control) { + if (control->name) return control->name; + return GetDefaultControlName(control->id, control->type); +} + +void InputDeviceManager::AddDevice(Device *d) { + devices = (Device**) realloc(devices, sizeof(Device*) * (numDevices+1)); + devices[numDevices++] = d; +} + +void InputDeviceManager::Update(InitInfo *info) { + for (int i=0; ienabled) { + if (!devices[i]->active) { + if (!devices[i]->Activate(info) || !devices[i]->Update()) continue; + devices[i]->CalcVirtualState(); + devices[i]->PostRead(); + } + if (devices[i]->Update()) + devices[i]->CalcVirtualState(); + } + } +} + +void InputDeviceManager::PostRead() { + for (int i=0; iactive) + devices[i]->PostRead(); + } +} + +Device *InputDeviceManager::GetActiveDevice(InitInfo *info, unsigned int *uid, int *index, int *value) { + int i, j; + Update(info); + int bestDiff = FULLY_DOWN/2; + Device *bestDevice = 0; + for (i=0; iactive) { + for (j=0; jnumVirtualControls; j++) { + if (devices[i]->virtualControlState[j] == devices[i]->oldVirtualControlState[j]) continue; + if (devices[i]->virtualControls[j].uid & UID_POV) continue; + // Fix for releasing button used to click on bind button + if (!((devices[i]->virtualControls[j].uid>>16) & (POV|RELAXIS|ABSAXIS))) { + if (abs(devices[i]->oldVirtualControlState[j]) > abs(devices[i]->virtualControlState[j])) { + devices[i]->oldVirtualControlState[j] = 0; + } + } + int diff = abs(devices[i]->virtualControlState[j] - devices[i]->oldVirtualControlState[j]); + // Make it require a bit more work to bind relative axes. + if (((devices[i]->virtualControls[j].uid>>16) & 0xFF) == RELAXIS) { + diff = diff/4+1; + } + // Less pressure needed to bind DS3 buttons. + if (devices[i]->api == DS3 && (((devices[i]->virtualControls[j].uid>>16) & 0xFF) & BUTTON)) { + diff *= 4; + } + if (diff > bestDiff) { + if (devices[i]->virtualControls[j].uid & UID_AXIS) { + if ((((devices[i]->virtualControls[j].uid>>16)&0xFF) != ABSAXIS)) continue; + // Very picky when binding entire axes. Prefer binding half-axes. + if (!((devices[i]->oldVirtualControlState[j] < FULLY_DOWN/32 && devices[i]->virtualControlState[j] > FULLY_DOWN/8) || + (devices[i]->oldVirtualControlState[j] > 31*FULLY_DOWN/32 && devices[i]->virtualControlState[j] < 7*FULLY_DOWN/8))) { + continue; + } + devices[i]->virtualControls[j].uid = devices[i]->virtualControls[j].uid; + } + else if ((((devices[i]->virtualControls[j].uid>>16)&0xFF) == ABSAXIS)) { + if (devices[i]->oldVirtualControlState[j] > 15*FULLY_DOWN/16) + continue; + } + bestDiff = diff; + *uid = devices[i]->virtualControls[j].uid; + *index = j; + bestDevice = devices[i]; + if (value) { + if ((devices[i]->virtualControls[j].uid>>16)& RELAXIS) { + *value = devices[i]->virtualControlState[j] - devices[i]->oldVirtualControlState[j]; + } + else { + *value = devices[i]->virtualControlState[j]; + } + } + } + } + } + } + // Don't call when binding. + // PostRead(); + return bestDevice; +} + +void InputDeviceManager::ReleaseInput() { + for (int i=0; iactive) devices[i]->Deactivate(); + } +} + +void InputDeviceManager::EnableDevices(DeviceType type, DeviceAPI api) { + for (int i=0; iapi == api && devices[i]->type == type) { + EnableDevice(i); + } + } +} + +void InputDeviceManager::DisableAllDevices() { + for (int i=0; ienabled = 0; + if (devices[index]->active) { + devices[index]->Deactivate(); + } +} + +ForceFeedbackEffectType *Device::GetForcefeedbackEffect(wchar_t *id) { + for (int i=0; ipads[port][slot].numBindings + old->pads[port][slot].numFFBindings) { + // Means that there are bindings. + oldMatches[i] = -1; + } + } + } + } + // Loops through ids looking for match, from most specific to most general. + for (int id=0; id<3; id++) { + for (i=0; i= 0) continue; + for (j=0; j= 0) { + continue; + } + wchar_t *id1 = devices[j]->IDs[id]; + wchar_t *id2 = oldDevices[i]->IDs[id]; + if (!id1 || !id2) { + continue; + } + if (!wcsicmp(id1, id2)) { + matches[j] = i; + oldMatches[i] = j; + break; + } + } + } + } + + for (i=0; iapi, old->type, old->displayName, old->instanceID, old->productID); + dev->attached = 0; + AddDevice(dev); + for (j=0; jnumVirtualControls; j++) { + VirtualControl *c = old->virtualControls+j; + dev->AddVirtualControl(c->uid, -1); + } + for (j=0; jnumFFEffectTypes; j++) { + ForceFeedbackEffectType * effect = old->ffEffectTypes + j; + dev->AddFFEffectType(effect->displayName, effect->effectID, effect->type); + } + for (j=0; jnumFFAxes; j++) { + ForceFeedbackAxis * axis = old->ffAxes + j; + dev->AddFFAxis(axis->displayName, axis->id); + } + // Just steal the old bindings directly when there's no matching device. + // Indices will be the same. + memcpy(dev->pads, old->pads, sizeof(old->pads)); + memset(old->pads, 0, sizeof(old->pads)); + } + else { + dev = devices[oldMatches[i]]; + for (port=0; port<2; port++) { + for (slot=0; slot<4; slot++) { + if (old->pads[port][slot].numBindings) { + dev->pads[port][slot].bindings = (Binding*) malloc(old->pads[port][slot].numBindings * sizeof(Binding)); + for (int j=0; jpads[port][slot].numBindings; j++) { + Binding *bo = old->pads[port][slot].bindings + j; + Binding *bn = dev->pads[port][slot].bindings + dev->pads[port][slot].numBindings; + VirtualControl *cn = dev->GetVirtualControl(old->virtualControls[bo->controlIndex].uid); + if (cn) { + *bn = *bo; + bn->controlIndex = cn - dev->virtualControls; + dev->pads[port][slot].numBindings++; + } + } + } + if (old->pads[port][slot].numFFBindings) { + dev->pads[port][slot].ffBindings = (ForceFeedbackBinding*) malloc(old->pads[port][slot].numFFBindings * sizeof(ForceFeedbackBinding)); + for (int j=0; jpads[port][slot].numFFBindings; j++) { + ForceFeedbackBinding *bo = old->pads[port][slot].ffBindings + j; + ForceFeedbackBinding *bn = dev->pads[port][slot].ffBindings + dev->pads[port][slot].numFFBindings; + ForceFeedbackEffectType *en = dev->GetForcefeedbackEffect(old->ffEffectTypes[bo->effectIndex].effectID); + if (en) { + *bn = *bo; + bn->effectIndex = en - dev->ffEffectTypes; + bn->axes = (AxisEffectInfo*)calloc(dev->numFFAxes, sizeof(AxisEffectInfo)); + for (int k=0; knumFFAxes; k++) { + ForceFeedbackAxis *newAxis = dev->GetForceFeedbackAxis(old->ffAxes[k].id); + if (newAxis) { + bn->axes[newAxis - dev->ffAxes] = bo->axes[k]; + } + } + dev->pads[port][slot].numFFBindings++; + } + } + } + } + } + } + } + free(oldMatches); + free(matches); +} + +void InputDeviceManager::SetEffect(unsigned char port, unsigned int slot, unsigned char motor, unsigned char force) { + for (int i=0; ienabled && dev->numFFEffectTypes) { + dev->SetEffects(port, slot, motor, force); + } + } +} + diff --git a/plugins/LilyPad/InputManager.h b/plugins/LilyPad/InputManager.h index 3507d4d34f..f4a2fa8af1 100644 --- a/plugins/LilyPad/InputManager.h +++ b/plugins/LilyPad/InputManager.h @@ -1,347 +1,347 @@ -#ifndef INPUT_MANAGER_H -#define INPUT_MANAGER_H - -// Both of these are hard coded in a lot of places, so don't modify them. -// Base sensitivity means that a sensitivity of that corresponds to a factor of 1. -// Fully down means that value corresponds to a button being fully down (255). -// a value of 128 or more corresponds to that button being pressed, for binary -// values. -#define BASE_SENSITIVITY (1<<16) -#define FULLY_DOWN (1<<16) - -#define DEFAULT_DEADZONE (BASE_SENSITIVITY * 201/1000) - -/* Idea is for this file and the associated cpp file to be Windows independent. - * Still more effort than it's worth to port to Linux, however. - */ - -// Mostly match DirectInput8 values. Note that these are for physical controls. -// One physical axis maps to 3 virtual ones, and one physical POV control maps to -// 4 virtual ones. -enum ControlType { - NO_CONTROL = 0, - // Axes are ints. Relative axes are for mice, mice wheels, etc, - // and are always reported relative to their last value. - // Absolute axes range from -65536 to 65536 and are absolute positions, - // like for joysticks and pressure sensitive buttons. - RELAXIS = 1, - ABSAXIS = 2, - - // Buttons range from 0 to 65536. - PSHBTN = 4, - TGLBTN = 8, - - // POV controls are ints, values range from -1 to 36000. - // -1 means not pressed, otherwise it's an angle. - // For easy DirectInput compatibility, anything outside. - // that range is treated as -1 (Though 36000-37000 is treated - // like 0 to 1000, just in case). - POV = 16, - - // Pressure sensitive buttons. Only a different type because - // they have configurable dead zones, unlike push or toggle buttons. - PRESSURE_BTN = 32, -}; - -// Masks to determine button type. Don't need one for POV. -#define BUTTON (PSHBTN | TGLBTN | PRESSURE_BTN) -#define BINARY_BUTTON (PSHBTN | TGLBTN) -#define AXIS 3 - -struct Binding { - int controlIndex; - int command; - int sensitivity; - int deadZone; - unsigned char turbo; -}; - -#define UID_AXIS (1<<31) -#define UID_POV (1<<30) - -#define UID_AXIS_POS (1<<24) -#define UID_AXIS_NEG (2<<24) -#define UID_POV_N (3<<24) -#define UID_POV_E (4<<24) -#define UID_POV_S (5<<24) -#define UID_POV_W (6<<24) - -// One of these exists for each bindable object. -// Bindable objects consist of buttons, axis, pov controls, -// and individual axis/pov directions. Not that pov controls -// cannot actually be bound, but when trying to bind as an axis, -// all directions are assigned individually. -struct VirtualControl { - // Unique id for control, given device. Based on source control's id, - // source control type, axis/pov flags if it's a pov/axis (Rather than - // a button or a pov/axis control's individual button), and an index, - // if the control is split. - unsigned int uid; - // virtual key code. 0 if none. - int physicalControlIndex; -}; - -// Need one for each button, axis, and pov control. -// API-specific code creates the PhysicalControls and -// updates their state, standard function then populates -// the VirtualControls and queues the keyboard messages, if -// needed. -struct PhysicalControl { - // index of the first virtual control corresponding to this. - // Buttons have 1 virtual control, axes 3, and povs 5, all - // in a row. - int baseVirtualControlIndex; - ControlType type; - // id. Must be unique for control type. - // short so can be combined with other values to get - // uid for virtual controls. - unsigned short id; - unsigned short vkey; - wchar_t *name; -}; - -enum DeviceAPI { - NO_API = 0, - DI = 1, - WM = 2, - RAW = 3, - XINPUT = 4, - DS3 = 5, - // Not currently used. - LLHOOK = 6, - // Not a real API, obviously. Only used with keyboards, - // to ignore individual buttons. Wrapper itself takes care - // of ignoring bound keys. Otherwise, works normally. - IGNORE_KEYBOARD = 7, -}; - -enum DeviceType { - NO_DEVICE = 0, - KEYBOARD = 1, - MOUSE = 2, - OTHER = 3 -}; - -enum EffectType { - EFFECT_CONSTANT, - EFFECT_PERIODIC, - EFFECT_RAMP -}; - -// force range sfrom -BASE_SENSITIVITY to BASE_SENSITIVITY. -// Order matches ForceFeedbackAxis order. force of 0 means to -// ignore that axis completely. Force of 1 or -1 means to initialize -// the axis with minimum force (Possibly 0 force), if applicable. -struct AxisEffectInfo { - int force; -}; - -struct ForceFeedbackBinding { - AxisEffectInfo *axes; - int effectIndex; - unsigned char motor; -}; - -// Bindings listed by effect, so I don't have to bother with -// indexing effects. -struct ForceFeedbackEffectType { - wchar_t *displayName; - // Because I'm lazy, can only have ASCII characters and no spaces. - wchar_t *effectID; - // constant, ramp, or periodic - EffectType type; -}; - - -struct ForceFeedbackAxis { - wchar_t *displayName; - int id; -}; - -// Used both for active devices and for sets of settings for devices. -// Way things work: -// LoadSettings() will delete all device info, then load settings to get -// one set of generic devices. Then I enumerate all devices. Then I merge -// them, moving settings from the generic devices to the enumerated ones. - -struct PadBindings { - Binding *bindings; - int numBindings; - ForceFeedbackBinding *ffBindings; - int numFFBindings; -}; - -class WndProcEater; - -struct InitInfo { - // 1 when binding key to ignore. - int bindingIgnore; - // 1 when binding. - int binding; - - HWND hWndTop; - - // For config screen, need to eat button's message handling. - //HWND hWndButton; - - WndProcEater* hWndProc; -}; - - -// Mostly self-contained, but bindings are modified by config.cpp, to make -// updating the ListView simpler. -class Device { -public: - DeviceAPI api; - DeviceType type; - char active; - char attached; - // Based on input modes. - char enabled; - - // Not all devices need to subclass the windproc, but most do so might as well - // put it here... --air - WndProcEater* hWndProc; - - union { - // Allows for one loop to compare all 3 in order. - wchar_t *IDs[3]; - struct { - // Same as DisplayName, when not given. Absolutely must be unique. - // Used for loading/saving controls. If matches, all other strings - // are ignored, so must be unique. - wchar_t *instanceID; - // Not required. Used when a device's instance id changes, doesn't have to - // be unique. For devices that can only have one instance, not needed. - wchar_t *productID; - - wchar_t *displayName; - }; - }; - - PadBindings pads[2][4]; - - // Virtual controls. All basically act like pressure sensitivity buttons, with - // values between 0 and 2^16. 2^16 is fully down, 0 is up. Larger values - // are allowed, but *only* for absolute axes (Which don't support the flip checkbox). - // Each control on a device must have a unique id, used for binding. - VirtualControl *virtualControls; - int numVirtualControls; - - int *virtualControlState; - int *oldVirtualControlState; - - PhysicalControl *physicalControls; - int numPhysicalControls; - int *physicalControlState; - - ForceFeedbackEffectType *ffEffectTypes; - int numFFEffectTypes; - ForceFeedbackAxis *ffAxes; - int numFFAxes; - void AddFFAxis(const wchar_t *displayName, int id); - void AddFFEffectType(const wchar_t *displayName, const wchar_t *effectID, EffectType type); - - Device(DeviceAPI, DeviceType, const wchar_t *displayName, const wchar_t *instanceID = 0, wchar_t *deviceID = 0); - virtual ~Device(); - - // Allocates memory for old and new state, sets everything to 0. - // all old states are in one array, buttons, axes, and then POVs. - // start of each section is int aligned. This makes it DirectInput - // compatible. - void AllocState(); - - // Doesn't actually flip. Copies current state to old state. - void FlipState(); - - // Frees state variables. - void FreeState(); - - ForceFeedbackEffectType *GetForcefeedbackEffect(wchar_t *id); - ForceFeedbackAxis *GetForceFeedbackAxis(int id); - - VirtualControl *GetVirtualControl(unsigned int uid); - - PhysicalControl *AddPhysicalControl(ControlType type, unsigned short id, unsigned short vkey, const wchar_t *name = 0); - VirtualControl *AddVirtualControl(unsigned int uid, int physicalControlIndex); - - virtual wchar_t *GetVirtualControlName(VirtualControl *c); - virtual wchar_t *GetPhysicalControlName(PhysicalControl *c); - - void CalcVirtualState(); - - virtual int Activate(InitInfo *args) { - return 0; - } - - inline virtual void Deactivate() { - FreeState(); - active = 0; - } - - // Default update proc. All that's needed for post-based APIs. - inline virtual int Update() { - return active; - } - - // force is from -FULLY_DOWN to FULLY_DOWN. - // Either function can be overridden. Second one by default calls the first - // for every bound effect that's affected. - - // Note: Only used externally for binding, so if override the other one, can assume - // all other forces are currently 0. - inline virtual void SetEffect(ForceFeedbackBinding *binding, unsigned char force) {} - inline virtual void SetEffects(unsigned char port, unsigned int slot, unsigned char motor, unsigned char force); - - // Called after reading. Basically calls FlipState(). - // Some device types (Those that don't incrementally update) - // could call FlipState elsewhere, but this makes it simpler to ignore - // while binding. - virtual void PostRead(); -}; - -class InputDeviceManager { -public: - Device **devices; - int numDevices; - - void ClearDevices(); - - // When refreshing devices, back up old devices, then - // populate this with new devices, then call copy bindings. - // All old bindings are copied to matching devices. - - // When old devices are missing, I do a slightly more careful search - // using productIDs and then (in desperation) displayName. - // Finally create new dummy devices if no matches found. - void CopyBindings(int numDevices, Device **devices); - - - InputDeviceManager(); - ~InputDeviceManager(); - - void AddDevice(Device *d); - Device *GetActiveDevice(InitInfo *info, unsigned int *uid, int *index, int *value); - void Update(InitInfo *initInfo); - - // Called after reading state, after Update(). - void PostRead(); - - void SetEffect(unsigned char port, unsigned int slot, unsigned char motor, unsigned char force); - - // Update does this as needed. - // void GetInput(void *v); - void ReleaseInput(); - - void DisableDevice(int index); - inline void EnableDevice(int i) { - devices[i]->enabled = 1; - } - - void EnableDevices(DeviceType type, DeviceAPI api); - void DisableAllDevices(); -}; - -extern InputDeviceManager *dm; - -#endif +#ifndef INPUT_MANAGER_H +#define INPUT_MANAGER_H + +// Both of these are hard coded in a lot of places, so don't modify them. +// Base sensitivity means that a sensitivity of that corresponds to a factor of 1. +// Fully down means that value corresponds to a button being fully down (255). +// a value of 128 or more corresponds to that button being pressed, for binary +// values. +#define BASE_SENSITIVITY (1<<16) +#define FULLY_DOWN (1<<16) + +#define DEFAULT_DEADZONE (BASE_SENSITIVITY * 201/1000) + +/* Idea is for this file and the associated cpp file to be Windows independent. + * Still more effort than it's worth to port to Linux, however. + */ + +// Mostly match DirectInput8 values. Note that these are for physical controls. +// One physical axis maps to 3 virtual ones, and one physical POV control maps to +// 4 virtual ones. +enum ControlType { + NO_CONTROL = 0, + // Axes are ints. Relative axes are for mice, mice wheels, etc, + // and are always reported relative to their last value. + // Absolute axes range from -65536 to 65536 and are absolute positions, + // like for joysticks and pressure sensitive buttons. + RELAXIS = 1, + ABSAXIS = 2, + + // Buttons range from 0 to 65536. + PSHBTN = 4, + TGLBTN = 8, + + // POV controls are ints, values range from -1 to 36000. + // -1 means not pressed, otherwise it's an angle. + // For easy DirectInput compatibility, anything outside. + // that range is treated as -1 (Though 36000-37000 is treated + // like 0 to 1000, just in case). + POV = 16, + + // Pressure sensitive buttons. Only a different type because + // they have configurable dead zones, unlike push or toggle buttons. + PRESSURE_BTN = 32, +}; + +// Masks to determine button type. Don't need one for POV. +#define BUTTON (PSHBTN | TGLBTN | PRESSURE_BTN) +#define BINARY_BUTTON (PSHBTN | TGLBTN) +#define AXIS 3 + +struct Binding { + int controlIndex; + int command; + int sensitivity; + int deadZone; + unsigned char turbo; +}; + +#define UID_AXIS (1<<31) +#define UID_POV (1<<30) + +#define UID_AXIS_POS (1<<24) +#define UID_AXIS_NEG (2<<24) +#define UID_POV_N (3<<24) +#define UID_POV_E (4<<24) +#define UID_POV_S (5<<24) +#define UID_POV_W (6<<24) + +// One of these exists for each bindable object. +// Bindable objects consist of buttons, axis, pov controls, +// and individual axis/pov directions. Not that pov controls +// cannot actually be bound, but when trying to bind as an axis, +// all directions are assigned individually. +struct VirtualControl { + // Unique id for control, given device. Based on source control's id, + // source control type, axis/pov flags if it's a pov/axis (Rather than + // a button or a pov/axis control's individual button), and an index, + // if the control is split. + unsigned int uid; + // virtual key code. 0 if none. + int physicalControlIndex; +}; + +// Need one for each button, axis, and pov control. +// API-specific code creates the PhysicalControls and +// updates their state, standard function then populates +// the VirtualControls and queues the keyboard messages, if +// needed. +struct PhysicalControl { + // index of the first virtual control corresponding to this. + // Buttons have 1 virtual control, axes 3, and povs 5, all + // in a row. + int baseVirtualControlIndex; + ControlType type; + // id. Must be unique for control type. + // short so can be combined with other values to get + // uid for virtual controls. + unsigned short id; + unsigned short vkey; + wchar_t *name; +}; + +enum DeviceAPI { + NO_API = 0, + DI = 1, + WM = 2, + RAW = 3, + XINPUT = 4, + DS3 = 5, + // Not currently used. + LLHOOK = 6, + // Not a real API, obviously. Only used with keyboards, + // to ignore individual buttons. Wrapper itself takes care + // of ignoring bound keys. Otherwise, works normally. + IGNORE_KEYBOARD = 7, +}; + +enum DeviceType { + NO_DEVICE = 0, + KEYBOARD = 1, + MOUSE = 2, + OTHER = 3 +}; + +enum EffectType { + EFFECT_CONSTANT, + EFFECT_PERIODIC, + EFFECT_RAMP +}; + +// force range sfrom -BASE_SENSITIVITY to BASE_SENSITIVITY. +// Order matches ForceFeedbackAxis order. force of 0 means to +// ignore that axis completely. Force of 1 or -1 means to initialize +// the axis with minimum force (Possibly 0 force), if applicable. +struct AxisEffectInfo { + int force; +}; + +struct ForceFeedbackBinding { + AxisEffectInfo *axes; + int effectIndex; + unsigned char motor; +}; + +// Bindings listed by effect, so I don't have to bother with +// indexing effects. +struct ForceFeedbackEffectType { + wchar_t *displayName; + // Because I'm lazy, can only have ASCII characters and no spaces. + wchar_t *effectID; + // constant, ramp, or periodic + EffectType type; +}; + + +struct ForceFeedbackAxis { + wchar_t *displayName; + int id; +}; + +// Used both for active devices and for sets of settings for devices. +// Way things work: +// LoadSettings() will delete all device info, then load settings to get +// one set of generic devices. Then I enumerate all devices. Then I merge +// them, moving settings from the generic devices to the enumerated ones. + +struct PadBindings { + Binding *bindings; + int numBindings; + ForceFeedbackBinding *ffBindings; + int numFFBindings; +}; + +class WndProcEater; + +struct InitInfo { + // 1 when binding key to ignore. + int bindingIgnore; + // 1 when binding. + int binding; + + HWND hWndTop; + + // For config screen, need to eat button's message handling. + //HWND hWndButton; + + WndProcEater* hWndProc; +}; + + +// Mostly self-contained, but bindings are modified by config.cpp, to make +// updating the ListView simpler. +class Device { +public: + DeviceAPI api; + DeviceType type; + char active; + char attached; + // Based on input modes. + char enabled; + + // Not all devices need to subclass the windproc, but most do so might as well + // put it here... --air + WndProcEater* hWndProc; + + union { + // Allows for one loop to compare all 3 in order. + wchar_t *IDs[3]; + struct { + // Same as DisplayName, when not given. Absolutely must be unique. + // Used for loading/saving controls. If matches, all other strings + // are ignored, so must be unique. + wchar_t *instanceID; + // Not required. Used when a device's instance id changes, doesn't have to + // be unique. For devices that can only have one instance, not needed. + wchar_t *productID; + + wchar_t *displayName; + }; + }; + + PadBindings pads[2][4]; + + // Virtual controls. All basically act like pressure sensitivity buttons, with + // values between 0 and 2^16. 2^16 is fully down, 0 is up. Larger values + // are allowed, but *only* for absolute axes (Which don't support the flip checkbox). + // Each control on a device must have a unique id, used for binding. + VirtualControl *virtualControls; + int numVirtualControls; + + int *virtualControlState; + int *oldVirtualControlState; + + PhysicalControl *physicalControls; + int numPhysicalControls; + int *physicalControlState; + + ForceFeedbackEffectType *ffEffectTypes; + int numFFEffectTypes; + ForceFeedbackAxis *ffAxes; + int numFFAxes; + void AddFFAxis(const wchar_t *displayName, int id); + void AddFFEffectType(const wchar_t *displayName, const wchar_t *effectID, EffectType type); + + Device(DeviceAPI, DeviceType, const wchar_t *displayName, const wchar_t *instanceID = 0, wchar_t *deviceID = 0); + virtual ~Device(); + + // Allocates memory for old and new state, sets everything to 0. + // all old states are in one array, buttons, axes, and then POVs. + // start of each section is int aligned. This makes it DirectInput + // compatible. + void AllocState(); + + // Doesn't actually flip. Copies current state to old state. + void FlipState(); + + // Frees state variables. + void FreeState(); + + ForceFeedbackEffectType *GetForcefeedbackEffect(wchar_t *id); + ForceFeedbackAxis *GetForceFeedbackAxis(int id); + + VirtualControl *GetVirtualControl(unsigned int uid); + + PhysicalControl *AddPhysicalControl(ControlType type, unsigned short id, unsigned short vkey, const wchar_t *name = 0); + VirtualControl *AddVirtualControl(unsigned int uid, int physicalControlIndex); + + virtual wchar_t *GetVirtualControlName(VirtualControl *c); + virtual wchar_t *GetPhysicalControlName(PhysicalControl *c); + + void CalcVirtualState(); + + virtual int Activate(InitInfo *args) { + return 0; + } + + inline virtual void Deactivate() { + FreeState(); + active = 0; + } + + // Default update proc. All that's needed for post-based APIs. + inline virtual int Update() { + return active; + } + + // force is from -FULLY_DOWN to FULLY_DOWN. + // Either function can be overridden. Second one by default calls the first + // for every bound effect that's affected. + + // Note: Only used externally for binding, so if override the other one, can assume + // all other forces are currently 0. + inline virtual void SetEffect(ForceFeedbackBinding *binding, unsigned char force) {} + inline virtual void SetEffects(unsigned char port, unsigned int slot, unsigned char motor, unsigned char force); + + // Called after reading. Basically calls FlipState(). + // Some device types (Those that don't incrementally update) + // could call FlipState elsewhere, but this makes it simpler to ignore + // while binding. + virtual void PostRead(); +}; + +class InputDeviceManager { +public: + Device **devices; + int numDevices; + + void ClearDevices(); + + // When refreshing devices, back up old devices, then + // populate this with new devices, then call copy bindings. + // All old bindings are copied to matching devices. + + // When old devices are missing, I do a slightly more careful search + // using productIDs and then (in desperation) displayName. + // Finally create new dummy devices if no matches found. + void CopyBindings(int numDevices, Device **devices); + + + InputDeviceManager(); + ~InputDeviceManager(); + + void AddDevice(Device *d); + Device *GetActiveDevice(InitInfo *info, unsigned int *uid, int *index, int *value); + void Update(InitInfo *initInfo); + + // Called after reading state, after Update(). + void PostRead(); + + void SetEffect(unsigned char port, unsigned int slot, unsigned char motor, unsigned char force); + + // Update does this as needed. + // void GetInput(void *v); + void ReleaseInput(); + + void DisableDevice(int index); + inline void EnableDevice(int i) { + devices[i]->enabled = 1; + } + + void EnableDevices(DeviceType type, DeviceAPI api); + void DisableAllDevices(); +}; + +extern InputDeviceManager *dm; + +#endif diff --git a/plugins/LilyPad/KeyboardHook.cpp b/plugins/LilyPad/KeyboardHook.cpp index 3bd0a2e76f..d46d4a1b2c 100644 --- a/plugins/LilyPad/KeyboardHook.cpp +++ b/plugins/LilyPad/KeyboardHook.cpp @@ -1,135 +1,135 @@ -#include "Global.h" -#include "InputManager.h" -#include "Config.h" - -#include "KeyboardHook.h" -#include "WindowsKeyboard.h" -#include "VKey.h" -#include "WndProcEater.h" - -extern HINSTANCE hInst; -LRESULT CALLBACK IgnoreKeyboardHook(int code, WPARAM wParam, LPARAM lParam); - -ExtraWndProcResult StartHooksWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *output); - -class IgnoreKeyboardHookDevice; -static IgnoreKeyboardHookDevice *ikhd = 0; - -class IgnoreKeyboardHookDevice : public WindowsKeyboard { -public: - HHOOK hHook; - // When binding, eat all keys - int binding; - - IgnoreKeyboardHookDevice() : WindowsKeyboard(IGNORE_KEYBOARD, L"Ignore Keyboard") { - // Don't want to send keypress/keyrelease messages. - for (int i=0; iDeactivate(); - binding = initInfo->bindingIgnore; - hWndProc = initInfo->hWndProc; - - return 0; - printf( "(Lilypad) StartHook\n" ); - hWndProc->Eat(StartHooksWndProc, EATPROC_NO_UPDATE_WHILE_UPDATING_DEVICES); - - InitState(); - ikhd = this; - active = 1; - return 1; - } - - void Deactivate() { - printf( "(Lilypad) KillHook\n" ); - - FreeState(); - if (active) { - if (hHook) { - UnhookWindowsHookEx(hHook); - hHook = 0; - } - if (ikhd == this) ikhd = 0; - active = 0; - } - } -}; - -void EnumHookDevices() { - dm->AddDevice(new IgnoreKeyboardHookDevice()); -} - -// Makes sure hooks are started in correct thread. -ExtraWndProcResult StartHooksWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *output) { - // Don't remove too quickly - could be other things happening in other threads. - // Attempted fix for keyboard input occasionally not working. - static int counter = 0; - if (ikhd && !ikhd->hHook && ikhd->active) { - counter = 0; - printf( "(Lilypad) SetHook\n" ); - ikhd->hHook = SetWindowsHookEx(WH_KEYBOARD_LL, IgnoreKeyboardHook, hInst, 0);//GetCurrentThreadId()); - if (ikhd->hHook == 0) ikhd->Deactivate(); - } - printf( "(Lilypad) HookThinking!\n" ); - counter ++; - if (counter % 1000 == 0) - return CONTINUE_BLISSFULLY_AND_RELEASE_PROC; - else - return CONTINUE_BLISSFULLY; -} - -LRESULT CALLBACK IgnoreKeyboardHook(int code, WPARAM wParam, LPARAM lParam) { - HHOOK hHook = 0; - if (ikhd) { - printf( "(Lilypad) Hooked!!\n" ); - hHook = ikhd->hHook; - if (hHook) { - if (code == HC_ACTION) { - if (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN) { - KBDLLHOOKSTRUCT* key = (KBDLLHOOKSTRUCT*) lParam; - if (key->vkCode < 256) { - for (int i=0; ipads[0][0].numBindings; i++) { - if (ikhd->pads[0][0].bindings[i].controlIndex == key->vkCode) { - return 1; - } - } - if (ikhd->binding){ - ikhd->UpdateKey(key->vkCode, 1); - return 1; - } - } - if (config.vistaVolume) { - if (key->vkCode == VK_VOLUME_DOWN) { - SetVolume(config.volume-3); - return 1; - } - if (key->vkCode == VK_VOLUME_UP) { - SetVolume(config.volume+3); - return 1; - } - } - } - else if (wParam == WM_KEYUP || wParam == WM_SYSKEYUP) { - KBDLLHOOKSTRUCT* key = (KBDLLHOOKSTRUCT*) lParam; - if (ikhd->binding && key->vkCode < 256) - ikhd->UpdateKey(key->vkCode, 0); - } - } - return CallNextHookEx(hHook, code, wParam, lParam); - } - } - return CallNextHookEx(hHook, code, wParam, lParam); -} - +#include "Global.h" +#include "InputManager.h" +#include "Config.h" + +#include "KeyboardHook.h" +#include "WindowsKeyboard.h" +#include "VKey.h" +#include "WndProcEater.h" + +extern HINSTANCE hInst; +LRESULT CALLBACK IgnoreKeyboardHook(int code, WPARAM wParam, LPARAM lParam); + +ExtraWndProcResult StartHooksWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *output); + +class IgnoreKeyboardHookDevice; +static IgnoreKeyboardHookDevice *ikhd = 0; + +class IgnoreKeyboardHookDevice : public WindowsKeyboard { +public: + HHOOK hHook; + // When binding, eat all keys + int binding; + + IgnoreKeyboardHookDevice() : WindowsKeyboard(IGNORE_KEYBOARD, L"Ignore Keyboard") { + // Don't want to send keypress/keyrelease messages. + for (int i=0; iDeactivate(); + binding = initInfo->bindingIgnore; + hWndProc = initInfo->hWndProc; + + return 0; + printf( "(Lilypad) StartHook\n" ); + hWndProc->Eat(StartHooksWndProc, EATPROC_NO_UPDATE_WHILE_UPDATING_DEVICES); + + InitState(); + ikhd = this; + active = 1; + return 1; + } + + void Deactivate() { + printf( "(Lilypad) KillHook\n" ); + + FreeState(); + if (active) { + if (hHook) { + UnhookWindowsHookEx(hHook); + hHook = 0; + } + if (ikhd == this) ikhd = 0; + active = 0; + } + } +}; + +void EnumHookDevices() { + dm->AddDevice(new IgnoreKeyboardHookDevice()); +} + +// Makes sure hooks are started in correct thread. +ExtraWndProcResult StartHooksWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *output) { + // Don't remove too quickly - could be other things happening in other threads. + // Attempted fix for keyboard input occasionally not working. + static int counter = 0; + if (ikhd && !ikhd->hHook && ikhd->active) { + counter = 0; + printf( "(Lilypad) SetHook\n" ); + ikhd->hHook = SetWindowsHookEx(WH_KEYBOARD_LL, IgnoreKeyboardHook, hInst, 0);//GetCurrentThreadId()); + if (ikhd->hHook == 0) ikhd->Deactivate(); + } + printf( "(Lilypad) HookThinking!\n" ); + counter ++; + if (counter % 1000 == 0) + return CONTINUE_BLISSFULLY_AND_RELEASE_PROC; + else + return CONTINUE_BLISSFULLY; +} + +LRESULT CALLBACK IgnoreKeyboardHook(int code, WPARAM wParam, LPARAM lParam) { + HHOOK hHook = 0; + if (ikhd) { + printf( "(Lilypad) Hooked!!\n" ); + hHook = ikhd->hHook; + if (hHook) { + if (code == HC_ACTION) { + if (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN) { + KBDLLHOOKSTRUCT* key = (KBDLLHOOKSTRUCT*) lParam; + if (key->vkCode < 256) { + for (int i=0; ipads[0][0].numBindings; i++) { + if (ikhd->pads[0][0].bindings[i].controlIndex == key->vkCode) { + return 1; + } + } + if (ikhd->binding){ + ikhd->UpdateKey(key->vkCode, 1); + return 1; + } + } + if (config.vistaVolume) { + if (key->vkCode == VK_VOLUME_DOWN) { + SetVolume(config.volume-3); + return 1; + } + if (key->vkCode == VK_VOLUME_UP) { + SetVolume(config.volume+3); + return 1; + } + } + } + else if (wParam == WM_KEYUP || wParam == WM_SYSKEYUP) { + KBDLLHOOKSTRUCT* key = (KBDLLHOOKSTRUCT*) lParam; + if (ikhd->binding && key->vkCode < 256) + ikhd->UpdateKey(key->vkCode, 0); + } + } + return CallNextHookEx(hHook, code, wParam, lParam); + } + } + return CallNextHookEx(hHook, code, wParam, lParam); +} + diff --git a/plugins/LilyPad/KeyboardHook.h b/plugins/LilyPad/KeyboardHook.h index d980a54d2e..209105f0a2 100644 --- a/plugins/LilyPad/KeyboardHook.h +++ b/plugins/LilyPad/KeyboardHook.h @@ -1,7 +1,7 @@ -#ifndef KEYBOARD_HOOK_H -#define KEYBOARD_HOOK_H - -void EnumHookDevices(); - -#endif - +#ifndef KEYBOARD_HOOK_H +#define KEYBOARD_HOOK_H + +void EnumHookDevices(); + +#endif + diff --git a/plugins/LilyPad/KeyboardQueue.cpp b/plugins/LilyPad/KeyboardQueue.cpp index c8498ee119..6176623288 100644 --- a/plugins/LilyPad/KeyboardQueue.cpp +++ b/plugins/LilyPad/KeyboardQueue.cpp @@ -1,62 +1,62 @@ -#include "Global.h" -// This is undoubtedly completely unnecessary. -#include "KeyboardQueue.h" - -// What MS calls a single process Mutex. Faster, supposedly. -// More importantly, can be abbreviated, amusingly, as cSection. -static CRITICAL_SECTION cSection; -static u8 csInitialized = 0; - -#define EVENT_QUEUE_LEN 16 -// Actually points one beyond the last queued event. -static u8 lastQueuedEvent = 0; -static u8 nextQueuedEvent = 0; -static keyEvent queuedEvents[EVENT_QUEUE_LEN]; - -void QueueKeyEvent(int key, int event) { - if (!csInitialized) { - csInitialized = 1; - InitializeCriticalSection(&cSection); - } - EnterCriticalSection(&cSection); - - // Don't queue events if escape is on top of queue. This is just for safety - // purposes when a game is killing the emulator for whatever reason. - if (nextQueuedEvent == lastQueuedEvent || - queuedEvents[nextQueuedEvent].key != VK_ESCAPE || - queuedEvents[nextQueuedEvent].evt != KEYPRESS) { - // Clear queue on escape down, bringing escape to front. May do something - // with shift/ctrl/alt and F-keys, later. - if (event == KEYPRESS && key == VK_ESCAPE) { - nextQueuedEvent = lastQueuedEvent; - } - - queuedEvents[lastQueuedEvent].key = key; - queuedEvents[lastQueuedEvent].evt = event; - - lastQueuedEvent = (lastQueuedEvent + 1) % EVENT_QUEUE_LEN; - // If queue wrapped around, remove last element. - if (nextQueuedEvent == lastQueuedEvent) { - nextQueuedEvent = (nextQueuedEvent + 1) % EVENT_QUEUE_LEN; - } - } - LeaveCriticalSection(&cSection); -} - -int GetQueuedKeyEvent(keyEvent *event) { - if (lastQueuedEvent == nextQueuedEvent) return 0; - - EnterCriticalSection(&cSection); - *event = queuedEvents[nextQueuedEvent]; - nextQueuedEvent = (nextQueuedEvent + 1) % EVENT_QUEUE_LEN; - LeaveCriticalSection(&cSection); - return 1; -} - -void ClearKeyQueue() { - lastQueuedEvent = nextQueuedEvent; - if (csInitialized) { - DeleteCriticalSection(&cSection); - csInitialized = 0; - } -} +#include "Global.h" +// This is undoubtedly completely unnecessary. +#include "KeyboardQueue.h" + +// What MS calls a single process Mutex. Faster, supposedly. +// More importantly, can be abbreviated, amusingly, as cSection. +static CRITICAL_SECTION cSection; +static u8 csInitialized = 0; + +#define EVENT_QUEUE_LEN 16 +// Actually points one beyond the last queued event. +static u8 lastQueuedEvent = 0; +static u8 nextQueuedEvent = 0; +static keyEvent queuedEvents[EVENT_QUEUE_LEN]; + +void QueueKeyEvent(int key, int event) { + if (!csInitialized) { + csInitialized = 1; + InitializeCriticalSection(&cSection); + } + EnterCriticalSection(&cSection); + + // Don't queue events if escape is on top of queue. This is just for safety + // purposes when a game is killing the emulator for whatever reason. + if (nextQueuedEvent == lastQueuedEvent || + queuedEvents[nextQueuedEvent].key != VK_ESCAPE || + queuedEvents[nextQueuedEvent].evt != KEYPRESS) { + // Clear queue on escape down, bringing escape to front. May do something + // with shift/ctrl/alt and F-keys, later. + if (event == KEYPRESS && key == VK_ESCAPE) { + nextQueuedEvent = lastQueuedEvent; + } + + queuedEvents[lastQueuedEvent].key = key; + queuedEvents[lastQueuedEvent].evt = event; + + lastQueuedEvent = (lastQueuedEvent + 1) % EVENT_QUEUE_LEN; + // If queue wrapped around, remove last element. + if (nextQueuedEvent == lastQueuedEvent) { + nextQueuedEvent = (nextQueuedEvent + 1) % EVENT_QUEUE_LEN; + } + } + LeaveCriticalSection(&cSection); +} + +int GetQueuedKeyEvent(keyEvent *event) { + if (lastQueuedEvent == nextQueuedEvent) return 0; + + EnterCriticalSection(&cSection); + *event = queuedEvents[nextQueuedEvent]; + nextQueuedEvent = (nextQueuedEvent + 1) % EVENT_QUEUE_LEN; + LeaveCriticalSection(&cSection); + return 1; +} + +void ClearKeyQueue() { + lastQueuedEvent = nextQueuedEvent; + if (csInitialized) { + DeleteCriticalSection(&cSection); + csInitialized = 0; + } +} diff --git a/plugins/LilyPad/KeyboardQueue.h b/plugins/LilyPad/KeyboardQueue.h index c5e44afad5..889764fd88 100644 --- a/plugins/LilyPad/KeyboardQueue.h +++ b/plugins/LilyPad/KeyboardQueue.h @@ -1,9 +1,9 @@ - -// This entire thing isn't really needed, -// but takes little enough effort to be safe... - -void QueueKeyEvent(int key, int event); -int GetQueuedKeyEvent(keyEvent *event); - -// Cleans up as well as clears queue. -void ClearKeyQueue(); + +// This entire thing isn't really needed, +// but takes little enough effort to be safe... + +void QueueKeyEvent(int key, int event); +int GetQueuedKeyEvent(keyEvent *event); + +// Cleans up as well as clears queue. +void ClearKeyQueue(); diff --git a/plugins/LilyPad/LilyPad.cpp b/plugins/LilyPad/LilyPad.cpp index 6577c82f75..037c3e35a2 100644 --- a/plugins/LilyPad/LilyPad.cpp +++ b/plugins/LilyPad/LilyPad.cpp @@ -1,1526 +1,1526 @@ -#include "Global.h" - -// For escape timer, so as not to break GSDX+DX9. -#include -#include "resource.h" -#include "InputManager.h" -#include "Config.h" - -#define PADdefs - -#include "DeviceEnumerator.h" -#include "WndProcEater.h" -#include "KeyboardQueue.h" -#include "svnrev.h" -#include "DualShock3.h" -#include "HidDevice.h" - -#define WMA_FORCE_UPDATE (WM_APP + 0x537) -#define FORCE_UPDATE_WPARAM ((WPARAM)0x74328943) -#define FORCE_UPDATE_LPARAM ((LPARAM)0x89437437) - -// LilyPad version. -#define VERSION ((0<<8) | 10 | (0<<24)) - -HINSTANCE hInst; -HWND hWnd; -HWND hWndTop; - -WndProcEater hWndGSProc; -WndProcEater hWndTopProc; - -// ButtonProc is used mostly by the Config panel for eating the procedures of the -// button with keyboard focus. -WndProcEater hWndButtonProc; - -// Keeps the various sources for Update polling (PADpoll, PADupdate, etc) from wreaking -// havoc on each other... -CRITICAL_SECTION updateLock; - -// Used to toggle mouse listening. -u8 miceEnabled; - -// 2 when both pads are initialized, 1 for one pad, etc. -int openCount = 0; - -int activeWindow = 0; -int windowThreadId = 0; -int updateQueued = 0; - -int bufSize = 0; -unsigned char outBuf[50]; -unsigned char inBuf[50]; - -// windowThreadId = GetWindowThreadProcessId(hWnd, 0); - -#define MODE_DIGITAL 0x41 -#define MODE_ANALOG 0x73 -#define MODE_DS2_NATIVE 0x79 - -int IsWindowMaximized (HWND hWnd) { - RECT rect; - if (GetWindowRect(hWnd, &rect)) { - POINT p; - p.x = rect.left; - p.y = rect.top; - MONITORINFO info; - memset(&info, 0, sizeof(info)); - info.cbSize = sizeof(info); - HMONITOR hMonitor; - if ((hMonitor = MonitorFromPoint(p, MONITOR_DEFAULTTOPRIMARY)) && - GetMonitorInfo(hMonitor, &info) && - memcmp(&info.rcMonitor, &rect, sizeof(rect)) == 0) { - return 1; - } - } - return 0; -} - -void DEBUG_TEXT_OUT(const char *text) { - if (config.debug) { - HANDLE hFile = CreateFileA("logs\\padLog.txt", FILE_APPEND_DATA, FILE_SHARE_READ, 0, OPEN_ALWAYS, 0, 0); - if (hFile != INVALID_HANDLE_VALUE) { - DWORD junk; - WriteFile(hFile, text, strlen(text), &junk, 0); - CloseHandle(hFile);; - } - } -} - -void DEBUG_NEW_SET() { - if (config.debug && bufSize>1) { - HANDLE hFile = CreateFileA("logs\\padLog.txt", FILE_APPEND_DATA, FILE_SHARE_READ, 0, OPEN_ALWAYS, 0, 0); - if (hFile != INVALID_HANDLE_VALUE) { - int i; - char temp[1500]; - char *end = temp; - sprintf(end, "%02X (%02X) ", inBuf[0], inBuf[1]); - end += 8; - for (i=2; i 0). - u8 enabled; -} pads[2][4]; - -// Active slots for each port. -int slots[2]; -// Which ports we're running on. -int portInitialized[2]; - -// Force value to be from 0 to 255. -u8 Cap (int i) { - if (i<0) return 0; - if (i>255) return 255; - return (u8) i; -} - -inline void ReleaseModifierKeys() { - QueueKeyEvent(VK_SHIFT, KEYRELEASE); - QueueKeyEvent(VK_MENU, KEYRELEASE); - QueueKeyEvent(VK_CONTROL, KEYRELEASE); -} - -// RefreshEnabledDevices() enables everything that can potentially -// be bound to, as well as the "Ignore keyboard" device. -// -// This enables everything that input should be read from while the -// emulator is running. Takes into account mouse and focus state -// and which devices have bindings for enabled pads. Releases -// keyboards if window is not focused. Releases game devices if -// background monitoring is not checked. -// And releases games if not focused and config.background is not set. -void UpdateEnabledDevices(int updateList = 0) { - // Enable all devices I might want. Can ignore the rest. - RefreshEnabledDevices(updateList); - // Figure out which pads I'm getting input for. - for (int port = 0; port<2; port++) { - for (int slot = 0; slot<4; slot++) { - if (slot && !config.multitap[port]) { - pads[port][slot].enabled = 0; - } - else { - pads[port][slot].enabled = pads[port][slot].initialized && config.padConfigs[port][slot].type != DisabledPad; - } - } - } - for (int i=0; inumDevices; i++) { - Device *dev = dm->devices[i]; - - if (!dev->enabled) continue; - if (!dev->attached) { - dm->DisableDevice(i); - continue; - } - - // Disable ignore keyboard if don't have focus or there are no keys to ignore. - if (dev->api == IGNORE_KEYBOARD) { - if ((!config.vistaVolume && (config.keyboardApi == NO_API || !dev->pads[0][0].numBindings)) || !activeWindow) { - dm->DisableDevice(i); - } - continue; - } - // Keep for PCSX2 keyboard shotcuts, unless unfocused. - if (dev->type == KEYBOARD) { - if (!activeWindow) dm->DisableDevice(i); - } - // Keep for cursor hiding consistency, unless unfocused. - // miceEnabled tracks state of mouse enable/disable button, not if mouse API is set to disabled. - else if (dev->type == MOUSE) { - if (!miceEnabled || !activeWindow) dm->DisableDevice(i); - } - else if (!activeWindow && !config.background) dm->DisableDevice(i); - else { - int numActiveBindings = 0; - for (int port=0; port<2; port++) { - for (int slot=0; slot<4; slot++) { - if (pads[port][slot].enabled) { - numActiveBindings += dev->pads[port][slot].numBindings + dev->pads[port][slot].numFFBindings; - } - } - } - if (!numActiveBindings) - dm->DisableDevice(i); - } - } -} - -BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD fdwReason, void* lpvReserved) { - hInst = hInstance; - if (fdwReason == DLL_PROCESS_ATTACH) { - InitializeCriticalSection( &updateLock ); - - DisableThreadLibraryCalls(hInstance); - } - else if (fdwReason == DLL_PROCESS_DETACH) { - while (openCount) - PADclose(); - PADshutdown(); - UninitHid(); - UninitLibUsb(); - DeleteCriticalSection( &updateLock ); - } - return 1; -} - -void AddForce(ButtonSum *sum, u8 cmd, int delta = 255) { - if (!delta) return; - if (cmd<0x14) { - sum->buttons[cmd-0x10] += delta; - } - // D-pad. Command numbering is based on ordering of digital values. - else if (cmd < 0x18) { - if (cmd == 0x14) { - sum->sticks[0].vert -= delta; - } - else if (cmd == 0x15) { - sum->sticks[0].horiz += delta; - } - else if (cmd == 0x16) { - sum->sticks[0].vert += delta; - } - else if (cmd == 0x17) { - sum->sticks[0].horiz -= delta; - } - } - else if (cmd < 0x20) { - sum->buttons[cmd-0x10-4] += delta; - } - // Left stick. - else if (cmd < 0x24) { - if (cmd == 32) { - sum->sticks[2].vert -= delta; - } - else if (cmd == 33) { - sum->sticks[2].horiz += delta; - } - else if (cmd == 34) { - sum->sticks[2].vert += delta; - } - else if (cmd == 35) { - sum->sticks[2].horiz -= delta; - } - } - // Right stick. - else if (cmd < 0x28) { - if (cmd == 36) { - sum->sticks[1].vert -= delta; - } - else if (cmd == 37) { - sum->sticks[1].horiz += delta; - } - else if (cmd == 38) { - sum->sticks[1].vert += delta; - } - else if (cmd == 39) { - sum->sticks[1].horiz -= delta; - } - } -} - -void ProcessButtonBinding(Binding *b, ButtonSum *sum, int value) { - if (value < b->deadZone || !value) return; - - int sensitivity = b->sensitivity; - if (sensitivity < 0) { - sensitivity = -sensitivity; - value = (1<<16)-value; - } - if (value < 0) return; - - /* Note: Value ranges of FULLY_DOWN, and sensitivity of - * BASE_SENSITIVITY corresponds to an axis/button being exactly fully down. - * Math in next line takes care of those two conditions, rounding as necessary. - * Done using __int64s because overflows will occur when - * sensitivity > BASE_SENSITIVITY and/or value > FULLY_DOWN. Latter only happens - * for relative axis. - */ - int force = (int)((((sensitivity*(255*(__int64)value)) + BASE_SENSITIVITY/2)/BASE_SENSITIVITY + FULLY_DOWN/2)/FULLY_DOWN); - AddForce(sum, b->command, force); -} - -// Restricts d-pad/analog stick values to be from -255 to 255 and button values to be from 0 to 255. -// With D-pad in DS2 native mode, the negative and positive ranges are both independently from 0 to 255, -// which is why I use 9 bits of all sticks. For left and right sticks, I have to remove a bit before sending. -void CapSum(ButtonSum *sum) { - int i; - for (i=0; i<3; i++) { - int div = max(abs(sum->sticks[i].horiz), abs(sum->sticks[i].vert)); - if (div > 255) { - sum->sticks[i].horiz = sum->sticks[i].horiz * 255 / div; - sum->sticks[i].vert = sum->sticks[i].vert * 255 / div; - } - } - for (i=0; i<12; i++) { - sum->buttons[i] = Cap(sum->buttons[i]); - } -} - -// Counter similar to stateUpdated for each pad, except used for PADkeyEvent instead. -// Only matters when GS thread updates is disabled (Just like summed pad values -// for pads beyond the first slot). - -// Values, in order, correspond to PADkeyEvent, PADupdate(0), PADupdate(1), and -// WndProc(WMA_FORCE_UPDATE). Last is always 0. -char padReadKeyUpdated[4] = {0, 0, 0, 0}; - -#define LOCK_DIRECTION 2 -#define LOCK_BUTTONS 4 -#define LOCK_BOTH 1 - -struct EnterScopedSection -{ - CRITICAL_SECTION& m_cs; - - EnterScopedSection( CRITICAL_SECTION& cs ) : m_cs( cs ) { - EnterCriticalSection( &m_cs ); - } - - ~EnterScopedSection() { - LeaveCriticalSection( &m_cs ); - } -}; - -void Update(unsigned int port, unsigned int slot) { - char *stateUpdated; - if (port < 2) { - stateUpdated = &pads[port][slot].stateUpdated; - } - else if (port < 6) { - stateUpdated = padReadKeyUpdated+port-2; - } - else return; - - if (*stateUpdated > 0) { - stateUpdated[0] --; - return; - } - - // Lock prior to timecheck code to avoid pesky race conditions. - EnterScopedSection padlock( updateLock ); - - static unsigned int LastCheck = 0; - unsigned int t = timeGetTime(); - if (t - LastCheck < 15 || !openCount) return; - - if (windowThreadId != GetCurrentThreadId()) { - if (stateUpdated[0] < 0) { - if (!updateQueued) { - updateQueued = 1; - PostMessage(hWnd, WMA_FORCE_UPDATE, FORCE_UPDATE_WPARAM, FORCE_UPDATE_LPARAM); - } - } else - { - stateUpdated[0] --; - } - return; - } - - LastCheck = t; - - int i; - ButtonSum s[2][4]; - u8 lockStateChanged[2][4]; - memset(lockStateChanged, 0, sizeof(lockStateChanged)); - - for (i=0; i<8; i++) { - s[i&1][i>>1] = pads[i&1][i>>1].lockedSum; - } - InitInfo info = { - 0, 0, hWndTop, &hWndGSProc - }; - - dm->Update(&info); - static int turbo = 0; - turbo++; - for (i=0; inumDevices; i++) { - Device *dev = dm->devices[i]; - // Skip both disabled devices and inactive enabled devices. - // Shouldn't be any of the latter, in general, but just in case... - if (!dev->active) continue; - for (int port=0; port<2; port++) { - for (int slot=0; slot<4; slot++) { - if (config.padConfigs[port][slot].type == DisabledPad || !pads[port][slot].initialized) continue; - for (int j=0; jpads[port][slot].numBindings; j++) { - Binding *b = dev->pads[port][slot].bindings+j; - int cmd = b->command; - int state = dev->virtualControlState[b->controlIndex]; - if (!(turbo & b->turbo)) { - if (cmd > 0x0F && cmd != 0x28) { - ProcessButtonBinding(b, &s[port][slot], state); - } - else if ((state>>15) && !(dev->oldVirtualControlState[b->controlIndex]>>15)) { - if (cmd == 0x0F) { - miceEnabled = !miceEnabled; - UpdateEnabledDevices(); - } - else if (cmd == 0x0C) { - lockStateChanged[port][slot] |= LOCK_BUTTONS; - } - else if (cmd == 0x0E) { - lockStateChanged[port][slot] |= LOCK_DIRECTION; - } - else if (cmd == 0x0D) { - lockStateChanged[port][slot] |= LOCK_BOTH; - } - else if (cmd == 0x28) { - if (!pads[port][slot].modeLock) { - if (pads[port][slot].mode != MODE_DIGITAL) - pads[port][slot].mode = MODE_DIGITAL; - else - pads[port][slot].mode = MODE_ANALOG; - } - } - } - } - } - } - } - } - dm->PostRead(); - - { - for (int port=0; port<2; port++) { - for (int slot=0; slot<4; slot++) { - for (int motor=0; motor<2; motor++) { - // TODO: Probably be better to send all of these at once. - if (pads[port][slot].nextVibrate[motor] | pads[port][slot].currentVibrate[motor]) { - pads[port][slot].currentVibrate[motor] = pads[port][slot].nextVibrate[motor]; - dm->SetEffect(port,slot, motor, pads[port][slot].nextVibrate[motor]); - } - } - } - } - - for (int port=0; port<2; port++) { - for (int slot=0; slot<4; slot++) { - pads[port][slot].stateUpdated = 1; - if (config.padConfigs[port][slot].type == DisabledPad || !pads[port][slot].initialized) continue; - if (config.padConfigs[port][slot].type == GuitarPad) { - if (!config.GH2) { - s[port][slot].sticks[1].vert = -s[port][slot].sticks[1].vert; - } - // GH2 hack. - else if (config.GH2) { - const unsigned int oldIdList[5] = {ID_R2, ID_CIRCLE, ID_TRIANGLE, ID_CROSS, ID_SQUARE}; - const unsigned int idList[5] = {ID_L2, ID_L1, ID_R1, ID_R2, ID_CROSS}; - int values[5]; - int i; - for (i=0; i<5; i++) { - int id = oldIdList[i] - 0x1104; - values[i] = s[port][slot].buttons[id]; - s[port][slot].buttons[id] = 0; - } - s[port][slot].buttons[ID_TRIANGLE-0x1104] = values[1]; - for (i=0; i<5; i++) { - int id = idList[i] - 0x1104; - s[port][slot].buttons[id] = values[i]; - } - if (abs(s[port][slot].sticks[0].vert) <= 48) { - for (int i=0; i<5; i++) { - unsigned int id = idList[i] - 0x1104; - if (pads[port][slot].sum.buttons[id] < s[port][slot].buttons[id]) { - s[port][slot].buttons[id] = pads[port][slot].sum.buttons[id]; - } - } - } - else if (abs(pads[port][slot].sum.sticks[0].vert) <= 48) { - for (int i=0; i<5; i++) { - unsigned int id = idList[i] - 0x1104; - if (pads[port][slot].sum.buttons[id]) { - s[port][slot].buttons[id] = 0; - } - } - } - } - } - - if (pads[port][slot].mode == 0x41) { - for (int i=1; i<=2; i++) { - if (abs(s[port][slot].sticks[i].horiz) >= 100) - s[port][slot].sticks[0].horiz += s[port][slot].sticks[i].horiz; - if (abs(s[port][slot].sticks[i].vert) >= 100) - s[port][slot].sticks[0].vert += s[port][slot].sticks[i].vert; - } - } - - CapSum(&s[port][slot]); - if (lockStateChanged[port][slot]) { - if (lockStateChanged[port][slot] & LOCK_BOTH) { - if (pads[port][slot].lockedState != (LOCK_DIRECTION | LOCK_BUTTONS)) { - // Enable the one that's not enabled. - lockStateChanged[port][slot] ^= pads[port][slot].lockedState^(LOCK_DIRECTION | LOCK_BUTTONS); - } - else { - // Disable both - lockStateChanged[port][slot] ^= LOCK_DIRECTION | LOCK_BUTTONS; - } - } - if (lockStateChanged[port][slot] & LOCK_DIRECTION) { - if (pads[port][slot].lockedState & LOCK_DIRECTION) { - memset(pads[port][slot].lockedSum.sticks, 0, sizeof(pads[port][slot].lockedSum.sticks)); - } - else { - memcpy(pads[port][slot].lockedSum.sticks, s[port][slot].sticks, sizeof(pads[port][slot].lockedSum.sticks)); - } - pads[port][slot].lockedState ^= LOCK_DIRECTION; - } - if (lockStateChanged[port][slot] & LOCK_BUTTONS) { - if (pads[port][slot].lockedState & LOCK_BUTTONS) { - memset(pads[port][slot].lockedSum.buttons, 0, sizeof(pads[port][slot].lockedSum.buttons)); - } - else { - memcpy(pads[port][slot].lockedSum.buttons, s[port][slot].buttons, sizeof(pads[port][slot].lockedSum.buttons)); - } - pads[port][slot].lockedState ^= LOCK_BUTTONS; - } - for (i=0; i>1].sum = s[i&1][i>>1]; - } - - padReadKeyUpdated[0] = padReadKeyUpdated[1] = padReadKeyUpdated[2] = 1; - - if( stateUpdated[0] > 0 ) - --stateUpdated[0]; -} - -void CALLBACK PADupdate(int port) { - Update(port+3, 0); -} - -inline void SetVibrate(int port, int slot, int motor, u8 val) { - pads[port][slot].nextVibrate[motor] = val; -} - -u32 CALLBACK PS2EgetLibType(void) { - ps2e = 1; - return PS2E_LT_PAD; -} - -u32 CALLBACK PS2EgetLibVersion2(u32 type) { - ps2e = 1; - if (type == PS2E_LT_PAD) - return (PS2E_PAD_VERSION<<16) | VERSION; - return 0; -} - -// Used in about and config screens. -void GetNameAndVersionString(wchar_t *out) { -#ifdef NO_CRT - wsprintfW(out, L"LilyPad %i.%i.%i", (VERSION>>8)&0xFF, VERSION&0xFF, (VERSION>>24)&0xFF, SVN_REV); -#elif defined(PCSX2_DEBUG) - wsprintfW(out, L"LilyPad Debug %i.%i.%i (r%i)", (VERSION>>8)&0xFF, VERSION&0xFF, (VERSION>>24)&0xFF, SVN_REV); -#else - wsprintfW(out, L"LilyPad svn %i.%i.%i (r%i)", (VERSION>>8)&0xFF, VERSION&0xFF, (VERSION>>24)&0xFF, SVN_REV); -#endif -} - -char* CALLBACK PSEgetLibName() { -#ifdef NO_CRT - return "LilyPad"; -#elif defined(PCSX2_DEBUG) - static char version[50]; - sprintf(version, "LilyPad Debug (r%i)", SVN_REV); - return version; -#else - static char version[50]; - sprintf(version, "LilyPad svn (r%i)", SVN_REV); - return version; -#endif -} - -char* CALLBACK PS2EgetLibName(void) { - ps2e = 1; - return PSEgetLibName(); -} - -//void CALLBACK PADgsDriverInfo(GSdriverInfo *info) { -// info=info; -//} - -void CALLBACK PADshutdown() { - DEBUG_TEXT_OUT("LilyPad shutdown.\n\n"); - for (int i=0; i<8; i++) - pads[i&1][i>>1].initialized = 0; - portInitialized[0] = portInitialized[1] = 0; - UnloadConfigs(); -} - -inline void StopVibrate() { - for (int i=0; i<8; i++) { - SetVibrate(i&1, i>>1, 0, 0); - SetVibrate(i&1, i>>1, 1, 0); - } -} - -inline void ResetVibrate(int port, int slot) { - SetVibrate(port, slot, 0, 0); - SetVibrate(port, slot, 1, 0); - ((int*)(pads[port][slot].vibrate))[0] = 0xFFFFFF5A; - ((int*)(pads[port][slot].vibrate))[1] = 0xFFFFFFFF; -} - -void ResetPad(int port, int slot) { - // Lines before memset currently don't do anything useful, - // but allow this function to be called at any time. - - // Need to backup, so can be called at any point. - u8 enabled = pads[port][slot].enabled; - - // Currently should never do anything. - SetVibrate(port, slot, 0, 0); - SetVibrate(port, slot, 1, 0); - - memset(&pads[port][slot], 0, sizeof(pads[0][0])); - pads[port][slot].mode = MODE_DIGITAL; - pads[port][slot].umask[0] = pads[port][slot].umask[1] = 0xFF; - // Sets up vibrate variable. - ResetVibrate(port, slot); - if (config.padConfigs[port][slot].autoAnalog) { - pads[port][slot].mode = MODE_ANALOG; - } - pads[port][slot].initialized = 1; - - pads[port][slot].enabled = enabled; -} - - -struct QueryInfo { - u8 port; - u8 slot; - u8 lastByte; - u8 currentCommand; - u8 numBytes; - u8 queryDone; - u8 response[42]; -} query = {0,0,0,0, 0,0xFF, 0xF3}; - -int saveStateIndex = 0; - -s32 CALLBACK PADinit(u32 flags) { - // Note: Won't load settings if already loaded. - if (LoadSettings() < 0) { - return -1; - } - int port = (flags & 3); - if (port == 3) { - if (PADinit(1) == -1) return -1; - return PADinit(2); - } - - #ifdef PCSX2_DEBUG - int tmpFlag = _CrtSetDbgFlag( _CRTDBG_REPORT_FLAG ); - tmpFlag |= _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF; - _CrtSetDbgFlag( tmpFlag ); - #endif - - port --; - - for (int i=0; i<4; i++) { - ResetPad(port, i); - } - slots[port] = 0; - portInitialized[port] = 1; - - query.lastByte = 1; - query.numBytes = 0; - ClearKeyQueue(); - // Just in case, when resuming emulation. - ReleaseModifierKeys(); - - DEBUG_TEXT_OUT("LilyPad initialized\n\n"); - return 0; -} - - - -// Note to self: Has to be a define for the sizeof() to work right. -// Note to self 2: All are the same size, anyways, except for longer full DS2 response -// and shorter digital mode response. -#define SET_RESULT(a) { \ - memcpy(query.response+2, a, sizeof(a)); \ - query.numBytes = 2+sizeof(a); \ -} - -#define SET_FINAL_RESULT(a) { \ - memcpy(query.response+2, a, sizeof(a));\ - query.numBytes = 2+sizeof(a); \ - query.queryDone = 1; \ -} - -static const u8 ConfigExit[7] = {0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; -//static const u8 ConfigExit[7] = {0x5A, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00}; - -static const u8 noclue[7] = {0x5A, 0x00, 0x00, 0x02, 0x00, 0x00, 0x5A}; -static u8 queryMaskMode[7] = {0x5A, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x5A}; -//static const u8 DSNonNativeMode[7] = {0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; -static const u8 setMode[7] = {0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - -// DS2 -static const u8 queryModelDS2[7] = {0x5A, 0x03, 0x02, 0x00, 0x02, 0x01, 0x00}; -// DS1 -static const u8 queryModelDS1[7] = {0x5A, 0x01, 0x02, 0x00, 0x02, 0x01, 0x00}; - -static const u8 queryAct[2][7] = {{0x5A, 0x00, 0x00, 0x01, 0x02, 0x00, 0x0A}, - {0x5A, 0x00, 0x00, 0x01, 0x01, 0x01, 0x14}}; - -static const u8 queryComb[7] = {0x5A, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00}; - -static const u8 queryMode[7] = {0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - - -static const u8 setNativeMode[7] = {0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5A}; - -// Implements a couple of the hacks that affect whatever top-level window -// the GS viewport belongs to (title, screensaver) -ExtraWndProcResult TitleHackWndProc(HWND hWndTop, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *output) { - switch (uMsg) { - case WM_SETTEXT: - if (config.saveStateTitle) { - wchar_t text[200]; - int len; - if (IsWindowUnicode(hWndTop)) { - len = wcslen((wchar_t*) lParam); - if (len < sizeof(text)/sizeof(wchar_t)) wcscpy(text, (wchar_t*) lParam); - } - else { - len = MultiByteToWideChar(CP_ACP, 0, (char*) lParam, -1, text, sizeof(text)/sizeof(wchar_t)); - } - if (len > 0 && len < 150 && !wcsstr(text, L" | State ")) { - wsprintfW(text+len, L" | State %i", saveStateIndex); - SetWindowText(hWndTop, text); - return NO_WND_PROC; - } - } - break; - case WM_SYSCOMMAND: - if ((wParam == SC_SCREENSAVE || wParam == SC_MONITORPOWER) && config.disableScreenSaver) - return NO_WND_PROC; - break; - default: - break; - } - return CONTINUE_BLISSFULLY; -} - -// responsible for monitoring device addition/removal, focus changes, and viewport closures. -ExtraWndProcResult StatusWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *output) { - switch (uMsg) { - case WMA_FORCE_UPDATE: - if (wParam == FORCE_UPDATE_WPARAM && lParam == FORCE_UPDATE_LPARAM) { - if (updateQueued) { - updateQueued = 0; - Update(5, 0); - } - return NO_WND_PROC; - } - case WM_DEVICECHANGE: - if (wParam == DBT_DEVNODES_CHANGED) { - UpdateEnabledDevices(1); - } - break; - case WM_ACTIVATE: - // Release any buttons PCSX2 may think are down when - // losing/gaining focus. - if (!wParam) { - ReleaseModifierKeys(); - } - activeWindow = (LOWORD(wParam) != WA_INACTIVE); - UpdateEnabledDevices(); - break; - case WM_CLOSE: - if (config.closeHacks & 1) { - QueueKeyEvent(VK_ESCAPE, KEYPRESS); - return NO_WND_PROC; - } - else if (config.closeHacks & 2) { - ExitProcess(0); - return NO_WND_PROC; - } - break; - case WM_DESTROY: - QueueKeyEvent(VK_ESCAPE, KEYPRESS); - break; - default: - break; - } - return CONTINUE_BLISSFULLY; -} - -// All that's needed to force hiding the cursor in the proper thread. -// Could have a special case elsewhere, but this make sure it's called -// only once, rather than repeatedly. -ExtraWndProcResult HideCursorProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *output) { - ShowCursor(0); - return CONTINUE_BLISSFULLY_AND_RELEASE_PROC; -} - -char restoreFullScreen = 0; -DWORD WINAPI MaximizeWindowThreadProc(void *lpParameter) { - Sleep(100); - keybd_event(VK_LMENU, MapVirtualKey(VK_LMENU, MAPVK_VK_TO_VSC), 0, 0); - keybd_event(VK_RETURN, MapVirtualKey(VK_RETURN, MAPVK_VK_TO_VSC), 0, 0); - Sleep(10); - keybd_event(VK_RETURN, MapVirtualKey(VK_RETURN, MAPVK_VK_TO_VSC), KEYEVENTF_KEYUP, 0); - keybd_event(VK_LMENU, MapVirtualKey(VK_LMENU, MAPVK_VK_TO_VSC), KEYEVENTF_KEYUP, 0); - return 0; -} - -void CALLBACK PADconfigure() { - if (openCount) { - return; - } - Configure(); -} - - -DWORD WINAPI RenameWindowThreadProc(void *lpParameter) { - wchar_t newTitle[200]; - if (hWndTop) { - int len = GetWindowTextW(hWndTop, newTitle, 200); - if (len > 0 && len < 199) { - wchar_t *end; - if (end = wcsstr(newTitle, L" | State ")) *end = 0; - SetWindowTextW(hWndTop, newTitle); - } - } - return 0; -} - -void SaveStateChanged() { - if (config.saveStateTitle) { - // GSDX only checks its window's message queue at certain points or something, so - // have to do this in another thread to prevent deadlock. - HANDLE hThread = CreateThread(0, 0, RenameWindowThreadProc, 0, 0, 0); - if (hThread) CloseHandle(hThread); - } -} - -s32 CALLBACK PADopen(void *pDsp) { - if (openCount++) return 0; - DEBUG_TEXT_OUT("LilyPad opened\n\n"); - - miceEnabled = !config.mouseUnfocus; - if (!hWnd) { - if (IsWindow((HWND)pDsp)) { - hWnd = (HWND) pDsp; - } - else if (pDsp && !IsBadReadPtr(pDsp, 4) && IsWindow(*(HWND*) pDsp)) { - hWnd = *(HWND*) pDsp; - } - else { - openCount = 0; - MessageBoxA(GetActiveWindow(), - "Invalid Window handle passed to LilyPad.\n" - "\n" - "Either your emulator or gs plugin is buggy,\n" - "Despite the fact the emulator is about to\n" - "blame LilyPad for failing to initialize.", - "Non-LilyPad Error", MB_OK | MB_ICONERROR); - return -1; - } - hWndTop = hWnd; - while (GetWindowLong (hWndTop, GWL_STYLE) & WS_CHILD) - hWndTop = GetParent (hWndTop); - - if (!hWndGSProc.SetWndHandle(hWnd)) { - openCount = 0; - return -1; - } - - // Implements most hacks, as well as enabling/disabling mouse - // capture when focus changes. - updateQueued = 0; - hWndGSProc.Eat(StatusWndProc, 0); - - if(hWnd != hWndTop) { - if (!hWndTopProc.SetWndHandle(hWndTop)) { - openCount = 0; - return -1; - } - hWndTopProc.Eat(TitleHackWndProc, 0); - } - else - hWndGSProc.Eat(TitleHackWndProc, 0); - - if (config.forceHide) { - hWndGSProc.Eat(HideCursorProc, 0); - } - SaveStateChanged(); - - windowThreadId = GetWindowThreadProcessId(hWndTop, 0); - } - - if (restoreFullScreen) { - if (!IsWindowMaximized(hWndTop)) { - HANDLE hThread = CreateThread(0, 0, MaximizeWindowThreadProc, hWndTop, 0, 0); - if (hThread) CloseHandle(hThread); - } - restoreFullScreen = 0; - } - for (int port=0; port<2; port++) { - for (int slot=0; slot<4; slot++) { - memset(&pads[port][slot].sum, 0, sizeof(pads[port][slot].sum)); - memset(&pads[port][slot].lockedSum, 0, sizeof(pads[port][slot].lockedSum)); - pads[port][slot].lockedState = 0; - } - } - - // I'd really rather use this line, but GetActiveWindow() does not have complete specs. - // It *seems* to return null when no window from this thread has focus, but the - // Microsoft specs seem to imply it returns the window from this thread that would have focus, - // if any window did (topmost in this thread?). Which isn't what I want, and doesn't seem - // to be what it actually does. - // activeWindow = GetActiveWindow() == hWnd; - - // activeWindow = (GetAncestor(hWnd, GA_ROOT) == GetAncestor(GetForegroundWindow(), GA_ROOT)); - activeWindow = 1; - UpdateEnabledDevices(); - return 0; -} - -void CALLBACK PADclose() { - if (openCount && !--openCount) { - DEBUG_TEXT_OUT("LilyPad closed\n\n"); - updateQueued = 0; - hWndGSProc.Release(); - hWndTopProc.Release(); - dm->ReleaseInput(); - hWnd = 0; - hWndTop = 0; - ClearKeyQueue(); - } -} - -u8 CALLBACK PADstartPoll(int port) { - DEBUG_NEW_SET(); - port--; - if ((unsigned int)port <= 1 && pads[port][slots[port]].enabled) { - query.queryDone = 0; - query.port = port; - query.slot = slots[port]; - query.numBytes = 2; - query.lastByte = 0; - DEBUG_IN(port); - DEBUG_OUT(0xFF); - DEBUG_IN(slots[port]); - DEBUG_OUT(pads[port][slots[port]].enabled); - return 0xFF; - } - else { - query.queryDone = 1; - query.numBytes = 0; - query.lastByte = 1; - DEBUG_IN(0); - DEBUG_OUT(0); - DEBUG_IN(port); - DEBUG_OUT(0); - return 0; - } -} - -inline int IsDualshock2(u8 port, u8 slot) { - return config.padConfigs[query.port][query.slot].type == Dualshock2Pad || - (config.padConfigs[query.port][query.slot].type == GuitarPad && config.GH2); -} - -u8 CALLBACK PADpoll(u8 value) { - DEBUG_IN(value); - if (query.lastByte+1 >= query.numBytes) { - DEBUG_OUT(0); - return 0; - } - if (query.lastByte && query.queryDone) { - DEBUG_OUT(query.response[1+query.lastByte]); - return query.response[++query.lastByte]; - } - - int i; - Pad *pad = &pads[query.port][query.slot]; - if (query.lastByte == 0) { - query.lastByte++; - query.currentCommand = value; - switch(value) { - // CONFIG_MODE - case 0x43: - if (pad->config) { - // In config mode. Might not actually be leaving it. - SET_RESULT(ConfigExit); - DEBUG_OUT(0xF3); - return 0xF3; - } - // READ_DATA_AND_VIBRATE - case 0x42: - query.response[2] = 0x5A; - { - Update(query.port, query.slot); - ButtonSum *sum = &pad->sum; - - u8 b1 = 0xFF, b2 = 0xFF; - for (i = 0; i<4; i++) { - b1 -= (sum->buttons[i] > 0) << i; - } - for (i = 0; i<8; i++) { - b2 -= (sum->buttons[i+4] > 0) << i; - } - if (config.padConfigs[query.port][query.slot].type == GuitarPad && !config.GH2) { - sum->sticks[0].horiz = -255; - // Not sure about this. Forces wammy to be from 0 to 0x7F. - // if (sum->sticks[2].vert > 0) sum->sticks[2].vert = 0; - } - b1 -= ((sum->sticks[0].vert < 0) << 4); - b1 -= ((sum->sticks[0].horiz > 0) << 5); - b1 -= ((sum->sticks[0].vert > 0) << 6); - b1 -= ((sum->sticks[0].horiz < 0) << 7); - query.response[3] = b1; - query.response[4] = b2; - - query.numBytes = 5; - if (pad->mode != MODE_DIGITAL) { - query.response[5] = Cap((sum->sticks[1].horiz+255)/2); - query.response[6] = Cap((sum->sticks[1].vert+255)/2); - query.response[7] = Cap((sum->sticks[2].horiz+255)/2); - query.response[8] = Cap((sum->sticks[2].vert+255)/2); - - query.numBytes = 9; - if (pad->mode != MODE_ANALOG) { - // Good idea? No clue. - //query.response[3] &= pad->mask[0]; - //query.response[4] &= pad->mask[1]; - - // Each value is from -255 to 255, so have to use cap to convert - // negative values to 0. - query.response[9] = Cap(sum->sticks[0].horiz); - query.response[10] = Cap(-sum->sticks[0].horiz); - query.response[11] = Cap(-sum->sticks[0].vert); - query.response[12] = Cap(sum->sticks[0].vert); - - // No need to cap these, already done int CapSum(). - query.response[13] = (unsigned char) sum->buttons[8]; - query.response[14] = (unsigned char) sum->buttons[9]; - query.response[15] = (unsigned char) sum->buttons[10]; - query.response[16] = (unsigned char) sum->buttons[11]; - query.response[17] = (unsigned char) sum->buttons[6]; - query.response[18] = (unsigned char) sum->buttons[7]; - query.response[19] = (unsigned char) sum->buttons[4]; - query.response[20] = (unsigned char) sum->buttons[5]; - query.numBytes = 21; - } - } - } - - query.lastByte=1; - DEBUG_OUT(pad->mode); - return pad->mode; - // SET_VREF_PARAM - case 0x40: - SET_FINAL_RESULT(noclue); - break; - // QUERY_DS2_ANALOG_MODE - case 0x41: - // Right? Wrong? No clue. - if (pad->mode == MODE_DIGITAL) { - queryMaskMode[1] = queryMaskMode[2] = queryMaskMode[3] = 0; - queryMaskMode[6] = 0x00; - } - else { - queryMaskMode[1] = pad->umask[0]; - queryMaskMode[2] = pad->umask[1]; - queryMaskMode[3] = 0x03; - // Not entirely sure about this. - //queryMaskMode[3] = 0x01 | (pad->mode == MODE_DS2_NATIVE)*2; - queryMaskMode[6] = 0x5A; - } - SET_FINAL_RESULT(queryMaskMode); - break; - // SET_MODE_AND_LOCK - case 0x44: - SET_RESULT(setMode); - ResetVibrate(query.port, query.slot); - break; - // QUERY_MODEL_AND_MODE - case 0x45: - if (IsDualshock2(query.port, query.slot)) { - SET_FINAL_RESULT(queryModelDS2) - } - else { - SET_FINAL_RESULT(queryModelDS1); - } - // Not digital mode. - query.response[5] = (pad->mode & 0xF) != 1; - break; - // QUERY_ACT - case 0x46: - SET_RESULT(queryAct[0]); - break; - // QUERY_COMB - case 0x47: - SET_FINAL_RESULT(queryComb); - break; - // QUERY_MODE - case 0x4C: - SET_RESULT(queryMode); - break; - // VIBRATION_TOGGLE - case 0x4D: - memcpy(query.response+2, pad->vibrate, 7); - query.numBytes = 9; - ResetVibrate(query.port, query.slot); - break; - // SET_DS2_NATIVE_MODE - case 0x4F: - if (IsDualshock2(query.port, query.slot)) { - SET_RESULT(setNativeMode); - } - else { - SET_FINAL_RESULT(setNativeMode); - } - break; - default: - query.numBytes = 0; - query.queryDone = 1; - break; - } - DEBUG_OUT(0xF3); - return 0xF3; - } - else { - query.lastByte++; - switch (query.currentCommand) { - // READ_DATA_AND_VIBRATE - case 0x42: - if (query.lastByte == pad->vibrateI[0]) { - SetVibrate(query.port, query.slot, 1, 255*(0!=value)); - } - else if (query.lastByte == pad->vibrateI[1]) { - SetVibrate(query.port, query.slot, 0, value); - } - break; - // CONFIG_MODE - case 0x43: - if (query.lastByte == 3) { - query.queryDone = 1; - pad->config = value; - } - break; - // SET_MODE_AND_LOCK - case 0x44: - if (query.lastByte == 3 && value < 2) { - static const u8 modes[2] = {MODE_DIGITAL, MODE_ANALOG}; - pad->mode = modes[value]; - } - else if (query.lastByte == 4) { - if (value == 3) { - pad->modeLock = 3; - } - else { - pad->modeLock = 0; - if (pad->mode == MODE_DIGITAL && config.padConfigs[query.port][query.slot].autoAnalog) { - pad->mode = MODE_ANALOG; - } - } - query.queryDone = 1; - } - break; - // QUERY_ACT - case 0x46: - if (query.lastByte == 3) { - if (value<2) SET_RESULT(queryAct[value]) - // bunch of 0's - // else SET_RESULT(setMode); - query.queryDone = 1; - } - break; - // QUERY_MODE - case 0x4C: - if (query.lastByte == 3 && value<2) { - query.response[6] = 4+value*3; - query.queryDone = 1; - } - // bunch of 0's - //else data = setMode; - break; - // VIBRATION_TOGGLE - case 0x4D: - if (query.lastByte>=3) { - if (value == 0) { - pad->vibrateI[0] = (u8)query.lastByte; - } - else if (value == 1) { - pad->vibrateI[1] = (u8)query.lastByte; - } - pad->vibrate[query.lastByte-2] = value; - } - break; - // SET_DS2_NATIVE_MODE - case 0x4F: - if (query.lastByte == 3 || query.lastByte == 4) { - pad->umask[query.lastByte-3] = value; - } - else if (query.lastByte == 5) { - if (!(value & 1)) { - pad->mode = MODE_DIGITAL; - } - else if (!(value & 2)) { - pad->mode = MODE_ANALOG; - } - else { - pad->mode = MODE_DS2_NATIVE; - } - } - break; - default: - DEBUG_OUT(0); - return 0; - } - DEBUG_OUT(query.response[query.lastByte]); - return query.response[query.lastByte]; - } -} - -// returns: 1 if supports pad1 -// 2 if supports pad2 -// 3 if both are supported -u32 CALLBACK PADquery() { - return 3; -} - -INT_PTR CALLBACK AboutDialogProc(HWND hWndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { - if (uMsg == WM_INITDIALOG) { - wchar_t idString[100]; - GetNameAndVersionString(idString); - SetDlgItemTextW(hWndDlg, IDC_VERSION, idString); - } - else if (uMsg == WM_COMMAND && (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)) { - EndDialog(hWndDlg, 0); - return 1; - } - return 0; -} - - -void CALLBACK PADabout() { - DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUT), 0, AboutDialogProc); -} - -s32 CALLBACK PADtest() { - return 0; -} - -keyEvent* CALLBACK PADkeyEvent() { - // If running both pads, ignore every other call. So if two keys pressed in same interval... - static char eventCount = 0; - eventCount++; - if (eventCount < openCount) { - return 0; - } - eventCount = 0; - - Update(2, 0); - static char shiftDown = 0; - static char altDown = 0; - static keyEvent ev; - if (!GetQueuedKeyEvent(&ev)) return 0; - if ((ev.key == VK_ESCAPE || (int)ev.key == -2) && ev.evt == KEYPRESS && config.escapeFullscreenHack) { - static int t; - if ((int)ev.key != -2 && IsWindowMaximized(hWndTop)) { - t = timeGetTime(); - QueueKeyEvent(-2, KEYPRESS); - HANDLE hThread = CreateThread(0, 0, MaximizeWindowThreadProc, 0, 0, 0); - if (hThread) CloseHandle(hThread); - restoreFullScreen = 1; - return 0; - } - if (ev.key != VK_ESCAPE) { - if (timeGetTime() - t < 1000) { - QueueKeyEvent(-2, KEYPRESS); - return 0; - } - } - ev.key = VK_ESCAPE; - } - - if (ev.key == VK_F2 && ev.evt == KEYPRESS) { - saveStateIndex += 1 - 2*shiftDown; - saveStateIndex = (saveStateIndex+10)%10; - SaveStateChanged(); - } - - // So don't change skip mode on alt-F4. - if (ev.key == VK_F4 && altDown) { - return 0; - } - - if (ev.key == VK_LSHIFT || ev.key == VK_RSHIFT || ev.key == VK_SHIFT) { - ev.key = VK_SHIFT; - shiftDown = (ev.evt == KEYPRESS); - } - else if (ev.key == VK_LCONTROL || ev.key == VK_RCONTROL) { - ev.key = VK_CONTROL; - } - else if (ev.key == VK_LMENU || ev.key == VK_RMENU || ev.key == VK_SHIFT) { - ev.key = VK_MENU; - altDown = (ev.evt == KEYPRESS); - } - return &ev; -} - -struct PadPluginFreezeData { - char format[8]; - // Currently all different versions are incompatible. - // May split into major/minor with some compatibility rules. - u32 version; - // So when loading, know which plugin's settings I'm loading. - // Not a big deal. Use a static variable when saving to figure it out. - u8 port; - // active slot for port - u8 slot; - PadFreezeData padData[4]; - QueryInfo query; -}; - -s32 CALLBACK PADfreeze(int mode, freezeData *data) { - if (mode == FREEZE_SIZE) { - data->size = sizeof(PadPluginFreezeData); - } - else if (mode == FREEZE_LOAD) { - PadPluginFreezeData &pdata = *(PadPluginFreezeData*)(data->data); - StopVibrate(); - if (data->size != sizeof(PadPluginFreezeData) || - pdata.version != PAD_SAVE_STATE_VERSION || - strcmp(pdata.format, "PadMode")) return 0; - unsigned int port = pdata.port; - if (port >= 2) return 0; - if (pdata.query.port == port && pdata.query.slot < 4) { - query = pdata.query; - } - for (int slot=0; slot<4; slot++) { - u8 mode = pdata.padData[slot].mode; - if (mode != MODE_DIGITAL && mode != MODE_ANALOG && mode != MODE_DS2_NATIVE) { - break; - } - - // Not sure if the cast is strictly necessary, but feel safest with it there... - *(PadFreezeData*)&pads[port][slot] = pdata.padData[slot]; - } - if (pdata.slot < 4) - slots[port] = pdata.slot; - } - else if (mode == FREEZE_SAVE) { - if (data->size != sizeof(PadPluginFreezeData)) return 0; - PadPluginFreezeData &pdata = *(PadPluginFreezeData*)(data->data); - static int nextPort = 0; - if (!portInitialized[nextPort]) nextPort ^= 1; - int port = nextPort; - if (!portInitialized[nextPort^1]) nextPort = 0; - else nextPort ^= 1; - - - memset(&pdata, 0, sizeof(pdata)); - strcpy(pdata.format, "PadMode"); - pdata.version = PAD_SAVE_STATE_VERSION; - pdata.port = port; - pdata.slot = slots[port]; - pdata.query = query; - for (int slot=0; slot<4; slot++) { - pdata.padData[slot] = pads[port][slot]; - } - } - else return -1; - return 0; - } - -u32 CALLBACK PADreadPort1 (PadDataS* pads) { - PADstartPoll(1); - PADpoll(0x42); - memcpy(pads, query.response+1, 7); - pads->controllerType = pads[0].controllerType>>4; - memset (pads+7, 0, sizeof(PadDataS)-7); - return 0; -} - -u32 CALLBACK PADreadPort2 (PadDataS* pads) { - PADstartPoll(2); - PADpoll(0x42); - memcpy(pads, query.response+1, 7); - pads->controllerType = pads->controllerType>>4; - memset (pads+7, 0, sizeof(PadDataS)-7); - return 0; -} - -u32 CALLBACK PSEgetLibType() { - return 8; -} - -u32 CALLBACK PSEgetLibVersion() { - return (VERSION & 0xFFFFFF); -} - -s32 CALLBACK PADqueryMtap(u8 port) { - port--; - if (port > 1) return 0; - return config.multitap[port]; -} - -s32 CALLBACK PADsetSlot(u8 port, u8 slot) { - port--; - slot--; - if (port > 1 || slot > 3) { - return 0; - } - // Even if no pad there, record the slot, as it is the active slot regardless. - slots[port] = slot; - // First slot always allowed. - // return pads[port][slot].enabled | !slot; - return 1; -} - -// Little funkiness to handle rounding floating points to ints without the C runtime. -// Unfortunately, means I can't use /GL optimization option when NO_CRT is defined. -#ifdef NO_CRT -extern "C" long _cdecl _ftol(); -extern "C" long _cdecl _ftol2_sse() { - return _ftol(); -} -extern "C" long _cdecl _ftol2() { - return _ftol(); -} -#endif - +#include "Global.h" + +// For escape timer, so as not to break GSDX+DX9. +#include +#include "resource.h" +#include "InputManager.h" +#include "Config.h" + +#define PADdefs + +#include "DeviceEnumerator.h" +#include "WndProcEater.h" +#include "KeyboardQueue.h" +#include "svnrev.h" +#include "DualShock3.h" +#include "HidDevice.h" + +#define WMA_FORCE_UPDATE (WM_APP + 0x537) +#define FORCE_UPDATE_WPARAM ((WPARAM)0x74328943) +#define FORCE_UPDATE_LPARAM ((LPARAM)0x89437437) + +// LilyPad version. +#define VERSION ((0<<8) | 10 | (0<<24)) + +HINSTANCE hInst; +HWND hWnd; +HWND hWndTop; + +WndProcEater hWndGSProc; +WndProcEater hWndTopProc; + +// ButtonProc is used mostly by the Config panel for eating the procedures of the +// button with keyboard focus. +WndProcEater hWndButtonProc; + +// Keeps the various sources for Update polling (PADpoll, PADupdate, etc) from wreaking +// havoc on each other... +CRITICAL_SECTION updateLock; + +// Used to toggle mouse listening. +u8 miceEnabled; + +// 2 when both pads are initialized, 1 for one pad, etc. +int openCount = 0; + +int activeWindow = 0; +int windowThreadId = 0; +int updateQueued = 0; + +int bufSize = 0; +unsigned char outBuf[50]; +unsigned char inBuf[50]; + +// windowThreadId = GetWindowThreadProcessId(hWnd, 0); + +#define MODE_DIGITAL 0x41 +#define MODE_ANALOG 0x73 +#define MODE_DS2_NATIVE 0x79 + +int IsWindowMaximized (HWND hWnd) { + RECT rect; + if (GetWindowRect(hWnd, &rect)) { + POINT p; + p.x = rect.left; + p.y = rect.top; + MONITORINFO info; + memset(&info, 0, sizeof(info)); + info.cbSize = sizeof(info); + HMONITOR hMonitor; + if ((hMonitor = MonitorFromPoint(p, MONITOR_DEFAULTTOPRIMARY)) && + GetMonitorInfo(hMonitor, &info) && + memcmp(&info.rcMonitor, &rect, sizeof(rect)) == 0) { + return 1; + } + } + return 0; +} + +void DEBUG_TEXT_OUT(const char *text) { + if (config.debug) { + HANDLE hFile = CreateFileA("logs\\padLog.txt", FILE_APPEND_DATA, FILE_SHARE_READ, 0, OPEN_ALWAYS, 0, 0); + if (hFile != INVALID_HANDLE_VALUE) { + DWORD junk; + WriteFile(hFile, text, strlen(text), &junk, 0); + CloseHandle(hFile);; + } + } +} + +void DEBUG_NEW_SET() { + if (config.debug && bufSize>1) { + HANDLE hFile = CreateFileA("logs\\padLog.txt", FILE_APPEND_DATA, FILE_SHARE_READ, 0, OPEN_ALWAYS, 0, 0); + if (hFile != INVALID_HANDLE_VALUE) { + int i; + char temp[1500]; + char *end = temp; + sprintf(end, "%02X (%02X) ", inBuf[0], inBuf[1]); + end += 8; + for (i=2; i 0). + u8 enabled; +} pads[2][4]; + +// Active slots for each port. +int slots[2]; +// Which ports we're running on. +int portInitialized[2]; + +// Force value to be from 0 to 255. +u8 Cap (int i) { + if (i<0) return 0; + if (i>255) return 255; + return (u8) i; +} + +inline void ReleaseModifierKeys() { + QueueKeyEvent(VK_SHIFT, KEYRELEASE); + QueueKeyEvent(VK_MENU, KEYRELEASE); + QueueKeyEvent(VK_CONTROL, KEYRELEASE); +} + +// RefreshEnabledDevices() enables everything that can potentially +// be bound to, as well as the "Ignore keyboard" device. +// +// This enables everything that input should be read from while the +// emulator is running. Takes into account mouse and focus state +// and which devices have bindings for enabled pads. Releases +// keyboards if window is not focused. Releases game devices if +// background monitoring is not checked. +// And releases games if not focused and config.background is not set. +void UpdateEnabledDevices(int updateList = 0) { + // Enable all devices I might want. Can ignore the rest. + RefreshEnabledDevices(updateList); + // Figure out which pads I'm getting input for. + for (int port = 0; port<2; port++) { + for (int slot = 0; slot<4; slot++) { + if (slot && !config.multitap[port]) { + pads[port][slot].enabled = 0; + } + else { + pads[port][slot].enabled = pads[port][slot].initialized && config.padConfigs[port][slot].type != DisabledPad; + } + } + } + for (int i=0; inumDevices; i++) { + Device *dev = dm->devices[i]; + + if (!dev->enabled) continue; + if (!dev->attached) { + dm->DisableDevice(i); + continue; + } + + // Disable ignore keyboard if don't have focus or there are no keys to ignore. + if (dev->api == IGNORE_KEYBOARD) { + if ((!config.vistaVolume && (config.keyboardApi == NO_API || !dev->pads[0][0].numBindings)) || !activeWindow) { + dm->DisableDevice(i); + } + continue; + } + // Keep for PCSX2 keyboard shotcuts, unless unfocused. + if (dev->type == KEYBOARD) { + if (!activeWindow) dm->DisableDevice(i); + } + // Keep for cursor hiding consistency, unless unfocused. + // miceEnabled tracks state of mouse enable/disable button, not if mouse API is set to disabled. + else if (dev->type == MOUSE) { + if (!miceEnabled || !activeWindow) dm->DisableDevice(i); + } + else if (!activeWindow && !config.background) dm->DisableDevice(i); + else { + int numActiveBindings = 0; + for (int port=0; port<2; port++) { + for (int slot=0; slot<4; slot++) { + if (pads[port][slot].enabled) { + numActiveBindings += dev->pads[port][slot].numBindings + dev->pads[port][slot].numFFBindings; + } + } + } + if (!numActiveBindings) + dm->DisableDevice(i); + } + } +} + +BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD fdwReason, void* lpvReserved) { + hInst = hInstance; + if (fdwReason == DLL_PROCESS_ATTACH) { + InitializeCriticalSection( &updateLock ); + + DisableThreadLibraryCalls(hInstance); + } + else if (fdwReason == DLL_PROCESS_DETACH) { + while (openCount) + PADclose(); + PADshutdown(); + UninitHid(); + UninitLibUsb(); + DeleteCriticalSection( &updateLock ); + } + return 1; +} + +void AddForce(ButtonSum *sum, u8 cmd, int delta = 255) { + if (!delta) return; + if (cmd<0x14) { + sum->buttons[cmd-0x10] += delta; + } + // D-pad. Command numbering is based on ordering of digital values. + else if (cmd < 0x18) { + if (cmd == 0x14) { + sum->sticks[0].vert -= delta; + } + else if (cmd == 0x15) { + sum->sticks[0].horiz += delta; + } + else if (cmd == 0x16) { + sum->sticks[0].vert += delta; + } + else if (cmd == 0x17) { + sum->sticks[0].horiz -= delta; + } + } + else if (cmd < 0x20) { + sum->buttons[cmd-0x10-4] += delta; + } + // Left stick. + else if (cmd < 0x24) { + if (cmd == 32) { + sum->sticks[2].vert -= delta; + } + else if (cmd == 33) { + sum->sticks[2].horiz += delta; + } + else if (cmd == 34) { + sum->sticks[2].vert += delta; + } + else if (cmd == 35) { + sum->sticks[2].horiz -= delta; + } + } + // Right stick. + else if (cmd < 0x28) { + if (cmd == 36) { + sum->sticks[1].vert -= delta; + } + else if (cmd == 37) { + sum->sticks[1].horiz += delta; + } + else if (cmd == 38) { + sum->sticks[1].vert += delta; + } + else if (cmd == 39) { + sum->sticks[1].horiz -= delta; + } + } +} + +void ProcessButtonBinding(Binding *b, ButtonSum *sum, int value) { + if (value < b->deadZone || !value) return; + + int sensitivity = b->sensitivity; + if (sensitivity < 0) { + sensitivity = -sensitivity; + value = (1<<16)-value; + } + if (value < 0) return; + + /* Note: Value ranges of FULLY_DOWN, and sensitivity of + * BASE_SENSITIVITY corresponds to an axis/button being exactly fully down. + * Math in next line takes care of those two conditions, rounding as necessary. + * Done using __int64s because overflows will occur when + * sensitivity > BASE_SENSITIVITY and/or value > FULLY_DOWN. Latter only happens + * for relative axis. + */ + int force = (int)((((sensitivity*(255*(__int64)value)) + BASE_SENSITIVITY/2)/BASE_SENSITIVITY + FULLY_DOWN/2)/FULLY_DOWN); + AddForce(sum, b->command, force); +} + +// Restricts d-pad/analog stick values to be from -255 to 255 and button values to be from 0 to 255. +// With D-pad in DS2 native mode, the negative and positive ranges are both independently from 0 to 255, +// which is why I use 9 bits of all sticks. For left and right sticks, I have to remove a bit before sending. +void CapSum(ButtonSum *sum) { + int i; + for (i=0; i<3; i++) { + int div = max(abs(sum->sticks[i].horiz), abs(sum->sticks[i].vert)); + if (div > 255) { + sum->sticks[i].horiz = sum->sticks[i].horiz * 255 / div; + sum->sticks[i].vert = sum->sticks[i].vert * 255 / div; + } + } + for (i=0; i<12; i++) { + sum->buttons[i] = Cap(sum->buttons[i]); + } +} + +// Counter similar to stateUpdated for each pad, except used for PADkeyEvent instead. +// Only matters when GS thread updates is disabled (Just like summed pad values +// for pads beyond the first slot). + +// Values, in order, correspond to PADkeyEvent, PADupdate(0), PADupdate(1), and +// WndProc(WMA_FORCE_UPDATE). Last is always 0. +char padReadKeyUpdated[4] = {0, 0, 0, 0}; + +#define LOCK_DIRECTION 2 +#define LOCK_BUTTONS 4 +#define LOCK_BOTH 1 + +struct EnterScopedSection +{ + CRITICAL_SECTION& m_cs; + + EnterScopedSection( CRITICAL_SECTION& cs ) : m_cs( cs ) { + EnterCriticalSection( &m_cs ); + } + + ~EnterScopedSection() { + LeaveCriticalSection( &m_cs ); + } +}; + +void Update(unsigned int port, unsigned int slot) { + char *stateUpdated; + if (port < 2) { + stateUpdated = &pads[port][slot].stateUpdated; + } + else if (port < 6) { + stateUpdated = padReadKeyUpdated+port-2; + } + else return; + + if (*stateUpdated > 0) { + stateUpdated[0] --; + return; + } + + // Lock prior to timecheck code to avoid pesky race conditions. + EnterScopedSection padlock( updateLock ); + + static unsigned int LastCheck = 0; + unsigned int t = timeGetTime(); + if (t - LastCheck < 15 || !openCount) return; + + if (windowThreadId != GetCurrentThreadId()) { + if (stateUpdated[0] < 0) { + if (!updateQueued) { + updateQueued = 1; + PostMessage(hWnd, WMA_FORCE_UPDATE, FORCE_UPDATE_WPARAM, FORCE_UPDATE_LPARAM); + } + } else + { + stateUpdated[0] --; + } + return; + } + + LastCheck = t; + + int i; + ButtonSum s[2][4]; + u8 lockStateChanged[2][4]; + memset(lockStateChanged, 0, sizeof(lockStateChanged)); + + for (i=0; i<8; i++) { + s[i&1][i>>1] = pads[i&1][i>>1].lockedSum; + } + InitInfo info = { + 0, 0, hWndTop, &hWndGSProc + }; + + dm->Update(&info); + static int turbo = 0; + turbo++; + for (i=0; inumDevices; i++) { + Device *dev = dm->devices[i]; + // Skip both disabled devices and inactive enabled devices. + // Shouldn't be any of the latter, in general, but just in case... + if (!dev->active) continue; + for (int port=0; port<2; port++) { + for (int slot=0; slot<4; slot++) { + if (config.padConfigs[port][slot].type == DisabledPad || !pads[port][slot].initialized) continue; + for (int j=0; jpads[port][slot].numBindings; j++) { + Binding *b = dev->pads[port][slot].bindings+j; + int cmd = b->command; + int state = dev->virtualControlState[b->controlIndex]; + if (!(turbo & b->turbo)) { + if (cmd > 0x0F && cmd != 0x28) { + ProcessButtonBinding(b, &s[port][slot], state); + } + else if ((state>>15) && !(dev->oldVirtualControlState[b->controlIndex]>>15)) { + if (cmd == 0x0F) { + miceEnabled = !miceEnabled; + UpdateEnabledDevices(); + } + else if (cmd == 0x0C) { + lockStateChanged[port][slot] |= LOCK_BUTTONS; + } + else if (cmd == 0x0E) { + lockStateChanged[port][slot] |= LOCK_DIRECTION; + } + else if (cmd == 0x0D) { + lockStateChanged[port][slot] |= LOCK_BOTH; + } + else if (cmd == 0x28) { + if (!pads[port][slot].modeLock) { + if (pads[port][slot].mode != MODE_DIGITAL) + pads[port][slot].mode = MODE_DIGITAL; + else + pads[port][slot].mode = MODE_ANALOG; + } + } + } + } + } + } + } + } + dm->PostRead(); + + { + for (int port=0; port<2; port++) { + for (int slot=0; slot<4; slot++) { + for (int motor=0; motor<2; motor++) { + // TODO: Probably be better to send all of these at once. + if (pads[port][slot].nextVibrate[motor] | pads[port][slot].currentVibrate[motor]) { + pads[port][slot].currentVibrate[motor] = pads[port][slot].nextVibrate[motor]; + dm->SetEffect(port,slot, motor, pads[port][slot].nextVibrate[motor]); + } + } + } + } + + for (int port=0; port<2; port++) { + for (int slot=0; slot<4; slot++) { + pads[port][slot].stateUpdated = 1; + if (config.padConfigs[port][slot].type == DisabledPad || !pads[port][slot].initialized) continue; + if (config.padConfigs[port][slot].type == GuitarPad) { + if (!config.GH2) { + s[port][slot].sticks[1].vert = -s[port][slot].sticks[1].vert; + } + // GH2 hack. + else if (config.GH2) { + const unsigned int oldIdList[5] = {ID_R2, ID_CIRCLE, ID_TRIANGLE, ID_CROSS, ID_SQUARE}; + const unsigned int idList[5] = {ID_L2, ID_L1, ID_R1, ID_R2, ID_CROSS}; + int values[5]; + int i; + for (i=0; i<5; i++) { + int id = oldIdList[i] - 0x1104; + values[i] = s[port][slot].buttons[id]; + s[port][slot].buttons[id] = 0; + } + s[port][slot].buttons[ID_TRIANGLE-0x1104] = values[1]; + for (i=0; i<5; i++) { + int id = idList[i] - 0x1104; + s[port][slot].buttons[id] = values[i]; + } + if (abs(s[port][slot].sticks[0].vert) <= 48) { + for (int i=0; i<5; i++) { + unsigned int id = idList[i] - 0x1104; + if (pads[port][slot].sum.buttons[id] < s[port][slot].buttons[id]) { + s[port][slot].buttons[id] = pads[port][slot].sum.buttons[id]; + } + } + } + else if (abs(pads[port][slot].sum.sticks[0].vert) <= 48) { + for (int i=0; i<5; i++) { + unsigned int id = idList[i] - 0x1104; + if (pads[port][slot].sum.buttons[id]) { + s[port][slot].buttons[id] = 0; + } + } + } + } + } + + if (pads[port][slot].mode == 0x41) { + for (int i=1; i<=2; i++) { + if (abs(s[port][slot].sticks[i].horiz) >= 100) + s[port][slot].sticks[0].horiz += s[port][slot].sticks[i].horiz; + if (abs(s[port][slot].sticks[i].vert) >= 100) + s[port][slot].sticks[0].vert += s[port][slot].sticks[i].vert; + } + } + + CapSum(&s[port][slot]); + if (lockStateChanged[port][slot]) { + if (lockStateChanged[port][slot] & LOCK_BOTH) { + if (pads[port][slot].lockedState != (LOCK_DIRECTION | LOCK_BUTTONS)) { + // Enable the one that's not enabled. + lockStateChanged[port][slot] ^= pads[port][slot].lockedState^(LOCK_DIRECTION | LOCK_BUTTONS); + } + else { + // Disable both + lockStateChanged[port][slot] ^= LOCK_DIRECTION | LOCK_BUTTONS; + } + } + if (lockStateChanged[port][slot] & LOCK_DIRECTION) { + if (pads[port][slot].lockedState & LOCK_DIRECTION) { + memset(pads[port][slot].lockedSum.sticks, 0, sizeof(pads[port][slot].lockedSum.sticks)); + } + else { + memcpy(pads[port][slot].lockedSum.sticks, s[port][slot].sticks, sizeof(pads[port][slot].lockedSum.sticks)); + } + pads[port][slot].lockedState ^= LOCK_DIRECTION; + } + if (lockStateChanged[port][slot] & LOCK_BUTTONS) { + if (pads[port][slot].lockedState & LOCK_BUTTONS) { + memset(pads[port][slot].lockedSum.buttons, 0, sizeof(pads[port][slot].lockedSum.buttons)); + } + else { + memcpy(pads[port][slot].lockedSum.buttons, s[port][slot].buttons, sizeof(pads[port][slot].lockedSum.buttons)); + } + pads[port][slot].lockedState ^= LOCK_BUTTONS; + } + for (i=0; i>1].sum = s[i&1][i>>1]; + } + + padReadKeyUpdated[0] = padReadKeyUpdated[1] = padReadKeyUpdated[2] = 1; + + if( stateUpdated[0] > 0 ) + --stateUpdated[0]; +} + +void CALLBACK PADupdate(int port) { + Update(port+3, 0); +} + +inline void SetVibrate(int port, int slot, int motor, u8 val) { + pads[port][slot].nextVibrate[motor] = val; +} + +u32 CALLBACK PS2EgetLibType(void) { + ps2e = 1; + return PS2E_LT_PAD; +} + +u32 CALLBACK PS2EgetLibVersion2(u32 type) { + ps2e = 1; + if (type == PS2E_LT_PAD) + return (PS2E_PAD_VERSION<<16) | VERSION; + return 0; +} + +// Used in about and config screens. +void GetNameAndVersionString(wchar_t *out) { +#ifdef NO_CRT + wsprintfW(out, L"LilyPad %i.%i.%i", (VERSION>>8)&0xFF, VERSION&0xFF, (VERSION>>24)&0xFF, SVN_REV); +#elif defined(PCSX2_DEBUG) + wsprintfW(out, L"LilyPad Debug %i.%i.%i (r%i)", (VERSION>>8)&0xFF, VERSION&0xFF, (VERSION>>24)&0xFF, SVN_REV); +#else + wsprintfW(out, L"LilyPad svn %i.%i.%i (r%i)", (VERSION>>8)&0xFF, VERSION&0xFF, (VERSION>>24)&0xFF, SVN_REV); +#endif +} + +char* CALLBACK PSEgetLibName() { +#ifdef NO_CRT + return "LilyPad"; +#elif defined(PCSX2_DEBUG) + static char version[50]; + sprintf(version, "LilyPad Debug (r%i)", SVN_REV); + return version; +#else + static char version[50]; + sprintf(version, "LilyPad svn (r%i)", SVN_REV); + return version; +#endif +} + +char* CALLBACK PS2EgetLibName(void) { + ps2e = 1; + return PSEgetLibName(); +} + +//void CALLBACK PADgsDriverInfo(GSdriverInfo *info) { +// info=info; +//} + +void CALLBACK PADshutdown() { + DEBUG_TEXT_OUT("LilyPad shutdown.\n\n"); + for (int i=0; i<8; i++) + pads[i&1][i>>1].initialized = 0; + portInitialized[0] = portInitialized[1] = 0; + UnloadConfigs(); +} + +inline void StopVibrate() { + for (int i=0; i<8; i++) { + SetVibrate(i&1, i>>1, 0, 0); + SetVibrate(i&1, i>>1, 1, 0); + } +} + +inline void ResetVibrate(int port, int slot) { + SetVibrate(port, slot, 0, 0); + SetVibrate(port, slot, 1, 0); + ((int*)(pads[port][slot].vibrate))[0] = 0xFFFFFF5A; + ((int*)(pads[port][slot].vibrate))[1] = 0xFFFFFFFF; +} + +void ResetPad(int port, int slot) { + // Lines before memset currently don't do anything useful, + // but allow this function to be called at any time. + + // Need to backup, so can be called at any point. + u8 enabled = pads[port][slot].enabled; + + // Currently should never do anything. + SetVibrate(port, slot, 0, 0); + SetVibrate(port, slot, 1, 0); + + memset(&pads[port][slot], 0, sizeof(pads[0][0])); + pads[port][slot].mode = MODE_DIGITAL; + pads[port][slot].umask[0] = pads[port][slot].umask[1] = 0xFF; + // Sets up vibrate variable. + ResetVibrate(port, slot); + if (config.padConfigs[port][slot].autoAnalog) { + pads[port][slot].mode = MODE_ANALOG; + } + pads[port][slot].initialized = 1; + + pads[port][slot].enabled = enabled; +} + + +struct QueryInfo { + u8 port; + u8 slot; + u8 lastByte; + u8 currentCommand; + u8 numBytes; + u8 queryDone; + u8 response[42]; +} query = {0,0,0,0, 0,0xFF, 0xF3}; + +int saveStateIndex = 0; + +s32 CALLBACK PADinit(u32 flags) { + // Note: Won't load settings if already loaded. + if (LoadSettings() < 0) { + return -1; + } + int port = (flags & 3); + if (port == 3) { + if (PADinit(1) == -1) return -1; + return PADinit(2); + } + + #ifdef PCSX2_DEBUG + int tmpFlag = _CrtSetDbgFlag( _CRTDBG_REPORT_FLAG ); + tmpFlag |= _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF; + _CrtSetDbgFlag( tmpFlag ); + #endif + + port --; + + for (int i=0; i<4; i++) { + ResetPad(port, i); + } + slots[port] = 0; + portInitialized[port] = 1; + + query.lastByte = 1; + query.numBytes = 0; + ClearKeyQueue(); + // Just in case, when resuming emulation. + ReleaseModifierKeys(); + + DEBUG_TEXT_OUT("LilyPad initialized\n\n"); + return 0; +} + + + +// Note to self: Has to be a define for the sizeof() to work right. +// Note to self 2: All are the same size, anyways, except for longer full DS2 response +// and shorter digital mode response. +#define SET_RESULT(a) { \ + memcpy(query.response+2, a, sizeof(a)); \ + query.numBytes = 2+sizeof(a); \ +} + +#define SET_FINAL_RESULT(a) { \ + memcpy(query.response+2, a, sizeof(a));\ + query.numBytes = 2+sizeof(a); \ + query.queryDone = 1; \ +} + +static const u8 ConfigExit[7] = {0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; +//static const u8 ConfigExit[7] = {0x5A, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00}; + +static const u8 noclue[7] = {0x5A, 0x00, 0x00, 0x02, 0x00, 0x00, 0x5A}; +static u8 queryMaskMode[7] = {0x5A, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x5A}; +//static const u8 DSNonNativeMode[7] = {0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; +static const u8 setMode[7] = {0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +// DS2 +static const u8 queryModelDS2[7] = {0x5A, 0x03, 0x02, 0x00, 0x02, 0x01, 0x00}; +// DS1 +static const u8 queryModelDS1[7] = {0x5A, 0x01, 0x02, 0x00, 0x02, 0x01, 0x00}; + +static const u8 queryAct[2][7] = {{0x5A, 0x00, 0x00, 0x01, 0x02, 0x00, 0x0A}, + {0x5A, 0x00, 0x00, 0x01, 0x01, 0x01, 0x14}}; + +static const u8 queryComb[7] = {0x5A, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00}; + +static const u8 queryMode[7] = {0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + +static const u8 setNativeMode[7] = {0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5A}; + +// Implements a couple of the hacks that affect whatever top-level window +// the GS viewport belongs to (title, screensaver) +ExtraWndProcResult TitleHackWndProc(HWND hWndTop, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *output) { + switch (uMsg) { + case WM_SETTEXT: + if (config.saveStateTitle) { + wchar_t text[200]; + int len; + if (IsWindowUnicode(hWndTop)) { + len = wcslen((wchar_t*) lParam); + if (len < sizeof(text)/sizeof(wchar_t)) wcscpy(text, (wchar_t*) lParam); + } + else { + len = MultiByteToWideChar(CP_ACP, 0, (char*) lParam, -1, text, sizeof(text)/sizeof(wchar_t)); + } + if (len > 0 && len < 150 && !wcsstr(text, L" | State ")) { + wsprintfW(text+len, L" | State %i", saveStateIndex); + SetWindowText(hWndTop, text); + return NO_WND_PROC; + } + } + break; + case WM_SYSCOMMAND: + if ((wParam == SC_SCREENSAVE || wParam == SC_MONITORPOWER) && config.disableScreenSaver) + return NO_WND_PROC; + break; + default: + break; + } + return CONTINUE_BLISSFULLY; +} + +// responsible for monitoring device addition/removal, focus changes, and viewport closures. +ExtraWndProcResult StatusWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *output) { + switch (uMsg) { + case WMA_FORCE_UPDATE: + if (wParam == FORCE_UPDATE_WPARAM && lParam == FORCE_UPDATE_LPARAM) { + if (updateQueued) { + updateQueued = 0; + Update(5, 0); + } + return NO_WND_PROC; + } + case WM_DEVICECHANGE: + if (wParam == DBT_DEVNODES_CHANGED) { + UpdateEnabledDevices(1); + } + break; + case WM_ACTIVATE: + // Release any buttons PCSX2 may think are down when + // losing/gaining focus. + if (!wParam) { + ReleaseModifierKeys(); + } + activeWindow = (LOWORD(wParam) != WA_INACTIVE); + UpdateEnabledDevices(); + break; + case WM_CLOSE: + if (config.closeHacks & 1) { + QueueKeyEvent(VK_ESCAPE, KEYPRESS); + return NO_WND_PROC; + } + else if (config.closeHacks & 2) { + ExitProcess(0); + return NO_WND_PROC; + } + break; + case WM_DESTROY: + QueueKeyEvent(VK_ESCAPE, KEYPRESS); + break; + default: + break; + } + return CONTINUE_BLISSFULLY; +} + +// All that's needed to force hiding the cursor in the proper thread. +// Could have a special case elsewhere, but this make sure it's called +// only once, rather than repeatedly. +ExtraWndProcResult HideCursorProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *output) { + ShowCursor(0); + return CONTINUE_BLISSFULLY_AND_RELEASE_PROC; +} + +char restoreFullScreen = 0; +DWORD WINAPI MaximizeWindowThreadProc(void *lpParameter) { + Sleep(100); + keybd_event(VK_LMENU, MapVirtualKey(VK_LMENU, MAPVK_VK_TO_VSC), 0, 0); + keybd_event(VK_RETURN, MapVirtualKey(VK_RETURN, MAPVK_VK_TO_VSC), 0, 0); + Sleep(10); + keybd_event(VK_RETURN, MapVirtualKey(VK_RETURN, MAPVK_VK_TO_VSC), KEYEVENTF_KEYUP, 0); + keybd_event(VK_LMENU, MapVirtualKey(VK_LMENU, MAPVK_VK_TO_VSC), KEYEVENTF_KEYUP, 0); + return 0; +} + +void CALLBACK PADconfigure() { + if (openCount) { + return; + } + Configure(); +} + + +DWORD WINAPI RenameWindowThreadProc(void *lpParameter) { + wchar_t newTitle[200]; + if (hWndTop) { + int len = GetWindowTextW(hWndTop, newTitle, 200); + if (len > 0 && len < 199) { + wchar_t *end; + if (end = wcsstr(newTitle, L" | State ")) *end = 0; + SetWindowTextW(hWndTop, newTitle); + } + } + return 0; +} + +void SaveStateChanged() { + if (config.saveStateTitle) { + // GSDX only checks its window's message queue at certain points or something, so + // have to do this in another thread to prevent deadlock. + HANDLE hThread = CreateThread(0, 0, RenameWindowThreadProc, 0, 0, 0); + if (hThread) CloseHandle(hThread); + } +} + +s32 CALLBACK PADopen(void *pDsp) { + if (openCount++) return 0; + DEBUG_TEXT_OUT("LilyPad opened\n\n"); + + miceEnabled = !config.mouseUnfocus; + if (!hWnd) { + if (IsWindow((HWND)pDsp)) { + hWnd = (HWND) pDsp; + } + else if (pDsp && !IsBadReadPtr(pDsp, 4) && IsWindow(*(HWND*) pDsp)) { + hWnd = *(HWND*) pDsp; + } + else { + openCount = 0; + MessageBoxA(GetActiveWindow(), + "Invalid Window handle passed to LilyPad.\n" + "\n" + "Either your emulator or gs plugin is buggy,\n" + "Despite the fact the emulator is about to\n" + "blame LilyPad for failing to initialize.", + "Non-LilyPad Error", MB_OK | MB_ICONERROR); + return -1; + } + hWndTop = hWnd; + while (GetWindowLong (hWndTop, GWL_STYLE) & WS_CHILD) + hWndTop = GetParent (hWndTop); + + if (!hWndGSProc.SetWndHandle(hWnd)) { + openCount = 0; + return -1; + } + + // Implements most hacks, as well as enabling/disabling mouse + // capture when focus changes. + updateQueued = 0; + hWndGSProc.Eat(StatusWndProc, 0); + + if(hWnd != hWndTop) { + if (!hWndTopProc.SetWndHandle(hWndTop)) { + openCount = 0; + return -1; + } + hWndTopProc.Eat(TitleHackWndProc, 0); + } + else + hWndGSProc.Eat(TitleHackWndProc, 0); + + if (config.forceHide) { + hWndGSProc.Eat(HideCursorProc, 0); + } + SaveStateChanged(); + + windowThreadId = GetWindowThreadProcessId(hWndTop, 0); + } + + if (restoreFullScreen) { + if (!IsWindowMaximized(hWndTop)) { + HANDLE hThread = CreateThread(0, 0, MaximizeWindowThreadProc, hWndTop, 0, 0); + if (hThread) CloseHandle(hThread); + } + restoreFullScreen = 0; + } + for (int port=0; port<2; port++) { + for (int slot=0; slot<4; slot++) { + memset(&pads[port][slot].sum, 0, sizeof(pads[port][slot].sum)); + memset(&pads[port][slot].lockedSum, 0, sizeof(pads[port][slot].lockedSum)); + pads[port][slot].lockedState = 0; + } + } + + // I'd really rather use this line, but GetActiveWindow() does not have complete specs. + // It *seems* to return null when no window from this thread has focus, but the + // Microsoft specs seem to imply it returns the window from this thread that would have focus, + // if any window did (topmost in this thread?). Which isn't what I want, and doesn't seem + // to be what it actually does. + // activeWindow = GetActiveWindow() == hWnd; + + // activeWindow = (GetAncestor(hWnd, GA_ROOT) == GetAncestor(GetForegroundWindow(), GA_ROOT)); + activeWindow = 1; + UpdateEnabledDevices(); + return 0; +} + +void CALLBACK PADclose() { + if (openCount && !--openCount) { + DEBUG_TEXT_OUT("LilyPad closed\n\n"); + updateQueued = 0; + hWndGSProc.Release(); + hWndTopProc.Release(); + dm->ReleaseInput(); + hWnd = 0; + hWndTop = 0; + ClearKeyQueue(); + } +} + +u8 CALLBACK PADstartPoll(int port) { + DEBUG_NEW_SET(); + port--; + if ((unsigned int)port <= 1 && pads[port][slots[port]].enabled) { + query.queryDone = 0; + query.port = port; + query.slot = slots[port]; + query.numBytes = 2; + query.lastByte = 0; + DEBUG_IN(port); + DEBUG_OUT(0xFF); + DEBUG_IN(slots[port]); + DEBUG_OUT(pads[port][slots[port]].enabled); + return 0xFF; + } + else { + query.queryDone = 1; + query.numBytes = 0; + query.lastByte = 1; + DEBUG_IN(0); + DEBUG_OUT(0); + DEBUG_IN(port); + DEBUG_OUT(0); + return 0; + } +} + +inline int IsDualshock2(u8 port, u8 slot) { + return config.padConfigs[query.port][query.slot].type == Dualshock2Pad || + (config.padConfigs[query.port][query.slot].type == GuitarPad && config.GH2); +} + +u8 CALLBACK PADpoll(u8 value) { + DEBUG_IN(value); + if (query.lastByte+1 >= query.numBytes) { + DEBUG_OUT(0); + return 0; + } + if (query.lastByte && query.queryDone) { + DEBUG_OUT(query.response[1+query.lastByte]); + return query.response[++query.lastByte]; + } + + int i; + Pad *pad = &pads[query.port][query.slot]; + if (query.lastByte == 0) { + query.lastByte++; + query.currentCommand = value; + switch(value) { + // CONFIG_MODE + case 0x43: + if (pad->config) { + // In config mode. Might not actually be leaving it. + SET_RESULT(ConfigExit); + DEBUG_OUT(0xF3); + return 0xF3; + } + // READ_DATA_AND_VIBRATE + case 0x42: + query.response[2] = 0x5A; + { + Update(query.port, query.slot); + ButtonSum *sum = &pad->sum; + + u8 b1 = 0xFF, b2 = 0xFF; + for (i = 0; i<4; i++) { + b1 -= (sum->buttons[i] > 0) << i; + } + for (i = 0; i<8; i++) { + b2 -= (sum->buttons[i+4] > 0) << i; + } + if (config.padConfigs[query.port][query.slot].type == GuitarPad && !config.GH2) { + sum->sticks[0].horiz = -255; + // Not sure about this. Forces wammy to be from 0 to 0x7F. + // if (sum->sticks[2].vert > 0) sum->sticks[2].vert = 0; + } + b1 -= ((sum->sticks[0].vert < 0) << 4); + b1 -= ((sum->sticks[0].horiz > 0) << 5); + b1 -= ((sum->sticks[0].vert > 0) << 6); + b1 -= ((sum->sticks[0].horiz < 0) << 7); + query.response[3] = b1; + query.response[4] = b2; + + query.numBytes = 5; + if (pad->mode != MODE_DIGITAL) { + query.response[5] = Cap((sum->sticks[1].horiz+255)/2); + query.response[6] = Cap((sum->sticks[1].vert+255)/2); + query.response[7] = Cap((sum->sticks[2].horiz+255)/2); + query.response[8] = Cap((sum->sticks[2].vert+255)/2); + + query.numBytes = 9; + if (pad->mode != MODE_ANALOG) { + // Good idea? No clue. + //query.response[3] &= pad->mask[0]; + //query.response[4] &= pad->mask[1]; + + // Each value is from -255 to 255, so have to use cap to convert + // negative values to 0. + query.response[9] = Cap(sum->sticks[0].horiz); + query.response[10] = Cap(-sum->sticks[0].horiz); + query.response[11] = Cap(-sum->sticks[0].vert); + query.response[12] = Cap(sum->sticks[0].vert); + + // No need to cap these, already done int CapSum(). + query.response[13] = (unsigned char) sum->buttons[8]; + query.response[14] = (unsigned char) sum->buttons[9]; + query.response[15] = (unsigned char) sum->buttons[10]; + query.response[16] = (unsigned char) sum->buttons[11]; + query.response[17] = (unsigned char) sum->buttons[6]; + query.response[18] = (unsigned char) sum->buttons[7]; + query.response[19] = (unsigned char) sum->buttons[4]; + query.response[20] = (unsigned char) sum->buttons[5]; + query.numBytes = 21; + } + } + } + + query.lastByte=1; + DEBUG_OUT(pad->mode); + return pad->mode; + // SET_VREF_PARAM + case 0x40: + SET_FINAL_RESULT(noclue); + break; + // QUERY_DS2_ANALOG_MODE + case 0x41: + // Right? Wrong? No clue. + if (pad->mode == MODE_DIGITAL) { + queryMaskMode[1] = queryMaskMode[2] = queryMaskMode[3] = 0; + queryMaskMode[6] = 0x00; + } + else { + queryMaskMode[1] = pad->umask[0]; + queryMaskMode[2] = pad->umask[1]; + queryMaskMode[3] = 0x03; + // Not entirely sure about this. + //queryMaskMode[3] = 0x01 | (pad->mode == MODE_DS2_NATIVE)*2; + queryMaskMode[6] = 0x5A; + } + SET_FINAL_RESULT(queryMaskMode); + break; + // SET_MODE_AND_LOCK + case 0x44: + SET_RESULT(setMode); + ResetVibrate(query.port, query.slot); + break; + // QUERY_MODEL_AND_MODE + case 0x45: + if (IsDualshock2(query.port, query.slot)) { + SET_FINAL_RESULT(queryModelDS2) + } + else { + SET_FINAL_RESULT(queryModelDS1); + } + // Not digital mode. + query.response[5] = (pad->mode & 0xF) != 1; + break; + // QUERY_ACT + case 0x46: + SET_RESULT(queryAct[0]); + break; + // QUERY_COMB + case 0x47: + SET_FINAL_RESULT(queryComb); + break; + // QUERY_MODE + case 0x4C: + SET_RESULT(queryMode); + break; + // VIBRATION_TOGGLE + case 0x4D: + memcpy(query.response+2, pad->vibrate, 7); + query.numBytes = 9; + ResetVibrate(query.port, query.slot); + break; + // SET_DS2_NATIVE_MODE + case 0x4F: + if (IsDualshock2(query.port, query.slot)) { + SET_RESULT(setNativeMode); + } + else { + SET_FINAL_RESULT(setNativeMode); + } + break; + default: + query.numBytes = 0; + query.queryDone = 1; + break; + } + DEBUG_OUT(0xF3); + return 0xF3; + } + else { + query.lastByte++; + switch (query.currentCommand) { + // READ_DATA_AND_VIBRATE + case 0x42: + if (query.lastByte == pad->vibrateI[0]) { + SetVibrate(query.port, query.slot, 1, 255*(0!=value)); + } + else if (query.lastByte == pad->vibrateI[1]) { + SetVibrate(query.port, query.slot, 0, value); + } + break; + // CONFIG_MODE + case 0x43: + if (query.lastByte == 3) { + query.queryDone = 1; + pad->config = value; + } + break; + // SET_MODE_AND_LOCK + case 0x44: + if (query.lastByte == 3 && value < 2) { + static const u8 modes[2] = {MODE_DIGITAL, MODE_ANALOG}; + pad->mode = modes[value]; + } + else if (query.lastByte == 4) { + if (value == 3) { + pad->modeLock = 3; + } + else { + pad->modeLock = 0; + if (pad->mode == MODE_DIGITAL && config.padConfigs[query.port][query.slot].autoAnalog) { + pad->mode = MODE_ANALOG; + } + } + query.queryDone = 1; + } + break; + // QUERY_ACT + case 0x46: + if (query.lastByte == 3) { + if (value<2) SET_RESULT(queryAct[value]) + // bunch of 0's + // else SET_RESULT(setMode); + query.queryDone = 1; + } + break; + // QUERY_MODE + case 0x4C: + if (query.lastByte == 3 && value<2) { + query.response[6] = 4+value*3; + query.queryDone = 1; + } + // bunch of 0's + //else data = setMode; + break; + // VIBRATION_TOGGLE + case 0x4D: + if (query.lastByte>=3) { + if (value == 0) { + pad->vibrateI[0] = (u8)query.lastByte; + } + else if (value == 1) { + pad->vibrateI[1] = (u8)query.lastByte; + } + pad->vibrate[query.lastByte-2] = value; + } + break; + // SET_DS2_NATIVE_MODE + case 0x4F: + if (query.lastByte == 3 || query.lastByte == 4) { + pad->umask[query.lastByte-3] = value; + } + else if (query.lastByte == 5) { + if (!(value & 1)) { + pad->mode = MODE_DIGITAL; + } + else if (!(value & 2)) { + pad->mode = MODE_ANALOG; + } + else { + pad->mode = MODE_DS2_NATIVE; + } + } + break; + default: + DEBUG_OUT(0); + return 0; + } + DEBUG_OUT(query.response[query.lastByte]); + return query.response[query.lastByte]; + } +} + +// returns: 1 if supports pad1 +// 2 if supports pad2 +// 3 if both are supported +u32 CALLBACK PADquery() { + return 3; +} + +INT_PTR CALLBACK AboutDialogProc(HWND hWndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { + if (uMsg == WM_INITDIALOG) { + wchar_t idString[100]; + GetNameAndVersionString(idString); + SetDlgItemTextW(hWndDlg, IDC_VERSION, idString); + } + else if (uMsg == WM_COMMAND && (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)) { + EndDialog(hWndDlg, 0); + return 1; + } + return 0; +} + + +void CALLBACK PADabout() { + DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUT), 0, AboutDialogProc); +} + +s32 CALLBACK PADtest() { + return 0; +} + +keyEvent* CALLBACK PADkeyEvent() { + // If running both pads, ignore every other call. So if two keys pressed in same interval... + static char eventCount = 0; + eventCount++; + if (eventCount < openCount) { + return 0; + } + eventCount = 0; + + Update(2, 0); + static char shiftDown = 0; + static char altDown = 0; + static keyEvent ev; + if (!GetQueuedKeyEvent(&ev)) return 0; + if ((ev.key == VK_ESCAPE || (int)ev.key == -2) && ev.evt == KEYPRESS && config.escapeFullscreenHack) { + static int t; + if ((int)ev.key != -2 && IsWindowMaximized(hWndTop)) { + t = timeGetTime(); + QueueKeyEvent(-2, KEYPRESS); + HANDLE hThread = CreateThread(0, 0, MaximizeWindowThreadProc, 0, 0, 0); + if (hThread) CloseHandle(hThread); + restoreFullScreen = 1; + return 0; + } + if (ev.key != VK_ESCAPE) { + if (timeGetTime() - t < 1000) { + QueueKeyEvent(-2, KEYPRESS); + return 0; + } + } + ev.key = VK_ESCAPE; + } + + if (ev.key == VK_F2 && ev.evt == KEYPRESS) { + saveStateIndex += 1 - 2*shiftDown; + saveStateIndex = (saveStateIndex+10)%10; + SaveStateChanged(); + } + + // So don't change skip mode on alt-F4. + if (ev.key == VK_F4 && altDown) { + return 0; + } + + if (ev.key == VK_LSHIFT || ev.key == VK_RSHIFT || ev.key == VK_SHIFT) { + ev.key = VK_SHIFT; + shiftDown = (ev.evt == KEYPRESS); + } + else if (ev.key == VK_LCONTROL || ev.key == VK_RCONTROL) { + ev.key = VK_CONTROL; + } + else if (ev.key == VK_LMENU || ev.key == VK_RMENU || ev.key == VK_SHIFT) { + ev.key = VK_MENU; + altDown = (ev.evt == KEYPRESS); + } + return &ev; +} + +struct PadPluginFreezeData { + char format[8]; + // Currently all different versions are incompatible. + // May split into major/minor with some compatibility rules. + u32 version; + // So when loading, know which plugin's settings I'm loading. + // Not a big deal. Use a static variable when saving to figure it out. + u8 port; + // active slot for port + u8 slot; + PadFreezeData padData[4]; + QueryInfo query; +}; + +s32 CALLBACK PADfreeze(int mode, freezeData *data) { + if (mode == FREEZE_SIZE) { + data->size = sizeof(PadPluginFreezeData); + } + else if (mode == FREEZE_LOAD) { + PadPluginFreezeData &pdata = *(PadPluginFreezeData*)(data->data); + StopVibrate(); + if (data->size != sizeof(PadPluginFreezeData) || + pdata.version != PAD_SAVE_STATE_VERSION || + strcmp(pdata.format, "PadMode")) return 0; + unsigned int port = pdata.port; + if (port >= 2) return 0; + if (pdata.query.port == port && pdata.query.slot < 4) { + query = pdata.query; + } + for (int slot=0; slot<4; slot++) { + u8 mode = pdata.padData[slot].mode; + if (mode != MODE_DIGITAL && mode != MODE_ANALOG && mode != MODE_DS2_NATIVE) { + break; + } + + // Not sure if the cast is strictly necessary, but feel safest with it there... + *(PadFreezeData*)&pads[port][slot] = pdata.padData[slot]; + } + if (pdata.slot < 4) + slots[port] = pdata.slot; + } + else if (mode == FREEZE_SAVE) { + if (data->size != sizeof(PadPluginFreezeData)) return 0; + PadPluginFreezeData &pdata = *(PadPluginFreezeData*)(data->data); + static int nextPort = 0; + if (!portInitialized[nextPort]) nextPort ^= 1; + int port = nextPort; + if (!portInitialized[nextPort^1]) nextPort = 0; + else nextPort ^= 1; + + + memset(&pdata, 0, sizeof(pdata)); + strcpy(pdata.format, "PadMode"); + pdata.version = PAD_SAVE_STATE_VERSION; + pdata.port = port; + pdata.slot = slots[port]; + pdata.query = query; + for (int slot=0; slot<4; slot++) { + pdata.padData[slot] = pads[port][slot]; + } + } + else return -1; + return 0; + } + +u32 CALLBACK PADreadPort1 (PadDataS* pads) { + PADstartPoll(1); + PADpoll(0x42); + memcpy(pads, query.response+1, 7); + pads->controllerType = pads[0].controllerType>>4; + memset (pads+7, 0, sizeof(PadDataS)-7); + return 0; +} + +u32 CALLBACK PADreadPort2 (PadDataS* pads) { + PADstartPoll(2); + PADpoll(0x42); + memcpy(pads, query.response+1, 7); + pads->controllerType = pads->controllerType>>4; + memset (pads+7, 0, sizeof(PadDataS)-7); + return 0; +} + +u32 CALLBACK PSEgetLibType() { + return 8; +} + +u32 CALLBACK PSEgetLibVersion() { + return (VERSION & 0xFFFFFF); +} + +s32 CALLBACK PADqueryMtap(u8 port) { + port--; + if (port > 1) return 0; + return config.multitap[port]; +} + +s32 CALLBACK PADsetSlot(u8 port, u8 slot) { + port--; + slot--; + if (port > 1 || slot > 3) { + return 0; + } + // Even if no pad there, record the slot, as it is the active slot regardless. + slots[port] = slot; + // First slot always allowed. + // return pads[port][slot].enabled | !slot; + return 1; +} + +// Little funkiness to handle rounding floating points to ints without the C runtime. +// Unfortunately, means I can't use /GL optimization option when NO_CRT is defined. +#ifdef NO_CRT +extern "C" long _cdecl _ftol(); +extern "C" long _cdecl _ftol2_sse() { + return _ftol(); +} +extern "C" long _cdecl _ftol2() { + return _ftol(); +} +#endif + diff --git a/plugins/LilyPad/RawInput.cpp b/plugins/LilyPad/RawInput.cpp index 8df6db392e..004aef2f35 100644 --- a/plugins/LilyPad/RawInput.cpp +++ b/plugins/LilyPad/RawInput.cpp @@ -1,317 +1,317 @@ -#include "Global.h" -#include "InputManager.h" -#include "WindowsMessaging.h" -#include "VKey.h" -#include "DeviceEnumerator.h" -#include "WndProcEater.h" -#include "WindowsKeyboard.h" -#include "WindowsMouse.h" - - -typedef BOOL (CALLBACK *_RegisterRawInputDevices)(PCRAWINPUTDEVICE pRawInputDevices, UINT uiNumDevices, UINT cbSize); -typedef UINT (CALLBACK *_GetRawInputDeviceInfo)(HANDLE hDevice, UINT uiCommand, LPVOID pData, PUINT pcbSize); -typedef UINT (CALLBACK *_GetRawInputData)(HRAWINPUT hRawInput, UINT uiCommand, LPVOID pData, PUINT pcbSize, UINT cbSizeHeader); -typedef UINT (CALLBACK *_GetRawInputDeviceList)(PRAWINPUTDEVICELIST pRawInputDeviceList, PUINT puiNumDevices, UINT cbSize); - -_RegisterRawInputDevices pRegisterRawInputDevices = 0; -_GetRawInputDeviceInfo pGetRawInputDeviceInfo = 0; -_GetRawInputData pGetRawInputData = 0; -_GetRawInputDeviceList pGetRawInputDeviceList = 0; - -ExtraWndProcResult RawInputWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *output); - -int GetRawKeyboards(HWND hWnd) { - RAWINPUTDEVICE Rid; - Rid.hwndTarget = hWnd; - - Rid.dwFlags = 0; - Rid.usUsagePage = 0x01; - Rid.usUsage = 0x06; - return pRegisterRawInputDevices(&Rid, 1, sizeof(Rid)); -} - -void ReleaseRawKeyboards() { - RAWINPUTDEVICE Rid; - Rid.hwndTarget = 0; - - Rid.dwFlags = RIDEV_REMOVE; - Rid.usUsagePage = 0x01; - Rid.usUsage = 0x06; - pRegisterRawInputDevices(&Rid, 1, sizeof(Rid)); -} - -int GetRawMice(HWND hWnd) { - RAWINPUTDEVICE Rid; - Rid.hwndTarget = hWnd; - - Rid.dwFlags = RIDEV_NOLEGACY | RIDEV_CAPTUREMOUSE; - Rid.usUsagePage = 0x01; - Rid.usUsage = 0x02; - return pRegisterRawInputDevices(&Rid, 1, sizeof(Rid)); -} - -void ReleaseRawMice() { - RAWINPUTDEVICE Rid; - Rid.hwndTarget = 0; - - Rid.dwFlags = RIDEV_REMOVE; - Rid.usUsagePage = 0x01; - Rid.usUsage = 0x02; - pRegisterRawInputDevices(&Rid, 1, sizeof(Rid)); -} - -// Count of active raw keyboard devices. -// when it gets to 0, release them all. -static int rawKeyboardActivatedCount = 0; -// Same for mice. -static int rawMouseActivatedCount = 0; - -class RawInputKeyboard : public WindowsKeyboard { -public: - HANDLE hDevice; - - RawInputKeyboard(HANDLE hDevice, wchar_t *name, wchar_t *instanceID=0) : WindowsKeyboard(RAW, name, instanceID) { - this->hDevice = hDevice; - } - - int Activate(InitInfo *initInfo) { - Deactivate(); - - hWndProc = initInfo->hWndProc; - - active = 1; - if (!rawKeyboardActivatedCount++) { - if (!rawMouseActivatedCount) - hWndProc->Eat(RawInputWndProc, EATPROC_NO_UPDATE_WHILE_UPDATING_DEVICES); - - if (!GetRawKeyboards(hWndProc->hWndEaten)) { - Deactivate(); - return 0; - } - } - - InitState(); - return 1; - } - - void Deactivate() { - FreeState(); - if (active) { - active = 0; - rawKeyboardActivatedCount --; - if (!rawKeyboardActivatedCount) { - ReleaseRawKeyboards(); - if (!rawMouseActivatedCount) - hWndProc->ReleaseExtraProc(RawInputWndProc); - } - } - } -}; - -class RawInputMouse : public WindowsMouse { -public: - HANDLE hDevice; - - RawInputMouse(HANDLE hDevice, wchar_t *name, wchar_t *instanceID=0, wchar_t *productID=0) : WindowsMouse(RAW, 0, name, instanceID, productID) { - this->hDevice = hDevice; - } - - int Activate(InitInfo *initInfo) { - Deactivate(); - - hWndProc = initInfo->hWndProc; - - active = 1; - - // Have to be careful with order. At worst, one unmatched call to ReleaseRawMice on - // EatWndProc fail. In all other cases, no unmatched initialization/cleanup - // lines. - if (!rawMouseActivatedCount++) { - GetMouseCapture(hWndProc->hWndEaten); - if (!rawKeyboardActivatedCount) - hWndProc->Eat(RawInputWndProc, EATPROC_NO_UPDATE_WHILE_UPDATING_DEVICES); - - if (!GetRawMice(hWndProc->hWndEaten)) { - Deactivate(); - return 0; - } - } - - AllocState(); - return 1; - } - - void Deactivate() { - FreeState(); - if (active) { - active = 0; - rawMouseActivatedCount --; - if (!rawMouseActivatedCount) { - ReleaseRawMice(); - ReleaseMouseCapture(); - if (!rawKeyboardActivatedCount) { - hWndProc->ReleaseExtraProc(RawInputWndProc); - } - } - } - } -}; - -ExtraWndProcResult RawInputWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *output) { - if (uMsg == WM_INPUT) { - if (GET_RAWINPUT_CODE_WPARAM (wParam) == RIM_INPUT && pGetRawInputData) { - RAWINPUT in; - unsigned int size = sizeof(RAWINPUT); - if (0 < pGetRawInputData((HRAWINPUT)lParam, RID_INPUT, &in, &size, sizeof(RAWINPUTHEADER))) { - for (int i=0; inumDevices; i++) { - Device *dev = dm->devices[i]; - if (dev->api != RAW || !dev->active) continue; - if (in.header.dwType == RIM_TYPEKEYBOARD && dev->type == KEYBOARD) { - RawInputKeyboard* rik = (RawInputKeyboard*)dev; - if (rik->hDevice != in.header.hDevice) continue; - - u32 uMsg = in.data.keyboard.Message; - if (!(in.data.keyboard.VKey>>8)) - rik->UpdateKey((u8) in.data.keyboard.VKey, (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN)); - } - else if (in.header.dwType == RIM_TYPEMOUSE && dev->type == MOUSE) { - RawInputMouse* rim = (RawInputMouse*)dev; - if (rim->hDevice != in.header.hDevice) continue; - if (in.data.mouse.usFlags) { - // Never been set for me, and specs on what most of them - // actually mean is sorely lacking. Also, specs erroneously - // indicate MOUSE_MOVE_RELATIVE is a flag, when it's really - // 0... - continue; - } - - unsigned short buttons = in.data.mouse.usButtonFlags & 0x3FF; - int button = 0; - while (buttons) { - if (buttons & 3) { - // 2 is up, 1 is down. Up takes precedence over down. - rim->UpdateButton(button, !(buttons & 2)); - } - button++; - buttons >>= 2; - } - if (in.data.mouse.usButtonFlags & RI_MOUSE_WHEEL) { - rim->UpdateAxis(2, ((short)in.data.mouse.usButtonData)/WHEEL_DELTA); - } - if (in.data.mouse.lLastX || in.data.mouse.lLastY) { - rim->UpdateAxis(0, in.data.mouse.lLastX); - rim->UpdateAxis(1, in.data.mouse.lLastY); - } - } - } - } - } - } - else if (uMsg == WM_ACTIVATE) { - for (int i=0; inumDevices; i++) { - Device *dev = dm->devices[i]; - if (dev->api != RAW || dev->physicalControlState == 0) continue; - memset(dev->physicalControlState, 0, sizeof(int) * dev->numPhysicalControls); - } - } - else if (uMsg == WM_SIZE && rawMouseActivatedCount) { - // Doesn't really matter for raw mice, as I disable legacy stuff, but shouldn't hurt. - WindowsMouse::WindowResized(hWnd); - } - - return CONTINUE_BLISSFULLY; -} - -int InitializeRawInput() { - static int RawInputFailed = 0; - if (RawInputFailed) return 0; - if (!pGetRawInputDeviceList) { - HMODULE user32 = LoadLibrary(L"user32.dll"); - if (!user32 || - !(pRegisterRawInputDevices = (_RegisterRawInputDevices) GetProcAddress(user32, "RegisterRawInputDevices")) || - !(pGetRawInputDeviceInfo = (_GetRawInputDeviceInfo) GetProcAddress(user32, "GetRawInputDeviceInfoW")) || - !(pGetRawInputData = (_GetRawInputData) GetProcAddress(user32, "GetRawInputData")) || - !(pGetRawInputDeviceList = (_GetRawInputDeviceList) GetProcAddress(user32, "GetRawInputDeviceList"))) { - FreeLibrary(user32); - RawInputFailed = 1; - return 0; - } - } - return 1; -} - -void EnumRawInputDevices() { - int count = 0; - if (InitializeRawInput() && pGetRawInputDeviceList(0, (unsigned int*)&count, sizeof(RAWINPUTDEVICELIST)) != (UINT)-1 && count > 0) { - wchar_t *instanceID = (wchar_t *) malloc(41000*sizeof(wchar_t)); - wchar_t *keyName = instanceID + 11000; - wchar_t *displayName = keyName + 10000; - wchar_t *productID = displayName + 10000; - - RAWINPUTDEVICELIST *list = (RAWINPUTDEVICELIST*) malloc(sizeof(RAWINPUTDEVICELIST) * count); - int keyboardCount = 1; - int mouseCount = 1; - count = pGetRawInputDeviceList(list, (unsigned int*)&count, sizeof(RAWINPUTDEVICELIST)); - - // Not necessary, but reminder that count is -1 on failure. - if (count > 0) { - for (int i=0; i= 4) { - // nameLen includes terminating null. - nameLen--; - - // Strip out GUID parts of instanceID to make it a generic product id, - // and reformat it to point to registry entry containing device description. - wcscpy(productID, instanceID); - wchar_t *temp = 0; - for (int j=0; j<3; j++) { - wchar_t *s = wcschr(productID, '#'); - if (!s) break; - *s = '\\'; - if (j==2) { - *s = 0; - } - if (j==1) temp = s; - } - - wsprintfW(keyName, L"SYSTEM\\CurrentControlSet\\Enum%s", productID+3); - if (temp) *temp = 0; - int haveDescription = 0; - HKEY hKey; - if (ERROR_SUCCESS == RegOpenKeyExW(HKEY_LOCAL_MACHINE, keyName, 0, KEY_QUERY_VALUE, &hKey)) { - DWORD type; - DWORD len = 10000 * sizeof(wchar_t); - if (ERROR_SUCCESS == RegQueryValueExW(hKey, L"DeviceDesc", 0, &type, (BYTE*)displayName, &len) && - len && type == REG_SZ) { - wchar_t *temp2 = wcsrchr(displayName, ';'); - if (!temp2) temp2 = displayName; - else temp2++; - // Could do without this, but more effort than it's worth. - wcscpy(keyName, temp2); - haveDescription = 1; - } - RegCloseKey(hKey); - } - if (list[i].dwType == RIM_TYPEKEYBOARD) { - if (!haveDescription) wsprintfW(displayName, L"Raw Keyboard %i", keyboardCount++); - else wsprintfW(displayName, L"Raw KB: %s", keyName); - dm->AddDevice(new RawInputKeyboard(list[i].hDevice, displayName, instanceID)); - } - else if (list[i].dwType == RIM_TYPEMOUSE) { - if (!haveDescription) wsprintfW(displayName, L"Raw Mouse %i", mouseCount++); - else wsprintfW(displayName, L"Raw MS: %s", keyName); - dm->AddDevice(new RawInputMouse(list[i].hDevice, displayName, instanceID, productID)); - } - } - } - } - free(list); - free(instanceID); - dm->AddDevice(new RawInputKeyboard(0, L"Simulated Keyboard")); - dm->AddDevice(new RawInputMouse(0, L"Simulated Mouse")); - } -} +#include "Global.h" +#include "InputManager.h" +#include "WindowsMessaging.h" +#include "VKey.h" +#include "DeviceEnumerator.h" +#include "WndProcEater.h" +#include "WindowsKeyboard.h" +#include "WindowsMouse.h" + + +typedef BOOL (CALLBACK *_RegisterRawInputDevices)(PCRAWINPUTDEVICE pRawInputDevices, UINT uiNumDevices, UINT cbSize); +typedef UINT (CALLBACK *_GetRawInputDeviceInfo)(HANDLE hDevice, UINT uiCommand, LPVOID pData, PUINT pcbSize); +typedef UINT (CALLBACK *_GetRawInputData)(HRAWINPUT hRawInput, UINT uiCommand, LPVOID pData, PUINT pcbSize, UINT cbSizeHeader); +typedef UINT (CALLBACK *_GetRawInputDeviceList)(PRAWINPUTDEVICELIST pRawInputDeviceList, PUINT puiNumDevices, UINT cbSize); + +_RegisterRawInputDevices pRegisterRawInputDevices = 0; +_GetRawInputDeviceInfo pGetRawInputDeviceInfo = 0; +_GetRawInputData pGetRawInputData = 0; +_GetRawInputDeviceList pGetRawInputDeviceList = 0; + +ExtraWndProcResult RawInputWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *output); + +int GetRawKeyboards(HWND hWnd) { + RAWINPUTDEVICE Rid; + Rid.hwndTarget = hWnd; + + Rid.dwFlags = 0; + Rid.usUsagePage = 0x01; + Rid.usUsage = 0x06; + return pRegisterRawInputDevices(&Rid, 1, sizeof(Rid)); +} + +void ReleaseRawKeyboards() { + RAWINPUTDEVICE Rid; + Rid.hwndTarget = 0; + + Rid.dwFlags = RIDEV_REMOVE; + Rid.usUsagePage = 0x01; + Rid.usUsage = 0x06; + pRegisterRawInputDevices(&Rid, 1, sizeof(Rid)); +} + +int GetRawMice(HWND hWnd) { + RAWINPUTDEVICE Rid; + Rid.hwndTarget = hWnd; + + Rid.dwFlags = RIDEV_NOLEGACY | RIDEV_CAPTUREMOUSE; + Rid.usUsagePage = 0x01; + Rid.usUsage = 0x02; + return pRegisterRawInputDevices(&Rid, 1, sizeof(Rid)); +} + +void ReleaseRawMice() { + RAWINPUTDEVICE Rid; + Rid.hwndTarget = 0; + + Rid.dwFlags = RIDEV_REMOVE; + Rid.usUsagePage = 0x01; + Rid.usUsage = 0x02; + pRegisterRawInputDevices(&Rid, 1, sizeof(Rid)); +} + +// Count of active raw keyboard devices. +// when it gets to 0, release them all. +static int rawKeyboardActivatedCount = 0; +// Same for mice. +static int rawMouseActivatedCount = 0; + +class RawInputKeyboard : public WindowsKeyboard { +public: + HANDLE hDevice; + + RawInputKeyboard(HANDLE hDevice, wchar_t *name, wchar_t *instanceID=0) : WindowsKeyboard(RAW, name, instanceID) { + this->hDevice = hDevice; + } + + int Activate(InitInfo *initInfo) { + Deactivate(); + + hWndProc = initInfo->hWndProc; + + active = 1; + if (!rawKeyboardActivatedCount++) { + if (!rawMouseActivatedCount) + hWndProc->Eat(RawInputWndProc, EATPROC_NO_UPDATE_WHILE_UPDATING_DEVICES); + + if (!GetRawKeyboards(hWndProc->hWndEaten)) { + Deactivate(); + return 0; + } + } + + InitState(); + return 1; + } + + void Deactivate() { + FreeState(); + if (active) { + active = 0; + rawKeyboardActivatedCount --; + if (!rawKeyboardActivatedCount) { + ReleaseRawKeyboards(); + if (!rawMouseActivatedCount) + hWndProc->ReleaseExtraProc(RawInputWndProc); + } + } + } +}; + +class RawInputMouse : public WindowsMouse { +public: + HANDLE hDevice; + + RawInputMouse(HANDLE hDevice, wchar_t *name, wchar_t *instanceID=0, wchar_t *productID=0) : WindowsMouse(RAW, 0, name, instanceID, productID) { + this->hDevice = hDevice; + } + + int Activate(InitInfo *initInfo) { + Deactivate(); + + hWndProc = initInfo->hWndProc; + + active = 1; + + // Have to be careful with order. At worst, one unmatched call to ReleaseRawMice on + // EatWndProc fail. In all other cases, no unmatched initialization/cleanup + // lines. + if (!rawMouseActivatedCount++) { + GetMouseCapture(hWndProc->hWndEaten); + if (!rawKeyboardActivatedCount) + hWndProc->Eat(RawInputWndProc, EATPROC_NO_UPDATE_WHILE_UPDATING_DEVICES); + + if (!GetRawMice(hWndProc->hWndEaten)) { + Deactivate(); + return 0; + } + } + + AllocState(); + return 1; + } + + void Deactivate() { + FreeState(); + if (active) { + active = 0; + rawMouseActivatedCount --; + if (!rawMouseActivatedCount) { + ReleaseRawMice(); + ReleaseMouseCapture(); + if (!rawKeyboardActivatedCount) { + hWndProc->ReleaseExtraProc(RawInputWndProc); + } + } + } + } +}; + +ExtraWndProcResult RawInputWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *output) { + if (uMsg == WM_INPUT) { + if (GET_RAWINPUT_CODE_WPARAM (wParam) == RIM_INPUT && pGetRawInputData) { + RAWINPUT in; + unsigned int size = sizeof(RAWINPUT); + if (0 < pGetRawInputData((HRAWINPUT)lParam, RID_INPUT, &in, &size, sizeof(RAWINPUTHEADER))) { + for (int i=0; inumDevices; i++) { + Device *dev = dm->devices[i]; + if (dev->api != RAW || !dev->active) continue; + if (in.header.dwType == RIM_TYPEKEYBOARD && dev->type == KEYBOARD) { + RawInputKeyboard* rik = (RawInputKeyboard*)dev; + if (rik->hDevice != in.header.hDevice) continue; + + u32 uMsg = in.data.keyboard.Message; + if (!(in.data.keyboard.VKey>>8)) + rik->UpdateKey((u8) in.data.keyboard.VKey, (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN)); + } + else if (in.header.dwType == RIM_TYPEMOUSE && dev->type == MOUSE) { + RawInputMouse* rim = (RawInputMouse*)dev; + if (rim->hDevice != in.header.hDevice) continue; + if (in.data.mouse.usFlags) { + // Never been set for me, and specs on what most of them + // actually mean is sorely lacking. Also, specs erroneously + // indicate MOUSE_MOVE_RELATIVE is a flag, when it's really + // 0... + continue; + } + + unsigned short buttons = in.data.mouse.usButtonFlags & 0x3FF; + int button = 0; + while (buttons) { + if (buttons & 3) { + // 2 is up, 1 is down. Up takes precedence over down. + rim->UpdateButton(button, !(buttons & 2)); + } + button++; + buttons >>= 2; + } + if (in.data.mouse.usButtonFlags & RI_MOUSE_WHEEL) { + rim->UpdateAxis(2, ((short)in.data.mouse.usButtonData)/WHEEL_DELTA); + } + if (in.data.mouse.lLastX || in.data.mouse.lLastY) { + rim->UpdateAxis(0, in.data.mouse.lLastX); + rim->UpdateAxis(1, in.data.mouse.lLastY); + } + } + } + } + } + } + else if (uMsg == WM_ACTIVATE) { + for (int i=0; inumDevices; i++) { + Device *dev = dm->devices[i]; + if (dev->api != RAW || dev->physicalControlState == 0) continue; + memset(dev->physicalControlState, 0, sizeof(int) * dev->numPhysicalControls); + } + } + else if (uMsg == WM_SIZE && rawMouseActivatedCount) { + // Doesn't really matter for raw mice, as I disable legacy stuff, but shouldn't hurt. + WindowsMouse::WindowResized(hWnd); + } + + return CONTINUE_BLISSFULLY; +} + +int InitializeRawInput() { + static int RawInputFailed = 0; + if (RawInputFailed) return 0; + if (!pGetRawInputDeviceList) { + HMODULE user32 = LoadLibrary(L"user32.dll"); + if (!user32 || + !(pRegisterRawInputDevices = (_RegisterRawInputDevices) GetProcAddress(user32, "RegisterRawInputDevices")) || + !(pGetRawInputDeviceInfo = (_GetRawInputDeviceInfo) GetProcAddress(user32, "GetRawInputDeviceInfoW")) || + !(pGetRawInputData = (_GetRawInputData) GetProcAddress(user32, "GetRawInputData")) || + !(pGetRawInputDeviceList = (_GetRawInputDeviceList) GetProcAddress(user32, "GetRawInputDeviceList"))) { + FreeLibrary(user32); + RawInputFailed = 1; + return 0; + } + } + return 1; +} + +void EnumRawInputDevices() { + int count = 0; + if (InitializeRawInput() && pGetRawInputDeviceList(0, (unsigned int*)&count, sizeof(RAWINPUTDEVICELIST)) != (UINT)-1 && count > 0) { + wchar_t *instanceID = (wchar_t *) malloc(41000*sizeof(wchar_t)); + wchar_t *keyName = instanceID + 11000; + wchar_t *displayName = keyName + 10000; + wchar_t *productID = displayName + 10000; + + RAWINPUTDEVICELIST *list = (RAWINPUTDEVICELIST*) malloc(sizeof(RAWINPUTDEVICELIST) * count); + int keyboardCount = 1; + int mouseCount = 1; + count = pGetRawInputDeviceList(list, (unsigned int*)&count, sizeof(RAWINPUTDEVICELIST)); + + // Not necessary, but reminder that count is -1 on failure. + if (count > 0) { + for (int i=0; i= 4) { + // nameLen includes terminating null. + nameLen--; + + // Strip out GUID parts of instanceID to make it a generic product id, + // and reformat it to point to registry entry containing device description. + wcscpy(productID, instanceID); + wchar_t *temp = 0; + for (int j=0; j<3; j++) { + wchar_t *s = wcschr(productID, '#'); + if (!s) break; + *s = '\\'; + if (j==2) { + *s = 0; + } + if (j==1) temp = s; + } + + wsprintfW(keyName, L"SYSTEM\\CurrentControlSet\\Enum%s", productID+3); + if (temp) *temp = 0; + int haveDescription = 0; + HKEY hKey; + if (ERROR_SUCCESS == RegOpenKeyExW(HKEY_LOCAL_MACHINE, keyName, 0, KEY_QUERY_VALUE, &hKey)) { + DWORD type; + DWORD len = 10000 * sizeof(wchar_t); + if (ERROR_SUCCESS == RegQueryValueExW(hKey, L"DeviceDesc", 0, &type, (BYTE*)displayName, &len) && + len && type == REG_SZ) { + wchar_t *temp2 = wcsrchr(displayName, ';'); + if (!temp2) temp2 = displayName; + else temp2++; + // Could do without this, but more effort than it's worth. + wcscpy(keyName, temp2); + haveDescription = 1; + } + RegCloseKey(hKey); + } + if (list[i].dwType == RIM_TYPEKEYBOARD) { + if (!haveDescription) wsprintfW(displayName, L"Raw Keyboard %i", keyboardCount++); + else wsprintfW(displayName, L"Raw KB: %s", keyName); + dm->AddDevice(new RawInputKeyboard(list[i].hDevice, displayName, instanceID)); + } + else if (list[i].dwType == RIM_TYPEMOUSE) { + if (!haveDescription) wsprintfW(displayName, L"Raw Mouse %i", mouseCount++); + else wsprintfW(displayName, L"Raw MS: %s", keyName); + dm->AddDevice(new RawInputMouse(list[i].hDevice, displayName, instanceID, productID)); + } + } + } + } + free(list); + free(instanceID); + dm->AddDevice(new RawInputKeyboard(0, L"Simulated Keyboard")); + dm->AddDevice(new RawInputMouse(0, L"Simulated Mouse")); + } +} diff --git a/plugins/LilyPad/RawInput.h b/plugins/LilyPad/RawInput.h index c63951d2b9..09edff1b6a 100644 --- a/plugins/LilyPad/RawInput.h +++ b/plugins/LilyPad/RawInput.h @@ -1,4 +1,4 @@ -// Can't enumerate raw devices, can only detect them when -// receiving data from them, so just use the list from before. -void EnumRawInputDevices(); -int InitializeRawInput(); +// Can't enumerate raw devices, can only detect them when +// receiving data from them, so just use the list from before. +void EnumRawInputDevices(); +int InitializeRawInput(); diff --git a/plugins/LilyPad/VKey.cpp b/plugins/LilyPad/VKey.cpp index f1dd0216c7..f4a244c1f4 100644 --- a/plugins/LilyPad/VKey.cpp +++ b/plugins/LilyPad/VKey.cpp @@ -1,68 +1,68 @@ -#include "Global.h" -#include "VKey.h" - -wchar_t *GetVKStringW(unsigned char vk) { - int flag; - static wchar_t t[20]; - switch(vk) { - case 0x0C: return L"Clear"; - case 0x13: return L"Pause"; - - case 0x21:// return "Page Up"; - case 0x22:// return "Page Down"; - case 0x23:// return "End"; - case 0x24:// return "Home"; - case 0x25:// return "Left"; - case 0x26:// return "Up"; - case 0x27:// return "Right"; - case 0x28:// return "Down"; - case 0x2D:// return "Insert"; - case 0x2E:// return "Delete"; - case 0x5B:// return "Left Windows"; - case 0x5C:// return "Right Windows"; - case 0x5D:// return "Application"; - case 0x6F:// return "Num /"; - flag = 1<<24; - break; - - case 0x29: return L"Select"; - case 0x2A: return L"Print"; - case 0x2B: return L"Execute"; - case 0x2C: return L"Prnt Scrn"; - case 0x2F: return L"Help"; - - case 0x6C: return L"|"; - case 0x90: return L"Num Lock"; - - case 0xA0: return L"Left Shift"; - case 0xA1: return L"Right Shift"; - case 0xA2: return L"Left Ctrl"; - case 0xA3: return L"Right Ctrl"; - case 0xA4: return L"Left Alt"; - case 0xA5: return L"Right Alt"; - - case 0xA6: return L"Back"; - case 0xA7: return L"Forward"; - case 0xA8: return L"Refresh"; - case 0xA9: return L"Stop"; - case 0xAA: return L"Search"; - case 0xAB: return L"Favorites"; - case 0xAC: return L"Browser"; - - case 0xFA: return L"Play"; - case 0xFB: return L"Zoom"; - default: - flag = 0; - break; - } - int res = MapVirtualKey(vk, MAPVK_VK_TO_VSC); - if (res && GetKeyNameText((res<<16) | flag, t, 20)) { - // don't trust windows - t[19] = 0; - } - else { - wsprintfW(t, L"Key %i", vk); - } - return t; -} - +#include "Global.h" +#include "VKey.h" + +wchar_t *GetVKStringW(unsigned char vk) { + int flag; + static wchar_t t[20]; + switch(vk) { + case 0x0C: return L"Clear"; + case 0x13: return L"Pause"; + + case 0x21:// return "Page Up"; + case 0x22:// return "Page Down"; + case 0x23:// return "End"; + case 0x24:// return "Home"; + case 0x25:// return "Left"; + case 0x26:// return "Up"; + case 0x27:// return "Right"; + case 0x28:// return "Down"; + case 0x2D:// return "Insert"; + case 0x2E:// return "Delete"; + case 0x5B:// return "Left Windows"; + case 0x5C:// return "Right Windows"; + case 0x5D:// return "Application"; + case 0x6F:// return "Num /"; + flag = 1<<24; + break; + + case 0x29: return L"Select"; + case 0x2A: return L"Print"; + case 0x2B: return L"Execute"; + case 0x2C: return L"Prnt Scrn"; + case 0x2F: return L"Help"; + + case 0x6C: return L"|"; + case 0x90: return L"Num Lock"; + + case 0xA0: return L"Left Shift"; + case 0xA1: return L"Right Shift"; + case 0xA2: return L"Left Ctrl"; + case 0xA3: return L"Right Ctrl"; + case 0xA4: return L"Left Alt"; + case 0xA5: return L"Right Alt"; + + case 0xA6: return L"Back"; + case 0xA7: return L"Forward"; + case 0xA8: return L"Refresh"; + case 0xA9: return L"Stop"; + case 0xAA: return L"Search"; + case 0xAB: return L"Favorites"; + case 0xAC: return L"Browser"; + + case 0xFA: return L"Play"; + case 0xFB: return L"Zoom"; + default: + flag = 0; + break; + } + int res = MapVirtualKey(vk, MAPVK_VK_TO_VSC); + if (res && GetKeyNameText((res<<16) | flag, t, 20)) { + // don't trust windows + t[19] = 0; + } + else { + wsprintfW(t, L"Key %i", vk); + } + return t; +} + diff --git a/plugins/LilyPad/VKey.h b/plugins/LilyPad/VKey.h index a43e6c48ed..8c899f5199 100644 --- a/plugins/LilyPad/VKey.h +++ b/plugins/LilyPad/VKey.h @@ -1,2 +1,2 @@ -// Maps virtual key codes to strings. -wchar_t *GetVKStringW(unsigned char vk); +// Maps virtual key codes to strings. +wchar_t *GetVKStringW(unsigned char vk); diff --git a/plugins/LilyPad/WindowsKeyboard.cpp b/plugins/LilyPad/WindowsKeyboard.cpp index 4141113a90..8f7f5812b7 100644 --- a/plugins/LilyPad/WindowsKeyboard.cpp +++ b/plugins/LilyPad/WindowsKeyboard.cpp @@ -1,48 +1,48 @@ -#include "Global.h" -#include "InputManager.h" -#include "VKey.h" -#include "WindowsKeyboard.h" -#include "KeyboardQueue.h" - -WindowsKeyboard::WindowsKeyboard(DeviceAPI api, wchar_t *displayName, wchar_t *instanceID, wchar_t *deviceID) : -Device(api, KEYBOARD, displayName, instanceID, deviceID) { - for (int i=0; i<256; i++) { - AddPhysicalControl(PSHBTN, i, i); - } -} - -wchar_t *WindowsKeyboard::GetPhysicalControlName(PhysicalControl *control) { - int id = control->id; - if (control->type == PSHBTN && id >= 0 && id < 256) { - wchar_t *w = GetVKStringW(id); - if (w) return w; - } - return Device::GetPhysicalControlName(control); -} - -void WindowsKeyboard::UpdateKey(int vkey, int state) { - if (vkey > 7 && vkey < 256) { - int newState = state * FULLY_DOWN; - if (newState != physicalControlState[vkey]) { - // Check for alt-F4 to avoid toggling skip mode incorrectly. - if (vkey != VK_F4 || !(physicalControlState[VK_MENU] || physicalControlState[VK_RMENU] || physicalControlState[VK_LMENU])) { - int event = KEYPRESS; - if (!newState) event = KEYRELEASE; - QueueKeyEvent(vkey, event); - } - } - physicalControlState[vkey] = newState; - } -} - -void WindowsKeyboard::InitState() { - AllocState(); - for (int vkey=5; vkey<256; vkey++) { - int value = (unsigned short)(((short)GetAsyncKeyState(vkey))>>15); - value += value&1; - if (vkey == VK_CONTROL || vkey == VK_MENU || vkey == VK_SHIFT) { - value = 0; - } - physicalControlState[vkey] = value; - } -} +#include "Global.h" +#include "InputManager.h" +#include "VKey.h" +#include "WindowsKeyboard.h" +#include "KeyboardQueue.h" + +WindowsKeyboard::WindowsKeyboard(DeviceAPI api, wchar_t *displayName, wchar_t *instanceID, wchar_t *deviceID) : +Device(api, KEYBOARD, displayName, instanceID, deviceID) { + for (int i=0; i<256; i++) { + AddPhysicalControl(PSHBTN, i, i); + } +} + +wchar_t *WindowsKeyboard::GetPhysicalControlName(PhysicalControl *control) { + int id = control->id; + if (control->type == PSHBTN && id >= 0 && id < 256) { + wchar_t *w = GetVKStringW(id); + if (w) return w; + } + return Device::GetPhysicalControlName(control); +} + +void WindowsKeyboard::UpdateKey(int vkey, int state) { + if (vkey > 7 && vkey < 256) { + int newState = state * FULLY_DOWN; + if (newState != physicalControlState[vkey]) { + // Check for alt-F4 to avoid toggling skip mode incorrectly. + if (vkey != VK_F4 || !(physicalControlState[VK_MENU] || physicalControlState[VK_RMENU] || physicalControlState[VK_LMENU])) { + int event = KEYPRESS; + if (!newState) event = KEYRELEASE; + QueueKeyEvent(vkey, event); + } + } + physicalControlState[vkey] = newState; + } +} + +void WindowsKeyboard::InitState() { + AllocState(); + for (int vkey=5; vkey<256; vkey++) { + int value = (unsigned short)(((short)GetAsyncKeyState(vkey))>>15); + value += value&1; + if (vkey == VK_CONTROL || vkey == VK_MENU || vkey == VK_SHIFT) { + value = 0; + } + physicalControlState[vkey] = value; + } +} diff --git a/plugins/LilyPad/WindowsKeyboard.h b/plugins/LilyPad/WindowsKeyboard.h index bff14e9418..5735685611 100644 --- a/plugins/LilyPad/WindowsKeyboard.h +++ b/plugins/LilyPad/WindowsKeyboard.h @@ -1,10 +1,10 @@ -// Shared functionality for WM and RAW keyboards. -class WindowsKeyboard : public Device { -public: - WindowsKeyboard(DeviceAPI api, wchar_t *displayName, wchar_t *instanceID=0, wchar_t *deviceID=0); - wchar_t *GetPhysicalControlName(PhysicalControl *control); - void UpdateKey(int vkey, int state); - // Calls AllocState() and initializes to current keyboard state using - // GetAsyncKeyState(). - void InitState(); -}; +// Shared functionality for WM and RAW keyboards. +class WindowsKeyboard : public Device { +public: + WindowsKeyboard(DeviceAPI api, wchar_t *displayName, wchar_t *instanceID=0, wchar_t *deviceID=0); + wchar_t *GetPhysicalControlName(PhysicalControl *control); + void UpdateKey(int vkey, int state); + // Calls AllocState() and initializes to current keyboard state using + // GetAsyncKeyState(). + void InitState(); +}; diff --git a/plugins/LilyPad/WindowsMessaging.cpp b/plugins/LilyPad/WindowsMessaging.cpp index 43dffec550..c4b8f29e43 100644 --- a/plugins/LilyPad/WindowsMessaging.cpp +++ b/plugins/LilyPad/WindowsMessaging.cpp @@ -1,174 +1,174 @@ -#include "Global.h" -#include "InputManager.h" -#include "WindowsMessaging.h" -#include "VKey.h" -#include "DeviceEnumerator.h" -#include "WndProcEater.h" -#include "WindowsKeyboard.h" -#include "WindowsMouse.h" - -ExtraWndProcResult WindowsMessagingWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *output); - -class WindowsMessagingKeyboard; -class WindowsMessagingMouse; - -static WindowsMessagingKeyboard *wmk = 0; -static WindowsMessagingMouse *wmm = 0; - -class WindowsMessagingKeyboard : public WindowsKeyboard { -public: - - WindowsMessagingKeyboard() : WindowsKeyboard(WM, L"WM Keyboard") { - } - - int Activate(InitInfo *initInfo) { - // Redundant. Should match the next line. - // Deactivate(); - if (wmk) wmk->Deactivate(); - - hWndProc = initInfo->hWndProc; - - if (!wmm) - hWndProc->Eat(WindowsMessagingWndProc, EATPROC_NO_UPDATE_WHILE_UPDATING_DEVICES); - - wmk = this; - InitState(); - - active = 1; - return 1; - } - - void Deactivate() { - if (active) { - if (!wmm) - hWndProc->ReleaseExtraProc(WindowsMessagingWndProc); - wmk = 0; - active = 0; - FreeState(); - } - } - - - void CheckKey(int vkey) { - UpdateKey(vkey, 1&(((unsigned short)GetAsyncKeyState(vkey))>>15)); - } -}; - -class WindowsMessagingMouse : public WindowsMouse { -public: - - WindowsMessagingMouse() : WindowsMouse(WM, 1, L"WM Mouse") { - } - - int Activate(InitInfo *initInfo) { - // Redundant. Should match the next line. - // Deactivate(); - if (wmm) wmm->Deactivate(); - hWndProc = initInfo->hWndProc; - - if (!wmk) - hWndProc->Eat(WindowsMessagingWndProc, EATPROC_NO_UPDATE_WHILE_UPDATING_DEVICES); - - GetMouseCapture(hWndProc->hWndEaten); - - active = 1; - - wmm = this; - AllocState(); - - return 1; - } - - void Deactivate() { - if (active) { - if (!wmk) - hWndProc->ReleaseExtraProc(WindowsMessagingWndProc); - ReleaseMouseCapture(); - wmm = 0; - active = 0; - FreeState(); - } - } -}; - -ExtraWndProcResult WindowsMessagingWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *output) { - if (wmk) { - if (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN || uMsg == WM_KEYUP || uMsg == WM_SYSKEYUP) { - if (wParam == VK_SHIFT) { - wmk->CheckKey(VK_RSHIFT); - wmk->CheckKey(VK_LSHIFT); - } - else if (wParam == VK_CONTROL) { - wmk->CheckKey(VK_RCONTROL); - wmk->CheckKey(VK_LCONTROL); - } - else if (wParam == VK_MENU) { - wmk->CheckKey(VK_RMENU); - wmk->CheckKey(VK_LMENU); - } - else - wmk->UpdateKey(wParam, (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN)); - return NO_WND_PROC; - } - // Needed to prevent default handling of keys in some situations. - else if (uMsg == WM_CHAR || uMsg == WM_UNICHAR) { - return NO_WND_PROC; - } - else if (uMsg == WM_ACTIVATE) { - // Not really needed, but doesn't hurt. - memset(wmk->physicalControlState, 0, sizeof(int) * wmk->numPhysicalControls); - } - } - if (wmm) { - if (uMsg == WM_MOUSEMOVE) { - POINT p; - GetCursorPos(&p); - // Need check to prevent cursor movement cascade. - if (p.x != wmm->center.x || p.y != wmm->center.y) { - wmm->UpdateAxis(0, p.x - wmm->center.x); - wmm->UpdateAxis(1, p.y - wmm->center.y); - - SetCursorPos(wmm->center.x, wmm->center.y); - } - return NO_WND_PROC; - } - else if (uMsg == WM_LBUTTONDOWN || uMsg == WM_LBUTTONUP) { - wmm->UpdateButton(0, uMsg == WM_LBUTTONDOWN); - return NO_WND_PROC; - } - else if (uMsg == WM_RBUTTONDOWN || uMsg == WM_RBUTTONUP) { - wmm->UpdateButton(1, uMsg == WM_RBUTTONDOWN); - return NO_WND_PROC; - } - else if (uMsg == WM_MBUTTONDOWN || uMsg == WM_MBUTTONUP) { - wmm->UpdateButton(2, uMsg == WM_MBUTTONDOWN); - return NO_WND_PROC; - } - else if (uMsg == WM_XBUTTONDOWN || uMsg == WM_XBUTTONUP) { - wmm->UpdateButton(3+((wParam>>16) == XBUTTON2), uMsg == WM_XBUTTONDOWN); - return NO_WND_PROC; - } - else if (uMsg == WM_MOUSEWHEEL) { - wmm->UpdateAxis(2, ((int)wParam>>16)/WHEEL_DELTA); - return NO_WND_PROC; - } - else if (uMsg == WM_MOUSEHWHEEL) { - wmm->UpdateAxis(3, ((int)wParam>>16)/WHEEL_DELTA); - return NO_WND_PROC; - } - else if (uMsg == WM_SIZE && wmm->active) { - WindowsMouse::WindowResized(hWnd); - } - // Taken care of elsewhere. When binding, killing focus means stop reading input. - // When running PCSX2, I release all mouse and keyboard input elsewhere. - /*else if (uMsg == WM_KILLFOCUS) { - wmm->Deactivate(); - }//*/ - } - return CONTINUE_BLISSFULLY; -} - -void EnumWindowsMessagingDevices() { - dm->AddDevice(new WindowsMessagingKeyboard()); - dm->AddDevice(new WindowsMessagingMouse()); -} +#include "Global.h" +#include "InputManager.h" +#include "WindowsMessaging.h" +#include "VKey.h" +#include "DeviceEnumerator.h" +#include "WndProcEater.h" +#include "WindowsKeyboard.h" +#include "WindowsMouse.h" + +ExtraWndProcResult WindowsMessagingWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *output); + +class WindowsMessagingKeyboard; +class WindowsMessagingMouse; + +static WindowsMessagingKeyboard *wmk = 0; +static WindowsMessagingMouse *wmm = 0; + +class WindowsMessagingKeyboard : public WindowsKeyboard { +public: + + WindowsMessagingKeyboard() : WindowsKeyboard(WM, L"WM Keyboard") { + } + + int Activate(InitInfo *initInfo) { + // Redundant. Should match the next line. + // Deactivate(); + if (wmk) wmk->Deactivate(); + + hWndProc = initInfo->hWndProc; + + if (!wmm) + hWndProc->Eat(WindowsMessagingWndProc, EATPROC_NO_UPDATE_WHILE_UPDATING_DEVICES); + + wmk = this; + InitState(); + + active = 1; + return 1; + } + + void Deactivate() { + if (active) { + if (!wmm) + hWndProc->ReleaseExtraProc(WindowsMessagingWndProc); + wmk = 0; + active = 0; + FreeState(); + } + } + + + void CheckKey(int vkey) { + UpdateKey(vkey, 1&(((unsigned short)GetAsyncKeyState(vkey))>>15)); + } +}; + +class WindowsMessagingMouse : public WindowsMouse { +public: + + WindowsMessagingMouse() : WindowsMouse(WM, 1, L"WM Mouse") { + } + + int Activate(InitInfo *initInfo) { + // Redundant. Should match the next line. + // Deactivate(); + if (wmm) wmm->Deactivate(); + hWndProc = initInfo->hWndProc; + + if (!wmk) + hWndProc->Eat(WindowsMessagingWndProc, EATPROC_NO_UPDATE_WHILE_UPDATING_DEVICES); + + GetMouseCapture(hWndProc->hWndEaten); + + active = 1; + + wmm = this; + AllocState(); + + return 1; + } + + void Deactivate() { + if (active) { + if (!wmk) + hWndProc->ReleaseExtraProc(WindowsMessagingWndProc); + ReleaseMouseCapture(); + wmm = 0; + active = 0; + FreeState(); + } + } +}; + +ExtraWndProcResult WindowsMessagingWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *output) { + if (wmk) { + if (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN || uMsg == WM_KEYUP || uMsg == WM_SYSKEYUP) { + if (wParam == VK_SHIFT) { + wmk->CheckKey(VK_RSHIFT); + wmk->CheckKey(VK_LSHIFT); + } + else if (wParam == VK_CONTROL) { + wmk->CheckKey(VK_RCONTROL); + wmk->CheckKey(VK_LCONTROL); + } + else if (wParam == VK_MENU) { + wmk->CheckKey(VK_RMENU); + wmk->CheckKey(VK_LMENU); + } + else + wmk->UpdateKey(wParam, (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN)); + return NO_WND_PROC; + } + // Needed to prevent default handling of keys in some situations. + else if (uMsg == WM_CHAR || uMsg == WM_UNICHAR) { + return NO_WND_PROC; + } + else if (uMsg == WM_ACTIVATE) { + // Not really needed, but doesn't hurt. + memset(wmk->physicalControlState, 0, sizeof(int) * wmk->numPhysicalControls); + } + } + if (wmm) { + if (uMsg == WM_MOUSEMOVE) { + POINT p; + GetCursorPos(&p); + // Need check to prevent cursor movement cascade. + if (p.x != wmm->center.x || p.y != wmm->center.y) { + wmm->UpdateAxis(0, p.x - wmm->center.x); + wmm->UpdateAxis(1, p.y - wmm->center.y); + + SetCursorPos(wmm->center.x, wmm->center.y); + } + return NO_WND_PROC; + } + else if (uMsg == WM_LBUTTONDOWN || uMsg == WM_LBUTTONUP) { + wmm->UpdateButton(0, uMsg == WM_LBUTTONDOWN); + return NO_WND_PROC; + } + else if (uMsg == WM_RBUTTONDOWN || uMsg == WM_RBUTTONUP) { + wmm->UpdateButton(1, uMsg == WM_RBUTTONDOWN); + return NO_WND_PROC; + } + else if (uMsg == WM_MBUTTONDOWN || uMsg == WM_MBUTTONUP) { + wmm->UpdateButton(2, uMsg == WM_MBUTTONDOWN); + return NO_WND_PROC; + } + else if (uMsg == WM_XBUTTONDOWN || uMsg == WM_XBUTTONUP) { + wmm->UpdateButton(3+((wParam>>16) == XBUTTON2), uMsg == WM_XBUTTONDOWN); + return NO_WND_PROC; + } + else if (uMsg == WM_MOUSEWHEEL) { + wmm->UpdateAxis(2, ((int)wParam>>16)/WHEEL_DELTA); + return NO_WND_PROC; + } + else if (uMsg == WM_MOUSEHWHEEL) { + wmm->UpdateAxis(3, ((int)wParam>>16)/WHEEL_DELTA); + return NO_WND_PROC; + } + else if (uMsg == WM_SIZE && wmm->active) { + WindowsMouse::WindowResized(hWnd); + } + // Taken care of elsewhere. When binding, killing focus means stop reading input. + // When running PCSX2, I release all mouse and keyboard input elsewhere. + /*else if (uMsg == WM_KILLFOCUS) { + wmm->Deactivate(); + }//*/ + } + return CONTINUE_BLISSFULLY; +} + +void EnumWindowsMessagingDevices() { + dm->AddDevice(new WindowsMessagingKeyboard()); + dm->AddDevice(new WindowsMessagingMouse()); +} diff --git a/plugins/LilyPad/WindowsMessaging.h b/plugins/LilyPad/WindowsMessaging.h index 8b96616381..26c682bd47 100644 --- a/plugins/LilyPad/WindowsMessaging.h +++ b/plugins/LilyPad/WindowsMessaging.h @@ -1 +1 @@ -void EnumWindowsMessagingDevices(); +void EnumWindowsMessagingDevices(); diff --git a/plugins/LilyPad/WindowsMouse.cpp b/plugins/LilyPad/WindowsMouse.cpp index 9f105108a0..cd3d682a2b 100644 --- a/plugins/LilyPad/WindowsMouse.cpp +++ b/plugins/LilyPad/WindowsMouse.cpp @@ -1,76 +1,76 @@ -#include "Global.h" -#include "InputManager.h" -#include "VKey.h" -#include "WindowsMouse.h" - -POINT WindowsMouse::origCursorPos; -POINT WindowsMouse::center; - -WindowsMouse::WindowsMouse(DeviceAPI api, int hWheel, wchar_t *displayName, wchar_t *instanceID, wchar_t *deviceID) : -Device(api, MOUSE, displayName, instanceID, deviceID) { - int i; - for (i=0; i<5; i++) { - AddPhysicalControl(PSHBTN, i, i); - } - - for (i=0; i<3+hWheel; i++) { - AddPhysicalControl(RELAXIS, i+5, i+5); - } -} - -wchar_t *WindowsMouse::GetPhysicalControlName(PhysicalControl *control) { - wchar_t *names[9] = { - L"L Button", - L"R Button", - L"M Button", - L"Mouse 4", - L"Mouse 5", - L"X Axis", - L"Y Axis", - L"Y Wheel", - L"X Wheel" - }; - if (control->id < 9) return names[control->id]; - return Device::GetPhysicalControlName(control); -} - -void WindowsMouse::UpdateButton(unsigned int button, int state) { - if (button > 4) return; - physicalControlState[button] = (state << 16); -} - -void WindowsMouse::UpdateAxis(unsigned int axis, int delta) { - if (axis > 3) return; - // 1 mouse pixel = 1/8th way down. - physicalControlState[5+axis] += (delta<<(16 - 3*(axis < 2))); -} - -void WindowsMouse::WindowResized(HWND hWnd) { - RECT r; - GetWindowRect(hWnd, &r); - ClipCursor(&r); - center.x = (r.left + r.right)/2; - center.y = (r.top + r.bottom)/2; - SetCursorPos(center.x, center.y); -} - -void WindowsMouse::GetMouseCapture(HWND hWnd) { - SetCapture(hWnd); - ShowCursor(0); - - GetCursorPos(&origCursorPos); - - RECT r; - GetWindowRect(hWnd, &r); - ClipCursor(&r); - center.x = (r.left + r.right)/2; - center.y = (r.top + r.bottom)/2; - SetCursorPos(center.x, center.y); -} - -void WindowsMouse::ReleaseMouseCapture() { - ClipCursor(0); - ReleaseCapture(); - ShowCursor(1); - SetCursorPos(origCursorPos.x, origCursorPos.y); -} +#include "Global.h" +#include "InputManager.h" +#include "VKey.h" +#include "WindowsMouse.h" + +POINT WindowsMouse::origCursorPos; +POINT WindowsMouse::center; + +WindowsMouse::WindowsMouse(DeviceAPI api, int hWheel, wchar_t *displayName, wchar_t *instanceID, wchar_t *deviceID) : +Device(api, MOUSE, displayName, instanceID, deviceID) { + int i; + for (i=0; i<5; i++) { + AddPhysicalControl(PSHBTN, i, i); + } + + for (i=0; i<3+hWheel; i++) { + AddPhysicalControl(RELAXIS, i+5, i+5); + } +} + +wchar_t *WindowsMouse::GetPhysicalControlName(PhysicalControl *control) { + wchar_t *names[9] = { + L"L Button", + L"R Button", + L"M Button", + L"Mouse 4", + L"Mouse 5", + L"X Axis", + L"Y Axis", + L"Y Wheel", + L"X Wheel" + }; + if (control->id < 9) return names[control->id]; + return Device::GetPhysicalControlName(control); +} + +void WindowsMouse::UpdateButton(unsigned int button, int state) { + if (button > 4) return; + physicalControlState[button] = (state << 16); +} + +void WindowsMouse::UpdateAxis(unsigned int axis, int delta) { + if (axis > 3) return; + // 1 mouse pixel = 1/8th way down. + physicalControlState[5+axis] += (delta<<(16 - 3*(axis < 2))); +} + +void WindowsMouse::WindowResized(HWND hWnd) { + RECT r; + GetWindowRect(hWnd, &r); + ClipCursor(&r); + center.x = (r.left + r.right)/2; + center.y = (r.top + r.bottom)/2; + SetCursorPos(center.x, center.y); +} + +void WindowsMouse::GetMouseCapture(HWND hWnd) { + SetCapture(hWnd); + ShowCursor(0); + + GetCursorPos(&origCursorPos); + + RECT r; + GetWindowRect(hWnd, &r); + ClipCursor(&r); + center.x = (r.left + r.right)/2; + center.y = (r.top + r.bottom)/2; + SetCursorPos(center.x, center.y); +} + +void WindowsMouse::ReleaseMouseCapture() { + ClipCursor(0); + ReleaseCapture(); + ShowCursor(1); + SetCursorPos(origCursorPos.x, origCursorPos.y); +} diff --git a/plugins/LilyPad/WindowsMouse.h b/plugins/LilyPad/WindowsMouse.h index 938e2256d8..8f0e6bdc91 100644 --- a/plugins/LilyPad/WindowsMouse.h +++ b/plugins/LilyPad/WindowsMouse.h @@ -1,24 +1,24 @@ -// Shared functionality for WM and RAW keyboards. -class WindowsMouse : public Device { -public: - // Used by GetMouseCapture()/ReleaseMouseCapture() - // Static because can have multiple raw mice active at once, - // and only get/release capture once. - static POINT origCursorPos; - static POINT center; - - static void GetMouseCapture(HWND hWnd); - static void WindowResized(HWND hWnd); - static void ReleaseMouseCapture(); - - // hWheel variable lets me display no horizontal wheel for raw input, just to make it clear - // that it's not supported. - WindowsMouse(DeviceAPI api, int hWheel, wchar_t *displayName, wchar_t *instanceID=0, wchar_t *deviceID=0); - wchar_t *GetPhysicalControlName(PhysicalControl *control); - // State is 0 for up, 1 for down. - void UpdateButton(unsigned int button, int state); - // 0/1 are x/y. 2 is vert wheel, 3 is horiz wheel. - // Delta is in my micro units. change of (1<<16) is 1 full unit, with - // the default sensitivity. - void UpdateAxis(unsigned int axis, int delta); -}; +// Shared functionality for WM and RAW keyboards. +class WindowsMouse : public Device { +public: + // Used by GetMouseCapture()/ReleaseMouseCapture() + // Static because can have multiple raw mice active at once, + // and only get/release capture once. + static POINT origCursorPos; + static POINT center; + + static void GetMouseCapture(HWND hWnd); + static void WindowResized(HWND hWnd); + static void ReleaseMouseCapture(); + + // hWheel variable lets me display no horizontal wheel for raw input, just to make it clear + // that it's not supported. + WindowsMouse(DeviceAPI api, int hWheel, wchar_t *displayName, wchar_t *instanceID=0, wchar_t *deviceID=0); + wchar_t *GetPhysicalControlName(PhysicalControl *control); + // State is 0 for up, 1 for down. + void UpdateButton(unsigned int button, int state); + // 0/1 are x/y. 2 is vert wheel, 3 is horiz wheel. + // Delta is in my micro units. change of (1<<16) is 1 full unit, with + // the default sensitivity. + void UpdateAxis(unsigned int axis, int delta); +}; diff --git a/plugins/LilyPad/WndProcEater.cpp b/plugins/LilyPad/WndProcEater.cpp index eeea23e0b6..b6ab59984c 100644 --- a/plugins/LilyPad/WndProcEater.cpp +++ b/plugins/LilyPad/WndProcEater.cpp @@ -1,130 +1,130 @@ -#include "Global.h" -#include "WndProcEater.h" - -WndProcEater::WndProcEater() -{ - hWndEaten = 0; - eatenWndProc = 0; - - extraProcs = 0; - numExtraProcs = 0; - - hMutex = CreateMutex(0, 0, L"LilyPad"); -} - -WndProcEater::~WndProcEater() throw() -{ - if (hMutex) { - ReleaseMutex(hMutex); - CloseHandle(hMutex); - } -} - -void WndProcEater::ReleaseExtraProc(ExtraWndProc proc) { - // Probably isn't needed, but just in case... - if (hMutex) WaitForSingleObject(hMutex, 100); - - //printf( "(Lilypad) Regurgitating! -> 0x%x\n", proc ); - - for (int i=0; i res) res = res2; - } - } - - if (res != NO_WND_PROC) { - if (out == WM_DESTROY) { - Release(); - } - if (res == CONTINUE_BLISSFULLY) - out = CallWindowProc(eatenWndProc, hWnd, uMsg, wParam, lParam); - else if (res == USE_DEFAULT_WND_PROC) - out = DefWindowProc(hWnd, uMsg, wParam, lParam); - } - return out; -} - -static LRESULT CALLBACK OverrideWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - WndProcEater* obj = (WndProcEater*)GetProp(hWnd, L"LilyHaxxor"); - return (obj == NULL) ? - DefWindowProc(hWnd, uMsg, wParam, lParam) : - obj->_OverrideWndProc( hWnd, uMsg, wParam, lParam ); -} - -bool WndProcEater::SetWndHandle(HWND hWnd) -{ - if(hWnd == hWndEaten) return true; - - //printf( "(Lilypad) (Re)-Setting window handle! -> this=0x%08x, hWnd=0x%08x\n", this, hWnd ); - - Release(); - SetProp(hWnd, L"LilyHaxxor", (HANDLE)this); - - eatenWndProc = (WNDPROC) SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)OverrideWndProc); - hWndEaten = (eatenWndProc) ? hWnd : 0; - - return !!hWndEaten; -} - -void WndProcEater::Eat(ExtraWndProc proc, DWORD flags) { - - // check if Subclassing failed to init during SetWndHandle - if (!hWndEaten) return; - - // Probably isn't needed, but just in case... - if (hMutex) WaitForSingleObject(hMutex, 100); - - //printf( "(Lilypad) EatingWndProc! -> 0x%x\n", proc ); - - extraProcs = (ExtraWndProcInfo*) realloc(extraProcs, sizeof(ExtraWndProcInfo)*(numExtraProcs+1)); - extraProcs[numExtraProcs].proc = proc; - extraProcs[numExtraProcs].flags = flags; - numExtraProcs++; -} +#include "Global.h" +#include "WndProcEater.h" + +WndProcEater::WndProcEater() +{ + hWndEaten = 0; + eatenWndProc = 0; + + extraProcs = 0; + numExtraProcs = 0; + + hMutex = CreateMutex(0, 0, L"LilyPad"); +} + +WndProcEater::~WndProcEater() throw() +{ + if (hMutex) { + ReleaseMutex(hMutex); + CloseHandle(hMutex); + } +} + +void WndProcEater::ReleaseExtraProc(ExtraWndProc proc) { + // Probably isn't needed, but just in case... + if (hMutex) WaitForSingleObject(hMutex, 100); + + //printf( "(Lilypad) Regurgitating! -> 0x%x\n", proc ); + + for (int i=0; i res) res = res2; + } + } + + if (res != NO_WND_PROC) { + if (out == WM_DESTROY) { + Release(); + } + if (res == CONTINUE_BLISSFULLY) + out = CallWindowProc(eatenWndProc, hWnd, uMsg, wParam, lParam); + else if (res == USE_DEFAULT_WND_PROC) + out = DefWindowProc(hWnd, uMsg, wParam, lParam); + } + return out; +} + +static LRESULT CALLBACK OverrideWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + WndProcEater* obj = (WndProcEater*)GetProp(hWnd, L"LilyHaxxor"); + return (obj == NULL) ? + DefWindowProc(hWnd, uMsg, wParam, lParam) : + obj->_OverrideWndProc( hWnd, uMsg, wParam, lParam ); +} + +bool WndProcEater::SetWndHandle(HWND hWnd) +{ + if(hWnd == hWndEaten) return true; + + //printf( "(Lilypad) (Re)-Setting window handle! -> this=0x%08x, hWnd=0x%08x\n", this, hWnd ); + + Release(); + SetProp(hWnd, L"LilyHaxxor", (HANDLE)this); + + eatenWndProc = (WNDPROC) SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)OverrideWndProc); + hWndEaten = (eatenWndProc) ? hWnd : 0; + + return !!hWndEaten; +} + +void WndProcEater::Eat(ExtraWndProc proc, DWORD flags) { + + // check if Subclassing failed to init during SetWndHandle + if (!hWndEaten) return; + + // Probably isn't needed, but just in case... + if (hMutex) WaitForSingleObject(hMutex, 100); + + //printf( "(Lilypad) EatingWndProc! -> 0x%x\n", proc ); + + extraProcs = (ExtraWndProcInfo*) realloc(extraProcs, sizeof(ExtraWndProcInfo)*(numExtraProcs+1)); + extraProcs[numExtraProcs].proc = proc; + extraProcs[numExtraProcs].flags = flags; + numExtraProcs++; +} diff --git a/plugins/LilyPad/WndProcEater.h b/plugins/LilyPad/WndProcEater.h index 03671308cd..1b3b836734 100644 --- a/plugins/LilyPad/WndProcEater.h +++ b/plugins/LilyPad/WndProcEater.h @@ -1,44 +1,44 @@ -#define EATPROC_NO_UPDATE_WHILE_UPDATING_DEVICES 1 - -/* Need this to let window be subclassed multiple times but still clean up nicely. - */ -enum ExtraWndProcResult { - CONTINUE_BLISSFULLY, - // Calls ReleaseExtraProc without messing up order. - CONTINUE_BLISSFULLY_AND_RELEASE_PROC, - USE_DEFAULT_WND_PROC, - NO_WND_PROC -}; - -typedef ExtraWndProcResult (*ExtraWndProc)(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *out); - -struct ExtraWndProcInfo { - ExtraWndProc proc; - DWORD flags; -}; - -class WndProcEater -{ -public: - HWND hWndEaten; - WNDPROC eatenWndProc; - ExtraWndProcInfo* extraProcs; - int numExtraProcs; - - HANDLE hMutex; - -public: - WndProcEater(); - virtual ~WndProcEater() throw(); - - bool SetWndHandle(HWND hWnd); - void Eat(ExtraWndProc proc, DWORD flags); - void ReleaseExtraProc(ExtraWndProc proc); - void Release(); - - LRESULT _OverrideWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); -}; - -extern WndProcEater hWndGSProc; -extern WndProcEater hWndTopProc; -extern WndProcEater hWndButtonProc; +#define EATPROC_NO_UPDATE_WHILE_UPDATING_DEVICES 1 + +/* Need this to let window be subclassed multiple times but still clean up nicely. + */ +enum ExtraWndProcResult { + CONTINUE_BLISSFULLY, + // Calls ReleaseExtraProc without messing up order. + CONTINUE_BLISSFULLY_AND_RELEASE_PROC, + USE_DEFAULT_WND_PROC, + NO_WND_PROC +}; + +typedef ExtraWndProcResult (*ExtraWndProc)(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *out); + +struct ExtraWndProcInfo { + ExtraWndProc proc; + DWORD flags; +}; + +class WndProcEater +{ +public: + HWND hWndEaten; + WNDPROC eatenWndProc; + ExtraWndProcInfo* extraProcs; + int numExtraProcs; + + HANDLE hMutex; + +public: + WndProcEater(); + virtual ~WndProcEater() throw(); + + bool SetWndHandle(HWND hWnd); + void Eat(ExtraWndProc proc, DWORD flags); + void ReleaseExtraProc(ExtraWndProc proc); + void Release(); + + LRESULT _OverrideWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); +}; + +extern WndProcEater hWndGSProc; +extern WndProcEater hWndTopProc; +extern WndProcEater hWndButtonProc; diff --git a/plugins/LilyPad/XInput.cpp b/plugins/LilyPad/XInput.cpp index 0b89cb2292..b43c80d117 100644 --- a/plugins/LilyPad/XInput.cpp +++ b/plugins/LilyPad/XInput.cpp @@ -1,206 +1,206 @@ -#include "Global.h" -#include -#include "VKey.h" -#include "InputManager.h" - -// This way, I don't require that XInput junk be installed. -typedef void (CALLBACK *_XInputEnable)(BOOL enable); -typedef DWORD (CALLBACK *_XInputGetState)(DWORD dwUserIndex, XINPUT_STATE* pState); -typedef DWORD (CALLBACK *_XInputSetState)(DWORD dwUserIndex, XINPUT_VIBRATION* pVibration); - -_XInputEnable pXInputEnable = 0; -_XInputGetState pXInputGetState = 0; -_XInputSetState pXInputSetState = 0; - -static int xInputActiveCount = 0; - -// Completely unncessary, really. -__forceinline int ShortToAxis(int v) { - // If positive and at least 1 << 14, increment. - v += (!((v>>15)&1)) & ((v>>14)&1); - // Just double. - return v * 2; -} - -class XInputDevice : public Device { - // Cached last vibration values by pad and motor. - // Need this, as only one value is changed at a time. - int ps2Vibration[2][4][2]; - // Minor optimization - cache last set vibration values - // When there's no change, no need to do anything. - XINPUT_VIBRATION xInputVibration; -public: - int index; - - XInputDevice(int index, wchar_t *displayName) : Device(XINPUT, OTHER, displayName) { - memset(ps2Vibration, 0, sizeof(ps2Vibration)); - memset(&xInputVibration, 0, sizeof(xInputVibration)); - this->index = index; - int i; - for (i=0; i<14; i++) { - // The i > 9 accounts for the 2 bit skip in button flags. - AddPhysicalControl(PSHBTN, i + 2*(i > 9), 0); - } - for (i=14; i<16; i++) { - // The i > 9 accounts for the 2 bit skip in button flags. - AddPhysicalControl(PRESSURE_BTN, i + 2*(i > 9), 0); - } - for (; i<20; i++) { - AddPhysicalControl(ABSAXIS, i + 2, 0); - } - AddFFAxis(L"Slow Motor", 0); - AddFFAxis(L"Fast Motor", 1); - AddFFEffectType(L"Constant Effect", L"Constant", EFFECT_CONSTANT); - } - - wchar_t *GetPhysicalControlName(PhysicalControl *c) { - const static wchar_t *names[] = { - L"D-pad Up", - L"D-pad Down", - L"D-pad Left", - L"D-pad Right", - L"Start", - L"Back", - L"Left Thumb", - L"Right Thumb", - L"Left Shoulder", - L"Right Shoulder", - L"A", - L"B", - L"X", - L"Y", - L"Left Trigger", - L"Right Trigger", - L"Left Thumb X", - L"Left Thumb Y", - L"Right Thumb X", - L"Right Thumb Y", - }; - unsigned int i = (unsigned int) (c - physicalControls); - if (i < 20) { - return (wchar_t*)names[i]; - } - return Device::GetPhysicalControlName(c); - } - - int Activate(InitInfo *initInfo) { - if (active) Deactivate(); - if (!xInputActiveCount) { - pXInputEnable(1); - } - xInputActiveCount++; - active = 1; - AllocState(); - return 1; - } - - int Update() { - if (!active) return 0; - XINPUT_STATE state; - if (ERROR_SUCCESS != pXInputGetState(index, &state)) { - Deactivate(); - return 0; - } - int buttons = state.Gamepad.wButtons; - for (int i=0; i<14; i++) { - physicalControlState[i] = ((buttons >> physicalControls[i].id)&1)<<16; - } - physicalControlState[14] = (((int)state.Gamepad.bLeftTrigger) + (state.Gamepad.bLeftTrigger>>7)) << 8; - physicalControlState[15] = (((int)state.Gamepad.bRightTrigger) + (state.Gamepad.bRightTrigger>>7)) << 8; - physicalControlState[16] = ShortToAxis(state.Gamepad.sThumbLX); - physicalControlState[17] = ShortToAxis(state.Gamepad.sThumbLY); - physicalControlState[18] = ShortToAxis(state.Gamepad.sThumbRX); - physicalControlState[19] = ShortToAxis(state.Gamepad.sThumbRY); - return 1; - } - - void SetEffects(unsigned char port, unsigned int slot, unsigned char motor, unsigned char force) { - ps2Vibration[port][slot][motor] = force; - int newVibration[2] = {0,0}; - for (int p=0; p<2; p++) { - for (int s=0; s<4; s++) { - for (int i=0; iaxes[0].force * (__int64)ps2Vibration[p][s][ffb->motor]) / 255); - newVibration[1] += (int)((ffb->axes[1].force * (__int64)ps2Vibration[p][s][ffb->motor]) / 255); - } - } - } - newVibration[0] = abs(newVibration[0]); - if (newVibration[0] > 65535) { - newVibration[0] = 65535; - } - newVibration[1] = abs(newVibration[1]); - if (newVibration[1] > 65535) { - newVibration[1] = 65535; - } - if (newVibration[0] || newVibration[1] || newVibration[0] != xInputVibration.wLeftMotorSpeed || newVibration[1] != xInputVibration.wRightMotorSpeed) { - XINPUT_VIBRATION newv = {newVibration[0], newVibration[1]}; - if (ERROR_SUCCESS == pXInputSetState(index, &newv)) { - xInputVibration = newv; - } - } - } - - void SetEffect(ForceFeedbackBinding *binding, unsigned char force) { - PadBindings pBackup = pads[0][0]; - pads[0][0].ffBindings = binding; - pads[0][0].numFFBindings = 1; - SetEffects(0, 0, binding->motor, 255); - pads[0][0] = pBackup; - } - - void Deactivate() { - memset(&xInputVibration, 0, sizeof(xInputVibration)); - memset(ps2Vibration, 0, sizeof(ps2Vibration)); - pXInputSetState(index, &xInputVibration); - - FreeState(); - if (active) { - if (!--xInputActiveCount) { - pXInputEnable(0); - } - active = 0; - } - } - - ~XInputDevice() { - } -}; - -void EnumXInputDevices() { - int i; - wchar_t temp[30]; - if (!pXInputSetState) { - // Also used as flag to indicute XInput not installed, so - // don't repeatedly try to load it. - if (pXInputEnable) return; - - HMODULE hMod = 0; - for (i=3; i>= 0; i--) { - wsprintfW(temp, L"xinput1_%i.dll", i); - if (hMod = LoadLibraryW(temp)) break; - } - if (hMod) { - if ((pXInputEnable = (_XInputEnable) GetProcAddress(hMod, "XInputEnable")) && - (pXInputGetState = (_XInputGetState) GetProcAddress(hMod, "XInputGetState"))) { - pXInputSetState = (_XInputSetState) GetProcAddress(hMod, "XInputSetState"); - } - } - if (!pXInputSetState) { - pXInputEnable = (_XInputEnable)-1; - return; - } - } - pXInputEnable(1); - for (i=0; i<4; i++) { - XINPUT_STATE state; - if (ERROR_SUCCESS == pXInputGetState(i, &state)) { - wsprintfW(temp, L"XInput Pad %i", i); - dm->AddDevice(new XInputDevice(i, temp)); - } - } - pXInputEnable(0); -} - +#include "Global.h" +#include +#include "VKey.h" +#include "InputManager.h" + +// This way, I don't require that XInput junk be installed. +typedef void (CALLBACK *_XInputEnable)(BOOL enable); +typedef DWORD (CALLBACK *_XInputGetState)(DWORD dwUserIndex, XINPUT_STATE* pState); +typedef DWORD (CALLBACK *_XInputSetState)(DWORD dwUserIndex, XINPUT_VIBRATION* pVibration); + +_XInputEnable pXInputEnable = 0; +_XInputGetState pXInputGetState = 0; +_XInputSetState pXInputSetState = 0; + +static int xInputActiveCount = 0; + +// Completely unncessary, really. +__forceinline int ShortToAxis(int v) { + // If positive and at least 1 << 14, increment. + v += (!((v>>15)&1)) & ((v>>14)&1); + // Just double. + return v * 2; +} + +class XInputDevice : public Device { + // Cached last vibration values by pad and motor. + // Need this, as only one value is changed at a time. + int ps2Vibration[2][4][2]; + // Minor optimization - cache last set vibration values + // When there's no change, no need to do anything. + XINPUT_VIBRATION xInputVibration; +public: + int index; + + XInputDevice(int index, wchar_t *displayName) : Device(XINPUT, OTHER, displayName) { + memset(ps2Vibration, 0, sizeof(ps2Vibration)); + memset(&xInputVibration, 0, sizeof(xInputVibration)); + this->index = index; + int i; + for (i=0; i<14; i++) { + // The i > 9 accounts for the 2 bit skip in button flags. + AddPhysicalControl(PSHBTN, i + 2*(i > 9), 0); + } + for (i=14; i<16; i++) { + // The i > 9 accounts for the 2 bit skip in button flags. + AddPhysicalControl(PRESSURE_BTN, i + 2*(i > 9), 0); + } + for (; i<20; i++) { + AddPhysicalControl(ABSAXIS, i + 2, 0); + } + AddFFAxis(L"Slow Motor", 0); + AddFFAxis(L"Fast Motor", 1); + AddFFEffectType(L"Constant Effect", L"Constant", EFFECT_CONSTANT); + } + + wchar_t *GetPhysicalControlName(PhysicalControl *c) { + const static wchar_t *names[] = { + L"D-pad Up", + L"D-pad Down", + L"D-pad Left", + L"D-pad Right", + L"Start", + L"Back", + L"Left Thumb", + L"Right Thumb", + L"Left Shoulder", + L"Right Shoulder", + L"A", + L"B", + L"X", + L"Y", + L"Left Trigger", + L"Right Trigger", + L"Left Thumb X", + L"Left Thumb Y", + L"Right Thumb X", + L"Right Thumb Y", + }; + unsigned int i = (unsigned int) (c - physicalControls); + if (i < 20) { + return (wchar_t*)names[i]; + } + return Device::GetPhysicalControlName(c); + } + + int Activate(InitInfo *initInfo) { + if (active) Deactivate(); + if (!xInputActiveCount) { + pXInputEnable(1); + } + xInputActiveCount++; + active = 1; + AllocState(); + return 1; + } + + int Update() { + if (!active) return 0; + XINPUT_STATE state; + if (ERROR_SUCCESS != pXInputGetState(index, &state)) { + Deactivate(); + return 0; + } + int buttons = state.Gamepad.wButtons; + for (int i=0; i<14; i++) { + physicalControlState[i] = ((buttons >> physicalControls[i].id)&1)<<16; + } + physicalControlState[14] = (((int)state.Gamepad.bLeftTrigger) + (state.Gamepad.bLeftTrigger>>7)) << 8; + physicalControlState[15] = (((int)state.Gamepad.bRightTrigger) + (state.Gamepad.bRightTrigger>>7)) << 8; + physicalControlState[16] = ShortToAxis(state.Gamepad.sThumbLX); + physicalControlState[17] = ShortToAxis(state.Gamepad.sThumbLY); + physicalControlState[18] = ShortToAxis(state.Gamepad.sThumbRX); + physicalControlState[19] = ShortToAxis(state.Gamepad.sThumbRY); + return 1; + } + + void SetEffects(unsigned char port, unsigned int slot, unsigned char motor, unsigned char force) { + ps2Vibration[port][slot][motor] = force; + int newVibration[2] = {0,0}; + for (int p=0; p<2; p++) { + for (int s=0; s<4; s++) { + for (int i=0; iaxes[0].force * (__int64)ps2Vibration[p][s][ffb->motor]) / 255); + newVibration[1] += (int)((ffb->axes[1].force * (__int64)ps2Vibration[p][s][ffb->motor]) / 255); + } + } + } + newVibration[0] = abs(newVibration[0]); + if (newVibration[0] > 65535) { + newVibration[0] = 65535; + } + newVibration[1] = abs(newVibration[1]); + if (newVibration[1] > 65535) { + newVibration[1] = 65535; + } + if (newVibration[0] || newVibration[1] || newVibration[0] != xInputVibration.wLeftMotorSpeed || newVibration[1] != xInputVibration.wRightMotorSpeed) { + XINPUT_VIBRATION newv = {newVibration[0], newVibration[1]}; + if (ERROR_SUCCESS == pXInputSetState(index, &newv)) { + xInputVibration = newv; + } + } + } + + void SetEffect(ForceFeedbackBinding *binding, unsigned char force) { + PadBindings pBackup = pads[0][0]; + pads[0][0].ffBindings = binding; + pads[0][0].numFFBindings = 1; + SetEffects(0, 0, binding->motor, 255); + pads[0][0] = pBackup; + } + + void Deactivate() { + memset(&xInputVibration, 0, sizeof(xInputVibration)); + memset(ps2Vibration, 0, sizeof(ps2Vibration)); + pXInputSetState(index, &xInputVibration); + + FreeState(); + if (active) { + if (!--xInputActiveCount) { + pXInputEnable(0); + } + active = 0; + } + } + + ~XInputDevice() { + } +}; + +void EnumXInputDevices() { + int i; + wchar_t temp[30]; + if (!pXInputSetState) { + // Also used as flag to indicute XInput not installed, so + // don't repeatedly try to load it. + if (pXInputEnable) return; + + HMODULE hMod = 0; + for (i=3; i>= 0; i--) { + wsprintfW(temp, L"xinput1_%i.dll", i); + if (hMod = LoadLibraryW(temp)) break; + } + if (hMod) { + if ((pXInputEnable = (_XInputEnable) GetProcAddress(hMod, "XInputEnable")) && + (pXInputGetState = (_XInputGetState) GetProcAddress(hMod, "XInputGetState"))) { + pXInputSetState = (_XInputSetState) GetProcAddress(hMod, "XInputSetState"); + } + } + if (!pXInputSetState) { + pXInputEnable = (_XInputEnable)-1; + return; + } + } + pXInputEnable(1); + for (i=0; i<4; i++) { + XINPUT_STATE state; + if (ERROR_SUCCESS == pXInputGetState(i, &state)) { + wsprintfW(temp, L"XInput Pad %i", i); + dm->AddDevice(new XInputDevice(i, temp)); + } + } + pXInputEnable(0); +} + diff --git a/plugins/LilyPad/XInput.h b/plugins/LilyPad/XInput.h index defdd6b8aa..a39e8ba029 100644 --- a/plugins/LilyPad/XInput.h +++ b/plugins/LilyPad/XInput.h @@ -1 +1 @@ -void EnumXInputDevices(); +void EnumXInputDevices(); diff --git a/plugins/LilyPad/resource.h b/plugins/LilyPad/resource.h index 672ec89b63..8b49bd48be 100644 --- a/plugins/LilyPad/resource.h +++ b/plugins/LilyPad/resource.h @@ -1,152 +1,152 @@ -//{{NO_DEPENDENCIES}} -// Microsoft Visual C++ generated include file. -// Used by LilyPad.rc -// -#define IDD_GENERAL 102 -#define IDD_ABOUT 103 -#define IDI_FROG 104 -#define IDD_CONFIG 105 -#define IDD_CONFIG_GUITAR 106 -#define IDD_PROPPAGE_LARGE 107 -#define IDC_CURSOR1 107 -#define IDC_ESCAPE_FULLSCREEN_HACK 107 -#define IDD_DIAG 109 -#define IDC_CLOSE_HACK1 1099 -#define IDC_KB_DISABLE 1100 -#define IDC_KB_DI 1101 -#define IDC_KB_WM 1102 -#define IDC_KB_RAW 1103 -#define IDC_DISABLE_PAD1 1104 -#define IDC_M_DISABLE 1106 -#define IDC_M_DI 1107 -#define IDC_M_WM 1108 -#define IDC_M_RAW 1109 -#define IDC_G_XI 1110 -#define IDC_G_DI 1111 -#define IDC_G_DS3 1112 -#define IDC_CLOSE_HACK2 1113 -#define IDC_DEBUG_FILE 1114 -#define IDC_GUITAR1 1115 -#define IDC_ANALOG_START1 1117 -#define IDC_MULTITAP1 1118 -#define IDC_DISABLE_SCREENSAVER 1119 -#define IDC_MOUSE_UNFOCUS 1120 -#define IDC_AXIS_BUTTONS 1121 -#define IDC_MULTITAP2 1121 -#define IDC_BACKGROUND 1122 -#define IDC_MULTIPLE_BINDING 1123 -#define IDC_DISABLE_SCREENSAVER2 1124 -#define IDC_FORCE_HIDE 1124 -#define IDC_GH2_HACK 1125 -#define IDC_FORCEFEEDBACK_HACK1 1126 -#define IDC_VISTA_VOLUME 1126 -#define IDC_SAVE_STATE_TITLE 1129 -#define IDC_PAD_LIST 1133 -#define IDC_COMBO1 1134 -#define IDC_PAD_TYPE 1134 -#define IDC_SLIDER1 0x1000 -#define IDC_FLIP1 0x1001 -#define IDC_AXIS_DIRECTION 0x1002 -#define IDC_AXIS_CONTROL1 0x1003 -#define IDC_AXIS_SENSITIVITY1 0x1004 -#define IDC_TURBO 0x1005 -#define IDC_AXIS_DEVICE1 0x1006 -#define ID_SENSITIVITY 0x1007 -#define IDC_DPAD 0x1008 -#define IDC_LSTICK 0x1009 -#define IDC_RSTICK 0x100A -#define IDC_SLIDER_DEADZONE 0x1010 -#define IDC_AXIS_DEADZONE 0x1014 -#define IDC_LABEL_SENSITIVITY 0x1020 -#define IDC_LABEL_DEADZONE 0x1021 -#define ID_LOCK_BUTTONS 0x10FC -#define ID_LOCK 0x10FD -#define ID_LOCK_DIRECTION 0x10FE -#define ID_MOUSE 0x10FF -#define ID_SELECT 0x1100 -#define ID_L3 0x1101 -#define ID_R3 0x1102 -#define ID_START 0x1103 -#define ID_DPAD_UP 0x1104 -#define ID_DPAD_RIGHT 0x1105 -#define ID_DPAD_DOWN 0x1106 -#define ID_DPAD_LEFT 0x1107 -#define ID_L2 0x1108 -#define ID_R2 0x1109 -#define ID_L1 0x110A -#define ID_R1 0x110B -#define ID_TRIANGLE 0x110C -#define ID_CIRCLE 0x110D -#define ID_CROSS 0x110E -#define ID_SQUARE 0x110F -#define ID_LSTICK_UP 0x1110 -#define ID_LSTICK_RIGHT 0x1111 -#define ID_LSTICK_DOWN 0x1112 -#define ID_LSTICK_LEFT 0x1113 -#define ID_RSTICK_UP 0x1114 -#define ID_RSTICK_RIGHT 0x1115 -#define ID_RSTICK_DOWN 0x1116 -#define ID_RSTICK_LEFT 0x1117 -#define ID_ANALOG 0x1118 -#define ID_DELETE 0x11FF -#define ID_DEBUG 0x1200 -#define ID_IGNORE 0x1201 -#define ID_CLEAR 0x1202 -#define ID_REFRESH 0x1202 -#define ID_SAVE 0x1204 -#define ID_LOAD 0x1205 -#define IDC_LIST 0x1207 -#define IDC_FORCEFEEDBACK 0x1208 -#define IDC_FORCEFEEDBACK_FUNCTION 0x1209 -#define ID_BIG_MOTOR 0x120A -#define ID_SMALL_MOTOR 0x120B -#define ID_TEST 0x1300 -#define ID_CONTROLS 0x1301 -#define IDC_FF_DEVICE 0x1302 -#define IDC_FF_MOTOR 0x1303 -#define ID_FF 0x1304 -#define IDC_FF_EFFECT 0x1305 -#define IDC_VERSION 0x1306 -#define IDC_FF_AXIS1_ENABLED 0x1310 -#define IDC_FF_AXIS1 0x1311 -#define IDC_FF_AXIS1_FLIP 0x1312 -#define IDC_FF_AXIS1_SCALE 0x1313 -#define IDC_FF_AXIS2_ENABLED 0x1320 -#define IDC_FF_AXIS2 0x1321 -#define IDC_FF_AXIS2_FLIP 0x1322 -#define IDC_FF_AXIS2_SCALE 0x1323 -#define IDC_FF_AXIS3_ENABLED 0x1330 -#define IDC_FF_AXIS3 0x1331 -#define IDC_FF_AXIS3_FLIP 0x1332 -#define IDC_FF_AXIS3_SCALE 0x1333 -#define IDC_FF_AXIS4_ENABLED 0x1340 -#define IDC_FF_AXIS4 0x1341 -#define IDC_FF_AXIS4_FLIP 0x1342 -#define IDC_FF_AXIS4_SCALE 0x1343 -#define IDC_FF_AXIS5_ENABLED 0x1350 -#define IDC_FF_AXIS5 0x1351 -#define IDC_FF_AXIS5_FLIP 0x1352 -#define IDC_FF_AXIS5_SCALE 0x1353 -#define IDC_FF_AXIS6_ENABLED 0x1360 -#define IDC_FF_AXIS6 0x1361 -#define IDC_FF_AXIS6_FLIP 0x1362 -#define IDC_FF_AXIS6_SCALE 0x1363 -#define IDC_FF_AXIS7_ENABLED 0x1370 -#define IDC_FF_AXIS7 0x1371 -#define IDC_FF_AXIS7_FLIP 0x1372 -#define IDC_FF_AXIS7_SCALE 0x1373 -#define IDC_FF_AXIS8_ENABLED 0x1380 -#define IDC_FF_AXIS8 0x1381 -#define IDC_FF_AXIS8_FLIP 0x1382 -#define IDC_FF_AXIS8_SCALE 0x1383 - -// Next default values for new objects -// -#ifdef APSTUDIO_INVOKED -#ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 112 -#define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1135 -#define _APS_NEXT_SYMED_VALUE 101 -#endif -#endif +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by LilyPad.rc +// +#define IDD_GENERAL 102 +#define IDD_ABOUT 103 +#define IDI_FROG 104 +#define IDD_CONFIG 105 +#define IDD_CONFIG_GUITAR 106 +#define IDD_PROPPAGE_LARGE 107 +#define IDC_CURSOR1 107 +#define IDC_ESCAPE_FULLSCREEN_HACK 107 +#define IDD_DIAG 109 +#define IDC_CLOSE_HACK1 1099 +#define IDC_KB_DISABLE 1100 +#define IDC_KB_DI 1101 +#define IDC_KB_WM 1102 +#define IDC_KB_RAW 1103 +#define IDC_DISABLE_PAD1 1104 +#define IDC_M_DISABLE 1106 +#define IDC_M_DI 1107 +#define IDC_M_WM 1108 +#define IDC_M_RAW 1109 +#define IDC_G_XI 1110 +#define IDC_G_DI 1111 +#define IDC_G_DS3 1112 +#define IDC_CLOSE_HACK2 1113 +#define IDC_DEBUG_FILE 1114 +#define IDC_GUITAR1 1115 +#define IDC_ANALOG_START1 1117 +#define IDC_MULTITAP1 1118 +#define IDC_DISABLE_SCREENSAVER 1119 +#define IDC_MOUSE_UNFOCUS 1120 +#define IDC_AXIS_BUTTONS 1121 +#define IDC_MULTITAP2 1121 +#define IDC_BACKGROUND 1122 +#define IDC_MULTIPLE_BINDING 1123 +#define IDC_DISABLE_SCREENSAVER2 1124 +#define IDC_FORCE_HIDE 1124 +#define IDC_GH2_HACK 1125 +#define IDC_FORCEFEEDBACK_HACK1 1126 +#define IDC_VISTA_VOLUME 1126 +#define IDC_SAVE_STATE_TITLE 1129 +#define IDC_PAD_LIST 1133 +#define IDC_COMBO1 1134 +#define IDC_PAD_TYPE 1134 +#define IDC_SLIDER1 0x1000 +#define IDC_FLIP1 0x1001 +#define IDC_AXIS_DIRECTION 0x1002 +#define IDC_AXIS_CONTROL1 0x1003 +#define IDC_AXIS_SENSITIVITY1 0x1004 +#define IDC_TURBO 0x1005 +#define IDC_AXIS_DEVICE1 0x1006 +#define ID_SENSITIVITY 0x1007 +#define IDC_DPAD 0x1008 +#define IDC_LSTICK 0x1009 +#define IDC_RSTICK 0x100A +#define IDC_SLIDER_DEADZONE 0x1010 +#define IDC_AXIS_DEADZONE 0x1014 +#define IDC_LABEL_SENSITIVITY 0x1020 +#define IDC_LABEL_DEADZONE 0x1021 +#define ID_LOCK_BUTTONS 0x10FC +#define ID_LOCK 0x10FD +#define ID_LOCK_DIRECTION 0x10FE +#define ID_MOUSE 0x10FF +#define ID_SELECT 0x1100 +#define ID_L3 0x1101 +#define ID_R3 0x1102 +#define ID_START 0x1103 +#define ID_DPAD_UP 0x1104 +#define ID_DPAD_RIGHT 0x1105 +#define ID_DPAD_DOWN 0x1106 +#define ID_DPAD_LEFT 0x1107 +#define ID_L2 0x1108 +#define ID_R2 0x1109 +#define ID_L1 0x110A +#define ID_R1 0x110B +#define ID_TRIANGLE 0x110C +#define ID_CIRCLE 0x110D +#define ID_CROSS 0x110E +#define ID_SQUARE 0x110F +#define ID_LSTICK_UP 0x1110 +#define ID_LSTICK_RIGHT 0x1111 +#define ID_LSTICK_DOWN 0x1112 +#define ID_LSTICK_LEFT 0x1113 +#define ID_RSTICK_UP 0x1114 +#define ID_RSTICK_RIGHT 0x1115 +#define ID_RSTICK_DOWN 0x1116 +#define ID_RSTICK_LEFT 0x1117 +#define ID_ANALOG 0x1118 +#define ID_DELETE 0x11FF +#define ID_DEBUG 0x1200 +#define ID_IGNORE 0x1201 +#define ID_CLEAR 0x1202 +#define ID_REFRESH 0x1202 +#define ID_SAVE 0x1204 +#define ID_LOAD 0x1205 +#define IDC_LIST 0x1207 +#define IDC_FORCEFEEDBACK 0x1208 +#define IDC_FORCEFEEDBACK_FUNCTION 0x1209 +#define ID_BIG_MOTOR 0x120A +#define ID_SMALL_MOTOR 0x120B +#define ID_TEST 0x1300 +#define ID_CONTROLS 0x1301 +#define IDC_FF_DEVICE 0x1302 +#define IDC_FF_MOTOR 0x1303 +#define ID_FF 0x1304 +#define IDC_FF_EFFECT 0x1305 +#define IDC_VERSION 0x1306 +#define IDC_FF_AXIS1_ENABLED 0x1310 +#define IDC_FF_AXIS1 0x1311 +#define IDC_FF_AXIS1_FLIP 0x1312 +#define IDC_FF_AXIS1_SCALE 0x1313 +#define IDC_FF_AXIS2_ENABLED 0x1320 +#define IDC_FF_AXIS2 0x1321 +#define IDC_FF_AXIS2_FLIP 0x1322 +#define IDC_FF_AXIS2_SCALE 0x1323 +#define IDC_FF_AXIS3_ENABLED 0x1330 +#define IDC_FF_AXIS3 0x1331 +#define IDC_FF_AXIS3_FLIP 0x1332 +#define IDC_FF_AXIS3_SCALE 0x1333 +#define IDC_FF_AXIS4_ENABLED 0x1340 +#define IDC_FF_AXIS4 0x1341 +#define IDC_FF_AXIS4_FLIP 0x1342 +#define IDC_FF_AXIS4_SCALE 0x1343 +#define IDC_FF_AXIS5_ENABLED 0x1350 +#define IDC_FF_AXIS5 0x1351 +#define IDC_FF_AXIS5_FLIP 0x1352 +#define IDC_FF_AXIS5_SCALE 0x1353 +#define IDC_FF_AXIS6_ENABLED 0x1360 +#define IDC_FF_AXIS6 0x1361 +#define IDC_FF_AXIS6_FLIP 0x1362 +#define IDC_FF_AXIS6_SCALE 0x1363 +#define IDC_FF_AXIS7_ENABLED 0x1370 +#define IDC_FF_AXIS7 0x1371 +#define IDC_FF_AXIS7_FLIP 0x1372 +#define IDC_FF_AXIS7_SCALE 0x1373 +#define IDC_FF_AXIS8_ENABLED 0x1380 +#define IDC_FF_AXIS8 0x1381 +#define IDC_FF_AXIS8_FLIP 0x1382 +#define IDC_FF_AXIS8_SCALE 0x1383 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 112 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1135 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/plugins/LilyPad/usb.h b/plugins/LilyPad/usb.h index c6c5dca40e..2a9000ce2b 100644 --- a/plugins/LilyPad/usb.h +++ b/plugins/LilyPad/usb.h @@ -1,394 +1,394 @@ -// Note: From libusb. - -#ifndef __USB_H__ -#define __USB_H__ - -/* - * 'interface' is defined somewhere in the Windows header files. This macro - * is deleted here to avoid conflicts and compile errors. - */ - -#ifdef interface -#undef interface -#endif - -/* - * PATH_MAX from limits.h can't be used on Windows if the dll and - * import libraries are build/used by different compilers - */ - -#define LIBUSB_PATH_MAX 512 - - -/* - * USB spec information - * - * This is all stuff grabbed from various USB specs and is pretty much - * not subject to change - */ - -/* - * Device and/or Interface Class codes - */ -#define USB_CLASS_PER_INTERFACE 0 /* for DeviceClass */ -#define USB_CLASS_AUDIO 1 -#define USB_CLASS_COMM 2 -#define USB_CLASS_HID 3 -#define USB_CLASS_PRINTER 7 -#define USB_CLASS_MASS_STORAGE 8 -#define USB_CLASS_HUB 9 -#define USB_CLASS_DATA 10 -#define USB_CLASS_VENDOR_SPEC 0xff - -/* - * Descriptor types - */ -#define USB_DT_DEVICE 0x01 -#define USB_DT_CONFIG 0x02 -#define USB_DT_STRING 0x03 -#define USB_DT_INTERFACE 0x04 -#define USB_DT_ENDPOINT 0x05 - -#define USB_DT_HID 0x21 -#define USB_DT_REPORT 0x22 -#define USB_DT_PHYSICAL 0x23 -#define USB_DT_HUB 0x29 - -/* - * Descriptor sizes per descriptor type - */ -#define USB_DT_DEVICE_SIZE 18 -#define USB_DT_CONFIG_SIZE 9 -#define USB_DT_INTERFACE_SIZE 9 -#define USB_DT_ENDPOINT_SIZE 7 -#define USB_DT_ENDPOINT_AUDIO_SIZE 9 /* Audio extension */ -#define USB_DT_HUB_NONVAR_SIZE 7 - - -/* ensure byte-packed structures */ -#include - - -/* All standard descriptors have these 2 fields in common */ -struct usb_descriptor_header { - unsigned char bLength; - unsigned char bDescriptorType; -}; - -/* String descriptor */ -struct usb_string_descriptor { - unsigned char bLength; - unsigned char bDescriptorType; - unsigned short wData[1]; -}; - -/* HID descriptor */ -struct usb_hid_descriptor { - unsigned char bLength; - unsigned char bDescriptorType; - unsigned short bcdHID; - unsigned char bCountryCode; - unsigned char bNumDescriptors; -}; - -/* Endpoint descriptor */ -#define USB_MAXENDPOINTS 32 -struct usb_endpoint_descriptor { - unsigned char bLength; - unsigned char bDescriptorType; - unsigned char bEndpointAddress; - unsigned char bmAttributes; - unsigned short wMaxPacketSize; - unsigned char bInterval; - unsigned char bRefresh; - unsigned char bSynchAddress; - - unsigned char *extra; /* Extra descriptors */ - int extralen; -}; - -#define USB_ENDPOINT_ADDRESS_MASK 0x0f /* in bEndpointAddress */ -#define USB_ENDPOINT_DIR_MASK 0x80 - -#define USB_ENDPOINT_TYPE_MASK 0x03 /* in bmAttributes */ -#define USB_ENDPOINT_TYPE_CONTROL 0 -#define USB_ENDPOINT_TYPE_ISOCHRONOUS 1 -#define USB_ENDPOINT_TYPE_BULK 2 -#define USB_ENDPOINT_TYPE_INTERRUPT 3 - -/* Interface descriptor */ -#define USB_MAXINTERFACES 32 -struct usb_interface_descriptor { - unsigned char bLength; - unsigned char bDescriptorType; - unsigned char bInterfaceNumber; - unsigned char bAlternateSetting; - unsigned char bNumEndpoints; - unsigned char bInterfaceClass; - unsigned char bInterfaceSubClass; - unsigned char bInterfaceProtocol; - unsigned char iInterface; - - struct usb_endpoint_descriptor *endpoint; - - unsigned char *extra; /* Extra descriptors */ - int extralen; -}; - -#define USB_MAXALTSETTING 128 /* Hard limit */ - -struct usb_interface { - struct usb_interface_descriptor *altsetting; - - int num_altsetting; -}; - -/* Configuration descriptor information.. */ -#define USB_MAXCONFIG 8 -struct usb_config_descriptor { - unsigned char bLength; - unsigned char bDescriptorType; - unsigned short wTotalLength; - unsigned char bNumInterfaces; - unsigned char bConfigurationValue; - unsigned char iConfiguration; - unsigned char bmAttributes; - unsigned char MaxPower; - - struct usb_interface *interface; - - unsigned char *extra; /* Extra descriptors */ - int extralen; -}; - -/* Device descriptor */ -struct usb_device_descriptor { - unsigned char bLength; - unsigned char bDescriptorType; - unsigned short bcdUSB; - unsigned char bDeviceClass; - unsigned char bDeviceSubClass; - unsigned char bDeviceProtocol; - unsigned char bMaxPacketSize0; - unsigned short idVendor; - unsigned short idProduct; - unsigned short bcdDevice; - unsigned char iManufacturer; - unsigned char iProduct; - unsigned char iSerialNumber; - unsigned char bNumConfigurations; -}; - -struct usb_ctrl_setup { - unsigned char bRequestType; - unsigned char bRequest; - unsigned short wValue; - unsigned short wIndex; - unsigned short wLength; -}; - -/* - * Standard requests - */ -#define USB_REQ_GET_STATUS 0x00 -#define USB_REQ_CLEAR_FEATURE 0x01 -/* 0x02 is reserved */ -#define USB_REQ_SET_FEATURE 0x03 -/* 0x04 is reserved */ -#define USB_REQ_SET_ADDRESS 0x05 -#define USB_REQ_GET_DESCRIPTOR 0x06 -#define USB_REQ_SET_DESCRIPTOR 0x07 -#define USB_REQ_GET_CONFIGURATION 0x08 -#define USB_REQ_SET_CONFIGURATION 0x09 -#define USB_REQ_GET_INTERFACE 0x0A -#define USB_REQ_SET_INTERFACE 0x0B -#define USB_REQ_SYNCH_FRAME 0x0C - -#define USB_TYPE_STANDARD (0x00 << 5) -#define USB_TYPE_CLASS (0x01 << 5) -#define USB_TYPE_VENDOR (0x02 << 5) -#define USB_TYPE_RESERVED (0x03 << 5) - -#define USB_RECIP_DEVICE 0x00 -#define USB_RECIP_INTERFACE 0x01 -#define USB_RECIP_ENDPOINT 0x02 -#define USB_RECIP_OTHER 0x03 - -/* - * Various libusb API related stuff - */ - -#define USB_ENDPOINT_IN 0x80 -#define USB_ENDPOINT_OUT 0x00 - -/* Error codes */ -#define USB_ERROR_BEGIN 500000 - -/* - * This is supposed to look weird. This file is generated from autoconf - * and I didn't want to make this too complicated. - */ -#define USB_LE16_TO_CPU(x) - -/* Data types */ -/* struct usb_device; */ -/* struct usb_bus; */ - -struct usb_device { - struct usb_device *next, *prev; - - char filename[LIBUSB_PATH_MAX]; - - struct usb_bus *bus; - - struct usb_device_descriptor descriptor; - struct usb_config_descriptor *config; - - void *dev; /* Darwin support */ - - unsigned char devnum; - - unsigned char num_children; - struct usb_device **children; -}; - -struct usb_bus { - struct usb_bus *next, *prev; - - char dirname[LIBUSB_PATH_MAX]; - - struct usb_device *devices; - unsigned long location; - - struct usb_device *root_dev; -}; - -/* Version information, Windows specific */ -struct usb_version { - struct { - int major; - int minor; - int micro; - int nano; - } dll; - struct { - int major; - int minor; - int micro; - int nano; - } driver; -}; - - -struct usb_dev_handle; -typedef struct usb_dev_handle usb_dev_handle; - -/* Variables */ -#ifndef __USB_C__ -#define usb_busses usb_get_busses() -#endif - - - -#include - -#ifdef GOAT -#ifdef __cplusplus -extern "C" { -#endif - - /* Function prototypes */ - - /* usb.c */ - usb_dev_handle *usb_open(struct usb_device *dev); - int usb_close(usb_dev_handle *dev); - int usb_get_string(usb_dev_handle *dev, int index, int langid, char *buf, - size_t buflen); - int usb_get_string_simple(usb_dev_handle *dev, int index, char *buf, - size_t buflen); - - /* descriptors.c */ - int usb_get_descriptor_by_endpoint(usb_dev_handle *udev, int ep, - unsigned char type, unsigned char index, - void *buf, int size); - int usb_get_descriptor(usb_dev_handle *udev, unsigned char type, - unsigned char index, void *buf, int size); - - /* .c */ - int usb_bulk_write(usb_dev_handle *dev, int ep, char *bytes, int size, - int timeout); - int usb_bulk_read(usb_dev_handle *dev, int ep, char *bytes, int size, - int timeout); - int usb_interrupt_write(usb_dev_handle *dev, int ep, char *bytes, int size, - int timeout); - int usb_interrupt_read(usb_dev_handle *dev, int ep, char *bytes, int size, - int timeout); - int usb_control_msg(usb_dev_handle *dev, int requesttype, int request, - int value, int index, char *bytes, int size, - int timeout); - int usb_set_configuration(usb_dev_handle *dev, int configuration); - int usb_claim_interface(usb_dev_handle *dev, int interface); - int usb_release_interface(usb_dev_handle *dev, int interface); - int usb_set_altinterface(usb_dev_handle *dev, int alternate); - int usb_resetep(usb_dev_handle *dev, unsigned int ep); - int usb_clear_halt(usb_dev_handle *dev, unsigned int ep); - int usb_reset(usb_dev_handle *dev); - - char *usb_strerror(void); - - void usb_init(void); - void usb_set_debug(int level); - int usb_find_busses(void); - int usb_find_devices(void); - struct usb_device *usb_device(usb_dev_handle *dev); - struct usb_bus *usb_get_busses(void); - - - /* Windows specific functions */ - - #define LIBUSB_HAS_INSTALL_SERVICE_NP 1 - int usb_install_service_np(void); - void CALLBACK usb_install_service_np_rundll(HWND wnd, HINSTANCE instance, - LPSTR cmd_line, int cmd_show); - - #define LIBUSB_HAS_UNINSTALL_SERVICE_NP 1 - int usb_uninstall_service_np(void); - void CALLBACK usb_uninstall_service_np_rundll(HWND wnd, HINSTANCE instance, - LPSTR cmd_line, int cmd_show); - - #define LIBUSB_HAS_INSTALL_DRIVER_NP 1 - int usb_install_driver_np(const char *inf_file); - void CALLBACK usb_install_driver_np_rundll(HWND wnd, HINSTANCE instance, - LPSTR cmd_line, int cmd_show); - - #define LIBUSB_HAS_TOUCH_INF_FILE_NP 1 - int usb_touch_inf_file_np(const char *inf_file); - void CALLBACK usb_touch_inf_file_np_rundll(HWND wnd, HINSTANCE instance, - LPSTR cmd_line, int cmd_show); - - #define LIBUSB_HAS_INSTALL_NEEDS_RESTART_NP 1 - int usb_install_needs_restart_np(void); - - const struct usb_version *usb_get_version(void); - - int usb_isochronous_setup_async(usb_dev_handle *dev, void **context, - unsigned char ep, int pktsize); - int usb_bulk_setup_async(usb_dev_handle *dev, void **context, - unsigned char ep); - int usb_interrupt_setup_async(usb_dev_handle *dev, void **context, - unsigned char ep); - - int usb_submit_async(void *context, char *bytes, int size); - int usb_reap_async(void *context, int timeout); - int usb_reap_async_nocancel(void *context, int timeout); - int usb_cancel_async(void *context); - int usb_free_async(void **context); - - -#ifdef __cplusplus -} -#endif -#endif - -#endif /* __USB_H__ */ - +// Note: From libusb. + +#ifndef __USB_H__ +#define __USB_H__ + +/* + * 'interface' is defined somewhere in the Windows header files. This macro + * is deleted here to avoid conflicts and compile errors. + */ + +#ifdef interface +#undef interface +#endif + +/* + * PATH_MAX from limits.h can't be used on Windows if the dll and + * import libraries are build/used by different compilers + */ + +#define LIBUSB_PATH_MAX 512 + + +/* + * USB spec information + * + * This is all stuff grabbed from various USB specs and is pretty much + * not subject to change + */ + +/* + * Device and/or Interface Class codes + */ +#define USB_CLASS_PER_INTERFACE 0 /* for DeviceClass */ +#define USB_CLASS_AUDIO 1 +#define USB_CLASS_COMM 2 +#define USB_CLASS_HID 3 +#define USB_CLASS_PRINTER 7 +#define USB_CLASS_MASS_STORAGE 8 +#define USB_CLASS_HUB 9 +#define USB_CLASS_DATA 10 +#define USB_CLASS_VENDOR_SPEC 0xff + +/* + * Descriptor types + */ +#define USB_DT_DEVICE 0x01 +#define USB_DT_CONFIG 0x02 +#define USB_DT_STRING 0x03 +#define USB_DT_INTERFACE 0x04 +#define USB_DT_ENDPOINT 0x05 + +#define USB_DT_HID 0x21 +#define USB_DT_REPORT 0x22 +#define USB_DT_PHYSICAL 0x23 +#define USB_DT_HUB 0x29 + +/* + * Descriptor sizes per descriptor type + */ +#define USB_DT_DEVICE_SIZE 18 +#define USB_DT_CONFIG_SIZE 9 +#define USB_DT_INTERFACE_SIZE 9 +#define USB_DT_ENDPOINT_SIZE 7 +#define USB_DT_ENDPOINT_AUDIO_SIZE 9 /* Audio extension */ +#define USB_DT_HUB_NONVAR_SIZE 7 + + +/* ensure byte-packed structures */ +#include + + +/* All standard descriptors have these 2 fields in common */ +struct usb_descriptor_header { + unsigned char bLength; + unsigned char bDescriptorType; +}; + +/* String descriptor */ +struct usb_string_descriptor { + unsigned char bLength; + unsigned char bDescriptorType; + unsigned short wData[1]; +}; + +/* HID descriptor */ +struct usb_hid_descriptor { + unsigned char bLength; + unsigned char bDescriptorType; + unsigned short bcdHID; + unsigned char bCountryCode; + unsigned char bNumDescriptors; +}; + +/* Endpoint descriptor */ +#define USB_MAXENDPOINTS 32 +struct usb_endpoint_descriptor { + unsigned char bLength; + unsigned char bDescriptorType; + unsigned char bEndpointAddress; + unsigned char bmAttributes; + unsigned short wMaxPacketSize; + unsigned char bInterval; + unsigned char bRefresh; + unsigned char bSynchAddress; + + unsigned char *extra; /* Extra descriptors */ + int extralen; +}; + +#define USB_ENDPOINT_ADDRESS_MASK 0x0f /* in bEndpointAddress */ +#define USB_ENDPOINT_DIR_MASK 0x80 + +#define USB_ENDPOINT_TYPE_MASK 0x03 /* in bmAttributes */ +#define USB_ENDPOINT_TYPE_CONTROL 0 +#define USB_ENDPOINT_TYPE_ISOCHRONOUS 1 +#define USB_ENDPOINT_TYPE_BULK 2 +#define USB_ENDPOINT_TYPE_INTERRUPT 3 + +/* Interface descriptor */ +#define USB_MAXINTERFACES 32 +struct usb_interface_descriptor { + unsigned char bLength; + unsigned char bDescriptorType; + unsigned char bInterfaceNumber; + unsigned char bAlternateSetting; + unsigned char bNumEndpoints; + unsigned char bInterfaceClass; + unsigned char bInterfaceSubClass; + unsigned char bInterfaceProtocol; + unsigned char iInterface; + + struct usb_endpoint_descriptor *endpoint; + + unsigned char *extra; /* Extra descriptors */ + int extralen; +}; + +#define USB_MAXALTSETTING 128 /* Hard limit */ + +struct usb_interface { + struct usb_interface_descriptor *altsetting; + + int num_altsetting; +}; + +/* Configuration descriptor information.. */ +#define USB_MAXCONFIG 8 +struct usb_config_descriptor { + unsigned char bLength; + unsigned char bDescriptorType; + unsigned short wTotalLength; + unsigned char bNumInterfaces; + unsigned char bConfigurationValue; + unsigned char iConfiguration; + unsigned char bmAttributes; + unsigned char MaxPower; + + struct usb_interface *interface; + + unsigned char *extra; /* Extra descriptors */ + int extralen; +}; + +/* Device descriptor */ +struct usb_device_descriptor { + unsigned char bLength; + unsigned char bDescriptorType; + unsigned short bcdUSB; + unsigned char bDeviceClass; + unsigned char bDeviceSubClass; + unsigned char bDeviceProtocol; + unsigned char bMaxPacketSize0; + unsigned short idVendor; + unsigned short idProduct; + unsigned short bcdDevice; + unsigned char iManufacturer; + unsigned char iProduct; + unsigned char iSerialNumber; + unsigned char bNumConfigurations; +}; + +struct usb_ctrl_setup { + unsigned char bRequestType; + unsigned char bRequest; + unsigned short wValue; + unsigned short wIndex; + unsigned short wLength; +}; + +/* + * Standard requests + */ +#define USB_REQ_GET_STATUS 0x00 +#define USB_REQ_CLEAR_FEATURE 0x01 +/* 0x02 is reserved */ +#define USB_REQ_SET_FEATURE 0x03 +/* 0x04 is reserved */ +#define USB_REQ_SET_ADDRESS 0x05 +#define USB_REQ_GET_DESCRIPTOR 0x06 +#define USB_REQ_SET_DESCRIPTOR 0x07 +#define USB_REQ_GET_CONFIGURATION 0x08 +#define USB_REQ_SET_CONFIGURATION 0x09 +#define USB_REQ_GET_INTERFACE 0x0A +#define USB_REQ_SET_INTERFACE 0x0B +#define USB_REQ_SYNCH_FRAME 0x0C + +#define USB_TYPE_STANDARD (0x00 << 5) +#define USB_TYPE_CLASS (0x01 << 5) +#define USB_TYPE_VENDOR (0x02 << 5) +#define USB_TYPE_RESERVED (0x03 << 5) + +#define USB_RECIP_DEVICE 0x00 +#define USB_RECIP_INTERFACE 0x01 +#define USB_RECIP_ENDPOINT 0x02 +#define USB_RECIP_OTHER 0x03 + +/* + * Various libusb API related stuff + */ + +#define USB_ENDPOINT_IN 0x80 +#define USB_ENDPOINT_OUT 0x00 + +/* Error codes */ +#define USB_ERROR_BEGIN 500000 + +/* + * This is supposed to look weird. This file is generated from autoconf + * and I didn't want to make this too complicated. + */ +#define USB_LE16_TO_CPU(x) + +/* Data types */ +/* struct usb_device; */ +/* struct usb_bus; */ + +struct usb_device { + struct usb_device *next, *prev; + + char filename[LIBUSB_PATH_MAX]; + + struct usb_bus *bus; + + struct usb_device_descriptor descriptor; + struct usb_config_descriptor *config; + + void *dev; /* Darwin support */ + + unsigned char devnum; + + unsigned char num_children; + struct usb_device **children; +}; + +struct usb_bus { + struct usb_bus *next, *prev; + + char dirname[LIBUSB_PATH_MAX]; + + struct usb_device *devices; + unsigned long location; + + struct usb_device *root_dev; +}; + +/* Version information, Windows specific */ +struct usb_version { + struct { + int major; + int minor; + int micro; + int nano; + } dll; + struct { + int major; + int minor; + int micro; + int nano; + } driver; +}; + + +struct usb_dev_handle; +typedef struct usb_dev_handle usb_dev_handle; + +/* Variables */ +#ifndef __USB_C__ +#define usb_busses usb_get_busses() +#endif + + + +#include + +#ifdef GOAT +#ifdef __cplusplus +extern "C" { +#endif + + /* Function prototypes */ + + /* usb.c */ + usb_dev_handle *usb_open(struct usb_device *dev); + int usb_close(usb_dev_handle *dev); + int usb_get_string(usb_dev_handle *dev, int index, int langid, char *buf, + size_t buflen); + int usb_get_string_simple(usb_dev_handle *dev, int index, char *buf, + size_t buflen); + + /* descriptors.c */ + int usb_get_descriptor_by_endpoint(usb_dev_handle *udev, int ep, + unsigned char type, unsigned char index, + void *buf, int size); + int usb_get_descriptor(usb_dev_handle *udev, unsigned char type, + unsigned char index, void *buf, int size); + + /* .c */ + int usb_bulk_write(usb_dev_handle *dev, int ep, char *bytes, int size, + int timeout); + int usb_bulk_read(usb_dev_handle *dev, int ep, char *bytes, int size, + int timeout); + int usb_interrupt_write(usb_dev_handle *dev, int ep, char *bytes, int size, + int timeout); + int usb_interrupt_read(usb_dev_handle *dev, int ep, char *bytes, int size, + int timeout); + int usb_control_msg(usb_dev_handle *dev, int requesttype, int request, + int value, int index, char *bytes, int size, + int timeout); + int usb_set_configuration(usb_dev_handle *dev, int configuration); + int usb_claim_interface(usb_dev_handle *dev, int interface); + int usb_release_interface(usb_dev_handle *dev, int interface); + int usb_set_altinterface(usb_dev_handle *dev, int alternate); + int usb_resetep(usb_dev_handle *dev, unsigned int ep); + int usb_clear_halt(usb_dev_handle *dev, unsigned int ep); + int usb_reset(usb_dev_handle *dev); + + char *usb_strerror(void); + + void usb_init(void); + void usb_set_debug(int level); + int usb_find_busses(void); + int usb_find_devices(void); + struct usb_device *usb_device(usb_dev_handle *dev); + struct usb_bus *usb_get_busses(void); + + + /* Windows specific functions */ + + #define LIBUSB_HAS_INSTALL_SERVICE_NP 1 + int usb_install_service_np(void); + void CALLBACK usb_install_service_np_rundll(HWND wnd, HINSTANCE instance, + LPSTR cmd_line, int cmd_show); + + #define LIBUSB_HAS_UNINSTALL_SERVICE_NP 1 + int usb_uninstall_service_np(void); + void CALLBACK usb_uninstall_service_np_rundll(HWND wnd, HINSTANCE instance, + LPSTR cmd_line, int cmd_show); + + #define LIBUSB_HAS_INSTALL_DRIVER_NP 1 + int usb_install_driver_np(const char *inf_file); + void CALLBACK usb_install_driver_np_rundll(HWND wnd, HINSTANCE instance, + LPSTR cmd_line, int cmd_show); + + #define LIBUSB_HAS_TOUCH_INF_FILE_NP 1 + int usb_touch_inf_file_np(const char *inf_file); + void CALLBACK usb_touch_inf_file_np_rundll(HWND wnd, HINSTANCE instance, + LPSTR cmd_line, int cmd_show); + + #define LIBUSB_HAS_INSTALL_NEEDS_RESTART_NP 1 + int usb_install_needs_restart_np(void); + + const struct usb_version *usb_get_version(void); + + int usb_isochronous_setup_async(usb_dev_handle *dev, void **context, + unsigned char ep, int pktsize); + int usb_bulk_setup_async(usb_dev_handle *dev, void **context, + unsigned char ep); + int usb_interrupt_setup_async(usb_dev_handle *dev, void **context, + unsigned char ep); + + int usb_submit_async(void *context, char *bytes, int size); + int usb_reap_async(void *context, int timeout); + int usb_reap_async_nocancel(void *context, int timeout); + int usb_cancel_async(void *context); + int usb_free_async(void **context); + + +#ifdef __cplusplus +} +#endif +#endif + +#endif /* __USB_H__ */ + diff --git a/plugins/PeopsSPU2/alsa.h b/plugins/PeopsSPU2/alsa.h index d003e447cd..382cc7744b 100644 --- a/plugins/PeopsSPU2/alsa.h +++ b/plugins/PeopsSPU2/alsa.h @@ -1,30 +1,30 @@ -/*************************************************************************** - alsa.h - description - ------------------- - begin : Sat Mar 01 2003 - copyright : (C) 2002 by Pete Bernert - email : BlackDove@addcom.de - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - - -#ifndef _ALSA_SOUND_H -#define _ALSA_SOUND_H - -#ifdef ALSA_MEM_DEF -#define ALSA_MEM_EXTERN -#else -#define ALSA_MEM_EXTERN extern -#endif - -ALSA_MEM_EXTERN int sound_buffer_size; - -#endif // _ALSA_SOUND_H +/*************************************************************************** + alsa.h - description + ------------------- + begin : Sat Mar 01 2003 + copyright : (C) 2002 by Pete Bernert + email : BlackDove@addcom.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + + +#ifndef _ALSA_SOUND_H +#define _ALSA_SOUND_H + +#ifdef ALSA_MEM_DEF +#define ALSA_MEM_EXTERN +#else +#define ALSA_MEM_EXTERN extern +#endif + +ALSA_MEM_EXTERN int sound_buffer_size; + +#endif // _ALSA_SOUND_H diff --git a/plugins/PeopsSPU2/dma.c b/plugins/PeopsSPU2/dma.c index 1378f6c18a..e850460d7e 100644 --- a/plugins/PeopsSPU2/dma.c +++ b/plugins/PeopsSPU2/dma.c @@ -1,414 +1,414 @@ -/*************************************************************************** - dma.c - description - ------------------- - begin : Wed May 15 2002 - copyright : (C) 2002 by Pete Bernert - email : BlackDove@addcom.de - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. See also the license.txt file for * - * additional informations. * - * * - ***************************************************************************/ - -//*************************************************************************// -// History of changes: -// -// 2004/04/04 - Pete -// - changed plugin to emulate PS2 spu -// -// 2002/05/15 - Pete -// - generic cleanup for the Peops release -// -//*************************************************************************// - -#include "stdafx.h" -#include "externals.h" -#include "registers.h" -#include "debug.h" -extern void (CALLBACK *irqCallbackDMA4)(); // func of main emu, called on spu irq -extern void (CALLBACK *irqCallbackDMA7)(); // func of main emu, called on spu irq -extern void (CALLBACK *irqCallbackSPU2)(); // func of main emu, called on spu irq -unsigned short interrupt; -extern unsigned long SPUCycles; -unsigned long SPUStartCycle[2]; -unsigned long SPUTargetCycle[2]; - -unsigned long MemAddr[2]; - -ADMA Adma4; -ADMA Adma7; - -//////////////////////////////////////////////////////////////////////// -// READ DMA (many values) -//////////////////////////////////////////////////////////////////////// - -EXPORT_GCC void CALLBACK SPU2readDMA4Mem(unsigned short * pusPSXMem,int iSize) -{ - int i; -#ifdef _WINDOWS - if(iDebugMode==1) - { - logprintf("READDMA4 %X - %X\r\n",spuAddr2[0],iSize); - - if(spuAddr2[0]<=0x1fff) - logprintf("# OUTPUT AREA ACCESS #############\r\n"); - } - -#endif - - for(i=0;i0xfffff) spuAddr2[0]=0; // wrap - } - - spuAddr2[0]+=19; //Transfer Local To Host TSAH/L + Data Size + 20 (already +1'd) - - - iSpuAsyncWait=0; - - // got from J.F. and Kanodin... is it needed? - spuStat2[0]&=~0x80; // DMA complete - //if(regArea[(PS2_C0_ADMAS)>>1] != 1) { - // if((regArea[(PS2_C0_ATTR)>>1] & 0x30)) { - SPUStartCycle[0] = SPUCycles; - SPUTargetCycle[0] = iSize; - interrupt |= (1<<1); - // } -//} - //regArea[(PS2_C0_ADMAS)>>1] = 0; -} - -EXPORT_GCC void CALLBACK SPU2readDMA7Mem(unsigned short * pusPSXMem,int iSize) -{ - int i; -#ifdef _WINDOWS - if(iDebugMode==1) - { - logprintf("READDMA7 %X - %X\r\n",spuAddr2[1],iSize); - - if(spuAddr2[1]<=0x1fff) - logprintf("# OUTPUT AREA ACCESS #############\r\n"); - } -#endif - - for(i=0;i0xfffff) spuAddr2[1]=0; // wrap - } - -spuAddr2[1]+=19; //Transfer Local To Host TSAH/L + Data Size + 20 (already +1'd) - - iSpuAsyncWait=0; - - // got from J.F. and Kanodin... is it needed? - spuStat2[1]&=~0x80; // DMA complete - // if(regArea[(PS2_C1_ADMAS)>>1] != 2) { - // if((regArea[(PS2_C1_ATTR)>>1] & 0x30)) { - SPUStartCycle[1] = SPUCycles; - SPUTargetCycle[1] = iSize; - interrupt |= (1<<2); - // } - //} - //regArea[(PS2_C1_ADMAS)>>1] = 0; -} - -//////////////////////////////////////////////////////////////////////// -// WRITE DMA (many values) -//////////////////////////////////////////////////////////////////////// - - -// AutoDMA's are used to transfer to the DIRECT INPUT area of the spu2 memory -// Left and Right channels are always interleaved together in the transfer so -// the AutoDMA's deinterleaves them and transfers them. An interrupt is -// generated when half of the buffer (256 short-words for left and 256 -// short-words for right ) has been transferred. Another interrupt occurs at -// the end of the transfer. - -int ADMAS4Write() -{ - if(interrupt & 0x2) return 0; - if(Adma4.AmountLeft <= 0) { - if(Adma4.TempAmount == 0) return 1; - Adma4.AmountLeft = Adma4.TempAmount; - Adma4.MemAddr = Adma4.TempMem; - Adma4.TempMem = NULL; - Adma4.TempAmount = 0; - } - Adma4.TransferAmount = min(512, Adma4.AmountLeft); - if(Adma4.ADMAPos == 512) Adma4.ADMAPos = 0; - -#ifdef _WINDOWS - if(iDebugMode==1) - { - logprintf("ADMAWRITE4 %X - %X\r\n",spuAddr2[0],Adma4.AmountLeft); - if(Adma4.AmountLeft<512) logprintf("FUCK YOU %X\r\n",Adma4.AmountLeft); - } -#endif - - // SPU2 Deinterleaves the Left and Right Channels - memcpy((short*)(spuMem + Adma4.ADMAPos + 0x2000),(short*)Adma4.MemAddr,Adma4.TransferAmount); - Adma4.MemAddr += Adma4.TransferAmount / 2; - memcpy((short*)(spuMem + Adma4.ADMAPos + 0x2200),(short*)Adma4.MemAddr,Adma4.TransferAmount); - Adma4.MemAddr += Adma4.TransferAmount / 2; - - Adma4.ADMAPos += Adma4.TransferAmount / 2; - MemAddr[0] += Adma4.TransferAmount * 2; - //MemAddr[0] += 1024; - Adma4.AmountLeft-= Adma4.TransferAmount; - spuStat2[0]&=~0x80; - - if(Adma4.AmountLeft == 0) - { - if(Adma4.IRQ == 0){ - Adma4.IRQ = 1; - irqCallbackDMA4(); - } - } - return 0; -} - - -int ADMAS7Write() -{ - if(interrupt & 0x4) return 0; - if(Adma7.AmountLeft <= 0) { - if(Adma7.TempAmount == 0) return 1; - Adma7.AmountLeft = Adma7.TempAmount; - Adma7.MemAddr = Adma7.TempMem; - Adma7.TempMem = NULL; - Adma7.TempAmount = 0; - } - Adma7.TransferAmount = min(512, Adma7.AmountLeft); - if(Adma7.ADMAPos == 512) Adma7.ADMAPos = 0; - -#ifdef _WINDOWS - if(iDebugMode==1) - { - logprintf("ADMAWRITE7 %X - %X\r\n",spuAddr2[1],Adma7.AmountLeft); - if(Adma7.AmountLeft<512) logprintf("FUCK YOU %X\r\n",Adma7.AmountLeft); - } -#endif - - // SPU2 Deinterleaves the Left and Right Channels - memcpy((short*)(spuMem + Adma7.ADMAPos + 0x2400),(short*)Adma7.MemAddr,Adma7.TransferAmount); - Adma7.MemAddr += Adma7.TransferAmount / 2; - memcpy((short*)(spuMem + Adma7.ADMAPos + 0x2600),(short*)Adma7.MemAddr,Adma7.TransferAmount); - Adma7.MemAddr += Adma7.TransferAmount / 2; - - Adma7.ADMAPos += Adma7.TransferAmount / 2; - MemAddr[1] += Adma7.TransferAmount * 2; - //MemAddr[1] += 1024; - Adma7.AmountLeft-=Adma7.TransferAmount; - spuStat2[1]&=~0x80; - - if(Adma7.AmountLeft == 0) - { - if(Adma7.IRQ == 0){ - Adma7.IRQ = 1; - irqCallbackDMA7(); - } - } - return 0; -} - -#include -extern FILE * LogFile; -EXPORT_GCC void CALLBACK SPU2writeDMA4Mem(short * pMem,unsigned int iSize) -{ - //if(Adma4.AmountLeft > 0) return; - if(regArea[PS2_C0_ADMAS] & 0x1 && (spuCtrl2[0] & 0x30) == 0 && iSize) - { - //fwrite(pMem,iSize<<1,1,LogFile); - // memset(&Adma4,0,sizeof(ADMA)); - //if( !Adma4.Enabled ) - // Adma4.Index = 0; - //Adma4.ADMAPos = 0; - if((Adma4.ADMAPos == 512 && Adma4.Index <= 256) || (Adma4.ADMAPos == 256 && Adma4.Index >= 256) || Adma4.AmountLeft >= 512) { - Adma4.TempMem = pMem; - Adma4.TempAmount = iSize; - } else { - Adma4.MemAddr = pMem; - Adma4.AmountLeft += iSize; - ADMAS4Write(); - } - return; - } - -#ifdef _WINDOWS - if(iDebugMode==1) - { - logprintf("WRITEDMA4 %X - %X\r\n",spuAddr2[0],iSize); - } -#endif - - memcpy((unsigned char*)(spuMem+spuAddr2[0]),(unsigned char*)pMem,iSize<<1); - if(spuCtrl2[0]&0x40 && (spuIrq2[0] >= spuAddr2[0] && spuIrq2[0] <= (spuAddr2[0] + iSize))){ - regArea[0x7C0] |= 0x4; - regArea[PS2_IRQINFO] |= 0x4; - irqCallbackSPU2(); - } - spuAddr2[0] += iSize; - - if(spuAddr2[0]>0x23FF) spuAddr2[0] = 0x2000; - - MemAddr[0] += iSize<<1; - spuStat2[0]&=~0x80; - SPUStartCycle[0] = SPUCycles; - SPUTargetCycle[0] = 1;//iSize; - interrupt |= (1<<1); -} - -void LogRawSound(void* pleft, int leftstride, void* pright, int rightstride, int numsamples) -{ -#ifdef _DEBUG - static FILE* g_fLogSound = NULL; - - char* left = (char*)pleft; - char* right = (char*)pright; - unsigned short* tempbuf; - int i; - - if( g_fLogSound == NULL ) { - g_fLogSound = fopen("rawsndbuf.pcm", "wb"); - if( g_fLogSound == NULL ) - return; - } - - tempbuf = (unsigned short*)malloc(4*numsamples); - - for(i = 0; i < numsamples; ++i) { - tempbuf[2*i+0] = *(unsigned short*)left; - tempbuf[2*i+1] = *(unsigned short*)right; - left += leftstride; - right += rightstride; - } - - fwrite(&tempbuf[0], 4*numsamples, 1, g_fLogSound); - free(tempbuf); -#endif -} - -EXPORT_GCC void CALLBACK SPU2writeDMA7Mem(unsigned short * pMem,int iSize) -{ - // For AutoDMA, the ATTR register's bit 5 and 6 are cleared. - // bit 5 means Data Input Thru Register - // bit 6 means Data Input Thru DMA - //if(Adma7.AmountLeft > 0) return; - - if((regArea[PS2_C1_ADMAS] & 0x2) && (spuCtrl2[1] & 0x30) == 0 && iSize) - { - //fwrite(pMem,iSize<<1,1,LogFile); - // memset(&Adma7,0,sizeof(ADMA)); - //if( !Adma7.Enabled ) - // Adma7.Index = 0; - //Adma7.ADMAPos = 0; - if((Adma7.ADMAPos == 512 && Adma7.Index <= 256) || (Adma7.ADMAPos == 256 && Adma7.Index >= 256) || Adma7.AmountLeft >= 512) { - Adma7.TempMem = pMem; - Adma7.TempAmount = iSize; - } else { - Adma7.MemAddr = pMem; - Adma7.AmountLeft += iSize; - ADMAS7Write(); - } - return; - } -#ifdef _WINDOWS - if(iDebugMode==1) - { - logprintf("WRITEDMA7 %X - %X\r\n",spuAddr2[1],iSize); - } -#endif - - memcpy((short*)(spuMem+spuAddr2[1]),(short*)pMem,iSize<<1); - if(spuCtrl2[1]&0x40 && (spuIrq2[1] >= spuAddr2[1] && spuIrq2[1] <= (spuAddr2[1] + iSize))){ - regArea[0x7C0] |= 0x8; - regArea[PS2_IRQINFO] |= 8; - irqCallbackSPU2(); - } - spuAddr2[1] += iSize; - - if(spuAddr2[1]>0x27FF) spuAddr2[1] = 0x2400; - - MemAddr[1] += iSize<<1; - spuStat2[1]&=~0x80; - SPUStartCycle[1] = SPUCycles; - SPUTargetCycle[1] = 1;//iSize; - interrupt |= (1<<2); -} - - -//////////////////////////////////////////////////////////////////////// -// INTERRUPTS -//////////////////////////////////////////////////////////////////////// - -void InterruptDMA4(void) -{ -// taken from linuzappz NULL spu2 -// spu2Rs16(CORE0_ATTR)&= ~0x30; -// spu2Rs16(REG__1B0) = 0; -// spu2Rs16(SPU2_STATX_WRDY_M)|= 0x80; - -#ifdef _WINDOWS - if(iDebugMode==1) logprintf("IRQDMA4\r\n"); -#endif - Adma4.IRQ = 0; - spuCtrl2[0]&=~0x30; - spuStat2[0]|=0x80; -} - -EXPORT_GCC void CALLBACK SPU2interruptDMA4(void) -{ - InterruptDMA4(); -} - -void InterruptDMA7(void) -{ -// taken from linuzappz NULL spu2 -// spu2Rs16(CORE1_ATTR)&= ~0x30; -// spu2Rs16(REG__5B0) = 0; -// spu2Rs16(SPU2_STATX_DREQ)|= 0x80; - -#ifdef _WINDOWS - if(iDebugMode==1) logprintf("IRQDMA7\r\n"); -#endif - Adma7.IRQ = 0; - spuStat2[1]|=0x80; - spuCtrl2[1]&=~0x30; -} - -EXPORT_GCC void CALLBACK SPU2interruptDMA7(void) -{ - InterruptDMA7(); -} - -EXPORT_GCC void CALLBACK SPU2WriteMemAddr(int core, unsigned long value) -{ - MemAddr[core] = value; -} - -EXPORT_GCC unsigned long CALLBACK SPU2ReadMemAddr(int core) -{ - return MemAddr[core]; +/*************************************************************************** + dma.c - description + ------------------- + begin : Wed May 15 2002 + copyright : (C) 2002 by Pete Bernert + email : BlackDove@addcom.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. See also the license.txt file for * + * additional informations. * + * * + ***************************************************************************/ + +//*************************************************************************// +// History of changes: +// +// 2004/04/04 - Pete +// - changed plugin to emulate PS2 spu +// +// 2002/05/15 - Pete +// - generic cleanup for the Peops release +// +//*************************************************************************// + +#include "stdafx.h" +#include "externals.h" +#include "registers.h" +#include "debug.h" +extern void (CALLBACK *irqCallbackDMA4)(); // func of main emu, called on spu irq +extern void (CALLBACK *irqCallbackDMA7)(); // func of main emu, called on spu irq +extern void (CALLBACK *irqCallbackSPU2)(); // func of main emu, called on spu irq +unsigned short interrupt; +extern unsigned long SPUCycles; +unsigned long SPUStartCycle[2]; +unsigned long SPUTargetCycle[2]; + +unsigned long MemAddr[2]; + +ADMA Adma4; +ADMA Adma7; + +//////////////////////////////////////////////////////////////////////// +// READ DMA (many values) +//////////////////////////////////////////////////////////////////////// + +EXPORT_GCC void CALLBACK SPU2readDMA4Mem(unsigned short * pusPSXMem,int iSize) +{ + int i; +#ifdef _WINDOWS + if(iDebugMode==1) + { + logprintf("READDMA4 %X - %X\r\n",spuAddr2[0],iSize); + + if(spuAddr2[0]<=0x1fff) + logprintf("# OUTPUT AREA ACCESS #############\r\n"); + } + +#endif + + for(i=0;i0xfffff) spuAddr2[0]=0; // wrap + } + + spuAddr2[0]+=19; //Transfer Local To Host TSAH/L + Data Size + 20 (already +1'd) + + + iSpuAsyncWait=0; + + // got from J.F. and Kanodin... is it needed? + spuStat2[0]&=~0x80; // DMA complete + //if(regArea[(PS2_C0_ADMAS)>>1] != 1) { + // if((regArea[(PS2_C0_ATTR)>>1] & 0x30)) { + SPUStartCycle[0] = SPUCycles; + SPUTargetCycle[0] = iSize; + interrupt |= (1<<1); + // } +//} + //regArea[(PS2_C0_ADMAS)>>1] = 0; +} + +EXPORT_GCC void CALLBACK SPU2readDMA7Mem(unsigned short * pusPSXMem,int iSize) +{ + int i; +#ifdef _WINDOWS + if(iDebugMode==1) + { + logprintf("READDMA7 %X - %X\r\n",spuAddr2[1],iSize); + + if(spuAddr2[1]<=0x1fff) + logprintf("# OUTPUT AREA ACCESS #############\r\n"); + } +#endif + + for(i=0;i0xfffff) spuAddr2[1]=0; // wrap + } + +spuAddr2[1]+=19; //Transfer Local To Host TSAH/L + Data Size + 20 (already +1'd) + + iSpuAsyncWait=0; + + // got from J.F. and Kanodin... is it needed? + spuStat2[1]&=~0x80; // DMA complete + // if(regArea[(PS2_C1_ADMAS)>>1] != 2) { + // if((regArea[(PS2_C1_ATTR)>>1] & 0x30)) { + SPUStartCycle[1] = SPUCycles; + SPUTargetCycle[1] = iSize; + interrupt |= (1<<2); + // } + //} + //regArea[(PS2_C1_ADMAS)>>1] = 0; +} + +//////////////////////////////////////////////////////////////////////// +// WRITE DMA (many values) +//////////////////////////////////////////////////////////////////////// + + +// AutoDMA's are used to transfer to the DIRECT INPUT area of the spu2 memory +// Left and Right channels are always interleaved together in the transfer so +// the AutoDMA's deinterleaves them and transfers them. An interrupt is +// generated when half of the buffer (256 short-words for left and 256 +// short-words for right ) has been transferred. Another interrupt occurs at +// the end of the transfer. + +int ADMAS4Write() +{ + if(interrupt & 0x2) return 0; + if(Adma4.AmountLeft <= 0) { + if(Adma4.TempAmount == 0) return 1; + Adma4.AmountLeft = Adma4.TempAmount; + Adma4.MemAddr = Adma4.TempMem; + Adma4.TempMem = NULL; + Adma4.TempAmount = 0; + } + Adma4.TransferAmount = min(512, Adma4.AmountLeft); + if(Adma4.ADMAPos == 512) Adma4.ADMAPos = 0; + +#ifdef _WINDOWS + if(iDebugMode==1) + { + logprintf("ADMAWRITE4 %X - %X\r\n",spuAddr2[0],Adma4.AmountLeft); + if(Adma4.AmountLeft<512) logprintf("FUCK YOU %X\r\n",Adma4.AmountLeft); + } +#endif + + // SPU2 Deinterleaves the Left and Right Channels + memcpy((short*)(spuMem + Adma4.ADMAPos + 0x2000),(short*)Adma4.MemAddr,Adma4.TransferAmount); + Adma4.MemAddr += Adma4.TransferAmount / 2; + memcpy((short*)(spuMem + Adma4.ADMAPos + 0x2200),(short*)Adma4.MemAddr,Adma4.TransferAmount); + Adma4.MemAddr += Adma4.TransferAmount / 2; + + Adma4.ADMAPos += Adma4.TransferAmount / 2; + MemAddr[0] += Adma4.TransferAmount * 2; + //MemAddr[0] += 1024; + Adma4.AmountLeft-= Adma4.TransferAmount; + spuStat2[0]&=~0x80; + + if(Adma4.AmountLeft == 0) + { + if(Adma4.IRQ == 0){ + Adma4.IRQ = 1; + irqCallbackDMA4(); + } + } + return 0; +} + + +int ADMAS7Write() +{ + if(interrupt & 0x4) return 0; + if(Adma7.AmountLeft <= 0) { + if(Adma7.TempAmount == 0) return 1; + Adma7.AmountLeft = Adma7.TempAmount; + Adma7.MemAddr = Adma7.TempMem; + Adma7.TempMem = NULL; + Adma7.TempAmount = 0; + } + Adma7.TransferAmount = min(512, Adma7.AmountLeft); + if(Adma7.ADMAPos == 512) Adma7.ADMAPos = 0; + +#ifdef _WINDOWS + if(iDebugMode==1) + { + logprintf("ADMAWRITE7 %X - %X\r\n",spuAddr2[1],Adma7.AmountLeft); + if(Adma7.AmountLeft<512) logprintf("FUCK YOU %X\r\n",Adma7.AmountLeft); + } +#endif + + // SPU2 Deinterleaves the Left and Right Channels + memcpy((short*)(spuMem + Adma7.ADMAPos + 0x2400),(short*)Adma7.MemAddr,Adma7.TransferAmount); + Adma7.MemAddr += Adma7.TransferAmount / 2; + memcpy((short*)(spuMem + Adma7.ADMAPos + 0x2600),(short*)Adma7.MemAddr,Adma7.TransferAmount); + Adma7.MemAddr += Adma7.TransferAmount / 2; + + Adma7.ADMAPos += Adma7.TransferAmount / 2; + MemAddr[1] += Adma7.TransferAmount * 2; + //MemAddr[1] += 1024; + Adma7.AmountLeft-=Adma7.TransferAmount; + spuStat2[1]&=~0x80; + + if(Adma7.AmountLeft == 0) + { + if(Adma7.IRQ == 0){ + Adma7.IRQ = 1; + irqCallbackDMA7(); + } + } + return 0; +} + +#include +extern FILE * LogFile; +EXPORT_GCC void CALLBACK SPU2writeDMA4Mem(short * pMem,unsigned int iSize) +{ + //if(Adma4.AmountLeft > 0) return; + if(regArea[PS2_C0_ADMAS] & 0x1 && (spuCtrl2[0] & 0x30) == 0 && iSize) + { + //fwrite(pMem,iSize<<1,1,LogFile); + // memset(&Adma4,0,sizeof(ADMA)); + //if( !Adma4.Enabled ) + // Adma4.Index = 0; + //Adma4.ADMAPos = 0; + if((Adma4.ADMAPos == 512 && Adma4.Index <= 256) || (Adma4.ADMAPos == 256 && Adma4.Index >= 256) || Adma4.AmountLeft >= 512) { + Adma4.TempMem = pMem; + Adma4.TempAmount = iSize; + } else { + Adma4.MemAddr = pMem; + Adma4.AmountLeft += iSize; + ADMAS4Write(); + } + return; + } + +#ifdef _WINDOWS + if(iDebugMode==1) + { + logprintf("WRITEDMA4 %X - %X\r\n",spuAddr2[0],iSize); + } +#endif + + memcpy((unsigned char*)(spuMem+spuAddr2[0]),(unsigned char*)pMem,iSize<<1); + if(spuCtrl2[0]&0x40 && (spuIrq2[0] >= spuAddr2[0] && spuIrq2[0] <= (spuAddr2[0] + iSize))){ + regArea[0x7C0] |= 0x4; + regArea[PS2_IRQINFO] |= 0x4; + irqCallbackSPU2(); + } + spuAddr2[0] += iSize; + + if(spuAddr2[0]>0x23FF) spuAddr2[0] = 0x2000; + + MemAddr[0] += iSize<<1; + spuStat2[0]&=~0x80; + SPUStartCycle[0] = SPUCycles; + SPUTargetCycle[0] = 1;//iSize; + interrupt |= (1<<1); +} + +void LogRawSound(void* pleft, int leftstride, void* pright, int rightstride, int numsamples) +{ +#ifdef _DEBUG + static FILE* g_fLogSound = NULL; + + char* left = (char*)pleft; + char* right = (char*)pright; + unsigned short* tempbuf; + int i; + + if( g_fLogSound == NULL ) { + g_fLogSound = fopen("rawsndbuf.pcm", "wb"); + if( g_fLogSound == NULL ) + return; + } + + tempbuf = (unsigned short*)malloc(4*numsamples); + + for(i = 0; i < numsamples; ++i) { + tempbuf[2*i+0] = *(unsigned short*)left; + tempbuf[2*i+1] = *(unsigned short*)right; + left += leftstride; + right += rightstride; + } + + fwrite(&tempbuf[0], 4*numsamples, 1, g_fLogSound); + free(tempbuf); +#endif +} + +EXPORT_GCC void CALLBACK SPU2writeDMA7Mem(unsigned short * pMem,int iSize) +{ + // For AutoDMA, the ATTR register's bit 5 and 6 are cleared. + // bit 5 means Data Input Thru Register + // bit 6 means Data Input Thru DMA + //if(Adma7.AmountLeft > 0) return; + + if((regArea[PS2_C1_ADMAS] & 0x2) && (spuCtrl2[1] & 0x30) == 0 && iSize) + { + //fwrite(pMem,iSize<<1,1,LogFile); + // memset(&Adma7,0,sizeof(ADMA)); + //if( !Adma7.Enabled ) + // Adma7.Index = 0; + //Adma7.ADMAPos = 0; + if((Adma7.ADMAPos == 512 && Adma7.Index <= 256) || (Adma7.ADMAPos == 256 && Adma7.Index >= 256) || Adma7.AmountLeft >= 512) { + Adma7.TempMem = pMem; + Adma7.TempAmount = iSize; + } else { + Adma7.MemAddr = pMem; + Adma7.AmountLeft += iSize; + ADMAS7Write(); + } + return; + } +#ifdef _WINDOWS + if(iDebugMode==1) + { + logprintf("WRITEDMA7 %X - %X\r\n",spuAddr2[1],iSize); + } +#endif + + memcpy((short*)(spuMem+spuAddr2[1]),(short*)pMem,iSize<<1); + if(spuCtrl2[1]&0x40 && (spuIrq2[1] >= spuAddr2[1] && spuIrq2[1] <= (spuAddr2[1] + iSize))){ + regArea[0x7C0] |= 0x8; + regArea[PS2_IRQINFO] |= 8; + irqCallbackSPU2(); + } + spuAddr2[1] += iSize; + + if(spuAddr2[1]>0x27FF) spuAddr2[1] = 0x2400; + + MemAddr[1] += iSize<<1; + spuStat2[1]&=~0x80; + SPUStartCycle[1] = SPUCycles; + SPUTargetCycle[1] = 1;//iSize; + interrupt |= (1<<2); +} + + +//////////////////////////////////////////////////////////////////////// +// INTERRUPTS +//////////////////////////////////////////////////////////////////////// + +void InterruptDMA4(void) +{ +// taken from linuzappz NULL spu2 +// spu2Rs16(CORE0_ATTR)&= ~0x30; +// spu2Rs16(REG__1B0) = 0; +// spu2Rs16(SPU2_STATX_WRDY_M)|= 0x80; + +#ifdef _WINDOWS + if(iDebugMode==1) logprintf("IRQDMA4\r\n"); +#endif + Adma4.IRQ = 0; + spuCtrl2[0]&=~0x30; + spuStat2[0]|=0x80; +} + +EXPORT_GCC void CALLBACK SPU2interruptDMA4(void) +{ + InterruptDMA4(); +} + +void InterruptDMA7(void) +{ +// taken from linuzappz NULL spu2 +// spu2Rs16(CORE1_ATTR)&= ~0x30; +// spu2Rs16(REG__5B0) = 0; +// spu2Rs16(SPU2_STATX_DREQ)|= 0x80; + +#ifdef _WINDOWS + if(iDebugMode==1) logprintf("IRQDMA7\r\n"); +#endif + Adma7.IRQ = 0; + spuStat2[1]|=0x80; + spuCtrl2[1]&=~0x30; +} + +EXPORT_GCC void CALLBACK SPU2interruptDMA7(void) +{ + InterruptDMA7(); +} + +EXPORT_GCC void CALLBACK SPU2WriteMemAddr(int core, unsigned long value) +{ + MemAddr[core] = value; +} + +EXPORT_GCC unsigned long CALLBACK SPU2ReadMemAddr(int core) +{ + return MemAddr[core]; } \ No newline at end of file diff --git a/plugins/PeopsSPU2/spu.c b/plugins/PeopsSPU2/spu.c index f86d83114a..1ee63593de 100644 --- a/plugins/PeopsSPU2/spu.c +++ b/plugins/PeopsSPU2/spu.c @@ -1,1311 +1,1311 @@ -/*************************************************************************** - spu.c - description - ------------------- - begin : Wed May 15 2002 - copyright : (C) 2002 by Pete Bernert - email : BlackDove@addcom.de - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. See also the license.txt file for * - * additional informations. * - * * - ***************************************************************************/ - -//*************************************************************************// -// History of changes: -// -// 2005/08/29 - Pete -// - changed to 48Khz output -// -// 2004/12/25 - Pete -// - inc'd version for pcsx2-0.7 -// -// 2004/04/18 - Pete -// - changed all kind of things in the plugin -// -// 2004/04/04 - Pete -// - changed plugin to emulate PS2 spu -// -// 2003/04/07 - Eric -// - adjusted cubic interpolation algorithm -// -// 2003/03/16 - Eric -// - added cubic interpolation -// -// 2003/03/01 - linuzappz -// - libraryName changes using ALSA -// -// 2003/02/28 - Pete -// - added option for type of interpolation -// - adjusted spu irqs again (Thousant Arms, Valkyrie Profile) -// - added MONO support for MSWindows DirectSound -// -// 2003/02/20 - kode54 -// - amended interpolation code, goto GOON could skip initialization of gpos and cause segfault -// -// 2003/02/19 - kode54 -// - moved SPU IRQ handler and changed sample flag processing -// -// 2003/02/18 - kode54 -// - moved ADSR calculation outside of the sample decode loop, somehow I doubt that -// ADSR timing is relative to the frequency at which a sample is played... I guess -// this remains to be seen, and I don't know whether ADSR is applied to noise channels... -// -// 2003/02/09 - kode54 -// - one-shot samples now process the end block before stopping -// - in light of removing fmod hack, now processing ADSR on frequency channel as well -// -// 2003/02/08 - kode54 -// - replaced easy interpolation with gaussian -// - removed fmod averaging hack -// - changed .sinc to be updated from .iRawPitch, no idea why it wasn't done this way already (<- Pete: because I sometimes fail to see the obvious, haharhar :) -// -// 2003/02/08 - linuzappz -// - small bugfix for one usleep that was 1 instead of 1000 -// - added iDisStereo for no stereo (Linux) -// -// 2003/01/22 - Pete -// - added easy interpolation & small noise adjustments -// -// 2003/01/19 - Pete -// - added Neill's reverb -// -// 2003/01/12 - Pete -// - added recording window handlers -// -// 2003/01/06 - Pete -// - added Neill's ADSR timings -// -// 2002/12/28 - Pete -// - adjusted spu irq handling, fmod handling and loop handling -// -// 2002/08/14 - Pete -// - added extra reverb -// -// 2002/06/08 - linuzappz -// - SPUupdate changed for SPUasync -// -// 2002/05/15 - Pete -// - generic cleanup for the Peops release -// -//*************************************************************************// - -#include "stdafx.h" - -#define _IN_SPU - -#include "externals.h" -#include "cfg.h" -#include "dsoundoss.h" -#include "regs.h" -#include "debug.h" -#include "record.h" -#include "resource.h" -#include "dma.h" -#include "registers.h" - -#if !defined(_WIN32) - -#include - -#if !defined(CALLBACK) -#define CALLBACK -#endif - -#define Sleep(x) sleep((x)*1000) -#endif - -//////////////////////////////////////////////////////////////////////// -// spu version infos/name -//////////////////////////////////////////////////////////////////////// - -const unsigned char version = 5; -const unsigned char revision = 1; -const unsigned char build = 9; -#ifdef _DEBUG -static char * libraryName = "P.E.Op.S. SPU2 (Debug) "; -#else -static char * libraryName = "P.E.Op.S. SPU2 "; -#endif -static char * libraryInfo = "P.E.Op.S. SPU2 Driver V1.6\nCoded by Pete Bernert, Saqib and the P.E.Op.S. team\n"; - -//////////////////////////////////////////////////////////////////////// -// globals -//////////////////////////////////////////////////////////////////////// - -// psx buffer / addresses - -unsigned short regArea[32*1024]; - short spuMem[2*1024*1024]; - char * spuMemC; -unsigned char * pSpuIrq[2]; -unsigned char * pSpuBuffer; -unsigned char * pSpuStreamBuffer[2]; - -// user settings - -int iVolume=3; -int iDebugMode=0; -int iRecordMode=0; -int iUseReverb=0; -int iUseInterpolation=2; - -// MAIN infos struct for each channel -int iDisStereo; - -SPUCHAN s_chan[MAXCHAN+1]; // channel + 1 infos (1 is security for fmod handling) -REVERBInfo rvb[2]; - -unsigned long dwNoiseVal=1; // global noise generator -unsigned short spuCtrl2[2]; // some vars to store psx reg infos -unsigned short spuStat2[2]; -unsigned long spuIrq2[2]; -unsigned long spuAddr2[2]; // address into spu mem -unsigned long spuRvbAddr2[2]; -unsigned long spuRvbAEnd2[2]; -int bEndThread=0; // thread handlers -int bSpuInit=0; -int bSPUIsOpen=0; -int bThreadEnded=0; -int iUseTimer=2; -int aSyncMode=0; -unsigned long aSyncCounter=0; -unsigned long aSyncWait=0; -DWORD aSyncTimerNew; -DWORD aSyncTimerOld; - -#ifdef _WIN32 -HWND hWMain=0; // window handle -HWND hWDebug=0; -HWND hWRecord=0; - -static HANDLE hMainThread; -#else -#include -static pthread_t hMainThread; -#endif - -unsigned long dwNewChannel2[2]; // flags for faster testing, if new channel starts -unsigned long dwEndChannel2[2]; - -void (CALLBACK *irqCallbackDMA4)()=0; // func of main emu, called on spu irq -void (CALLBACK *irqCallbackDMA7)()=0; // func of main emu, called on spu irq -void (CALLBACK *irqCallbackSPU2)()=0; // func of main emu, called on spu irq - -// certain globals (were local before, but with the new timeproc I need em global) - -const int f[5][2] = { { 0, 0 }, - { 60, 0 }, - { 115, -52 }, - { 98, -55 }, - { 122, -60 } }; -int SSumR[NSSIZE]; -int SSumL[NSSIZE]; - -extern ADMA Adma4; -extern ADMA Adma7; -DINPUT DirectInputC0, DirectInputC1; - -extern unsigned short interrupt; -unsigned long SPUCycles; -extern unsigned long SPUStartCycle[2]; -extern unsigned long SPUTargetCycle[2]; - -int iCycle=0; -short * pS; -short * pS1; - -static int lastch=-1; // last channel processed on spu irq in timer mode -static int lastns=0; // last ns pos -static int iSecureStart=0; // secure start counter - -//////////////////////////////////////////////////////////////////////// -// CODE AREA -//////////////////////////////////////////////////////////////////////// - -// dirty inline func includes - -#include "reverb.c" -#include "adsr.c" - -//////////////////////////////////////////////////////////////////////// -// helpers for simple interpolation - -// -// easy interpolation on upsampling, no special filter, just "Pete's common sense" tm -// -// instead of having n equal sample values in a row like: -// ____ -// |____ -// -// we compare the current delta change with the next delta change. -// -// if curr_delta is positive, -// -// - and next delta is smaller (or changing direction): -// \. -// -__ -// -// - and next delta significant (at least twice) bigger: -// --_ -// \. -// -// - and next delta is nearly same: -// \. -// \. -// -// -// if curr_delta is negative, -// -// - and next delta is smaller (or changing direction): -// _-- -// / -// -// - and next delta significant (at least twice) bigger: -// / -// __- -// -// - and next delta is nearly same: -// / -// / -// - - -INLINE void InterpolateUp(int ch) -{ - if(s_chan[ch].SB[32]==1) // flag == 1? calc step and set flag... and don't change the value in this pass - { - const int id1=s_chan[ch].SB[30]-s_chan[ch].SB[29]; // curr delta to next val - const int id2=s_chan[ch].SB[31]-s_chan[ch].SB[30]; // and next delta to next-next val :) - - s_chan[ch].SB[32]=0; - - if(id1>0) // curr delta positive - { - if(id2id1) - {s_chan[ch].SB[28]=id1;s_chan[ch].SB[32]=2;} - else - if(id2>(id1<<1)) - s_chan[ch].SB[28]=(id1*s_chan[ch].sinc)/0x10000L; - else - s_chan[ch].SB[28]=(id1*s_chan[ch].sinc)/0x20000L; - } - } - else - if(s_chan[ch].SB[32]==2) // flag 1: calc step and set flag... and don't change the value in this pass - { - s_chan[ch].SB[32]=0; - - s_chan[ch].SB[28]=(s_chan[ch].SB[28]*s_chan[ch].sinc)/0x20000L; - if(s_chan[ch].sinc<=0x8000) - s_chan[ch].SB[29]=s_chan[ch].SB[30]-(s_chan[ch].SB[28]*((0x10000/s_chan[ch].sinc)-1)); - else s_chan[ch].SB[29]+=s_chan[ch].SB[28]; - } - else // no flags? add bigger val (if possible), calc smaller step, set flag1 - s_chan[ch].SB[29]+=s_chan[ch].SB[28]; -} - -// -// even easier interpolation on downsampling, also no special filter, again just "Pete's common sense" tm -// - -INLINE void InterpolateDown(int ch) -{ - if(s_chan[ch].sinc>=0x20000) // we would skip at least one val? - { - s_chan[ch].SB[29]+=(s_chan[ch].SB[30]-s_chan[ch].SB[29])/2; // add easy weight - if(s_chan[ch].sinc>=0x30000) // we would skip even more vals? - s_chan[ch].SB[29]+=(s_chan[ch].SB[31]-s_chan[ch].SB[30])/2;// add additional next weight - } -} - -//////////////////////////////////////////////////////////////////////// -// helpers for gauss interpolation - -#define gval0 (((short*)(&s_chan[ch].SB[29]))[gpos]) -#define gval(x) (((short*)(&s_chan[ch].SB[29]))[(gpos+x)&3]) - -#include "gauss_i.h" - -//////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////// -// START SOUND... called by main thread to setup a new sound on a channel -//////////////////////////////////////////////////////////////////////// - -INLINE void StartSound(int ch) -{ - dwNewChannel2[ch/24]&=~(1<<(ch%24)); // clear new channel bit - dwEndChannel2[ch/24]&=~(1<<(ch%24)); // clear end channel bit - - StartADSR(ch); - StartREVERB(ch); - - s_chan[ch].pCurr=s_chan[ch].pStart; // set sample start - - s_chan[ch].s_1=0; // init mixing vars - s_chan[ch].s_2=0; - s_chan[ch].iSBPos=28; - - s_chan[ch].bNew=0; // init channel flags - s_chan[ch].bStop=0; - s_chan[ch].bOn=1; - - s_chan[ch].SB[29]=0; // init our interpolation helpers - s_chan[ch].SB[30]=0; - - if(iUseInterpolation>=2) // gauss interpolation? - {s_chan[ch].spos=0x30000L;s_chan[ch].SB[28]=0;} // -> start with more decoding - else {s_chan[ch].spos=0x10000L;s_chan[ch].SB[31]=0;} // -> no/simple interpolation starts with one 44100 decoding -} - - -void UpdateMainVolL() // LEFT VOLUME -{ - short vol = regArea[PS2_C0_MVOLL]; - if(vol&0x8000) // sweep? - { - short sInc=1; // -> sweep up? - if(vol&0x2000) sInc=-1; // -> or down? - if(vol&0x1000) vol^=0xffff; // -> mmm... phase inverted? have to investigate this - vol=((vol&0x7f)+1)/2; // -> sweep: 0..127 -> 0..64 - vol+=vol/(2*sInc); // -> HACK: we don't sweep right now, so we just raise/lower the volume by the half! - vol*=128; - } - else // no sweep: - { - if(vol&0x4000) // -> mmm... phase inverted? have to investigate this - //vol^=0xffff; - vol=0x3fff-(vol&0x3fff); - } - - vol&=0x3fff; - regArea[PS2_C0_MVOLL]=vol; // store volume -} - -//////////////////////////////////////////////////////////////////////// -// RIGHT VOLUME register write -//////////////////////////////////////////////////////////////////////// - -void UpdateMainVolR() // RIGHT VOLUME -{ - short vol = regArea[PS2_C0_MVOLR]; - if(vol&0x8000) // comments... see above :) - { - short sInc=1; - if(vol&0x2000) sInc=-1; - if(vol&0x1000) vol^=0xffff; - vol=((vol&0x7f)+1)/2; - vol+=vol/(2*sInc); - vol*=128; - } - else - { - if(vol&0x4000) //vol=vol^=0xffff; - vol=0x3fff-(vol&0x3fff); - } - - vol&=0x3fff; - - regArea[PS2_C0_MVOLR]=vol; -} -//////////////////////////////////////////////////////////////////////// -// MAIN SPU FUNCTION -// here is the main job handler... thread, timer or direct func call -// basically the whole sound processing is done in this fat func! -//////////////////////////////////////////////////////////////////////// - -// 5 ms waiting phase, if buffer is full and no new sound has to get started -// .. can be made smaller (smallest val: 1 ms), but bigger waits give -// better performance - -#define PAUSE_W 5 -#define PAUSE_L 5000 -extern unsigned long MemAddr[2]; -//////////////////////////////////////////////////////////////////////// - -int iSpuAsyncWait=0; -extern int MMIXC0, MMIXC1; -extern int ADMAS4Write(); -extern int ADMAS7Write(); - -extern void SoundFeedVoiceData(); - -void CALLBACK MainSPU2Proc(unsigned int nTimerId, unsigned int msg, unsigned int dwUser, - unsigned int dwParam1, unsigned int dwParam2) -{ - int s_1,s_2,fa,ns; - int core = 0; - unsigned char * start; - unsigned int nSample; - int d; - int ch,predict_nr,shift_factor,flags,s; - int gpos; - - while(!bEndThread) // until we are shutting down - { - //--------------------------------------------------// - // ok, at the beginning we are looking if there is - // enuff free place in the dsound/oss buffer to - // fill in new data, or if there is a new channel to start. - // if not, we wait (thread) or return (timer/spuasync) - // until enuff free place is available/a new channel gets - // started - if (aSyncMode==1) // Async supported? and enabled? - { - if (aSyncCounter<=737280) // If we have 10ms in the buffer, don't wait - { - if (aSyncWait<1000) Sleep(aSyncWait); // Wait a little to be more Synced (No more than 1 sec) - else Sleep (1000); - } - - while (aSyncCounter<=368640 && !bEndThread && aSyncMode==1) - Sleep (1); // bEndThread/aSyncMode are needed, to avoid close problems - - aSyncCounter -= 36864; // 1ms more done (48Hz*768cycles/Hz) - } - else - if(dwNewChannel2[0] || dwNewChannel2[1]) // new channel should start immedately? - { // (at least one bit 0 ... MAXCHANNEL is set?) - iSecureStart++; // -> set iSecure - if(iSecureStart>5) iSecureStart=0; // (if it is set 5 times - that means on 5 tries a new samples has been started - in a row, we will reset it, to give the sound update a chance) - } - else iSecureStart=0; // 0: no new channel should start - - while(!iSecureStart && !bEndThread && // no new start? no thread end? - (SoundGetBytesBuffered()>TESTSIZE)) // and still enuff data in sound buffer? - { - iSecureStart=0; // reset secure - - if(iUseTimer) // no-thread mode? - { - return; // -> and done this time (timer mode 1 or 2) - } - // win thread mode: - Sleep(PAUSE_W); // sleep for x ms (win) - - if(dwNewChannel2[0] || dwNewChannel2[1]) - iSecureStart=1; // if a new channel kicks in (or, of course, sound buffer runs low), we will leave the loop - } - //--------------------------------------------------// continue from irq handling in timer mode? - - if(lastch>=0) // will be -1 if no continue is pending - { - ch=lastch; ns=lastns; lastch=-1; // -> setup all kind of vars to continue - if( s_chan[ch].iSBPos < 28 ) { - goto GOON; // -> directly jump to the continue point - } - } - - //--------------------------------------------------// - //- main channel loop -// - //--------------------------------------------------// - { - for(ch=0;ch take it and calc steps - s_chan[ch].sinc = s_chan[ch].iRawPitch<<4; - if(!s_chan[ch].sinc) s_chan[ch].sinc=1; - if(iUseInterpolation==1) s_chan[ch].SB[32]=1; // -> freq change in simle imterpolation mode: set flag - } - - ns=0; - while(ns=0x10000L) - { - if(s_chan[ch].iSBPos==28) // 28 reached? - { - start=s_chan[ch].pCurr; // set up the current pos - - if (s_chan[ch].bOn==0) goto ENDX; // special "stop" sign - - s_chan[ch].iSBPos=0; - - s_1=s_chan[ch].s_1; - s_2=s_chan[ch].s_2; - - predict_nr = (int)*start; - start++; - flags = (int)*start; - start++; - - shift_factor = predict_nr&0xf; - predict_nr >>= 4; - // -------------------------------------- // - - for (nSample=0;nSample<28;nSample+=2,start++) - { - d=(int)*start; - s=((d&0xf)<<12); - if(s&0x8000) s|=0xffff0000; - - fa=(s >> shift_factor); - fa=fa + ((s_1 * f[predict_nr][0])>>6) + ((s_2 * f[predict_nr][1])>>6); - s_2=s_1; - s_1=fa; - s=((d & 0xf0) << 8); - - s_chan[ch].SB[nSample]=fa; - - if(s&0x8000) s|=0xffff0000; - fa=(s>>shift_factor); - fa=fa + ((s_1 * f[predict_nr][0])>>6) + ((s_2 * f[predict_nr][1])>>6); - s_2=s_1; - s_1=fa; - - s_chan[ch].SB[nSample+1]=fa; - } - - //////////////////////////////////////////// irq check - - if(spuCtrl2[core]&0x40) // some irq active? - { - if(iDebugMode==1) logprintf("IRQ Active ch %x, C%x\r\n", ch, ch/24); - - if( pSpuIrq[core] >= start-16 && // irq address reached? - pSpuIrq[core] <= start) - { - s_chan[ch].iIrqDone=1; // -> debug flag - - if(iDebugMode==1) logprintf("Sample End ch %x, C%x\r\n", ch, ch/24); - - regArea[0x7C0] |= 0x4< let's see what is happening if we call our irqs instead ;) - - } - } - - //////////////////////////////////////////// flag handler - if(flags & 0x2) s_chan[ch].bIgnoreLoop=1; // LOOP bit - - if(flags&0x4) s_chan[ch].pLoop=start-16; // LOOP/START bit - - if(flags&0x1) // 1: LOOP/END bit - { - dwEndChannel2[core]|=(1<<(ch%24)); - - if((flags&0xF) != 0x3 || s_chan[ch].pLoop == NULL) // Check if no loop is present - { - s_chan[ch].bIgnoreLoop=0; - s_chan[ch].bStop = 1; - logprintf("Stopping\r\n"); - } - else start = s_chan[ch].pLoop; - } - - s_chan[ch].pCurr=start; // store values for next cycle - s_chan[ch].s_1=s_1; - s_chan[ch].s_2=s_2; - - - //////////////////////////////////////////// - -GOON: ; - } - - fa=s_chan[ch].SB[s_chan[ch].iSBPos++]; // get sample data - - if((spuCtrl2[core]&0x4000)==0) fa=0; // muted? - else // else adjust - { - if(fa>32767L) fa=32767L; - if(fa<-32767L) fa=-32767L; - } - - if(iUseInterpolation>=2) // gauss/cubic interpolation - { - gpos = s_chan[ch].SB[28]; - gval0 = fa; - gpos = (gpos+1) & 3; - s_chan[ch].SB[28] = gpos; - } - else - if(iUseInterpolation==1) // simple interpolation - { - s_chan[ch].SB[28] = 0; - s_chan[ch].SB[29] = s_chan[ch].SB[30]; // -> helpers for simple linear interpolation: delay real val for two slots, and calc the two deltas, for a 'look at the future behaviour' - s_chan[ch].SB[30] = s_chan[ch].SB[31]; - s_chan[ch].SB[31] = fa; - s_chan[ch].SB[32] = 1; // -> flag: calc new interolation - } - else s_chan[ch].SB[29]=fa; // no interpolation - - s_chan[ch].spos -= 0x10000L; - } - - //////////////////////////////////////////////// - // noise handler... just produces some noise data - // surely wrong... and no noise frequency (spuCtrl&0x3f00) will be used... - // and sometimes the noise will be used as fmod modulation... pfff - - if(s_chan[ch].bNoise) - { - if((dwNoiseVal<<=1)&0x80000000L) - { - dwNoiseVal^=0x0040001L; - fa=((dwNoiseVal>>2)&0x7fff); - fa=-fa; - } - else fa=(dwNoiseVal>>2)&0x7fff; - - // mmm... depending on the noise freq we allow bigger/smaller changes to the previous val - fa=s_chan[ch].iOldNoise+((fa-s_chan[ch].iOldNoise)/((0x001f-((spuCtrl2[core]&0x3f00)>>9))+1)); - if(fa>32767L) fa=32767L; - if(fa<-32767L) fa=-32767L; - s_chan[ch].iOldNoise=fa; - - if(iUseInterpolation<2) // no gauss/cubic interpolation? - s_chan[ch].SB[29] = fa; // -> store noise val in "current sample" slot - } //---------------------------------------- - else // NO NOISE (NORMAL SAMPLE DATA) HERE - {//------------------------------------------// - if(iUseInterpolation==3) // cubic interpolation - { - long xd; - xd = ((s_chan[ch].spos) >> 1)+1; - gpos = s_chan[ch].SB[28]; - - fa = gval(3) - 3*gval(2) + 3*gval(1) - gval0; - fa *= (xd - (2<<15)) / 6; - fa >>= 15; - fa += gval(2) - gval(1) - gval(1) + gval0; - fa *= (xd - (1<<15)) >> 1; - fa >>= 15; - fa += gval(1) - gval0; - fa *= xd; - fa >>= 15; - fa = fa + gval0; - } - //------------------------------------------// - else - if(iUseInterpolation==2) // gauss interpolation - { - int vl, vr; - vl = (s_chan[ch].spos >> 6) & ~3; - gpos = s_chan[ch].SB[28]; - vr=(gauss[vl]*gval0)&~2047; - vr+=(gauss[vl+1]*gval(1))&~2047; - vr+=(gauss[vl+2]*gval(2))&~2047; - vr+=(gauss[vl+3]*gval(3))&~2047; - fa = vr>>11; - } - //------------------------------------------// - else - if(iUseInterpolation==1) // simple interpolation - { - if(s_chan[ch].sinc<0x10000L) // -> upsampling? - InterpolateUp(ch); // --> interpolate up - else InterpolateDown(ch); // --> else down - fa=s_chan[ch].SB[29]; - } - //------------------------------------------// - else fa=s_chan[ch].SB[29]; // no interpolation - } - - s_chan[ch].sval = (MixADSR(ch) * fa) / 1023; // add adsr - - if(s_chan[ch].bFMod==2) // fmod freq channel - { - int NP=((32768L+s_chan[ch].sval)*s_chan[ch+1].iRawPitch)>>14; - - if(NP>0x3fff) NP=0x3fff; - else if(NP<0x1) NP=0x1; - - NP=(48000L*NP)>>12; // calc frequency ( 48hz ) - - s_chan[ch+1].iActFreq=NP; - s_chan[ch+1].iUsedFreq=NP; - s_chan[ch+1].sinc=(((NP/10)<<16)/48000); // check , was 4800 - if(!s_chan[ch+1].sinc) s_chan[ch+1].sinc=1; - if(iUseInterpolation==1) // freq change in sipmle interpolation mode - s_chan[ch+1].SB[32]=1; - - } - else - { - ////////////////////////////////////////////// - // ok, left/right sound volume (ps2 volume goes from 0 ... 0x3fff) - - if(s_chan[ch].iMute) s_chan[ch].sval=0; // debug mute - else - { - if(s_chan[ch].bVolumeL) - SSumL[ns]+=(s_chan[ch].sval*s_chan[ch].iLeftVolume)>>14; - - if(s_chan[ch].bVolumeR) - SSumR[ns]+=(s_chan[ch].sval*s_chan[ch].iRightVolume)>>14; - } - - ////////////////////////////////////////////// - // now let us store sound data for reverb - - if(s_chan[ch].bRVBActive) StoreREVERB(ch,ns); - } - - //////////////////////////////////////////////// - // ok, go on until 1 ms data of this channel is collected - - ns++; - s_chan[ch].spos += s_chan[ch].sinc; - } -ENDX: ; - } - } - - //---------------------------------------------------// - //- here we have another 1 ms of sound data - //---------------------------------------------------// - /////////////////////////////////////////////////////// - // mix all channels (including reverb) into one buffer - for(ns=0;ns Adma4.Index)) - { - if(ADMAS4Write()) - { - //if( Adma4.AmountLeft == 0 ) - if(Adma4.IRQ == 0 && (spuCtrl2[0] & 0x30)){ - Adma4.IRQ = 1; - irqCallbackDMA4(); - } - } - } - if(Adma4.Index == 512) { - Adma4.Index = 0; - } - - - } - - if((regArea[PS2_C1_MMIX] & 0xF0) && regArea[PS2_C1_ADMAS] & 0x2 /*&& !(spuCtrl2[1] & 0x30)*/) - { - DirectInputC1.Left = ((short*)spuMem)[0x2400+Adma7.Index]; - DirectInputC1.Right = ((short*)spuMem)[0x2600+Adma7.Index]; - if(spuCtrl2[1]&0x40 && (spuIrq2[1] == (0x2400+Adma7.Index) || spuIrq2[1] == (0x2600+Adma7.Index))){ - regArea[0x7C0] |= 0x8; - regArea[PS2_IRQINFO] |= 0x8; - irqCallbackSPU2(); - } - Adma7.Index +=1; - - if((Adma7.ADMAPos - (Adma7.Index + 255)) <= 0 && (Adma7.ADMAPos > Adma7.Index)) - { - if(ADMAS7Write()) - { - //if( Adma4.AmountLeft == 0 ) - if(Adma7.IRQ == 0 && (spuCtrl2[1] & 0x30)){ - Adma7.IRQ = 1; - irqCallbackDMA7(); - } - } - } - if(Adma7.Index == 512) { - Adma7.Index = 0; - } - - } - - SSumL[ns]+=MixREVERBLeft(ns,0); - SSumL[ns]+=MixREVERBLeft(ns,1); - - if((regArea[PS2_C0_MMIX] & 0x80)) SSumL[ns] += (DirectInputC0.Left*(int)regArea[PS2_C0_BVOLL])>>16; - if((regArea[PS2_C1_MMIX] & 0x80)) SSumL[ns] += (DirectInputC1.Left*(int)regArea[PS2_C1_BVOLL])>>16; - - UpdateMainVolL(); - - if(iVolume == 5) d = 0; - else d=SSumL[ns]/iVolume; - SSumL[ns]=0; - *pS++=(d<-32767) ? -32767 : ((d>32767) ? 32767 : d); - - SSumR[ns]+=MixREVERBRight(0); - SSumR[ns]+=MixREVERBRight(1); - - if((regArea[PS2_C0_MMIX] & 0x40)) SSumR[ns] += (DirectInputC0.Right*(int)regArea[PS2_C0_BVOLR])>>16; - if((regArea[PS2_C1_MMIX] & 0x40)) SSumR[ns] += (DirectInputC1.Right*(int)regArea[PS2_C1_BVOLR])>>16; - - UpdateMainVolR(); - if(iVolume == 5) d = 0; - else d=SSumR[ns]/iVolume; - SSumR[ns]=0; - *pS++=(d<-32767) ? -32767 : ((d>32767) ? 32767 : d); - } - InitREVERB(); - - ////////////////////////////////////////////////////// - // feed the sound - // wanna have around 1/60 sec (16.666 ms) updates - - if(iCycle++>16) - { - SoundFeedVoiceData((unsigned char*)pSpuBuffer, - ((unsigned char *)pS)- - ((unsigned char *)pSpuBuffer)); - pS=(short *)pSpuBuffer; - iCycle=0; - } -} - // end of big main loop... - bThreadEnded=1; - return; -} - - -//////////////////////////////////////////////////////////////////////// -// SPU ASYNC... even newer epsxe func -// 1 time every 'cycle' cycles... harhar -//////////////////////////////////////////////////////////////////////// -EXPORT_GCC void CALLBACK SPU2async(unsigned long cycle) -{ - SPUCycles += cycle; - if(interrupt & (1<<2)){ - if(SPUCycles - SPUStartCycle[1] >= SPUTargetCycle[1]){ - interrupt &= ~(1<<2); - irqCallbackDMA7(); - } - - } - - if(interrupt & (1<<1)){ - if(SPUCycles - SPUStartCycle[0] >= SPUTargetCycle[0]){ - interrupt &= ~(1<<1); - irqCallbackDMA4(); - } - } - - /*if(iSpuAsyncWait) - { - iSpuAsyncWait++; - if(iSpuAsyncWait<=64) return; - iSpuAsyncWait=0; - }*/ - - if(iDebugMode==2) - { -#ifdef _WIN32 - if(IsWindow(hWDebug)) DestroyWindow(hWDebug); - hWDebug=0; -#endif - iDebugMode=0; - } - if(iRecordMode==2) - { -#ifdef _WIN32 - if(IsWindow(hWRecord)) DestroyWindow(hWRecord); - hWRecord=0; -#endif - iRecordMode=0; - } - - if(iUseTimer==0) // does the emu support SPUAsync, is it in thread mode, and in Thread Sync ON? - { - aSyncMode=1; // Ten, activate main function Sync system flag - - aSyncTimerOld = aSyncTimerNew; // Recalculate, AsyncWait (ms) - aSyncTimerNew = timeGetTime(); - - aSyncWait =(unsigned int)((aSyncTimerNew - aSyncTimerOld)/2); - - aSyncCounter += cycle ; - - return; - } - if(iUseTimer==2) // special mode, only used in Linux by this spu (or if you enable the experimental Windows mode) - { - //printf("spu2async: calling mainspu2proc\n"); - aSyncMode = 0; - if(!bSpuInit) return; // -> no init, no call - MainSPU2Proc(0,0,0,0,0); // -> experimental win mode... not really tested... don't like the drawbacks - } -} - - -//////////////////////////////////////////////////////////////////////// -// INIT/EXIT STUFF -//////////////////////////////////////////////////////////////////////// - -//////////////////////////////////////////////////////////////////////// -// SPUINIT: this func will be called first by the main emu -//////////////////////////////////////////////////////////////////////// - -#ifdef _WIN32 -static HINSTANCE hIRE = NULL; -#endif - -EXPORT_GCC long CALLBACK SPU2init(void) -{ - spuMemC=(unsigned char *)spuMem; // just small setup - memset((void *)s_chan,0,MAXCHAN*sizeof(SPUCHAN)); - memset(rvb,0,2*sizeof(REVERBInfo)); - - InitADSR(); - -#ifdef _WIN32 - if(hIRE==NULL) hIRE=LoadLibrary("Riched32.dll "); // needed for debug output -#endif - - return 0; -} - -//////////////////////////////////////////////////////////////////////// -// SETUPTIMER: init of certain buffers and threads/timers -//////////////////////////////////////////////////////////////////////// -#ifdef _WIN32 -DWORD WINAPI MAINThreadEx(LPVOID lpParameter) -{ - MainSPU2Proc(0,0,0,0,0); - return 0; -} -#else -void* MAINThreadEx(void* param) -{ - MainSPU2Proc(0,0,0,0,0); - return NULL; -} -#endif - -void SetupTimer(void) -{ - memset(SSumR,0,NSSIZE*sizeof(int)); // init some mixing buffers - memset(SSumL,0,NSSIZE*sizeof(int)); - pS=(short *)pSpuBuffer; // setup soundbuffer pointer - pS1=(short *)pSpuStreamBuffer[0]; // setup soundbuffer pointer - - bEndThread=0; // init thread vars - bSpuInit=1; // flag: we are inited - - bSpuInit=1; // flag: we are inited - -#ifdef _WIN32 - - if(iUseTimer==0) // windows: use thread - { - //_beginthread(MAINThread,0,NULL); - DWORD dw; - hMainThread=CreateThread(NULL,0,MAINThreadEx,0,0,&dw); - SetThreadPriority(hMainThread, - //THREAD_PRIORITY_TIME_CRITICAL); - THREAD_PRIORITY_HIGHEST); - } -#else - if( pthread_create(&hMainThread, NULL, MAINThreadEx, NULL) != 0 ) { - printf("Failed to create spu2thread\n"); - return; - } -#endif -} - -//////////////////////////////////////////////////////////////////////// -// REMOVETIMER: kill threads/timers -//////////////////////////////////////////////////////////////////////// - -void RemoveTimer(void) -{ - bEndThread=1; // raise flag to end thread - - if(iUseTimer!=2) // windows thread? - { - while(!bThreadEnded) {Sleep(5L);} // -> wait till thread has ended - Sleep(5L); - } - - bSpuInit=0; - - bThreadEnded=0; // no more spu is running -} - -//////////////////////////////////////////////////////////////////////// -// SETUPSTREAMS: init most of the spu buffers -//////////////////////////////////////////////////////////////////////// - -void SetupStreams(void) -{ - int i; - - pSpuBuffer=(unsigned char *)malloc(38400); // alloc mixing buffer - i=NSSIZE*2; - - sRVBStart[0] = (int *)malloc(i*4); // alloc reverb buffer - memset(sRVBStart[0],0,i*4); - sRVBEnd[0] = sRVBStart[0] + i; - sRVBPlay[0] = sRVBStart[0]; - sRVBStart[1] = (int *)malloc(i*4); // alloc reverb buffer - memset(sRVBStart[1],0,i*4); - sRVBEnd[1] = sRVBStart[1] + i; - sRVBPlay[1] = sRVBStart[1]; - - for(i=0;i init sustain - s_chan[i].iMute=0; - s_chan[i].iIrqDone=0; - s_chan[i].pLoop=spuMemC+(s_chan[i].iStartAdr<<1); - s_chan[i].pStart=spuMemC+(s_chan[i].iStartAdr<<1); - s_chan[i].pCurr=spuMemC+(s_chan[i].iStartAdr<<1); - } -} - -//////////////////////////////////////////////////////////////////////// -// REMOVESTREAMS: free most buffer -//////////////////////////////////////////////////////////////////////// - -void RemoveStreams(void) -{ - free(pSpuBuffer); // free mixing buffer - pSpuBuffer=NULL; - free(sRVBStart[0]); // free reverb buffer - sRVBStart[0]=0; - free(sRVBStart[1]); // free reverb buffer - sRVBStart[1]=0; -} - - -//////////////////////////////////////////////////////////////////////// -// SPUOPEN: called by main emu after init -//////////////////////////////////////////////////////////////////////// -#include -FILE * LogFile; -EXPORT_GCC long CALLBACK SPU2open(void* pWindow) -{ -#ifdef _WIN32 - HWND hW= pWindow == NULL ? NULL : *(HWND*)pWindow; -#else - Display* dsp = *(Display**)pWindow; -#endif - - if(bSPUIsOpen) return 0; // security for some stupid main emus - LogFile = fopen("logs/spu2.txt","wb"); - iVolume=3; - bEndThread=0; - bThreadEnded=0; - spuMemC=(unsigned char *)spuMem; - memset((void *)s_chan,0,(MAXCHAN+1)*sizeof(SPUCHAN)); - pSpuIrq[0]=spuMemC; - pSpuIrq[1]=spuMemC; - dwNewChannel2[0]=0; - dwNewChannel2[1]=0; - dwEndChannel2[0]=0; - dwEndChannel2[1]=0; - spuCtrl2[0]=0; - spuCtrl2[1]=0; - spuStat2[0]=0; - spuStat2[1]=0; - spuIrq2[0]=0; - spuIrq2[1]=0; - spuAddr2[0]=0x0; - spuAddr2[1]=0x0; - spuRvbAddr2[0]=0; - spuRvbAddr2[1]=0; - spuRvbAEnd2[0]=0; - spuRvbAEnd2[1]=0; - - memset(&Adma4,0,sizeof(ADMA)); - memset(&Adma7,0,sizeof(ADMA)); - - memset(&DirectInputC0,0,sizeof(DINPUT)); - memset(&DirectInputC1,0,sizeof(DINPUT)); - - -#ifdef _WIN32 - LastWrite=0x00000000;LastPlay=0; // init some play vars - - if(!IsWindow(hW)) hW=GetActiveWindow(); - hWMain = hW; // store hwnd - - ReadConfig(); // read user stuff -#endif - - - SetupSound(); // setup midas (before init!) - - SetupStreams(); // prepare streaming - - SetupTimer(); // timer for feeding data - - bSPUIsOpen=1; - -#ifdef _WIN32 - if(iDebugMode) // windows debug dialog - { - hWDebug=CreateDialog(hInst,MAKEINTRESOURCE(IDD_DEBUG), - NULL,(DLGPROC)DebugDlgProc); - SetWindowPos(hWDebug,HWND_TOPMOST,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE|SWP_SHOWWINDOW|SWP_NOACTIVATE); - UpdateWindow(hWDebug); - SetFocus(hWMain); - } - - if(iRecordMode) // windows recording dialog - { - hWRecord=CreateDialog(hInst,MAKEINTRESOURCE(IDD_RECORD), - NULL,(DLGPROC)RecordDlgProc); - SetWindowPos(hWRecord,HWND_TOPMOST,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE|SWP_SHOWWINDOW|SWP_NOACTIVATE); - UpdateWindow(hWRecord); - SetFocus(hWMain); - } -#endif - - return 0; -} - - -//////////////////////////////////////////////////////////////////////// -// SPUCLOSE: called before shutdown -//////////////////////////////////////////////////////////////////////// - -EXPORT_GCC void CALLBACK SPU2close(void) -{ - if(!bSPUIsOpen) return; // some security - - bSPUIsOpen=0; // no more open - //fclose(LogFile); -#ifdef _WIN32 - if(IsWindow(hWDebug)) DestroyWindow(hWDebug); - hWDebug=0; - if(IsWindow(hWRecord)) DestroyWindow(hWRecord); - hWRecord=0; -#endif - - RemoveTimer(); // no more feeding - - RemoveSound(); // no more sound handling - - RemoveStreams(); // no more streaming -} - -//////////////////////////////////////////////////////////////////////// -// SPUSHUTDOWN: called by main emu on final exit -//////////////////////////////////////////////////////////////////////// - -EXPORT_GCC void CALLBACK SPU2shutdown(void) -{ -#ifdef _WIN32 - if(hIRE!=NULL) {FreeLibrary(hIRE);hIRE=NULL;} -#endif - - return; -} - -//////////////////////////////////////////////////////////////////////// -// SPUTEST: we don't test, we are always fine ;) -//////////////////////////////////////////////////////////////////////// - -EXPORT_GCC long CALLBACK SPU2test(void) -{ - return 0; -} - -//////////////////////////////////////////////////////////////////////// -// SPUCONFIGURE: call config dialog -//////////////////////////////////////////////////////////////////////// - -EXPORT_GCC void CALLBACK SPU2configure(void) -{ -#ifdef _WIN32 - DialogBox(hInst,MAKEINTRESOURCE(IDD_CFGDLG), - GetActiveWindow(),(DLGPROC)DSoundDlgProc); -#else - //StartCfgTool("CFG"); -#endif -} - -//////////////////////////////////////////////////////////////////////// -// SPUABOUT: show about window -//////////////////////////////////////////////////////////////////////// - -EXPORT_GCC void CALLBACK SPU2about(void) -{ -#ifdef _WIN32 - DialogBox(hInst,MAKEINTRESOURCE(IDD_ABOUT), - GetActiveWindow(),(DLGPROC)AboutDlgProc); -#else - //StartCfgTool("ABOUT"); -#endif -} - -//////////////////////////////////////////////////////////////////////// -// SETUP CALLBACKS -// this functions will be called once, -// passes a callback that should be called on SPU-IRQ/cdda volume change -//////////////////////////////////////////////////////////////////////// - -EXPORT_GCC void CALLBACK SPU2irqCallback(void (CALLBACK *SPU2callback)(int), - void (CALLBACK *DMA4callback)(int), - void (CALLBACK *DMA7callback)(int)) -{ - irqCallbackSPU2 = SPU2callback; - irqCallbackDMA4 = DMA4callback; - irqCallbackDMA7 = DMA7callback; -} - -//////////////////////////////////////////////////////////////////////// -// COMMON PLUGIN INFO FUNCS -//////////////////////////////////////////////////////////////////////// - -EXPORT_GCC char * CALLBACK PS2EgetLibName(void) -{ - return libraryName; -} - -#define PS2E_LT_SPU2 0x4 - -EXPORT_GCC unsigned long CALLBACK PS2EgetLibType(void) -{ - return PS2E_LT_SPU2; -} - -EXPORT_GCC unsigned long CALLBACK PS2EgetLibVersion2(unsigned long type) -{ - unsigned char v=version; - - // key hack to fake a lower version: - //if(GetAsyncKeyState(VK_SHIFT)&0x8000) v--; - - // compile hack to set lib version to PCSX2 0.6 standards - //v=2; - - return v<<16|revision<<8|build; -} - -//////////////////////////////////////////////////////////////////////// - +/*************************************************************************** + spu.c - description + ------------------- + begin : Wed May 15 2002 + copyright : (C) 2002 by Pete Bernert + email : BlackDove@addcom.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. See also the license.txt file for * + * additional informations. * + * * + ***************************************************************************/ + +//*************************************************************************// +// History of changes: +// +// 2005/08/29 - Pete +// - changed to 48Khz output +// +// 2004/12/25 - Pete +// - inc'd version for pcsx2-0.7 +// +// 2004/04/18 - Pete +// - changed all kind of things in the plugin +// +// 2004/04/04 - Pete +// - changed plugin to emulate PS2 spu +// +// 2003/04/07 - Eric +// - adjusted cubic interpolation algorithm +// +// 2003/03/16 - Eric +// - added cubic interpolation +// +// 2003/03/01 - linuzappz +// - libraryName changes using ALSA +// +// 2003/02/28 - Pete +// - added option for type of interpolation +// - adjusted spu irqs again (Thousant Arms, Valkyrie Profile) +// - added MONO support for MSWindows DirectSound +// +// 2003/02/20 - kode54 +// - amended interpolation code, goto GOON could skip initialization of gpos and cause segfault +// +// 2003/02/19 - kode54 +// - moved SPU IRQ handler and changed sample flag processing +// +// 2003/02/18 - kode54 +// - moved ADSR calculation outside of the sample decode loop, somehow I doubt that +// ADSR timing is relative to the frequency at which a sample is played... I guess +// this remains to be seen, and I don't know whether ADSR is applied to noise channels... +// +// 2003/02/09 - kode54 +// - one-shot samples now process the end block before stopping +// - in light of removing fmod hack, now processing ADSR on frequency channel as well +// +// 2003/02/08 - kode54 +// - replaced easy interpolation with gaussian +// - removed fmod averaging hack +// - changed .sinc to be updated from .iRawPitch, no idea why it wasn't done this way already (<- Pete: because I sometimes fail to see the obvious, haharhar :) +// +// 2003/02/08 - linuzappz +// - small bugfix for one usleep that was 1 instead of 1000 +// - added iDisStereo for no stereo (Linux) +// +// 2003/01/22 - Pete +// - added easy interpolation & small noise adjustments +// +// 2003/01/19 - Pete +// - added Neill's reverb +// +// 2003/01/12 - Pete +// - added recording window handlers +// +// 2003/01/06 - Pete +// - added Neill's ADSR timings +// +// 2002/12/28 - Pete +// - adjusted spu irq handling, fmod handling and loop handling +// +// 2002/08/14 - Pete +// - added extra reverb +// +// 2002/06/08 - linuzappz +// - SPUupdate changed for SPUasync +// +// 2002/05/15 - Pete +// - generic cleanup for the Peops release +// +//*************************************************************************// + +#include "stdafx.h" + +#define _IN_SPU + +#include "externals.h" +#include "cfg.h" +#include "dsoundoss.h" +#include "regs.h" +#include "debug.h" +#include "record.h" +#include "resource.h" +#include "dma.h" +#include "registers.h" + +#if !defined(_WIN32) + +#include + +#if !defined(CALLBACK) +#define CALLBACK +#endif + +#define Sleep(x) sleep((x)*1000) +#endif + +//////////////////////////////////////////////////////////////////////// +// spu version infos/name +//////////////////////////////////////////////////////////////////////// + +const unsigned char version = 5; +const unsigned char revision = 1; +const unsigned char build = 9; +#ifdef _DEBUG +static char * libraryName = "P.E.Op.S. SPU2 (Debug) "; +#else +static char * libraryName = "P.E.Op.S. SPU2 "; +#endif +static char * libraryInfo = "P.E.Op.S. SPU2 Driver V1.6\nCoded by Pete Bernert, Saqib and the P.E.Op.S. team\n"; + +//////////////////////////////////////////////////////////////////////// +// globals +//////////////////////////////////////////////////////////////////////// + +// psx buffer / addresses + +unsigned short regArea[32*1024]; + short spuMem[2*1024*1024]; + char * spuMemC; +unsigned char * pSpuIrq[2]; +unsigned char * pSpuBuffer; +unsigned char * pSpuStreamBuffer[2]; + +// user settings + +int iVolume=3; +int iDebugMode=0; +int iRecordMode=0; +int iUseReverb=0; +int iUseInterpolation=2; + +// MAIN infos struct for each channel +int iDisStereo; + +SPUCHAN s_chan[MAXCHAN+1]; // channel + 1 infos (1 is security for fmod handling) +REVERBInfo rvb[2]; + +unsigned long dwNoiseVal=1; // global noise generator +unsigned short spuCtrl2[2]; // some vars to store psx reg infos +unsigned short spuStat2[2]; +unsigned long spuIrq2[2]; +unsigned long spuAddr2[2]; // address into spu mem +unsigned long spuRvbAddr2[2]; +unsigned long spuRvbAEnd2[2]; +int bEndThread=0; // thread handlers +int bSpuInit=0; +int bSPUIsOpen=0; +int bThreadEnded=0; +int iUseTimer=2; +int aSyncMode=0; +unsigned long aSyncCounter=0; +unsigned long aSyncWait=0; +DWORD aSyncTimerNew; +DWORD aSyncTimerOld; + +#ifdef _WIN32 +HWND hWMain=0; // window handle +HWND hWDebug=0; +HWND hWRecord=0; + +static HANDLE hMainThread; +#else +#include +static pthread_t hMainThread; +#endif + +unsigned long dwNewChannel2[2]; // flags for faster testing, if new channel starts +unsigned long dwEndChannel2[2]; + +void (CALLBACK *irqCallbackDMA4)()=0; // func of main emu, called on spu irq +void (CALLBACK *irqCallbackDMA7)()=0; // func of main emu, called on spu irq +void (CALLBACK *irqCallbackSPU2)()=0; // func of main emu, called on spu irq + +// certain globals (were local before, but with the new timeproc I need em global) + +const int f[5][2] = { { 0, 0 }, + { 60, 0 }, + { 115, -52 }, + { 98, -55 }, + { 122, -60 } }; +int SSumR[NSSIZE]; +int SSumL[NSSIZE]; + +extern ADMA Adma4; +extern ADMA Adma7; +DINPUT DirectInputC0, DirectInputC1; + +extern unsigned short interrupt; +unsigned long SPUCycles; +extern unsigned long SPUStartCycle[2]; +extern unsigned long SPUTargetCycle[2]; + +int iCycle=0; +short * pS; +short * pS1; + +static int lastch=-1; // last channel processed on spu irq in timer mode +static int lastns=0; // last ns pos +static int iSecureStart=0; // secure start counter + +//////////////////////////////////////////////////////////////////////// +// CODE AREA +//////////////////////////////////////////////////////////////////////// + +// dirty inline func includes + +#include "reverb.c" +#include "adsr.c" + +//////////////////////////////////////////////////////////////////////// +// helpers for simple interpolation + +// +// easy interpolation on upsampling, no special filter, just "Pete's common sense" tm +// +// instead of having n equal sample values in a row like: +// ____ +// |____ +// +// we compare the current delta change with the next delta change. +// +// if curr_delta is positive, +// +// - and next delta is smaller (or changing direction): +// \. +// -__ +// +// - and next delta significant (at least twice) bigger: +// --_ +// \. +// +// - and next delta is nearly same: +// \. +// \. +// +// +// if curr_delta is negative, +// +// - and next delta is smaller (or changing direction): +// _-- +// / +// +// - and next delta significant (at least twice) bigger: +// / +// __- +// +// - and next delta is nearly same: +// / +// / +// + + +INLINE void InterpolateUp(int ch) +{ + if(s_chan[ch].SB[32]==1) // flag == 1? calc step and set flag... and don't change the value in this pass + { + const int id1=s_chan[ch].SB[30]-s_chan[ch].SB[29]; // curr delta to next val + const int id2=s_chan[ch].SB[31]-s_chan[ch].SB[30]; // and next delta to next-next val :) + + s_chan[ch].SB[32]=0; + + if(id1>0) // curr delta positive + { + if(id2id1) + {s_chan[ch].SB[28]=id1;s_chan[ch].SB[32]=2;} + else + if(id2>(id1<<1)) + s_chan[ch].SB[28]=(id1*s_chan[ch].sinc)/0x10000L; + else + s_chan[ch].SB[28]=(id1*s_chan[ch].sinc)/0x20000L; + } + } + else + if(s_chan[ch].SB[32]==2) // flag 1: calc step and set flag... and don't change the value in this pass + { + s_chan[ch].SB[32]=0; + + s_chan[ch].SB[28]=(s_chan[ch].SB[28]*s_chan[ch].sinc)/0x20000L; + if(s_chan[ch].sinc<=0x8000) + s_chan[ch].SB[29]=s_chan[ch].SB[30]-(s_chan[ch].SB[28]*((0x10000/s_chan[ch].sinc)-1)); + else s_chan[ch].SB[29]+=s_chan[ch].SB[28]; + } + else // no flags? add bigger val (if possible), calc smaller step, set flag1 + s_chan[ch].SB[29]+=s_chan[ch].SB[28]; +} + +// +// even easier interpolation on downsampling, also no special filter, again just "Pete's common sense" tm +// + +INLINE void InterpolateDown(int ch) +{ + if(s_chan[ch].sinc>=0x20000) // we would skip at least one val? + { + s_chan[ch].SB[29]+=(s_chan[ch].SB[30]-s_chan[ch].SB[29])/2; // add easy weight + if(s_chan[ch].sinc>=0x30000) // we would skip even more vals? + s_chan[ch].SB[29]+=(s_chan[ch].SB[31]-s_chan[ch].SB[30])/2;// add additional next weight + } +} + +//////////////////////////////////////////////////////////////////////// +// helpers for gauss interpolation + +#define gval0 (((short*)(&s_chan[ch].SB[29]))[gpos]) +#define gval(x) (((short*)(&s_chan[ch].SB[29]))[(gpos+x)&3]) + +#include "gauss_i.h" + +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// +// START SOUND... called by main thread to setup a new sound on a channel +//////////////////////////////////////////////////////////////////////// + +INLINE void StartSound(int ch) +{ + dwNewChannel2[ch/24]&=~(1<<(ch%24)); // clear new channel bit + dwEndChannel2[ch/24]&=~(1<<(ch%24)); // clear end channel bit + + StartADSR(ch); + StartREVERB(ch); + + s_chan[ch].pCurr=s_chan[ch].pStart; // set sample start + + s_chan[ch].s_1=0; // init mixing vars + s_chan[ch].s_2=0; + s_chan[ch].iSBPos=28; + + s_chan[ch].bNew=0; // init channel flags + s_chan[ch].bStop=0; + s_chan[ch].bOn=1; + + s_chan[ch].SB[29]=0; // init our interpolation helpers + s_chan[ch].SB[30]=0; + + if(iUseInterpolation>=2) // gauss interpolation? + {s_chan[ch].spos=0x30000L;s_chan[ch].SB[28]=0;} // -> start with more decoding + else {s_chan[ch].spos=0x10000L;s_chan[ch].SB[31]=0;} // -> no/simple interpolation starts with one 44100 decoding +} + + +void UpdateMainVolL() // LEFT VOLUME +{ + short vol = regArea[PS2_C0_MVOLL]; + if(vol&0x8000) // sweep? + { + short sInc=1; // -> sweep up? + if(vol&0x2000) sInc=-1; // -> or down? + if(vol&0x1000) vol^=0xffff; // -> mmm... phase inverted? have to investigate this + vol=((vol&0x7f)+1)/2; // -> sweep: 0..127 -> 0..64 + vol+=vol/(2*sInc); // -> HACK: we don't sweep right now, so we just raise/lower the volume by the half! + vol*=128; + } + else // no sweep: + { + if(vol&0x4000) // -> mmm... phase inverted? have to investigate this + //vol^=0xffff; + vol=0x3fff-(vol&0x3fff); + } + + vol&=0x3fff; + regArea[PS2_C0_MVOLL]=vol; // store volume +} + +//////////////////////////////////////////////////////////////////////// +// RIGHT VOLUME register write +//////////////////////////////////////////////////////////////////////// + +void UpdateMainVolR() // RIGHT VOLUME +{ + short vol = regArea[PS2_C0_MVOLR]; + if(vol&0x8000) // comments... see above :) + { + short sInc=1; + if(vol&0x2000) sInc=-1; + if(vol&0x1000) vol^=0xffff; + vol=((vol&0x7f)+1)/2; + vol+=vol/(2*sInc); + vol*=128; + } + else + { + if(vol&0x4000) //vol=vol^=0xffff; + vol=0x3fff-(vol&0x3fff); + } + + vol&=0x3fff; + + regArea[PS2_C0_MVOLR]=vol; +} +//////////////////////////////////////////////////////////////////////// +// MAIN SPU FUNCTION +// here is the main job handler... thread, timer or direct func call +// basically the whole sound processing is done in this fat func! +//////////////////////////////////////////////////////////////////////// + +// 5 ms waiting phase, if buffer is full and no new sound has to get started +// .. can be made smaller (smallest val: 1 ms), but bigger waits give +// better performance + +#define PAUSE_W 5 +#define PAUSE_L 5000 +extern unsigned long MemAddr[2]; +//////////////////////////////////////////////////////////////////////// + +int iSpuAsyncWait=0; +extern int MMIXC0, MMIXC1; +extern int ADMAS4Write(); +extern int ADMAS7Write(); + +extern void SoundFeedVoiceData(); + +void CALLBACK MainSPU2Proc(unsigned int nTimerId, unsigned int msg, unsigned int dwUser, + unsigned int dwParam1, unsigned int dwParam2) +{ + int s_1,s_2,fa,ns; + int core = 0; + unsigned char * start; + unsigned int nSample; + int d; + int ch,predict_nr,shift_factor,flags,s; + int gpos; + + while(!bEndThread) // until we are shutting down + { + //--------------------------------------------------// + // ok, at the beginning we are looking if there is + // enuff free place in the dsound/oss buffer to + // fill in new data, or if there is a new channel to start. + // if not, we wait (thread) or return (timer/spuasync) + // until enuff free place is available/a new channel gets + // started + if (aSyncMode==1) // Async supported? and enabled? + { + if (aSyncCounter<=737280) // If we have 10ms in the buffer, don't wait + { + if (aSyncWait<1000) Sleep(aSyncWait); // Wait a little to be more Synced (No more than 1 sec) + else Sleep (1000); + } + + while (aSyncCounter<=368640 && !bEndThread && aSyncMode==1) + Sleep (1); // bEndThread/aSyncMode are needed, to avoid close problems + + aSyncCounter -= 36864; // 1ms more done (48Hz*768cycles/Hz) + } + else + if(dwNewChannel2[0] || dwNewChannel2[1]) // new channel should start immedately? + { // (at least one bit 0 ... MAXCHANNEL is set?) + iSecureStart++; // -> set iSecure + if(iSecureStart>5) iSecureStart=0; // (if it is set 5 times - that means on 5 tries a new samples has been started - in a row, we will reset it, to give the sound update a chance) + } + else iSecureStart=0; // 0: no new channel should start + + while(!iSecureStart && !bEndThread && // no new start? no thread end? + (SoundGetBytesBuffered()>TESTSIZE)) // and still enuff data in sound buffer? + { + iSecureStart=0; // reset secure + + if(iUseTimer) // no-thread mode? + { + return; // -> and done this time (timer mode 1 or 2) + } + // win thread mode: + Sleep(PAUSE_W); // sleep for x ms (win) + + if(dwNewChannel2[0] || dwNewChannel2[1]) + iSecureStart=1; // if a new channel kicks in (or, of course, sound buffer runs low), we will leave the loop + } + //--------------------------------------------------// continue from irq handling in timer mode? + + if(lastch>=0) // will be -1 if no continue is pending + { + ch=lastch; ns=lastns; lastch=-1; // -> setup all kind of vars to continue + if( s_chan[ch].iSBPos < 28 ) { + goto GOON; // -> directly jump to the continue point + } + } + + //--------------------------------------------------// + //- main channel loop -// + //--------------------------------------------------// + { + for(ch=0;ch take it and calc steps + s_chan[ch].sinc = s_chan[ch].iRawPitch<<4; + if(!s_chan[ch].sinc) s_chan[ch].sinc=1; + if(iUseInterpolation==1) s_chan[ch].SB[32]=1; // -> freq change in simle imterpolation mode: set flag + } + + ns=0; + while(ns=0x10000L) + { + if(s_chan[ch].iSBPos==28) // 28 reached? + { + start=s_chan[ch].pCurr; // set up the current pos + + if (s_chan[ch].bOn==0) goto ENDX; // special "stop" sign + + s_chan[ch].iSBPos=0; + + s_1=s_chan[ch].s_1; + s_2=s_chan[ch].s_2; + + predict_nr = (int)*start; + start++; + flags = (int)*start; + start++; + + shift_factor = predict_nr&0xf; + predict_nr >>= 4; + // -------------------------------------- // + + for (nSample=0;nSample<28;nSample+=2,start++) + { + d=(int)*start; + s=((d&0xf)<<12); + if(s&0x8000) s|=0xffff0000; + + fa=(s >> shift_factor); + fa=fa + ((s_1 * f[predict_nr][0])>>6) + ((s_2 * f[predict_nr][1])>>6); + s_2=s_1; + s_1=fa; + s=((d & 0xf0) << 8); + + s_chan[ch].SB[nSample]=fa; + + if(s&0x8000) s|=0xffff0000; + fa=(s>>shift_factor); + fa=fa + ((s_1 * f[predict_nr][0])>>6) + ((s_2 * f[predict_nr][1])>>6); + s_2=s_1; + s_1=fa; + + s_chan[ch].SB[nSample+1]=fa; + } + + //////////////////////////////////////////// irq check + + if(spuCtrl2[core]&0x40) // some irq active? + { + if(iDebugMode==1) logprintf("IRQ Active ch %x, C%x\r\n", ch, ch/24); + + if( pSpuIrq[core] >= start-16 && // irq address reached? + pSpuIrq[core] <= start) + { + s_chan[ch].iIrqDone=1; // -> debug flag + + if(iDebugMode==1) logprintf("Sample End ch %x, C%x\r\n", ch, ch/24); + + regArea[0x7C0] |= 0x4< let's see what is happening if we call our irqs instead ;) + + } + } + + //////////////////////////////////////////// flag handler + if(flags & 0x2) s_chan[ch].bIgnoreLoop=1; // LOOP bit + + if(flags&0x4) s_chan[ch].pLoop=start-16; // LOOP/START bit + + if(flags&0x1) // 1: LOOP/END bit + { + dwEndChannel2[core]|=(1<<(ch%24)); + + if((flags&0xF) != 0x3 || s_chan[ch].pLoop == NULL) // Check if no loop is present + { + s_chan[ch].bIgnoreLoop=0; + s_chan[ch].bStop = 1; + logprintf("Stopping\r\n"); + } + else start = s_chan[ch].pLoop; + } + + s_chan[ch].pCurr=start; // store values for next cycle + s_chan[ch].s_1=s_1; + s_chan[ch].s_2=s_2; + + + //////////////////////////////////////////// + +GOON: ; + } + + fa=s_chan[ch].SB[s_chan[ch].iSBPos++]; // get sample data + + if((spuCtrl2[core]&0x4000)==0) fa=0; // muted? + else // else adjust + { + if(fa>32767L) fa=32767L; + if(fa<-32767L) fa=-32767L; + } + + if(iUseInterpolation>=2) // gauss/cubic interpolation + { + gpos = s_chan[ch].SB[28]; + gval0 = fa; + gpos = (gpos+1) & 3; + s_chan[ch].SB[28] = gpos; + } + else + if(iUseInterpolation==1) // simple interpolation + { + s_chan[ch].SB[28] = 0; + s_chan[ch].SB[29] = s_chan[ch].SB[30]; // -> helpers for simple linear interpolation: delay real val for two slots, and calc the two deltas, for a 'look at the future behaviour' + s_chan[ch].SB[30] = s_chan[ch].SB[31]; + s_chan[ch].SB[31] = fa; + s_chan[ch].SB[32] = 1; // -> flag: calc new interolation + } + else s_chan[ch].SB[29]=fa; // no interpolation + + s_chan[ch].spos -= 0x10000L; + } + + //////////////////////////////////////////////// + // noise handler... just produces some noise data + // surely wrong... and no noise frequency (spuCtrl&0x3f00) will be used... + // and sometimes the noise will be used as fmod modulation... pfff + + if(s_chan[ch].bNoise) + { + if((dwNoiseVal<<=1)&0x80000000L) + { + dwNoiseVal^=0x0040001L; + fa=((dwNoiseVal>>2)&0x7fff); + fa=-fa; + } + else fa=(dwNoiseVal>>2)&0x7fff; + + // mmm... depending on the noise freq we allow bigger/smaller changes to the previous val + fa=s_chan[ch].iOldNoise+((fa-s_chan[ch].iOldNoise)/((0x001f-((spuCtrl2[core]&0x3f00)>>9))+1)); + if(fa>32767L) fa=32767L; + if(fa<-32767L) fa=-32767L; + s_chan[ch].iOldNoise=fa; + + if(iUseInterpolation<2) // no gauss/cubic interpolation? + s_chan[ch].SB[29] = fa; // -> store noise val in "current sample" slot + } //---------------------------------------- + else // NO NOISE (NORMAL SAMPLE DATA) HERE + {//------------------------------------------// + if(iUseInterpolation==3) // cubic interpolation + { + long xd; + xd = ((s_chan[ch].spos) >> 1)+1; + gpos = s_chan[ch].SB[28]; + + fa = gval(3) - 3*gval(2) + 3*gval(1) - gval0; + fa *= (xd - (2<<15)) / 6; + fa >>= 15; + fa += gval(2) - gval(1) - gval(1) + gval0; + fa *= (xd - (1<<15)) >> 1; + fa >>= 15; + fa += gval(1) - gval0; + fa *= xd; + fa >>= 15; + fa = fa + gval0; + } + //------------------------------------------// + else + if(iUseInterpolation==2) // gauss interpolation + { + int vl, vr; + vl = (s_chan[ch].spos >> 6) & ~3; + gpos = s_chan[ch].SB[28]; + vr=(gauss[vl]*gval0)&~2047; + vr+=(gauss[vl+1]*gval(1))&~2047; + vr+=(gauss[vl+2]*gval(2))&~2047; + vr+=(gauss[vl+3]*gval(3))&~2047; + fa = vr>>11; + } + //------------------------------------------// + else + if(iUseInterpolation==1) // simple interpolation + { + if(s_chan[ch].sinc<0x10000L) // -> upsampling? + InterpolateUp(ch); // --> interpolate up + else InterpolateDown(ch); // --> else down + fa=s_chan[ch].SB[29]; + } + //------------------------------------------// + else fa=s_chan[ch].SB[29]; // no interpolation + } + + s_chan[ch].sval = (MixADSR(ch) * fa) / 1023; // add adsr + + if(s_chan[ch].bFMod==2) // fmod freq channel + { + int NP=((32768L+s_chan[ch].sval)*s_chan[ch+1].iRawPitch)>>14; + + if(NP>0x3fff) NP=0x3fff; + else if(NP<0x1) NP=0x1; + + NP=(48000L*NP)>>12; // calc frequency ( 48hz ) + + s_chan[ch+1].iActFreq=NP; + s_chan[ch+1].iUsedFreq=NP; + s_chan[ch+1].sinc=(((NP/10)<<16)/48000); // check , was 4800 + if(!s_chan[ch+1].sinc) s_chan[ch+1].sinc=1; + if(iUseInterpolation==1) // freq change in sipmle interpolation mode + s_chan[ch+1].SB[32]=1; + + } + else + { + ////////////////////////////////////////////// + // ok, left/right sound volume (ps2 volume goes from 0 ... 0x3fff) + + if(s_chan[ch].iMute) s_chan[ch].sval=0; // debug mute + else + { + if(s_chan[ch].bVolumeL) + SSumL[ns]+=(s_chan[ch].sval*s_chan[ch].iLeftVolume)>>14; + + if(s_chan[ch].bVolumeR) + SSumR[ns]+=(s_chan[ch].sval*s_chan[ch].iRightVolume)>>14; + } + + ////////////////////////////////////////////// + // now let us store sound data for reverb + + if(s_chan[ch].bRVBActive) StoreREVERB(ch,ns); + } + + //////////////////////////////////////////////// + // ok, go on until 1 ms data of this channel is collected + + ns++; + s_chan[ch].spos += s_chan[ch].sinc; + } +ENDX: ; + } + } + + //---------------------------------------------------// + //- here we have another 1 ms of sound data + //---------------------------------------------------// + /////////////////////////////////////////////////////// + // mix all channels (including reverb) into one buffer + for(ns=0;ns Adma4.Index)) + { + if(ADMAS4Write()) + { + //if( Adma4.AmountLeft == 0 ) + if(Adma4.IRQ == 0 && (spuCtrl2[0] & 0x30)){ + Adma4.IRQ = 1; + irqCallbackDMA4(); + } + } + } + if(Adma4.Index == 512) { + Adma4.Index = 0; + } + + + } + + if((regArea[PS2_C1_MMIX] & 0xF0) && regArea[PS2_C1_ADMAS] & 0x2 /*&& !(spuCtrl2[1] & 0x30)*/) + { + DirectInputC1.Left = ((short*)spuMem)[0x2400+Adma7.Index]; + DirectInputC1.Right = ((short*)spuMem)[0x2600+Adma7.Index]; + if(spuCtrl2[1]&0x40 && (spuIrq2[1] == (0x2400+Adma7.Index) || spuIrq2[1] == (0x2600+Adma7.Index))){ + regArea[0x7C0] |= 0x8; + regArea[PS2_IRQINFO] |= 0x8; + irqCallbackSPU2(); + } + Adma7.Index +=1; + + if((Adma7.ADMAPos - (Adma7.Index + 255)) <= 0 && (Adma7.ADMAPos > Adma7.Index)) + { + if(ADMAS7Write()) + { + //if( Adma4.AmountLeft == 0 ) + if(Adma7.IRQ == 0 && (spuCtrl2[1] & 0x30)){ + Adma7.IRQ = 1; + irqCallbackDMA7(); + } + } + } + if(Adma7.Index == 512) { + Adma7.Index = 0; + } + + } + + SSumL[ns]+=MixREVERBLeft(ns,0); + SSumL[ns]+=MixREVERBLeft(ns,1); + + if((regArea[PS2_C0_MMIX] & 0x80)) SSumL[ns] += (DirectInputC0.Left*(int)regArea[PS2_C0_BVOLL])>>16; + if((regArea[PS2_C1_MMIX] & 0x80)) SSumL[ns] += (DirectInputC1.Left*(int)regArea[PS2_C1_BVOLL])>>16; + + UpdateMainVolL(); + + if(iVolume == 5) d = 0; + else d=SSumL[ns]/iVolume; + SSumL[ns]=0; + *pS++=(d<-32767) ? -32767 : ((d>32767) ? 32767 : d); + + SSumR[ns]+=MixREVERBRight(0); + SSumR[ns]+=MixREVERBRight(1); + + if((regArea[PS2_C0_MMIX] & 0x40)) SSumR[ns] += (DirectInputC0.Right*(int)regArea[PS2_C0_BVOLR])>>16; + if((regArea[PS2_C1_MMIX] & 0x40)) SSumR[ns] += (DirectInputC1.Right*(int)regArea[PS2_C1_BVOLR])>>16; + + UpdateMainVolR(); + if(iVolume == 5) d = 0; + else d=SSumR[ns]/iVolume; + SSumR[ns]=0; + *pS++=(d<-32767) ? -32767 : ((d>32767) ? 32767 : d); + } + InitREVERB(); + + ////////////////////////////////////////////////////// + // feed the sound + // wanna have around 1/60 sec (16.666 ms) updates + + if(iCycle++>16) + { + SoundFeedVoiceData((unsigned char*)pSpuBuffer, + ((unsigned char *)pS)- + ((unsigned char *)pSpuBuffer)); + pS=(short *)pSpuBuffer; + iCycle=0; + } +} + // end of big main loop... + bThreadEnded=1; + return; +} + + +//////////////////////////////////////////////////////////////////////// +// SPU ASYNC... even newer epsxe func +// 1 time every 'cycle' cycles... harhar +//////////////////////////////////////////////////////////////////////// +EXPORT_GCC void CALLBACK SPU2async(unsigned long cycle) +{ + SPUCycles += cycle; + if(interrupt & (1<<2)){ + if(SPUCycles - SPUStartCycle[1] >= SPUTargetCycle[1]){ + interrupt &= ~(1<<2); + irqCallbackDMA7(); + } + + } + + if(interrupt & (1<<1)){ + if(SPUCycles - SPUStartCycle[0] >= SPUTargetCycle[0]){ + interrupt &= ~(1<<1); + irqCallbackDMA4(); + } + } + + /*if(iSpuAsyncWait) + { + iSpuAsyncWait++; + if(iSpuAsyncWait<=64) return; + iSpuAsyncWait=0; + }*/ + + if(iDebugMode==2) + { +#ifdef _WIN32 + if(IsWindow(hWDebug)) DestroyWindow(hWDebug); + hWDebug=0; +#endif + iDebugMode=0; + } + if(iRecordMode==2) + { +#ifdef _WIN32 + if(IsWindow(hWRecord)) DestroyWindow(hWRecord); + hWRecord=0; +#endif + iRecordMode=0; + } + + if(iUseTimer==0) // does the emu support SPUAsync, is it in thread mode, and in Thread Sync ON? + { + aSyncMode=1; // Ten, activate main function Sync system flag + + aSyncTimerOld = aSyncTimerNew; // Recalculate, AsyncWait (ms) + aSyncTimerNew = timeGetTime(); + + aSyncWait =(unsigned int)((aSyncTimerNew - aSyncTimerOld)/2); + + aSyncCounter += cycle ; + + return; + } + if(iUseTimer==2) // special mode, only used in Linux by this spu (or if you enable the experimental Windows mode) + { + //printf("spu2async: calling mainspu2proc\n"); + aSyncMode = 0; + if(!bSpuInit) return; // -> no init, no call + MainSPU2Proc(0,0,0,0,0); // -> experimental win mode... not really tested... don't like the drawbacks + } +} + + +//////////////////////////////////////////////////////////////////////// +// INIT/EXIT STUFF +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +// SPUINIT: this func will be called first by the main emu +//////////////////////////////////////////////////////////////////////// + +#ifdef _WIN32 +static HINSTANCE hIRE = NULL; +#endif + +EXPORT_GCC long CALLBACK SPU2init(void) +{ + spuMemC=(unsigned char *)spuMem; // just small setup + memset((void *)s_chan,0,MAXCHAN*sizeof(SPUCHAN)); + memset(rvb,0,2*sizeof(REVERBInfo)); + + InitADSR(); + +#ifdef _WIN32 + if(hIRE==NULL) hIRE=LoadLibrary("Riched32.dll "); // needed for debug output +#endif + + return 0; +} + +//////////////////////////////////////////////////////////////////////// +// SETUPTIMER: init of certain buffers and threads/timers +//////////////////////////////////////////////////////////////////////// +#ifdef _WIN32 +DWORD WINAPI MAINThreadEx(LPVOID lpParameter) +{ + MainSPU2Proc(0,0,0,0,0); + return 0; +} +#else +void* MAINThreadEx(void* param) +{ + MainSPU2Proc(0,0,0,0,0); + return NULL; +} +#endif + +void SetupTimer(void) +{ + memset(SSumR,0,NSSIZE*sizeof(int)); // init some mixing buffers + memset(SSumL,0,NSSIZE*sizeof(int)); + pS=(short *)pSpuBuffer; // setup soundbuffer pointer + pS1=(short *)pSpuStreamBuffer[0]; // setup soundbuffer pointer + + bEndThread=0; // init thread vars + bSpuInit=1; // flag: we are inited + + bSpuInit=1; // flag: we are inited + +#ifdef _WIN32 + + if(iUseTimer==0) // windows: use thread + { + //_beginthread(MAINThread,0,NULL); + DWORD dw; + hMainThread=CreateThread(NULL,0,MAINThreadEx,0,0,&dw); + SetThreadPriority(hMainThread, + //THREAD_PRIORITY_TIME_CRITICAL); + THREAD_PRIORITY_HIGHEST); + } +#else + if( pthread_create(&hMainThread, NULL, MAINThreadEx, NULL) != 0 ) { + printf("Failed to create spu2thread\n"); + return; + } +#endif +} + +//////////////////////////////////////////////////////////////////////// +// REMOVETIMER: kill threads/timers +//////////////////////////////////////////////////////////////////////// + +void RemoveTimer(void) +{ + bEndThread=1; // raise flag to end thread + + if(iUseTimer!=2) // windows thread? + { + while(!bThreadEnded) {Sleep(5L);} // -> wait till thread has ended + Sleep(5L); + } + + bSpuInit=0; + + bThreadEnded=0; // no more spu is running +} + +//////////////////////////////////////////////////////////////////////// +// SETUPSTREAMS: init most of the spu buffers +//////////////////////////////////////////////////////////////////////// + +void SetupStreams(void) +{ + int i; + + pSpuBuffer=(unsigned char *)malloc(38400); // alloc mixing buffer + i=NSSIZE*2; + + sRVBStart[0] = (int *)malloc(i*4); // alloc reverb buffer + memset(sRVBStart[0],0,i*4); + sRVBEnd[0] = sRVBStart[0] + i; + sRVBPlay[0] = sRVBStart[0]; + sRVBStart[1] = (int *)malloc(i*4); // alloc reverb buffer + memset(sRVBStart[1],0,i*4); + sRVBEnd[1] = sRVBStart[1] + i; + sRVBPlay[1] = sRVBStart[1]; + + for(i=0;i init sustain + s_chan[i].iMute=0; + s_chan[i].iIrqDone=0; + s_chan[i].pLoop=spuMemC+(s_chan[i].iStartAdr<<1); + s_chan[i].pStart=spuMemC+(s_chan[i].iStartAdr<<1); + s_chan[i].pCurr=spuMemC+(s_chan[i].iStartAdr<<1); + } +} + +//////////////////////////////////////////////////////////////////////// +// REMOVESTREAMS: free most buffer +//////////////////////////////////////////////////////////////////////// + +void RemoveStreams(void) +{ + free(pSpuBuffer); // free mixing buffer + pSpuBuffer=NULL; + free(sRVBStart[0]); // free reverb buffer + sRVBStart[0]=0; + free(sRVBStart[1]); // free reverb buffer + sRVBStart[1]=0; +} + + +//////////////////////////////////////////////////////////////////////// +// SPUOPEN: called by main emu after init +//////////////////////////////////////////////////////////////////////// +#include +FILE * LogFile; +EXPORT_GCC long CALLBACK SPU2open(void* pWindow) +{ +#ifdef _WIN32 + HWND hW= pWindow == NULL ? NULL : *(HWND*)pWindow; +#else + Display* dsp = *(Display**)pWindow; +#endif + + if(bSPUIsOpen) return 0; // security for some stupid main emus + LogFile = fopen("logs/spu2.txt","wb"); + iVolume=3; + bEndThread=0; + bThreadEnded=0; + spuMemC=(unsigned char *)spuMem; + memset((void *)s_chan,0,(MAXCHAN+1)*sizeof(SPUCHAN)); + pSpuIrq[0]=spuMemC; + pSpuIrq[1]=spuMemC; + dwNewChannel2[0]=0; + dwNewChannel2[1]=0; + dwEndChannel2[0]=0; + dwEndChannel2[1]=0; + spuCtrl2[0]=0; + spuCtrl2[1]=0; + spuStat2[0]=0; + spuStat2[1]=0; + spuIrq2[0]=0; + spuIrq2[1]=0; + spuAddr2[0]=0x0; + spuAddr2[1]=0x0; + spuRvbAddr2[0]=0; + spuRvbAddr2[1]=0; + spuRvbAEnd2[0]=0; + spuRvbAEnd2[1]=0; + + memset(&Adma4,0,sizeof(ADMA)); + memset(&Adma7,0,sizeof(ADMA)); + + memset(&DirectInputC0,0,sizeof(DINPUT)); + memset(&DirectInputC1,0,sizeof(DINPUT)); + + +#ifdef _WIN32 + LastWrite=0x00000000;LastPlay=0; // init some play vars + + if(!IsWindow(hW)) hW=GetActiveWindow(); + hWMain = hW; // store hwnd + + ReadConfig(); // read user stuff +#endif + + + SetupSound(); // setup midas (before init!) + + SetupStreams(); // prepare streaming + + SetupTimer(); // timer for feeding data + + bSPUIsOpen=1; + +#ifdef _WIN32 + if(iDebugMode) // windows debug dialog + { + hWDebug=CreateDialog(hInst,MAKEINTRESOURCE(IDD_DEBUG), + NULL,(DLGPROC)DebugDlgProc); + SetWindowPos(hWDebug,HWND_TOPMOST,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE|SWP_SHOWWINDOW|SWP_NOACTIVATE); + UpdateWindow(hWDebug); + SetFocus(hWMain); + } + + if(iRecordMode) // windows recording dialog + { + hWRecord=CreateDialog(hInst,MAKEINTRESOURCE(IDD_RECORD), + NULL,(DLGPROC)RecordDlgProc); + SetWindowPos(hWRecord,HWND_TOPMOST,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE|SWP_SHOWWINDOW|SWP_NOACTIVATE); + UpdateWindow(hWRecord); + SetFocus(hWMain); + } +#endif + + return 0; +} + + +//////////////////////////////////////////////////////////////////////// +// SPUCLOSE: called before shutdown +//////////////////////////////////////////////////////////////////////// + +EXPORT_GCC void CALLBACK SPU2close(void) +{ + if(!bSPUIsOpen) return; // some security + + bSPUIsOpen=0; // no more open + //fclose(LogFile); +#ifdef _WIN32 + if(IsWindow(hWDebug)) DestroyWindow(hWDebug); + hWDebug=0; + if(IsWindow(hWRecord)) DestroyWindow(hWRecord); + hWRecord=0; +#endif + + RemoveTimer(); // no more feeding + + RemoveSound(); // no more sound handling + + RemoveStreams(); // no more streaming +} + +//////////////////////////////////////////////////////////////////////// +// SPUSHUTDOWN: called by main emu on final exit +//////////////////////////////////////////////////////////////////////// + +EXPORT_GCC void CALLBACK SPU2shutdown(void) +{ +#ifdef _WIN32 + if(hIRE!=NULL) {FreeLibrary(hIRE);hIRE=NULL;} +#endif + + return; +} + +//////////////////////////////////////////////////////////////////////// +// SPUTEST: we don't test, we are always fine ;) +//////////////////////////////////////////////////////////////////////// + +EXPORT_GCC long CALLBACK SPU2test(void) +{ + return 0; +} + +//////////////////////////////////////////////////////////////////////// +// SPUCONFIGURE: call config dialog +//////////////////////////////////////////////////////////////////////// + +EXPORT_GCC void CALLBACK SPU2configure(void) +{ +#ifdef _WIN32 + DialogBox(hInst,MAKEINTRESOURCE(IDD_CFGDLG), + GetActiveWindow(),(DLGPROC)DSoundDlgProc); +#else + //StartCfgTool("CFG"); +#endif +} + +//////////////////////////////////////////////////////////////////////// +// SPUABOUT: show about window +//////////////////////////////////////////////////////////////////////// + +EXPORT_GCC void CALLBACK SPU2about(void) +{ +#ifdef _WIN32 + DialogBox(hInst,MAKEINTRESOURCE(IDD_ABOUT), + GetActiveWindow(),(DLGPROC)AboutDlgProc); +#else + //StartCfgTool("ABOUT"); +#endif +} + +//////////////////////////////////////////////////////////////////////// +// SETUP CALLBACKS +// this functions will be called once, +// passes a callback that should be called on SPU-IRQ/cdda volume change +//////////////////////////////////////////////////////////////////////// + +EXPORT_GCC void CALLBACK SPU2irqCallback(void (CALLBACK *SPU2callback)(int), + void (CALLBACK *DMA4callback)(int), + void (CALLBACK *DMA7callback)(int)) +{ + irqCallbackSPU2 = SPU2callback; + irqCallbackDMA4 = DMA4callback; + irqCallbackDMA7 = DMA7callback; +} + +//////////////////////////////////////////////////////////////////////// +// COMMON PLUGIN INFO FUNCS +//////////////////////////////////////////////////////////////////////// + +EXPORT_GCC char * CALLBACK PS2EgetLibName(void) +{ + return libraryName; +} + +#define PS2E_LT_SPU2 0x4 + +EXPORT_GCC unsigned long CALLBACK PS2EgetLibType(void) +{ + return PS2E_LT_SPU2; +} + +EXPORT_GCC unsigned long CALLBACK PS2EgetLibVersion2(unsigned long type) +{ + unsigned char v=version; + + // key hack to fake a lower version: + //if(GetAsyncKeyState(VK_SHIFT)&0x8000) v--; + + // compile hack to set lib version to PCSX2 0.6 standards + //v=2; + + return v<<16|revision<<8|build; +} + +//////////////////////////////////////////////////////////////////////// + diff --git a/plugins/SPU2null/Windows/Win32.cpp b/plugins/SPU2null/Windows/Win32.cpp index 5695dacb75..bab022daf8 100644 --- a/plugins/SPU2null/Windows/Win32.cpp +++ b/plugins/SPU2null/Windows/Win32.cpp @@ -1,81 +1,81 @@ -#include -#include -#include - -#include "SPU2.h" -#include "resource.h" - -HINSTANCE hInst; - -void SysMessage(char *fmt, ...) { - va_list list; - char tmp[512]; - - va_start(list,fmt); - vsprintf(tmp,fmt,list); - va_end(list); - MessageBox(0, tmp, "SPU2NULL Msg", 0); -} - -BOOL CALLBACK ConfigureDlgProc(HWND hW, UINT uMsg, WPARAM wParam, LPARAM lParam) { - - switch(uMsg) { - case WM_INITDIALOG: - LoadConfig(); - if (conf.Log) CheckDlgButton(hW, IDC_LOGGING, TRUE); - return TRUE; - - case WM_COMMAND: - switch(LOWORD(wParam)) { - case IDCANCEL: - EndDialog(hW, TRUE); - return TRUE; - case IDOK: - if (IsDlgButtonChecked(hW, IDC_LOGGING)) - conf.Log = 1; - else conf.Log = 0; - SaveConfig(); - EndDialog(hW, FALSE); - return TRUE; - } - } - return FALSE; -} - -BOOL CALLBACK AboutDlgProc(HWND hW, UINT uMsg, WPARAM wParam, LPARAM lParam) { - switch(uMsg) { - case WM_INITDIALOG: - return TRUE; - - case WM_COMMAND: - switch(LOWORD(wParam)) { - case IDOK: - EndDialog(hW, FALSE); - return TRUE; - } - } - return FALSE; -} - -void CALLBACK SPU2configure() { - DialogBox(hInst, - MAKEINTRESOURCE(IDD_CONFIG), - GetActiveWindow(), - (DLGPROC)ConfigureDlgProc); - -} - -void CALLBACK SPU2about() { - DialogBox(hInst, - MAKEINTRESOURCE(IDD_ABOUT), - GetActiveWindow(), - (DLGPROC)AboutDlgProc); -} - -BOOL APIENTRY DllMain(HANDLE hModule, // DLL INIT - DWORD dwReason, - LPVOID lpReserved) { - hInst = (HINSTANCE)hModule; - return TRUE; // very quick :) -} - +#include +#include +#include + +#include "SPU2.h" +#include "resource.h" + +HINSTANCE hInst; + +void SysMessage(char *fmt, ...) { + va_list list; + char tmp[512]; + + va_start(list,fmt); + vsprintf(tmp,fmt,list); + va_end(list); + MessageBox(0, tmp, "SPU2NULL Msg", 0); +} + +BOOL CALLBACK ConfigureDlgProc(HWND hW, UINT uMsg, WPARAM wParam, LPARAM lParam) { + + switch(uMsg) { + case WM_INITDIALOG: + LoadConfig(); + if (conf.Log) CheckDlgButton(hW, IDC_LOGGING, TRUE); + return TRUE; + + case WM_COMMAND: + switch(LOWORD(wParam)) { + case IDCANCEL: + EndDialog(hW, TRUE); + return TRUE; + case IDOK: + if (IsDlgButtonChecked(hW, IDC_LOGGING)) + conf.Log = 1; + else conf.Log = 0; + SaveConfig(); + EndDialog(hW, FALSE); + return TRUE; + } + } + return FALSE; +} + +BOOL CALLBACK AboutDlgProc(HWND hW, UINT uMsg, WPARAM wParam, LPARAM lParam) { + switch(uMsg) { + case WM_INITDIALOG: + return TRUE; + + case WM_COMMAND: + switch(LOWORD(wParam)) { + case IDOK: + EndDialog(hW, FALSE); + return TRUE; + } + } + return FALSE; +} + +void CALLBACK SPU2configure() { + DialogBox(hInst, + MAKEINTRESOURCE(IDD_CONFIG), + GetActiveWindow(), + (DLGPROC)ConfigureDlgProc); + +} + +void CALLBACK SPU2about() { + DialogBox(hInst, + MAKEINTRESOURCE(IDD_ABOUT), + GetActiveWindow(), + (DLGPROC)AboutDlgProc); +} + +BOOL APIENTRY DllMain(HANDLE hModule, // DLL INIT + DWORD dwReason, + LPVOID lpReserved) { + hInst = (HINSTANCE)hModule; + return TRUE; // very quick :) +} + diff --git a/plugins/USBnull/Windows/Config.cpp b/plugins/USBnull/Windows/Config.cpp index aff2667582..5e31d6dcb5 100644 --- a/plugins/USBnull/Windows/Config.cpp +++ b/plugins/USBnull/Windows/Config.cpp @@ -1,51 +1,51 @@ -#include - -#include "../USB.h" - -extern HINSTANCE hInst; -void SaveConfig() -{ - - Config *Conf1 = &conf; - char *szTemp; - char szIniFile[256], szValue[256]; - - GetModuleFileName(GetModuleHandle((LPCSTR)hInst), szIniFile, 256); - szTemp = strrchr(szIniFile, '\\'); - - if(!szTemp) return; - strcpy(szTemp, "\\inis\\usbnull.ini"); - sprintf(szValue,"%u",Conf1->Log); - WritePrivateProfileString("Interface", "Logging",szValue,szIniFile); - -} - -void LoadConfig() { - FILE *fp; - - - Config *Conf1 = &conf; - char *szTemp; - char szIniFile[256], szValue[256]; - - GetModuleFileName(GetModuleHandle((LPCSTR)hInst), szIniFile, 256); - szTemp = strrchr(szIniFile, '\\'); - - if(!szTemp) return ; - strcpy(szTemp, "\\inis\\usbnull.ini"); - fp=fopen("inis\\usbnull.ini","rt");//check if usbnull.ini really exists - if (!fp) - { - CreateDirectory("inis",NULL); - memset(&conf, 0, sizeof(conf)); - conf.Log = 0;//default value - SaveConfig();//save and return - return ; - } - fclose(fp); - GetPrivateProfileString("Interface", "Logging", NULL, szValue, 20, szIniFile); - Conf1->Log = strtoul(szValue, NULL, 10); - return ; - -} - +#include + +#include "../USB.h" + +extern HINSTANCE hInst; +void SaveConfig() +{ + + Config *Conf1 = &conf; + char *szTemp; + char szIniFile[256], szValue[256]; + + GetModuleFileName(GetModuleHandle((LPCSTR)hInst), szIniFile, 256); + szTemp = strrchr(szIniFile, '\\'); + + if(!szTemp) return; + strcpy(szTemp, "\\inis\\usbnull.ini"); + sprintf(szValue,"%u",Conf1->Log); + WritePrivateProfileString("Interface", "Logging",szValue,szIniFile); + +} + +void LoadConfig() { + FILE *fp; + + + Config *Conf1 = &conf; + char *szTemp; + char szIniFile[256], szValue[256]; + + GetModuleFileName(GetModuleHandle((LPCSTR)hInst), szIniFile, 256); + szTemp = strrchr(szIniFile, '\\'); + + if(!szTemp) return ; + strcpy(szTemp, "\\inis\\usbnull.ini"); + fp=fopen("inis\\usbnull.ini","rt");//check if usbnull.ini really exists + if (!fp) + { + CreateDirectory("inis",NULL); + memset(&conf, 0, sizeof(conf)); + conf.Log = 0;//default value + SaveConfig();//save and return + return ; + } + fclose(fp); + GetPrivateProfileString("Interface", "Logging", NULL, szValue, 20, szIniFile); + Conf1->Log = strtoul(szValue, NULL, 10); + return ; + +} + diff --git a/plugins/USBnull/Windows/Win32.cpp b/plugins/USBnull/Windows/Win32.cpp index d62646aadc..e89af62e52 100644 --- a/plugins/USBnull/Windows/Win32.cpp +++ b/plugins/USBnull/Windows/Win32.cpp @@ -1,81 +1,81 @@ -#include -#include -#include - -#include "../USB.h" -#include "resource.h" - -HINSTANCE hInst; - -void SysMessage(char *fmt, ...) { - va_list list; - char tmp[512]; - - va_start(list,fmt); - vsprintf(tmp,fmt,list); - va_end(list); - MessageBox(GetActiveWindow(), tmp, "USBnull Msg", MB_SETFOREGROUND | MB_OK); -} - -BOOL CALLBACK ConfigureDlgProc(HWND hW, UINT uMsg, WPARAM wParam, LPARAM lParam) { - - switch(uMsg) { - case WM_INITDIALOG: - LoadConfig(); - if (conf.Log) CheckDlgButton(hW, IDC_LOGGING, TRUE); - return TRUE; - - case WM_COMMAND: - switch(LOWORD(wParam)) { - case IDCANCEL: - EndDialog(hW, TRUE); - return TRUE; - case IDOK: - if (IsDlgButtonChecked(hW, IDC_LOGGING)) - conf.Log = 1; - else conf.Log = 0; - SaveConfig(); - EndDialog(hW, FALSE); - return TRUE; - } - } - return FALSE; -} - -BOOL CALLBACK AboutDlgProc(HWND hW, UINT uMsg, WPARAM wParam, LPARAM lParam) { - switch(uMsg) { - case WM_INITDIALOG: - return TRUE; - - case WM_COMMAND: - switch(LOWORD(wParam)) { - case IDOK: - EndDialog(hW, FALSE); - return TRUE; - } - } - return FALSE; -} - -void CALLBACK USBconfigure() { - DialogBox(hInst, - MAKEINTRESOURCE(IDD_CONFIG), - GetActiveWindow(), - (DLGPROC)ConfigureDlgProc); - -} - -void CALLBACK USBabout() { - DialogBox(hInst, - MAKEINTRESOURCE(IDD_ABOUT), - GetActiveWindow(), - (DLGPROC)AboutDlgProc); -} - -BOOL APIENTRY DllMain(HANDLE hModule, // DLL INIT - DWORD dwReason, - LPVOID lpReserved) { - hInst = (HINSTANCE)hModule; - return TRUE; // very quick :) -} - +#include +#include +#include + +#include "../USB.h" +#include "resource.h" + +HINSTANCE hInst; + +void SysMessage(char *fmt, ...) { + va_list list; + char tmp[512]; + + va_start(list,fmt); + vsprintf(tmp,fmt,list); + va_end(list); + MessageBox(GetActiveWindow(), tmp, "USBnull Msg", MB_SETFOREGROUND | MB_OK); +} + +BOOL CALLBACK ConfigureDlgProc(HWND hW, UINT uMsg, WPARAM wParam, LPARAM lParam) { + + switch(uMsg) { + case WM_INITDIALOG: + LoadConfig(); + if (conf.Log) CheckDlgButton(hW, IDC_LOGGING, TRUE); + return TRUE; + + case WM_COMMAND: + switch(LOWORD(wParam)) { + case IDCANCEL: + EndDialog(hW, TRUE); + return TRUE; + case IDOK: + if (IsDlgButtonChecked(hW, IDC_LOGGING)) + conf.Log = 1; + else conf.Log = 0; + SaveConfig(); + EndDialog(hW, FALSE); + return TRUE; + } + } + return FALSE; +} + +BOOL CALLBACK AboutDlgProc(HWND hW, UINT uMsg, WPARAM wParam, LPARAM lParam) { + switch(uMsg) { + case WM_INITDIALOG: + return TRUE; + + case WM_COMMAND: + switch(LOWORD(wParam)) { + case IDOK: + EndDialog(hW, FALSE); + return TRUE; + } + } + return FALSE; +} + +void CALLBACK USBconfigure() { + DialogBox(hInst, + MAKEINTRESOURCE(IDD_CONFIG), + GetActiveWindow(), + (DLGPROC)ConfigureDlgProc); + +} + +void CALLBACK USBabout() { + DialogBox(hInst, + MAKEINTRESOURCE(IDD_ABOUT), + GetActiveWindow(), + (DLGPROC)AboutDlgProc); +} + +BOOL APIENTRY DllMain(HANDLE hModule, // DLL INIT + DWORD dwReason, + LPVOID lpReserved) { + hInst = (HINSTANCE)hModule; + return TRUE; // very quick :) +} + diff --git a/plugins/cdvdGigaherz/src/CDVD.cpp b/plugins/cdvdGigaherz/src/CDVD.cpp index 951e99252e..3732c55e0b 100644 --- a/plugins/cdvdGigaherz/src/CDVD.cpp +++ b/plugins/cdvdGigaherz/src/CDVD.cpp @@ -1,533 +1,533 @@ -#include - -#include "CDVD.h" -#include "resource.h" -#include "Shlwapi.h" - -#include "svnrev.h" - -#include - -void (*newDiscCB)(); - -#define STRFY(x) #x -#define TOSTR(x) STRFY(x) - -/////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////// -// State Information // - -int strack; -int etrack; -track tracks[100]; - -int curDiskType; -int curTrayStatus; - -int csector; -int cmode; - -#define SFY(x) STRFY(x) -/////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////// -// Plugin Interface // - -char *LibName = "cdvdGigaherz " -#ifdef PCSX2_DEBUG - " Debug " -#endif - "(r" SFY(SVN_REV) -#if SVN_MODS - "/modded" -#endif - ")"; - -const unsigned char version = PS2E_CDVD_VERSION; -const unsigned char revision = 0; -const unsigned char build = 8; - -HINSTANCE hinst; - -BOOL WINAPI DllMain( - HINSTANCE hinstDLL, // handle to DLL module - DWORD fdwReason, // reason for calling function - LPVOID lpvReserved // reserved -) -{ - if(fdwReason==DLL_PROCESS_ATTACH) { - hinst=hinstDLL; - } - return TRUE; -} - -char* CALLBACK PS2EgetLibName() { - return LibName; -} - -u32 CALLBACK PS2EgetLibType() { - return PS2E_LT_CDVD; -} - -u32 CALLBACK PS2EgetLibVersion2(u32 type) { - return (version << 16) | (revision << 8) | build; -} - -/////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////// -// Utility Functions // - -void SysMessage(char *fmt, ...) { - va_list list; - char tmp[512]; - - va_start(list,fmt); - vsprintf(tmp,fmt,list); - va_end(list); - MessageBox(0, tmp, "cdvdGigaherz Msg", 0); -} - -u8 __inline dec_to_bcd(u8 dec) -{ - return ((dec/10)<<4)|(dec%10); -} - -void __inline lsn_to_msf(u8* minute, u8* second, u8* frame, u32 lsn) -{ - *frame = dec_to_bcd(lsn%75); - lsn/=75; - *second= dec_to_bcd(lsn%60); - lsn/=60; - *minute= dec_to_bcd(lsn%100); -} - -void __inline lba_to_msf(s32 lba, u8* m, u8* s, u8* f) { - lba += 150; - *m = (u8)(lba / (60*75)); - *s = (u8)((lba / 75) % 60); - *f = (u8)(lba % 75); -} - -/////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////// -// CDVD processing functions // - -char csrc[20]; - -BOOL cdvd_is_open=FALSE; - -Source *src; - -s32 disc_has_changed=0; - -int weAreInNewDiskCB=0; - -char bfr[2352]; - -/////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////// -// CDVD Pluin Interface // - -void CALLBACK CDVDsetSettingsDir(const char* dir) -{ - CfgSetSettingsDir(dir); -} - -s32 CALLBACK CDVDinit() -{ - return 0; -} - -s32 CALLBACK CDVDopen(const char* pTitleFilename) -{ - ReadSettings(); - - if(source_drive=='-') - { - // MSDN : Trailing backslash is required to ensure consistent behavior across - // various versions of Windows and storage types. - char temp[]="A:\\"; - - for(char d='A';d<='Z';d++) - { - temp[0]=d; - if(GetDriveType(temp)==DRIVE_CDROM) - { - source_drive=d; - break; - } - } - } - - if(source_drive=='@') - { - curDiskType=CDVD_TYPE_NODISC; - return 0; - } - - if(source_drive=='$') - { - printf(" * CDVD: Opening image '%s'...\n",source_file); - - //open device file - src= TryLoaders(source_file); - } - else - { - sprintf(csrc,"\\\\.\\%c:",source_drive); - - printf(" * CDVD: Opening drive '%s'...\n",csrc); - - //open device file - src=new IOCtlSrc(csrc); - } - - if(!src->IsOK()) - { - printf(" * CDVD: Error opening source.\n"); - return -1; - } - - //setup threading manager - cdvdStartThread(); - - return cdvdRefreshData(); -} - -void CALLBACK CDVDclose() -{ - cdvdStopThread(); - - //close device - delete src; - src=NULL; -} - -void CALLBACK CDVDshutdown() -{ - //nothing to do here -} - -s32 CALLBACK CDVDgetDualInfo(s32* dualType, u32* _layer1start) -{ - switch(src->GetMediaType()) - { - case 1: - *dualType = 1; - *_layer1start = src->GetLayerBreakAddress(); - return 1; - case 2: - *dualType = 2; - *_layer1start = src->GetLayerBreakAddress(); - return 1; - case 0: - *dualType = 0; - *_layer1start = 0; - return 1; - } - return 0; -} - -int lastReadInNewDiskCB=0; -char fuckThisSector[2352]; - -s32 CALLBACK CDVDreadSector(u8* buffer, s32 lsn, int mode) -{ - return cdvdDirectReadSector(lsn,mode,(char*)buffer); -} - -s32 CALLBACK CDVDreadTrack(u32 lsn, int mode) -{ - csector=lsn; - cmode=mode; - - if(weAreInNewDiskCB) - { - int ret = cdvdDirectReadSector(lsn,mode,fuckThisSector); - if(ret==0) lastReadInNewDiskCB=1; - return ret; - } - - if(lsn>tracks[0].length) // track 0 is total disc. - { - return -1; - } - - return cdvdRequestSector(lsn,mode); -} - -// return can be NULL (for async modes) -u8* CALLBACK CDVDgetBuffer() -{ - if(lastReadInNewDiskCB) - { - lastReadInNewDiskCB=0; - return (u8*)fuckThisSector; - } - - u8 *s = (u8*)cdvdGetSector(csector,cmode); - - return s; -} - -// return can be NULL (for async modes) -int CALLBACK CDVDgetBuffer2(u8* dest) -{ - int csize = 2352; - switch(cmode) - { - case CDVD_MODE_2048: csize = 2048; break; - case CDVD_MODE_2328: csize = 2328; break; - case CDVD_MODE_2340: csize = 2340; break; - } - - if(lastReadInNewDiskCB) - { - lastReadInNewDiskCB=0; - - memcpy(dest, fuckThisSector, csize); - return 0; - } - - memcpy(dest, cdvdGetSector(csector,cmode), csize); - - return 0; -} - -s32 CALLBACK CDVDreadSubQ(u32 lsn, cdvdSubQ* subq) -{ - int i; - // the formatted subq command returns: control/adr, track, index, trk min, trk sec, trk frm, 0x00, abs min, abs sec, abs frm - - if(lsn>tracks[0].length) // track 0 is total disc. - return -1; - - memset(subq,0,sizeof(cdvdSubQ)); - - lsn_to_msf(&subq->discM,&subq->discS,&subq->discF,lsn+150); - - i=strack; - while(i<=etrack) - { - if(lsn<=tracks[i].length) - break; - lsn-=tracks[i].length; - i++; - } - - if(i>etrack) - i=etrack; - - lsn_to_msf(&subq->trackM,&subq->trackS,&subq->trackF,lsn); - - subq->mode=1; - subq->ctrl=tracks[i].type; - subq->trackNum=i; - subq->trackIndex=1; - - return 0; -} - -s32 CALLBACK CDVDgetTN(cdvdTN *Buffer) -{ - Buffer->strack=strack; - Buffer->etrack=etrack; - return 0; -} - -s32 CALLBACK CDVDgetTD(u8 Track, cdvdTD *Buffer) -{ - if(Track==0) - { - Buffer->lsn = tracks[0].length; - Buffer->type= 0; - return 0; - } - - if(Tracketrack) return -1; - - Buffer->lsn = tracks[Track].start_lba; - Buffer->type= tracks[Track].type; - return 0; -} - -u32 layer1start=-1; - -s32 CALLBACK CDVDgetTOC(u8* tocBuff) -{ - //return src->ReadTOC((char*)toc,2048); - //that didn't work too well... - - if(curDiskType==CDVD_TYPE_NODISC) - return -1; - - if((curDiskType == CDVD_TYPE_DVDV) || - (curDiskType == CDVD_TYPE_PS2DVD)) - { - memset(tocBuff, 0, 2048); - - s32 mt=src->GetMediaType(); - - if(mt<0) - return -1; - - if(mt==0) //single layer - { - // fake it - tocBuff[ 0] = 0x04; - tocBuff[ 1] = 0x02; - tocBuff[ 2] = 0xF2; - tocBuff[ 3] = 0x00; - tocBuff[ 4] = 0x86; - tocBuff[ 5] = 0x72; - - tocBuff[16] = 0x00; // first sector for layer 0 - tocBuff[17] = 0x03; - tocBuff[18] = 0x00; - tocBuff[19] = 0x00; - } - else if(mt==1) //PTP - { - layer1start = src->GetLayerBreakAddress() + 0x30000; - - // dual sided - tocBuff[ 0] = 0x24; - tocBuff[ 1] = 0x02; - tocBuff[ 2] = 0xF2; - tocBuff[ 3] = 0x00; - tocBuff[ 4] = 0x41; - tocBuff[ 5] = 0x95; - - tocBuff[14] = 0x61; // PTP - - tocBuff[16] = 0x00; - tocBuff[17] = 0x03; - tocBuff[18] = 0x00; - tocBuff[19] = 0x00; - - tocBuff[20] = (layer1start>>24); - tocBuff[21] = (layer1start>>16)&0xff; - tocBuff[22] = (layer1start>> 8)&0xff; - tocBuff[23] = (layer1start>> 0)&0xff; - } - else //OTP - { - layer1start = src->GetLayerBreakAddress() + 0x30000; - - // dual sided - tocBuff[ 0] = 0x24; - tocBuff[ 1] = 0x02; - tocBuff[ 2] = 0xF2; - tocBuff[ 3] = 0x00; - tocBuff[ 4] = 0x41; - tocBuff[ 5] = 0x95; - - tocBuff[14] = 0x71; // OTP - - tocBuff[16] = 0x00; - tocBuff[17] = 0x03; - tocBuff[18] = 0x00; - tocBuff[19] = 0x00; - - tocBuff[24] = (layer1start>>24); - tocBuff[25] = (layer1start>>16)&0xff; - tocBuff[26] = (layer1start>> 8)&0xff; - tocBuff[27] = (layer1start>> 0)&0xff; - } - } - else if(curDiskType == CDVD_TYPE_CDDA || - curDiskType == CDVD_TYPE_PS2CDDA || - curDiskType == CDVD_TYPE_PS2CD || - curDiskType == CDVD_TYPE_PSCDDA || - curDiskType == CDVD_TYPE_PSCD) - { - // cd toc - // (could be replaced by 1 command that reads the full toc) - u8 min, sec, frm,i; - s32 err; - cdvdTN diskInfo; - cdvdTD trackInfo; - memset(tocBuff, 0, 1024); - if (CDVDgetTN(&diskInfo) == -1) { diskInfo.etrack = 0;diskInfo.strack = 1; } - if (CDVDgetTD(0, &trackInfo) == -1) trackInfo.lsn = 0; - - tocBuff[0] = 0x41; - tocBuff[1] = 0x00; - -#define itob(n) ((((n)/10)<<4)+((n)%10)) - - //Number of FirstTrack - tocBuff[2] = 0xA0; - tocBuff[7] = itob(diskInfo.strack); - - //Number of LastTrack - tocBuff[12] = 0xA1; - tocBuff[17] = itob(diskInfo.etrack); - - //DiskLength - lba_to_msf(trackInfo.lsn, &min, &sec, &frm); - tocBuff[22] = 0xA2; - tocBuff[27] = itob(min); - tocBuff[28] = itob(sec); - tocBuff[29] = itob(frm); - - fprintf(stderr,"Track 0: %d mins %d secs %d frames\n",min,sec,frm); - - for (i=diskInfo.strack; i<=diskInfo.etrack; i++) - { - err = CDVDgetTD(i, &trackInfo); - lba_to_msf(trackInfo.lsn, &min, &sec, &frm); - tocBuff[i*10+30] = trackInfo.type; - tocBuff[i*10+32] = err == -1 ? 0 : itob(i); //number - tocBuff[i*10+37] = itob(min); - tocBuff[i*10+38] = itob(sec); - tocBuff[i*10+39] = itob(frm); - fprintf(stderr,"Track %d: %d mins %d secs %d frames\n",i,min,sec,frm); - } - } - else - return -1; - - return 0; -} - -s32 CALLBACK CDVDgetDiskType() -{ - return curDiskType; -} - -s32 CALLBACK CDVDgetTrayStatus() -{ - return curTrayStatus; -} - -s32 CALLBACK CDVDctrlTrayOpen() -{ - curTrayStatus=CDVD_TRAY_OPEN; - return 0; -} - -s32 CALLBACK CDVDctrlTrayClose() -{ - curTrayStatus=CDVD_TRAY_CLOSE; - return 0; -} - -void CALLBACK CDVDnewDiskCB(void (*callback)()) -{ - newDiscCB=callback; -} - -void configure(); -void CALLBACK CDVDconfigure() -{ - configure(); -} - -void CALLBACK CDVDabout() { - SysMessage("%s %d.%d", LibName, revision, build); -} - -s32 CALLBACK CDVDtest() { - return 0; -} +#include + +#include "CDVD.h" +#include "resource.h" +#include "Shlwapi.h" + +#include "svnrev.h" + +#include + +void (*newDiscCB)(); + +#define STRFY(x) #x +#define TOSTR(x) STRFY(x) + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// State Information // + +int strack; +int etrack; +track tracks[100]; + +int curDiskType; +int curTrayStatus; + +int csector; +int cmode; + +#define SFY(x) STRFY(x) +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// Plugin Interface // + +char *LibName = "cdvdGigaherz " +#ifdef PCSX2_DEBUG + " Debug " +#endif + "(r" SFY(SVN_REV) +#if SVN_MODS + "/modded" +#endif + ")"; + +const unsigned char version = PS2E_CDVD_VERSION; +const unsigned char revision = 0; +const unsigned char build = 8; + +HINSTANCE hinst; + +BOOL WINAPI DllMain( + HINSTANCE hinstDLL, // handle to DLL module + DWORD fdwReason, // reason for calling function + LPVOID lpvReserved // reserved +) +{ + if(fdwReason==DLL_PROCESS_ATTACH) { + hinst=hinstDLL; + } + return TRUE; +} + +char* CALLBACK PS2EgetLibName() { + return LibName; +} + +u32 CALLBACK PS2EgetLibType() { + return PS2E_LT_CDVD; +} + +u32 CALLBACK PS2EgetLibVersion2(u32 type) { + return (version << 16) | (revision << 8) | build; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// Utility Functions // + +void SysMessage(char *fmt, ...) { + va_list list; + char tmp[512]; + + va_start(list,fmt); + vsprintf(tmp,fmt,list); + va_end(list); + MessageBox(0, tmp, "cdvdGigaherz Msg", 0); +} + +u8 __inline dec_to_bcd(u8 dec) +{ + return ((dec/10)<<4)|(dec%10); +} + +void __inline lsn_to_msf(u8* minute, u8* second, u8* frame, u32 lsn) +{ + *frame = dec_to_bcd(lsn%75); + lsn/=75; + *second= dec_to_bcd(lsn%60); + lsn/=60; + *minute= dec_to_bcd(lsn%100); +} + +void __inline lba_to_msf(s32 lba, u8* m, u8* s, u8* f) { + lba += 150; + *m = (u8)(lba / (60*75)); + *s = (u8)((lba / 75) % 60); + *f = (u8)(lba % 75); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// CDVD processing functions // + +char csrc[20]; + +BOOL cdvd_is_open=FALSE; + +Source *src; + +s32 disc_has_changed=0; + +int weAreInNewDiskCB=0; + +char bfr[2352]; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// CDVD Pluin Interface // + +void CALLBACK CDVDsetSettingsDir(const char* dir) +{ + CfgSetSettingsDir(dir); +} + +s32 CALLBACK CDVDinit() +{ + return 0; +} + +s32 CALLBACK CDVDopen(const char* pTitleFilename) +{ + ReadSettings(); + + if(source_drive=='-') + { + // MSDN : Trailing backslash is required to ensure consistent behavior across + // various versions of Windows and storage types. + char temp[]="A:\\"; + + for(char d='A';d<='Z';d++) + { + temp[0]=d; + if(GetDriveType(temp)==DRIVE_CDROM) + { + source_drive=d; + break; + } + } + } + + if(source_drive=='@') + { + curDiskType=CDVD_TYPE_NODISC; + return 0; + } + + if(source_drive=='$') + { + printf(" * CDVD: Opening image '%s'...\n",source_file); + + //open device file + src= TryLoaders(source_file); + } + else + { + sprintf(csrc,"\\\\.\\%c:",source_drive); + + printf(" * CDVD: Opening drive '%s'...\n",csrc); + + //open device file + src=new IOCtlSrc(csrc); + } + + if(!src->IsOK()) + { + printf(" * CDVD: Error opening source.\n"); + return -1; + } + + //setup threading manager + cdvdStartThread(); + + return cdvdRefreshData(); +} + +void CALLBACK CDVDclose() +{ + cdvdStopThread(); + + //close device + delete src; + src=NULL; +} + +void CALLBACK CDVDshutdown() +{ + //nothing to do here +} + +s32 CALLBACK CDVDgetDualInfo(s32* dualType, u32* _layer1start) +{ + switch(src->GetMediaType()) + { + case 1: + *dualType = 1; + *_layer1start = src->GetLayerBreakAddress(); + return 1; + case 2: + *dualType = 2; + *_layer1start = src->GetLayerBreakAddress(); + return 1; + case 0: + *dualType = 0; + *_layer1start = 0; + return 1; + } + return 0; +} + +int lastReadInNewDiskCB=0; +char fuckThisSector[2352]; + +s32 CALLBACK CDVDreadSector(u8* buffer, s32 lsn, int mode) +{ + return cdvdDirectReadSector(lsn,mode,(char*)buffer); +} + +s32 CALLBACK CDVDreadTrack(u32 lsn, int mode) +{ + csector=lsn; + cmode=mode; + + if(weAreInNewDiskCB) + { + int ret = cdvdDirectReadSector(lsn,mode,fuckThisSector); + if(ret==0) lastReadInNewDiskCB=1; + return ret; + } + + if(lsn>tracks[0].length) // track 0 is total disc. + { + return -1; + } + + return cdvdRequestSector(lsn,mode); +} + +// return can be NULL (for async modes) +u8* CALLBACK CDVDgetBuffer() +{ + if(lastReadInNewDiskCB) + { + lastReadInNewDiskCB=0; + return (u8*)fuckThisSector; + } + + u8 *s = (u8*)cdvdGetSector(csector,cmode); + + return s; +} + +// return can be NULL (for async modes) +int CALLBACK CDVDgetBuffer2(u8* dest) +{ + int csize = 2352; + switch(cmode) + { + case CDVD_MODE_2048: csize = 2048; break; + case CDVD_MODE_2328: csize = 2328; break; + case CDVD_MODE_2340: csize = 2340; break; + } + + if(lastReadInNewDiskCB) + { + lastReadInNewDiskCB=0; + + memcpy(dest, fuckThisSector, csize); + return 0; + } + + memcpy(dest, cdvdGetSector(csector,cmode), csize); + + return 0; +} + +s32 CALLBACK CDVDreadSubQ(u32 lsn, cdvdSubQ* subq) +{ + int i; + // the formatted subq command returns: control/adr, track, index, trk min, trk sec, trk frm, 0x00, abs min, abs sec, abs frm + + if(lsn>tracks[0].length) // track 0 is total disc. + return -1; + + memset(subq,0,sizeof(cdvdSubQ)); + + lsn_to_msf(&subq->discM,&subq->discS,&subq->discF,lsn+150); + + i=strack; + while(i<=etrack) + { + if(lsn<=tracks[i].length) + break; + lsn-=tracks[i].length; + i++; + } + + if(i>etrack) + i=etrack; + + lsn_to_msf(&subq->trackM,&subq->trackS,&subq->trackF,lsn); + + subq->mode=1; + subq->ctrl=tracks[i].type; + subq->trackNum=i; + subq->trackIndex=1; + + return 0; +} + +s32 CALLBACK CDVDgetTN(cdvdTN *Buffer) +{ + Buffer->strack=strack; + Buffer->etrack=etrack; + return 0; +} + +s32 CALLBACK CDVDgetTD(u8 Track, cdvdTD *Buffer) +{ + if(Track==0) + { + Buffer->lsn = tracks[0].length; + Buffer->type= 0; + return 0; + } + + if(Tracketrack) return -1; + + Buffer->lsn = tracks[Track].start_lba; + Buffer->type= tracks[Track].type; + return 0; +} + +u32 layer1start=-1; + +s32 CALLBACK CDVDgetTOC(u8* tocBuff) +{ + //return src->ReadTOC((char*)toc,2048); + //that didn't work too well... + + if(curDiskType==CDVD_TYPE_NODISC) + return -1; + + if((curDiskType == CDVD_TYPE_DVDV) || + (curDiskType == CDVD_TYPE_PS2DVD)) + { + memset(tocBuff, 0, 2048); + + s32 mt=src->GetMediaType(); + + if(mt<0) + return -1; + + if(mt==0) //single layer + { + // fake it + tocBuff[ 0] = 0x04; + tocBuff[ 1] = 0x02; + tocBuff[ 2] = 0xF2; + tocBuff[ 3] = 0x00; + tocBuff[ 4] = 0x86; + tocBuff[ 5] = 0x72; + + tocBuff[16] = 0x00; // first sector for layer 0 + tocBuff[17] = 0x03; + tocBuff[18] = 0x00; + tocBuff[19] = 0x00; + } + else if(mt==1) //PTP + { + layer1start = src->GetLayerBreakAddress() + 0x30000; + + // dual sided + tocBuff[ 0] = 0x24; + tocBuff[ 1] = 0x02; + tocBuff[ 2] = 0xF2; + tocBuff[ 3] = 0x00; + tocBuff[ 4] = 0x41; + tocBuff[ 5] = 0x95; + + tocBuff[14] = 0x61; // PTP + + tocBuff[16] = 0x00; + tocBuff[17] = 0x03; + tocBuff[18] = 0x00; + tocBuff[19] = 0x00; + + tocBuff[20] = (layer1start>>24); + tocBuff[21] = (layer1start>>16)&0xff; + tocBuff[22] = (layer1start>> 8)&0xff; + tocBuff[23] = (layer1start>> 0)&0xff; + } + else //OTP + { + layer1start = src->GetLayerBreakAddress() + 0x30000; + + // dual sided + tocBuff[ 0] = 0x24; + tocBuff[ 1] = 0x02; + tocBuff[ 2] = 0xF2; + tocBuff[ 3] = 0x00; + tocBuff[ 4] = 0x41; + tocBuff[ 5] = 0x95; + + tocBuff[14] = 0x71; // OTP + + tocBuff[16] = 0x00; + tocBuff[17] = 0x03; + tocBuff[18] = 0x00; + tocBuff[19] = 0x00; + + tocBuff[24] = (layer1start>>24); + tocBuff[25] = (layer1start>>16)&0xff; + tocBuff[26] = (layer1start>> 8)&0xff; + tocBuff[27] = (layer1start>> 0)&0xff; + } + } + else if(curDiskType == CDVD_TYPE_CDDA || + curDiskType == CDVD_TYPE_PS2CDDA || + curDiskType == CDVD_TYPE_PS2CD || + curDiskType == CDVD_TYPE_PSCDDA || + curDiskType == CDVD_TYPE_PSCD) + { + // cd toc + // (could be replaced by 1 command that reads the full toc) + u8 min, sec, frm,i; + s32 err; + cdvdTN diskInfo; + cdvdTD trackInfo; + memset(tocBuff, 0, 1024); + if (CDVDgetTN(&diskInfo) == -1) { diskInfo.etrack = 0;diskInfo.strack = 1; } + if (CDVDgetTD(0, &trackInfo) == -1) trackInfo.lsn = 0; + + tocBuff[0] = 0x41; + tocBuff[1] = 0x00; + +#define itob(n) ((((n)/10)<<4)+((n)%10)) + + //Number of FirstTrack + tocBuff[2] = 0xA0; + tocBuff[7] = itob(diskInfo.strack); + + //Number of LastTrack + tocBuff[12] = 0xA1; + tocBuff[17] = itob(diskInfo.etrack); + + //DiskLength + lba_to_msf(trackInfo.lsn, &min, &sec, &frm); + tocBuff[22] = 0xA2; + tocBuff[27] = itob(min); + tocBuff[28] = itob(sec); + tocBuff[29] = itob(frm); + + fprintf(stderr,"Track 0: %d mins %d secs %d frames\n",min,sec,frm); + + for (i=diskInfo.strack; i<=diskInfo.etrack; i++) + { + err = CDVDgetTD(i, &trackInfo); + lba_to_msf(trackInfo.lsn, &min, &sec, &frm); + tocBuff[i*10+30] = trackInfo.type; + tocBuff[i*10+32] = err == -1 ? 0 : itob(i); //number + tocBuff[i*10+37] = itob(min); + tocBuff[i*10+38] = itob(sec); + tocBuff[i*10+39] = itob(frm); + fprintf(stderr,"Track %d: %d mins %d secs %d frames\n",i,min,sec,frm); + } + } + else + return -1; + + return 0; +} + +s32 CALLBACK CDVDgetDiskType() +{ + return curDiskType; +} + +s32 CALLBACK CDVDgetTrayStatus() +{ + return curTrayStatus; +} + +s32 CALLBACK CDVDctrlTrayOpen() +{ + curTrayStatus=CDVD_TRAY_OPEN; + return 0; +} + +s32 CALLBACK CDVDctrlTrayClose() +{ + curTrayStatus=CDVD_TRAY_CLOSE; + return 0; +} + +void CALLBACK CDVDnewDiskCB(void (*callback)()) +{ + newDiscCB=callback; +} + +void configure(); +void CALLBACK CDVDconfigure() +{ + configure(); +} + +void CALLBACK CDVDabout() { + SysMessage("%s %d.%d", LibName, revision, build); +} + +s32 CALLBACK CDVDtest() { + return 0; +} diff --git a/plugins/cdvdGigaherz/src/CDVD.h b/plugins/cdvdGigaherz/src/CDVD.h index b39eb8c82c..4dbb24483b 100644 --- a/plugins/cdvdGigaherz/src/CDVD.h +++ b/plugins/cdvdGigaherz/src/CDVD.h @@ -1,164 +1,164 @@ -#ifndef __CDVD_H__ -#define __CDVD_H__ - -#include -#include - -#define CDVDdefs -#include - -typedef struct _track -{ - u32 start_lba; - u32 length; - u32 type; -} track; - -extern int strack; -extern int etrack; -extern track tracks[100]; - -extern int curDiskType; -extern int curTrayStatus; - -typedef struct _toc_entry { - UCHAR SessionNumber; - UCHAR Control : 4; - UCHAR Adr : 4; - UCHAR Reserved1; - UCHAR Point; - UCHAR MsfExtra[3]; - UCHAR Zero; - UCHAR Msf[3]; -} toc_entry; - -typedef struct _toc_data -{ - UCHAR Length[2]; - UCHAR FirstCompleteSession; - UCHAR LastCompleteSession; - - toc_entry Descriptors[255]; -} toc_data; - -extern toc_data cdtoc; - -class Source //abstract class as base for source modules -{ - Source(Source&); -public: - Source(){}; - - //virtual destructor - virtual ~Source() - { - } - - //virtual members - virtual s32 GetSectorCount()=0; - virtual s32 ReadTOC(char *toc,int size)=0; - virtual s32 ReadSectors2048(u32 sector, u32 count, char *buffer)=0; - virtual s32 ReadSectors2352(u32 sector, u32 count, char *buffer)=0; - virtual s32 GetLayerBreakAddress()=0; - - virtual s32 GetMediaType()=0; - - virtual s32 IsOK()=0; - virtual s32 Reopen()=0; - - virtual s32 DiscChanged()=0; -}; - -class IOCtlSrc: public Source -{ - IOCtlSrc(IOCtlSrc&); - - HANDLE device; - - bool OpenOK; - - s32 last_read_mode; - - s32 last_sector_count; - - char sectorbuffer[32*2048]; - - char fName[256]; - - DWORD sessID; - - bool tocCached; - char tocCacheData[2048]; - - bool mediaTypeCached; - int mediaType; - - bool discSizeCached; - s32 discSize; - - bool layerBreakCached; - s32 layerBreak; - -public: - IOCtlSrc(const char* fileName); - - //virtual destructor - virtual ~IOCtlSrc(); - - //virtual members - virtual s32 GetSectorCount(); - virtual s32 ReadTOC(char *toc,int size); - virtual s32 ReadSectors2048(u32 sector, u32 count, char *buffer); - virtual s32 ReadSectors2352(u32 sector, u32 count, char *buffer); - virtual s32 GetLayerBreakAddress(); - - virtual s32 GetMediaType(); - - virtual s32 IsOK(); - virtual s32 Reopen(); - - virtual s32 DiscChanged(); -}; - -extern Source *src; - -Source* TryLoaders(const char* fileName); - -int FindDiskType(); - -void configure(); - -extern char source_drive; -extern char source_file[]; - -extern HINSTANCE hinst; - -#define MSF_TO_LBA(m,s,f) ((m*60+s)*75+f-150) - -s32 cdvdDirectReadSector(s32 first, s32 mode, char *buffer); - -s32 cdvdGetMediaType(); - -void ReadSettings(); -void WriteSettings(); -void CfgSetSettingsDir( const char* dir ); - -extern char csrc[]; -extern BOOL cdvd_is_open; -extern int weAreInNewDiskCB; - -extern void (*newDiscCB)(); - -extern s32 disc_has_changed; - -s32 cdvdStartThread(); -void cdvdStopThread(); -s32 cdvdRequestSector(u32 sector, s32 mode); -s32 cdvdRequestComplete(); -char* cdvdGetSector(s32 sector, s32 mode); -s32 cdvdDirectReadSector(s32 first, s32 mode, char *buffer); -s32 cdvdGetMediaType(); -s32 cdvdRefreshData(); -s32 cdvdParseTOC(); - -#endif /* __CDVD_H__ */ +#ifndef __CDVD_H__ +#define __CDVD_H__ + +#include +#include + +#define CDVDdefs +#include + +typedef struct _track +{ + u32 start_lba; + u32 length; + u32 type; +} track; + +extern int strack; +extern int etrack; +extern track tracks[100]; + +extern int curDiskType; +extern int curTrayStatus; + +typedef struct _toc_entry { + UCHAR SessionNumber; + UCHAR Control : 4; + UCHAR Adr : 4; + UCHAR Reserved1; + UCHAR Point; + UCHAR MsfExtra[3]; + UCHAR Zero; + UCHAR Msf[3]; +} toc_entry; + +typedef struct _toc_data +{ + UCHAR Length[2]; + UCHAR FirstCompleteSession; + UCHAR LastCompleteSession; + + toc_entry Descriptors[255]; +} toc_data; + +extern toc_data cdtoc; + +class Source //abstract class as base for source modules +{ + Source(Source&); +public: + Source(){}; + + //virtual destructor + virtual ~Source() + { + } + + //virtual members + virtual s32 GetSectorCount()=0; + virtual s32 ReadTOC(char *toc,int size)=0; + virtual s32 ReadSectors2048(u32 sector, u32 count, char *buffer)=0; + virtual s32 ReadSectors2352(u32 sector, u32 count, char *buffer)=0; + virtual s32 GetLayerBreakAddress()=0; + + virtual s32 GetMediaType()=0; + + virtual s32 IsOK()=0; + virtual s32 Reopen()=0; + + virtual s32 DiscChanged()=0; +}; + +class IOCtlSrc: public Source +{ + IOCtlSrc(IOCtlSrc&); + + HANDLE device; + + bool OpenOK; + + s32 last_read_mode; + + s32 last_sector_count; + + char sectorbuffer[32*2048]; + + char fName[256]; + + DWORD sessID; + + bool tocCached; + char tocCacheData[2048]; + + bool mediaTypeCached; + int mediaType; + + bool discSizeCached; + s32 discSize; + + bool layerBreakCached; + s32 layerBreak; + +public: + IOCtlSrc(const char* fileName); + + //virtual destructor + virtual ~IOCtlSrc(); + + //virtual members + virtual s32 GetSectorCount(); + virtual s32 ReadTOC(char *toc,int size); + virtual s32 ReadSectors2048(u32 sector, u32 count, char *buffer); + virtual s32 ReadSectors2352(u32 sector, u32 count, char *buffer); + virtual s32 GetLayerBreakAddress(); + + virtual s32 GetMediaType(); + + virtual s32 IsOK(); + virtual s32 Reopen(); + + virtual s32 DiscChanged(); +}; + +extern Source *src; + +Source* TryLoaders(const char* fileName); + +int FindDiskType(); + +void configure(); + +extern char source_drive; +extern char source_file[]; + +extern HINSTANCE hinst; + +#define MSF_TO_LBA(m,s,f) ((m*60+s)*75+f-150) + +s32 cdvdDirectReadSector(s32 first, s32 mode, char *buffer); + +s32 cdvdGetMediaType(); + +void ReadSettings(); +void WriteSettings(); +void CfgSetSettingsDir( const char* dir ); + +extern char csrc[]; +extern BOOL cdvd_is_open; +extern int weAreInNewDiskCB; + +extern void (*newDiscCB)(); + +extern s32 disc_has_changed; + +s32 cdvdStartThread(); +void cdvdStopThread(); +s32 cdvdRequestSector(u32 sector, s32 mode); +s32 cdvdRequestComplete(); +char* cdvdGetSector(s32 sector, s32 mode); +s32 cdvdDirectReadSector(s32 first, s32 mode, char *buffer); +s32 cdvdGetMediaType(); +s32 cdvdRefreshData(); +s32 cdvdParseTOC(); + +#endif /* __CDVD_H__ */ diff --git a/plugins/cdvdGigaherz/src/FileStream.cpp b/plugins/cdvdGigaherz/src/FileStream.cpp index c8e12455b6..57b1b0ee9a 100644 --- a/plugins/cdvdGigaherz/src/FileStream.cpp +++ b/plugins/cdvdGigaherz/src/FileStream.cpp @@ -1,133 +1,133 @@ -#include -#include "CDVD.h" -#include "FileStream.h" - -using namespace std; - -FileStream::FileStream(const char* fileName) -{ - struct __stat64 stat_info; - _stat64(fileName, &stat_info); - fileSize = stat_info.st_size; - - handle = fopen(fileName, "rb"); -} - -void FileStream::seek(s64 offset) -{ - seek(offset,SEEK_SET); -} - -void FileStream::seek(s64 offset, int ref_position) -{ - int ret = _fseeki64(handle, offset, SEEK_SET); -#ifdef __LINUX__ - if (ret) throw "Seek offset out of bounds."; -#else - if (ret) - throw new exception("Seek offset out of bounds."); -#endif -} - -void FileStream::reset() -{ - seek(0); -} - -s64 FileStream::skip(s64 n) -{ - s64 before = getFilePointer(); - seek(n,SEEK_CUR); - - return getFilePointer() - before; -} - -s64 FileStream::getFilePointer() -{ - return _ftelli64(handle); -} - -bool FileStream::eof() -{ - return feof(handle)!=0; -} - -int FileStream::read() -{ - return fgetc(handle); -} - -int FileStream::read(byte* b, int len) -{ - if (b == NULL) - { -#ifdef __LINUX__ - throw "NULL buffer passed."; -#else - throw new exception("NULL buffer passed."); -#endif - } - - if (len < 0) - { -#ifdef __LINUX__ - throw "off<0 or len<0."; -#else - throw new exception("off<0 or len<0."); -#endif - } - - return fread(b,1,len,handle); -} - -string FileStream::readLine() -{ - string s; - char c; - - s.clear(); - do { - if(eof()) - break; - - c = read(); - - if((c=='\n')||(c=='\r')||(c==0)) - break; - - s.append(1,c); - } while(true); - - return s; -} - -wstring FileStream::readLineW() -{ - wstring s; - wchar_t c; - - s.clear(); - do { - if(eof()) - break; - - c = read(); - - if((c==L'\n')||(c==L'\r')||(c==0)) - break; - - s.append(1,c); - } while(true); - - return s; -} - -s64 FileStream::getLength() -{ - return fileSize; -} - -FileStream::~FileStream(void) -{ - fclose(handle); -} +#include +#include "CDVD.h" +#include "FileStream.h" + +using namespace std; + +FileStream::FileStream(const char* fileName) +{ + struct __stat64 stat_info; + _stat64(fileName, &stat_info); + fileSize = stat_info.st_size; + + handle = fopen(fileName, "rb"); +} + +void FileStream::seek(s64 offset) +{ + seek(offset,SEEK_SET); +} + +void FileStream::seek(s64 offset, int ref_position) +{ + int ret = _fseeki64(handle, offset, SEEK_SET); +#ifdef __LINUX__ + if (ret) throw "Seek offset out of bounds."; +#else + if (ret) + throw new exception("Seek offset out of bounds."); +#endif +} + +void FileStream::reset() +{ + seek(0); +} + +s64 FileStream::skip(s64 n) +{ + s64 before = getFilePointer(); + seek(n,SEEK_CUR); + + return getFilePointer() - before; +} + +s64 FileStream::getFilePointer() +{ + return _ftelli64(handle); +} + +bool FileStream::eof() +{ + return feof(handle)!=0; +} + +int FileStream::read() +{ + return fgetc(handle); +} + +int FileStream::read(byte* b, int len) +{ + if (b == NULL) + { +#ifdef __LINUX__ + throw "NULL buffer passed."; +#else + throw new exception("NULL buffer passed."); +#endif + } + + if (len < 0) + { +#ifdef __LINUX__ + throw "off<0 or len<0."; +#else + throw new exception("off<0 or len<0."); +#endif + } + + return fread(b,1,len,handle); +} + +string FileStream::readLine() +{ + string s; + char c; + + s.clear(); + do { + if(eof()) + break; + + c = read(); + + if((c=='\n')||(c=='\r')||(c==0)) + break; + + s.append(1,c); + } while(true); + + return s; +} + +wstring FileStream::readLineW() +{ + wstring s; + wchar_t c; + + s.clear(); + do { + if(eof()) + break; + + c = read(); + + if((c==L'\n')||(c==L'\r')||(c==0)) + break; + + s.append(1,c); + } while(true); + + return s; +} + +s64 FileStream::getLength() +{ + return fileSize; +} + +FileStream::~FileStream(void) +{ + fclose(handle); +} diff --git a/plugins/cdvdGigaherz/src/FileStream.h b/plugins/cdvdGigaherz/src/FileStream.h index f9dcfa3c15..6331e7c560 100644 --- a/plugins/cdvdGigaherz/src/FileStream.h +++ b/plugins/cdvdGigaherz/src/FileStream.h @@ -1,51 +1,51 @@ -#pragma once - -#include -#include - -typedef unsigned char byte; - -class FileStream -{ - int internalRead(byte* b, int off, int len); - - FILE* handle; - s64 fileSize; - - FileStream(FileStream&); -public: - - FileStream(const char* fileName); - - virtual void seek(s64 offset); - virtual void seek(s64 offset, int ref_position); - virtual void reset(); - - virtual s64 skip(s64 n); - virtual s64 getFilePointer(); - - virtual bool eof(); - virtual int read(); - virtual int read(byte* b, int len); - - // Tool to read a specific value type, including structs. - template - T read() - { - if(sizeof(T)==1) - return (T)read(); - else - { - T t; - read((byte*)&t,sizeof(t)); - return t; - } - } - - virtual std::string readLine(); // Assume null-termination - virtual std::wstring readLineW(); // (this one too) - - virtual s64 getLength(); - - virtual ~FileStream(void); -}; +#pragma once + +#include +#include + +typedef unsigned char byte; + +class FileStream +{ + int internalRead(byte* b, int off, int len); + + FILE* handle; + s64 fileSize; + + FileStream(FileStream&); +public: + + FileStream(const char* fileName); + + virtual void seek(s64 offset); + virtual void seek(s64 offset, int ref_position); + virtual void reset(); + + virtual s64 skip(s64 n); + virtual s64 getFilePointer(); + + virtual bool eof(); + virtual int read(); + virtual int read(byte* b, int len); + + // Tool to read a specific value type, including structs. + template + T read() + { + if(sizeof(T)==1) + return (T)read(); + else + { + T t; + read((byte*)&t,sizeof(t)); + return t; + } + } + + virtual std::string readLine(); // Assume null-termination + virtual std::wstring readLineW(); // (this one too) + + virtual s64 getLength(); + + virtual ~FileStream(void); +}; diff --git a/plugins/cdvdGigaherz/src/PlainIso.cpp b/plugins/cdvdGigaherz/src/PlainIso.cpp index c12ae13408..9793c5a2b9 100644 --- a/plugins/cdvdGigaherz/src/PlainIso.cpp +++ b/plugins/cdvdGigaherz/src/PlainIso.cpp @@ -1,214 +1,214 @@ -#undef _WIN32_WINNT -#define _WIN32_WINNT 0x0600 -#include "CDVD.h" -#pragma warning(disable:4200) -#pragma pack(1) -#include -#include "rosddk/ntddcdvd.h" -#include "rosddk/ntddcdrm.h" -#include "rosddk/ntddscsi.h" -#pragma warning(default:4200) -#include - -#include "ReaderModules.h" -#include "SectorConverters.h" - -#include - -#define RETURN(v) {OpenOK=v; return;} - -s32 PlainIso::Reopen() -{ - if(fileSource) - { - delete fileSource; - fileSource = NULL; - } - - OpenOK = false; - - fileSource = new FileStream(fName); - - tocCached = false; - mediaTypeCached = false; - discSizeCached = false; - layerBreakCached = false; - - OpenOK=true; - return 0; -} - -PlainIso::PlainIso(const char* fileName) -{ - fileSource=NULL; - - strcpy_s(fName,256,fileName); - - Reopen(); -} - -PlainIso::~PlainIso() -{ - if(fileSource && OpenOK) - { - delete fileSource; - } -} - -s32 PlainIso::GetSectorCount() -{ - int plain_sectors = 0; - - if(discSizeCached) - return discSize; - - s64 length = fileSource->getLength(); - - discSizeCached = true; - discSize = (s32)(length / 2048); - return discSize; -} - -s32 PlainIso::GetLayerBreakAddress() -{ - return 0; -} - -s32 PlainIso::GetMediaType() -{ - if(mediaTypeCached) - return mediaType; - - // assume single layer DVD, for now - mediaTypeCached = true; - mediaType = 0; - return mediaType; -} - -#define MKDESCRIPTOR(n,ses,ctr,adr,pt,me,se,fe,tm,ts,tf) \ - ftd->Descriptors[n].SessionNumber = ses; \ - ftd->Descriptors[n].Control = ctr; \ - ftd->Descriptors[n].Adr = adr; \ - ftd->Descriptors[n].Reserved1 = 0; \ - ftd->Descriptors[n].Point = pt; \ - ftd->Descriptors[n].MsfExtra[0] = me; \ - ftd->Descriptors[n].MsfExtra[1] = se; \ - ftd->Descriptors[n].MsfExtra[2] = fe; \ - ftd->Descriptors[n].Zero = 0; \ - ftd->Descriptors[n].Msf[0] = tm; \ - ftd->Descriptors[n].Msf[1] = ts; \ - ftd->Descriptors[n].Msf[2] = tf; - -#define MSF_TO_LBA(m,s,f) ((m*60+s)*75+f-150) -#define LBA_TO_MSF(m,s,f,l) \ - m = (l)/(75*60); \ - s = (((l)/75)%60); \ - f = ((l)%75); - - -s32 PlainIso::ReadTOC(char *toc,int msize) -{ - DWORD size=0; - - if(GetMediaType()>=0) - return -1; - - if(!tocCached) - { - CDROM_TOC_FULL_TOC_DATA *ftd=(CDROM_TOC_FULL_TOC_DATA*)tocCacheData; - - // Build basic TOC - int length = 6 * sizeof(ftd->Descriptors[0]) + 2; - ftd->Length[0] = length>>8; - ftd->Length[1] = length; - ftd->FirstCompleteSession=1; - ftd->LastCompleteSession=2; - - int dm,ds,df; - - LBA_TO_MSF(dm,ds,df,sector_count); - - MKDESCRIPTOR(0,1,0x00,1,0xA0,0,0,0,1,0,0); - MKDESCRIPTOR(1,1,0x00,1,0xA1,0,0,0,1,0,0); - MKDESCRIPTOR(2,1,0x00,1,0xA2,0,0,0,dm,ds,df); - MKDESCRIPTOR(3,1,0x00,5,0xB0,0,0,0,0,0,0); - MKDESCRIPTOR(4,1,0x00,5,0xC0,0,0,0,0,0,0); - MKDESCRIPTOR(5,1,0x04,1,0x01,0,0,0,0,2,0); - - tocCached = true; - } - - memcpy(toc,tocCacheData,min(2048,msize)); - - return 0; -} - -s32 PlainIso::ReadSectors2048(u32 sector, u32 count, char *buffer) -{ - if(!OpenOK) return -1; - - fileSource->seek(sector*(u64)2048); - - s32 bytes = count * 2048; - s32 size = fileSource->read((byte*)buffer,bytes); - - if(size!=bytes) - { - return -1; - } - - return 0; -} - - -s32 PlainIso::ReadSectors2352(u32 sector, u32 count, char *buffer) -{ - if(!OpenOK) return -1; - - fileSource->seek(sector*(u64)2352); - - for(uint i=0;iread((byte*)sectorbuffer,2352); - - if(size!=2352) - { - return -1; - } - - Convert<2048,2352>(buffer+2048*i,sectorbuffer,sector+i); - } - - return 0; -} - -s32 PlainIso::DiscChanged() -{ - DWORD size=0; - - if(!OpenOK) return -1; - - return 0; -} - -s32 PlainIso::IsOK() -{ - return OpenOK; -} - -Reader* PlainIso::TryLoad(const char* fName) -{ - std::string fileName = fName; - std::string::size_type pos = fileName.find_last_of('.'); - - if(pos == std::string::npos) // no "." found, error. - return NULL; - - std::string extension = fileName.substr(pos); - - if((extension.compare(".iso")!=0)&& - (extension.compare(".ISO")!=0)) // assume valid - return NULL; - - return new PlainIso(fName); +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x0600 +#include "CDVD.h" +#pragma warning(disable:4200) +#pragma pack(1) +#include +#include "rosddk/ntddcdvd.h" +#include "rosddk/ntddcdrm.h" +#include "rosddk/ntddscsi.h" +#pragma warning(default:4200) +#include + +#include "ReaderModules.h" +#include "SectorConverters.h" + +#include + +#define RETURN(v) {OpenOK=v; return;} + +s32 PlainIso::Reopen() +{ + if(fileSource) + { + delete fileSource; + fileSource = NULL; + } + + OpenOK = false; + + fileSource = new FileStream(fName); + + tocCached = false; + mediaTypeCached = false; + discSizeCached = false; + layerBreakCached = false; + + OpenOK=true; + return 0; +} + +PlainIso::PlainIso(const char* fileName) +{ + fileSource=NULL; + + strcpy_s(fName,256,fileName); + + Reopen(); +} + +PlainIso::~PlainIso() +{ + if(fileSource && OpenOK) + { + delete fileSource; + } +} + +s32 PlainIso::GetSectorCount() +{ + int plain_sectors = 0; + + if(discSizeCached) + return discSize; + + s64 length = fileSource->getLength(); + + discSizeCached = true; + discSize = (s32)(length / 2048); + return discSize; +} + +s32 PlainIso::GetLayerBreakAddress() +{ + return 0; +} + +s32 PlainIso::GetMediaType() +{ + if(mediaTypeCached) + return mediaType; + + // assume single layer DVD, for now + mediaTypeCached = true; + mediaType = 0; + return mediaType; +} + +#define MKDESCRIPTOR(n,ses,ctr,adr,pt,me,se,fe,tm,ts,tf) \ + ftd->Descriptors[n].SessionNumber = ses; \ + ftd->Descriptors[n].Control = ctr; \ + ftd->Descriptors[n].Adr = adr; \ + ftd->Descriptors[n].Reserved1 = 0; \ + ftd->Descriptors[n].Point = pt; \ + ftd->Descriptors[n].MsfExtra[0] = me; \ + ftd->Descriptors[n].MsfExtra[1] = se; \ + ftd->Descriptors[n].MsfExtra[2] = fe; \ + ftd->Descriptors[n].Zero = 0; \ + ftd->Descriptors[n].Msf[0] = tm; \ + ftd->Descriptors[n].Msf[1] = ts; \ + ftd->Descriptors[n].Msf[2] = tf; + +#define MSF_TO_LBA(m,s,f) ((m*60+s)*75+f-150) +#define LBA_TO_MSF(m,s,f,l) \ + m = (l)/(75*60); \ + s = (((l)/75)%60); \ + f = ((l)%75); + + +s32 PlainIso::ReadTOC(char *toc,int msize) +{ + DWORD size=0; + + if(GetMediaType()>=0) + return -1; + + if(!tocCached) + { + CDROM_TOC_FULL_TOC_DATA *ftd=(CDROM_TOC_FULL_TOC_DATA*)tocCacheData; + + // Build basic TOC + int length = 6 * sizeof(ftd->Descriptors[0]) + 2; + ftd->Length[0] = length>>8; + ftd->Length[1] = length; + ftd->FirstCompleteSession=1; + ftd->LastCompleteSession=2; + + int dm,ds,df; + + LBA_TO_MSF(dm,ds,df,sector_count); + + MKDESCRIPTOR(0,1,0x00,1,0xA0,0,0,0,1,0,0); + MKDESCRIPTOR(1,1,0x00,1,0xA1,0,0,0,1,0,0); + MKDESCRIPTOR(2,1,0x00,1,0xA2,0,0,0,dm,ds,df); + MKDESCRIPTOR(3,1,0x00,5,0xB0,0,0,0,0,0,0); + MKDESCRIPTOR(4,1,0x00,5,0xC0,0,0,0,0,0,0); + MKDESCRIPTOR(5,1,0x04,1,0x01,0,0,0,0,2,0); + + tocCached = true; + } + + memcpy(toc,tocCacheData,min(2048,msize)); + + return 0; +} + +s32 PlainIso::ReadSectors2048(u32 sector, u32 count, char *buffer) +{ + if(!OpenOK) return -1; + + fileSource->seek(sector*(u64)2048); + + s32 bytes = count * 2048; + s32 size = fileSource->read((byte*)buffer,bytes); + + if(size!=bytes) + { + return -1; + } + + return 0; +} + + +s32 PlainIso::ReadSectors2352(u32 sector, u32 count, char *buffer) +{ + if(!OpenOK) return -1; + + fileSource->seek(sector*(u64)2352); + + for(uint i=0;iread((byte*)sectorbuffer,2352); + + if(size!=2352) + { + return -1; + } + + Convert<2048,2352>(buffer+2048*i,sectorbuffer,sector+i); + } + + return 0; +} + +s32 PlainIso::DiscChanged() +{ + DWORD size=0; + + if(!OpenOK) return -1; + + return 0; +} + +s32 PlainIso::IsOK() +{ + return OpenOK; +} + +Reader* PlainIso::TryLoad(const char* fName) +{ + std::string fileName = fName; + std::string::size_type pos = fileName.find_last_of('.'); + + if(pos == std::string::npos) // no "." found, error. + return NULL; + + std::string extension = fileName.substr(pos); + + if((extension.compare(".iso")!=0)&& + (extension.compare(".ISO")!=0)) // assume valid + return NULL; + + return new PlainIso(fName); } \ No newline at end of file diff --git a/plugins/cdvdGigaherz/src/ReadThread.cpp b/plugins/cdvdGigaherz/src/ReadThread.cpp index f6eaa50ef9..5a55aca0af 100644 --- a/plugins/cdvdGigaherz/src/ReadThread.cpp +++ b/plugins/cdvdGigaherz/src/ReadThread.cpp @@ -1,444 +1,444 @@ -#include "CDVD.h" - -const s32 prefetch_max_blocks=16; -s32 prefetch_mode=0; -s32 prefetch_last_lba=0; -s32 prefetch_last_mode=0; -s32 prefetch_left=0; - -HANDLE hNotify = INVALID_HANDLE_VALUE; -HANDLE hThread = INVALID_HANDLE_VALUE; -HANDLE hRequestComplete = INVALID_HANDLE_VALUE; - -CRITICAL_SECTION CacheMutex; - -DWORD pidThread= 0; - -enum loadStatus -{ - LoadIdle, - LoadPending, - LoadSuccess, -}; - -typedef struct -{ - int lsn; - int mode; - char data[2352*16]; //we will read in blocks of 16 sectors -} SectorInfo; - -//bits: 8 would use 1<<8 entries, or 256*16 sectors -#define CACHE_SIZE 10 - -const s32 CacheSize=(1<=0) - { - t^=lsn&m; - lsn>>=CACHE_SIZE; - i-=CACHE_SIZE; - } - - return (t^mode)&m; -} - -void cdvdCacheUpdate(int lsn, int mode, char* data) -{ - EnterCriticalSection( &CacheMutex ); - u32 entry = cdvdSectorHash(lsn,mode); - - memcpy(Cache[entry].data,data,2352*16); - Cache[entry].lsn = lsn; - Cache[entry].mode = mode; - LeaveCriticalSection( &CacheMutex ); -} - -bool cdvdCacheFetch(int lsn, int mode, char* data) -{ - EnterCriticalSection( &CacheMutex ); - u32 entry = cdvdSectorHash(lsn,mode); - - if((Cache[entry].lsn==lsn) && - (Cache[entry].mode==mode)) - { - memcpy(data,Cache[entry].data,2352*16); - LeaveCriticalSection( &CacheMutex ); - return true; - } - - LeaveCriticalSection( &CacheMutex ); - return false; -} - -void cdvdCacheReset() -{ - EnterCriticalSection( &CacheMutex ); - for(int i=0;iDiscChanged(); - - if(change==-1) //error getting status (no disc in drive?) - { - //try to recreate the device - src->Reopen(); - - if(src->IsOK()) - { - change = 1; - } - else - { - curDiskType=CDVD_TYPE_NODISC; - curTrayStatus=CDVD_TRAY_OPEN; - return true; - } - } - - if(change==1) - { - if(!disc_has_changed) - { - disc_has_changed=1; - curDiskType=CDVD_TYPE_NODISC; - curTrayStatus=CDVD_TRAY_OPEN; - cdvdCallNewDiscCB(); - } - } - else - { - if(disc_has_changed) - { - curDiskType=CDVD_TYPE_NODISC; - curTrayStatus=CDVD_TRAY_CLOSE; - - // just a test - src->Reopen(); - - disc_has_changed=0; - cdvdRefreshData(); - cdvdCallNewDiscCB(); - } - } - return (change!=0); -} - -DWORD CALLBACK cdvdThread(PVOID param) -{ - printf(" * CDVD: IO thread started...\n"); - - while(cdvd_is_open) - { - DWORD f=0; - - if(!src) break; - - if(cdvdUpdateDiscStatus()) - { - // Need to sleep some to avoid an aggressive spin that sucks the cpu dry. - Sleep( 10 ); - continue; - } - - if(prefetch_left) - WaitForSingleObject(hNotify,1); - else - WaitForSingleObject(hNotify,250); - - // check again to make sure we're not done here... - if(!cdvd_is_open) break; - - static SectorInfo info; - - bool handlingRequest = false; - - if(threadRequestPending) - { - info=threadRequestInfo; - handlingRequest = true; - } - else - { - info.lsn = prefetch_last_lba; - info.mode = prefetch_last_mode; - } - - if(threadRequestPending || prefetch_left) - { - s32 ret = -1; - s32 tries=5; - - s32 count = 16; - - s32 left = tracks[0].length-info.lsn; - - if(leftReadSectors2048(info.lsn,count,info.data); - else - ret = src->ReadSectors2352(info.lsn,count,info.data); - - if(ret==0) - break; - - tries--; - - } while((ret<0)&&(tries>0)); - - cdvdCacheUpdate(info.lsn,info.mode,info.data); - - if(handlingRequest) - { - threadRequestInfo = info; - - handlingRequest = false; - threadRequestPending = false; - PulseEvent(hRequestComplete); - - prefetch_last_lba=info.lsn; - prefetch_last_mode=info.mode; - - prefetch_left = prefetch_max_blocks; - } - else - { - prefetch_last_lba+=16; - prefetch_left--; - } - } - } - printf(" * CDVD: IO thread finished.\n"); - return 0; -} - -s32 cdvdStartThread() -{ - InitializeCriticalSection( &CacheMutex ); - - hNotify = CreateEvent(NULL,FALSE,FALSE,NULL); - if(hNotify==INVALID_HANDLE_VALUE) - return -1; - - hRequestComplete = CreateEvent(NULL,FALSE,FALSE,NULL); - if(hRequestComplete==INVALID_HANDLE_VALUE) - return -1; - - cdvd_is_open=true; - hThread = CreateThread(NULL,0,cdvdThread,NULL,0,&pidThread); - - if(hThread==INVALID_HANDLE_VALUE) - return -1; - - SetThreadPriority(hThread,THREAD_PRIORITY_NORMAL); - - cdvdCacheReset(); - - return 0; -} - -void cdvdStopThread() -{ - cdvd_is_open=false; - PulseEvent(hNotify); - if(WaitForSingleObject(hThread,4000)==WAIT_TIMEOUT) - { - TerminateThread(hThread,0); - } - CloseHandle(hThread); - CloseHandle(hNotify); - CloseHandle(hRequestComplete); - - DeleteCriticalSection( &CacheMutex ); -} - -s32 cdvdRequestSector(u32 sector, s32 mode) -{ - if(sector>=tracks[0].length) - return -1; - - sector&=~15; //align to 16-sector block - - threadRequestInfo.lsn = sector; - threadRequestInfo.mode = mode; - threadRequestPending = false; - if(cdvdCacheFetch(sector,mode,threadRequestInfo.data)) - { - return 0; - } - - threadRequestPending = true; - ResetEvent(hRequestComplete); - PulseEvent(hNotify); - - return 0; -} - -s32 cdvdRequestComplete() -{ - return !threadRequestPending; -} - -s8* cdvdGetSector(s32 sector, s32 mode) -{ - while(threadRequestPending) - { - WaitForSingleObject( hRequestComplete, 10 ); - } - - s32 offset; - - if(mode==CDVD_MODE_2048) - { - offset = 2048*(sector-threadRequestInfo.lsn); - return threadRequestInfo.data + offset; - } - - offset = 2352*(sector-threadRequestInfo.lsn); - s8* data = threadRequestInfo.data + offset; - - switch(mode) - { - case CDVD_MODE_2328: - return data + 24; - case CDVD_MODE_2340: - return data + 12; - } - return data; -} - -s32 cdvdDirectReadSector(s32 first, s32 mode, char *buffer) -{ - static char data[16*2352]; - - if((u32)first>=tracks[0].length) - return -1; - - s32 sector = first&(~15); //align to 16-sector block - - EnterCriticalSection( &CacheMutex ); - if(!cdvdCacheFetch(sector,mode,data)) - { - s32 ret = -1; - s32 tries=5; - - s32 count = 16; - - s32 left = tracks[0].length-sector; - - if(leftReadSectors2048(sector,count,data); - else - ret = src->ReadSectors2352(sector,count,data); - - if(ret==0) - break; - - tries--; - - } while((ret<0)&&(tries>0)); - - cdvdCacheUpdate(sector,mode,data); - } - LeaveCriticalSection( &CacheMutex ); - - s32 offset; - - if(mode==CDVD_MODE_2048) - { - offset = 2048*(first-sector); - memcpy(buffer,data + offset,2048); - return 0; - } - - offset = 2352*(first-sector); - s8* bfr = data + offset; - - switch(mode) - { - case CDVD_MODE_2328: - memcpy(buffer,bfr+24,2328); - return 0; - case CDVD_MODE_2340: - memcpy(buffer,bfr+12,2340); - return 0; - default: - memcpy(buffer,bfr+12,2352); - return 0; - } - return 0; -} - -s32 cdvdGetMediaType() -{ - return src->GetMediaType(); -} - -s32 refreshes=0; - -s32 cdvdRefreshData() -{ - char *diskTypeName="Unknown"; - - //read TOC from device - cdvdParseTOC(); - - if((etrack==0)||(strack>etrack)) - { - curDiskType=CDVD_TYPE_NODISC; - } - else - { - s32 mt=cdvdGetMediaType(); - - if(mt<0) curDiskType = CDVD_TYPE_DETCTCD; - else if(mt == 0) curDiskType = CDVD_TYPE_DETCTDVDS; - else curDiskType = CDVD_TYPE_DETCTDVDD; - } - - curTrayStatus = CDVD_TRAY_CLOSE; - - switch(curDiskType) - { - case CDVD_TYPE_DETCTDVDD: diskTypeName="Single-Layer DVD"; break; - case CDVD_TYPE_DETCTDVDS: diskTypeName="Double-Layer DVD"; break; - case CDVD_TYPE_DETCTCD: diskTypeName="CD-ROM"; break; - case CDVD_TYPE_NODISC: diskTypeName="No Disc"; break; - } - - printf(" * CDVD: Disk Type: %s\n",diskTypeName); - - cdvdCacheReset(); - - return 0; -} +#include "CDVD.h" + +const s32 prefetch_max_blocks=16; +s32 prefetch_mode=0; +s32 prefetch_last_lba=0; +s32 prefetch_last_mode=0; +s32 prefetch_left=0; + +HANDLE hNotify = INVALID_HANDLE_VALUE; +HANDLE hThread = INVALID_HANDLE_VALUE; +HANDLE hRequestComplete = INVALID_HANDLE_VALUE; + +CRITICAL_SECTION CacheMutex; + +DWORD pidThread= 0; + +enum loadStatus +{ + LoadIdle, + LoadPending, + LoadSuccess, +}; + +typedef struct +{ + int lsn; + int mode; + char data[2352*16]; //we will read in blocks of 16 sectors +} SectorInfo; + +//bits: 8 would use 1<<8 entries, or 256*16 sectors +#define CACHE_SIZE 10 + +const s32 CacheSize=(1<=0) + { + t^=lsn&m; + lsn>>=CACHE_SIZE; + i-=CACHE_SIZE; + } + + return (t^mode)&m; +} + +void cdvdCacheUpdate(int lsn, int mode, char* data) +{ + EnterCriticalSection( &CacheMutex ); + u32 entry = cdvdSectorHash(lsn,mode); + + memcpy(Cache[entry].data,data,2352*16); + Cache[entry].lsn = lsn; + Cache[entry].mode = mode; + LeaveCriticalSection( &CacheMutex ); +} + +bool cdvdCacheFetch(int lsn, int mode, char* data) +{ + EnterCriticalSection( &CacheMutex ); + u32 entry = cdvdSectorHash(lsn,mode); + + if((Cache[entry].lsn==lsn) && + (Cache[entry].mode==mode)) + { + memcpy(data,Cache[entry].data,2352*16); + LeaveCriticalSection( &CacheMutex ); + return true; + } + + LeaveCriticalSection( &CacheMutex ); + return false; +} + +void cdvdCacheReset() +{ + EnterCriticalSection( &CacheMutex ); + for(int i=0;iDiscChanged(); + + if(change==-1) //error getting status (no disc in drive?) + { + //try to recreate the device + src->Reopen(); + + if(src->IsOK()) + { + change = 1; + } + else + { + curDiskType=CDVD_TYPE_NODISC; + curTrayStatus=CDVD_TRAY_OPEN; + return true; + } + } + + if(change==1) + { + if(!disc_has_changed) + { + disc_has_changed=1; + curDiskType=CDVD_TYPE_NODISC; + curTrayStatus=CDVD_TRAY_OPEN; + cdvdCallNewDiscCB(); + } + } + else + { + if(disc_has_changed) + { + curDiskType=CDVD_TYPE_NODISC; + curTrayStatus=CDVD_TRAY_CLOSE; + + // just a test + src->Reopen(); + + disc_has_changed=0; + cdvdRefreshData(); + cdvdCallNewDiscCB(); + } + } + return (change!=0); +} + +DWORD CALLBACK cdvdThread(PVOID param) +{ + printf(" * CDVD: IO thread started...\n"); + + while(cdvd_is_open) + { + DWORD f=0; + + if(!src) break; + + if(cdvdUpdateDiscStatus()) + { + // Need to sleep some to avoid an aggressive spin that sucks the cpu dry. + Sleep( 10 ); + continue; + } + + if(prefetch_left) + WaitForSingleObject(hNotify,1); + else + WaitForSingleObject(hNotify,250); + + // check again to make sure we're not done here... + if(!cdvd_is_open) break; + + static SectorInfo info; + + bool handlingRequest = false; + + if(threadRequestPending) + { + info=threadRequestInfo; + handlingRequest = true; + } + else + { + info.lsn = prefetch_last_lba; + info.mode = prefetch_last_mode; + } + + if(threadRequestPending || prefetch_left) + { + s32 ret = -1; + s32 tries=5; + + s32 count = 16; + + s32 left = tracks[0].length-info.lsn; + + if(leftReadSectors2048(info.lsn,count,info.data); + else + ret = src->ReadSectors2352(info.lsn,count,info.data); + + if(ret==0) + break; + + tries--; + + } while((ret<0)&&(tries>0)); + + cdvdCacheUpdate(info.lsn,info.mode,info.data); + + if(handlingRequest) + { + threadRequestInfo = info; + + handlingRequest = false; + threadRequestPending = false; + PulseEvent(hRequestComplete); + + prefetch_last_lba=info.lsn; + prefetch_last_mode=info.mode; + + prefetch_left = prefetch_max_blocks; + } + else + { + prefetch_last_lba+=16; + prefetch_left--; + } + } + } + printf(" * CDVD: IO thread finished.\n"); + return 0; +} + +s32 cdvdStartThread() +{ + InitializeCriticalSection( &CacheMutex ); + + hNotify = CreateEvent(NULL,FALSE,FALSE,NULL); + if(hNotify==INVALID_HANDLE_VALUE) + return -1; + + hRequestComplete = CreateEvent(NULL,FALSE,FALSE,NULL); + if(hRequestComplete==INVALID_HANDLE_VALUE) + return -1; + + cdvd_is_open=true; + hThread = CreateThread(NULL,0,cdvdThread,NULL,0,&pidThread); + + if(hThread==INVALID_HANDLE_VALUE) + return -1; + + SetThreadPriority(hThread,THREAD_PRIORITY_NORMAL); + + cdvdCacheReset(); + + return 0; +} + +void cdvdStopThread() +{ + cdvd_is_open=false; + PulseEvent(hNotify); + if(WaitForSingleObject(hThread,4000)==WAIT_TIMEOUT) + { + TerminateThread(hThread,0); + } + CloseHandle(hThread); + CloseHandle(hNotify); + CloseHandle(hRequestComplete); + + DeleteCriticalSection( &CacheMutex ); +} + +s32 cdvdRequestSector(u32 sector, s32 mode) +{ + if(sector>=tracks[0].length) + return -1; + + sector&=~15; //align to 16-sector block + + threadRequestInfo.lsn = sector; + threadRequestInfo.mode = mode; + threadRequestPending = false; + if(cdvdCacheFetch(sector,mode,threadRequestInfo.data)) + { + return 0; + } + + threadRequestPending = true; + ResetEvent(hRequestComplete); + PulseEvent(hNotify); + + return 0; +} + +s32 cdvdRequestComplete() +{ + return !threadRequestPending; +} + +s8* cdvdGetSector(s32 sector, s32 mode) +{ + while(threadRequestPending) + { + WaitForSingleObject( hRequestComplete, 10 ); + } + + s32 offset; + + if(mode==CDVD_MODE_2048) + { + offset = 2048*(sector-threadRequestInfo.lsn); + return threadRequestInfo.data + offset; + } + + offset = 2352*(sector-threadRequestInfo.lsn); + s8* data = threadRequestInfo.data + offset; + + switch(mode) + { + case CDVD_MODE_2328: + return data + 24; + case CDVD_MODE_2340: + return data + 12; + } + return data; +} + +s32 cdvdDirectReadSector(s32 first, s32 mode, char *buffer) +{ + static char data[16*2352]; + + if((u32)first>=tracks[0].length) + return -1; + + s32 sector = first&(~15); //align to 16-sector block + + EnterCriticalSection( &CacheMutex ); + if(!cdvdCacheFetch(sector,mode,data)) + { + s32 ret = -1; + s32 tries=5; + + s32 count = 16; + + s32 left = tracks[0].length-sector; + + if(leftReadSectors2048(sector,count,data); + else + ret = src->ReadSectors2352(sector,count,data); + + if(ret==0) + break; + + tries--; + + } while((ret<0)&&(tries>0)); + + cdvdCacheUpdate(sector,mode,data); + } + LeaveCriticalSection( &CacheMutex ); + + s32 offset; + + if(mode==CDVD_MODE_2048) + { + offset = 2048*(first-sector); + memcpy(buffer,data + offset,2048); + return 0; + } + + offset = 2352*(first-sector); + s8* bfr = data + offset; + + switch(mode) + { + case CDVD_MODE_2328: + memcpy(buffer,bfr+24,2328); + return 0; + case CDVD_MODE_2340: + memcpy(buffer,bfr+12,2340); + return 0; + default: + memcpy(buffer,bfr+12,2352); + return 0; + } + return 0; +} + +s32 cdvdGetMediaType() +{ + return src->GetMediaType(); +} + +s32 refreshes=0; + +s32 cdvdRefreshData() +{ + char *diskTypeName="Unknown"; + + //read TOC from device + cdvdParseTOC(); + + if((etrack==0)||(strack>etrack)) + { + curDiskType=CDVD_TYPE_NODISC; + } + else + { + s32 mt=cdvdGetMediaType(); + + if(mt<0) curDiskType = CDVD_TYPE_DETCTCD; + else if(mt == 0) curDiskType = CDVD_TYPE_DETCTDVDS; + else curDiskType = CDVD_TYPE_DETCTDVDD; + } + + curTrayStatus = CDVD_TRAY_CLOSE; + + switch(curDiskType) + { + case CDVD_TYPE_DETCTDVDD: diskTypeName="Single-Layer DVD"; break; + case CDVD_TYPE_DETCTDVDS: diskTypeName="Double-Layer DVD"; break; + case CDVD_TYPE_DETCTCD: diskTypeName="CD-ROM"; break; + case CDVD_TYPE_NODISC: diskTypeName="No Disc"; break; + } + + printf(" * CDVD: Disk Type: %s\n",diskTypeName); + + cdvdCacheReset(); + + return 0; +} diff --git a/plugins/cdvdGigaherz/src/ReaderModules.cpp b/plugins/cdvdGigaherz/src/ReaderModules.cpp index 3d69c7ec08..8f0da54b57 100644 --- a/plugins/cdvdGigaherz/src/ReaderModules.cpp +++ b/plugins/cdvdGigaherz/src/ReaderModules.cpp @@ -1,12 +1,12 @@ -#include "CDVD.h" -#include "ReaderModules.h" - -Source* TryLoaders(const char* fileName) -{ - Source *src=NULL; - if((src=PlainIso::TryLoad(fileName))!=NULL) return src; - //if((src=CueSheet::TryLoad(fileName))!=NULL) return src; - //if((src=CloneCD::TryLoad(fileName))!=NULL) return src; - //error - return NULL; +#include "CDVD.h" +#include "ReaderModules.h" + +Source* TryLoaders(const char* fileName) +{ + Source *src=NULL; + if((src=PlainIso::TryLoad(fileName))!=NULL) return src; + //if((src=CueSheet::TryLoad(fileName))!=NULL) return src; + //if((src=CloneCD::TryLoad(fileName))!=NULL) return src; + //error + return NULL; } \ No newline at end of file diff --git a/plugins/cdvdGigaherz/src/ReaderModules.h b/plugins/cdvdGigaherz/src/ReaderModules.h index 40529e246a..d521699438 100644 --- a/plugins/cdvdGigaherz/src/ReaderModules.h +++ b/plugins/cdvdGigaherz/src/ReaderModules.h @@ -1,79 +1,79 @@ -#pragma once - -#include "FileStream.h" - -class Reader: public Source //abstract class as base for Reader modules -{ - Reader(Reader&); -public: - Reader(){}; - - //virtual destructor - virtual ~Reader() {} - - //virtual members - virtual s32 GetSectorCount()=0; - virtual s32 ReadTOC(char *toc,int size)=0; - virtual s32 ReadSectors2048(u32 sector, u32 count, char *buffer)=0; - virtual s32 ReadSectors2352(u32 sector, u32 count, char *buffer)=0; - virtual s32 GetLayerBreakAddress()=0; - - virtual s32 GetMediaType()=0; - - virtual s32 IsOK()=0; - virtual s32 Reopen()=0; - - virtual s32 DiscChanged()=0; - - //added members - static Reader* TryLoad(const char* fileName); -}; - -class PlainIso: public Reader -{ - FileStream* fileSource; - - bool OpenOK; - - s32 sector_count; - - char sectorbuffer[32*2048]; - - char fName[256]; - - DWORD sessID; - - bool tocCached; - char tocCacheData[2048]; - - bool mediaTypeCached; - int mediaType; - - bool discSizeCached; - s32 discSize; - - bool layerBreakCached; - s32 layerBreak; - - PlainIso(PlainIso&); -public: - - PlainIso(const char* fileName); - virtual ~PlainIso(); - - //virtual members - virtual s32 GetSectorCount(); - virtual s32 ReadTOC(char *toc,int size); - virtual s32 ReadSectors2048(u32 sector, u32 count, char *buffer); - virtual s32 ReadSectors2352(u32 sector, u32 count, char *buffer); - virtual s32 GetLayerBreakAddress(); - - virtual s32 GetMediaType(); - - virtual s32 IsOK(); - virtual s32 Reopen(); - - virtual s32 DiscChanged(); - - static Reader* TryLoad(const char* fileName); +#pragma once + +#include "FileStream.h" + +class Reader: public Source //abstract class as base for Reader modules +{ + Reader(Reader&); +public: + Reader(){}; + + //virtual destructor + virtual ~Reader() {} + + //virtual members + virtual s32 GetSectorCount()=0; + virtual s32 ReadTOC(char *toc,int size)=0; + virtual s32 ReadSectors2048(u32 sector, u32 count, char *buffer)=0; + virtual s32 ReadSectors2352(u32 sector, u32 count, char *buffer)=0; + virtual s32 GetLayerBreakAddress()=0; + + virtual s32 GetMediaType()=0; + + virtual s32 IsOK()=0; + virtual s32 Reopen()=0; + + virtual s32 DiscChanged()=0; + + //added members + static Reader* TryLoad(const char* fileName); +}; + +class PlainIso: public Reader +{ + FileStream* fileSource; + + bool OpenOK; + + s32 sector_count; + + char sectorbuffer[32*2048]; + + char fName[256]; + + DWORD sessID; + + bool tocCached; + char tocCacheData[2048]; + + bool mediaTypeCached; + int mediaType; + + bool discSizeCached; + s32 discSize; + + bool layerBreakCached; + s32 layerBreak; + + PlainIso(PlainIso&); +public: + + PlainIso(const char* fileName); + virtual ~PlainIso(); + + //virtual members + virtual s32 GetSectorCount(); + virtual s32 ReadTOC(char *toc,int size); + virtual s32 ReadSectors2048(u32 sector, u32 count, char *buffer); + virtual s32 ReadSectors2352(u32 sector, u32 count, char *buffer); + virtual s32 GetLayerBreakAddress(); + + virtual s32 GetMediaType(); + + virtual s32 IsOK(); + virtual s32 Reopen(); + + virtual s32 DiscChanged(); + + static Reader* TryLoad(const char* fileName); }; \ No newline at end of file diff --git a/plugins/cdvdGigaherz/src/SectorConverters.h b/plugins/cdvdGigaherz/src/SectorConverters.h index 44de8c86f7..5ddabeceee 100644 --- a/plugins/cdvdGigaherz/src/SectorConverters.h +++ b/plugins/cdvdGigaherz/src/SectorConverters.h @@ -1,40 +1,40 @@ -#pragma once - -template -int Convert(char *dest, const char* psrc, int lsn); - -template<> -int Convert<2048,2048>(char *dest, const char* psrc, int lsn) -{ - memcpy(dest,psrc,2048); - return 0; -} - -template<> -int Convert<2352,2352>(char *dest, const char* psrc, int lsn) -{ - memcpy(dest,psrc,2352); - return 0; -} - -template<> -int Convert<2352,2048>(char *dest, const char* psrc, int lsn) -{ - memcpy(dest,psrc+16,2048); - return 0; -} - -template<> -int Convert<2048,2352>(char *dest, const char* psrc, int lsn) -{ - int m = lsn / (75*60); - int s = (lsn/75) % 60; - int f = lsn%75; - unsigned char header[16] = { - 0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00, m, s, f,0x02 - }; - - memcpy(dest,header,16); - memcpy(dest+16,psrc,2048); - return 0; -} +#pragma once + +template +int Convert(char *dest, const char* psrc, int lsn); + +template<> +int Convert<2048,2048>(char *dest, const char* psrc, int lsn) +{ + memcpy(dest,psrc,2048); + return 0; +} + +template<> +int Convert<2352,2352>(char *dest, const char* psrc, int lsn) +{ + memcpy(dest,psrc,2352); + return 0; +} + +template<> +int Convert<2352,2048>(char *dest, const char* psrc, int lsn) +{ + memcpy(dest,psrc+16,2048); + return 0; +} + +template<> +int Convert<2048,2352>(char *dest, const char* psrc, int lsn) +{ + int m = lsn / (75*60); + int s = (lsn/75) % 60; + int f = lsn%75; + unsigned char header[16] = { + 0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00, m, s, f,0x02 + }; + + memcpy(dest,header,16); + memcpy(dest+16,psrc,2048); + return 0; +} diff --git a/plugins/cdvdGigaherz/src/TocStuff.cpp b/plugins/cdvdGigaherz/src/TocStuff.cpp index 85a0d54512..1a67c6b316 100644 --- a/plugins/cdvdGigaherz/src/TocStuff.cpp +++ b/plugins/cdvdGigaherz/src/TocStuff.cpp @@ -1,149 +1,149 @@ -#include "CDVD.h" - -toc_data cdtoc; - -s32 cdvdParseTOC() -{ - memset(&cdtoc,0,sizeof(cdtoc)); - - s32 len = src->GetSectorCount(); - - tracks[0].length = len; - tracks[0].start_lba=0; - tracks[0].type=0; - tracks[1].start_lba=0; - - if(len<=0) - { - curDiskType=CDVD_TYPE_NODISC; - tracks[0].length=0; - strack=1; - etrack=0; - return 0; - } - - s32 lastaddr = src->GetLayerBreakAddress(); - - - if(lastaddr>=0) - { - if((lastaddr > 0)&&(tracks[0].length>lastaddr)) - { - tracks[1].length=lastaddr+1; - tracks[1].type=0; - - tracks[2].start_lba = tracks[1].length; - tracks[2].length = tracks[0].length-tracks[1].length; - tracks[2].type=0; - - strack=1; - etrack=2; - } - else - { - tracks[1].length=tracks[0].length; - tracks[1].type=0; - - strack=1; - etrack=1; - } - } - else - { - u8 min, sec, frm; - - if(src->ReadTOC((char*)&cdtoc,sizeof(cdtoc))<0) - { - /* - printf(" * CDVD: WARNING ReadTOC() failed, trying to use MCI instead...\n"); - delete src; - int status = MCI_CDGetTOC(source_drive); - - src=new SOURCECLASS(csrc); - - if(status<0)*/ - return -1; - - //return 0; - } - -#define btoi(b) ((b>>4)*10+(b&0xF)) - - int length = (cdtoc.Length[0]<<8) | cdtoc.Length[1]; - int descriptors = length/sizeof(cdtoc.Descriptors[0]); - for(int i=0;i0)) - { - int tn=cdtoc.Descriptors[i].Point; - - min=cdtoc.Descriptors[i].Msf[0]; - sec=cdtoc.Descriptors[i].Msf[1]; - frm=cdtoc.Descriptors[i].Msf[2]; - - tracks[tn].start_lba = MSF_TO_LBA(min,sec,frm); - if(tn>1) - tracks[tn-1].length = tracks[tn].start_lba - tracks[tn-1].start_lba; - - if((cdtoc.Descriptors[i].Control&4)==0) - { - tracks[tn].type = 1; - } - else if((cdtoc.Descriptors[i].Control&0xE)==4) - { - tracks[tn].type = CDVD_MODE1_TRACK; - } - else - { - tracks[tn].type = CDVD_MODE1_TRACK; - } - - fprintf(stderr,"Track %d: %d mins %d secs %d frames\n",tn,min,sec,frm); - } - else if(cdtoc.Descriptors[i].Point>0) - { - printf("Found code 0x%02x\n",cdtoc.Descriptors[i].Point); - } - break; - } - } - - tracks[etrack].length = tracks[0].length - tracks[etrack].start_lba; - } - - return 0; -} +#include "CDVD.h" + +toc_data cdtoc; + +s32 cdvdParseTOC() +{ + memset(&cdtoc,0,sizeof(cdtoc)); + + s32 len = src->GetSectorCount(); + + tracks[0].length = len; + tracks[0].start_lba=0; + tracks[0].type=0; + tracks[1].start_lba=0; + + if(len<=0) + { + curDiskType=CDVD_TYPE_NODISC; + tracks[0].length=0; + strack=1; + etrack=0; + return 0; + } + + s32 lastaddr = src->GetLayerBreakAddress(); + + + if(lastaddr>=0) + { + if((lastaddr > 0)&&(tracks[0].length>lastaddr)) + { + tracks[1].length=lastaddr+1; + tracks[1].type=0; + + tracks[2].start_lba = tracks[1].length; + tracks[2].length = tracks[0].length-tracks[1].length; + tracks[2].type=0; + + strack=1; + etrack=2; + } + else + { + tracks[1].length=tracks[0].length; + tracks[1].type=0; + + strack=1; + etrack=1; + } + } + else + { + u8 min, sec, frm; + + if(src->ReadTOC((char*)&cdtoc,sizeof(cdtoc))<0) + { + /* + printf(" * CDVD: WARNING ReadTOC() failed, trying to use MCI instead...\n"); + delete src; + int status = MCI_CDGetTOC(source_drive); + + src=new SOURCECLASS(csrc); + + if(status<0)*/ + return -1; + + //return 0; + } + +#define btoi(b) ((b>>4)*10+(b&0xF)) + + int length = (cdtoc.Length[0]<<8) | cdtoc.Length[1]; + int descriptors = length/sizeof(cdtoc.Descriptors[0]); + for(int i=0;i0)) + { + int tn=cdtoc.Descriptors[i].Point; + + min=cdtoc.Descriptors[i].Msf[0]; + sec=cdtoc.Descriptors[i].Msf[1]; + frm=cdtoc.Descriptors[i].Msf[2]; + + tracks[tn].start_lba = MSF_TO_LBA(min,sec,frm); + if(tn>1) + tracks[tn-1].length = tracks[tn].start_lba - tracks[tn-1].start_lba; + + if((cdtoc.Descriptors[i].Control&4)==0) + { + tracks[tn].type = 1; + } + else if((cdtoc.Descriptors[i].Control&0xE)==4) + { + tracks[tn].type = CDVD_MODE1_TRACK; + } + else + { + tracks[tn].type = CDVD_MODE1_TRACK; + } + + fprintf(stderr,"Track %d: %d mins %d secs %d frames\n",tn,min,sec,frm); + } + else if(cdtoc.Descriptors[i].Point>0) + { + printf("Found code 0x%02x\n",cdtoc.Descriptors[i].Point); + } + break; + } + } + + tracks[etrack].length = tracks[0].length - tracks[etrack].start_lba; + } + + return 0; +} diff --git a/plugins/cdvdGigaherz/src/Windows/IOCtlSrc.cpp b/plugins/cdvdGigaherz/src/Windows/IOCtlSrc.cpp index c5b2561d4c..4e42371ad1 100644 --- a/plugins/cdvdGigaherz/src/Windows/IOCtlSrc.cpp +++ b/plugins/cdvdGigaherz/src/Windows/IOCtlSrc.cpp @@ -1,673 +1,673 @@ -#undef _WIN32_WINNT -#define _WIN32_WINNT 0x0600 -#include "../CDVD.h" -#pragma warning(disable:4200) -#pragma pack(1) -#include -#include "rosddk/ntddcdvd.h" -#include "rosddk/ntddcdrm.h" -#include "rosddk/ntddscsi.h" -#pragma warning(default:4200) -#include - -template -bool ApiErrorCheck(T t,T okValue,bool cmpEq) -{ - bool cond=(t==okValue); - - if(!cmpEq) cond=!cond; - - if(!cond) - { - static char buff[1024]; - DWORD ec = GetLastError(); - FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,NULL,ec,0,buff,1023,NULL); - MessageBoxEx(0,buff,"ERROR?!",MB_OK,0); - } - return cond; -} - -#if FALSE -typedef struct { - SCSI_PASS_THROUGH_DIRECT spt; - ULONG Filler; - UCHAR ucSenseBuf[32]; -} SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, *PSCSI_PASS_THROUGH_DIRECT_WITH_BUFFER; - -SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER sptIOCTL; // global read bufs - -char szBuf[1024]; - -PSCSI_ADDRESS pSA; - -DWORD SendIoCtlScsiCommand(HANDLE hDevice, u8 direction, - u8 cdbLength, u8* cdb, - u32 dataLength, void* dataBuffer - ) -{ - DWORD dwRet; - - memset(szBuf,0,1024); - - pSA=(PSCSI_ADDRESS)szBuf; - pSA->Length=sizeof(SCSI_ADDRESS); - - if(!DeviceIoControl(hDevice,IOCTL_SCSI_GET_ADDRESS,NULL, - 0,pSA,sizeof(SCSI_ADDRESS), - &dwRet,NULL)) - { - return -1; - } - - memset(&sptIOCTL,0,sizeof(sptIOCTL)); - - sptIOCTL.spt.Length = sizeof(SCSI_PASS_THROUGH_DIRECT); - sptIOCTL.spt.TimeOutValue = 60; - sptIOCTL.spt.SenseInfoLength = 14; - sptIOCTL.spt.SenseInfoOffset = offsetof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, ucSenseBuf); - - sptIOCTL.spt.PathId = pSA->PortNumber; - sptIOCTL.spt.TargetId = pSA->TargetId; - sptIOCTL.spt.Lun = pSA->Lun; - - sptIOCTL.spt.DataIn = (direction>0)?SCSI_IOCTL_DATA_IN:((direction<0)?SCSI_IOCTL_DATA_OUT:SCSI_IOCTL_DATA_UNSPECIFIED); - sptIOCTL.spt.DataTransferLength = dataLength; - sptIOCTL.spt.DataBuffer = dataBuffer; - - sptIOCTL.spt.CdbLength = cdbLength; - memcpy(sptIOCTL.spt.Cdb,cdb,cdbLength); - - DWORD code = DeviceIoControl(hDevice, - IOCTL_SCSI_PASS_THROUGH_DIRECT, - &sptIOCTL,sizeof(sptIOCTL), - &sptIOCTL,sizeof(sptIOCTL), - &dwRet,NULL); - ApiErrorCheck(code,0,false); - return code; -} - -//0x00 = PHYSICAL STRUCTURE -DWORD ScsiReadStructure(HANDLE hDevice, u32 layer, u32 format, u32 buffer_length, void* buffer) -{ - u8 cdb[12]={0}; - - - cdb[0] = 0xAD; - /* - cdb[2] = (unsigned char)((addr >> 24) & 0xFF); - cdb[3] = (unsigned char)((addr >> 16) & 0xFF); - cdb[4] = (unsigned char)((addr >> 8) & 0xFF); - cdb[5] = (unsigned char)(addr & 0xFF); - */ - cdb[6] = layer; - cdb[7] = format; - cdb[8] = (unsigned char)((buffer_length>>8) & 0xFF); - cdb[9] = (unsigned char)((buffer_length) & 0xFF); - - return SendIoCtlScsiCommand(hDevice, 1, 12, cdb, buffer_length, buffer); -} - - -DWORD ScsiReadBE_2(HANDLE hDevice, u32 addr, u32 count, u8* buffer) -{ - u8 cdb[12]={0}; - - cdb[0] = 0xBE; - cdb[2] = (unsigned char)((addr >> 24) & 0xFF); - cdb[3] = (unsigned char)((addr >> 16) & 0xFF); - cdb[4] = (unsigned char)((addr >> 8) & 0xFF); - cdb[5] = (unsigned char)(addr & 0xFF); - cdb[8] = count; - cdb[9] = 0xF8; - cdb[10] = 0x2; - - return SendIoCtlScsiCommand(hDevice, 1, 12, cdb, 2352, buffer); -} - -DWORD ScsiRead10(HANDLE hDevice, u32 addr, u32 count, u8* buffer) -{ - u8 cdb[10]={0}; - - cdb[0] = 0x28; - //cdb[1] = lun<<5; - cdb[2] = (unsigned char)((addr >> 24) & 0xFF); - cdb[3] = (unsigned char)((addr >> 16) & 0xFF); - cdb[4] = (unsigned char)((addr >> 8) & 0xFF); - cdb[5] = (unsigned char)(addr & 0xFF); - cdb[8] = count; - - return SendIoCtlScsiCommand(hDevice, 1, 10, cdb, 2352, buffer); -} - -DWORD ScsiReadTOC(HANDLE hDevice, u32 addr, u8* buffer, bool use_msf) -{ - u8 cdb[12]={0}; - - cdb[0] = 0x43; - cdb[7] = 0x03; - cdb[8] = 0x24; - - if(use_msf) cdb[1]=2; - - return SendIoCtlScsiCommand(hDevice, 1, 10, cdb, 2352, buffer); -} - -s32 IOCtlSrc::GetSectorCount() -{ - DWORD size; - - LARGE_INTEGER li; - int plain_sectors = 0; - - if(GetFileSizeEx(device,&li)) - { - return li.QuadPart / 2048; - } - - GET_LENGTH_INFORMATION info; - if(DeviceIoControl(device, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0, &info, sizeof(info), &size, NULL)) - { - return info.Length.QuadPart / 2048; - } - - CDROM_READ_TOC_EX tocrq={0}; - - tocrq.Format = CDROM_READ_TOC_EX_FORMAT_FULL_TOC; - tocrq.Msf=1; - tocrq.SessionTrack=1; - - CDROM_TOC_FULL_TOC_DATA *ftd=(CDROM_TOC_FULL_TOC_DATA*)sectorbuffer; - - if(DeviceIoControl(device,IOCTL_CDROM_READ_TOC_EX,&tocrq,sizeof(tocrq),ftd, 2048, &size, NULL)) - { - for(int i=0;i<101;i++) - { - if(ftd->Descriptors[i].Point==0xa2) - { - if(ftd->Descriptors[i].SessionNumber==ftd->LastCompleteSession) - { - int min=ftd->Descriptors[i].Msf[0]; - int sec=ftd->Descriptors[i].Msf[1]; - int frm=ftd->Descriptors[i].Msf[2]; - - return MSF_TO_LBA(min,sec,frm); - } - } - } - } - - int sectors1=-1; - - if(ScsiReadStructure(device,0,DvdPhysicalDescriptor, sizeof(dld), &dld)!=0) - { - if(dld.ld.EndLayerZeroSector>0) // OTP? - { - sectors1 = dld.ld.EndLayerZeroSector - dld.ld.StartingDataSector; - } - else //PTP or single layer - { - sectors1 = dld.ld.EndDataSector - dld.ld.StartingDataSector; - } - - if(ScsiReadStructure(device,1,DvdPhysicalDescriptor, sizeof(dld), &dld)!=0) - { - // PTP second layer - //sectors1 += dld.ld.EndDataSector - dld.ld.StartingDataSector; - if(dld.ld.EndLayerZeroSector>0) // OTP? - { - sectors1 += dld.ld.EndLayerZeroSector - dld.ld.StartingDataSector; - } - else //PTP - { - sectors1 += dld.ld.EndDataSector - dld.ld.StartingDataSector; - } - } - - return sectors1; - } - - return -1; -} - -s32 IOCtlSrc::GetLayerBreakAddress() -{ - DWORD size; - if(ScsiReadStructure(device,0,DvdPhysicalDescriptor, sizeof(dld), &dld)!=0) - { - if(dld.ld.EndLayerZeroSector>0) // OTP? - { - return dld.ld.EndLayerZeroSector - dld.ld.StartingDataSector; - } - else //PTP or single layer - { - u32 s1 = dld.ld.EndDataSector - dld.ld.StartingDataSector; - - if(ScsiReadStructure(device,1,DvdPhysicalDescriptor, sizeof(dld), &dld)!=0) - { - //PTP - return s1; - } - - // single layer - return 0; - } - } - - return -1; -} -#endif - -#define RETURN(v) {OpenOK=v; return;} - -s32 IOCtlSrc::Reopen() -{ - if(device!=INVALID_HANDLE_VALUE) - { - DWORD size; - DeviceIoControl(device,IOCTL_DVD_END_SESSION,&sessID,sizeof(DVD_SESSION_ID),NULL,0,&size, NULL); - CloseHandle(device); - } - - DWORD share = FILE_SHARE_READ; - DWORD flags = FILE_ATTRIBUTE_READONLY | FILE_FLAG_SEQUENTIAL_SCAN; - DWORD size; - - OpenOK = false; - - device = CreateFile(fName, GENERIC_READ|GENERIC_WRITE|FILE_READ_ATTRIBUTES, share, NULL, OPEN_EXISTING, flags, 0); - if(device==INVALID_HANDLE_VALUE) - { - device = CreateFile(fName, GENERIC_READ|FILE_READ_ATTRIBUTES, share, NULL, OPEN_EXISTING, flags, 0); - if(device==INVALID_HANDLE_VALUE) - return -1; - } - - sessID=0; - DeviceIoControl(device,IOCTL_DVD_START_SESSION,NULL,0,&sessID,sizeof(DVD_SESSION_ID), &size, NULL); - - tocCached = false; - mediaTypeCached = false; - discSizeCached = false; - layerBreakCached = false; - - OpenOK=true; - return 0; -} - -IOCtlSrc::IOCtlSrc(const char* fileName) -{ - device=INVALID_HANDLE_VALUE; - - strcpy_s(fName,256,fileName); - - Reopen(); -} - -IOCtlSrc::~IOCtlSrc() -{ - if(OpenOK) - { - DWORD size; - DeviceIoControl(device,IOCTL_DVD_END_SESSION,&sessID,sizeof(DVD_SESSION_ID),NULL,0,&size, NULL); - - CloseHandle(device); - } -} - -struct mycrap -{ - DWORD shit; - DVD_LAYER_DESCRIPTOR ld; -}; - -DVD_READ_STRUCTURE dvdrs; -mycrap dld; -DISK_GEOMETRY dg; -CDROM_READ_TOC_EX tocrq={0}; - -s32 IOCtlSrc::GetSectorCount() -{ - DWORD size; - - LARGE_INTEGER li; - int plain_sectors = 0; - - if(discSizeCached) - return discSize; - - if(GetFileSizeEx(device,&li)) - { - discSizeCached = true; - discSize = (s32)(li.QuadPart / 2048); - return discSize; - } - - GET_LENGTH_INFORMATION info; - if(DeviceIoControl(device, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0, &info, sizeof(info), &size, NULL)) - { - discSizeCached = true; - discSize = (s32)(info.Length.QuadPart / 2048); - return discSize; - } - - memset(&tocrq,0,sizeof(CDROM_READ_TOC_EX)); - tocrq.Format = CDROM_READ_TOC_EX_FORMAT_FULL_TOC; - tocrq.Msf=1; - tocrq.SessionTrack=1; - - CDROM_TOC_FULL_TOC_DATA *ftd=(CDROM_TOC_FULL_TOC_DATA*)sectorbuffer; - - if(DeviceIoControl(device,IOCTL_CDROM_READ_TOC_EX,&tocrq,sizeof(tocrq),ftd, 2048, &size, NULL)) - { - for(int i=0;i<101;i++) - { - if(ftd->Descriptors[i].Point==0xa2) - { - if(ftd->Descriptors[i].SessionNumber==ftd->LastCompleteSession) - { - int min=ftd->Descriptors[i].Msf[0]; - int sec=ftd->Descriptors[i].Msf[1]; - int frm=ftd->Descriptors[i].Msf[2]; - - discSizeCached = true; - discSize = (s32)MSF_TO_LBA(min,sec,frm); - return discSize; - } - } - } - } - - int sectors1=-1; - - dvdrs.BlockByteOffset.QuadPart=0; - dvdrs.Format=DvdPhysicalDescriptor; - dvdrs.SessionId=sessID; - dvdrs.LayerNumber=0; - if(DeviceIoControl(device,IOCTL_DVD_READ_STRUCTURE,&dvdrs,sizeof(dvdrs),&dld, sizeof(dld), &size, NULL)!=0) - { - if(dld.ld.EndLayerZeroSector>0) // OTP? - { - sectors1 = dld.ld.EndLayerZeroSector - dld.ld.StartingDataSector; - } - else //PTP or single layer - { - sectors1 = dld.ld.EndDataSector - dld.ld.StartingDataSector; - } - dvdrs.BlockByteOffset.QuadPart=0; - dvdrs.Format=DvdPhysicalDescriptor; - dvdrs.SessionId=sessID; - dvdrs.LayerNumber=1; - if(DeviceIoControl(device,IOCTL_DVD_READ_STRUCTURE,&dvdrs,sizeof(dvdrs),&dld, sizeof(dld), &size, NULL)!=0) - { - // PTP second layer - //sectors1 += dld.ld.EndDataSector - dld.ld.StartingDataSector; - if(dld.ld.EndLayerZeroSector>0) // OTP? - { - sectors1 += dld.ld.EndLayerZeroSector - dld.ld.StartingDataSector; - } - else //PTP - { - sectors1 += dld.ld.EndDataSector - dld.ld.StartingDataSector; - } - } - - discSizeCached = true; - discSize = sectors1; - return discSize; - } - - return -1; -} - -s32 IOCtlSrc::GetLayerBreakAddress() -{ - DWORD size; - DWORD code; - - if(GetMediaType()<0) - return -1; - - if(layerBreakCached) - return layerBreak; - - dvdrs.BlockByteOffset.QuadPart=0; - dvdrs.Format=DvdPhysicalDescriptor; - dvdrs.SessionId=sessID; - dvdrs.LayerNumber=0; - if(code=DeviceIoControl(device,IOCTL_DVD_READ_STRUCTURE,&dvdrs,sizeof(dvdrs),&dld, sizeof(dld), &size, NULL)!=0) - { - if(dld.ld.EndLayerZeroSector>0) // OTP? - { - layerBreakCached = true; - layerBreak = dld.ld.EndLayerZeroSector - dld.ld.StartingDataSector; - return layerBreak; - } - else //PTP or single layer - { - u32 s1 = dld.ld.EndDataSector - dld.ld.StartingDataSector; - - dvdrs.BlockByteOffset.QuadPart=0; - dvdrs.Format=DvdPhysicalDescriptor; - dvdrs.SessionId=sessID; - dvdrs.LayerNumber=1; - - if(DeviceIoControl(device,IOCTL_DVD_READ_STRUCTURE,&dvdrs,sizeof(dvdrs),&dld, sizeof(dld), &size, NULL)!=0) - { - //PTP - layerBreakCached = true; - layerBreak = s1; - return layerBreak; - } - - // single layer - layerBreakCached = true; - layerBreak = 0; - return layerBreak; - } - } - - //if not a cd, and fails, assume single layer - return 0; -} - -s32 IOCtlSrc::GetMediaType() -{ - DWORD size; - DWORD code; - - if(mediaTypeCached) - return mediaType; - - memset(&tocrq,0,sizeof(CDROM_READ_TOC_EX)); - tocrq.Format = CDROM_READ_TOC_EX_FORMAT_FULL_TOC; - tocrq.Msf=1; - tocrq.SessionTrack=1; - - CDROM_TOC_FULL_TOC_DATA *ftd=(CDROM_TOC_FULL_TOC_DATA*)sectorbuffer; - - if(DeviceIoControl(device,IOCTL_CDROM_READ_TOC_EX,&tocrq,sizeof(tocrq),ftd, 2048, &size, NULL)) - { - mediaTypeCached = true; - mediaType = -1; - return mediaType; - } - - dvdrs.BlockByteOffset.QuadPart=0; - dvdrs.Format=DvdPhysicalDescriptor; - dvdrs.SessionId=sessID; - dvdrs.LayerNumber=0; - if(code=DeviceIoControl(device,IOCTL_DVD_READ_STRUCTURE,&dvdrs,sizeof(dvdrs),&dld, sizeof(dld), &size, NULL)!=0) - { - if(dld.ld.EndLayerZeroSector>0) // OTP? - { - mediaTypeCached = true; - mediaType = 2; - return mediaType; - } - else //PTP or single layer - { - u32 s1 = dld.ld.EndDataSector - dld.ld.StartingDataSector; - - dvdrs.BlockByteOffset.QuadPart=0; - dvdrs.Format=DvdPhysicalDescriptor; - dvdrs.SessionId=sessID; - dvdrs.LayerNumber=1; - - if(DeviceIoControl(device,IOCTL_DVD_READ_STRUCTURE,&dvdrs,sizeof(dvdrs),&dld, sizeof(dld), &size, NULL)!=0) - { - //PTP - mediaTypeCached = true; - mediaType = 2; - return mediaType; - } - - // single layer - mediaTypeCached = true; - mediaType = 0; - return mediaType; - } - } - - //if not a cd, and fails, assume single layer - mediaTypeCached = true; - mediaType = 0; - return mediaType; -} - -s32 IOCtlSrc::ReadTOC(char *toc,int msize) -{ - DWORD size=0; - - if(GetMediaType()>=0) - return -1; - - if(!tocCached) - { - memset(&tocrq,0,sizeof(CDROM_READ_TOC_EX)); - tocrq.Format = CDROM_READ_TOC_EX_FORMAT_FULL_TOC; - tocrq.Msf=1; - tocrq.SessionTrack=1; - - CDROM_TOC_FULL_TOC_DATA *ftd=(CDROM_TOC_FULL_TOC_DATA*)tocCacheData; - - if(!OpenOK) return -1; - - int code = DeviceIoControl(device,IOCTL_CDROM_READ_TOC_EX,&tocrq,sizeof(tocrq),tocCacheData, 2048, &size, NULL); - - if(code==0) - return -1; - - tocCached = true; - } - - memcpy(toc,tocCacheData,min(2048,msize)); - - return 0; -} - -s32 IOCtlSrc::ReadSectors2048(u32 sector, u32 count, char *buffer) -{ - RAW_READ_INFO rri; - - DWORD size=0; - - if(!OpenOK) return -1; - - rri.DiskOffset.QuadPart=sector*(u64)2048; - rri.SectorCount=count; - - //fall back to standard reading - if(SetFilePointer(device,rri.DiskOffset.LowPart,&rri.DiskOffset.HighPart,FILE_BEGIN)==-1) - { - if(GetLastError()!=0) - return -1; - } - - if(ReadFile(device,buffer,2048*count,&size,NULL)==0) - { - return -1; - } - - if(size!=(2048*count)) - { - return -1; - } - - return 0; -} - - -s32 IOCtlSrc::ReadSectors2352(u32 sector, u32 count, char *buffer) -{ - RAW_READ_INFO rri; - - DWORD size=0; - - if(!OpenOK) return -1; - - rri.DiskOffset.QuadPart=sector*(u64)2048; - rri.SectorCount=count; - - rri.TrackMode=(TRACK_MODE_TYPE)last_read_mode; - if(DeviceIoControl(device,IOCTL_CDROM_RAW_READ,&rri,sizeof(rri),buffer, 2352*count, &size, NULL)==0) - { - rri.TrackMode=CDDA; - if(DeviceIoControl(device,IOCTL_CDROM_RAW_READ,&rri,sizeof(rri),buffer, 2352*count, &size, NULL)==0) - { - rri.TrackMode=XAForm2; - if(DeviceIoControl(device,IOCTL_CDROM_RAW_READ,&rri,sizeof(rri),buffer, 2352*count, &size, NULL)==0) - { - rri.TrackMode=YellowMode2; - if(DeviceIoControl(device,IOCTL_CDROM_RAW_READ,&rri,sizeof(rri),buffer, 2352*count, &size, NULL)==0) - { - return -1; - } - } - } - } - - last_read_mode=rri.TrackMode; - - if(size!=(2352*count)) - { - return -1; - } - - return 0; -} - -s32 IOCtlSrc::DiscChanged() -{ - DWORD size=0; - - if(!OpenOK) return -1; - - int ret = DeviceIoControl(device,IOCTL_STORAGE_CHECK_VERIFY,NULL,0,NULL,0, &size, NULL); - - if(ret==0) - { - tocCached = false; - mediaTypeCached = false; - discSizeCached = false; - layerBreakCached = false; - - if(sessID!=0) - { - DeviceIoControl(device,IOCTL_DVD_END_SESSION,&sessID,sizeof(DVD_SESSION_ID),NULL,0,&size, NULL); - sessID=0; - } - return 1; - } - - if(sessID==0) - { - DeviceIoControl(device,IOCTL_DVD_START_SESSION,NULL,0,&sessID,sizeof(DVD_SESSION_ID), &size, NULL); - } - - return 0; -} - -s32 IOCtlSrc::IsOK() -{ - return OpenOK; -} +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x0600 +#include "../CDVD.h" +#pragma warning(disable:4200) +#pragma pack(1) +#include +#include "rosddk/ntddcdvd.h" +#include "rosddk/ntddcdrm.h" +#include "rosddk/ntddscsi.h" +#pragma warning(default:4200) +#include + +template +bool ApiErrorCheck(T t,T okValue,bool cmpEq) +{ + bool cond=(t==okValue); + + if(!cmpEq) cond=!cond; + + if(!cond) + { + static char buff[1024]; + DWORD ec = GetLastError(); + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,NULL,ec,0,buff,1023,NULL); + MessageBoxEx(0,buff,"ERROR?!",MB_OK,0); + } + return cond; +} + +#if FALSE +typedef struct { + SCSI_PASS_THROUGH_DIRECT spt; + ULONG Filler; + UCHAR ucSenseBuf[32]; +} SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, *PSCSI_PASS_THROUGH_DIRECT_WITH_BUFFER; + +SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER sptIOCTL; // global read bufs + +char szBuf[1024]; + +PSCSI_ADDRESS pSA; + +DWORD SendIoCtlScsiCommand(HANDLE hDevice, u8 direction, + u8 cdbLength, u8* cdb, + u32 dataLength, void* dataBuffer + ) +{ + DWORD dwRet; + + memset(szBuf,0,1024); + + pSA=(PSCSI_ADDRESS)szBuf; + pSA->Length=sizeof(SCSI_ADDRESS); + + if(!DeviceIoControl(hDevice,IOCTL_SCSI_GET_ADDRESS,NULL, + 0,pSA,sizeof(SCSI_ADDRESS), + &dwRet,NULL)) + { + return -1; + } + + memset(&sptIOCTL,0,sizeof(sptIOCTL)); + + sptIOCTL.spt.Length = sizeof(SCSI_PASS_THROUGH_DIRECT); + sptIOCTL.spt.TimeOutValue = 60; + sptIOCTL.spt.SenseInfoLength = 14; + sptIOCTL.spt.SenseInfoOffset = offsetof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, ucSenseBuf); + + sptIOCTL.spt.PathId = pSA->PortNumber; + sptIOCTL.spt.TargetId = pSA->TargetId; + sptIOCTL.spt.Lun = pSA->Lun; + + sptIOCTL.spt.DataIn = (direction>0)?SCSI_IOCTL_DATA_IN:((direction<0)?SCSI_IOCTL_DATA_OUT:SCSI_IOCTL_DATA_UNSPECIFIED); + sptIOCTL.spt.DataTransferLength = dataLength; + sptIOCTL.spt.DataBuffer = dataBuffer; + + sptIOCTL.spt.CdbLength = cdbLength; + memcpy(sptIOCTL.spt.Cdb,cdb,cdbLength); + + DWORD code = DeviceIoControl(hDevice, + IOCTL_SCSI_PASS_THROUGH_DIRECT, + &sptIOCTL,sizeof(sptIOCTL), + &sptIOCTL,sizeof(sptIOCTL), + &dwRet,NULL); + ApiErrorCheck(code,0,false); + return code; +} + +//0x00 = PHYSICAL STRUCTURE +DWORD ScsiReadStructure(HANDLE hDevice, u32 layer, u32 format, u32 buffer_length, void* buffer) +{ + u8 cdb[12]={0}; + + + cdb[0] = 0xAD; + /* + cdb[2] = (unsigned char)((addr >> 24) & 0xFF); + cdb[3] = (unsigned char)((addr >> 16) & 0xFF); + cdb[4] = (unsigned char)((addr >> 8) & 0xFF); + cdb[5] = (unsigned char)(addr & 0xFF); + */ + cdb[6] = layer; + cdb[7] = format; + cdb[8] = (unsigned char)((buffer_length>>8) & 0xFF); + cdb[9] = (unsigned char)((buffer_length) & 0xFF); + + return SendIoCtlScsiCommand(hDevice, 1, 12, cdb, buffer_length, buffer); +} + + +DWORD ScsiReadBE_2(HANDLE hDevice, u32 addr, u32 count, u8* buffer) +{ + u8 cdb[12]={0}; + + cdb[0] = 0xBE; + cdb[2] = (unsigned char)((addr >> 24) & 0xFF); + cdb[3] = (unsigned char)((addr >> 16) & 0xFF); + cdb[4] = (unsigned char)((addr >> 8) & 0xFF); + cdb[5] = (unsigned char)(addr & 0xFF); + cdb[8] = count; + cdb[9] = 0xF8; + cdb[10] = 0x2; + + return SendIoCtlScsiCommand(hDevice, 1, 12, cdb, 2352, buffer); +} + +DWORD ScsiRead10(HANDLE hDevice, u32 addr, u32 count, u8* buffer) +{ + u8 cdb[10]={0}; + + cdb[0] = 0x28; + //cdb[1] = lun<<5; + cdb[2] = (unsigned char)((addr >> 24) & 0xFF); + cdb[3] = (unsigned char)((addr >> 16) & 0xFF); + cdb[4] = (unsigned char)((addr >> 8) & 0xFF); + cdb[5] = (unsigned char)(addr & 0xFF); + cdb[8] = count; + + return SendIoCtlScsiCommand(hDevice, 1, 10, cdb, 2352, buffer); +} + +DWORD ScsiReadTOC(HANDLE hDevice, u32 addr, u8* buffer, bool use_msf) +{ + u8 cdb[12]={0}; + + cdb[0] = 0x43; + cdb[7] = 0x03; + cdb[8] = 0x24; + + if(use_msf) cdb[1]=2; + + return SendIoCtlScsiCommand(hDevice, 1, 10, cdb, 2352, buffer); +} + +s32 IOCtlSrc::GetSectorCount() +{ + DWORD size; + + LARGE_INTEGER li; + int plain_sectors = 0; + + if(GetFileSizeEx(device,&li)) + { + return li.QuadPart / 2048; + } + + GET_LENGTH_INFORMATION info; + if(DeviceIoControl(device, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0, &info, sizeof(info), &size, NULL)) + { + return info.Length.QuadPart / 2048; + } + + CDROM_READ_TOC_EX tocrq={0}; + + tocrq.Format = CDROM_READ_TOC_EX_FORMAT_FULL_TOC; + tocrq.Msf=1; + tocrq.SessionTrack=1; + + CDROM_TOC_FULL_TOC_DATA *ftd=(CDROM_TOC_FULL_TOC_DATA*)sectorbuffer; + + if(DeviceIoControl(device,IOCTL_CDROM_READ_TOC_EX,&tocrq,sizeof(tocrq),ftd, 2048, &size, NULL)) + { + for(int i=0;i<101;i++) + { + if(ftd->Descriptors[i].Point==0xa2) + { + if(ftd->Descriptors[i].SessionNumber==ftd->LastCompleteSession) + { + int min=ftd->Descriptors[i].Msf[0]; + int sec=ftd->Descriptors[i].Msf[1]; + int frm=ftd->Descriptors[i].Msf[2]; + + return MSF_TO_LBA(min,sec,frm); + } + } + } + } + + int sectors1=-1; + + if(ScsiReadStructure(device,0,DvdPhysicalDescriptor, sizeof(dld), &dld)!=0) + { + if(dld.ld.EndLayerZeroSector>0) // OTP? + { + sectors1 = dld.ld.EndLayerZeroSector - dld.ld.StartingDataSector; + } + else //PTP or single layer + { + sectors1 = dld.ld.EndDataSector - dld.ld.StartingDataSector; + } + + if(ScsiReadStructure(device,1,DvdPhysicalDescriptor, sizeof(dld), &dld)!=0) + { + // PTP second layer + //sectors1 += dld.ld.EndDataSector - dld.ld.StartingDataSector; + if(dld.ld.EndLayerZeroSector>0) // OTP? + { + sectors1 += dld.ld.EndLayerZeroSector - dld.ld.StartingDataSector; + } + else //PTP + { + sectors1 += dld.ld.EndDataSector - dld.ld.StartingDataSector; + } + } + + return sectors1; + } + + return -1; +} + +s32 IOCtlSrc::GetLayerBreakAddress() +{ + DWORD size; + if(ScsiReadStructure(device,0,DvdPhysicalDescriptor, sizeof(dld), &dld)!=0) + { + if(dld.ld.EndLayerZeroSector>0) // OTP? + { + return dld.ld.EndLayerZeroSector - dld.ld.StartingDataSector; + } + else //PTP or single layer + { + u32 s1 = dld.ld.EndDataSector - dld.ld.StartingDataSector; + + if(ScsiReadStructure(device,1,DvdPhysicalDescriptor, sizeof(dld), &dld)!=0) + { + //PTP + return s1; + } + + // single layer + return 0; + } + } + + return -1; +} +#endif + +#define RETURN(v) {OpenOK=v; return;} + +s32 IOCtlSrc::Reopen() +{ + if(device!=INVALID_HANDLE_VALUE) + { + DWORD size; + DeviceIoControl(device,IOCTL_DVD_END_SESSION,&sessID,sizeof(DVD_SESSION_ID),NULL,0,&size, NULL); + CloseHandle(device); + } + + DWORD share = FILE_SHARE_READ; + DWORD flags = FILE_ATTRIBUTE_READONLY | FILE_FLAG_SEQUENTIAL_SCAN; + DWORD size; + + OpenOK = false; + + device = CreateFile(fName, GENERIC_READ|GENERIC_WRITE|FILE_READ_ATTRIBUTES, share, NULL, OPEN_EXISTING, flags, 0); + if(device==INVALID_HANDLE_VALUE) + { + device = CreateFile(fName, GENERIC_READ|FILE_READ_ATTRIBUTES, share, NULL, OPEN_EXISTING, flags, 0); + if(device==INVALID_HANDLE_VALUE) + return -1; + } + + sessID=0; + DeviceIoControl(device,IOCTL_DVD_START_SESSION,NULL,0,&sessID,sizeof(DVD_SESSION_ID), &size, NULL); + + tocCached = false; + mediaTypeCached = false; + discSizeCached = false; + layerBreakCached = false; + + OpenOK=true; + return 0; +} + +IOCtlSrc::IOCtlSrc(const char* fileName) +{ + device=INVALID_HANDLE_VALUE; + + strcpy_s(fName,256,fileName); + + Reopen(); +} + +IOCtlSrc::~IOCtlSrc() +{ + if(OpenOK) + { + DWORD size; + DeviceIoControl(device,IOCTL_DVD_END_SESSION,&sessID,sizeof(DVD_SESSION_ID),NULL,0,&size, NULL); + + CloseHandle(device); + } +} + +struct mycrap +{ + DWORD shit; + DVD_LAYER_DESCRIPTOR ld; +}; + +DVD_READ_STRUCTURE dvdrs; +mycrap dld; +DISK_GEOMETRY dg; +CDROM_READ_TOC_EX tocrq={0}; + +s32 IOCtlSrc::GetSectorCount() +{ + DWORD size; + + LARGE_INTEGER li; + int plain_sectors = 0; + + if(discSizeCached) + return discSize; + + if(GetFileSizeEx(device,&li)) + { + discSizeCached = true; + discSize = (s32)(li.QuadPart / 2048); + return discSize; + } + + GET_LENGTH_INFORMATION info; + if(DeviceIoControl(device, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0, &info, sizeof(info), &size, NULL)) + { + discSizeCached = true; + discSize = (s32)(info.Length.QuadPart / 2048); + return discSize; + } + + memset(&tocrq,0,sizeof(CDROM_READ_TOC_EX)); + tocrq.Format = CDROM_READ_TOC_EX_FORMAT_FULL_TOC; + tocrq.Msf=1; + tocrq.SessionTrack=1; + + CDROM_TOC_FULL_TOC_DATA *ftd=(CDROM_TOC_FULL_TOC_DATA*)sectorbuffer; + + if(DeviceIoControl(device,IOCTL_CDROM_READ_TOC_EX,&tocrq,sizeof(tocrq),ftd, 2048, &size, NULL)) + { + for(int i=0;i<101;i++) + { + if(ftd->Descriptors[i].Point==0xa2) + { + if(ftd->Descriptors[i].SessionNumber==ftd->LastCompleteSession) + { + int min=ftd->Descriptors[i].Msf[0]; + int sec=ftd->Descriptors[i].Msf[1]; + int frm=ftd->Descriptors[i].Msf[2]; + + discSizeCached = true; + discSize = (s32)MSF_TO_LBA(min,sec,frm); + return discSize; + } + } + } + } + + int sectors1=-1; + + dvdrs.BlockByteOffset.QuadPart=0; + dvdrs.Format=DvdPhysicalDescriptor; + dvdrs.SessionId=sessID; + dvdrs.LayerNumber=0; + if(DeviceIoControl(device,IOCTL_DVD_READ_STRUCTURE,&dvdrs,sizeof(dvdrs),&dld, sizeof(dld), &size, NULL)!=0) + { + if(dld.ld.EndLayerZeroSector>0) // OTP? + { + sectors1 = dld.ld.EndLayerZeroSector - dld.ld.StartingDataSector; + } + else //PTP or single layer + { + sectors1 = dld.ld.EndDataSector - dld.ld.StartingDataSector; + } + dvdrs.BlockByteOffset.QuadPart=0; + dvdrs.Format=DvdPhysicalDescriptor; + dvdrs.SessionId=sessID; + dvdrs.LayerNumber=1; + if(DeviceIoControl(device,IOCTL_DVD_READ_STRUCTURE,&dvdrs,sizeof(dvdrs),&dld, sizeof(dld), &size, NULL)!=0) + { + // PTP second layer + //sectors1 += dld.ld.EndDataSector - dld.ld.StartingDataSector; + if(dld.ld.EndLayerZeroSector>0) // OTP? + { + sectors1 += dld.ld.EndLayerZeroSector - dld.ld.StartingDataSector; + } + else //PTP + { + sectors1 += dld.ld.EndDataSector - dld.ld.StartingDataSector; + } + } + + discSizeCached = true; + discSize = sectors1; + return discSize; + } + + return -1; +} + +s32 IOCtlSrc::GetLayerBreakAddress() +{ + DWORD size; + DWORD code; + + if(GetMediaType()<0) + return -1; + + if(layerBreakCached) + return layerBreak; + + dvdrs.BlockByteOffset.QuadPart=0; + dvdrs.Format=DvdPhysicalDescriptor; + dvdrs.SessionId=sessID; + dvdrs.LayerNumber=0; + if(code=DeviceIoControl(device,IOCTL_DVD_READ_STRUCTURE,&dvdrs,sizeof(dvdrs),&dld, sizeof(dld), &size, NULL)!=0) + { + if(dld.ld.EndLayerZeroSector>0) // OTP? + { + layerBreakCached = true; + layerBreak = dld.ld.EndLayerZeroSector - dld.ld.StartingDataSector; + return layerBreak; + } + else //PTP or single layer + { + u32 s1 = dld.ld.EndDataSector - dld.ld.StartingDataSector; + + dvdrs.BlockByteOffset.QuadPart=0; + dvdrs.Format=DvdPhysicalDescriptor; + dvdrs.SessionId=sessID; + dvdrs.LayerNumber=1; + + if(DeviceIoControl(device,IOCTL_DVD_READ_STRUCTURE,&dvdrs,sizeof(dvdrs),&dld, sizeof(dld), &size, NULL)!=0) + { + //PTP + layerBreakCached = true; + layerBreak = s1; + return layerBreak; + } + + // single layer + layerBreakCached = true; + layerBreak = 0; + return layerBreak; + } + } + + //if not a cd, and fails, assume single layer + return 0; +} + +s32 IOCtlSrc::GetMediaType() +{ + DWORD size; + DWORD code; + + if(mediaTypeCached) + return mediaType; + + memset(&tocrq,0,sizeof(CDROM_READ_TOC_EX)); + tocrq.Format = CDROM_READ_TOC_EX_FORMAT_FULL_TOC; + tocrq.Msf=1; + tocrq.SessionTrack=1; + + CDROM_TOC_FULL_TOC_DATA *ftd=(CDROM_TOC_FULL_TOC_DATA*)sectorbuffer; + + if(DeviceIoControl(device,IOCTL_CDROM_READ_TOC_EX,&tocrq,sizeof(tocrq),ftd, 2048, &size, NULL)) + { + mediaTypeCached = true; + mediaType = -1; + return mediaType; + } + + dvdrs.BlockByteOffset.QuadPart=0; + dvdrs.Format=DvdPhysicalDescriptor; + dvdrs.SessionId=sessID; + dvdrs.LayerNumber=0; + if(code=DeviceIoControl(device,IOCTL_DVD_READ_STRUCTURE,&dvdrs,sizeof(dvdrs),&dld, sizeof(dld), &size, NULL)!=0) + { + if(dld.ld.EndLayerZeroSector>0) // OTP? + { + mediaTypeCached = true; + mediaType = 2; + return mediaType; + } + else //PTP or single layer + { + u32 s1 = dld.ld.EndDataSector - dld.ld.StartingDataSector; + + dvdrs.BlockByteOffset.QuadPart=0; + dvdrs.Format=DvdPhysicalDescriptor; + dvdrs.SessionId=sessID; + dvdrs.LayerNumber=1; + + if(DeviceIoControl(device,IOCTL_DVD_READ_STRUCTURE,&dvdrs,sizeof(dvdrs),&dld, sizeof(dld), &size, NULL)!=0) + { + //PTP + mediaTypeCached = true; + mediaType = 2; + return mediaType; + } + + // single layer + mediaTypeCached = true; + mediaType = 0; + return mediaType; + } + } + + //if not a cd, and fails, assume single layer + mediaTypeCached = true; + mediaType = 0; + return mediaType; +} + +s32 IOCtlSrc::ReadTOC(char *toc,int msize) +{ + DWORD size=0; + + if(GetMediaType()>=0) + return -1; + + if(!tocCached) + { + memset(&tocrq,0,sizeof(CDROM_READ_TOC_EX)); + tocrq.Format = CDROM_READ_TOC_EX_FORMAT_FULL_TOC; + tocrq.Msf=1; + tocrq.SessionTrack=1; + + CDROM_TOC_FULL_TOC_DATA *ftd=(CDROM_TOC_FULL_TOC_DATA*)tocCacheData; + + if(!OpenOK) return -1; + + int code = DeviceIoControl(device,IOCTL_CDROM_READ_TOC_EX,&tocrq,sizeof(tocrq),tocCacheData, 2048, &size, NULL); + + if(code==0) + return -1; + + tocCached = true; + } + + memcpy(toc,tocCacheData,min(2048,msize)); + + return 0; +} + +s32 IOCtlSrc::ReadSectors2048(u32 sector, u32 count, char *buffer) +{ + RAW_READ_INFO rri; + + DWORD size=0; + + if(!OpenOK) return -1; + + rri.DiskOffset.QuadPart=sector*(u64)2048; + rri.SectorCount=count; + + //fall back to standard reading + if(SetFilePointer(device,rri.DiskOffset.LowPart,&rri.DiskOffset.HighPart,FILE_BEGIN)==-1) + { + if(GetLastError()!=0) + return -1; + } + + if(ReadFile(device,buffer,2048*count,&size,NULL)==0) + { + return -1; + } + + if(size!=(2048*count)) + { + return -1; + } + + return 0; +} + + +s32 IOCtlSrc::ReadSectors2352(u32 sector, u32 count, char *buffer) +{ + RAW_READ_INFO rri; + + DWORD size=0; + + if(!OpenOK) return -1; + + rri.DiskOffset.QuadPart=sector*(u64)2048; + rri.SectorCount=count; + + rri.TrackMode=(TRACK_MODE_TYPE)last_read_mode; + if(DeviceIoControl(device,IOCTL_CDROM_RAW_READ,&rri,sizeof(rri),buffer, 2352*count, &size, NULL)==0) + { + rri.TrackMode=CDDA; + if(DeviceIoControl(device,IOCTL_CDROM_RAW_READ,&rri,sizeof(rri),buffer, 2352*count, &size, NULL)==0) + { + rri.TrackMode=XAForm2; + if(DeviceIoControl(device,IOCTL_CDROM_RAW_READ,&rri,sizeof(rri),buffer, 2352*count, &size, NULL)==0) + { + rri.TrackMode=YellowMode2; + if(DeviceIoControl(device,IOCTL_CDROM_RAW_READ,&rri,sizeof(rri),buffer, 2352*count, &size, NULL)==0) + { + return -1; + } + } + } + } + + last_read_mode=rri.TrackMode; + + if(size!=(2352*count)) + { + return -1; + } + + return 0; +} + +s32 IOCtlSrc::DiscChanged() +{ + DWORD size=0; + + if(!OpenOK) return -1; + + int ret = DeviceIoControl(device,IOCTL_STORAGE_CHECK_VERIFY,NULL,0,NULL,0, &size, NULL); + + if(ret==0) + { + tocCached = false; + mediaTypeCached = false; + discSizeCached = false; + layerBreakCached = false; + + if(sessID!=0) + { + DeviceIoControl(device,IOCTL_DVD_END_SESSION,&sessID,sizeof(DVD_SESSION_ID),NULL,0,&size, NULL); + sessID=0; + } + return 1; + } + + if(sessID==0) + { + DeviceIoControl(device,IOCTL_DVD_START_SESSION,NULL,0,&sessID,sizeof(DVD_SESSION_ID), &size, NULL); + } + + return 0; +} + +s32 IOCtlSrc::IsOK() +{ + return OpenOK; +} diff --git a/plugins/cdvdGigaherz/src/Windows/PlainIso.cpp b/plugins/cdvdGigaherz/src/Windows/PlainIso.cpp index 6ac335dbfd..3870f1e132 100644 --- a/plugins/cdvdGigaherz/src/Windows/PlainIso.cpp +++ b/plugins/cdvdGigaherz/src/Windows/PlainIso.cpp @@ -1,263 +1,263 @@ -#undef _WIN32_WINNT -#define _WIN32_WINNT 0x0600 -#include "../CDVD.h" -#pragma warning(disable:4200) -#pragma pack(1) -#include -#include "rosddk/ntddcdvd.h" -#include "rosddk/ntddcdrm.h" -#include "rosddk/ntddscsi.h" -#pragma warning(default:4200) -#include - -#include "../ReaderModules.h" -#include "../SectorConverters.h" - -#include - -template -bool ApiErrorCheck(T t,T okValue,bool cmpEq) -{ - bool cond=(t==okValue); - - if(!cmpEq) cond=!cond; - - if(!cond) - { - static char buff[1024]; - DWORD ec = GetLastError(); - FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,NULL,ec,0,buff,1023,NULL); - MessageBoxEx(0,buff,"ERROR?!",MB_OK,0); - } - return cond; -} - -#define RETURN(v) {OpenOK=v; return;} - -s32 PlainIso::Reopen() -{ - if(fileSource!=INVALID_HANDLE_VALUE) - { - CloseHandle(fileSource); - } - - DWORD share = FILE_SHARE_READ; - DWORD flags = FILE_ATTRIBUTE_READONLY | FILE_FLAG_SEQUENTIAL_SCAN; - - OpenOK = false; - - fileSource = CreateFile(fName, GENERIC_READ|FILE_READ_ATTRIBUTES, share, NULL, OPEN_EXISTING, flags, 0); - - tocCached = false; - mediaTypeCached = false; - discSizeCached = false; - layerBreakCached = false; - - OpenOK=true; - return 0; -} - -PlainIso::PlainIso(const char* fileName) -{ - fileSource=INVALID_HANDLE_VALUE; - - strcpy_s(fName,256,fileName); - - Reopen(); -} - -PlainIso::~PlainIso() -{ - if(OpenOK) - { - CloseHandle(fileSource); - } -} - -s32 PlainIso::GetSectorCount() -{ - LARGE_INTEGER li; - int plain_sectors = 0; - - if(discSizeCached) - return discSize; - - if(GetFileSizeEx(fileSource,&li)) - { - discSizeCached = true; - discSize = (s32)(li.QuadPart / 2048); - return discSize; - } - - return -1; -} - -s32 PlainIso::GetLayerBreakAddress() -{ - return 0; -} - -s32 PlainIso::GetMediaType() -{ - if(mediaTypeCached) - return mediaType; - - // assume single layer DVD, for now - mediaTypeCached = true; - mediaType = 0; - return mediaType; -} - -#define MKDESCRIPTOR(n,ses,ctr,adr,pt,me,se,fe,tm,ts,tf) \ - ftd->Descriptors[n].SessionNumber = ses; \ - ftd->Descriptors[n].Control = ctr; \ - ftd->Descriptors[n].Adr = adr; \ - ftd->Descriptors[n].Reserved1 = 0; \ - ftd->Descriptors[n].Point = pt; \ - ftd->Descriptors[n].MsfExtra[0] = me; \ - ftd->Descriptors[n].MsfExtra[1] = se; \ - ftd->Descriptors[n].MsfExtra[2] = fe; \ - ftd->Descriptors[n].Zero = 0; \ - ftd->Descriptors[n].Msf[0] = tm; \ - ftd->Descriptors[n].Msf[1] = ts; \ - ftd->Descriptors[n].Msf[2] = tf; - -#define MSF_TO_LBA(m,s,f) ((m*60+s)*75+f-150) -#define LBA_TO_MSF(m,s,f,l) \ - m = (l)/(75*60); \ - s = (((l)/75)%60); \ - f = ((l)%75); - - -s32 PlainIso::ReadTOC(char *toc,int msize) -{ - DWORD size=0; - - if(GetMediaType()>=0) - return -1; - - if(!tocCached) - { - CDROM_TOC_FULL_TOC_DATA *ftd=(CDROM_TOC_FULL_TOC_DATA*)tocCacheData; - - // Build basic TOC - int length = 6 * sizeof(ftd->Descriptors[0]) + 2; - ftd->Length[0] = length>>8; - ftd->Length[1] = length; - ftd->FirstCompleteSession=1; - ftd->LastCompleteSession=2; - - int dm,ds,df; - - LBA_TO_MSF(dm,ds,df,sector_count); - - MKDESCRIPTOR(0,1,0x00,1,0xA0,0,0,0,1,0,0); - MKDESCRIPTOR(1,1,0x00,1,0xA1,0,0,0,1,0,0); - MKDESCRIPTOR(2,1,0x00,1,0xA2,0,0,0,dm,ds,df); - MKDESCRIPTOR(3,1,0x00,5,0xB0,0,0,0,0,0,0); - MKDESCRIPTOR(4,1,0x00,5,0xC0,0,0,0,0,0,0); - MKDESCRIPTOR(5,1,0x04,1,0x01,0,0,0,0,2,0); - - tocCached = true; - } - - memcpy(toc,tocCacheData,min(2048,msize)); - - return 0; -} - -s32 PlainIso::ReadSectors2048(u32 sector, u32 count, char *buffer) -{ - DWORD size=0; - LARGE_INTEGER Offset; - - if(!OpenOK) return -1; - - Offset.QuadPart=sector*(u64)2048; - - //fall back to standard reading - if(SetFilePointer(fileSource,Offset.LowPart,&Offset.HighPart,FILE_BEGIN)==-1) - { - if(GetLastError()!=0) - return -1; - } - - if(ReadFile(fileSource,buffer,2048*count,&size,NULL)==0) - { - return -1; - } - - if(size!=(2048*count)) - { - return -1; - } - - return 0; -} - - -s32 PlainIso::ReadSectors2352(u32 sector, u32 count, char *buffer) -{ - DWORD size=0; - LARGE_INTEGER Offset; - - if(!OpenOK) return -1; - - assert(count <= 32); - - Offset.QuadPart=sector*(u64)2048; - - //fall back to standard reading - if(SetFilePointer(fileSource,Offset.LowPart,&Offset.HighPart,FILE_BEGIN)==-1) - { - if(GetLastError()!=0) - return -1; - } - - if(ReadFile(fileSource,sectorbuffer,2048*count,&size,NULL)==0) - { - return -1; - } - - if(size!=(2048*count)) - { - return -1; - } - - for(uint i=0;i(buffer+2352*i,sectorbuffer+2048*i,sector+i); - } - - return 0; -} - -s32 PlainIso::DiscChanged() -{ - DWORD size=0; - - if(!OpenOK) return -1; - - return 0; -} - -s32 PlainIso::IsOK() -{ - return OpenOK; -} - -Reader* PlainIso::TryLoad(const char* fName) -{ - std::string fileName = fName; - std::string::size_type pos = fileName.find_last_of('.'); - - if(pos == std::string::npos) // no "." found, error. - return NULL; - - std::string extension = fileName.substr(pos); - - if(extension.compare(".iso")!=0) // assume valid - return NULL; - - return new PlainIso(fName); +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x0600 +#include "../CDVD.h" +#pragma warning(disable:4200) +#pragma pack(1) +#include +#include "rosddk/ntddcdvd.h" +#include "rosddk/ntddcdrm.h" +#include "rosddk/ntddscsi.h" +#pragma warning(default:4200) +#include + +#include "../ReaderModules.h" +#include "../SectorConverters.h" + +#include + +template +bool ApiErrorCheck(T t,T okValue,bool cmpEq) +{ + bool cond=(t==okValue); + + if(!cmpEq) cond=!cond; + + if(!cond) + { + static char buff[1024]; + DWORD ec = GetLastError(); + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,NULL,ec,0,buff,1023,NULL); + MessageBoxEx(0,buff,"ERROR?!",MB_OK,0); + } + return cond; +} + +#define RETURN(v) {OpenOK=v; return;} + +s32 PlainIso::Reopen() +{ + if(fileSource!=INVALID_HANDLE_VALUE) + { + CloseHandle(fileSource); + } + + DWORD share = FILE_SHARE_READ; + DWORD flags = FILE_ATTRIBUTE_READONLY | FILE_FLAG_SEQUENTIAL_SCAN; + + OpenOK = false; + + fileSource = CreateFile(fName, GENERIC_READ|FILE_READ_ATTRIBUTES, share, NULL, OPEN_EXISTING, flags, 0); + + tocCached = false; + mediaTypeCached = false; + discSizeCached = false; + layerBreakCached = false; + + OpenOK=true; + return 0; +} + +PlainIso::PlainIso(const char* fileName) +{ + fileSource=INVALID_HANDLE_VALUE; + + strcpy_s(fName,256,fileName); + + Reopen(); +} + +PlainIso::~PlainIso() +{ + if(OpenOK) + { + CloseHandle(fileSource); + } +} + +s32 PlainIso::GetSectorCount() +{ + LARGE_INTEGER li; + int plain_sectors = 0; + + if(discSizeCached) + return discSize; + + if(GetFileSizeEx(fileSource,&li)) + { + discSizeCached = true; + discSize = (s32)(li.QuadPart / 2048); + return discSize; + } + + return -1; +} + +s32 PlainIso::GetLayerBreakAddress() +{ + return 0; +} + +s32 PlainIso::GetMediaType() +{ + if(mediaTypeCached) + return mediaType; + + // assume single layer DVD, for now + mediaTypeCached = true; + mediaType = 0; + return mediaType; +} + +#define MKDESCRIPTOR(n,ses,ctr,adr,pt,me,se,fe,tm,ts,tf) \ + ftd->Descriptors[n].SessionNumber = ses; \ + ftd->Descriptors[n].Control = ctr; \ + ftd->Descriptors[n].Adr = adr; \ + ftd->Descriptors[n].Reserved1 = 0; \ + ftd->Descriptors[n].Point = pt; \ + ftd->Descriptors[n].MsfExtra[0] = me; \ + ftd->Descriptors[n].MsfExtra[1] = se; \ + ftd->Descriptors[n].MsfExtra[2] = fe; \ + ftd->Descriptors[n].Zero = 0; \ + ftd->Descriptors[n].Msf[0] = tm; \ + ftd->Descriptors[n].Msf[1] = ts; \ + ftd->Descriptors[n].Msf[2] = tf; + +#define MSF_TO_LBA(m,s,f) ((m*60+s)*75+f-150) +#define LBA_TO_MSF(m,s,f,l) \ + m = (l)/(75*60); \ + s = (((l)/75)%60); \ + f = ((l)%75); + + +s32 PlainIso::ReadTOC(char *toc,int msize) +{ + DWORD size=0; + + if(GetMediaType()>=0) + return -1; + + if(!tocCached) + { + CDROM_TOC_FULL_TOC_DATA *ftd=(CDROM_TOC_FULL_TOC_DATA*)tocCacheData; + + // Build basic TOC + int length = 6 * sizeof(ftd->Descriptors[0]) + 2; + ftd->Length[0] = length>>8; + ftd->Length[1] = length; + ftd->FirstCompleteSession=1; + ftd->LastCompleteSession=2; + + int dm,ds,df; + + LBA_TO_MSF(dm,ds,df,sector_count); + + MKDESCRIPTOR(0,1,0x00,1,0xA0,0,0,0,1,0,0); + MKDESCRIPTOR(1,1,0x00,1,0xA1,0,0,0,1,0,0); + MKDESCRIPTOR(2,1,0x00,1,0xA2,0,0,0,dm,ds,df); + MKDESCRIPTOR(3,1,0x00,5,0xB0,0,0,0,0,0,0); + MKDESCRIPTOR(4,1,0x00,5,0xC0,0,0,0,0,0,0); + MKDESCRIPTOR(5,1,0x04,1,0x01,0,0,0,0,2,0); + + tocCached = true; + } + + memcpy(toc,tocCacheData,min(2048,msize)); + + return 0; +} + +s32 PlainIso::ReadSectors2048(u32 sector, u32 count, char *buffer) +{ + DWORD size=0; + LARGE_INTEGER Offset; + + if(!OpenOK) return -1; + + Offset.QuadPart=sector*(u64)2048; + + //fall back to standard reading + if(SetFilePointer(fileSource,Offset.LowPart,&Offset.HighPart,FILE_BEGIN)==-1) + { + if(GetLastError()!=0) + return -1; + } + + if(ReadFile(fileSource,buffer,2048*count,&size,NULL)==0) + { + return -1; + } + + if(size!=(2048*count)) + { + return -1; + } + + return 0; +} + + +s32 PlainIso::ReadSectors2352(u32 sector, u32 count, char *buffer) +{ + DWORD size=0; + LARGE_INTEGER Offset; + + if(!OpenOK) return -1; + + assert(count <= 32); + + Offset.QuadPart=sector*(u64)2048; + + //fall back to standard reading + if(SetFilePointer(fileSource,Offset.LowPart,&Offset.HighPart,FILE_BEGIN)==-1) + { + if(GetLastError()!=0) + return -1; + } + + if(ReadFile(fileSource,sectorbuffer,2048*count,&size,NULL)==0) + { + return -1; + } + + if(size!=(2048*count)) + { + return -1; + } + + for(uint i=0;i(buffer+2352*i,sectorbuffer+2048*i,sector+i); + } + + return 0; +} + +s32 PlainIso::DiscChanged() +{ + DWORD size=0; + + if(!OpenOK) return -1; + + return 0; +} + +s32 PlainIso::IsOK() +{ + return OpenOK; +} + +Reader* PlainIso::TryLoad(const char* fName) +{ + std::string fileName = fName; + std::string::size_type pos = fileName.find_last_of('.'); + + if(pos == std::string::npos) // no "." found, error. + return NULL; + + std::string extension = fileName.substr(pos); + + if(extension.compare(".iso")!=0) // assume valid + return NULL; + + return new PlainIso(fName); } \ No newline at end of file diff --git a/plugins/cdvdGigaherz/src/Windows/config.cpp b/plugins/cdvdGigaherz/src/Windows/config.cpp index 4092ef7ec5..1a7ec95435 100644 --- a/plugins/cdvdGigaherz/src/Windows/config.cpp +++ b/plugins/cdvdGigaherz/src/Windows/config.cpp @@ -1,234 +1,234 @@ -//GiGaHeRz SPU2 Driver -//Copyright (c) David Quintana -// -//This library 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 Foundation; either -//version 2.1 of the License, or (at your option) any later version. -// -//This library 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 -//Lesser General Public License for more details. -// -//You should have received a copy of the GNU Lesser General Public -//License along with this library; if not, write to the Free Software -//Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -// -//#include "spu2.h" -#include -#include -#include "resource.h" - -#include "../cdvd.h" - -// Config Vars - -// DEBUG - -char source_drive; -char source_file[MAX_PATH]; - -char CfgFile[MAX_PATH+10] = "inis\\cdvdGigaherz.ini"; - -void CfgSetSettingsDir( const char* dir ) -{ - // a better std::string version, but it's inconvenient for other reasons. - //CfgFile = std::string(( dir == NULL ) ? "inis\\" : dir) + "cdvdGigaherz.ini"; - - // emulator assures a trailing slash/backslash (yay!) - strcpy_s(CfgFile, (dir==NULL) ? "inis\\" : dir); - strcat_s(CfgFile, "cdvdGigaherz.ini"); -} - - - /*| Config File Format: |¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯*\ -+--+---------------------+------------------------+ -| | -| Option=Value | -| | -| | -| Boolean Values: TRUE,YES,1,T,Y mean 'true', | -| everything else means 'false'. | -| | -| All Values are limited to 255 chars. | -| | -+-------------------------------------------------+ - \*_____________________________________________*/ - - -void CfgWriteBool(char *Section, char*Name, char Value) { - char *Data=Value?"TRUE":"FALSE"; - - WritePrivateProfileString(Section,Name,Data,CfgFile); -} - -void CfgWriteInt(char *Section, char*Name, int Value) { - char Data[255]; - _itoa(Value,Data,10); - - WritePrivateProfileString(Section,Name,Data,CfgFile); -} - -void CfgWriteStr(char *Section, char*Name,char *Data) { - WritePrivateProfileString(Section,Name,Data,CfgFile); -} - -/*****************************************************************************/ - -char CfgReadBool(char *Section,char *Name,char Default) { - char Data[255]=""; - GetPrivateProfileString(Section,Name,"",Data,255,CfgFile); - Data[254]=0; - if(strlen(Data)==0) { - CfgWriteBool(Section,Name,Default); - return Default; - } - - if(strcmp(Data,"1")==0) return -1; - if(strcmp(Data,"Y")==0) return -1; - if(strcmp(Data,"T")==0) return -1; - if(strcmp(Data,"YES")==0) return -1; - if(strcmp(Data,"TRUE")==0) return -1; - return 0; -} - -int CfgReadInt(char *Section, char*Name,int Default) { - char Data[255]=""; - GetPrivateProfileString(Section,Name,"",Data,255,CfgFile); - Data[254]=0; - - if(strlen(Data)==0) { - CfgWriteInt(Section,Name,Default); - return Default; - } - - return atoi(Data); -} - -void CfgReadStr(char *Section, char*Name,char *Data,int DataSize,char *Default) { - int sl; - GetPrivateProfileString(Section,Name,"",Data,DataSize,CfgFile); - - if(strlen(Data)==0) { - sl=(int)strlen(Default); - strncpy(Data,Default,sl>255?255:sl); - CfgWriteStr(Section,Name,Data); - } -} - -/*****************************************************************************/ - -void ReadSettings() -{ - char temp[512]; - - CfgReadStr("Config","Source",temp,511,"-"); - source_drive=temp[0]; - - if(source_drive=='$') - strcpy_s(source_file,sizeof(source_file),temp+1); - else - source_file[0]=0; -} - -/*****************************************************************************/ - -void WriteSettings() -{ - char temp[511]; - - temp[0]=source_drive; - temp[1]=0; - - strcat(temp,source_file); - - CfgWriteStr("Config","Source",temp); -} - -char* path[] = { - "A:","B:","C:","D:","E:","F:","G:","H:","I:","J:","K:","L:","M:", - "N:","O:","P:","Q:","R:","S:","T:","U:","V:","W:","X:","Y:","Z:", -}; - -int n,s; - -#define SET_CHECK(idc,value) SendMessage(GetDlgItem(hWnd,idc),BM_SETCHECK,((value)==0)?BST_UNCHECKED:BST_CHECKED,0) -#define HANDLE_CHECK(idc,hvar) case idc: hvar=hvar?0:1; SendMessage(GetDlgItem(hWnd,idc),BM_SETCHECK,(hvar==1)?BST_CHECKED:BST_UNCHECKED,0); break -#define HANDLE_CHECKNB(idc,hvar)case idc: hvar=hvar?0:1; SendMessage(GetDlgItem(hWnd,idc),BM_SETCHECK,(hvar==1)?BST_CHECKED:BST_UNCHECKED,0) -#define ENABLE_CONTROL(idc,value) EnableWindow(GetDlgItem(hWnd,idc),value) - -BOOL CALLBACK ConfigProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam) -{ - int wmId,wmEvent; - char temp[20]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; - - switch(uMsg) - { - - case WM_PAINT: - return FALSE; - case WM_INITDIALOG: - - n=0;s=0; - - SendMessage(GetDlgItem(hWnd,IDC_DRIVE),CB_RESETCONTENT,0,0); - SendMessage(GetDlgItem(hWnd,IDC_DRIVE),CB_ADDSTRING,0,(LPARAM)"@ (No disc)"); - for(char d='A';d<='Z';d++) - { - if(GetDriveType(path[d-'A'])==DRIVE_CDROM) - { - n++; - - SendMessage(GetDlgItem(hWnd,IDC_DRIVE),CB_ADDSTRING,0,(LPARAM)path[d-'A']); - - if(source_drive==d) - { - s=n; - } - } - } - - SendMessage(GetDlgItem(hWnd,IDC_DRIVE),CB_SETCURSEL,s,0); - - break; - case WM_COMMAND: - wmId = LOWORD(wParam); - wmEvent = HIWORD(wParam); - // Parse the menu selections: - switch (wmId) - { - case IDOK: - GetDlgItemText(hWnd,IDC_DRIVE,temp,20); - temp[19]=0; - source_drive=temp[0]; - - WriteSettings(); - EndDialog(hWnd,0); - break; - case IDCANCEL: - EndDialog(hWnd,0); - break; - - default: - return FALSE; - } - break; - default: - return FALSE; - } - return TRUE; -} - -void configure() -{ - INT_PTR ret; - ReadSettings(); - ret=DialogBoxParam(hinst,MAKEINTRESOURCE(IDD_CONFIG),GetActiveWindow(),(DLGPROC)ConfigProc,1); - if(ret==-1) - { - MessageBoxEx(GetActiveWindow(),"Error Opening the config dialog.","OMG ERROR!",MB_OK,0); - return; - } - ReadSettings(); -} +//GiGaHeRz SPU2 Driver +//Copyright (c) David Quintana +// +//This library 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 Foundation; either +//version 2.1 of the License, or (at your option) any later version. +// +//This library 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 +//Lesser General Public License for more details. +// +//You should have received a copy of the GNU Lesser General Public +//License along with this library; if not, write to the Free Software +//Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +//#include "spu2.h" +#include +#include +#include "resource.h" + +#include "../cdvd.h" + +// Config Vars + +// DEBUG + +char source_drive; +char source_file[MAX_PATH]; + +char CfgFile[MAX_PATH+10] = "inis\\cdvdGigaherz.ini"; + +void CfgSetSettingsDir( const char* dir ) +{ + // a better std::string version, but it's inconvenient for other reasons. + //CfgFile = std::string(( dir == NULL ) ? "inis\\" : dir) + "cdvdGigaherz.ini"; + + // emulator assures a trailing slash/backslash (yay!) + strcpy_s(CfgFile, (dir==NULL) ? "inis\\" : dir); + strcat_s(CfgFile, "cdvdGigaherz.ini"); +} + + + /*| Config File Format: |¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯*\ ++--+---------------------+------------------------+ +| | +| Option=Value | +| | +| | +| Boolean Values: TRUE,YES,1,T,Y mean 'true', | +| everything else means 'false'. | +| | +| All Values are limited to 255 chars. | +| | ++-------------------------------------------------+ + \*_____________________________________________*/ + + +void CfgWriteBool(char *Section, char*Name, char Value) { + char *Data=Value?"TRUE":"FALSE"; + + WritePrivateProfileString(Section,Name,Data,CfgFile); +} + +void CfgWriteInt(char *Section, char*Name, int Value) { + char Data[255]; + _itoa(Value,Data,10); + + WritePrivateProfileString(Section,Name,Data,CfgFile); +} + +void CfgWriteStr(char *Section, char*Name,char *Data) { + WritePrivateProfileString(Section,Name,Data,CfgFile); +} + +/*****************************************************************************/ + +char CfgReadBool(char *Section,char *Name,char Default) { + char Data[255]=""; + GetPrivateProfileString(Section,Name,"",Data,255,CfgFile); + Data[254]=0; + if(strlen(Data)==0) { + CfgWriteBool(Section,Name,Default); + return Default; + } + + if(strcmp(Data,"1")==0) return -1; + if(strcmp(Data,"Y")==0) return -1; + if(strcmp(Data,"T")==0) return -1; + if(strcmp(Data,"YES")==0) return -1; + if(strcmp(Data,"TRUE")==0) return -1; + return 0; +} + +int CfgReadInt(char *Section, char*Name,int Default) { + char Data[255]=""; + GetPrivateProfileString(Section,Name,"",Data,255,CfgFile); + Data[254]=0; + + if(strlen(Data)==0) { + CfgWriteInt(Section,Name,Default); + return Default; + } + + return atoi(Data); +} + +void CfgReadStr(char *Section, char*Name,char *Data,int DataSize,char *Default) { + int sl; + GetPrivateProfileString(Section,Name,"",Data,DataSize,CfgFile); + + if(strlen(Data)==0) { + sl=(int)strlen(Default); + strncpy(Data,Default,sl>255?255:sl); + CfgWriteStr(Section,Name,Data); + } +} + +/*****************************************************************************/ + +void ReadSettings() +{ + char temp[512]; + + CfgReadStr("Config","Source",temp,511,"-"); + source_drive=temp[0]; + + if(source_drive=='$') + strcpy_s(source_file,sizeof(source_file),temp+1); + else + source_file[0]=0; +} + +/*****************************************************************************/ + +void WriteSettings() +{ + char temp[511]; + + temp[0]=source_drive; + temp[1]=0; + + strcat(temp,source_file); + + CfgWriteStr("Config","Source",temp); +} + +char* path[] = { + "A:","B:","C:","D:","E:","F:","G:","H:","I:","J:","K:","L:","M:", + "N:","O:","P:","Q:","R:","S:","T:","U:","V:","W:","X:","Y:","Z:", +}; + +int n,s; + +#define SET_CHECK(idc,value) SendMessage(GetDlgItem(hWnd,idc),BM_SETCHECK,((value)==0)?BST_UNCHECKED:BST_CHECKED,0) +#define HANDLE_CHECK(idc,hvar) case idc: hvar=hvar?0:1; SendMessage(GetDlgItem(hWnd,idc),BM_SETCHECK,(hvar==1)?BST_CHECKED:BST_UNCHECKED,0); break +#define HANDLE_CHECKNB(idc,hvar)case idc: hvar=hvar?0:1; SendMessage(GetDlgItem(hWnd,idc),BM_SETCHECK,(hvar==1)?BST_CHECKED:BST_UNCHECKED,0) +#define ENABLE_CONTROL(idc,value) EnableWindow(GetDlgItem(hWnd,idc),value) + +BOOL CALLBACK ConfigProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam) +{ + int wmId,wmEvent; + char temp[20]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + + switch(uMsg) + { + + case WM_PAINT: + return FALSE; + case WM_INITDIALOG: + + n=0;s=0; + + SendMessage(GetDlgItem(hWnd,IDC_DRIVE),CB_RESETCONTENT,0,0); + SendMessage(GetDlgItem(hWnd,IDC_DRIVE),CB_ADDSTRING,0,(LPARAM)"@ (No disc)"); + for(char d='A';d<='Z';d++) + { + if(GetDriveType(path[d-'A'])==DRIVE_CDROM) + { + n++; + + SendMessage(GetDlgItem(hWnd,IDC_DRIVE),CB_ADDSTRING,0,(LPARAM)path[d-'A']); + + if(source_drive==d) + { + s=n; + } + } + } + + SendMessage(GetDlgItem(hWnd,IDC_DRIVE),CB_SETCURSEL,s,0); + + break; + case WM_COMMAND: + wmId = LOWORD(wParam); + wmEvent = HIWORD(wParam); + // Parse the menu selections: + switch (wmId) + { + case IDOK: + GetDlgItemText(hWnd,IDC_DRIVE,temp,20); + temp[19]=0; + source_drive=temp[0]; + + WriteSettings(); + EndDialog(hWnd,0); + break; + case IDCANCEL: + EndDialog(hWnd,0); + break; + + default: + return FALSE; + } + break; + default: + return FALSE; + } + return TRUE; +} + +void configure() +{ + INT_PTR ret; + ReadSettings(); + ret=DialogBoxParam(hinst,MAKEINTRESOURCE(IDD_CONFIG),GetActiveWindow(),(DLGPROC)ConfigProc,1); + if(ret==-1) + { + MessageBoxEx(GetActiveWindow(),"Error Opening the config dialog.","OMG ERROR!",MB_OK,0); + return; + } + ReadSettings(); +} diff --git a/plugins/cdvdGigaherz/src/Windows/resource.h b/plugins/cdvdGigaherz/src/Windows/resource.h index 062dbc4208..f07e165c5c 100644 --- a/plugins/cdvdGigaherz/src/Windows/resource.h +++ b/plugins/cdvdGigaherz/src/Windows/resource.h @@ -1,37 +1,37 @@ -//{{NO_DEPENDENCIES}} -// Microsoft Visual C++ generated include file. -// Used by cdvd.rc -// -#define IDD_CONFIG 101 -#define IDC_PLUGINS 1000 -#define IDC_CONFIGURE 1001 -#define IDC_TEST 1002 -#define IDC_ABOUT 1003 -#define IDC_IMAGE_BROWSE 1004 -#define IDC_USEISO 1005 -#define IDC_NOCD 1006 -#define IDC_PLUGIN 1007 -#define IDC_IMAGE 1008 -#define IDC_IMAGE_FILE 1009 -#define IDC_NOSUB 1010 -#define IDC_FAKE 1011 -#define IDC_SAME 1012 -#define IDC_DRIVE 1012 -#define IDC_SUB_FILE 1014 -#define IDC_ABOUT3 1015 -#define IDC_SUB_BROWSE 1015 -#define IDC_FILE 1016 -#define IDC_NODUMP 1020 -#define IDC_DUMP_ISO 1021 -#define IDC_DUMP_BDV2 1022 - -// Next default values for new objects -// -#ifdef APSTUDIO_INVOKED -#ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 102 -#define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1011 -#define _APS_NEXT_SYMED_VALUE 101 -#endif -#endif +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by cdvd.rc +// +#define IDD_CONFIG 101 +#define IDC_PLUGINS 1000 +#define IDC_CONFIGURE 1001 +#define IDC_TEST 1002 +#define IDC_ABOUT 1003 +#define IDC_IMAGE_BROWSE 1004 +#define IDC_USEISO 1005 +#define IDC_NOCD 1006 +#define IDC_PLUGIN 1007 +#define IDC_IMAGE 1008 +#define IDC_IMAGE_FILE 1009 +#define IDC_NOSUB 1010 +#define IDC_FAKE 1011 +#define IDC_SAME 1012 +#define IDC_DRIVE 1012 +#define IDC_SUB_FILE 1014 +#define IDC_ABOUT3 1015 +#define IDC_SUB_BROWSE 1015 +#define IDC_FILE 1016 +#define IDC_NODUMP 1020 +#define IDC_DUMP_ISO 1021 +#define IDC_DUMP_BDV2 1022 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1011 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/plugins/cdvdGigaherz/src/Windows/rosddk/ntddcdrm.h b/plugins/cdvdGigaherz/src/Windows/rosddk/ntddcdrm.h index c430cb7c1c..ae03b4aa95 100644 --- a/plugins/cdvdGigaherz/src/Windows/rosddk/ntddcdrm.h +++ b/plugins/cdvdGigaherz/src/Windows/rosddk/ntddcdrm.h @@ -1,348 +1,348 @@ -/* - * ntddcdrm.h - * - * CDROM IOCTL interface. - * - * This file is part of the w32api package. - * - * Contributors: - * Created by Casper S. Hornstrup - * - * THIS SOFTWARE IS NOT COPYRIGHTED - * - * This source code is offered for use in the public domain. You may - * use, modify or distribute it freely. - * - * This code is distributed in the hope that it will be useful but - * WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY - * DISCLAIMED. This includes but is not limited to warranties of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * - */ - -#ifndef _NTDDCDRM_H_ -#define _NTDDCDRM_H_ - -#include "ntddstor.h" - -#define _ANONYMOUS_UNION -#define DUMMYUNIONNAME - -#ifdef __cplusplus -extern "C" { -#endif - -#define IOCTL_CDROM_BASE FILE_DEVICE_CD_ROM - -#define IOCTL_CDROM_CHECK_VERIFY \ - CTL_CODE(IOCTL_CDROM_BASE, 0x0200, METHOD_BUFFERED, FILE_READ_ACCESS) - -#define IOCTL_CDROM_FIND_NEW_DEVICES \ - CTL_CODE(IOCTL_CDROM_BASE, 0x0206, METHOD_BUFFERED, FILE_READ_ACCESS) - -#define IOCTL_CDROM_GET_CONTROL \ - CTL_CODE(IOCTL_CDROM_BASE, 0x000D, METHOD_BUFFERED, FILE_READ_ACCESS) - -#define IOCTL_CDROM_GET_DRIVE_GEOMETRY \ - CTL_CODE(IOCTL_CDROM_BASE, 0x0013, METHOD_BUFFERED, FILE_READ_ACCESS) - -#define IOCTL_CDROM_GET_LAST_SESSION \ - CTL_CODE(IOCTL_CDROM_BASE, 0x000E, METHOD_BUFFERED, FILE_READ_ACCESS) - -#define IOCTL_CDROM_GET_VOLUME \ - CTL_CODE(IOCTL_CDROM_BASE, 0x0005, METHOD_BUFFERED, FILE_READ_ACCESS) - -#define IOCTL_CDROM_PAUSE_AUDIO \ - CTL_CODE(IOCTL_CDROM_BASE, 0x0003, METHOD_BUFFERED, FILE_READ_ACCESS) - -#define IOCTL_CDROM_PLAY_AUDIO_MSF \ - CTL_CODE(IOCTL_CDROM_BASE, 0x0006, METHOD_BUFFERED, FILE_READ_ACCESS) - -#define IOCTL_CDROM_RAW_READ \ - CTL_CODE(IOCTL_CDROM_BASE, 0x000F, METHOD_OUT_DIRECT, FILE_READ_ACCESS) - -#define IOCTL_CDROM_DISK_TYPE \ - CTL_CODE(IOCTL_CDROM_BASE, 0x0010, METHOD_BUFFERED, FILE_ANY_ACCESS) - -#define IOCTL_CDROM_READ_Q_CHANNEL \ - CTL_CODE(IOCTL_CDROM_BASE, 0x000B, METHOD_BUFFERED, FILE_READ_ACCESS) - -#define IOCTL_CDROM_READ_TOC \ - CTL_CODE(IOCTL_CDROM_BASE, 0x0000, METHOD_BUFFERED, FILE_READ_ACCESS) - -#define IOCTL_CDROM_READ_TOC_EX \ - CTL_CODE(IOCTL_CDROM_BASE, 0x0015, METHOD_BUFFERED, FILE_READ_ACCESS) - -#define IOCTL_CDROM_RESUME_AUDIO \ - CTL_CODE(IOCTL_CDROM_BASE, 0x0004, METHOD_BUFFERED, FILE_READ_ACCESS) - -#define IOCTL_CDROM_SEEK_AUDIO_MSF \ - CTL_CODE(IOCTL_CDROM_BASE, 0x0001, METHOD_BUFFERED, FILE_READ_ACCESS) - -#define IOCTL_CDROM_SET_VOLUME \ - CTL_CODE(IOCTL_CDROM_BASE, 0x000A, METHOD_BUFFERED, FILE_READ_ACCESS) - -#define IOCTL_CDROM_SIMBAD \ - CTL_CODE(IOCTL_CDROM_BASE, 0x1003, METHOD_BUFFERED, FILE_READ_ACCESS) - -#define IOCTL_CDROM_STOP_AUDIO \ - CTL_CODE(IOCTL_CDROM_BASE, 0x0002, METHOD_BUFFERED, FILE_READ_ACCESS) - - -#define MAXIMUM_NUMBER_TRACKS 100 -#define MAXIMUM_CDROM_SIZE 804 -#define MINIMUM_CDROM_READ_TOC_EX_SIZE 2 - -typedef struct _TRACK_DATA { - UCHAR Reserved; - UCHAR Control : 4; - UCHAR Adr : 4; - UCHAR TrackNumber; - UCHAR Reserved1; - UCHAR Address[4]; -} TRACK_DATA, *PTRACK_DATA; - -/* CDROM_DISK_DATA.DiskData flags */ -#define CDROM_DISK_AUDIO_TRACK 0x00000001 -#define CDROM_DISK_DATA_TRACK 0x00000002 - -typedef struct _CDROM_DISK_DATA { - ULONG DiskData; -} CDROM_DISK_DATA, *PCDROM_DISK_DATA; - -typedef struct _CDROM_PLAY_AUDIO_MSF { - UCHAR StartingM; - UCHAR StartingS; - UCHAR StartingF; - UCHAR EndingM; - UCHAR EndingS; - UCHAR EndingF; -} CDROM_PLAY_AUDIO_MSF, *PCDROM_PLAY_AUDIO_MSF; - -/* CDROM_READ_TOC_EX.Format constants */ -#define CDROM_READ_TOC_EX_FORMAT_TOC 0x00 -#define CDROM_READ_TOC_EX_FORMAT_SESSION 0x01 -#define CDROM_READ_TOC_EX_FORMAT_FULL_TOC 0x02 -#define CDROM_READ_TOC_EX_FORMAT_PMA 0x03 -#define CDROM_READ_TOC_EX_FORMAT_ATIP 0x04 -#define CDROM_READ_TOC_EX_FORMAT_CDTEXT 0x05 - -typedef struct _CDROM_READ_TOC_EX { - UCHAR Format : 4; - UCHAR Reserved1 : 3; - UCHAR Msf : 1; - UCHAR SessionTrack; - UCHAR Reserved2; - UCHAR Reserved3; -} CDROM_READ_TOC_EX, *PCDROM_READ_TOC_EX; - -typedef struct _CDROM_SEEK_AUDIO_MSF { - UCHAR M; - UCHAR S; - UCHAR F; -} CDROM_SEEK_AUDIO_MSF, *PCDROM_SEEK_AUDIO_MSF; - -/* CDROM_SUB_Q_DATA_FORMAT.Format constants */ -#define IOCTL_CDROM_SUB_Q_CHANNEL 0x00 -#define IOCTL_CDROM_CURRENT_POSITION 0x01 -#define IOCTL_CDROM_MEDIA_CATALOG 0x02 -#define IOCTL_CDROM_TRACK_ISRC 0x03 - -typedef struct _CDROM_SUB_Q_DATA_FORMAT { - UCHAR Format; - UCHAR Track; -} CDROM_SUB_Q_DATA_FORMAT, *PCDROM_SUB_Q_DATA_FORMAT; - -typedef struct _CDROM_TOC { - UCHAR Length[2]; - UCHAR FirstTrack; - UCHAR LastTrack; - TRACK_DATA TrackData[MAXIMUM_NUMBER_TRACKS]; -} CDROM_TOC, *PCDROM_TOC; - -#define CDROM_TOC_SIZE sizeof(CDROM_TOC) - -typedef struct _CDROM_TOC_ATIP_DATA_BLOCK { - UCHAR CdrwReferenceSpeed : 3; - UCHAR Reserved3 : 1; - UCHAR WritePower : 3; - UCHAR True1 : 1; - UCHAR Reserved4 : 6; - UCHAR UnrestrictedUse : 1; - UCHAR Reserved5 : 1; - UCHAR A3Valid : 1; - UCHAR A2Valid : 1; - UCHAR A1Valid : 1; - UCHAR Reserved6 : 3; - UCHAR IsCdrw : 1; - UCHAR True2 : 1; - UCHAR Reserved7; - UCHAR LeadInMsf[3]; - UCHAR Reserved8; - UCHAR LeadOutMsf[3]; - UCHAR Reserved9; - UCHAR A1Values[3]; - UCHAR Reserved10; - UCHAR A2Values[3]; - UCHAR Reserved11; - UCHAR A3Values[3]; - UCHAR Reserved12; -} CDROM_TOC_ATIP_DATA_BLOCK, *PCDROM_TOC_ATIP_DATA_BLOCK; - -typedef struct _CDROM_TOC_ATIP_DATA { - UCHAR Length[2]; - UCHAR Reserved1; - UCHAR Reserved2; - CDROM_TOC_ATIP_DATA_BLOCK Descriptors[0]; -} CDROM_TOC_ATIP_DATA, *PCDROM_TOC_ATIP_DATA; - -/* CDROM_TOC_CD_TEXT_DATA_BLOCK.PackType constants */ -#define CDROM_CD_TEXT_PACK_ALBUM_NAME 0x80 -#define CDROM_CD_TEXT_PACK_PERFORMER 0x81 -#define CDROM_CD_TEXT_PACK_SONGWRITER 0x82 -#define CDROM_CD_TEXT_PACK_COMPOSER 0x83 -#define CDROM_CD_TEXT_PACK_ARRANGER 0x84 -#define CDROM_CD_TEXT_PACK_MESSAGES 0x85 -#define CDROM_CD_TEXT_PACK_DISC_ID 0x86 -#define CDROM_CD_TEXT_PACK_GENRE 0x87 -#define CDROM_CD_TEXT_PACK_TOC_INFO 0x88 -#define CDROM_CD_TEXT_PACK_TOC_INFO2 0x89 -#define CDROM_CD_TEXT_PACK_UPC_EAN 0x8e -#define CDROM_CD_TEXT_PACK_SIZE_INFO 0x8f - -typedef struct _CDROM_TOC_CD_TEXT_DATA_BLOCK { - UCHAR PackType; - UCHAR TrackNumber : 7; - UCHAR ExtensionFlag : 1; - UCHAR SequenceNumber; - UCHAR CharacterPosition : 4; - UCHAR BlockNumber : 3; - UCHAR Unicode : 1; - _ANONYMOUS_UNION union { - UCHAR Text[12]; - WCHAR WText[6]; - } DUMMYUNIONNAME; - UCHAR CRC[2]; -} CDROM_TOC_CD_TEXT_DATA_BLOCK, *PCDROM_TOC_CD_TEXT_DATA_BLOCK; - -typedef struct _CDROM_TOC_CD_TEXT_DATA { - UCHAR Length[2]; - UCHAR Reserved1; - UCHAR Reserved2; - CDROM_TOC_CD_TEXT_DATA_BLOCK Descriptors[0]; -} CDROM_TOC_CD_TEXT_DATA, *PCDROM_TOC_CD_TEXT_DATA; - -/* CDROM_TOC_FULL_TOC_DATA_BLOCK.Adr constants */ -#define ADR_NO_MODE_INFORMATION 0x0 -#define ADR_ENCODES_CURRENT_POSITION 0x1 -#define ADR_ENCODES_MEDIA_CATALOG 0x2 -#define ADR_ENCODES_ISRC 0x3 - -typedef struct _CDROM_TOC_FULL_TOC_DATA_BLOCK { - UCHAR SessionNumber; - UCHAR Control : 4; - UCHAR Adr : 4; - UCHAR Reserved1; - UCHAR Point; - UCHAR MsfExtra[3]; - UCHAR Zero; - UCHAR Msf[3]; -} CDROM_TOC_FULL_TOC_DATA_BLOCK, *PCDROM_TOC_FULL_TOC_DATA_BLOCK; - -typedef struct _CDROM_TOC_FULL_TOC_DATA { - UCHAR Length[2]; - UCHAR FirstCompleteSession; - UCHAR LastCompleteSession; - CDROM_TOC_FULL_TOC_DATA_BLOCK Descriptors[0]; -} CDROM_TOC_FULL_TOC_DATA, *PCDROM_TOC_FULL_TOC_DATA; - -typedef struct _CDROM_TOC_PMA_DATA { - UCHAR Length[2]; - UCHAR Reserved1; - UCHAR Reserved2; - CDROM_TOC_FULL_TOC_DATA_BLOCK Descriptors[0]; -} CDROM_TOC_PMA_DATA, *PCDROM_TOC_PMA_DATA; - -/* SUB_Q_HEADER.AudioStatus constants */ -#define AUDIO_STATUS_NOT_SUPPORTED 0x00 -#define AUDIO_STATUS_IN_PROGRESS 0x11 -#define AUDIO_STATUS_PAUSED 0x12 -#define AUDIO_STATUS_PLAY_COMPLETE 0x13 -#define AUDIO_STATUS_PLAY_ERROR 0x14 -#define AUDIO_STATUS_NO_STATUS 0x15 - -typedef struct _SUB_Q_HEADER { - UCHAR Reserved; - UCHAR AudioStatus; - UCHAR DataLength[2]; -} SUB_Q_HEADER, *PSUB_Q_HEADER; - -typedef struct _SUB_Q_MEDIA_CATALOG_NUMBER { - SUB_Q_HEADER Header; - UCHAR FormatCode; - UCHAR Reserved[3]; - UCHAR Reserved1 : 7; - UCHAR Mcval :1; - UCHAR MediaCatalog[15]; -} SUB_Q_MEDIA_CATALOG_NUMBER, *PSUB_Q_MEDIA_CATALOG_NUMBER; - -typedef struct _SUB_Q_TRACK_ISRC { - SUB_Q_HEADER Header; - UCHAR FormatCode; - UCHAR Reserved0; - UCHAR Track; - UCHAR Reserved1; - UCHAR Reserved2 : 7; - UCHAR Tcval : 1; - UCHAR TrackIsrc[15]; -} SUB_Q_TRACK_ISRC, *PSUB_Q_TRACK_ISRC; - -typedef struct _SUB_Q_CURRENT_POSITION { - SUB_Q_HEADER Header; - UCHAR FormatCode; - UCHAR Control : 4; - UCHAR ADR : 4; - UCHAR TrackNumber; - UCHAR IndexNumber; - UCHAR AbsoluteAddress[4]; - UCHAR TrackRelativeAddress[4]; -} SUB_Q_CURRENT_POSITION, *PSUB_Q_CURRENT_POSITION; - -typedef union _SUB_Q_CHANNEL_DATA { - SUB_Q_CURRENT_POSITION CurrentPosition; - SUB_Q_MEDIA_CATALOG_NUMBER MediaCatalog; - SUB_Q_TRACK_ISRC TrackIsrc; -} SUB_Q_CHANNEL_DATA, *PSUB_Q_CHANNEL_DATA; - -/* CDROM_AUDIO_CONTROL.LbaFormat constants */ -#define AUDIO_WITH_PREEMPHASIS 0x1 -#define DIGITAL_COPY_PERMITTED 0x2 -#define AUDIO_DATA_TRACK 0x4 -#define TWO_FOUR_CHANNEL_AUDIO 0x8 - -typedef struct _CDROM_AUDIO_CONTROL { - UCHAR LbaFormat; - USHORT LogicalBlocksPerSecond; -} CDROM_AUDIO_CONTROL, *PCDROM_AUDIO_CONTROL; - -typedef struct _VOLUME_CONTROL { - UCHAR PortVolume[4]; -} VOLUME_CONTROL, *PVOLUME_CONTROL; - -typedef enum _TRACK_MODE_TYPE { - YellowMode2, - XAForm2, - CDDA -} TRACK_MODE_TYPE, *PTRACK_MODE_TYPE; - -typedef struct __RAW_READ_INFO { - LARGE_INTEGER DiskOffset; - ULONG SectorCount; - TRACK_MODE_TYPE TrackMode; -} RAW_READ_INFO, *PRAW_READ_INFO; - -#ifdef __cplusplus -} -#endif - -#endif /* _NTDDCDRM_H_ */ +/* + * ntddcdrm.h + * + * CDROM IOCTL interface. + * + * This file is part of the w32api package. + * + * Contributors: + * Created by Casper S. Hornstrup + * + * THIS SOFTWARE IS NOT COPYRIGHTED + * + * This source code is offered for use in the public domain. You may + * use, modify or distribute it freely. + * + * This code is distributed in the hope that it will be useful but + * WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY + * DISCLAIMED. This includes but is not limited to warranties of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + */ + +#ifndef _NTDDCDRM_H_ +#define _NTDDCDRM_H_ + +#include "ntddstor.h" + +#define _ANONYMOUS_UNION +#define DUMMYUNIONNAME + +#ifdef __cplusplus +extern "C" { +#endif + +#define IOCTL_CDROM_BASE FILE_DEVICE_CD_ROM + +#define IOCTL_CDROM_CHECK_VERIFY \ + CTL_CODE(IOCTL_CDROM_BASE, 0x0200, METHOD_BUFFERED, FILE_READ_ACCESS) + +#define IOCTL_CDROM_FIND_NEW_DEVICES \ + CTL_CODE(IOCTL_CDROM_BASE, 0x0206, METHOD_BUFFERED, FILE_READ_ACCESS) + +#define IOCTL_CDROM_GET_CONTROL \ + CTL_CODE(IOCTL_CDROM_BASE, 0x000D, METHOD_BUFFERED, FILE_READ_ACCESS) + +#define IOCTL_CDROM_GET_DRIVE_GEOMETRY \ + CTL_CODE(IOCTL_CDROM_BASE, 0x0013, METHOD_BUFFERED, FILE_READ_ACCESS) + +#define IOCTL_CDROM_GET_LAST_SESSION \ + CTL_CODE(IOCTL_CDROM_BASE, 0x000E, METHOD_BUFFERED, FILE_READ_ACCESS) + +#define IOCTL_CDROM_GET_VOLUME \ + CTL_CODE(IOCTL_CDROM_BASE, 0x0005, METHOD_BUFFERED, FILE_READ_ACCESS) + +#define IOCTL_CDROM_PAUSE_AUDIO \ + CTL_CODE(IOCTL_CDROM_BASE, 0x0003, METHOD_BUFFERED, FILE_READ_ACCESS) + +#define IOCTL_CDROM_PLAY_AUDIO_MSF \ + CTL_CODE(IOCTL_CDROM_BASE, 0x0006, METHOD_BUFFERED, FILE_READ_ACCESS) + +#define IOCTL_CDROM_RAW_READ \ + CTL_CODE(IOCTL_CDROM_BASE, 0x000F, METHOD_OUT_DIRECT, FILE_READ_ACCESS) + +#define IOCTL_CDROM_DISK_TYPE \ + CTL_CODE(IOCTL_CDROM_BASE, 0x0010, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_CDROM_READ_Q_CHANNEL \ + CTL_CODE(IOCTL_CDROM_BASE, 0x000B, METHOD_BUFFERED, FILE_READ_ACCESS) + +#define IOCTL_CDROM_READ_TOC \ + CTL_CODE(IOCTL_CDROM_BASE, 0x0000, METHOD_BUFFERED, FILE_READ_ACCESS) + +#define IOCTL_CDROM_READ_TOC_EX \ + CTL_CODE(IOCTL_CDROM_BASE, 0x0015, METHOD_BUFFERED, FILE_READ_ACCESS) + +#define IOCTL_CDROM_RESUME_AUDIO \ + CTL_CODE(IOCTL_CDROM_BASE, 0x0004, METHOD_BUFFERED, FILE_READ_ACCESS) + +#define IOCTL_CDROM_SEEK_AUDIO_MSF \ + CTL_CODE(IOCTL_CDROM_BASE, 0x0001, METHOD_BUFFERED, FILE_READ_ACCESS) + +#define IOCTL_CDROM_SET_VOLUME \ + CTL_CODE(IOCTL_CDROM_BASE, 0x000A, METHOD_BUFFERED, FILE_READ_ACCESS) + +#define IOCTL_CDROM_SIMBAD \ + CTL_CODE(IOCTL_CDROM_BASE, 0x1003, METHOD_BUFFERED, FILE_READ_ACCESS) + +#define IOCTL_CDROM_STOP_AUDIO \ + CTL_CODE(IOCTL_CDROM_BASE, 0x0002, METHOD_BUFFERED, FILE_READ_ACCESS) + + +#define MAXIMUM_NUMBER_TRACKS 100 +#define MAXIMUM_CDROM_SIZE 804 +#define MINIMUM_CDROM_READ_TOC_EX_SIZE 2 + +typedef struct _TRACK_DATA { + UCHAR Reserved; + UCHAR Control : 4; + UCHAR Adr : 4; + UCHAR TrackNumber; + UCHAR Reserved1; + UCHAR Address[4]; +} TRACK_DATA, *PTRACK_DATA; + +/* CDROM_DISK_DATA.DiskData flags */ +#define CDROM_DISK_AUDIO_TRACK 0x00000001 +#define CDROM_DISK_DATA_TRACK 0x00000002 + +typedef struct _CDROM_DISK_DATA { + ULONG DiskData; +} CDROM_DISK_DATA, *PCDROM_DISK_DATA; + +typedef struct _CDROM_PLAY_AUDIO_MSF { + UCHAR StartingM; + UCHAR StartingS; + UCHAR StartingF; + UCHAR EndingM; + UCHAR EndingS; + UCHAR EndingF; +} CDROM_PLAY_AUDIO_MSF, *PCDROM_PLAY_AUDIO_MSF; + +/* CDROM_READ_TOC_EX.Format constants */ +#define CDROM_READ_TOC_EX_FORMAT_TOC 0x00 +#define CDROM_READ_TOC_EX_FORMAT_SESSION 0x01 +#define CDROM_READ_TOC_EX_FORMAT_FULL_TOC 0x02 +#define CDROM_READ_TOC_EX_FORMAT_PMA 0x03 +#define CDROM_READ_TOC_EX_FORMAT_ATIP 0x04 +#define CDROM_READ_TOC_EX_FORMAT_CDTEXT 0x05 + +typedef struct _CDROM_READ_TOC_EX { + UCHAR Format : 4; + UCHAR Reserved1 : 3; + UCHAR Msf : 1; + UCHAR SessionTrack; + UCHAR Reserved2; + UCHAR Reserved3; +} CDROM_READ_TOC_EX, *PCDROM_READ_TOC_EX; + +typedef struct _CDROM_SEEK_AUDIO_MSF { + UCHAR M; + UCHAR S; + UCHAR F; +} CDROM_SEEK_AUDIO_MSF, *PCDROM_SEEK_AUDIO_MSF; + +/* CDROM_SUB_Q_DATA_FORMAT.Format constants */ +#define IOCTL_CDROM_SUB_Q_CHANNEL 0x00 +#define IOCTL_CDROM_CURRENT_POSITION 0x01 +#define IOCTL_CDROM_MEDIA_CATALOG 0x02 +#define IOCTL_CDROM_TRACK_ISRC 0x03 + +typedef struct _CDROM_SUB_Q_DATA_FORMAT { + UCHAR Format; + UCHAR Track; +} CDROM_SUB_Q_DATA_FORMAT, *PCDROM_SUB_Q_DATA_FORMAT; + +typedef struct _CDROM_TOC { + UCHAR Length[2]; + UCHAR FirstTrack; + UCHAR LastTrack; + TRACK_DATA TrackData[MAXIMUM_NUMBER_TRACKS]; +} CDROM_TOC, *PCDROM_TOC; + +#define CDROM_TOC_SIZE sizeof(CDROM_TOC) + +typedef struct _CDROM_TOC_ATIP_DATA_BLOCK { + UCHAR CdrwReferenceSpeed : 3; + UCHAR Reserved3 : 1; + UCHAR WritePower : 3; + UCHAR True1 : 1; + UCHAR Reserved4 : 6; + UCHAR UnrestrictedUse : 1; + UCHAR Reserved5 : 1; + UCHAR A3Valid : 1; + UCHAR A2Valid : 1; + UCHAR A1Valid : 1; + UCHAR Reserved6 : 3; + UCHAR IsCdrw : 1; + UCHAR True2 : 1; + UCHAR Reserved7; + UCHAR LeadInMsf[3]; + UCHAR Reserved8; + UCHAR LeadOutMsf[3]; + UCHAR Reserved9; + UCHAR A1Values[3]; + UCHAR Reserved10; + UCHAR A2Values[3]; + UCHAR Reserved11; + UCHAR A3Values[3]; + UCHAR Reserved12; +} CDROM_TOC_ATIP_DATA_BLOCK, *PCDROM_TOC_ATIP_DATA_BLOCK; + +typedef struct _CDROM_TOC_ATIP_DATA { + UCHAR Length[2]; + UCHAR Reserved1; + UCHAR Reserved2; + CDROM_TOC_ATIP_DATA_BLOCK Descriptors[0]; +} CDROM_TOC_ATIP_DATA, *PCDROM_TOC_ATIP_DATA; + +/* CDROM_TOC_CD_TEXT_DATA_BLOCK.PackType constants */ +#define CDROM_CD_TEXT_PACK_ALBUM_NAME 0x80 +#define CDROM_CD_TEXT_PACK_PERFORMER 0x81 +#define CDROM_CD_TEXT_PACK_SONGWRITER 0x82 +#define CDROM_CD_TEXT_PACK_COMPOSER 0x83 +#define CDROM_CD_TEXT_PACK_ARRANGER 0x84 +#define CDROM_CD_TEXT_PACK_MESSAGES 0x85 +#define CDROM_CD_TEXT_PACK_DISC_ID 0x86 +#define CDROM_CD_TEXT_PACK_GENRE 0x87 +#define CDROM_CD_TEXT_PACK_TOC_INFO 0x88 +#define CDROM_CD_TEXT_PACK_TOC_INFO2 0x89 +#define CDROM_CD_TEXT_PACK_UPC_EAN 0x8e +#define CDROM_CD_TEXT_PACK_SIZE_INFO 0x8f + +typedef struct _CDROM_TOC_CD_TEXT_DATA_BLOCK { + UCHAR PackType; + UCHAR TrackNumber : 7; + UCHAR ExtensionFlag : 1; + UCHAR SequenceNumber; + UCHAR CharacterPosition : 4; + UCHAR BlockNumber : 3; + UCHAR Unicode : 1; + _ANONYMOUS_UNION union { + UCHAR Text[12]; + WCHAR WText[6]; + } DUMMYUNIONNAME; + UCHAR CRC[2]; +} CDROM_TOC_CD_TEXT_DATA_BLOCK, *PCDROM_TOC_CD_TEXT_DATA_BLOCK; + +typedef struct _CDROM_TOC_CD_TEXT_DATA { + UCHAR Length[2]; + UCHAR Reserved1; + UCHAR Reserved2; + CDROM_TOC_CD_TEXT_DATA_BLOCK Descriptors[0]; +} CDROM_TOC_CD_TEXT_DATA, *PCDROM_TOC_CD_TEXT_DATA; + +/* CDROM_TOC_FULL_TOC_DATA_BLOCK.Adr constants */ +#define ADR_NO_MODE_INFORMATION 0x0 +#define ADR_ENCODES_CURRENT_POSITION 0x1 +#define ADR_ENCODES_MEDIA_CATALOG 0x2 +#define ADR_ENCODES_ISRC 0x3 + +typedef struct _CDROM_TOC_FULL_TOC_DATA_BLOCK { + UCHAR SessionNumber; + UCHAR Control : 4; + UCHAR Adr : 4; + UCHAR Reserved1; + UCHAR Point; + UCHAR MsfExtra[3]; + UCHAR Zero; + UCHAR Msf[3]; +} CDROM_TOC_FULL_TOC_DATA_BLOCK, *PCDROM_TOC_FULL_TOC_DATA_BLOCK; + +typedef struct _CDROM_TOC_FULL_TOC_DATA { + UCHAR Length[2]; + UCHAR FirstCompleteSession; + UCHAR LastCompleteSession; + CDROM_TOC_FULL_TOC_DATA_BLOCK Descriptors[0]; +} CDROM_TOC_FULL_TOC_DATA, *PCDROM_TOC_FULL_TOC_DATA; + +typedef struct _CDROM_TOC_PMA_DATA { + UCHAR Length[2]; + UCHAR Reserved1; + UCHAR Reserved2; + CDROM_TOC_FULL_TOC_DATA_BLOCK Descriptors[0]; +} CDROM_TOC_PMA_DATA, *PCDROM_TOC_PMA_DATA; + +/* SUB_Q_HEADER.AudioStatus constants */ +#define AUDIO_STATUS_NOT_SUPPORTED 0x00 +#define AUDIO_STATUS_IN_PROGRESS 0x11 +#define AUDIO_STATUS_PAUSED 0x12 +#define AUDIO_STATUS_PLAY_COMPLETE 0x13 +#define AUDIO_STATUS_PLAY_ERROR 0x14 +#define AUDIO_STATUS_NO_STATUS 0x15 + +typedef struct _SUB_Q_HEADER { + UCHAR Reserved; + UCHAR AudioStatus; + UCHAR DataLength[2]; +} SUB_Q_HEADER, *PSUB_Q_HEADER; + +typedef struct _SUB_Q_MEDIA_CATALOG_NUMBER { + SUB_Q_HEADER Header; + UCHAR FormatCode; + UCHAR Reserved[3]; + UCHAR Reserved1 : 7; + UCHAR Mcval :1; + UCHAR MediaCatalog[15]; +} SUB_Q_MEDIA_CATALOG_NUMBER, *PSUB_Q_MEDIA_CATALOG_NUMBER; + +typedef struct _SUB_Q_TRACK_ISRC { + SUB_Q_HEADER Header; + UCHAR FormatCode; + UCHAR Reserved0; + UCHAR Track; + UCHAR Reserved1; + UCHAR Reserved2 : 7; + UCHAR Tcval : 1; + UCHAR TrackIsrc[15]; +} SUB_Q_TRACK_ISRC, *PSUB_Q_TRACK_ISRC; + +typedef struct _SUB_Q_CURRENT_POSITION { + SUB_Q_HEADER Header; + UCHAR FormatCode; + UCHAR Control : 4; + UCHAR ADR : 4; + UCHAR TrackNumber; + UCHAR IndexNumber; + UCHAR AbsoluteAddress[4]; + UCHAR TrackRelativeAddress[4]; +} SUB_Q_CURRENT_POSITION, *PSUB_Q_CURRENT_POSITION; + +typedef union _SUB_Q_CHANNEL_DATA { + SUB_Q_CURRENT_POSITION CurrentPosition; + SUB_Q_MEDIA_CATALOG_NUMBER MediaCatalog; + SUB_Q_TRACK_ISRC TrackIsrc; +} SUB_Q_CHANNEL_DATA, *PSUB_Q_CHANNEL_DATA; + +/* CDROM_AUDIO_CONTROL.LbaFormat constants */ +#define AUDIO_WITH_PREEMPHASIS 0x1 +#define DIGITAL_COPY_PERMITTED 0x2 +#define AUDIO_DATA_TRACK 0x4 +#define TWO_FOUR_CHANNEL_AUDIO 0x8 + +typedef struct _CDROM_AUDIO_CONTROL { + UCHAR LbaFormat; + USHORT LogicalBlocksPerSecond; +} CDROM_AUDIO_CONTROL, *PCDROM_AUDIO_CONTROL; + +typedef struct _VOLUME_CONTROL { + UCHAR PortVolume[4]; +} VOLUME_CONTROL, *PVOLUME_CONTROL; + +typedef enum _TRACK_MODE_TYPE { + YellowMode2, + XAForm2, + CDDA +} TRACK_MODE_TYPE, *PTRACK_MODE_TYPE; + +typedef struct __RAW_READ_INFO { + LARGE_INTEGER DiskOffset; + ULONG SectorCount; + TRACK_MODE_TYPE TrackMode; +} RAW_READ_INFO, *PRAW_READ_INFO; + +#ifdef __cplusplus +} +#endif + +#endif /* _NTDDCDRM_H_ */ diff --git a/plugins/cdvdGigaherz/src/Windows/rosddk/ntddcdvd.h b/plugins/cdvdGigaherz/src/Windows/rosddk/ntddcdvd.h index f346f34422..121cc070d6 100644 --- a/plugins/cdvdGigaherz/src/Windows/rosddk/ntddcdvd.h +++ b/plugins/cdvdGigaherz/src/Windows/rosddk/ntddcdvd.h @@ -1,208 +1,208 @@ -/* - * ntddcdvd.h - * - * DVD IOCTL interface. - * - * This file is part of the w32api package. - * - * Contributors: - * Created by Casper S. Hornstrup - * - * THIS SOFTWARE IS NOT COPYRIGHTED - * - * This source code is offered for use in the public domain. You may - * use, modify or distribute it freely. - * - * This code is distributed in the hope that it will be useful but - * WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY - * DISCLAIMED. This includes but is not limited to warranties of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * - */ - -#ifndef _NTDDCDVD_H_ -#define _NTDDCDVD_H_ - -#include "ntddstor.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#define IOCTL_DVD_BASE FILE_DEVICE_DVD - -#define IOCTL_DVD_END_SESSION \ - CTL_CODE(IOCTL_DVD_BASE, 0x0403, METHOD_BUFFERED, FILE_READ_ACCESS) - -#define IOCTL_DVD_GET_REGION \ - CTL_CODE(IOCTL_DVD_BASE, 0x0405, METHOD_BUFFERED, FILE_READ_ACCESS) - -#define IOCTL_DVD_READ_KEY \ - CTL_CODE(IOCTL_DVD_BASE, 0x0401, METHOD_BUFFERED, FILE_READ_ACCESS) - -#define IOCTL_DVD_READ_STRUCTURE \ - CTL_CODE(IOCTL_DVD_BASE, 0x0450, METHOD_BUFFERED, FILE_READ_ACCESS) - -#define IOCTL_DVD_SEND_KEY \ - CTL_CODE(IOCTL_DVD_BASE, 0x0402, METHOD_BUFFERED, FILE_READ_ACCESS) - -#define IOCTL_DVD_START_SESSION \ - CTL_CODE(IOCTL_DVD_BASE, 0x0400, METHOD_BUFFERED, FILE_READ_ACCESS) - -#define IOCTL_DVD_SET_READ_AHEAD \ - CTL_CODE(IOCTL_DVD_BASE, 0x0404, METHOD_BUFFERED, FILE_READ_ACCESS) - - -typedef ULONG DVD_SESSION_ID, *PDVD_SESSION_ID; - -typedef struct _STORAGE_SET_READ_AHEAD { - LARGE_INTEGER TriggerAddress; - LARGE_INTEGER TargetAddress; -} STORAGE_SET_READ_AHEAD, *PSTORAGE_SET_READ_AHEAD; - -typedef enum DVD_STRUCTURE_FORMAT { - DvdPhysicalDescriptor, - DvdCopyrightDescriptor, - DvdDiskKeyDescriptor, - DvdBCADescriptor, - DvdManufacturerDescriptor, - DvdMaxDescriptor -} DVD_STRUCTURE_FORMAT, *PDVD_STRUCTURE_FORMAT; - -#include -typedef struct DVD_READ_STRUCTURE { - LARGE_INTEGER BlockByteOffset; - DVD_STRUCTURE_FORMAT Format; - DVD_SESSION_ID SessionId; - UCHAR LayerNumber; -} DVD_READ_STRUCTURE, *PDVD_READ_STRUCTURE; -#include - -typedef struct _DVD_DESCRIPTOR_HEADER { - USHORT Length; - UCHAR Reserved[2]; - UCHAR Data[0]; -} DVD_DESCRIPTOR_HEADER, *PDVD_DESCRIPTOR_HEADER; - -#include -typedef struct _DVD_LAYER_DESCRIPTOR { - UCHAR BookVersion : 4; - UCHAR BookType : 4; - UCHAR MinimumRate : 4; - UCHAR DiskSize : 4; - UCHAR LayerType : 4; - UCHAR TrackPath : 1; - UCHAR NumberOfLayers : 2; - UCHAR Reserved1 : 1; - UCHAR TrackDensity : 4; - UCHAR LinearDensity : 4; - ULONG StartingDataSector; - ULONG EndDataSector; - ULONG EndLayerZeroSector; - UCHAR Reserved5 : 7; - UCHAR BCAFlag : 1; - UCHAR Reserved6; -} DVD_LAYER_DESCRIPTOR, *PDVD_LAYER_DESCRIPTOR; -#include - -typedef struct _DVD_COPYRIGHT_DESCRIPTOR { - UCHAR CopyrightProtectionType; - UCHAR RegionManagementInformation; - USHORT Reserved; -} DVD_COPYRIGHT_DESCRIPTOR, *PDVD_COPYRIGHT_DESCRIPTOR; - -typedef struct _DVD_DISK_KEY_DESCRIPTOR { - UCHAR DiskKeyData[2048]; -} DVD_DISK_KEY_DESCRIPTOR, *PDVD_DISK_KEY_DESCRIPTOR; - -typedef enum _DVD_KEY_TYPE { - DvdChallengeKey = 0x01, - DvdBusKey1, - DvdBusKey2, - DvdTitleKey, - DvdAsf, - DvdSetRpcKey = 0x6, - DvdGetRpcKey = 0x8, - DvdDiskKey = 0x80, - DvdInvalidateAGID = 0x3f -} DVD_KEY_TYPE; - -typedef struct _DVD_COPY_PROTECT_KEY { - ULONG KeyLength; - DVD_SESSION_ID SessionId; - DVD_KEY_TYPE KeyType; - ULONG KeyFlags; - union { - HANDLE FileHandle; - LARGE_INTEGER TitleOffset; - } Parameters; - UCHAR KeyData[0]; -} DVD_COPY_PROTECT_KEY, *PDVD_COPY_PROTECT_KEY; - -#define DVD_CHALLENGE_KEY_LENGTH (12 + sizeof(DVD_COPY_PROTECT_KEY)) -#define DVD_BUS_KEY_LENGTH (8 + sizeof(DVD_COPY_PROTECT_KEY)) -#define DVD_TITLE_KEY_LENGTH (8 + sizeof(DVD_COPY_PROTECT_KEY)) -#define DVD_DISK_KEY_LENGTH (2048 + sizeof(DVD_COPY_PROTECT_KEY)) -#define DVD_RPC_KEY_LENGTH (sizeof(DVD_RPC_KEY) + sizeof(DVD_COPY_PROTECT_KEY)) -#define DVD_SET_RPC_KEY_LENGTH (sizeof(DVD_SET_RPC_KEY) + sizeof(DVD_COPY_PROTECT_KEY)) -#define DVD_ASF_LENGTH (sizeof(DVD_ASF) + sizeof(DVD_COPY_PROTECT_KEY)) - -#define DVD_END_ALL_SESSIONS ((DVD_SESSION_ID) 0xffffffff) - - -#define DVD_CGMS_RESERVED_MASK 0x00000078 - -#define DVD_CGMS_COPY_PROTECT_MASK 0x00000018 -#define DVD_CGMS_COPY_PERMITTED 0x00000000 -#define DVD_CGMS_COPY_ONCE 0x00000010 -#define DVD_CGMS_NO_COPY 0x00000018 - -#define DVD_COPYRIGHT_MASK 0x00000040 -#define DVD_NOT_COPYRIGHTED 0x00000000 -#define DVD_COPYRIGHTED 0x00000040 - -#define DVD_SECTOR_PROTECT_MASK 0x00000020 -#define DVD_SECTOR_NOT_PROTECTED 0x00000000 -#define DVD_SECTOR_PROTECTED 0x00000020 - - -typedef struct _DVD_BCA_DESCRIPTOR { - UCHAR BCAInformation[0]; -} DVD_BCA_DESCRIPTOR, *PDVD_BCA_DESCRIPTOR; - -typedef struct _DVD_MANUFACTURER_DESCRIPTOR { - UCHAR ManufacturingInformation[2048]; -} DVD_MANUFACTURER_DESCRIPTOR, *PDVD_MANUFACTURER_DESCRIPTOR; - -typedef struct _DVD_RPC_KEY { - UCHAR UserResetsAvailable : 3; - UCHAR ManufacturerResetsAvailable : 3; - UCHAR TypeCode : 2; - UCHAR RegionMask; - UCHAR RpcScheme; - UCHAR Reserved2[1]; -} DVD_RPC_KEY, *PDVD_RPC_KEY; - -typedef struct _DVD_SET_RPC_KEY { - UCHAR PreferredDriveRegionCode; - UCHAR Reserved[3]; -} DVD_SET_RPC_KEY, *PDVD_SET_RPC_KEY; - -typedef struct _DVD_ASF { - UCHAR Reserved0[3]; - UCHAR SuccessFlag : 1; - UCHAR Reserved1 : 7; -} DVD_ASF, *PDVD_ASF; - -typedef struct _DVD_REGION { - UCHAR CopySystem; - UCHAR RegionData; - UCHAR SystemRegion; - UCHAR ResetCount; -} DVD_REGION, *PDVD_REGION; - -#ifdef __cplusplus -} -#endif - -#endif /* _NTDDCDVD_H_ */ +/* + * ntddcdvd.h + * + * DVD IOCTL interface. + * + * This file is part of the w32api package. + * + * Contributors: + * Created by Casper S. Hornstrup + * + * THIS SOFTWARE IS NOT COPYRIGHTED + * + * This source code is offered for use in the public domain. You may + * use, modify or distribute it freely. + * + * This code is distributed in the hope that it will be useful but + * WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY + * DISCLAIMED. This includes but is not limited to warranties of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + */ + +#ifndef _NTDDCDVD_H_ +#define _NTDDCDVD_H_ + +#include "ntddstor.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define IOCTL_DVD_BASE FILE_DEVICE_DVD + +#define IOCTL_DVD_END_SESSION \ + CTL_CODE(IOCTL_DVD_BASE, 0x0403, METHOD_BUFFERED, FILE_READ_ACCESS) + +#define IOCTL_DVD_GET_REGION \ + CTL_CODE(IOCTL_DVD_BASE, 0x0405, METHOD_BUFFERED, FILE_READ_ACCESS) + +#define IOCTL_DVD_READ_KEY \ + CTL_CODE(IOCTL_DVD_BASE, 0x0401, METHOD_BUFFERED, FILE_READ_ACCESS) + +#define IOCTL_DVD_READ_STRUCTURE \ + CTL_CODE(IOCTL_DVD_BASE, 0x0450, METHOD_BUFFERED, FILE_READ_ACCESS) + +#define IOCTL_DVD_SEND_KEY \ + CTL_CODE(IOCTL_DVD_BASE, 0x0402, METHOD_BUFFERED, FILE_READ_ACCESS) + +#define IOCTL_DVD_START_SESSION \ + CTL_CODE(IOCTL_DVD_BASE, 0x0400, METHOD_BUFFERED, FILE_READ_ACCESS) + +#define IOCTL_DVD_SET_READ_AHEAD \ + CTL_CODE(IOCTL_DVD_BASE, 0x0404, METHOD_BUFFERED, FILE_READ_ACCESS) + + +typedef ULONG DVD_SESSION_ID, *PDVD_SESSION_ID; + +typedef struct _STORAGE_SET_READ_AHEAD { + LARGE_INTEGER TriggerAddress; + LARGE_INTEGER TargetAddress; +} STORAGE_SET_READ_AHEAD, *PSTORAGE_SET_READ_AHEAD; + +typedef enum DVD_STRUCTURE_FORMAT { + DvdPhysicalDescriptor, + DvdCopyrightDescriptor, + DvdDiskKeyDescriptor, + DvdBCADescriptor, + DvdManufacturerDescriptor, + DvdMaxDescriptor +} DVD_STRUCTURE_FORMAT, *PDVD_STRUCTURE_FORMAT; + +#include +typedef struct DVD_READ_STRUCTURE { + LARGE_INTEGER BlockByteOffset; + DVD_STRUCTURE_FORMAT Format; + DVD_SESSION_ID SessionId; + UCHAR LayerNumber; +} DVD_READ_STRUCTURE, *PDVD_READ_STRUCTURE; +#include + +typedef struct _DVD_DESCRIPTOR_HEADER { + USHORT Length; + UCHAR Reserved[2]; + UCHAR Data[0]; +} DVD_DESCRIPTOR_HEADER, *PDVD_DESCRIPTOR_HEADER; + +#include +typedef struct _DVD_LAYER_DESCRIPTOR { + UCHAR BookVersion : 4; + UCHAR BookType : 4; + UCHAR MinimumRate : 4; + UCHAR DiskSize : 4; + UCHAR LayerType : 4; + UCHAR TrackPath : 1; + UCHAR NumberOfLayers : 2; + UCHAR Reserved1 : 1; + UCHAR TrackDensity : 4; + UCHAR LinearDensity : 4; + ULONG StartingDataSector; + ULONG EndDataSector; + ULONG EndLayerZeroSector; + UCHAR Reserved5 : 7; + UCHAR BCAFlag : 1; + UCHAR Reserved6; +} DVD_LAYER_DESCRIPTOR, *PDVD_LAYER_DESCRIPTOR; +#include + +typedef struct _DVD_COPYRIGHT_DESCRIPTOR { + UCHAR CopyrightProtectionType; + UCHAR RegionManagementInformation; + USHORT Reserved; +} DVD_COPYRIGHT_DESCRIPTOR, *PDVD_COPYRIGHT_DESCRIPTOR; + +typedef struct _DVD_DISK_KEY_DESCRIPTOR { + UCHAR DiskKeyData[2048]; +} DVD_DISK_KEY_DESCRIPTOR, *PDVD_DISK_KEY_DESCRIPTOR; + +typedef enum _DVD_KEY_TYPE { + DvdChallengeKey = 0x01, + DvdBusKey1, + DvdBusKey2, + DvdTitleKey, + DvdAsf, + DvdSetRpcKey = 0x6, + DvdGetRpcKey = 0x8, + DvdDiskKey = 0x80, + DvdInvalidateAGID = 0x3f +} DVD_KEY_TYPE; + +typedef struct _DVD_COPY_PROTECT_KEY { + ULONG KeyLength; + DVD_SESSION_ID SessionId; + DVD_KEY_TYPE KeyType; + ULONG KeyFlags; + union { + HANDLE FileHandle; + LARGE_INTEGER TitleOffset; + } Parameters; + UCHAR KeyData[0]; +} DVD_COPY_PROTECT_KEY, *PDVD_COPY_PROTECT_KEY; + +#define DVD_CHALLENGE_KEY_LENGTH (12 + sizeof(DVD_COPY_PROTECT_KEY)) +#define DVD_BUS_KEY_LENGTH (8 + sizeof(DVD_COPY_PROTECT_KEY)) +#define DVD_TITLE_KEY_LENGTH (8 + sizeof(DVD_COPY_PROTECT_KEY)) +#define DVD_DISK_KEY_LENGTH (2048 + sizeof(DVD_COPY_PROTECT_KEY)) +#define DVD_RPC_KEY_LENGTH (sizeof(DVD_RPC_KEY) + sizeof(DVD_COPY_PROTECT_KEY)) +#define DVD_SET_RPC_KEY_LENGTH (sizeof(DVD_SET_RPC_KEY) + sizeof(DVD_COPY_PROTECT_KEY)) +#define DVD_ASF_LENGTH (sizeof(DVD_ASF) + sizeof(DVD_COPY_PROTECT_KEY)) + +#define DVD_END_ALL_SESSIONS ((DVD_SESSION_ID) 0xffffffff) + + +#define DVD_CGMS_RESERVED_MASK 0x00000078 + +#define DVD_CGMS_COPY_PROTECT_MASK 0x00000018 +#define DVD_CGMS_COPY_PERMITTED 0x00000000 +#define DVD_CGMS_COPY_ONCE 0x00000010 +#define DVD_CGMS_NO_COPY 0x00000018 + +#define DVD_COPYRIGHT_MASK 0x00000040 +#define DVD_NOT_COPYRIGHTED 0x00000000 +#define DVD_COPYRIGHTED 0x00000040 + +#define DVD_SECTOR_PROTECT_MASK 0x00000020 +#define DVD_SECTOR_NOT_PROTECTED 0x00000000 +#define DVD_SECTOR_PROTECTED 0x00000020 + + +typedef struct _DVD_BCA_DESCRIPTOR { + UCHAR BCAInformation[0]; +} DVD_BCA_DESCRIPTOR, *PDVD_BCA_DESCRIPTOR; + +typedef struct _DVD_MANUFACTURER_DESCRIPTOR { + UCHAR ManufacturingInformation[2048]; +} DVD_MANUFACTURER_DESCRIPTOR, *PDVD_MANUFACTURER_DESCRIPTOR; + +typedef struct _DVD_RPC_KEY { + UCHAR UserResetsAvailable : 3; + UCHAR ManufacturerResetsAvailable : 3; + UCHAR TypeCode : 2; + UCHAR RegionMask; + UCHAR RpcScheme; + UCHAR Reserved2[1]; +} DVD_RPC_KEY, *PDVD_RPC_KEY; + +typedef struct _DVD_SET_RPC_KEY { + UCHAR PreferredDriveRegionCode; + UCHAR Reserved[3]; +} DVD_SET_RPC_KEY, *PDVD_SET_RPC_KEY; + +typedef struct _DVD_ASF { + UCHAR Reserved0[3]; + UCHAR SuccessFlag : 1; + UCHAR Reserved1 : 7; +} DVD_ASF, *PDVD_ASF; + +typedef struct _DVD_REGION { + UCHAR CopySystem; + UCHAR RegionData; + UCHAR SystemRegion; + UCHAR ResetCount; +} DVD_REGION, *PDVD_REGION; + +#ifdef __cplusplus +} +#endif + +#endif /* _NTDDCDVD_H_ */ diff --git a/plugins/cdvdGigaherz/src/Windows/rosddk/ntddscsi.h b/plugins/cdvdGigaherz/src/Windows/rosddk/ntddscsi.h index 0cf785e7ef..5f925ef843 100644 --- a/plugins/cdvdGigaherz/src/Windows/rosddk/ntddscsi.h +++ b/plugins/cdvdGigaherz/src/Windows/rosddk/ntddscsi.h @@ -1,171 +1,171 @@ -/* - * ntddscsi.h - * - * SCSI port IOCTL interface. - * - * This file is part of the w32api package. - * - * Contributors: - * Created by Casper S. Hornstrup - * - * THIS SOFTWARE IS NOT COPYRIGHTED - * - * This source code is offered for use in the public domain. You may - * use, modify or distribute it freely. - * - * This code is distributed in the hope that it will be useful but - * WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY - * DISCLAIMED. This includes but is not limited to warranties of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * - */ - -#ifndef __NTDDSCSI_H -#define __NTDDSCSI_H - -#ifdef __cplusplus -extern "C" { -#endif - -#define DD_SCSI_DEVICE_NAME "\\Device\\ScsiPort" -#define DD_SCSI_DEVICE_NAME_U L"\\Device\\ScsiPort" - -#define IOCTL_SCSI_BASE FILE_DEVICE_CONTROLLER - -#define IOCTL_SCSI_FREE_DUMP_POINTERS \ - CTL_CODE(IOCTL_SCSI_BASE, 0x0409, METHOD_BUFFERED, FILE_ANY_ACCESS) - -#define IOCTL_SCSI_GET_INQUIRY_DATA \ - CTL_CODE(IOCTL_SCSI_BASE, 0x0403, METHOD_BUFFERED, FILE_ANY_ACCESS) - -#define IOCTL_SCSI_GET_CAPABILITIES \ - CTL_CODE(IOCTL_SCSI_BASE, 0x0404, METHOD_BUFFERED, FILE_ANY_ACCESS) - -#define IOCTL_SCSI_GET_ADDRESS \ - CTL_CODE(IOCTL_SCSI_BASE, 0x0406, METHOD_BUFFERED, FILE_ANY_ACCESS) - -#define IOCTL_SCSI_GET_DUMP_POINTERS \ - CTL_CODE(IOCTL_SCSI_BASE, 0x0408, METHOD_BUFFERED, FILE_ANY_ACCESS) - -#define IOCTL_SCSI_MINIPORT \ - CTL_CODE(IOCTL_SCSI_BASE, 0x0402, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) - -#define IOCTL_SCSI_PASS_THROUGH \ - CTL_CODE(IOCTL_SCSI_BASE, 0x0401, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) - -#define IOCTL_SCSI_PASS_THROUGH_DIRECT \ - CTL_CODE(IOCTL_SCSI_BASE, 0x0405, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) - -#define IOCTL_SCSI_RESCAN_BUS \ - CTL_CODE(IOCTL_SCSI_BASE, 0x0407, METHOD_BUFFERED, FILE_ANY_ACCESS) - - -DEFINE_GUID(ScsiRawInterfaceGuid, \ - 0x53f56309L, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b); - -DEFINE_GUID(WmiScsiAddressGuid, \ - 0x53f5630fL, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b); - -typedef struct _SCSI_PASS_THROUGH { - USHORT Length; - UCHAR ScsiStatus; - UCHAR PathId; - UCHAR TargetId; - UCHAR Lun; - UCHAR CdbLength; - UCHAR SenseInfoLength; - UCHAR DataIn; - ULONG DataTransferLength; - ULONG TimeOutValue; - ULONG_PTR DataBufferOffset; - ULONG SenseInfoOffset; - UCHAR Cdb[16]; -} SCSI_PASS_THROUGH, *PSCSI_PASS_THROUGH; - -typedef struct _SCSI_PASS_THROUGH_DIRECT { - USHORT Length; - UCHAR ScsiStatus; - UCHAR PathId; - UCHAR TargetId; - UCHAR Lun; - UCHAR CdbLength; - UCHAR SenseInfoLength; - UCHAR DataIn; - ULONG DataTransferLength; - ULONG TimeOutValue; - PVOID DataBuffer; - ULONG SenseInfoOffset; - UCHAR Cdb[16]; -} SCSI_PASS_THROUGH_DIRECT, *PSCSI_PASS_THROUGH_DIRECT; - -typedef struct _SRB_IO_CONTROL { - ULONG HeaderLength; - UCHAR Signature[8]; - ULONG Timeout; - ULONG ControlCode; - ULONG ReturnCode; - ULONG Length; -} SRB_IO_CONTROL, *PSRB_IO_CONTROL; - -typedef struct _SCSI_ADDRESS { - ULONG Length; - UCHAR PortNumber; - UCHAR PathId; - UCHAR TargetId; - UCHAR Lun; -} SCSI_ADDRESS, *PSCSI_ADDRESS; - -typedef struct _SCSI_BUS_DATA { - UCHAR NumberOfLogicalUnits; - UCHAR InitiatorBusId; - ULONG InquiryDataOffset; -}SCSI_BUS_DATA, *PSCSI_BUS_DATA; - -typedef struct _SCSI_ADAPTER_BUS_INFO { - UCHAR NumberOfBuses; - SCSI_BUS_DATA BusData[1]; -} SCSI_ADAPTER_BUS_INFO, *PSCSI_ADAPTER_BUS_INFO; - -typedef struct _IO_SCSI_CAPABILITIES { - ULONG Length; - ULONG MaximumTransferLength; - ULONG MaximumPhysicalPages; - ULONG SupportedAsynchronousEvents; - ULONG AlignmentMask; - BOOLEAN TaggedQueuing; - BOOLEAN AdapterScansDown; - BOOLEAN AdapterUsesPio; -} IO_SCSI_CAPABILITIES, *PIO_SCSI_CAPABILITIES; - -typedef struct _SCSI_INQUIRY_DATA { - UCHAR PathId; - UCHAR TargetId; - UCHAR Lun; - BOOLEAN DeviceClaimed; - ULONG InquiryDataLength; - ULONG NextInquiryDataOffset; - UCHAR InquiryData[1]; -} SCSI_INQUIRY_DATA, *PSCSI_INQUIRY_DATA; - -#define SCSI_IOCTL_DATA_OUT 0 -#define SCSI_IOCTL_DATA_IN 1 -#define SCSI_IOCTL_DATA_UNSPECIFIED 2 - -typedef struct _DUMP_POINTERS { - struct _ADAPTER_OBJECT *AdapterObject; - PVOID MappedRegisterBase; - PVOID DumpData; - PVOID CommonBufferVa; - LARGE_INTEGER CommonBufferPa; - ULONG CommonBufferSize; - BOOLEAN AllocateCommonBuffers; - BOOLEAN UseDiskDump; - UCHAR Spare1[2]; - PVOID DeviceObject; -} DUMP_POINTERS, *PDUMP_POINTERS; - -#ifdef __cplusplus -} -#endif - -#endif /* __NTDDSCSI_H */ +/* + * ntddscsi.h + * + * SCSI port IOCTL interface. + * + * This file is part of the w32api package. + * + * Contributors: + * Created by Casper S. Hornstrup + * + * THIS SOFTWARE IS NOT COPYRIGHTED + * + * This source code is offered for use in the public domain. You may + * use, modify or distribute it freely. + * + * This code is distributed in the hope that it will be useful but + * WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY + * DISCLAIMED. This includes but is not limited to warranties of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + */ + +#ifndef __NTDDSCSI_H +#define __NTDDSCSI_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define DD_SCSI_DEVICE_NAME "\\Device\\ScsiPort" +#define DD_SCSI_DEVICE_NAME_U L"\\Device\\ScsiPort" + +#define IOCTL_SCSI_BASE FILE_DEVICE_CONTROLLER + +#define IOCTL_SCSI_FREE_DUMP_POINTERS \ + CTL_CODE(IOCTL_SCSI_BASE, 0x0409, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_SCSI_GET_INQUIRY_DATA \ + CTL_CODE(IOCTL_SCSI_BASE, 0x0403, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_SCSI_GET_CAPABILITIES \ + CTL_CODE(IOCTL_SCSI_BASE, 0x0404, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_SCSI_GET_ADDRESS \ + CTL_CODE(IOCTL_SCSI_BASE, 0x0406, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_SCSI_GET_DUMP_POINTERS \ + CTL_CODE(IOCTL_SCSI_BASE, 0x0408, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_SCSI_MINIPORT \ + CTL_CODE(IOCTL_SCSI_BASE, 0x0402, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) + +#define IOCTL_SCSI_PASS_THROUGH \ + CTL_CODE(IOCTL_SCSI_BASE, 0x0401, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) + +#define IOCTL_SCSI_PASS_THROUGH_DIRECT \ + CTL_CODE(IOCTL_SCSI_BASE, 0x0405, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) + +#define IOCTL_SCSI_RESCAN_BUS \ + CTL_CODE(IOCTL_SCSI_BASE, 0x0407, METHOD_BUFFERED, FILE_ANY_ACCESS) + + +DEFINE_GUID(ScsiRawInterfaceGuid, \ + 0x53f56309L, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b); + +DEFINE_GUID(WmiScsiAddressGuid, \ + 0x53f5630fL, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b); + +typedef struct _SCSI_PASS_THROUGH { + USHORT Length; + UCHAR ScsiStatus; + UCHAR PathId; + UCHAR TargetId; + UCHAR Lun; + UCHAR CdbLength; + UCHAR SenseInfoLength; + UCHAR DataIn; + ULONG DataTransferLength; + ULONG TimeOutValue; + ULONG_PTR DataBufferOffset; + ULONG SenseInfoOffset; + UCHAR Cdb[16]; +} SCSI_PASS_THROUGH, *PSCSI_PASS_THROUGH; + +typedef struct _SCSI_PASS_THROUGH_DIRECT { + USHORT Length; + UCHAR ScsiStatus; + UCHAR PathId; + UCHAR TargetId; + UCHAR Lun; + UCHAR CdbLength; + UCHAR SenseInfoLength; + UCHAR DataIn; + ULONG DataTransferLength; + ULONG TimeOutValue; + PVOID DataBuffer; + ULONG SenseInfoOffset; + UCHAR Cdb[16]; +} SCSI_PASS_THROUGH_DIRECT, *PSCSI_PASS_THROUGH_DIRECT; + +typedef struct _SRB_IO_CONTROL { + ULONG HeaderLength; + UCHAR Signature[8]; + ULONG Timeout; + ULONG ControlCode; + ULONG ReturnCode; + ULONG Length; +} SRB_IO_CONTROL, *PSRB_IO_CONTROL; + +typedef struct _SCSI_ADDRESS { + ULONG Length; + UCHAR PortNumber; + UCHAR PathId; + UCHAR TargetId; + UCHAR Lun; +} SCSI_ADDRESS, *PSCSI_ADDRESS; + +typedef struct _SCSI_BUS_DATA { + UCHAR NumberOfLogicalUnits; + UCHAR InitiatorBusId; + ULONG InquiryDataOffset; +}SCSI_BUS_DATA, *PSCSI_BUS_DATA; + +typedef struct _SCSI_ADAPTER_BUS_INFO { + UCHAR NumberOfBuses; + SCSI_BUS_DATA BusData[1]; +} SCSI_ADAPTER_BUS_INFO, *PSCSI_ADAPTER_BUS_INFO; + +typedef struct _IO_SCSI_CAPABILITIES { + ULONG Length; + ULONG MaximumTransferLength; + ULONG MaximumPhysicalPages; + ULONG SupportedAsynchronousEvents; + ULONG AlignmentMask; + BOOLEAN TaggedQueuing; + BOOLEAN AdapterScansDown; + BOOLEAN AdapterUsesPio; +} IO_SCSI_CAPABILITIES, *PIO_SCSI_CAPABILITIES; + +typedef struct _SCSI_INQUIRY_DATA { + UCHAR PathId; + UCHAR TargetId; + UCHAR Lun; + BOOLEAN DeviceClaimed; + ULONG InquiryDataLength; + ULONG NextInquiryDataOffset; + UCHAR InquiryData[1]; +} SCSI_INQUIRY_DATA, *PSCSI_INQUIRY_DATA; + +#define SCSI_IOCTL_DATA_OUT 0 +#define SCSI_IOCTL_DATA_IN 1 +#define SCSI_IOCTL_DATA_UNSPECIFIED 2 + +typedef struct _DUMP_POINTERS { + struct _ADAPTER_OBJECT *AdapterObject; + PVOID MappedRegisterBase; + PVOID DumpData; + PVOID CommonBufferVa; + LARGE_INTEGER CommonBufferPa; + ULONG CommonBufferSize; + BOOLEAN AllocateCommonBuffers; + BOOLEAN UseDiskDump; + UCHAR Spare1[2]; + PVOID DeviceObject; +} DUMP_POINTERS, *PDUMP_POINTERS; + +#ifdef __cplusplus +} +#endif + +#endif /* __NTDDSCSI_H */ diff --git a/plugins/cdvdGigaherz/src/Windows/rosddk/ntddstor.h b/plugins/cdvdGigaherz/src/Windows/rosddk/ntddstor.h index bc16dccd83..5c076e5536 100644 --- a/plugins/cdvdGigaherz/src/Windows/rosddk/ntddstor.h +++ b/plugins/cdvdGigaherz/src/Windows/rosddk/ntddstor.h @@ -1,356 +1,356 @@ -/* - * ntddstor.h - * - * Storage class IOCTL interface. - * - * This file is part of the w32api package. - * - * Contributors: - * Created by Casper S. Hornstrup - * - * THIS SOFTWARE IS NOT COPYRIGHTED - * - * This source code is offered for use in the public domain. You may - * use, modify or distribute it freely. - * - * This code is distributed in the hope that it will be useful but - * WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY - * DISCLAIMED. This includes but is not limited to warranties of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * - */ - -#ifndef _NTDDSTOR_H_ -#define _NTDDSTOR_H_ - -#ifdef __cplusplus -extern "C" { -#endif - -#define IOCTL_STORAGE_BASE FILE_DEVICE_MASS_STORAGE - -#define IOCTL_STORAGE_CHECK_VERIFY \ - CTL_CODE(IOCTL_STORAGE_BASE, 0x0200, METHOD_BUFFERED, FILE_READ_ACCESS) - -#define IOCTL_STORAGE_CHECK_VERIFY2 \ - CTL_CODE(IOCTL_STORAGE_BASE, 0x0200, METHOD_BUFFERED, FILE_ANY_ACCESS) - -#define IOCTL_STORAGE_EJECT_MEDIA \ - CTL_CODE(IOCTL_STORAGE_BASE, 0x0202, METHOD_BUFFERED, FILE_READ_ACCESS) - -#define IOCTL_STORAGE_EJECTION_CONTROL \ - CTL_CODE(IOCTL_STORAGE_BASE, 0x0250, METHOD_BUFFERED, FILE_ANY_ACCESS) - -#define IOCTL_STORAGE_FIND_NEW_DEVICES \ - CTL_CODE(IOCTL_STORAGE_BASE, 0x0206, METHOD_BUFFERED, FILE_READ_ACCESS) - -#define IOCTL_STORAGE_GET_DEVICE_NUMBER \ - CTL_CODE(IOCTL_STORAGE_BASE, 0x0420, METHOD_BUFFERED, FILE_ANY_ACCESS) - -#define IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER \ - CTL_CODE(IOCTL_STORAGE_BASE, 0x0304, METHOD_BUFFERED, FILE_ANY_ACCESS) - -#define IOCTL_STORAGE_GET_MEDIA_TYPES \ - CTL_CODE(IOCTL_STORAGE_BASE, 0x0300, METHOD_BUFFERED, FILE_ANY_ACCESS) - -#define IOCTL_STORAGE_GET_MEDIA_TYPES_EX \ - CTL_CODE(IOCTL_STORAGE_BASE, 0x0301, METHOD_BUFFERED, FILE_ANY_ACCESS) - -#define IOCTL_STORAGE_LOAD_MEDIA \ - CTL_CODE(IOCTL_STORAGE_BASE, 0x0203, METHOD_BUFFERED, FILE_READ_ACCESS) - -#define IOCTL_STORAGE_LOAD_MEDIA2 \ - CTL_CODE(IOCTL_STORAGE_BASE, 0x0203, METHOD_BUFFERED, FILE_ANY_ACCESS) - -#define IOCTL_STORAGE_MCN_CONTROL \ - CTL_CODE(IOCTL_STORAGE_BASE, 0x0251, METHOD_BUFFERED, FILE_ANY_ACCESS) - -#define IOCTL_STORAGE_MEDIA_REMOVAL \ - CTL_CODE(IOCTL_STORAGE_BASE, 0x0201, METHOD_BUFFERED, FILE_READ_ACCESS) - -#define IOCTL_STORAGE_PREDICT_FAILURE \ - CTL_CODE(IOCTL_STORAGE_BASE, 0x0440, METHOD_BUFFERED, FILE_ANY_ACCESS) - -#define IOCTL_STORAGE_QUERY_PROPERTY \ - CTL_CODE(IOCTL_STORAGE_BASE, 0x0500, METHOD_BUFFERED, FILE_ANY_ACCESS) - -#define IOCTL_STORAGE_RELEASE \ - CTL_CODE(IOCTL_STORAGE_BASE, 0x0205, METHOD_BUFFERED, FILE_READ_ACCESS) - -#define IOCTL_STORAGE_RESERVE \ - CTL_CODE(IOCTL_STORAGE_BASE, 0x0204, METHOD_BUFFERED, FILE_READ_ACCESS) - -#define IOCTL_STORAGE_GET_HOTPLUG_INFO \ - CTL_CODE(IOCTL_STORAGE_BASE, 0x0305, METHOD_BUFFERED, FILE_ANY_ACCESS) - -#define IOCTL_STORAGE_SET_HOTPLUG_INFO \ - CTL_CODE(IOCTL_STORAGE_BASE, 0x0306, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) - -#define IOCTL_STORAGE_RESET_BUS \ - CTL_CODE(IOCTL_STORAGE_BASE, 0x0400, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) - -#define IOCTL_STORAGE_RESET_DEVICE \ - CTL_CODE(IOCTL_STORAGE_BASE, 0x0401, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) - - -DEFINE_GUID(GUID_DEVINTERFACE_DISK, - 0x53f56307L, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b); - -DEFINE_GUID(GUID_DEVINTERFACE_CDROM, - 0x53f56308L, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b); - -DEFINE_GUID(GUID_DEVINTERFACE_PARTITION, - 0x53f5630aL, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b); - -DEFINE_GUID(GUID_DEVINTERFACE_TAPE, - 0x53f5630bL, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b); - -DEFINE_GUID(GUID_DEVINTERFACE_WRITEONCEDISK, - 0x53f5630cL, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b); - -DEFINE_GUID(GUID_DEVINTERFACE_VOLUME, - 0x53f5630dL, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b); - -DEFINE_GUID(GUID_DEVINTERFACE_MEDIUMCHANGER, - 0x53f56310L, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b); - -DEFINE_GUID(GUID_DEVINTERFACE_FLOPPY, - 0x53f56311L, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b); - -DEFINE_GUID(GUID_DEVINTERFACE_CDCHANGER, - 0x53f56312L, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b); - -DEFINE_GUID(GUID_DEVINTERFACE_STORAGEPORT, - 0x2accfe60L, 0xc130, 0x11d2, 0xb0, 0x82, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b); - -DEFINE_GUID(GUID_DEVINTERFACE_HIDDEN_VOLUME, - 0x7f108a28L, 0x9833, 0x4b3b, 0xb7, 0x80, 0x2c, 0x6b, 0x5f, 0xa5, 0xc0, 0x62); - -/* Aliases for storage guids */ -#define DiskClassGuid GUID_DEVINTERFACE_DISK -#define CdRomClassGuid GUID_DEVINTERFACE_CDROM -#define PartitionClassGuid GUID_DEVINTERFACE_PARTITION -#define TapeClassGuid GUID_DEVINTERFACE_TAPE -#define WriteOnceDiskClassGuid GUID_DEVINTERFACE_WRITEONCEDISK -#define VolumeClassGuid GUID_DEVINTERFACE_VOLUME -#define MediumChangerClassGuid GUID_DEVINTERFACE_MEDIUMCHANGER -#define FloppyClassGuid GUID_DEVINTERFACE_FLOPPY -#define CdChangerClassGuid GUID_DEVINTERFACE_CDCHANGER -#define StoragePortClassGuid GUID_DEVINTERFACE_STORAGEPORT -#define HiddenVolumeClassGuid GUID_DEVINTERFACE_HIDDEN_VOLUME - -typedef enum _STORAGE_MEDIA_TYPE { - DDS_4mm = 0x20, - MiniQic, - Travan, - QIC, - MP_8mm, - AME_8mm, - AIT1_8mm, - DLT, - NCTP, - IBM_3480, - IBM_3490E, - IBM_Magstar_3590, - IBM_Magstar_MP, - STK_DATA_D3, - SONY_DTF, - DV_6mm, - DMI, - SONY_D2, - CLEANER_CARTRIDGE, - CD_ROM, - CD_R, - CD_RW, - DVD_ROM, - DVD_R, - DVD_RW, - MO_3_RW, - MO_5_WO, - MO_5_RW, - MO_5_LIMDOW, - PC_5_WO, - PC_5_RW, - PD_5_RW, - ABL_5_WO, - PINNACLE_APEX_5_RW, - SONY_12_WO, - PHILIPS_12_WO, - HITACHI_12_WO, - CYGNET_12_WO, - KODAK_14_WO, - MO_NFR_525, - NIKON_12_RW, - IOMEGA_ZIP, - IOMEGA_JAZ, - SYQUEST_EZ135, - SYQUEST_EZFLYER, - SYQUEST_SYJET, - AVATAR_F2, - MP2_8mm, - DST_S, - DST_M, - DST_L, - VXATape_1, - VXATape_2, - STK_9840, - LTO_Ultrium, - LTO_Accelis, - DVD_RAM, - AIT_8mm, - ADR_1, - ADR_2 -} STORAGE_MEDIA_TYPE, *PSTORAGE_MEDIA_TYPE; - -typedef enum _STORAGE_BUS_TYPE { - BusTypeUnknown = 0x00, - BusTypeScsi, - BusTypeAtapi, - BusTypeAta, - BusType1394, - BusTypeSsa, - BusTypeFibre, - BusTypeUsb, - BusTypeRAID, - BusTypeMaxReserved = 0x7F -} STORAGE_BUS_TYPE, *PSTORAGE_BUS_TYPE; - -/* DEVICE_MEDIA_INFO.DeviceSpecific.DiskInfo.MediaCharacteristics constants */ -#define MEDIA_ERASEABLE 0x00000001 -#define MEDIA_WRITE_ONCE 0x00000002 -#define MEDIA_READ_ONLY 0x00000004 -#define MEDIA_READ_WRITE 0x00000008 -#define MEDIA_WRITE_PROTECTED 0x00000100 -#define MEDIA_CURRENTLY_MOUNTED 0x80000000 - -typedef struct _DEVICE_MEDIA_INFO { - union { - struct { - LARGE_INTEGER Cylinders; - STORAGE_MEDIA_TYPE MediaType; - ULONG TracksPerCylinder; - ULONG SectorsPerTrack; - ULONG BytesPerSector; - ULONG NumberMediaSides; - ULONG MediaCharacteristics; - } DiskInfo; - struct { - LARGE_INTEGER Cylinders; - STORAGE_MEDIA_TYPE MediaType; - ULONG TracksPerCylinder; - ULONG SectorsPerTrack; - ULONG BytesPerSector; - ULONG NumberMediaSides; - ULONG MediaCharacteristics; - } RemovableDiskInfo; - struct { - STORAGE_MEDIA_TYPE MediaType; - ULONG MediaCharacteristics; - ULONG CurrentBlockSize; - STORAGE_BUS_TYPE BusType; - union { - struct { - UCHAR MediumType; - UCHAR DensityCode; - } ScsiInformation; - } BusSpecificData; - } TapeInfo; - } DeviceSpecific; -} DEVICE_MEDIA_INFO, *PDEVICE_MEDIA_INFO; - -typedef struct _GET_MEDIA_TYPES { - ULONG DeviceType; - ULONG MediaInfoCount; - DEVICE_MEDIA_INFO MediaInfo[1]; -} GET_MEDIA_TYPES, *PGET_MEDIA_TYPES; - -typedef struct _STORAGE_ADAPTER_DESCRIPTOR { - ULONG Version; - ULONG Size; - ULONG MaximumTransferLength; - ULONG MaximumPhysicalPages; - ULONG AlignmentMask; - BOOLEAN AdapterUsesPio; - BOOLEAN AdapterScansDown; - BOOLEAN CommandQueueing; - BOOLEAN AcceleratedTransfer; - STORAGE_BUS_TYPE BusType; - USHORT BusMajorVersion; - USHORT BusMinorVersion; -} STORAGE_ADAPTER_DESCRIPTOR, *PSTORAGE_ADAPTER_DESCRIPTOR; - -typedef struct _STORAGE_BUS_RESET_REQUEST { - UCHAR PathId; -} STORAGE_BUS_RESET_REQUEST, *PSTORAGE_BUS_RESET_REQUEST; - -typedef struct _STORAGE_DESCRIPTOR_HEADER { - ULONG Version; - ULONG Size; -} STORAGE_DESCRIPTOR_HEADER, *PSTORAGE_DESCRIPTOR_HEADER; - -typedef struct _STORAGE_DEVICE_DESCRIPTOR { - ULONG Version; - ULONG Size; - UCHAR DeviceType; - UCHAR DeviceTypeModifier; - BOOLEAN RemovableMedia; - BOOLEAN CommandQueueing; - ULONG VendorIdOffset; - ULONG ProductIdOffset; - ULONG ProductRevisionOffset; - ULONG SerialNumberOffset; - STORAGE_BUS_TYPE BusType; - ULONG RawPropertiesLength; - UCHAR RawDeviceProperties[1]; -} STORAGE_DEVICE_DESCRIPTOR, *PSTORAGE_DEVICE_DESCRIPTOR; - -typedef struct _STORAGE_DEVICE_ID_DESCRIPTOR { - ULONG Version; - ULONG Size; - ULONG NumberOfIdentifiers; - UCHAR Identifiers[1]; -} STORAGE_DEVICE_ID_DESCRIPTOR, *PSTORAGE_DEVICE_ID_DESCRIPTOR; - -typedef struct _STORAGE_DEVICE_NUMBER { - DEVICE_TYPE DeviceType; - ULONG DeviceNumber; - ULONG PartitionNumber; -} STORAGE_DEVICE_NUMBER, *PSTORAGE_DEVICE_NUMBER; - -typedef struct _STORAGE_PREDICT_FAILURE { - ULONG PredictFailure; - UCHAR VendorSpecific[512]; -} STORAGE_PREDICT_FAILURE, *PSTORAGE_PREDICT_FAILURE; - -typedef enum _STORAGE_PROPERTY_ID { - StorageDeviceProperty = 0, - StorageAdapterProperty, - StorageDeviceIdProperty, - StorageDeviceUniqueIdProperty, - StorageDeviceWriteCacheProperty, - StorageMiniportProperty, - StorageAccessAlignmentProperty -} STORAGE_PROPERTY_ID, *PSTORAGE_PROPERTY_ID; - -typedef enum _STORAGE_QUERY_TYPE { - PropertyStandardQuery = 0, - PropertyExistsQuery, - PropertyMaskQuery, - PropertyQueryMaxDefined -} STORAGE_QUERY_TYPE, *PSTORAGE_QUERY_TYPE; - -typedef struct _STORAGE_PROPERTY_QUERY { - STORAGE_PROPERTY_ID PropertyId; - STORAGE_QUERY_TYPE QueryType; - UCHAR AdditionalParameters[1]; -} STORAGE_PROPERTY_QUERY, *PSTORAGE_PROPERTY_QUERY; - -typedef struct _PREVENT_MEDIA_REMOVAL { - BOOLEAN PreventMediaRemoval; -} PREVENT_MEDIA_REMOVAL, *PPREVENT_MEDIA_REMOVAL; - -#ifdef __cplusplus -} -#endif - -#endif /* _NTDDSTOR_H_ */ +/* + * ntddstor.h + * + * Storage class IOCTL interface. + * + * This file is part of the w32api package. + * + * Contributors: + * Created by Casper S. Hornstrup + * + * THIS SOFTWARE IS NOT COPYRIGHTED + * + * This source code is offered for use in the public domain. You may + * use, modify or distribute it freely. + * + * This code is distributed in the hope that it will be useful but + * WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY + * DISCLAIMED. This includes but is not limited to warranties of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + */ + +#ifndef _NTDDSTOR_H_ +#define _NTDDSTOR_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#define IOCTL_STORAGE_BASE FILE_DEVICE_MASS_STORAGE + +#define IOCTL_STORAGE_CHECK_VERIFY \ + CTL_CODE(IOCTL_STORAGE_BASE, 0x0200, METHOD_BUFFERED, FILE_READ_ACCESS) + +#define IOCTL_STORAGE_CHECK_VERIFY2 \ + CTL_CODE(IOCTL_STORAGE_BASE, 0x0200, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_STORAGE_EJECT_MEDIA \ + CTL_CODE(IOCTL_STORAGE_BASE, 0x0202, METHOD_BUFFERED, FILE_READ_ACCESS) + +#define IOCTL_STORAGE_EJECTION_CONTROL \ + CTL_CODE(IOCTL_STORAGE_BASE, 0x0250, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_STORAGE_FIND_NEW_DEVICES \ + CTL_CODE(IOCTL_STORAGE_BASE, 0x0206, METHOD_BUFFERED, FILE_READ_ACCESS) + +#define IOCTL_STORAGE_GET_DEVICE_NUMBER \ + CTL_CODE(IOCTL_STORAGE_BASE, 0x0420, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER \ + CTL_CODE(IOCTL_STORAGE_BASE, 0x0304, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_STORAGE_GET_MEDIA_TYPES \ + CTL_CODE(IOCTL_STORAGE_BASE, 0x0300, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_STORAGE_GET_MEDIA_TYPES_EX \ + CTL_CODE(IOCTL_STORAGE_BASE, 0x0301, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_STORAGE_LOAD_MEDIA \ + CTL_CODE(IOCTL_STORAGE_BASE, 0x0203, METHOD_BUFFERED, FILE_READ_ACCESS) + +#define IOCTL_STORAGE_LOAD_MEDIA2 \ + CTL_CODE(IOCTL_STORAGE_BASE, 0x0203, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_STORAGE_MCN_CONTROL \ + CTL_CODE(IOCTL_STORAGE_BASE, 0x0251, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_STORAGE_MEDIA_REMOVAL \ + CTL_CODE(IOCTL_STORAGE_BASE, 0x0201, METHOD_BUFFERED, FILE_READ_ACCESS) + +#define IOCTL_STORAGE_PREDICT_FAILURE \ + CTL_CODE(IOCTL_STORAGE_BASE, 0x0440, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_STORAGE_QUERY_PROPERTY \ + CTL_CODE(IOCTL_STORAGE_BASE, 0x0500, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_STORAGE_RELEASE \ + CTL_CODE(IOCTL_STORAGE_BASE, 0x0205, METHOD_BUFFERED, FILE_READ_ACCESS) + +#define IOCTL_STORAGE_RESERVE \ + CTL_CODE(IOCTL_STORAGE_BASE, 0x0204, METHOD_BUFFERED, FILE_READ_ACCESS) + +#define IOCTL_STORAGE_GET_HOTPLUG_INFO \ + CTL_CODE(IOCTL_STORAGE_BASE, 0x0305, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_STORAGE_SET_HOTPLUG_INFO \ + CTL_CODE(IOCTL_STORAGE_BASE, 0x0306, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) + +#define IOCTL_STORAGE_RESET_BUS \ + CTL_CODE(IOCTL_STORAGE_BASE, 0x0400, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) + +#define IOCTL_STORAGE_RESET_DEVICE \ + CTL_CODE(IOCTL_STORAGE_BASE, 0x0401, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) + + +DEFINE_GUID(GUID_DEVINTERFACE_DISK, + 0x53f56307L, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b); + +DEFINE_GUID(GUID_DEVINTERFACE_CDROM, + 0x53f56308L, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b); + +DEFINE_GUID(GUID_DEVINTERFACE_PARTITION, + 0x53f5630aL, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b); + +DEFINE_GUID(GUID_DEVINTERFACE_TAPE, + 0x53f5630bL, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b); + +DEFINE_GUID(GUID_DEVINTERFACE_WRITEONCEDISK, + 0x53f5630cL, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b); + +DEFINE_GUID(GUID_DEVINTERFACE_VOLUME, + 0x53f5630dL, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b); + +DEFINE_GUID(GUID_DEVINTERFACE_MEDIUMCHANGER, + 0x53f56310L, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b); + +DEFINE_GUID(GUID_DEVINTERFACE_FLOPPY, + 0x53f56311L, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b); + +DEFINE_GUID(GUID_DEVINTERFACE_CDCHANGER, + 0x53f56312L, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b); + +DEFINE_GUID(GUID_DEVINTERFACE_STORAGEPORT, + 0x2accfe60L, 0xc130, 0x11d2, 0xb0, 0x82, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b); + +DEFINE_GUID(GUID_DEVINTERFACE_HIDDEN_VOLUME, + 0x7f108a28L, 0x9833, 0x4b3b, 0xb7, 0x80, 0x2c, 0x6b, 0x5f, 0xa5, 0xc0, 0x62); + +/* Aliases for storage guids */ +#define DiskClassGuid GUID_DEVINTERFACE_DISK +#define CdRomClassGuid GUID_DEVINTERFACE_CDROM +#define PartitionClassGuid GUID_DEVINTERFACE_PARTITION +#define TapeClassGuid GUID_DEVINTERFACE_TAPE +#define WriteOnceDiskClassGuid GUID_DEVINTERFACE_WRITEONCEDISK +#define VolumeClassGuid GUID_DEVINTERFACE_VOLUME +#define MediumChangerClassGuid GUID_DEVINTERFACE_MEDIUMCHANGER +#define FloppyClassGuid GUID_DEVINTERFACE_FLOPPY +#define CdChangerClassGuid GUID_DEVINTERFACE_CDCHANGER +#define StoragePortClassGuid GUID_DEVINTERFACE_STORAGEPORT +#define HiddenVolumeClassGuid GUID_DEVINTERFACE_HIDDEN_VOLUME + +typedef enum _STORAGE_MEDIA_TYPE { + DDS_4mm = 0x20, + MiniQic, + Travan, + QIC, + MP_8mm, + AME_8mm, + AIT1_8mm, + DLT, + NCTP, + IBM_3480, + IBM_3490E, + IBM_Magstar_3590, + IBM_Magstar_MP, + STK_DATA_D3, + SONY_DTF, + DV_6mm, + DMI, + SONY_D2, + CLEANER_CARTRIDGE, + CD_ROM, + CD_R, + CD_RW, + DVD_ROM, + DVD_R, + DVD_RW, + MO_3_RW, + MO_5_WO, + MO_5_RW, + MO_5_LIMDOW, + PC_5_WO, + PC_5_RW, + PD_5_RW, + ABL_5_WO, + PINNACLE_APEX_5_RW, + SONY_12_WO, + PHILIPS_12_WO, + HITACHI_12_WO, + CYGNET_12_WO, + KODAK_14_WO, + MO_NFR_525, + NIKON_12_RW, + IOMEGA_ZIP, + IOMEGA_JAZ, + SYQUEST_EZ135, + SYQUEST_EZFLYER, + SYQUEST_SYJET, + AVATAR_F2, + MP2_8mm, + DST_S, + DST_M, + DST_L, + VXATape_1, + VXATape_2, + STK_9840, + LTO_Ultrium, + LTO_Accelis, + DVD_RAM, + AIT_8mm, + ADR_1, + ADR_2 +} STORAGE_MEDIA_TYPE, *PSTORAGE_MEDIA_TYPE; + +typedef enum _STORAGE_BUS_TYPE { + BusTypeUnknown = 0x00, + BusTypeScsi, + BusTypeAtapi, + BusTypeAta, + BusType1394, + BusTypeSsa, + BusTypeFibre, + BusTypeUsb, + BusTypeRAID, + BusTypeMaxReserved = 0x7F +} STORAGE_BUS_TYPE, *PSTORAGE_BUS_TYPE; + +/* DEVICE_MEDIA_INFO.DeviceSpecific.DiskInfo.MediaCharacteristics constants */ +#define MEDIA_ERASEABLE 0x00000001 +#define MEDIA_WRITE_ONCE 0x00000002 +#define MEDIA_READ_ONLY 0x00000004 +#define MEDIA_READ_WRITE 0x00000008 +#define MEDIA_WRITE_PROTECTED 0x00000100 +#define MEDIA_CURRENTLY_MOUNTED 0x80000000 + +typedef struct _DEVICE_MEDIA_INFO { + union { + struct { + LARGE_INTEGER Cylinders; + STORAGE_MEDIA_TYPE MediaType; + ULONG TracksPerCylinder; + ULONG SectorsPerTrack; + ULONG BytesPerSector; + ULONG NumberMediaSides; + ULONG MediaCharacteristics; + } DiskInfo; + struct { + LARGE_INTEGER Cylinders; + STORAGE_MEDIA_TYPE MediaType; + ULONG TracksPerCylinder; + ULONG SectorsPerTrack; + ULONG BytesPerSector; + ULONG NumberMediaSides; + ULONG MediaCharacteristics; + } RemovableDiskInfo; + struct { + STORAGE_MEDIA_TYPE MediaType; + ULONG MediaCharacteristics; + ULONG CurrentBlockSize; + STORAGE_BUS_TYPE BusType; + union { + struct { + UCHAR MediumType; + UCHAR DensityCode; + } ScsiInformation; + } BusSpecificData; + } TapeInfo; + } DeviceSpecific; +} DEVICE_MEDIA_INFO, *PDEVICE_MEDIA_INFO; + +typedef struct _GET_MEDIA_TYPES { + ULONG DeviceType; + ULONG MediaInfoCount; + DEVICE_MEDIA_INFO MediaInfo[1]; +} GET_MEDIA_TYPES, *PGET_MEDIA_TYPES; + +typedef struct _STORAGE_ADAPTER_DESCRIPTOR { + ULONG Version; + ULONG Size; + ULONG MaximumTransferLength; + ULONG MaximumPhysicalPages; + ULONG AlignmentMask; + BOOLEAN AdapterUsesPio; + BOOLEAN AdapterScansDown; + BOOLEAN CommandQueueing; + BOOLEAN AcceleratedTransfer; + STORAGE_BUS_TYPE BusType; + USHORT BusMajorVersion; + USHORT BusMinorVersion; +} STORAGE_ADAPTER_DESCRIPTOR, *PSTORAGE_ADAPTER_DESCRIPTOR; + +typedef struct _STORAGE_BUS_RESET_REQUEST { + UCHAR PathId; +} STORAGE_BUS_RESET_REQUEST, *PSTORAGE_BUS_RESET_REQUEST; + +typedef struct _STORAGE_DESCRIPTOR_HEADER { + ULONG Version; + ULONG Size; +} STORAGE_DESCRIPTOR_HEADER, *PSTORAGE_DESCRIPTOR_HEADER; + +typedef struct _STORAGE_DEVICE_DESCRIPTOR { + ULONG Version; + ULONG Size; + UCHAR DeviceType; + UCHAR DeviceTypeModifier; + BOOLEAN RemovableMedia; + BOOLEAN CommandQueueing; + ULONG VendorIdOffset; + ULONG ProductIdOffset; + ULONG ProductRevisionOffset; + ULONG SerialNumberOffset; + STORAGE_BUS_TYPE BusType; + ULONG RawPropertiesLength; + UCHAR RawDeviceProperties[1]; +} STORAGE_DEVICE_DESCRIPTOR, *PSTORAGE_DEVICE_DESCRIPTOR; + +typedef struct _STORAGE_DEVICE_ID_DESCRIPTOR { + ULONG Version; + ULONG Size; + ULONG NumberOfIdentifiers; + UCHAR Identifiers[1]; +} STORAGE_DEVICE_ID_DESCRIPTOR, *PSTORAGE_DEVICE_ID_DESCRIPTOR; + +typedef struct _STORAGE_DEVICE_NUMBER { + DEVICE_TYPE DeviceType; + ULONG DeviceNumber; + ULONG PartitionNumber; +} STORAGE_DEVICE_NUMBER, *PSTORAGE_DEVICE_NUMBER; + +typedef struct _STORAGE_PREDICT_FAILURE { + ULONG PredictFailure; + UCHAR VendorSpecific[512]; +} STORAGE_PREDICT_FAILURE, *PSTORAGE_PREDICT_FAILURE; + +typedef enum _STORAGE_PROPERTY_ID { + StorageDeviceProperty = 0, + StorageAdapterProperty, + StorageDeviceIdProperty, + StorageDeviceUniqueIdProperty, + StorageDeviceWriteCacheProperty, + StorageMiniportProperty, + StorageAccessAlignmentProperty +} STORAGE_PROPERTY_ID, *PSTORAGE_PROPERTY_ID; + +typedef enum _STORAGE_QUERY_TYPE { + PropertyStandardQuery = 0, + PropertyExistsQuery, + PropertyMaskQuery, + PropertyQueryMaxDefined +} STORAGE_QUERY_TYPE, *PSTORAGE_QUERY_TYPE; + +typedef struct _STORAGE_PROPERTY_QUERY { + STORAGE_PROPERTY_ID PropertyId; + STORAGE_QUERY_TYPE QueryType; + UCHAR AdditionalParameters[1]; +} STORAGE_PROPERTY_QUERY, *PSTORAGE_PROPERTY_QUERY; + +typedef struct _PREVENT_MEDIA_REMOVAL { + BOOLEAN PreventMediaRemoval; +} PREVENT_MEDIA_REMOVAL, *PPREVENT_MEDIA_REMOVAL; + +#ifdef __cplusplus +} +#endif + +#endif /* _NTDDSTOR_H_ */ diff --git a/plugins/spu2-x/src/Linux/Alsa.cpp b/plugins/spu2-x/src/Linux/Alsa.cpp index 03d5cd2191..9992b6bc0b 100644 --- a/plugins/spu2-x/src/Linux/Alsa.cpp +++ b/plugins/spu2-x/src/Linux/Alsa.cpp @@ -1,238 +1,238 @@ -/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2 - * Developed and maintained by the Pcsx2 Development Team. - * - * Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz] - * - * SPU2-X 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. - * - * SPU2-X 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 Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with SPU2-X. If not, see . - */ - - // Adapted from ZeroSPU2 code by Zerofrog. Heavily modified by Arcum42. - -#include - +/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2 + * Developed and maintained by the Pcsx2 Development Team. + * + * Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz] + * + * SPU2-X 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. + * + * SPU2-X 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with SPU2-X. If not, see . + */ + + // Adapted from ZeroSPU2 code by Zerofrog. Heavily modified by Arcum42. + +#include + #define ALSA_MEM_DEF -#include "Global.h" -#include "Alsa.h" -#include "SndOut.h" - -class AlsaMod: public SndOutModule -{ -protected: - static const int PacketsPerBuffer = 1; // increase this if ALSA can't keep up with 512-sample packets - static const int MAX_BUFFER_COUNT = 4; - static const int NumBuffers = 4; // TODO: this should be configurable someday -- lower values reduce latency. - unsigned int pspeed; - - snd_pcm_t *handle; - snd_pcm_uframes_t buffer_size; - snd_async_handler_t *pcm_callback; - - uint period_time; - uint buffer_time; - -protected: - // Invoked by the static ExternalCallback method below. - void _InternalCallback() - { - snd_pcm_sframes_t avail; - //int err; - - avail = snd_pcm_avail_update( handle ); - while (avail >= period_time ) - { - StereoOut16 buff[PacketsPerBuffer * SndOutPacketSize]; - StereoOut16* p1 = buff; - - for( int p=0; phandle == snd_async_handler_get_pcm(pcm_callback) ); - - // Not sure if we just need an assert, or something like this: - //if( data->handle != snd_async_handler_get_pcm(pcm_callback) ) return; - - data->_InternalCallback(); - } - -public: - - s32 Init() - { - snd_pcm_hw_params_t *hwparams; - snd_pcm_sw_params_t *swparams; - snd_pcm_status_t *status; - int pchannels = 2; - snd_pcm_format_t format = SND_PCM_FORMAT_S16_LE; - - handle = NULL; - pcm_callback = NULL; - pspeed = SAMPLE_RATE; - - // buffer time and period time are in microseconds... - // (don't simplify the equation below -- it'll just cause integer rounding errors. - period_time = (SndOutPacketSize*1000) / (SampleRate / 1000); - buffer_time = period_time * NumBuffers; - - int err; - - err = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); - if(err < 0) - { - ERROR_LOG("Audio open error: %s\n", snd_strerror(err)); - return -1; - } - - err = snd_pcm_nonblock(handle, 0); - if(err < 0) - { - ERROR_LOG("Can't set blocking mode: %s\n", snd_strerror(err)); - return -1; - } - - snd_pcm_hw_params_alloca(&hwparams); - snd_pcm_sw_params_alloca(&swparams); - - err = snd_pcm_hw_params_any(handle, hwparams); - if (err < 0) - { - ERROR_LOG("Broken configuration for this PCM: %s\n", snd_strerror(err)); - return -1; - } - - err = snd_pcm_hw_params_set_access(handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED); - if (err < 0) - { - ERROR_LOG("Access type not available: %s\n", snd_strerror(err)); - return -1; - } - - err = snd_pcm_hw_params_set_format(handle, hwparams, format); - if (err < 0) - { - ERROR_LOG("Sample format not available: %s\n", snd_strerror(err)); - return -1; - } - - err = snd_pcm_hw_params_set_channels(handle, hwparams, pchannels); - if (err < 0) - { - ERROR_LOG("Channels count not available: %s\n", snd_strerror(err)); - return -1; - } - err = snd_pcm_hw_params_set_rate_near(handle, hwparams, &pspeed, 0); - if (err < 0) - { - ERROR_LOG("Rate not available: %s\n", snd_strerror(err)); - return -1; - } - - err = snd_pcm_hw_params_set_buffer_time_near(handle, hwparams, &buffer_time, 0); - if(err < 0) { - ERROR_LOG("Buffer time error: %s\n", snd_strerror(err)); - return -1; - } - - err = snd_pcm_hw_params_set_period_time_near(handle, hwparams, &period_time, 0); - if (err < 0) - { - ERROR_LOG("Period time error: %s\n", snd_strerror(err)); - return -1; - } - - err = snd_pcm_hw_params(handle, hwparams); - if (err < 0) - { - ERROR_LOG("Unable to install hw params: %s\n", snd_strerror(err)); - return -1; - } - - snd_pcm_status_alloca(&status); - err = snd_pcm_status(handle, status); - if(err < 0) - { - ERROR_LOG("Unable to get status: %s\n", snd_strerror(err)); - return -1; - } - - // Bind our asynchronous callback magic: - - snd_async_add_pcm_handler( &pcm_callback, handle, ExternalCallback, this ); - - snd_pcm_start( handle ); - // Diagnostic code: - //buffer_size = snd_pcm_status_get_avail(status); - - return 0; - } - - void Close() - { - if(handle == NULL) return; - - snd_pcm_drop(handle); - snd_pcm_close(handle); - handle = NULL; - } - - virtual void Configure(uptr parent) - { - } - - virtual bool Is51Out() const { return false; } - - s32 Test() const - { - return 0; - } - - int GetEmptySampleCount() - { - if(handle == NULL) return 0; - - // Returns the amount of free buffer space, in samples. - uint l = snd_pcm_avail_update(handle); - if( l < 0 ) return 0; - return (l / 1000) * (SampleRate / 1000); - } - - const wchar_t* GetIdent() const - { - return L"Alsa"; - } - - const wchar_t* GetLongName() const - { - return L"Alsa ('tis all ya get)"; - } - - void ReadSettings() - { - } - - void WriteSettings() const - { - } -} static Alsa; - -SndOutModule *AlsaOut = &Alsa; +#include "Global.h" +#include "Alsa.h" +#include "SndOut.h" + +class AlsaMod: public SndOutModule +{ +protected: + static const int PacketsPerBuffer = 1; // increase this if ALSA can't keep up with 512-sample packets + static const int MAX_BUFFER_COUNT = 4; + static const int NumBuffers = 4; // TODO: this should be configurable someday -- lower values reduce latency. + unsigned int pspeed; + + snd_pcm_t *handle; + snd_pcm_uframes_t buffer_size; + snd_async_handler_t *pcm_callback; + + uint period_time; + uint buffer_time; + +protected: + // Invoked by the static ExternalCallback method below. + void _InternalCallback() + { + snd_pcm_sframes_t avail; + //int err; + + avail = snd_pcm_avail_update( handle ); + while (avail >= period_time ) + { + StereoOut16 buff[PacketsPerBuffer * SndOutPacketSize]; + StereoOut16* p1 = buff; + + for( int p=0; phandle == snd_async_handler_get_pcm(pcm_callback) ); + + // Not sure if we just need an assert, or something like this: + //if( data->handle != snd_async_handler_get_pcm(pcm_callback) ) return; + + data->_InternalCallback(); + } + +public: + + s32 Init() + { + snd_pcm_hw_params_t *hwparams; + snd_pcm_sw_params_t *swparams; + snd_pcm_status_t *status; + int pchannels = 2; + snd_pcm_format_t format = SND_PCM_FORMAT_S16_LE; + + handle = NULL; + pcm_callback = NULL; + pspeed = SAMPLE_RATE; + + // buffer time and period time are in microseconds... + // (don't simplify the equation below -- it'll just cause integer rounding errors. + period_time = (SndOutPacketSize*1000) / (SampleRate / 1000); + buffer_time = period_time * NumBuffers; + + int err; + + err = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); + if(err < 0) + { + ERROR_LOG("Audio open error: %s\n", snd_strerror(err)); + return -1; + } + + err = snd_pcm_nonblock(handle, 0); + if(err < 0) + { + ERROR_LOG("Can't set blocking mode: %s\n", snd_strerror(err)); + return -1; + } + + snd_pcm_hw_params_alloca(&hwparams); + snd_pcm_sw_params_alloca(&swparams); + + err = snd_pcm_hw_params_any(handle, hwparams); + if (err < 0) + { + ERROR_LOG("Broken configuration for this PCM: %s\n", snd_strerror(err)); + return -1; + } + + err = snd_pcm_hw_params_set_access(handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED); + if (err < 0) + { + ERROR_LOG("Access type not available: %s\n", snd_strerror(err)); + return -1; + } + + err = snd_pcm_hw_params_set_format(handle, hwparams, format); + if (err < 0) + { + ERROR_LOG("Sample format not available: %s\n", snd_strerror(err)); + return -1; + } + + err = snd_pcm_hw_params_set_channels(handle, hwparams, pchannels); + if (err < 0) + { + ERROR_LOG("Channels count not available: %s\n", snd_strerror(err)); + return -1; + } + err = snd_pcm_hw_params_set_rate_near(handle, hwparams, &pspeed, 0); + if (err < 0) + { + ERROR_LOG("Rate not available: %s\n", snd_strerror(err)); + return -1; + } + + err = snd_pcm_hw_params_set_buffer_time_near(handle, hwparams, &buffer_time, 0); + if(err < 0) { + ERROR_LOG("Buffer time error: %s\n", snd_strerror(err)); + return -1; + } + + err = snd_pcm_hw_params_set_period_time_near(handle, hwparams, &period_time, 0); + if (err < 0) + { + ERROR_LOG("Period time error: %s\n", snd_strerror(err)); + return -1; + } + + err = snd_pcm_hw_params(handle, hwparams); + if (err < 0) + { + ERROR_LOG("Unable to install hw params: %s\n", snd_strerror(err)); + return -1; + } + + snd_pcm_status_alloca(&status); + err = snd_pcm_status(handle, status); + if(err < 0) + { + ERROR_LOG("Unable to get status: %s\n", snd_strerror(err)); + return -1; + } + + // Bind our asynchronous callback magic: + + snd_async_add_pcm_handler( &pcm_callback, handle, ExternalCallback, this ); + + snd_pcm_start( handle ); + // Diagnostic code: + //buffer_size = snd_pcm_status_get_avail(status); + + return 0; + } + + void Close() + { + if(handle == NULL) return; + + snd_pcm_drop(handle); + snd_pcm_close(handle); + handle = NULL; + } + + virtual void Configure(uptr parent) + { + } + + virtual bool Is51Out() const { return false; } + + s32 Test() const + { + return 0; + } + + int GetEmptySampleCount() + { + if(handle == NULL) return 0; + + // Returns the amount of free buffer space, in samples. + uint l = snd_pcm_avail_update(handle); + if( l < 0 ) return 0; + return (l / 1000) * (SampleRate / 1000); + } + + const wchar_t* GetIdent() const + { + return L"Alsa"; + } + + const wchar_t* GetLongName() const + { + return L"Alsa ('tis all ya get)"; + } + + void ReadSettings() + { + } + + void WriteSettings() const + { + } +} static Alsa; + +SndOutModule *AlsaOut = &Alsa; diff --git a/plugins/spu2-x/src/Linux/Config.cpp b/plugins/spu2-x/src/Linux/Config.cpp index a59998f242..8de0d08c6e 100644 --- a/plugins/spu2-x/src/Linux/Config.cpp +++ b/plugins/spu2-x/src/Linux/Config.cpp @@ -1,33 +1,33 @@ -/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2 - * Developed and maintained by the Pcsx2 Development Team. - * - * Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz] - * - * SPU2-X 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. - * - * SPU2-X 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 Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with SPU2-X. If not, see . - */ - -#include "Global.h" -#include "Dialogs.h" +/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2 + * Developed and maintained by the Pcsx2 Development Team. + * + * Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz] + * + * SPU2-X 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. + * + * SPU2-X 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with SPU2-X. If not, see . + */ + +#include "Global.h" +#include "Dialogs.h" #include "Config.h" - -#ifdef PCSX2_DEVBUILD -static const int LATENCY_MAX = 3000; -#else -static const int LATENCY_MAX = 750; -#endif - -static const int LATENCY_MIN = 40; - -int AutoDMAPlayRate[2] = {0,0}; + +#ifdef PCSX2_DEVBUILD +static const int LATENCY_MAX = 3000; +#else +static const int LATENCY_MAX = 750; +#endif + +static const int LATENCY_MIN = 40; + +int AutoDMAPlayRate[2] = {0,0}; // Default settings. @@ -47,12 +47,12 @@ int ReverbBoost = 0; // OUTPUT u32 OutputModule = FindOutputModuleById( PortaudioOut->GetIdent() ); int SndOutLatencyMS = 160; -bool timeStretchEnabled = true; -bool asyncMixingEnabled = false; - -/*****************************************************************************/ - -void ReadSettings() +bool timeStretchEnabled = true; +bool asyncMixingEnabled = false; + +/*****************************************************************************/ + +void ReadSettings() { Interpolation = CfgReadInt( L"MIXING",L"Interpolation", 1 ); EffectsDisabled = CfgReadBool( L"MIXING", L"Disable_Effects", false ); @@ -75,12 +75,12 @@ void ReadSettings() Clampify( SndOutLatencyMS, LATENCY_MIN, LATENCY_MAX ); - WriteSettings(); -} - -/*****************************************************************************/ - -void WriteSettings() + WriteSettings(); +} + +/*****************************************************************************/ + +void WriteSettings() { CfgWriteInt(L"MIXING",L"Interpolation",Interpolation); CfgWriteBool(L"MIXING",L"Disable_Effects",EffectsDisabled); @@ -93,8 +93,8 @@ void WriteSettings() PortaudioOut->WriteSettings(); SoundtouchCfg::WriteSettings(); - DebugConfig::WriteSettings(); -} + DebugConfig::WriteSettings(); +} void advanced_dialog() { diff --git a/plugins/spu2-x/src/Linux/Config.h b/plugins/spu2-x/src/Linux/Config.h index 48d54420d7..d910b36feb 100644 --- a/plugins/spu2-x/src/Linux/Config.h +++ b/plugins/spu2-x/src/Linux/Config.h @@ -3,17 +3,17 @@ * * Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz] * - * SPU2-X 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. - * - * SPU2-X 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 Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with SPU2-X. If not, see . - */ + * SPU2-X 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. + * + * SPU2-X 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with SPU2-X. If not, see . + */ #ifndef CONFIG_H_INCLUDED #define CONFIG_H_INCLUDED diff --git a/plugins/spu2-x/src/Linux/Dialogs.cpp b/plugins/spu2-x/src/Linux/Dialogs.cpp index 61189e3019..56693dc214 100644 --- a/plugins/spu2-x/src/Linux/Dialogs.cpp +++ b/plugins/spu2-x/src/Linux/Dialogs.cpp @@ -3,17 +3,17 @@ * * Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz] * - * SPU2-X 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. - * - * SPU2-X 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 Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with SPU2-X. If not, see . - */ + * SPU2-X 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. + * + * SPU2-X 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with SPU2-X. If not, see . + */ // To be continued... diff --git a/plugins/spu2-x/src/Linux/Dialogs.h b/plugins/spu2-x/src/Linux/Dialogs.h index 1084a6de6b..c085fd62d8 100644 --- a/plugins/spu2-x/src/Linux/Dialogs.h +++ b/plugins/spu2-x/src/Linux/Dialogs.h @@ -3,17 +3,17 @@ * * Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz] * - * SPU2-X 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. - * - * SPU2-X 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 Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with SPU2-X. If not, see . - */ + * SPU2-X 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. + * + * SPU2-X 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with SPU2-X. If not, see . + */ #ifndef DIALOG_H_INCLUDED #define DIALOG_H_INCLUDED diff --git a/plugins/spu2-x/src/Mixer.h b/plugins/spu2-x/src/Mixer.h index debf00dcb2..da309c5f76 100644 --- a/plugins/spu2-x/src/Mixer.h +++ b/plugins/spu2-x/src/Mixer.h @@ -1,84 +1,84 @@ -/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2 - * Developed and maintained by the Pcsx2 Development Team. - * - * SPU2-X 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. - * - * SPU2-X 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 Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with SPU2-X. If not, see . - */ - - -#pragma once - -struct StereoOut32 -{ - static StereoOut32 Empty; - - s32 Left; - s32 Right; - - StereoOut32() : - Left( 0 ), - Right( 0 ) - { - } - - StereoOut32( s32 left, s32 right ) : - Left( left ), - Right( right ) - { - } - - StereoOut32( const StereoOut16& src ); - explicit StereoOut32( const StereoOutFloat& src ); - - StereoOut16 DownSample() const; - - StereoOut32 operator*( const int& factor ) const - { - return StereoOut32( - Left * factor, - Right * factor - ); - } - - StereoOut32& operator*=( const int& factor ) - { - Left *= factor; - Right *= factor; - return *this; - } - - StereoOut32 operator+( const StereoOut32& right ) const - { - return StereoOut32( - Left + right.Left, - Right + right.Right - ); - } - - StereoOut32 operator/( int src ) const - { - return StereoOut32( Left / src, Right / src ); - } - - void ResampleFrom( const StereoOut32& src ) - { - this->Left = src.Left << 2; - this->Right = src.Right << 2; - } - -}; - - -extern void Mix(); -extern s32 clamp_mix( s32 x, u8 bitshift=0 ); -extern s32 MulShr32( s32 srcval, s32 mulval ); - -extern StereoOut32 clamp_mix( const StereoOut32& sample, u8 bitshift=0 ); +/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2 + * Developed and maintained by the Pcsx2 Development Team. + * + * SPU2-X 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. + * + * SPU2-X 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with SPU2-X. If not, see . + */ + + +#pragma once + +struct StereoOut32 +{ + static StereoOut32 Empty; + + s32 Left; + s32 Right; + + StereoOut32() : + Left( 0 ), + Right( 0 ) + { + } + + StereoOut32( s32 left, s32 right ) : + Left( left ), + Right( right ) + { + } + + StereoOut32( const StereoOut16& src ); + explicit StereoOut32( const StereoOutFloat& src ); + + StereoOut16 DownSample() const; + + StereoOut32 operator*( const int& factor ) const + { + return StereoOut32( + Left * factor, + Right * factor + ); + } + + StereoOut32& operator*=( const int& factor ) + { + Left *= factor; + Right *= factor; + return *this; + } + + StereoOut32 operator+( const StereoOut32& right ) const + { + return StereoOut32( + Left + right.Left, + Right + right.Right + ); + } + + StereoOut32 operator/( int src ) const + { + return StereoOut32( Left / src, Right / src ); + } + + void ResampleFrom( const StereoOut32& src ) + { + this->Left = src.Left << 2; + this->Right = src.Right << 2; + } + +}; + + +extern void Mix(); +extern s32 clamp_mix( s32 x, u8 bitshift=0 ); +extern s32 MulShr32( s32 srcval, s32 mulval ); + +extern StereoOut32 clamp_mix( const StereoOut32& sample, u8 bitshift=0 ); diff --git a/plugins/spu2-x/src/PrecompiledHeader.cpp b/plugins/spu2-x/src/PrecompiledHeader.cpp index 076b1eb792..9d74cf1286 100644 --- a/plugins/spu2-x/src/PrecompiledHeader.cpp +++ b/plugins/spu2-x/src/PrecompiledHeader.cpp @@ -1,2 +1,2 @@ - + #include "Global.h" \ No newline at end of file diff --git a/plugins/spu2-x/src/SndOut_Portaudio.cpp b/plugins/spu2-x/src/SndOut_Portaudio.cpp index 931435283a..f1f7c73697 100644 --- a/plugins/spu2-x/src/SndOut_Portaudio.cpp +++ b/plugins/spu2-x/src/SndOut_Portaudio.cpp @@ -1,361 +1,361 @@ -/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2 - * Developed and maintained by the Pcsx2 Development Team. - * - * Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz] - * - * SPU2-X 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. - * - * SPU2-X 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 Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with SPU2-X. If not, see . - */ - -#include "Global.h" - -#define _WIN32_DCOM -#include "Dialogs.h" - -#include "portaudio/include/portaudio.h" - -#ifdef __WIN32__ -#include "portaudio/include/pa_win_wasapi.h" -#endif - -#ifdef __LINUX__ -int PaLinuxCallback( const void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo* timeInfo, - PaStreamCallbackFlags statusFlags, - void *userData ); -#endif - -class Portaudio : public SndOutModule -{ -private: - ////////////////////////////////////////////////////////////////////////////////////////// - // Configuration Vars (unused still) - - int m_ApiId; - wstring m_Device; - - bool m_UseHardware; - - bool m_WasapiExclusiveMode; - - ////////////////////////////////////////////////////////////////////////////////////////// - // Instance vars - - int writtenSoFar; - int writtenLastTime; - int availableLastTime; - - bool started; - PaStream* stream; - -#ifndef __LINUX__ - static int PaCallback( const void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo* timeInfo, - PaStreamCallbackFlags statusFlags, - void *userData ) - { - return PA.ActualPaCallback(inputBuffer,outputBuffer,framesPerBuffer,timeInfo,statusFlags,userData); - } -#endif - -public: - int ActualPaCallback( const void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo* timeInfo, - PaStreamCallbackFlags statusFlags, - void *userData ) - { - StereoOut32* p1 = (StereoOut32*)outputBuffer; - - int packets = framesPerBuffer / SndOutPacketSize; - - for(int p=0; phostApi); - - fprintf(stderr," *** Device %d: '%s' (%s)", i, info->name, apiinfo->name); - - if(apiinfo->type == m_ApiId) - { -#ifdef __WIN32__ - static wchar_t buffer [1000]; - MultiByteToWideChar(CP_UTF8,0,info->name,strlen(info->name),buffer,999); - buffer[999]=0; -#else -//# error TODO - static wchar_t buffer [1000]; - //MultiByteToWideChar(CP_UTF8,0,info->name,strlen(info->name),buffer,999); - buffer[999]=0; -#endif - - if(m_Device == buffer) - { - deviceIndex = i; - fprintf(stderr," (selected)"); - } - - } - fprintf(stderr,"\n"); - } - - if(deviceIndex<0 && m_ApiId>=0) - { - for(int i=0;itype == m_ApiId) - { - deviceIndex = apiinfo->defaultOutputDevice; - } - } - } - - if(deviceIndex>=0) - { - void* infoPtr = NULL; - -#ifdef __WIN32__ - PaWasapiStreamInfo info = { - sizeof(PaWasapiStreamInfo), - paWASAPI, - 1, - paWinWasapiExclusive - }; - - if((m_ApiId == paWASAPI) && m_WasapiExclusiveMode) - { - // Pass it the Exclusive mode enable flag - infoPtr = &info; - } -#endif - - PaStreamParameters outParams = { - - // PaDeviceIndex device; - // int channelCount; - // PaSampleFormat sampleFormat; - // PaTime suggestedLatency; - // void *hostApiSpecificStreamInfo; - deviceIndex, - 2, - paInt32, - 0, //? - infoPtr - }; - - err = Pa_OpenStream(&stream, - NULL, &outParams, SampleRate, - SndOutPacketSize, - paNoFlag, -#ifndef __LINUX__ - PaCallback, -#else - PaLinuxCallback, -#endif - NULL); - } - else - { - err = Pa_OpenDefaultStream( &stream, - 0, 2, paInt32, 48000, - SndOutPacketSize, -#ifndef __LINUX__ - PaCallback, -#else - PaLinuxCallback, -#endif - NULL ); - } - if( err != paNoError ) - { - fprintf(stderr," * SPU2: PortAudio error: %s\n", Pa_GetErrorText( err ) ); - Pa_Terminate(); - return -1; - } - - err = Pa_StartStream( stream ); - if( err != paNoError ) - { - fprintf(stderr," * SPU2: PortAudio error: %s\n", Pa_GetErrorText( err ) ); - Pa_CloseStream(stream); - stream=NULL; - Pa_Terminate(); - return -1; - } - - return 0; - } - - void Close() - { - PaError err; - if(started) - { - if(stream) - { - if(Pa_IsStreamActive(stream)) - { - err = Pa_StopStream(stream); - if( err != paNoError ) - fprintf(stderr," * SPU2: PortAudio error: %s\n", Pa_GetErrorText( err ) ); - } - - err = Pa_CloseStream(stream); - if( err != paNoError ) - fprintf(stderr," * SPU2: PortAudio error: %s\n", Pa_GetErrorText( err ) ); - - stream=NULL; - } - - PaError err = Pa_Terminate(); - if( err != paNoError ) - fprintf(stderr," * SPU2: PortAudio error: %s\n", Pa_GetErrorText( err ) ); - - started=false; - } - } - - virtual void Configure(uptr parent) - { - } - - virtual bool Is51Out() const { return false; } - - s32 Test() const - { - return 0; - } - - int GetEmptySampleCount() - { - long availableNow = Pa_GetStreamWriteAvailable(stream); - - int playedSinceLastTime = (writtenSoFar - writtenLastTime) + (availableNow - availableLastTime); - writtenLastTime = writtenSoFar; - availableLastTime = availableNow; - - // Lowest resolution here is the SndOutPacketSize we use. - return playedSinceLastTime; - } - - const wchar_t* GetIdent() const - { - return L"portaudio"; - } - - const wchar_t* GetLongName() const - { - return L"Portaudio (crossplatform)"; - } - - void ReadSettings() - { - wstring api=L"EMPTYEMPTYEMPTY"; - m_Device = L"EMPTYEMPTYEMPTY"; - CfgReadStr( L"PORTAUDIO", L"HostApi", api, 254, L"Unknown" ); - CfgReadStr( L"PORTAUDIO", L"Device", m_Device, 254, L"default" ); - - m_ApiId = -1; - if(api == L"InDevelopment") m_ApiId = paInDevelopment; /* use while developing support for a new host API */ - if(api == L"DirectSound") m_ApiId = paDirectSound; - if(api == L"MME") m_ApiId = paMME; - if(api == L"ASIO") m_ApiId = paASIO; - if(api == L"SoundManager") m_ApiId = paSoundManager; - if(api == L"CoreAudio") m_ApiId = paCoreAudio; - if(api == L"OSS") m_ApiId = paOSS; - if(api == L"ALSA") m_ApiId = paALSA; - if(api == L"AL") m_ApiId = paAL; - if(api == L"BeOS") m_ApiId = paBeOS; - if(api == L"WDMKS") m_ApiId = paWDMKS; - if(api == L"JACK") m_ApiId = paJACK; - if(api == L"WASAPI") m_ApiId = paWASAPI; - if(api == L"AudioScienceHPI") m_ApiId = paAudioScienceHPI; - - m_WasapiExclusiveMode = CfgReadBool( L"PORTAUDIO", L"Wasapi_Exclusive_Mode", false); - } - - void WriteSettings() const - { - wstring api; - switch(m_ApiId) - { - case paInDevelopment: api = L"InDevelopment"; break; /* use while developing support for a new host API */ - case paDirectSound: api = L"DirectSound"; break; - case paMME: api = L"MME"; break; - case paASIO: api = L"ASIO"; break; - case paSoundManager: api = L"SoundManager"; break; - case paCoreAudio: api = L"CoreAudio"; break; - case paOSS: api = L"OSS"; break; - case paALSA: api = L"ALSA"; break; - case paAL: api = L"AL"; break; - case paBeOS: api = L"BeOS"; break; - case paWDMKS: api = L"WDMKS"; break; - case paJACK: api = L"JACK"; break; - case paWASAPI: api = L"WASAPI"; break; - case paAudioScienceHPI: api = L"AudioScienceHPI"; break; - default: api = L"Unknown"; - } - - CfgWriteStr( L"PORTAUDIO", L"HostApi", api); - CfgWriteStr( L"PORTAUDIO", L"Device", m_Device); - - CfgWriteBool( L"PORTAUDIO", L"Wasapi_Exclusive_Mode", m_WasapiExclusiveMode); - } - -} static PA; - -#ifdef __LINUX__ - int PaLinuxCallback( const void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo* timeInfo, - PaStreamCallbackFlags statusFlags, - void *userData ) - { - return PA.ActualPaCallback(inputBuffer,outputBuffer,framesPerBuffer,timeInfo,statusFlags,userData); - } -#endif - -SndOutModule *PortaudioOut = &PA; +/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2 + * Developed and maintained by the Pcsx2 Development Team. + * + * Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz] + * + * SPU2-X 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. + * + * SPU2-X 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with SPU2-X. If not, see . + */ + +#include "Global.h" + +#define _WIN32_DCOM +#include "Dialogs.h" + +#include "portaudio/include/portaudio.h" + +#ifdef __WIN32__ +#include "portaudio/include/pa_win_wasapi.h" +#endif + +#ifdef __LINUX__ +int PaLinuxCallback( const void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + const PaStreamCallbackTimeInfo* timeInfo, + PaStreamCallbackFlags statusFlags, + void *userData ); +#endif + +class Portaudio : public SndOutModule +{ +private: + ////////////////////////////////////////////////////////////////////////////////////////// + // Configuration Vars (unused still) + + int m_ApiId; + wstring m_Device; + + bool m_UseHardware; + + bool m_WasapiExclusiveMode; + + ////////////////////////////////////////////////////////////////////////////////////////// + // Instance vars + + int writtenSoFar; + int writtenLastTime; + int availableLastTime; + + bool started; + PaStream* stream; + +#ifndef __LINUX__ + static int PaCallback( const void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + const PaStreamCallbackTimeInfo* timeInfo, + PaStreamCallbackFlags statusFlags, + void *userData ) + { + return PA.ActualPaCallback(inputBuffer,outputBuffer,framesPerBuffer,timeInfo,statusFlags,userData); + } +#endif + +public: + int ActualPaCallback( const void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + const PaStreamCallbackTimeInfo* timeInfo, + PaStreamCallbackFlags statusFlags, + void *userData ) + { + StereoOut32* p1 = (StereoOut32*)outputBuffer; + + int packets = framesPerBuffer / SndOutPacketSize; + + for(int p=0; phostApi); + + fprintf(stderr," *** Device %d: '%s' (%s)", i, info->name, apiinfo->name); + + if(apiinfo->type == m_ApiId) + { +#ifdef __WIN32__ + static wchar_t buffer [1000]; + MultiByteToWideChar(CP_UTF8,0,info->name,strlen(info->name),buffer,999); + buffer[999]=0; +#else +//# error TODO + static wchar_t buffer [1000]; + //MultiByteToWideChar(CP_UTF8,0,info->name,strlen(info->name),buffer,999); + buffer[999]=0; +#endif + + if(m_Device == buffer) + { + deviceIndex = i; + fprintf(stderr," (selected)"); + } + + } + fprintf(stderr,"\n"); + } + + if(deviceIndex<0 && m_ApiId>=0) + { + for(int i=0;itype == m_ApiId) + { + deviceIndex = apiinfo->defaultOutputDevice; + } + } + } + + if(deviceIndex>=0) + { + void* infoPtr = NULL; + +#ifdef __WIN32__ + PaWasapiStreamInfo info = { + sizeof(PaWasapiStreamInfo), + paWASAPI, + 1, + paWinWasapiExclusive + }; + + if((m_ApiId == paWASAPI) && m_WasapiExclusiveMode) + { + // Pass it the Exclusive mode enable flag + infoPtr = &info; + } +#endif + + PaStreamParameters outParams = { + + // PaDeviceIndex device; + // int channelCount; + // PaSampleFormat sampleFormat; + // PaTime suggestedLatency; + // void *hostApiSpecificStreamInfo; + deviceIndex, + 2, + paInt32, + 0, //? + infoPtr + }; + + err = Pa_OpenStream(&stream, + NULL, &outParams, SampleRate, + SndOutPacketSize, + paNoFlag, +#ifndef __LINUX__ + PaCallback, +#else + PaLinuxCallback, +#endif + NULL); + } + else + { + err = Pa_OpenDefaultStream( &stream, + 0, 2, paInt32, 48000, + SndOutPacketSize, +#ifndef __LINUX__ + PaCallback, +#else + PaLinuxCallback, +#endif + NULL ); + } + if( err != paNoError ) + { + fprintf(stderr," * SPU2: PortAudio error: %s\n", Pa_GetErrorText( err ) ); + Pa_Terminate(); + return -1; + } + + err = Pa_StartStream( stream ); + if( err != paNoError ) + { + fprintf(stderr," * SPU2: PortAudio error: %s\n", Pa_GetErrorText( err ) ); + Pa_CloseStream(stream); + stream=NULL; + Pa_Terminate(); + return -1; + } + + return 0; + } + + void Close() + { + PaError err; + if(started) + { + if(stream) + { + if(Pa_IsStreamActive(stream)) + { + err = Pa_StopStream(stream); + if( err != paNoError ) + fprintf(stderr," * SPU2: PortAudio error: %s\n", Pa_GetErrorText( err ) ); + } + + err = Pa_CloseStream(stream); + if( err != paNoError ) + fprintf(stderr," * SPU2: PortAudio error: %s\n", Pa_GetErrorText( err ) ); + + stream=NULL; + } + + PaError err = Pa_Terminate(); + if( err != paNoError ) + fprintf(stderr," * SPU2: PortAudio error: %s\n", Pa_GetErrorText( err ) ); + + started=false; + } + } + + virtual void Configure(uptr parent) + { + } + + virtual bool Is51Out() const { return false; } + + s32 Test() const + { + return 0; + } + + int GetEmptySampleCount() + { + long availableNow = Pa_GetStreamWriteAvailable(stream); + + int playedSinceLastTime = (writtenSoFar - writtenLastTime) + (availableNow - availableLastTime); + writtenLastTime = writtenSoFar; + availableLastTime = availableNow; + + // Lowest resolution here is the SndOutPacketSize we use. + return playedSinceLastTime; + } + + const wchar_t* GetIdent() const + { + return L"portaudio"; + } + + const wchar_t* GetLongName() const + { + return L"Portaudio (crossplatform)"; + } + + void ReadSettings() + { + wstring api=L"EMPTYEMPTYEMPTY"; + m_Device = L"EMPTYEMPTYEMPTY"; + CfgReadStr( L"PORTAUDIO", L"HostApi", api, 254, L"Unknown" ); + CfgReadStr( L"PORTAUDIO", L"Device", m_Device, 254, L"default" ); + + m_ApiId = -1; + if(api == L"InDevelopment") m_ApiId = paInDevelopment; /* use while developing support for a new host API */ + if(api == L"DirectSound") m_ApiId = paDirectSound; + if(api == L"MME") m_ApiId = paMME; + if(api == L"ASIO") m_ApiId = paASIO; + if(api == L"SoundManager") m_ApiId = paSoundManager; + if(api == L"CoreAudio") m_ApiId = paCoreAudio; + if(api == L"OSS") m_ApiId = paOSS; + if(api == L"ALSA") m_ApiId = paALSA; + if(api == L"AL") m_ApiId = paAL; + if(api == L"BeOS") m_ApiId = paBeOS; + if(api == L"WDMKS") m_ApiId = paWDMKS; + if(api == L"JACK") m_ApiId = paJACK; + if(api == L"WASAPI") m_ApiId = paWASAPI; + if(api == L"AudioScienceHPI") m_ApiId = paAudioScienceHPI; + + m_WasapiExclusiveMode = CfgReadBool( L"PORTAUDIO", L"Wasapi_Exclusive_Mode", false); + } + + void WriteSettings() const + { + wstring api; + switch(m_ApiId) + { + case paInDevelopment: api = L"InDevelopment"; break; /* use while developing support for a new host API */ + case paDirectSound: api = L"DirectSound"; break; + case paMME: api = L"MME"; break; + case paASIO: api = L"ASIO"; break; + case paSoundManager: api = L"SoundManager"; break; + case paCoreAudio: api = L"CoreAudio"; break; + case paOSS: api = L"OSS"; break; + case paALSA: api = L"ALSA"; break; + case paAL: api = L"AL"; break; + case paBeOS: api = L"BeOS"; break; + case paWDMKS: api = L"WDMKS"; break; + case paJACK: api = L"JACK"; break; + case paWASAPI: api = L"WASAPI"; break; + case paAudioScienceHPI: api = L"AudioScienceHPI"; break; + default: api = L"Unknown"; + } + + CfgWriteStr( L"PORTAUDIO", L"HostApi", api); + CfgWriteStr( L"PORTAUDIO", L"Device", m_Device); + + CfgWriteBool( L"PORTAUDIO", L"Wasapi_Exclusive_Mode", m_WasapiExclusiveMode); + } + +} static PA; + +#ifdef __LINUX__ + int PaLinuxCallback( const void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + const PaStreamCallbackTimeInfo* timeInfo, + PaStreamCallbackFlags statusFlags, + void *userData ) + { + return PA.ActualPaCallback(inputBuffer,outputBuffer,framesPerBuffer,timeInfo,statusFlags,userData); + } +#endif + +SndOutModule *PortaudioOut = &PA; diff --git a/plugins/spu2-x/src/Windows/WinConfig.h b/plugins/spu2-x/src/Windows/WinConfig.h index 0b39a163de..a037c7eadc 100644 --- a/plugins/spu2-x/src/Windows/WinConfig.h +++ b/plugins/spu2-x/src/Windows/WinConfig.h @@ -1,63 +1,63 @@ - -#pragma once - -#ifndef WINVER -# define WINVER 0x0501 -# define _WIN32_WINNT 0x0501 -#endif - -#include -#include -#include -#include -#include - -#include "resource.h" - -extern HINSTANCE hInstance; - -#define SET_CHECK(idc,value) SendMessage(GetDlgItem(hWnd,idc),BM_SETCHECK,((value)==0)?BST_UNCHECKED:BST_CHECKED,0) -#define HANDLE_CHECK(idc,hvar) case idc: (hvar) = !(hvar); SendMessage(GetDlgItem(hWnd,idc),BM_SETCHECK,(hvar)?BST_CHECKED:BST_UNCHECKED,0); break -#define HANDLE_CHECKNB(idc,hvar)case idc: (hvar) = !(hvar); SendMessage(GetDlgItem(hWnd,idc),BM_SETCHECK,(hvar)?BST_CHECKED:BST_UNCHECKED,0) -#define ENABLE_CONTROL(idc,value) EnableWindow(GetDlgItem(hWnd,idc),value) - -#define INIT_SLIDER(idc,minrange,maxrange,tickfreq,pagesize,linesize) \ - SendMessage(GetDlgItem(hWnd,idc),TBM_SETRANGEMIN,FALSE,minrange); \ - SendMessage(GetDlgItem(hWnd,idc),TBM_SETRANGEMAX,FALSE,maxrange); \ - SendMessage(GetDlgItem(hWnd,idc),TBM_SETTICFREQ,tickfreq,0); \ - SendMessage(GetDlgItem(hWnd,idc),TBM_SETPAGESIZE,0,pagesize); \ - SendMessage(GetDlgItem(hWnd,idc),TBM_SETLINESIZE,0,linesize) - -#define HANDLE_SCROLL_MESSAGE(idc,idcDisplay) \ - if((HWND)lParam == GetDlgItem(hWnd,idc)) return DoHandleScrollMessage( GetDlgItem(hWnd,idcDisplay), wParam, lParam ) - - -// *** BEGIN DRIVER-SPECIFIC CONFIGURATION *** -// ------------------------------------------- - -struct CONFIG_XAUDIO2 -{ - std::wstring Device; - s8 NumBuffers; - - CONFIG_XAUDIO2() : - Device(), - NumBuffers( 2 ) - { - } -}; - -struct CONFIG_WAVEOUT -{ - std::wstring Device; - s8 NumBuffers; - - CONFIG_WAVEOUT() : - Device(), - NumBuffers( 4 ) - { - } -}; - -extern CONFIG_WAVEOUT Config_WaveOut; -extern CONFIG_XAUDIO2 Config_XAudio2; + +#pragma once + +#ifndef WINVER +# define WINVER 0x0501 +# define _WIN32_WINNT 0x0501 +#endif + +#include +#include +#include +#include +#include + +#include "resource.h" + +extern HINSTANCE hInstance; + +#define SET_CHECK(idc,value) SendMessage(GetDlgItem(hWnd,idc),BM_SETCHECK,((value)==0)?BST_UNCHECKED:BST_CHECKED,0) +#define HANDLE_CHECK(idc,hvar) case idc: (hvar) = !(hvar); SendMessage(GetDlgItem(hWnd,idc),BM_SETCHECK,(hvar)?BST_CHECKED:BST_UNCHECKED,0); break +#define HANDLE_CHECKNB(idc,hvar)case idc: (hvar) = !(hvar); SendMessage(GetDlgItem(hWnd,idc),BM_SETCHECK,(hvar)?BST_CHECKED:BST_UNCHECKED,0) +#define ENABLE_CONTROL(idc,value) EnableWindow(GetDlgItem(hWnd,idc),value) + +#define INIT_SLIDER(idc,minrange,maxrange,tickfreq,pagesize,linesize) \ + SendMessage(GetDlgItem(hWnd,idc),TBM_SETRANGEMIN,FALSE,minrange); \ + SendMessage(GetDlgItem(hWnd,idc),TBM_SETRANGEMAX,FALSE,maxrange); \ + SendMessage(GetDlgItem(hWnd,idc),TBM_SETTICFREQ,tickfreq,0); \ + SendMessage(GetDlgItem(hWnd,idc),TBM_SETPAGESIZE,0,pagesize); \ + SendMessage(GetDlgItem(hWnd,idc),TBM_SETLINESIZE,0,linesize) + +#define HANDLE_SCROLL_MESSAGE(idc,idcDisplay) \ + if((HWND)lParam == GetDlgItem(hWnd,idc)) return DoHandleScrollMessage( GetDlgItem(hWnd,idcDisplay), wParam, lParam ) + + +// *** BEGIN DRIVER-SPECIFIC CONFIGURATION *** +// ------------------------------------------- + +struct CONFIG_XAUDIO2 +{ + std::wstring Device; + s8 NumBuffers; + + CONFIG_XAUDIO2() : + Device(), + NumBuffers( 2 ) + { + } +}; + +struct CONFIG_WAVEOUT +{ + std::wstring Device; + s8 NumBuffers; + + CONFIG_WAVEOUT() : + Device(), + NumBuffers( 4 ) + { + } +}; + +extern CONFIG_WAVEOUT Config_WaveOut; +extern CONFIG_XAUDIO2 Config_XAudio2; diff --git a/plugins/zerogs/dx/x86.cpp b/plugins/zerogs/dx/x86.cpp index 3f14a14c25..c09b491c62 100644 --- a/plugins/zerogs/dx/x86.cpp +++ b/plugins/zerogs/dx/x86.cpp @@ -1,573 +1,573 @@ -/* ZeroGS KOSMOS - * Copyright (C) 2005-2006 Gabest/zerofrog@gmail.com - * http://www.gabest.org - * - * This Program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This Program 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 GNU Make; see the file COPYING. If not, write to - * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - * http://www.gnu.org/copyleft/gpl.html - * - */ - -#include - -#include "GS.h" -#include "Mem.h" -#include "x86.h" - -#if defined(ZEROGS_SSE2) && (defined(_WIN32)||defined(__x86_64__)) -#include -#include -#endif - -// swizzling - -void FASTCALL(SwizzleBlock32_c(u8* dst, u8* src, int srcpitch, u32 WriteMask)) -{ - u32* d = &g_columnTable32[0][0]; - - if(WriteMask == 0xffffffff) - { - for(int j = 0; j < 8; j++, d += 8, src += srcpitch) - for(int i = 0; i < 8; i++) - ((u32*)dst)[d[i]] = ((u32*)src)[i]; - } - else - { - for(int j = 0; j < 8; j++, d += 8, src += srcpitch) - for(int i = 0; i < 8; i++) - ((u32*)dst)[d[i]] = (((u32*)dst)[d[i]] & ~WriteMask) | (((u32*)src)[i] & WriteMask); - } -} - -void FASTCALL(SwizzleBlock16_c(u8* dst, u8* src, int srcpitch)) -{ - u32* d = &g_columnTable16[0][0]; - - for(int j = 0; j < 8; j++, d += 16, src += srcpitch) - for(int i = 0; i < 16; i++) - ((u16*)dst)[d[i]] = ((u16*)src)[i]; -} - -void FASTCALL(SwizzleBlock8_c(u8* dst, u8* src, int srcpitch)) -{ - u32* d = &g_columnTable8[0][0]; - - for(int j = 0; j < 16; j++, d += 16, src += srcpitch) - for(int i = 0; i < 16; i++) - dst[d[i]] = src[i]; -} - -void FASTCALL(SwizzleBlock4_c(u8* dst, u8* src, int srcpitch)) -{ - u32* d = &g_columnTable4[0][0]; - - for(int j = 0; j < 16; j++, d += 32, src += srcpitch) - { - for(int i = 0; i < 32; i++) - { - u32 addr = d[i]; - u8 c = (src[i>>1] >> ((i&1) << 2)) & 0x0f; - u32 shift = (addr&1) << 2; - dst[addr >> 1] = (dst[addr >> 1] & (0xf0 >> shift)) | (c << shift); - } - } -} - -#define _FrameSwizzleBlock(type, transfer, transfer16, incsrc) \ -/* FrameSwizzleBlock32 */ \ -void FASTCALL(FrameSwizzleBlock32##type##c(u32* dst, u32* src, int srcpitch, u32 WriteMask)) \ -{ \ - u32* d = &g_columnTable32[0][0]; \ - \ - if( WriteMask == 0xffffffff ) { \ - for(int i = 0; i < 8; ++i, d += 8) { \ - for(int j = 0; j < 8; ++j) { \ - dst[d[j]] = (transfer); \ - } \ - src += srcpitch << incsrc; \ - } \ - } \ - else { \ - for(int i = 0; i < 8; ++i, d += 8) { \ - for(int j = 0; j < 8; ++j) { \ - dst[d[j]] = ((transfer)&WriteMask)|(dst[d[j]]&~WriteMask); \ - } \ - src += srcpitch << incsrc; \ - } \ - } \ -} \ -\ -/* FrameSwizzleBlock16 */ \ -void FASTCALL(FrameSwizzleBlock16##type##c(u16* dst, u32* src, int srcpitch, u32 WriteMask)) \ -{ \ - u32* d = &g_columnTable16[0][0]; \ - \ - if( WriteMask == 0xffff ) { \ - for(int i = 0; i < 8; ++i, d += 16) { \ - for(int j = 0; j < 16; ++j) { \ - u32 temp = (transfer); \ - dst[d[j]] = RGBA32to16(temp); \ - } \ - src += srcpitch << incsrc; \ - } \ - } \ - else { \ - for(int i = 0; i < 8; ++i, d += 16) { \ - for(int j = 0; j < 16; ++j) { \ - u32 temp = (transfer); \ - u32 dsrc = RGBA32to16(temp); \ - dst[d[j]] = (dsrc&WriteMask)|(dst[d[j]]&~WriteMask); \ - } \ - src += srcpitch << incsrc; \ - } \ - } \ -} \ -\ -/* Frame16SwizzleBlock32 */ \ -void FASTCALL(Frame16SwizzleBlock32##type##c(u32* dst, Vector_16F* src, int srcpitch, u32 WriteMask)) \ -{ \ - u32* d = &g_columnTable32[0][0]; \ -\ - if( WriteMask == 0xffffffff ) { \ - for(int i = 0; i < 8; ++i, d += 8) { \ - for(int j = 0; j < 8; ++j) { \ - Vector_16F dsrc16 = (transfer16); \ - dst[d[j]] = Float16ToARGB(dsrc16); \ - } \ - src += srcpitch << incsrc; \ - } \ - } \ - else { \ - for(int i = 0; i < 8; ++i, d += 8) { \ - for(int j = 0; j < 8; ++j) { \ - Vector_16F dsrc16 = (transfer16); \ - u32 dsrc = Float16ToARGB(dsrc16); \ - dst[d[j]] = (dsrc&WriteMask)|(dst[d[j]]&~WriteMask); \ - } \ - src += srcpitch << incsrc; \ - } \ - } \ - } \ -\ -/* Frame16SwizzleBlock32Z */ \ -void FASTCALL(Frame16SwizzleBlock32Z##type##c(u32* dst, Vector_16F* src, int srcpitch, u32 WriteMask)) \ -{ \ - u32* d = &g_columnTable32[0][0]; \ - if( WriteMask == 0xffffffff ) { /* breaks KH text if not checked */ \ - for(int i = 0; i < 8; ++i, d += 8) { \ - for(int j = 0; j < 8; ++j) { \ - Vector_16F dsrc16 = (transfer16); \ - dst[d[j]] = Float16ToARGB_Z(dsrc16); \ - } \ - src += srcpitch << incsrc; \ - } \ - } \ - else { \ - for(int i = 0; i < 8; ++i, d += 8) { \ - for(int j = 0; j < 8; ++j) { \ - Vector_16F dsrc16 = (transfer16); \ - u32 dsrc = Float16ToARGB_Z(dsrc16); \ - dst[d[j]] = (dsrc&WriteMask)|(dst[d[j]]&~WriteMask); \ - } \ - src += srcpitch << incsrc; \ - } \ - } \ - } \ - \ - /* Frame16SwizzleBlock16 */ \ -void FASTCALL(Frame16SwizzleBlock16##type##c(u16* dst, Vector_16F* src, int srcpitch, u32 WriteMask)) \ -{ \ - u32* d = &g_columnTable16[0][0]; \ - \ - if( (WriteMask&0xfff8f8f8) == 0xfff8f8f8) { \ - for(int i = 0; i < 8; ++i, d += 16) { \ - for(int j = 0; j < 16; ++j) { \ - Vector_16F dsrc16 = (transfer16); \ - dst[d[j]] = Float16ToARGB16(dsrc16); \ - } \ - src += srcpitch << incsrc; \ - } \ - } \ - else { \ - for(int i = 0; i < 8; ++i, d += 16) { \ - for(int j = 0; j < 16; ++j) { \ - Vector_16F dsrc16 = (transfer16); \ - u32 dsrc = Float16ToARGB16(dsrc16); \ - dst[d[j]] = (dsrc&WriteMask)|(dst[d[j]]&~WriteMask); \ - } \ - src += srcpitch << incsrc; \ - } \ - } \ - } \ - \ - /* Frame16SwizzleBlock16Z */ \ -void FASTCALL(Frame16SwizzleBlock16Z##type##c(u16* dst, Vector_16F* src, int srcpitch, u32 WriteMask)) \ -{ \ - u32* d = &g_columnTable16[0][0]; \ - \ - for(int i = 0; i < 8; ++i, d += 16) { \ - for(int j = 0; j < 16; ++j) { \ - Vector_16F dsrc16 = (transfer16); \ - dst[d[j]] = Float16ToARGB16_Z(dsrc16); \ - } \ - src += srcpitch << incsrc; \ - } \ -} \ - -_FrameSwizzleBlock(_, src[j], src[j], 0); -_FrameSwizzleBlock(A2_, (src[2*j]+src[2*j+1])>>1, src[2*j], 0); -_FrameSwizzleBlock(A4_, (src[2*j]+src[2*j+1]+src[2*j+srcpitch]+src[2*j+srcpitch+1])>>2, src[2*j], 1); - -#ifdef ZEROGS_SSE2 - -//void FASTCALL(WriteCLUT_T16_I8_CSM1_sse2(u32* vm, u32* clut) -//{ -// __asm { -// mov eax, vm -// mov ecx, clut -// mov edx, 8 -// } -// -//Extract32x2: -// __asm { -// movdqa xmm0, qword ptr [eax] -// movdqa xmm1, qword ptr [eax+16] -// movdqa xmm2, qword ptr [eax+32] -// movdqa xmm3, qword ptr [eax+48] -// -// // rearrange -// pshuflw xmm0, xmm0, 0xd8 -// pshufhw xmm0, xmm0, 0xd8 -// pshuflw xmm1, xmm1, 0xd8 -// pshufhw xmm1, xmm1, 0xd8 -// pshuflw xmm2, xmm2, 0xd8 -// pshufhw xmm2, xmm2, 0xd8 -// pshuflw xmm3, xmm3, 0xd8 -// pshufhw xmm3, xmm3, 0xd8 -// -// movdqa xmm4, xmm0 -// movdqa xmm6, xmm2 -// -// shufps xmm0, xmm1, 0x88 -// shufps xmm2, xmm3, 0x88 -// -// shufps xmm4, xmm1, 0xdd -// shufps xmm6, xmm3, 0xdd -// -// pshufd xmm0, xmm0, 0xd8 -// pshufd xmm2, xmm2, 0xd8 -// pshufd xmm4, xmm4, 0xd8 -// pshufd xmm6, xmm6, 0xd8 -// -// // left column -// movhlps xmm1, xmm0 -// movlhps xmm0, xmm2 -// //movdqa xmm7, [ecx] -// -// movdqa [ecx], xmm0 -// shufps xmm1, xmm2, 0xe4 -// movdqa [ecx+16], xmm1 -// -// // right column -// movhlps xmm3, xmm4 -// movlhps xmm4, xmm6 -// movdqa [ecx+32], xmm4 -// shufps xmm3, xmm6, 0xe4 -// movdqa [ecx+48], xmm3 -// -// add eax, 16*4 -// add ecx, 16*8 -// sub edx, 1 -// cmp edx, 0 -// jne Extract32x2 -// } -//} - -#if (defined(_WIN32)||defined(__x86_64__)) - -extern "C" void FASTCALL(WriteCLUT_T32_I8_CSM1_sse2(u32* vm, u32* clut)) -{ - __m128i* src = (__m128i*)vm; - __m128i* dst = (__m128i*)clut; - - for(int j = 0; j < 64; j += 32, src += 32, dst += 32) - { - for(int i = 0; i < 16; i += 4) - { - __m128i r0 = _mm_load_si128(&src[i+0]); - __m128i r1 = _mm_load_si128(&src[i+1]); - __m128i r2 = _mm_load_si128(&src[i+2]); - __m128i r3 = _mm_load_si128(&src[i+3]); - - _mm_store_si128(&dst[i*2+0], _mm_unpacklo_epi64(r0, r1)); - _mm_store_si128(&dst[i*2+1], _mm_unpacklo_epi64(r2, r3)); - _mm_store_si128(&dst[i*2+2], _mm_unpackhi_epi64(r0, r1)); - _mm_store_si128(&dst[i*2+3], _mm_unpackhi_epi64(r2, r3)); - - __m128i r4 = _mm_load_si128(&src[i+0+16]); - __m128i r5 = _mm_load_si128(&src[i+1+16]); - __m128i r6 = _mm_load_si128(&src[i+2+16]); - __m128i r7 = _mm_load_si128(&src[i+3+16]); - - _mm_store_si128(&dst[i*2+4], _mm_unpacklo_epi64(r4, r5)); - _mm_store_si128(&dst[i*2+5], _mm_unpacklo_epi64(r6, r7)); - _mm_store_si128(&dst[i*2+6], _mm_unpackhi_epi64(r4, r5)); - _mm_store_si128(&dst[i*2+7], _mm_unpackhi_epi64(r6, r7)); - } - } -} - -extern "C" void FASTCALL(WriteCLUT_T32_I4_CSM1_sse2(u32* vm, u32* clut)) -{ - __m128i* src = (__m128i*)vm; - __m128i* dst = (__m128i*)clut; - - __m128i r0 = _mm_load_si128(&src[0]); - __m128i r1 = _mm_load_si128(&src[1]); - __m128i r2 = _mm_load_si128(&src[2]); - __m128i r3 = _mm_load_si128(&src[3]); - - _mm_store_si128(&dst[0], _mm_unpacklo_epi64(r0, r1)); - _mm_store_si128(&dst[1], _mm_unpacklo_epi64(r2, r3)); - _mm_store_si128(&dst[2], _mm_unpackhi_epi64(r0, r1)); - _mm_store_si128(&dst[3], _mm_unpackhi_epi64(r2, r3)); -} -#endif - -#if defined(_MSC_VER) - -extern "C" { -__aligned16 int s_clut16mask2[4] = { 0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff }; -__aligned16 int s_clut16mask[8] = { 0xffff0000, 0xffff0000, 0xffff0000, 0xffff0000, - 0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff}; -} - -#if !defined(__x86_64__) - -extern "C" void FASTCALL(WriteCLUT_T16_I4_CSM1_sse2(u32* vm, u32* clut)) -{ - __asm { - mov eax, vm - mov ecx, clut - movdqa xmm0, qword ptr [eax] - movdqa xmm1, qword ptr [eax+16] - movdqa xmm2, qword ptr [eax+32] - movdqa xmm3, qword ptr [eax+48] - - // rearrange - pshuflw xmm0, xmm0, 0x88 - pshufhw xmm0, xmm0, 0x88 - pshuflw xmm1, xmm1, 0x88 - pshufhw xmm1, xmm1, 0x88 - pshuflw xmm2, xmm2, 0x88 - pshufhw xmm2, xmm2, 0x88 - pshuflw xmm3, xmm3, 0x88 - pshufhw xmm3, xmm3, 0x88 - - shufps xmm0, xmm1, 0x88 - shufps xmm2, xmm3, 0x88 - - pshufd xmm0, xmm0, 0xd8 - pshufd xmm2, xmm2, 0xd8 - - pxor xmm6, xmm6 - - test ecx, 15 - jnz WriteUnaligned - - movdqa xmm7, s_clut16mask // saves upper 16 bits - - // have to save interlaced with the old data - movdqa xmm4, [ecx] - movdqa xmm5, [ecx+32] - movhlps xmm1, xmm0 - movlhps xmm0, xmm2 // lower 8 colors - - pand xmm4, xmm7 - pand xmm5, xmm7 - - shufps xmm1, xmm2, 0xe4 // upper 8 colors - movdqa xmm2, xmm0 - movdqa xmm3, xmm1 - - punpcklwd xmm0, xmm6 - punpcklwd xmm1, xmm6 - por xmm0, xmm4 - por xmm1, xmm5 - - punpckhwd xmm2, xmm6 - punpckhwd xmm3, xmm6 - - movdqa [ecx], xmm0 - movdqa [ecx+32], xmm1 - - movdqa xmm5, xmm7 - pand xmm7, [ecx+16] - pand xmm5, [ecx+48] - - por xmm2, xmm7 - por xmm3, xmm5 - - movdqa [ecx+16], xmm2 - movdqa [ecx+48], xmm3 - jmp End - -WriteUnaligned: - // ecx is offset by 2 - sub ecx, 2 - - movdqa xmm7, s_clut16mask2 // saves lower 16 bits - - // have to save interlaced with the old data - movdqa xmm4, [ecx] - movdqa xmm5, [ecx+32] - movhlps xmm1, xmm0 - movlhps xmm0, xmm2 // lower 8 colors - - pand xmm4, xmm7 - pand xmm5, xmm7 - - shufps xmm1, xmm2, 0xe4 // upper 8 colors - movdqa xmm2, xmm0 - movdqa xmm3, xmm1 - - punpcklwd xmm0, xmm6 - punpcklwd xmm1, xmm6 - pslld xmm0, 16 - pslld xmm1, 16 - por xmm0, xmm4 - por xmm1, xmm5 - - punpckhwd xmm2, xmm6 - punpckhwd xmm3, xmm6 - pslld xmm2, 16 - pslld xmm3, 16 - - movdqa [ecx], xmm0 - movdqa [ecx+32], xmm1 - - movdqa xmm5, xmm7 - pand xmm7, [ecx+16] - pand xmm5, [ecx+48] - - por xmm2, xmm7 - por xmm3, xmm5 - - movdqa [ecx+16], xmm2 - movdqa [ecx+48], xmm3 -End: - } -} -#endif // __x86_64__ -#endif // _MSC_VER - -#endif // ZEROGS_SSE2 - -void FASTCALL(WriteCLUT_T16_I8_CSM1_c(u32* _vm, u32* _clut)) -{ - const static u32 map[] = - { - 0, 2, 8, 10, 16, 18, 24, 26, - 4, 6, 12, 14, 20, 22, 28, 30, - 1, 3, 9, 11, 17, 19, 25, 27, - 5, 7, 13, 15, 21, 23, 29, 31 - }; - - u16* vm = (u16*)_vm; - u16* clut = (u16*)_clut; - - int left = ((u32)(uptr)clut&2) ? 512 : 512-(((u32)(uptr)clut)&0x3ff)/2; - - for(int j = 0; j < 8; j++, vm += 32, clut += 64, left -= 32) - { - if(left == 32) { - assert( left == 32 ); - for(int i = 0; i < 16; i++) - clut[2*i] = vm[map[i]]; - - clut = (u16*)((uptr)clut & ~0x3ff) + 1; - - for(int i = 16; i < 32; i++) - clut[2*i] = vm[map[i]]; - } - else { - if( left == 0 ) { - clut = (u16*)((uptr)clut & ~0x3ff) + 1; - left = -1; - } - - for(int i = 0; i < 32; i++) - clut[2*i] = vm[map[i]]; - } - } -} - -void FASTCALL(WriteCLUT_T32_I8_CSM1_c(u32* vm, u32* clut)) -{ - u64* src = (u64*)vm; - u64* dst = (u64*)clut; - - for(int j = 0; j < 2; j++, src += 32) { - for(int i = 0; i < 4; i++, dst+=16, src+=8) - { - dst[0] = src[0]; - dst[1] = src[2]; - dst[2] = src[4]; - dst[3] = src[6]; - dst[4] = src[1]; - dst[5] = src[3]; - dst[6] = src[5]; - dst[7] = src[7]; - - dst[8] = src[32]; - dst[9] = src[32+2]; - dst[10] = src[32+4]; - dst[11] = src[32+6]; - dst[12] = src[32+1]; - dst[13] = src[32+3]; - dst[14] = src[32+5]; - dst[15] = src[32+7]; - } - } -} - -void FASTCALL(WriteCLUT_T16_I4_CSM1_c(u32* _vm, u32* _clut)) -{ - u16* dst = (u16*)_clut; - u16* src = (u16*)_vm; - - dst[0] = src[0]; dst[2] = src[2]; - dst[4] = src[8]; dst[6] = src[10]; - dst[8] = src[16]; dst[10] = src[18]; - dst[12] = src[24]; dst[14] = src[26]; - dst[16] = src[4]; dst[18] = src[6]; - dst[20] = src[12]; dst[22] = src[14]; - dst[24] = src[20]; dst[26] = src[22]; - dst[28] = src[28]; dst[30] = src[30]; -} - -void FASTCALL(WriteCLUT_T32_I4_CSM1_c(u32* vm, u32* clut)) -{ - u64* src = (u64*)vm; - u64* dst = (u64*)clut; - - dst[0] = src[0]; - dst[1] = src[2]; - dst[2] = src[4]; - dst[3] = src[6]; - dst[4] = src[1]; - dst[5] = src[3]; - dst[6] = src[5]; - dst[7] = src[7]; -} +/* ZeroGS KOSMOS + * Copyright (C) 2005-2006 Gabest/zerofrog@gmail.com + * http://www.gabest.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program 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 GNU Make; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include + +#include "GS.h" +#include "Mem.h" +#include "x86.h" + +#if defined(ZEROGS_SSE2) && (defined(_WIN32)||defined(__x86_64__)) +#include +#include +#endif + +// swizzling + +void FASTCALL(SwizzleBlock32_c(u8* dst, u8* src, int srcpitch, u32 WriteMask)) +{ + u32* d = &g_columnTable32[0][0]; + + if(WriteMask == 0xffffffff) + { + for(int j = 0; j < 8; j++, d += 8, src += srcpitch) + for(int i = 0; i < 8; i++) + ((u32*)dst)[d[i]] = ((u32*)src)[i]; + } + else + { + for(int j = 0; j < 8; j++, d += 8, src += srcpitch) + for(int i = 0; i < 8; i++) + ((u32*)dst)[d[i]] = (((u32*)dst)[d[i]] & ~WriteMask) | (((u32*)src)[i] & WriteMask); + } +} + +void FASTCALL(SwizzleBlock16_c(u8* dst, u8* src, int srcpitch)) +{ + u32* d = &g_columnTable16[0][0]; + + for(int j = 0; j < 8; j++, d += 16, src += srcpitch) + for(int i = 0; i < 16; i++) + ((u16*)dst)[d[i]] = ((u16*)src)[i]; +} + +void FASTCALL(SwizzleBlock8_c(u8* dst, u8* src, int srcpitch)) +{ + u32* d = &g_columnTable8[0][0]; + + for(int j = 0; j < 16; j++, d += 16, src += srcpitch) + for(int i = 0; i < 16; i++) + dst[d[i]] = src[i]; +} + +void FASTCALL(SwizzleBlock4_c(u8* dst, u8* src, int srcpitch)) +{ + u32* d = &g_columnTable4[0][0]; + + for(int j = 0; j < 16; j++, d += 32, src += srcpitch) + { + for(int i = 0; i < 32; i++) + { + u32 addr = d[i]; + u8 c = (src[i>>1] >> ((i&1) << 2)) & 0x0f; + u32 shift = (addr&1) << 2; + dst[addr >> 1] = (dst[addr >> 1] & (0xf0 >> shift)) | (c << shift); + } + } +} + +#define _FrameSwizzleBlock(type, transfer, transfer16, incsrc) \ +/* FrameSwizzleBlock32 */ \ +void FASTCALL(FrameSwizzleBlock32##type##c(u32* dst, u32* src, int srcpitch, u32 WriteMask)) \ +{ \ + u32* d = &g_columnTable32[0][0]; \ + \ + if( WriteMask == 0xffffffff ) { \ + for(int i = 0; i < 8; ++i, d += 8) { \ + for(int j = 0; j < 8; ++j) { \ + dst[d[j]] = (transfer); \ + } \ + src += srcpitch << incsrc; \ + } \ + } \ + else { \ + for(int i = 0; i < 8; ++i, d += 8) { \ + for(int j = 0; j < 8; ++j) { \ + dst[d[j]] = ((transfer)&WriteMask)|(dst[d[j]]&~WriteMask); \ + } \ + src += srcpitch << incsrc; \ + } \ + } \ +} \ +\ +/* FrameSwizzleBlock16 */ \ +void FASTCALL(FrameSwizzleBlock16##type##c(u16* dst, u32* src, int srcpitch, u32 WriteMask)) \ +{ \ + u32* d = &g_columnTable16[0][0]; \ + \ + if( WriteMask == 0xffff ) { \ + for(int i = 0; i < 8; ++i, d += 16) { \ + for(int j = 0; j < 16; ++j) { \ + u32 temp = (transfer); \ + dst[d[j]] = RGBA32to16(temp); \ + } \ + src += srcpitch << incsrc; \ + } \ + } \ + else { \ + for(int i = 0; i < 8; ++i, d += 16) { \ + for(int j = 0; j < 16; ++j) { \ + u32 temp = (transfer); \ + u32 dsrc = RGBA32to16(temp); \ + dst[d[j]] = (dsrc&WriteMask)|(dst[d[j]]&~WriteMask); \ + } \ + src += srcpitch << incsrc; \ + } \ + } \ +} \ +\ +/* Frame16SwizzleBlock32 */ \ +void FASTCALL(Frame16SwizzleBlock32##type##c(u32* dst, Vector_16F* src, int srcpitch, u32 WriteMask)) \ +{ \ + u32* d = &g_columnTable32[0][0]; \ +\ + if( WriteMask == 0xffffffff ) { \ + for(int i = 0; i < 8; ++i, d += 8) { \ + for(int j = 0; j < 8; ++j) { \ + Vector_16F dsrc16 = (transfer16); \ + dst[d[j]] = Float16ToARGB(dsrc16); \ + } \ + src += srcpitch << incsrc; \ + } \ + } \ + else { \ + for(int i = 0; i < 8; ++i, d += 8) { \ + for(int j = 0; j < 8; ++j) { \ + Vector_16F dsrc16 = (transfer16); \ + u32 dsrc = Float16ToARGB(dsrc16); \ + dst[d[j]] = (dsrc&WriteMask)|(dst[d[j]]&~WriteMask); \ + } \ + src += srcpitch << incsrc; \ + } \ + } \ + } \ +\ +/* Frame16SwizzleBlock32Z */ \ +void FASTCALL(Frame16SwizzleBlock32Z##type##c(u32* dst, Vector_16F* src, int srcpitch, u32 WriteMask)) \ +{ \ + u32* d = &g_columnTable32[0][0]; \ + if( WriteMask == 0xffffffff ) { /* breaks KH text if not checked */ \ + for(int i = 0; i < 8; ++i, d += 8) { \ + for(int j = 0; j < 8; ++j) { \ + Vector_16F dsrc16 = (transfer16); \ + dst[d[j]] = Float16ToARGB_Z(dsrc16); \ + } \ + src += srcpitch << incsrc; \ + } \ + } \ + else { \ + for(int i = 0; i < 8; ++i, d += 8) { \ + for(int j = 0; j < 8; ++j) { \ + Vector_16F dsrc16 = (transfer16); \ + u32 dsrc = Float16ToARGB_Z(dsrc16); \ + dst[d[j]] = (dsrc&WriteMask)|(dst[d[j]]&~WriteMask); \ + } \ + src += srcpitch << incsrc; \ + } \ + } \ + } \ + \ + /* Frame16SwizzleBlock16 */ \ +void FASTCALL(Frame16SwizzleBlock16##type##c(u16* dst, Vector_16F* src, int srcpitch, u32 WriteMask)) \ +{ \ + u32* d = &g_columnTable16[0][0]; \ + \ + if( (WriteMask&0xfff8f8f8) == 0xfff8f8f8) { \ + for(int i = 0; i < 8; ++i, d += 16) { \ + for(int j = 0; j < 16; ++j) { \ + Vector_16F dsrc16 = (transfer16); \ + dst[d[j]] = Float16ToARGB16(dsrc16); \ + } \ + src += srcpitch << incsrc; \ + } \ + } \ + else { \ + for(int i = 0; i < 8; ++i, d += 16) { \ + for(int j = 0; j < 16; ++j) { \ + Vector_16F dsrc16 = (transfer16); \ + u32 dsrc = Float16ToARGB16(dsrc16); \ + dst[d[j]] = (dsrc&WriteMask)|(dst[d[j]]&~WriteMask); \ + } \ + src += srcpitch << incsrc; \ + } \ + } \ + } \ + \ + /* Frame16SwizzleBlock16Z */ \ +void FASTCALL(Frame16SwizzleBlock16Z##type##c(u16* dst, Vector_16F* src, int srcpitch, u32 WriteMask)) \ +{ \ + u32* d = &g_columnTable16[0][0]; \ + \ + for(int i = 0; i < 8; ++i, d += 16) { \ + for(int j = 0; j < 16; ++j) { \ + Vector_16F dsrc16 = (transfer16); \ + dst[d[j]] = Float16ToARGB16_Z(dsrc16); \ + } \ + src += srcpitch << incsrc; \ + } \ +} \ + +_FrameSwizzleBlock(_, src[j], src[j], 0); +_FrameSwizzleBlock(A2_, (src[2*j]+src[2*j+1])>>1, src[2*j], 0); +_FrameSwizzleBlock(A4_, (src[2*j]+src[2*j+1]+src[2*j+srcpitch]+src[2*j+srcpitch+1])>>2, src[2*j], 1); + +#ifdef ZEROGS_SSE2 + +//void FASTCALL(WriteCLUT_T16_I8_CSM1_sse2(u32* vm, u32* clut) +//{ +// __asm { +// mov eax, vm +// mov ecx, clut +// mov edx, 8 +// } +// +//Extract32x2: +// __asm { +// movdqa xmm0, qword ptr [eax] +// movdqa xmm1, qword ptr [eax+16] +// movdqa xmm2, qword ptr [eax+32] +// movdqa xmm3, qword ptr [eax+48] +// +// // rearrange +// pshuflw xmm0, xmm0, 0xd8 +// pshufhw xmm0, xmm0, 0xd8 +// pshuflw xmm1, xmm1, 0xd8 +// pshufhw xmm1, xmm1, 0xd8 +// pshuflw xmm2, xmm2, 0xd8 +// pshufhw xmm2, xmm2, 0xd8 +// pshuflw xmm3, xmm3, 0xd8 +// pshufhw xmm3, xmm3, 0xd8 +// +// movdqa xmm4, xmm0 +// movdqa xmm6, xmm2 +// +// shufps xmm0, xmm1, 0x88 +// shufps xmm2, xmm3, 0x88 +// +// shufps xmm4, xmm1, 0xdd +// shufps xmm6, xmm3, 0xdd +// +// pshufd xmm0, xmm0, 0xd8 +// pshufd xmm2, xmm2, 0xd8 +// pshufd xmm4, xmm4, 0xd8 +// pshufd xmm6, xmm6, 0xd8 +// +// // left column +// movhlps xmm1, xmm0 +// movlhps xmm0, xmm2 +// //movdqa xmm7, [ecx] +// +// movdqa [ecx], xmm0 +// shufps xmm1, xmm2, 0xe4 +// movdqa [ecx+16], xmm1 +// +// // right column +// movhlps xmm3, xmm4 +// movlhps xmm4, xmm6 +// movdqa [ecx+32], xmm4 +// shufps xmm3, xmm6, 0xe4 +// movdqa [ecx+48], xmm3 +// +// add eax, 16*4 +// add ecx, 16*8 +// sub edx, 1 +// cmp edx, 0 +// jne Extract32x2 +// } +//} + +#if (defined(_WIN32)||defined(__x86_64__)) + +extern "C" void FASTCALL(WriteCLUT_T32_I8_CSM1_sse2(u32* vm, u32* clut)) +{ + __m128i* src = (__m128i*)vm; + __m128i* dst = (__m128i*)clut; + + for(int j = 0; j < 64; j += 32, src += 32, dst += 32) + { + for(int i = 0; i < 16; i += 4) + { + __m128i r0 = _mm_load_si128(&src[i+0]); + __m128i r1 = _mm_load_si128(&src[i+1]); + __m128i r2 = _mm_load_si128(&src[i+2]); + __m128i r3 = _mm_load_si128(&src[i+3]); + + _mm_store_si128(&dst[i*2+0], _mm_unpacklo_epi64(r0, r1)); + _mm_store_si128(&dst[i*2+1], _mm_unpacklo_epi64(r2, r3)); + _mm_store_si128(&dst[i*2+2], _mm_unpackhi_epi64(r0, r1)); + _mm_store_si128(&dst[i*2+3], _mm_unpackhi_epi64(r2, r3)); + + __m128i r4 = _mm_load_si128(&src[i+0+16]); + __m128i r5 = _mm_load_si128(&src[i+1+16]); + __m128i r6 = _mm_load_si128(&src[i+2+16]); + __m128i r7 = _mm_load_si128(&src[i+3+16]); + + _mm_store_si128(&dst[i*2+4], _mm_unpacklo_epi64(r4, r5)); + _mm_store_si128(&dst[i*2+5], _mm_unpacklo_epi64(r6, r7)); + _mm_store_si128(&dst[i*2+6], _mm_unpackhi_epi64(r4, r5)); + _mm_store_si128(&dst[i*2+7], _mm_unpackhi_epi64(r6, r7)); + } + } +} + +extern "C" void FASTCALL(WriteCLUT_T32_I4_CSM1_sse2(u32* vm, u32* clut)) +{ + __m128i* src = (__m128i*)vm; + __m128i* dst = (__m128i*)clut; + + __m128i r0 = _mm_load_si128(&src[0]); + __m128i r1 = _mm_load_si128(&src[1]); + __m128i r2 = _mm_load_si128(&src[2]); + __m128i r3 = _mm_load_si128(&src[3]); + + _mm_store_si128(&dst[0], _mm_unpacklo_epi64(r0, r1)); + _mm_store_si128(&dst[1], _mm_unpacklo_epi64(r2, r3)); + _mm_store_si128(&dst[2], _mm_unpackhi_epi64(r0, r1)); + _mm_store_si128(&dst[3], _mm_unpackhi_epi64(r2, r3)); +} +#endif + +#if defined(_MSC_VER) + +extern "C" { +__aligned16 int s_clut16mask2[4] = { 0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff }; +__aligned16 int s_clut16mask[8] = { 0xffff0000, 0xffff0000, 0xffff0000, 0xffff0000, + 0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff}; +} + +#if !defined(__x86_64__) + +extern "C" void FASTCALL(WriteCLUT_T16_I4_CSM1_sse2(u32* vm, u32* clut)) +{ + __asm { + mov eax, vm + mov ecx, clut + movdqa xmm0, qword ptr [eax] + movdqa xmm1, qword ptr [eax+16] + movdqa xmm2, qword ptr [eax+32] + movdqa xmm3, qword ptr [eax+48] + + // rearrange + pshuflw xmm0, xmm0, 0x88 + pshufhw xmm0, xmm0, 0x88 + pshuflw xmm1, xmm1, 0x88 + pshufhw xmm1, xmm1, 0x88 + pshuflw xmm2, xmm2, 0x88 + pshufhw xmm2, xmm2, 0x88 + pshuflw xmm3, xmm3, 0x88 + pshufhw xmm3, xmm3, 0x88 + + shufps xmm0, xmm1, 0x88 + shufps xmm2, xmm3, 0x88 + + pshufd xmm0, xmm0, 0xd8 + pshufd xmm2, xmm2, 0xd8 + + pxor xmm6, xmm6 + + test ecx, 15 + jnz WriteUnaligned + + movdqa xmm7, s_clut16mask // saves upper 16 bits + + // have to save interlaced with the old data + movdqa xmm4, [ecx] + movdqa xmm5, [ecx+32] + movhlps xmm1, xmm0 + movlhps xmm0, xmm2 // lower 8 colors + + pand xmm4, xmm7 + pand xmm5, xmm7 + + shufps xmm1, xmm2, 0xe4 // upper 8 colors + movdqa xmm2, xmm0 + movdqa xmm3, xmm1 + + punpcklwd xmm0, xmm6 + punpcklwd xmm1, xmm6 + por xmm0, xmm4 + por xmm1, xmm5 + + punpckhwd xmm2, xmm6 + punpckhwd xmm3, xmm6 + + movdqa [ecx], xmm0 + movdqa [ecx+32], xmm1 + + movdqa xmm5, xmm7 + pand xmm7, [ecx+16] + pand xmm5, [ecx+48] + + por xmm2, xmm7 + por xmm3, xmm5 + + movdqa [ecx+16], xmm2 + movdqa [ecx+48], xmm3 + jmp End + +WriteUnaligned: + // ecx is offset by 2 + sub ecx, 2 + + movdqa xmm7, s_clut16mask2 // saves lower 16 bits + + // have to save interlaced with the old data + movdqa xmm4, [ecx] + movdqa xmm5, [ecx+32] + movhlps xmm1, xmm0 + movlhps xmm0, xmm2 // lower 8 colors + + pand xmm4, xmm7 + pand xmm5, xmm7 + + shufps xmm1, xmm2, 0xe4 // upper 8 colors + movdqa xmm2, xmm0 + movdqa xmm3, xmm1 + + punpcklwd xmm0, xmm6 + punpcklwd xmm1, xmm6 + pslld xmm0, 16 + pslld xmm1, 16 + por xmm0, xmm4 + por xmm1, xmm5 + + punpckhwd xmm2, xmm6 + punpckhwd xmm3, xmm6 + pslld xmm2, 16 + pslld xmm3, 16 + + movdqa [ecx], xmm0 + movdqa [ecx+32], xmm1 + + movdqa xmm5, xmm7 + pand xmm7, [ecx+16] + pand xmm5, [ecx+48] + + por xmm2, xmm7 + por xmm3, xmm5 + + movdqa [ecx+16], xmm2 + movdqa [ecx+48], xmm3 +End: + } +} +#endif // __x86_64__ +#endif // _MSC_VER + +#endif // ZEROGS_SSE2 + +void FASTCALL(WriteCLUT_T16_I8_CSM1_c(u32* _vm, u32* _clut)) +{ + const static u32 map[] = + { + 0, 2, 8, 10, 16, 18, 24, 26, + 4, 6, 12, 14, 20, 22, 28, 30, + 1, 3, 9, 11, 17, 19, 25, 27, + 5, 7, 13, 15, 21, 23, 29, 31 + }; + + u16* vm = (u16*)_vm; + u16* clut = (u16*)_clut; + + int left = ((u32)(uptr)clut&2) ? 512 : 512-(((u32)(uptr)clut)&0x3ff)/2; + + for(int j = 0; j < 8; j++, vm += 32, clut += 64, left -= 32) + { + if(left == 32) { + assert( left == 32 ); + for(int i = 0; i < 16; i++) + clut[2*i] = vm[map[i]]; + + clut = (u16*)((uptr)clut & ~0x3ff) + 1; + + for(int i = 16; i < 32; i++) + clut[2*i] = vm[map[i]]; + } + else { + if( left == 0 ) { + clut = (u16*)((uptr)clut & ~0x3ff) + 1; + left = -1; + } + + for(int i = 0; i < 32; i++) + clut[2*i] = vm[map[i]]; + } + } +} + +void FASTCALL(WriteCLUT_T32_I8_CSM1_c(u32* vm, u32* clut)) +{ + u64* src = (u64*)vm; + u64* dst = (u64*)clut; + + for(int j = 0; j < 2; j++, src += 32) { + for(int i = 0; i < 4; i++, dst+=16, src+=8) + { + dst[0] = src[0]; + dst[1] = src[2]; + dst[2] = src[4]; + dst[3] = src[6]; + dst[4] = src[1]; + dst[5] = src[3]; + dst[6] = src[5]; + dst[7] = src[7]; + + dst[8] = src[32]; + dst[9] = src[32+2]; + dst[10] = src[32+4]; + dst[11] = src[32+6]; + dst[12] = src[32+1]; + dst[13] = src[32+3]; + dst[14] = src[32+5]; + dst[15] = src[32+7]; + } + } +} + +void FASTCALL(WriteCLUT_T16_I4_CSM1_c(u32* _vm, u32* _clut)) +{ + u16* dst = (u16*)_clut; + u16* src = (u16*)_vm; + + dst[0] = src[0]; dst[2] = src[2]; + dst[4] = src[8]; dst[6] = src[10]; + dst[8] = src[16]; dst[10] = src[18]; + dst[12] = src[24]; dst[14] = src[26]; + dst[16] = src[4]; dst[18] = src[6]; + dst[20] = src[12]; dst[22] = src[14]; + dst[24] = src[20]; dst[26] = src[22]; + dst[28] = src[28]; dst[30] = src[30]; +} + +void FASTCALL(WriteCLUT_T32_I4_CSM1_c(u32* vm, u32* clut)) +{ + u64* src = (u64*)vm; + u64* dst = (u64*)clut; + + dst[0] = src[0]; + dst[1] = src[2]; + dst[2] = src[4]; + dst[3] = src[6]; + dst[4] = src[1]; + dst[5] = src[3]; + dst[6] = src[5]; + dst[7] = src[7]; +} diff --git a/plugins/zeropad/Windows/win.h b/plugins/zeropad/Windows/win.h index 0401f20031..7e6c72225f 100644 --- a/plugins/zeropad/Windows/win.h +++ b/plugins/zeropad/Windows/win.h @@ -30,5 +30,5 @@ extern void SaveConfig(); extern void LoadConfig(); extern void SysMessage(char *fmt, ...); -extern LRESULT WINAPI PADwndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); -extern HINSTANCE hInst; \ No newline at end of file +extern LRESULT WINAPI PADwndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); +extern HINSTANCE hInst; diff --git a/plugins/zeropad/joystick.h b/plugins/zeropad/joystick.h index 30d278c496..26cb230fcc 100644 --- a/plugins/zeropad/joystick.h +++ b/plugins/zeropad/joystick.h @@ -18,7 +18,7 @@ #ifndef __JOYSTICK_H__ #define __JOYSTICK_H__ - + #ifdef __LINUX__ #include #else diff --git a/plugins/zerospu2/Targets/Alsa.h b/plugins/zerospu2/Targets/Alsa.h index c08f912552..efae32d903 100644 --- a/plugins/zerospu2/Targets/Alsa.h +++ b/plugins/zerospu2/Targets/Alsa.h @@ -16,8 +16,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef ALSA_H_INCLUDED -#define ALSA_H_INCLUDED +#ifndef ALSA_H_INCLUDED +#define ALSA_H_INCLUDED #include "SoundTargets.h" @@ -45,6 +45,6 @@ static SoundCallbacks AlsaCmds = (voidFunction)AlsaRemoveSound, (intFunction)AlsaSoundGetBytesBuffered, (soundFeedFunction)AlsaSoundFeedVoiceData -}; - -#endif // ALSA_H_INCLUDED +}; + +#endif // ALSA_H_INCLUDED diff --git a/plugins/zerospu2/Targets/OSS.h b/plugins/zerospu2/Targets/OSS.h index 633ac0d8fc..629d1bc37e 100644 --- a/plugins/zerospu2/Targets/OSS.h +++ b/plugins/zerospu2/Targets/OSS.h @@ -16,15 +16,15 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef OSS_H_INCLUDED -#define OSS_H_INCLUDED +#ifndef OSS_H_INCLUDED +#define OSS_H_INCLUDED #define OSS_MODE_STEREO 1 extern int OSSSetupSound(); extern void OSSRemoveSound(); extern int OSSSoundGetBytesBuffered(); -extern void OSSSoundFeedVoiceData(unsigned char* pSound,long lBytes); +extern void OSSSoundFeedVoiceData(unsigned char* pSound,long lBytes); static SoundCallbacks OSSCmds = { @@ -33,5 +33,5 @@ static SoundCallbacks OSSCmds = (intFunction)OSSSoundGetBytesBuffered, (soundFeedFunction)OSSSoundFeedVoiceData }; - -#endif // OSS_H_INCLUDED + +#endif // OSS_H_INCLUDED diff --git a/plugins/zerospu2/Targets/PortAudio.h b/plugins/zerospu2/Targets/PortAudio.h index 18d1c7d309..7749c29aa6 100644 --- a/plugins/zerospu2/Targets/PortAudio.h +++ b/plugins/zerospu2/Targets/PortAudio.h @@ -17,10 +17,10 @@ */ // PortAudio support implemented by Zedr0n. -#ifndef PORTAUDIO_H_INCLUDED -#define PORTAUDIO_H_INCLUDED +#ifndef PORTAUDIO_H_INCLUDED +#define PORTAUDIO_H_INCLUDED -#include "portaudio.h" +#include "portaudio.h" #include "SoundTargets.h" extern int paSetupSound(); @@ -36,5 +36,5 @@ static SoundCallbacks PACmds = (intFunction)paSoundGetBytesBuffered, (soundFeedFunction)paSoundFeedVoiceData }; - -#endif // PORTAUDIO_H_INCLUDED + +#endif // PORTAUDIO_H_INCLUDED diff --git a/plugins/zerospu2/Targets/dsound51.cpp b/plugins/zerospu2/Targets/dsound51.cpp index 12fd9149d3..83e5cf6f89 100644 --- a/plugins/zerospu2/Targets/dsound51.cpp +++ b/plugins/zerospu2/Targets/dsound51.cpp @@ -1,239 +1,239 @@ -/* ZeroSPU2 - * Copyright (C) 2006-2007 zerofrog - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "dsound51.h" -HWND hWMain = NULL; - -LPDIRECTSOUND lpDS; -LPDIRECTSOUNDBUFFER lpDSBPRIMARY = NULL; -LPDIRECTSOUNDBUFFER lpDSBSECONDARY1 = NULL; -LPDIRECTSOUNDBUFFER lpDSBSECONDARY2 = NULL; -DSBUFFERDESC dsbd; -DSBUFFERDESC dsbdesc; -DSCAPS dscaps; -DSBCAPS dsbcaps; - -unsigned long LastWrite = 0xffffffff; -unsigned long LastWriteS = 0xffffffff; -unsigned long LastPlay = 0; - -int DSSetupSound() -{ - HRESULT dsval; - WAVEFORMATEX pcmwf; - - dsval = DirectSoundCreate(NULL,&lpDS,NULL); - if(dsval != DS_OK) - { - MessageBox(hWMain,"DirectSoundCreate!","Error",MB_OK); - return -1; - } - - if(DS_OK != IDirectSound_SetCooperativeLevel(lpDS,hWMain, DSSCL_PRIORITY)) - { - if(DS_OK != IDirectSound_SetCooperativeLevel(lpDS,hWMain, DSSCL_NORMAL)) - { - MessageBox(hWMain,"SetCooperativeLevel!","Error",MB_OK); - return -1; - } - } - - memset(&dsbd,0,sizeof(DSBUFFERDESC)); - dsbd.dwSize = sizeof(DSBUFFERDESC); // NT4 hack! sizeof(dsbd); - dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER; - dsbd.dwBufferBytes = 0; - dsbd.lpwfxFormat = NULL; - - dsval = IDirectSound_CreateSoundBuffer(lpDS,&dsbd,&lpDSBPRIMARY,NULL); - if(dsval != DS_OK) - { - MessageBox(hWMain, "CreateSoundBuffer (Primary)", "Error",MB_OK); - return -1; - } - - memset(&pcmwf, 0, sizeof(WAVEFORMATEX)); - pcmwf.wFormatTag = WAVE_FORMAT_PCM; - - pcmwf.nChannels = 2; - pcmwf.nBlockAlign = 4; - - pcmwf.nSamplesPerSec = SAMPLE_RATE; - - pcmwf.nAvgBytesPerSec = pcmwf.nSamplesPerSec * pcmwf.nBlockAlign; - pcmwf.wBitsPerSample = 16; - - dsval = IDirectSoundBuffer_SetFormat(lpDSBPRIMARY,&pcmwf); - if(dsval != DS_OK) - { - MessageBox(hWMain, "SetFormat!", "Error",MB_OK); - return -1; - } - - dscaps.dwSize = sizeof(DSCAPS); - dsbcaps.dwSize = sizeof(DSBCAPS); - IDirectSound_GetCaps(lpDS,&dscaps); - IDirectSoundBuffer_GetCaps(lpDSBPRIMARY,&dsbcaps); - - memset(&dsbdesc, 0, sizeof(DSBUFFERDESC)); - // NT4 hack! sizeof(DSBUFFERDESC); - dsbdesc.dwSize = sizeof(DSBUFFERDESC); - dsbdesc.dwFlags = DSBCAPS_LOCHARDWARE | DSBCAPS_STICKYFOCUS | DSBCAPS_GETCURRENTPOSITION2; - dsbdesc.dwBufferBytes = SOUNDSIZE; - dsbdesc.lpwfxFormat = (LPWAVEFORMATEX)&pcmwf; - - dsval = IDirectSound_CreateSoundBuffer(lpDS,&dsbdesc,&lpDSBSECONDARY1,NULL); - if(dsval != DS_OK) - { - dsbdesc.dwFlags = DSBCAPS_LOCSOFTWARE | DSBCAPS_STICKYFOCUS | DSBCAPS_GETCURRENTPOSITION2; - dsval = IDirectSound_CreateSoundBuffer(lpDS,&dsbdesc,&lpDSBSECONDARY1,NULL); - if(dsval != DS_OK) - { - MessageBox(hWMain,"CreateSoundBuffer (Secondary1)", "Error",MB_OK); - return -1; - } - } - - dsval = IDirectSoundBuffer_Play(lpDSBPRIMARY,0,0,DSBPLAY_LOOPING); - if(dsval != DS_OK) - { - MessageBox(hWMain,"Play (Primary)","Error",MB_OK); - return -1; - } - - dsval = IDirectSoundBuffer_Play(lpDSBSECONDARY1,0,0,DSBPLAY_LOOPING); - if(dsval != DS_OK) - { - MessageBox(hWMain,"Play (Secondary1)","Error",MB_OK); - return -1; - } - - // init some play vars - LastWrite = 0x00000000; - LastPlay = 0; - - return 0; -} - -void DSRemoveSound() -{ - int iRes; - - if (lpDSBSECONDARY1 != NULL) - { - IDirectSoundBuffer_Stop(lpDSBSECONDARY1); - iRes = IDirectSoundBuffer_Release(lpDSBSECONDARY1); - - // FF says such a loop is bad... Demo says it's good... Pete doesn't care - while(iRes!=0) iRes = IDirectSoundBuffer_Release(lpDSBSECONDARY1); - - lpDSBSECONDARY1 = NULL; - } - - if (lpDSBPRIMARY != NULL) - { - IDirectSoundBuffer_Stop(lpDSBPRIMARY); - iRes = IDirectSoundBuffer_Release(lpDSBPRIMARY); - - // FF says such a loop is bad... Demo says it's good... Pete doesn't care - while(iRes!=0) iRes = IDirectSoundBuffer_Release(lpDSBPRIMARY); - - lpDSBPRIMARY = NULL; - } - - if (lpDS!=NULL) - { - iRes = IDirectSound_Release(lpDS); - - // FF says such a loop is bad... Demo says it's good... Pete doesn't care - while(iRes!=0) iRes = IDirectSound_Release(lpDS); - - lpDS = NULL; - } - -} - -int DSSoundGetBytesBuffered() -{ - unsigned long cplay,cwrite; - - if (LastWrite == 0xffffffff) return 0; - - IDirectSoundBuffer_GetCurrentPosition(lpDSBSECONDARY1,&cplay,&cwrite); - - if(cplay > SOUNDSIZE) return SOUNDSIZE; - if(cplay < LastWrite) return LastWrite - cplay; - - return (SOUNDSIZE - cplay) + LastWrite; -} - -void DSSoundFeedVoiceData(unsigned char* pSound,long lBytes) -{ - LPVOID lpvPtr1, lpvPtr2; - unsigned long dwBytes1,dwBytes2; - unsigned long *lpSS, *lpSD; - unsigned long dw,cplay,cwrite; - HRESULT hr; - unsigned long status; - - IDirectSoundBuffer_GetStatus(lpDSBSECONDARY1,&status); - if (status & DSBSTATUS_BUFFERLOST) - { - if (IDirectSoundBuffer_Restore(lpDSBSECONDARY1) != DS_OK) return; - IDirectSoundBuffer_Play(lpDSBSECONDARY1,0,0,DSBPLAY_LOOPING); - } - - IDirectSoundBuffer_GetCurrentPosition(lpDSBSECONDARY1,&cplay,&cwrite); - - if(LastWrite == 0xffffffff) LastWrite=cwrite; - - hr = IDirectSoundBuffer_Lock(lpDSBSECONDARY1,LastWrite,lBytes, - &lpvPtr1, &dwBytes1, &lpvPtr2, &dwBytes2, 0); - - if (hr != DS_OK) - { - LastWrite=0xffffffff; - return; - } - - lpSS = (unsigned long *)pSound; - lpSD = (unsigned long *)lpvPtr1; - dw = dwBytes1 >> 2; - - while(dw) - { - *lpSD++=*lpSS++; - dw--; - } - - if (lpvPtr2) - { - lpSD = (unsigned long *)lpvPtr2; - dw = dwBytes2 >> 2; - - while(dw) - { - *lpSD++ = *lpSS++; - dw--; - } - } - - IDirectSoundBuffer_Unlock(lpDSBSECONDARY1,lpvPtr1,dwBytes1,lpvPtr2,dwBytes2); - LastWrite += lBytes; - if(LastWrite >= SOUNDSIZE) LastWrite -= SOUNDSIZE; - LastPlay = cplay; +/* ZeroSPU2 + * Copyright (C) 2006-2007 zerofrog + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "dsound51.h" +HWND hWMain = NULL; + +LPDIRECTSOUND lpDS; +LPDIRECTSOUNDBUFFER lpDSBPRIMARY = NULL; +LPDIRECTSOUNDBUFFER lpDSBSECONDARY1 = NULL; +LPDIRECTSOUNDBUFFER lpDSBSECONDARY2 = NULL; +DSBUFFERDESC dsbd; +DSBUFFERDESC dsbdesc; +DSCAPS dscaps; +DSBCAPS dsbcaps; + +unsigned long LastWrite = 0xffffffff; +unsigned long LastWriteS = 0xffffffff; +unsigned long LastPlay = 0; + +int DSSetupSound() +{ + HRESULT dsval; + WAVEFORMATEX pcmwf; + + dsval = DirectSoundCreate(NULL,&lpDS,NULL); + if(dsval != DS_OK) + { + MessageBox(hWMain,"DirectSoundCreate!","Error",MB_OK); + return -1; + } + + if(DS_OK != IDirectSound_SetCooperativeLevel(lpDS,hWMain, DSSCL_PRIORITY)) + { + if(DS_OK != IDirectSound_SetCooperativeLevel(lpDS,hWMain, DSSCL_NORMAL)) + { + MessageBox(hWMain,"SetCooperativeLevel!","Error",MB_OK); + return -1; + } + } + + memset(&dsbd,0,sizeof(DSBUFFERDESC)); + dsbd.dwSize = sizeof(DSBUFFERDESC); // NT4 hack! sizeof(dsbd); + dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER; + dsbd.dwBufferBytes = 0; + dsbd.lpwfxFormat = NULL; + + dsval = IDirectSound_CreateSoundBuffer(lpDS,&dsbd,&lpDSBPRIMARY,NULL); + if(dsval != DS_OK) + { + MessageBox(hWMain, "CreateSoundBuffer (Primary)", "Error",MB_OK); + return -1; + } + + memset(&pcmwf, 0, sizeof(WAVEFORMATEX)); + pcmwf.wFormatTag = WAVE_FORMAT_PCM; + + pcmwf.nChannels = 2; + pcmwf.nBlockAlign = 4; + + pcmwf.nSamplesPerSec = SAMPLE_RATE; + + pcmwf.nAvgBytesPerSec = pcmwf.nSamplesPerSec * pcmwf.nBlockAlign; + pcmwf.wBitsPerSample = 16; + + dsval = IDirectSoundBuffer_SetFormat(lpDSBPRIMARY,&pcmwf); + if(dsval != DS_OK) + { + MessageBox(hWMain, "SetFormat!", "Error",MB_OK); + return -1; + } + + dscaps.dwSize = sizeof(DSCAPS); + dsbcaps.dwSize = sizeof(DSBCAPS); + IDirectSound_GetCaps(lpDS,&dscaps); + IDirectSoundBuffer_GetCaps(lpDSBPRIMARY,&dsbcaps); + + memset(&dsbdesc, 0, sizeof(DSBUFFERDESC)); + // NT4 hack! sizeof(DSBUFFERDESC); + dsbdesc.dwSize = sizeof(DSBUFFERDESC); + dsbdesc.dwFlags = DSBCAPS_LOCHARDWARE | DSBCAPS_STICKYFOCUS | DSBCAPS_GETCURRENTPOSITION2; + dsbdesc.dwBufferBytes = SOUNDSIZE; + dsbdesc.lpwfxFormat = (LPWAVEFORMATEX)&pcmwf; + + dsval = IDirectSound_CreateSoundBuffer(lpDS,&dsbdesc,&lpDSBSECONDARY1,NULL); + if(dsval != DS_OK) + { + dsbdesc.dwFlags = DSBCAPS_LOCSOFTWARE | DSBCAPS_STICKYFOCUS | DSBCAPS_GETCURRENTPOSITION2; + dsval = IDirectSound_CreateSoundBuffer(lpDS,&dsbdesc,&lpDSBSECONDARY1,NULL); + if(dsval != DS_OK) + { + MessageBox(hWMain,"CreateSoundBuffer (Secondary1)", "Error",MB_OK); + return -1; + } + } + + dsval = IDirectSoundBuffer_Play(lpDSBPRIMARY,0,0,DSBPLAY_LOOPING); + if(dsval != DS_OK) + { + MessageBox(hWMain,"Play (Primary)","Error",MB_OK); + return -1; + } + + dsval = IDirectSoundBuffer_Play(lpDSBSECONDARY1,0,0,DSBPLAY_LOOPING); + if(dsval != DS_OK) + { + MessageBox(hWMain,"Play (Secondary1)","Error",MB_OK); + return -1; + } + + // init some play vars + LastWrite = 0x00000000; + LastPlay = 0; + + return 0; +} + +void DSRemoveSound() +{ + int iRes; + + if (lpDSBSECONDARY1 != NULL) + { + IDirectSoundBuffer_Stop(lpDSBSECONDARY1); + iRes = IDirectSoundBuffer_Release(lpDSBSECONDARY1); + + // FF says such a loop is bad... Demo says it's good... Pete doesn't care + while(iRes!=0) iRes = IDirectSoundBuffer_Release(lpDSBSECONDARY1); + + lpDSBSECONDARY1 = NULL; + } + + if (lpDSBPRIMARY != NULL) + { + IDirectSoundBuffer_Stop(lpDSBPRIMARY); + iRes = IDirectSoundBuffer_Release(lpDSBPRIMARY); + + // FF says such a loop is bad... Demo says it's good... Pete doesn't care + while(iRes!=0) iRes = IDirectSoundBuffer_Release(lpDSBPRIMARY); + + lpDSBPRIMARY = NULL; + } + + if (lpDS!=NULL) + { + iRes = IDirectSound_Release(lpDS); + + // FF says such a loop is bad... Demo says it's good... Pete doesn't care + while(iRes!=0) iRes = IDirectSound_Release(lpDS); + + lpDS = NULL; + } + +} + +int DSSoundGetBytesBuffered() +{ + unsigned long cplay,cwrite; + + if (LastWrite == 0xffffffff) return 0; + + IDirectSoundBuffer_GetCurrentPosition(lpDSBSECONDARY1,&cplay,&cwrite); + + if(cplay > SOUNDSIZE) return SOUNDSIZE; + if(cplay < LastWrite) return LastWrite - cplay; + + return (SOUNDSIZE - cplay) + LastWrite; +} + +void DSSoundFeedVoiceData(unsigned char* pSound,long lBytes) +{ + LPVOID lpvPtr1, lpvPtr2; + unsigned long dwBytes1,dwBytes2; + unsigned long *lpSS, *lpSD; + unsigned long dw,cplay,cwrite; + HRESULT hr; + unsigned long status; + + IDirectSoundBuffer_GetStatus(lpDSBSECONDARY1,&status); + if (status & DSBSTATUS_BUFFERLOST) + { + if (IDirectSoundBuffer_Restore(lpDSBSECONDARY1) != DS_OK) return; + IDirectSoundBuffer_Play(lpDSBSECONDARY1,0,0,DSBPLAY_LOOPING); + } + + IDirectSoundBuffer_GetCurrentPosition(lpDSBSECONDARY1,&cplay,&cwrite); + + if(LastWrite == 0xffffffff) LastWrite=cwrite; + + hr = IDirectSoundBuffer_Lock(lpDSBSECONDARY1,LastWrite,lBytes, + &lpvPtr1, &dwBytes1, &lpvPtr2, &dwBytes2, 0); + + if (hr != DS_OK) + { + LastWrite=0xffffffff; + return; + } + + lpSS = (unsigned long *)pSound; + lpSD = (unsigned long *)lpvPtr1; + dw = dwBytes1 >> 2; + + while(dw) + { + *lpSD++=*lpSS++; + dw--; + } + + if (lpvPtr2) + { + lpSD = (unsigned long *)lpvPtr2; + dw = dwBytes2 >> 2; + + while(dw) + { + *lpSD++ = *lpSS++; + dw--; + } + } + + IDirectSoundBuffer_Unlock(lpDSBSECONDARY1,lpvPtr1,dwBytes1,lpvPtr2,dwBytes2); + LastWrite += lBytes; + if(LastWrite >= SOUNDSIZE) LastWrite -= SOUNDSIZE; + LastPlay = cplay; } \ No newline at end of file diff --git a/plugins/zerospu2/Targets/dsound51.h b/plugins/zerospu2/Targets/dsound51.h index cf7ca8aca2..eeaf8073d5 100644 --- a/plugins/zerospu2/Targets/dsound51.h +++ b/plugins/zerospu2/Targets/dsound51.h @@ -1,46 +1,46 @@ -/* ZeroSPU2 - * Copyright (C) 2006-2010 zerofrog - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef DSOUND51_H_INCLUDED -#define DSOUND51_H_INCLUDED - -#include -#include -#include - -#include "SoundTargets.h" -#include "../zerospu2.h" - -///////////////////////////////////// -// use DirectSound for the sound -#include - -extern int DSSetupSound(); -extern void DSRemoveSound(); -extern int DSSoundGetBytesBuffered(); -extern void DSSoundFeedVoiceData(unsigned char* pSound,long lBytes); - -static SoundCallbacks DSCmds = -{ - (intFunction)DSSetupSound, - (voidFunction)DSRemoveSound, - (intFunction)DSSoundGetBytesBuffered, - (soundFeedFunction)DSSoundFeedVoiceData -}; - +/* ZeroSPU2 + * Copyright (C) 2006-2010 zerofrog + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef DSOUND51_H_INCLUDED +#define DSOUND51_H_INCLUDED + +#include +#include +#include + +#include "SoundTargets.h" +#include "../zerospu2.h" + +///////////////////////////////////// +// use DirectSound for the sound +#include + +extern int DSSetupSound(); +extern void DSRemoveSound(); +extern int DSSoundGetBytesBuffered(); +extern void DSSoundFeedVoiceData(unsigned char* pSound,long lBytes); + +static SoundCallbacks DSCmds = +{ + (intFunction)DSSetupSound, + (voidFunction)DSRemoveSound, + (intFunction)DSSoundGetBytesBuffered, + (soundFeedFunction)DSSoundFeedVoiceData +}; + #endif \ No newline at end of file diff --git a/plugins/zerospu2/zerodma.h b/plugins/zerospu2/zerodma.h index 880b0ee7ca..5c765cb84d 100644 --- a/plugins/zerospu2/zerodma.h +++ b/plugins/zerospu2/zerodma.h @@ -1,5 +1,5 @@ -#ifndef ZERODMA_H_INCLUDED -#define ZERODMA_H_INCLUDED +#ifndef ZERODMA_H_INCLUDED +#define ZERODMA_H_INCLUDED #include @@ -8,7 +8,7 @@ #include "reg.h" #include "misc.h" - + static __forceinline bool irq_test1(u32 ch, u32 addr) { return ((addr + 0x2400) <= C_IRQA(ch) && (addr + 0x2500) >= C_IRQA(ch)); @@ -18,6 +18,6 @@ static __forceinline bool irq_test2(u32 ch, u32 addr) { return ((addr + 0x2600) <= C_IRQA(ch) && (addr + 0x2700) >= C_IRQA(ch)); } - - -#endif // ZERODMA_H_INCLUDED + + +#endif // ZERODMA_H_INCLUDED diff --git a/plugins/zerospu2/zeroworker.h b/plugins/zerospu2/zeroworker.h index 7728a3d69f..6ff6470b0d 100644 --- a/plugins/zerospu2/zeroworker.h +++ b/plugins/zerospu2/zeroworker.h @@ -16,8 +16,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef ZEROWORKER_H_INCLUDED -#define ZEROWORKER_H_INCLUDED +#ifndef ZEROWORKER_H_INCLUDED +#define ZEROWORKER_H_INCLUDED extern u32 dwNewChannel2[2]; // keeps track of what channels that have been turned on extern u32 dwEndChannel2[2]; // keeps track of what channels have ended @@ -34,11 +34,11 @@ extern u64 s_GlobalTimeStamp; extern s32 s_nDurations[64]; extern s32 s_nCurDuration, s_nTotalDuration; extern u32 g_startcount, g_packetcount; - + extern void SPU2Worker(); extern void LogPacketSound(void* packet, s32 memsize); -extern void LogRawSound(void* pleft, s32 leftstride, void* pright, s32 rightstride, s32 numsamples); +extern void LogRawSound(void* pleft, s32 leftstride, void* pright, s32 rightstride, s32 numsamples); extern s32 MixADSR(VOICE_PROCESSED* pvoice); -extern void MixChannels(s32 core); -#endif // ZEROWORKER_H_INCLUDED +extern void MixChannels(s32 core); +#endif // ZEROWORKER_H_INCLUDED diff --git a/plugins/zzogl-pg/opengl/Win32/tsvnrev/svnrev_template.h b/plugins/zzogl-pg/opengl/Win32/tsvnrev/svnrev_template.h index 50327f3f30..a67a4629bd 100644 --- a/plugins/zzogl-pg/opengl/Win32/tsvnrev/svnrev_template.h +++ b/plugins/zzogl-pg/opengl/Win32/tsvnrev/svnrev_template.h @@ -1,18 +1,18 @@ -// svnrev_template.h --> svnrev.h -// -// This file acts as a template for the automatic SVN revision/version tag. -// It is used by the utility SubWCrev.exe to create an "svnrev.h" file for -// whichever project is being compiled (as indicated by command line options -// passed to SubWCRev.exe during the project's pre-build step). -// -// The SubWCRev.exe utility is part of TortoiseSVN and requires several DLLs -// installed by TortoiseSVN, so it will only be available if you have TortoiseSVN -// installed on your system. If you do not have it installed, a generic template -// is used instead (see svnrev_generic.h). Having TortoiseSVN is handy but not -// necessary. If you do not have it installed, everything will still compile -// fine except without the SVN revision tagged to the application/dll version. -// -// TortoiseSVN can be downloaded from http://tortoisesvn.tigris.org - -#define SVN_REV $WCREV$ +// svnrev_template.h --> svnrev.h +// +// This file acts as a template for the automatic SVN revision/version tag. +// It is used by the utility SubWCrev.exe to create an "svnrev.h" file for +// whichever project is being compiled (as indicated by command line options +// passed to SubWCRev.exe during the project's pre-build step). +// +// The SubWCRev.exe utility is part of TortoiseSVN and requires several DLLs +// installed by TortoiseSVN, so it will only be available if you have TortoiseSVN +// installed on your system. If you do not have it installed, a generic template +// is used instead (see svnrev_generic.h). Having TortoiseSVN is handy but not +// necessary. If you do not have it installed, everything will still compile +// fine except without the SVN revision tagged to the application/dll version. +// +// TortoiseSVN can be downloaded from http://tortoisesvn.tigris.org + +#define SVN_REV $WCREV$ #define SVN_MODS $WCMODS?1:0$ \ No newline at end of file diff --git a/plugins/zzogl-pg/opengl/Win32/tsvnrev/svnrev_unknown.h b/plugins/zzogl-pg/opengl/Win32/tsvnrev/svnrev_unknown.h index a2a3703186..4872e23b20 100644 --- a/plugins/zzogl-pg/opengl/Win32/tsvnrev/svnrev_unknown.h +++ b/plugins/zzogl-pg/opengl/Win32/tsvnrev/svnrev_unknown.h @@ -1,23 +1,23 @@ -// svnrev_genric.h --> svnrev.h -// -// This file acts as a placebo for people who do not have TortoiseSVN installed. -// It provides "empty" revision information to the Pcsx2 Playground projects in -// the absence of real revisions derived from the repository being built. -// -// This file does not affect application/dll builds in any significant manner, -// other than the lack of automatic revision tags inserted into the app (which -// is very convenient but hardly necessary). -// -// See svn_template.h for more information on how the process of revision -// templating works. -// -// If you would like to enable automatic revisin tagging, TortoiseSVN can be -// downloaded from http://tortoisesvn.tigris.org - -#define SVN_REV_UNKNOWN - -// The following defines are included so that code will still compile even if it -// doesn't check for the SVN_REV_UNKNOWN define. - -#define SVN_REV 0 +// svnrev_genric.h --> svnrev.h +// +// This file acts as a placebo for people who do not have TortoiseSVN installed. +// It provides "empty" revision information to the Pcsx2 Playground projects in +// the absence of real revisions derived from the repository being built. +// +// This file does not affect application/dll builds in any significant manner, +// other than the lack of automatic revision tags inserted into the app (which +// is very convenient but hardly necessary). +// +// See svn_template.h for more information on how the process of revision +// templating works. +// +// If you would like to enable automatic revisin tagging, TortoiseSVN can be +// downloaded from http://tortoisesvn.tigris.org + +#define SVN_REV_UNKNOWN + +// The following defines are included so that code will still compile even if it +// doesn't check for the SVN_REV_UNKNOWN define. + +#define SVN_REV 0 #define SVN_MODS "" \ No newline at end of file