diff --git a/Cocoa/Document.h b/Cocoa/Document.h index b651646..e0a3aa3 100644 --- a/Cocoa/Document.h +++ b/Cocoa/Document.h @@ -39,6 +39,14 @@ @property (nonatomic, strong) IBOutlet GBCheatWindowController *cheatWindowController; @property (nonatomic, readonly) Document *partner; @property (nonatomic, readonly) bool isSlave; +@property (strong) IBOutlet NSView *gbsPlayerView; +@property (strong) IBOutlet NSTextField *gbsTitle; +@property (strong) IBOutlet NSTextField *gbsAuthor; +@property (strong) IBOutlet NSTextField *gbsCopyright; +@property (strong) IBOutlet NSPopUpButton *gbsTracks; +@property (strong) IBOutlet NSButton *gbsPlayPauseButton; +@property (strong) IBOutlet NSButton *gbsRewindButton; +@property (strong) IBOutlet NSSegmentedControl *gbsNextPrevButton; -(uint8_t) readMemory:(uint16_t) addr; -(void) writeMemory:(uint16_t) addr value:(uint8_t)value; diff --git a/Cocoa/Document.m b/Cocoa/Document.m index e7812d4..a5d291e 100644 --- a/Cocoa/Document.m +++ b/Cocoa/Document.m @@ -513,6 +513,7 @@ static unsigned *multiplication_table_for_frequency(unsigned frequency) - (void) start { + self.gbsPlayPauseButton.state = true; self.view.mouseHidingEnabled = (self.mainWindow.styleMask & NSWindowStyleMaskFullScreen) != 0; if (master) { [master start]; @@ -524,6 +525,7 @@ static unsigned *multiplication_table_for_frequency(unsigned frequency) - (void) stop { + self.gbsPlayPauseButton.state = false; if (master) { if (!master->running) return; GB_debugger_set_disabled(&gb, true); @@ -834,14 +836,86 @@ static unsigned *multiplication_table_for_frequency(unsigned frequency) return YES; } +- (IBAction)changeGBSTrack:(id)sender +{ + [self performAtomicBlock:^{ + GB_gbs_switch_track(&gb, self.gbsTracks.indexOfSelectedItem); + }]; +} +- (IBAction)gbsNextPrevPushed:(id)sender +{ + if (self.gbsNextPrevButton.selectedSegment == 0) { + // Previous + if (self.gbsTracks.indexOfSelectedItem == 0) { + [self.gbsTracks selectItemAtIndex:self.gbsTracks.numberOfItems - 1]; + } + else { + [self.gbsTracks selectItemAtIndex:self.gbsTracks.indexOfSelectedItem - 1]; + } + } + else { + // Next + if (self.gbsTracks.indexOfSelectedItem == self.gbsTracks.numberOfItems - 1) { + [self.gbsTracks selectItemAtIndex: 0]; + } + else { + [self.gbsTracks selectItemAtIndex:self.gbsTracks.indexOfSelectedItem + 1]; + } + } + [self changeGBSTrack:sender]; +} + +- (void)prepareGBSInterface: (GB_gbs_info_t *)info +{ + GB_set_rendering_disabled(&gb, true); + _view = nil; + for (NSView *view in _mainWindow.contentView.subviews) { + [view removeFromSuperview]; + } + [[NSBundle mainBundle] loadNibNamed:@"GBS" owner:self topLevelObjects:nil]; + [_mainWindow setContentSize:self.gbsPlayerView.bounds.size]; + _mainWindow.styleMask &= ~NSWindowStyleMaskResizable; + [_mainWindow.contentView addSubview:self.gbsPlayerView]; + + self.gbsTitle.stringValue = [NSString stringWithCString:info->title encoding:NSISOLatin1StringEncoding] ?: @"GBS Player"; + self.gbsAuthor.stringValue = [NSString stringWithCString:info->author encoding:NSISOLatin1StringEncoding] ?: @"Unknown Composer"; + NSString *copyright = [NSString stringWithCString:info->copyright encoding:NSISOLatin1StringEncoding]; + if (copyright) { + copyright = [@"©" stringByAppendingString:copyright]; + } + self.gbsCopyright.stringValue = copyright ?: @"Missing copyright information"; + for (unsigned i = 0; i < info->track_count; i++) { + [self.gbsTracks addItemWithTitle:[NSString stringWithFormat:@"Track %u", i + 1]]; + } + [self.gbsTracks selectItemAtIndex:info->first_track]; + self.gbsPlayPauseButton.image.template = true; + self.gbsPlayPauseButton.alternateImage.template = true; + self.gbsRewindButton.image.template = true; + for (unsigned i = 0; i < 2; i++) { + [self.gbsNextPrevButton imageForSegment:i].template = true; + } + + if (!self.audioClient.isPlaying) { + [self.audioClient start]; + } + + if (@available(macOS 10.10, *)) { + _mainWindow.titlebarAppearsTransparent = true; + } +} + - (void) loadROM { NSString *rom_warnings = [self captureOutputForBlock:^{ GB_debugger_clear_symbols(&gb); - if ([[self.fileType pathExtension] isEqualToString:@"isx"]) { + if ([[[self.fileType pathExtension] lowercaseString] isEqualToString:@"isx"]) { GB_load_isx(&gb, self.fileURL.path.UTF8String); GB_load_battery(&gb, [[self.fileURL URLByDeletingPathExtension] URLByAppendingPathExtension:@"ram"].path.UTF8String); - + } + else if ([[[self.fileType pathExtension] lowercaseString] isEqualToString:@"gbs"]) { + GB_gbs_info_t info; + GB_load_gbs(&gb, self.fileURL.path.UTF8String, &info); + [self prepareGBSInterface:&info]; } else { GB_load_rom(&gb, [self.fileURL.path UTF8String]); @@ -861,8 +935,10 @@ static unsigned *multiplication_table_for_frequency(unsigned frequency) - (void)close { [self disconnectLinkCable]; - [[NSUserDefaults standardUserDefaults] setInteger:self.mainWindow.frame.size.width forKey:@"LastWindowWidth"]; - [[NSUserDefaults standardUserDefaults] setInteger:self.mainWindow.frame.size.height forKey:@"LastWindowHeight"]; + if (!self.gbsPlayerView) { + [[NSUserDefaults standardUserDefaults] setInteger:self.mainWindow.frame.size.width forKey:@"LastWindowWidth"]; + [[NSUserDefaults standardUserDefaults] setInteger:self.mainWindow.frame.size.height forKey:@"LastWindowHeight"]; + } [self stop]; [self.consoleWindow close]; [self.memoryWindow close]; diff --git a/Cocoa/GBS.xib b/Cocoa/GBS.xib new file mode 100644 index 0000000..6d5ba01 --- /dev/null +++ b/Cocoa/GBS.xib @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Cocoa/Info.plist b/Cocoa/Info.plist index 4d42b46..5e409c9 100644 --- a/Cocoa/Info.plist +++ b/Cocoa/Info.plist @@ -51,7 +51,7 @@ CFBundleTypeExtensions - gbc + isx CFBundleTypeIconFile ColorCartridge @@ -68,6 +68,26 @@ NSDocumentClass Document + + CFBundleTypeExtensions + + gbs + + CFBundleTypeIconFile + ColorCartridge + CFBundleTypeName + Game Boy Sound File + CFBundleTypeRole + Viewer + LSItemContentTypes + + com.github.liji32.sameboy.gbs + + LSTypeIsPackage + 0 + NSDocumentClass + Document + CFBundleExecutable SameBoy @@ -156,6 +176,25 @@ + + UTTypeConformsTo + + public.data + + UTTypeDescription + Game Boy Sound File + UTTypeIconFile + ColorCartridge + UTTypeIdentifier + com.github.liji32.sameboy.gbs + UTTypeTagSpecification + + public.filename-extension + + gbs + + + NSCameraUsageDescription SameBoy needs to access your camera to emulate the Game Boy Camera diff --git a/Cocoa/Next.png b/Cocoa/Next.png new file mode 100644 index 0000000..cd9a4c3 Binary files /dev/null and b/Cocoa/Next.png differ diff --git a/Cocoa/Next@2x.png b/Cocoa/Next@2x.png new file mode 100644 index 0000000..1debb1d Binary files /dev/null and b/Cocoa/Next@2x.png differ diff --git a/Cocoa/Pause.png b/Cocoa/Pause.png new file mode 100644 index 0000000..2bb380b Binary files /dev/null and b/Cocoa/Pause.png differ diff --git a/Cocoa/Pause@2x.png b/Cocoa/Pause@2x.png new file mode 100644 index 0000000..36b6da0 Binary files /dev/null and b/Cocoa/Pause@2x.png differ diff --git a/Cocoa/Play.png b/Cocoa/Play.png new file mode 100644 index 0000000..3f87092 Binary files /dev/null and b/Cocoa/Play.png differ diff --git a/Cocoa/Play@2x.png b/Cocoa/Play@2x.png new file mode 100644 index 0000000..0de0553 Binary files /dev/null and b/Cocoa/Play@2x.png differ diff --git a/Cocoa/Previous.png b/Cocoa/Previous.png new file mode 100644 index 0000000..cc91221 Binary files /dev/null and b/Cocoa/Previous.png differ diff --git a/Cocoa/Previous@2x.png b/Cocoa/Previous@2x.png new file mode 100644 index 0000000..77b0157 Binary files /dev/null and b/Cocoa/Previous@2x.png differ diff --git a/Cocoa/Rewind.png b/Cocoa/Rewind.png new file mode 100644 index 0000000..999f358 Binary files /dev/null and b/Cocoa/Rewind.png differ diff --git a/Cocoa/Rewind@2x.png b/Cocoa/Rewind@2x.png new file mode 100644 index 0000000..d845b54 Binary files /dev/null and b/Cocoa/Rewind@2x.png differ diff --git a/Core/display.c b/Core/display.c index 2545e3b..03d600d 100644 --- a/Core/display.c +++ b/Core/display.c @@ -126,7 +126,7 @@ static void display_vblank(GB_gameboy_t *gb) bool is_ppu_stopped = !GB_is_cgb(gb) && gb->stopped && gb->io_registers[GB_IO_LCDC] & 0x80; - if (!gb->disable_rendering && ((!(gb->io_registers[GB_IO_LCDC] & 0x80) || is_ppu_stopped) || gb->cgb_repeated_a_frame)) { + if (!gb->disable_rendering && ((!(gb->io_registers[GB_IO_LCDC] & 0x80) || is_ppu_stopped) || gb->cgb_repeated_a_frame)) { /* LCD is off, set screen to white or black (if LCD is on in stop mode) */ if (!GB_is_sgb(gb)) { uint32_t color = 0; @@ -153,7 +153,7 @@ static void display_vblank(GB_gameboy_t *gb) } } - if (gb->border_mode == GB_BORDER_ALWAYS && !GB_is_sgb(gb)) { + if (!gb->disable_rendering && gb->border_mode == GB_BORDER_ALWAYS && !GB_is_sgb(gb)) { GB_borrow_sgb_border(gb); uint32_t border_colors[16 * 4];