Merge branch 'master' into console_flash

This commit is contained in:
Lior Halphon 2024-06-22 16:28:13 +03:00
commit 2bf5819c36
435 changed files with 24058 additions and 6468 deletions

3
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1,3 @@
# These are supported funding model platforms
github: LIJI32

View File

@ -4,7 +4,7 @@ case `echo $1 | cut -d '-' -f 1` in
sudo apt-get install -yq bison libpng-dev pkg-config libsdl2-dev
(
cd `mktemp -d`
curl -L https://github.com/rednex/rgbds/archive/v0.4.0.zip > rgbds.zip
curl -L https://github.com/rednex/rgbds/archive/v0.6.0.zip > rgbds.zip
unzip rgbds.zip
cd rgbds-*
make -sj
@ -12,9 +12,22 @@ case `echo $1 | cut -d '-' -f 1` in
cd ..
rm -rf *
)
(
cd `mktemp -d`
curl -L https://github.com/BR903/cppp/archive/refs/heads/master.zip > cppp.zip
unzip cppp.zip
cd cppp-*
make -sj
sudo make install
cd ..
rm -rf *
)
;;
macos)
brew install rgbds sdl2
brew install rgbds sdl2 cppp
;;
*)
echo "Unsupported OS"

View File

@ -1,3 +1,5 @@
#!/bin/sh
set -e
./build/bin/tester/sameboy_tester --jobs 5 \
@ -7,7 +9,7 @@ set -e
--dmg --length 45 .github/actions/dmg_sound-2.gb \
--dmg --length 20 .github/actions/oam_bug-2.gb
mv .github/actions/dmg{,-mode}-acid2.bmp
mv .github/actions/dmg-acid2.bmp .github/actions/dmg-mode-acid2.bmp
./build/bin/tester/sameboy_tester \
--dmg --length 10 .github/actions/dmg-acid2.gb
@ -16,10 +18,10 @@ set +e
FAILED_TESTS=`
shasum .github/actions/*.bmp | grep -E -v \(\
5283564df0cf5bb78a7a90aff026c1a4692fd39e\ \ .github/actions/cgb-acid2.bmp\|\
64c3fd9a5fe9aee40fe15f3371029c0d2f20f5bc\ \ .github/actions/cgb-acid2.bmp\|\
dbcc438dcea13b5d1b80c5cd06bda2592cc5d9e0\ \ .github/actions/cgb_sound.bmp\|\
0caadf9634e40247ae9c15ff71992e8f77bbf89e\ \ .github/actions/dmg-acid2.bmp\|\
a732077f98f43d9231453b1764d9f797a836924d\ \ .github/actions/dmg-mode-acid2.bmp\|\
fbdb5e342bfdd2edda3ea5601d35d0ca60d18034\ \ .github/actions/dmg-mode-acid2.bmp\|\
c9e944b7e01078bdeba1819bc2fa9372b111f52d\ \ .github/actions/dmg_sound-2.bmp\|\
f0172cc91867d3343fbd113a2bb98100074be0de\ \ .github/actions/oam_bug-2.bmp\
\)`

30
.github/actions/update_libretro.sh vendored Executable file
View File

@ -0,0 +1,30 @@
set -ex
git fetch --tags
LATEST=$(git tag --sort=-creatordate | grep "^v" | grep -v libretro | head -n 1)
if [ $(git tag -l "$LATEST"-libretro) ]; then
echo "The libretro branch is already up-to-date"
exit 0
fi
git config --global --add --bool push.autoSetupRemote true
git config --global user.name 'Libretro Updater'
git config --global user.email '<>'
cp libretro/gitlab-ci.yml .gitlab-ci.yml
echo "Switching to tag $LATEST"
git branch --delete libretro || true
git checkout tags/$LATEST -b libretro
echo "Building boot ROMs..."
make -j bootroms
echo "Updating branch"
mv build/bin/BootROMs BootROMs/prebuilt
git add BootROMs/prebuilt/* .gitlab-ci.yml
git commit -m "Update libretro branch to $LATEST"
git tag "$LATEST"-libretro
git push --force
git push --tags

28
.github/workflows/libretro.yml vendored Normal file
View File

@ -0,0 +1,28 @@
name: "libretro branch update"
on:
push:
branches:
- master
jobs:
libretro-prebuilt-update:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
name: Checkout
with:
repository: LIJI32/SameBoy
token: ${{ secrets.WEBSITETOKEN }}
submodules: false
- name: Install Deps
shell: bash
run: |
./.github/actions/install_deps.sh ${{ matrix.os }}
- name: Build Boot ROMs and Push
run: |
./.github/actions/update_libretro.sh

View File

@ -1,36 +1,44 @@
name: "Bulidability and Sanity"
on: push
on:
push:
branches:
- master
- '*'
- '!libretro'
jobs:
sanity:
strategy:
fail-fast: false
matrix:
os: [macos-latest, ubuntu-latest, ubuntu-18.04]
os: [macos-latest, ubuntu-latest, ubuntu-20.04]
cc: [gcc, clang]
include:
- os: macos-latest
cc: clang
extra_target: cocoa
extra_targets: cocoa ios-ipa ios-deb
exclude:
- os: macos-latest
cc: gcc
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Install deps
shell: bash
run: |
./.github/actions/install_deps.sh ${{ matrix.os }}
- name: Build
run: |
${{ matrix.cc }} -v; (make -j sdl tester libretro ${{ matrix.extra_target }} CONF=release CC=${{ matrix.cc }} || (echo "==== Build Failed ==="; make sdl tester libretro ${{ matrix.extra_target }} CONF=release CC=${{ matrix.cc }}))
${{ matrix.cc }} -v; (make -j all CONF=release CC=${{ matrix.cc }} || (echo "==== Build Failed ==="; make all CONF=release CC=${{ matrix.cc }}))
- name: Sanity tests
shell: bash
run: |
./.github/actions/sanity_tests.sh
- name: Upload binaries
uses: actions/upload-artifact@v1
uses: actions/upload-artifact@v3
with:
name: sameboy-canary-${{ matrix.os }}-${{ matrix.cc }}
path: build/bin
path: |
build/bin
build/lib
build/include

View File

@ -35,30 +35,43 @@ static OSStatus render(
// kAudioUnitSubType_DefaultOutput on Mac OS X)
AudioComponentDescription defaultOutputDescription;
defaultOutputDescription.componentType = kAudioUnitType_Output;
#if TARGET_OS_IPHONE
defaultOutputDescription.componentSubType = kAudioUnitSubType_RemoteIO;
#else
defaultOutputDescription.componentSubType = kAudioUnitSubType_DefaultOutput;
#endif
defaultOutputDescription.componentManufacturer = kAudioUnitManufacturer_Apple;
defaultOutputDescription.componentFlags = 0;
defaultOutputDescription.componentFlagsMask = 0;
// Get the default playback output unit
AudioComponent defaultOutput = AudioComponentFindNext(NULL, &defaultOutputDescription);
NSAssert(defaultOutput, @"Can't find default output");
if (!defaultOutput) {
NSLog(@"Can't find default output");
return nil;
}
// Create a new unit based on this that we'll use for output
OSErr err = AudioComponentInstanceNew(defaultOutput, &audioUnit);
NSAssert1(audioUnit, @"Error creating unit: %hd", err);
if (!audioUnit) {
NSLog(@"Error creating unit: %hd", err);
return nil;
}
// Set our tone rendering function on the unit
AURenderCallbackStruct input;
input.inputProc = (void*)render;
input.inputProcRefCon = (__bridge void * _Nullable)(self);
input.inputProcRefCon = (__bridge void *)(self);
err = AudioUnitSetProperty(audioUnit,
kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Input,
0,
&input,
sizeof(input));
NSAssert1(err == noErr, @"Error setting callback: %hd", err);
if (err) {
NSLog(@"Error setting callback: %hd", err);
return nil;
}
AudioStreamBasicDescription streamFormat;
streamFormat.mSampleRate = rate;
@ -76,9 +89,16 @@ static OSStatus render(
0,
&streamFormat,
sizeof(AudioStreamBasicDescription));
NSAssert1(err == noErr, @"Error setting stream format: %hd", err);
if (err) {
NSLog(@"Error setting stream format: %hd", err);
return nil;
}
err = AudioUnitInitialize(audioUnit);
NSAssert1(err == noErr, @"Error initializing unit: %hd", err);
if (err) {
NSLog(@"Error initializing unit: %hd", err);
return nil;
}
self.renderBlock = block;
_rate = rate;
@ -89,7 +109,10 @@ static OSStatus render(
-(void) start
{
OSErr err = AudioOutputUnitStart(audioUnit);
NSAssert1(err == noErr, @"Error starting unit: %hd", err);
if (err) {
NSLog(@"Error starting unit: %hd", err);
return;
}
_playing = true;
}

36
AppleCommon/GBViewBase.h Normal file
View File

@ -0,0 +1,36 @@
#import <TargetConditionals.h>
#if TARGET_OS_IPHONE
#define NSView UIView
#import <UIKit/UIKit.h>
#else
#import <Cocoa/Cocoa.h>
#endif
#import <Core/gb.h>
typedef enum {
GB_FRAME_BLENDING_MODE_DISABLED,
GB_FRAME_BLENDING_MODE_SIMPLE,
GB_FRAME_BLENDING_MODE_ACCURATE,
GB_FRAME_BLENDING_MODE_ACCURATE_EVEN = GB_FRAME_BLENDING_MODE_ACCURATE,
GB_FRAME_BLENDING_MODE_ACCURATE_ODD,
} GB_frame_blending_mode_t;
@interface GBViewBase : NSView
{
@public
GB_gameboy_t *_gb;
}
@property (nonatomic) GB_gameboy_t *gb;
@property (nonatomic) GB_frame_blending_mode_t frameBlendingMode;
@property (nonatomic, strong) NSView *internalView;
- (void) flip;
- (uint32_t *) pixels;
- (void)screenSizeChanged;
- (void) createInternalView;
- (uint32_t *)currentBuffer;
- (uint32_t *)previousBuffer;
@end

85
AppleCommon/GBViewBase.m Normal file
View File

@ -0,0 +1,85 @@
#import "GBViewBase.h"
@implementation GBViewBase
{
uint32_t *_imageBuffers[3];
unsigned _currentBuffer;
GB_frame_blending_mode_t _frameBlendingMode;
bool _oddFrame;
}
- (void)screenSizeChanged
{
if (_imageBuffers[0]) free(_imageBuffers[0]);
if (_imageBuffers[1]) free(_imageBuffers[1]);
if (_imageBuffers[2]) free(_imageBuffers[2]);
size_t buffer_size = sizeof(_imageBuffers[0][0]) * GB_get_screen_width(_gb) * GB_get_screen_height(_gb);
_imageBuffers[0] = calloc(1, buffer_size);
_imageBuffers[1] = calloc(1, buffer_size);
_imageBuffers[2] = calloc(1, buffer_size);
}
- (void)flip
{
_currentBuffer = (_currentBuffer + 1) % self.numberOfBuffers;
_oddFrame = GB_is_odd_frame(_gb);
}
- (unsigned) numberOfBuffers
{
return _frameBlendingMode? 3 : 2;
}
- (void) createInternalView
{
assert(false && "createInternalView must not be inherited");
}
- (uint32_t *)currentBuffer
{
return _imageBuffers[_currentBuffer];
}
- (uint32_t *)previousBuffer
{
return _imageBuffers[(_currentBuffer + 2) % self.numberOfBuffers];
}
- (uint32_t *) pixels
{
return _imageBuffers[(_currentBuffer + 1) % self.numberOfBuffers];
}
- (void) setFrameBlendingMode:(GB_frame_blending_mode_t)frameBlendingMode
{
_frameBlendingMode = frameBlendingMode;
[self setNeedsDisplay];
}
- (GB_frame_blending_mode_t)frameBlendingMode
{
if (_frameBlendingMode == GB_FRAME_BLENDING_MODE_ACCURATE) {
if (!_gb || GB_is_sgb(_gb)) {
return GB_FRAME_BLENDING_MODE_SIMPLE;
}
return _oddFrame ? GB_FRAME_BLENDING_MODE_ACCURATE_ODD : GB_FRAME_BLENDING_MODE_ACCURATE_EVEN;
}
return _frameBlendingMode;
}
- (void)dealloc
{
free(_imageBuffers[0]);
free(_imageBuffers[1]);
free(_imageBuffers[2]);
}
#if !TARGET_OS_IPHONE
- (void)setNeedsDisplay
{
[self setNeedsDisplay:true];
}
#endif
@end

11
AppleCommon/GBViewMetal.h Normal file
View File

@ -0,0 +1,11 @@
#import <TargetConditionals.h>
#import <MetalKit/MetalKit.h>
#if TARGET_OS_IPHONE
#import "../iOS/GBView.h"
#else
#import "../Cocoa/GBView.h"
#endif
@interface GBViewMetal : GBView<MTKViewDelegate>
+ (bool) isSupported;
@end

View File

@ -1,7 +1,9 @@
#import <CoreImage/CoreImage.h>
#import "GBViewMetal.h"
#pragma clang diagnostic ignored "-Wpartial-availability"
#if !TARGET_OS_IPHONE
#import "../Cocoa/NSObject+DefaultsObserver.h"
#endif
static const vector_float2 rect[] =
{
@ -13,27 +15,32 @@ static const vector_float2 rect[] =
@implementation GBViewMetal
{
id<MTLDevice> device;
id<MTLTexture> texture, previous_texture;
id<MTLBuffer> vertices;
id<MTLRenderPipelineState> pipeline_state;
id<MTLCommandQueue> command_queue;
id<MTLBuffer> frame_blending_mode_buffer;
id<MTLBuffer> output_resolution_buffer;
vector_float2 output_resolution;
id<MTLDevice> _device;
id<MTLTexture> _texture, _previousTexture;
id<MTLBuffer> _vertices;
id<MTLRenderPipelineState> _pipelineState;
id<MTLCommandQueue> _commandQueue;
id<MTLBuffer> _frameBlendingModeBuffer;
id<MTLBuffer> _outputResolutionBuffer;
vector_float2 _outputResolution;
id<MTLCommandBuffer> _commandBuffer;
bool _waitedForFrame;
}
+ (bool)isSupported
{
#if TARGET_OS_IPHONE
return true;
#else
if (MTLCopyAllDevices) {
return [MTLCopyAllDevices() count];
}
return false;
#endif
}
- (void) allocateTextures
{
if (!device) return;
if (!_device) return;
MTLTextureDescriptor *texture_descriptor = [[MTLTextureDescriptor alloc] init];
@ -42,39 +49,44 @@ static const vector_float2 rect[] =
texture_descriptor.width = GB_get_screen_width(self.gb);
texture_descriptor.height = GB_get_screen_height(self.gb);
texture = [device newTextureWithDescriptor:texture_descriptor];
previous_texture = [device newTextureWithDescriptor:texture_descriptor];
_texture = [_device newTextureWithDescriptor:texture_descriptor];
_previousTexture = [_device newTextureWithDescriptor:texture_descriptor];
}
- (void)createInternalView
{
MTKView *view = [[MTKView alloc] initWithFrame:self.frame device:(device = MTLCreateSystemDefaultDevice())];
MTKView *view = [[MTKView alloc] initWithFrame:self.frame device:(_device = MTLCreateSystemDefaultDevice())];
view.delegate = self;
self.internalView = view;
view.paused = true;
view.enableSetNeedsDisplay = true;
view.framebufferOnly = false;
vertices = [device newBufferWithBytes:rect
_vertices = [_device newBufferWithBytes:rect
length:sizeof(rect)
options:MTLResourceStorageModeShared];
static const GB_frame_blending_mode_t default_blending_mode = GB_FRAME_BLENDING_MODE_DISABLED;
frame_blending_mode_buffer = [device newBufferWithBytes:&default_blending_mode
_frameBlendingModeBuffer = [_device newBufferWithBytes:&default_blending_mode
length:sizeof(default_blending_mode)
options:MTLResourceStorageModeShared];
output_resolution_buffer = [device newBufferWithBytes:&output_resolution
length:sizeof(output_resolution)
_outputResolutionBuffer = [_device newBufferWithBytes:&_outputResolution
length:sizeof(_outputResolution)
options:MTLResourceStorageModeShared];
output_resolution = (simd_float2){view.drawableSize.width, view.drawableSize.height};
_outputResolution = (simd_float2){view.drawableSize.width, view.drawableSize.height};
/* TODO: NSObject+DefaultsObserver can replace the less flexible `addDefaultObserver` in iOS */
#if TARGET_OS_IPHONE
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(loadShader) name:@"GBFilterChanged" object:nil];
[self loadShader];
#else
[self observeStandardDefaultsKey:@"GBFilter" selector:@selector(loadShader)];
#endif
}
- (void) loadShader
- (void)loadShader
{
NSError *error = nil;
NSString *shader_source = [NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"MasterShader"
@ -95,7 +107,7 @@ static const vector_float2 rect[] =
MTLCompileOptions *options = [[MTLCompileOptions alloc] init];
options.fastMathEnabled = true;
id<MTLLibrary> library = [device newLibraryWithSource:shader_source
id<MTLLibrary> library = [_device newLibraryWithSource:shader_source
options:options
error:&error];
if (error) {
@ -115,19 +127,19 @@ static const vector_float2 rect[] =
pipeline_state_descriptor.colorAttachments[0].pixelFormat = ((MTKView *)self.internalView).colorPixelFormat;
error = nil;
pipeline_state = [device newRenderPipelineStateWithDescriptor:pipeline_state_descriptor
_pipelineState = [_device newRenderPipelineStateWithDescriptor:pipeline_state_descriptor
error:&error];
if (error) {
NSLog(@"Failed to created pipeline state, error %@", error);
return;
}
command_queue = [device newCommandQueue];
_commandQueue = [_device newCommandQueue];
}
- (void)mtkView:(MTKView *)view drawableSizeWillChange:(CGSize)size
{
output_resolution = (vector_float2){size.width, size.height};
_outputResolution = (vector_float2){size.width, size.height};
dispatch_async(dispatch_get_main_queue(), ^{
[(MTKView *)self.internalView draw];
});
@ -135,90 +147,107 @@ static const vector_float2 rect[] =
- (void)drawInMTKView:(MTKView *)view
{
#if !TARGET_OS_IPHONE
if (!(view.window.occlusionState & NSWindowOcclusionStateVisible)) return;
#endif
if (!self.gb) return;
if (texture.width != GB_get_screen_width(self.gb) ||
texture.height != GB_get_screen_height(self.gb)) {
if (_texture.width != GB_get_screen_width(self.gb) ||
_texture.height != GB_get_screen_height(self.gb)) {
[self allocateTextures];
}
MTLRegion region = {
{0, 0, 0}, // MTLOrigin
{texture.width, texture.height, 1} // MTLSize
{_texture.width, _texture.height, 1} // MTLSize
};
/* Don't start rendering if the previous frame hasn't finished yet. Either wait, or skip the frame */
if (_commandBuffer && _commandBuffer.status != MTLCommandBufferStatusCompleted) {
if (_waitedForFrame) return;
[_commandBuffer waitUntilCompleted];
_waitedForFrame = true;
}
else {
_waitedForFrame = false;
}
[texture replaceRegion:region
GB_frame_blending_mode_t mode = [self frameBlendingMode];
[_texture replaceRegion:region
mipmapLevel:0
withBytes:[self currentBuffer]
bytesPerRow:texture.width * 4];
if ([self frameBlendingMode]) {
[previous_texture replaceRegion:region
bytesPerRow:_texture.width * 4];
if (mode) {
[_previousTexture replaceRegion:region
mipmapLevel:0
withBytes:[self previousBuffer]
bytesPerRow:texture.width * 4];
bytesPerRow:_texture.width * 4];
}
MTLRenderPassDescriptor *render_pass_descriptor = view.currentRenderPassDescriptor;
id<MTLCommandBuffer> command_buffer = [command_queue commandBuffer];
if (render_pass_descriptor != nil) {
*(GB_frame_blending_mode_t *)[frame_blending_mode_buffer contents] = [self frameBlendingMode];
*(vector_float2 *)[output_resolution_buffer contents] = output_resolution;
id<MTLRenderCommandEncoder> render_encoder =
[command_buffer renderCommandEncoderWithDescriptor:render_pass_descriptor];
[render_encoder setViewport:(MTLViewport){0.0, 0.0,
output_resolution.x,
output_resolution.y,
MTLRenderPassDescriptor *renderPassDescriptor = view.currentRenderPassDescriptor;
_commandBuffer = [_commandQueue commandBuffer];
if (renderPassDescriptor) {
*(GB_frame_blending_mode_t *)[_frameBlendingModeBuffer contents] = mode;
*(vector_float2 *)[_outputResolutionBuffer contents] = _outputResolution;
id<MTLRenderCommandEncoder> renderEncoder =
[_commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];
[renderEncoder setViewport:(MTLViewport){0.0, 0.0,
_outputResolution.x,
_outputResolution.y,
-1.0, 1.0}];
[render_encoder setRenderPipelineState:pipeline_state];
[renderEncoder setRenderPipelineState:_pipelineState];
[render_encoder setVertexBuffer:vertices
[renderEncoder setVertexBuffer:_vertices
offset:0
atIndex:0];
[render_encoder setFragmentBuffer:frame_blending_mode_buffer
[renderEncoder setFragmentBuffer:_frameBlendingModeBuffer
offset:0
atIndex:0];
[render_encoder setFragmentBuffer:output_resolution_buffer
[renderEncoder setFragmentBuffer:_outputResolutionBuffer
offset:0
atIndex:1];
[render_encoder setFragmentTexture:texture
[renderEncoder setFragmentTexture:_texture
atIndex:0];
[render_encoder setFragmentTexture:previous_texture
[renderEncoder setFragmentTexture:_previousTexture
atIndex:1];
[render_encoder drawPrimitives:MTLPrimitiveTypeTriangleStrip
[renderEncoder drawPrimitives:MTLPrimitiveTypeTriangleStrip
vertexStart:0
vertexCount:4];
[render_encoder endEncoding];
[renderEncoder endEncoding];
[command_buffer presentDrawable:view.currentDrawable];
[_commandBuffer presentDrawable:view.currentDrawable];
}
[command_buffer commit];
[_commandBuffer commit];
}
- (void)flip
{
[super flip];
dispatch_async(dispatch_get_main_queue(), ^{
[(MTKView *)self.internalView setNeedsDisplay:true];
[(MTKView *)self.internalView draw];
});
}
#if !TARGET_OS_IPHONE
- (NSImage *)renderToImage
{
CIImage *ciImage = [CIImage imageWithMTLTexture:[[(MTKView *)self.internalView currentDrawable] texture]
options:@{
kCIImageColorSpace: (__bridge_transfer id)CGColorSpaceCreateDeviceRGB()
kCIImageColorSpace: (__bridge_transfer id)CGColorSpaceCreateDeviceRGB(),
kCIImageProperties: [NSNull null]
}];
ciImage = [ciImage imageByApplyingTransform:CGAffineTransformTranslate(CGAffineTransformMakeScale(1, -1),
0, ciImage.extent.size.height)];
@ -228,5 +257,6 @@ static const vector_float2 rect[] =
CGImageRelease(cgImage);
return ret;
}
#endif
@end

42
BESS.md
View File

@ -176,6 +176,48 @@ The length of this block is 0x11 bytes long and it follows the following structu
| 0x0E | Scheduled alarm time days (16-bit) |
| 0x10 | Alarm enabled flag (8-bits, either 0 or 1) |
#### TPP1 block
The TPP1 block uses the `'TPP1'` identifier, and is an optional block that is used while emulating a TPP1 cartridge to store RTC information. This block can be omitted if the ROM header does not specify the inclusion of a RTC.
The length of this block is 0x11 bytes long and it follows the following structure:
| Offset | Content |
|--------|-------------------------------------------------------|
| 0x00 | UNIX timestamp at the time of the save state (64-bit) |
| 0x08 | The current RTC data (4 bytes) |
| 0x0C | The latched RTC data (4 bytes) |
| 0x10 | The value of the MR4 register (8-bits) |
#### MBC7 block
The MBC7 block uses the `'MBC7'` identifier, and is an optional block that is used while emulating an MBC7 cartridge to store the EEPROM communication state and motion control state.
The length of this block is 0xA bytes long and it follows the following structure:
| Offset | Content |
|--------|-------------------------------------------------------|
| 0x00 | Flags (8-bits) |
| 0x01 | Argument bits left (8-bits) |
| 0x02 | Current EEPROM command (16-bits) |
| 0x04 | Pending bits to read (16-bits) |
| 0x06 | Latched gyro X value (16-bits) |
| 0x08 | Latched gyro Y value (16-bits) |
The meaning of the individual bits in flags are:
* Bit 0: Latch ready; set after writing `0x55` to `0xAX0X` and reset after writing `0xAA` to `0xAX1X`
* Bit 1: EEPROM DO line
* Bit 2: EEPROM DI line
* Bit 3: EEPROM CLK line
* Bit 4: EEPROM CS line
* Bit 5: EEPROM write enable; set after an `EWEN` command, reset after an `EWDS` command
* Bits 6-7: Unused.
The current EEPROM command field has bits pushed to its LSB first, padded with zeros. For example, if the ROM clocked a single `1` bit, this field should contain `0b1`; if the ROM later clocks a `0` bit, this field should contain `0b10`.
If the currently transmitted command has an argument, the "Argument bits left" field should contain the number argument bits remaining. Otherwise, it should contain 0.
The "Pending bits to read" field contains the pending bits waiting to be shifted into the DO signal, MSB first, padded with ones.
#### SGB block
The SGB block uses the `'SGB '` identifier, and is an optional block that is only used while emulating an SGB or SGB2 *and* SGB commands enabled. Implementations must not save this block on other models or when SGB commands are disabled, and should assume SGB commands are disabled if this block is missing.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 479 B

After

Width:  |  Height:  |  Size: 477 B

View File

@ -1,2 +1,2 @@
AGB EQU 1
include "cgb_boot.asm"
DEF AGB = 1
include "cgb_boot.asm"

View File

@ -1,2 +1,2 @@
CGB0 EQU 1
include "cgb_boot.asm"
DEF CGB0 = 1
include "cgb_boot.asm"

File diff suppressed because it is too large Load Diff

View File

@ -1,2 +1,2 @@
FAST EQU 1
include "cgb_boot.asm"
DEF FAST = 1
include "cgb_boot.asm"

View File

@ -1,12 +1,15 @@
; SameBoy DMG bootstrap ROM
; Todo: use friendly names for HW registers instead of magic numbers
SECTION "BootCode", ROM0[$0]
include "sameboot.inc"
SECTION "BootCode", ROM0[$0000]
Start:
; Init stack pointer
ld sp, $fffe
ld sp, $FFFE
; Clear memory VRAM
ld hl, $8000
ld hl, _VRAM
xor a
.clearVRAMLoop
ldi [hl], a
@ -14,24 +17,25 @@ Start:
jr z, .clearVRAMLoop
; Init Audio
ld a, $80
ldh [$26], a
ldh [$11], a
ld a, $f3
ldh [$12], a
ldh [$25], a
ld a, AUDENA_ON
ldh [rNR52], a
assert AUDENA_ON == AUDLEN_DUTY_50
ldh [rNR11], a
ld a, $F3
ldh [rNR12], a ; Envelope $F, decreasing, sweep $3
ldh [rNR51], a ; Channels 1+2+3+4 left, channels 1+2 right
ld a, $77
ldh [$24], a
ldh [rNR50], a ; Volume $7, left and right
; Init BG palette
ld a, $54
ldh [$47], a
ld a, %01_01_01_00
ldh [rBGP], a
; Load logo from ROM.
; A nibble represents a 4-pixels line, 2 bytes represent a 4x4 tile, scaled to 8x8.
; Tiles are ordered left to right, top to bottom.
ld de, $104 ; Logo start
ld hl, $8010 ; This is where we load the tiles in VRAM
ld de, NintendoLogo
ld hl, _VRAM + $10 ; This is where we load the tiles in VRAM
.loadLogoLoop
ld a, [de] ; Read 2 rows
@ -40,92 +44,92 @@ Start:
call DoubleBitsAndWriteRow
inc de
ld a, e
xor $34 ; End of logo
xor LOW(NintendoLogoEnd)
jr nz, .loadLogoLoop
; Load trademark symbol
ld de, TrademarkSymbol
ld c,$08
ld c, TrademarkSymbolEnd - TrademarkSymbol
.loadTrademarkSymbolLoop:
ld a,[de]
ld a, [de]
inc de
ldi [hl],a
ldi [hl], a
inc hl
dec c
jr nz, .loadTrademarkSymbolLoop
; Set up tilemap
ld a,$19 ; Trademark symbol
ld [$9910], a ; ... put in the superscript position
ld hl,$992f ; Bottom right corner of the logo
ld c,$c ; Tiles in a logo row
ld a, $19 ; Trademark symbol tile ID
ld [_SCRN0 + 8 * SCRN_VX_B + 16], a ; ... put in the superscript position
ld hl, _SCRN0 + 9 * SCRN_VX_B + 15 ; Bottom right corner of the logo
ld c, 12 ; Tiles in a logo row
.tilemapLoop
dec a
jr z, .tilemapDone
ldd [hl], a
dec c
jr nz, .tilemapLoop
ld l,$0f ; Jump to top row
ld l, $0F ; Jump to top row
jr .tilemapLoop
.tilemapDone
ld a, 30
ldh [$ff42], a
; Turn on LCD
ld a, $91
ldh [$40], a
ldh [rSCY], a
ld d, (-119) & $FF
; Turn on LCD
ld a, LCDCF_ON | LCDCF_BLK01 | LCDCF_BGON
ldh [rLCDC], a
ld d, LOW(-119)
ld c, 15
.animate
call WaitFrame
ld a, d
sra a
sra a
ldh [$ff42], a
ldh [rSCY], a
ld a, d
add c
ld d, a
ld a, c
cp 8
jr nz, .noPaletteChange
ld a, $A8
ldh [$47], a
ld a, %10_10_10_00
ldh [rBGP], a
.noPaletteChange
dec c
jr nz, .animate
ld a, $fc
ldh [$47], a
ld a, %11_11_11_00
ldh [rBGP], a
; Play first sound
ld a, $83
call PlaySound
ld b, 5
call WaitBFrames
; Play second sound
ld a, $c1
ld a, $C1
call PlaySound
; Wait ~1 second
ld b, 60
call WaitBFrames
; Set registers to match the original DMG boot
IF DEF(MGB)
ld hl, $FFB0
lb hl, BOOTUP_A_MGB, %10110000
ELSE
ld hl, $01B0
lb hl, BOOTUP_A_DMG, %10110000
ENDC
push hl
pop af
ld hl, $014D
ld bc, $0013
ld de, $00D8
ld hl, HeaderChecksum
lb bc, 0, LOW(rNR13) ; $0013
lb de, 0, $D8 ; $00D8
; Boot the game
jp BootGame
@ -152,7 +156,7 @@ DoubleBitsAndWriteRow:
WaitFrame:
push hl
ld hl, $FF0F
ld hl, rIF
res 0, [hl]
.wait
bit 0, [hl]
@ -167,15 +171,26 @@ WaitBFrames:
ret
PlaySound:
ldh [$13], a
ld a, $87
ldh [$14], a
ldh [rNR13], a
ld a, AUDHIGH_RESTART | $7
ldh [rNR14], a
ret
TrademarkSymbol:
db $3c,$42,$b9,$a5,$b9,$a5,$42,$3c
pusho
opt b.X
db %..XXXX..
db %.X....X.
db %X.XXX..X
db %X.X..X.X
db %X.XXX..X
db %X.X..X.X
db %.X....X.
db %..XXXX..
popo
TrademarkSymbolEnd:
SECTION "BootGame", ROM0[$fe]
SECTION "BootGame", ROM0[$00FE]
BootGame:
ldh [$50], a
ldh [rBANK], a ; unmap boot ROM

1113
BootROMs/hardware.inc Executable file

File diff suppressed because it is too large Load Diff

View File

@ -1,2 +1,2 @@
MGB EQU 1
include "dmg_boot.asm"
DEF MGB = 1
include "dmg_boot.asm"

View File

@ -5,7 +5,7 @@
#include <unistd.h>
#include <assert.h>
void opts(uint8_t byte, uint8_t *options)
static void opts(uint8_t byte, uint8_t *options)
{
*(options++) = byte | ((byte << 1) & 0xff);
*(options++) = byte & (byte << 1);
@ -13,7 +13,7 @@ void opts(uint8_t byte, uint8_t *options)
*(options++) = byte & (byte >> 1);
}
void write_all(int fd, const void *buf, size_t count) {
static void write_all(int fd, const void *buf, size_t count) {
while (count) {
ssize_t written = write(fd, buf, count);
if (written < 0) {
@ -25,7 +25,7 @@ void write_all(int fd, const void *buf, size_t count) {
}
}
int main()
int main(void)
{
static uint8_t source[0x4000];
size_t size = read(STDIN_FILENO, &source, sizeof(source));
@ -87,7 +87,7 @@ int main()
prev[1] = byte;
if (bits >= 8) {
uint8_t outctl = control >> (bits - 8);
assert(outctl != 1);
assert(outctl != 1); // 1 is reserved as the end byte
write_all(STDOUT_FILENO, &outctl, 1);
write_all(STDOUT_FILENO, literals, literals_size);
bits -= 8;

40
BootROMs/sameboot.inc Normal file
View File

@ -0,0 +1,40 @@
IF !DEF(SAMEBOY_INC)
DEF SAMEBOY_INC EQU 1
include "hardware.inc"
DEF rKEY0 EQU $FF4C
DEF rBANK EQU $FF50
DEF rJOYP EQU rP1
MACRO lb ; r16, high, low
ld \1, LOW(\2) << 8 | LOW(\3)
ENDM
MACRO header_section ; name, address
PUSHS
SECTION "\1", ROM0[\2]
\1:
POPS
ENDM
header_section EntryPoint, $0100
header_section NintendoLogo, $0104
header_section NintendoLogoEnd, $0134
header_section Title, $0134
header_section ManufacturerCode, $013F
header_section CGBFlag, $0143
header_section NewLicenseeCode, $0144
header_section SGBFlag, $0146
header_section CartridgeType, $0147
header_section ROMSize, $0148
header_section RAMSize, $0149
header_section DestinationCode, $014A
header_section OldLicenseeCode, $014B
header_section MaskRomVersion, $014C
header_section HeaderChecksum, $014D
header_section GlobalChecksum, $014E
ENDC

View File

@ -1,2 +1,2 @@
SGB2 EQU 1
include "sgb_boot.asm"
DEF SGB2 = 1
include "sgb_boot.asm"

View File

@ -1,12 +1,15 @@
; SameBoy SGB bootstrap ROM
; Todo: use friendly names for HW registers instead of magic numbers
SECTION "BootCode", ROM0[$0]
include "sameboot.inc"
SECTION "BootCode", ROM0[$0000]
Start:
; Init stack pointer
ld sp, $fffe
ld sp, $FFFE
; Clear memory VRAM
ld hl, $8000
ld hl, _VRAM
xor a
.clearVRAMLoop
ldi [hl], a
@ -14,24 +17,25 @@ Start:
jr z, .clearVRAMLoop
; Init Audio
ld a, $80
ldh [$26], a
ldh [$11], a
ld a, $f3
ldh [$12], a
ldh [$25], a
ld a, AUDENA_ON
ldh [rNR52], a
assert AUDENA_ON == AUDLEN_DUTY_50
ldh [rNR11], a
ld a, $F3
ldh [rNR12], a ; Envelope $F, decreasing, sweep $3
ldh [rNR51], a ; Channels 1+2+3+4 left, channels 1+2 right
ld a, $77
ldh [$24], a
ldh [rNR50], a ; Volume $7, left and right
; Init BG palette to white
ld a, $0
ldh [$47], a
ld a, %00_00_00_00
ldh [rBGP], a
; Load logo from ROM.
; A nibble represents a 4-pixels line, 2 bytes represent a 4x4 tile, scaled to 8x8.
; Tiles are ordered left to right, top to bottom.
ld de, $104 ; Logo start
ld hl, $8010 ; This is where we load the tiles in VRAM
ld de, NintendoLogo
ld hl, _VRAM + $10 ; This is where we load the tiles in VRAM
.loadLogoLoop
ld a, [de] ; Read 2 rows
@ -40,43 +44,43 @@ Start:
call DoubleBitsAndWriteRow
inc de
ld a, e
xor $34 ; End of logo
xor LOW(NintendoLogoEnd)
jr nz, .loadLogoLoop
; Load trademark symbol
ld de, TrademarkSymbol
ld c,$08
ld c, TrademarkSymbolEnd - TrademarkSymbol
.loadTrademarkSymbolLoop:
ld a,[de]
ld a, [de]
inc de
ldi [hl],a
ldi [hl], a
inc hl
dec c
jr nz, .loadTrademarkSymbolLoop
; Set up tilemap
ld a,$19 ; Trademark symbol
ld [$9910], a ; ... put in the superscript position
ld hl,$992f ; Bottom right corner of the logo
ld c,$c ; Tiles in a logo row
ld a, $19 ; Trademark symbol tile ID
ld [_SCRN0 + 8 * SCRN_VX_B + 16], a ; ... put in the superscript position
ld hl, _SCRN0 + 9 * SCRN_VX_B + 15 ; Bottom right corner of the logo
ld c, 12 ; Tiles in a logo row
.tilemapLoop
dec a
jr z, .tilemapDone
ldd [hl], a
dec c
jr nz, .tilemapLoop
ld l,$0f ; Jump to top row
ld l, $0F ; Jump to top row
jr .tilemapLoop
.tilemapDone
; Turn on LCD
ld a, $91
ldh [$40], a
ld a, LCDCF_ON | LCDCF_BLK01 | LCDCF_BGON
ldh [rLCDC], a
ld a, $F1 ; Packet magic, increases by 2 for every packet
ldh [hCommand], a
ld hl, NintendoLogo ; Header start
ld a, $f1 ; Packet magic, increases by 2 for every packet
ldh [$80], a
ld hl, $104 ; Header start
xor a
ld c, a ; JOYP
@ -85,66 +89,79 @@ Start:
ld [c], a
ld a, $30
ld [c], a
ldh a, [$80]
ldh a, [hCommand]
call SendByte
push hl
ld b, $e
ld b, 14
ld d, 0
.checksumLoop
call ReadHeaderByte
add d
ld d, a
dec b
jr nz, .checksumLoop
; Send checksum
call SendByte
pop hl
ld b, $e
ld b, 14
.sendLoop
call ReadHeaderByte
call SendByte
dec b
jr nz, .sendLoop
; Done bit
ld a, $20
ld [c], a
ld a, $30
ld [c], a
; Wait 4 frames
ld e, 4
ld a, 1
ldh [rIE], a
xor a
.waitLoop
ldh [rIF], a
halt
nop
dec e
jr nz, .waitLoop
ldh [rIE], a
; Update command
ldh a, [$80]
ldh a, [hCommand]
add 2
ldh [$80], a
ldh [hCommand], a
ld a, $58
cp l
jr nz, .sendCommand
; Write to sound registers for DMG compatibility
ld c, $13
ld a, $c1
ld c, LOW(rNR13)
ld a, $C1
ld [c], a
inc c
ld a, 7
ld a, $7
ld [c], a
; Init BG palette
ld a, $fc
ldh [$47], a
ld a, %11_11_11_00
ldh [rBGP], a
; Set registers to match the original SGB boot
IF DEF(SGB2)
ld a, $FF
ld a, BOOTUP_A_MGB
ELSE
ld a, 1
ld a, BOOTUP_A_DMG
ENDC
ld hl, $c060
ld hl, $C060
; Boot the game
jp BootGame
@ -195,19 +212,24 @@ DoubleBitsAndWriteRow:
inc hl
ret
WaitFrame:
push hl
ld hl, $FF0F
res 0, [hl]
.wait
bit 0, [hl]
jr z, .wait
pop hl
ret
TrademarkSymbol:
db $3c,$42,$b9,$a5,$b9,$a5,$42,$3c
pusho
opt b.X
db %..XXXX..
db %.X....X.
db %X.XXX..X
db %X.X..X.X
db %X.XXX..X
db %X.X..X.X
db %.X....X.
db %..XXXX..
popo
TrademarkSymbolEnd:
SECTION "BootGame", ROM0[$fe]
SECTION "BootGame", ROM0[$00FE]
BootGame:
ldh [$50], a
ldh [rBANK], a
SECTION "HRAM", HRAM[_HRAM]
hCommand:
ds 1

View File

@ -24,7 +24,7 @@ SameBoy's main target compiler is Clang, but GCC is also supported when targetin
### Third Party Libraries and Tools
Avoid adding new required dependencies; run-time and compile-time dependencies alike. Most importantly, avoid linking against GPL licensed libraries (LGPL libraries are fine), so SameBoy can retain its MIT license.
Avoid adding new required dependencies; run-time and compile-time dependencies alike. Most importantly, avoid linking against GPL licensed libraries (LGPL libraries are fine), so SameBoy can retain its Expat license.
### Spacing, Indentation and Formatting

Binary file not shown.

View File

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14868" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14868"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="Document">
<connections>
<outlet property="audioFormatButton" destination="knX-AW-zt5" id="fKt-eI-H0y"/>
<outlet property="audioRecordingAccessoryView" destination="c22-O7-iKe" id="XD8-Gi-qOC"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customView id="c22-O7-iKe">
<rect key="frame" x="0.0" y="0.0" width="354" height="36"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Atq-RE-328">
<rect key="frame" x="18" y="10" width="56" height="16"/>
<autoresizingMask key="autoresizingMask"/>
<textFieldCell key="cell" lineBreakMode="clipping" alignment="right" title="Format:" id="dso-NS-JlD">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="knX-AW-zt5">
<rect key="frame" x="81" y="4" width="256" height="25"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="push" title="Apple AIFF" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" tag="1" imageScaling="proportionallyDown" inset="2" selectedItem="M3Z-UN-VKZ" id="tLM-Di-Dy3">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
<menu key="menu" id="gqn-SL-AA5">
<items>
<menuItem title="Apple AIFF" state="on" tag="1" id="M3Z-UN-VKZ"/>
<menuItem title="RIFF WAVE" tag="2" id="zA0-Np-4XD"/>
<menuItem title="Raw PCM (Stereo 96KHz, 16-bit LE)" id="r9J-4k-XH5"/>
</items>
</menu>
</popUpButtonCell>
<connections>
<action selector="audioFormatChanged:" target="-2" id="I1k-d9-afp"/>
</connections>
</popUpButton>
</subviews>
<point key="canvasLocation" x="75" y="19"/>
</customView>
</objects>
</document>

BIN
Cocoa/BackstepTemplate.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 201 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 336 B

View File

@ -1,6 +1,4 @@
#import <Cocoa/Cocoa.h>
#ifndef BigSurToolbar_h
#define BigSurToolbar_h
/* Backport the toolbarStyle property to allow compilation with older SDKs*/
#ifndef __MAC_10_16
@ -26,5 +24,3 @@ typedef NS_ENUM(NSInteger, NSWindowToolbarStyle) {
@end
#endif
#endif

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
Cocoa/CPU~solid.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 283 B

BIN
Cocoa/CPU~solid@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 565 B

BIN
Cocoa/CPU~solid~dark.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 290 B

BIN
Cocoa/CPU~solid~dark@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 584 B

Binary file not shown.

BIN
Cocoa/ContinueTemplate.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 314 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 462 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 849 B

BIN
Cocoa/Display~solid.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 231 B

BIN
Cocoa/Display~solid@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 435 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 234 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 433 B

View File

@ -1,57 +1,76 @@
#import <Cocoa/Cocoa.h>
#include "GBView.h"
#include "GBImageView.h"
#include "GBSplitView.h"
#include "GBVisualizerView.h"
#include "GBOSDView.h"
#import "GBView.h"
#import "GBImageView.h"
#import "GBSplitView.h"
#import "GBVisualizerView.h"
#import "GBOSDView.h"
#import "GBDebuggerButton.h"
@class GBCheatWindowController;
@class GBPaletteView;
@class GBObjectView;
@interface Document : NSDocument <NSWindowDelegate, GBImageViewDelegate, NSTableViewDataSource, NSTableViewDelegate, NSSplitViewDelegate>
@property (nonatomic, readonly) GB_gameboy_t *gb;
@property (nonatomic, strong) IBOutlet GBView *view;
@property (nonatomic, strong) IBOutlet NSTextView *consoleOutput;
@property (nonatomic, strong) IBOutlet NSPanel *consoleWindow;
@property (nonatomic, strong) IBOutlet NSTextField *consoleInput;
@property (nonatomic, strong) IBOutlet NSWindow *mainWindow;
@property (nonatomic, strong) IBOutlet NSView *memoryView;
@property (nonatomic, strong) IBOutlet NSPanel *memoryWindow;
@property (nonatomic, readonly) GB_gameboy_t *gameboy;
@property (nonatomic, strong) IBOutlet NSTextField *memoryBankInput;
@property (nonatomic, strong) IBOutlet NSToolbarItem *memoryBankItem;
@property (nonatomic, strong) IBOutlet GBImageView *tilesetImageView;
@property (nonatomic, strong) IBOutlet NSPopUpButton *tilesetPaletteButton;
@property (nonatomic, strong) IBOutlet GBImageView *tilemapImageView;
@property (nonatomic, strong) IBOutlet NSPopUpButton *tilemapPaletteButton;
@property (nonatomic, strong) IBOutlet NSPopUpButton *tilemapMapButton;
@property (nonatomic, strong) IBOutlet NSPopUpButton *TilemapSetButton;
@property (nonatomic, strong) IBOutlet NSButton *gridButton;
@property (nonatomic, strong) IBOutlet NSTabView *vramTabView;
@property (nonatomic, strong) IBOutlet NSPanel *vramWindow;
@property (nonatomic, strong) IBOutlet NSTextField *vramStatusLabel;
@property (nonatomic, strong) IBOutlet NSTableView *paletteTableView;
@property (nonatomic, strong) IBOutlet NSTableView *objectsTableView;
@property (nonatomic, strong) IBOutlet NSPanel *printerFeedWindow;
@property (nonatomic, strong) IBOutlet NSImageView *feedImageView;
@property (nonatomic, strong) IBOutlet NSTextView *debuggerSideViewInput;
@property (nonatomic, strong) IBOutlet NSTextView *debuggerSideView;
@property (nonatomic, strong) IBOutlet GBSplitView *debuggerSplitView;
@property (nonatomic, strong) IBOutlet NSBox *debuggerVerticalLine;
@property (nonatomic, strong) IBOutlet NSPanel *cheatsWindow;
@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;
@property (strong) IBOutlet GBVisualizerView *gbsVisualizer;
@property (strong) IBOutlet GBOSDView *osdView;
@interface Document : NSDocument <NSWindowDelegate, GBImageViewDelegate, NSSplitViewDelegate>
@property (readonly) GB_gameboy_t *gb;
@property IBOutlet GBView *view;
@property IBOutlet NSTextView *consoleOutput;
@property IBOutlet NSPanel *consoleWindow;
@property IBOutlet NSTextField *consoleInput;
@property IBOutlet NSWindow *mainWindow;
@property IBOutlet NSView *memoryView;
@property IBOutlet NSPanel *memoryWindow;
@property (readonly) GB_gameboy_t *gameboy;
@property IBOutlet NSTextField *memoryBankInput;
@property IBOutlet NSToolbarItem *memoryBankItem;
@property IBOutlet NSPopUpButton *memorySpaceButton;
@property IBOutlet GBImageView *tilesetImageView;
@property IBOutlet NSPopUpButton *tilesetPaletteButton;
@property IBOutlet GBImageView *tilemapImageView;
@property IBOutlet NSPopUpButton *tilemapPaletteButton;
@property IBOutlet NSPopUpButton *tilemapMapButton;
@property IBOutlet NSPopUpButton *TilemapSetButton;
@property IBOutlet NSButton *gridButton;
@property IBOutlet NSTabView *vramTabView;
@property IBOutlet NSPanel *vramWindow;
@property IBOutlet NSTextField *vramStatusLabel;
@property IBOutlet GBPaletteView *paletteView;
@property IBOutlet GBObjectView *objectView;
@property IBOutlet NSPanel *printerFeedWindow;
@property IBOutlet NSProgressIndicator *printerSpinner;
@property IBOutlet NSImageView *feedImageView;
@property IBOutlet NSTextView *debuggerSideViewInput;
@property IBOutlet NSTextView *debuggerSideView;
@property IBOutlet GBSplitView *debuggerSplitView;
@property IBOutlet NSBox *debuggerVerticalLine;
@property IBOutlet NSPanel *cheatsWindow;
@property IBOutlet GBCheatWindowController *cheatWindowController;
@property (readonly) Document *partner;
@property (readonly) bool isSlave;
@property IBOutlet NSView *gbsPlayerView;
@property IBOutlet NSTextField *gbsTitle;
@property IBOutlet NSTextField *gbsAuthor;
@property IBOutlet NSTextField *gbsCopyright;
@property IBOutlet NSPopUpButton *gbsTracks;
@property IBOutlet NSButton *gbsPlayPauseButton;
@property IBOutlet NSButton *gbsRewindButton;
@property IBOutlet NSSegmentedControl *gbsNextPrevButton;
@property IBOutlet GBVisualizerView *gbsVisualizer;
@property IBOutlet GBOSDView *osdView;
@property (readonly) GB_oam_info_t *oamInfo;
@property uint8_t oamCount;
@property uint8_t oamHeight;
@property IBOutlet NSView *audioRecordingAccessoryView;
@property IBOutlet NSPopUpButton *audioFormatButton;
@property IBOutlet NSVisualEffectView *debuggerSidebarEffectView API_AVAILABLE(macos(10.10));
@property IBOutlet GBDebuggerButton *debuggerContinueButton;
@property IBOutlet GBDebuggerButton *debuggerNextButton;
@property IBOutlet GBDebuggerButton *debuggerStepButton;
@property IBOutlet GBDebuggerButton *debuggerFinishButton;
@property (strong) IBOutlet GBDebuggerButton *debuggerBackstepButton;
+ (NSImage *) imageFromData:(NSData *)data width:(NSUInteger) width height:(NSUInteger) height scale:(double) scale;
-(uint8_t) readMemory:(uint16_t) addr;
-(void) writeMemory:(uint16_t) addr value:(uint8_t)value;
-(void) performAtomicBlock: (void (^)())block;

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14868" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="21507" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14868"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="21507"/>
<capability name="System colors introduced in macOS 10.14" minToolsVersion="10.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
@ -14,21 +15,29 @@
<outlet property="consoleInput" destination="l22-S8-uji" id="Heu-am-YgB"/>
<outlet property="consoleOutput" destination="doS-dM-hnl" id="Gn5-ju-Wb0"/>
<outlet property="consoleWindow" destination="21F-Ah-yHX" id="eQ4-ug-LsT"/>
<outlet property="debuggerBackstepButton" destination="onn-R8-3vg" id="gi1-dC-tJj"/>
<outlet property="debuggerContinueButton" destination="G4h-KO-Z3g" id="Dad-5e-XYM"/>
<outlet property="debuggerFinishButton" destination="vtb-n5-Qlz" id="9vv-fJ-gxp"/>
<outlet property="debuggerNextButton" destination="cOi-Rs-9gS" id="Xjx-uM-D0u"/>
<outlet property="debuggerSideView" destination="JgV-7E-iwp" id="RaA-fw-3i1"/>
<outlet property="debuggerSideViewInput" destination="w0g-eK-jM4" id="GBf-WK-ryI"/>
<outlet property="debuggerSidebarEffectView" destination="4Z2-33-dYY" id="Ja6-bC-rQg"/>
<outlet property="debuggerSplitView" destination="pUc-ZN-vl5" id="0sG-0D-cID"/>
<outlet property="debuggerStepButton" destination="DsN-Ce-QoH" id="Ts4-uK-pvI"/>
<outlet property="debuggerVerticalLine" destination="7bR-gM-1At" id="rfy-7Z-388"/>
<outlet property="feedImageView" destination="Ar0-nN-eop" id="wHa-St-o4G"/>
<outlet property="gridButton" destination="fL6-2S-Rgd" id="jtV-jh-GHC"/>
<outlet property="mainWindow" destination="xOd-HO-29H" id="h8M-YB-vcC"/>
<outlet property="memoryBankInput" destination="rdV-q6-hc6" id="KBx-9T-2mX"/>
<outlet property="memoryBankItem" destination="bWC-FW-IYP" id="Lf2-dh-z32"/>
<outlet property="memorySpaceButton" destination="vfJ-vu-gqJ" id="ris-gF-9U7"/>
<outlet property="memoryView" destination="8hr-8o-3rN" id="fF0-rh-8ND"/>
<outlet property="memoryWindow" destination="mRm-dL-mCj" id="VPR-lu-vtI"/>
<outlet property="objectsTableView" destination="TOc-XJ-w9w" id="O4R-4Z-9hU"/>
<outlet property="objectView" destination="fIM-GT-QXJ" id="jzs-q8-Z2U"/>
<outlet property="osdView" destination="MX4-l2-7NE" id="Am7-fq-uvu"/>
<outlet property="paletteTableView" destination="gfC-d3-dmq" id="fTC-eL-Qg3"/>
<outlet property="paletteView" destination="ZuP-AU-0pA" id="ef6-27-Bci"/>
<outlet property="printerFeedWindow" destination="NdE-0B-WCf" id="yVK-cS-NOJ"/>
<outlet property="printerSpinner" destination="rrz-Uh-Nae" id="CI8-Y2-s3l"/>
<outlet property="tilemapImageView" destination="LlK-tV-bjv" id="nSY-Xd-BjZ"/>
<outlet property="tilemapMapButton" destination="YIJ-Qc-SIZ" id="BB7-Gg-7XP"/>
<outlet property="tilemapPaletteButton" destination="loB-0k-Qff" id="2Or-7l-6vn"/>
@ -43,11 +52,11 @@
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<window title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="xOd-HO-29H" userLabel="Window">
<window title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" tabbingMode="disallowed" id="xOd-HO-29H" userLabel="Window">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
<windowCollectionBehavior key="collectionBehavior" fullScreenPrimary="YES"/>
<rect key="contentRect" x="0.0" y="0.0" width="160" height="144"/>
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1417"/>
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1415"/>
<value key="minSize" type="size" width="160" height="144"/>
<view key="contentView" id="gIp-Ho-8D9">
<rect key="frame" x="0.0" y="0.0" width="160" height="144"/>
@ -77,60 +86,41 @@
</connections>
<point key="canvasLocation" x="293" y="347"/>
</window>
<window title="Debug Console" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" restorable="NO" hidesOnDeactivate="YES" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="21F-Ah-yHX" customClass="NSPanel">
<window title="Debug Console" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" restorable="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="21F-Ah-yHX" customClass="NSPanel">
<windowStyleMask key="styleMask" titled="YES" closable="YES" resizable="YES" utility="YES" HUD="YES"/>
<windowCollectionBehavior key="collectionBehavior" fullScreenAuxiliary="YES"/>
<rect key="contentRect" x="0.0" y="0.0" width="921" height="400"/>
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1417"/>
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1415"/>
<value key="minSize" type="size" width="921" height="400"/>
<view key="contentView" id="dCP-E5-7Fi">
<view key="contentView" id="dCP-E5-7Fi" customClass="GBOptionalVisualEffectView">
<rect key="frame" x="0.0" y="0.0" width="921" height="400"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" mirrorLayoutDirectionWhenInternationalizing="never" translatesAutoresizingMaskIntoConstraints="NO" id="l22-S8-uji">
<rect key="frame" x="0.0" y="0.0" width="921" height="24"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" state="on" focusRingType="none" id="p3j-nS-44f" customClass="GBTerminalTextFieldCell">
<font key="font" metaFont="fixedUser" size="11"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
<color key="backgroundColor" red="0.16470588235294117" green="0.16470588235294117" blue="0.16470588235294117" alpha="1" colorSpace="calibratedRGB"/>
<allowedInputSourceLocales>
<string>NSAllRomanInputSourcesLocaleIdentifier</string>
</allowedInputSourceLocales>
</textFieldCell>
<connections>
<action selector="consoleInput:" target="-2" id="ylQ-vw-ARS"/>
</connections>
</textField>
<box verticalHuggingPriority="750" fixedFrame="YES" boxType="separator" translatesAutoresizingMaskIntoConstraints="NO" id="960-dL-7ZY">
<rect key="frame" x="0.0" y="23" width="921" height="5"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
</box>
<box horizontalHuggingPriority="750" boxType="separator" id="7bR-gM-1At">
<rect key="frame" x="590" y="25" width="5" height="376"/>
<rect key="frame" x="590" y="0.0" width="5" height="401"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" heightSizable="YES"/>
</box>
<splitView dividerStyle="thin" vertical="YES" id="pUc-ZN-vl5" customClass="GBSplitView">
<rect key="frame" x="0.0" y="25" width="921" height="376"/>
<rect key="frame" x="0.0" y="0.0" width="921" height="401"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<customView fixedFrame="YES" id="2rj-7i-kxc">
<rect key="frame" x="0.0" y="0.0" width="591" height="376"/>
<rect key="frame" x="0.0" y="0.0" width="591" height="401"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<scrollView fixedFrame="YES" borderType="none" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="oTo-zx-o6N">
<rect key="frame" x="0.0" y="0.0" width="591" height="376"/>
<rect key="frame" x="0.0" y="52" width="591" height="349"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<clipView key="contentView" ambiguous="YES" drawsBackground="NO" copiesOnScroll="NO" id="EQe-Ad-L7S">
<rect key="frame" x="0.0" y="0.0" width="591" height="376"/>
<autoresizingMask key="autoresizingMask"/>
<clipView key="contentView" drawsBackground="NO" copiesOnScroll="NO" id="EQe-Ad-L7S">
<rect key="frame" x="0.0" y="0.0" width="591" height="349"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textView ambiguous="YES" editable="NO" drawsBackground="NO" importsGraphics="NO" richText="NO" verticallyResizable="YES" baseWritingDirection="leftToRight" findStyle="bar" allowsNonContiguousLayout="YES" id="doS-dM-hnl">
<rect key="frame" x="0.0" y="0.0" width="591" height="376"/>
<textView editable="NO" importsGraphics="NO" richText="NO" verticallyResizable="YES" baseWritingDirection="leftToRight" findStyle="bar" allowsNonContiguousLayout="YES" id="doS-dM-hnl">
<rect key="frame" x="0.0" y="0.0" width="591" height="349"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<color key="backgroundColor" red="0.14901960784313725" green="0.14901960784313725" blue="0.14901960784313725" alpha="1" colorSpace="calibratedRGB"/>
<size key="minSize" width="591" height="376"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.25" colorSpace="custom" customColorSpace="sRGB"/>
<size key="minSize" width="591" height="349"/>
<size key="maxSize" width="1160" height="10000000"/>
<color key="insertionPointColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<allowedInputSourceLocales>
@ -138,31 +128,126 @@
</allowedInputSourceLocales>
</textView>
</subviews>
<color key="backgroundColor" red="0.16470588235294117" green="0.16470588235294117" blue="0.16470588235294117" alpha="1" colorSpace="calibratedRGB"/>
</clipView>
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" doubleValue="1" horizontal="YES" id="3fZ-tl-Zi7">
<rect key="frame" x="-100" y="-100" width="87" height="18"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<scroller key="verticalScroller" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="cwi-6E-rbh">
<rect key="frame" x="575" y="0.0" width="16" height="376"/>
<rect key="frame" x="575" y="0.0" width="16" height="349"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
</scrollView>
<textField focusRingType="none" verticalHuggingPriority="750" mirrorLayoutDirectionWhenInternationalizing="never" id="l22-S8-uji">
<rect key="frame" x="0.0" y="0.0" width="593" height="26"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" state="on" focusRingType="none" drawsBackground="YES" id="p3j-nS-44f" customClass="GBTerminalTextFieldCell">
<font key="font" metaFont="fixedUser" size="11"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.25" colorSpace="custom" customColorSpace="calibratedRGB"/>
<allowedInputSourceLocales>
<string>NSAllRomanInputSourcesLocaleIdentifier</string>
</allowedInputSourceLocales>
</textFieldCell>
<connections>
<action selector="consoleInput:" target="-2" id="ylQ-vw-ARS"/>
</connections>
</textField>
<box verticalHuggingPriority="750" fixedFrame="YES" boxType="separator" translatesAutoresizingMaskIntoConstraints="NO" id="xJr-K6-B4B">
<rect key="frame" x="0.0" y="49" width="591" height="4"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
</box>
<box verticalHuggingPriority="750" fixedFrame="YES" boxType="separator" translatesAutoresizingMaskIntoConstraints="NO" id="9DF-fj-a2v">
<rect key="frame" x="0.0" y="24" width="591" height="4"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
</box>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="G4h-KO-Z3g" customClass="GBDebuggerButton">
<rect key="frame" x="2" y="26" width="26" height="26"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<buttonCell key="cell" type="bevel" title="Interrupt" alternateTitle="interrupt" bezelStyle="rounded" image="InterruptTemplate" imagePosition="only" alignment="center" imageScaling="proportionallyDown" inset="2" id="wfO-32-eG5">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<color key="contentTintColor" name="controlAccentColor" catalog="System" colorSpace="catalog"/>
<connections>
<action selector="debuggerButtonPressed:" target="-2" id="SjG-90-8dJ"/>
<outlet property="textField" destination="l22-S8-uji" id="NZ8-eJ-Uyd"/>
</connections>
</button>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="cOi-Rs-9gS" customClass="GBDebuggerButton">
<rect key="frame" x="28" y="26" width="26" height="26"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<buttonCell key="cell" type="bevel" title="Step Over" alternateTitle="next" bezelStyle="rounded" image="NextTemplate" imagePosition="only" alignment="center" enabled="NO" imageScaling="proportionallyDown" inset="2" id="buW-ke-lgF">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="debuggerButtonPressed:" target="-2" id="g7E-UM-SHV"/>
<outlet property="textField" destination="l22-S8-uji" id="1uM-AA-yHi"/>
</connections>
</button>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="DsN-Ce-QoH" customClass="GBDebuggerButton">
<rect key="frame" x="54" y="26" width="26" height="26"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<buttonCell key="cell" type="bevel" title="Step Into" alternateTitle="step" bezelStyle="rounded" image="StepTemplate" imagePosition="only" alignment="center" enabled="NO" imageScaling="proportionallyDown" inset="2" id="ATJ-Vx-zbo">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="debuggerButtonPressed:" target="-2" id="QMw-82-HKJ"/>
<outlet property="textField" destination="l22-S8-uji" id="qaF-TF-cwg"/>
</connections>
</button>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="vtb-n5-Qlz" customClass="GBDebuggerButton">
<rect key="frame" x="78" y="26" width="26" height="26"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<buttonCell key="cell" type="bevel" title="Step Out" alternateTitle="finish" bezelStyle="rounded" image="FinishTemplate" imagePosition="only" alignment="center" enabled="NO" imageScaling="proportionallyDown" inset="2" id="8mF-ii-5Hx">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="debuggerButtonPressed:" target="-2" id="krr-9m-Jfz"/>
<outlet property="textField" destination="l22-S8-uji" id="uYK-tU-BnO"/>
</connections>
</button>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="oCa-UH-1mi" customClass="GBDebuggerButton">
<rect key="frame" x="563" y="26" width="26" height="26"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
<buttonCell key="cell" type="bevel" title="Help" alternateTitle="help" bezelStyle="rounded" image="HelpTemplate" imagePosition="only" alignment="center" imageScaling="proportionallyDown" inset="2" id="tvn-kY-8FO">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="debuggerButtonPressed:" target="-2" id="PwN-rf-AcB"/>
<outlet property="textField" destination="l22-S8-uji" id="blK-B3-MWI"/>
</connections>
</button>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="onn-R8-3vg" customClass="GBDebuggerButton">
<rect key="frame" x="102" y="26" width="26" height="26"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<buttonCell key="cell" type="bevel" title="Step Backwards" alternateTitle="backstep" bezelStyle="rounded" image="BackstepTemplate" imagePosition="only" alignment="center" enabled="NO" imageScaling="proportionallyDown" inset="2" id="WtK-kD-bf6">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="debuggerButtonPressed:" target="-2" id="S9a-jV-Z6g"/>
<outlet property="textField" destination="l22-S8-uji" id="1gw-Lc-QFS"/>
</connections>
</button>
</subviews>
</customView>
<customView fixedFrame="YES" id="4Z2-33-dYY">
<rect key="frame" x="592" y="0.0" width="329" height="376"/>
<customView fixedFrame="YES" id="4Z2-33-dYY" customClass="GBOptionalVisualEffectView">
<rect key="frame" x="592" y="0.0" width="329" height="401"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<scrollView fixedFrame="YES" borderType="none" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" usesPredominantAxisScrolling="NO" scrollerKnobStyle="dark" translatesAutoresizingMaskIntoConstraints="NO" id="V9U-Ua-F4z">
<rect key="frame" x="0.0" y="329" width="329" height="47"/>
<rect key="frame" x="0.0" y="354" width="329" height="47"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
<clipView key="contentView" ambiguous="YES" drawsBackground="NO" copiesOnScroll="NO" id="YHx-TM-zIC">
<clipView key="contentView" drawsBackground="NO" copiesOnScroll="NO" id="YHx-TM-zIC">
<rect key="frame" x="0.0" y="0.0" width="329" height="47"/>
<autoresizingMask key="autoresizingMask"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textView ambiguous="YES" drawsBackground="NO" importsGraphics="NO" richText="NO" verticallyResizable="YES" allowsCharacterPickerTouchBarItem="NO" allowsUndo="YES" allowsNonContiguousLayout="YES" textCompletion="NO" spellingCorrection="YES" id="w0g-eK-jM4">
<textView drawsBackground="NO" importsGraphics="NO" richText="NO" verticallyResizable="YES" allowsCharacterPickerTouchBarItem="NO" allowsUndo="YES" allowsNonContiguousLayout="YES" textCompletion="NO" spellingCorrection="YES" id="w0g-eK-jM4">
<rect key="frame" x="0.0" y="0.0" width="329" height="47"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
@ -186,22 +271,22 @@
</scroller>
</scrollView>
<box verticalHuggingPriority="750" fixedFrame="YES" boxType="separator" translatesAutoresizingMaskIntoConstraints="NO" id="5qI-qZ-nkh">
<rect key="frame" x="0.0" y="327" width="327" height="5"/>
<rect key="frame" x="0.0" y="352" width="329" height="5"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
</box>
<scrollView fixedFrame="YES" borderType="none" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="vts-CC-ZjQ">
<rect key="frame" x="0.0" y="2" width="329" height="328"/>
<rect key="frame" x="0.0" y="0.0" width="329" height="354"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<clipView key="contentView" ambiguous="YES" drawsBackground="NO" copiesOnScroll="NO" id="Cs9-3x-ATg">
<rect key="frame" x="0.0" y="0.0" width="329" height="328"/>
<autoresizingMask key="autoresizingMask"/>
<clipView key="contentView" drawsBackground="NO" copiesOnScroll="NO" id="Cs9-3x-ATg">
<rect key="frame" x="0.0" y="0.0" width="329" height="354"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textView ambiguous="YES" editable="NO" drawsBackground="NO" importsGraphics="NO" richText="NO" verticallyResizable="YES" baseWritingDirection="leftToRight" findStyle="bar" allowsNonContiguousLayout="YES" spellingCorrection="YES" id="JgV-7E-iwp">
<rect key="frame" x="0.0" y="0.0" width="329" height="328"/>
<textView editable="NO" drawsBackground="NO" importsGraphics="NO" richText="NO" verticallyResizable="YES" baseWritingDirection="leftToRight" findStyle="bar" allowsNonContiguousLayout="YES" spellingCorrection="YES" id="JgV-7E-iwp">
<rect key="frame" x="0.0" y="0.0" width="329" height="354"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" red="0.14901960780000001" green="0.14901960780000001" blue="0.14901960780000001" alpha="1" colorSpace="calibratedRGB"/>
<size key="minSize" width="329" height="328"/>
<size key="minSize" width="329" height="354"/>
<size key="maxSize" width="1160" height="10000000"/>
<color key="insertionPointColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<allowedInputSourceLocales>
@ -216,7 +301,7 @@
<autoresizingMask key="autoresizingMask"/>
</scroller>
<scroller key="verticalScroller" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="4jm-Gm-D2R">
<rect key="frame" x="313" y="0.0" width="16" height="328"/>
<rect key="frame" x="313" y="0.0" width="16" height="354"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
</scrollView>
@ -235,11 +320,11 @@
</view>
<point key="canvasLocation" x="347.5" y="-29"/>
</window>
<window title="Memory" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" restorable="NO" hidesOnDeactivate="YES" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="mRm-dL-mCj" customClass="NSPanel">
<window title="Memory" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" restorable="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" toolbarStyle="expanded" id="mRm-dL-mCj" customClass="NSPanel">
<windowStyleMask key="styleMask" titled="YES" closable="YES" resizable="YES"/>
<windowCollectionBehavior key="collectionBehavior" fullScreenAuxiliary="YES"/>
<rect key="contentRect" x="0.0" y="0.0" width="528" height="320"/>
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1417"/>
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1415"/>
<view key="contentView" id="8hr-8o-3rN">
<rect key="frame" x="0.0" y="0.0" width="528" height="320"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
@ -318,11 +403,11 @@
<point key="canvasLocation" x="-185" y="61"/>
</window>
<menuItem title="Cartridge RAM" id="ylM-ah-PNQ"/>
<window title="VRAM Viewer" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" restorable="NO" hidesOnDeactivate="YES" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="mbr-db-iZh" customClass="NSPanel">
<window title="VRAM Viewer" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" restorable="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" toolbarStyle="expanded" id="mbr-db-iZh" customClass="NSPanel">
<windowStyleMask key="styleMask" titled="YES" closable="YES"/>
<windowCollectionBehavior key="collectionBehavior" fullScreenAuxiliary="YES"/>
<rect key="contentRect" x="0.0" y="0.0" width="512" height="432"/>
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1417"/>
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1415"/>
<view key="contentView" id="GYW-dv-Um1">
<rect key="frame" x="0.0" y="0.0" width="512" height="432"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
@ -341,17 +426,17 @@
</textFieldCell>
</textField>
<tabView fixedFrame="YES" drawsBackground="NO" type="noTabsNoBorder" initialItem="pXb-od-Wb1" translatesAutoresizingMaskIntoConstraints="NO" id="AZz-Mh-rPA">
<rect key="frame" x="0.0" y="24" width="512" height="408"/>
<rect key="frame" x="0.0" y="0.0" width="512" height="432"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<font key="font" metaFont="system"/>
<tabViewItems>
<tabViewItem label="Tileset" identifier="1" id="pXb-od-Wb1">
<view key="view" ambiguous="YES" id="lCG-Gt-XMF">
<rect key="frame" x="0.0" y="0.0" width="512" height="408"/>
<view key="view" id="lCG-Gt-XMF">
<rect key="frame" x="0.0" y="0.0" width="512" height="432"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="QcQ-ex-36R" customClass="GBImageView">
<rect key="frame" x="0.0" y="0.0" width="512" height="384"/>
<rect key="frame" x="0.0" y="24" width="512" height="384"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="axesIndependently" id="pXc-O8-jg5"/>
<connections>
@ -360,7 +445,7 @@
</connections>
</imageView>
<popUpButton focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="TLv-xS-X5K">
<rect key="frame" x="4" y="388" width="128" height="17"/>
<rect key="frame" x="4" y="412" width="128" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="roundRect" title="None" bezelStyle="roundedRect" alignment="left" controlSize="mini" lineBreakMode="truncatingTail" state="on" borderStyle="border" focusRingType="none" imageScaling="proportionallyDown" inset="2" selectedItem="G8p-CH-PlV" id="1jI-s4-4YY">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
@ -392,7 +477,7 @@
</connections>
</popUpButton>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="fL6-2S-Rgd">
<rect key="frame" x="452" y="388" width="56" height="17"/>
<rect key="frame" x="452" y="412" width="56" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="roundRect" title="Grid" bezelStyle="roundedRect" alignment="center" controlSize="mini" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="pDn-9a-Se6">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES" changeBackground="YES" changeGray="YES"/>
@ -407,11 +492,11 @@
</tabViewItem>
<tabViewItem label="Tilemap" identifier="2" id="kaY-Wy-Yt1">
<view key="view" id="c6j-cM-dDx">
<rect key="frame" x="0.0" y="0.0" width="512" height="408"/>
<rect key="frame" x="0.0" y="0.0" width="512" height="432"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="DhM-Em-hj7">
<rect key="frame" x="385" y="388" width="63" height="17"/>
<rect key="frame" x="385" y="412" width="63" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="roundRect" title="Scrolling" bezelStyle="roundedRect" alignment="center" controlSize="mini" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="P2E-5t-BN9">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES" changeBackground="YES" changeGray="YES"/>
@ -422,7 +507,7 @@
</connections>
</button>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="LlK-tV-bjv" customClass="GBImageView">
<rect key="frame" x="0.0" y="0.0" width="512" height="384"/>
<rect key="frame" x="0.0" y="24" width="512" height="384"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="axesIndependently" id="RvP-El-ILj"/>
<connections>
@ -431,7 +516,7 @@
</connections>
</imageView>
<popUpButton focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="loB-0k-Qff">
<rect key="frame" x="4" y="388" width="128" height="17"/>
<rect key="frame" x="4" y="412" width="128" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="roundRect" title="Effective Palettes" bezelStyle="roundedRect" alignment="left" controlSize="mini" lineBreakMode="truncatingTail" state="on" borderStyle="border" focusRingType="none" imageScaling="proportionallyDown" inset="2" selectedItem="oUH-Sa-Ec1" id="Eij-Cp-URa">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
@ -464,7 +549,7 @@
</connections>
</popUpButton>
<popUpButton focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="YIJ-Qc-SIZ">
<rect key="frame" x="135" y="388" width="96" height="17"/>
<rect key="frame" x="135" y="412" width="96" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="roundRect" title="Effective Tilemap" bezelStyle="roundedRect" alignment="left" controlSize="mini" lineBreakMode="truncatingTail" state="on" borderStyle="border" focusRingType="none" imageScaling="proportionallyDown" inset="2" selectedItem="XRF-Vj-3gs" id="3W1-Db-wDn">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
@ -482,7 +567,7 @@
</connections>
</popUpButton>
<popUpButton focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="k4c-Vg-MBu">
<rect key="frame" x="235" y="388" width="96" height="17"/>
<rect key="frame" x="235" y="412" width="96" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="roundRect" title="Effective Tileset" bezelStyle="roundedRect" alignment="left" controlSize="mini" lineBreakMode="truncatingTail" state="on" borderStyle="border" focusRingType="none" imageScaling="proportionallyDown" inset="2" selectedItem="CRe-dX-rzY" id="h53-sb-Odg">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
@ -504,250 +589,45 @@
</tabViewItem>
<tabViewItem label="Objects" identifier="" id="a08-eg-Maw">
<view key="view" id="EiO-p0-3xn">
<rect key="frame" x="0.0" y="0.0" width="512" height="408"/>
<rect key="frame" x="0.0" y="0.0" width="512" height="432"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<scrollView fixedFrame="YES" borderType="none" autohidesScrollers="YES" horizontalLineScroll="20" horizontalPageScroll="10" verticalLineScroll="20" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="krD-gH-o5I">
<rect key="frame" x="0.0" y="0.0" width="512" height="408"/>
<scrollView fixedFrame="YES" borderType="none" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Vhq-K4-baH">
<rect key="frame" x="0.0" y="0.0" width="512" height="432"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<clipView key="contentView" ambiguous="YES" drawsBackground="NO" id="3VT-AA-xVT">
<rect key="frame" x="0.0" y="0.0" width="512" height="408"/>
<clipView key="contentView" ambiguous="YES" id="JYu-CM-49p">
<rect key="frame" x="0.0" y="0.0" width="512" height="432"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="none" alternatingRowBackgroundColors="YES" columnReordering="NO" columnResizing="NO" multipleSelection="NO" autosaveColumns="NO" typeSelect="NO" rowHeight="18" headerView="of1-KC-dXC" id="TOc-XJ-w9w">
<rect key="frame" x="0.0" y="0.0" width="512" height="391"/>
<autoresizingMask key="autoresizingMask"/>
<size key="intercellSpacing" width="3" height="2"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
<color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
<tableColumns>
<tableColumn width="32" minWidth="32" maxWidth="1000" id="hRp-Kh-nWC">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<imageCell key="dataCell" scrollable="YES" lineBreakMode="clipping" refusesFirstResponder="YES" alignment="left" id="Jhk-KW-Hoc" customClass="GBImageCell"/>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
</tableColumn>
<tableColumn width="28" minWidth="28" maxWidth="1000" id="Vrl-in-npm">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="center" title="X">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" id="czf-Bn-nZN">
<font key="font" metaFont="system"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
</tableColumn>
<tableColumn width="28" minWidth="28" maxWidth="1000" id="636-Td-Zcm">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="center" title="Y">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<textFieldCell key="dataCell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" id="zh6-hI-Ss8">
<font key="font" metaFont="system"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
</tableColumn>
<tableColumn width="28" minWidth="28" maxWidth="1000" id="vMj-ya-pGG">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="center" title="Tile">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<textFieldCell key="dataCell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" id="0s8-eF-rgd">
<font key="font" metaFont="system"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
</tableColumn>
<tableColumn width="68" minWidth="40" maxWidth="1000" id="U5B-eh-aer">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="center" title="Tile Addr.">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<textFieldCell key="dataCell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" id="PtW-wF-c0o">
<font key="font" metaFont="system"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
</tableColumn>
<tableColumn width="68" minWidth="40" maxWidth="1000" id="LSg-Sc-Sdr">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="center" title="OAM Addr.">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<textFieldCell key="dataCell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" id="a7k-83-iPE">
<font key="font" metaFont="system"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
</tableColumn>
<tableColumn width="65" minWidth="40" maxWidth="1000" id="S9B-Hc-6Ee">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="center" title="Attributes">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<textFieldCell key="dataCell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" id="sJa-oU-QZp">
<font key="font" metaFont="system"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
</tableColumn>
<tableColumn width="168" minWidth="40" maxWidth="1000" id="8fQ-EC-E5K">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="center">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<textFieldCell key="dataCell" selectable="YES" editable="YES" id="YSL-O0-ZwU">
<font key="font" metaFont="miniSystem"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
</tableColumn>
</tableColumns>
<connections>
<outlet property="dataSource" destination="-2" id="ypl-SU-g5n"/>
<outlet property="delegate" destination="-2" id="HzE-pT-cX5"/>
</connections>
</tableView>
<view ambiguous="YES" id="fIM-GT-QXJ" customClass="GBObjectView">
<rect key="frame" x="0.0" y="0.0" width="512" height="706"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
</view>
</subviews>
<nil key="backgroundColor"/>
<edgeInsets key="contentInsets" left="0.0" right="0.0" top="0.0" bottom="0.0"/>
</clipView>
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="n53-qA-NpY">
<rect key="frame" x="0.0" y="392" width="512" height="16"/>
<edgeInsets key="contentInsets" left="0.0" right="0.0" top="0.0" bottom="0.0"/>
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="zUB-xg-cXq">
<rect key="frame" x="-100" y="-100" width="512" height="16"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="mqp-NY-g8d">
<rect key="frame" x="224" y="17" width="15" height="102"/>
<scroller key="verticalScroller" wantsLayer="YES" verticalHuggingPriority="750" doubleValue="1" horizontal="NO" id="jMe-gO-ERw">
<rect key="frame" x="496" y="0.0" width="16" height="432"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<tableHeaderView key="headerView" id="of1-KC-dXC">
<rect key="frame" x="0.0" y="0.0" width="512" height="17"/>
<autoresizingMask key="autoresizingMask"/>
</tableHeaderView>
</scrollView>
</subviews>
</view>
</tabViewItem>
<tabViewItem label="Palettes" identifier="" id="vLb-Nh-UYE">
<view key="view" id="qIg-ci-WTA">
<rect key="frame" x="0.0" y="0.0" width="512" height="408"/>
<rect key="frame" x="0.0" y="0.0" width="512" height="432"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<scrollView fixedFrame="YES" borderType="none" horizontalLineScroll="20" horizontalPageScroll="10" verticalLineScroll="20" verticalPageScroll="10" hasHorizontalScroller="NO" hasVerticalScroller="NO" usesPredominantAxisScrolling="NO" horizontalScrollElasticity="none" verticalScrollElasticity="none" translatesAutoresizingMaskIntoConstraints="NO" id="iSs-Ow-wwb">
<rect key="frame" x="0.0" y="0.0" width="512" height="408"/>
<customView fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="ZuP-AU-0pA" customClass="GBPaletteView">
<rect key="frame" x="0.0" y="0.0" width="512" height="432"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<clipView key="contentView" ambiguous="YES" id="bP9-su-zQw">
<rect key="frame" x="0.0" y="0.0" width="512" height="408"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnReordering="NO" columnResizing="NO" multipleSelection="NO" autosaveColumns="NO" typeSelect="NO" rowHeight="18" id="gfC-d3-dmq">
<rect key="frame" x="0.0" y="0.0" width="512" height="408"/>
<autoresizingMask key="autoresizingMask"/>
<size key="intercellSpacing" width="3" height="2"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
<tableViewGridLines key="gridStyleMask" dashed="YES"/>
<color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
<tableColumns>
<tableColumn width="125" minWidth="40" maxWidth="1000" id="Aje-FQ-fUw">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" refusesFirstResponder="YES" title="Text Cell" id="Ygb-xo-X8v">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
</tableColumn>
<tableColumn width="93" minWidth="40" maxWidth="1000" id="4EN-0j-lda">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" refusesFirstResponder="YES" title="Text Cell" id="3a2-A4-XQW" customClass="GBColorCell">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
</tableColumn>
<tableColumn width="93" minWidth="10" maxWidth="3.4028234663852886e+38" id="syl-os-nSf">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" refusesFirstResponder="YES" alignment="left" title="Text Cell" id="3ql-bn-AxP" customClass="GBColorCell">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
</tableColumn>
<tableColumn width="93" minWidth="10" maxWidth="3.4028234663852886e+38" id="Qw3-u2-c1s">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" refusesFirstResponder="YES" title="Text Cell" id="9at-mH-PWc" customClass="GBColorCell">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
</tableColumn>
<tableColumn width="93" minWidth="10" maxWidth="3.4028234663852886e+38" id="gTl-gN-qLn">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" refusesFirstResponder="YES" alignment="left" title="Text Cell" id="dY8-Zu-d4t" customClass="GBColorCell">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
</tableColumn>
</tableColumns>
<connections>
<outlet property="dataSource" destination="-2" id="F2f-nK-QTN"/>
<outlet property="delegate" destination="-2" id="7hn-MM-DDD"/>
</connections>
</tableView>
</subviews>
</clipView>
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="OS3-sw-bjv">
<rect key="frame" x="-100" y="-100" width="510" height="16"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="4HA-2m-8TZ">
<rect key="frame" x="-100" y="-100" width="15" height="102"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
</scrollView>
</customView>
</subviews>
</view>
</tabViewItem>
@ -792,7 +672,7 @@
<window title="Printer Feed" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" restorable="NO" hidesOnDeactivate="YES" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="NdE-0B-WCf" customClass="NSPanel">
<windowStyleMask key="styleMask" titled="YES" closable="YES" resizable="YES"/>
<rect key="contentRect" x="0.0" y="0.0" width="320" height="288"/>
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1417"/>
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1415"/>
<value key="minSize" type="size" width="320" height="16"/>
<view key="contentView" id="RRS-aa-bPT">
<rect key="frame" x="0.0" y="0.0" width="320" height="288"/>
@ -808,18 +688,33 @@
<toolbar key="toolbar" implicitIdentifier="1FF86A2B-6637-4EE6-A25A-7298D79AE84E" autosavesConfiguration="NO" allowsUserCustomization="NO" displayMode="iconAndLabel" sizeMode="regular" id="gH3-SH-7il">
<allowedToolbarItems>
<toolbarItem implicitItemIdentifier="15EB8D49-8C6E-42F2-9F7F-F7D7A0BBDAAF" label="Save" paletteLabel="Save" tag="-1" image="NSFolder" id="CBz-1N-o0Q">
<size key="minSize" width="22" height="22"/>
<size key="maxSize" width="22" height="22"/>
<connections>
<action selector="savePrinterFeed:" target="-2" id="Dm3-h0-ch4"/>
</connections>
</toolbarItem>
<toolbarItem implicitItemIdentifier="NSToolbarPrintItem" explicitItemIdentifier="Print" id="mtd-zS-DXa"/>
<toolbarItem implicitItemIdentifier="0496ED81-2A03-445D-9818-97386BC78CBC" label="Print" paletteLabel="Print" tag="-1" image="printer" catalog="system" bordered="YES" id="L6N-6G-UWl">
<size key="minSize" width="30" height="25"/>
<size key="maxSize" width="30" height="25"/>
<connections>
<action selector="printDocument:" target="-1" id="RLy-7k-iIR"/>
</connections>
</toolbarItem>
<toolbarItem implicitItemIdentifier="NSToolbarSpaceItem" id="AoG-LH-J4b"/>
<toolbarItem implicitItemIdentifier="NSToolbarFlexibleSpaceItem" id="Q0x-n5-Q2Y"/>
<toolbarItem implicitItemIdentifier="E8F74F8F-6DE2-4774-A690-F71D92CD932E" label="" paletteLabel="" tag="-1" sizingBehavior="auto" id="CJX-Ff-7iQ">
<nil key="toolTip"/>
<progressIndicator key="view" wantsLayer="YES" maxValue="100" displayedWhenStopped="NO" indeterminate="YES" controlSize="small" style="spinning" id="rrz-Uh-Nae">
<rect key="frame" x="0.0" y="14" width="16" height="16"/>
<autoresizingMask key="autoresizingMask"/>
</progressIndicator>
</toolbarItem>
</allowedToolbarItems>
<defaultToolbarItems>
<toolbarItem reference="CJX-Ff-7iQ"/>
<toolbarItem reference="CBz-1N-o0Q"/>
<toolbarItem reference="Q0x-n5-Q2Y"/>
<toolbarItem reference="mtd-zS-DXa"/>
<toolbarItem reference="L6N-6G-UWl"/>
</defaultToolbarItems>
</toolbar>
<point key="canvasLocation" x="-159" y="356"/>
@ -827,7 +722,7 @@
<window title="Cheats" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" restorable="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" id="4Yb-Np-JrF" customClass="NSPanel">
<windowStyleMask key="styleMask" titled="YES" closable="YES"/>
<rect key="contentRect" x="0.0" y="0.0" width="692" height="272"/>
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1417"/>
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1415"/>
<view key="contentView" id="gBP-5p-BTh">
<rect key="frame" x="0.0" y="0.0" width="692" height="272"/>
<autoresizingMask key="autoresizingMask"/>
@ -840,7 +735,7 @@
<rect key="frame" x="16" y="174" width="154" height="19"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="To value:" id="Ycx-oE-aA4">
<font key="font" usesAppearanceFont="YES"/>
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
@ -909,7 +804,7 @@
</connections>
</textField>
<button verticalHuggingPriority="750" id="C3V-Ep-bMj">
<rect key="frame" x="202.5" y="12" width="83" height="23"/>
<rect key="frame" x="203" y="12" width="83" height="23"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="roundTextured" title="Import" bezelStyle="texturedRounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="mMP-KW-YNy">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
@ -931,7 +826,6 @@
</allowedInputSourceLocales>
</textFieldCell>
<connections>
<action selector="consoleInput:" target="-2" id="sMf-aM-OvR"/>
<outlet property="delegate" destination="v7q-gT-jHT" id="79v-33-R1X"/>
</connections>
</textField>
@ -947,7 +841,6 @@
</allowedInputSourceLocales>
</textFieldCell>
<connections>
<action selector="consoleInput:" target="-2" id="SYC-cW-RjC"/>
<outlet property="delegate" destination="v7q-gT-jHT" id="P69-nT-oOt"/>
</connections>
</textField>
@ -963,7 +856,6 @@
</allowedInputSourceLocales>
</textFieldCell>
<connections>
<action selector="consoleInput:" target="-2" id="io6-ha-QNb"/>
<outlet property="delegate" destination="v7q-gT-jHT" id="6RH-dg-SL7"/>
</connections>
</textField>
@ -971,7 +863,7 @@
<rect key="frame" x="16" y="204" width="152" height="19"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="Change byte at address:" id="xwa-TF-eY1">
<font key="font" usesAppearanceFont="YES"/>
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
@ -982,11 +874,11 @@
<rect key="frame" x="293" y="-1" width="400" height="275"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" heightSizable="YES"/>
<clipView key="contentView" id="mzf-yu-RID">
<rect key="frame" x="1" y="0.0" width="398" height="274"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<rect key="frame" x="1" y="1" width="398" height="273"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="none" alternatingRowBackgroundColors="YES" columnReordering="NO" columnResizing="NO" multipleSelection="NO" emptySelection="NO" autosaveColumns="NO" typeSelect="NO" headerView="pvX-uJ-qK5" id="tA3-8T-bxb">
<rect key="frame" x="0.0" y="0.0" width="398" height="249"/>
<rect key="frame" x="0.0" y="0.0" width="398" height="256"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<size key="intercellSpacing" width="3" height="2"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
@ -994,19 +886,17 @@
<tableColumns>
<tableColumn width="32" minWidth="32" maxWidth="1000" id="JNd-HW-LvS">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<buttonCell key="dataCell" type="bevel" bezelStyle="regularSquare" image="NSStopProgressFreestandingTemplate" imagePosition="only" inset="2" id="5xh-hN-jHH">
<buttonCell key="dataCell" type="inline" bezelStyle="inline" image="NSStopProgressFreestandingTemplate" imagePosition="only" alignment="center" imageScaling="proportionallyDown" inset="2" id="5xh-hN-jHH">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
</tableColumn>
<tableColumn width="60" minWidth="40" maxWidth="1000" id="9DZ-oW-Scx">
<tableColumn width="40" minWidth="40" maxWidth="1000" id="9DZ-oW-Scx">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="Enabled">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
@ -1016,9 +906,8 @@
</buttonCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
</tableColumn>
<tableColumn editable="NO" width="160" minWidth="40" maxWidth="1000" id="4Qa-FQ-QWY">
<tableColumn editable="NO" width="142" minWidth="40" maxWidth="1000" id="4Qa-FQ-QWY">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="Description">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
@ -1031,7 +920,6 @@
</tableColumn>
<tableColumn editable="NO" width="134" minWidth="40" maxWidth="1000" id="ACq-gU-K36">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="Action">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
@ -1051,15 +939,15 @@
</subviews>
</clipView>
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="3Hg-LL-VqH">
<rect key="frame" x="1" y="258" width="398" height="16"/>
<rect key="frame" x="1" y="119" width="223" height="15"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" doubleValue="1" horizontal="NO" id="zET-KH-qF4">
<rect key="frame" x="224" y="17" width="15" height="102"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<tableHeaderView key="headerView" id="pvX-uJ-qK5">
<rect key="frame" x="0.0" y="0.0" width="398" height="25"/>
<tableHeaderView key="headerView" wantsLayer="YES" id="pvX-uJ-qK5">
<rect key="frame" x="0.0" y="0.0" width="398" height="17"/>
<autoresizingMask key="autoresizingMask"/>
</tableHeaderView>
</scrollView>
@ -1082,7 +970,14 @@
</customObject>
</objects>
<resources>
<image name="BackstepTemplate" width="14" height="14"/>
<image name="FinishTemplate" width="14" height="14"/>
<image name="HelpTemplate" width="14" height="14"/>
<image name="InterruptTemplate" width="14" height="14"/>
<image name="NSFolder" width="32" height="32"/>
<image name="NSStopProgressFreestandingTemplate" width="14" height="14"/>
<image name="NSStopProgressFreestandingTemplate" width="15" height="15"/>
<image name="NextTemplate" width="14" height="14"/>
<image name="StepTemplate" width="14" height="14"/>
<image name="printer" catalog="system" width="18" height="16"/>
</resources>
</document>

BIN
Cocoa/FinishTemplate.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 B

BIN
Cocoa/FinishTemplate@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 298 B

View File

@ -1,7 +1,8 @@
#import <Cocoa/Cocoa.h>
#import <WebKit/WebKit.h>
#import <JoyKit/JoyKit.h>
@interface AppDelegate : NSObject <NSApplicationDelegate, NSUserNotificationCenterDelegate, NSMenuDelegate, WebUIDelegate, WebPolicyDelegate, WebFrameLoadDelegate>
@interface GBApp : NSApplication <NSApplicationDelegate, NSUserNotificationCenterDelegate, NSMenuDelegate, WebUIDelegate, WebPolicyDelegate, WebFrameLoadDelegate, JOYListener>
@property (nonatomic, strong) IBOutlet NSWindow *preferencesWindow;
@property (nonatomic, strong) IBOutlet NSView *graphicsTab;

View File

@ -1,10 +1,13 @@
#import "AppDelegate.h"
#include "GBButtons.h"
#include "GBView.h"
#include <Core/gb.h>
#import "GBApp.h"
#import "GBButtons.h"
#import "GBView.h"
#import "Document.h"
#import "GBJoyConManager.h"
#import <Core/gb.h>
#import <Carbon/Carbon.h>
#import <JoyKit/JoyKit.h>
#import <WebKit/WebKit.h>
#import <mach-o/dyld.h>
#define UPDATE_SERVER "https://sameboy.github.io"
@ -16,10 +19,9 @@ static uint32_t color_to_int(NSColor *color)
((unsigned)(color.blueComponent * 0xFF));
}
@implementation AppDelegate
@implementation GBApp
{
NSWindow *preferences_window;
NSArray<NSView *> *preferences_tabs;
NSArray<NSView *> *_preferencesTabs;
NSString *_lastVersion;
NSString *_updateURL;
NSURLSessionDownloadTask *_updateTask;
@ -31,10 +33,15 @@ static uint32_t color_to_int(NSColor *color)
UPDATE_FAILED,
} _updateState;
NSString *_downloadDirectory;
AuthorizationRef _auth;
bool _simulatingMenuEvent;
}
- (void) applicationDidFinishLaunching:(NSNotification *)notification
{
// Refresh icon if launched via a software update
[NSApplication sharedApplication].applicationIconImage = [NSImage imageNamed:@"AppIcon"];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
for (unsigned i = 0; i < GBButtonCount; i++) {
if ([[defaults objectForKey:button_to_preference_name(i, 0)] isKindOfClass:[NSString class]]) {
@ -57,13 +64,14 @@ static uint32_t color_to_int(NSColor *color)
@"GBSlow-Motion": @(kVK_Shift),
@"GBFilter": @"NearestNeighbor",
@"GBColorCorrection": @(GB_COLOR_CORRECTION_EMULATE_HARDWARE),
@"GBHighpassFilter": @(GB_HIGHPASS_REMOVE_DC_OFFSET),
@"GBRewindLength": @(10),
@"GBColorCorrection": @(GB_COLOR_CORRECTION_MODERN_BALANCED),
@"GBHighpassFilter": @(GB_HIGHPASS_ACCURATE),
@"GBRewindLength": @(120),
@"GBFrameBlendingMode": @([defaults boolForKey:@"DisableFrameBlending"]? GB_FRAME_BLENDING_MODE_DISABLED : GB_FRAME_BLENDING_MODE_ACCURATE),
@"GBDMGModel": @(GB_MODEL_DMG_B),
@"GBCGBModel": @(GB_MODEL_CGB_E),
@"GBAGBModel": @(GB_MODEL_AGB_A),
@"GBSGBModel": @(GB_MODEL_SGB2),
@"GBRumbleMode": @(GB_RUMBLE_CARTRIDGE_ONLY),
@ -71,6 +79,72 @@ static uint32_t color_to_int(NSColor *color)
@"GBMBC7JoystickOverride": @NO,
@"GBMBC7AllowMouse": @YES,
@"GBJoyConAutoPair": @YES,
@"GBJoyConsDefaultsToHorizontal": @YES,
// Default themes
@"GBThemes": @{
@"Desert": @{
@"BrightnessBias": @0.0,
@"Colors": @[@0xff302f3e, @0xff576674, @0xff839ba4, @0xffb1d0d2, @0xffb7d7d8],
@"DisabledLCDColor": @YES,
@"HueBias": @0.10087773904382469,
@"HueBiasStrength": @0.062142056772908363,
@"Manual": @NO,
},
@"Evening": @{
@"BrightnessBias": @-0.10168700106441975,
@"Colors": @[@0xff362601, @0xff695518, @0xff899853, @0xffa6e4ae, @0xffa9eebb],
@"DisabledLCDColor": @YES,
@"HueBias": @0.60027079191058874,
@"HueBiasStrength": @0.33816297305747867,
@"Manual": @NO,
},
@"Fog": @{
@"BrightnessBias": @0.0,
@"Colors": @[@0xff373c34, @0xff737256, @0xff9da386, @0xffc3d2bf, @0xffc7d8c6],
@"DisabledLCDColor": @YES,
@"HueBias": @0.55750435756972117,
@"HueBiasStrength": @0.18424738545816732,
@"Manual": @NO,
},
@"Magic Eggplant": @{
@"BrightnessBias": @0.0,
@"Colors": @[@0xff3c2136, @0xff942e84, @0xffc7699d, @0xfff1e4b0, @0xfff6f9b2],
@"DisabledLCDColor": @YES,
@"HueBias": @0.87717878486055778,
@"HueBiasStrength": @0.65018052788844627,
@"Manual": @NO,
},
@"Radioactive Pea": @{
@"BrightnessBias": @-0.48079556772908372,
@"Colors": @[@0xff215200, @0xff1f7306, @0xff169e34, @0xff03ceb8, @0xff00d4d1],
@"DisabledLCDColor": @YES,
@"HueBias": @0.3795131972111554,
@"HueBiasStrength": @0.34337649402390436,
@"Manual": @NO,
},
@"Seaweed": @{
@"BrightnessBias": @-0.28532744023904377,
@"Colors": @[@0xff3f0015, @0xff426532, @0xff58a778, @0xff95e0df, @0xffa0e7ee],
@"DisabledLCDColor": @YES,
@"HueBias": @0.2694067480079681,
@"HueBiasStrength": @0.51565612549800799,
@"Manual": @NO,
},
@"Twilight": @{
@"BrightnessBias": @-0.091789093625498031,
@"Colors": @[@0xff3f0015, @0xff461286, @0xff6254bd, @0xff97d3e9, @0xffa0e7ee],
@"DisabledLCDColor": @YES,
@"HueBias": @0.0,
@"HueBiasStrength": @0.49710532868525897,
@"Manual": @NO,
},
},
@"NSToolbarItemForcesStandardSize": @YES, // Forces Monterey to resepect toolbar item sizes
@"NSToolbarItemWarnOnMinMaxSize": @NO, // Not going to use Constraints, Apple
}];
[JOYController startOnRunLoop:[NSRunLoop currentRunLoop] withOptions:@{
@ -78,6 +152,10 @@ static uint32_t color_to_int(NSColor *color)
JOYHatsEmulateButtonsKey: @YES,
}];
[GBJoyConManager sharedInstance]; // Starts handling Joy-Cons
[JOYController registerListener:self];
if ([[NSUserDefaults standardUserDefaults] boolForKey:@"GBNotificationsUsed"]) {
[NSUserNotificationCenter defaultUserNotificationCenter].delegate = self;
}
@ -101,10 +179,10 @@ static uint32_t color_to_int(NSColor *color)
- (IBAction)switchPreferencesTab:(id)sender
{
for (NSView *view in preferences_tabs) {
for (NSView *view in _preferencesTabs) {
[view removeFromSuperview];
}
NSView *tab = preferences_tabs[[sender tag]];
NSView *tab = _preferencesTabs[[sender tag]];
NSRect old = [_preferencesWindow frame];
NSRect new = [_preferencesWindow frameRectForContentRect:tab.frame];
new.origin.x = old.origin.x;
@ -148,12 +226,16 @@ static uint32_t color_to_int(NSColor *color)
[[NSBundle mainBundle] loadNibNamed:@"Preferences" owner:self topLevelObjects:&objects];
NSToolbarItem *first_toolbar_item = [_preferencesWindow.toolbar.items firstObject];
_preferencesWindow.toolbar.selectedItemIdentifier = [first_toolbar_item itemIdentifier];
preferences_tabs = @[self.emulationTab, self.graphicsTab, self.audioTab, self.controlsTab, self.updatesTab];
_preferencesTabs = @[self.emulationTab, self.graphicsTab, self.audioTab, self.controlsTab, self.updatesTab];
[self switchPreferencesTab:first_toolbar_item];
[_preferencesWindow center];
#ifndef UPDATE_SUPPORT
[_preferencesWindow.toolbar removeItemAtIndex:4];
#endif
if (@available(macOS 11.0, *)) {
[_preferencesWindow.toolbar insertItemWithItemIdentifier:NSToolbarFlexibleSpaceItemIdentifier atIndex:0];
[_preferencesWindow.toolbar insertItemWithItemIdentifier:NSToolbarFlexibleSpaceItemIdentifier atIndex:_preferencesWindow.toolbar.items.count];
}
}
[_preferencesWindow makeKeyAndOrderFront:self];
}
@ -203,7 +285,15 @@ static uint32_t color_to_int(NSColor *color)
dispatch_async(dispatch_get_main_queue(), ^{
NSArray *objects;
[[NSBundle mainBundle] loadNibNamed:@"UpdateWindow" owner:self topLevelObjects:&objects];
self.updateChanges.preferences.standardFontFamily = [NSFont systemFontOfSize:0].familyName;
if (@available(macOS 10.11, *)) {
self.updateChanges.preferences.standardFontFamily = @"-apple-system";
}
else if (@available(macOS 10.10, *)) {
self.updateChanges.preferences.standardFontFamily = @"Helvetica Neue";
}
else {
self.updateChanges.preferences.standardFontFamily = @"Lucida Grande";
}
self.updateChanges.preferences.fixedFontFamily = @"Menlo";
self.updateChanges.drawsBackground = false;
[self.updateChanges.mainFrame loadHTMLString:html baseURL:nil];
@ -297,18 +387,65 @@ static uint32_t color_to_int(NSColor *color)
[self.updateWindow performClose:sender];
}
- (bool)executePath:(NSString *)path withArguments:(NSArray <NSString *> *)arguments
{
if (!_auth) {
NSTask *task = [[NSTask alloc] init];
task.launchPath = path;
task.arguments = arguments;
[task launch];
[task waitUntilExit];
return task.terminationStatus == 0 && task.terminationReason == NSTaskTerminationReasonExit;
}
char *argv[arguments.count + 1];
argv[arguments.count] = NULL;
for (unsigned i = 0; i < arguments.count; i++) {
argv[i] = (char *)arguments[i].UTF8String;
}
return AuthorizationExecuteWithPrivileges(_auth, path.UTF8String, kAuthorizationFlagDefaults, argv, NULL) == errAuthorizationSuccess;
}
- (void)deauthorize
{
if (_auth) {
AuthorizationFree(_auth, kAuthorizationFlagDefaults);
_auth = nil;
}
}
- (IBAction)installUpdate:(id)sender
{
bool needsAuthorization = false;
if ([self executePath:@"/usr/sbin/spctl" withArguments:@[@"--status"]]) { // Succeeds when GateKeeper is on
// GateKeeper is on, we need to --add ourselves as root, else we might get a GateKeeper crash
needsAuthorization = true;
}
else if (access(_dyld_get_image_name(0), W_OK)) {
// We don't have write access, so we need authorization to update as root
needsAuthorization = true;
}
if (needsAuthorization && !_auth) {
AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagPreAuthorize | kAuthorizationFlagInteractionAllowed, &_auth);
// Make sure we can modify the bundle
if (![self executePath:@"/usr/sbin/chown" withArguments:@[@"-R", [NSString stringWithFormat:@"%d:%d", getuid(), getgid()], [NSBundle mainBundle].bundlePath]]) {
[self deauthorize];
return;
}
}
[self.updateProgressSpinner startAnimation:nil];
self.updateProgressButton.title = @"Cancel";
self.updateProgressButton.enabled = true;
self.updateProgressLabel.stringValue = @"Downloading update...";
self.updateProgressLabel.stringValue = @"Downloading update";
_updateState = UPDATE_DOWNLOADING;
_updateTask = [[NSURLSession sharedSession] downloadTaskWithURL: [NSURL URLWithString:_updateURL] completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {
_updateTask = nil;
dispatch_sync(dispatch_get_main_queue(), ^{
self.updateProgressButton.enabled = false;
self.updateProgressLabel.stringValue = @"Extracting update...";
self.updateProgressLabel.stringValue = @"Extracting update";
_updateState = UPDATE_EXTRACTING;
});
@ -317,8 +454,8 @@ static uint32_t color_to_int(NSColor *color)
appropriateForURL:[[NSBundle mainBundle] bundleURL]
create:true
error:nil] path];
NSTask *unzipTask;
if (!_downloadDirectory) {
[self deauthorize];
dispatch_sync(dispatch_get_main_queue(), ^{
self.updateProgressButton.enabled = false;
self.updateProgressLabel.stringValue = @"Failed to extract update.";
@ -330,12 +467,14 @@ static uint32_t color_to_int(NSColor *color)
return;
}
NSTask *unzipTask;
unzipTask = [[NSTask alloc] init];
unzipTask.launchPath = @"/usr/bin/unzip";
unzipTask.arguments = @[location.path, @"-d", _downloadDirectory];
[unzipTask launch];
[unzipTask waitUntilExit];
if (unzipTask.terminationStatus != 0 || unzipTask.terminationReason != NSTaskTerminationReasonExit) {
[self deauthorize];
[[NSFileManager defaultManager] removeItemAtPath:_downloadDirectory error:nil];
dispatch_sync(dispatch_get_main_queue(), ^{
self.updateProgressButton.enabled = false;
@ -368,7 +507,7 @@ static uint32_t color_to_int(NSColor *color)
- (void)performUpgrade
{
self.updateProgressButton.enabled = false;
self.updateProgressLabel.stringValue = @"Instaling update...";
self.updateProgressLabel.stringValue = @"Instaling update";
_updateState = UPDATE_INSTALLING;
self.updateProgressButton.enabled = false;
[self.updateProgressSpinner startAnimation:nil];
@ -380,6 +519,7 @@ static uint32_t color_to_int(NSColor *color)
NSError *error = nil;
[[NSFileManager defaultManager] moveItemAtPath:contentsPath toPath:contentsTempPath error:&error];
if (error) {
[self deauthorize];
[[NSFileManager defaultManager] removeItemAtPath:_downloadDirectory error:nil];
_downloadDirectory = nil;
dispatch_sync(dispatch_get_main_queue(), ^{
@ -394,6 +534,7 @@ static uint32_t color_to_int(NSColor *color)
}
[[NSFileManager defaultManager] moveItemAtPath:updateContentsPath toPath:contentsPath error:&error];
if (error) {
[self deauthorize];
[[NSFileManager defaultManager] moveItemAtPath:contentsTempPath toPath:contentsPath error:nil];
[[NSFileManager defaultManager] removeItemAtPath:_downloadDirectory error:nil];
_downloadDirectory = nil;
@ -440,6 +581,84 @@ static uint32_t color_to_int(NSColor *color)
}
}
- (void)orderFrontAboutPanel:(id)sender
{
// NSAboutPanelOptionApplicationIcon is not available prior to 10.13, but the key is still there and working.
[[NSApplication sharedApplication] orderFrontStandardAboutPanelWithOptions:@{
@"ApplicationIcon": [NSImage imageNamed:@"Icon"]
}];
}
- (void)controller:(JOYController *)controller buttonChangedState:(JOYButton *)button
{
if (!button.isPressed) return;
NSDictionary *mapping = [[NSUserDefaults standardUserDefaults] dictionaryForKey:@"JoyKitInstanceMapping"][controller.uniqueID];
if (!mapping) {
mapping = [[NSUserDefaults standardUserDefaults] dictionaryForKey:@"JoyKitNameMapping"][controller.deviceName];
}
JOYButtonUsage usage = ((JOYButtonUsage)[mapping[n2s(button.uniqueID)] unsignedIntValue]) ?: -1;
if (!mapping && usage >= JOYButtonUsageGeneric0) {
usage = GB_inline_const(JOYButtonUsage[], {JOYButtonUsageY, JOYButtonUsageA, JOYButtonUsageB, JOYButtonUsageX})[(usage - JOYButtonUsageGeneric0) & 3];
}
if (usage == GBJoyKitHotkey1 || usage == GBJoyKitHotkey2) {
if (_preferencesWindow && self.keyWindow == _preferencesWindow) {
return;
}
if (![[NSUserDefaults standardUserDefaults] boolForKey:@"GBAllowBackgroundControllers"] && !self.keyWindow) {
return;
}
NSString *keyEquivalent = [[NSUserDefaults standardUserDefaults] stringForKey:usage == GBJoyKitHotkey1? @"GBJoypadHotkey1" : @"GBJoypadHotkey2"];
NSEventModifierFlags flags = NSEventModifierFlagCommand;
if ([keyEquivalent hasPrefix:@"^"]) {
flags |= NSEventModifierFlagShift;
[keyEquivalent substringFromIndex:1];
}
_simulatingMenuEvent = true;
[[NSApplication sharedApplication] sendEvent:[NSEvent keyEventWithType:NSEventTypeKeyDown
location:(NSPoint){0,}
modifierFlags:flags
timestamp:0
windowNumber:0
context:NULL
characters:keyEquivalent
charactersIgnoringModifiers:keyEquivalent
isARepeat:false
keyCode:0]];
_simulatingMenuEvent = false;
}
}
- (NSWindow *)keyWindow
{
NSWindow *ret = [super keyWindow];
if (!ret && _simulatingMenuEvent) {
ret = [(Document *)self.orderedDocuments.firstObject mainWindow];
}
return ret;
}
- (NSWindow *)mainWindow
{
NSWindow *ret = [super mainWindow];
if (!ret && _simulatingMenuEvent) {
ret = [(Document *)self.orderedDocuments.firstObject mainWindow];
}
return ret;
}
- (IBAction)openDebuggerHelp:(id)sender
{
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"https://sameboy.github.io/debugger/"]];
}
- (IBAction)openSponsor:(id)sender
{
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"https://github.com/sponsors/LIJI32"]];
}
- (void)dealloc
{
if (_downloadDirectory) {

View File

@ -1,7 +1,4 @@
#ifndef GBButtons_h
#define GBButtons_h
typedef enum : NSUInteger {
typedef enum {
GBRight,
GBLeft,
GBUp,
@ -13,11 +10,17 @@ typedef enum : NSUInteger {
GBTurbo,
GBRewind,
GBUnderclock,
GBButtonCount,
GBHotkey1,
GBHotkey2,
GBJoypadButtonCount,
GBButtonCount = GBUnderclock + 1,
GBGameBoyButtonCount = GBStart + 1,
} GBButton;
extern NSString const *GBButtonNames[GBButtonCount];
#define GBJoyKitHotkey1 JOYButtonUsageGeneric0 + 0x100
#define GBJoyKitHotkey2 JOYButtonUsageGeneric0 + 0x101
extern NSString const *GBButtonNames[GBJoypadButtonCount];
static inline NSString *n2s(uint64_t number)
{
@ -31,5 +34,3 @@ static inline NSString *button_to_preference_name(GBButton button, unsigned play
}
return [NSString stringWithFormat:@"GB%@", GBButtonNames[button]];
}
#endif

View File

@ -1,4 +1,4 @@
#import <Foundation/Foundation.h>
#import "GBButtons.h"
NSString const *GBButtonNames[] = {@"Right", @"Left", @"Up", @"Down", @"A", @"B", @"Select", @"Start", @"Turbo", @"Rewind", @"Slow-Motion"};
NSString const *GBButtonNames[] = {@"Right", @"Left", @"Up", @"Down", @"A", @"B", @"Select", @"Start", @"Turbo", @"Rewind", @"Slow-Motion", @"Hotkey 1", @"Hotkey 2"};

View File

@ -6,7 +6,7 @@
@implementation GBCheatTextView
- (bool)_insertText:(NSString *)string replacementRange:(NSRange)range
- (bool)_internalInsertText:(NSString *)string replacementRange:(NSRange)range
{
if (range.location == NSNotFound) {
range = self.selectedRange;
@ -60,19 +60,19 @@
return true;
}
if (([string isEqualToString:@"$"] || [string isEqualToString:@":"]) && range.length == 0 && range.location == 0) {
if ([self _insertText:@"$00:" replacementRange:range]) {
if ([self _internalInsertText:@"$00:" replacementRange:range]) {
self.selectedRange = NSMakeRange(1, 2);
return true;
}
}
if ([string isEqualToString:@":"] && range.length + range.location == self.string.length) {
if ([self _insertText:@":$0" replacementRange:range]) {
if ([self _internalInsertText:@":$0" replacementRange:range]) {
self.selectedRange = NSMakeRange(self.string.length - 2, 2);
return true;
}
}
if ([string isEqualToString:@"$"]) {
if ([self _insertText:@"$0" replacementRange:range]) {
if ([self _internalInsertText:@"$0" replacementRange:range]) {
self.selectedRange = NSMakeRange(range.location + 1, 1);
return true;
}
@ -88,8 +88,10 @@
- (void)insertText:(id)string replacementRange:(NSRange)replacementRange
{
if (![self _insertText:string replacementRange:replacementRange]) {
NSBeep();
if (![self _internalInsertText:string replacementRange:replacementRange]) {
if (![self _internalInsertText:[@"$" stringByAppendingString:string] replacementRange:replacementRange]) {
NSBeep();
}
}
}

View File

@ -42,7 +42,7 @@
return nil;
}
- (nullable id)tableView:(NSTableView *)tableView objectValueForTableColumn:(nullable NSTableColumn *)tableColumn row:(NSInteger)row
- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
size_t cheatCount;
GB_gameboy_t *gb = self.document.gameboy;
@ -58,7 +58,7 @@
return @NO;
case 2:
return @"Add Cheat...";
return @"Add Cheat";
case 3:
return @"";
@ -92,14 +92,18 @@
self.importCodeField.stringValue.UTF8String,
self.importDescriptionField.stringValue.UTF8String,
true)) {
self.importCodeField.stringValue = @"";
self.importDescriptionField.stringValue = @"";
[self.cheatsTable reloadData];
[self tableViewSelectionDidChange:nil];
dispatch_async(dispatch_get_main_queue(), ^{
self.importCodeField.stringValue = @"";
self.importDescriptionField.stringValue = @"";
[self.cheatsTable reloadData];
[self tableViewSelectionDidChange:nil];
});
}
else {
NSBeep();
[GBWarningPopover popoverWithContents:@"This code is not a valid GameShark or GameGenie code" onView:self.importCodeField];
dispatch_async(dispatch_get_main_queue(), ^{
NSBeep();
[GBWarningPopover popoverWithContents:@"This code is not a valid GameShark or GameGenie code" onView:self.importCodeField];
});
}
}];
}

View File

@ -1,5 +0,0 @@
#import <Cocoa/Cocoa.h>
@interface GBColorCell : NSTextFieldCell
@end

View File

@ -1,49 +0,0 @@
#import "GBColorCell.h"
static inline double scale_channel(uint8_t x)
{
x &= 0x1f;
return x / 31.0;
}
@implementation GBColorCell
{
NSInteger _integerValue;
}
- (void)setObjectValue:(id)objectValue
{
_integerValue = [objectValue integerValue];
uint8_t r = _integerValue & 0x1F,
g = (_integerValue >> 5) & 0x1F,
b = (_integerValue >> 10) & 0x1F;
super.objectValue = [[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@"$%04x", (uint16_t)(_integerValue & 0x7FFF)] attributes:@{
NSForegroundColorAttributeName: r * 3 + g * 4 + b * 2 > 120? [NSColor blackColor] : [NSColor whiteColor],
NSFontAttributeName: [NSFont userFixedPitchFontOfSize:12]
}];
}
- (NSInteger)integerValue
{
return _integerValue;
}
- (int)intValue
{
return (int)_integerValue;
}
- (NSColor *) backgroundColor
{
/* Todo: color correction */
uint16_t color = self.integerValue;
return [NSColor colorWithRed:scale_channel(color) green:scale_channel(color >> 5) blue:scale_channel(color >> 10) alpha:1.0];
}
- (BOOL)drawsBackground
{
return true;
}
@end

7
Cocoa/GBDebuggerButton.h Normal file
View File

@ -0,0 +1,7 @@
#import <Cocoa/Cocoa.h>
@class GBDocument;
@interface GBDebuggerButton : NSButton
@property (weak) IBOutlet NSTextField *textField;
@end

49
Cocoa/GBDebuggerButton.m Normal file
View File

@ -0,0 +1,49 @@
#import "GBDebuggerButton.h"
@implementation GBDebuggerButton
{
NSTrackingArea *_trackingArea;
}
- (instancetype)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
self.toolTip = self.title;
self.imagePosition = NSImageOnly; // Newer versions of AppKit refuse to respect the value from the nib file
return self;
}
- (void)mouseEntered:(NSEvent *)event
{
if (@available(macOS 10.10, *)) {
NSDictionary *attributes = @{
NSForegroundColorAttributeName: [NSColor colorWithWhite:1.0 alpha:0.5],
NSFontAttributeName: self.textField.font
};
self.textField.placeholderAttributedString =
[[NSAttributedString alloc] initWithString:self.alternateTitle attributes:attributes];
}
}
- (void)mouseExited:(NSEvent *)event
{
if (@available(macOS 10.10, *)) {
if ([self.textField.placeholderAttributedString.string isEqualToString:self.alternateTitle]) {
self.textField.placeholderAttributedString = nil;
}
}
}
- (void)updateTrackingAreas
{
[super updateTrackingAreas];
if (_trackingArea) {
[self removeTrackingArea:_trackingArea];
}
_trackingArea = [ [NSTrackingArea alloc] initWithRect:[self bounds]
options:NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways
owner:self
userInfo:nil];
[self addTrackingArea:_trackingArea];
}
@end

View File

@ -163,7 +163,7 @@ void main(void) {\n\
/* OpenGL is black magic. Closing one view causes others to be completely black unless we reload their shaders */
/* We're probably not freeing thing in the right place. */
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBFilterChanged" object:nil];
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBFilterChanged$DefaultsObserver" object:nil];
}
+ (GLuint)shaderWithContents:(NSString*)contents type:(GLenum)type

View File

@ -0,0 +1,10 @@
#import <Core/gb.h>
#import <HexFiend/HFRepresenter.h>
@interface GBHexStatusBarRepresenter : HFRepresenter
@property GB_gameboy_t *gb;
@property (nonatomic) bool useDecimalLength;
@property (nonatomic) uint16_t bankForDescription;
@property (nonatomic) uint16_t baseAddress;
@end

View File

@ -0,0 +1,220 @@
#import "GBHexStatusBarRepresenter.h"
#import <HexFiend/HFFunctions.h>
@interface GBHexStatusBarView : NSView
{
NSCell *_cell;
NSSize _cellSize;
GBHexStatusBarRepresenter *_representer;
NSDictionary *_cellAttributes;
bool _registeredForAppNotifications;
}
- (void)setRepresenter:(GBHexStatusBarRepresenter *)rep;
- (void)setString:(NSString *)string;
@end
@implementation GBHexStatusBarView
- (void)_sharedInitStatusBarView
{
NSMutableParagraphStyle *style = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
[style setAlignment:NSCenterTextAlignment];
style.lineBreakMode = NSLineBreakByTruncatingTail;
_cellAttributes = @{
NSForegroundColorAttributeName: [NSColor windowFrameTextColor],
NSFontAttributeName: [NSFont labelFontOfSize:[NSFont smallSystemFontSize]],
NSParagraphStyleAttributeName: style,
};
_cell = [[NSCell alloc] initTextCell:@""];
[_cell setAlignment:NSCenterTextAlignment];
[_cell setBackgroundStyle:NSBackgroundStyleRaised];
}
- (instancetype)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
[self _sharedInitStatusBarView];
return self;
}
- (instancetype)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
[self _sharedInitStatusBarView];
return self;
}
- (BOOL)isFlipped
{
return true;
}
- (void)setRepresenter:(GBHexStatusBarRepresenter *)rep
{
_representer = rep;
}
- (void)setString:(NSString *)string
{
[_cell setAttributedStringValue:[[NSAttributedString alloc] initWithString:string attributes:_cellAttributes]];
_cellSize = [_cell cellSize];
[self setNeedsDisplay:true];
}
- (void)drawRect:(NSRect)clip
{
NSRect bounds = [self bounds];
NSRect cellRect = NSMakeRect(NSMinX(bounds), HFCeil(NSMidY(bounds) - _cellSize.height / 2), NSWidth(bounds), _cellSize.height);
[_cell drawWithFrame:cellRect inView:self];
}
- (void)setFrame:(NSRect)frame
{
[super setFrame:frame];
[self.window setContentBorderThickness:frame.origin.y + frame.size.height forEdge:NSMinYEdge];
}
- (void)mouseDown:(NSEvent *)event
{
_representer.useDecimalLength ^= true;
}
- (void)windowDidChangeKeyStatus:(NSNotification *)note
{
[self setNeedsDisplay:true];
}
- (void)viewDidMoveToWindow
{
HFRegisterViewForWindowAppearanceChanges(self, @selector(windowDidChangeKeyStatus:), !_registeredForAppNotifications);
_registeredForAppNotifications = true;
[self.window setContentBorderThickness:self.frame.origin.y + self.frame.size.height forEdge:NSMinYEdge];
[super viewDidMoveToWindow];
}
- (void)viewWillMoveToWindow:(NSWindow *)newWindow
{
HFUnregisterViewForWindowAppearanceChanges(self, NO);
[super viewWillMoveToWindow:newWindow];
}
- (void)dealloc
{
HFUnregisterViewForWindowAppearanceChanges(self, _registeredForAppNotifications);
}
@end
@implementation GBHexStatusBarRepresenter
- (instancetype)init
{
self = [super init];
return self;
}
- (NSView *)createView {
GBHexStatusBarView *view = [[GBHexStatusBarView alloc] initWithFrame:NSMakeRect(0, 0, 100, 18)];
[view setRepresenter:self];
[view setAutoresizingMask:NSViewWidthSizable];
return view;
}
- (NSString *)describeLength:(unsigned long long)length
{
if (self.useDecimalLength) {
return [NSString stringWithFormat:@"%llu byte%s", length, length == 1 ? "" : "s"];
}
return [NSString stringWithFormat:@"$%llX byte%s", length, length == 1 ? "" : "s"];
}
- (NSString *)describeOffset:(unsigned long long)offset isRangeEnd:(bool)isRangeEnd
{
if (!_gb) {
return [NSString stringWithFormat:@"$%llX", offset];
}
return @(GB_debugger_describe_address(_gb, offset + _baseAddress, _bankForDescription, false, isRangeEnd));
}
- (NSString *)stringForEmptySelectionAtOffset:(unsigned long long)offset length:(unsigned long long)length
{
return [self describeOffset:offset isRangeEnd:false];
}
- (NSString *)stringForSingleByteSelectionAtOffset:(unsigned long long)offset length:(unsigned long long)length
{
return [NSString stringWithFormat:@"Byte %@ selected", [self describeOffset:offset isRangeEnd:false]];
}
- (NSString *)stringForSingleRangeSelection:(HFRange)range length:(unsigned long long)length
{
return [NSString stringWithFormat:@"Range %@ to %@ selected (%@)",
[self describeOffset:range.location isRangeEnd:false],
[self describeOffset:range.location + range.length isRangeEnd:true],
[self describeLength:range.length]];
}
- (void)updateString
{
NSString *string = nil;
HFController *controller = [self controller];
if (controller) {
unsigned long long length = [controller contentsLength];
NSArray *ranges = [controller selectedContentsRanges];
NSUInteger rangeCount = [ranges count];
if (rangeCount == 1) {
HFRange range = [ranges[0] HFRange];
if (range.length == 0) {
string = [self stringForEmptySelectionAtOffset:range.location length:length];
}
else if (range.length == 1) {
string = [self stringForSingleByteSelectionAtOffset:range.location length:length];
}
else {
string = [self stringForSingleRangeSelection:range length:length];
}
}
else {
string = @"Multiple ranges selected";
}
}
if (! string) string = @"";
[[self view] setString:string];
}
- (void)setUseDecimalLength:(bool)useDecimalLength
{
_useDecimalLength = useDecimalLength;
[self updateString];
}
- (void)setBaseAddress:(uint16_t)baseAddress
{
_baseAddress = baseAddress;
[self updateString];
}
- (void) setBankForDescription:(uint16_t)bankForDescription
{
_bankForDescription = bankForDescription;
[self updateString];
}
- (void)controllerDidChange:(HFControllerPropertyBits)bits
{
if (bits & (HFControllerContentLength | HFControllerSelectedRanges)) {
[self updateString];
}
}
+ (NSPoint)defaultLayoutPosition
{
return NSMakePoint(0, -1);
}
@end

View File

@ -1,5 +0,0 @@
#import <Cocoa/Cocoa.h>
@interface GBImageCell : NSImageCell
@end

View File

@ -1,10 +0,0 @@
#import "GBImageCell.h"
@implementation GBImageCell
- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView
{
CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort];
CGContextSetInterpolationQuality(context, kCGInterpolationNone);
[super drawWithFrame:cellFrame inView:controlView];
}
@end

View File

@ -10,18 +10,18 @@
}
@end
@implementation GBImageView
{
NSTrackingArea *trackingArea;
}
@interface GBGridView : NSView
@end
@implementation GBGridView
- (void)drawRect:(NSRect)dirtyRect
{
CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort];
CGContextSetInterpolationQuality(context, kCGInterpolationNone);
[super drawRect:dirtyRect];
CGFloat y_ratio = self.frame.size.height / self.image.size.height;
CGFloat x_ratio = self.frame.size.width / self.image.size.width;
for (GBImageViewGridConfiguration *conf in self.verticalGrids) {
GBImageView *parent = (GBImageView *)self.superview;
CGFloat y_ratio = parent.frame.size.height / parent.image.size.height;
CGFloat x_ratio = parent.frame.size.width / parent.image.size.width;
for (GBImageViewGridConfiguration *conf in parent.verticalGrids) {
[conf.color set];
for (CGFloat y = conf.size * y_ratio; y < self.frame.size.height; y += conf.size * y_ratio) {
NSBezierPath *line = [NSBezierPath bezierPath];
@ -32,7 +32,7 @@
}
}
for (GBImageViewGridConfiguration *conf in self.horizontalGrids) {
for (GBImageViewGridConfiguration *conf in parent.horizontalGrids) {
[conf.color set];
for (CGFloat x = conf.size * x_ratio; x < self.frame.size.width; x += conf.size * x_ratio) {
NSBezierPath *line = [NSBezierPath bezierPath];
@ -43,11 +43,11 @@
}
}
if (self.displayScrollRect) {
if (parent.displayScrollRect) {
NSBezierPath *path = [NSBezierPath bezierPathWithRect:CGRectInfinite];
for (unsigned x = 0; x < 2; x++) {
for (unsigned y = 0; y < 2; y++) {
NSRect rect = self.scrollRect;
NSRect rect = parent.scrollRect;
rect.origin.x *= x_ratio;
rect.origin.y *= y_ratio;
rect.size.width *= x_ratio;
@ -56,7 +56,7 @@
rect.origin.x -= self.frame.size.width * x;
rect.origin.y += self.frame.size.height * y;
NSBezierPath *subpath = [NSBezierPath bezierPathWithRect:rect];
[path appendBezierPath:subpath];
@ -72,36 +72,62 @@
[path stroke];
}
}
@end
@implementation GBImageView
{
NSTrackingArea *_trackingArea;
GBGridView *_gridView;
NSRect _scrollRect;
}
- (instancetype)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
self.wantsLayer = true;
_gridView = [[GBGridView alloc] initWithFrame:self.bounds];
_gridView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
[self addSubview:_gridView];
return self;
}
-(void)viewWillDraw
{
[super viewWillDraw];
for (CALayer *layer in self.layer.sublayers) {
layer.magnificationFilter = kCAFilterNearest;
}
}
- (void)setHorizontalGrids:(NSArray *)horizontalGrids
{
self->_horizontalGrids = horizontalGrids;
[self setNeedsDisplay];
[_gridView setNeedsDisplay:true];
}
- (void)setVerticalGrids:(NSArray *)verticalGrids
{
self->_verticalGrids = verticalGrids;
[self setNeedsDisplay];
[_gridView setNeedsDisplay:true];
}
- (void)setDisplayScrollRect:(bool)displayScrollRect
{
self->_displayScrollRect = displayScrollRect;
[self setNeedsDisplay];
[_gridView setNeedsDisplay:true];
}
- (void)updateTrackingAreas
{
if (trackingArea != nil) {
[self removeTrackingArea:trackingArea];
if (_trackingArea != nil) {
[self removeTrackingArea:_trackingArea];
}
trackingArea = [ [NSTrackingArea alloc] initWithRect:[self bounds]
_trackingArea = [ [NSTrackingArea alloc] initWithRect:[self bounds]
options:NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways | NSTrackingMouseMoved
owner:self
userInfo:nil];
[self addTrackingArea:trackingArea];
[self addTrackingArea:_trackingArea];
}
- (void)mouseExited:(NSEvent *)theEvent
@ -124,4 +150,17 @@
}
}
- (void)setScrollRect:(NSRect)scrollRect
{
if (memcmp(&scrollRect, &_scrollRect, sizeof(scrollRect)) != 0) {
_scrollRect = scrollRect;
[_gridView setNeedsDisplay:true];
}
}
- (NSRect)scrollRect
{
return _scrollRect;
}
@end

12
Cocoa/GBJoyConManager.h Normal file
View File

@ -0,0 +1,12 @@
#import <Foundation/Foundation.h>
#import <AppKit/AppKit.h>
#import <JoyKit/JoyKit.h>
@interface GBJoyConManager : NSObject<JOYListener, NSTableViewDataSource, NSTableViewDelegate>
+ (instancetype)sharedInstance;
- (IBAction)autopair:(id)sender;
@property (nonatomic) bool arrangementMode;
@property (weak) IBOutlet NSTableView *tableView;
@end

303
Cocoa/GBJoyConManager.m Normal file
View File

@ -0,0 +1,303 @@
#import "GBJoyConManager.h"
#import "GBTintedImageCell.h"
#import <objc/runtime.h>
@implementation GBJoyConManager
{
GBTintedImageCell *_tintedImageCell;
NSImageCell *_imageCell;
NSMutableDictionary<NSString *, NSString *> *_pairings;
NSMutableDictionary<NSString *, NSNumber *> *_gripSettings;
bool _unpairing;
}
+ (instancetype)sharedInstance
{
static GBJoyConManager *manager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
manager = [[self alloc] _init];
});
return manager;
}
- (NSArray <JOYController *> *)joycons
{
NSMutableArray *ret = [[JOYController allControllers] mutableCopy];
for (JOYController *controller in [JOYController allControllers]) {
if (controller.joyconType == JOYJoyConTypeNone) {
[ret removeObject:controller];
}
}
return ret;
}
- (instancetype)init
{
return [self.class sharedInstance];
}
- (instancetype) _init
{
self = [super init];
_imageCell = [[NSImageCell alloc] init];
_tintedImageCell = [[GBTintedImageCell alloc] init];
if (@available(macOS 10.14, *)) {
_tintedImageCell.tint = [NSColor controlAccentColor];
}
else {
_tintedImageCell.tint = [NSColor selectedMenuItemColor];
}
_pairings = [[[NSUserDefaults standardUserDefaults] dictionaryForKey:@"GBJoyConPairings"] ?: @{} mutableCopy];
_gripSettings = [[[NSUserDefaults standardUserDefaults] dictionaryForKey:@"GBJoyConGrips"] ?: @{} mutableCopy];
// Sanity check the pairings
for (NSString *key in _pairings) {
if (![_pairings[_pairings[key]] isEqualToString:key]) {
[_pairings removeAllObjects];
break;
}
}
[JOYController registerListener:self];
for (JOYController *controller in [JOYController allControllers]) {
[self controllerConnected:controller];
}
return self;
}
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
{
return self.joycons.count;
}
- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
if (row >= [self numberOfRowsInTableView:tableView]) return nil;
unsigned columnIndex = [[tableView tableColumns] indexOfObject:tableColumn];
JOYController *controller = self.joycons[row];
switch (columnIndex) {
case 0: {
switch (controller.joyconType) {
case JOYJoyConTypeNone:
return nil;
case JOYJoyConTypeLeft:
return [NSImage imageNamed:[NSString stringWithFormat:@"%sJoyConLeftTemplate", controller.usesHorizontalJoyConGrip? "Horizontal" :""]];
case JOYJoyConTypeRight:
return [NSImage imageNamed:[NSString stringWithFormat:@"%sJoyConRightTemplate", controller.usesHorizontalJoyConGrip? "Horizontal" :""]];
case JOYJoyConTypeDual:
return [NSImage imageNamed:@"JoyConDualTemplate"];
}
}
case 1: {
NSMutableAttributedString *ret = [[NSMutableAttributedString alloc] initWithString:controller.deviceName
attributes:@{NSFontAttributeName:
[NSFont systemFontOfSize:[NSFont systemFontSize]]}];
[ret appendAttributedString:[[NSAttributedString alloc] initWithString:[@"\n" stringByAppendingString:controller.uniqueID]
attributes:@{NSFontAttributeName:
[NSFont systemFontOfSize:[NSFont smallSystemFontSize]],
NSForegroundColorAttributeName:[NSColor disabledControlTextColor]}]];
return ret;
}
case 2:
return @([(_gripSettings[controller.uniqueID] ?: @(-1)) unsignedIntValue] + 1);
}
return nil;
}
- (void)updateGripForController:(JOYController *)controller
{
NSNumber *grip = _gripSettings[controller.uniqueID];
if (!grip) {
controller.usesHorizontalJoyConGrip = [[NSUserDefaults standardUserDefaults] boolForKey:@"GBJoyConsDefaultsToHorizontal"];
return;
}
controller.usesHorizontalJoyConGrip = [grip unsignedIntValue] == 1;
}
- (void)tableView:(NSTableView *)tableView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
unsigned columnIndex = [[tableView tableColumns] indexOfObject:tableColumn];
if (columnIndex != 2) return;
if (row >= [self numberOfRowsInTableView:tableView]) return;
JOYController *controller = self.joycons[row];
if (controller.joyconType == JOYJoyConTypeDual) {
return;
}
switch ([object unsignedIntValue]) {
case 0:
[_gripSettings removeObjectForKey:controller.uniqueID];
break;
case 1:
_gripSettings[controller.uniqueID] = @(0);
break;
case 2:
_gripSettings[controller.uniqueID] = @(1);
break;
}
[[NSUserDefaults standardUserDefaults] setObject:_gripSettings forKey:@"GBJoyConGrips"];
[self updateGripForController:controller];
}
- (NSCell *)tableView:(NSTableView *)tableView dataCellForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
if (row >= [self numberOfRowsInTableView:tableView]) return [[NSCell alloc] init];
unsigned columnIndex = [[tableView tableColumns] indexOfObject:tableColumn];
if (columnIndex == 2) {
JOYCombinedController *controller = (JOYCombinedController *)self.joycons[row];
if (controller.joyconType == JOYJoyConTypeDual) {
NSButtonCell *cell = [[NSButtonCell alloc] initTextCell:@"Separate Joy-Cons"];
cell.bezelStyle = NSBezelStyleRounded;
cell.action = @selector(invoke);
id block = ^(void) {
dispatch_async(dispatch_get_main_queue(), ^{
for (JOYController *child in controller.children) {
[_pairings removeObjectForKey:child.uniqueID];
}
[[NSUserDefaults standardUserDefaults] setObject:_pairings forKey:@"GBJoyConPairings"];
_unpairing = true;
[controller breakApart];
_unpairing = false;
});
};
// To retain the block
objc_setAssociatedObject(cell, @selector(breakApart), block, OBJC_ASSOCIATION_RETAIN);
cell.target = block;
return cell;
}
}
if (columnIndex == 0) {
JOYController *controller = self.joycons[row];
for (JOYButton *button in controller.buttons) {
if (button.isPressed) {
return _tintedImageCell;
}
}
return _imageCell;
}
return nil;
}
- (void)controllerConnected:(JOYController *)controller
{
[self updateGripForController:controller];
for (JOYController *partner in [JOYController allControllers]) {
if ([partner.uniqueID isEqualToString:_pairings[controller.uniqueID]]) {
[self pairJoyCon:controller withJoyCon:partner];
break;
}
}
if (controller.joyconType == JOYJoyConTypeLeft || controller.joyconType == JOYJoyConTypeRight) {
[self autopair:nil];
}
if (_arrangementMode) {
[self.tableView reloadData];
}
}
- (IBAction)autopair:(id)sender
{
if (_unpairing) return;
if (![[NSUserDefaults standardUserDefaults] boolForKey:@"GBJoyConAutoPair"]) return;
NSArray<JOYController *> *controllers = [[JOYController allControllers] copy];
for (JOYController *first in controllers) {
if (_pairings[first.uniqueID]) continue; // Has an established partner
if (first.joyconType != JOYJoyConTypeLeft) continue;
for (JOYController *second in controllers) {
if (_pairings[second.uniqueID]) continue; // Has an established partner
if (second.joyconType != JOYJoyConTypeRight) continue;
[self pairJoyCon:first withJoyCon:second];
break;
}
}
if (_arrangementMode) {
[self.tableView reloadData];
}
}
- (void)controllerDisconnected:(JOYController *)controller
{
if (_arrangementMode) {
[self.tableView reloadData];
}
}
- (BOOL)tableView:(NSTableView *)tableView shouldEditTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
unsigned columnIndex = [[tableView tableColumns] indexOfObject:tableColumn];
return columnIndex == 2;
}
- (JOYCombinedController *)pairJoyCon:(JOYController *)first withJoyCon:(JOYController *)second
{
if (first.joyconType != JOYJoyConTypeLeft && first.joyconType != JOYJoyConTypeRight) return nil; // Not a Joy-Con
if (second.joyconType != JOYJoyConTypeLeft && second.joyconType != JOYJoyConTypeRight) return nil; // Not a Joy-Con
if (first.joyconType == second.joyconType) return nil; // Not a sensible pair
_pairings[first.uniqueID] = second.uniqueID;
_pairings[second.uniqueID] = first.uniqueID;
first.usesHorizontalJoyConGrip = false;
second.usesHorizontalJoyConGrip = false;
[[NSUserDefaults standardUserDefaults] setObject:_pairings forKey:@"GBJoyConPairings"];
return [[JOYCombinedController alloc] initWithChildren:@[first, second]];
}
- (void)controller:(JOYController *)controller buttonChangedState:(JOYButton *)button
{
if (!_arrangementMode) return;
if (controller.joyconType == JOYJoyConTypeNone) return;
[self.tableView setNeedsDisplay:true];
if (controller.joyconType != JOYJoyConTypeLeft && controller.joyconType != JOYJoyConTypeRight) return;
if (button.usage != JOYButtonUsageL1 && button.usage != JOYButtonUsageR1) return;
// L or R were pressed on a single Joy-Con, try and pair available Joy-Cons
JOYController *left = nil;
JOYController *right = nil;
for (JOYController *controller in [JOYController allControllers]) {
if (!left && controller.joyconType == JOYJoyConTypeLeft) {
for (JOYButton *button in controller.buttons) {
if (button.usage == JOYButtonUsageL1 && button.isPressed) {
left = controller;
break;
}
}
}
if (!right && controller.joyconType == JOYJoyConTypeRight) {
for (JOYButton *button in controller.buttons) {
if (button.usage == JOYButtonUsageR1 && button.isPressed) {
right = controller;
break;
}
}
}
if (left && right) {
[self pairJoyCon:left withJoyCon:right];
return;
}
}
}
- (IBAction)toggleHorizontalDefault:(NSButton *)sender
{
for (JOYController *controller in self.joycons) {
[self updateGripForController:controller];
}
if (_arrangementMode) {
[self.tableView reloadData];
}
}
- (void)setArrangementMode:(bool)arrangementMode
{
_arrangementMode = arrangementMode;
if (arrangementMode) {
[self.tableView reloadData];
}
}
@end

View File

@ -1,4 +1,3 @@
#define GB_INTERNAL // Todo: Some memory accesses are being done using the struct directly
#import "GBMemoryByteArray.h"
#import "GBCompleteByteSlice.h"
@ -32,69 +31,110 @@
}
}
- (uint16_t)base
{
switch (_mode) {
case GBMemoryEntireSpace: return 0;
case GBMemoryROM: return 0;
case GBMemoryVRAM: return 0x8000;
case GBMemoryExternalRAM: return 0xA000;
case GBMemoryRAM: return 0xC000;
}
}
- (void)copyBytes:(unsigned char *)dst range:(HFRange)range
{
__block uint16_t addr = (uint16_t) range.location;
__block unsigned long long length = range.length;
if (_mode == GBMemoryEntireSpace) {
while (length) {
*(dst++) = [_document readMemory:addr++];
length--;
}
// Do everything in 0x1000 chunks, never cross a 0x1000 boundary
if ((range.location & 0xF000) != ((range.location + range.length) & 0xF000)) {
size_t partial = 0x1000 - (range.location & 0xFFF);
[self copyBytes:dst + partial range:HFRangeMake(range.location + partial, range.length - partial)];
range.length = partial;
}
else {
[_document performAtomicBlock:^{
unsigned char *_dst = dst;
uint16_t bank_backup = 0;
GB_gameboy_t *gb = _document.gameboy;
switch (_mode) {
case GBMemoryROM:
bank_backup = gb->mbc_rom_bank;
gb->mbc_rom_bank = self.selectedBank;
break;
case GBMemoryVRAM:
bank_backup = gb->cgb_vram_bank;
if (GB_is_cgb(gb)) {
gb->cgb_vram_bank = self.selectedBank;
range.location += self.base;
GB_gameboy_t *gb = _document.gameboy;
switch (range.location >> 12) {
case 0x0:
case 0x1:
case 0x2:
case 0x3: {
uint16_t bank;
uint8_t *data = GB_get_direct_access(gb, GB_DIRECT_ACCESS_ROM0, NULL, &bank);
memcpy(dst, data + bank * 0x4000 + range.location, range.length);
break;
}
case 0x4:
case 0x5:
case 0x6:
case 0x7: {
uint16_t bank;
size_t size;
uint8_t *data = GB_get_direct_access(gb, GB_DIRECT_ACCESS_ROM, &size, &bank);
if (_mode != GBMemoryEntireSpace) {
bank = self.selectedBank & (size / 0x4000 - 1);
}
memcpy(dst, data + bank * 0x4000 + range.location - 0x4000, range.length);
break;
}
case 0x8:
case 0x9: {
uint16_t bank;
size_t size;
uint8_t *data = GB_get_direct_access(gb, GB_DIRECT_ACCESS_VRAM, &size, &bank);
if (_mode != GBMemoryEntireSpace) {
bank = self.selectedBank & (size / 0x2000 - 1);
}
memcpy(dst, data + bank * 0x2000 + range.location - 0x8000, range.length);
break;
}
case 0xA:
case 0xB: {
// Some carts are special, use memory read directly in full mem mode
if (_mode == GBMemoryEntireSpace) {
case 0xF:
slow_path:
[_document performAtomicBlock:^{
for (unsigned i = 0; i < range.length; i++) {
dst[i] = GB_safe_read_memory(gb, range.location + i);
}
addr += 0x8000;
break;
case GBMemoryExternalRAM:
bank_backup = gb->mbc_ram_bank;
gb->mbc_ram_bank = self.selectedBank;
addr += 0xA000;
break;
case GBMemoryRAM:
bank_backup = gb->cgb_ram_bank;
if (GB_is_cgb(gb)) {
gb->cgb_ram_bank = self.selectedBank;
}
addr += 0xC000;
break;
default:
assert(false);
}];
break;
}
while (length) {
*(_dst++) = [_document readMemory:addr++];
length--;
else {
uint16_t bank;
size_t size;
uint8_t *data = GB_get_direct_access(gb, GB_DIRECT_ACCESS_CART_RAM, &size, &bank);
bank = self.selectedBank & (size / 0x2000 - 1);
if (size == 0) {
memset(dst, 0xFF, range.length);
}
else if (range.location + range.length - 0xA000 > size) {
goto slow_path;
}
else {
memcpy(dst, data + bank * 0x2000 + range.location - 0xA000, range.length);
}
break;
}
switch (_mode) {
case GBMemoryROM:
gb->mbc_rom_bank = bank_backup;
break;
case GBMemoryVRAM:
gb->cgb_vram_bank = bank_backup;
break;
case GBMemoryExternalRAM:
gb->mbc_ram_bank = bank_backup;
break;
case GBMemoryRAM:
gb->cgb_ram_bank = bank_backup;
break;
default:
assert(false);
}
case 0xC:
case 0xE: {
uint8_t *data = GB_get_direct_access(gb, GB_DIRECT_ACCESS_RAM, NULL, NULL);
memcpy(dst, data + (range.location & 0xFFF), range.length);
break;
}
case 0xD: {
uint16_t bank;
size_t size;
uint8_t *data = GB_get_direct_access(gb, GB_DIRECT_ACCESS_RAM, &size, &bank);
if (_mode != GBMemoryEntireSpace) {
bank = self.selectedBank & (size / 0x1000 - 1);
}
}];
memcpy(dst, data + bank * 0x1000 + range.location - 0xD000, range.length);
break;
}
}
}
@ -113,65 +153,104 @@
return ret;
}
- (void)insertByteSlice:(HFByteSlice *)slice inRange:(HFRange)lrange
- (void)insertByteSlice:(HFByteSlice *)slice inRange:(HFRange)range
{
if (slice.length != lrange.length) return; /* Insertion is not allowed, only overwriting. */
[_document performAtomicBlock:^{
uint16_t addr = (uint16_t) lrange.location;
uint16_t bank_backup = 0;
GB_gameboy_t *gb = _document.gameboy;
switch (_mode) {
case GBMemoryROM:
bank_backup = gb->mbc_rom_bank;
gb->mbc_rom_bank = self.selectedBank;
if (slice.length != range.length) return; /* Insertion is not allowed, only overwriting. */
// Do everything in 0x1000 chunks, never cross a 0x1000 boundary
if ((range.location & 0xF000) != ((range.location + range.length) & 0xF000)) {
size_t partial = 0x1000 - (range.location & 0xFFF);
if (slice.length - partial) {
[self insertByteSlice:[slice subsliceWithRange:HFRangeMake(partial, slice.length - partial)]
inRange:HFRangeMake(range.location + partial, range.length - partial)];
}
range.length = partial;
}
range.location += self.base;
GB_gameboy_t *gb = _document.gameboy;
switch (range.location >> 12) {
case 0x0:
case 0x1:
case 0x2:
case 0x3:
case 0x4:
case 0x5:
case 0x6:
case 0x7: {
return; // ROM not writeable
}
case 0x8:
case 0x9: {
uint16_t bank;
size_t size;
uint8_t *data = GB_get_direct_access(gb, GB_DIRECT_ACCESS_VRAM, &size, &bank);
if (_mode != GBMemoryEntireSpace) {
bank = self.selectedBank & (size / 0x2000 - 1);
}
uint8_t sliceData[range.length];
[slice copyBytes:sliceData range:HFRangeMake(0, range.length)];
memcpy(data + bank * 0x2000 + range.location - 0x8000, sliceData, range.length);
break;
}
case 0xA:
case 0xB: {
// Some carts are special, use memory write directly in full mem mode
if (_mode == GBMemoryEntireSpace) {
case 0xF:
slow_path:
[_document performAtomicBlock:^{
uint8_t sliceData[range.length];
[slice copyBytes:sliceData range:HFRangeMake(0, range.length)];
for (unsigned i = 0; i < range.length; i++) {
GB_write_memory(gb, range.location + i, sliceData[i]);
}
}];
break;
case GBMemoryVRAM:
bank_backup = gb->cgb_vram_bank;
if (GB_is_cgb(gb)) {
gb->cgb_vram_bank = self.selectedBank;
}
else {
uint16_t bank;
size_t size;
uint8_t *data = GB_get_direct_access(gb, GB_DIRECT_ACCESS_CART_RAM, &size, &bank);
bank = self.selectedBank & (size / 0x2000 - 1);
if (size == 0) {
// Nothing to write to
}
addr += 0x8000;
break;
case GBMemoryExternalRAM:
bank_backup = gb->mbc_ram_bank;
gb->mbc_ram_bank = self.selectedBank;
addr += 0xA000;
break;
case GBMemoryRAM:
bank_backup = gb->cgb_ram_bank;
if (GB_is_cgb(gb)) {
gb->cgb_ram_bank = self.selectedBank;
else if (range.location + range.length - 0xA000 > size) {
goto slow_path;
}
else {
uint8_t sliceData[range.length];
[slice copyBytes:sliceData range:HFRangeMake(0, range.length)];
memcpy(data + bank * 0x2000 + range.location - 0xA000, sliceData, range.length);
}
addr += 0xC000;
break;
default:
break;
}
}
uint8_t values[lrange.length];
[slice copyBytes:values range:HFRangeMake(0, lrange.length)];
uint8_t *src = values;
unsigned long long length = lrange.length;
while (length) {
[_document writeMemory:addr++ value:*(src++)];
length--;
case 0xC:
case 0xE: {
uint8_t *data = GB_get_direct_access(gb, GB_DIRECT_ACCESS_RAM, NULL, NULL);
uint8_t sliceData[range.length];
[slice copyBytes:sliceData range:HFRangeMake(0, range.length)];
memcpy(data + (range.location & 0xFFF), sliceData, range.length);
break;
}
switch (_mode) {
case GBMemoryROM:
gb->mbc_rom_bank = bank_backup;
break;
case GBMemoryVRAM:
gb->cgb_vram_bank = bank_backup;
break;
case GBMemoryExternalRAM:
gb->mbc_ram_bank = bank_backup;
break;
case GBMemoryRAM:
gb->cgb_ram_bank = bank_backup;
break;
default:
break;
case 0xD: {
uint16_t bank;
size_t size;
uint8_t *data = GB_get_direct_access(gb, GB_DIRECT_ACCESS_RAM, &size, &bank);
if (_mode != GBMemoryEntireSpace) {
bank = self.selectedBank & (size / 0x1000 - 1);
}
uint8_t sliceData[range.length];
[slice copyBytes:sliceData range:HFRangeMake(0, range.length)];
memcpy(data + bank * 0x1000 + range.location - 0xD000, sliceData, range.length);
break;
}
}];
}
}
@end

6
Cocoa/GBObjectView.h Normal file
View File

@ -0,0 +1,6 @@
#import <Cocoa/Cocoa.h>
#import "Document.h"
@interface GBObjectView : NSView
- (void)reloadData:(Document *)document;
@end

124
Cocoa/GBObjectView.m Normal file
View File

@ -0,0 +1,124 @@
#import "GBObjectView.h"
@interface GBObjectViewItem : NSObject
@property IBOutlet NSView *view;
@property IBOutlet NSImageView *image;
@property IBOutlet NSTextField *oamAddress;
@property IBOutlet NSTextField *position;
@property IBOutlet NSTextField *attributes;
@property IBOutlet NSTextField *tile;
@property IBOutlet NSTextField *tileAddress;
@property IBOutlet NSImageView *warningIcon;
@property IBOutlet NSBox *verticalLine;
@end
@implementation GBObjectViewItem
{
@public
uint32_t _lastImageData[128];
uint8_t _lastHeight;
}
@end
@implementation GBObjectView
{
NSMutableArray<GBObjectViewItem *> *_items;
}
- (instancetype)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
_items = [NSMutableArray array];
CGFloat height = self.frame.size.height;
for (unsigned i = 0; i < 40; i++) {
GBObjectViewItem *item = [[GBObjectViewItem alloc] init];
[_items addObject:item];
[[NSBundle mainBundle] loadNibNamed:@"GBObjectViewItem" owner:item topLevelObjects:nil];
item.view.hidden = true;
[self addSubview:item.view];
[item.view setFrameOrigin:NSMakePoint((i % 4) * 120, height - (i / 4 * 68) - 68)];
item.oamAddress.toolTip = @"OAM address";
item.position.toolTip = @"Position";
item.attributes.toolTip = @"Attributes";
item.tile.toolTip = @"Tile index";
item.tileAddress.toolTip = @"Tile address";
item.warningIcon.toolTip = @"Dropped: too many objects in line";
if ((i % 4) == 3) {
[item.verticalLine removeFromSuperview];
}
item.view.autoresizingMask = NSViewMaxXMargin | NSViewMinYMargin;
}
return self;
}
- (void)reloadData:(Document *)document
{
GB_oam_info_t *info = document.oamInfo;
uint8_t length = document.oamCount;
bool cgb = GB_is_cgb(document.gb);
uint8_t height = document.oamHeight;
for (unsigned i = 0; i < 40; i++) {
GBObjectViewItem *item = _items[i];
if (i >= length) {
item.view.hidden = true;
}
else {
item.view.hidden = false;
item.oamAddress.stringValue = [NSString stringWithFormat:@"$%04X", info[i].oam_addr];
item.position.stringValue = [NSString stringWithFormat:@"(%d, %d)",
((signed)(unsigned)info[i].x) - 8,
((signed)(unsigned)info[i].y) - 16];
item.tile.stringValue = [NSString stringWithFormat:@"$%02X", info[i].tile];
item.tileAddress.stringValue = [NSString stringWithFormat:@"$%04X", 0x8000 + info[i].tile * 0x10];
item.warningIcon.hidden = !info[i].obscured_by_line_limit;
if (cgb) {
item.attributes.stringValue = [NSString stringWithFormat:@"%c%c%c%d%d",
info[i].flags & 0x80? 'P' : '-',
info[i].flags & 0x40? 'Y' : '-',
info[i].flags & 0x20? 'X' : '-',
info[i].flags & 0x08? 1 : 0,
info[i].flags & 0x07];
}
else {
item.attributes.stringValue = [NSString stringWithFormat:@"%c%c%c%d",
info[i].flags & 0x80? 'P' : '-',
info[i].flags & 0x40? 'Y' : '-',
info[i].flags & 0x20? 'X' : '-',
info[i].flags & 0x10? 1 : 0];
}
size_t imageSize = 8 * 4 * height;
if (height == item->_lastHeight && memcmp(item->_lastImageData, info[i].image, imageSize) == 0) {
continue;
}
memcpy(item->_lastImageData, info[i].image, imageSize);
item->_lastHeight = height;
item.image.image = [Document imageFromData:[NSData dataWithBytesNoCopy:info[i].image
length:64 * 4 * 2
freeWhenDone:false]
width:8
height:height
scale:32.0 / height];
}
}
NSRect frame = self.frame;
CGFloat newHeight = MAX(68 * ((length + 3) / 4), self.superview.frame.size.height);
frame.origin.y -= newHeight - frame.size.height;
frame.size.height = newHeight;
self.frame = frame;
}
- (void)drawRect:(NSRect)dirtyRect
{
if (@available(macOS 10.14, *)) {
[[NSColor alternatingContentBackgroundColors].lastObject setFill];
}
else {
[[NSColor colorWithDeviceWhite:0.96 alpha:1] setFill];
}
NSRect frame = self.frame;
for (unsigned i = 1; i <= 5; i++) {
NSRectFill(NSMakeRect(0, frame.size.height - i * 68 * 2, frame.size.width, 68));
}
}
@end

View File

@ -0,0 +1,94 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14868" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14868"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="GBObjectViewItem">
<connections>
<outlet property="attributes" destination="AVH-lh-aVu" id="F5l-vc-9YB"/>
<outlet property="image" destination="GWF-Vk-eLx" id="cQa-lM-SqU"/>
<outlet property="oamAddress" destination="HiB-x7-HCb" id="LLX-HU-t00"/>
<outlet property="position" destination="bxJ-ig-rox" id="woq-X2-lCK"/>
<outlet property="tile" destination="Kco-1J-bKc" id="C8u-jH-bCh"/>
<outlet property="tileAddress" destination="ptE-vl-9HD" id="9K3-Oq-1vs"/>
<outlet property="verticalLine" destination="Q91-eq-oeo" id="6kc-qh-cFx"/>
<outlet property="view" destination="c22-O7-iKe" id="50f-xf-9Gg"/>
<outlet property="warningIcon" destination="dQV-cO-218" id="Rxi-Wq-Upl"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customView id="c22-O7-iKe">
<rect key="frame" x="0.0" y="0.0" width="120" height="68"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Kco-1J-bKc">
<rect key="frame" x="44" y="4" width="58" height="16"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" lineBreakMode="clipping" selectable="YES" alignment="left" title="$32" id="Aps-81-wR7">
<font key="font" size="11" name="Menlo-Regular"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="ptE-vl-9HD">
<rect key="frame" x="0.0" y="4" width="44" height="16"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" lineBreakMode="clipping" selectable="YES" alignment="center" title="$8320" id="t2G-E2-vt5">
<font key="font" size="11" name="Menlo-Regular"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="bxJ-ig-rox">
<rect key="frame" x="44" y="36" width="76" height="16"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" lineBreakMode="clipping" selectable="YES" title="(32,16)" id="oac-yZ-h47">
<font key="font" size="11" name="Menlo-Regular"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="AVH-lh-aVu">
<rect key="frame" x="44" y="20" width="76" height="16"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" lineBreakMode="clipping" selectable="YES" title="---04" id="tJX-6t-5Kx">
<font key="font" size="11" name="Menlo-Regular"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="HiB-x7-HCb">
<rect key="frame" x="44" y="52" width="76" height="16"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" lineBreakMode="clipping" selectable="YES" alignment="left" title="$FE32" id="ysm-jq-PKy">
<font key="font" size="11" name="Menlo-Bold"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" id="GWF-Vk-eLx" customClass="GBImageView">
<rect key="frame" x="2" y="24" width="40" height="40"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" id="AJz-KH-eeo"/>
</imageView>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="dQV-cO-218">
<rect key="frame" x="100" y="4" width="16" height="16"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="NSCaution" id="TQB-Vp-9Jm"/>
</imageView>
<box horizontalHuggingPriority="750" fixedFrame="YES" boxType="separator" translatesAutoresizingMaskIntoConstraints="NO" id="Q91-eq-oeo">
<rect key="frame" x="116" y="4" width="5" height="60"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
</box>
</subviews>
<point key="canvasLocation" x="132" y="149"/>
</customView>
</objects>
<resources>
<image name="NSCaution" width="32" height="32"/>
</resources>
</document>

View File

@ -1,6 +1,7 @@
#import "GBOpenGLView.h"
#import "GBView.h"
#include <OpenGL/gl.h>
#import "NSObject+DefaultsObserver.h"
#import <OpenGL/gl.h>
@implementation GBOpenGLView
@ -27,13 +28,12 @@
- (instancetype)initWithFrame:(NSRect)frameRect pixelFormat:(NSOpenGLPixelFormat *)format
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(filterChanged) name:@"GBFilterChanged" object:nil];
__unsafe_unretained GBOpenGLView *weakSelf = self;
[self observeStandardDefaultsKey:@"GBFilter" withBlock:^(id newValue) {
weakSelf.shader = nil;
[weakSelf setNeedsDisplay:true];
}];
return [super initWithFrame:frameRect pixelFormat:format];
}
- (void) filterChanged
{
self.shader = nil;
[self setNeedsDisplay:true];
}
@end

6
Cocoa/GBPaletteView.h Normal file
View File

@ -0,0 +1,6 @@
#import <Cocoa/Cocoa.h>
#import "Document.h"
@interface GBPaletteView : NSView
- (void)reloadData:(Document *)document;
@end

91
Cocoa/GBPaletteView.m Normal file
View File

@ -0,0 +1,91 @@
#import "GBPaletteView.h"
@interface GBPaletteViewItem : NSObject
@property IBOutlet NSView *view;
@property (strong) IBOutlet NSTextField *label;
@property (strong) IBOutlet NSTextField *color0;
@property (strong) IBOutlet NSTextField *color1;
@property (strong) IBOutlet NSTextField *color2;
@property (strong) IBOutlet NSTextField *color3;
@end
@implementation GBPaletteViewItem
@end
@implementation GBPaletteView
{
NSMutableArray<NSTextField *> *_colors;
}
- (instancetype)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
_colors = [NSMutableArray array];
CGFloat height = self.frame.size.height;
for (unsigned i = 0; i < 16; i++) {
GBPaletteViewItem *item = [[GBPaletteViewItem alloc] init];
[[NSBundle mainBundle] loadNibNamed:@"GBPaletteViewRow" owner:item topLevelObjects:nil];
[self addSubview:item.view];
[item.view setFrameOrigin:NSMakePoint(0, height - (i * 24) - 24)];
item.label.stringValue = [NSString stringWithFormat:@"%@ %d", i < 8? @"Background" : @"Object", i % 8];
item.view.autoresizingMask = NSViewMaxXMargin | NSViewMinYMargin;
[_colors addObject:item.color0];
[_colors addObject:item.color1];
[_colors addObject:item.color2];
[_colors addObject:item.color3];
}
return self;
}
- (void)reloadData:(Document *)document
{
GB_gameboy_t *gb = document.gb;
uint8_t *bg = GB_get_direct_access(gb, GB_DIRECT_ACCESS_BGP, NULL, NULL);
uint8_t *obj = GB_get_direct_access(gb, GB_DIRECT_ACCESS_OBP, NULL, NULL);
for (unsigned i = 0; i < 4 * 8 * 2; i++) {
uint8_t index = i % (4 * 8);
uint8_t *palette = i >= 4 * 8 ? obj : bg;
uint16_t color = (palette[(index << 1) + 1] << 8) | palette[(index << 1)];
uint32_t nativeColor = GB_convert_rgb15(gb, color, false);
uint8_t r = color & 0x1F,
g = (color >> 5) & 0x1F,
b = (color >> 10) & 0x1F;
NSTextField *field = _colors[i];
field.stringValue = [NSString stringWithFormat:@"$%04X", color];
field.textColor = r * 3 + g * 4 + b * 2 > 120? [NSColor blackColor] : [NSColor whiteColor];
field.toolTip = [NSString stringWithFormat:@"Red: %d, Green: %d, Blue: %d", r, g, b];
field.backgroundColor = [NSColor colorWithRed:(nativeColor & 0xFF) / 255.0
green:((nativeColor >> 8) & 0xFF) / 255.0
blue:((nativeColor >> 16) & 0xFF) / 255.0
alpha:1.0];
}
}
- (void)drawRect:(NSRect)dirtyRect
{
NSRect frame = self.frame;
if (@available(macOS 10.14, *)) {
[[NSColor alternatingContentBackgroundColors].lastObject setFill];
}
else {
[[NSColor colorWithDeviceWhite:0.96 alpha:1] setFill];
}
for (unsigned i = 1; i <= 8; i++) {
NSRectFill(NSMakeRect(0, frame.size.height - i * 24 * 2, frame.size.width, 24));
}
if (@available(macOS 10.14, *)) {
[[NSColor alternatingContentBackgroundColors].firstObject setFill];
}
else {
[[NSColor controlBackgroundColor] setFill];
}
for (unsigned i = 0; i < 8; i++) {
NSRectFill(NSMakeRect(0, frame.size.height - i * 24 * 2 - 24, frame.size.width, 24));
}
}
@end

View File

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14868" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14868"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="GBPaletteViewItem">
<connections>
<outlet property="color0" destination="ypt-t4-Mf3" id="Bam-dX-hHk"/>
<outlet property="color1" destination="KkX-Z8-Sqi" id="uCl-UT-PWu"/>
<outlet property="color2" destination="jDk-Ej-4yI" id="Xjs-el-m3s"/>
<outlet property="color3" destination="7PI-YE-fTk" id="7J8-aH-LEO"/>
<outlet property="label" destination="NOK-yI-LKh" id="AK3-g5-H7a"/>
<outlet property="view" destination="c22-O7-iKe" id="DPx-8k-YQB"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customView id="c22-O7-iKe">
<rect key="frame" x="0.0" y="0.0" width="512" height="24"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="ypt-t4-Mf3">
<rect key="frame" x="131" y="0.0" width="96" height="24"/>
<autoresizingMask key="autoresizingMask"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" sendsActionOnEndEditing="YES" borderStyle="border" alignment="center" title="$7FFF" drawsBackground="YES" id="sKu-Uy-2LG">
<font key="font" size="13" name="Menlo-Regular"/>
<color key="textColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</textFieldCell>
</textField>
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="KkX-Z8-Sqi">
<rect key="frame" x="226" y="0.0" width="96" height="24"/>
<autoresizingMask key="autoresizingMask"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" sendsActionOnEndEditing="YES" borderStyle="border" alignment="center" title="$7FFF" drawsBackground="YES" id="9LH-TF-W1L">
<font key="font" size="13" name="Menlo-Regular"/>
<color key="textColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</textFieldCell>
</textField>
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="jDk-Ej-4yI">
<rect key="frame" x="321" y="0.0" width="96" height="24"/>
<autoresizingMask key="autoresizingMask"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" sendsActionOnEndEditing="YES" borderStyle="border" alignment="center" title="$7FFF" drawsBackground="YES" id="arE-i5-zCC">
<font key="font" size="13" name="Menlo-Regular"/>
<color key="textColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</textFieldCell>
</textField>
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="7PI-YE-fTk">
<rect key="frame" x="416" y="0.0" width="96" height="24"/>
<autoresizingMask key="autoresizingMask"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" sendsActionOnEndEditing="YES" borderStyle="border" alignment="center" title="$7FFF" drawsBackground="YES" id="ZbU-nE-FsE">
<font key="font" size="13" name="Menlo-Regular"/>
<color key="textColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="NOK-yI-LKh">
<rect key="frame" x="4" y="0.0" width="121" height="20"/>
<autoresizingMask key="autoresizingMask"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="Background 0" id="qM4-cY-SDE">
<font key="font" usesAppearanceFont="YES"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<point key="canvasLocation" x="139" y="31"/>
</customView>
</objects>
</document>

View File

@ -0,0 +1,6 @@
#import <Cocoa/Cocoa.h>
@interface GBPreferenceButton : NSButton
@property (nonatomic) IBInspectable NSString *preferenceName;
@property IBInspectable BOOL invertValue;
@end

View File

@ -0,0 +1,30 @@
#import "GBPreferenceButton.h"
#import "NSObject+DefaultsObserver.h"
@implementation GBPreferenceButton
- (BOOL)sendAction:(SEL)action to:(id)target
{
[[NSUserDefaults standardUserDefaults] setBool:self.state ^ self.invertValue forKey:_preferenceName];
return [super sendAction:action to:target];
}
- (void)updateValue
{
if (!_preferenceName) return;
self.state = [[NSUserDefaults standardUserDefaults] boolForKey:_preferenceName] ^ self.invertValue;
}
- (void)setPreferenceName:(NSString *)preferenceName
{
_preferenceName = preferenceName;
[self observeStandardDefaultsKey:_preferenceName selector:@selector(updateValue)];
}
- (void)viewDidMoveToWindow
{
[super viewDidMoveToWindow];
[self updateValue];
}
@end

View File

@ -0,0 +1,9 @@
#import <Cocoa/Cocoa.h>
@interface GBPreferenceMenuItem : NSMenuItem
@property (nonatomic) IBInspectable NSString *preferenceValue;
@end
@interface GBPreferencePopUpButton : NSPopUpButton
@property (nonatomic) IBInspectable NSString *preferenceName;
@end

View File

@ -0,0 +1,51 @@
#import "GBPreferencePopUpButton.h"
#import "NSObject+DefaultsObserver.h"
@implementation GBPreferenceMenuItem
@end
@implementation GBPreferencePopUpButton
- (BOOL)sendAction:(SEL)action to:(id)target
{
GBPreferenceMenuItem *item = (GBPreferenceMenuItem *)self.selectedItem;
if ([item isKindOfClass:[GBPreferenceMenuItem class]]) {
[[NSUserDefaults standardUserDefaults] setObject:item.preferenceValue forKey:_preferenceName];
}
else {
[[NSUserDefaults standardUserDefaults] setInteger:item.tag forKey:_preferenceName];
}
return [super sendAction:action to:target];
}
- (void)updateValue
{
if (!_preferenceName) return;
NSString *stringValue = [[NSUserDefaults standardUserDefaults] objectForKey:_preferenceName];
if ([stringValue isKindOfClass:[NSString class]]) {
for (GBPreferenceMenuItem *item in self.menu.itemArray) {
if ([item isKindOfClass:[GBPreferenceMenuItem class]] &&
[item.preferenceValue isEqualToString:stringValue]) {
[self selectItem:item];
return;
}
}
}
else {
[self selectItemWithTag:[[NSUserDefaults standardUserDefaults] integerForKey:_preferenceName]];
}
}
- (void)setPreferenceName:(NSString *)preferenceName
{
_preferenceName = preferenceName;
[self observeStandardDefaultsKey:_preferenceName selector:@selector(updateValue)];
}
- (void)viewDidMoveToWindow
{
[super viewDidMoveToWindow];
[self updateValue];
}
@end

View File

@ -0,0 +1,6 @@
#import <Cocoa/Cocoa.h>
@interface GBPreferencesSlider : NSSlider
@property (nonatomic) IBInspectable NSString *preferenceName;
@property IBInspectable unsigned denominator;
@end

View File

@ -0,0 +1,29 @@
#import "GBPreferencesSlider.h"
#import "NSObject+DefaultsObserver.h"
@implementation GBPreferencesSlider
- (BOOL)sendAction:(SEL)action to:(id)target
{
[[NSUserDefaults standardUserDefaults] setDouble:self.doubleValue / (self.denominator ?: 1) forKey:_preferenceName];
return [super sendAction:action to:target];
}
- (void)updateValue
{
if (!_preferenceName) return;
self.doubleValue = [[NSUserDefaults standardUserDefaults] doubleForKey:_preferenceName] * (self.denominator ?: 1);
}
- (void)setPreferenceName:(NSString *)preferenceName
{
_preferenceName = preferenceName;
[self observeStandardDefaultsKey:_preferenceName selector:@selector(updateValue)];
}
- (void)viewDidMoveToWindow
{
[super viewDidMoveToWindow];
[self updateValue];
}
@end

View File

@ -3,35 +3,19 @@
#import "GBPaletteEditorController.h"
@interface GBPreferencesWindow : NSWindow <NSTableViewDelegate, NSTableViewDataSource, JOYListener>
@property (nonatomic, strong) IBOutlet NSTableView *controlsTableView;
@property (nonatomic, strong) IBOutlet NSPopUpButton *graphicsFilterPopupButton;
@property (nonatomic, strong) IBOutlet NSButton *analogControlsCheckbox;
@property (nonatomic, strong) IBOutlet NSButton *aspectRatioCheckbox;
@property (nonatomic, strong) IBOutlet NSPopUpButton *highpassFilterPopupButton;
@property (nonatomic, strong) IBOutlet NSPopUpButton *colorCorrectionPopupButton;
@property (nonatomic, strong) IBOutlet NSPopUpButton *frameBlendingModePopupButton;
@property (nonatomic, strong) IBOutlet NSPopUpButton *colorPalettePopupButton;
@property (nonatomic, strong) IBOutlet NSPopUpButton *displayBorderPopupButton;
@property (nonatomic, strong) IBOutlet NSPopUpButton *rewindPopupButton;
@property (nonatomic, strong) IBOutlet NSPopUpButton *rtcPopupButton;
@property (nonatomic, strong) IBOutlet NSButton *configureJoypadButton;
@property (nonatomic, strong) IBOutlet NSButton *skipButton;
@property (nonatomic, strong) IBOutlet NSMenuItem *bootROMsFolderItem;
@property (nonatomic, strong) IBOutlet NSPopUpButtonCell *bootROMsButton;
@property (nonatomic, strong) IBOutlet NSPopUpButton *rumbleModePopupButton;
@property (nonatomic, weak) IBOutlet NSSlider *temperatureSlider;
@property (nonatomic, weak) IBOutlet NSSlider *interferenceSlider;
@property (nonatomic, weak) IBOutlet NSPopUpButton *dmgPopupButton;
@property (nonatomic, weak) IBOutlet NSPopUpButton *sgbPopupButton;
@property (nonatomic, weak) IBOutlet NSPopUpButton *cgbPopupButton;
@property (nonatomic, weak) IBOutlet NSPopUpButton *preferredJoypadButton;
@property (nonatomic, weak) IBOutlet NSPopUpButton *playerListButton;
@property (nonatomic, weak) IBOutlet NSButton *autoUpdatesCheckbox;
@property (weak) IBOutlet NSSlider *volumeSlider;
@property (weak) IBOutlet NSButton *OSDCheckbox;
@property (weak) IBOutlet NSButton *screenshotFilterCheckbox;
@property (weak) IBOutlet GBPaletteEditorController *paletteEditorController;
@property (strong) IBOutlet NSWindow *paletteEditor;
@property (weak) IBOutlet NSButton *joystickMBC7Checkbox;
@property (weak) IBOutlet NSButton *mouseMBC7Checkbox;
@property IBOutlet NSTableView *controlsTableView;
@property IBOutlet NSButton *configureJoypadButton;
@property IBOutlet NSButton *skipButton;
@property IBOutlet NSMenuItem *bootROMsFolderItem;
@property IBOutlet NSPopUpButtonCell *bootROMsButton;
@property IBOutlet NSPopUpButton *preferredJoypadButton;
@property IBOutlet NSPopUpButton *playerListButton;
@property IBOutlet GBPaletteEditorController *paletteEditorController;
@property IBOutlet NSWindow *paletteEditor;
@property IBOutlet NSWindow *joyconsSheet;
@property IBOutlet NSPopUpButton *colorCorrectionPopupButton;
@property IBOutlet NSPopUpButton *highpassFilterPopupButton;
@property IBOutlet NSPopUpButton *colorPalettePopupButton;
@property IBOutlet NSPopUpButton *hotkey1PopupButton;
@property IBOutlet NSPopUpButton *hotkey2PopupButton;
@end

View File

@ -1,8 +1,10 @@
#import "GBPreferencesWindow.h"
#import "GBJoyConManager.h"
#import "NSString+StringForKey.h"
#import "GBButtons.h"
#import "BigSurToolbar.h"
#import "GBViewMetal.h"
#import "GBWarningPopover.h"
#import <Carbon/Carbon.h>
@implementation GBPreferencesWindow
@ -13,59 +15,12 @@
NSString *joystick_being_configured;
bool joypad_wait;
NSPopUpButton *_graphicsFilterPopupButton;
NSPopUpButton *_highpassFilterPopupButton;
NSPopUpButton *_colorCorrectionPopupButton;
NSPopUpButton *_frameBlendingModePopupButton;
NSPopUpButton *_colorPalettePopupButton;
NSPopUpButton *_displayBorderPopupButton;
NSPopUpButton *_rewindPopupButton;
NSPopUpButton *_rtcPopupButton;
NSButton *_aspectRatioCheckbox;
NSButton *_analogControlsCheckbox;
NSEventModifierFlags previousModifiers;
NSPopUpButton *_dmgPopupButton, *_sgbPopupButton, *_cgbPopupButton;
NSPopUpButton *_preferredJoypadButton;
NSPopUpButton *_rumbleModePopupButton;
NSSlider *_temperatureSlider;
NSSlider *_interferenceSlider;
NSSlider *_volumeSlider;
NSButton *_autoUpdatesCheckbox;
NSButton *_OSDCheckbox;
NSButton *_screenshotFilterCheckbox;
NSButton *_joystickMBC7Checkbox;
NSButton *_mouseMBC7Checkbox;
}
+ (NSArray *)filterList
{
/* The filter list as ordered in the popup button */
static NSArray * filters = nil;
if (!filters) {
filters = @[
@"NearestNeighbor",
@"Bilinear",
@"SmoothBilinear",
@"MonoLCD",
@"LCD",
@"CRT",
@"Scale2x",
@"Scale4x",
@"AAScale2x",
@"AAScale4x",
@"HQ2x",
@"OmniScale",
@"OmniScaleLegacy",
@"AAOmniScaleLegacy",
];
}
return filters;
}
- (NSWindowToolbarStyle)toolbarStyle
{
return NSWindowToolbarStylePreference;
return NSWindowToolbarStyleExpanded;
}
- (void)close
@ -77,151 +32,9 @@
[super close];
}
- (NSPopUpButton *)graphicsFilterPopupButton
static inline NSString *keyEquivalentString(NSMenuItem *item)
{
return _graphicsFilterPopupButton;
}
- (void)setGraphicsFilterPopupButton:(NSPopUpButton *)graphicsFilterPopupButton
{
_graphicsFilterPopupButton = graphicsFilterPopupButton;
NSString *filter = [[NSUserDefaults standardUserDefaults] objectForKey:@"GBFilter"];
[_graphicsFilterPopupButton selectItemAtIndex:[[[self class] filterList] indexOfObject:filter]];
}
- (NSPopUpButton *)highpassFilterPopupButton
{
return _highpassFilterPopupButton;
}
- (void)setColorCorrectionPopupButton:(NSPopUpButton *)colorCorrectionPopupButton
{
_colorCorrectionPopupButton = colorCorrectionPopupButton;
NSInteger mode = [[NSUserDefaults standardUserDefaults] integerForKey:@"GBColorCorrection"];
[_colorCorrectionPopupButton selectItemAtIndex:mode];
}
- (NSPopUpButton *)colorCorrectionPopupButton
{
return _colorCorrectionPopupButton;
}
- (void)setTemperatureSlider:(NSSlider *)temperatureSlider
{
_temperatureSlider = temperatureSlider;
[temperatureSlider setDoubleValue:[[NSUserDefaults standardUserDefaults] doubleForKey:@"GBLightTemperature"] * 256];
}
- (NSSlider *)temperatureSlider
{
return _temperatureSlider;
}
- (void)setInterferenceSlider:(NSSlider *)interferenceSlider
{
_interferenceSlider = interferenceSlider;
[interferenceSlider setDoubleValue:[[NSUserDefaults standardUserDefaults] doubleForKey:@"GBInterferenceVolume"] * 256];
}
- (NSSlider *)interferenceSlider
{
return _interferenceSlider;
}
- (void)setVolumeSlider:(NSSlider *)volumeSlider
{
_volumeSlider = volumeSlider;
[volumeSlider setDoubleValue:[[NSUserDefaults standardUserDefaults] doubleForKey:@"GBVolume"] * 256];
}
- (NSSlider *)volumeSlider
{
return _volumeSlider;
}
- (void)setFrameBlendingModePopupButton:(NSPopUpButton *)frameBlendingModePopupButton
{
_frameBlendingModePopupButton = frameBlendingModePopupButton;
NSInteger mode = [[NSUserDefaults standardUserDefaults] integerForKey:@"GBFrameBlendingMode"];
[_frameBlendingModePopupButton selectItemAtIndex:mode];
}
- (NSPopUpButton *)frameBlendingModePopupButton
{
return _frameBlendingModePopupButton;
}
- (void)setColorPalettePopupButton:(NSPopUpButton *)colorPalettePopupButton
{
_colorPalettePopupButton = colorPalettePopupButton;
[self updatePalettesMenu];
NSInteger mode = [[NSUserDefaults standardUserDefaults] integerForKey:@"GBColorPalette"];
if (mode >= 0) {
[_colorPalettePopupButton selectItemWithTag:mode];
}
else {
[_colorPalettePopupButton selectItemWithTitle:[[NSUserDefaults standardUserDefaults] stringForKey:@"GBCurrentTheme"] ?: @""];
}
}
- (NSPopUpButton *)colorPalettePopupButton
{
return _colorPalettePopupButton;
}
- (void)setDisplayBorderPopupButton:(NSPopUpButton *)displayBorderPopupButton
{
_displayBorderPopupButton = displayBorderPopupButton;
NSInteger mode = [[NSUserDefaults standardUserDefaults] integerForKey:@"GBBorderMode"];
[_displayBorderPopupButton selectItemWithTag:mode];
}
- (NSPopUpButton *)displayBorderPopupButton
{
return _displayBorderPopupButton;
}
- (void)setRumbleModePopupButton:(NSPopUpButton *)rumbleModePopupButton
{
_rumbleModePopupButton = rumbleModePopupButton;
NSInteger mode = [[NSUserDefaults standardUserDefaults] integerForKey:@"GBRumbleMode"];
[_rumbleModePopupButton selectItemWithTag:mode];
}
- (NSPopUpButton *)rumbleModePopupButton
{
return _rumbleModePopupButton;
}
- (void)setRewindPopupButton:(NSPopUpButton *)rewindPopupButton
{
_rewindPopupButton = rewindPopupButton;
NSInteger length = [[NSUserDefaults standardUserDefaults] integerForKey:@"GBRewindLength"];
[_rewindPopupButton selectItemWithTag:length];
}
- (NSPopUpButton *)rewindPopupButton
{
return _rewindPopupButton;
}
- (NSPopUpButton *)rtcPopupButton
{
return _rtcPopupButton;
}
- (void)setRtcPopupButton:(NSPopUpButton *)rtcPopupButton
{
_rtcPopupButton = rtcPopupButton;
NSInteger mode = [[NSUserDefaults standardUserDefaults] integerForKey:@"GBRTCMode"];
[_rtcPopupButton selectItemAtIndex:mode];
}
- (void)setHighpassFilterPopupButton:(NSPopUpButton *)highpassFilterPopupButton
{
_highpassFilterPopupButton = highpassFilterPopupButton;
[_highpassFilterPopupButton selectItemAtIndex:[[[NSUserDefaults standardUserDefaults] objectForKey:@"GBHighpassFilter"] unsignedIntegerValue]];
return [NSString stringWithFormat:@"%s%@", (item.keyEquivalentModifierMask & NSEventModifierFlagShift)? "^":"", item.keyEquivalent];
}
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
@ -253,7 +66,7 @@
}
if (is_button_being_modified && button_being_modified == row) {
return @"Select a new key...";
return @"Select a new key";
}
NSNumber *key = [[NSUserDefaults standardUserDefaults] valueForKey:button_to_preference_name(row, self.playerListButton.selectedTag)];
@ -312,82 +125,6 @@
previousModifiers = event.modifierFlags;
}
- (IBAction)graphicFilterChanged:(NSPopUpButton *)sender
{
[[NSUserDefaults standardUserDefaults] setObject:[[self class] filterList][[sender indexOfSelectedItem]]
forKey:@"GBFilter"];
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBFilterChanged" object:nil];
}
- (IBAction)highpassFilterChanged:(id)sender
{
[[NSUserDefaults standardUserDefaults] setObject:@([sender indexOfSelectedItem])
forKey:@"GBHighpassFilter"];
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBHighpassFilterChanged" object:nil];
}
- (IBAction)changeMBC7JoystickOverride:(id)sender
{
[[NSUserDefaults standardUserDefaults] setBool: [(NSButton *)sender state] == NSOnState
forKey:@"GBMBC7JoystickOverride"];
}
- (IBAction)changeMBC7AllowMouse:(id)sender
{
[[NSUserDefaults standardUserDefaults] setBool: [(NSButton *)sender state] == NSOnState
forKey:@"GBMBC7AllowMouse"];
}
- (IBAction)changeAnalogControls:(id)sender
{
[[NSUserDefaults standardUserDefaults] setBool: [(NSButton *)sender state] == NSOnState
forKey:@"GBAnalogControls"];
}
- (IBAction)changeAspectRatio:(id)sender
{
[[NSUserDefaults standardUserDefaults] setBool: [(NSButton *)sender state] != NSOnState
forKey:@"GBAspectRatioUnkept"];
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBAspectChanged" object:nil];
}
- (IBAction)colorCorrectionChanged:(id)sender
{
[[NSUserDefaults standardUserDefaults] setObject:@([sender indexOfSelectedItem])
forKey:@"GBColorCorrection"];
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBColorCorrectionChanged" object:nil];
}
- (IBAction)lightTemperatureChanged:(id)sender
{
[[NSUserDefaults standardUserDefaults] setObject:@([sender doubleValue] / 256.0)
forKey:@"GBLightTemperature"];
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBLightTemperatureChanged" object:nil];
}
- (IBAction)interferenceVolumeChanged:(id)sender
{
[[NSUserDefaults standardUserDefaults] setObject:@([sender doubleValue] / 256.0)
forKey:@"GBInterferenceVolume"];
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBInterferenceVolumeChanged" object:nil];
}
- (IBAction)volumeChanged:(id)sender
{
[[NSUserDefaults standardUserDefaults] setObject:@([sender doubleValue] / 256.0)
forKey:@"GBVolume"];
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBVolumeChanged" object:nil];
}
- (IBAction)franeBlendingModeChanged:(id)sender
{
[[NSUserDefaults standardUserDefaults] setObject:@([sender indexOfSelectedItem])
forKey:@"GBFrameBlendingMode"];
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBFrameBlendingModeChanged" object:nil];
}
- (void)updatePalettesMenu
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
@ -436,40 +173,18 @@
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBColorPaletteChanged" object:nil];
}
- (IBAction)displayBorderChanged:(id)sender
- (IBAction)hotkey1Changed:(id)sender
{
[[NSUserDefaults standardUserDefaults] setObject:@([sender selectedItem].tag)
forKey:@"GBBorderMode"];
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBBorderModeChanged" object:nil];
[[NSUserDefaults standardUserDefaults] setObject:keyEquivalentString([sender selectedItem])
forKey:@"GBJoypadHotkey1"];
}
- (IBAction)rumbleModeChanged:(id)sender
- (IBAction)hotkey2Changed:(id)sender
{
[[NSUserDefaults standardUserDefaults] setObject:@([sender selectedItem].tag)
forKey:@"GBRumbleMode"];
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBRumbleModeChanged" object:nil];
[[NSUserDefaults standardUserDefaults] setObject:keyEquivalentString([sender selectedItem])
forKey:@"GBJoypadHotkey2"];
}
- (IBAction)rewindLengthChanged:(id)sender
{
[[NSUserDefaults standardUserDefaults] setObject:@([sender selectedTag])
forKey:@"GBRewindLength"];
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBRewindLengthChanged" object:nil];
}
- (IBAction)rtcModeChanged:(id)sender
{
[[NSUserDefaults standardUserDefaults] setObject:@([sender indexOfSelectedItem])
forKey:@"GBRTCMode"];
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBRTCModeChanged" object:nil];
}
- (IBAction)changeAutoUpdates:(id)sender
{
[[NSUserDefaults standardUserDefaults] setBool: [(NSButton *)sender state] == NSOnState
forKey:@"GBAutoUpdatesEnabled"];
}
- (IBAction) configureJoypad:(id)sender
{
@ -490,7 +205,7 @@
if (joystick_configuration_state == GBUnderclock) {
[self.configureJoypadButton setTitle:@"Press Button for Slo-Mo"]; // Full name is too long :<
}
else if (joystick_configuration_state < GBButtonCount) {
else if (joystick_configuration_state < GBJoypadButtonCount) {
[self.configureJoypadButton setTitle:[NSString stringWithFormat:@"Press Button for %@", GBButtonNames[joystick_configuration_state]]];
}
else {
@ -512,7 +227,7 @@
if (!button.isPressed) return;
if (joystick_configuration_state == -1) return;
if (joystick_configuration_state == GBButtonCount) return;
if (joystick_configuration_state == GBJoypadButtonCount) return;
if (!joystick_being_configured) {
joystick_being_configured = controller.uniqueID;
}
@ -554,6 +269,8 @@
[GBTurbo] = JOYButtonUsageL1,
[GBRewind] = JOYButtonUsageL2,
[GBUnderclock] = JOYButtonUsageR1,
[GBHotkey1] = GBJoyKitHotkey1,
[GBHotkey2] = GBJoyKitHotkey2,
};
if (joystick_configuration_state == GBUnderclock) {
@ -587,50 +304,6 @@
[self advanceConfigurationStateMachine];
}
- (NSButton *)joystickMBC7Checkbox
{
return _joystickMBC7Checkbox;
}
- (void)setJoystickMBC7Checkbox:(NSButton *)joystickMBC7Checkbox
{
_joystickMBC7Checkbox = joystickMBC7Checkbox;
[_joystickMBC7Checkbox setState: [[NSUserDefaults standardUserDefaults] boolForKey:@"GBMBC7JoystickOverride"]];
}
- (NSButton *)mouseMBC7Checkbox
{
return _mouseMBC7Checkbox;
}
- (void)setMouseMBC7Checkbox:(NSButton *)mouseMBC7Checkbox
{
_mouseMBC7Checkbox = mouseMBC7Checkbox;
[_mouseMBC7Checkbox setState: [[NSUserDefaults standardUserDefaults] boolForKey:@"GBMBC7AllowMouse"]];
}
- (NSButton *)analogControlsCheckbox
{
return _analogControlsCheckbox;
}
- (void)setAnalogControlsCheckbox:(NSButton *)analogControlsCheckbox
{
_analogControlsCheckbox = analogControlsCheckbox;
[_analogControlsCheckbox setState: [[NSUserDefaults standardUserDefaults] boolForKey:@"GBAnalogControls"]];
}
- (NSButton *)aspectRatioCheckbox
{
return _aspectRatioCheckbox;
}
- (void)setAspectRatioCheckbox:(NSButton *)aspectRatioCheckbox
{
_aspectRatioCheckbox = aspectRatioCheckbox;
[_aspectRatioCheckbox setState: ![[NSUserDefaults standardUserDefaults] boolForKey:@"GBAspectRatioUnkept"]];
}
- (void)awakeFromNib
{
[super awakeFromNib];
@ -638,6 +311,32 @@
[[NSDistributedNotificationCenter defaultCenter] addObserver:self.controlsTableView selector:@selector(reloadData) name:(NSString*)kTISNotifySelectedKeyboardInputSourceChanged object:nil];
[JOYController registerListener:self];
joystick_configuration_state = -1;
[self refreshJoypadMenu:nil];
NSString *keyEquivalent = [[NSUserDefaults standardUserDefaults] stringForKey:@"GBJoypadHotkey1"];
for (NSMenuItem *item in _hotkey1PopupButton.menu.itemArray) {
if ([keyEquivalent isEqualToString:keyEquivalentString(item)]) {
[_hotkey1PopupButton selectItem:item];
break;
}
}
keyEquivalent = [[NSUserDefaults standardUserDefaults] stringForKey:@"GBJoypadHotkey2"];
for (NSMenuItem *item in _hotkey2PopupButton.menu.itemArray) {
if ([keyEquivalent isEqualToString:keyEquivalentString(item)]) {
[_hotkey2PopupButton selectItem:item];
break;
}
}
[self updatePalettesMenu];
NSInteger mode = [[NSUserDefaults standardUserDefaults] integerForKey:@"GBColorPalette"];
if (mode >= 0) {
[_colorPalettePopupButton selectItemWithTag:mode];
}
else {
[_colorPalettePopupButton selectItemWithTitle:[[NSUserDefaults standardUserDefaults] stringForKey:@"GBCurrentTheme"] ?: @""];
}
}
- (void)dealloc
@ -690,77 +389,6 @@
[self updateBootROMFolderButton];
}
- (void)setDmgPopupButton:(NSPopUpButton *)dmgPopupButton
{
_dmgPopupButton = dmgPopupButton;
[_dmgPopupButton selectItemWithTag:[[NSUserDefaults standardUserDefaults] integerForKey:@"GBDMGModel"]];
}
- (NSPopUpButton *)dmgPopupButton
{
return _dmgPopupButton;
}
- (void)setSgbPopupButton:(NSPopUpButton *)sgbPopupButton
{
_sgbPopupButton = sgbPopupButton;
[_sgbPopupButton selectItemWithTag:[[NSUserDefaults standardUserDefaults] integerForKey:@"GBSGBModel"]];
}
- (NSPopUpButton *)sgbPopupButton
{
return _sgbPopupButton;
}
- (void)setCgbPopupButton:(NSPopUpButton *)cgbPopupButton
{
_cgbPopupButton = cgbPopupButton;
[_cgbPopupButton selectItemWithTag:[[NSUserDefaults standardUserDefaults] integerForKey:@"GBCGBModel"]];
}
- (NSPopUpButton *)cgbPopupButton
{
return _cgbPopupButton;
}
- (IBAction)dmgModelChanged:(id)sender
{
[[NSUserDefaults standardUserDefaults] setObject:@([sender selectedTag])
forKey:@"GBDMGModel"];
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBDMGModelChanged" object:nil];
}
- (IBAction)sgbModelChanged:(id)sender
{
[[NSUserDefaults standardUserDefaults] setObject:@([sender selectedTag])
forKey:@"GBSGBModel"];
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBSGBModelChanged" object:nil];
}
- (IBAction)cgbModelChanged:(id)sender
{
[[NSUserDefaults standardUserDefaults] setObject:@([sender selectedTag])
forKey:@"GBCGBModel"];
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBCGBModelChanged" object:nil];
}
- (IBAction)reloadButtonsData:(id)sender
{
[self.controlsTableView reloadData];
}
- (void)setPreferredJoypadButton:(NSPopUpButton *)preferredJoypadButton
{
_preferredJoypadButton = preferredJoypadButton;
[self refreshJoypadMenu:nil];
}
- (NSPopUpButton *)preferredJoypadButton
{
return _preferredJoypadButton;
}
- (void)controllerConnected:(JOYController *)controller
{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.25 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
@ -824,55 +452,48 @@
[[NSUserDefaults standardUserDefaults] setObject:default_joypads forKey:@"JoyKitDefaultControllers"];
}
- (NSButton *)autoUpdatesCheckbox
- (IBAction)displayColorCorrectionHelp:(id)sender
{
return _autoUpdatesCheckbox;
[GBWarningPopover popoverWithContents:
GB_inline_const(NSString *[], {
[GB_COLOR_CORRECTION_DISABLED] = @"Colors are directly interpreted as sRGB, resulting in unbalanced colors and inaccurate hues.",
[GB_COLOR_CORRECTION_CORRECT_CURVES] = @"Colors have their brightness corrected, but hues remain unbalanced.",
[GB_COLOR_CORRECTION_MODERN_BALANCED] = @"Emulates a modern display. Blue contrast is moderately enhanced at the cost of slight hue inaccuracy.",
[GB_COLOR_CORRECTION_MODERN_BOOST_CONTRAST] = @"Like Modern Balanced, but further boosts the contrast of greens and magentas that is lacking on the original hardware.",
[GB_COLOR_CORRECTION_REDUCE_CONTRAST] = @"Slightly reduce the contrast to better represent the tint and contrast of the original display.",
[GB_COLOR_CORRECTION_LOW_CONTRAST] = @"Harshly reduce the contrast to accurately represent the tint and low constrast of the original display.",
[GB_COLOR_CORRECTION_MODERN_ACCURATE] = @"Emulates a modern display. Colors have their hues and brightness corrected.",
}) [self.colorCorrectionPopupButton.selectedItem.tag]
title:self.colorCorrectionPopupButton.selectedItem.title
onView:sender
timeout:6
preferredEdge:NSRectEdgeMaxX];
}
- (void)setAutoUpdatesCheckbox:(NSButton *)autoUpdatesCheckbox
- (IBAction)displayHighPassHelp:(id)sender
{
_autoUpdatesCheckbox = autoUpdatesCheckbox;
[_autoUpdatesCheckbox setState: [[NSUserDefaults standardUserDefaults] boolForKey:@"GBAutoUpdatesEnabled"]];
[GBWarningPopover popoverWithContents:
GB_inline_const(NSString *[], {
[GB_HIGHPASS_OFF] = @"No high-pass filter will be applied. DC offset will be kept, pausing and resuming will trigger audio pops.",
[GB_HIGHPASS_ACCURATE] = @"An accurate high-pass filter will be applied, removing the DC offset while somewhat attenuating the bass.",
[GB_HIGHPASS_REMOVE_DC_OFFSET] = @"A high-pass filter will be applied to the DC offset itself, removing the DC offset while preserving the waveform.",
}) [self.highpassFilterPopupButton.selectedItem.tag]
title:self.highpassFilterPopupButton.selectedItem.title
onView:sender
timeout:6
preferredEdge:NSRectEdgeMaxX];
}
- (NSButton *)OSDCheckbox
- (IBAction)arrangeJoyCons:(id)sender
{
return _OSDCheckbox;
[GBJoyConManager sharedInstance].arrangementMode = true;
[self beginSheet:self.joyconsSheet completionHandler:nil];
}
- (void)setOSDCheckbox:(NSButton *)OSDCheckbox
- (IBAction)closeJoyConsSheet:(id)sender
{
_OSDCheckbox = OSDCheckbox;
[_OSDCheckbox setState: [[NSUserDefaults standardUserDefaults] boolForKey:@"GBOSDEnabled"]];
}
- (IBAction)changeOSDEnabled:(id)sender
{
[[NSUserDefaults standardUserDefaults] setBool:[(NSButton *)sender state] == NSOnState
forKey:@"GBOSDEnabled"];
}
- (IBAction)changeFilterScreenshots:(id)sender
{
[[NSUserDefaults standardUserDefaults] setBool:[(NSButton *)sender state] == NSOnState
forKey:@"GBFilterScreenshots"];
}
- (NSButton *)screenshotFilterCheckbox
{
return _screenshotFilterCheckbox;
}
- (void)setScreenshotFilterCheckbox:(NSButton *)screenshotFilterCheckbox
{
_screenshotFilterCheckbox = screenshotFilterCheckbox;
if (![GBViewMetal isSupported]) {
[_screenshotFilterCheckbox setEnabled:false];
}
else {
[_screenshotFilterCheckbox setState: [[NSUserDefaults standardUserDefaults] boolForKey:@"GBFilterScreenshots"]];
}
[self endSheet:self.joyconsSheet];
[GBJoyConManager sharedInstance].arrangementMode = false;
}
@end

54
Cocoa/GBS.xib Normal file → Executable file
View File

@ -44,7 +44,7 @@
</textFieldCell>
</textField>
<button focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="qxJ-pH-d0y">
<rect key="frame" x="61.5" y="127" width="39" height="23"/>
<rect key="frame" x="63.5" y="128" width="38" height="23"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="roundTextured" bezelStyle="texturedRounded" image="Play" imagePosition="only" alignment="center" alternateImage="Pause" state="on" borderStyle="border" focusRingType="none" inset="2" id="3ZK-br-UrS">
<behavior key="behavior" pushIn="YES" changeContents="YES" lightByContents="YES"/>
@ -54,37 +54,14 @@
<action selector="togglePause:" target="-2" id="AUe-I7-nOK"/>
</connections>
</button>
<button focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="0yD-Sp-Ilo">
<rect key="frame" x="19.5" y="127" width="38" height="23"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="roundTextured" bezelStyle="texturedRounded" image="Rewind" imagePosition="only" alignment="center" state="on" borderStyle="border" focusRingType="none" inset="2" id="ZIF-TP-Fqn">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="changeGBSTrack:" target="-2" id="jug-AS-bW7"/>
</connections>
</button>
<popUpButton focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="I1T-VS-Vse">
<rect key="frame" x="106" y="127" width="131" height="23"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="roundTextured" bezelStyle="texturedRounded" alignment="left" lineBreakMode="truncatingTail" borderStyle="border" focusRingType="none" imageScaling="proportionallyDown" inset="2" id="YJh-dI-A5D">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="menu"/>
<menu key="menu" id="Knp-Ok-Pb4"/>
</popUpButtonCell>
<connections>
<action selector="changeGBSTrack:" target="-2" id="HET-AT-CfQ"/>
</connections>
</popUpButton>
<segmentedControl verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="SRS-M5-VVL">
<rect key="frame" x="240.5" y="127" width="72" height="23"/>
<rect key="frame" x="244.5" y="128" width="68" height="23"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<segmentedCell key="cell" borderStyle="border" alignment="left" style="texturedRounded" trackingMode="momentary" id="cmq-I8-cFL">
<font key="font" metaFont="system"/>
<segments>
<segment toolTip="Previous Track" image="Previous" width="33"/>
<segment toolTip="Next Track" image="Next" width="32" tag="1"/>
<segment toolTip="Previous Track" image="Previous" width="31"/>
<segment toolTip="Next Track" image="Next" width="30" tag="1"/>
</segments>
</segmentedCell>
<connections>
@ -114,6 +91,29 @@
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="0yD-Sp-Ilo">
<rect key="frame" x="20.5" y="128" width="38" height="23"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="roundTextured" bezelStyle="texturedRounded" image="Rewind" imagePosition="only" alignment="center" state="on" borderStyle="border" focusRingType="none" inset="2" id="ZIF-TP-Fqn">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="changeGBSTrack:" target="-2" id="jug-AS-bW7"/>
</connections>
</button>
<popUpButton focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="I1T-VS-Vse">
<rect key="frame" x="107" y="127" width="131" height="25"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="roundTextured" bezelStyle="texturedRounded" alignment="center" lineBreakMode="truncatingTail" borderStyle="border" focusRingType="none" imageScaling="proportionallyDown" inset="2" id="YJh-dI-A5D">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="menu"/>
<menu key="menu" id="Knp-Ok-Pb4"/>
</popUpButtonCell>
<connections>
<action selector="changeGBSTrack:" target="-2" id="HET-AT-CfQ"/>
</connections>
</popUpButton>
</subviews>
<point key="canvasLocation" x="67" y="292.5"/>
</customView>

128
Cocoa/GBS11.xib Executable file
View File

@ -0,0 +1,128 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14868" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14868"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="Document">
<connections>
<outlet property="gbsAuthor" destination="gaD-ZH-Beh" id="2i7-BD-bJ2"/>
<outlet property="gbsCopyright" destination="2dl-dH-E3J" id="LnT-Vb-pN6"/>
<outlet property="gbsNextPrevButton" destination="SRS-M5-VVL" id="YEN-01-wRX"/>
<outlet property="gbsPlayPauseButton" destination="qxJ-pH-d0y" id="qk8-8I-9u5"/>
<outlet property="gbsPlayerView" destination="c22-O7-iKe" id="A1w-e5-EQE"/>
<outlet property="gbsRewindButton" destination="0yD-Sp-Ilo" id="FgR-xd-JW5"/>
<outlet property="gbsTitle" destination="H3v-X3-48q" id="DCl-wL-oy8"/>
<outlet property="gbsTracks" destination="I1T-VS-Vse" id="Vk4-GP-RjB"/>
<outlet property="gbsVisualizer" destination="Q3o-bK-DIN" id="1YC-C5-Je6"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customView id="c22-O7-iKe">
<rect key="frame" x="0.0" y="0.0" width="332" height="221"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" id="H3v-X3-48q">
<rect key="frame" x="18" y="192" width="296" height="19"/>
<autoresizingMask key="autoresizingMask" flexibleMinY="YES"/>
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" title="Title" id="BwZ-Zj-sP6">
<font key="font" metaFont="systemBold" size="16"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" id="gaD-ZH-Beh">
<rect key="frame" x="18" y="166" width="296" height="16"/>
<autoresizingMask key="autoresizingMask" flexibleMinY="YES"/>
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" title="Author" id="IgT-r1-T38">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="qxJ-pH-d0y">
<rect key="frame" x="57" y="124" width="50" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" bezelStyle="rounded" image="Play" imagePosition="only" alignment="center" alternateImage="Pause" state="on" borderStyle="border" focusRingType="none" inset="2" id="3ZK-br-UrS">
<behavior key="behavior" pushIn="YES" changeContents="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="togglePause:" target="-2" id="AUe-I7-nOK"/>
</connections>
</button>
<popUpButton focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="I1T-VS-Vse">
<rect key="frame" x="105" y="127" width="141" height="25"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="roundTextured" bezelStyle="texturedRounded" alignment="center" lineBreakMode="truncatingTail" borderStyle="border" focusRingType="none" imageScaling="proportionallyDown" inset="2" id="YJh-dI-A5D">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="menu"/>
<menu key="menu" id="Knp-Ok-Pb4"/>
</popUpButtonCell>
<connections>
<action selector="changeGBSTrack:" target="-2" id="HET-AT-CfQ"/>
</connections>
</popUpButton>
<segmentedControl verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="SRS-M5-VVL">
<rect key="frame" x="247" y="129" width="68" height="24"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<segmentedCell key="cell" borderStyle="border" alignment="left" style="rounded" trackingMode="momentary" id="cmq-I8-cFL">
<font key="font" metaFont="system"/>
<segments>
<segment toolTip="Previous Track" image="Previous" width="31"/>
<segment toolTip="Next Track" image="Next" width="30" tag="1"/>
</segments>
</segmentedCell>
<connections>
<action selector="gbsNextPrevPushed:" target="-2" id="roN-Iy-tDQ"/>
</connections>
</segmentedControl>
<box verticalHuggingPriority="750" fixedFrame="YES" boxType="separator" translatesAutoresizingMaskIntoConstraints="NO" id="b9A-cd-ias">
<rect key="frame" x="0.0" y="117" width="332" height="5"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
</box>
<customView appearanceType="darkAqua" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="tRy-Gw-IaG" customClass="GBOptionalVisualEffectView">
<rect key="frame" x="0.0" y="24" width="332" height="95"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<customView fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Q3o-bK-DIN" customClass="GBVisualizerView">
<rect key="frame" x="0.0" y="0.0" width="332" height="95"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
</customView>
</subviews>
</customView>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" id="2dl-dH-E3J">
<rect key="frame" x="18" y="5" width="296" height="14"/>
<autoresizingMask key="autoresizingMask" flexibleMinY="YES"/>
<textFieldCell key="cell" controlSize="small" lineBreakMode="clipping" alignment="center" title="Copyright" id="nM9-oF-OV9">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="0yD-Sp-Ilo">
<rect key="frame" x="13" y="124" width="50" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" bezelStyle="rounded" image="Rewind" imagePosition="only" alignment="center" state="on" borderStyle="border" focusRingType="none" inset="2" id="ZIF-TP-Fqn">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="changeGBSTrack:" target="-2" id="jug-AS-bW7"/>
</connections>
</button>
</subviews>
<point key="canvasLocation" x="67" y="292.5"/>
</customView>
</objects>
<resources>
<image name="Next" width="16" height="10"/>
<image name="Pause" width="10" height="10"/>
<image name="Play" width="10" height="10"/>
<image name="Previous" width="16" height="10"/>
<image name="Rewind" width="10" height="10"/>
</resources>
</document>

View File

@ -19,6 +19,12 @@
return [super dividerColor];
}
- (void)drawDividerInRect:(NSRect)rect
{
[self.dividerColor set];
NSRectFill(rect);
}
/* Mavericks comaptibility */
- (NSArray<NSView *> *)arrangedSubviews
{

View File

@ -1,5 +1,5 @@
#import <Cocoa/Cocoa.h>
#include <Core/gb.h>
#import <Core/gb.h>
@interface GBTerminalTextFieldCell : NSTextFieldCell
@property (nonatomic) GB_gameboy_t *gb;

View File

@ -1,7 +1,11 @@
#import <Carbon/Carbon.h>
#import "GBTerminalTextFieldCell.h"
#import "NSTextFieldCell+Inset.h"
@interface GBTerminalTextView : NSTextView
{
@public __weak NSTextField *_field;
}
@property GB_gameboy_t *gb;
@end
@ -10,7 +14,7 @@
GBTerminalTextView *field_editor;
}
- (NSTextView *)fieldEditorForView:(NSView *)controlView
- (NSTextView *)fieldEditorForView:(NSTextField *)controlView
{
if (field_editor) {
field_editor.gb = self.gb;
@ -19,6 +23,10 @@
field_editor = [[GBTerminalTextView alloc] init];
[field_editor setFieldEditor:true];
field_editor.gb = self.gb;
field_editor->_field = (NSTextField *)controlView;
((NSTextFieldCell *)controlView.cell).textInset =
field_editor.textContainerInset =
NSMakeSize(0, 2);
return field_editor;
}
@ -185,14 +193,21 @@
return [super resignFirstResponder];
}
-(void)drawRect:(NSRect)dirtyRect
- (NSColor *)backgroundColor
{
return nil;
}
- (void)drawRect:(NSRect)dirtyRect
{
[super drawRect:dirtyRect];
if (reverse_search_mode && [super string].length == 0) {
NSMutableDictionary *attributes = [self.typingAttributes mutableCopy];
NSColor *color = [attributes[NSForegroundColorAttributeName] colorWithAlphaComponent:0.5];
[attributes setObject:color forKey:NSForegroundColorAttributeName];
[[[NSAttributedString alloc] initWithString:@"Reverse search..." attributes:attributes] drawAtPoint:NSMakePoint(2, 0)];
[[[NSAttributedString alloc] initWithString:@"Reverse search..." attributes:attributes] drawAtPoint:NSMakePoint(2, 2)];
}
else {
[super drawRect:dirtyRect];
}
}

View File

@ -0,0 +1,5 @@
#import <Cocoa/Cocoa.h>
@interface GBTintedImageCell : NSImageCell
@property NSColor *tint;
@end

20
Cocoa/GBTintedImageCell.m Normal file
View File

@ -0,0 +1,20 @@
#import "GBTintedImageCell.h"
@implementation GBTintedImageCell
- (NSImage *)image
{
if (!self.tint || !super.image.isTemplate) {
return [super image];
}
NSImage *tinted = [super.image copy];
[tinted lockFocus];
[self.tint set];
NSRectFillUsingOperation((NSRect){.size = tinted.size}, NSCompositeSourceIn);
[tinted unlockFocus];
tinted.template = false;
return tinted;
}
@end

View File

@ -1,31 +1,16 @@
#import <Cocoa/Cocoa.h>
#include <Core/gb.h>
#import <JoyKit/JoyKit.h>
#import "GBOSDView.h"
#import "GBViewBase.h"
@class Document;
typedef enum {
GB_FRAME_BLENDING_MODE_DISABLED,
GB_FRAME_BLENDING_MODE_SIMPLE,
GB_FRAME_BLENDING_MODE_ACCURATE,
GB_FRAME_BLENDING_MODE_ACCURATE_EVEN = GB_FRAME_BLENDING_MODE_ACCURATE,
GB_FRAME_BLENDING_MODE_ACCURATE_ODD,
} GB_frame_blending_mode_t;
@interface GBView : NSView<JOYListener>
- (void) flip;
- (uint32_t *) pixels;
@interface GBView : GBViewBase<JOYListener>
@property (nonatomic, weak) IBOutlet Document *document;
@property (nonatomic) GB_gameboy_t *gb;
@property (nonatomic) GB_frame_blending_mode_t frameBlendingMode;
@property (nonatomic, getter=isMouseHidingEnabled) bool mouseHidingEnabled;
@property (nonatomic) bool isRewinding;
@property (nonatomic, strong) NSView *internalView;
@property (weak) GBOSDView *osdView;
- (void) createInternalView;
- (uint32_t *)currentBuffer;
- (uint32_t *)previousBuffer;
- (void)screenSizeChanged;
- (void)setRumble: (double)amp;
- (NSImage *)renderToImage;
- (void)setRumble: (double)amp;
@end

View File

@ -5,6 +5,7 @@
#import "GBViewMetal.h"
#import "GBButtons.h"
#import "NSString+StringForKey.h"
#import "NSObject+DefaultsObserver.h"
#import "Document.h"
#define JOYSTICK_HIGH 0x4000
@ -104,8 +105,6 @@ static const uint8_t workboy_vk_to_key[] = {
@implementation GBView
{
uint32_t *image_buffers[3];
unsigned char current_buffer;
bool mouse_hidden;
NSTrackingArea *tracking_area;
bool _mouseHidingEnabled;
@ -116,9 +115,10 @@ static const uint8_t workboy_vk_to_key[] = {
bool analogClockMultiplierValid;
NSEventModifierFlags previousModifiers;
JOYController *lastController;
GB_frame_blending_mode_t _frameBlendingMode;
bool _turbo;
bool _mouseControlEnabled;
NSMutableDictionary<NSNumber *, JOYController *> *_controllerMapping;
unsigned _lastPlayerCount;
}
+ (instancetype)alloc
@ -137,16 +137,17 @@ static const uint8_t workboy_vk_to_key[] = {
return [super allocWithZone:zone];
}
- (void) createInternalView
{
assert(false && "createInternalView must not be inherited");
}
- (void) _init
{
[self registerForDraggedTypes:[NSArray arrayWithObjects: NSFilenamesPboardType, nil]];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(ratioKeepingChanged) name:@"GBAspectChanged" object:nil];
__unsafe_unretained GBView *weakSelf = self;
[self observeStandardDefaultsKey:@"GBAspectRatioUnkept" withBlock:^(id newValue) {
[weakSelf setFrame:weakSelf.superview.frame];
}];
[self observeStandardDefaultsKey:@"JoyKitDefaultControllers" withBlock:^(id newValue) {
[weakSelf reassignControllers];
}];
tracking_area = [ [NSTrackingArea alloc] initWithRect:(NSRect){}
options:NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways | NSTrackingInVisibleRect | NSTrackingMouseMoved
owner:self
@ -158,57 +159,97 @@ static const uint8_t workboy_vk_to_key[] = {
self.internalView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
[JOYController registerListener:self];
_mouseControlEnabled = true;
[self reassignControllers];
}
- (void)controllerConnected:(JOYController *)controller
{
[self reassignControllers];
}
- (void)controllerDisconnected:(JOYController *)controller
{
[self reassignControllers];
}
- (unsigned)playerCount
{
if (self.document.partner) {
return 2;
}
if (!_gb) {
return 1;
}
return GB_get_player_count(_gb);
}
- (void)reassignControllers
{
unsigned playerCount = self.playerCount;
/* Don't assign controlelrs if there's only one player, allow all controllers. */
if (playerCount == 1) {
_controllerMapping = [NSMutableDictionary dictionary];
return;
}
if (!_controllerMapping) {
_controllerMapping = [NSMutableDictionary dictionary];
}
for (NSNumber *player in [_controllerMapping copy]) {
if (player.unsignedIntValue >= playerCount || !_controllerMapping[player].connected) {
[_controllerMapping removeObjectForKey:player];
}
}
_lastPlayerCount = playerCount;
for (unsigned i = 0; i < playerCount; i++) {
NSString *preferredJoypad = [[[NSUserDefaults standardUserDefaults] dictionaryForKey:@"JoyKitDefaultControllers"]
objectForKey:n2s(i)];
for (JOYController *controller in [JOYController allControllers]) {
if (!controller.connected) continue;
if ([controller.uniqueID isEqual:preferredJoypad]) {
_controllerMapping[@(i)] = controller;
break;
}
}
}
}
- (void)tryAssigningController:(JOYController *)controller
{
unsigned playerCount = self.playerCount;
if (playerCount == 1) return;
if (_controllerMapping.count == playerCount) return;
if ([_controllerMapping.allValues containsObject:controller]) return;
for (unsigned i = 0; i < playerCount; i++) {
if (!_controllerMapping[@(i)]) {
_controllerMapping[@(i)] = controller;
return;
}
}
}
- (NSDictionary<NSNumber *, JOYController *> *)controllerMapping
{
if (_lastPlayerCount != self.playerCount) {
[self reassignControllers];
}
return _controllerMapping;
}
- (void)screenSizeChanged
{
if (image_buffers[0]) free(image_buffers[0]);
if (image_buffers[1]) free(image_buffers[1]);
if (image_buffers[2]) free(image_buffers[2]);
size_t buffer_size = sizeof(image_buffers[0][0]) * GB_get_screen_width(_gb) * GB_get_screen_height(_gb);
image_buffers[0] = calloc(1, buffer_size);
image_buffers[1] = calloc(1, buffer_size);
image_buffers[2] = calloc(1, buffer_size);
[super screenSizeChanged];
dispatch_async(dispatch_get_main_queue(), ^{
[self setFrame:self.superview.frame];
});
}
- (void) ratioKeepingChanged
{
[self setFrame:self.superview.frame];
}
- (void) setFrameBlendingMode:(GB_frame_blending_mode_t)frameBlendingMode
{
_frameBlendingMode = frameBlendingMode;
[self setNeedsDisplay:true];
}
- (GB_frame_blending_mode_t)frameBlendingMode
{
if (_frameBlendingMode == GB_FRAME_BLENDING_MODE_ACCURATE) {
if (!_gb || GB_is_sgb(_gb)) {
return GB_FRAME_BLENDING_MODE_SIMPLE;
}
return GB_is_odd_frame(_gb)? GB_FRAME_BLENDING_MODE_ACCURATE_ODD : GB_FRAME_BLENDING_MODE_ACCURATE_EVEN;
}
return _frameBlendingMode;
}
- (unsigned char) numberOfBuffers
{
return _frameBlendingMode? 3 : 2;
}
- (void)dealloc
{
free(image_buffers[0]);
free(image_buffers[1]);
free(image_buffers[2]);
if (mouse_hidden) {
mouse_hidden = false;
[NSCursor unhide];
@ -217,6 +258,7 @@ static const uint8_t workboy_vk_to_key[] = {
[self setRumble:0];
[JOYController unregisterListener:self];
}
- (instancetype)initWithCoder:(NSCoder *)coder
{
if (!(self = [super initWithCoder:coder])) {
@ -295,18 +337,13 @@ static const uint8_t workboy_vk_to_key[] = {
}
if ((!analogClockMultiplierValid && clockMultiplier > 1) ||
_turbo || (analogClockMultiplierValid && analogClockMultiplier > 1)) {
[self.osdView displayText:@"Fast forwarding..."];
[self.osdView displayText:@"Fast forwarding"];
}
else if ((!analogClockMultiplierValid && clockMultiplier < 1) ||
(analogClockMultiplierValid && analogClockMultiplier < 1)) {
[self.osdView displayText:@"Slow motion..."];
[self.osdView displayText:@"Slow motion"];
}
current_buffer = (current_buffer + 1) % self.numberOfBuffers;
}
- (uint32_t *) pixels
{
return image_buffers[(current_buffer + 1) % self.numberOfBuffers];
[super flip];
}
-(void)keyDown:(NSEvent *)theEvent
@ -474,10 +511,21 @@ static const uint8_t workboy_vk_to_key[] = {
return true;
}
- (bool)allowController
{
if ([self.window isMainWindow]) return true;
if ([[NSUserDefaults standardUserDefaults] boolForKey:@"GBAllowBackgroundControllers"]) {
if ([(Document *)[NSApplication sharedApplication].orderedDocuments.firstObject mainWindow] == self.window) {
return true;
}
}
return false;
}
- (void)controller:(JOYController *)controller movedAxis:(JOYAxis *)axis
{
if (!_gb) return;
if (![self.window isMainWindow]) return;
if (![self allowController]) return;
NSDictionary *mapping = [[NSUserDefaults standardUserDefaults] dictionaryForKey:@"JoyKitInstanceMapping"][controller.uniqueID];
if (!mapping) {
@ -512,6 +560,28 @@ static const uint8_t workboy_vk_to_key[] = {
if (!_gb) return;
if ([[NSUserDefaults standardUserDefaults] boolForKey:@"GBMBC7JoystickOverride"]) return;
if (self.mouseControlsActive) return;
if (controller != lastController) return;
// When using Joy-Cons in dual-controller grip, ignore motion data from the left Joy-Con
if (controller.joyconType == JOYJoyConTypeDual) {
for (JOYController *child in [(JOYCombinedController *)controller children]) {
if (child.joyconType != JOYJoyConTypeRight && [child.axes3D containsObject:axes]) {
return;
}
}
}
NSDictionary<NSNumber *, JOYController *> *controllerMapping = [self controllerMapping];
GB_gameboy_t *effectiveGB = _gb;
if (self.document.partner) {
if (controllerMapping[@1] == controller) {
effectiveGB = self.document.partner.gb;
}
if (controllerMapping[@0] != controller) {
return;
}
}
if (axes.usage == JOYAxes3DUsageOrientation) {
for (JOYAxes3D *axes in controller.axes3D) {
@ -521,37 +591,35 @@ static const uint8_t workboy_vk_to_key[] = {
}
}
JOYPoint3D point = axes.normalizedValue;
GB_set_accelerometer_values(_gb, point.x, point.z);
GB_set_accelerometer_values(effectiveGB, point.x, point.z);
}
else if (axes.usage == JOYAxes3DUsageAcceleration) {
JOYPoint3D point = axes.gUnitsValue;
GB_set_accelerometer_values(_gb, point.x, point.z);
GB_set_accelerometer_values(effectiveGB, point.x, point.z);
}
}
- (void)controller:(JOYController *)controller buttonChangedState:(JOYButton *)button
{
if (!_gb) return;
if (![self.window isMainWindow]) return;
if (![self allowController]) return;
_mouseControlEnabled = false;
if (button.type == JOYButtonTypeAxes2DEmulated && [self shouldControllerUseJoystickForMotion:controller]) return;
unsigned player_count = GB_get_player_count(_gb);
if (self.document.partner) {
player_count = 2;
}
[self tryAssigningController:controller];
unsigned playerCount = self.playerCount;
IOPMAssertionID assertionID;
IOPMAssertionDeclareUserActivity(CFSTR(""), kIOPMUserActiveLocal, &assertionID);
for (unsigned player = 0; player < player_count; player++) {
NSString *preferred_joypad = [[[NSUserDefaults standardUserDefaults] dictionaryForKey:@"JoyKitDefaultControllers"]
objectForKey:n2s(player)];
if (player_count != 1 && // Single player, accpet inputs from all joypads
!(player == 0 && !preferred_joypad) && // Multiplayer, but player 1 has no joypad configured, so it takes inputs from all joypads
![preferred_joypad isEqualToString:controller.uniqueID]) {
continue;
}
NSDictionary<NSNumber *, JOYController *> *controllerMapping = [self controllerMapping];
for (unsigned player = 0; player < playerCount; player++) {
JOYController *preferredJoypad = controllerMapping[@(player)];
if (preferredJoypad && preferredJoypad != controller) continue; // The player has a different assigned controller
if (!preferredJoypad && playerCount != 1) continue; // The player has no assigned controller in multiplayer mode, prevent controller inputs
dispatch_async(dispatch_get_main_queue(), ^{
[controller setPlayerLEDs:[controller LEDMaskForPlayer:player]];
});
@ -562,7 +630,7 @@ static const uint8_t workboy_vk_to_key[] = {
JOYButtonUsage usage = ((JOYButtonUsage)[mapping[n2s(button.uniqueID)] unsignedIntValue]) ?: button.usage;
if (!mapping && usage >= JOYButtonUsageGeneric0) {
usage = (const JOYButtonUsage[]){JOYButtonUsageY, JOYButtonUsageA, JOYButtonUsageB, JOYButtonUsageX}[(usage - JOYButtonUsageGeneric0) & 3];
usage = GB_inline_const(JOYButtonUsage[], {JOYButtonUsageY, JOYButtonUsageA, JOYButtonUsageB, JOYButtonUsageX})[(usage - JOYButtonUsageGeneric0) & 3];
}
GB_gameboy_t *effectiveGB = _gb;
@ -740,16 +808,6 @@ static const uint8_t workboy_vk_to_key[] = {
previousModifiers = event.modifierFlags;
}
- (uint32_t *)currentBuffer
{
return image_buffers[current_buffer];
}
- (uint32_t *)previousBuffer
{
return image_buffers[(current_buffer + 2) % self.numberOfBuffers];
}
-(NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender
{
NSPasteboard *pboard = [sender draggingPasteboard];

View File

@ -1,7 +0,0 @@
#import <Cocoa/Cocoa.h>
#import <MetalKit/MetalKit.h>
#import "GBView.h"
@interface GBViewMetal : GBView<MTKViewDelegate>
+ (bool) isSupported;
@end

View File

@ -1,5 +1,5 @@
#import <Cocoa/Cocoa.h>
#include <Core/gb.h>
#import <Core/gb.h>
@interface GBVisualizerView : NSView
- (void)addSample:(GB_sample_t *)sample;

View File

@ -1,6 +1,6 @@
#import "GBVisualizerView.h"
#import "GBPaletteEditorController.h"
#include <Core/gb.h>
#import <Core/gb.h>
#define SAMPLE_COUNT 1024

View File

@ -4,5 +4,6 @@
+ (GBWarningPopover *) popoverWithContents:(NSString *)contents onView:(NSView *)view;
+ (GBWarningPopover *) popoverWithContents:(NSString *)contents onWindow:(NSWindow *)window;
+ (GBWarningPopover *) popoverWithContents:(NSString *)contents title:(NSString *)title onView:(NSView *)view timeout:(double)seconds preferredEdge:(NSRectEdge)preferredEdge;
@end

View File

@ -4,7 +4,7 @@ static GBWarningPopover *lastPopover;
@implementation GBWarningPopover
+ (GBWarningPopover *) popoverWithContents:(NSString *)contents onView:(NSView *)view
+ (GBWarningPopover *) popoverWithContents:(NSString *)contents title:(NSString *)title onView:(NSView *)view timeout:(double)seconds preferredEdge:(NSRectEdge)preferredEdge
{
[lastPopover close];
lastPopover = [[self alloc] init];
@ -13,7 +13,17 @@ static GBWarningPopover *lastPopover;
[lastPopover setAnimates:true];
lastPopover.contentViewController = [[NSViewController alloc] initWithNibName:@"PopoverView" bundle:nil];
NSTextField *field = (NSTextField *)lastPopover.contentViewController.view;
[field setStringValue:contents];
if (!title) {
[field setStringValue:contents];
}
else {
NSMutableAttributedString *fullContents = [[NSMutableAttributedString alloc] initWithString:title
attributes:@{NSFontAttributeName: [NSFont boldSystemFontOfSize:[NSFont systemFontSize]]}];
[fullContents appendAttributedString:[[NSAttributedString alloc] initWithString:[@"\n" stringByAppendingString:contents]
attributes:@{NSFontAttributeName: [NSFont systemFontOfSize:[NSFont systemFontSize]]}]];
[field setAttributedStringValue:fullContents];
}
NSSize textSize = [field.cell cellSizeForBounds:[field.cell drawingRectForBounds:NSMakeRect(0, 0, 240, CGFLOAT_MAX)]];
textSize.width = ceil(textSize.width) + 16;
textSize.height = ceil(textSize.height) + 12;
@ -25,11 +35,13 @@ static GBWarningPopover *lastPopover;
[lastPopover showRelativeToRect:view.bounds
ofView:view
preferredEdge:NSMinYEdge];
preferredEdge:preferredEdge];
NSRect frame = field.frame;
frame.origin.x += 8;
frame.origin.y -= 6;
frame.origin.y += 6;
frame.size.width -= 16;
frame.size.height -= 12;
field.frame = frame;
@ -38,6 +50,11 @@ static GBWarningPopover *lastPopover;
return lastPopover;
}
+ (GBWarningPopover *) popoverWithContents:(NSString *)contents onView:(NSView *)view
{
return [self popoverWithContents:contents title:nil onView:view timeout:3.0 preferredEdge:NSMinYEdge];
}
+ (GBWarningPopover *)popoverWithContents:(NSString *)contents onWindow:(NSWindow *)window
{
return [self popoverWithContents:contents onView:window.contentView.superview.subviews.lastObject];

BIN
Cocoa/HelpTemplate.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 B

Some files were not shown because too many files have changed in this diff Show More