Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 02499a735b | |||
| 2875815a60 | |||
| f1828430d3 | |||
| 682625653b |
+146
-47
@@ -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:
|
||||
@@ -36,6 +36,14 @@
|
||||
#include "shared.h"
|
||||
#include "scrc32.h"
|
||||
|
||||
#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 : @"",}
|
||||
|
||||
static const double pal_fps = 53203424.0 / (3420.0 * 313.0);
|
||||
static const double ntsc_fps = 53693175.0 / (3420.0 * 262.0);
|
||||
|
||||
@@ -99,9 +107,10 @@ typedef NS_ENUM(NSInteger, MultiTapType)
|
||||
|
||||
@interface GenPlusGameCore () <OEGenesisSystemResponderClient, OESegaCDSystemResponderClient>
|
||||
{
|
||||
uint8_t *videoBuffer;
|
||||
int16_t *soundBuffer;
|
||||
NSMutableDictionary *cheatList;
|
||||
uint8_t *_videoBuffer;
|
||||
int16_t *_soundBuffer;
|
||||
NSMutableDictionary<NSString *, NSNumber *> *_cheatList;
|
||||
NSMutableArray <NSMutableDictionary <NSString *, id> *> *_availableDisplayModes;
|
||||
NSURL *_romFile;
|
||||
MultiTapType _multiTapType;
|
||||
}
|
||||
@@ -119,9 +128,9 @@ static __weak GenPlusGameCore *_current;
|
||||
{
|
||||
if((self = [super init]))
|
||||
{
|
||||
videoBuffer = (uint8_t*)malloc(720 * 576 * 4);
|
||||
soundBuffer = (int16_t *)malloc(2048 * 2 * 2);
|
||||
cheatList = [NSMutableDictionary dictionary];
|
||||
_videoBuffer = (uint8_t *)malloc(720 * 576 * sizeof(uint32_t));
|
||||
_soundBuffer = (int16_t *)malloc(2048 * 2 * sizeof(int16_t));
|
||||
_cheatList = [NSMutableDictionary dictionary];
|
||||
}
|
||||
|
||||
_current = self;
|
||||
@@ -131,8 +140,8 @@ static __weak GenPlusGameCore *_current;
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
free(videoBuffer);
|
||||
free(soundBuffer);
|
||||
free(_videoBuffer);
|
||||
free(_soundBuffer);
|
||||
}
|
||||
|
||||
# pragma mark - Execution
|
||||
@@ -142,13 +151,13 @@ static __weak GenPlusGameCore *_current;
|
||||
_romFile = [NSURL fileURLWithPath:path];
|
||||
|
||||
// Set CD BIOS and BRAM/RAM Cart paths
|
||||
snprintf(CD_BIOS_EU, sizeof(CD_BIOS_EU), "%s%sbios_CD_E.bin", [[self biosDirectoryPath] fileSystemRepresentation], "/");
|
||||
snprintf(CD_BIOS_US, sizeof(CD_BIOS_US), "%s%sbios_CD_U.bin", [[self biosDirectoryPath] fileSystemRepresentation], "/");
|
||||
snprintf(CD_BIOS_JP, sizeof(CD_BIOS_JP), "%s%sbios_CD_J.bin", [[self biosDirectoryPath] fileSystemRepresentation], "/");
|
||||
snprintf(CD_BRAM_EU, sizeof(CD_BRAM_EU), "%s%sscd_E.brm", [[self batterySavesDirectoryPath] fileSystemRepresentation], "/");
|
||||
snprintf(CD_BRAM_US, sizeof(CD_BRAM_US), "%s%sscd_U.brm", [[self batterySavesDirectoryPath] fileSystemRepresentation], "/");
|
||||
snprintf(CD_BRAM_JP, sizeof(CD_BRAM_JP), "%s%sscd_J.brm", [[self batterySavesDirectoryPath] fileSystemRepresentation], "/");
|
||||
snprintf(CART_BRAM, sizeof(CART_BRAM), "%s%scart.brm", [[self batterySavesDirectoryPath] fileSystemRepresentation], "/");
|
||||
snprintf(CD_BIOS_EU, sizeof(CD_BIOS_EU), "%s%sbios_CD_E.bin", self.biosDirectoryPath.fileSystemRepresentation, "/");
|
||||
snprintf(CD_BIOS_US, sizeof(CD_BIOS_US), "%s%sbios_CD_U.bin", self.biosDirectoryPath.fileSystemRepresentation, "/");
|
||||
snprintf(CD_BIOS_JP, sizeof(CD_BIOS_JP), "%s%sbios_CD_J.bin", self.biosDirectoryPath.fileSystemRepresentation, "/");
|
||||
snprintf(CD_BRAM_EU, sizeof(CD_BRAM_EU), "%s%sscd_E.brm", self.batterySavesDirectoryPath.fileSystemRepresentation, "/");
|
||||
snprintf(CD_BRAM_US, sizeof(CD_BRAM_US), "%s%sscd_U.brm", self.batterySavesDirectoryPath.fileSystemRepresentation, "/");
|
||||
snprintf(CD_BRAM_JP, sizeof(CD_BRAM_JP), "%s%sscd_J.brm", self.batterySavesDirectoryPath.fileSystemRepresentation, "/");
|
||||
snprintf(CART_BRAM, sizeof(CART_BRAM), "%s%scart.brm", self.batterySavesDirectoryPath.fileSystemRepresentation, "/");
|
||||
|
||||
[self configureOptions];
|
||||
|
||||
@@ -167,7 +176,7 @@ static __weak GenPlusGameCore *_current;
|
||||
{
|
||||
config.region_detect = 3;
|
||||
region_code = REGION_JAPAN_NTSC;
|
||||
NSLog(@"Genesis Plus GX: Forcing region to Japan for multi-region cart");
|
||||
NSLog(@"[Genesis Plus GX] Forcing region to Japan for multi-region cart");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,17 +190,17 @@ static __weak GenPlusGameCore *_current;
|
||||
bram_load();
|
||||
|
||||
// Set battery saves dir and load sram
|
||||
NSString *extensionlessFilename = [[_romFile lastPathComponent] stringByDeletingPathExtension];
|
||||
NSURL *batterySavesDirectory = [NSURL fileURLWithPath:[self batterySavesDirectoryPath]];
|
||||
[[NSFileManager defaultManager] createDirectoryAtURL:batterySavesDirectory withIntermediateDirectories:YES attributes:nil error:nil];
|
||||
NSString *extensionlessFilename = _romFile.lastPathComponent.stringByDeletingPathExtension;
|
||||
NSURL *batterySavesDirectory = [NSURL fileURLWithPath:self.batterySavesDirectoryPath];
|
||||
[NSFileManager.defaultManager createDirectoryAtURL:batterySavesDirectory withIntermediateDirectories:YES attributes:nil error:nil];
|
||||
NSURL *saveFile = [batterySavesDirectory URLByAppendingPathComponent:[extensionlessFilename stringByAppendingPathExtension:@"sav"]];
|
||||
|
||||
if ([saveFile checkResourceIsReachableAndReturnError:nil])
|
||||
{
|
||||
NSData *saveData = [NSData dataWithContentsOfURL:saveFile];
|
||||
memcpy(sram.sram, [saveData bytes], 0x10000);
|
||||
memcpy(sram.sram, saveData.bytes, 0x10000);
|
||||
sram.crc = crc32(0, sram.sram, 0x10000);
|
||||
NSLog(@"GenesisPlusGX: Loaded sram");
|
||||
NSLog(@"[Genesis Plus GX] Loaded sram");
|
||||
}
|
||||
|
||||
if([self.systemIdentifier isEqualToString:@"openemu.system.sg"] || [self.systemIdentifier isEqualToString:@"openemu.system.scd"])
|
||||
@@ -213,8 +222,8 @@ static __weak GenPlusGameCore *_current;
|
||||
else
|
||||
system_frame_sms(0);
|
||||
|
||||
int samples = audio_update(soundBuffer);
|
||||
[[self ringBufferAtIndex:0] write:soundBuffer maxLength:samples << 2];
|
||||
int samples = audio_update(_soundBuffer);
|
||||
[[self ringBufferAtIndex:0] write:_soundBuffer maxLength:samples << 2];
|
||||
}
|
||||
|
||||
- (void)resetEmulation
|
||||
@@ -241,8 +250,8 @@ static __weak GenPlusGameCore *_current;
|
||||
if ((filesize != 0) || (crc32(0, &sram.sram[0], 0x10000) != sram.crc))
|
||||
{
|
||||
NSError *error = nil;
|
||||
NSString *extensionlessFilename = [[_romFile lastPathComponent] stringByDeletingPathExtension];
|
||||
NSURL *batterySavesDirectory = [NSURL fileURLWithPath:[self batterySavesDirectoryPath]];
|
||||
NSString *extensionlessFilename = _romFile.lastPathComponent.stringByDeletingPathExtension;
|
||||
NSURL *batterySavesDirectory = [NSURL fileURLWithPath:self.batterySavesDirectoryPath];
|
||||
NSURL *saveFile = [batterySavesDirectory URLByAppendingPathComponent:[extensionlessFilename stringByAppendingPathExtension:@"sav"]];
|
||||
|
||||
// copy SRAM data
|
||||
@@ -253,9 +262,9 @@ static __weak GenPlusGameCore *_current;
|
||||
sram.crc = crc32(0, sram.sram, 0x10000);
|
||||
|
||||
if (error)
|
||||
NSLog(@"GenesisPlusGX: Error writing sram file: %@", error);
|
||||
NSLog(@"[Genesis Plus GX] Error writing sram file: %@", error);
|
||||
else
|
||||
NSLog(@"GenesisPlusGX: Saved sram file: %@", saveFile);
|
||||
NSLog(@"[Genesis Plus GX] Saved sram file: %@", saveFile);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -277,7 +286,7 @@ static __weak GenPlusGameCore *_current;
|
||||
- (const void *)getVideoBufferWithHint:(void *)hint
|
||||
{
|
||||
if (!hint) {
|
||||
hint = videoBuffer;
|
||||
hint = _videoBuffer;
|
||||
}
|
||||
|
||||
return bitmap.data = (uint8_t*)hint;
|
||||
@@ -347,7 +356,7 @@ static __weak GenPlusGameCore *_current;
|
||||
int serial_size = STATE_SIZE;
|
||||
NSMutableData *stateData = [NSMutableData dataWithLength:serial_size];
|
||||
|
||||
if(!state_save([stateData mutableBytes]))
|
||||
if(!state_save(stateData.mutableBytes))
|
||||
{
|
||||
NSError *error = [NSError errorWithDomain:OEGameCoreErrorDomain code:OEGameCoreCouldNotSaveStateError userInfo:@{
|
||||
NSLocalizedDescriptionKey : @"Save state data could not be written",
|
||||
@@ -375,17 +384,17 @@ static __weak GenPlusGameCore *_current;
|
||||
}
|
||||
|
||||
int serial_size = STATE_SIZE;
|
||||
if(serial_size != [data length])
|
||||
if(serial_size != data.length)
|
||||
{
|
||||
NSError *error = [NSError errorWithDomain:OEGameCoreErrorDomain code:OEGameCoreStateHasWrongSizeError userInfo:@{
|
||||
NSLocalizedDescriptionKey : @"Save state has wrong file size.",
|
||||
NSLocalizedRecoverySuggestionErrorKey : [NSString stringWithFormat:@"The size of the file %@ does not have the right size, %d expected, got: %ld.", fileName, serial_size, [data length]],
|
||||
NSLocalizedRecoverySuggestionErrorKey : [NSString stringWithFormat:@"The size of the file %@ does not have the right size, %d expected, got: %ld.", fileName, serial_size, data.length],
|
||||
}];
|
||||
block(NO, error);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!state_load((uint8_t *)[data bytes]))
|
||||
if(!state_load((uint8_t *)data.bytes))
|
||||
{
|
||||
NSError *error = [NSError errorWithDomain:OEGameCoreErrorDomain code:OEGameCoreCouldNotLoadStateError userInfo:@{
|
||||
NSLocalizedDescriptionKey : @"The save state data could not be read",
|
||||
@@ -418,15 +427,15 @@ static __weak GenPlusGameCore *_current;
|
||||
|
||||
- (BOOL)deserializeState:(NSData *)state withError:(NSError **)outError
|
||||
{
|
||||
const void *bytes = [state bytes];
|
||||
size_t length = [state length];
|
||||
const void *bytes = state.bytes;
|
||||
size_t length = state.length;
|
||||
size_t serialSize = STATE_SIZE;
|
||||
|
||||
if(serialSize != length) {
|
||||
if (outError) {
|
||||
*outError = [NSError errorWithDomain:OEGameCoreErrorDomain code:OEGameCoreStateHasWrongSizeError userInfo:@{
|
||||
NSLocalizedDescriptionKey : @"Save state has wrong file size.",
|
||||
NSLocalizedRecoverySuggestionErrorKey : [NSString stringWithFormat:@"The size of the save state does not have the right size, %lu expected, got: %ld.", serialSize, [state length]],
|
||||
NSLocalizedRecoverySuggestionErrorKey : [NSString stringWithFormat:@"The size of the save state does not have the right size, %lu expected, got: %ld.", serialSize, state.length],
|
||||
}];
|
||||
}
|
||||
|
||||
@@ -653,18 +662,18 @@ const int MasterSystemMap[] = {INPUT_UP, INPUT_DOWN, INPUT_LEFT, INPUT_RIGHT, IN
|
||||
code = [code stringByReplacingOccurrencesOfString:@" " withString:@""];
|
||||
|
||||
if (enabled)
|
||||
cheatList[code] = @YES;
|
||||
_cheatList[code] = @YES;
|
||||
else
|
||||
[cheatList removeObjectForKey:code];
|
||||
[_cheatList removeObjectForKey:code];
|
||||
|
||||
[self resetCheats];
|
||||
|
||||
NSArray *multipleCodes = [NSArray array];
|
||||
NSArray<NSString *> *multipleCodes = [NSArray array];
|
||||
|
||||
// Apply enabled cheats found in dictionary
|
||||
for (id key in cheatList)
|
||||
for (NSString *key in _cheatList)
|
||||
{
|
||||
if ([cheatList[key] isEqual:@YES])
|
||||
if ([_cheatList[key] boolValue])
|
||||
{
|
||||
// Handle multi-line cheats
|
||||
multipleCodes = [key componentsSeparatedByString:@"+"];
|
||||
@@ -676,6 +685,84 @@ const int MasterSystemMap[] = {INPUT_UP, INPUT_DOWN, INPUT_LEFT, INPUT_RIGHT, IN
|
||||
}
|
||||
}
|
||||
|
||||
# pragma mark - Display Mode
|
||||
|
||||
- (NSArray <NSDictionary <NSString *, id> *> *)displayModes
|
||||
{
|
||||
if (![self.systemIdentifier isEqualToString:@"openemu.system.gg"])
|
||||
return nil;
|
||||
|
||||
if (_availableDisplayModes.count == 0)
|
||||
{
|
||||
_availableDisplayModes = [NSMutableArray array];
|
||||
|
||||
NSArray <NSDictionary <NSString *, id> *> *availableModesWithDefault =
|
||||
@[
|
||||
Label(@"Screen"),
|
||||
OptionToggleable(@"LCD Ghosting", @"ggLCDFilter"),
|
||||
];
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
// Game Gear: LCD ghosting / motion blur
|
||||
// Required for proper display of some effects in a few games (James Pond 3, Power Drift, Super Monaco GP II)
|
||||
if ([displayMode isEqualToString:@"LCD Ghosting"])
|
||||
{
|
||||
if (!displayModeState)
|
||||
config.lcd = (uint8)(0.80 * 256);
|
||||
else
|
||||
config.lcd = 0;
|
||||
}
|
||||
}
|
||||
|
||||
# pragma mark - Misc Helper Methods
|
||||
|
||||
- (void)applyCheat:(NSString *)code
|
||||
@@ -684,7 +771,7 @@ const int MasterSystemMap[] = {INPUT_UP, INPUT_DOWN, INPUT_LEFT, INPUT_RIGHT, IN
|
||||
clear_cheats();
|
||||
|
||||
/* interpret code and give it an index */
|
||||
decode_cheat((char *)[code UTF8String], maxcheats);
|
||||
decode_cheat((char *)code.UTF8String, maxcheats);
|
||||
|
||||
/* increment cheat count */
|
||||
maxcheats++;
|
||||
@@ -737,7 +824,19 @@ const int MasterSystemMap[] = {INPUT_UP, INPUT_DOWN, INPUT_LEFT, INPUT_RIGHT, IN
|
||||
config.overscan = 0; /* 3 == FULL */
|
||||
config.gg_extra = 0; /* 1 = show extended Game Gear screen (256x192) */
|
||||
config.ntsc = 0;
|
||||
config.lcd = 0;
|
||||
|
||||
// Only temporary, so core doesn't crash on an older OpenEmu version
|
||||
if ([self respondsToSelector:@selector(displayModeInfo)]) {
|
||||
BOOL isLCDFilterEnabled = [self.displayModeInfo[@"ggLCDFilter"] boolValue];
|
||||
if (isLCDFilterEnabled)
|
||||
[self changeDisplayWithMode:@"LCD Ghosting"];
|
||||
else
|
||||
config.lcd = 0;
|
||||
|
||||
}
|
||||
else
|
||||
config.lcd = 0;
|
||||
|
||||
config.render = 0;
|
||||
|
||||
/* initialize bitmap */
|
||||
@@ -745,7 +844,7 @@ const int MasterSystemMap[] = {INPUT_UP, INPUT_DOWN, INPUT_LEFT, INPUT_RIGHT, IN
|
||||
bitmap.width = 720;
|
||||
bitmap.height = 576;
|
||||
bitmap.pitch = bitmap.width * sizeof(uint32_t);
|
||||
bitmap.data = (uint8_t *)videoBuffer;
|
||||
bitmap.data = (uint8_t *)_videoBuffer;
|
||||
}
|
||||
|
||||
- (void)configureInput
|
||||
@@ -753,7 +852,7 @@ const int MasterSystemMap[] = {INPUT_UP, INPUT_DOWN, INPUT_LEFT, INPUT_RIGHT, IN
|
||||
_multiTapType = MultiTapTypeNone;
|
||||
|
||||
// Overrides: Six button controller-supported games missing '6' byte in cart header, so they cannot be auto-detected
|
||||
NSArray *pad6Buttons = @[
|
||||
NSArray<NSString *> *pad6Buttons = @[
|
||||
@"b04c06df1009c60182df902a4ec7c959", // Batman Forever (World)
|
||||
@"7b144947f6e8842dd4419d5166cddff6", // Boogerman - A Pick and Flick Adventure (Europe)
|
||||
@"265a10bf2ea2d1ee30e6cde631d54474", // Boogerman - A Pick and Flick Adventure (USA)
|
||||
@@ -790,7 +889,7 @@ const int MasterSystemMap[] = {INPUT_UP, INPUT_DOWN, INPUT_LEFT, INPUT_RIGHT, IN
|
||||
// Different port configurations and multitap devices are used depending on the game
|
||||
// NOTE: J-Cart games are automatically handled.
|
||||
// TODO: Identify supported Sega CD games by rominfo.domestic/rominfo.international?
|
||||
NSDictionary *multiTapGames =
|
||||
NSDictionary<NSString *, NSNumber *> *multiTapGames =
|
||||
@{
|
||||
//@"3cc6df243e714097f1599cf618f94d0b" : @(TeamPlayerPort1), // Aq Renkan Awa (Taiwan) (Unl)
|
||||
@"2b27a61cdae4492044bd273c5807de75" : @(TeamPlayerPort1), // Barkley Shut Up and Jam! (USA, Europe)
|
||||
@@ -1563,7 +1662,7 @@ void RAMCheatUpdate(void)
|
||||
/* apply RAM patch */
|
||||
//if (cheatlist[index].data & 0xFF00)
|
||||
//if (cheatlist[index].data & 0x00FF) // For LSB?
|
||||
BOOL isSega8bit = [[_current systemIdentifier] isEqualToString:@"openemu.system.gg"] || [[_current systemIdentifier] isEqualToString:@"openemu.system.sms"] || [[_current systemIdentifier] isEqualToString:@"openemu.system.sg1000"];
|
||||
BOOL isSega8bit = [_current.systemIdentifier isEqualToString:@"openemu.system.gg"] || [_current.systemIdentifier isEqualToString:@"openemu.system.sms"] || [_current.systemIdentifier isEqualToString:@"openemu.system.sg1000"];
|
||||
// TODO: Figure out why this is. Some PAR cheats for Genesis don't work otherwise.
|
||||
if (isSega8bit ? cheatlist[index].data & 0xFF00 : cheatlist[index].data & 0x00FF)
|
||||
{
|
||||
|
||||
+3
-1
@@ -19,7 +19,7 @@
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.7.4.9</string>
|
||||
<string>1.7.4.10</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>OEGameCoreController</string>
|
||||
<key>OEGameCoreClass</key>
|
||||
@@ -34,6 +34,8 @@
|
||||
<integer>0</integer>
|
||||
<key>OEGameCoreSupportsCheatCode</key>
|
||||
<true/>
|
||||
<key>OEGameCoreSupportsDisplayModeChange</key>
|
||||
<true/>
|
||||
<key>OEGameCoreSupportsRewinding</key>
|
||||
<true/>
|
||||
</dict>
|
||||
|
||||
@@ -211,10 +211,10 @@ void cdd_reset(void)
|
||||
cdd.lba = 0;
|
||||
|
||||
/* reset status */
|
||||
cdd.status = cdd.loaded ? CD_STOP : NO_DISC;
|
||||
cdd.status = cdd.loaded ? CD_TOC : NO_DISC;
|
||||
|
||||
/* reset CD-DA fader (full volume) */
|
||||
cdd.volume = 0x400;
|
||||
cdd.fader[0] = cdd.fader[1] = 0x400;
|
||||
|
||||
/* clear CD-DA output */
|
||||
cdd.audio[0] = cdd.audio[1] = 0;
|
||||
@@ -229,7 +229,7 @@ int cdd_context_save(uint8 *state)
|
||||
save_param(&cdd.index, sizeof(cdd.index));
|
||||
save_param(&cdd.lba, sizeof(cdd.lba));
|
||||
save_param(&cdd.scanOffset, sizeof(cdd.scanOffset));
|
||||
save_param(&cdd.volume, sizeof(cdd.volume));
|
||||
save_param(&cdd.fader, sizeof(cdd.fader));
|
||||
save_param(&cdd.status, sizeof(cdd.status));
|
||||
|
||||
return bufferptr;
|
||||
@@ -255,7 +255,7 @@ int cdd_context_load(uint8 *state)
|
||||
load_param(&cdd.index, sizeof(cdd.index));
|
||||
load_param(&cdd.lba, sizeof(cdd.lba));
|
||||
load_param(&cdd.scanOffset, sizeof(cdd.scanOffset));
|
||||
load_param(&cdd.volume, sizeof(cdd.volume));
|
||||
load_param(&cdd.fader, sizeof(cdd.fader));
|
||||
load_param(&cdd.status, sizeof(cdd.status));
|
||||
|
||||
/* adjust current LBA within track limit */
|
||||
@@ -1287,10 +1287,10 @@ void cdd_read_audio(unsigned int samples)
|
||||
int i, mul, l, r;
|
||||
|
||||
/* current CD-DA fader volume */
|
||||
int curVol = cdd.volume;
|
||||
int curVol = cdd.fader[0];
|
||||
|
||||
/* CD-DA fader volume setup (0-1024) */
|
||||
int endVol = scd.regs[0x34>>1].w >> 4;
|
||||
int endVol = cdd.fader[1];
|
||||
|
||||
/* read samples from current block */
|
||||
#if defined(USE_LIBCHDR)
|
||||
@@ -1476,7 +1476,7 @@ void cdd_read_audio(unsigned int samples)
|
||||
}
|
||||
|
||||
/* save current CD-DA fader volume */
|
||||
cdd.volume = curVol;
|
||||
cdd.fader[0] = curVol;
|
||||
|
||||
/* save last audio output for next frame */
|
||||
cdd.audio[0] = prev_l;
|
||||
@@ -1778,7 +1778,7 @@ void cdd_process(void)
|
||||
/* Process CDD command */
|
||||
switch (scd.regs[0x42>>1].byte.h & 0x0f)
|
||||
{
|
||||
case 0x00: /* Drive Status */
|
||||
case 0x00: /* Report Drive Status */
|
||||
{
|
||||
/* RS1-RS8 normally unchanged */
|
||||
scd.regs[0x38>>1].byte.h = cdd.status;
|
||||
@@ -1800,21 +1800,21 @@ void cdd_process(void)
|
||||
case 0x01: /* Stop Drive */
|
||||
{
|
||||
/* update status */
|
||||
cdd.status = cdd.loaded ? CD_STOP : NO_DISC;
|
||||
cdd.status = cdd.loaded ? CD_TOC : NO_DISC;
|
||||
|
||||
/* no audio track playing */
|
||||
scd.regs[0x36>>1].byte.h = 0x01;
|
||||
|
||||
/* RS1-RS8 ignored, expects 0x0 (drive busy ?) in RS0 once */
|
||||
scd.regs[0x38>>1].w = CD_BUSY << 8;
|
||||
/* RS1-RS8 ignored, expects 0x0 (CD_STOP) in RS0 once */
|
||||
scd.regs[0x38>>1].w = CD_STOP << 8;
|
||||
scd.regs[0x3a>>1].w = 0x0000;
|
||||
scd.regs[0x3c>>1].w = 0x0000;
|
||||
scd.regs[0x3e>>1].w = 0x0000;
|
||||
scd.regs[0x40>>1].w = ~CD_BUSY & 0x0f;
|
||||
scd.regs[0x40>>1].w = ~CD_STOP & 0x0f;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x02: /* Read TOC */
|
||||
case 0x02: /* Report TOC infos */
|
||||
{
|
||||
/* Infos automatically retrieved by CDD processor from Q-Channel */
|
||||
/* commands 0x00-0x02 (current block) and 0x03-0x05 (Lead-In) */
|
||||
@@ -1886,10 +1886,20 @@ void cdd_process(void)
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x06: /* Latest Error Information */
|
||||
{
|
||||
scd.regs[0x38>>1].w = (cdd.status << 8) | 0x06;
|
||||
scd.regs[0x3a>>1].w = 0x0000; /* no error */
|
||||
scd.regs[0x3c>>1].w = 0x0000;
|
||||
scd.regs[0x3e>>1].w = 0x0000;
|
||||
scd.regs[0x40>>1].byte.h = 0x00;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
#ifdef LOG_ERROR
|
||||
error("Unknown CDD Command %02X (%X)\n", scd.regs[0x44>>1].byte.l, s68k.pc);
|
||||
error("Invalid CDD request code %02X (%X)\n", scd.regs[0x44>>1].byte.l, s68k.pc);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
@@ -2157,7 +2167,6 @@ void cdd_process(void)
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case 0x0a: /* N-Track Jump Control ? (usually sent before CD_SEEK or CD_PLAY commands) */
|
||||
{
|
||||
/* TC3 corresponds to seek direction (00=forward, FF=reverse) */
|
||||
@@ -2179,14 +2188,14 @@ void cdd_process(void)
|
||||
scd.regs[0x36>>1].byte.h = 0x01;
|
||||
|
||||
/* update status */
|
||||
cdd.status = cdd.loaded ? CD_STOP : NO_DISC;
|
||||
cdd.status = cdd.loaded ? CD_TOC : NO_DISC;
|
||||
|
||||
/* RS1-RS8 ignored, expects 0x0 (drive busy ?) in RS0 once */
|
||||
scd.regs[0x38>>1].w = CD_BUSY << 8;
|
||||
/* RS1-RS8 ignored, expects CD_STOP in RS0 once */
|
||||
scd.regs[0x38>>1].w = CD_STOP << 8;
|
||||
scd.regs[0x3a>>1].w = 0x0000;
|
||||
scd.regs[0x3c>>1].w = 0x0000;
|
||||
scd.regs[0x3e>>1].w = 0x0000;
|
||||
scd.regs[0x40>>1].w = ~CD_BUSY & 0x0f;
|
||||
scd.regs[0x40>>1].w = ~CD_STOP & 0x0f;
|
||||
|
||||
#ifdef CD_TRAY_CALLBACK
|
||||
CD_TRAY_CALLBACK
|
||||
@@ -2214,8 +2223,8 @@ void cdd_process(void)
|
||||
}
|
||||
|
||||
default: /* Unknown command */
|
||||
#ifdef LOG_CDD
|
||||
error("Unknown CDD Command !!!\n");
|
||||
#ifdef LOG_ERROR
|
||||
error("Unsupported CDD command %02X (%X)\n", scd.regs[0x42>>1].byte.h & 0x0f, s68k.pc);
|
||||
#endif
|
||||
scd.regs[0x38>>1].byte.h = cdd.status;
|
||||
break;
|
||||
|
||||
@@ -54,15 +54,26 @@
|
||||
#define cdd scd.cdd_hw
|
||||
|
||||
/* CDD status */
|
||||
#define CD_BUSY 0x00
|
||||
#define CD_PLAY 0x01
|
||||
#define CD_SEEK 0x02
|
||||
#define CD_SCAN 0x03
|
||||
#define CD_PAUSE 0x04
|
||||
#define CD_OPEN 0x05
|
||||
#define CD_STOP 0x09
|
||||
#define NO_DISC 0x0B
|
||||
#define CD_END 0x0C
|
||||
#define CD_STOP 0x00
|
||||
#define CD_PLAY 0x01
|
||||
#define CD_SEEK 0x02
|
||||
#define CD_SCAN 0x03
|
||||
#define CD_PAUSE 0x04
|
||||
#define CD_OPEN 0x05
|
||||
#define NO_VALID_CHK 0x06 /* unused */
|
||||
#define NO_VALID_CMD 0x07 /* unused */
|
||||
#define CD_ERROR 0x08 /* unused */
|
||||
#define CD_TOC 0x09
|
||||
#define CD_TRACK_MOVE 0x0A /* unused */
|
||||
#define NO_DISC 0x0B
|
||||
#define CD_END 0x0C
|
||||
#define CD_TRAY 0x0E /* unused */
|
||||
#define CD_TEST 0x0F /* unusec */
|
||||
|
||||
/* CD-DA digital filter types */
|
||||
#define CD_TYPE_DEFAULT 0x00
|
||||
#define CD_TYPE_WONDERMEGA 0x01
|
||||
#define CD_TYPE_WONDERMEGA_M2 0x02
|
||||
|
||||
/* CD track */
|
||||
typedef struct
|
||||
@@ -103,11 +114,12 @@ typedef struct
|
||||
{
|
||||
uint32 cycles;
|
||||
uint32 latency;
|
||||
int type;
|
||||
int loaded;
|
||||
int index;
|
||||
int lba;
|
||||
int scanOffset;
|
||||
int volume;
|
||||
uint16 fader[2];
|
||||
uint8 status;
|
||||
uint16 sectorSize;
|
||||
toc_t toc;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* Genesis Plus
|
||||
* Mega CD / Sega CD hardware
|
||||
*
|
||||
* Copyright (C) 2012-2016 Eke-Eke (Genesis Plus GX)
|
||||
* Copyright (C) 2012-2018 Eke-Eke (Genesis Plus GX)
|
||||
*
|
||||
* Redistribution and use of this code or any derivative works are permitted
|
||||
* provided that the following conditions are met:
|
||||
@@ -1281,6 +1281,46 @@ static void scd_write_word(unsigned int address, unsigned int data)
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x34: /* CD Fader */
|
||||
{
|
||||
/* Wondermega hardware (CXD2554M digital filter) */
|
||||
if (cdd.type == CD_TYPE_WONDERMEGA)
|
||||
{
|
||||
/* only MSB is latched by CXD2554M chip, LSB is ignored (8-bit digital filter) */
|
||||
/* attenuator data is 7-bit only (bits 0-7) */
|
||||
data = (data >> 8) & 0x7f;
|
||||
|
||||
/* scale CXD2554M volume (0-127) to full (LC7883KM compatible) volume range (0-1024) */
|
||||
cdd.fader[1] = (1024 * data) / 127 ;
|
||||
}
|
||||
|
||||
/* Wondermega M2 / X'Eye hardware (SM5841A digital filter) */
|
||||
else if (cdd.type == CD_TYPE_WONDERMEGA_M2)
|
||||
{
|
||||
/* only MSB is latched by SM5841A chip, LSB is ignored (8-bit digital filter) */
|
||||
data = data >> 8;
|
||||
|
||||
/* attenuator data is set only when command bit (bit 0) is cleared (other commands are ignored) */
|
||||
if (data & 0x01) return;
|
||||
|
||||
/* attenuator data is 7-bit only (bits 8-1) and reverted (bit 1 = msb) */
|
||||
/* bit reversing formula taken from http://graphics.stanford.edu/~seander/bithacks.html#ReverseByteWith32Bits */
|
||||
data = (((((data * 0x0802) & 0x22110) | ((data * 0x8020) & 0x88440)) * 0x10101) >> 16) & 0x7f;
|
||||
|
||||
/* convert & scale SM5841A attenuation (127-0) to full (LC7883KM compatible) volume range (0-1024) */
|
||||
cdd.fader[1] = (1024 * (127 - data)) / 127 ;
|
||||
}
|
||||
|
||||
/* default CD hardware (LC7883KM digital filter) */
|
||||
else
|
||||
{
|
||||
/* get LC7883KM volume data (12-bit) */
|
||||
cdd.fader[1] = data >> 4 ;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x4a: /* CDD command 9 (controlled by BIOS, word access only ?) */
|
||||
{
|
||||
scd.regs[0x4a>>1].w = 0;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* Genesis Plus
|
||||
* Mega CD / Sega CD hardware
|
||||
*
|
||||
* Copyright (C) 2012-2016 Eke-Eke (Genesis Plus GX)
|
||||
* Copyright (C) 2012-2018 Eke-Eke (Genesis Plus GX)
|
||||
*
|
||||
* Redistribution and use of this code or any derivative works are permitted
|
||||
* provided that the following conditions are met:
|
||||
|
||||
@@ -417,6 +417,23 @@ int load_bios(int system)
|
||||
/* CD BOOTROM loaded ? */
|
||||
if (size > 0)
|
||||
{
|
||||
/* auto-detect CD hardware model */
|
||||
if (!memcmp (&scd.bootrom[0x120], "WONDER-MEGA BOOT", 16))
|
||||
{
|
||||
/* Wondermega CD hardware */
|
||||
cdd.type = CD_TYPE_WONDERMEGA;
|
||||
}
|
||||
else if (!memcmp (&scd.bootrom[0x120], "WONDERMEGA2 BOOT", 16))
|
||||
{
|
||||
/* Wondermega M2 / X'Eye CD hardware */
|
||||
cdd.type = CD_TYPE_WONDERMEGA_M2;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* default CD hardware */
|
||||
cdd.type = CD_TYPE_DEFAULT;
|
||||
}
|
||||
|
||||
#ifdef LSB_FIRST
|
||||
/* Byteswap ROM to optimize 16-bit access */
|
||||
int i;
|
||||
|
||||
Reference in New Issue
Block a user