mirror of
https://github.com/OpenEmu/OpenEmu-SDK.git
synced 2025-11-01 10:15:12 +00:00
497 lines
16 KiB
Objective-C
497 lines
16 KiB
Objective-C
/*
|
|
Copyright (c) 2009, OpenEmu Team
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions are met:
|
|
* Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
* Redistributions in binary form must reproduce the above copyright
|
|
notice, this list of conditions and the following disclaimer in the
|
|
documentation and/or other materials provided with the distribution.
|
|
* Neither the name of the OpenEmu Team nor the
|
|
names of its contributors may be used to endorse or promote products
|
|
derived from this software without specific prior written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY OpenEmu Team ''AS IS'' AND ANY
|
|
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
DISCLAIMED. IN NO EVENT SHALL OpenEmu Team BE LIABLE FOR ANY
|
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#import <Cocoa/Cocoa.h>
|
|
#import "OEGameCoreController.h"
|
|
#import "OESystemResponderClient.h"
|
|
#import "OEGeometry.h"
|
|
#import "OEDiffQueue.h"
|
|
|
|
#ifndef DLog
|
|
|
|
#ifdef DEBUG_PRINT
|
|
/*!
|
|
* @function DLog
|
|
* @abstract NSLogs when the source is built in Debug, otherwise does nothing.
|
|
*/
|
|
#define DLog(format, ...) NSLog(@"%@:%d: %s: " format, [[NSString stringWithUTF8String:__FILE__] lastPathComponent], __LINE__, __FUNCTION__, ##__VA_ARGS__)
|
|
#else
|
|
#define DLog(format, ...) do {} while(0)
|
|
#endif
|
|
#endif
|
|
|
|
/*!
|
|
* @function GET_CURRENT_OR_RETURN
|
|
* @abstract Fetch the current game core, or fail with given return code if there is none.
|
|
*/
|
|
#define GET_CURRENT_OR_RETURN(...) __strong __typeof__(_current) current = _current; if(current == nil) return __VA_ARGS__;
|
|
|
|
/*!
|
|
* @macro OE_EXPORTED_CLASS
|
|
* @abstract Define "Symbols Hidden By Default" on core projects and declare the core class with this
|
|
* for the optimizations. Especially effective for dead code stripping and LTO.
|
|
*/
|
|
#define OE_EXPORTED_CLASS __attribute__((visibility("default")))
|
|
#define OE_DEPRECATED(reason) __attribute__((deprecated(reason)))
|
|
|
|
#pragma mark -
|
|
|
|
extern NSString *const OEGameCoreErrorDomain;
|
|
|
|
typedef enum : NSInteger {
|
|
OEGameCoreCouldNotStartCoreError = -1,
|
|
OEGameCoreCouldNotLoadROMError = -2,
|
|
OEGameCoreCouldNotLoadStateError = -3,
|
|
OEGameCoreStateHasWrongSizeError = -4,
|
|
OEGameCoreCouldNotSaveStateError = -5,
|
|
OEGameCoreDoesNotSupportSaveStatesError = -6,
|
|
} _OEGameCoreErrorCodes;
|
|
|
|
/*!
|
|
* @enum OEGameCoreRendering
|
|
* @abstract Which renderer will be set up for the game core.
|
|
*/
|
|
typedef enum : NSUInteger {
|
|
OEGameCoreRendering2DVideo, //!< The game bitmap will be put directly into an IOSurface.
|
|
OEGameCoreRenderingOpenGL2Video, //!< The core will be provided a CGL OpenGL 2.1 (Compatibility) context.
|
|
OEGameCoreRenderingOpenGL3Video, //!< The core will be provided a CGL OpenGL 3.2+ Core/OpenGLES3 context.
|
|
OEGameCoreRenderingMetal1Video //!< Not yet implemented.
|
|
} OEGameCoreRendering;
|
|
|
|
@protocol OERenderDelegate
|
|
@required
|
|
|
|
/*!
|
|
* @method presentDoubleBufferedFBO
|
|
* @discussion
|
|
* If the core returns YES from needsDoubleBufferedFBO,
|
|
* call this method when you wish to swap buffers.
|
|
*/
|
|
- (void)presentDoubleBufferedFBO;
|
|
|
|
/*!
|
|
* @method willRenderFrameOnAlternateThread
|
|
* @discussion
|
|
* 2D - Not used.
|
|
* 3D -
|
|
* If rendering video on a secondary thread, call this method before every frame rendered.
|
|
*/
|
|
- (void)willRenderFrameOnAlternateThread;
|
|
|
|
/*!
|
|
* @method didRenderFrameOnAlternateThread
|
|
* @discussion
|
|
* 2D - Not used.
|
|
* 3D -
|
|
* If rendering video on a secondary thread, call this method after every frame rendered.
|
|
*/
|
|
- (void)didRenderFrameOnAlternateThread;
|
|
|
|
@property (nonatomic) BOOL enableVSync;
|
|
|
|
/*!
|
|
* @property presentationFramebuffer
|
|
* @discussion
|
|
* 2D - Not used.
|
|
* 3D - For cores which can directly render to a GL FBO or equivalent,
|
|
* this will return the FBO which game pixels eventually go to. This
|
|
* allows porting of cores that overwrite GL_DRAW_FRAMEBUFFER.
|
|
*/
|
|
@property (nonatomic, readonly) id presentationFramebuffer;
|
|
|
|
// For internal use only.
|
|
- (void)willExecute;
|
|
- (void)didExecute;
|
|
- (void)suspendFPSLimiting;
|
|
- (void)resumeFPSLimiting;
|
|
@end
|
|
|
|
@protocol OEGameCoreDelegate <NSObject>
|
|
@required
|
|
|
|
// For internal use only.
|
|
- (void)gameCoreDidFinishFrameRefreshThread:(OEGameCore *)gameCore;
|
|
@end
|
|
|
|
#pragma mark -
|
|
|
|
@protocol OEAudioDelegate
|
|
@required
|
|
- (void)audioSampleRateDidChange;
|
|
|
|
// If you expect no audio for an extended period of time, stop the playback thread.
|
|
- (void)pauseAudio;
|
|
- (void)resumeAudio;
|
|
@end
|
|
|
|
@class OEHIDEvent, OERingBuffer;
|
|
|
|
#pragma mark -
|
|
|
|
OE_EXPORTED_CLASS
|
|
@interface OEGameCore : NSResponder <OESystemResponderClient>
|
|
|
|
// TODO: Move all ivars/properties that don't need overriding to a category?
|
|
@property(weak) id<OEGameCoreDelegate> delegate;
|
|
@property(weak) id<OERenderDelegate> renderDelegate;
|
|
@property(weak) id<OEAudioDelegate> audioDelegate;
|
|
|
|
@property(nonatomic, weak) OEGameCoreController *owner;
|
|
@property(nonatomic, readonly) NSString *pluginName;
|
|
|
|
@property(nonatomic, readonly) NSString *biosDirectoryPath;
|
|
@property(nonatomic, readonly) NSString *supportDirectoryPath;
|
|
@property(nonatomic, readonly) NSString *batterySavesDirectoryPath;
|
|
|
|
@property(nonatomic, readonly) BOOL supportsRewinding;
|
|
@property(nonatomic, readonly) NSUInteger rewindInterval;
|
|
@property(nonatomic, readonly) NSUInteger rewindBufferSeconds;
|
|
@property(nonatomic, readonly) OEDiffQueue *rewindQueue;
|
|
|
|
@property(nonatomic, copy) NSString *systemIdentifier;
|
|
@property(nonatomic, copy) NSString *systemRegion;
|
|
@property(nonatomic, copy) NSString *ROMCRC32;
|
|
@property(nonatomic, copy) NSString *ROMMD5;
|
|
@property(nonatomic, copy) NSString *ROMHeader;
|
|
@property(nonatomic, copy) NSString *ROMSerial;
|
|
|
|
- (void)dispatchBlock:(void(^)(void))block;
|
|
|
|
#pragma mark - Starting
|
|
|
|
/*!
|
|
* @method loadFileAtPath:error
|
|
* @discussion
|
|
* Try to load a ROM and return NO if it fails, or YES if it succeeds.
|
|
* You can do any setup you want here.
|
|
*/
|
|
- (BOOL)loadFileAtPath:(NSString *)path error:(NSError **)error;
|
|
|
|
- (void)setupEmulationWithCompletionHandler:(void(^)(void))completionHandler;
|
|
- (void)startEmulationWithCompletionHandler:(void(^)(void))completionHandler;
|
|
- (void)resetEmulationWithCompletionHandler:(void(^)(void))completionHandler;
|
|
- (void)stopEmulationWithCompletionHandler:(void(^)(void))completionHandler;
|
|
|
|
#pragma mark - Stopping
|
|
|
|
/*!
|
|
* @method stopEmulation
|
|
* @discussion
|
|
* Shut down the core. In non-debugging modes of core execution,
|
|
* the process will be exit immediately after, so you don't need to
|
|
* free any CPU or OpenGL resources.
|
|
*
|
|
* The OpenGL context is available in this method.
|
|
*/
|
|
- (void)stopEmulation;
|
|
|
|
#pragma mark - Execution
|
|
|
|
/*!
|
|
* @property frameInterval
|
|
* @abstract The ideal time between -executeFrame calls when rate=1.0.
|
|
* This property is only read at the start and cannot be changed.
|
|
*/
|
|
@property (nonatomic, readonly) CGFloat numberOfFramesPerSeconds;
|
|
@property (nonatomic, readonly) NSTimeInterval frameInterval;
|
|
|
|
/*!
|
|
* @property rate
|
|
* @discussion
|
|
* The rate the game is currently running at. Generally 1.0.
|
|
* If 0, the core is paused.
|
|
* If >1.0, the core is fast-forwarding and -executeFrame will be called more often.
|
|
* Values <1.0 are not expected.
|
|
*
|
|
* There is no need to check this property if your core does all work inside -executeFrame.
|
|
*/
|
|
@property (nonatomic, assign) float rate;
|
|
|
|
/*!
|
|
* @method executeFrame
|
|
* @discussion
|
|
* Called every 1/(rate*frameInterval) seconds by -runGameLoop:.
|
|
* The core should produce 1 frameInterval worth of audio and can output 1 frame of video.
|
|
* If the game core option OEGameCoreOptionCanSkipFrames is set, the property shouldSkipFrame may be YES.
|
|
* In this case the core can read from videoBuffer but must not write to it. All work done to render video can be skipped.
|
|
*/
|
|
- (void)executeFrame;
|
|
|
|
/*!
|
|
* @method resetEmulation
|
|
* @abstract Presses the reset button on the console.
|
|
*/
|
|
- (void)resetEmulation;
|
|
|
|
/*!
|
|
* @method beginPausedExecution
|
|
* @abstract Run the thread without appearing to execute the game.
|
|
* @discussion OpenEmu may ask the core to save the game, etc. even though it is paused.
|
|
* Some cores need to be run their -executeFrame to process the save message (e.g. Mupen).
|
|
* Call this method from inside the save method to handle this case without disturbing the UI.
|
|
*/
|
|
- (void)beginPausedExecution;
|
|
- (void)endPausedExecution;
|
|
|
|
#pragma mark - Video
|
|
|
|
/*!
|
|
* @method getVideoBufferWithHint:
|
|
* @param hint If possible, use 'hint' as the video buffer for this frame.
|
|
* @discussion
|
|
* Called before each -executeFrame call. The method should return
|
|
* a video buffer containing 'bufferSize' packed pixels, and -executeFrame
|
|
* should draw into this buffer. If 'hint' is set, using that as the video
|
|
* buffer may be faster. Besides that, returning the same buffer each time
|
|
* may be faster.
|
|
*/
|
|
- (const void *)getVideoBufferWithHint:(void *)hint;
|
|
|
|
/*!
|
|
* @method tryToResizeVideoTo:
|
|
* @discussion
|
|
* If the core can natively draw at any resolution, change the resolution
|
|
* to 'size' and return YES. Otherwise, return NO. If YES, the next call to
|
|
* -executeFrame will have a newly sized framebuffer.
|
|
* It is assumed that only 3D cores can do this.
|
|
*/
|
|
- (BOOL)tryToResizeVideoTo:(OEIntSize)size;
|
|
|
|
/*!
|
|
* @property gameCoreRendering
|
|
* @discussion
|
|
* What kind of 3D API the core requires, or none.
|
|
* Defaults to 2D.
|
|
*/
|
|
@property (nonatomic, readonly) OEGameCoreRendering gameCoreRendering;
|
|
|
|
/*!
|
|
* @property hasAlternateRenderingThread
|
|
* @abstract If the core starts another thread to do 3D operations on.
|
|
* @discussion
|
|
* 3D -
|
|
* OE will provide one extra GL context for this thread to avoid corruption
|
|
* of the main context. More than one rendering thread is not supported.
|
|
*/
|
|
@property (nonatomic, readonly) BOOL hasAlternateRenderingThread;
|
|
|
|
/*!
|
|
* @property needsDoubleBufferedFBO
|
|
* @abstract If the game flickers when rendering directly to IOSurface.
|
|
* @discussion
|
|
* 3D -
|
|
* Some cores' OpenGL renderers accidentally cause the IOSurface to update early,
|
|
* either by calling glFlush() or through GL driver bugs. This implements a workaround.
|
|
* Used by Mupen64Plus.
|
|
*/
|
|
@property (nonatomic, readonly) BOOL needsDoubleBufferedFBO;
|
|
|
|
/*!
|
|
* @property bufferSize
|
|
* @discussion
|
|
* 2D -
|
|
* The size in pixels to allocate the framebuffer at.
|
|
* Cores should output at their largest native size, including overdraw, without aspect ratio correction.
|
|
* 3D -
|
|
* The initial size to allocate the framebuffer at.
|
|
* The user may decide to resize it later, but OE will try to request new sizes at the same aspect ratio as bufferSize.
|
|
*/
|
|
@property(readonly) OEIntSize bufferSize;
|
|
|
|
/*!
|
|
* @property screenRect
|
|
* @discussion
|
|
* The rect inside the framebuffer showing the currently displayed picture, not including overdraw, but
|
|
* without aspect ratio correction.
|
|
* Aspect ratio correction is not used for 3D.
|
|
*/
|
|
@property(readonly) OEIntRect screenRect;
|
|
|
|
/*!
|
|
* @property aspectSize
|
|
* @discussion
|
|
* The size at the display aspect ratio (DAR) of the picture.
|
|
* The actual pixel values are not used; only the ratio is used.
|
|
* Aspect ratio correction is not used for 3D.
|
|
*/
|
|
@property(readonly) OEIntSize aspectSize;
|
|
|
|
/*!
|
|
* @property internalPixelFormat
|
|
* @discussion
|
|
* The 'internalPixelFormat' parameter to glTexImage2D, used to create the framebuffer.
|
|
* Defaults to GL_RGB.
|
|
* Ignored for 3D cores.
|
|
*/
|
|
@property(readonly) GLenum internalPixelFormat;
|
|
|
|
/*!
|
|
* @property pixelFormat
|
|
* @discussion
|
|
* The 'type' parameter to glTexImage2D, used to create the framebuffer.
|
|
* GL_BGRA is preferred, but avoid doing any conversions inside the core.
|
|
* Ignored for 3D cores.
|
|
*/
|
|
@property(readonly) GLenum pixelType;
|
|
|
|
/*!
|
|
* @property pixelFormat
|
|
* @discussion
|
|
* The 'format' parameter to glTexImage2D, used to create the framebuffer.
|
|
* GL_UNSIGNED_SHORT_1_5_5_5_REV or GL_UNSIGNED_INT_8_8_8_8_REV are preferred, but
|
|
* avoid doing any conversions inside the core.
|
|
* Ignored for 3D cores.
|
|
*/
|
|
@property(readonly) GLenum pixelFormat;
|
|
|
|
/*!
|
|
* @property shouldSkipFrame
|
|
* @abstract See -executeFrame.
|
|
*/
|
|
@property(assign) BOOL shouldSkipFrame;
|
|
|
|
#pragma mark - Audio
|
|
|
|
// TODO: Should this return void? What does it do?
|
|
- (void)getAudioBuffer:(void *)buffer frameCount:(NSUInteger)frameCount bufferIndex:(NSUInteger)index;
|
|
- (OERingBuffer *)ringBufferAtIndex:(NSUInteger)index;
|
|
|
|
/*!
|
|
* @property audioBufferCount
|
|
* @discussion
|
|
* Defaults to 1. Return a value other than 1 if the core can export
|
|
* multiple audio tracks. There is currently not much need for this.
|
|
*/
|
|
@property(readonly) NSUInteger audioBufferCount;
|
|
|
|
// Used when audioBufferCount == 1
|
|
@property(readonly) NSUInteger channelCount;
|
|
@property(readonly) NSUInteger audioBitDepth;
|
|
@property(readonly) double audioSampleRate;
|
|
|
|
// Used when audioBufferCount > 1
|
|
- (NSUInteger)channelCountForBuffer:(NSUInteger)buffer;
|
|
- (NSUInteger)audioBufferSizeForBuffer:(NSUInteger)buffer;
|
|
- (double)audioSampleRateForBuffer:(NSUInteger)buffer;
|
|
|
|
#pragma mark - Save States
|
|
|
|
- (void)saveStateToFileAtPath:(NSString *)fileName completionHandler:(void(^)(BOOL success, NSError *error))block;
|
|
|
|
- (void)loadStateFromFileAtPath:(NSString *)fileName completionHandler:(void(^)(BOOL success, NSError *error))block;
|
|
|
|
@end
|
|
|
|
#pragma mark - Optional
|
|
|
|
@interface OEGameCore (OptionalMethods)
|
|
|
|
- (NSTrackingAreaOptions)mouseTrackingOptions;
|
|
|
|
- (void)setRandomByte;
|
|
|
|
#pragma mark - Save state - Optional
|
|
|
|
- (NSData *)serializeStateWithError:(NSError **)outError;
|
|
- (BOOL)deserializeState:(NSData *)state withError:(NSError **)outError;
|
|
|
|
#pragma mark - Cheats - Optional
|
|
|
|
- (void)setCheat:(NSString *)code setType:(NSString *)type setEnabled:(BOOL)enabled;
|
|
|
|
#pragma mark - Discs - Optional
|
|
|
|
@property(readonly) NSUInteger discCount;
|
|
- (void)setDisc:(NSUInteger)discNumber;
|
|
|
|
@end
|
|
|
|
#pragma mark - Internal
|
|
|
|
// There should be no need to override these methods.
|
|
@interface OEGameCore (Internal)
|
|
/*!
|
|
* @method runGameLoop:
|
|
* @discussion
|
|
* Cores may implement this if they wish to control their entire event loop.
|
|
* This is not recommended.
|
|
*/
|
|
- (void)runGameLoop:(id)anArgument;
|
|
|
|
/*!
|
|
* @method setupEmulation
|
|
* @discussion
|
|
* Try to setup emulation as much as possible before the UI appears.
|
|
* Audio/video properties don't need to be valid before this method, but
|
|
* do need to be valid after.
|
|
*
|
|
* It's not necessary to implement this, all setup can be done in loadFileAtPath
|
|
* or in the first executeFrame. But you're more likely to run into OE bugs that way.
|
|
*/
|
|
- (void)setupEmulation;
|
|
|
|
/*!
|
|
* @method startEmulation
|
|
* @discussion
|
|
* A method called on OEGameCore after -setupEmulation and
|
|
* before -executeFrame. You may implement it for organizational
|
|
* purposes but it is not necessary.
|
|
*
|
|
* The OpenGL context is available in this method.
|
|
*/
|
|
- (void)startEmulation;
|
|
- (void)didStopEmulation;
|
|
- (void)runStartUpFrameWithCompletionHandler:(void(^)(void))handler;
|
|
|
|
/*!
|
|
* @property pauseEmulation
|
|
* @discussion Pauses the emulator "nicely".
|
|
* When set to YES, pauses emulation. When set to NO,
|
|
* resets the rate to whatever it previously was.
|
|
* The FPS limiter will stop, causing your rendering thread to pause.
|
|
* You should probably not override this.
|
|
*/
|
|
@property(getter=isEmulationPaused) BOOL pauseEmulation;
|
|
- (void)setPauseEmulation:(BOOL)pauseEmulation NS_REQUIRES_SUPER;
|
|
|
|
@end
|
|
|
|
|
|
#pragma mark - Deprecated
|
|
|
|
// These methods will be removed after some time.
|
|
@interface OEGameCore (Deprecated)
|
|
|
|
- (BOOL)loadFileAtPath:(NSString *)path DEPRECATED_ATTRIBUTE;
|
|
|
|
- (void)fastForward:(BOOL)flag OE_DEPRECATED("use -rate");
|
|
- (void)rewind:(BOOL)fla OE_DEPRECATED("use -rate");
|
|
|
|
- (BOOL)rendersToOpenGL OE_DEPRECATED("use -gameCoreRendering");
|
|
@property(readonly) const void *videoBuffer OE_DEPRECATED("use -getVideoBufferWithHint:");
|
|
|
|
@end
|