git.y1.nz

SameBoy

Accurate GB/GBC emulator
download: https://git.y1.nz/archives/sameboy.tar.gz
README | Files | Log | Refs | LICENSE

commit d85a2614e5fda84316b49cf269d37f2231475d47
parent 2f4a6f231ec40ecfc0ab7df0a09eb932e7ccddec
Author: Lior Halphon <LIJI32@gmail.com>
Date:   Sat,  3 Jan 2026 12:28:24 +0200

Add the ability to modify and save the ROM via the Cocoa memory view. Closes #685

Diffstat:
MCocoa/Document.h1+
MCocoa/Document.m61+++++++++++++++++++++++++++++++++++++++++++------------------
MCocoa/GBMemoryByteArray.m12+++++++++++-
MCocoa/MainMenu.xib16++++++++++++++++
4 files changed, 71 insertions(+), 19 deletions(-)

diff --git a/Cocoa/Document.h b/Cocoa/Document.h @@ -94,5 +94,6 @@ enum model { - (int)loadStateFile:(const char *)path noErrorOnNotFound:(bool)noErrorOnFileNotFound; - (NSString *)captureOutputForBlock: (void (^)())block; - (NSFont *)debuggerFontOfSize:(unsigned)size; +- (void)setROMModified; @end diff --git a/Cocoa/Document.m b/Cocoa/Document.m @@ -120,6 +120,8 @@ __weak NSThread *_emulationThread; GBCheatSearchController *_cheatSearchController; + + bool _romModified; } static void boot_rom_load(GB_gameboy_t *gb, GB_boot_rom_t type) @@ -1095,11 +1097,6 @@ again:; self.memoryBankItem.enabled = false; } -+ (BOOL)autosavesInPlace -{ - return true; -} - - (NSString *)windowNibName { // Override returning the nib file name of the document @@ -1295,20 +1292,22 @@ static bool is_path_writeable(const char *path) } NSString *rom_warnings = [self captureOutputForBlock:^{ - GB_debugger_clear_symbols(&_gb); - if ([[[fileName pathExtension] lowercaseString] isEqualToString:@"isx"]) { - ret = GB_load_isx(&_gb, fileName.UTF8String); - if (!self.isCartContainer) { - GB_load_battery(&_gb, [[self.fileURL URLByDeletingPathExtension] URLByAppendingPathExtension:@"ram"].path.UTF8String); + if (!_romModified) { + GB_debugger_clear_symbols(&_gb); + if ([[[fileName pathExtension] lowercaseString] isEqualToString:@"isx"]) { + ret = GB_load_isx(&_gb, fileName.UTF8String); + if (!self.isCartContainer) { + GB_load_battery(&_gb, [[self.fileURL URLByDeletingPathExtension] URLByAppendingPathExtension:@"ram"].path.UTF8String); + } + } + else if ([[[fileName pathExtension] lowercaseString] isEqualToString:@"gbs"]) { + __block GB_gbs_info_t info; + ret = GB_load_gbs(&_gb, fileName.UTF8String, &info); + [self prepareGBSInterface:&info]; + } + else { + ret = GB_load_rom(&_gb, [fileName UTF8String]); } - } - else if ([[[fileName pathExtension] lowercaseString] isEqualToString:@"gbs"]) { - __block GB_gbs_info_t info; - ret = GB_load_gbs(&_gb, fileName.UTF8String, &info); - [self prepareGBSInterface:&info]; - } - else { - ret = GB_load_rom(&_gb, [fileName UTF8String]); } if (GB_save_battery_size(&_gb)) { if (!is_path_writeable(self.savPath.UTF8String)) { @@ -1468,6 +1467,12 @@ static bool is_path_writeable(const char *path) else if ([anItem action] == @selector(reloadROM:)) { return !_gbsTracks; } + else if ([anItem action] == @selector(saveDocument:)) { + return _romModified; + } + else if ([anItem action] == @selector(saveDocumentAs:)) { + return _romModified && !self.isCartContainer; + } return [super validateUserInterfaceItem:anItem]; } @@ -2915,6 +2920,8 @@ enum GBWindowResizeAction [self stop]; } + _romModified = false; + [self updateChangeCount:NSChangeCleared]; [self loadROM]; if (wasRunning) { @@ -2969,6 +2976,24 @@ enum GBWindowResizeAction [self queueDebuggerCommand:sender.alternateTitle]; } +- (void)setROMModified +{ + _romModified = true; + [self updateChangeCount:NSChangeDone]; +} + +- (BOOL)writeToFile:(NSString *)path ofType:(NSString *)type +{ + if ([type isEqualToString:@"Game Boy Cartridge"]) { + if (![[NSFileManager defaultManager] copyItemAtPath:self.fileName toPath:path error:nil]) return false; + path = self.romPath; + if (!path) return false; + } + size_t size; + uint8_t *data = GB_get_direct_access(&_gb, GB_DIRECT_ACCESS_ROM, &size, NULL); + return [[NSData dataWithBytesNoCopy:data length:size freeWhenDone:false] writeToFile:path atomically:true]; +} + + (NSArray<NSString *> *)readableTypes { NSMutableSet *set = [NSMutableSet setWithArray:[super readableTypes]]; diff --git a/Cocoa/GBMemoryByteArray.m b/Cocoa/GBMemoryByteArray.m @@ -179,7 +179,17 @@ case 0x5: case 0x6: case 0x7: { - return; // ROM not writeable + uint16_t bank; + size_t size; + uint8_t *data = GB_get_direct_access(gb, range.location < 0x4000? GB_DIRECT_ACCESS_ROM0 : GB_DIRECT_ACCESS_ROM, &size, &bank); + if (_mode != GBMemoryEntireSpace && range.location >= 0x4000) { + bank = self.selectedBank & (size / 0x4000 - 1); + } + uint8_t sliceData[range.length]; + [slice copyBytes:sliceData range:HFRangeMake(0, range.length)]; + memcpy(data + bank * 0x4000 + (range.location & 0x3FFF), sliceData, range.length); + [_document setROMModified]; + break; } case 0x8: case 0x9: { diff --git a/Cocoa/MainMenu.xib b/Cocoa/MainMenu.xib @@ -113,6 +113,21 @@ <action selector="performClose:" target="-1" id="HmO-Ls-i7Q"/> </connections> </menuItem> + <menuItem title="Save ROM Modifications" secondaryImage="square.and.arrow.down" catalog="system" keyEquivalent="s" id="Qru-qh-5Z6"> + <connections> + <action selector="saveDocument:" target="-1" id="pnc-7c-MfV"/> + </connections> + </menuItem> + <menuItem title="Save ROM Modifications As…" secondaryImage="square.and.arrow.down.on.square" catalog="system" keyEquivalent="S" id="VfB-dj-N2W"> + <connections> + <action selector="saveDocumentAs:" target="-1" id="WoY-vv-bxg"/> + </connections> + </menuItem> + <menuItem title="Reload ROM" secondaryImage="arrow.trianglehead.2.counterclockwise.rotate.90" catalog="system" keyEquivalent="R" id="taf-tK-jqI"> + <connections> + <action selector="reloadROM:" target="-1" id="Uyv-4C-cYd"/> + </connections> + </menuItem> </items> </menu> </menuItem> @@ -636,6 +651,7 @@ <image name="speaker.slash" catalog="system" width="14" height="16"/> <image name="speaker.wave.3" catalog="system" width="22" height="15"/> <image name="square.and.arrow.down" catalog="system" width="15" height="17"/> + <image name="square.and.arrow.down.on.square" catalog="system" width="18" height="20"/> <image name="square.and.arrow.up" catalog="system" width="15" height="18"/> <image name="text.page.badge.magnifyingglass" catalog="system" width="15" height="18"/> <image name="wrench.and.screwdriver" catalog="system" width="19" height="18"/>

This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.