Add Cartridge Instances – allow multiple saves without multiple ROM copies
This commit is contained in:
parent
641f26e13e
commit
2c635c7a87
157
Cocoa/Document.m
157
Cocoa/Document.m
@ -14,6 +14,30 @@
|
|||||||
#import "GBObjectView.h"
|
#import "GBObjectView.h"
|
||||||
#import "GBPaletteView.h"
|
#import "GBPaletteView.h"
|
||||||
|
|
||||||
|
@implementation NSString (relativePath)
|
||||||
|
|
||||||
|
- (NSString *)pathRelativeToDirectory:(NSString *)directory
|
||||||
|
{
|
||||||
|
NSMutableArray<NSString *> *baseComponents = [[directory pathComponents] mutableCopy];
|
||||||
|
NSMutableArray<NSString *> *selfComponents = [[self pathComponents] mutableCopy];
|
||||||
|
|
||||||
|
while (baseComponents.count) {
|
||||||
|
if (![baseComponents.firstObject isEqualToString:selfComponents.firstObject]) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
[baseComponents removeObjectAtIndex:0];
|
||||||
|
[selfComponents removeObjectAtIndex:0];
|
||||||
|
}
|
||||||
|
while (baseComponents.count) {
|
||||||
|
[baseComponents removeObjectAtIndex:0];
|
||||||
|
[selfComponents insertObject:@".." atIndex:0];
|
||||||
|
}
|
||||||
|
return [selfComponents componentsJoinedByString:@"/"];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
#define GB_MODEL_PAL_BIT_OLD 0x1000
|
#define GB_MODEL_PAL_BIT_OLD 0x1000
|
||||||
|
|
||||||
/* Todo: The general Objective-C coding style conflicts with SameBoy's. This file needs a cleanup. */
|
/* Todo: The general Objective-C coding style conflicts with SameBoy's. This file needs a cleanup. */
|
||||||
@ -499,8 +523,8 @@ static unsigned *multiplication_table_for_frequency(unsigned frequency)
|
|||||||
[_audioClient stop];
|
[_audioClient stop];
|
||||||
_audioClient = nil;
|
_audioClient = nil;
|
||||||
self.view.mouseHidingEnabled = false;
|
self.view.mouseHidingEnabled = false;
|
||||||
GB_save_battery(&gb, [[[self.fileURL URLByDeletingPathExtension] URLByAppendingPathExtension:@"sav"].path UTF8String]);
|
GB_save_battery(&gb, self.savPath.UTF8String);
|
||||||
GB_save_cheats(&gb, [[[self.fileURL URLByDeletingPathExtension] URLByAppendingPathExtension:@"cht"].path UTF8String]);
|
GB_save_cheats(&gb, self.chtPath.UTF8String);
|
||||||
unsigned time_to_alarm = GB_time_to_alarm(&gb);
|
unsigned time_to_alarm = GB_time_to_alarm(&gb);
|
||||||
|
|
||||||
if (time_to_alarm) {
|
if (time_to_alarm) {
|
||||||
@ -943,28 +967,107 @@ static unsigned *multiplication_table_for_frequency(unsigned frequency)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (bool)isCartContainer
|
||||||
|
{
|
||||||
|
return [self.fileName.pathExtension.lowercaseString isEqualToString:@"gbcart"];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)savPath
|
||||||
|
{
|
||||||
|
if (self.isCartContainer) {
|
||||||
|
return [self.fileName stringByAppendingPathComponent:@"battery.sav"];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [[self.fileURL URLByDeletingPathExtension] URLByAppendingPathExtension:@"sav"].path;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)chtPath
|
||||||
|
{
|
||||||
|
if (self.isCartContainer) {
|
||||||
|
return [self.fileName stringByAppendingPathComponent:@"cheats.cht"];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [[self.fileURL URLByDeletingPathExtension] URLByAppendingPathExtension:@"cht"].path;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)saveStatePath:(unsigned)index
|
||||||
|
{
|
||||||
|
if (self.isCartContainer) {
|
||||||
|
return [self.fileName stringByAppendingPathComponent:[NSString stringWithFormat:@"state.s%u", index]];
|
||||||
|
}
|
||||||
|
return [[self.fileURL URLByDeletingPathExtension] URLByAppendingPathExtension:[NSString stringWithFormat:@"s%u", index]].path;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)romPath
|
||||||
|
{
|
||||||
|
NSString *fileName = self.fileName;
|
||||||
|
if (self.isCartContainer) {
|
||||||
|
NSArray *paths = [[NSString stringWithContentsOfFile:[fileName stringByAppendingPathComponent:@"rom.gbl"]
|
||||||
|
encoding:NSUTF8StringEncoding
|
||||||
|
error:nil] componentsSeparatedByString:@"\n"];
|
||||||
|
fileName = nil;
|
||||||
|
bool needsRebuild = false;
|
||||||
|
for (NSString *path in paths) {
|
||||||
|
NSURL *url = [NSURL URLWithString:path relativeToURL:self.fileURL];
|
||||||
|
if ([[NSFileManager defaultManager] fileExistsAtPath:url.path]) {
|
||||||
|
if (fileName && ![fileName isEqualToString:url.path]) {
|
||||||
|
needsRebuild = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
fileName = url.path;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
needsRebuild = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (fileName && needsRebuild) {
|
||||||
|
[[NSString stringWithFormat:@"%@\n%@\n%@",
|
||||||
|
[fileName pathRelativeToDirectory:self.fileName],
|
||||||
|
fileName,
|
||||||
|
[[NSURL fileURLWithPath:fileName].fileReferenceURL.absoluteString substringFromIndex:strlen("file://")]]
|
||||||
|
writeToFile:[self.fileName stringByAppendingPathComponent:@"rom.gbl"]
|
||||||
|
atomically:false
|
||||||
|
encoding:NSUTF8StringEncoding
|
||||||
|
error:nil];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fileName;
|
||||||
|
}
|
||||||
|
|
||||||
- (int) loadROM
|
- (int) loadROM
|
||||||
{
|
{
|
||||||
__block int ret = 0;
|
__block int ret = 0;
|
||||||
|
NSString *fileName = self.romPath;
|
||||||
|
if (!fileName) {
|
||||||
|
NSAlert *alert = [[NSAlert alloc] init];
|
||||||
|
[alert setMessageText:@"Could not locate the ROM referenced by this Game Boy Cartridge"];
|
||||||
|
[alert setAlertStyle:NSAlertStyleCritical];
|
||||||
|
[alert runModal];
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
NSString *rom_warnings = [self captureOutputForBlock:^{
|
NSString *rom_warnings = [self captureOutputForBlock:^{
|
||||||
GB_debugger_clear_symbols(&gb);
|
GB_debugger_clear_symbols(&gb);
|
||||||
if ([[[self.fileType pathExtension] lowercaseString] isEqualToString:@"isx"]) {
|
if ([[[fileName pathExtension] lowercaseString] isEqualToString:@"isx"]) {
|
||||||
ret = GB_load_isx(&gb, self.fileURL.path.UTF8String);
|
ret = GB_load_isx(&gb, fileName.UTF8String);
|
||||||
GB_load_battery(&gb, [[self.fileURL URLByDeletingPathExtension] URLByAppendingPathExtension:@"ram"].path.UTF8String);
|
if (!self.isCartContainer) {
|
||||||
|
GB_load_battery(&gb, [[self.fileURL URLByDeletingPathExtension] URLByAppendingPathExtension:@"ram"].path.UTF8String);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if ([[[self.fileType pathExtension] lowercaseString] isEqualToString:@"gbs"]) {
|
else if ([[[fileName pathExtension] lowercaseString] isEqualToString:@"gbs"]) {
|
||||||
__block GB_gbs_info_t info;
|
__block GB_gbs_info_t info;
|
||||||
ret = GB_load_gbs(&gb, self.fileURL.path.UTF8String, &info);
|
ret = GB_load_gbs(&gb, fileName.UTF8String, &info);
|
||||||
[self prepareGBSInterface:&info];
|
[self prepareGBSInterface:&info];
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
ret = GB_load_rom(&gb, [self.fileURL.path UTF8String]);
|
ret = GB_load_rom(&gb, [fileName UTF8String]);
|
||||||
}
|
}
|
||||||
GB_load_battery(&gb, [[self.fileURL URLByDeletingPathExtension] URLByAppendingPathExtension:@"sav"].path.UTF8String);
|
GB_load_battery(&gb, self.savPath.UTF8String);
|
||||||
GB_load_cheats(&gb, [[self.fileURL URLByDeletingPathExtension] URLByAppendingPathExtension:@"cht"].path.UTF8String);
|
GB_load_cheats(&gb, self.chtPath.UTF8String);
|
||||||
[self.cheatWindowController cheatsUpdated];
|
[self.cheatWindowController cheatsUpdated];
|
||||||
GB_debugger_load_symbol_file(&gb, [[[NSBundle mainBundle] pathForResource:@"registers" ofType:@"sym"] UTF8String]);
|
GB_debugger_load_symbol_file(&gb, [[[NSBundle mainBundle] pathForResource:@"registers" ofType:@"sym"] UTF8String]);
|
||||||
GB_debugger_load_symbol_file(&gb, [[self.fileURL URLByDeletingPathExtension] URLByAppendingPathExtension:@"sym"].path.UTF8String);
|
GB_debugger_load_symbol_file(&gb, [[fileName stringByDeletingPathExtension] stringByAppendingPathExtension:@"sym"].UTF8String);
|
||||||
}];
|
}];
|
||||||
if (ret) {
|
if (ret) {
|
||||||
NSAlert *alert = [[NSAlert alloc] init];
|
NSAlert *alert = [[NSAlert alloc] init];
|
||||||
@ -1295,7 +1398,7 @@ static unsigned *multiplication_table_for_frequency(unsigned frequency)
|
|||||||
{
|
{
|
||||||
bool __block success = false;
|
bool __block success = false;
|
||||||
[self performAtomicBlock:^{
|
[self performAtomicBlock:^{
|
||||||
success = GB_save_state(&gb, [[self.fileURL URLByDeletingPathExtension] URLByAppendingPathExtension:[NSString stringWithFormat:@"s%ld", (long)[sender tag] ]].path.UTF8String) == 0;
|
success = GB_save_state(&gb, [self saveStatePath:[sender tag]].UTF8String) == 0;
|
||||||
}];
|
}];
|
||||||
|
|
||||||
if (!success) {
|
if (!success) {
|
||||||
@ -1333,8 +1436,8 @@ static unsigned *multiplication_table_for_frequency(unsigned frequency)
|
|||||||
|
|
||||||
- (IBAction)loadState:(id)sender
|
- (IBAction)loadState:(id)sender
|
||||||
{
|
{
|
||||||
int ret = [self loadStateFile:[[self.fileURL URLByDeletingPathExtension] URLByAppendingPathExtension:[NSString stringWithFormat:@"s%ld", (long)[sender tag]]].path.UTF8String noErrorOnNotFound:true];
|
int ret = [self loadStateFile:[self saveStatePath:[sender tag]].UTF8String noErrorOnNotFound:true];
|
||||||
if (ret == ENOENT) {
|
if (ret == ENOENT && !self.isCartContainer) {
|
||||||
[self loadStateFile:[[self.fileURL URLByDeletingPathExtension] URLByAppendingPathExtension:[NSString stringWithFormat:@"sn%ld", (long)[sender tag]]].path.UTF8String noErrorOnNotFound:false];
|
[self loadStateFile:[[self.fileURL URLByDeletingPathExtension] URLByAppendingPathExtension:[NSString stringWithFormat:@"sn%ld", (long)[sender tag]]].path.UTF8String noErrorOnNotFound:false];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2260,4 +2363,30 @@ static unsigned *multiplication_table_for_frequency(unsigned frequency)
|
|||||||
GB_set_object_rendering_disabled(&gb, !GB_is_object_rendering_disabled(&gb));
|
GB_set_object_rendering_disabled(&gb, !GB_is_object_rendering_disabled(&gb));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (IBAction)newCartridgeInstance:(id)sender
|
||||||
|
{
|
||||||
|
bool shouldResume = running;
|
||||||
|
[self stop];
|
||||||
|
NSSavePanel *savePanel = [NSSavePanel savePanel];
|
||||||
|
[savePanel setAllowedFileTypes:@[@"gbcart"]];
|
||||||
|
[savePanel beginSheetModalForWindow:self.mainWindow completionHandler:^(NSInteger result) {
|
||||||
|
if (result == NSModalResponseOK) {
|
||||||
|
[savePanel orderOut:self];
|
||||||
|
NSString *romPath = self.romPath;
|
||||||
|
[[NSFileManager defaultManager] trashItemAtURL:savePanel.URL resultingItemURL:nil error:nil];
|
||||||
|
[[NSFileManager defaultManager] createDirectoryAtURL:savePanel.URL withIntermediateDirectories:false attributes:nil error:nil];
|
||||||
|
[[NSString stringWithFormat:@"%@\n%@\n%@",
|
||||||
|
[romPath pathRelativeToDirectory:savePanel.URL.path],
|
||||||
|
romPath,
|
||||||
|
[[NSURL fileURLWithPath:romPath].fileReferenceURL.absoluteString substringFromIndex:strlen("file://")]
|
||||||
|
] writeToURL:[savePanel.URL URLByAppendingPathComponent:@"rom.gbl"] atomically:false encoding:NSUTF8StringEncoding error:nil];
|
||||||
|
[[NSDocumentController sharedDocumentController] openDocumentWithContentsOfURL:savePanel.URL display:true completionHandler:nil];
|
||||||
|
}
|
||||||
|
if (shouldResume) {
|
||||||
|
[self start];
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -88,6 +88,24 @@
|
|||||||
<key>NSDocumentClass</key>
|
<key>NSDocumentClass</key>
|
||||||
<string>Document</string>
|
<string>Document</string>
|
||||||
</dict>
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleTypeExtensions</key>
|
||||||
|
<array>
|
||||||
|
<string>gbcart</string>
|
||||||
|
</array>
|
||||||
|
<key>CFBundleTypeIconFile</key>
|
||||||
|
<string>ColorCartridge</string>
|
||||||
|
<key>CFBundleTypeName</key>
|
||||||
|
<string>Game Boy Cartridge</string>
|
||||||
|
<key>CFBundleTypeRole</key>
|
||||||
|
<string>Viewer</string>
|
||||||
|
<key>LSItemContentTypes</key>
|
||||||
|
<array/>
|
||||||
|
<key>LSTypeIsPackage</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>NSDocumentClass</key>
|
||||||
|
<string>Document</string>
|
||||||
|
</dict>
|
||||||
</array>
|
</array>
|
||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key>
|
||||||
<string>SameBoy</string>
|
<string>SameBoy</string>
|
||||||
|
@ -91,6 +91,12 @@
|
|||||||
</menu>
|
</menu>
|
||||||
</menuItem>
|
</menuItem>
|
||||||
<menuItem isSeparatorItem="YES" id="m54-Is-iLE"/>
|
<menuItem isSeparatorItem="YES" id="m54-Is-iLE"/>
|
||||||
|
<menuItem title="New Cartridge Instance…" keyEquivalent="n" id="Vld-be-NZu">
|
||||||
|
<connections>
|
||||||
|
<action selector="newCartridgeInstance:" target="-1" id="GJc-xU-ZEr"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem isSeparatorItem="YES" id="vQH-Yd-TH4"/>
|
||||||
<menuItem title="Close" keyEquivalent="w" id="DVo-aG-piG">
|
<menuItem title="Close" keyEquivalent="w" id="DVo-aG-piG">
|
||||||
<connections>
|
<connections>
|
||||||
<action selector="performClose:" target="-1" id="HmO-Ls-i7Q"/>
|
<action selector="performClose:" target="-1" id="HmO-Ls-i7Q"/>
|
||||||
|
Loading…
Reference in New Issue
Block a user