Allow drag&drop of state files

This commit is contained in:
Lior Halphon 2021-04-14 15:20:01 +03:00
parent c1509b6339
commit 8a84a5897e
9 changed files with 103 additions and 14 deletions

View File

@ -44,5 +44,6 @@
-(void) writeMemory:(uint16_t) addr value:(uint8_t)value; -(void) writeMemory:(uint16_t) addr value:(uint8_t)value;
-(void) performAtomicBlock: (void (^)())block; -(void) performAtomicBlock: (void (^)())block;
-(void) connectLinkCable:(NSMenuItem *)sender; -(void) connectLinkCable:(NSMenuItem *)sender;
- (bool)loadStateFile:(const char *)path;
@end @end

View File

@ -1172,12 +1172,12 @@ static unsigned *multiplication_table_for_frequency(unsigned frequency)
} }
} }
- (IBAction)loadState:(id)sender - (bool)loadStateFile:(const char *)path
{ {
bool __block success = false; bool __block success = false;
NSString *error = NSString *error =
[self captureOutputForBlock:^{ [self captureOutputForBlock:^{
success = GB_load_state(&gb, [[self.fileURL URLByDeletingPathExtension] URLByAppendingPathExtension:[NSString stringWithFormat:@"s%ld", (long)[sender tag] ]].path.UTF8String) == 0; success = GB_load_state(&gb, path) == 0;
}]; }];
if (!success) { if (!success) {
@ -1186,6 +1186,12 @@ static unsigned *multiplication_table_for_frequency(unsigned frequency)
if (error) { if (error) {
[GBWarningPopover popoverWithContents:error onWindow:self.mainWindow]; [GBWarningPopover popoverWithContents:error onWindow:self.mainWindow];
} }
return success;
}
- (IBAction)loadState:(id)sender
{
[self loadStateFile:[[self.fileURL URLByDeletingPathExtension] URLByAppendingPathExtension:[NSString stringWithFormat:@"s%ld", (long)[sender tag] ]].path.UTF8String];
} }
- (IBAction)clearConsole:(id)sender - (IBAction)clearConsole:(id)sender

View File

@ -142,6 +142,8 @@ static const uint8_t workboy_vk_to_key[] = {
- (void) _init - (void) _init
{ {
[self registerForDraggedTypes:[NSArray arrayWithObjects: NSPasteboardTypeFileURL, nil]];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(ratioKeepingChanged) name:@"GBAspectChanged" object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(ratioKeepingChanged) name:@"GBAspectChanged" object:nil];
tracking_area = [ [NSTrackingArea alloc] initWithRect:(NSRect){} tracking_area = [ [NSTrackingArea alloc] initWithRect:(NSRect){}
options:NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways | NSTrackingInVisibleRect options:NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways | NSTrackingInVisibleRect
@ -626,4 +628,29 @@ static const uint8_t workboy_vk_to_key[] = {
return image_buffers[(current_buffer + 2) % self.numberOfBuffers]; return image_buffers[(current_buffer + 2) % self.numberOfBuffers];
} }
-(NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender
{
NSPasteboard *pboard = [sender draggingPasteboard];
if ( [[pboard types] containsObject:NSURLPboardType] ) {
NSURL *fileURL = [NSURL URLFromPasteboard:pboard];
if (GB_is_stave_state(fileURL.fileSystemRepresentation)) {
return NSDragOperationGeneric;
}
}
return NSDragOperationNone;
}
-(BOOL)performDragOperation:(id<NSDraggingInfo>)sender
{
NSPasteboard *pboard = [sender draggingPasteboard];
if ( [[pboard types] containsObject:NSURLPboardType] ) {
NSURL *fileURL = [NSURL URLFromPasteboard:pboard];
return [_document loadStateFile:fileURL.fileSystemRepresentation];
}
return false;
}
@end @end

View File

@ -19,12 +19,6 @@
#endif #endif
static inline uint32_t state_magic(void)
{
if (sizeof(bool) == 1) return 'SAME';
return 'S4ME';
}
void GB_attributed_logv(GB_gameboy_t *gb, GB_log_attributes attributes, const char *fmt, va_list args) void GB_attributed_logv(GB_gameboy_t *gb, GB_log_attributes attributes, const char *fmt, va_list args)
{ {
char *string = NULL; char *string = NULL;

View File

@ -1170,3 +1170,36 @@ int GB_load_state_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t le
return load_state_internal(gb, &file); return load_state_internal(gb, &file);
} }
bool GB_is_stave_state(const char *path)
{
bool ret = false;
FILE *f = fopen(path, "rb");
if (!f) return false;
uint32_t magic = 0;
fread(&magic, sizeof(magic), 1, f);
if (magic == state_magic()) {
ret = true;
goto exit;
}
// Legacy corrupted Windows save state
if (magic == 0) {
fread(&magic, sizeof(magic), 1, f);
if (magic == state_magic()) {
ret = true;
goto exit;
}
}
fseek(f, -sizeof(magic), SEEK_END);
fread(&magic, sizeof(magic), 1, f);
if (magic == BE32('BESS')) {
ret = true;
}
exit:
fclose(f);
return ret;
}

View File

@ -27,8 +27,14 @@ void GB_save_state_to_buffer(GB_gameboy_t *gb, uint8_t *buffer);
int GB_load_state(GB_gameboy_t *gb, const char *path); int GB_load_state(GB_gameboy_t *gb, const char *path);
int GB_load_state_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t length); int GB_load_state_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t length);
bool GB_is_stave_state(const char *path);
#ifdef GB_INTERNAL #ifdef GB_INTERNAL
static inline uint32_t state_magic(void)
{
if (sizeof(bool) == 1) return 'SAME';
return 'S4ME';
}
/* For internal in-memory save states (rewind, debugger) that do not need BESS */ /* For internal in-memory save states (rewind, debugger) that do not need BESS */
size_t GB_get_save_state_size_no_bess(GB_gameboy_t *gb); size_t GB_get_save_state_size_no_bess(GB_gameboy_t *gb);
void GB_save_state_to_buffer_no_bess(GB_gameboy_t *gb, uint8_t *buffer); void GB_save_state_to_buffer_no_bess(GB_gameboy_t *gb, uint8_t *buffer);

View File

@ -18,6 +18,7 @@ SDL_Texture *texture = NULL;
SDL_PixelFormat *pixel_format = NULL; SDL_PixelFormat *pixel_format = NULL;
enum pending_command pending_command; enum pending_command pending_command;
unsigned command_parameter; unsigned command_parameter;
char *dropped_state_file = NULL;
#ifdef __APPLE__ #ifdef __APPLE__
#define MODIFIER_NAME " " CMD_STRING #define MODIFIER_NAME " " CMD_STRING
@ -1300,9 +1301,15 @@ void run_gui(bool is_running)
break; break;
} }
case SDL_DROPFILE: { case SDL_DROPFILE: {
set_filename(event.drop.file, SDL_free); if (GB_is_stave_state(event.drop.file)) {
pending_command = GB_SDL_NEW_FILE_COMMAND; dropped_state_file = event.drop.file;
return; pending_command = GB_SDL_LOAD_STATE_FROM_FILE_COMMAND;
}
else {
set_filename(event.drop.file, SDL_free);
pending_command = GB_SDL_NEW_FILE_COMMAND;
return;
}
} }
case SDL_JOYBUTTONDOWN: case SDL_JOYBUTTONDOWN:
{ {

View File

@ -39,12 +39,14 @@ enum pending_command {
GB_SDL_RESET_COMMAND, GB_SDL_RESET_COMMAND,
GB_SDL_NEW_FILE_COMMAND, GB_SDL_NEW_FILE_COMMAND,
GB_SDL_QUIT_COMMAND, GB_SDL_QUIT_COMMAND,
GB_SDL_LOAD_STATE_FROM_FILE_COMMAND,
}; };
#define GB_SDL_DEFAULT_SCALE_MAX 8 #define GB_SDL_DEFAULT_SCALE_MAX 8
extern enum pending_command pending_command; extern enum pending_command pending_command;
extern unsigned command_parameter; extern unsigned command_parameter;
extern char *dropped_state_file;
typedef enum { typedef enum {
JOYPAD_BUTTON_LEFT, JOYPAD_BUTTON_LEFT,

View File

@ -142,8 +142,14 @@ static void handle_events(GB_gameboy_t *gb)
break; break;
case SDL_DROPFILE: { case SDL_DROPFILE: {
set_filename(event.drop.file, SDL_free); if (GB_is_stave_state(event.drop.file)) {
pending_command = GB_SDL_NEW_FILE_COMMAND; dropped_state_file = event.drop.file;
pending_command = GB_SDL_LOAD_STATE_FROM_FILE_COMMAND;
}
else {
set_filename(event.drop.file, SDL_free);
pending_command = GB_SDL_NEW_FILE_COMMAND;
}
break; break;
} }
@ -433,6 +439,13 @@ static bool handle_pending_command(void)
end_capturing_logs(true, false); end_capturing_logs(true, false);
return false; return false;
} }
case GB_SDL_LOAD_STATE_FROM_FILE_COMMAND:
start_capturing_logs();
GB_load_state(&gb, dropped_state_file);
end_capturing_logs(true, false);
SDL_free(dropped_state_file);
return false;
case GB_SDL_NO_COMMAND: case GB_SDL_NO_COMMAND:
return false; return false;