Files
PPSSPP-Core/PPSSPPGameCore.mm
2023-01-25 21:16:02 -07:00

396 lines
13 KiB
Plaintext

/*
Copyright (c) 2013, 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 "PPSSPPGameCore.h"
#import <OpenEmuBase/OERingBuffer.h>
#import <OpenGL/gl.h>
#include "Common/GPU/OpenGL/OpenEmuGLContext.h"
#include "System/NativeApp.h"
#define ExceptionInfo PPSSPPExceptionInfo
#include "Core/Core.h"
#include "Core/Config.h"
#include "Core/ConfigValues.h"
#include "Core/CoreParameter.h"
#include "Core/CoreTiming.h"
#include "Core/HLE/sceCtrl.h"
#include "Core/HLE/sceUtility.h"
#include "Core/Host.h"
#include "Core/SaveState.h"
#include "Core/System.h"
#undef ExceptionInfo
#include "Common/GraphicsContext.h"
#include "Common/LogManager.h"
#include "Common/Data/Text/I18n.h"
#include "GPU/GPUInterface.h"
#include "thin3d_create.h"
#include "GLRenderManager.h"
#include "DataFormatGL.h"
#define AUDIO_FREQ 44100
#define AUDIO_CHANNELS 2
#define AUDIO_SAMPLESIZE sizeof(int16_t)
namespace SaveState {
struct SaveStart {
void DoState(PointerWrap &p);
};
} // namespace SaveState
namespace OpenEmuCoreThread {
enum class EmuThreadState {
DISABLED,
START_REQUESTED,
RUNNING,
PAUSE_REQUESTED,
PAUSED,
QUIT_REQUESTED,
STOPPED,
};
} //namespace OpenEmuThreadCore
void NativeSetThreadState(OpenEmuCoreThread::EmuThreadState threadState);
@interface PPSSPPGameCore () <OEPSPSystemResponderClient, OEAudioBuffer>
{
CoreParameter _coreParam;
bool _isInitialized;
bool _shouldReset;
//Hack for analog stick.
float x;
float y;
OpenEmuGLContext *OEgraphicsContext;
}
@end
PPSSPPGameCore *_current = 0;
@implementation PPSSPPGameCore
- (instancetype)init
{
(self = [super init]);
_current = self;
return self;
}
# pragma mark - Execution
- (BOOL)loadFileAtPath:(NSString *)path error:(NSError **)error
{
NSURL *romURL = [NSURL fileURLWithPath:path];
NSURL *resourceURL = self.owner.bundle.resourceURL;
NSURL *supportDirectoryURL = [NSURL fileURLWithPath:self.supportDirectoryPath isDirectory:YES];
// Copy over font files if needed
NSFileManager *fileManager = NSFileManager.defaultManager;
NSURL *fontSourceDirectory = [resourceURL URLByAppendingPathComponent:@"flash0/font" isDirectory:YES];
NSURL *fontDestinationDirectory = [supportDirectoryURL URLByAppendingPathComponent:@"font" isDirectory:YES];
NSArray *fontFiles = [fileManager contentsOfDirectoryAtURL:fontSourceDirectory includingPropertiesForKeys:@[NSURLNameKey] options:0 error:nil];
[fileManager createDirectoryAtURL:fontDestinationDirectory withIntermediateDirectories:YES attributes:nil error:nil];
for(NSURL *fontURL in fontFiles)
{
NSURL *destinationFontURL = [fontDestinationDirectory URLByAppendingPathComponent:fontURL.lastPathComponent];
[fileManager copyItemAtURL:fontURL toURL:destinationFontURL error:nil];
}
g_Config.bEnableLogging = true;
g_Config.iFastForwardMode = (int)FastForwardMode::CONTINUOUS;
g_Config.bMemStickInserted = true;
g_Config.iGlobalVolume = VOLUME_FULL - 1;
g_Config.iAltSpeedVolume = -1;
g_Config.bEnableSound = true;
g_Config.iCwCheatRefreshRate = 60;
g_Config.iMemStickSizeGB = 16;
g_Config.iFirmwareVersion = PSP_DEFAULT_FIRMWARE;
g_Config.iPSPModel = PSP_MODEL_SLIM;
LogManager::Init(&g_Config.bEnableLogging);
g_Config.Load("");
// Force a trailing forward slash that PPSSPP requires
NSString *directoryString = [supportDirectoryURL.path stringByAppendingString:@"/"];
//NSURL *directoryURL3 = [supportDirectoryURL URLByAppendingPathComponent:@"/" isDirectory:YES];
g_Config.currentDirectory = Path(directoryString.fileSystemRepresentation);
g_Config.defaultCurrentDirectory = Path(directoryString.fileSystemRepresentation);
g_Config.memStickDirectory = Path(directoryString.fileSystemRepresentation);
g_Config.flash0Directory = Path(directoryString.fileSystemRepresentation);
g_Config.internalDataDirectory = Path(directoryString.fileSystemRepresentation);
g_Config.appCacheDirectory = Path([directoryString stringByAppendingString:@"/cache/" ].fileSystemRepresentation);
g_Config.iGPUBackend = (int)GPUBackend::OPENGL;
g_Config.bHideStateWarnings = false;
g_Config.iLanguage = PSP_SYSTEMPARAM_LANGUAGE_ENGLISH;
if (!LogManager::GetInstance()) {
LogManager::Init(&g_Config.bEnableLogging);
}
g_Config.SetSearchPath(GetSysDirectory(DIRECTORY_SYSTEM));
g_Config.Load();
_coreParam.cpuCore = CPUCore::JIT;
_coreParam.gpuCore = GPUCORE_GLES;
_coreParam.enableSound = true;
_coreParam.fileToStart = Path(romURL.fileSystemRepresentation);
_coreParam.mountIso = Path();
_coreParam.startBreak = false;
_coreParam.printfEmuLog = false;
_coreParam.headLess = false;
_coreParam.renderWidth = 480;
_coreParam.renderHeight = 272;
_coreParam.pixelWidth = 480;
_coreParam.pixelHeight = 272;
coreState = CORE_POWERUP;
return true;
}
- (void)stopEmulation
{
NativeSetThreadState(OpenEmuCoreThread::EmuThreadState::PAUSE_REQUESTED);
PSP_Shutdown();
NativeShutdownGraphics();
NativeShutdown();
[super stopEmulation];
}
- (void)resetEmulation
{
_shouldReset = YES;
}
- (void)executeFrame
{
if(!_isInitialized)
{
// This is where PPSSPP will look for ppge_atlas.zim, requires trailing forward slash
NSString *resourcePath = [self.owner.bundle.resourcePath stringByAppendingString:@"/"];
OEgraphicsContext = OpenEmuGLContext::CreateGraphicsContext();
NativeInit(0, nil, nil, resourcePath.fileSystemRepresentation, nil);
OEgraphicsContext->InitFromRenderThread(nullptr);
_coreParam.graphicsContext = OEgraphicsContext;
NativeInitGraphics(OEgraphicsContext);
}
if(_shouldReset)
{
NativeSetThreadState(OpenEmuCoreThread::EmuThreadState::PAUSE_REQUESTED);
PSP_Shutdown();
}
if(!_isInitialized || _shouldReset)
{
_isInitialized = YES;
_shouldReset = NO;
std::string error_string;
if(!PSP_Init(_coreParam, &error_string))
NSLog(@"[PPSSPP] ERROR: %s", error_string.c_str());
host->BootDone();
host->UpdateDisassembly();
if (PSP_CoreParameter().compat.flags().RequireBufferedRendering && g_Config.bSkipBufferEffects) {
g_Config.bSkipBufferEffects = false;
}
if (PSP_CoreParameter().compat.flags().RequireBlockTransfer && g_Config.bSkipGPUReadbacks) {
g_Config.bSkipGPUReadbacks = false;
}
if (PSP_CoreParameter().compat.flags().RequireDefaultCPUClock && g_Config.iLockedCPUSpeed != 0) {
g_Config.iLockedCPUSpeed = 0;
}
gpu->NotifyConfigChanged();
//Start the Emulator Thread
NativeSetThreadState(OpenEmuCoreThread::EmuThreadState::START_REQUESTED);
} else {
//If Fast forward rate is detected, unthrottle the rndering
PSP_CoreParameter().fastForward = (self.rate > 1) ? true : false;
//Let PPSSPP Core run a loop and return
UpdateRunLoop();
}
}
# pragma mark - Video
- (OEGameCoreRendering)gameCoreRendering
{
return OEGameCoreRenderingOpenGL3Video;
}
- (OEIntSize)bufferSize
{
return OEIntSizeMake(480, 272);
}
- (OEIntSize)aspectSize
{
return OEIntSizeMake(16, 9);
}
- (NSTimeInterval)frameInterval
{
return 59.94;
}
# pragma mark - Audio
- (NSUInteger)channelCount
{
return AUDIO_CHANNELS;
}
- (double)audioSampleRate
{
return AUDIO_FREQ;
}
- (id<OEAudioBuffer>)audioBufferAtIndex:(NSUInteger)index
{
return self;
}
- (NSUInteger)read:(void *)buffer maxLength:(NSUInteger)len
{
NativeMix((short *)buffer, (int)(len / (AUDIO_CHANNELS * sizeof(uint16_t))));
return len;
}
- (NSUInteger)write:(const void *)buffer maxLength:(NSUInteger)length
{
return 0;
}
- (NSUInteger)length
{
return AUDIO_FREQ / 15;
}
# pragma mark - Save States
static void _OESaveStateCallback(SaveState::Status status, std::string message, void *cbUserData)
{
void (^block)(BOOL, NSError *) = (__bridge_transfer void(^)(BOOL, NSError *))cbUserData;
[_current endPausedExecution];
block((status != SaveState::Status::FAILURE), nil);
}
static void _OELoadStateCallback(SaveState::Status status, std::string message, void *cbUserData)
{
void (^block)(BOOL, NSError *) = (__bridge_transfer void(^)(BOOL, NSError *))cbUserData;
//Unpause the EmuThread by requesting it to start again
NativeSetThreadState(OpenEmuCoreThread::EmuThreadState::START_REQUESTED);
NSError *error = nil;
if(status == SaveState::Status::WARNING) {
error = [NSError errorWithDomain:OEGameCoreErrorDomain code:OEGameCoreCouldNotLoadStateError userInfo:@{
NSLocalizedDescriptionKey : NSLocalizedString(@"PPSSPP Save State Warning", @"PPSSPP Save State Warning description."),
NSLocalizedRecoverySuggestionErrorKey : [NSString stringWithFormat:NSLocalizedString(@"This save state was created from a previous version of PPSSPP, or simulates over 4 hours of time played.\n\nSave states preserve bugs from old PPSSPP versions and states from long sessions can also expose bugs rarely seen on a real PSP.\n\nIt is recommended to \"clean load\" for less bugs:\n\n1. Save in-game (memory stick, not save state), then stop emulation.\n2. Go to OpenEmu > Preferences > Library, click \"Reset warnings\".\n3. Reopen your game and click \"No\" when prompted to \"Continue where you left off\".", @"PPSSPP Save State Warning.")]
}];
} else if(status == SaveState::Status::FAILURE) {
error = [NSError errorWithDomain:OEGameCoreErrorDomain code:OEGameCoreCouldNotLoadStateError userInfo:@{
NSLocalizedDescriptionKey : NSLocalizedString(@"The Save State failed to Load", @"PPSSPP Save State Failure description."),
NSLocalizedRecoverySuggestionErrorKey : [NSString stringWithFormat:NSLocalizedString(@"Could not load Save State.", @"PPSSPP Save State Failure.")]
}];
}
block((status == SaveState::Status::SUCCESS), error);
}
- (void)saveStateToFileAtPath:(NSString *)fileName completionHandler:(void (^)(BOOL, NSError *))block
{
[self beginPausedExecution];
SaveState::Save(Path(fileName.fileSystemRepresentation),0, _OESaveStateCallback, (__bridge_retained void *)[block copy]);
}
- (void)loadStateFromFileAtPath:(NSString *)fileName completionHandler:(void (^)(BOOL, NSError *))block
{
SaveState::Load(Path(fileName.fileSystemRepresentation), 0,_OELoadStateCallback, (__bridge_retained void *)[block copy]);
if(_isInitialized){
//We need to pause our EmuThread so we don't try to process the save state in the middle of a Frame Render
NativeSetThreadState(OpenEmuCoreThread::EmuThreadState::PAUSE_REQUESTED);
SaveState::Process();
}
}
# pragma mark - Input
const int buttonMap[] = { CTRL_UP, CTRL_DOWN, CTRL_LEFT, CTRL_RIGHT, 0, 0, 0, 0, CTRL_TRIANGLE, CTRL_CIRCLE, CTRL_CROSS, CTRL_SQUARE, CTRL_LTRIGGER, CTRL_RTRIGGER, CTRL_START, CTRL_SELECT };
- (oneway void)didMovePSPJoystickDirection:(OEPSPButton)button withValue:(CGFloat)value forPlayer:(NSUInteger)player
{
if(button == OEPSPAnalogUp || button == OEPSPAnalogDown)
y = (button == OEPSPAnalogUp ? value : -value);
else
x = (button == OEPSPAnalogRight ? value : -value);
__CtrlSetAnalogXY(0, x, y);
}
- (oneway void)didPushPSPButton:(OEPSPButton)button forPlayer:(NSUInteger)player
{
__CtrlButtonDown(buttonMap[button]);
}
- (oneway void)didReleasePSPButton:(OEPSPButton)button forPlayer:(NSUInteger)player
{
__CtrlButtonUp(buttonMap[button]);
}
@end