Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| cd166c0561 | |||
| dd9e1c3cee | |||
| 28ae039566 | |||
| 33026a6424 | |||
| 10e4d8fd38 | |||
| 2758d42404 | |||
| 0fa341d5c1 | |||
| 5bfdfc26d4 | |||
| 22d81842fa |
@@ -1131,6 +1131,7 @@
|
||||
);
|
||||
name = VisualBoyAdvance;
|
||||
sourceTree = "<group>";
|
||||
usesTabs = 0;
|
||||
};
|
||||
089C1671FE841209C02AAC07 /* Frameworks and Libraries */ = {
|
||||
isa = PBXGroup;
|
||||
|
||||
+389
-75
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (c) 2015, OpenEmu Team
|
||||
Copyright (c) 2018, OpenEmu Team
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
@@ -41,18 +41,37 @@
|
||||
extern uint8 *XBuf;
|
||||
static uint32_t palette[256];
|
||||
|
||||
#define OVERSCAN_VERTICAL 8
|
||||
#define OVERSCAN_HORIZONTAL 8
|
||||
|
||||
#define OptionDefault(_NAME_, _PREFKEY_) @{ OEGameCoreDisplayModeNameKey : _NAME_, OEGameCoreDisplayModePrefKeyNameKey : _PREFKEY_, OEGameCoreDisplayModeStateKey : @YES, }
|
||||
#define Option(_NAME_, _PREFKEY_) @{ OEGameCoreDisplayModeNameKey : _NAME_, OEGameCoreDisplayModePrefKeyNameKey : _PREFKEY_, OEGameCoreDisplayModeStateKey : @NO, }
|
||||
#define OptionIndented(_NAME_, _PREFKEY_) @{ OEGameCoreDisplayModeNameKey : _NAME_, OEGameCoreDisplayModePrefKeyNameKey : _PREFKEY_, OEGameCoreDisplayModeStateKey : @NO, OEGameCoreDisplayModeIndentationLevelKey : @(1), }
|
||||
#define OptionToggleable(_NAME_, _PREFKEY_) @{ OEGameCoreDisplayModeNameKey : _NAME_, OEGameCoreDisplayModePrefKeyNameKey : _PREFKEY_, OEGameCoreDisplayModeStateKey : @NO, OEGameCoreDisplayModeAllowsToggleKey : @YES, }
|
||||
#define OptionToggleableNoSave(_NAME_, _PREFKEY_) @{ OEGameCoreDisplayModeNameKey : _NAME_, OEGameCoreDisplayModePrefKeyNameKey : _PREFKEY_, OEGameCoreDisplayModeStateKey : @NO, OEGameCoreDisplayModeAllowsToggleKey : @YES, OEGameCoreDisplayModeDisallowPrefSaveKey : @YES, }
|
||||
#define Label(_NAME_) @{ OEGameCoreDisplayModeLabelKey : _NAME_, }
|
||||
#define SeparatorItem() @{ OEGameCoreDisplayModeSeparatorItemKey : @"",}
|
||||
|
||||
@interface FCEUGameCore () <OENESSystemResponderClient>
|
||||
{
|
||||
uint32_t *videoBuffer;
|
||||
uint8_t *pXBuf;
|
||||
int32_t *soundBuffer;
|
||||
int32_t soundSize;
|
||||
uint32_t pad;
|
||||
uint32_t arkanoid[3];
|
||||
uint32_t zapper[3];
|
||||
uint32_t hypershot[4];
|
||||
uint32_t *_videoBuffer;
|
||||
int32_t *_soundBuffer;
|
||||
int32_t _soundSize;
|
||||
uint32_t _pad;
|
||||
uint32_t _arkanoid[3];
|
||||
uint32_t _zapper[3];
|
||||
uint32_t _hypershot[4];
|
||||
int _videoWidth, _videoHeight;
|
||||
int _videoOffsetX, _videoOffsetY;
|
||||
int _aspectWidth, _aspectHeight;
|
||||
BOOL _isHorzOverscanCropped;
|
||||
BOOL _isVertOverscanCropped;
|
||||
NSMutableDictionary<NSString *, NSNumber *> *_cheatList;
|
||||
NSMutableArray <NSMutableDictionary <NSString *, id> *> *_availableDisplayModes;
|
||||
}
|
||||
|
||||
- (void)loadDisplayModeOptions;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FCEUGameCore
|
||||
@@ -63,7 +82,7 @@ static __weak FCEUGameCore *_current;
|
||||
{
|
||||
if((self = [super init]))
|
||||
{
|
||||
videoBuffer = (uint32_t *)malloc(256 * 240 * 4);
|
||||
_cheatList = [NSMutableDictionary dictionary];
|
||||
}
|
||||
|
||||
_current = self;
|
||||
@@ -73,51 +92,51 @@ static __weak FCEUGameCore *_current;
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
free(videoBuffer);
|
||||
free(_videoBuffer);
|
||||
}
|
||||
|
||||
# pragma mark - Execution
|
||||
|
||||
- (BOOL)loadFileAtPath:(NSString *)path error:(NSError **)error
|
||||
{
|
||||
pad = 0;
|
||||
memset(arkanoid, 0, sizeof(arkanoid));
|
||||
memset(zapper, 0, sizeof(zapper));
|
||||
memset(hypershot, 0, sizeof(hypershot));
|
||||
_pad = 0;
|
||||
memset(_arkanoid, 0, sizeof(_arkanoid));
|
||||
memset(_zapper, 0, sizeof(_zapper));
|
||||
memset(_hypershot, 0, sizeof(_hypershot));
|
||||
|
||||
//newppu = 0 default off, set 1 to enable
|
||||
|
||||
FCEUI_Initialize();
|
||||
|
||||
NSURL *batterySavesDirectory = [NSURL fileURLWithPath:[self batterySavesDirectoryPath]];
|
||||
[[NSFileManager defaultManager] createDirectoryAtURL:batterySavesDirectory withIntermediateDirectories:YES attributes:nil error:nil];
|
||||
//FCEUI_SetBaseDirectory([[self biosDirectoryPath] UTF8String]); unused for now
|
||||
FCEUI_SetDirOverride(FCEUIOD_NV, strdup([[batterySavesDirectory path] UTF8String]));
|
||||
NSURL *batterySavesDirectory = [NSURL fileURLWithPath:self.batterySavesDirectoryPath];
|
||||
[NSFileManager.defaultManager createDirectoryAtURL:batterySavesDirectory withIntermediateDirectories:YES attributes:nil error:nil];
|
||||
//FCEUI_SetBaseDirectory([[self biosDirectoryPath] fileSystemRepresentation]); unused for now
|
||||
FCEUI_SetDirOverride(FCEUIOD_NV, strdup(batterySavesDirectory.fileSystemRepresentation));
|
||||
|
||||
FCEUI_SetSoundVolume(256);
|
||||
FCEUI_Sound(48000);
|
||||
|
||||
FCEUGI *FCEUGameInfo;
|
||||
FCEUGameInfo = FCEUI_LoadGame([path UTF8String], 1, false);
|
||||
FCEUGameInfo = FCEUI_LoadGame(path.fileSystemRepresentation, 1, false);
|
||||
|
||||
if(!FCEUGameInfo)
|
||||
return NO;
|
||||
|
||||
//NSLog(@"FPS: %d", FCEUI_GetDesiredFPS() >> 24); // Hz
|
||||
|
||||
FCEUI_SetInput(0, SI_GAMEPAD, &pad, 0); // Controllers 1 and 3
|
||||
FCEUI_SetInput(0, SI_GAMEPAD, &_pad, 0); // Controllers 1 and 3
|
||||
|
||||
if(FCEUGameInfo->input[1] == SI_ZAPPER)
|
||||
FCEUI_SetInput(1, SI_ZAPPER, &zapper, 0);
|
||||
FCEUI_SetInput(1, SI_ZAPPER, &_zapper, 0);
|
||||
else if(FCEUGameInfo->input[1] == SI_ARKANOID)
|
||||
FCEUI_SetInput(1, SI_ARKANOID, &arkanoid, 0);
|
||||
FCEUI_SetInput(1, SI_ARKANOID, &_arkanoid, 0);
|
||||
else
|
||||
FCEUI_SetInput(1, SI_GAMEPAD, &pad, 0); // Controllers 2 and 4
|
||||
FCEUI_SetInput(1, SI_GAMEPAD, &_pad, 0); // Controllers 2 and 4
|
||||
|
||||
if(FCEUGameInfo->inputfc == SIFC_SHADOW)
|
||||
FCEUI_SetInputFC(SIFC_SHADOW, &hypershot, 0);
|
||||
FCEUI_SetInputFC(SIFC_SHADOW, &_hypershot, 0);
|
||||
else if(FCEUGameInfo->inputfc == SIFC_ARKANOID)
|
||||
FCEUI_SetInputFC(SIFC_ARKANOID, &arkanoid, 0);
|
||||
FCEUI_SetInputFC(SIFC_ARKANOID, &_arkanoid, 0);
|
||||
|
||||
extern uint32_t iNESGameCRC32;
|
||||
NSString *cartCRC32 = [NSString stringWithFormat:@"%08x", iNESGameCRC32];
|
||||
@@ -192,29 +211,29 @@ static __weak FCEUGameCore *_current;
|
||||
FCEUI_SetInputFourscore(true);
|
||||
|
||||
if([famicom4Player containsObject:cartCRC32])
|
||||
FCEUI_SetInputFC(SIFC_4PLAYER, &pad, 0);
|
||||
FCEUI_SetInputFC(SIFC_4PLAYER, &_pad, 0);
|
||||
|
||||
FCEU_ResetPalette();
|
||||
|
||||
// Only temporary, so core doesn't crash on an older OpenEmu version
|
||||
if ([self respondsToSelector:@selector(displayModeInfo)]) {
|
||||
[self loadDisplayModeOptions];
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)executeFrame
|
||||
{
|
||||
pXBuf = 0;
|
||||
soundSize = 0;
|
||||
uint8_t *pXBuf;
|
||||
_soundSize = 0;
|
||||
|
||||
FCEUI_Emulate(&pXBuf, &soundBuffer, &soundSize, 0);
|
||||
FCEUI_Emulate(&pXBuf, &_soundBuffer, &_soundSize, 0);
|
||||
|
||||
pXBuf = XBuf;
|
||||
for (unsigned y = 0; y < 240; y++)
|
||||
for (unsigned x = 0; x < 256; x++, pXBuf++)
|
||||
videoBuffer[y * 256 + x] = palette[*pXBuf];
|
||||
for (int i = 0; i < _soundSize; i++)
|
||||
_soundBuffer[i] = (_soundBuffer[i] << 16) | (_soundBuffer[i] & 0xffff);
|
||||
|
||||
for (int i = 0; i < soundSize; i++)
|
||||
soundBuffer[i] = (soundBuffer[i] << 16) | (soundBuffer[i] & 0xffff);
|
||||
|
||||
[[self ringBufferAtIndex:0] write:soundBuffer maxLength:soundSize << 2];
|
||||
[[self ringBufferAtIndex:0] write:_soundBuffer maxLength:_soundSize << 2];
|
||||
}
|
||||
|
||||
- (void)resetEmulation
|
||||
@@ -237,19 +256,39 @@ static __weak FCEUGameCore *_current;
|
||||
|
||||
# pragma mark - Video
|
||||
|
||||
- (const void *)videoBuffer
|
||||
- (const void *)getVideoBufferWithHint:(void *)hint
|
||||
{
|
||||
return videoBuffer;
|
||||
if (!hint) {
|
||||
if (!_videoBuffer) _videoBuffer = (uint32_t *)malloc(256 * 240 * sizeof(uint32_t));
|
||||
hint = _videoBuffer;
|
||||
}
|
||||
|
||||
// TODO: support paletted video in OE
|
||||
uint8_t *pXBuf = XBuf;
|
||||
uint32_t *pOBuf = (uint32_t *)hint;
|
||||
for (unsigned y = 0; y < 240; y++)
|
||||
for (unsigned x = 0; x < 256; x++, pXBuf++)
|
||||
pOBuf[y * 256 + x] = palette[*pXBuf];
|
||||
|
||||
return hint;
|
||||
}
|
||||
|
||||
- (OEIntRect)screenRect
|
||||
{
|
||||
return OEIntRectMake(0, 0, 256, 240);
|
||||
_videoOffsetX = _isHorzOverscanCropped ? OVERSCAN_HORIZONTAL : 0;
|
||||
_videoOffsetY = _isVertOverscanCropped ? OVERSCAN_VERTICAL : 0;
|
||||
_videoWidth = _isHorzOverscanCropped ? 256 - (OVERSCAN_HORIZONTAL * 2) : 256;
|
||||
_videoHeight = _isVertOverscanCropped ? 240 - (OVERSCAN_VERTICAL * 2) : 240;
|
||||
|
||||
return OEIntRectMake(_videoOffsetX, _videoOffsetY, _videoWidth, _videoHeight);
|
||||
}
|
||||
|
||||
- (OEIntSize)aspectSize
|
||||
{
|
||||
return OEIntSizeMake(256 * (8.0/7.0), 240);
|
||||
_aspectWidth = _isHorzOverscanCropped ? (256 - (OVERSCAN_HORIZONTAL * 2)) * (8.0/7.0) : 256 * (8.0/7.0);
|
||||
_aspectHeight = _isVertOverscanCropped ? 240 - (OVERSCAN_VERTICAL * 2) : 240;
|
||||
|
||||
return OEIntSizeMake(_aspectWidth, _aspectHeight);
|
||||
}
|
||||
|
||||
- (OEIntSize)bufferSize
|
||||
@@ -267,11 +306,6 @@ static __weak FCEUGameCore *_current;
|
||||
return GL_UNSIGNED_INT_8_8_8_8_REV;
|
||||
}
|
||||
|
||||
- (GLenum)internalPixelFormat
|
||||
{
|
||||
return GL_RGB8;
|
||||
}
|
||||
|
||||
# pragma mark - Audio
|
||||
|
||||
- (double)audioSampleRate
|
||||
@@ -316,8 +350,8 @@ static __weak FCEUGameCore *_current;
|
||||
|
||||
- (BOOL)deserializeState:(NSData *)state withError:(NSError **)outError
|
||||
{
|
||||
u8 *bytes = (u8 *)[state bytes];
|
||||
size_t length = [state length];
|
||||
u8 *bytes = (u8 *)state.bytes;
|
||||
size_t length = state.length;
|
||||
std::vector<u8> byteVector(bytes, bytes + length);
|
||||
EMUFILE *emuFile = new EMUFILE_MEMORY(&byteVector);
|
||||
|
||||
@@ -349,7 +383,7 @@ const int NESMap[] = {JOY_UP, JOY_DOWN, JOY_LEFT, JOY_RIGHT, JOY_A, JOY_B, JOY_S
|
||||
break;
|
||||
}
|
||||
|
||||
pad |= NESMap[button] << playerShift;
|
||||
_pad |= NESMap[button] << playerShift;
|
||||
}
|
||||
|
||||
- (oneway void)didReleaseNESButton:(OENESButton)button forPlayer:(NSUInteger)player;
|
||||
@@ -370,76 +404,77 @@ const int NESMap[] = {JOY_UP, JOY_DOWN, JOY_LEFT, JOY_RIGHT, JOY_A, JOY_B, JOY_S
|
||||
break;
|
||||
}
|
||||
|
||||
pad &= ~(NESMap[button] << playerShift);
|
||||
_pad &= ~(NESMap[button] << playerShift);
|
||||
}
|
||||
|
||||
- (oneway void)didTriggerGunAtPoint:(OEIntPoint)aPoint
|
||||
{
|
||||
[self mouseMovedAtPoint:aPoint];
|
||||
|
||||
arkanoid[2] = 1;
|
||||
int xcoord = _isHorzOverscanCropped ? (aPoint.x + OVERSCAN_HORIZONTAL) * 0.876712 : aPoint.x * 0.876712;
|
||||
int ycoord = _isVertOverscanCropped ? aPoint.y + OVERSCAN_VERTICAL : aPoint.y;
|
||||
|
||||
zapper[0] = aPoint.x * 0.876712;
|
||||
zapper[1] = aPoint.y;
|
||||
zapper[2] = 1;
|
||||
_arkanoid[2] = 1;
|
||||
|
||||
hypershot[0] = aPoint.x * 0.876712;
|
||||
hypershot[1] = aPoint.y;
|
||||
hypershot[2] = 1;
|
||||
_zapper[0] = xcoord;
|
||||
_zapper[1] = ycoord;
|
||||
_zapper[2] = 1;
|
||||
|
||||
_hypershot[0] = xcoord;
|
||||
_hypershot[1] = ycoord;
|
||||
_hypershot[2] = 1;
|
||||
}
|
||||
|
||||
- (oneway void)didReleaseTrigger
|
||||
{
|
||||
arkanoid[2] = 0;
|
||||
zapper[2] = 0;
|
||||
hypershot[2] = 0;
|
||||
_arkanoid[2] = 0;
|
||||
_zapper[2] = 0;
|
||||
_hypershot[2] = 0;
|
||||
}
|
||||
|
||||
- (oneway void)mouseMovedAtPoint:(OEIntPoint)aPoint
|
||||
{
|
||||
arkanoid[0] = aPoint.x * 0.876712;
|
||||
_arkanoid[0] = _isHorzOverscanCropped ? (aPoint.x + OVERSCAN_HORIZONTAL) * 0.876712 : aPoint.x * 0.876712;
|
||||
}
|
||||
|
||||
- (oneway void)rightMouseDownAtPoint:(OEIntPoint)point
|
||||
{
|
||||
hypershot[3] = 1; // "move" button
|
||||
_hypershot[3] = 1; // "move" button
|
||||
}
|
||||
|
||||
- (oneway void)rightMouseUp;
|
||||
{
|
||||
hypershot[3] = 0;
|
||||
_hypershot[3] = 0;
|
||||
}
|
||||
|
||||
#pragma mark - Cheats
|
||||
|
||||
NSMutableDictionary *cheatList = [[NSMutableDictionary alloc] init];
|
||||
|
||||
- (void)setCheat:(NSString *)code setType:(NSString *)type setEnabled:(BOOL)enabled
|
||||
{
|
||||
// Sanitize
|
||||
code = [code stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
|
||||
code = [code stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceAndNewlineCharacterSet];
|
||||
|
||||
// Remove any spaces
|
||||
code = [code stringByReplacingOccurrencesOfString:@" " withString:@""];
|
||||
|
||||
if (enabled)
|
||||
[cheatList setValue:@YES forKey:code];
|
||||
_cheatList[code] = @YES;
|
||||
else
|
||||
[cheatList removeObjectForKey:code];
|
||||
[_cheatList removeObjectForKey:code];
|
||||
|
||||
FCEU_FlushGameCheats(0, 1);
|
||||
|
||||
NSArray *multipleCodes = [[NSArray alloc] init];
|
||||
NSArray<NSString *> *multipleCodes = [NSArray array];
|
||||
|
||||
// Apply enabled cheats found in dictionary
|
||||
for (id key in cheatList)
|
||||
for (NSString *key in _cheatList)
|
||||
{
|
||||
if ([[cheatList valueForKey:key] isEqual:@YES])
|
||||
if ([_cheatList[key] boolValue])
|
||||
{
|
||||
// Handle multi-line cheats
|
||||
multipleCodes = [key componentsSeparatedByString:@"+"];
|
||||
for (NSString *singleCode in multipleCodes) {
|
||||
const char *cCode = [singleCode UTF8String];
|
||||
const char *cCode = singleCode.UTF8String;
|
||||
|
||||
int address, value, compare;
|
||||
int type = 1;
|
||||
@@ -451,6 +486,285 @@ NSMutableDictionary *cheatList = [[NSMutableDictionary alloc] init];
|
||||
}
|
||||
}
|
||||
|
||||
# pragma mark - Display Mode
|
||||
|
||||
- (NSArray <NSDictionary <NSString *, id> *> *)displayModes
|
||||
{
|
||||
if (_availableDisplayModes.count == 0)
|
||||
{
|
||||
_availableDisplayModes = [NSMutableArray array];
|
||||
|
||||
NSArray <NSDictionary <NSString *, id> *> *availableModesWithDefault =
|
||||
@[
|
||||
OptionToggleableNoSave(@"No Sprite Limit", @"noSpriteLimit"),
|
||||
SeparatorItem(),
|
||||
Label(@"Overscan"),
|
||||
OptionToggleable(@"Crop Horizontal", @"cropHorizontalOverscan"),
|
||||
OptionToggleable(@"Crop Vertical", @"cropVerticalOverscan"),
|
||||
SeparatorItem(),
|
||||
Label(@"Palette"),
|
||||
OptionDefault(@"Default — FCEUX", @"palette"),
|
||||
Option(@"RGB (PlayChoice-10)", @"palette"),
|
||||
Option(@"NESCAP", @"palette"),
|
||||
Option(@"Sony CXA2025AS", @"palette"),
|
||||
Option(@"Smooth (FBX)", @"palette"),
|
||||
Option(@"Wavebeam", @"palette"),
|
||||
];
|
||||
|
||||
// Deep mutable copy
|
||||
_availableDisplayModes = (NSMutableArray *)CFBridgingRelease(CFPropertyListCreateDeepCopy(kCFAllocatorDefault, (CFArrayRef)availableModesWithDefault, kCFPropertyListMutableContainers));
|
||||
}
|
||||
|
||||
return [_availableDisplayModes copy];
|
||||
}
|
||||
|
||||
- (void)changeDisplayWithMode:(NSString *)displayMode
|
||||
{
|
||||
if (_availableDisplayModes.count == 0)
|
||||
[self displayModes];
|
||||
|
||||
// First check if 'displayMode' is valid
|
||||
BOOL isDisplayModeToggleable = NO;
|
||||
BOOL isValidDisplayMode = NO;
|
||||
BOOL displayModeState = NO;
|
||||
NSString *displayModePrefKey;
|
||||
|
||||
for (NSDictionary *modeDict in _availableDisplayModes) {
|
||||
if ([modeDict[OEGameCoreDisplayModeNameKey] isEqualToString:displayMode]) {
|
||||
displayModeState = [modeDict[OEGameCoreDisplayModeStateKey] boolValue];
|
||||
displayModePrefKey = modeDict[OEGameCoreDisplayModePrefKeyNameKey];
|
||||
isDisplayModeToggleable = [modeDict[OEGameCoreDisplayModeAllowsToggleKey] boolValue];
|
||||
isValidDisplayMode = YES;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Disallow a 'displayMode' not found in _availableDisplayModes
|
||||
if (!isValidDisplayMode)
|
||||
return;
|
||||
|
||||
// Handle option state changes
|
||||
for (NSMutableDictionary *optionDict in _availableDisplayModes) {
|
||||
NSString *modeName = optionDict[OEGameCoreDisplayModeNameKey];
|
||||
NSString *prefKey = optionDict[OEGameCoreDisplayModePrefKeyNameKey];
|
||||
|
||||
if (!modeName)
|
||||
continue;
|
||||
// Mutually exclusive option state change
|
||||
else if ([modeName isEqualToString:displayMode] && !isDisplayModeToggleable)
|
||||
optionDict[OEGameCoreDisplayModeStateKey] = @YES;
|
||||
// Reset mutually exclusive options that are the same prefs group as 'displayMode'
|
||||
else if (!isDisplayModeToggleable && [prefKey isEqualToString:displayModePrefKey])
|
||||
optionDict[OEGameCoreDisplayModeStateKey] = @NO;
|
||||
// Toggleable option state change
|
||||
else if ([modeName isEqualToString:displayMode] && isDisplayModeToggleable)
|
||||
optionDict[OEGameCoreDisplayModeStateKey] = @(!displayModeState);
|
||||
}
|
||||
|
||||
if ([displayMode isEqualToString:@"Crop Horizontal"])
|
||||
{
|
||||
_isHorzOverscanCropped = !_isHorzOverscanCropped;
|
||||
}
|
||||
else if ([displayMode isEqualToString:@"Crop Vertical"])
|
||||
{
|
||||
_isVertOverscanCropped = !_isVertOverscanCropped;
|
||||
}
|
||||
else if ([displayMode isEqualToString:@"No Sprite Limit"])
|
||||
{
|
||||
FCEUI_DisableSpriteLimitation(!displayModeState);
|
||||
}
|
||||
else if ([displayMode isEqualToString:@"Default — FCEUX"])
|
||||
{
|
||||
FCEU_ResetPalette();
|
||||
}
|
||||
else if ([displayMode isEqualToString:@"RGB (PlayChoice-10)"])
|
||||
{
|
||||
unsigned int pc10_palette[64] =
|
||||
{
|
||||
0x6D6D6D, 0x002492, 0x0000DB, 0x6D49DB,
|
||||
0x92006D, 0xB6006D, 0xB62400, 0x924900,
|
||||
0x6D4900, 0x244900, 0x006D24, 0x009200,
|
||||
0x004949, 0x000000, 0x000000, 0x000000,
|
||||
0xB6B6B6, 0x006DDB, 0x0049FF, 0x9200FF,
|
||||
0xB600FF, 0xFF0092, 0xFF0000, 0xDB6D00,
|
||||
0x926D00, 0x249200, 0x009200, 0x00B66D,
|
||||
0x009292, 0x242424, 0x000000, 0x000000,
|
||||
0xFFFFFF, 0x6DB6FF, 0x9292FF, 0xDB6DFF,
|
||||
0xFF00FF, 0xFF6DFF, 0xFF9200, 0xFFB600,
|
||||
0xDBDB00, 0x6DDB00, 0x00FF00, 0x49FFDB,
|
||||
0x00FFFF, 0x494949, 0x000000, 0x000000,
|
||||
0xFFFFFF, 0xB6DBFF, 0xDBB6FF, 0xFFB6FF,
|
||||
0xFF92FF, 0xFFB6B6, 0xFFDB92, 0xFFFF49,
|
||||
0xFFFF6D, 0xB6FF49, 0x92FF6D, 0x49FFDB,
|
||||
0x92DBFF, 0x929292, 0x000000, 0x000000
|
||||
};
|
||||
|
||||
for (int i = 0; i < 64; i++)
|
||||
{
|
||||
int r = pc10_palette[i] >> 16;
|
||||
int g = (pc10_palette[i] & 0xff00) >> 8;
|
||||
int b = pc10_palette[i] & 0xff;
|
||||
FCEUD_SetPalette(i, r, g, b);
|
||||
FCEUD_SetPalette(i + 64, r, g, b);
|
||||
FCEUD_SetPalette(i + 128, r, g, b);
|
||||
FCEUD_SetPalette(i + 192, r, g, b);
|
||||
}
|
||||
}
|
||||
else if ([displayMode isEqualToString:@"NESCAP"])
|
||||
{
|
||||
unsigned int nescap_palette[64] =
|
||||
{
|
||||
0x646365, 0x001580, 0x1D0090, 0x380082,
|
||||
0x56005D, 0x5A001A, 0x4F0900, 0x381B00,
|
||||
0x1E3100, 0x003D00, 0x004100, 0x003A1B,
|
||||
0x002F55, 0x000000, 0x000000, 0x000000,
|
||||
0xAFADAF, 0x164BCA, 0x472AE7, 0x6B1BDB,
|
||||
0x9617B0, 0x9F185B, 0x963001, 0x7B4800,
|
||||
0x5A6600, 0x237800, 0x017F00, 0x00783D,
|
||||
0x006C8C, 0x000000, 0x000000, 0x000000,
|
||||
0xFFFFFF, 0x60A6FF, 0x8F84FF, 0xB473FF,
|
||||
0xE26CFF, 0xF268C3, 0xEF7E61, 0xD89527,
|
||||
0xBAB307, 0x81C807, 0x57D43D, 0x47CF7E,
|
||||
0x4BC5CD, 0x4C4B4D, 0x000000, 0x000000,
|
||||
0xFFFFFF, 0xC2E0FF, 0xD5D2FF, 0xE3CBFF,
|
||||
0xF7C8FF, 0xFEC6EE, 0xFECEC6, 0xF6D7AE,
|
||||
0xE9E49F, 0xD3ED9D, 0xC0F2B2, 0xB9F1CC,
|
||||
0xBAEDED, 0xBAB9BB, 0x000000, 0x000000
|
||||
};
|
||||
|
||||
for (int i = 0; i < 64; i++)
|
||||
{
|
||||
int r = nescap_palette[i] >> 16;
|
||||
int g = (nescap_palette[i] & 0xff00) >> 8;
|
||||
int b = nescap_palette[i] & 0xff;
|
||||
FCEUD_SetPalette(i, r, g, b);
|
||||
FCEUD_SetPalette(i + 64, r, g, b);
|
||||
FCEUD_SetPalette(i + 128, r, g, b);
|
||||
FCEUD_SetPalette(i + 192, r, g, b);
|
||||
}
|
||||
}
|
||||
else if ([displayMode isEqualToString:@"Sony CXA2025AS"])
|
||||
{
|
||||
unsigned int cxa2025as_palette[64] =
|
||||
{
|
||||
0x585858, 0x00238C, 0x00139B, 0x2D0585,
|
||||
0x5D0052, 0x7A0017, 0x7A0800, 0x5F1800,
|
||||
0x352A00, 0x093900, 0x003F00, 0x003C22,
|
||||
0x00325D, 0x000000, 0x000000, 0x000000,
|
||||
0xA1A1A1, 0x0053EE, 0x153CFE, 0x6028E4,
|
||||
0xA91D98, 0xD41E41, 0xD22C00, 0xAA4400,
|
||||
0x6C5E00, 0x2D7300, 0x007D06, 0x007852,
|
||||
0x0069A9, 0x000000, 0x000000, 0x000000,
|
||||
0xFFFFFF, 0x1FA5FE, 0x5E89FE, 0xB572FE,
|
||||
0xFE65F6, 0xFE6790, 0xFE773C, 0xFE9308,
|
||||
0xC4B200, 0x79CA10, 0x3AD54A, 0x11D1A4,
|
||||
0x06BFFE, 0x424242, 0x000000, 0x000000,
|
||||
0xFFFFFF, 0xA0D9FE, 0xBDCCFE, 0xE1C2FE,
|
||||
0xFEBCFB, 0xFEBDD0, 0xFEC5A9, 0xFED18E,
|
||||
0xE9DE86, 0xC7E992, 0xA8EEB0, 0x95ECD9,
|
||||
0x91E4FE, 0xACACAC, 0x000000, 0x000000
|
||||
};
|
||||
|
||||
for (int i = 0; i < 64; i++)
|
||||
{
|
||||
int r = cxa2025as_palette[i] >> 16;
|
||||
int g = (cxa2025as_palette[i] & 0xff00) >> 8;
|
||||
int b = cxa2025as_palette[i] & 0xff;
|
||||
FCEUD_SetPalette(i, r, g, b);
|
||||
FCEUD_SetPalette(i + 64, r, g, b);
|
||||
FCEUD_SetPalette(i + 128, r, g, b);
|
||||
FCEUD_SetPalette(i + 192, r, g, b);
|
||||
}
|
||||
}
|
||||
else if ([displayMode isEqualToString:@"Smooth (FBX)"])
|
||||
{
|
||||
unsigned int smoothfbx_palette[64] =
|
||||
{
|
||||
0x6A6D6A, 0x001380, 0x1E008A, 0x39007A,
|
||||
0x550056, 0x5A0018, 0x4F1000, 0x3D1C00,
|
||||
0x253200, 0x003D00, 0x004000, 0x003924,
|
||||
0x002E55, 0x000000, 0x000000, 0x000000,
|
||||
0xB9BCB9, 0x1850C7, 0x4B30E3, 0x7322D6,
|
||||
0x951FA9, 0x9D285C, 0x983700, 0x7F4C00,
|
||||
0x5E6400, 0x227700, 0x027E02, 0x007645,
|
||||
0x006E8A, 0x000000, 0x000000, 0x000000,
|
||||
0xFFFFFF, 0x68A6FF, 0x8C9CFF, 0xB586FF,
|
||||
0xD975FD, 0xE377B9, 0xE58D68, 0xD49D29,
|
||||
0xB3AF0C, 0x7BC211, 0x55CA47, 0x46CB81,
|
||||
0x47C1C5, 0x4A4D4A, 0x000000, 0x000000,
|
||||
0xFFFFFF, 0xCCEAFF, 0xDDDEFF, 0xECDAFF,
|
||||
0xF8D7FE, 0xFCD6F5, 0xFDDBCF, 0xF9E7B5,
|
||||
0xF1F0AA, 0xDAFAA9, 0xC9FFBC, 0xC3FBD7,
|
||||
0xC4F6F6, 0xBEC1BE, 0x000000, 0x000000
|
||||
};
|
||||
|
||||
for (int i = 0; i < 64; i++)
|
||||
{
|
||||
int r = smoothfbx_palette[i] >> 16;
|
||||
int g = (smoothfbx_palette[i] & 0xff00) >> 8;
|
||||
int b = smoothfbx_palette[i] & 0xff;
|
||||
FCEUD_SetPalette(i, r, g, b);
|
||||
FCEUD_SetPalette(i + 64, r, g, b);
|
||||
FCEUD_SetPalette(i + 128, r, g, b);
|
||||
FCEUD_SetPalette(i + 192, r, g, b);
|
||||
}
|
||||
}
|
||||
else if ([displayMode isEqualToString:@"Wavebeam"])
|
||||
{
|
||||
unsigned int wavebeam_palette[64] =
|
||||
{
|
||||
0x6B6B6B, 0x001B88, 0x21009A, 0x40008C,
|
||||
0x600067, 0x64001E, 0x590800, 0x481600,
|
||||
0x283600, 0x004500, 0x004908, 0x00421D,
|
||||
0x003659, 0x000000, 0x000000, 0x000000,
|
||||
0xB4B4B4, 0x1555D3, 0x4337EF, 0x7425DF,
|
||||
0x9C19B9, 0xAC0F64, 0xAA2C00, 0x8A4B00,
|
||||
0x666B00, 0x218300, 0x008A00, 0x008144,
|
||||
0x007691, 0x000000, 0x000000, 0x000000,
|
||||
0xFFFFFF, 0x63B2FF, 0x7C9CFF, 0xC07DFE,
|
||||
0xE977FF, 0xF572CD, 0xF4886B, 0xDDA029,
|
||||
0xBDBD0A, 0x89D20E, 0x5CDE3E, 0x4BD886,
|
||||
0x4DCFD2, 0x525252, 0x000000, 0x000000,
|
||||
0xFFFFFF, 0xBCDFFF, 0xD2D2FF, 0xE1C8FF,
|
||||
0xEFC7FF, 0xFFC3E1, 0xFFCAC6, 0xF2DAAD,
|
||||
0xEBE3A0, 0xD2EDA2, 0xBCF4B4, 0xB5F1CE,
|
||||
0xB6ECF1, 0xBFBFBF, 0x000000, 0x000000
|
||||
};
|
||||
|
||||
for (int i = 0; i < 64; i++)
|
||||
{
|
||||
int r = wavebeam_palette[i] >> 16;
|
||||
int g = (wavebeam_palette[i] & 0xff00) >> 8;
|
||||
int b = wavebeam_palette[i] & 0xff;
|
||||
FCEUD_SetPalette(i, r, g, b);
|
||||
FCEUD_SetPalette(i + 64, r, g, b);
|
||||
FCEUD_SetPalette(i + 128, r, g, b);
|
||||
FCEUD_SetPalette(i + 192, r, g, b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)loadDisplayModeOptions
|
||||
{
|
||||
// Restore palette
|
||||
NSString *lastPalette = self.displayModeInfo[@"palette"];
|
||||
if (lastPalette && ![lastPalette isEqualToString:@"Default — FCEUX"]) {
|
||||
[self changeDisplayWithMode:lastPalette];
|
||||
}
|
||||
|
||||
// Crop horizontal overscan
|
||||
BOOL isHorizontalOverscanCropped = [self.displayModeInfo[@"cropHorizontalOverscan"] boolValue];
|
||||
if (isHorizontalOverscanCropped) {
|
||||
[self changeDisplayWithMode:@"Crop Horizontal"];
|
||||
}
|
||||
|
||||
// Crop vertical overscan
|
||||
BOOL isVerticalOverscanCropped = [self.displayModeInfo[@"cropVerticalOverscan"] boolValue];
|
||||
if (isVerticalOverscanCropped) {
|
||||
[self changeDisplayWithMode:@"Crop Vertical"];
|
||||
}
|
||||
}
|
||||
|
||||
// FCEUX internal functions and stubs
|
||||
void FCEUD_SetPalette(unsigned char index, unsigned char r, unsigned char g, unsigned char b)
|
||||
{
|
||||
@@ -505,11 +819,11 @@ ArchiveScanRecord FCEUD_ScanArchive(std::string fname) { return ArchiveScanRecor
|
||||
void GetMouseData(uint32 (&md)[3]) {}
|
||||
void FCEUD_PrintError(const char *s)
|
||||
{
|
||||
NSLog(@"FCEUX error: %s", s);
|
||||
NSLog(@"[FCEUX] error: %s", s);
|
||||
}
|
||||
void FCEUD_Message(const char *s)
|
||||
{
|
||||
NSLog(@"FCEUX message: %s", s);
|
||||
NSLog(@"[FCEUX] message: %s", s);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+3
-1
@@ -17,7 +17,7 @@
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>2.2.3</string>
|
||||
<string>2.2.3.1</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>OEGameCoreController</string>
|
||||
<key>OEGameCoreClass</key>
|
||||
@@ -32,6 +32,8 @@
|
||||
<integer>0</integer>
|
||||
<key>OEGameCoreSupportsCheatCode</key>
|
||||
<true/>
|
||||
<key>OEGameCoreSupportsDisplayModeChange</key>
|
||||
<true/>
|
||||
<key>OEGameCoreSupportsRewinding</key>
|
||||
<true/>
|
||||
</dict>
|
||||
|
||||
+1
-1
@@ -216,7 +216,7 @@ void FCEU_LoadGameCheats(FILE *override)
|
||||
}
|
||||
|
||||
FCEU_DispMessage("Cheats file loaded.",0); //Tells user a cheats file was loaded.
|
||||
while(fgets(linebuf,2048,fp)>0)
|
||||
while(fgets(linebuf,2048,fp) != nullptr)
|
||||
{
|
||||
char *tbuf=linebuf;
|
||||
int doc=0;
|
||||
|
||||
Reference in New Issue
Block a user