Compare commits
1 Commits
master
...
fps-counte
Author | SHA1 | Date | |
---|---|---|---|
2c58e669ef |
5
.gitattributes
vendored
@ -1,9 +1,4 @@
|
|||||||
# Always use LF line endings for shaders
|
|
||||||
*.fsh text eol=lf
|
|
||||||
*.metal text eol=lf
|
|
||||||
|
|
||||||
HexFiend/* linguist-vendored
|
HexFiend/* linguist-vendored
|
||||||
*.inc linguist-language=C
|
|
||||||
Core/*.h linguist-language=C
|
Core/*.h linguist-language=C
|
||||||
SDL/*.h linguist-language=C
|
SDL/*.h linguist-language=C
|
||||||
Windows/*.h linguist-language=C
|
Windows/*.h linguist-language=C
|
||||||
|
25
.github/actions/LICENSE
vendored
@ -1,25 +0,0 @@
|
|||||||
Blargg's Test ROMs by Shay Green <gblargg@gmail.com>
|
|
||||||
|
|
||||||
Acid2 tests by Matt Currie under MIT:
|
|
||||||
|
|
||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2020 Matt Currie
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
BIN
.github/actions/cgb-acid2.gbc
vendored
BIN
.github/actions/cgb_sound.gb
vendored
BIN
.github/actions/dmg-acid2.gb
vendored
BIN
.github/actions/dmg_sound-2.gb
vendored
23
.github/actions/install_deps.sh
vendored
@ -1,23 +0,0 @@
|
|||||||
case `echo $1 | cut -d '-' -f 1` in
|
|
||||||
ubuntu)
|
|
||||||
sudo apt-get -qq update
|
|
||||||
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
|
|
||||||
unzip rgbds.zip
|
|
||||||
cd rgbds-*
|
|
||||||
make -sj
|
|
||||||
sudo make install
|
|
||||||
cd ..
|
|
||||||
rm -rf *
|
|
||||||
)
|
|
||||||
;;
|
|
||||||
macos)
|
|
||||||
brew install rgbds sdl2
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo "Unsupported OS"
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
|
BIN
.github/actions/oam_bug-2.gb
vendored
33
.github/actions/sanity_tests.sh
vendored
@ -1,33 +0,0 @@
|
|||||||
set -e
|
|
||||||
|
|
||||||
./build/bin/tester/sameboy_tester --jobs 5 \
|
|
||||||
--length 45 .github/actions/cgb_sound.gb \
|
|
||||||
--length 10 .github/actions/cgb-acid2.gbc \
|
|
||||||
--length 10 .github/actions/dmg-acid2.gb \
|
|
||||||
--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
|
|
||||||
|
|
||||||
./build/bin/tester/sameboy_tester \
|
|
||||||
--dmg --length 10 .github/actions/dmg-acid2.gb
|
|
||||||
|
|
||||||
set +e
|
|
||||||
|
|
||||||
FAILED_TESTS=`
|
|
||||||
shasum .github/actions/*.bmp | grep -E -v \(\
|
|
||||||
5283564df0cf5bb78a7a90aff026c1a4692fd39e\ \ .github/actions/cgb-acid2.bmp\|\
|
|
||||||
dbcc438dcea13b5d1b80c5cd06bda2592cc5d9e0\ \ .github/actions/cgb_sound.bmp\|\
|
|
||||||
0caadf9634e40247ae9c15ff71992e8f77bbf89e\ \ .github/actions/dmg-acid2.bmp\|\
|
|
||||||
a732077f98f43d9231453b1764d9f797a836924d\ \ .github/actions/dmg-mode-acid2.bmp\|\
|
|
||||||
c9e944b7e01078bdeba1819bc2fa9372b111f52d\ \ .github/actions/dmg_sound-2.bmp\|\
|
|
||||||
f0172cc91867d3343fbd113a2bb98100074be0de\ \ .github/actions/oam_bug-2.bmp\
|
|
||||||
\)`
|
|
||||||
|
|
||||||
if [ -n "$FAILED_TESTS" ] ; then
|
|
||||||
echo "Failed the following tests:"
|
|
||||||
echo $FAILED_TESTS | tr " " "\n" | grep -o -E "[^/]+\.bmp" | sed s/.bmp// | sort
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo Passed all tests
|
|
36
.github/workflows/sanity.yml
vendored
@ -1,36 +0,0 @@
|
|||||||
name: "Bulidability and Sanity"
|
|
||||||
on: push
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
sanity:
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
os: [macos-latest, ubuntu-latest, ubuntu-18.04]
|
|
||||||
cc: [gcc, clang]
|
|
||||||
include:
|
|
||||||
- os: macos-latest
|
|
||||||
cc: clang
|
|
||||||
extra_target: cocoa
|
|
||||||
exclude:
|
|
||||||
- os: macos-latest
|
|
||||||
cc: gcc
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
- 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 }}))
|
|
||||||
- name: Sanity tests
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
./.github/actions/sanity_tests.sh
|
|
||||||
- name: Upload binaries
|
|
||||||
uses: actions/upload-artifact@v1
|
|
||||||
with:
|
|
||||||
name: sameboy-canary-${{ matrix.os }}-${{ matrix.cc }}
|
|
||||||
path: build/bin
|
|
217
BESS.md
@ -1,217 +0,0 @@
|
|||||||
# BESS – Best Effort Save State 1.0
|
|
||||||
|
|
||||||
## Motivation
|
|
||||||
|
|
||||||
BESS is a save state format specification designed to allow different emulators, as well as majorly different versions of the same emulator, to import save states from other BESS-compliant save states. BESS works by appending additional, implementation-agnostic information about the emulation state. This allows a single save state file to be read as both a fully-featured, implementation specific save state which includes detailed timing information; and as a portable "best effort" save state that represents a state accurately enough to be restored in casual use-cases.
|
|
||||||
|
|
||||||
## Specification
|
|
||||||
|
|
||||||
Every integer used in the BESS specification is stored in Little Endian encoding.
|
|
||||||
|
|
||||||
### BESS footer
|
|
||||||
|
|
||||||
BESS works by appending a detectable footer at the end of an existing save state format. The footer uses the following format:
|
|
||||||
|
|
||||||
| Offset from end of file | Content |
|
|
||||||
|-------------------------|-------------------------------------------------------|
|
|
||||||
| -8 | Offset to the first BESS Block, from the file's start |
|
|
||||||
| -4 | The ASCII string 'BESS' |
|
|
||||||
|
|
||||||
### BESS blocks
|
|
||||||
|
|
||||||
BESS uses a block format where each block contains the following header:
|
|
||||||
|
|
||||||
| Offset | Content |
|
|
||||||
|--------|---------------------------------------|
|
|
||||||
| 0 | A four-letter ASCII identifier |
|
|
||||||
| 4 | Length of the block, excluding header |
|
|
||||||
|
|
||||||
Every block is followed by another block, until the END block is reached. If an implementation encounters an unsupported block, it should be completely ignored (Should not have any effect and should not trigger a failure).
|
|
||||||
|
|
||||||
#### NAME block
|
|
||||||
|
|
||||||
The NAME block uses the `'NAME'` identifier, and is an optional block that contains the name of the emulator that created this save state. While optional, it is highly recommended to be included in every implementation – it allows the user to know which emulator and version is compatible with the native save state format contained in this file. When used, this block should come first.
|
|
||||||
|
|
||||||
The length of the NAME block is variable, and it only contains the name and version of the originating emulator in ASCII.
|
|
||||||
|
|
||||||
|
|
||||||
#### INFO block
|
|
||||||
|
|
||||||
The INFO block uses the `'INFO'` identifier, and is an optional block that contains information about the ROM this save state originates from. When used, this block should come before `CORE` but after `NAME`. This block is 0x12 bytes long, and it follows this structure:
|
|
||||||
|
|
||||||
| Offset | Content |
|
|
||||||
|--------|--------------------------------------------------|
|
|
||||||
| 0x00 | Bytes 0x134-0x143 from the ROM (Title) |
|
|
||||||
| 0x10 | Bytes 0x14E-0x14F from the ROM (Global checksum) |
|
|
||||||
|
|
||||||
#### CORE block
|
|
||||||
|
|
||||||
The CORE block uses the `'CORE'` identifier, and is a required block that contains both core state information, as well as basic information about the BESS version used. This block must be the first block, unless the `NAME` or `INFO` blocks exist then it must come directly after them. An implementation should not enforce block order on blocks unknown to it for future compatibility.
|
|
||||||
|
|
||||||
The length of the CORE block is 0xD0 bytes, but implementations are expected to ignore any excess bytes. Following the BESS block header, the structure is as follows:
|
|
||||||
|
|
||||||
| Offset | Content |
|
|
||||||
|--------|----------------------------------------|
|
|
||||||
| 0x00 | Major BESS version as a 16-bit integer |
|
|
||||||
| 0x02 | Minor BESS version as a 16-bit integer |
|
|
||||||
|
|
||||||
Both major and minor versions should be 1. Implementations are expected to reject incompatible majors, but still attempt to read newer minor versions.
|
|
||||||
|
|
||||||
| Offset | Content |
|
|
||||||
|--------|----------------------------------------|
|
|
||||||
| 0x04 | A four-character ASCII model identifier |
|
|
||||||
|
|
||||||
BESS uses a four-character string to identify Game Boy models:
|
|
||||||
|
|
||||||
* The first letter represents mutually-incompatible families of models and is required. The allowed values are `'G'` for the original Game Boy family, `'S'` for the Super Game Boy family, and `'C'` for the Game Boy Color and Advance family.
|
|
||||||
* The second letter represents a specific model within the family, and is optional (If an implementation does not distinguish between specific models in a family, a space character may be used). The allowed values for family G are `'D'` for DMG and `'M'` for MGB; the allowed values for family S are `'N'` for NTSC, `'P'` for PAL, and `'2'` for SGB2; and the allowed values for family C are `'C'` for CGB, and `'A'` for the various GBA line models.
|
|
||||||
* The third letter represents a specific CPU revision within a model, and is optional (If an implementation does not distinguish between revisions, a space character may be used). The allowed values for model GD (DMG) are `'0'` and `'A'`, through `'C'`; the allowed values for model CC (CGB) are `'0'` and `'A'`, through `'E'`; the allowed values for model CA (AGB, AGS, GBP) are `'0'`, `'A'` and `'B'`; and for every other model this value must be a space character.
|
|
||||||
* The last character is used for padding and must be a space character.
|
|
||||||
|
|
||||||
For example; `'GD '` represents a DMG of an unspecified revision, `'S '` represents some model of the SGB family, and `'CCE '` represent a CGB using CPU revision E.
|
|
||||||
|
|
||||||
| Offset | Content |
|
|
||||||
|--------|--------------------------------------------------------|
|
|
||||||
| 0x08 | The value of the PC register |
|
|
||||||
| 0x0A | The value of the AF register |
|
|
||||||
| 0x0C | The value of the BC register |
|
|
||||||
| 0x0E | The value of the DE register |
|
|
||||||
| 0x10 | The value of the HL register |
|
|
||||||
| 0x12 | The value of the SP register |
|
|
||||||
| 0x14 | The value of IME (0 or 1) |
|
|
||||||
| 0x15 | The value of the IE register |
|
|
||||||
| 0x16 | Execution state (0 = running; 1 = halted; 2 = stopped) |
|
|
||||||
| 0x17 | Reserved, must be 0 |
|
|
||||||
| 0x18 | The values of every memory-mapped register (128 bytes) |
|
|
||||||
|
|
||||||
The values of memory-mapped registers should be written 'as-is' to memory as if the actual ROM wrote them, with the following exceptions and note:
|
|
||||||
* Unused registers have Don't-Care values which should be ignored
|
|
||||||
* Unused register bits have Don't-Care values which should be ignored
|
|
||||||
* If the model is CGB or newer, the value of KEY0 (FF4C) must be valid as it determines DMG mode
|
|
||||||
* Bit 2 determines DMG mode. A value of 0x04 usually denotes DMG mode, while a value of `0x80` usually denotes CGB mode.
|
|
||||||
* Object priority is derived from KEY0 (FF4C) instead of OPRI (FF6C) because OPRI can be modified after booting, but only the value of OPRI during boot ROM execution takes effect
|
|
||||||
* If a register doesn't exist on the emulated model (For example, KEY0 (FF4C) on a DMG), its value should be ignored.
|
|
||||||
* BANK (FF50) should be 0 if the boot ROM is still mapped, and 1 otherwise, and must be valid.
|
|
||||||
* Implementations should not start a serial transfer when writing the value of SB
|
|
||||||
* Similarly, no value of NRx4 should trigger a sound pulse on save state load
|
|
||||||
* And similarly again, implementations should not trigger DMA transfers when writing the values of DMA or HDMA5
|
|
||||||
* The value store for DIV will be used to set the internal divisor to `DIV << 8`
|
|
||||||
* Implementation should apply care when ordering the write operations (For example, writes to NR52 must come before writes to the other APU registers)
|
|
||||||
|
|
||||||
| Offset | Content |
|
|
||||||
|--------|--------------------------------------------------------------------|
|
|
||||||
| 0x98 | The size of RAM (32-bit integer) |
|
|
||||||
| 0x9C | The offset of RAM from file start (32-bit integer) |
|
|
||||||
| 0xA0 | The size of VRAM (32-bit integer) |
|
|
||||||
| 0xA4 | The offset of VRAM from file start (32-bit integer) |
|
|
||||||
| 0xA8 | The size of MBC RAM (32-bit integer) |
|
|
||||||
| 0xAC | The offset of MBC RAM from file start (32-bit integer) |
|
|
||||||
| 0xB0 | The size of OAM (=0xA0, 32-bit integer) |
|
|
||||||
| 0xB4 | The offset of OAM from file start (32-bit integer) |
|
|
||||||
| 0xB8 | The size of HRAM (=0x7F, 32-bit integer) |
|
|
||||||
| 0xBC | The offset of HRAM from file start (32-bit integer) |
|
|
||||||
| 0xC0 | The size of background palettes (=0x40 or 0, 32-bit integer) |
|
|
||||||
| 0xC4 | The offset of background palettes from file start (32-bit integer) |
|
|
||||||
| 0xC8 | The size of object palettes (=0x40 or 0, 32-bit integer) |
|
|
||||||
| 0xCC | The offset of object palettes from file start (32-bit integer) |
|
|
||||||
|
|
||||||
The contents of large buffers are stored outside of BESS structure so data from an implementation's native save state format can be reused. The offsets are absolute offsets from the save state file's start. Background and object palette sizes must be 0 for models prior to Game Boy Color.
|
|
||||||
|
|
||||||
An implementation needs handle size mismatches gracefully. For example, if too large MBC RAM size is specified, the superfluous data should be ignored. On the other hand, if a too small VRAM size is specified (For example, if it's a save state from an emulator emulating a CGB in DMG mode, and it didn't save the second CGB VRAM bank), the implementation is expected to set that extra bank to all zeros.
|
|
||||||
|
|
||||||
#### XOAM block
|
|
||||||
|
|
||||||
The XOAM block uses the `'XOAM'` identifier, and is an optional block that contains the data of extra OAM (addresses `0xFEA0-0xFEFF`). This block length must be `0x60`. Implementations that do not emulate this extra range are free to ignore the excess bytes, and to not create this block.
|
|
||||||
|
|
||||||
|
|
||||||
#### MBC block
|
|
||||||
|
|
||||||
The MBC block uses the `'MBC '` identifier, and is an optional block that is only used when saving states of ROMs that use an MBC. The length of this block is variable and must be divisible by 3.
|
|
||||||
|
|
||||||
This block contains an MBC-specific number of 3-byte-long pairs that represent the values of each MBC register. For example, for MBC5 the contents would look like:
|
|
||||||
|
|
||||||
| Offset | Content |
|
|
||||||
|--------|---------------------------------------|
|
|
||||||
| 0x0 | The value 0x0000 as a 16-bit integer |
|
|
||||||
| 0x2 | 0x0A if RAM is enabled, 0 otherwise |
|
|
||||||
| 0x3 | The value 0x2000 as a 16-bit integer |
|
|
||||||
| 0x5 | The lower 8 bits of the ROM bank |
|
|
||||||
| 0x6 | The value 0x3000 as a 16-bit integer |
|
|
||||||
| 0x8 | The bit 9 of the ROM bank |
|
|
||||||
| 0x9 | The value 0x4000 as a 16-bit integer |
|
|
||||||
| 0xB | The current RAM bank |
|
|
||||||
|
|
||||||
An implementation should parse this block as a series of writes to be made. Values outside the `0x0000-0x7FFF` and `0xA000-0xBFFF` ranges are not allowed. Implementations must perform the writes in order (i.e. not reverse, sorted, or any other transformation on their order)
|
|
||||||
|
|
||||||
#### RTC block
|
|
||||||
The RTC block uses the `'RTC '` identifier, and is an optional block that is used while emulating an MBC3 with an RTC. The contents of this block are identical to 64-bit RTC saves from VBA, which are also used by SameBoy and different emulators such as BGB.
|
|
||||||
|
|
||||||
The length of this block is 0x30 bytes long and it follows the following structure:
|
|
||||||
|
|
||||||
| Offset | Content |
|
|
||||||
|--------|------------------------------------------------------------------------|
|
|
||||||
| 0x00 | Current seconds (1 byte), followed by 3 bytes of padding |
|
|
||||||
| 0x04 | Current minutes (1 byte), followed by 3 bytes of padding |
|
|
||||||
| 0x08 | Current hours (1 byte), followed by 3 bytes of padding |
|
|
||||||
| 0x0C | Current days (1 byte), followed by 3 bytes of padding |
|
|
||||||
| 0x10 | Current high/overflow/running (1 byte), followed by 3 bytes of padding |
|
|
||||||
| 0x14 | Latched seconds (1 byte), followed by 3 bytes of padding |
|
|
||||||
| 0x18 | Latched minutes (1 byte), followed by 3 bytes of padding |
|
|
||||||
| 0x1C | Latched hours (1 byte), followed by 3 bytes of padding |
|
|
||||||
| 0x20 | Latched days (1 byte), followed by 3 bytes of padding |
|
|
||||||
| 0x24 | Latched high/overflow/running (1 byte), followed by 3 bytes of padding |
|
|
||||||
| 0x28 | UNIX timestamp at the time of the save state (64-bit) |
|
|
||||||
|
|
||||||
#### HUC3 block
|
|
||||||
The HUC3 block uses the `'HUC3'` identifier, and is an optional block that is used while emulating an HuC3 cartridge to store RTC and alarm information. The contents of this block are identical to HuC3 RTC saves from SameBoy.
|
|
||||||
|
|
||||||
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 | RTC minutes (16-bit) |
|
|
||||||
| 0x0A | RTC days (16-bit) |
|
|
||||||
| 0x0C | Scheduled alarm time minutes (16-bit) |
|
|
||||||
| 0x0E | Scheduled alarm time days (16-bit) |
|
|
||||||
| 0x10 | Alarm enabled flag (8-bits, either 0 or 1) |
|
|
||||||
|
|
||||||
#### 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.
|
|
||||||
|
|
||||||
The length of this block is 0x39 bytes, but implementations should allow and ignore excess data in this block for extensions. The block follows the following structure:
|
|
||||||
|
|
||||||
| Offset | Content |
|
|
||||||
|--------|--------------------------------------------------------------------------------------------------------------------------|
|
|
||||||
| 0x00 | The size of the border tile data (=0x2000, 32-bit integer) |
|
|
||||||
| 0x04 | The offset of the border tile data (SNES tile format, 32-bit integer) |
|
|
||||||
| 0x08 | The size of the border tilemap (=0x800, 32-bit integer) |
|
|
||||||
| 0x0C | The offset of the border tilemap (LE 16-bit sequences, 32-bit integer) |
|
|
||||||
| 0x10 | The size of the border palettes (=0x80, 32-bit integer) |
|
|
||||||
| 0x14 | The offset of the border palettes (LE 16-bit sequences, 32-bit integer) |
|
|
||||||
| 0x18 | The size of active colorization palettes (=0x20, 32-bit integer) |
|
|
||||||
| 0x1C | The offset of the active colorization palettes (LE 16-bit sequences, 32-bit integer) |
|
|
||||||
| 0x20 | The size of RAM colorization palettes (=0x1000, 32-bit integer) |
|
|
||||||
| 0x24 | The offset of the RAM colorization palettes (LE 16-bit sequences, 32-bit integer) |
|
|
||||||
| 0x28 | The size of the attribute map (=0x168, 32-bit integer) |
|
|
||||||
| 0x2C | The offset of the attribute map (32-bit integer) |
|
|
||||||
| 0x30 | The size of the attribute files (=0xfd2, 32-bit integer) |
|
|
||||||
| 0x34 | The offset of the attribute files (32-bit integer) |
|
|
||||||
| 0x38 | Multiplayer status (1 byte); high nibble is player count (1, 2 or 4), low nibble is current player (Where Player 1 is 0) |
|
|
||||||
|
|
||||||
If only some of the size-offset pairs are available (for example, partial HLE SGB implementation), missing fields are allowed to have 0 as their size, and implementations are expected to fall back to a sane default.
|
|
||||||
|
|
||||||
#### END block
|
|
||||||
The END block uses the `'END '` identifier, and is a required block that marks the end of BESS data. Naturally, it must be the last block. The length of the END block must be 0.
|
|
||||||
|
|
||||||
## Validation and Failures
|
|
||||||
|
|
||||||
Other than previously specified required fail conditions, an implementation is free to decide what format errors should abort the loading of a save file. Structural errors (e.g. a block with an invalid length, a file offset that is outside the file's range, or a missing END block) should be considered as irrecoverable errors. Other errors that are considered fatal by SameBoy's implementation:
|
|
||||||
* Duplicate CORE block
|
|
||||||
* A known block, other than NAME, appearing before CORE
|
|
||||||
* An invalid length for the XOAM, RTC, SGB or HUC3 blocks
|
|
||||||
* An invalid length of MBC (not a multiple of 3)
|
|
||||||
* A write outside the $0000-$7FFF and $A000-$BFFF ranges in the MBC block
|
|
||||||
* An SGB block on a save state targeting another model
|
|
||||||
* An END block with non-zero length
|
|
Before Width: | Height: | Size: 479 B |
BIN
BootROMs/SameboyLogo.1bpp
Normal file
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 14 KiB |
@ -1,2 +0,0 @@
|
|||||||
CGB0 EQU 1
|
|
||||||
include "cgb_boot.asm"
|
|
@ -1,2 +1,782 @@
|
|||||||
FAST EQU 1
|
; Sameboy CGB bootstrap ROM
|
||||||
include "cgb_boot.asm"
|
; Todo: use friendly names for HW registers instead of magic numbers
|
||||||
|
; Todo: add support for games that assume DMG boot logo (Such as X), like the
|
||||||
|
; original boot ROM.
|
||||||
|
SECTION "BootCode", ROM0[$0]
|
||||||
|
Start:
|
||||||
|
; Init stack pointer
|
||||||
|
ld sp, $fffe
|
||||||
|
|
||||||
|
xor a
|
||||||
|
; Clear memory VRAM
|
||||||
|
ld hl, $8000
|
||||||
|
call ClearMemoryPage
|
||||||
|
ld h, $d0
|
||||||
|
call ClearMemoryPage
|
||||||
|
|
||||||
|
; Clear OAM
|
||||||
|
ld hl, $fe00
|
||||||
|
ld c, $a0
|
||||||
|
xor a
|
||||||
|
.clearOAMLoop
|
||||||
|
ldi [hl], a
|
||||||
|
dec c
|
||||||
|
jr nz, .clearOAMLoop
|
||||||
|
|
||||||
|
; Init Audio
|
||||||
|
ld a, $80
|
||||||
|
ldh [$26], a
|
||||||
|
ldh [$11], a
|
||||||
|
ld a, $f3
|
||||||
|
ldh [$12], a
|
||||||
|
ldh [$25], a
|
||||||
|
ld a, $77
|
||||||
|
ldh [$24], a
|
||||||
|
|
||||||
|
ld hl, $FF30
|
||||||
|
; Init waveform
|
||||||
|
xor a
|
||||||
|
ld c, $10
|
||||||
|
.waveformLoop
|
||||||
|
ldi [hl], a
|
||||||
|
cpl
|
||||||
|
dec c
|
||||||
|
jr nz, .waveformLoop
|
||||||
|
|
||||||
|
; Init BG palette
|
||||||
|
ld a, $fc
|
||||||
|
ldh [$47], 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.
|
||||||
|
; These tiles are not used, but are required for DMG compatibility. This is done
|
||||||
|
; by the original CGB Boot ROM as well.
|
||||||
|
ld de, $104 ; Logo start
|
||||||
|
ld hl, $8010 ; This is where we load the tiles in VRAM
|
||||||
|
|
||||||
|
.loadLogoLoop
|
||||||
|
ld a, [de] ; Read 2 rows
|
||||||
|
ld b, a
|
||||||
|
call DoubleBitsAndWriteRow
|
||||||
|
call DoubleBitsAndWriteRow
|
||||||
|
inc de
|
||||||
|
ld a, e
|
||||||
|
xor $34 ; End of logo
|
||||||
|
jr nz, .loadLogoLoop
|
||||||
|
call ReadTrademarkSymbol
|
||||||
|
|
||||||
|
; Clear the second VRAM bank
|
||||||
|
ld a, 1
|
||||||
|
ldh [$4F], a
|
||||||
|
xor a
|
||||||
|
ld hl, $8000
|
||||||
|
call ClearMemoryPage
|
||||||
|
|
||||||
|
; Copy (unresized) ROM logo
|
||||||
|
ld de, $104
|
||||||
|
ld c, 6
|
||||||
|
.CGBROMLogoLoop
|
||||||
|
push bc
|
||||||
|
call ReadCGBLogoTile
|
||||||
|
pop bc
|
||||||
|
dec c
|
||||||
|
jr nz, .CGBROMLogoLoop
|
||||||
|
inc hl
|
||||||
|
call ReadTrademarkSymbol
|
||||||
|
|
||||||
|
; Load Tilemap
|
||||||
|
ld hl, $98C2
|
||||||
|
ld b, 3
|
||||||
|
ld a, 8
|
||||||
|
|
||||||
|
.tilemapLoop
|
||||||
|
ld c, $10
|
||||||
|
|
||||||
|
.tilemapRowLoop
|
||||||
|
|
||||||
|
ld [hl], a
|
||||||
|
push af
|
||||||
|
; Switch to second VRAM Bank
|
||||||
|
ld a, 1
|
||||||
|
ldh [$4F], a
|
||||||
|
ld a, 8
|
||||||
|
ld [hl], a
|
||||||
|
; Switch to back first VRAM Bank
|
||||||
|
xor a
|
||||||
|
ldh [$4F], a
|
||||||
|
pop af
|
||||||
|
ldi [hl], a
|
||||||
|
inc a
|
||||||
|
dec c
|
||||||
|
jr nz, .tilemapRowLoop
|
||||||
|
ld de, $10
|
||||||
|
add hl, de
|
||||||
|
dec b
|
||||||
|
jr nz, .tilemapLoop
|
||||||
|
|
||||||
|
cp $38
|
||||||
|
jr nz, .doneTilemap
|
||||||
|
|
||||||
|
ld hl, $99a7
|
||||||
|
ld b, 1
|
||||||
|
ld c, 7
|
||||||
|
jr .tilemapRowLoop
|
||||||
|
.doneTilemap
|
||||||
|
|
||||||
|
; Clear Palettes
|
||||||
|
ld c, 64
|
||||||
|
ld hl, BgPalettes
|
||||||
|
ld a, $FF
|
||||||
|
.clearPalettesLoop:
|
||||||
|
ldi [hl], a
|
||||||
|
dec c
|
||||||
|
jr nz, .clearPalettesLoop
|
||||||
|
|
||||||
|
ld hl, BgPalettes
|
||||||
|
ld d, 64 ; Length of write
|
||||||
|
ld e, 0 ; Index of write
|
||||||
|
call LoadBGPalettes
|
||||||
|
|
||||||
|
; Turn on LCD
|
||||||
|
ld a, $91
|
||||||
|
ldh [$40], a
|
||||||
|
|
||||||
|
call Preboot
|
||||||
|
|
||||||
|
; Will be filled with NOPs
|
||||||
|
|
||||||
|
SECTION "BootGame", ROM0[$fe]
|
||||||
|
BootGame:
|
||||||
|
ldh [$50], a
|
||||||
|
|
||||||
|
SECTION "MoreStuff", ROM0[$200]
|
||||||
|
|
||||||
|
; Game Palettes Data
|
||||||
|
TitleChecksums:
|
||||||
|
db $00 ; Default
|
||||||
|
db $88 ; ALLEY WAY
|
||||||
|
db $16 ; YAKUMAN
|
||||||
|
db $36 ; BASEBALL, (Game and Watch 2)
|
||||||
|
db $D1 ; TENNIS
|
||||||
|
db $DB ; TETRIS
|
||||||
|
db $F2 ; QIX
|
||||||
|
db $3C ; DR.MARIO
|
||||||
|
db $8C ; RADARMISSION
|
||||||
|
db $92 ; F1RACE
|
||||||
|
db $3D ; YOSSY NO TAMAGO
|
||||||
|
db $5C ;
|
||||||
|
db $58 ; X
|
||||||
|
db $C9 ; MARIOLAND2
|
||||||
|
db $3E ; YOSSY NO COOKIE
|
||||||
|
db $70 ; ZELDA
|
||||||
|
db $1D ;
|
||||||
|
db $59 ;
|
||||||
|
db $69 ; TETRIS FLASH
|
||||||
|
db $19 ; DONKEY KONG
|
||||||
|
db $35 ; MARIO'S PICROSS
|
||||||
|
db $A8 ;
|
||||||
|
db $14 ; POKEMON RED, (GAMEBOYCAMERA G)
|
||||||
|
db $AA ; POKEMON GREEN
|
||||||
|
db $75 ; PICROSS 2
|
||||||
|
db $95 ; YOSSY NO PANEPON
|
||||||
|
db $99 ; KIRAKIRA KIDS
|
||||||
|
db $34 ; GAMEBOY GALLERY
|
||||||
|
db $6F ; POCKETCAMERA
|
||||||
|
db $15 ;
|
||||||
|
db $FF ; BALLOON KID
|
||||||
|
db $97 ; KINGOFTHEZOO
|
||||||
|
db $4B ; DMG FOOTBALL
|
||||||
|
db $90 ; WORLD CUP
|
||||||
|
db $17 ; OTHELLO
|
||||||
|
db $10 ; SUPER RC PRO-AM
|
||||||
|
db $39 ; DYNABLASTER
|
||||||
|
db $F7 ; BOY AND BLOB GB2
|
||||||
|
db $F6 ; MEGAMAN
|
||||||
|
db $A2 ; STAR WARS-NOA
|
||||||
|
db $49 ;
|
||||||
|
db $4E ; WAVERACE
|
||||||
|
db $43 | $80 ;
|
||||||
|
db $68 ; LOLO2
|
||||||
|
db $E0 ; YOSHI'S COOKIE
|
||||||
|
db $8B ; MYSTIC QUEST
|
||||||
|
db $F0 ;
|
||||||
|
db $CE ; TOPRANKINGTENNIS
|
||||||
|
db $0C ; MANSELL
|
||||||
|
db $29 ; MEGAMAN3
|
||||||
|
db $E8 ; SPACE INVADERS
|
||||||
|
db $B7 ; GAME&WATCH
|
||||||
|
db $86 ; DONKEYKONGLAND95
|
||||||
|
db $9A ; ASTEROIDS/MISCMD
|
||||||
|
db $52 ; STREET FIGHTER 2
|
||||||
|
db $01 ; DEFENDER/JOUST
|
||||||
|
db $9D ; KILLERINSTINCT95
|
||||||
|
db $71 ; TETRIS BLAST
|
||||||
|
db $9C ; PINOCCHIO
|
||||||
|
db $BD ;
|
||||||
|
db $5D ; BA.TOSHINDEN
|
||||||
|
db $6D ; NETTOU KOF 95
|
||||||
|
db $67 ;
|
||||||
|
db $3F ; TETRIS PLUS
|
||||||
|
db $6B ; DONKEYKONGLAND 3
|
||||||
|
; For these games, the 4th letter is taken into account
|
||||||
|
FirstChecksumWithDuplicate:
|
||||||
|
; Let's play hangman!
|
||||||
|
db $B3 ; ???[B]????????
|
||||||
|
db $46 ; SUP[E]R MARIOLAND
|
||||||
|
db $28 ; GOL[F]
|
||||||
|
db $A5 ; SOL[A]RSTRIKER
|
||||||
|
db $C6 ; GBW[A]RS
|
||||||
|
db $D3 ; KAE[R]UNOTAMENI
|
||||||
|
db $27 ; ???[B]????????
|
||||||
|
db $61 ; POK[E]MON BLUE
|
||||||
|
db $18 ; DON[K]EYKONGLAND
|
||||||
|
db $66 ; GAM[E]BOY GALLERY2
|
||||||
|
db $6A ; DON[K]EYKONGLAND 2
|
||||||
|
db $BF ; KID[ ]ICARUS
|
||||||
|
db $0D ; TET[R]IS2
|
||||||
|
db $F4 ; ???[-]????????
|
||||||
|
db $B3 ; MOG[U]RANYA
|
||||||
|
db $46 ; ???[R]????????
|
||||||
|
db $28 ; GAL[A]GA&GALAXIAN
|
||||||
|
db $A5 ; BT2[R]AGNAROKWORLD
|
||||||
|
db $C6 ; KEN[ ]GRIFFEY JR
|
||||||
|
db $D3 ; ???[I]????????
|
||||||
|
db $27 ; MAG[N]ETIC SOCCER
|
||||||
|
db $61 ; VEG[A]S STAKES
|
||||||
|
db $18 ; ???[I]????????
|
||||||
|
db $66 ; MIL[L]I/CENTI/PEDE
|
||||||
|
db $6A ; MAR[I]O & YOSHI
|
||||||
|
db $BF ; SOC[C]ER
|
||||||
|
db $0D ; POK[E]BOM
|
||||||
|
db $F4 ; G&W[ ]GALLERY
|
||||||
|
db $B3 ; TET[R]IS ATTACK
|
||||||
|
ChecksumsEnd:
|
||||||
|
|
||||||
|
PalettePerChecksum:
|
||||||
|
; | $80 means game requires DMG boot tilemap
|
||||||
|
db 0 ; Default Palette
|
||||||
|
db 4 ; ALLEY WAY
|
||||||
|
db 5 ; YAKUMAN
|
||||||
|
db 35 ; BASEBALL, (Game and Watch 2)
|
||||||
|
db 34 ; TENNIS
|
||||||
|
db 3 ; TETRIS
|
||||||
|
db 31 ; QIX
|
||||||
|
db 15 ; DR.MARIO
|
||||||
|
db 10 ; RADARMISSION
|
||||||
|
db 5 ; F1RACE
|
||||||
|
db 19 ; YOSSY NO TAMAGO
|
||||||
|
db 36 ;
|
||||||
|
db 7 | $80 ; X
|
||||||
|
db 37 ; MARIOLAND2
|
||||||
|
db 30 ; YOSSY NO COOKIE
|
||||||
|
db 44 ; ZELDA
|
||||||
|
db 21 ;
|
||||||
|
db 32 ;
|
||||||
|
db 31 ; TETRIS FLASH
|
||||||
|
db 20 ; DONKEY KONG
|
||||||
|
db 5 ; MARIO'S PICROSS
|
||||||
|
db 33 ;
|
||||||
|
db 13 ; POKEMON RED, (GAMEBOYCAMERA G)
|
||||||
|
db 14 ; POKEMON GREEN
|
||||||
|
db 5 ; PICROSS 2
|
||||||
|
db 29 ; YOSSY NO PANEPON
|
||||||
|
db 5 ; KIRAKIRA KIDS
|
||||||
|
db 18 ; GAMEBOY GALLERY
|
||||||
|
db 9 ; POCKETCAMERA
|
||||||
|
db 3 ;
|
||||||
|
db 2 ; BALLOON KID
|
||||||
|
db 26 ; KINGOFTHEZOO
|
||||||
|
db 25 ; DMG FOOTBALL
|
||||||
|
db 25 ; WORLD CUP
|
||||||
|
db 41 ; OTHELLO
|
||||||
|
db 42 ; SUPER RC PRO-AM
|
||||||
|
db 26 ; DYNABLASTER
|
||||||
|
db 45 ; BOY AND BLOB GB2
|
||||||
|
db 42 ; MEGAMAN
|
||||||
|
db 45 ; STAR WARS-NOA
|
||||||
|
db 36 ;
|
||||||
|
db 38 ; WAVERACE
|
||||||
|
db 26 ;
|
||||||
|
db 42 ; LOLO2
|
||||||
|
db 30 ; YOSHI'S COOKIE
|
||||||
|
db 41 ; MYSTIC QUEST
|
||||||
|
db 34 ;
|
||||||
|
db 34 ; TOPRANKINGTENNIS
|
||||||
|
db 5 ; MANSELL
|
||||||
|
db 42 ; MEGAMAN3
|
||||||
|
db 6 ; SPACE INVADERS
|
||||||
|
db 5 ; GAME&WATCH
|
||||||
|
db 33 ; DONKEYKONGLAND95
|
||||||
|
db 25 ; ASTEROIDS/MISCMD
|
||||||
|
db 42 ; STREET FIGHTER 2
|
||||||
|
db 42 ; DEFENDER/JOUST
|
||||||
|
db 40 ; KILLERINSTINCT95
|
||||||
|
db 2 ; TETRIS BLAST
|
||||||
|
db 16 ; PINOCCHIO
|
||||||
|
db 25 ;
|
||||||
|
db 42 ; BA.TOSHINDEN
|
||||||
|
db 42 ; NETTOU KOF 95
|
||||||
|
db 5 ;
|
||||||
|
db 0 ; TETRIS PLUS
|
||||||
|
db 39 ; DONKEYKONGLAND 3
|
||||||
|
db 36 ;
|
||||||
|
db 22 ; SUPER MARIOLAND
|
||||||
|
db 25 ; GOLF
|
||||||
|
db 6 ; SOLARSTRIKER
|
||||||
|
db 32 ; GBWARS
|
||||||
|
db 12 ; KAERUNOTAMENI
|
||||||
|
db 36 ;
|
||||||
|
db 11 ; POKEMON BLUE
|
||||||
|
db 39 ; DONKEYKONGLAND
|
||||||
|
db 18 ; GAMEBOY GALLERY2
|
||||||
|
db 39 ; DONKEYKONGLAND 2
|
||||||
|
db 24 ; KID ICARUS
|
||||||
|
db 31 ; TETRIS2
|
||||||
|
db 50 ;
|
||||||
|
db 17 ; MOGURANYA
|
||||||
|
db 46 ;
|
||||||
|
db 6 ; GALAGA&GALAXIAN
|
||||||
|
db 27 ; BT2RAGNAROKWORLD
|
||||||
|
db 0 ; KEN GRIFFEY JR
|
||||||
|
db 47 ;
|
||||||
|
db 41 ; MAGNETIC SOCCER
|
||||||
|
db 41 ; VEGAS STAKES
|
||||||
|
db 0 ;
|
||||||
|
db 0 ; MILLI/CENTI/PEDE
|
||||||
|
db 19 ; MARIO & YOSHI
|
||||||
|
db 34 ; SOCCER
|
||||||
|
db 23 ; POKEBOM
|
||||||
|
db 18 ; G&W GALLERY
|
||||||
|
db 29 ; TETRIS ATTACK
|
||||||
|
|
||||||
|
Dups4thLetterArray:
|
||||||
|
db "BEFAARBEKEK R-URAR INAILICE R"
|
||||||
|
|
||||||
|
; We assume the last three arrays fit in the same $100 byte page!
|
||||||
|
|
||||||
|
PaletteCombinations:
|
||||||
|
palette_comb: MACRO ; Obj0, Obj1, Bg
|
||||||
|
db \1 * 8, \2 * 8, \3 *8
|
||||||
|
ENDM
|
||||||
|
palette_comb 4, 4, 29
|
||||||
|
palette_comb 18, 18, 18
|
||||||
|
palette_comb 20, 20, 20
|
||||||
|
palette_comb 24, 24, 24
|
||||||
|
palette_comb 9, 9, 9
|
||||||
|
palette_comb 0, 0, 0
|
||||||
|
palette_comb 27, 27, 27
|
||||||
|
palette_comb 5, 5, 5
|
||||||
|
palette_comb 12, 12, 12
|
||||||
|
palette_comb 26, 26, 26
|
||||||
|
palette_comb 16, 8, 8
|
||||||
|
palette_comb 4, 28, 28
|
||||||
|
palette_comb 4, 2, 2
|
||||||
|
palette_comb 3, 4, 4
|
||||||
|
palette_comb 4, 29, 29
|
||||||
|
palette_comb 28, 4, 28
|
||||||
|
palette_comb 2, 17, 2
|
||||||
|
palette_comb 16, 16, 8
|
||||||
|
palette_comb 4, 4, 7
|
||||||
|
palette_comb 4, 4, 18
|
||||||
|
palette_comb 4, 4, 20
|
||||||
|
palette_comb 19, 19, 9
|
||||||
|
palette_comb 3, 3, 11
|
||||||
|
palette_comb 17, 17, 2
|
||||||
|
palette_comb 4, 4, 2
|
||||||
|
palette_comb 4, 4, 3
|
||||||
|
palette_comb 28, 28, 0
|
||||||
|
palette_comb 3, 3, 0
|
||||||
|
palette_comb 0, 0, 1
|
||||||
|
palette_comb 18, 22, 18
|
||||||
|
palette_comb 20, 22, 20
|
||||||
|
palette_comb 24, 22, 24
|
||||||
|
palette_comb 16, 22, 8
|
||||||
|
palette_comb 17, 4, 13
|
||||||
|
palette_comb 27, 0, 14
|
||||||
|
palette_comb 27, 4, 15
|
||||||
|
palette_comb 19, 22, 9
|
||||||
|
palette_comb 16, 28, 10
|
||||||
|
palette_comb 4, 23, 28
|
||||||
|
palette_comb 17, 22, 2
|
||||||
|
palette_comb 4, 0, 2
|
||||||
|
palette_comb 4, 28, 3
|
||||||
|
palette_comb 28, 3, 0
|
||||||
|
palette_comb 3, 28, 4
|
||||||
|
palette_comb 21, 28, 4
|
||||||
|
palette_comb 3, 28, 0
|
||||||
|
palette_comb 25, 3, 28
|
||||||
|
palette_comb 0, 28, 8
|
||||||
|
palette_comb 4, 3, 28
|
||||||
|
palette_comb 28, 3, 6
|
||||||
|
palette_comb 4, 28, 29
|
||||||
|
; Sameboy "Exclusives"
|
||||||
|
palette_comb 30, 30, 30 ; CGA
|
||||||
|
palette_comb 31, 31, 31 ; DMG LCD
|
||||||
|
palette_comb 28, 4, 1
|
||||||
|
palette_comb 0, 0, 2
|
||||||
|
|
||||||
|
Palettes:
|
||||||
|
dw $7FFF, $32BF, $00D0, $0000
|
||||||
|
dw $639F, $4279, $15B0, $04CB
|
||||||
|
dw $7FFF, $6E31, $454A, $0000
|
||||||
|
dw $7FFF, $1BEF, $0200, $0000
|
||||||
|
dw $7FFF, $421F, $1CF2, $0000
|
||||||
|
dw $7FFF, $5294, $294A, $0000
|
||||||
|
dw $7FFF, $03FF, $012F, $0000
|
||||||
|
dw $7FFF, $03EF, $01D6, $0000
|
||||||
|
dw $7FFF, $42B5, $3DC8, $0000
|
||||||
|
dw $7E74, $03FF, $0180, $0000
|
||||||
|
dw $67FF, $77AC, $1A13, $2D6B
|
||||||
|
dw $7ED6, $4BFF, $2175, $0000
|
||||||
|
dw $53FF, $4A5F, $7E52, $0000
|
||||||
|
dw $4FFF, $7ED2, $3A4C, $1CE0
|
||||||
|
dw $03ED, $7FFF, $255F, $0000
|
||||||
|
dw $036A, $021F, $03FF, $7FFF
|
||||||
|
dw $7FFF, $01DF, $0112, $0000
|
||||||
|
dw $231F, $035F, $00F2, $0009
|
||||||
|
dw $7FFF, $03EA, $011F, $0000
|
||||||
|
dw $299F, $001A, $000C, $0000
|
||||||
|
dw $7FFF, $027F, $001F, $0000
|
||||||
|
dw $7FFF, $03E0, $0206, $0120
|
||||||
|
dw $7FFF, $7EEB, $001F, $7C00
|
||||||
|
dw $7FFF, $3FFF, $7E00, $001F
|
||||||
|
dw $7FFF, $03FF, $001F, $0000
|
||||||
|
dw $03FF, $001F, $000C, $0000
|
||||||
|
dw $7FFF, $033F, $0193, $0000
|
||||||
|
dw $0000, $4200, $037F, $7FFF
|
||||||
|
dw $7FFF, $7E8C, $7C00, $0000
|
||||||
|
dw $7FFF, $1BEF, $6180, $0000
|
||||||
|
; Sameboy "Exclusives"
|
||||||
|
dw $7FFF, $7FEA, $7D5F, $0000 ; CGA 1
|
||||||
|
dw $1B77, $0AD2, $25E9, $1545 ; DMG LCD
|
||||||
|
|
||||||
|
KeyCombinationPalettes
|
||||||
|
db 1 ; Right
|
||||||
|
db 48 ; Left
|
||||||
|
db 5 ; Up
|
||||||
|
db 8 ; Down
|
||||||
|
db 0 ; Right + A
|
||||||
|
db 40 ; Left + A
|
||||||
|
db 43 ; Up + A
|
||||||
|
db 3 ; Down + A
|
||||||
|
db 6 ; Right + B
|
||||||
|
db 7 ; Left + B
|
||||||
|
db 28 ; Up + B
|
||||||
|
db 49 ; Down + B
|
||||||
|
; Sameboy "Exclusives"
|
||||||
|
db 51 ; Right + A + B
|
||||||
|
db 52 ; Left + A + B
|
||||||
|
db 53 ; Up + A + B
|
||||||
|
db 54 ; Down + A + B
|
||||||
|
|
||||||
|
TrademarkSymbol:
|
||||||
|
db $3c,$42,$b9,$a5,$b9,$a5,$42,$3c
|
||||||
|
|
||||||
|
DMGPalettes:
|
||||||
|
dw $7FFF, $32BF, $00D0, $0000
|
||||||
|
|
||||||
|
; Helper Functions
|
||||||
|
DoubleBitsAndWriteRow:
|
||||||
|
; Double the most significant 4 bits, b is shifted by 4
|
||||||
|
ld a, 4
|
||||||
|
ld c, 0
|
||||||
|
.doubleCurrentBit
|
||||||
|
sla b
|
||||||
|
push af
|
||||||
|
rl c
|
||||||
|
pop af
|
||||||
|
rl c
|
||||||
|
dec a
|
||||||
|
jr nz, .doubleCurrentBit
|
||||||
|
ld a, c
|
||||||
|
; Write as two rows
|
||||||
|
ldi [hl], a
|
||||||
|
inc hl
|
||||||
|
ldi [hl], a
|
||||||
|
inc hl
|
||||||
|
ret
|
||||||
|
|
||||||
|
WaitFrame:
|
||||||
|
push hl
|
||||||
|
ld hl, $FF0F
|
||||||
|
res 0, [hl]
|
||||||
|
.wait
|
||||||
|
bit 0, [hl]
|
||||||
|
jr z, .wait
|
||||||
|
pop hl
|
||||||
|
ret
|
||||||
|
|
||||||
|
PlaySound:
|
||||||
|
ldh [$13], a
|
||||||
|
ld a, $87
|
||||||
|
ldh [$14], a
|
||||||
|
ret
|
||||||
|
|
||||||
|
; Clear from HL to HL | 0x2000
|
||||||
|
ClearMemoryPage:
|
||||||
|
ldi [hl], a
|
||||||
|
bit 5, h
|
||||||
|
jr z, ClearMemoryPage
|
||||||
|
ret
|
||||||
|
|
||||||
|
; c = $f0 for even lines, $f for odd lines.
|
||||||
|
ReadTileLine:
|
||||||
|
ld a, [de]
|
||||||
|
and c
|
||||||
|
ld b, a
|
||||||
|
inc e
|
||||||
|
inc e
|
||||||
|
ld a, [de]
|
||||||
|
dec e
|
||||||
|
dec e
|
||||||
|
and c
|
||||||
|
swap a
|
||||||
|
or b
|
||||||
|
bit 0, c
|
||||||
|
jr z, .dontSwap
|
||||||
|
swap a
|
||||||
|
.dontSwap
|
||||||
|
inc hl
|
||||||
|
ldi [hl], a
|
||||||
|
ret
|
||||||
|
|
||||||
|
|
||||||
|
ReadCGBLogoHalfTile:
|
||||||
|
ld c, $f0
|
||||||
|
call ReadTileLine
|
||||||
|
ld c, $f
|
||||||
|
call ReadTileLine
|
||||||
|
inc e
|
||||||
|
ld c, $f0
|
||||||
|
call ReadTileLine
|
||||||
|
ld c, $f
|
||||||
|
call ReadTileLine
|
||||||
|
inc e
|
||||||
|
ret
|
||||||
|
|
||||||
|
ReadCGBLogoTile:
|
||||||
|
call ReadCGBLogoHalfTile
|
||||||
|
ld a, e
|
||||||
|
add a, 22
|
||||||
|
ld e, a
|
||||||
|
call ReadCGBLogoHalfTile
|
||||||
|
ld a, e
|
||||||
|
sub a, 22
|
||||||
|
ld e, a
|
||||||
|
ret
|
||||||
|
|
||||||
|
|
||||||
|
ReadTrademarkSymbol:
|
||||||
|
ld de, TrademarkSymbol
|
||||||
|
ld c,$08
|
||||||
|
.loadTrademarkSymbolLoop:
|
||||||
|
ld a,[de]
|
||||||
|
inc de
|
||||||
|
ldi [hl],a
|
||||||
|
inc hl
|
||||||
|
dec c
|
||||||
|
jr nz, .loadTrademarkSymbolLoop
|
||||||
|
ret
|
||||||
|
|
||||||
|
LoadObjPalettes:
|
||||||
|
ld c, $6A
|
||||||
|
jr LoadPalettes
|
||||||
|
|
||||||
|
LoadBGPalettes:
|
||||||
|
ld c, $68
|
||||||
|
|
||||||
|
LoadPalettes:
|
||||||
|
ld a, $80
|
||||||
|
or e
|
||||||
|
ld [c], a
|
||||||
|
inc c
|
||||||
|
.loop
|
||||||
|
ld a, [hli]
|
||||||
|
ld [c], a
|
||||||
|
dec d
|
||||||
|
jr nz, .loop
|
||||||
|
ret
|
||||||
|
|
||||||
|
|
||||||
|
Preboot:
|
||||||
|
call ClearVRAMViaHDMA
|
||||||
|
; Select the first bank
|
||||||
|
xor a
|
||||||
|
ldh [$4F], a
|
||||||
|
call ClearVRAMViaHDMA
|
||||||
|
|
||||||
|
ld a, [$143]
|
||||||
|
bit 7, a
|
||||||
|
jr nz, .cgbGame
|
||||||
|
|
||||||
|
call EmulateDMG
|
||||||
|
|
||||||
|
.cgbGame
|
||||||
|
ldh [$4C], a ; One day, I will know what this switch does and how it differs from FF6C
|
||||||
|
ld a, $11
|
||||||
|
ret
|
||||||
|
|
||||||
|
EmulateDMG:
|
||||||
|
ld a, 1
|
||||||
|
ldh [$6C], a ; DMG Emulation
|
||||||
|
call GetPaletteIndex
|
||||||
|
bit 7, a
|
||||||
|
call nz, LoadDMGTilemap
|
||||||
|
and $7F
|
||||||
|
call WaitFrame
|
||||||
|
call LoadPalettesFromIndex
|
||||||
|
ld a, 4
|
||||||
|
ret
|
||||||
|
|
||||||
|
GetPaletteIndex:
|
||||||
|
ld a, [$14B] ; Old Licensee
|
||||||
|
cp $33
|
||||||
|
jr z, .newLicensee
|
||||||
|
cp 1 ; Nintendo
|
||||||
|
jr nz, .notNintendo
|
||||||
|
jr .doChecksum
|
||||||
|
.newLicensee
|
||||||
|
ld a, [$144]
|
||||||
|
cp "0"
|
||||||
|
jr nz, .notNintendo
|
||||||
|
ld a, [$145]
|
||||||
|
cp "1"
|
||||||
|
jr nz, .notNintendo
|
||||||
|
|
||||||
|
.doChecksum
|
||||||
|
ld hl, $134
|
||||||
|
ld c, $10
|
||||||
|
ld b, 0
|
||||||
|
|
||||||
|
.checksumLoop
|
||||||
|
ld a, [hli]
|
||||||
|
add b
|
||||||
|
ld b, a
|
||||||
|
dec c
|
||||||
|
jr nz, .checksumLoop
|
||||||
|
|
||||||
|
; c = 0
|
||||||
|
ld hl, TitleChecksums
|
||||||
|
|
||||||
|
.searchLoop
|
||||||
|
ld a, l
|
||||||
|
cp ChecksumsEnd & $FF
|
||||||
|
jr z, .notNintendo
|
||||||
|
ld a, [hli]
|
||||||
|
cp b
|
||||||
|
jr nz, .searchLoop
|
||||||
|
|
||||||
|
; We might have a match, Do duplicate/4th letter check
|
||||||
|
ld a, l
|
||||||
|
sub FirstChecksumWithDuplicate - TitleChecksums
|
||||||
|
jr c, .match ; Does not have a duplicate, must be a match!
|
||||||
|
; Has a duplicate; check 4th letter
|
||||||
|
push hl
|
||||||
|
ld a, l
|
||||||
|
add Dups4thLetterArray - FirstChecksumWithDuplicate - 1 ; -1 since hl was incremented
|
||||||
|
ld l, a
|
||||||
|
ld a, [hl]
|
||||||
|
pop hl
|
||||||
|
ld c, a
|
||||||
|
ld a, [$134 + 3] ; Get 4th letter
|
||||||
|
cp c
|
||||||
|
jr nz, .searchLoop ; Not a match, continue
|
||||||
|
|
||||||
|
.match
|
||||||
|
ld a, l
|
||||||
|
add PalettePerChecksum - TitleChecksums - 1; -1 since hl was incremented
|
||||||
|
ld l, a
|
||||||
|
ld a, [hl]
|
||||||
|
ret
|
||||||
|
|
||||||
|
.notNintendo
|
||||||
|
xor a
|
||||||
|
ret
|
||||||
|
|
||||||
|
LoadPalettesFromIndex: ; a = index of combination
|
||||||
|
ld b, a
|
||||||
|
; Multiply by 3
|
||||||
|
add b
|
||||||
|
add b
|
||||||
|
|
||||||
|
ld hl, PaletteCombinations
|
||||||
|
ld b, 0
|
||||||
|
ld c, a
|
||||||
|
add hl, bc
|
||||||
|
|
||||||
|
; Obj Palettes
|
||||||
|
ld e, 0
|
||||||
|
.loadObjPalette
|
||||||
|
ld a, [hli]
|
||||||
|
push hl
|
||||||
|
ld hl, Palettes
|
||||||
|
ld b, 0
|
||||||
|
ld c, a
|
||||||
|
add hl, bc
|
||||||
|
ld d, 8
|
||||||
|
call LoadObjPalettes
|
||||||
|
pop hl
|
||||||
|
bit 3, e
|
||||||
|
jr nz, .loadBGPalette
|
||||||
|
ld e, 8
|
||||||
|
jr .loadObjPalette
|
||||||
|
.loadBGPalette
|
||||||
|
;BG Palette
|
||||||
|
ld a, [hli]
|
||||||
|
ld hl, Palettes
|
||||||
|
ld b, 0
|
||||||
|
ld c, a
|
||||||
|
add hl, bc
|
||||||
|
ld d, 8
|
||||||
|
ld e, 0
|
||||||
|
call LoadBGPalettes
|
||||||
|
ret
|
||||||
|
|
||||||
|
ClearVRAMViaHDMA:
|
||||||
|
ld hl, $FF51
|
||||||
|
|
||||||
|
; Src
|
||||||
|
ld a, $D0
|
||||||
|
ld [hli], a
|
||||||
|
xor a
|
||||||
|
ld [hli], a
|
||||||
|
|
||||||
|
; Dest
|
||||||
|
ld a, $98
|
||||||
|
ld [hli], a
|
||||||
|
ld a, $A0
|
||||||
|
ld [hli], a
|
||||||
|
|
||||||
|
; Do it
|
||||||
|
ld a, $12
|
||||||
|
ld [hli], a
|
||||||
|
ret
|
||||||
|
|
||||||
|
|
||||||
|
LoadDMGTilemap:
|
||||||
|
push af
|
||||||
|
call WaitFrame
|
||||||
|
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
|
||||||
|
.tilemapLoop
|
||||||
|
dec a
|
||||||
|
jr z, .tilemapDone
|
||||||
|
ldd [hl], a
|
||||||
|
dec c
|
||||||
|
jr nz, .tilemapLoop
|
||||||
|
ld l,$0f ; Jump to top row
|
||||||
|
jr .tilemapLoop
|
||||||
|
.tilemapDone
|
||||||
|
pop af
|
||||||
|
ret
|
||||||
|
|
||||||
|
SECTION "ROMMax", ROM0[$900]
|
||||||
|
; Prevent us from overflowing
|
||||||
|
ds 1
|
||||||
|
|
||||||
|
SECTION "RAM", WRAM0[$C000]
|
||||||
|
BgPalettes:
|
||||||
|
ds 8 * 4 * 2
|
@ -1,4 +1,4 @@
|
|||||||
; SameBoy DMG bootstrap ROM
|
; Sameboy CGB bootstrap ROM
|
||||||
; Todo: use friendly names for HW registers instead of magic numbers
|
; Todo: use friendly names for HW registers instead of magic numbers
|
||||||
SECTION "BootCode", ROM0[$0]
|
SECTION "BootCode", ROM0[$0]
|
||||||
Start:
|
Start:
|
||||||
@ -24,7 +24,7 @@ Start:
|
|||||||
ldh [$24], a
|
ldh [$24], a
|
||||||
|
|
||||||
; Init BG palette
|
; Init BG palette
|
||||||
ld a, $54
|
ld a, $fc
|
||||||
ldh [$47], a
|
ldh [$47], a
|
||||||
|
|
||||||
; Load logo from ROM.
|
; Load logo from ROM.
|
||||||
@ -69,35 +69,13 @@ Start:
|
|||||||
jr .tilemapLoop
|
jr .tilemapLoop
|
||||||
.tilemapDone
|
.tilemapDone
|
||||||
|
|
||||||
ld a, 30
|
|
||||||
ldh [$ff42], a
|
|
||||||
|
|
||||||
; Turn on LCD
|
; Turn on LCD
|
||||||
ld a, $91
|
ld a, $91
|
||||||
ldh [$40], a
|
ldh [$40], a
|
||||||
|
|
||||||
ld d, (-119) & $FF
|
; Wait ~0.75 seconds
|
||||||
ld c, 15
|
ld b, 45
|
||||||
|
call WaitBFrames
|
||||||
.animate
|
|
||||||
call WaitFrame
|
|
||||||
ld a, d
|
|
||||||
sra a
|
|
||||||
sra a
|
|
||||||
ldh [$ff42], a
|
|
||||||
ld a, d
|
|
||||||
add c
|
|
||||||
ld d, a
|
|
||||||
ld a, c
|
|
||||||
cp 8
|
|
||||||
jr nz, .noPaletteChange
|
|
||||||
ld a, $A8
|
|
||||||
ldh [$47], a
|
|
||||||
.noPaletteChange
|
|
||||||
dec c
|
|
||||||
jr nz, .animate
|
|
||||||
ld a, $fc
|
|
||||||
ldh [$47], a
|
|
||||||
|
|
||||||
; Play first sound
|
; Play first sound
|
||||||
ld a, $83
|
ld a, $83
|
||||||
@ -108,18 +86,12 @@ Start:
|
|||||||
ld a, $c1
|
ld a, $c1
|
||||||
call PlaySound
|
call PlaySound
|
||||||
|
|
||||||
|
; Wait ~1.15 seconds
|
||||||
|
ld b, 70
|
||||||
; Wait ~1 second
|
|
||||||
ld b, 60
|
|
||||||
call WaitBFrames
|
call WaitBFrames
|
||||||
|
|
||||||
; Set registers to match the original DMG boot
|
; Set registers to match the original DMG boot
|
||||||
IF DEF(MGB)
|
|
||||||
ld hl, $FFB0
|
|
||||||
ELSE
|
|
||||||
ld hl, $01B0
|
ld hl, $01B0
|
||||||
ENDC
|
|
||||||
push hl
|
push hl
|
||||||
pop af
|
pop af
|
||||||
ld hl, $014D
|
ld hl, $014D
|
||||||
|
@ -1,2 +0,0 @@
|
|||||||
MGB EQU 1
|
|
||||||
include "dmg_boot.asm"
|
|
102
BootROMs/pb12.c
@ -1,102 +0,0 @@
|
|||||||
#include <stdio.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
void opts(uint8_t byte, uint8_t *options)
|
|
||||||
{
|
|
||||||
*(options++) = byte | ((byte << 1) & 0xff);
|
|
||||||
*(options++) = byte & (byte << 1);
|
|
||||||
*(options++) = byte | ((byte >> 1) & 0xff);
|
|
||||||
*(options++) = byte & (byte >> 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void write_all(int fd, const void *buf, size_t count) {
|
|
||||||
while (count) {
|
|
||||||
ssize_t written = write(fd, buf, count);
|
|
||||||
if (written < 0) {
|
|
||||||
fprintf(stderr, "write");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
count -= written;
|
|
||||||
buf += written;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
static uint8_t source[0x4000];
|
|
||||||
size_t size = read(STDIN_FILENO, &source, sizeof(source));
|
|
||||||
unsigned pos = 0;
|
|
||||||
assert(size <= 0x4000);
|
|
||||||
while (size && source[size - 1] == 0) {
|
|
||||||
size--;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t literals[8];
|
|
||||||
size_t literals_size = 0;
|
|
||||||
unsigned bits = 0;
|
|
||||||
unsigned control = 0;
|
|
||||||
unsigned prev[2] = {-1, -1}; // Unsigned to allow "not set" values
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
|
|
||||||
uint8_t byte = 0;
|
|
||||||
if (pos == size){
|
|
||||||
if (bits == 0) break;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
byte = source[pos++];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (byte == prev[0] || byte == prev[1]) {
|
|
||||||
bits += 2;
|
|
||||||
control <<= 1;
|
|
||||||
control |= 1;
|
|
||||||
control <<= 1;
|
|
||||||
if (byte == prev[1]) {
|
|
||||||
control |= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
bits += 2;
|
|
||||||
control <<= 2;
|
|
||||||
uint8_t options[4];
|
|
||||||
opts(prev[1], options);
|
|
||||||
bool found = false;
|
|
||||||
for (unsigned i = 0; i < 4; i++) {
|
|
||||||
if (options[i] == byte) {
|
|
||||||
// 01 = modify
|
|
||||||
control |= 1;
|
|
||||||
|
|
||||||
bits += 2;
|
|
||||||
control <<= 2;
|
|
||||||
control |= i;
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!found) {
|
|
||||||
literals[literals_size++] = byte;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
prev[0] = prev[1];
|
|
||||||
prev[1] = byte;
|
|
||||||
if (bits >= 8) {
|
|
||||||
uint8_t outctl = control >> (bits - 8);
|
|
||||||
assert(outctl != 1);
|
|
||||||
write_all(STDOUT_FILENO, &outctl, 1);
|
|
||||||
write_all(STDOUT_FILENO, literals, literals_size);
|
|
||||||
bits -= 8;
|
|
||||||
control &= (1 << bits) - 1;
|
|
||||||
literals_size = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
uint8_t end_byte = 1;
|
|
||||||
write_all(STDOUT_FILENO, &end_byte, 1);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
@ -1,2 +0,0 @@
|
|||||||
SGB2 EQU 1
|
|
||||||
include "sgb_boot.asm"
|
|
@ -1,213 +0,0 @@
|
|||||||
; SameBoy SGB bootstrap ROM
|
|
||||||
; Todo: use friendly names for HW registers instead of magic numbers
|
|
||||||
SECTION "BootCode", ROM0[$0]
|
|
||||||
Start:
|
|
||||||
; Init stack pointer
|
|
||||||
ld sp, $fffe
|
|
||||||
|
|
||||||
; Clear memory VRAM
|
|
||||||
ld hl, $8000
|
|
||||||
|
|
||||||
.clearVRAMLoop
|
|
||||||
ldi [hl], a
|
|
||||||
bit 5, h
|
|
||||||
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, $77
|
|
||||||
ldh [$24], a
|
|
||||||
|
|
||||||
; Init BG palette to white
|
|
||||||
ld a, $0
|
|
||||||
ldh [$47], 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
|
|
||||||
|
|
||||||
.loadLogoLoop
|
|
||||||
ld a, [de] ; Read 2 rows
|
|
||||||
ld b, a
|
|
||||||
call DoubleBitsAndWriteRow
|
|
||||||
call DoubleBitsAndWriteRow
|
|
||||||
inc de
|
|
||||||
ld a, e
|
|
||||||
xor $34 ; End of logo
|
|
||||||
jr nz, .loadLogoLoop
|
|
||||||
|
|
||||||
; Load trademark symbol
|
|
||||||
ld de, TrademarkSymbol
|
|
||||||
ld c,$08
|
|
||||||
.loadTrademarkSymbolLoop:
|
|
||||||
ld a,[de]
|
|
||||||
inc de
|
|
||||||
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
|
|
||||||
.tilemapLoop
|
|
||||||
dec a
|
|
||||||
jr z, .tilemapDone
|
|
||||||
ldd [hl], a
|
|
||||||
dec c
|
|
||||||
jr nz, .tilemapLoop
|
|
||||||
ld l,$0f ; Jump to top row
|
|
||||||
jr .tilemapLoop
|
|
||||||
.tilemapDone
|
|
||||||
|
|
||||||
; Turn on LCD
|
|
||||||
ld a, $91
|
|
||||||
ldh [$40], a
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
.sendCommand
|
|
||||||
xor a
|
|
||||||
ld [c], a
|
|
||||||
ld a, $30
|
|
||||||
ld [c], a
|
|
||||||
|
|
||||||
ldh a, [$80]
|
|
||||||
call SendByte
|
|
||||||
push hl
|
|
||||||
ld b, $e
|
|
||||||
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
|
|
||||||
.sendLoop
|
|
||||||
call ReadHeaderByte
|
|
||||||
call SendByte
|
|
||||||
dec b
|
|
||||||
jr nz, .sendLoop
|
|
||||||
|
|
||||||
; Done bit
|
|
||||||
ld a, $20
|
|
||||||
ld [c], a
|
|
||||||
ld a, $30
|
|
||||||
ld [c], a
|
|
||||||
|
|
||||||
; Update command
|
|
||||||
ldh a, [$80]
|
|
||||||
add 2
|
|
||||||
ldh [$80], a
|
|
||||||
|
|
||||||
ld a, $58
|
|
||||||
cp l
|
|
||||||
jr nz, .sendCommand
|
|
||||||
|
|
||||||
; Write to sound registers for DMG compatibility
|
|
||||||
ld c, $13
|
|
||||||
ld a, $c1
|
|
||||||
ld [c], a
|
|
||||||
inc c
|
|
||||||
ld a, 7
|
|
||||||
ld [c], a
|
|
||||||
|
|
||||||
; Init BG palette
|
|
||||||
ld a, $fc
|
|
||||||
ldh [$47], a
|
|
||||||
|
|
||||||
; Set registers to match the original SGB boot
|
|
||||||
IF DEF(SGB2)
|
|
||||||
ld a, $FF
|
|
||||||
ELSE
|
|
||||||
ld a, 1
|
|
||||||
ENDC
|
|
||||||
ld hl, $c060
|
|
||||||
|
|
||||||
; Boot the game
|
|
||||||
jp BootGame
|
|
||||||
|
|
||||||
ReadHeaderByte:
|
|
||||||
ld a, $4F
|
|
||||||
cp l
|
|
||||||
jr c, .zero
|
|
||||||
ld a, [hli]
|
|
||||||
ret
|
|
||||||
.zero:
|
|
||||||
inc hl
|
|
||||||
xor a
|
|
||||||
ret
|
|
||||||
|
|
||||||
SendByte:
|
|
||||||
ld e, a
|
|
||||||
ld d, 8
|
|
||||||
.loop
|
|
||||||
ld a, $10
|
|
||||||
rr e
|
|
||||||
jr c, .zeroBit
|
|
||||||
add a ; 10 -> 20
|
|
||||||
.zeroBit
|
|
||||||
ld [c], a
|
|
||||||
ld a, $30
|
|
||||||
ld [c], a
|
|
||||||
dec d
|
|
||||||
ret z
|
|
||||||
jr .loop
|
|
||||||
|
|
||||||
DoubleBitsAndWriteRow:
|
|
||||||
; Double the most significant 4 bits, b is shifted by 4
|
|
||||||
ld a, 4
|
|
||||||
ld c, 0
|
|
||||||
.doubleCurrentBit
|
|
||||||
sla b
|
|
||||||
push af
|
|
||||||
rl c
|
|
||||||
pop af
|
|
||||||
rl c
|
|
||||||
dec a
|
|
||||||
jr nz, .doubleCurrentBit
|
|
||||||
ld a, c
|
|
||||||
; Write as two rows
|
|
||||||
ldi [hl], a
|
|
||||||
inc hl
|
|
||||||
ldi [hl], a
|
|
||||||
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
|
|
||||||
|
|
||||||
SECTION "BootGame", ROM0[$fe]
|
|
||||||
BootGame:
|
|
||||||
ldh [$50], a
|
|
@ -1,79 +0,0 @@
|
|||||||
# SameBoy Coding and Contribution Guidelines
|
|
||||||
|
|
||||||
## Issues
|
|
||||||
|
|
||||||
GitHub Issues are the most effective way to report a bug or request a feature in SameBoy. When reporting a bug, make sure you use the latest stable release, and make sure you mention the SameBoy frontend (Cocoa, SDL, Libretro) and operating system you're using. If you're using Linux/BSD/etc, or you build your own copy of SameBoy for another reason, give as much details as possible on your environment.
|
|
||||||
|
|
||||||
If your bug involves a crash, please attach a crash log or a core dump. If you're using Linux/BSD/etc, or if you're using the Libretro core, please attach the `sameboy` binary (or `libretro_sameboy` library) in that case.
|
|
||||||
|
|
||||||
If your bug is a regression, it'd be extremely helpful if you can report the the first affected version. You get extra credits if you use `git bisect` to point the exact breaking commit.
|
|
||||||
|
|
||||||
If your bug is an emulation bug (Such as a failing test ROM), and you have access to a Game Boy you can test on, please confirm SameBoy is indeed behaving differently from hardware, and report both the emulated model and revision in SameBoy, and the hardware revision you're testing on.
|
|
||||||
|
|
||||||
If your issue is a feature request, demonstrating use cases can help me better prioritize it.
|
|
||||||
|
|
||||||
## Pull Requests
|
|
||||||
|
|
||||||
To allow quicker integration into SameBoy's master branch, contributors are asked to follow SameBoy's style and coding guidelines. Keep in mind that despite the seemingly strict guidelines, all pull requests are welcome – not following the guidelines does not mean your pull request will not be accepted, but it will require manual tweaks from my side for integrating.
|
|
||||||
|
|
||||||
### Languages and Compilers
|
|
||||||
|
|
||||||
SameBoy's core, SDL frontend, Libretro frontend, and automatic tester (Folders `Core`, `SDL` & `OpenDialog`, `libretro`, and `Tester`; respectively) are all written in C11. The Cocoa frontend, SameBoy's fork of Hex Fiend, JoyKit and the Quick Look previewer (Folders `Cocoa`, `HexFiend`, `JoyKit` and `QuickLook`; respectively) are all written in ARC-enabled Objective-C. The SameBoot ROMs (Under `BootROMs`) are written in rgbds-flavor SM83 assembly, with build tools in C11. The shaders (inside `Shaders`) are written in a polyglot GLSL and Metal style, with a few GLSL- and Metal-specific sources. The build system uses standalone Make, in the GNU flavor. Avoid adding new languages (C++, Swift, Python, CMake...) to any of the existing sub-projects.
|
|
||||||
|
|
||||||
SameBoy's main target compiler is Clang, but GCC is also supported when targeting Linux and Libretro. Other compilers (e.g. MSVC) are not supported, and unless there's a good reason, there's no need to go out of your way to add specific support for them. Extensions that are supported by both compilers (Such as `typeof`) may be used if it makes sense. It's OK if you can't test one of these compilers yourself; once you push a commit, the CI bot will let you know if you broke something.
|
|
||||||
|
|
||||||
### 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.
|
|
||||||
|
|
||||||
### Spacing, Indentation and Formatting
|
|
||||||
|
|
||||||
In all files and languages (Other than Makefiles when required), 4 spaces are used for indentation. Unix line endings (`\n`) are used exclusively, even in Windows-specific source files. (`\r` and `\t` shouldn't appear in any source file). Opening braces belong on the same line as their control flow directive, and on their own line when following a function prototype. The `else` keyword always starts on its own line. The `case` keyword is indented relative to its `switch` block, and the code inside a `case` is indented relative to its label. A control flow keyword should have a space between it and the following `(`, commas should follow a space, and operator (except `.` and `->`) should be surrounded by spaces.
|
|
||||||
|
|
||||||
Control flow statements must use `{}`, with the exception of `if` statements that only contain a single `break`, `continue`, or trivial `return` statements. If `{}`s are omitted, the statement must be on the same line as the `if` condition. Functions that do not have any argument must be specified as `(void)`, as mandated by the C standard. The `sizeof` and `typeof` operators should be used as if they're functions (With `()`). `*`, when used to declare pointer types (including functions that return pointers), and when used to dereference a pointer, is attached to the right side (The variable name) – not to the left, and not with spaces on both sides.
|
|
||||||
|
|
||||||
No strict limitations on a line's maximum width, but use your best judgement if you think a statement would benefit from an additional line break.
|
|
||||||
|
|
||||||
Well formatted code example:
|
|
||||||
|
|
||||||
```
|
|
||||||
static void my_function(void)
|
|
||||||
{
|
|
||||||
GB_something_t *thing = GB_function(&gb, GB_FLAG_ONE | GB_FLAG_TWO, sizeof(thing));
|
|
||||||
if (GB_is_thing(thing)) return;
|
|
||||||
|
|
||||||
switch (*thing) {
|
|
||||||
case GB_QUACK:
|
|
||||||
// Something
|
|
||||||
case GB_DUCK:
|
|
||||||
// Something else
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Badly formatted code example:
|
|
||||||
```
|
|
||||||
static void my_function(){
|
|
||||||
GB_something_t* thing=GB_function(&gb , GB_FLAG_ONE|GB_FLAG_TWO , sizeof thing);
|
|
||||||
if( GB_is_thing ( thing ) )
|
|
||||||
return;
|
|
||||||
|
|
||||||
switch(* thing)
|
|
||||||
{
|
|
||||||
case GB_QUACK:
|
|
||||||
// Something
|
|
||||||
case GB_DUCK:
|
|
||||||
// Something else
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Other Coding Conventions
|
|
||||||
|
|
||||||
The primitive types to be used in SameBoy are `unsigned` and `signed` (Without the `int` keyword), the `(u)int*_t` types, `char *` for UTF-8 strings, `double` for non-integer numbers, and `bool` for booleans (Including in Objective-C code, avoid `BOOL`). As long as it's not mandated by a 3rd-party API (e.g. `int` when using file descriptors), avoid using other primitive types. Use `const` whenever possible.
|
|
||||||
|
|
||||||
Most C names should be `lower_case_snake_case`. Constants and macros use `UPPER_CASE_SNAKE_CASE`. Type definitions use a `_t` suffix. Type definitions, as well as non-static (exported) core symbols, should be prefixed with `GB_` (SameBoy's core is intended to be used as a library, so it shouldn't contaminate the global namespace without prefixes). Exported symbols that are only meant to be used by other parts of the core should still get the `GB_` prefix, but their header definition should be inside `#ifdef GB_INTERNAL`.
|
|
||||||
|
|
||||||
For Objective-C naming conventions, use Apple's conventions (Some old Objective-C code mixes these with the C naming convention; new code should use Apple's convention exclusively). The name prefix for SameBoy classes and constants is `GB`. JoyKit's prefix is `JOY`, and Hex Fiend's prefix is `HF`.
|
|
||||||
|
|
||||||
In all languages, prefer long, unambiguous names over short ambiguous ones.
|
|
@ -1,25 +1,10 @@
|
|||||||
#import <Cocoa/Cocoa.h>
|
#import <Cocoa/Cocoa.h>
|
||||||
#import <WebKit/WebKit.h>
|
|
||||||
|
|
||||||
@interface AppDelegate : NSObject <NSApplicationDelegate, NSUserNotificationCenterDelegate, NSMenuDelegate, WebUIDelegate, WebPolicyDelegate, WebFrameLoadDelegate>
|
@interface AppDelegate : NSObject <NSApplicationDelegate>
|
||||||
|
|
||||||
@property (nonatomic, strong) IBOutlet NSWindow *preferencesWindow;
|
@property IBOutlet NSWindow *preferencesWindow;
|
||||||
@property (nonatomic, strong) IBOutlet NSView *graphicsTab;
|
|
||||||
@property (nonatomic, strong) IBOutlet NSView *emulationTab;
|
|
||||||
@property (nonatomic, strong) IBOutlet NSView *audioTab;
|
|
||||||
@property (nonatomic, strong) IBOutlet NSView *controlsTab;
|
|
||||||
@property (nonatomic, strong) IBOutlet NSView *updatesTab;
|
|
||||||
- (IBAction)showPreferences: (id) sender;
|
- (IBAction)showPreferences: (id) sender;
|
||||||
- (IBAction)toggleDeveloperMode:(id)sender;
|
- (IBAction)toggleDeveloperMode:(id)sender;
|
||||||
- (IBAction)switchPreferencesTab:(id)sender;
|
|
||||||
@property (nonatomic, weak) IBOutlet NSMenuItem *linkCableMenuItem;
|
|
||||||
@property (nonatomic, strong) IBOutlet NSWindow *updateWindow;
|
|
||||||
@property (nonatomic, strong) IBOutlet WebView *updateChanges;
|
|
||||||
@property (nonatomic, strong) IBOutlet NSProgressIndicator *updatesSpinner;
|
|
||||||
@property (strong) IBOutlet NSButton *updatesButton;
|
|
||||||
@property (strong) IBOutlet NSTextField *updateProgressLabel;
|
|
||||||
@property (strong) IBOutlet NSButton *updateProgressButton;
|
|
||||||
@property (strong) IBOutlet NSWindow *updateProgressWindow;
|
|
||||||
@property (strong) IBOutlet NSProgressIndicator *updateProgressSpinner;
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
@ -1,44 +1,19 @@
|
|||||||
#import "AppDelegate.h"
|
#import "AppDelegate.h"
|
||||||
#include "GBButtons.h"
|
#include "GBButtons.h"
|
||||||
#include "GBView.h"
|
|
||||||
#include <Core/gb.h>
|
#include <Core/gb.h>
|
||||||
#import <Carbon/Carbon.h>
|
#import <Carbon/Carbon.h>
|
||||||
#import <JoyKit/JoyKit.h>
|
|
||||||
#import <WebKit/WebKit.h>
|
|
||||||
|
|
||||||
#define UPDATE_SERVER "https://sameboy.github.io"
|
|
||||||
|
|
||||||
static uint32_t color_to_int(NSColor *color)
|
|
||||||
{
|
|
||||||
color = [color colorUsingColorSpace:[NSColorSpace deviceRGBColorSpace]];
|
|
||||||
return (((unsigned)(color.redComponent * 0xFF)) << 16) |
|
|
||||||
(((unsigned)(color.greenComponent * 0xFF)) << 8) |
|
|
||||||
((unsigned)(color.blueComponent * 0xFF));
|
|
||||||
}
|
|
||||||
|
|
||||||
@implementation AppDelegate
|
@implementation AppDelegate
|
||||||
{
|
{
|
||||||
NSWindow *preferences_window;
|
NSWindow *preferences_window;
|
||||||
NSArray<NSView *> *preferences_tabs;
|
|
||||||
NSString *_lastVersion;
|
|
||||||
NSString *_updateURL;
|
|
||||||
NSURLSessionDownloadTask *_updateTask;
|
|
||||||
enum {
|
|
||||||
UPDATE_DOWNLOADING,
|
|
||||||
UPDATE_EXTRACTING,
|
|
||||||
UPDATE_WAIT_INSTALL,
|
|
||||||
UPDATE_INSTALLING,
|
|
||||||
UPDATE_FAILED,
|
|
||||||
} _updateState;
|
|
||||||
NSString *_downloadDirectory;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) applicationDidFinishLaunching:(NSNotification *)notification
|
- (void) applicationDidFinishLaunching:(NSNotification *)notification
|
||||||
{
|
{
|
||||||
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
||||||
for (unsigned i = 0; i < GBButtonCount; i++) {
|
for (unsigned i = 0; i < GBButtonCount; i++) {
|
||||||
if ([[defaults objectForKey:button_to_preference_name(i, 0)] isKindOfClass:[NSString class]]) {
|
if ([[defaults objectForKey:button_to_preference_name(i)] isKindOfClass:[NSString class]]) {
|
||||||
[defaults removeObjectForKey:button_to_preference_name(i, 0)];
|
[defaults removeObjectForKey:button_to_preference_name(i)];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
[[NSUserDefaults standardUserDefaults] registerDefaults:@{
|
[[NSUserDefaults standardUserDefaults] registerDefaults:@{
|
||||||
@ -59,38 +34,8 @@ static uint32_t color_to_int(NSColor *color)
|
|||||||
@"GBFilter": @"NearestNeighbor",
|
@"GBFilter": @"NearestNeighbor",
|
||||||
@"GBColorCorrection": @(GB_COLOR_CORRECTION_EMULATE_HARDWARE),
|
@"GBColorCorrection": @(GB_COLOR_CORRECTION_EMULATE_HARDWARE),
|
||||||
@"GBHighpassFilter": @(GB_HIGHPASS_REMOVE_DC_OFFSET),
|
@"GBHighpassFilter": @(GB_HIGHPASS_REMOVE_DC_OFFSET),
|
||||||
@"GBRewindLength": @(10),
|
@"GBRewindLength": @(10)
|
||||||
@"GBFrameBlendingMode": @([defaults boolForKey:@"DisableFrameBlending"]? GB_FRAME_BLENDING_MODE_DISABLED : GB_FRAME_BLENDING_MODE_ACCURATE),
|
|
||||||
|
|
||||||
@"GBDMGModel": @(GB_MODEL_DMG_B),
|
|
||||||
@"GBCGBModel": @(GB_MODEL_CGB_E),
|
|
||||||
@"GBSGBModel": @(GB_MODEL_SGB2),
|
|
||||||
@"GBRumbleMode": @(GB_RUMBLE_CARTRIDGE_ONLY),
|
|
||||||
|
|
||||||
@"GBVolume": @(1.0),
|
|
||||||
|
|
||||||
@"GBMBC7JoystickOverride": @NO,
|
|
||||||
@"GBMBC7AllowMouse": @YES,
|
|
||||||
}];
|
}];
|
||||||
|
|
||||||
[JOYController startOnRunLoop:[NSRunLoop currentRunLoop] withOptions:@{
|
|
||||||
JOYAxes2DEmulateButtonsKey: @YES,
|
|
||||||
JOYHatsEmulateButtonsKey: @YES,
|
|
||||||
}];
|
|
||||||
|
|
||||||
if ([[NSUserDefaults standardUserDefaults] boolForKey:@"GBNotificationsUsed"]) {
|
|
||||||
[NSUserNotificationCenter defaultUserNotificationCenter].delegate = self;
|
|
||||||
}
|
|
||||||
|
|
||||||
[self askAutoUpdates];
|
|
||||||
|
|
||||||
if ([[NSUserDefaults standardUserDefaults] boolForKey:@"GBAutoUpdatesEnabled"]) {
|
|
||||||
[self checkForUpdates];
|
|
||||||
}
|
|
||||||
|
|
||||||
if ([[NSProcessInfo processInfo].arguments containsObject:@"--update-launch"]) {
|
|
||||||
[NSApp activateIgnoringOtherApps:true];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)toggleDeveloperMode:(id)sender
|
- (IBAction)toggleDeveloperMode:(id)sender
|
||||||
@ -99,355 +44,28 @@ static uint32_t color_to_int(NSColor *color)
|
|||||||
[defaults setBool:![defaults boolForKey:@"DeveloperMode"] forKey:@"DeveloperMode"];
|
[defaults setBool:![defaults boolForKey:@"DeveloperMode"] forKey:@"DeveloperMode"];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)switchPreferencesTab:(id)sender
|
|
||||||
{
|
|
||||||
for (NSView *view in preferences_tabs) {
|
|
||||||
[view removeFromSuperview];
|
|
||||||
}
|
|
||||||
NSView *tab = preferences_tabs[[sender tag]];
|
|
||||||
NSRect old = [_preferencesWindow frame];
|
|
||||||
NSRect new = [_preferencesWindow frameRectForContentRect:tab.frame];
|
|
||||||
new.origin.x = old.origin.x;
|
|
||||||
new.origin.y = old.origin.y + (old.size.height - new.size.height);
|
|
||||||
[_preferencesWindow setFrame:new display:true animate:_preferencesWindow.visible];
|
|
||||||
[_preferencesWindow.contentView addSubview:tab];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)validateMenuItem:(NSMenuItem *)anItem
|
- (BOOL)validateMenuItem:(NSMenuItem *)anItem
|
||||||
{
|
{
|
||||||
if ([anItem action] == @selector(toggleDeveloperMode:)) {
|
if ([anItem action] == @selector(toggleDeveloperMode:)) {
|
||||||
[(NSMenuItem *)anItem setState:[[NSUserDefaults standardUserDefaults] boolForKey:@"DeveloperMode"]];
|
[(NSMenuItem*)anItem setState:[[NSUserDefaults standardUserDefaults] boolForKey:@"DeveloperMode"]];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (anItem == self.linkCableMenuItem) {
|
|
||||||
return [[NSDocumentController sharedDocumentController] documents].count > 1;
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)menuNeedsUpdate:(NSMenu *)menu
|
|
||||||
{
|
|
||||||
NSMutableArray *items = [NSMutableArray array];
|
|
||||||
NSDocument *currentDocument = [[NSDocumentController sharedDocumentController] currentDocument];
|
|
||||||
|
|
||||||
for (NSDocument *document in [[NSDocumentController sharedDocumentController] documents]) {
|
|
||||||
if (document == currentDocument) continue;
|
|
||||||
NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:document.displayName action:@selector(connectLinkCable:) keyEquivalent:@""];
|
|
||||||
item.representedObject = document;
|
|
||||||
item.image = [[NSWorkspace sharedWorkspace] iconForFile:document.fileURL.path];
|
|
||||||
[item.image setSize:NSMakeSize(16, 16)];
|
|
||||||
[items addObject:item];
|
|
||||||
}
|
|
||||||
menu.itemArray = items;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction) showPreferences: (id) sender
|
- (IBAction) showPreferences: (id) sender
|
||||||
{
|
{
|
||||||
NSArray *objects;
|
NSArray *objects;
|
||||||
if (!_preferencesWindow) {
|
if (!_preferencesWindow) {
|
||||||
[[NSBundle mainBundle] loadNibNamed:@"Preferences" owner:self topLevelObjects:&objects];
|
[[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];
|
|
||||||
[self switchPreferencesTab:first_toolbar_item];
|
|
||||||
[_preferencesWindow center];
|
|
||||||
#ifndef UPDATE_SUPPORT
|
|
||||||
[_preferencesWindow.toolbar removeItemAtIndex:4];
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
[_preferencesWindow makeKeyAndOrderFront:self];
|
[_preferencesWindow makeKeyAndOrderFront:self];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)applicationOpenUntitledFile:(NSApplication *)sender
|
- (BOOL)applicationOpenUntitledFile:(NSApplication *)sender
|
||||||
{
|
{
|
||||||
[self askAutoUpdates];
|
|
||||||
/* Bring an existing panel to the foreground */
|
|
||||||
for (NSWindow *window in [[NSApplication sharedApplication] windows]) {
|
|
||||||
if ([window isKindOfClass:[NSOpenPanel class]]) {
|
|
||||||
[(NSOpenPanel *)window makeKeyAndOrderFront:nil];
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
[[NSDocumentController sharedDocumentController] openDocument:self];
|
[[NSDocumentController sharedDocumentController] openDocument:self];
|
||||||
return true;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)userNotificationCenter:(NSUserNotificationCenter *)center didActivateNotification:(NSUserNotification *)notification
|
|
||||||
{
|
|
||||||
[[NSDocumentController sharedDocumentController] openDocumentWithContentsOfFile:notification.identifier display:true];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)updateFound
|
|
||||||
{
|
|
||||||
[[[NSURLSession sharedSession] dataTaskWithURL:[NSURL URLWithString:@UPDATE_SERVER "/raw_changes"] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
|
|
||||||
|
|
||||||
NSColor *linkColor = [NSColor colorWithRed:0.125 green:0.325 blue:1.0 alpha:1.0];
|
|
||||||
if (@available(macOS 10.10, *)) {
|
|
||||||
linkColor = [NSColor linkColor];
|
|
||||||
}
|
|
||||||
|
|
||||||
NSString *changes = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
|
||||||
NSRange cutoffRange = [changes rangeOfString:@"<!--(" GB_VERSION ")-->"];
|
|
||||||
if (cutoffRange.location != NSNotFound) {
|
|
||||||
changes = [changes substringToIndex:cutoffRange.location];
|
|
||||||
}
|
|
||||||
|
|
||||||
NSString *html = [NSString stringWithFormat:@"<!DOCTYPE html><html><head><title></title>"
|
|
||||||
"<style>html {background-color:transparent; color: #%06x; line-height:1.5} a:link, a:visited{color:#%06x; text-decoration:none}</style>"
|
|
||||||
"</head><body>%@</body></html>",
|
|
||||||
color_to_int([NSColor textColor]),
|
|
||||||
color_to_int(linkColor),
|
|
||||||
changes];
|
|
||||||
|
|
||||||
if ([(NSHTTPURLResponse *)response statusCode] == 200) {
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
|
||||||
NSArray *objects;
|
|
||||||
[[NSBundle mainBundle] loadNibNamed:@"UpdateWindow" owner:self topLevelObjects:&objects];
|
|
||||||
self.updateChanges.preferences.standardFontFamily = [NSFont systemFontOfSize:0].familyName;
|
|
||||||
self.updateChanges.preferences.fixedFontFamily = @"Menlo";
|
|
||||||
self.updateChanges.drawsBackground = false;
|
|
||||||
[self.updateChanges.mainFrame loadHTMLString:html baseURL:nil];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}] resume];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSArray *)webView:(WebView *)sender contextMenuItemsForElement:(NSDictionary *)element defaultMenuItems:(NSArray *)defaultMenuItems
|
|
||||||
{
|
|
||||||
// Disable reload context menu
|
|
||||||
if ([defaultMenuItems count] <= 2) {
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
return defaultMenuItems;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame
|
|
||||||
{
|
|
||||||
static dispatch_once_t onceToken;
|
|
||||||
dispatch_once(&onceToken, ^{
|
|
||||||
sender.mainFrame.frameView.documentView.enclosingScrollView.drawsBackground = true;
|
|
||||||
sender.mainFrame.frameView.documentView.enclosingScrollView.backgroundColor = [NSColor textBackgroundColor];
|
|
||||||
sender.policyDelegate = self;
|
|
||||||
[self.updateWindow center];
|
|
||||||
[self.updateWindow makeKeyAndOrderFront:nil];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)webView:(WebView *)webView decidePolicyForNavigationAction:(NSDictionary *)actionInformation request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener
|
|
||||||
{
|
|
||||||
[listener ignore];
|
|
||||||
[[NSWorkspace sharedWorkspace] openURL:[request URL]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)checkForUpdates
|
|
||||||
{
|
|
||||||
#ifdef UPDATE_SUPPORT
|
|
||||||
[[[NSURLSession sharedSession] dataTaskWithURL:[NSURL URLWithString:@UPDATE_SERVER "/latest_version"] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
|
||||||
[self.updatesSpinner stopAnimation:nil];
|
|
||||||
[self.updatesButton setEnabled:true];
|
|
||||||
});
|
|
||||||
if ([(NSHTTPURLResponse *)response statusCode] == 200) {
|
|
||||||
NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
|
||||||
NSArray <NSString *> *components = [string componentsSeparatedByString:@"|"];
|
|
||||||
if (components.count != 2) return;
|
|
||||||
_lastVersion = components[0];
|
|
||||||
_updateURL = components[1];
|
|
||||||
if (![@GB_VERSION isEqualToString:_lastVersion] &&
|
|
||||||
![[[NSUserDefaults standardUserDefaults] stringForKey:@"GBSkippedVersion"] isEqualToString:_lastVersion]) {
|
|
||||||
[self updateFound];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}] resume];
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)userCheckForUpdates:(id)sender
|
|
||||||
{
|
|
||||||
if (self.updateWindow) {
|
|
||||||
[self.updateWindow makeKeyAndOrderFront:sender];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
[[NSUserDefaults standardUserDefaults] setObject:nil forKey:@"GBSkippedVersion"];
|
|
||||||
[self checkForUpdates];
|
|
||||||
[sender setEnabled:false];
|
|
||||||
[self.updatesSpinner startAnimation:sender];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)askAutoUpdates
|
|
||||||
{
|
|
||||||
#ifdef UPDATE_SUPPORT
|
|
||||||
if (![[NSUserDefaults standardUserDefaults] boolForKey:@"GBAskedAutoUpdates"]) {
|
|
||||||
NSAlert *alert = [[NSAlert alloc] init];
|
|
||||||
alert.messageText = @"Should SameBoy check for updates when launched?";
|
|
||||||
alert.informativeText = @"SameBoy is frequently updated with new features, accuracy improvements, and bug fixes. This setting can always be changed in the preferences window.";
|
|
||||||
[alert addButtonWithTitle:@"Check on Launch"];
|
|
||||||
[alert addButtonWithTitle:@"Don't Check on Launch"];
|
|
||||||
|
|
||||||
[[NSUserDefaults standardUserDefaults] setBool:[alert runModal] == NSAlertFirstButtonReturn forKey:@"GBAutoUpdatesEnabled"];
|
|
||||||
[[NSUserDefaults standardUserDefaults] setBool:true forKey:@"GBAskedAutoUpdates"];
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)skipVersion:(id)sender
|
|
||||||
{
|
|
||||||
[[NSUserDefaults standardUserDefaults] setObject:_lastVersion forKey:@"GBSkippedVersion"];
|
|
||||||
[self.updateWindow performClose:sender];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)installUpdate:(id)sender
|
|
||||||
{
|
|
||||||
[self.updateProgressSpinner startAnimation:nil];
|
|
||||||
self.updateProgressButton.title = @"Cancel";
|
|
||||||
self.updateProgressButton.enabled = true;
|
|
||||||
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...";
|
|
||||||
_updateState = UPDATE_EXTRACTING;
|
|
||||||
});
|
|
||||||
|
|
||||||
_downloadDirectory = [[[NSFileManager defaultManager] URLForDirectory:NSItemReplacementDirectory
|
|
||||||
inDomain:NSUserDomainMask
|
|
||||||
appropriateForURL:[[NSBundle mainBundle] bundleURL]
|
|
||||||
create:true
|
|
||||||
error:nil] path];
|
|
||||||
NSTask *unzipTask;
|
|
||||||
if (!_downloadDirectory) {
|
|
||||||
dispatch_sync(dispatch_get_main_queue(), ^{
|
|
||||||
self.updateProgressButton.enabled = false;
|
|
||||||
self.updateProgressLabel.stringValue = @"Failed to extract update.";
|
|
||||||
_updateState = UPDATE_FAILED;
|
|
||||||
self.updateProgressButton.title = @"Close";
|
|
||||||
self.updateProgressButton.enabled = true;
|
|
||||||
[self.updateProgressSpinner stopAnimation:nil];
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
|
||||||
[[NSFileManager defaultManager] removeItemAtPath:_downloadDirectory error:nil];
|
|
||||||
dispatch_sync(dispatch_get_main_queue(), ^{
|
|
||||||
self.updateProgressButton.enabled = false;
|
|
||||||
self.updateProgressLabel.stringValue = @"Failed to extract update.";
|
|
||||||
_updateState = UPDATE_FAILED;
|
|
||||||
self.updateProgressButton.title = @"Close";
|
|
||||||
self.updateProgressButton.enabled = true;
|
|
||||||
[self.updateProgressSpinner stopAnimation:nil];
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatch_sync(dispatch_get_main_queue(), ^{
|
|
||||||
self.updateProgressButton.enabled = false;
|
|
||||||
self.updateProgressLabel.stringValue = @"Update ready, save your game progress and click Install.";
|
|
||||||
_updateState = UPDATE_WAIT_INSTALL;
|
|
||||||
self.updateProgressButton.title = @"Install";
|
|
||||||
self.updateProgressButton.enabled = true;
|
|
||||||
[self.updateProgressSpinner stopAnimation:nil];
|
|
||||||
});
|
|
||||||
}];
|
|
||||||
[_updateTask resume];
|
|
||||||
|
|
||||||
self.updateProgressWindow.preventsApplicationTerminationWhenModal = false;
|
|
||||||
[self.updateWindow beginSheet:self.updateProgressWindow completionHandler:^(NSModalResponse returnCode) {
|
|
||||||
[self.updateWindow close];
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)performUpgrade
|
|
||||||
{
|
|
||||||
self.updateProgressButton.enabled = false;
|
|
||||||
self.updateProgressLabel.stringValue = @"Instaling update...";
|
|
||||||
_updateState = UPDATE_INSTALLING;
|
|
||||||
self.updateProgressButton.enabled = false;
|
|
||||||
[self.updateProgressSpinner startAnimation:nil];
|
|
||||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
|
||||||
NSString *executablePath = [[NSBundle mainBundle] executablePath];
|
|
||||||
NSString *contentsPath = [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:@"Contents"];
|
|
||||||
NSString *contentsTempPath = [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:@"TempContents"];
|
|
||||||
NSString *updateContentsPath = [_downloadDirectory stringByAppendingPathComponent:@"SameBoy.app/Contents"];
|
|
||||||
NSError *error = nil;
|
|
||||||
[[NSFileManager defaultManager] moveItemAtPath:contentsPath toPath:contentsTempPath error:&error];
|
|
||||||
if (error) {
|
|
||||||
[[NSFileManager defaultManager] removeItemAtPath:_downloadDirectory error:nil];
|
|
||||||
_downloadDirectory = nil;
|
|
||||||
dispatch_sync(dispatch_get_main_queue(), ^{
|
|
||||||
self.updateProgressButton.enabled = false;
|
|
||||||
self.updateProgressLabel.stringValue = @"Failed to install update.";
|
|
||||||
_updateState = UPDATE_FAILED;
|
|
||||||
self.updateProgressButton.title = @"Close";
|
|
||||||
self.updateProgressButton.enabled = true;
|
|
||||||
[self.updateProgressSpinner stopAnimation:nil];
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
[[NSFileManager defaultManager] moveItemAtPath:updateContentsPath toPath:contentsPath error:&error];
|
|
||||||
if (error) {
|
|
||||||
[[NSFileManager defaultManager] moveItemAtPath:contentsTempPath toPath:contentsPath error:nil];
|
|
||||||
[[NSFileManager defaultManager] removeItemAtPath:_downloadDirectory error:nil];
|
|
||||||
_downloadDirectory = nil;
|
|
||||||
dispatch_sync(dispatch_get_main_queue(), ^{
|
|
||||||
self.updateProgressButton.enabled = false;
|
|
||||||
self.updateProgressLabel.stringValue = @"Failed to install update.";
|
|
||||||
_updateState = UPDATE_FAILED;
|
|
||||||
self.updateProgressButton.title = @"Close";
|
|
||||||
self.updateProgressButton.enabled = true;
|
|
||||||
[self.updateProgressSpinner stopAnimation:nil];
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
[[NSFileManager defaultManager] removeItemAtPath:_downloadDirectory error:nil];
|
|
||||||
[[NSFileManager defaultManager] removeItemAtPath:contentsTempPath error:nil];
|
|
||||||
_downloadDirectory = nil;
|
|
||||||
atexit_b(^{
|
|
||||||
execl(executablePath.UTF8String, executablePath.UTF8String, "--update-launch", NULL);
|
|
||||||
});
|
|
||||||
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
|
||||||
[NSApp terminate:nil];
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)updateAction:(id)sender
|
|
||||||
{
|
|
||||||
switch (_updateState) {
|
|
||||||
case UPDATE_DOWNLOADING:
|
|
||||||
[_updateTask cancelByProducingResumeData:nil];
|
|
||||||
_updateTask = nil;
|
|
||||||
[self.updateProgressWindow close];
|
|
||||||
break;
|
|
||||||
case UPDATE_WAIT_INSTALL:
|
|
||||||
[self performUpgrade];
|
|
||||||
break;
|
|
||||||
case UPDATE_EXTRACTING:
|
|
||||||
case UPDATE_INSTALLING:
|
|
||||||
break;
|
|
||||||
case UPDATE_FAILED:
|
|
||||||
[self.updateProgressWindow close];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)dealloc
|
|
||||||
{
|
|
||||||
if (_downloadDirectory) {
|
|
||||||
[[NSFileManager defaultManager] removeItemAtPath:_downloadDirectory error:nil];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)nop:(id)sender
|
|
||||||
{
|
|
||||||
}
|
|
||||||
@end
|
@end
|
||||||
|
@ -1,30 +0,0 @@
|
|||||||
#import <Cocoa/Cocoa.h>
|
|
||||||
#ifndef BigSurToolbar_h
|
|
||||||
#define BigSurToolbar_h
|
|
||||||
|
|
||||||
/* Backport the toolbarStyle property to allow compilation with older SDKs*/
|
|
||||||
#ifndef __MAC_10_16
|
|
||||||
typedef NS_ENUM(NSInteger, NSWindowToolbarStyle) {
|
|
||||||
// The default value. The style will be determined by the window's given configuration
|
|
||||||
NSWindowToolbarStyleAutomatic,
|
|
||||||
// The toolbar will appear below the window title
|
|
||||||
NSWindowToolbarStyleExpanded,
|
|
||||||
// The toolbar will appear below the window title and the items in the toolbar will attempt to have equal widths when possible
|
|
||||||
NSWindowToolbarStylePreference,
|
|
||||||
// The window title will appear inline with the toolbar when visible
|
|
||||||
NSWindowToolbarStyleUnified,
|
|
||||||
// Same as NSWindowToolbarStyleUnified, but with reduced margins in the toolbar allowing more focus to be on the contents of the window
|
|
||||||
NSWindowToolbarStyleUnifiedCompact
|
|
||||||
} API_AVAILABLE(macos(11.0));
|
|
||||||
|
|
||||||
@interface NSWindow (toolbarStyle)
|
|
||||||
@property (nonatomic) NSWindowToolbarStyle toolbarStyle API_AVAILABLE(macos(11.0));
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface NSImage (SFSymbols)
|
|
||||||
+ (instancetype)imageWithSystemSymbolName:(NSString *)symbolName accessibilityDescription:(NSString *)description API_AVAILABLE(macos(11.0));
|
|
||||||
@end
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
BIN
Cocoa/CPU@2x.png
Before Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 20 KiB |
@ -1,61 +1,39 @@
|
|||||||
#import <Cocoa/Cocoa.h>
|
#import <Cocoa/Cocoa.h>
|
||||||
#include "GBView.h"
|
#include "GBView.h"
|
||||||
#include "GBImageView.h"
|
#include "GBImageView.h"
|
||||||
#include "GBSplitView.h"
|
|
||||||
#include "GBVisualizerView.h"
|
|
||||||
#include "GBOSDView.h"
|
|
||||||
|
|
||||||
@class GBCheatWindowController;
|
@interface Document : NSDocument <NSWindowDelegate, GBImageViewDelegate, NSTableViewDataSource, NSTableViewDelegate>
|
||||||
|
@property (strong) IBOutlet GBView *view;
|
||||||
@interface Document : NSDocument <NSWindowDelegate, GBImageViewDelegate, NSTableViewDataSource, NSTableViewDelegate, NSSplitViewDelegate>
|
@property (strong) IBOutlet NSTextView *consoleOutput;
|
||||||
@property (nonatomic, readonly) GB_gameboy_t *gb;
|
@property (strong) IBOutlet NSPanel *consoleWindow;
|
||||||
@property (nonatomic, strong) IBOutlet GBView *view;
|
@property (strong) IBOutlet NSTextField *consoleInput;
|
||||||
@property (nonatomic, strong) IBOutlet NSTextView *consoleOutput;
|
@property (strong) IBOutlet NSWindow *mainWindow;
|
||||||
@property (nonatomic, strong) IBOutlet NSPanel *consoleWindow;
|
@property (strong) IBOutlet NSView *memoryView;
|
||||||
@property (nonatomic, strong) IBOutlet NSTextField *consoleInput;
|
@property (strong) IBOutlet NSPanel *memoryWindow;
|
||||||
@property (nonatomic, strong) IBOutlet NSWindow *mainWindow;
|
@property (readonly) GB_gameboy_t *gameboy;
|
||||||
@property (nonatomic, strong) IBOutlet NSView *memoryView;
|
@property (strong) IBOutlet NSTextField *memoryBankInput;
|
||||||
@property (nonatomic, strong) IBOutlet NSPanel *memoryWindow;
|
@property (strong) IBOutlet NSToolbarItem *memoryBankItem;
|
||||||
@property (nonatomic, readonly) GB_gameboy_t *gameboy;
|
@property (strong) IBOutlet GBImageView *tilesetImageView;
|
||||||
@property (nonatomic, strong) IBOutlet NSTextField *memoryBankInput;
|
@property (strong) IBOutlet NSPopUpButton *tilesetPaletteButton;
|
||||||
@property (nonatomic, strong) IBOutlet NSToolbarItem *memoryBankItem;
|
@property (strong) IBOutlet GBImageView *tilemapImageView;
|
||||||
@property (nonatomic, strong) IBOutlet GBImageView *tilesetImageView;
|
@property (strong) IBOutlet NSPopUpButton *tilemapPaletteButton;
|
||||||
@property (nonatomic, strong) IBOutlet NSPopUpButton *tilesetPaletteButton;
|
@property (strong) IBOutlet NSPopUpButton *tilemapMapButton;
|
||||||
@property (nonatomic, strong) IBOutlet GBImageView *tilemapImageView;
|
@property (strong) IBOutlet NSPopUpButton *TilemapSetButton;
|
||||||
@property (nonatomic, strong) IBOutlet NSPopUpButton *tilemapPaletteButton;
|
@property (strong) IBOutlet NSButton *gridButton;
|
||||||
@property (nonatomic, strong) IBOutlet NSPopUpButton *tilemapMapButton;
|
@property (strong) IBOutlet NSTabView *vramTabView;
|
||||||
@property (nonatomic, strong) IBOutlet NSPopUpButton *TilemapSetButton;
|
@property (strong) IBOutlet NSPanel *vramWindow;
|
||||||
@property (nonatomic, strong) IBOutlet NSButton *gridButton;
|
@property (strong) IBOutlet NSTextField *vramStatusLabel;
|
||||||
@property (nonatomic, strong) IBOutlet NSTabView *vramTabView;
|
@property (strong) IBOutlet NSTableView *paletteTableView;
|
||||||
@property (nonatomic, strong) IBOutlet NSPanel *vramWindow;
|
@property (strong) IBOutlet NSTableView *spritesTableView;
|
||||||
@property (nonatomic, strong) IBOutlet NSTextField *vramStatusLabel;
|
@property (strong) IBOutlet NSPanel *printerFeedWindow;
|
||||||
@property (nonatomic, strong) IBOutlet NSTableView *paletteTableView;
|
@property (strong) IBOutlet NSImageView *feedImageView;
|
||||||
@property (nonatomic, strong) IBOutlet NSTableView *objectsTableView;
|
@property (strong) IBOutlet NSButton *feedSaveButton;
|
||||||
@property (nonatomic, strong) IBOutlet NSPanel *printerFeedWindow;
|
@property (strong) IBOutlet NSTextView *debuggerSideViewInput;
|
||||||
@property (nonatomic, strong) IBOutlet NSImageView *feedImageView;
|
@property (strong) IBOutlet NSTextView *debuggerSideView;
|
||||||
@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;
|
|
||||||
|
|
||||||
-(uint8_t) readMemory:(uint16_t) addr;
|
-(uint8_t) readMemory:(uint16_t) addr;
|
||||||
-(void) writeMemory:(uint16_t) addr value:(uint8_t)value;
|
-(void) writeMemory:(uint16_t) addr value:(uint8_t)value;
|
||||||
-(void) performAtomicBlock: (void (^)())block;
|
-(void) performAtomicBlock: (void (^)())block;
|
||||||
-(void) connectLinkCable:(NSMenuItem *)sender;
|
|
||||||
-(int)loadStateFile:(const char *)path noErrorOnNotFound:(bool)noErrorOnFileNotFound;
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
1260
Cocoa/Document.m
@ -1,34 +1,30 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?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="12120" systemVersion="16D32" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" customObjectInstantitationMethod="direct">
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<deployment identifier="macosx"/>
|
<deployment identifier="macosx"/>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14868"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="12120"/>
|
||||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<objects>
|
<objects>
|
||||||
<customObject id="-2" userLabel="File's Owner" customClass="Document">
|
<customObject id="-2" userLabel="File's Owner" customClass="Document">
|
||||||
<connections>
|
<connections>
|
||||||
<outlet property="TilemapSetButton" destination="k4c-Vg-MBu" id="Ak1-6d-B1N"/>
|
<outlet property="TilemapSetButton" destination="k4c-Vg-MBu" id="Ak1-6d-B1N"/>
|
||||||
<outlet property="cheatWindowController" destination="v7q-gT-jHT" id="UNn-g4-2kP"/>
|
|
||||||
<outlet property="cheatsWindow" destination="4Yb-Np-JrF" id="YCZ-cj-gn5"/>
|
|
||||||
<outlet property="consoleInput" destination="l22-S8-uji" id="Heu-am-YgB"/>
|
<outlet property="consoleInput" destination="l22-S8-uji" id="Heu-am-YgB"/>
|
||||||
<outlet property="consoleOutput" destination="doS-dM-hnl" id="Gn5-ju-Wb0"/>
|
<outlet property="consoleOutput" destination="doS-dM-hnl" id="Gn5-ju-Wb0"/>
|
||||||
<outlet property="consoleWindow" destination="21F-Ah-yHX" id="eQ4-ug-LsT"/>
|
<outlet property="consoleWindow" destination="21F-Ah-yHX" id="eQ4-ug-LsT"/>
|
||||||
<outlet property="debuggerSideView" destination="JgV-7E-iwp" id="RaA-fw-3i1"/>
|
<outlet property="debuggerSideView" destination="JgV-7E-iwp" id="RaA-fw-3i1"/>
|
||||||
<outlet property="debuggerSideViewInput" destination="w0g-eK-jM4" id="GBf-WK-ryI"/>
|
<outlet property="debuggerSideViewInput" destination="w0g-eK-jM4" id="GBf-WK-ryI"/>
|
||||||
<outlet property="debuggerSplitView" destination="pUc-ZN-vl5" id="0sG-0D-cID"/>
|
|
||||||
<outlet property="debuggerVerticalLine" destination="7bR-gM-1At" id="rfy-7Z-388"/>
|
|
||||||
<outlet property="feedImageView" destination="Ar0-nN-eop" id="wHa-St-o4G"/>
|
<outlet property="feedImageView" destination="Ar0-nN-eop" id="wHa-St-o4G"/>
|
||||||
|
<outlet property="feedSaveButton" destination="RLc-0I-sYZ" id="Yy9-dG-xXY"/>
|
||||||
<outlet property="gridButton" destination="fL6-2S-Rgd" id="jtV-jh-GHC"/>
|
<outlet property="gridButton" destination="fL6-2S-Rgd" id="jtV-jh-GHC"/>
|
||||||
<outlet property="mainWindow" destination="xOd-HO-29H" id="h8M-YB-vcC"/>
|
<outlet property="mainWindow" destination="xOd-HO-29H" id="h8M-YB-vcC"/>
|
||||||
<outlet property="memoryBankInput" destination="rdV-q6-hc6" id="KBx-9T-2mX"/>
|
<outlet property="memoryBankInput" destination="rdV-q6-hc6" id="KBx-9T-2mX"/>
|
||||||
<outlet property="memoryBankItem" destination="bWC-FW-IYP" id="Lf2-dh-z32"/>
|
<outlet property="memoryBankItem" destination="bWC-FW-IYP" id="Lf2-dh-z32"/>
|
||||||
<outlet property="memoryView" destination="8hr-8o-3rN" id="fF0-rh-8ND"/>
|
<outlet property="memoryView" destination="8hr-8o-3rN" id="fF0-rh-8ND"/>
|
||||||
<outlet property="memoryWindow" destination="mRm-dL-mCj" id="VPR-lu-vtI"/>
|
<outlet property="memoryWindow" destination="mRm-dL-mCj" id="VPR-lu-vtI"/>
|
||||||
<outlet property="objectsTableView" destination="TOc-XJ-w9w" id="O4R-4Z-9hU"/>
|
|
||||||
<outlet property="osdView" destination="MX4-l2-7NE" id="Am7-fq-uvu"/>
|
|
||||||
<outlet property="paletteTableView" destination="gfC-d3-dmq" id="fTC-eL-Qg3"/>
|
<outlet property="paletteTableView" destination="gfC-d3-dmq" id="fTC-eL-Qg3"/>
|
||||||
<outlet property="printerFeedWindow" destination="NdE-0B-WCf" id="yVK-cS-NOJ"/>
|
<outlet property="printerFeedWindow" destination="NdE-0B-WCf" id="yVK-cS-NOJ"/>
|
||||||
|
<outlet property="spritesTableView" destination="TOc-XJ-w9w" id="O4R-4Z-9hU"/>
|
||||||
<outlet property="tilemapImageView" destination="LlK-tV-bjv" id="nSY-Xd-BjZ"/>
|
<outlet property="tilemapImageView" destination="LlK-tV-bjv" id="nSY-Xd-BjZ"/>
|
||||||
<outlet property="tilemapMapButton" destination="YIJ-Qc-SIZ" id="BB7-Gg-7XP"/>
|
<outlet property="tilemapMapButton" destination="YIJ-Qc-SIZ" id="BB7-Gg-7XP"/>
|
||||||
<outlet property="tilemapPaletteButton" destination="loB-0k-Qff" id="2Or-7l-6vn"/>
|
<outlet property="tilemapPaletteButton" destination="loB-0k-Qff" id="2Or-7l-6vn"/>
|
||||||
@ -43,33 +39,27 @@
|
|||||||
</customObject>
|
</customObject>
|
||||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
<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" oneShot="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="xOd-HO-29H" userLabel="Window">
|
||||||
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
|
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
|
||||||
<windowCollectionBehavior key="collectionBehavior" fullScreenPrimary="YES"/>
|
<windowCollectionBehavior key="collectionBehavior" fullScreenPrimary="YES"/>
|
||||||
<rect key="contentRect" x="0.0" y="0.0" width="160" height="144"/>
|
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
||||||
|
<rect key="contentRect" x="133" y="235" 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="1417"/>
|
||||||
<value key="minSize" type="size" width="160" height="144"/>
|
<value key="minSize" type="size" width="160" height="144"/>
|
||||||
<view key="contentView" id="gIp-Ho-8D9">
|
<view key="contentView" id="gIp-Ho-8D9">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="160" height="144"/>
|
<rect key="frame" x="0.0" y="0.0" width="160" height="144"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<customView fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="KTk-4M-J7t" customClass="GBBorderView">
|
<customView id="KTk-4M-J7t" customClass="GBBorderView">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="160" height="144"/>
|
<rect key="frame" x="0.0" y="0.0" width="160" height="144"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<view fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="uqf-pe-VAF" customClass="GBView">
|
<view id="uqf-pe-VAF" customClass="GBView">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="160" height="144"/>
|
<rect key="frame" x="0.0" y="0.0" width="160" height="144"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<connections>
|
|
||||||
<outlet property="document" destination="-2" id="Fvh-rD-z4r"/>
|
|
||||||
</connections>
|
|
||||||
</view>
|
</view>
|
||||||
</subviews>
|
</subviews>
|
||||||
</customView>
|
</customView>
|
||||||
<customView fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="MX4-l2-7NE" customClass="GBOSDView">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="160" height="144"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
|
||||||
</customView>
|
|
||||||
</subviews>
|
</subviews>
|
||||||
</view>
|
</view>
|
||||||
<connections>
|
<connections>
|
||||||
@ -77,17 +67,49 @@
|
|||||||
</connections>
|
</connections>
|
||||||
<point key="canvasLocation" x="293" y="347"/>
|
<point key="canvasLocation" x="293" y="347"/>
|
||||||
</window>
|
</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" hidesOnDeactivate="YES" oneShot="NO" releasedWhenClosed="NO" showsToolbarButton="NO" visibleAtLaunch="NO" animationBehavior="default" id="21F-Ah-yHX" customClass="NSPanel">
|
||||||
<windowStyleMask key="styleMask" titled="YES" closable="YES" resizable="YES" utility="YES" HUD="YES"/>
|
<windowStyleMask key="styleMask" titled="YES" closable="YES" resizable="YES" utility="YES" HUD="YES"/>
|
||||||
<windowCollectionBehavior key="collectionBehavior" fullScreenAuxiliary="YES"/>
|
<windowCollectionBehavior key="collectionBehavior" fullScreenAuxiliary="YES"/>
|
||||||
<rect key="contentRect" x="0.0" y="0.0" width="921" height="400"/>
|
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
||||||
|
<rect key="contentRect" x="272" y="172" 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="1417"/>
|
||||||
<value key="minSize" type="size" width="921" height="400"/>
|
<value key="minSize" type="size" width="921" height="400"/>
|
||||||
<view key="contentView" id="dCP-E5-7Fi">
|
<view key="contentView" id="dCP-E5-7Fi">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="921" height="400"/>
|
<rect key="frame" x="0.0" y="0.0" width="921" height="400"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" mirrorLayoutDirectionWhenInternationalizing="never" translatesAutoresizingMaskIntoConstraints="NO" id="l22-S8-uji">
|
<scrollView misplaced="YES" borderType="none" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" usesPredominantAxisScrolling="NO" id="oTo-zx-o6N">
|
||||||
|
<rect key="frame" x="0.0" y="25" width="600" height="376"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<clipView key="contentView" drawsBackground="NO" copiesOnScroll="NO" id="EQe-Ad-L7S">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="600" height="376"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<textView editable="NO" drawsBackground="NO" importsGraphics="NO" richText="NO" baseWritingDirection="leftToRight" findStyle="bar" allowsNonContiguousLayout="YES" id="doS-dM-hnl">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="600" height="376"/>
|
||||||
|
<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="600" height="376"/>
|
||||||
|
<size key="maxSize" width="1160" height="10000000"/>
|
||||||
|
<color key="insertionPointColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<allowedInputSourceLocales>
|
||||||
|
<string>NSAllRomanInputSourcesLocaleIdentifier</string>
|
||||||
|
</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" 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" verticalHuggingPriority="750" horizontal="NO" id="cwi-6E-rbh">
|
||||||
|
<rect key="frame" x="584" y="0.0" width="16" height="376"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</scroller>
|
||||||
|
</scrollView>
|
||||||
|
<textField focusRingType="none" verticalHuggingPriority="750" mirrorLayoutDirectionWhenInternationalizing="never" allowsCharacterPickerTouchBarItem="NO" id="l22-S8-uji">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="921" height="24"/>
|
<rect key="frame" x="0.0" y="0.0" width="921" height="24"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
<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">
|
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" state="on" focusRingType="none" id="p3j-nS-44f" customClass="GBTerminalTextFieldCell">
|
||||||
@ -102,147 +124,91 @@
|
|||||||
<action selector="consoleInput:" target="-2" id="ylQ-vw-ARS"/>
|
<action selector="consoleInput:" target="-2" id="ylQ-vw-ARS"/>
|
||||||
</connections>
|
</connections>
|
||||||
</textField>
|
</textField>
|
||||||
<box verticalHuggingPriority="750" fixedFrame="YES" boxType="separator" translatesAutoresizingMaskIntoConstraints="NO" id="960-dL-7ZY">
|
<box verticalHuggingPriority="750" boxType="separator" id="960-dL-7ZY">
|
||||||
<rect key="frame" x="0.0" y="23" width="921" height="5"/>
|
<rect key="frame" x="0.0" y="23" width="921" height="5"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
||||||
</box>
|
</box>
|
||||||
<box horizontalHuggingPriority="750" boxType="separator" id="7bR-gM-1At">
|
<scrollView misplaced="YES" borderType="none" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" usesPredominantAxisScrolling="NO" id="vts-CC-ZjQ">
|
||||||
<rect key="frame" x="590" y="25" width="5" height="376"/>
|
<rect key="frame" x="601" y="25" width="320" height="328"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" heightSizable="YES"/>
|
||||||
|
<clipView key="contentView" drawsBackground="NO" copiesOnScroll="NO" id="Cs9-3x-ATg">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="320" height="328"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<textView editable="NO" drawsBackground="NO" importsGraphics="NO" richText="NO" baseWritingDirection="leftToRight" findStyle="bar" allowsNonContiguousLayout="YES" spellingCorrection="YES" id="JgV-7E-iwp">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="320" height="328"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<color key="backgroundColor" red="0.14901960780000001" green="0.14901960780000001" blue="0.14901960780000001" alpha="1" colorSpace="calibratedRGB"/>
|
||||||
|
<size key="minSize" width="320" height="328"/>
|
||||||
|
<size key="maxSize" width="1160" height="10000000"/>
|
||||||
|
<color key="insertionPointColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<allowedInputSourceLocales>
|
||||||
|
<string>NSAllRomanInputSourcesLocaleIdentifier</string>
|
||||||
|
</allowedInputSourceLocales>
|
||||||
|
</textView>
|
||||||
|
</subviews>
|
||||||
|
<color key="backgroundColor" red="0.1647058824" green="0.1647058824" blue="0.1647058824" alpha="1" colorSpace="calibratedRGB"/>
|
||||||
|
</clipView>
|
||||||
|
<scroller key="horizontalScroller" hidden="YES" verticalHuggingPriority="750" doubleValue="1" horizontal="YES" id="J2i-lz-QJW">
|
||||||
|
<rect key="frame" x="-100" y="-100" width="87" height="18"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</scroller>
|
||||||
|
<scroller key="verticalScroller" verticalHuggingPriority="750" horizontal="NO" id="4jm-Gm-D2R">
|
||||||
|
<rect key="frame" x="304" y="0.0" width="16" height="328"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</scroller>
|
||||||
|
</scrollView>
|
||||||
|
<scrollView misplaced="YES" borderType="none" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" usesPredominantAxisScrolling="NO" scrollerKnobStyle="dark" id="V9U-Ua-F4z">
|
||||||
|
<rect key="frame" x="601" y="354" width="320" height="47"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
|
||||||
|
<clipView key="contentView" drawsBackground="NO" copiesOnScroll="NO" id="YHx-TM-zIC">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="320" height="47"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<textView drawsBackground="NO" importsGraphics="NO" richText="NO" allowsCharacterPickerTouchBarItem="NO" allowsUndo="YES" allowsNonContiguousLayout="YES" textCompletion="NO" spellingCorrection="YES" id="w0g-eK-jM4">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="320" height="47"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||||
|
<size key="minSize" width="320" height="47"/>
|
||||||
|
<size key="maxSize" width="463" height="10000000"/>
|
||||||
|
<color key="insertionPointColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<allowedInputSourceLocales>
|
||||||
|
<string>NSAllRomanInputSourcesLocaleIdentifier</string>
|
||||||
|
</allowedInputSourceLocales>
|
||||||
|
</textView>
|
||||||
|
</subviews>
|
||||||
|
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||||
|
</clipView>
|
||||||
|
<scroller key="horizontalScroller" hidden="YES" verticalHuggingPriority="750" doubleValue="1" horizontal="YES" id="24d-1i-uBk">
|
||||||
|
<rect key="frame" x="-100" y="-100" width="87" height="18"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</scroller>
|
||||||
|
<scroller key="verticalScroller" verticalHuggingPriority="750" doubleValue="1" horizontal="NO" id="qgN-F8-fdB">
|
||||||
|
<rect key="frame" x="304" y="0.0" width="16" height="47"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</scroller>
|
||||||
|
</scrollView>
|
||||||
|
<box verticalHuggingPriority="750" misplaced="YES" boxType="separator" id="5qI-qZ-nkh">
|
||||||
|
<rect key="frame" x="603" y="352" width="318" height="5"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
|
||||||
|
</box>
|
||||||
|
<box horizontalHuggingPriority="750" misplaced="YES" boxType="separator" id="Onx-Oe-fBx">
|
||||||
|
<rect key="frame" x="600" y="25" width="5" height="376"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" heightSizable="YES"/>
|
||||||
</box>
|
</box>
|
||||||
<splitView dividerStyle="thin" vertical="YES" id="pUc-ZN-vl5" customClass="GBSplitView">
|
|
||||||
<rect key="frame" x="0.0" y="25" width="921" height="376"/>
|
|
||||||
<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"/>
|
|
||||||
<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"/>
|
|
||||||
<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"/>
|
|
||||||
<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"/>
|
|
||||||
<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"/>
|
|
||||||
<size key="maxSize" width="1160" height="10000000"/>
|
|
||||||
<color key="insertionPointColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
<allowedInputSourceLocales>
|
|
||||||
<string>NSAllRomanInputSourcesLocaleIdentifier</string>
|
|
||||||
</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"/>
|
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
|
||||||
</scroller>
|
|
||||||
</scrollView>
|
|
||||||
</subviews>
|
|
||||||
</customView>
|
|
||||||
<customView fixedFrame="YES" id="4Z2-33-dYY">
|
|
||||||
<rect key="frame" x="592" y="0.0" width="329" height="376"/>
|
|
||||||
<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"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
|
||||||
<clipView key="contentView" ambiguous="YES" drawsBackground="NO" copiesOnScroll="NO" id="YHx-TM-zIC">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="329" height="47"/>
|
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
|
||||||
<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">
|
|
||||||
<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"/>
|
|
||||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
<size key="minSize" width="329" height="47"/>
|
|
||||||
<size key="maxSize" width="463" height="10000000"/>
|
|
||||||
<color key="insertionPointColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
<allowedInputSourceLocales>
|
|
||||||
<string>NSAllRomanInputSourcesLocaleIdentifier</string>
|
|
||||||
</allowedInputSourceLocales>
|
|
||||||
</textView>
|
|
||||||
</subviews>
|
|
||||||
</clipView>
|
|
||||||
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" doubleValue="1" horizontal="YES" id="24d-1i-uBk">
|
|
||||||
<rect key="frame" x="-100" y="-100" width="87" height="18"/>
|
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
|
||||||
</scroller>
|
|
||||||
<scroller key="verticalScroller" wantsLayer="YES" verticalHuggingPriority="750" doubleValue="1" horizontal="NO" id="qgN-F8-fdB">
|
|
||||||
<rect key="frame" x="313" y="0.0" width="16" height="47"/>
|
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
|
||||||
</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"/>
|
|
||||||
<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"/>
|
|
||||||
<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"/>
|
|
||||||
<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"/>
|
|
||||||
<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="maxSize" width="1160" height="10000000"/>
|
|
||||||
<color key="insertionPointColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
<allowedInputSourceLocales>
|
|
||||||
<string>NSAllRomanInputSourcesLocaleIdentifier</string>
|
|
||||||
</allowedInputSourceLocales>
|
|
||||||
</textView>
|
|
||||||
</subviews>
|
|
||||||
<color key="backgroundColor" red="0.1647058824" green="0.1647058824" blue="0.1647058824" alpha="1" colorSpace="calibratedRGB"/>
|
|
||||||
</clipView>
|
|
||||||
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" doubleValue="1" horizontal="YES" id="J2i-lz-QJW">
|
|
||||||
<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="4jm-Gm-D2R">
|
|
||||||
<rect key="frame" x="313" y="0.0" width="16" height="328"/>
|
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
|
||||||
</scroller>
|
|
||||||
</scrollView>
|
|
||||||
</subviews>
|
|
||||||
</customView>
|
|
||||||
</subviews>
|
|
||||||
<holdingPriorities>
|
|
||||||
<real value="250"/>
|
|
||||||
<real value="250"/>
|
|
||||||
</holdingPriorities>
|
|
||||||
<connections>
|
|
||||||
<outlet property="delegate" destination="-2" id="c8R-36-VRD"/>
|
|
||||||
</connections>
|
|
||||||
</splitView>
|
|
||||||
</subviews>
|
</subviews>
|
||||||
</view>
|
</view>
|
||||||
<point key="canvasLocation" x="347.5" y="-29"/>
|
<point key="canvasLocation" x="347.5" y="-29"/>
|
||||||
</window>
|
</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" hidesOnDeactivate="YES" oneShot="NO" releasedWhenClosed="NO" showsToolbarButton="NO" visibleAtLaunch="NO" animationBehavior="default" id="mRm-dL-mCj" customClass="NSPanel">
|
||||||
<windowStyleMask key="styleMask" titled="YES" closable="YES" resizable="YES"/>
|
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
|
||||||
<windowCollectionBehavior key="collectionBehavior" fullScreenAuxiliary="YES"/>
|
<windowCollectionBehavior key="collectionBehavior" fullScreenAuxiliary="YES"/>
|
||||||
<rect key="contentRect" x="0.0" y="0.0" width="528" height="320"/>
|
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
||||||
|
<rect key="contentRect" x="272" y="172" 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="1417"/>
|
||||||
<view key="contentView" id="8hr-8o-3rN">
|
<view key="contentView" id="8hr-8o-3rN">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="528" height="320"/>
|
<rect key="frame" x="0.0" y="0.0" width="528" height="320"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
</view>
|
</view>
|
||||||
<toolbar key="toolbar" implicitIdentifier="D857E961-E523-4295-83F8-0849316E827C" autosavesConfiguration="NO" allowsUserCustomization="NO" displayMode="iconAndLabel" sizeMode="regular" id="82v-uB-RPi">
|
<toolbar key="toolbar" implicitIdentifier="D857E961-E523-4295-83F8-0849316E827C" autosavesConfiguration="NO" allowsUserCustomization="NO" displayMode="iconAndLabel" sizeMode="regular" id="82v-uB-RPi">
|
||||||
<allowedToolbarItems>
|
<allowedToolbarItems>
|
||||||
@ -251,9 +217,9 @@
|
|||||||
<toolbarItem implicitItemIdentifier="4F6AAE25-1E9D-4111-9E5B-91F0792E56CD" label="Address Space" paletteLabel="Address Space" id="VTy-lj-K0H">
|
<toolbarItem implicitItemIdentifier="4F6AAE25-1E9D-4111-9E5B-91F0792E56CD" label="Address Space" paletteLabel="Address Space" id="VTy-lj-K0H">
|
||||||
<nil key="toolTip"/>
|
<nil key="toolTip"/>
|
||||||
<size key="minSize" width="100" height="25"/>
|
<size key="minSize" width="100" height="25"/>
|
||||||
<size key="maxSize" width="130" height="25"/>
|
<size key="maxSize" width="100" height="25"/>
|
||||||
<popUpButton key="view" verticalHuggingPriority="750" id="vfJ-vu-gqJ">
|
<popUpButton key="view" verticalHuggingPriority="750" id="vfJ-vu-gqJ">
|
||||||
<rect key="frame" x="0.0" y="14" width="128" height="25"/>
|
<rect key="frame" x="0.0" y="14" width="100" height="25"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
<popUpButtonCell key="cell" type="roundTextured" bezelStyle="texturedRounded" alignment="left" lineBreakMode="truncatingTail" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="bpD-j9-omo">
|
<popUpButtonCell key="cell" type="roundTextured" bezelStyle="texturedRounded" alignment="left" lineBreakMode="truncatingTail" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="bpD-j9-omo">
|
||||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
@ -277,12 +243,12 @@
|
|||||||
<nil key="toolTip"/>
|
<nil key="toolTip"/>
|
||||||
<size key="minSize" width="64" height="22"/>
|
<size key="minSize" width="64" height="22"/>
|
||||||
<size key="maxSize" width="64" height="22"/>
|
<size key="maxSize" width="64" height="22"/>
|
||||||
<textField key="view" verticalHuggingPriority="750" id="rdV-q6-hc6">
|
<textField key="view" verticalHuggingPriority="750" allowsCharacterPickerTouchBarItem="NO" id="rdV-q6-hc6">
|
||||||
<rect key="frame" x="0.0" y="14" width="64" height="22"/>
|
<rect key="frame" x="0.0" y="14" width="64" height="22"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" id="JCn-Y1-eHS">
|
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" id="JCn-Y1-eHS">
|
||||||
<font key="font" metaFont="system"/>
|
<font key="font" metaFont="system"/>
|
||||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
</textFieldCell>
|
</textFieldCell>
|
||||||
<connections>
|
<connections>
|
||||||
@ -294,12 +260,12 @@
|
|||||||
<nil key="toolTip"/>
|
<nil key="toolTip"/>
|
||||||
<size key="minSize" width="96" height="22"/>
|
<size key="minSize" width="96" height="22"/>
|
||||||
<size key="maxSize" width="128" height="22"/>
|
<size key="maxSize" width="128" height="22"/>
|
||||||
<textField key="view" verticalHuggingPriority="750" id="EJd-jG-hmH">
|
<textField key="view" verticalHuggingPriority="750" allowsCharacterPickerTouchBarItem="NO" id="EJd-jG-hmH">
|
||||||
<rect key="frame" x="0.0" y="14" width="96" height="22"/>
|
<rect key="frame" x="0.0" y="14" width="96" height="22"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" state="on" borderStyle="bezel" bezelStyle="round" id="vg5-Nn-abb">
|
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" state="on" borderStyle="bezel" bezelStyle="round" id="vg5-Nn-abb">
|
||||||
<font key="font" metaFont="system"/>
|
<font key="font" metaFont="system"/>
|
||||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
</textFieldCell>
|
</textFieldCell>
|
||||||
<connections>
|
<connections>
|
||||||
@ -318,20 +284,21 @@
|
|||||||
<point key="canvasLocation" x="-185" y="61"/>
|
<point key="canvasLocation" x="-185" y="61"/>
|
||||||
</window>
|
</window>
|
||||||
<menuItem title="Cartridge RAM" id="ylM-ah-PNQ"/>
|
<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" hidesOnDeactivate="YES" oneShot="NO" releasedWhenClosed="NO" showsToolbarButton="NO" visibleAtLaunch="NO" animationBehavior="default" id="mbr-db-iZh" customClass="NSPanel">
|
||||||
<windowStyleMask key="styleMask" titled="YES" closable="YES"/>
|
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" unifiedTitleAndToolbar="YES"/>
|
||||||
<windowCollectionBehavior key="collectionBehavior" fullScreenAuxiliary="YES"/>
|
<windowCollectionBehavior key="collectionBehavior" fullScreenAuxiliary="YES"/>
|
||||||
<rect key="contentRect" x="0.0" y="0.0" width="512" height="432"/>
|
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
||||||
|
<rect key="contentRect" x="283" y="305" 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="1417"/>
|
||||||
<view key="contentView" id="GYW-dv-Um1">
|
<view key="contentView" id="GYW-dv-Um1">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="512" height="432"/>
|
<rect key="frame" x="0.0" y="0.0" width="512" height="432"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<box verticalHuggingPriority="750" fixedFrame="YES" boxType="separator" translatesAutoresizingMaskIntoConstraints="NO" id="ucG-cD-wfs">
|
<box verticalHuggingPriority="750" boxType="separator" id="ucG-cD-wfs">
|
||||||
<rect key="frame" x="0.0" y="406" width="512" height="5"/>
|
<rect key="frame" x="0.0" y="406" width="512" height="5"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
</box>
|
</box>
|
||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="6vK-IP-PmP">
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" allowsCharacterPickerTouchBarItem="NO" id="6vK-IP-PmP">
|
||||||
<rect key="frame" x="-2" y="4" width="516" height="14"/>
|
<rect key="frame" x="-2" y="4" width="516" height="14"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
||||||
<textFieldCell key="cell" controlSize="small" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="center" id="umk-4r-VNg">
|
<textFieldCell key="cell" controlSize="small" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="center" id="umk-4r-VNg">
|
||||||
@ -340,17 +307,17 @@
|
|||||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||||
</textFieldCell>
|
</textFieldCell>
|
||||||
</textField>
|
</textField>
|
||||||
<tabView fixedFrame="YES" drawsBackground="NO" type="noTabsNoBorder" initialItem="pXb-od-Wb1" translatesAutoresizingMaskIntoConstraints="NO" id="AZz-Mh-rPA">
|
<tabView drawsBackground="NO" type="noTabsNoBorder" initialItem="pXb-od-Wb1" id="AZz-Mh-rPA">
|
||||||
<rect key="frame" x="0.0" y="24" width="512" height="408"/>
|
<rect key="frame" x="0.0" y="24" width="512" height="408"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<font key="font" metaFont="system"/>
|
<font key="font" metaFont="system"/>
|
||||||
<tabViewItems>
|
<tabViewItems>
|
||||||
<tabViewItem label="Tileset" identifier="1" id="pXb-od-Wb1">
|
<tabViewItem label="Tileset" identifier="1" id="pXb-od-Wb1">
|
||||||
<view key="view" ambiguous="YES" id="lCG-Gt-XMF">
|
<view key="view" id="lCG-Gt-XMF">
|
||||||
<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="408"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="QcQ-ex-36R" customClass="GBImageView">
|
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" 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="0.0" width="512" height="384"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="axesIndependently" id="pXc-O8-jg5"/>
|
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="axesIndependently" id="pXc-O8-jg5"/>
|
||||||
@ -359,7 +326,7 @@
|
|||||||
<outlet property="delegate" destination="-2" id="xoo-Uo-872"/>
|
<outlet property="delegate" destination="-2" id="xoo-Uo-872"/>
|
||||||
</connections>
|
</connections>
|
||||||
</imageView>
|
</imageView>
|
||||||
<popUpButton focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="TLv-xS-X5K">
|
<popUpButton focusRingType="none" verticalHuggingPriority="750" id="TLv-xS-X5K">
|
||||||
<rect key="frame" x="4" y="388" width="128" height="17"/>
|
<rect key="frame" x="4" y="388" width="128" height="17"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<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">
|
<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">
|
||||||
@ -391,7 +358,7 @@
|
|||||||
<action selector="reloadVRAMData:" target="-2" id="Qtf-p4-Rqh"/>
|
<action selector="reloadVRAMData:" target="-2" id="Qtf-p4-Rqh"/>
|
||||||
</connections>
|
</connections>
|
||||||
</popUpButton>
|
</popUpButton>
|
||||||
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="fL6-2S-Rgd">
|
<button verticalHuggingPriority="750" id="fL6-2S-Rgd">
|
||||||
<rect key="frame" x="452" y="388" width="56" height="17"/>
|
<rect key="frame" x="452" y="388" width="56" height="17"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
|
<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">
|
<buttonCell key="cell" type="roundRect" title="Grid" bezelStyle="roundedRect" alignment="center" controlSize="mini" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="pDn-9a-Se6">
|
||||||
@ -410,18 +377,7 @@
|
|||||||
<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="408"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="DhM-Em-hj7">
|
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" id="LlK-tV-bjv" customClass="GBImageView">
|
||||||
<rect key="frame" x="385" y="388" 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"/>
|
|
||||||
<font key="font" metaFont="miniSystem"/>
|
|
||||||
</buttonCell>
|
|
||||||
<connections>
|
|
||||||
<action selector="toggleScrollingDisplay:" target="-2" id="VhQ-9W-sjU"/>
|
|
||||||
</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="0.0" width="512" height="384"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="axesIndependently" id="RvP-El-ILj"/>
|
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="axesIndependently" id="RvP-El-ILj"/>
|
||||||
@ -430,8 +386,8 @@
|
|||||||
<outlet property="delegate" destination="-2" id="EAG-Zh-oRi"/>
|
<outlet property="delegate" destination="-2" id="EAG-Zh-oRi"/>
|
||||||
</connections>
|
</connections>
|
||||||
</imageView>
|
</imageView>
|
||||||
<popUpButton focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="loB-0k-Qff">
|
<popUpButton focusRingType="none" verticalHuggingPriority="750" id="loB-0k-Qff">
|
||||||
<rect key="frame" x="4" y="388" width="128" height="17"/>
|
<rect key="frame" x="4" y="387" width="128" height="18"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<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">
|
<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"/>
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
@ -463,8 +419,8 @@
|
|||||||
<action selector="reloadVRAMData:" target="-2" id="BmB-JE-W8g"/>
|
<action selector="reloadVRAMData:" target="-2" id="BmB-JE-W8g"/>
|
||||||
</connections>
|
</connections>
|
||||||
</popUpButton>
|
</popUpButton>
|
||||||
<popUpButton focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="YIJ-Qc-SIZ">
|
<popUpButton focusRingType="none" verticalHuggingPriority="750" id="YIJ-Qc-SIZ">
|
||||||
<rect key="frame" x="135" y="388" width="96" height="17"/>
|
<rect key="frame" x="135" y="387" width="96" height="18"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<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">
|
<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"/>
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
@ -481,8 +437,8 @@
|
|||||||
<action selector="reloadVRAMData:" target="-2" id="xwp-Ju-p00"/>
|
<action selector="reloadVRAMData:" target="-2" id="xwp-Ju-p00"/>
|
||||||
</connections>
|
</connections>
|
||||||
</popUpButton>
|
</popUpButton>
|
||||||
<popUpButton focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="k4c-Vg-MBu">
|
<popUpButton focusRingType="none" verticalHuggingPriority="750" id="k4c-Vg-MBu">
|
||||||
<rect key="frame" x="235" y="388" width="96" height="17"/>
|
<rect key="frame" x="235" y="387" width="96" height="18"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<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">
|
<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"/>
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
@ -502,19 +458,19 @@
|
|||||||
</subviews>
|
</subviews>
|
||||||
</view>
|
</view>
|
||||||
</tabViewItem>
|
</tabViewItem>
|
||||||
<tabViewItem label="Objects" identifier="" id="a08-eg-Maw">
|
<tabViewItem label="Sprites" identifier="" id="a08-eg-Maw">
|
||||||
<view key="view" id="EiO-p0-3xn">
|
<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="408"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<scrollView fixedFrame="YES" borderType="none" autohidesScrollers="YES" horizontalLineScroll="20" horizontalPageScroll="10" verticalLineScroll="20" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="krD-gH-o5I">
|
<scrollView borderType="none" autohidesScrollers="YES" horizontalLineScroll="20" horizontalPageScroll="10" verticalLineScroll="20" verticalPageScroll="10" usesPredominantAxisScrolling="NO" id="krD-gH-o5I">
|
||||||
<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="408"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<clipView key="contentView" ambiguous="YES" drawsBackground="NO" id="3VT-AA-xVT">
|
<clipView key="contentView" drawsBackground="NO" id="3VT-AA-xVT">
|
||||||
<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="408"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<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">
|
<tableView appearanceType="vibrantLight" 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"/>
|
<rect key="frame" x="0.0" y="0.0" width="512" height="391"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<size key="intercellSpacing" width="3" height="2"/>
|
<size key="intercellSpacing" width="3" height="2"/>
|
||||||
@ -630,11 +586,11 @@
|
|||||||
</subviews>
|
</subviews>
|
||||||
<nil key="backgroundColor"/>
|
<nil key="backgroundColor"/>
|
||||||
</clipView>
|
</clipView>
|
||||||
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="n53-qA-NpY">
|
<scroller key="horizontalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="YES" id="n53-qA-NpY">
|
||||||
<rect key="frame" x="0.0" y="392" width="512" height="16"/>
|
<rect key="frame" x="0.0" y="392" width="512" height="16"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
</scroller>
|
</scroller>
|
||||||
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="mqp-NY-g8d">
|
<scroller key="verticalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="NO" id="mqp-NY-g8d">
|
||||||
<rect key="frame" x="224" y="17" width="15" height="102"/>
|
<rect key="frame" x="224" y="17" width="15" height="102"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
</scroller>
|
</scroller>
|
||||||
@ -651,10 +607,10 @@
|
|||||||
<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="408"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<subviews>
|
<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">
|
<scrollView borderType="none" horizontalLineScroll="20" horizontalPageScroll="10" verticalLineScroll="20" verticalPageScroll="10" hasHorizontalScroller="NO" hasVerticalScroller="NO" usesPredominantAxisScrolling="NO" horizontalScrollElasticity="none" verticalScrollElasticity="none" id="iSs-Ow-wwb">
|
||||||
<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="408"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<clipView key="contentView" ambiguous="YES" id="bP9-su-zQw">
|
<clipView key="contentView" id="bP9-su-zQw">
|
||||||
<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="408"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
@ -692,7 +648,7 @@
|
|||||||
</textFieldCell>
|
</textFieldCell>
|
||||||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||||
</tableColumn>
|
</tableColumn>
|
||||||
<tableColumn width="93" minWidth="10" maxWidth="3.4028234663852886e+38" id="syl-os-nSf">
|
<tableColumn identifier="" width="93" minWidth="10" maxWidth="3.4028234663852886e+38" id="syl-os-nSf">
|
||||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left">
|
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left">
|
||||||
<font key="font" metaFont="smallSystem"/>
|
<font key="font" metaFont="smallSystem"/>
|
||||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
@ -705,7 +661,7 @@
|
|||||||
</textFieldCell>
|
</textFieldCell>
|
||||||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||||
</tableColumn>
|
</tableColumn>
|
||||||
<tableColumn width="93" minWidth="10" maxWidth="3.4028234663852886e+38" id="Qw3-u2-c1s">
|
<tableColumn identifier="" width="93" minWidth="10" maxWidth="3.4028234663852886e+38" id="Qw3-u2-c1s">
|
||||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left">
|
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left">
|
||||||
<font key="font" metaFont="smallSystem"/>
|
<font key="font" metaFont="smallSystem"/>
|
||||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
@ -718,7 +674,7 @@
|
|||||||
</textFieldCell>
|
</textFieldCell>
|
||||||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||||
</tableColumn>
|
</tableColumn>
|
||||||
<tableColumn width="93" minWidth="10" maxWidth="3.4028234663852886e+38" id="gTl-gN-qLn">
|
<tableColumn identifier="" width="93" minWidth="10" maxWidth="3.4028234663852886e+38" id="gTl-gN-qLn">
|
||||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left">
|
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left">
|
||||||
<font key="font" metaFont="smallSystem"/>
|
<font key="font" metaFont="smallSystem"/>
|
||||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
@ -739,11 +695,11 @@
|
|||||||
</tableView>
|
</tableView>
|
||||||
</subviews>
|
</subviews>
|
||||||
</clipView>
|
</clipView>
|
||||||
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="OS3-sw-bjv">
|
<scroller key="horizontalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="YES" id="OS3-sw-bjv">
|
||||||
<rect key="frame" x="-100" y="-100" width="510" height="16"/>
|
<rect key="frame" x="-100" y="-100" width="510" height="16"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
</scroller>
|
</scroller>
|
||||||
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="4HA-2m-8TZ">
|
<scroller key="verticalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="NO" id="4HA-2m-8TZ">
|
||||||
<rect key="frame" x="-100" y="-100" width="15" height="102"/>
|
<rect key="frame" x="-100" y="-100" width="15" height="102"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
</scroller>
|
</scroller>
|
||||||
@ -770,7 +726,7 @@
|
|||||||
<segments>
|
<segments>
|
||||||
<segment label="Tileset" selected="YES"/>
|
<segment label="Tileset" selected="YES"/>
|
||||||
<segment label="Tilemap" tag="1"/>
|
<segment label="Tilemap" tag="1"/>
|
||||||
<segment label="Objects"/>
|
<segment label="Sprites"/>
|
||||||
<segment label="Palettes"/>
|
<segment label="Palettes"/>
|
||||||
</segments>
|
</segments>
|
||||||
</segmentedCell>
|
</segmentedCell>
|
||||||
@ -789,300 +745,35 @@
|
|||||||
<contentBorderThickness minY="24"/>
|
<contentBorderThickness minY="24"/>
|
||||||
<point key="canvasLocation" x="182" y="760"/>
|
<point key="canvasLocation" x="182" y="760"/>
|
||||||
</window>
|
</window>
|
||||||
<window title="Printer Feed" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" restorable="NO" hidesOnDeactivate="YES" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="NdE-0B-WCf" customClass="NSPanel">
|
<window title="Printer Feed" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" hidesOnDeactivate="YES" oneShot="NO" releasedWhenClosed="NO" showsToolbarButton="NO" visibleAtLaunch="NO" animationBehavior="default" id="NdE-0B-WCf" customClass="NSPanel">
|
||||||
<windowStyleMask key="styleMask" titled="YES" closable="YES" resizable="YES"/>
|
<windowStyleMask key="styleMask" titled="YES" closable="YES"/>
|
||||||
<rect key="contentRect" x="0.0" y="0.0" width="320" height="288"/>
|
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
||||||
|
<rect key="contentRect" x="272" y="172" 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="1417"/>
|
||||||
<value key="minSize" type="size" width="320" height="16"/>
|
|
||||||
<view key="contentView" id="RRS-aa-bPT">
|
<view key="contentView" id="RRS-aa-bPT">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="320" height="288"/>
|
<rect key="frame" x="0.0" y="0.0" width="320" height="288"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Ar0-nN-eop" customClass="GBImageView">
|
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" id="Ar0-nN-eop" customClass="GBImageView">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="320" height="288"/>
|
<rect key="frame" x="0.0" y="0.0" width="320" height="288"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageAlignment="topLeft" id="sff-hk-4nM"/>
|
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageAlignment="topLeft" id="sff-hk-4nM"/>
|
||||||
</imageView>
|
</imageView>
|
||||||
</subviews>
|
</subviews>
|
||||||
</view>
|
</view>
|
||||||
<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">
|
|
||||||
<connections>
|
|
||||||
<action selector="savePrinterFeed:" target="-2" id="Dm3-h0-ch4"/>
|
|
||||||
</connections>
|
|
||||||
</toolbarItem>
|
|
||||||
<toolbarItem implicitItemIdentifier="NSToolbarPrintItem" explicitItemIdentifier="Print" id="mtd-zS-DXa"/>
|
|
||||||
<toolbarItem implicitItemIdentifier="NSToolbarSpaceItem" id="AoG-LH-J4b"/>
|
|
||||||
<toolbarItem implicitItemIdentifier="NSToolbarFlexibleSpaceItem" id="Q0x-n5-Q2Y"/>
|
|
||||||
</allowedToolbarItems>
|
|
||||||
<defaultToolbarItems>
|
|
||||||
<toolbarItem reference="CBz-1N-o0Q"/>
|
|
||||||
<toolbarItem reference="Q0x-n5-Q2Y"/>
|
|
||||||
<toolbarItem reference="mtd-zS-DXa"/>
|
|
||||||
</defaultToolbarItems>
|
|
||||||
</toolbar>
|
|
||||||
<point key="canvasLocation" x="-159" y="356"/>
|
<point key="canvasLocation" x="-159" y="356"/>
|
||||||
</window>
|
</window>
|
||||||
<window title="Cheats" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" restorable="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" id="4Yb-Np-JrF" customClass="NSPanel">
|
<button verticalHuggingPriority="750" id="RLc-0I-sYZ">
|
||||||
<windowStyleMask key="styleMask" titled="YES" closable="YES"/>
|
<rect key="frame" x="0.0" y="0.0" width="48" height="25"/>
|
||||||
<rect key="contentRect" x="0.0" y="0.0" width="692" height="272"/>
|
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
|
||||||
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1417"/>
|
<buttonCell key="cell" type="roundTextured" title="Save" bezelStyle="texturedRounded" alignment="center" controlSize="mini" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="shw-MJ-B3T">
|
||||||
<view key="contentView" id="gBP-5p-BTh">
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
<rect key="frame" x="0.0" y="0.0" width="692" height="272"/>
|
<font key="font" metaFont="miniSystem"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
</buttonCell>
|
||||||
<subviews>
|
|
||||||
<view id="fWr-0i-K1d" customClass="GBOptionalVisualEffectView">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="294" height="272"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" heightSizable="YES"/>
|
|
||||||
<subviews>
|
|
||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" id="hqi-ob-NW9">
|
|
||||||
<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"/>
|
|
||||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
</textFieldCell>
|
|
||||||
</textField>
|
|
||||||
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Kq8-6F-9GK">
|
|
||||||
<rect key="frame" x="16" y="142" width="154" height="19"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
|
||||||
<buttonCell key="cell" type="check" title="Only if old value was: " bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="LkB-WQ-9Qd">
|
|
||||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
|
||||||
<font key="font" metaFont="system"/>
|
|
||||||
</buttonCell>
|
|
||||||
<connections>
|
|
||||||
<action selector="updateCheat:" target="v7q-gT-jHT" id="kNc-cj-bmF"/>
|
|
||||||
</connections>
|
|
||||||
</button>
|
|
||||||
<box verticalHuggingPriority="750" boxType="separator" id="D6k-Pe-23u">
|
|
||||||
<rect key="frame" x="10" y="129" width="274" height="5"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
|
||||||
</box>
|
|
||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="r5T-ol-Dod">
|
|
||||||
<rect key="frame" x="16" y="107" width="270" height="16"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMinY="YES"/>
|
|
||||||
<textFieldCell key="cell" lineBreakMode="clipping" title="Import GameShark or GameGenie cheat:" id="0mf-EN-cKc">
|
|
||||||
<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>
|
|
||||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="X7K-nJ-alF">
|
|
||||||
<rect key="frame" x="39" y="78" width="233" height="21"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
|
||||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" borderStyle="bezel" focusRingType="none" placeholderString="Code" drawsBackground="YES" usesSingleLineMode="YES" id="2bz-dT-7Fi">
|
|
||||||
<font key="font" metaFont="system"/>
|
|
||||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
<allowedInputSourceLocales>
|
|
||||||
<string>NSAllRomanInputSourcesLocaleIdentifier</string>
|
|
||||||
</allowedInputSourceLocales>
|
|
||||||
</textFieldCell>
|
|
||||||
<connections>
|
|
||||||
<action selector="selectText:" target="KHj-uX-Wbk" id="11z-0U-tMA"/>
|
|
||||||
</connections>
|
|
||||||
</textField>
|
|
||||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="KHj-uX-Wbk">
|
|
||||||
<rect key="frame" x="39" y="47" width="233" height="21"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
|
||||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" borderStyle="bezel" focusRingType="none" placeholderString="Description" drawsBackground="YES" usesSingleLineMode="YES" id="50d-va-Cen">
|
|
||||||
<font key="font" metaFont="system"/>
|
|
||||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
</textFieldCell>
|
|
||||||
<connections>
|
|
||||||
<action selector="performClick:" target="C3V-Ep-bMj" id="kIN-jl-A8d"/>
|
|
||||||
</connections>
|
|
||||||
</textField>
|
|
||||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="C6E-oI-hDC">
|
|
||||||
<rect key="frame" x="20" y="233" width="252" height="21"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
|
||||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" focusRingType="none" placeholderString="Description" drawsBackground="YES" usesSingleLineMode="YES" id="2uR-9N-hBb">
|
|
||||||
<font key="font" metaFont="system"/>
|
|
||||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
</textFieldCell>
|
|
||||||
<connections>
|
|
||||||
<outlet property="delegate" destination="v7q-gT-jHT" id="zyw-h0-hRP"/>
|
|
||||||
</connections>
|
|
||||||
</textField>
|
|
||||||
<button verticalHuggingPriority="750" id="C3V-Ep-bMj">
|
|
||||||
<rect key="frame" x="202.5" 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"/>
|
|
||||||
<font key="font" metaFont="system"/>
|
|
||||||
</buttonCell>
|
|
||||||
<connections>
|
|
||||||
<action selector="importCheat:" target="v7q-gT-jHT" id="lkX-N5-wD1"/>
|
|
||||||
</connections>
|
|
||||||
</button>
|
|
||||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" mirrorLayoutDirectionWhenInternationalizing="never" translatesAutoresizingMaskIntoConstraints="NO" id="qHx-1z-daR">
|
|
||||||
<rect key="frame" x="176" y="204" width="96" height="21"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
|
||||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" state="on" borderStyle="bezel" focusRingType="none" drawsBackground="YES" usesSingleLineMode="YES" id="edq-46-JeP" customClass="GBCheatTextFieldCell">
|
|
||||||
<font key="font" metaFont="system"/>
|
|
||||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
<allowedInputSourceLocales>
|
|
||||||
<string>NSAllRomanInputSourcesLocaleIdentifier</string>
|
|
||||||
</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>
|
|
||||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" mirrorLayoutDirectionWhenInternationalizing="never" translatesAutoresizingMaskIntoConstraints="NO" id="N3I-PP-X85">
|
|
||||||
<rect key="frame" x="176" y="174" width="96" height="21"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
|
||||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" state="on" borderStyle="bezel" focusRingType="none" drawsBackground="YES" usesSingleLineMode="YES" id="CV2-D9-WsB" customClass="GBCheatTextFieldCell">
|
|
||||||
<font key="font" metaFont="system"/>
|
|
||||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
<allowedInputSourceLocales>
|
|
||||||
<string>NSAllRomanInputSourcesLocaleIdentifier</string>
|
|
||||||
</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>
|
|
||||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" mirrorLayoutDirectionWhenInternationalizing="never" translatesAutoresizingMaskIntoConstraints="NO" id="S6O-LB-gSj">
|
|
||||||
<rect key="frame" x="176" y="144" width="96" height="21"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
|
||||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" state="on" borderStyle="bezel" focusRingType="none" drawsBackground="YES" usesSingleLineMode="YES" id="tpM-ys-MEO" customClass="GBCheatTextFieldCell">
|
|
||||||
<font key="font" metaFont="system"/>
|
|
||||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
<allowedInputSourceLocales>
|
|
||||||
<string>NSAllRomanInputSourcesLocaleIdentifier</string>
|
|
||||||
</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>
|
|
||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" id="uFo-ly-Veq">
|
|
||||||
<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"/>
|
|
||||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
</textFieldCell>
|
|
||||||
</textField>
|
|
||||||
</subviews>
|
|
||||||
</view>
|
|
||||||
<scrollView autohidesScrollers="YES" horizontalLineScroll="19" horizontalPageScroll="10" verticalLineScroll="19" verticalPageScroll="10" usesPredominantAxisScrolling="NO" id="6rU-Xg-KHc">
|
|
||||||
<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"/>
|
|
||||||
<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"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
|
||||||
<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="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">
|
|
||||||
<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">
|
|
||||||
<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>
|
|
||||||
<buttonCell key="dataCell" type="check" bezelStyle="regularSquare" imagePosition="only" inset="2" id="SdJ-Xw-UAG">
|
|
||||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
|
||||||
<font key="font" metaFont="system"/>
|
|
||||||
</buttonCell>
|
|
||||||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
|
||||||
</tableColumn>
|
|
||||||
<tableColumn editable="NO" width="160" 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>
|
|
||||||
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Description" id="1hX-Sr-bGz">
|
|
||||||
<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 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>
|
|
||||||
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Action" id="8Sq-h9-eV7">
|
|
||||||
<font key="font" metaFont="fixedUser" size="11"/>
|
|
||||||
<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="v7q-gT-jHT" id="3ns-bk-yQI"/>
|
|
||||||
<outlet property="delegate" destination="v7q-gT-jHT" id="fVI-Z9-gTJ"/>
|
|
||||||
</connections>
|
|
||||||
</tableView>
|
|
||||||
</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"/>
|
|
||||||
<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"/>
|
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
|
||||||
</tableHeaderView>
|
|
||||||
</scrollView>
|
|
||||||
</subviews>
|
|
||||||
</view>
|
|
||||||
<point key="canvasLocation" x="254" y="-463"/>
|
|
||||||
</window>
|
|
||||||
<customObject id="v7q-gT-jHT" customClass="GBCheatWindowController">
|
|
||||||
<connections>
|
<connections>
|
||||||
<outlet property="addressField" destination="qHx-1z-daR" id="FWo-4u-Qse"/>
|
<action selector="savePrinterFeed:" target="-2" id="Y3g-fU-2te"/>
|
||||||
<outlet property="cheatsTable" destination="tA3-8T-bxb" id="Z2r-AQ-5th"/>
|
|
||||||
<outlet property="descriptionField" destination="C6E-oI-hDC" id="tc0-gI-FBa"/>
|
|
||||||
<outlet property="document" destination="-2" id="5NX-N4-5Rt"/>
|
|
||||||
<outlet property="importCodeField" destination="X7K-nJ-alF" id="ni0-zH-XDU"/>
|
|
||||||
<outlet property="importDescriptionField" destination="KHj-uX-Wbk" id="W0E-ec-BXk"/>
|
|
||||||
<outlet property="oldValueCheckbox" destination="Kq8-6F-9GK" id="A4a-nz-KMN"/>
|
|
||||||
<outlet property="oldValueField" destination="S6O-LB-gSj" id="fAc-OW-ZC9"/>
|
|
||||||
<outlet property="valueField" destination="N3I-PP-X85" id="0zP-9x-4LQ"/>
|
|
||||||
</connections>
|
</connections>
|
||||||
</customObject>
|
<point key="canvasLocation" x="-507" y="397"/>
|
||||||
|
</button>
|
||||||
</objects>
|
</objects>
|
||||||
<resources>
|
|
||||||
<image name="NSFolder" width="32" height="32"/>
|
|
||||||
<image name="NSStopProgressFreestandingTemplate" width="14" height="14"/>
|
|
||||||
</resources>
|
|
||||||
</document>
|
</document>
|
||||||
|
@ -2,9 +2,9 @@
|
|||||||
#import <Core/gb.h>
|
#import <Core/gb.h>
|
||||||
|
|
||||||
@interface GBAudioClient : NSObject
|
@interface GBAudioClient : NSObject
|
||||||
@property (nonatomic, strong) void (^renderBlock)(UInt32 sampleRate, UInt32 nFrames, GB_sample_t *buffer);
|
@property (strong) void (^renderBlock)(UInt32 sampleRate, UInt32 nFrames, GB_sample_t *buffer);
|
||||||
@property (nonatomic, readonly) UInt32 rate;
|
@property (readonly) UInt32 rate;
|
||||||
@property (nonatomic, readonly, getter=isPlaying) bool playing;
|
@property (readonly, getter=isPlaying) bool playing;
|
||||||
-(void) start;
|
-(void) start;
|
||||||
-(void) stop;
|
-(void) stop;
|
||||||
-(id) initWithRendererBlock:(void (^)(UInt32 sampleRate, UInt32 nFrames, GB_sample_t *buffer)) block
|
-(id) initWithRendererBlock:(void (^)(UInt32 sampleRate, UInt32 nFrames, GB_sample_t *buffer)) block
|
||||||
|
@ -26,7 +26,8 @@ static OSStatus render(
|
|||||||
-(id) initWithRendererBlock:(void (^)(UInt32 sampleRate, UInt32 nFrames, GB_sample_t *buffer)) block
|
-(id) initWithRendererBlock:(void (^)(UInt32 sampleRate, UInt32 nFrames, GB_sample_t *buffer)) block
|
||||||
andSampleRate:(UInt32) rate
|
andSampleRate:(UInt32) rate
|
||||||
{
|
{
|
||||||
if (!(self = [super init])) {
|
if(!(self = [super init]))
|
||||||
|
{
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,7 +91,7 @@ static OSStatus render(
|
|||||||
{
|
{
|
||||||
OSErr err = AudioOutputUnitStart(audioUnit);
|
OSErr err = AudioOutputUnitStart(audioUnit);
|
||||||
NSAssert1(err == noErr, @"Error starting unit: %hd", err);
|
NSAssert1(err == noErr, @"Error starting unit: %hd", err);
|
||||||
_playing = true;
|
_playing = YES;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,11 +99,10 @@ static OSStatus render(
|
|||||||
-(void) stop
|
-(void) stop
|
||||||
{
|
{
|
||||||
AudioOutputUnitStop(audioUnit);
|
AudioOutputUnitStop(audioUnit);
|
||||||
_playing = false;
|
_playing = NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
-(void) dealloc
|
-(void) dealloc {
|
||||||
{
|
|
||||||
[self stop];
|
[self stop];
|
||||||
AudioUnitUninitialize(audioUnit);
|
AudioUnitUninitialize(audioUnit);
|
||||||
AudioComponentInstanceDispose(audioUnit);
|
AudioComponentInstanceDispose(audioUnit);
|
||||||
|
@ -5,12 +5,12 @@
|
|||||||
|
|
||||||
- (void)awakeFromNib
|
- (void)awakeFromNib
|
||||||
{
|
{
|
||||||
self.wantsLayer = true;
|
self.wantsLayer = YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)wantsUpdateLayer
|
- (BOOL)wantsUpdateLayer
|
||||||
{
|
{
|
||||||
return true;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)updateLayer
|
- (void)updateLayer
|
||||||
|
@ -13,22 +13,13 @@ typedef enum : NSUInteger {
|
|||||||
GBTurbo,
|
GBTurbo,
|
||||||
GBRewind,
|
GBRewind,
|
||||||
GBUnderclock,
|
GBUnderclock,
|
||||||
GBButtonCount,
|
GBButtonCount
|
||||||
GBGameBoyButtonCount = GBStart + 1,
|
|
||||||
} GBButton;
|
} GBButton;
|
||||||
|
|
||||||
extern NSString const *GBButtonNames[GBButtonCount];
|
extern NSString const *GBButtonNames[GBButtonCount];
|
||||||
|
|
||||||
static inline NSString *n2s(uint64_t number)
|
static inline NSString *button_to_preference_name(GBButton button)
|
||||||
{
|
{
|
||||||
return [NSString stringWithFormat:@"%llx", number];
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline NSString *button_to_preference_name(GBButton button, unsigned player)
|
|
||||||
{
|
|
||||||
if (player) {
|
|
||||||
return [NSString stringWithFormat:@"GBPlayer%d%@", player + 1, GBButtonNames[button]];
|
|
||||||
}
|
|
||||||
return [NSString stringWithFormat:@"GB%@", GBButtonNames[button]];
|
return [NSString stringWithFormat:@"GB%@", GBButtonNames[button]];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
#import <Cocoa/Cocoa.h>
|
|
||||||
|
|
||||||
@interface GBCheatTextFieldCell : NSTextFieldCell
|
|
||||||
@property (nonatomic) bool usesAddressFormat;
|
|
||||||
@end
|
|
@ -1,121 +0,0 @@
|
|||||||
#import "GBCheatTextFieldCell.h"
|
|
||||||
|
|
||||||
@interface GBCheatTextView : NSTextView
|
|
||||||
@property bool usesAddressFormat;
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation GBCheatTextView
|
|
||||||
|
|
||||||
- (bool)_insertText:(NSString *)string replacementRange:(NSRange)range
|
|
||||||
{
|
|
||||||
if (range.location == NSNotFound) {
|
|
||||||
range = self.selectedRange;
|
|
||||||
}
|
|
||||||
|
|
||||||
NSString *new = [self.string stringByReplacingCharactersInRange:range withString:string];
|
|
||||||
if (!self.usesAddressFormat) {
|
|
||||||
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"^(\\$[0-9A-Fa-f]{1,2}|[0-9]{1,3})$" options:0 error:NULL];
|
|
||||||
if ([regex numberOfMatchesInString:new options:0 range:NSMakeRange(0, new.length)]) {
|
|
||||||
[super insertText:string replacementRange:range];
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if ([regex numberOfMatchesInString:[@"$" stringByAppendingString:new] options:0 range:NSMakeRange(0, new.length + 1)]) {
|
|
||||||
[super insertText:string replacementRange:range];
|
|
||||||
[super insertText:@"$" replacementRange:NSMakeRange(0, 0)];
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if ([new isEqualToString:@"$"] || [string length] == 0) {
|
|
||||||
self.string = @"$00";
|
|
||||||
self.selectedRange = NSMakeRange(1, 2);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"^(\\$[0-9A-Fa-f]{1,3}:)?\\$[0-9a-fA-F]{1,4}$" options:0 error:NULL];
|
|
||||||
if ([regex numberOfMatchesInString:new options:0 range:NSMakeRange(0, new.length)]) {
|
|
||||||
[super insertText:string replacementRange:range];
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if ([string length] == 0) {
|
|
||||||
NSUInteger index = [new rangeOfString:@":"].location;
|
|
||||||
if (index != NSNotFound) {
|
|
||||||
if (range.location > index) {
|
|
||||||
self.string = [[new componentsSeparatedByString:@":"] firstObject];
|
|
||||||
self.selectedRange = NSMakeRange(self.string.length, 0);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
self.string = [[new componentsSeparatedByString:@":"] lastObject];
|
|
||||||
self.selectedRange = NSMakeRange(0, 0);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else if ([[self.string substringWithRange:range] isEqualToString:@":"]) {
|
|
||||||
self.string = [[self.string componentsSeparatedByString:@":"] lastObject];
|
|
||||||
self.selectedRange = NSMakeRange(0, 0);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ([new isEqualToString:@"$"] || [string length] == 0) {
|
|
||||||
self.string = @"$0000";
|
|
||||||
self.selectedRange = NSMakeRange(1, 4);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (([string isEqualToString:@"$"] || [string isEqualToString:@":"]) && range.length == 0 && range.location == 0) {
|
|
||||||
if ([self _insertText:@"$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]) {
|
|
||||||
self.selectedRange = NSMakeRange(self.string.length - 2, 2);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ([string isEqualToString:@"$"]) {
|
|
||||||
if ([self _insertText:@"$0" replacementRange:range]) {
|
|
||||||
self.selectedRange = NSMakeRange(range.location + 1, 1);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSUndoManager *)undoManager
|
|
||||||
{
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)insertText:(id)string replacementRange:(NSRange)replacementRange
|
|
||||||
{
|
|
||||||
if (![self _insertText:string replacementRange:replacementRange]) {
|
|
||||||
NSBeep();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Private API, don't tell the police! */
|
|
||||||
- (void)_userReplaceRange:(NSRange)range withString:(NSString *)string
|
|
||||||
{
|
|
||||||
[self insertText:string replacementRange:range];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation GBCheatTextFieldCell
|
|
||||||
{
|
|
||||||
bool _drawing, _editing;
|
|
||||||
GBCheatTextView *_fieldEditor;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSTextView *)fieldEditorForView:(NSView *)controlView
|
|
||||||
{
|
|
||||||
if (_fieldEditor) {
|
|
||||||
_fieldEditor.usesAddressFormat = self.usesAddressFormat;
|
|
||||||
return _fieldEditor;
|
|
||||||
}
|
|
||||||
_fieldEditor = [[GBCheatTextView alloc] initWithFrame:controlView.frame];
|
|
||||||
_fieldEditor.fieldEditor = true;
|
|
||||||
_fieldEditor.usesAddressFormat = self.usesAddressFormat;
|
|
||||||
return _fieldEditor;
|
|
||||||
}
|
|
||||||
@end
|
|
@ -1,16 +0,0 @@
|
|||||||
#import <Foundation/Foundation.h>
|
|
||||||
#import <AppKit/AppKit.h>
|
|
||||||
#import "Document.h"
|
|
||||||
|
|
||||||
@interface GBCheatWindowController : NSObject <NSTableViewDelegate, NSTableViewDataSource, NSTextFieldDelegate>
|
|
||||||
@property (nonatomic, weak) IBOutlet NSTableView *cheatsTable;
|
|
||||||
@property (nonatomic, weak) IBOutlet NSTextField *addressField;
|
|
||||||
@property (nonatomic, weak) IBOutlet NSTextField *valueField;
|
|
||||||
@property (nonatomic, weak) IBOutlet NSTextField *oldValueField;
|
|
||||||
@property (nonatomic, weak) IBOutlet NSButton *oldValueCheckbox;
|
|
||||||
@property (nonatomic, weak) IBOutlet NSTextField *descriptionField;
|
|
||||||
@property (nonatomic, weak) IBOutlet NSTextField *importCodeField;
|
|
||||||
@property (nonatomic, weak) IBOutlet NSTextField *importDescriptionField;
|
|
||||||
@property (nonatomic, weak) IBOutlet Document *document;
|
|
||||||
- (void)cheatsUpdated;
|
|
||||||
@end
|
|
@ -1,240 +0,0 @@
|
|||||||
#import "GBCheatWindowController.h"
|
|
||||||
#import "GBWarningPopover.h"
|
|
||||||
#import "GBCheatTextFieldCell.h"
|
|
||||||
|
|
||||||
@implementation GBCheatWindowController
|
|
||||||
|
|
||||||
+ (NSString *)addressStringFromCheat:(const GB_cheat_t *)cheat
|
|
||||||
{
|
|
||||||
if (cheat->bank != GB_CHEAT_ANY_BANK) {
|
|
||||||
return [NSString stringWithFormat:@"$%x:$%04x", cheat->bank, cheat->address];
|
|
||||||
}
|
|
||||||
return [NSString stringWithFormat:@"$%04x", cheat->address];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (NSString *)actionDescriptionForCheat:(const GB_cheat_t *)cheat
|
|
||||||
{
|
|
||||||
if (cheat->use_old_value) {
|
|
||||||
return [NSString stringWithFormat:@"[%@]($%02x) = $%02x", [self addressStringFromCheat:cheat], cheat->old_value, cheat->value];
|
|
||||||
}
|
|
||||||
return [NSString stringWithFormat:@"[%@] = $%02x", [self addressStringFromCheat:cheat], cheat->value];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
|
|
||||||
{
|
|
||||||
GB_gameboy_t *gb = self.document.gameboy;
|
|
||||||
if (!gb) return 0;
|
|
||||||
size_t cheatCount;
|
|
||||||
GB_get_cheats(gb, &cheatCount);
|
|
||||||
return cheatCount + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSCell *)tableView:(NSTableView *)tableView dataCellForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
|
|
||||||
{
|
|
||||||
GB_gameboy_t *gb = self.document.gameboy;
|
|
||||||
if (!gb) return nil;
|
|
||||||
size_t cheatCount;
|
|
||||||
GB_get_cheats(gb, &cheatCount);
|
|
||||||
NSUInteger columnIndex = [[tableView tableColumns] indexOfObject:tableColumn];
|
|
||||||
if (row >= cheatCount && columnIndex == 0) {
|
|
||||||
return [[NSCell alloc] init];
|
|
||||||
}
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (nullable id)tableView:(NSTableView *)tableView objectValueForTableColumn:(nullable NSTableColumn *)tableColumn row:(NSInteger)row
|
|
||||||
{
|
|
||||||
size_t cheatCount;
|
|
||||||
GB_gameboy_t *gb = self.document.gameboy;
|
|
||||||
if (!gb) return nil;
|
|
||||||
const GB_cheat_t *const *cheats = GB_get_cheats(gb, &cheatCount);
|
|
||||||
NSUInteger columnIndex = [[tableView tableColumns] indexOfObject:tableColumn];
|
|
||||||
if (row >= cheatCount) {
|
|
||||||
switch (columnIndex) {
|
|
||||||
case 0:
|
|
||||||
return @YES;
|
|
||||||
|
|
||||||
case 1:
|
|
||||||
return @NO;
|
|
||||||
|
|
||||||
case 2:
|
|
||||||
return @"Add Cheat...";
|
|
||||||
|
|
||||||
case 3:
|
|
||||||
return @"";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (columnIndex) {
|
|
||||||
case 0:
|
|
||||||
return @NO;
|
|
||||||
|
|
||||||
case 1:
|
|
||||||
return @(cheats[row]->enabled);
|
|
||||||
|
|
||||||
case 2:
|
|
||||||
return @(cheats[row]->description);
|
|
||||||
|
|
||||||
case 3:
|
|
||||||
return [GBCheatWindowController actionDescriptionForCheat:cheats[row]];
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)importCheat:(id)sender
|
|
||||||
{
|
|
||||||
GB_gameboy_t *gb = self.document.gameboy;
|
|
||||||
if (!gb) return;
|
|
||||||
|
|
||||||
[self.document performAtomicBlock:^{
|
|
||||||
if (GB_import_cheat(gb,
|
|
||||||
self.importCodeField.stringValue.UTF8String,
|
|
||||||
self.importDescriptionField.stringValue.UTF8String,
|
|
||||||
true)) {
|
|
||||||
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];
|
|
||||||
}
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)tableView:(NSTableView *)tableView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
|
|
||||||
{
|
|
||||||
GB_gameboy_t *gb = self.document.gameboy;
|
|
||||||
if (!gb) return;
|
|
||||||
size_t cheatCount;
|
|
||||||
const GB_cheat_t *const *cheats = GB_get_cheats(gb, &cheatCount);
|
|
||||||
NSUInteger columnIndex = [[tableView tableColumns] indexOfObject:tableColumn];
|
|
||||||
[self.document performAtomicBlock:^{
|
|
||||||
if (columnIndex == 1) {
|
|
||||||
if (row >= cheatCount) {
|
|
||||||
GB_add_cheat(gb, "New Cheat", 0, 0, 0, 0, false, true);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
GB_update_cheat(gb, cheats[row], cheats[row]->description, cheats[row]->address, cheats[row]->bank, cheats[row]->value, cheats[row]->old_value, cheats[row]->use_old_value, !cheats[row]->enabled);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (row < cheatCount) {
|
|
||||||
GB_remove_cheat(gb, cheats[row]);
|
|
||||||
}
|
|
||||||
}];
|
|
||||||
[self.cheatsTable reloadData];
|
|
||||||
[self tableViewSelectionDidChange:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)tableViewSelectionDidChange:(NSNotification *)notification
|
|
||||||
{
|
|
||||||
GB_gameboy_t *gb = self.document.gameboy;
|
|
||||||
if (!gb) return;
|
|
||||||
|
|
||||||
size_t cheatCount;
|
|
||||||
const GB_cheat_t *const *cheats = GB_get_cheats(gb, &cheatCount);
|
|
||||||
unsigned row = self.cheatsTable.selectedRow;
|
|
||||||
const GB_cheat_t *cheat = NULL;
|
|
||||||
if (row >= cheatCount) {
|
|
||||||
static const GB_cheat_t template = {
|
|
||||||
.address = 0,
|
|
||||||
.bank = 0,
|
|
||||||
.value = 0,
|
|
||||||
.old_value = 0,
|
|
||||||
.use_old_value = false,
|
|
||||||
.enabled = false,
|
|
||||||
.description = "New Cheat",
|
|
||||||
};
|
|
||||||
cheat = &template;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
cheat = cheats[row];
|
|
||||||
}
|
|
||||||
|
|
||||||
self.addressField.stringValue = [GBCheatWindowController addressStringFromCheat:cheat];
|
|
||||||
self.valueField.stringValue = [NSString stringWithFormat:@"$%02x", cheat->value];
|
|
||||||
self.oldValueField.stringValue = [NSString stringWithFormat:@"$%02x", cheat->old_value];
|
|
||||||
self.oldValueCheckbox.state = cheat->use_old_value;
|
|
||||||
self.descriptionField.stringValue = @(cheat->description);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)awakeFromNib
|
|
||||||
{
|
|
||||||
[self tableViewSelectionDidChange:nil];
|
|
||||||
((GBCheatTextFieldCell *)self.addressField.cell).usesAddressFormat = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)controlTextDidChange:(NSNotification *)obj
|
|
||||||
{
|
|
||||||
[self updateCheat:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)updateCheat:(id)sender
|
|
||||||
{
|
|
||||||
GB_gameboy_t *gb = self.document.gameboy;
|
|
||||||
if (!gb) return;
|
|
||||||
|
|
||||||
uint16_t address = 0;
|
|
||||||
uint16_t bank = GB_CHEAT_ANY_BANK;
|
|
||||||
if ([self.addressField.stringValue rangeOfString:@":"].location != NSNotFound) {
|
|
||||||
sscanf(self.addressField.stringValue.UTF8String, "$%hx:$%hx", &bank, &address);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
sscanf(self.addressField.stringValue.UTF8String, "$%hx", &address);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t value = 0;
|
|
||||||
if ([self.valueField.stringValue characterAtIndex:0] == '$') {
|
|
||||||
sscanf(self.valueField.stringValue.UTF8String, "$%02hhx", &value);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
sscanf(self.valueField.stringValue.UTF8String, "%hhd", &value);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t oldValue = 0;
|
|
||||||
if ([self.oldValueField.stringValue characterAtIndex:0] == '$') {
|
|
||||||
sscanf(self.oldValueField.stringValue.UTF8String, "$%02hhx", &oldValue);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
sscanf(self.oldValueField.stringValue.UTF8String, "%hhd", &oldValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t cheatCount;
|
|
||||||
const GB_cheat_t *const *cheats = GB_get_cheats(gb, &cheatCount);
|
|
||||||
unsigned row = self.cheatsTable.selectedRow;
|
|
||||||
|
|
||||||
[self.document performAtomicBlock:^{
|
|
||||||
if (row >= cheatCount) {
|
|
||||||
GB_add_cheat(gb,
|
|
||||||
self.descriptionField.stringValue.UTF8String,
|
|
||||||
address,
|
|
||||||
bank,
|
|
||||||
value,
|
|
||||||
oldValue,
|
|
||||||
self.oldValueCheckbox.state,
|
|
||||||
false);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
GB_update_cheat(gb,
|
|
||||||
cheats[row],
|
|
||||||
self.descriptionField.stringValue.UTF8String,
|
|
||||||
address,
|
|
||||||
bank,
|
|
||||||
value,
|
|
||||||
oldValue,
|
|
||||||
self.oldValueCheckbox.state,
|
|
||||||
cheats[row]->enabled);
|
|
||||||
}
|
|
||||||
}];
|
|
||||||
[self.cheatsTable reloadData];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)cheatsUpdated
|
|
||||||
{
|
|
||||||
[self.cheatsTable reloadData];
|
|
||||||
[self tableViewSelectionDidChange:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
@ -14,13 +14,7 @@ static inline double scale_channel(uint8_t x)
|
|||||||
- (void)setObjectValue:(id)objectValue
|
- (void)setObjectValue:(id)objectValue
|
||||||
{
|
{
|
||||||
_integerValue = [objectValue integerValue];
|
_integerValue = [objectValue integerValue];
|
||||||
uint8_t r = _integerValue & 0x1F,
|
super.objectValue = [NSString stringWithFormat:@"$%04x", (uint16_t)(_integerValue & 0x7FFF)];
|
||||||
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
|
- (NSInteger)integerValue
|
||||||
@ -36,14 +30,13 @@ static inline double scale_channel(uint8_t x)
|
|||||||
|
|
||||||
- (NSColor *) backgroundColor
|
- (NSColor *) backgroundColor
|
||||||
{
|
{
|
||||||
/* Todo: color correction */
|
|
||||||
uint16_t color = self.integerValue;
|
uint16_t color = self.integerValue;
|
||||||
return [NSColor colorWithRed:scale_channel(color) green:scale_channel(color >> 5) blue:scale_channel(color >> 10) alpha:1.0];
|
return [NSColor colorWithRed:scale_channel(color) green:scale_channel(color >> 5) blue:scale_channel(color >> 10) alpha:1.0];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)drawsBackground
|
- (BOOL)drawsBackground
|
||||||
{
|
{
|
||||||
return true;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
#import "GBView.h"
|
|
||||||
|
|
||||||
@interface GBGLShader : NSObject
|
@interface GBGLShader : NSObject
|
||||||
- (instancetype)initWithName:(NSString *) shaderName;
|
- (instancetype)initWithName:(NSString *) shaderName;
|
||||||
- (void) renderBitmap: (void *)bitmap previous:(void*) previous sized:(NSSize)srcSize inSize:(NSSize)dstSize scale: (double) scale withBlendingMode: (GB_frame_blending_mode_t)blendingMode;
|
- (void) renderBitmap: (void *)bitmap previous:(void*) previous inSize:(NSSize)size scale: (double) scale;
|
||||||
@end
|
@end
|
||||||
|
@ -21,7 +21,7 @@ void main(void) {\n\
|
|||||||
GLuint resolution_uniform;
|
GLuint resolution_uniform;
|
||||||
GLuint texture_uniform;
|
GLuint texture_uniform;
|
||||||
GLuint previous_texture_uniform;
|
GLuint previous_texture_uniform;
|
||||||
GLuint frame_blending_mode_uniform;
|
GLuint mix_previous_uniform;
|
||||||
|
|
||||||
GLuint position_attribute;
|
GLuint position_attribute;
|
||||||
GLuint texture;
|
GLuint texture;
|
||||||
@ -70,7 +70,7 @@ void main(void) {\n\
|
|||||||
glBindTexture(GL_TEXTURE_2D, 0);
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
previous_texture_uniform = glGetUniformLocation(program, "previous_image");
|
previous_texture_uniform = glGetUniformLocation(program, "previous_image");
|
||||||
|
|
||||||
frame_blending_mode_uniform = glGetUniformLocation(program, "frame_blending_mode");
|
mix_previous_uniform = glGetUniformLocation(program, "mix_previous");
|
||||||
|
|
||||||
// Configure OpenGL
|
// Configure OpenGL
|
||||||
[self configureOpenGL];
|
[self configureOpenGL];
|
||||||
@ -79,19 +79,19 @@ void main(void) {\n\
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) renderBitmap: (void *)bitmap previous:(void*) previous sized:(NSSize)srcSize inSize:(NSSize)dstSize scale: (double) scale withBlendingMode:(GB_frame_blending_mode_t)blendingMode
|
- (void) renderBitmap: (void *)bitmap previous:(void*) previous inSize:(NSSize)size scale: (double) scale
|
||||||
{
|
{
|
||||||
glUseProgram(program);
|
glUseProgram(program);
|
||||||
glUniform2f(resolution_uniform, dstSize.width * scale, dstSize.height * scale);
|
glUniform2f(resolution_uniform, size.width * scale, size.height * scale);
|
||||||
glActiveTexture(GL_TEXTURE0);
|
glActiveTexture(GL_TEXTURE0);
|
||||||
glBindTexture(GL_TEXTURE_2D, texture);
|
glBindTexture(GL_TEXTURE_2D, texture);
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, srcSize.width, srcSize.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, bitmap);
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 160, 144, 0, GL_RGBA, GL_UNSIGNED_BYTE, bitmap);
|
||||||
glUniform1i(texture_uniform, 0);
|
glUniform1i(texture_uniform, 0);
|
||||||
glUniform1i(frame_blending_mode_uniform, blendingMode);
|
glUniform1i(mix_previous_uniform, previous != NULL);
|
||||||
if (blendingMode) {
|
if (previous) {
|
||||||
glActiveTexture(GL_TEXTURE1);
|
glActiveTexture(GL_TEXTURE1);
|
||||||
glBindTexture(GL_TEXTURE_2D, previous_texture);
|
glBindTexture(GL_TEXTURE_2D, previous_texture);
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, srcSize.width, srcSize.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, previous);
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 160, 144, 0, GL_RGBA, GL_UNSIGNED_BYTE, previous);
|
||||||
glUniform1i(previous_texture_uniform, 1);
|
glUniform1i(previous_texture_uniform, 1);
|
||||||
}
|
}
|
||||||
glBindFragDataLocation(program, 0, "frag_color");
|
glBindFragDataLocation(program, 0, "frag_color");
|
||||||
@ -169,7 +169,7 @@ void main(void) {\n\
|
|||||||
+ (GLuint)shaderWithContents:(NSString*)contents type:(GLenum)type
|
+ (GLuint)shaderWithContents:(NSString*)contents type:(GLenum)type
|
||||||
{
|
{
|
||||||
|
|
||||||
const GLchar *source = [contents UTF8String];
|
const GLchar* source = [contents UTF8String];
|
||||||
// Create the shader object
|
// Create the shader object
|
||||||
GLuint shader = glCreateShader(type);
|
GLuint shader = glCreateShader(type);
|
||||||
// Load the shader source
|
// Load the shader source
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
#import <Cocoa/Cocoa.h>
|
|
||||||
|
|
||||||
@interface NSSlider (GBHueSlider)
|
|
||||||
-(NSColor *)colorValue;
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface GBHueSliderCell : NSSliderCell
|
|
||||||
-(NSColor *)colorValue;
|
|
||||||
@end
|
|
@ -1,113 +0,0 @@
|
|||||||
#import "GBHueSliderCell.h"
|
|
||||||
|
|
||||||
@interface NSSliderCell(privateAPI)
|
|
||||||
- (double)_normalizedDoubleValue;
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation GBHueSliderCell
|
|
||||||
{
|
|
||||||
bool _drawingTrack;
|
|
||||||
}
|
|
||||||
|
|
||||||
-(NSColor *)colorValue
|
|
||||||
{
|
|
||||||
double hue = self.doubleValue / 360.0;
|
|
||||||
double r = 0, g = 0, b =0 ;
|
|
||||||
double t = fmod(hue * 6, 1);
|
|
||||||
switch ((int)(hue * 6) % 6) {
|
|
||||||
case 0:
|
|
||||||
r = 1;
|
|
||||||
g = t;
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
r = 1 - t;
|
|
||||||
g = 1;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
g = 1;
|
|
||||||
b = t;
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
g = 1 - t;
|
|
||||||
b = 1;
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
b = 1;
|
|
||||||
r = t;
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
b = 1 - t;
|
|
||||||
r = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return [NSColor colorWithRed:r green:g blue:b alpha:1.0];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void)drawKnob:(NSRect)knobRect
|
|
||||||
{
|
|
||||||
[super drawKnob:knobRect];
|
|
||||||
NSRect peekRect = knobRect;
|
|
||||||
peekRect.size.width /= 2;
|
|
||||||
peekRect.size.height = peekRect.size.width;
|
|
||||||
peekRect.origin.x += peekRect.size.width / 2;
|
|
||||||
peekRect.origin.y += peekRect.size.height / 2;
|
|
||||||
NSColor *color = self.colorValue;
|
|
||||||
if (!self.enabled) {
|
|
||||||
color = [color colorWithAlphaComponent:0.5];
|
|
||||||
}
|
|
||||||
[color setFill];
|
|
||||||
NSBezierPath *path = [NSBezierPath bezierPathWithOvalInRect:peekRect];
|
|
||||||
[path fill];
|
|
||||||
[[NSColor colorWithWhite:0 alpha:0.25] setStroke];
|
|
||||||
[path setLineWidth:0.5];
|
|
||||||
[path stroke];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(double)_normalizedDoubleValue
|
|
||||||
{
|
|
||||||
if (_drawingTrack) return 0;
|
|
||||||
return [super _normalizedDoubleValue];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void)drawBarInside:(NSRect)rect flipped:(BOOL)flipped
|
|
||||||
{
|
|
||||||
if (!self.enabled) {
|
|
||||||
[super drawBarInside:rect flipped:flipped];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_drawingTrack = true;
|
|
||||||
[super drawBarInside:rect flipped:flipped];
|
|
||||||
_drawingTrack = false;
|
|
||||||
|
|
||||||
NSGradient *gradient = [[NSGradient alloc] initWithColors:@[
|
|
||||||
[NSColor redColor],
|
|
||||||
[NSColor yellowColor],
|
|
||||||
[NSColor greenColor],
|
|
||||||
[NSColor cyanColor],
|
|
||||||
[NSColor blueColor],
|
|
||||||
[NSColor magentaColor],
|
|
||||||
[NSColor redColor],
|
|
||||||
]];
|
|
||||||
|
|
||||||
rect.origin.y += rect.size.height / 2 - 0.5;
|
|
||||||
rect.size.height = 1;
|
|
||||||
rect.size.width -= 2;
|
|
||||||
rect.origin.x += 1;
|
|
||||||
[[NSColor redColor] set];
|
|
||||||
NSRectFill(rect);
|
|
||||||
|
|
||||||
rect.size.width -= self.knobThickness + 2;
|
|
||||||
rect.origin.x += self.knobThickness / 2 - 1;
|
|
||||||
|
|
||||||
[gradient drawInRect:rect angle:0];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation NSSlider (GBHueSlider)
|
|
||||||
- (NSColor *)colorValue
|
|
||||||
{
|
|
||||||
return ((GBHueSliderCell *)self.cell).colorValue;
|
|
||||||
}
|
|
||||||
@end
|
|
@ -3,7 +3,7 @@
|
|||||||
@implementation GBImageCell
|
@implementation GBImageCell
|
||||||
- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView
|
- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView
|
||||||
{
|
{
|
||||||
CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort];
|
CGContextRef context = [[NSGraphicsContext currentContext] CGContext];
|
||||||
CGContextSetInterpolationQuality(context, kCGInterpolationNone);
|
CGContextSetInterpolationQuality(context, kCGInterpolationNone);
|
||||||
[super drawWithFrame:cellFrame inView:controlView];
|
[super drawWithFrame:cellFrame inView:controlView];
|
||||||
}
|
}
|
||||||
|
@ -3,17 +3,15 @@
|
|||||||
@protocol GBImageViewDelegate;
|
@protocol GBImageViewDelegate;
|
||||||
|
|
||||||
@interface GBImageViewGridConfiguration : NSObject
|
@interface GBImageViewGridConfiguration : NSObject
|
||||||
@property (nonatomic, strong) NSColor *color;
|
@property NSColor *color;
|
||||||
@property (nonatomic) NSUInteger size;
|
@property NSUInteger size;
|
||||||
- (instancetype) initWithColor: (NSColor *) color size: (NSUInteger) size;
|
- (instancetype) initWithColor: (NSColor *) color size: (NSUInteger) size;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface GBImageView : NSImageView
|
@interface GBImageView : NSImageView
|
||||||
@property (nonatomic, strong) NSArray<GBImageViewGridConfiguration *> *horizontalGrids;
|
@property (nonatomic) NSArray *horizontalGrids;
|
||||||
@property (nonatomic, strong) NSArray<GBImageViewGridConfiguration *> *verticalGrids;
|
@property (nonatomic) NSArray *verticalGrids;
|
||||||
@property (nonatomic) bool displayScrollRect;
|
@property (weak) IBOutlet id<GBImageViewDelegate> delegate;
|
||||||
@property NSRect scrollRect;
|
|
||||||
@property (nonatomic, weak) IBOutlet id<GBImageViewDelegate> delegate;
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@protocol GBImageViewDelegate <NSObject>
|
@protocol GBImageViewDelegate <NSObject>
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
}
|
}
|
||||||
- (void)drawRect:(NSRect)dirtyRect
|
- (void)drawRect:(NSRect)dirtyRect
|
||||||
{
|
{
|
||||||
CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort];
|
CGContextRef context = [[NSGraphicsContext currentContext] CGContext];
|
||||||
CGContextSetInterpolationQuality(context, kCGInterpolationNone);
|
CGContextSetInterpolationQuality(context, kCGInterpolationNone);
|
||||||
[super drawRect:dirtyRect];
|
[super drawRect:dirtyRect];
|
||||||
CGFloat y_ratio = self.frame.size.height / self.image.size.height;
|
CGFloat y_ratio = self.frame.size.height / self.image.size.height;
|
||||||
@ -25,8 +25,8 @@
|
|||||||
[conf.color set];
|
[conf.color set];
|
||||||
for (CGFloat y = conf.size * y_ratio; y < self.frame.size.height; y += conf.size * y_ratio) {
|
for (CGFloat y = conf.size * y_ratio; y < self.frame.size.height; y += conf.size * y_ratio) {
|
||||||
NSBezierPath *line = [NSBezierPath bezierPath];
|
NSBezierPath *line = [NSBezierPath bezierPath];
|
||||||
[line moveToPoint:NSMakePoint(0, y - 0.5)];
|
[line moveToPoint:NSMakePoint(0, y + 0.5)];
|
||||||
[line lineToPoint:NSMakePoint(self.frame.size.width, y - 0.5)];
|
[line lineToPoint:NSMakePoint(self.frame.size.width, y + 0.5)];
|
||||||
[line setLineWidth:1.0];
|
[line setLineWidth:1.0];
|
||||||
[line stroke];
|
[line stroke];
|
||||||
}
|
}
|
||||||
@ -42,35 +42,6 @@
|
|||||||
[line stroke];
|
[line stroke];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (self.displayScrollRect) {
|
|
||||||
NSBezierPath *path = [NSBezierPath bezierPathWithRect:CGRectInfinite];
|
|
||||||
for (unsigned x = 0; x < 2; x++) {
|
|
||||||
for (unsigned y = 0; y < 2; y++) {
|
|
||||||
NSRect rect = self.scrollRect;
|
|
||||||
rect.origin.x *= x_ratio;
|
|
||||||
rect.origin.y *= y_ratio;
|
|
||||||
rect.size.width *= x_ratio;
|
|
||||||
rect.size.height *= y_ratio;
|
|
||||||
rect.origin.y = self.frame.size.height - rect.origin.y - rect.size.height;
|
|
||||||
|
|
||||||
rect.origin.x -= self.frame.size.width * x;
|
|
||||||
rect.origin.y += self.frame.size.height * y;
|
|
||||||
|
|
||||||
|
|
||||||
NSBezierPath *subpath = [NSBezierPath bezierPathWithRect:rect];
|
|
||||||
[path appendBezierPath:subpath];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
[path setWindingRule:NSEvenOddWindingRule];
|
|
||||||
[path setLineWidth:4.0];
|
|
||||||
[path setLineJoinStyle:NSRoundLineJoinStyle];
|
|
||||||
[[NSColor colorWithWhite:0.2 alpha:0.5] set];
|
|
||||||
[path fill];
|
|
||||||
[path addClip];
|
|
||||||
[[NSColor colorWithWhite:0.0 alpha:0.6] set];
|
|
||||||
[path stroke];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setHorizontalGrids:(NSArray *)horizontalGrids
|
- (void)setHorizontalGrids:(NSArray *)horizontalGrids
|
||||||
@ -85,15 +56,9 @@
|
|||||||
[self setNeedsDisplay];
|
[self setNeedsDisplay];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setDisplayScrollRect:(bool)displayScrollRect
|
|
||||||
{
|
|
||||||
self->_displayScrollRect = displayScrollRect;
|
|
||||||
[self setNeedsDisplay];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)updateTrackingAreas
|
- (void)updateTrackingAreas
|
||||||
{
|
{
|
||||||
if (trackingArea != nil) {
|
if(trackingArea != nil) {
|
||||||
[self removeTrackingArea:trackingArea];
|
[self removeTrackingArea:trackingArea];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
8
Cocoa/GBJoystickListener.h
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#import <AppKit/AppKit.h>
|
||||||
|
|
||||||
|
@protocol GBJoystickListener <NSObject>
|
||||||
|
|
||||||
|
- (void) joystick:(NSString *)joystick_name button: (unsigned)button changedState: (bool) state;
|
||||||
|
- (void) joystick:(NSString *)joystick_name axis: (unsigned)axis movedTo: (signed) value;
|
||||||
|
|
||||||
|
@end
|
@ -12,6 +12,6 @@ typedef enum {
|
|||||||
|
|
||||||
@interface GBMemoryByteArray : HFByteArray
|
@interface GBMemoryByteArray : HFByteArray
|
||||||
- (instancetype) initWithDocument:(Document *)document;
|
- (instancetype) initWithDocument:(Document *)document;
|
||||||
@property (nonatomic) uint16_t selectedBank;
|
@property uint16_t selectedBank;
|
||||||
@property (nonatomic) GB_memory_mode_t mode;
|
@property GB_memory_mode_t mode;
|
||||||
@end
|
@end
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
#import <Cocoa/Cocoa.h>
|
|
||||||
|
|
||||||
@interface GBOSDView : NSView
|
|
||||||
@property bool usesSGBScale;
|
|
||||||
- (void)displayText:(NSString *)text;
|
|
||||||
@end
|
|
@ -1,104 +0,0 @@
|
|||||||
#import "GBOSDView.h"
|
|
||||||
|
|
||||||
@implementation GBOSDView
|
|
||||||
{
|
|
||||||
bool _usesSGBScale;
|
|
||||||
NSString *_text;
|
|
||||||
double _animation;
|
|
||||||
NSTimer *_timer;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setUsesSGBScale:(bool)usesSGBScale
|
|
||||||
{
|
|
||||||
_usesSGBScale = usesSGBScale;
|
|
||||||
[self setNeedsDisplay:true];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (bool)usesSGBScale
|
|
||||||
{
|
|
||||||
return _usesSGBScale;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)displayText:(NSString *)text
|
|
||||||
{
|
|
||||||
if (![[NSUserDefaults standardUserDefaults] boolForKey:@"GBOSDEnabled"]) return;
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
|
||||||
if (![_text isEqualToString:text]) {
|
|
||||||
[self setNeedsDisplay:true];
|
|
||||||
}
|
|
||||||
_text = text;
|
|
||||||
self.alphaValue = 1.0;
|
|
||||||
_animation = 2.5;
|
|
||||||
// Longer strings should appear longer
|
|
||||||
if ([_text rangeOfString:@"\n"].location != NSNotFound) {
|
|
||||||
_animation += 4;
|
|
||||||
}
|
|
||||||
[_timer invalidate];
|
|
||||||
self.hidden = false;
|
|
||||||
_timer = [NSTimer scheduledTimerWithTimeInterval:0.025 target:self selector:@selector(animate) userInfo:nil repeats:true];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)animate
|
|
||||||
{
|
|
||||||
_animation -= 0.1;
|
|
||||||
if (_animation < 1.0) {
|
|
||||||
self.alphaValue = _animation;
|
|
||||||
};
|
|
||||||
if (_animation == 0) {
|
|
||||||
self.hidden = true;
|
|
||||||
[_timer invalidate];
|
|
||||||
_text = nil;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)drawRect:(NSRect)dirtyRect
|
|
||||||
{
|
|
||||||
[super drawRect:dirtyRect];
|
|
||||||
if (!_text.length) return;
|
|
||||||
|
|
||||||
double fontSize = 8;
|
|
||||||
NSSize size = self.frame.size;
|
|
||||||
if (_usesSGBScale) {
|
|
||||||
fontSize *= MIN(size.width / 256, size.height / 224);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
fontSize *= MIN(size.width / 160, size.height / 144);
|
|
||||||
}
|
|
||||||
|
|
||||||
NSFont *font = [NSFont boldSystemFontOfSize:fontSize];
|
|
||||||
|
|
||||||
/* The built in stroke attribute uses an inside stroke, which is typographically terrible.
|
|
||||||
We'll use a naïve manual stroke instead which looks better. */
|
|
||||||
|
|
||||||
NSDictionary *attributes = @{
|
|
||||||
NSFontAttributeName: font,
|
|
||||||
NSForegroundColorAttributeName: [NSColor blackColor],
|
|
||||||
};
|
|
||||||
|
|
||||||
NSAttributedString *text = [[NSAttributedString alloc] initWithString:_text attributes:attributes];
|
|
||||||
|
|
||||||
[text drawAtPoint:NSMakePoint(fontSize + 1, fontSize + 0)];
|
|
||||||
[text drawAtPoint:NSMakePoint(fontSize - 1, fontSize + 0)];
|
|
||||||
[text drawAtPoint:NSMakePoint(fontSize + 0, fontSize + 1)];
|
|
||||||
[text drawAtPoint:NSMakePoint(fontSize + 0, fontSize - 1)];
|
|
||||||
|
|
||||||
// The uses of sqrt(2)/2, which is more correct, results in severe ugly-looking rounding errors
|
|
||||||
if (self.window.screen.backingScaleFactor > 1) {
|
|
||||||
[text drawAtPoint:NSMakePoint(fontSize + 0.5, fontSize + 0.5)];
|
|
||||||
[text drawAtPoint:NSMakePoint(fontSize - 0.5, fontSize + 0.5)];
|
|
||||||
[text drawAtPoint:NSMakePoint(fontSize - 0.5, fontSize - 0.5)];
|
|
||||||
[text drawAtPoint:NSMakePoint(fontSize + 0.5, fontSize - 0.5)];
|
|
||||||
}
|
|
||||||
|
|
||||||
attributes = @{
|
|
||||||
NSFontAttributeName: font,
|
|
||||||
NSForegroundColorAttributeName: [NSColor whiteColor],
|
|
||||||
};
|
|
||||||
|
|
||||||
text = [[NSAttributedString alloc] initWithString:_text attributes:attributes];
|
|
||||||
|
|
||||||
[text drawAtPoint:NSMakePoint(fontSize, fontSize)];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
@ -2,5 +2,5 @@
|
|||||||
#import "GBGLShader.h"
|
#import "GBGLShader.h"
|
||||||
|
|
||||||
@interface GBOpenGLView : NSOpenGLView
|
@interface GBOpenGLView : NSOpenGLView
|
||||||
@property (nonatomic) GBGLShader *shader;
|
@property GBGLShader *shader;
|
||||||
@end
|
@end
|
||||||
|
@ -4,8 +4,7 @@
|
|||||||
|
|
||||||
@implementation GBOpenGLView
|
@implementation GBOpenGLView
|
||||||
|
|
||||||
- (void)drawRect:(NSRect)dirtyRect
|
- (void)drawRect:(NSRect)dirtyRect {
|
||||||
{
|
|
||||||
if (!self.shader) {
|
if (!self.shader) {
|
||||||
self.shader = [[GBGLShader alloc] initWithName:[[NSUserDefaults standardUserDefaults] objectForKey:@"GBFilter"]];
|
self.shader = [[GBGLShader alloc] initWithName:[[NSUserDefaults standardUserDefaults] objectForKey:@"GBFilter"]];
|
||||||
}
|
}
|
||||||
@ -14,13 +13,17 @@
|
|||||||
double scale = self.window.backingScaleFactor;
|
double scale = self.window.backingScaleFactor;
|
||||||
glViewport(0, 0, self.bounds.size.width * scale, self.bounds.size.height * scale);
|
glViewport(0, 0, self.bounds.size.width * scale, self.bounds.size.height * scale);
|
||||||
|
|
||||||
if (gbview.gb) {
|
if (gbview.shouldBlendFrameWithPrevious) {
|
||||||
[self.shader renderBitmap:gbview.currentBuffer
|
[self.shader renderBitmap:gbview.currentBuffer
|
||||||
previous:gbview.frameBlendingMode? gbview.previousBuffer : NULL
|
previous:gbview.previousBuffer
|
||||||
sized:NSMakeSize(GB_get_screen_width(gbview.gb), GB_get_screen_height(gbview.gb))
|
|
||||||
inSize:self.bounds.size
|
inSize:self.bounds.size
|
||||||
scale:scale
|
scale:scale];
|
||||||
withBlendingMode:gbview.frameBlendingMode];
|
}
|
||||||
|
else {
|
||||||
|
[self.shader renderBitmap:gbview.currentBuffer
|
||||||
|
previous:NULL
|
||||||
|
inSize:self.bounds.size
|
||||||
|
scale:scale];
|
||||||
}
|
}
|
||||||
glFlush();
|
glFlush();
|
||||||
}
|
}
|
||||||
@ -34,6 +37,6 @@
|
|||||||
- (void) filterChanged
|
- (void) filterChanged
|
||||||
{
|
{
|
||||||
self.shader = nil;
|
self.shader = nil;
|
||||||
[self setNeedsDisplay:true];
|
[self setNeedsDisplay:YES];
|
||||||
}
|
}
|
||||||
@end
|
@end
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
#import <Cocoa/Cocoa.h>
|
|
||||||
|
|
||||||
/* Fake interface so the compiler assumes it conforms to NSVisualEffectView */
|
|
||||||
@interface GBOptionalVisualEffectView : NSVisualEffectView
|
|
||||||
|
|
||||||
@end
|
|
@ -1,18 +0,0 @@
|
|||||||
#import <Cocoa/Cocoa.h>
|
|
||||||
|
|
||||||
@interface GBOptionalVisualEffectView : NSView
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation GBOptionalVisualEffectView
|
|
||||||
|
|
||||||
+ (instancetype)allocWithZone:(struct _NSZone *)zone
|
|
||||||
{
|
|
||||||
Class NSVisualEffectView = NSClassFromString(@"NSVisualEffectView");
|
|
||||||
if (NSVisualEffectView) {
|
|
||||||
return (id)[NSVisualEffectView alloc];
|
|
||||||
}
|
|
||||||
return [super allocWithZone:zone];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
@ -1,18 +0,0 @@
|
|||||||
#import <AppKit/AppKit.h>
|
|
||||||
#import <Core/gb.h>
|
|
||||||
|
|
||||||
@interface GBPaletteEditorController : NSObject<NSTableViewDataSource, NSTableViewDelegate>
|
|
||||||
@property (weak) IBOutlet NSColorWell *colorWell0;
|
|
||||||
@property (weak) IBOutlet NSColorWell *colorWell1;
|
|
||||||
@property (weak) IBOutlet NSColorWell *colorWell2;
|
|
||||||
@property (weak) IBOutlet NSColorWell *colorWell3;
|
|
||||||
@property (weak) IBOutlet NSColorWell *colorWell4;
|
|
||||||
@property (weak) IBOutlet NSButton *disableLCDColorCheckbox;
|
|
||||||
@property (weak) IBOutlet NSButton *manualModeCheckbox;
|
|
||||||
@property (weak) IBOutlet NSSlider *brightnessSlider;
|
|
||||||
@property (weak) IBOutlet NSSlider *hueSlider;
|
|
||||||
@property (weak) IBOutlet NSSlider *hueStrengthSlider;
|
|
||||||
@property (weak) IBOutlet NSTableView *themesList;
|
|
||||||
@property (weak) IBOutlet NSMenu *menu;
|
|
||||||
+ (const GB_palette_t *)userPalette;
|
|
||||||
@end
|
|
@ -1,378 +0,0 @@
|
|||||||
#import "GBPaletteEditorController.h"
|
|
||||||
#import "GBHueSliderCell.h"
|
|
||||||
#import <Core/gb.h>
|
|
||||||
|
|
||||||
#define MAGIC 'SBPL'
|
|
||||||
|
|
||||||
typedef struct __attribute__ ((packed)) {
|
|
||||||
uint32_t magic;
|
|
||||||
bool manual:1;
|
|
||||||
bool disabled_lcd_color:1;
|
|
||||||
unsigned padding:6;
|
|
||||||
struct GB_color_s colors[5];
|
|
||||||
int32_t brightness_bias;
|
|
||||||
uint32_t hue_bias;
|
|
||||||
uint32_t hue_bias_strength;
|
|
||||||
} theme_t;
|
|
||||||
|
|
||||||
static double blend(double from, double to, double position)
|
|
||||||
{
|
|
||||||
return from * (1 - position) + to * position;
|
|
||||||
}
|
|
||||||
|
|
||||||
@implementation NSColor (GBColor)
|
|
||||||
|
|
||||||
- (struct GB_color_s)gbColor
|
|
||||||
{
|
|
||||||
NSColor *sRGB = [self colorUsingColorSpace:[NSColorSpace deviceRGBColorSpace]];
|
|
||||||
return (struct GB_color_s){round(sRGB.redComponent * 255), round(sRGB.greenComponent * 255), round(sRGB.blueComponent * 255)};
|
|
||||||
}
|
|
||||||
|
|
||||||
- (uint32_t)intValue
|
|
||||||
{
|
|
||||||
struct GB_color_s color = self.gbColor;
|
|
||||||
return (color.r << 0) | (color.g << 8) | (color.b << 16) | 0xFF000000;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation GBPaletteEditorController
|
|
||||||
|
|
||||||
- (NSArray<NSColorWell *> *)colorWells
|
|
||||||
{
|
|
||||||
return @[_colorWell0, _colorWell1, _colorWell2, _colorWell3, _colorWell4];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)updateEnabledControls
|
|
||||||
{
|
|
||||||
if (self.manualModeCheckbox.state) {
|
|
||||||
_brightnessSlider.enabled = false;
|
|
||||||
_hueSlider.enabled = false;
|
|
||||||
_hueStrengthSlider.enabled = false;
|
|
||||||
_colorWell1.enabled = true;
|
|
||||||
_colorWell2.enabled = true;
|
|
||||||
_colorWell3.enabled = true;
|
|
||||||
if (!(_colorWell4.enabled = self.disableLCDColorCheckbox.state)) {
|
|
||||||
_colorWell4.color = _colorWell3.color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
_colorWell1.enabled = false;
|
|
||||||
_colorWell2.enabled = false;
|
|
||||||
_colorWell3.enabled = false;
|
|
||||||
_colorWell4.enabled = true;
|
|
||||||
_brightnessSlider.enabled = true;
|
|
||||||
_hueSlider.enabled = true;
|
|
||||||
_hueStrengthSlider.enabled = true;
|
|
||||||
[self updateAutoColors];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSColor *)autoColorAtPositon:(double)position
|
|
||||||
{
|
|
||||||
NSColor *first = [_colorWell0.color colorUsingColorSpace:[NSColorSpace deviceRGBColorSpace]];
|
|
||||||
NSColor *second = [_colorWell4.color colorUsingColorSpace:[NSColorSpace deviceRGBColorSpace]];
|
|
||||||
double brightness = 1 / pow(4, (_brightnessSlider.doubleValue - 128) / 128.0);
|
|
||||||
position = pow(position, brightness);
|
|
||||||
NSColor *hue = _hueSlider.colorValue;
|
|
||||||
double bias = _hueStrengthSlider.doubleValue / 256.0;
|
|
||||||
double red = 1 / pow(4, (hue.redComponent * 2 - 1) * bias);
|
|
||||||
double green = 1 / pow(4, (hue.greenComponent * 2 - 1) * bias);
|
|
||||||
double blue = 1 / pow(4, (hue.blueComponent * 2 - 1) * bias);
|
|
||||||
NSColor *ret = [NSColor colorWithRed:blend(first.redComponent, second.redComponent, pow(position, red))
|
|
||||||
green:blend(first.greenComponent, second.greenComponent, pow(position, green))
|
|
||||||
blue:blend(first.blueComponent, second.blueComponent, pow(position, blue))
|
|
||||||
alpha:1.0];
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)updateAutoColors:(id)sender
|
|
||||||
{
|
|
||||||
if (!self.manualModeCheckbox.state) {
|
|
||||||
[self updateAutoColors];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
[self savePalette:sender];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)updateAutoColors
|
|
||||||
{
|
|
||||||
if (_disableLCDColorCheckbox.state) {
|
|
||||||
_colorWell1.color = [self autoColorAtPositon:8 / 25.0];
|
|
||||||
_colorWell2.color = [self autoColorAtPositon:16 / 25.0];
|
|
||||||
_colorWell3.color = [self autoColorAtPositon:24 / 25.0];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
_colorWell1.color = [self autoColorAtPositon:1 / 3.0];
|
|
||||||
_colorWell2.color = [self autoColorAtPositon:2 / 3.0];
|
|
||||||
_colorWell3.color = _colorWell4.color;
|
|
||||||
}
|
|
||||||
[self savePalette:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)disabledLCDColorCheckboxChanged:(id)sender
|
|
||||||
{
|
|
||||||
[self updateEnabledControls];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)manualModeChanged:(id)sender
|
|
||||||
{
|
|
||||||
[self updateEnabledControls];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)updateColor4:(id)sender
|
|
||||||
{
|
|
||||||
if (!self.disableLCDColorCheckbox.state) {
|
|
||||||
self.colorWell4.color = self.colorWell3.color;
|
|
||||||
}
|
|
||||||
[self savePalette:self];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
|
|
||||||
{
|
|
||||||
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
|
||||||
NSDictionary *themes = [defaults dictionaryForKey:@"GBThemes"];
|
|
||||||
if (themes.count == 0) {
|
|
||||||
[defaults setObject:@"Untitled Palette" forKey:@"GBCurrentTheme"];
|
|
||||||
[self savePalette:nil];
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return themes.count;
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void)tableView:(NSTableView *)tableView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
|
|
||||||
{
|
|
||||||
NSString *oldName = [self tableView:tableView objectValueForTableColumn:tableColumn row:row];
|
|
||||||
if ([oldName isEqualToString:object]) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
|
||||||
NSMutableDictionary *themes = [[defaults dictionaryForKey:@"GBThemes"] ?: @{} mutableCopy];
|
|
||||||
NSString *newName = object;
|
|
||||||
unsigned i = 2;
|
|
||||||
if (!newName.length) {
|
|
||||||
newName = @"Untitled Palette";
|
|
||||||
}
|
|
||||||
while (themes[newName]) {
|
|
||||||
newName = [NSString stringWithFormat:@"%@ %d", object, i];
|
|
||||||
}
|
|
||||||
themes[newName] = themes[oldName];
|
|
||||||
[themes removeObjectForKey:oldName];
|
|
||||||
if ([oldName isEqualToString:[defaults stringForKey:@"GBCurrentTheme"]]) {
|
|
||||||
[defaults setObject:newName forKey:@"GBCurrentTheme"];
|
|
||||||
}
|
|
||||||
[defaults setObject:themes forKey:@"GBThemes"];
|
|
||||||
[tableView reloadData];
|
|
||||||
[self awakeFromNib];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)deleteTheme:(id)sender
|
|
||||||
{
|
|
||||||
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
|
||||||
NSString *name = [defaults stringForKey:@"GBCurrentTheme"];
|
|
||||||
NSMutableDictionary *themes = [[defaults dictionaryForKey:@"GBThemes"] ?: @{} mutableCopy];
|
|
||||||
[themes removeObjectForKey:name];
|
|
||||||
[defaults setObject:themes forKey:@"GBThemes"];
|
|
||||||
[_themesList reloadData];
|
|
||||||
[self awakeFromNib];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)tableViewSelectionDidChange:(NSNotification *)notification
|
|
||||||
{
|
|
||||||
NSString *name = [self tableView:nil objectValueForTableColumn:nil row:_themesList.selectedRow];
|
|
||||||
[[NSUserDefaults standardUserDefaults] setObject:name forKey:@"GBCurrentTheme"];
|
|
||||||
[self loadPalette];
|
|
||||||
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBColorPaletteChanged" object:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)tableViewSelectionIsChanging:(NSNotification *)notification
|
|
||||||
{
|
|
||||||
[self tableViewSelectionDidChange:notification];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)awakeFromNib
|
|
||||||
{
|
|
||||||
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
|
||||||
NSDictionary *themes = [defaults dictionaryForKey:@"GBThemes"];
|
|
||||||
NSString *theme = [defaults stringForKey:@"GBCurrentTheme"];
|
|
||||||
if (theme && themes[theme]) {
|
|
||||||
unsigned index = [[themes.allKeys sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)] indexOfObject:theme];
|
|
||||||
[_themesList selectRowIndexes:[NSIndexSet indexSetWithIndex:index] byExtendingSelection:false];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
[_themesList selectRowIndexes:[NSIndexSet indexSetWithIndex:0] byExtendingSelection:false];
|
|
||||||
}
|
|
||||||
[self tableViewSelectionDidChange:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)addTheme:(id)sender
|
|
||||||
{
|
|
||||||
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
|
||||||
NSDictionary *themes = [defaults dictionaryForKey:@"GBThemes"];
|
|
||||||
NSString *newName = @"Untitled Palette";
|
|
||||||
unsigned i = 2;
|
|
||||||
while (themes[newName]) {
|
|
||||||
newName = [NSString stringWithFormat:@"Untitled Palette %d", i++];
|
|
||||||
}
|
|
||||||
[defaults setObject:newName forKey:@"GBCurrentTheme"];
|
|
||||||
[self savePalette:sender];
|
|
||||||
[_themesList reloadData];
|
|
||||||
[self awakeFromNib];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
|
|
||||||
{
|
|
||||||
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
|
||||||
NSDictionary *themes = [defaults dictionaryForKey:@"GBThemes"];
|
|
||||||
return [themes.allKeys sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)][row];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)loadPalette
|
|
||||||
{
|
|
||||||
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
|
||||||
NSDictionary *theme = [defaults dictionaryForKey:@"GBThemes"][[defaults stringForKey:@"GBCurrentTheme"]];
|
|
||||||
NSArray *colors = theme[@"Colors"];
|
|
||||||
if (colors.count == 5) {
|
|
||||||
unsigned i = 0;
|
|
||||||
for (NSNumber *color in colors) {
|
|
||||||
uint32_t c = [color unsignedIntValue];
|
|
||||||
self.colorWells[i++].color = [NSColor colorWithRed:(c & 0xFF) / 255.0
|
|
||||||
green:((c >> 8) & 0xFF) / 255.0
|
|
||||||
blue:((c >> 16) & 0xFF) / 255.0
|
|
||||||
alpha:1.0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_disableLCDColorCheckbox.state = [theme[@"DisabledLCDColor"] boolValue];
|
|
||||||
_manualModeCheckbox.state = [theme[@"Manual"] boolValue];
|
|
||||||
_brightnessSlider.doubleValue = [theme[@"BrightnessBias"] doubleValue] * 128 + 128;
|
|
||||||
_hueSlider.doubleValue = [theme[@"HueBias"] doubleValue] * 360;
|
|
||||||
_hueStrengthSlider.doubleValue = [theme[@"HueBiasStrength"] doubleValue] * 256;
|
|
||||||
[self updateEnabledControls];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)savePalette:(id)sender
|
|
||||||
{
|
|
||||||
NSDictionary *theme = @{
|
|
||||||
@"Colors":
|
|
||||||
@[@(_colorWell0.color.intValue),
|
|
||||||
@(_colorWell1.color.intValue),
|
|
||||||
@(_colorWell2.color.intValue),
|
|
||||||
@(_colorWell3.color.intValue),
|
|
||||||
@(_colorWell4.color.intValue)],
|
|
||||||
@"DisabledLCDColor": _disableLCDColorCheckbox.state? @YES : @NO,
|
|
||||||
@"Manual": _manualModeCheckbox.state? @YES : @NO,
|
|
||||||
@"BrightnessBias": @((_brightnessSlider.doubleValue - 128) / 128.0),
|
|
||||||
@"HueBias": @(_hueSlider.doubleValue / 360.0),
|
|
||||||
@"HueBiasStrength": @(_hueStrengthSlider.doubleValue / 256.0)
|
|
||||||
};
|
|
||||||
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
|
||||||
NSMutableDictionary *themes = [[defaults dictionaryForKey:@"GBThemes"] ?: @{} mutableCopy];
|
|
||||||
themes[[defaults stringForKey:@"GBCurrentTheme"]] = theme;
|
|
||||||
[defaults setObject:themes forKey:@"GBThemes"];
|
|
||||||
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBColorPaletteChanged" object:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (const GB_palette_t *)userPalette
|
|
||||||
{
|
|
||||||
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
|
||||||
switch ([defaults integerForKey:@"GBColorPalette"]) {
|
|
||||||
case 1: return &GB_PALETTE_DMG;
|
|
||||||
case 2: return &GB_PALETTE_MGB;
|
|
||||||
case 3: return &GB_PALETTE_GBL;
|
|
||||||
default: return &GB_PALETTE_GREY;
|
|
||||||
case -1: {
|
|
||||||
static GB_palette_t customPalette;
|
|
||||||
NSArray *colors = [defaults dictionaryForKey:@"GBThemes"][[defaults stringForKey:@"GBCurrentTheme"]][@"Colors"];
|
|
||||||
if (colors.count == 5) {
|
|
||||||
unsigned i = 0;
|
|
||||||
for (NSNumber *color in colors) {
|
|
||||||
uint32_t c = [color unsignedIntValue];
|
|
||||||
customPalette.colors[i++] = (struct GB_color_s) {c, c >> 8, c >> 16};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return &customPalette;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)export:(id)sender
|
|
||||||
{
|
|
||||||
NSSavePanel *savePanel = [NSSavePanel savePanel];
|
|
||||||
[savePanel setAllowedFileTypes:@[@"sbp"]];
|
|
||||||
savePanel.nameFieldStringValue = [NSString stringWithFormat:@"%@.sbp", [[NSUserDefaults standardUserDefaults] stringForKey:@"GBCurrentTheme"]];
|
|
||||||
if ([savePanel runModal] == NSModalResponseOK) {
|
|
||||||
theme_t theme = {0,};
|
|
||||||
theme.magic = MAGIC;
|
|
||||||
theme.manual = _manualModeCheckbox.state;
|
|
||||||
theme.disabled_lcd_color = _disableLCDColorCheckbox.state;
|
|
||||||
unsigned i = 0;
|
|
||||||
for (NSColorWell *well in self.colorWells) {
|
|
||||||
theme.colors[i++] = well.color.gbColor;
|
|
||||||
}
|
|
||||||
theme.brightness_bias = (_brightnessSlider.doubleValue - 128) * (0x40000000 / 128);
|
|
||||||
theme.hue_bias = round(_hueSlider.doubleValue * (0x80000000 / 360.0));
|
|
||||||
theme.hue_bias_strength = (_hueStrengthSlider.doubleValue) * (0x80000000 / 256);
|
|
||||||
size_t size = sizeof(theme);
|
|
||||||
if (theme.manual) {
|
|
||||||
size = theme.disabled_lcd_color? 5 + 5 * sizeof(theme.colors[0]) : 5 + 4 * sizeof(theme.colors[0]);
|
|
||||||
}
|
|
||||||
[[NSData dataWithBytes:&theme length:size] writeToURL:savePanel.URL atomically:false];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)import:(id)sender
|
|
||||||
{
|
|
||||||
NSOpenPanel *openPanel = [NSOpenPanel openPanel];
|
|
||||||
[openPanel setAllowedFileTypes:@[@"sbp"]];
|
|
||||||
if ([openPanel runModal] == NSModalResponseOK) {
|
|
||||||
NSData *data = [NSData dataWithContentsOfURL:openPanel.URL];
|
|
||||||
theme_t theme = {0,};
|
|
||||||
memcpy(&theme, data.bytes, MIN(sizeof(theme), data.length));
|
|
||||||
if (theme.magic != MAGIC) {
|
|
||||||
NSBeep();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_manualModeCheckbox.state = theme.manual;
|
|
||||||
_disableLCDColorCheckbox.state = theme.disabled_lcd_color;
|
|
||||||
unsigned i = 0;
|
|
||||||
for (NSColorWell *well in self.colorWells) {
|
|
||||||
well.color = [NSColor colorWithRed:theme.colors[i].r / 255.0
|
|
||||||
green:theme.colors[i].g / 255.0
|
|
||||||
blue:theme.colors[i].b / 255.0
|
|
||||||
alpha:1.0];
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
if (!theme.disabled_lcd_color) {
|
|
||||||
_colorWell4.color = _colorWell3.color;
|
|
||||||
}
|
|
||||||
_brightnessSlider.doubleValue = theme.brightness_bias / (0x40000000 / 128.0) + 128;
|
|
||||||
_hueSlider.doubleValue = theme.hue_bias / (0x80000000 / 360.0);
|
|
||||||
_hueStrengthSlider.doubleValue = theme.hue_bias_strength / (0x80000000 / 256.0);
|
|
||||||
|
|
||||||
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
|
||||||
NSDictionary *themes = [defaults dictionaryForKey:@"GBThemes"];
|
|
||||||
NSString *baseName = openPanel.URL.lastPathComponent.stringByDeletingPathExtension;
|
|
||||||
NSString *newName = baseName;
|
|
||||||
i = 2;
|
|
||||||
while (themes[newName]) {
|
|
||||||
newName = [NSString stringWithFormat:@"%@ %d", baseName, i++];
|
|
||||||
}
|
|
||||||
[defaults setObject:newName forKey:@"GBCurrentTheme"];
|
|
||||||
[self savePalette:sender];
|
|
||||||
[self awakeFromNib];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)done:(NSButton *)sender
|
|
||||||
{
|
|
||||||
[sender.window.sheetParent endSheet:sender.window];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (instancetype)init
|
|
||||||
{
|
|
||||||
static id singleton = nil;
|
|
||||||
if (singleton) return singleton;
|
|
||||||
return (singleton = [super init]);
|
|
||||||
}
|
|
||||||
@end
|
|
@ -1,37 +1,15 @@
|
|||||||
#import <Cocoa/Cocoa.h>
|
#import <Cocoa/Cocoa.h>
|
||||||
#import <JoyKit/JoyKit.h>
|
#import "GBJoystickListener.h"
|
||||||
#import "GBPaletteEditorController.h"
|
|
||||||
|
|
||||||
@interface GBPreferencesWindow : NSWindow <NSTableViewDelegate, NSTableViewDataSource, JOYListener>
|
@interface GBPreferencesWindow : NSWindow <NSTableViewDelegate, NSTableViewDataSource, GBJoystickListener>
|
||||||
@property (nonatomic, strong) IBOutlet NSTableView *controlsTableView;
|
@property IBOutlet NSTableView *controlsTableView;
|
||||||
@property (nonatomic, strong) IBOutlet NSPopUpButton *graphicsFilterPopupButton;
|
@property IBOutlet NSPopUpButton *graphicsFilterPopupButton;
|
||||||
@property (nonatomic, strong) IBOutlet NSButton *analogControlsCheckbox;
|
@property (strong) IBOutlet NSButton *aspectRatioCheckbox;
|
||||||
@property (nonatomic, strong) IBOutlet NSButton *aspectRatioCheckbox;
|
@property (strong) IBOutlet NSPopUpButton *highpassFilterPopupButton;
|
||||||
@property (nonatomic, strong) IBOutlet NSPopUpButton *highpassFilterPopupButton;
|
@property (strong) IBOutlet NSPopUpButton *colorCorrectionPopupButton;
|
||||||
@property (nonatomic, strong) IBOutlet NSPopUpButton *colorCorrectionPopupButton;
|
@property (strong) IBOutlet NSPopUpButton *rewindPopupButton;
|
||||||
@property (nonatomic, strong) IBOutlet NSPopUpButton *frameBlendingModePopupButton;
|
@property (strong) IBOutlet NSButton *configureJoypadButton;
|
||||||
@property (nonatomic, strong) IBOutlet NSPopUpButton *colorPalettePopupButton;
|
@property (strong) IBOutlet NSButton *skipButton;
|
||||||
@property (nonatomic, strong) IBOutlet NSPopUpButton *displayBorderPopupButton;
|
@property (strong) IBOutlet NSMenuItem *bootROMsFolderItem;
|
||||||
@property (nonatomic, strong) IBOutlet NSPopUpButton *rewindPopupButton;
|
@property (strong) IBOutlet NSPopUpButtonCell *bootROMsButton;
|
||||||
@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;
|
|
||||||
@end
|
@end
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
#import "GBPreferencesWindow.h"
|
#import "GBPreferencesWindow.h"
|
||||||
#import "NSString+StringForKey.h"
|
#import "NSString+StringForKey.h"
|
||||||
#import "GBButtons.h"
|
#import "GBButtons.h"
|
||||||
#import "BigSurToolbar.h"
|
|
||||||
#import "GBViewMetal.h"
|
|
||||||
#import <Carbon/Carbon.h>
|
#import <Carbon/Carbon.h>
|
||||||
|
|
||||||
@implementation GBPreferencesWindow
|
@implementation GBPreferencesWindow
|
||||||
@ -11,31 +9,14 @@
|
|||||||
NSInteger button_being_modified;
|
NSInteger button_being_modified;
|
||||||
signed joystick_configuration_state;
|
signed joystick_configuration_state;
|
||||||
NSString *joystick_being_configured;
|
NSString *joystick_being_configured;
|
||||||
bool joypad_wait;
|
signed last_axis;
|
||||||
|
|
||||||
NSPopUpButton *_graphicsFilterPopupButton;
|
NSPopUpButton *_graphicsFilterPopupButton;
|
||||||
NSPopUpButton *_highpassFilterPopupButton;
|
NSPopUpButton *_highpassFilterPopupButton;
|
||||||
NSPopUpButton *_colorCorrectionPopupButton;
|
NSPopUpButton *_colorCorrectionPopupButton;
|
||||||
NSPopUpButton *_frameBlendingModePopupButton;
|
|
||||||
NSPopUpButton *_colorPalettePopupButton;
|
|
||||||
NSPopUpButton *_displayBorderPopupButton;
|
|
||||||
NSPopUpButton *_rewindPopupButton;
|
NSPopUpButton *_rewindPopupButton;
|
||||||
NSPopUpButton *_rtcPopupButton;
|
|
||||||
NSButton *_aspectRatioCheckbox;
|
NSButton *_aspectRatioCheckbox;
|
||||||
NSButton *_analogControlsCheckbox;
|
|
||||||
NSEventModifierFlags previousModifiers;
|
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
|
+ (NSArray *)filterList
|
||||||
@ -47,9 +28,7 @@
|
|||||||
@"NearestNeighbor",
|
@"NearestNeighbor",
|
||||||
@"Bilinear",
|
@"Bilinear",
|
||||||
@"SmoothBilinear",
|
@"SmoothBilinear",
|
||||||
@"MonoLCD",
|
|
||||||
@"LCD",
|
@"LCD",
|
||||||
@"CRT",
|
|
||||||
@"Scale2x",
|
@"Scale2x",
|
||||||
@"Scale4x",
|
@"Scale4x",
|
||||||
@"AAScale2x",
|
@"AAScale2x",
|
||||||
@ -63,17 +42,12 @@
|
|||||||
return filters;
|
return filters;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSWindowToolbarStyle)toolbarStyle
|
|
||||||
{
|
|
||||||
return NSWindowToolbarStylePreference;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)close
|
- (void)close
|
||||||
{
|
{
|
||||||
joystick_configuration_state = -1;
|
joystick_configuration_state = -1;
|
||||||
[self.configureJoypadButton setEnabled:true];
|
[self.configureJoypadButton setEnabled:YES];
|
||||||
[self.skipButton setEnabled:false];
|
[self.skipButton setEnabled:NO];
|
||||||
[self.configureJoypadButton setTitle:@"Configure Controller"];
|
[self.configureJoypadButton setTitle:@"Configure Joypad"];
|
||||||
[super close];
|
[super close];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,99 +75,11 @@
|
|||||||
[_colorCorrectionPopupButton selectItemAtIndex:mode];
|
[_colorCorrectionPopupButton selectItemAtIndex:mode];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSPopUpButton *)colorCorrectionPopupButton
|
- (NSPopUpButton *)colorCorrectionPopupButton
|
||||||
{
|
{
|
||||||
return _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
|
- (void)setRewindPopupButton:(NSPopUpButton *)rewindPopupButton
|
||||||
{
|
{
|
||||||
_rewindPopupButton = rewindPopupButton;
|
_rewindPopupButton = rewindPopupButton;
|
||||||
@ -206,18 +92,6 @@
|
|||||||
return _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
|
- (void)setHighpassFilterPopupButton:(NSPopUpButton *)highpassFilterPopupButton
|
||||||
{
|
{
|
||||||
_highpassFilterPopupButton = highpassFilterPopupButton;
|
_highpassFilterPopupButton = highpassFilterPopupButton;
|
||||||
@ -226,24 +100,7 @@
|
|||||||
|
|
||||||
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
|
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
|
||||||
{
|
{
|
||||||
if (self.playerListButton.selectedTag == 0) {
|
return GBButtonCount;
|
||||||
return GBButtonCount;
|
|
||||||
}
|
|
||||||
return GBGameBoyButtonCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (unsigned) usesForKey:(unsigned) key
|
|
||||||
{
|
|
||||||
unsigned ret = 0;
|
|
||||||
for (unsigned player = 4; player--;) {
|
|
||||||
for (unsigned button = player == 0? GBButtonCount:GBGameBoyButtonCount; button--;) {
|
|
||||||
NSNumber *other = [[NSUserDefaults standardUserDefaults] valueForKey:button_to_preference_name(button, player)];
|
|
||||||
if (other && [other unsignedIntValue] == key) {
|
|
||||||
ret++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
|
- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
|
||||||
@ -256,32 +113,20 @@
|
|||||||
return @"Select a new key...";
|
return @"Select a new key...";
|
||||||
}
|
}
|
||||||
|
|
||||||
NSNumber *key = [[NSUserDefaults standardUserDefaults] valueForKey:button_to_preference_name(row, self.playerListButton.selectedTag)];
|
return [NSString displayStringForKeyCode:[[NSUserDefaults standardUserDefaults] integerForKey:
|
||||||
if (key) {
|
button_to_preference_name(row)]];
|
||||||
if ([self usesForKey:[key unsignedIntValue]] > 1) {
|
|
||||||
return [[NSAttributedString alloc] initWithString:[NSString displayStringForKeyCode: [key unsignedIntegerValue]]
|
|
||||||
attributes:@{NSForegroundColorAttributeName: [NSColor colorWithRed:0.9375 green:0.25 blue:0.25 alpha:1.0],
|
|
||||||
NSFontAttributeName: [NSFont boldSystemFontOfSize:[NSFont systemFontSize]]
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
return [NSString displayStringForKeyCode: [key unsignedIntegerValue]];
|
|
||||||
}
|
|
||||||
|
|
||||||
return @"";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)tableView:(NSTableView *)tableView shouldEditTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
|
- (BOOL)tableView:(NSTableView *)tableView shouldEditTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
|
||||||
{
|
{
|
||||||
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
is_button_being_modified = true;
|
is_button_being_modified = true;
|
||||||
button_being_modified = row;
|
button_being_modified = row;
|
||||||
tableView.enabled = false;
|
tableView.enabled = NO;
|
||||||
self.playerListButton.enabled = false;
|
|
||||||
[tableView reloadData];
|
[tableView reloadData];
|
||||||
[self makeFirstResponder:self];
|
[self makeFirstResponder:self];
|
||||||
});
|
});
|
||||||
return false;
|
return NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
-(void)keyDown:(NSEvent *)theEvent
|
-(void)keyDown:(NSEvent *)theEvent
|
||||||
@ -296,9 +141,8 @@
|
|||||||
is_button_being_modified = false;
|
is_button_being_modified = false;
|
||||||
|
|
||||||
[[NSUserDefaults standardUserDefaults] setInteger:theEvent.keyCode
|
[[NSUserDefaults standardUserDefaults] setInteger:theEvent.keyCode
|
||||||
forKey:button_to_preference_name(button_being_modified, self.playerListButton.selectedTag)];
|
forKey:button_to_preference_name(button_being_modified)];
|
||||||
self.controlsTableView.enabled = true;
|
self.controlsTableView.enabled = YES;
|
||||||
self.playerListButton.enabled = true;
|
|
||||||
[self.controlsTableView reloadData];
|
[self.controlsTableView reloadData];
|
||||||
[self makeFirstResponder:self.controlsTableView];
|
[self makeFirstResponder:self.controlsTableView];
|
||||||
}
|
}
|
||||||
@ -326,25 +170,6 @@
|
|||||||
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBHighpassFilterChanged" object:nil];
|
[[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
|
- (IBAction)changeAspectRatio:(id)sender
|
||||||
{
|
{
|
||||||
[[NSUserDefaults standardUserDefaults] setBool: [(NSButton *)sender state] != NSOnState
|
[[NSUserDefaults standardUserDefaults] setBool: [(NSButton *)sender state] != NSOnState
|
||||||
@ -357,97 +182,7 @@
|
|||||||
[[NSUserDefaults standardUserDefaults] setObject:@([sender indexOfSelectedItem])
|
[[NSUserDefaults standardUserDefaults] setObject:@([sender indexOfSelectedItem])
|
||||||
forKey:@"GBColorCorrection"];
|
forKey:@"GBColorCorrection"];
|
||||||
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBColorCorrectionChanged" object:nil];
|
[[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];
|
|
||||||
NSDictionary *themes = [defaults dictionaryForKey:@"GBThemes"];
|
|
||||||
NSMenu *menu = _colorPalettePopupButton.menu;
|
|
||||||
while (menu.itemArray.count != 4) {
|
|
||||||
[menu removeItemAtIndex:4];
|
|
||||||
}
|
|
||||||
[menu addItem:[NSMenuItem separatorItem]];
|
|
||||||
for (NSString *name in [themes.allKeys sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)]) {
|
|
||||||
NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:name action:nil keyEquivalent:@""];
|
|
||||||
item.tag = -2;
|
|
||||||
[menu addItem:item];
|
|
||||||
}
|
|
||||||
if (themes) {
|
|
||||||
[menu addItem:[NSMenuItem separatorItem]];
|
|
||||||
}
|
|
||||||
NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:@"Custom…" action:nil keyEquivalent:@""];
|
|
||||||
item.tag = -1;
|
|
||||||
[menu addItem:item];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)colorPaletteChanged:(id)sender
|
|
||||||
{
|
|
||||||
signed tag = [sender selectedItem].tag;
|
|
||||||
if (tag == -2) {
|
|
||||||
[[NSUserDefaults standardUserDefaults] setObject:@(-1)
|
|
||||||
forKey:@"GBColorPalette"];
|
|
||||||
[[NSUserDefaults standardUserDefaults] setObject:[sender selectedItem].title
|
|
||||||
forKey:@"GBCurrentTheme"];
|
|
||||||
|
|
||||||
}
|
|
||||||
else if (tag == -1) {
|
|
||||||
[[NSUserDefaults standardUserDefaults] setObject:@(-1)
|
|
||||||
forKey:@"GBColorPalette"];
|
|
||||||
[_paletteEditorController awakeFromNib];
|
|
||||||
[self beginSheet:_paletteEditor completionHandler:^(NSModalResponse returnCode) {
|
|
||||||
[self updatePalettesMenu];
|
|
||||||
[_colorPalettePopupButton selectItemWithTitle:[[NSUserDefaults standardUserDefaults] stringForKey:@"GBCurrentTheme"] ?: @""];
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
[[NSUserDefaults standardUserDefaults] setObject:@([sender selectedItem].tag)
|
|
||||||
forKey:@"GBColorPalette"];
|
|
||||||
}
|
|
||||||
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBColorPaletteChanged" object:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)displayBorderChanged:(id)sender
|
|
||||||
{
|
|
||||||
[[NSUserDefaults standardUserDefaults] setObject:@([sender selectedItem].tag)
|
|
||||||
forKey:@"GBBorderMode"];
|
|
||||||
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBBorderModeChanged" object:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)rumbleModeChanged:(id)sender
|
|
||||||
{
|
|
||||||
[[NSUserDefaults standardUserDefaults] setObject:@([sender selectedItem].tag)
|
|
||||||
forKey:@"GBRumbleMode"];
|
|
||||||
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBRumbleModeChanged" object:nil];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)rewindLengthChanged:(id)sender
|
- (IBAction)rewindLengthChanged:(id)sender
|
||||||
@ -457,26 +192,13 @@
|
|||||||
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBRewindLengthChanged" object:nil];
|
[[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
|
- (IBAction) configureJoypad:(id)sender
|
||||||
{
|
{
|
||||||
[self.configureJoypadButton setEnabled:false];
|
[self.configureJoypadButton setEnabled:NO];
|
||||||
[self.skipButton setEnabled:true];
|
[self.skipButton setEnabled:YES];
|
||||||
joystick_being_configured = nil;
|
joystick_being_configured = nil;
|
||||||
[self advanceConfigurationStateMachine];
|
[self advanceConfigurationStateMachine];
|
||||||
|
last_axis = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction) skipButton:(id)sender
|
- (IBAction) skipButton:(id)sender
|
||||||
@ -487,137 +209,89 @@
|
|||||||
- (void) advanceConfigurationStateMachine
|
- (void) advanceConfigurationStateMachine
|
||||||
{
|
{
|
||||||
joystick_configuration_state++;
|
joystick_configuration_state++;
|
||||||
if (joystick_configuration_state == GBUnderclock) {
|
if (joystick_configuration_state < GBButtonCount) {
|
||||||
[self.configureJoypadButton setTitle:@"Press Button for Slo-Mo"]; // Full name is too long :<
|
|
||||||
}
|
|
||||||
else if (joystick_configuration_state < GBButtonCount) {
|
|
||||||
[self.configureJoypadButton setTitle:[NSString stringWithFormat:@"Press Button for %@", GBButtonNames[joystick_configuration_state]]];
|
[self.configureJoypadButton setTitle:[NSString stringWithFormat:@"Press Button for %@", GBButtonNames[joystick_configuration_state]]];
|
||||||
}
|
}
|
||||||
|
else if (joystick_configuration_state == GBButtonCount) {
|
||||||
|
[self.configureJoypadButton setTitle:@"Move the Analog Stick"];
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
joystick_configuration_state = -1;
|
joystick_configuration_state = -1;
|
||||||
[self.configureJoypadButton setEnabled:true];
|
[self.configureJoypadButton setEnabled:YES];
|
||||||
[self.skipButton setEnabled:false];
|
[self.skipButton setEnabled:NO];
|
||||||
[self.configureJoypadButton setTitle:@"Configure Joypad"];
|
[self.configureJoypadButton setTitle:@"Configure Joypad"];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)controller:(JOYController *)controller buttonChangedState:(JOYButton *)button
|
- (void) joystick:(NSString *)joystick_name button: (unsigned)button changedState: (bool) state
|
||||||
{
|
{
|
||||||
/* Debounce */
|
if (!state) return;
|
||||||
if (joypad_wait) return;
|
|
||||||
joypad_wait = true;
|
|
||||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.25 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
|
||||||
joypad_wait = false;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!button.isPressed) return;
|
|
||||||
if (joystick_configuration_state == -1) return;
|
if (joystick_configuration_state == -1) return;
|
||||||
if (joystick_configuration_state == GBButtonCount) return;
|
if (joystick_configuration_state == GBButtonCount) return;
|
||||||
if (!joystick_being_configured) {
|
if (!joystick_being_configured) {
|
||||||
joystick_being_configured = controller.uniqueID;
|
joystick_being_configured = joystick_name;
|
||||||
}
|
}
|
||||||
else if (![joystick_being_configured isEqualToString:controller.uniqueID]) {
|
else if (![joystick_being_configured isEqualToString:joystick_name]) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
NSMutableDictionary *instance_mappings = [[[NSUserDefaults standardUserDefaults] dictionaryForKey:@"JoyKitInstanceMapping"] mutableCopy];
|
NSMutableDictionary *all_mappings = [[[NSUserDefaults standardUserDefaults] dictionaryForKey:@"GBJoypadMappings"] mutableCopy];
|
||||||
|
|
||||||
NSMutableDictionary *name_mappings = [[[NSUserDefaults standardUserDefaults] dictionaryForKey:@"JoyKitNameMapping"] mutableCopy];
|
if (!all_mappings) {
|
||||||
|
all_mappings = [[NSMutableDictionary alloc] init];
|
||||||
|
|
||||||
if (!instance_mappings) {
|
|
||||||
instance_mappings = [[NSMutableDictionary alloc] init];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!name_mappings) {
|
NSMutableDictionary *mapping = [[all_mappings objectForKey:joystick_name] mutableCopy];
|
||||||
name_mappings = [[NSMutableDictionary alloc] init];
|
|
||||||
}
|
|
||||||
|
|
||||||
NSMutableDictionary *mapping = nil;
|
if (!mapping) {
|
||||||
if (joystick_configuration_state != 0) {
|
|
||||||
mapping = [instance_mappings[controller.uniqueID] mutableCopy];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
mapping = [[NSMutableDictionary alloc] init];
|
mapping = [[NSMutableDictionary alloc] init];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mapping[GBButtonNames[joystick_configuration_state]] = @(button);
|
||||||
|
|
||||||
static const unsigned gb_to_joykit[] = {
|
all_mappings[joystick_name] = mapping;
|
||||||
[GBRight] = JOYButtonUsageDPadRight,
|
[[NSUserDefaults standardUserDefaults] setObject:all_mappings forKey:@"GBJoypadMappings"];
|
||||||
[GBLeft] = JOYButtonUsageDPadLeft,
|
|
||||||
[GBUp] = JOYButtonUsageDPadUp,
|
|
||||||
[GBDown] = JOYButtonUsageDPadDown,
|
|
||||||
[GBA] = JOYButtonUsageA,
|
|
||||||
[GBB] = JOYButtonUsageB,
|
|
||||||
[GBSelect] = JOYButtonUsageSelect,
|
|
||||||
[GBStart] = JOYButtonUsageStart,
|
|
||||||
[GBTurbo] = JOYButtonUsageL1,
|
|
||||||
[GBRewind] = JOYButtonUsageL2,
|
|
||||||
[GBUnderclock] = JOYButtonUsageR1,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (joystick_configuration_state == GBUnderclock) {
|
|
||||||
mapping[@"AnalogUnderclock"] = nil;
|
|
||||||
double max = 0;
|
|
||||||
for (JOYAxis *axis in controller.axes) {
|
|
||||||
if ((axis.value > 0.5 || (axis.equivalentButtonUsage == button.usage)) && axis.value >= max) {
|
|
||||||
mapping[@"AnalogUnderclock"] = @(axis.uniqueID);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (joystick_configuration_state == GBTurbo) {
|
|
||||||
mapping[@"AnalogTurbo"] = nil;
|
|
||||||
double max = 0;
|
|
||||||
for (JOYAxis *axis in controller.axes) {
|
|
||||||
if ((axis.value > 0.5 || (axis.equivalentButtonUsage == button.usage)) && axis.value >= max) {
|
|
||||||
max = axis.value;
|
|
||||||
mapping[@"AnalogTurbo"] = @(axis.uniqueID);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mapping[n2s(button.uniqueID)] = @(gb_to_joykit[joystick_configuration_state]);
|
|
||||||
|
|
||||||
instance_mappings[controller.uniqueID] = mapping;
|
|
||||||
name_mappings[controller.deviceName] = mapping;
|
|
||||||
[[NSUserDefaults standardUserDefaults] setObject:instance_mappings forKey:@"JoyKitInstanceMapping"];
|
|
||||||
[[NSUserDefaults standardUserDefaults] setObject:name_mappings forKey:@"JoyKitNameMapping"];
|
|
||||||
[self advanceConfigurationStateMachine];
|
[self advanceConfigurationStateMachine];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSButton *)joystickMBC7Checkbox
|
- (void) joystick:(NSString *)joystick_name axis: (unsigned)axis movedTo: (signed) value
|
||||||
{
|
{
|
||||||
return _joystickMBC7Checkbox;
|
if (abs(value) < 0x4000) return;
|
||||||
}
|
if (joystick_configuration_state != GBButtonCount) return;
|
||||||
|
if (!joystick_being_configured) {
|
||||||
|
joystick_being_configured = joystick_name;
|
||||||
|
}
|
||||||
|
else if (![joystick_being_configured isEqualToString:joystick_name]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
- (void)setJoystickMBC7Checkbox:(NSButton *)joystickMBC7Checkbox
|
if (last_axis == -1) {
|
||||||
{
|
last_axis = axis;
|
||||||
_joystickMBC7Checkbox = joystickMBC7Checkbox;
|
return;
|
||||||
[_joystickMBC7Checkbox setState: [[NSUserDefaults standardUserDefaults] boolForKey:@"GBMBC7JoystickOverride"]];
|
}
|
||||||
}
|
|
||||||
|
|
||||||
- (NSButton *)mouseMBC7Checkbox
|
if (axis == last_axis) {
|
||||||
{
|
return;
|
||||||
return _mouseMBC7Checkbox;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setMouseMBC7Checkbox:(NSButton *)mouseMBC7Checkbox
|
NSMutableDictionary *all_mappings = [[[NSUserDefaults standardUserDefaults] dictionaryForKey:@"GBJoypadMappings"] mutableCopy];
|
||||||
{
|
|
||||||
_mouseMBC7Checkbox = mouseMBC7Checkbox;
|
|
||||||
[_mouseMBC7Checkbox setState: [[NSUserDefaults standardUserDefaults] boolForKey:@"GBMBC7AllowMouse"]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSButton *)analogControlsCheckbox
|
if (!all_mappings) {
|
||||||
{
|
all_mappings = [[NSMutableDictionary alloc] init];
|
||||||
return _analogControlsCheckbox;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setAnalogControlsCheckbox:(NSButton *)analogControlsCheckbox
|
NSMutableDictionary *mapping = [[all_mappings objectForKey:joystick_name] mutableCopy];
|
||||||
{
|
|
||||||
_analogControlsCheckbox = analogControlsCheckbox;
|
if (!mapping) {
|
||||||
[_analogControlsCheckbox setState: [[NSUserDefaults standardUserDefaults] boolForKey:@"GBAnalogControls"]];
|
mapping = [[NSMutableDictionary alloc] init];
|
||||||
|
}
|
||||||
|
|
||||||
|
mapping[@"XAxis"] = @(MIN(axis, last_axis));
|
||||||
|
mapping[@"YAxis"] = @(MAX(axis, last_axis));
|
||||||
|
|
||||||
|
all_mappings[joystick_name] = mapping;
|
||||||
|
[[NSUserDefaults standardUserDefaults] setObject:all_mappings forKey:@"GBJoypadMappings"];
|
||||||
|
[self advanceConfigurationStateMachine];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSButton *)aspectRatioCheckbox
|
- (NSButton *)aspectRatioCheckbox
|
||||||
@ -636,21 +310,18 @@
|
|||||||
[super awakeFromNib];
|
[super awakeFromNib];
|
||||||
[self updateBootROMFolderButton];
|
[self updateBootROMFolderButton];
|
||||||
[[NSDistributedNotificationCenter defaultCenter] addObserver:self.controlsTableView selector:@selector(reloadData) name:(NSString*)kTISNotifySelectedKeyboardInputSourceChanged object:nil];
|
[[NSDistributedNotificationCenter defaultCenter] addObserver:self.controlsTableView selector:@selector(reloadData) name:(NSString*)kTISNotifySelectedKeyboardInputSourceChanged object:nil];
|
||||||
[JOYController registerListener:self];
|
|
||||||
joystick_configuration_state = -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)dealloc
|
- (void)dealloc
|
||||||
{
|
{
|
||||||
[JOYController unregisterListener:self];
|
|
||||||
[[NSDistributedNotificationCenter defaultCenter] removeObserver:self.controlsTableView];
|
[[NSDistributedNotificationCenter defaultCenter] removeObserver:self.controlsTableView];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)selectOtherBootROMFolder:(id)sender
|
- (IBAction)selectOtherBootROMFolder:(id)sender
|
||||||
{
|
{
|
||||||
NSOpenPanel *panel = [[NSOpenPanel alloc] init];
|
NSOpenPanel *panel = [[NSOpenPanel alloc] init];
|
||||||
[panel setCanChooseDirectories:true];
|
[panel setCanChooseDirectories:YES];
|
||||||
[panel setCanChooseFiles:false];
|
[panel setCanChooseFiles:NO];
|
||||||
[panel setPrompt:@"Select"];
|
[panel setPrompt:@"Select"];
|
||||||
[panel setDirectoryURL:[[NSUserDefaults standardUserDefaults] URLForKey:@"GBBootROMsFolder"]];
|
[panel setDirectoryURL:[[NSUserDefaults standardUserDefaults] URLForKey:@"GBBootROMsFolder"]];
|
||||||
[panel beginSheetModalForWindow:self completionHandler:^(NSModalResponse result) {
|
[panel beginSheetModalForWindow:self completionHandler:^(NSModalResponse result) {
|
||||||
@ -674,12 +345,12 @@
|
|||||||
[self.bootROMsFolderItem setTitle:[url lastPathComponent]];
|
[self.bootROMsFolderItem setTitle:[url lastPathComponent]];
|
||||||
NSImage *icon = [[NSWorkspace sharedWorkspace] iconForFile:[url path]];
|
NSImage *icon = [[NSWorkspace sharedWorkspace] iconForFile:[url path]];
|
||||||
[icon setSize:NSMakeSize(16, 16)];
|
[icon setSize:NSMakeSize(16, 16)];
|
||||||
[self.bootROMsFolderItem setHidden:false];
|
[self.bootROMsFolderItem setHidden:NO];
|
||||||
[self.bootROMsFolderItem setImage:icon];
|
[self.bootROMsFolderItem setImage:icon];
|
||||||
[self.bootROMsButton selectItemAtIndex:1];
|
[self.bootROMsButton selectItemAtIndex:1];
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
[self.bootROMsFolderItem setHidden:true];
|
[self.bootROMsFolderItem setHidden:YES];
|
||||||
[self.bootROMsButton selectItemAtIndex:0];
|
[self.bootROMsButton selectItemAtIndex:0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -690,189 +361,4 @@
|
|||||||
[self updateBootROMFolderButton];
|
[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(), ^{
|
|
||||||
[self refreshJoypadMenu:nil];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)controllerDisconnected:(JOYController *)controller
|
|
||||||
{
|
|
||||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.25 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
|
||||||
[self refreshJoypadMenu:nil];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)refreshJoypadMenu:(id)sender
|
|
||||||
{
|
|
||||||
bool preferred_is_connected = false;
|
|
||||||
NSString *player_string = n2s(self.playerListButton.selectedTag);
|
|
||||||
NSString *selected_controller = [[NSUserDefaults standardUserDefaults] dictionaryForKey:@"JoyKitDefaultControllers"][player_string];
|
|
||||||
|
|
||||||
[self.preferredJoypadButton removeAllItems];
|
|
||||||
[self.preferredJoypadButton addItemWithTitle:@"None"];
|
|
||||||
for (JOYController *controller in [JOYController allControllers]) {
|
|
||||||
[self.preferredJoypadButton addItemWithTitle:[NSString stringWithFormat:@"%@ (%@)", controller.deviceName, controller.uniqueID]];
|
|
||||||
|
|
||||||
self.preferredJoypadButton.lastItem.identifier = controller.uniqueID;
|
|
||||||
|
|
||||||
if ([controller.uniqueID isEqualToString:selected_controller]) {
|
|
||||||
preferred_is_connected = true;
|
|
||||||
[self.preferredJoypadButton selectItem:self.preferredJoypadButton.lastItem];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!preferred_is_connected && selected_controller) {
|
|
||||||
[self.preferredJoypadButton addItemWithTitle:[NSString stringWithFormat:@"Unavailable Controller (%@)", selected_controller]];
|
|
||||||
self.preferredJoypadButton.lastItem.identifier = selected_controller;
|
|
||||||
[self.preferredJoypadButton selectItem:self.preferredJoypadButton.lastItem];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (!selected_controller) {
|
|
||||||
[self.preferredJoypadButton selectItemWithTitle:@"None"];
|
|
||||||
}
|
|
||||||
[self.controlsTableView reloadData];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)changeDefaultJoypad:(id)sender
|
|
||||||
{
|
|
||||||
NSMutableDictionary *default_joypads = [[[NSUserDefaults standardUserDefaults] dictionaryForKey:@"JoyKitDefaultControllers"] mutableCopy];
|
|
||||||
if (!default_joypads) {
|
|
||||||
default_joypads = [[NSMutableDictionary alloc] init];
|
|
||||||
}
|
|
||||||
|
|
||||||
NSString *player_string = n2s(self.playerListButton.selectedTag);
|
|
||||||
if ([[sender titleOfSelectedItem] isEqualToString:@"None"]) {
|
|
||||||
[default_joypads removeObjectForKey:player_string];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
default_joypads[player_string] = [[sender selectedItem] identifier];
|
|
||||||
}
|
|
||||||
[[NSUserDefaults standardUserDefaults] setObject:default_joypads forKey:@"JoyKitDefaultControllers"];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSButton *)autoUpdatesCheckbox
|
|
||||||
{
|
|
||||||
return _autoUpdatesCheckbox;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setAutoUpdatesCheckbox:(NSButton *)autoUpdatesCheckbox
|
|
||||||
{
|
|
||||||
_autoUpdatesCheckbox = autoUpdatesCheckbox;
|
|
||||||
[_autoUpdatesCheckbox setState: [[NSUserDefaults standardUserDefaults] boolForKey:@"GBAutoUpdatesEnabled"]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSButton *)OSDCheckbox
|
|
||||||
{
|
|
||||||
return _OSDCheckbox;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setOSDCheckbox:(NSButton *)OSDCheckbox
|
|
||||||
{
|
|
||||||
_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"]];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
128
Cocoa/GBS.xib
@ -1,128 +0,0 @@
|
|||||||
<?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="61.5" y="127" width="39" 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"/>
|
|
||||||
<font key="font" metaFont="system"/>
|
|
||||||
</buttonCell>
|
|
||||||
<connections>
|
|
||||||
<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"/>
|
|
||||||
<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"/>
|
|
||||||
</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>
|
|
||||||
</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>
|
|
@ -1,7 +0,0 @@
|
|||||||
#import <Cocoa/Cocoa.h>
|
|
||||||
|
|
||||||
@interface GBSplitView : NSSplitView
|
|
||||||
|
|
||||||
-(void) setDividerColor:(NSColor *)color;
|
|
||||||
- (NSArray<NSView *> *)arrangedSubviews;
|
|
||||||
@end
|
|
@ -1,33 +0,0 @@
|
|||||||
#import "GBSplitView.h"
|
|
||||||
|
|
||||||
@implementation GBSplitView
|
|
||||||
{
|
|
||||||
NSColor *_dividerColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setDividerColor:(NSColor *)color
|
|
||||||
{
|
|
||||||
_dividerColor = color;
|
|
||||||
[self setNeedsDisplay:true];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSColor *)dividerColor
|
|
||||||
{
|
|
||||||
if (_dividerColor) {
|
|
||||||
return _dividerColor;
|
|
||||||
}
|
|
||||||
return [super dividerColor];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Mavericks comaptibility */
|
|
||||||
- (NSArray<NSView *> *)arrangedSubviews
|
|
||||||
{
|
|
||||||
if (@available(macOS 10.11, *)) {
|
|
||||||
return [super arrangedSubviews];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return [self subviews];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
@ -1,6 +1,5 @@
|
|||||||
#import <Cocoa/Cocoa.h>
|
#import <Cocoa/Cocoa.h>
|
||||||
#include <Core/gb.h>
|
|
||||||
|
|
||||||
@interface GBTerminalTextFieldCell : NSTextFieldCell
|
@interface GBTerminalTextFieldCell : NSTextFieldCell
|
||||||
@property (nonatomic) GB_gameboy_t *gb;
|
|
||||||
@end
|
@end
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
#import "GBTerminalTextFieldCell.h"
|
#import "GBTerminalTextFieldCell.h"
|
||||||
|
|
||||||
@interface GBTerminalTextView : NSTextView
|
@interface GBTerminalTextView : NSTextView
|
||||||
@property GB_gameboy_t *gb;
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation GBTerminalTextFieldCell
|
@implementation GBTerminalTextFieldCell
|
||||||
@ -13,12 +12,10 @@
|
|||||||
- (NSTextView *)fieldEditorForView:(NSView *)controlView
|
- (NSTextView *)fieldEditorForView:(NSView *)controlView
|
||||||
{
|
{
|
||||||
if (field_editor) {
|
if (field_editor) {
|
||||||
field_editor.gb = self.gb;
|
|
||||||
return field_editor;
|
return field_editor;
|
||||||
}
|
}
|
||||||
field_editor = [[GBTerminalTextView alloc] init];
|
field_editor = [[GBTerminalTextView alloc] init];
|
||||||
[field_editor setFieldEditor:true];
|
[field_editor setFieldEditor:YES];
|
||||||
field_editor.gb = self.gb;
|
|
||||||
return field_editor;
|
return field_editor;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,8 +26,6 @@
|
|||||||
NSMutableOrderedSet *lines;
|
NSMutableOrderedSet *lines;
|
||||||
NSUInteger current_line;
|
NSUInteger current_line;
|
||||||
bool reverse_search_mode;
|
bool reverse_search_mode;
|
||||||
NSRange auto_complete_range;
|
|
||||||
uintptr_t auto_complete_context;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)init
|
- (instancetype)init
|
||||||
@ -109,7 +104,7 @@
|
|||||||
[self updateReverseSearch];
|
[self updateReverseSearch];
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
[self setNeedsDisplay:true];
|
[self setNeedsDisplay:YES];
|
||||||
reverse_search_mode = true;
|
reverse_search_mode = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -175,12 +170,10 @@
|
|||||||
-(void)setSelectedRanges:(NSArray<NSValue *> *)ranges affinity:(NSSelectionAffinity)affinity stillSelecting:(BOOL)stillSelectingFlag
|
-(void)setSelectedRanges:(NSArray<NSValue *> *)ranges affinity:(NSSelectionAffinity)affinity stillSelecting:(BOOL)stillSelectingFlag
|
||||||
{
|
{
|
||||||
reverse_search_mode = false;
|
reverse_search_mode = false;
|
||||||
auto_complete_context = 0;
|
|
||||||
[super setSelectedRanges:ranges affinity:affinity stillSelecting:stillSelectingFlag];
|
[super setSelectedRanges:ranges affinity:affinity stillSelecting:stillSelectingFlag];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)resignFirstResponder
|
- (BOOL)resignFirstResponder {
|
||||||
{
|
|
||||||
reverse_search_mode = false;
|
reverse_search_mode = false;
|
||||||
return [super resignFirstResponder];
|
return [super resignFirstResponder];
|
||||||
}
|
}
|
||||||
@ -194,38 +187,6 @@
|
|||||||
[attributes setObject:color forKey:NSForegroundColorAttributeName];
|
[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, 0)];
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Todo: lazy design, make it use a delegate instead of having a gb reference*/
|
|
||||||
|
|
||||||
- (void)insertTab:(id)sender
|
|
||||||
{
|
|
||||||
if (auto_complete_context == 0) {
|
|
||||||
NSRange selection = self.selectedRange;
|
|
||||||
if (selection.length) {
|
|
||||||
[self delete:nil];
|
|
||||||
}
|
|
||||||
auto_complete_range = NSMakeRange(selection.location, 0);
|
|
||||||
}
|
|
||||||
char *substring = strdup([self.string substringToIndex:auto_complete_range.location].UTF8String);
|
|
||||||
uintptr_t context = auto_complete_context;
|
|
||||||
char *completion = GB_debugger_complete_substring(self.gb, substring, &context);
|
|
||||||
free(substring);
|
|
||||||
if (completion) {
|
|
||||||
NSString *ns_completion = @(completion);
|
|
||||||
free(completion);
|
|
||||||
if (!ns_completion) {
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
self.selectedRange = auto_complete_range;
|
|
||||||
auto_complete_range.length = ns_completion.length;
|
|
||||||
[self replaceCharactersInRange:self.selectedRange withString:ns_completion];
|
|
||||||
auto_complete_context = context;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
error:
|
|
||||||
auto_complete_context = context;
|
|
||||||
NSBeep();
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -1,31 +1,16 @@
|
|||||||
#import <Cocoa/Cocoa.h>
|
#import <Cocoa/Cocoa.h>
|
||||||
#include <Core/gb.h>
|
#include <Core/gb.h>
|
||||||
#import <JoyKit/JoyKit.h>
|
#import "GBJoystickListener.h"
|
||||||
#import "GBOSDView.h"
|
|
||||||
@class Document;
|
|
||||||
|
|
||||||
typedef enum {
|
@interface GBView<GBJoystickListener> : NSView
|
||||||
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;
|
- (void) flip;
|
||||||
- (uint32_t *) pixels;
|
- (uint32_t *) pixels;
|
||||||
@property (nonatomic, weak) IBOutlet Document *document;
|
@property GB_gameboy_t *gb;
|
||||||
@property (nonatomic) GB_gameboy_t *gb;
|
@property (nonatomic) BOOL shouldBlendFrameWithPrevious;
|
||||||
@property (nonatomic) GB_frame_blending_mode_t frameBlendingMode;
|
@property (getter=isMouseHidingEnabled) BOOL mouseHidingEnabled;
|
||||||
@property (nonatomic, getter=isMouseHidingEnabled) bool mouseHidingEnabled;
|
@property bool isRewinding;
|
||||||
@property (nonatomic) bool isRewinding;
|
@property NSView *internalView;
|
||||||
@property (nonatomic, strong) NSView *internalView;
|
|
||||||
@property (weak) GBOSDView *osdView;
|
|
||||||
- (void) createInternalView;
|
- (void) createInternalView;
|
||||||
- (uint32_t *)currentBuffer;
|
- (uint32_t *)currentBuffer;
|
||||||
- (uint32_t *)previousBuffer;
|
- (uint32_t *)previousBuffer;
|
||||||
- (void)screenSizeChanged;
|
|
||||||
- (void)setRumble: (double)amp;
|
|
||||||
- (NSImage *)renderToImage;
|
|
||||||
@end
|
@end
|
||||||
|
681
Cocoa/GBView.m
@ -1,124 +1,24 @@
|
|||||||
#import <IOKit/pwr_mgt/IOPMLib.h>
|
|
||||||
#import <Carbon/Carbon.h>
|
#import <Carbon/Carbon.h>
|
||||||
#import "GBView.h"
|
#import "GBView.h"
|
||||||
#import "GBViewGL.h"
|
#import "GBViewGL.h"
|
||||||
#import "GBViewMetal.h"
|
#import "GBViewMetal.h"
|
||||||
#import "GBButtons.h"
|
#import "GBButtons.h"
|
||||||
#import "NSString+StringForKey.h"
|
#import "NSString+StringForKey.h"
|
||||||
#import "Document.h"
|
|
||||||
|
|
||||||
#define JOYSTICK_HIGH 0x4000
|
#define JOYSTICK_HIGH 0x4000
|
||||||
#define JOYSTICK_LOW 0x3800
|
#define JOYSTICK_LOW 0x3800
|
||||||
|
|
||||||
static const uint8_t workboy_ascii_to_key[] = {
|
|
||||||
['0'] = GB_WORKBOY_0,
|
|
||||||
['`'] = GB_WORKBOY_UMLAUT,
|
|
||||||
['1'] = GB_WORKBOY_1,
|
|
||||||
['2'] = GB_WORKBOY_2,
|
|
||||||
['3'] = GB_WORKBOY_3,
|
|
||||||
['4'] = GB_WORKBOY_4,
|
|
||||||
['5'] = GB_WORKBOY_5,
|
|
||||||
['6'] = GB_WORKBOY_6,
|
|
||||||
['7'] = GB_WORKBOY_7,
|
|
||||||
['8'] = GB_WORKBOY_8,
|
|
||||||
['9'] = GB_WORKBOY_9,
|
|
||||||
|
|
||||||
['\r'] = GB_WORKBOY_ENTER,
|
|
||||||
[3] = GB_WORKBOY_ENTER,
|
|
||||||
|
|
||||||
['!'] = GB_WORKBOY_EXCLAMATION_MARK,
|
|
||||||
['$'] = GB_WORKBOY_DOLLAR,
|
|
||||||
['#'] = GB_WORKBOY_HASH,
|
|
||||||
['~'] = GB_WORKBOY_TILDE,
|
|
||||||
['*'] = GB_WORKBOY_ASTERISK,
|
|
||||||
['+'] = GB_WORKBOY_PLUS,
|
|
||||||
['-'] = GB_WORKBOY_MINUS,
|
|
||||||
['('] = GB_WORKBOY_LEFT_PARENTHESIS,
|
|
||||||
[')'] = GB_WORKBOY_RIGHT_PARENTHESIS,
|
|
||||||
[';'] = GB_WORKBOY_SEMICOLON,
|
|
||||||
[':'] = GB_WORKBOY_COLON,
|
|
||||||
['%'] = GB_WORKBOY_PERCENT,
|
|
||||||
['='] = GB_WORKBOY_EQUAL,
|
|
||||||
[','] = GB_WORKBOY_COMMA,
|
|
||||||
['<'] = GB_WORKBOY_LT,
|
|
||||||
['.'] = GB_WORKBOY_DOT,
|
|
||||||
['>'] = GB_WORKBOY_GT,
|
|
||||||
['/'] = GB_WORKBOY_SLASH,
|
|
||||||
['?'] = GB_WORKBOY_QUESTION_MARK,
|
|
||||||
[' '] = GB_WORKBOY_SPACE,
|
|
||||||
['\''] = GB_WORKBOY_QUOTE,
|
|
||||||
['@'] = GB_WORKBOY_AT,
|
|
||||||
|
|
||||||
['q'] = GB_WORKBOY_Q,
|
|
||||||
['w'] = GB_WORKBOY_W,
|
|
||||||
['e'] = GB_WORKBOY_E,
|
|
||||||
['r'] = GB_WORKBOY_R,
|
|
||||||
['t'] = GB_WORKBOY_T,
|
|
||||||
['y'] = GB_WORKBOY_Y,
|
|
||||||
['u'] = GB_WORKBOY_U,
|
|
||||||
['i'] = GB_WORKBOY_I,
|
|
||||||
['o'] = GB_WORKBOY_O,
|
|
||||||
['p'] = GB_WORKBOY_P,
|
|
||||||
['a'] = GB_WORKBOY_A,
|
|
||||||
['s'] = GB_WORKBOY_S,
|
|
||||||
['d'] = GB_WORKBOY_D,
|
|
||||||
['f'] = GB_WORKBOY_F,
|
|
||||||
['g'] = GB_WORKBOY_G,
|
|
||||||
['h'] = GB_WORKBOY_H,
|
|
||||||
['j'] = GB_WORKBOY_J,
|
|
||||||
['k'] = GB_WORKBOY_K,
|
|
||||||
['l'] = GB_WORKBOY_L,
|
|
||||||
['z'] = GB_WORKBOY_Z,
|
|
||||||
['x'] = GB_WORKBOY_X,
|
|
||||||
['c'] = GB_WORKBOY_C,
|
|
||||||
['v'] = GB_WORKBOY_V,
|
|
||||||
['b'] = GB_WORKBOY_B,
|
|
||||||
['n'] = GB_WORKBOY_N,
|
|
||||||
['m'] = GB_WORKBOY_M,
|
|
||||||
};
|
|
||||||
|
|
||||||
static const uint8_t workboy_vk_to_key[] = {
|
|
||||||
[kVK_F1] = GB_WORKBOY_CLOCK,
|
|
||||||
[kVK_F2] = GB_WORKBOY_TEMPERATURE,
|
|
||||||
[kVK_F3] = GB_WORKBOY_MONEY,
|
|
||||||
[kVK_F4] = GB_WORKBOY_CALCULATOR,
|
|
||||||
[kVK_F5] = GB_WORKBOY_DATE,
|
|
||||||
[kVK_F6] = GB_WORKBOY_CONVERSION,
|
|
||||||
[kVK_F7] = GB_WORKBOY_RECORD,
|
|
||||||
[kVK_F8] = GB_WORKBOY_WORLD,
|
|
||||||
[kVK_F9] = GB_WORKBOY_PHONE,
|
|
||||||
[kVK_F10] = GB_WORKBOY_UNKNOWN,
|
|
||||||
[kVK_Delete] = GB_WORKBOY_BACKSPACE,
|
|
||||||
[kVK_Shift] = GB_WORKBOY_SHIFT_DOWN,
|
|
||||||
[kVK_RightShift] = GB_WORKBOY_SHIFT_DOWN,
|
|
||||||
[kVK_UpArrow] = GB_WORKBOY_UP,
|
|
||||||
[kVK_DownArrow] = GB_WORKBOY_DOWN,
|
|
||||||
[kVK_LeftArrow] = GB_WORKBOY_LEFT,
|
|
||||||
[kVK_RightArrow] = GB_WORKBOY_RIGHT,
|
|
||||||
[kVK_Escape] = GB_WORKBOY_ESCAPE,
|
|
||||||
[kVK_ANSI_KeypadDecimal] = GB_WORKBOY_DECIMAL_POINT,
|
|
||||||
[kVK_ANSI_KeypadClear] = GB_WORKBOY_M,
|
|
||||||
[kVK_ANSI_KeypadMultiply] = GB_WORKBOY_H,
|
|
||||||
[kVK_ANSI_KeypadDivide] = GB_WORKBOY_J,
|
|
||||||
};
|
|
||||||
|
|
||||||
@implementation GBView
|
@implementation GBView
|
||||||
{
|
{
|
||||||
uint32_t *image_buffers[3];
|
uint32_t *image_buffers[3];
|
||||||
unsigned char current_buffer;
|
unsigned char current_buffer;
|
||||||
bool mouse_hidden;
|
BOOL mouse_hidden;
|
||||||
NSTrackingArea *tracking_area;
|
NSTrackingArea *tracking_area;
|
||||||
bool _mouseHidingEnabled;
|
BOOL _mouseHidingEnabled;
|
||||||
bool axisActive[2];
|
bool axisActive[2];
|
||||||
bool underclockKeyDown;
|
bool underclockKeyDown;
|
||||||
double clockMultiplier;
|
double clockMultiplier;
|
||||||
double analogClockMultiplier;
|
|
||||||
bool analogClockMultiplierValid;
|
|
||||||
NSEventModifierFlags previousModifiers;
|
NSEventModifierFlags previousModifiers;
|
||||||
JOYController *lastController;
|
|
||||||
GB_frame_blending_mode_t _frameBlendingMode;
|
|
||||||
bool _turbo;
|
|
||||||
bool _mouseControlEnabled;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (instancetype)alloc
|
+ (instancetype)alloc
|
||||||
@ -144,11 +44,13 @@ static const uint8_t workboy_vk_to_key[] = {
|
|||||||
|
|
||||||
- (void) _init
|
- (void) _init
|
||||||
{
|
{
|
||||||
[self registerForDraggedTypes:[NSArray arrayWithObjects: NSFilenamesPboardType, nil]];
|
image_buffers[0] = malloc(160 * 144 * 4);
|
||||||
|
image_buffers[1] = malloc(160 * 144 * 4);
|
||||||
|
image_buffers[2] = malloc(160 * 144 * 4);
|
||||||
|
_shouldBlendFrameWithPrevious = 1;
|
||||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(ratioKeepingChanged) name:@"GBAspectChanged" object:nil];
|
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(ratioKeepingChanged) name:@"GBAspectChanged" object:nil];
|
||||||
tracking_area = [ [NSTrackingArea alloc] initWithRect:(NSRect){}
|
tracking_area = [ [NSTrackingArea alloc] initWithRect:(NSRect){}
|
||||||
options:NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways | NSTrackingInVisibleRect | NSTrackingMouseMoved
|
options:NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways | NSTrackingInVisibleRect
|
||||||
owner:self
|
owner:self
|
||||||
userInfo:nil];
|
userInfo:nil];
|
||||||
[self addTrackingArea:tracking_area];
|
[self addTrackingArea:tracking_area];
|
||||||
@ -156,25 +58,6 @@ static const uint8_t workboy_vk_to_key[] = {
|
|||||||
[self createInternalView];
|
[self createInternalView];
|
||||||
[self addSubview:self.internalView];
|
[self addSubview:self.internalView];
|
||||||
self.internalView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
|
self.internalView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
|
||||||
[JOYController registerListener:self];
|
|
||||||
_mouseControlEnabled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (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);
|
|
||||||
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
|
||||||
[self setFrame:self.superview.frame];
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) ratioKeepingChanged
|
- (void) ratioKeepingChanged
|
||||||
@ -182,26 +65,15 @@ static const uint8_t workboy_vk_to_key[] = {
|
|||||||
[self setFrame:self.superview.frame];
|
[self setFrame:self.superview.frame];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) setFrameBlendingMode:(GB_frame_blending_mode_t)frameBlendingMode
|
- (void) setShouldBlendFrameWithPrevious:(BOOL)shouldBlendFrameWithPrevious
|
||||||
{
|
{
|
||||||
_frameBlendingMode = frameBlendingMode;
|
_shouldBlendFrameWithPrevious = shouldBlendFrameWithPrevious;
|
||||||
[self setNeedsDisplay:true];
|
[self setNeedsDisplay:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (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
|
- (unsigned char) numberOfBuffers
|
||||||
{
|
{
|
||||||
return _frameBlendingMode? 3 : 2;
|
return _shouldBlendFrameWithPrevious? 3 : 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)dealloc
|
- (void)dealloc
|
||||||
@ -214,12 +86,11 @@ static const uint8_t workboy_vk_to_key[] = {
|
|||||||
[NSCursor unhide];
|
[NSCursor unhide];
|
||||||
}
|
}
|
||||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||||
[self setRumble:0];
|
|
||||||
[JOYController unregisterListener:self];
|
|
||||||
}
|
}
|
||||||
- (instancetype)initWithCoder:(NSCoder *)coder
|
- (instancetype)initWithCoder:(NSCoder *)coder
|
||||||
{
|
{
|
||||||
if (!(self = [super initWithCoder:coder])) {
|
if (!(self = [super initWithCoder:coder]))
|
||||||
|
{
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
[self _init];
|
[self _init];
|
||||||
@ -228,7 +99,8 @@ static const uint8_t workboy_vk_to_key[] = {
|
|||||||
|
|
||||||
- (instancetype)initWithFrame:(NSRect)frameRect
|
- (instancetype)initWithFrame:(NSRect)frameRect
|
||||||
{
|
{
|
||||||
if (!(self = [super initWithFrame:frameRect])) {
|
if (!(self = [super initWithFrame:frameRect]))
|
||||||
|
{
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
[self _init];
|
[self _init];
|
||||||
@ -238,18 +110,16 @@ static const uint8_t workboy_vk_to_key[] = {
|
|||||||
- (void)setFrame:(NSRect)frame
|
- (void)setFrame:(NSRect)frame
|
||||||
{
|
{
|
||||||
frame = self.superview.frame;
|
frame = self.superview.frame;
|
||||||
if (_gb && ![[NSUserDefaults standardUserDefaults] boolForKey:@"GBAspectRatioUnkept"]) {
|
if (![[NSUserDefaults standardUserDefaults] boolForKey:@"GBAspectRatioUnkept"]) {
|
||||||
double ratio = frame.size.width / frame.size.height;
|
double ratio = frame.size.width / frame.size.height;
|
||||||
double width = GB_get_screen_width(_gb);
|
if (ratio >= 160.0/144.0) {
|
||||||
double height = GB_get_screen_height(_gb);
|
double new_width = round(frame.size.height / 144.0 * 160.0);
|
||||||
if (ratio >= width / height) {
|
|
||||||
double new_width = round(frame.size.height / height * width);
|
|
||||||
frame.origin.x = floor((frame.size.width - new_width) / 2);
|
frame.origin.x = floor((frame.size.width - new_width) / 2);
|
||||||
frame.size.width = new_width;
|
frame.size.width = new_width;
|
||||||
frame.origin.y = 0;
|
frame.origin.y = 0;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
double new_height = round(frame.size.width / width * height);
|
double new_height = round(frame.size.width / 160.0 * 144.0);
|
||||||
frame.origin.y = floor((frame.size.height - new_height) / 2);
|
frame.origin.y = floor((frame.size.height - new_height) / 2);
|
||||||
frame.size.height = new_height;
|
frame.size.height = new_height;
|
||||||
frame.origin.x = 0;
|
frame.origin.x = 0;
|
||||||
@ -261,47 +131,18 @@ static const uint8_t workboy_vk_to_key[] = {
|
|||||||
|
|
||||||
- (void) flip
|
- (void) flip
|
||||||
{
|
{
|
||||||
if (analogClockMultiplierValid && [[NSUserDefaults standardUserDefaults] boolForKey:@"GBAnalogControls"]) {
|
if (underclockKeyDown && clockMultiplier > 0.5) {
|
||||||
clockMultiplier = 1.0;
|
clockMultiplier -= 0.1;
|
||||||
GB_set_clock_multiplier(_gb, analogClockMultiplier);
|
GB_set_clock_multiplier(_gb, clockMultiplier);
|
||||||
if (self.document.partner) {
|
|
||||||
GB_set_clock_multiplier(self.document.partner.gb, analogClockMultiplier);
|
|
||||||
}
|
|
||||||
if (analogClockMultiplier == 1.0) {
|
|
||||||
analogClockMultiplierValid = false;
|
|
||||||
}
|
|
||||||
if (analogClockMultiplier < 2.0 && analogClockMultiplier > 1.0) {
|
|
||||||
GB_set_turbo_mode(_gb, false, false);
|
|
||||||
if (self.document.partner) {
|
|
||||||
GB_set_turbo_mode(self.document.partner.gb, false, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
if (!underclockKeyDown && clockMultiplier < 1.0) {
|
||||||
if (underclockKeyDown && clockMultiplier > 0.5) {
|
clockMultiplier += 0.1;
|
||||||
clockMultiplier -= 1.0/16;
|
GB_set_clock_multiplier(_gb, clockMultiplier);
|
||||||
GB_set_clock_multiplier(_gb, clockMultiplier);
|
|
||||||
if (self.document.partner) {
|
|
||||||
GB_set_clock_multiplier(self.document.partner.gb, clockMultiplier);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!underclockKeyDown && clockMultiplier < 1.0) {
|
|
||||||
clockMultiplier += 1.0/16;
|
|
||||||
GB_set_clock_multiplier(_gb, clockMultiplier);
|
|
||||||
if (self.document.partner) {
|
|
||||||
GB_set_clock_multiplier(self.document.partner.gb, clockMultiplier);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ((!analogClockMultiplierValid && clockMultiplier > 1) ||
|
|
||||||
_turbo || (analogClockMultiplierValid && analogClockMultiplier > 1)) {
|
|
||||||
[self.osdView displayText:@"Fast forwarding..."];
|
|
||||||
}
|
|
||||||
else if ((!analogClockMultiplierValid && clockMultiplier < 1) ||
|
|
||||||
(analogClockMultiplierValid && analogClockMultiplier < 1)) {
|
|
||||||
[self.osdView displayText:@"Slow motion..."];
|
|
||||||
}
|
}
|
||||||
current_buffer = (current_buffer + 1) % self.numberOfBuffers;
|
current_buffer = (current_buffer + 1) % self.numberOfBuffers;
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
[self setNeedsDisplay:YES];
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
- (uint32_t *) pixels
|
- (uint32_t *) pixels
|
||||||
@ -311,73 +152,30 @@ static const uint8_t workboy_vk_to_key[] = {
|
|||||||
|
|
||||||
-(void)keyDown:(NSEvent *)theEvent
|
-(void)keyDown:(NSEvent *)theEvent
|
||||||
{
|
{
|
||||||
if ([theEvent type] != NSEventTypeFlagsChanged && theEvent.isARepeat) return;
|
|
||||||
unsigned short keyCode = theEvent.keyCode;
|
unsigned short keyCode = theEvent.keyCode;
|
||||||
if (GB_workboy_is_enabled(_gb)) {
|
|
||||||
if (theEvent.keyCode < sizeof(workboy_vk_to_key) && workboy_vk_to_key[theEvent.keyCode]) {
|
|
||||||
GB_workboy_set_key(_gb, workboy_vk_to_key[theEvent.keyCode]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
unichar c = [theEvent type] != NSEventTypeFlagsChanged? [theEvent.charactersIgnoringModifiers.lowercaseString characterAtIndex:0] : 0;
|
|
||||||
if (c < sizeof(workboy_ascii_to_key) && workboy_ascii_to_key[c]) {
|
|
||||||
GB_workboy_set_key(_gb, workboy_ascii_to_key[c]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool handled = false;
|
bool handled = false;
|
||||||
|
|
||||||
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
||||||
unsigned player_count = GB_get_player_count(_gb);
|
for (GBButton i = 0; i < GBButtonCount; i++) {
|
||||||
if (self.document.partner) {
|
if ([defaults integerForKey:button_to_preference_name(i)] == keyCode) {
|
||||||
player_count = 2;
|
handled = true;
|
||||||
}
|
switch (i) {
|
||||||
for (unsigned player = 0; player < player_count; player++) {
|
case GBTurbo:
|
||||||
for (GBButton button = 0; button < GBButtonCount; button++) {
|
GB_set_turbo_mode(_gb, true, self.isRewinding);
|
||||||
NSNumber *key = [defaults valueForKey:button_to_preference_name(button, player)];
|
break;
|
||||||
if (!key) continue;
|
|
||||||
|
|
||||||
if (key.unsignedShortValue == keyCode) {
|
case GBRewind:
|
||||||
handled = true;
|
self.isRewinding = true;
|
||||||
switch (button) {
|
GB_set_turbo_mode(_gb, false, false);
|
||||||
case GBTurbo:
|
break;
|
||||||
if (self.document.isSlave) {
|
|
||||||
GB_set_turbo_mode(self.document.partner.gb, true, false);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
GB_set_turbo_mode(_gb, true, self.isRewinding);
|
|
||||||
}
|
|
||||||
_turbo = true;
|
|
||||||
analogClockMultiplierValid = false;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case GBRewind:
|
case GBUnderclock:
|
||||||
if (!self.document.partner) {
|
underclockKeyDown = true;
|
||||||
self.isRewinding = true;
|
break;
|
||||||
GB_set_turbo_mode(_gb, false, false);
|
|
||||||
_turbo = false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case GBUnderclock:
|
default:
|
||||||
underclockKeyDown = true;
|
GB_set_key_state(_gb, (GB_key_t)i, true);
|
||||||
analogClockMultiplierValid = false;
|
break;
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
if (self.document.partner) {
|
|
||||||
if (player == 0) {
|
|
||||||
GB_set_key_state_for_player(_gb, (GB_key_t)button, 0, true);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
GB_set_key_state_for_player(self.document.partner.gb, (GB_key_t)button, 0, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
GB_set_key_state_for_player(_gb, (GB_key_t)button, player, true);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -390,267 +188,123 @@ static const uint8_t workboy_vk_to_key[] = {
|
|||||||
-(void)keyUp:(NSEvent *)theEvent
|
-(void)keyUp:(NSEvent *)theEvent
|
||||||
{
|
{
|
||||||
unsigned short keyCode = theEvent.keyCode;
|
unsigned short keyCode = theEvent.keyCode;
|
||||||
if (GB_workboy_is_enabled(_gb)) {
|
|
||||||
if (keyCode == kVK_Shift || keyCode == kVK_RightShift) {
|
|
||||||
GB_workboy_set_key(_gb, GB_WORKBOY_SHIFT_UP);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
GB_workboy_set_key(_gb, GB_WORKBOY_NONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
bool handled = false;
|
bool handled = false;
|
||||||
|
|
||||||
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
||||||
unsigned player_count = GB_get_player_count(_gb);
|
for (GBButton i = 0; i < GBButtonCount; i++) {
|
||||||
if (self.document.partner) {
|
if ([defaults integerForKey:button_to_preference_name(i)] == keyCode) {
|
||||||
player_count = 2;
|
handled = true;
|
||||||
}
|
switch (i) {
|
||||||
for (unsigned player = 0; player < player_count; player++) {
|
case GBTurbo:
|
||||||
for (GBButton button = 0; button < GBButtonCount; button++) {
|
GB_set_turbo_mode(_gb, false, false);
|
||||||
NSNumber *key = [defaults valueForKey:button_to_preference_name(button, player)];
|
break;
|
||||||
if (!key) continue;
|
|
||||||
|
|
||||||
if (key.unsignedShortValue == keyCode) {
|
case GBRewind:
|
||||||
handled = true;
|
self.isRewinding = false;
|
||||||
switch (button) {
|
break;
|
||||||
case GBTurbo:
|
|
||||||
if (self.document.isSlave) {
|
|
||||||
GB_set_turbo_mode(self.document.partner.gb, false, false);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
GB_set_turbo_mode(_gb, false, false);
|
|
||||||
}
|
|
||||||
_turbo = false;
|
|
||||||
analogClockMultiplierValid = false;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case GBRewind:
|
case GBUnderclock:
|
||||||
self.isRewinding = false;
|
underclockKeyDown = false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GBUnderclock:
|
default:
|
||||||
underclockKeyDown = false;
|
GB_set_key_state(_gb, (GB_key_t)i, false);
|
||||||
analogClockMultiplierValid = false;
|
break;
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
if (self.document.partner) {
|
|
||||||
if (player == 0) {
|
|
||||||
GB_set_key_state_for_player(_gb, (GB_key_t)button, 0, false);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
GB_set_key_state_for_player(self.document.partner.gb, (GB_key_t)button, 0, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
GB_set_key_state_for_player(_gb, (GB_key_t)button, player, false);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!handled && [theEvent type] != NSEventTypeFlagsChanged) {
|
if (!handled && [theEvent type] != NSEventTypeFlagsChanged) {
|
||||||
[super keyUp:theEvent];
|
[super keyUp:theEvent];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setRumble:(double)amp
|
- (void) joystick:(NSString *)joystick_name button: (unsigned)button changedState: (bool) state
|
||||||
{
|
{
|
||||||
[lastController setRumbleAmplitude:amp];
|
UpdateSystemActivity(UsrActivity);
|
||||||
}
|
NSDictionary *mapping = [[NSUserDefaults standardUserDefaults] dictionaryForKey:@"GBJoypadMappings"][joystick_name];
|
||||||
|
|
||||||
- (bool)shouldControllerUseJoystickForMotion:(JOYController *)controller
|
for (GBButton i = 0; i < GBButtonCount; i++) {
|
||||||
{
|
NSNumber *mapped_button = [mapping objectForKey:GBButtonNames[i]];
|
||||||
if (!_gb) return false;
|
if (mapped_button && [mapped_button integerValue] == button) {
|
||||||
if (!GB_has_accelerometer(_gb)) return false;
|
switch (i) {
|
||||||
if ([[NSUserDefaults standardUserDefaults] boolForKey:@"GBMBC7JoystickOverride"]) return true;
|
case GBTurbo:
|
||||||
for (JOYAxes3D *axes in controller.axes3D) {
|
GB_set_turbo_mode(_gb, state, state && self.isRewinding);
|
||||||
if (axes.usage == JOYAxes3DUsageOrientation || axes.usage == JOYAxes3DUsageAcceleration) {
|
break;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)controller:(JOYController *)controller movedAxis:(JOYAxis *)axis
|
case GBRewind:
|
||||||
{
|
self.isRewinding = state;
|
||||||
if (!_gb) return;
|
if (state) {
|
||||||
if (![self.window isMainWindow]) return;
|
|
||||||
|
|
||||||
NSDictionary *mapping = [[NSUserDefaults standardUserDefaults] dictionaryForKey:@"JoyKitInstanceMapping"][controller.uniqueID];
|
|
||||||
if (!mapping) {
|
|
||||||
mapping = [[NSUserDefaults standardUserDefaults] dictionaryForKey:@"JoyKitNameMapping"][controller.deviceName];
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((axis.usage == JOYAxisUsageR1 && !mapping) ||
|
|
||||||
axis.uniqueID == [mapping[@"AnalogUnderclock"] unsignedLongValue]){
|
|
||||||
analogClockMultiplier = MIN(MAX(1 - axis.value + 0.05, 1.0 / 3), 1.0);
|
|
||||||
analogClockMultiplierValid = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
else if ((axis.usage == JOYAxisUsageL1 && !mapping) ||
|
|
||||||
axis.uniqueID == [mapping[@"AnalogTurbo"] unsignedLongValue]){
|
|
||||||
analogClockMultiplier = MIN(MAX(axis.value * 3 + 0.95, 1.0), 3.0);
|
|
||||||
analogClockMultiplierValid = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)controller:(JOYController *)controller movedAxes2D:(JOYAxes2D *)axes
|
|
||||||
{
|
|
||||||
if (!_gb) return;
|
|
||||||
if ([self shouldControllerUseJoystickForMotion:controller]) {
|
|
||||||
if (!self.mouseControlsActive) {
|
|
||||||
GB_set_accelerometer_values(_gb, -axes.value.x, -axes.value.y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)controller:(JOYController *)controller movedAxes3D:(JOYAxes3D *)axes
|
|
||||||
{
|
|
||||||
if (!_gb) return;
|
|
||||||
if ([[NSUserDefaults standardUserDefaults] boolForKey:@"GBMBC7JoystickOverride"]) return;
|
|
||||||
if (self.mouseControlsActive) return;
|
|
||||||
|
|
||||||
if (axes.usage == JOYAxes3DUsageOrientation) {
|
|
||||||
for (JOYAxes3D *axes in controller.axes3D) {
|
|
||||||
// Only use orientation if there's no acceleration axes
|
|
||||||
if (axes.usage == JOYAxes3DUsageAcceleration) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
JOYPoint3D point = axes.normalizedValue;
|
|
||||||
GB_set_accelerometer_values(_gb, point.x, point.z);
|
|
||||||
}
|
|
||||||
else if (axes.usage == JOYAxes3DUsageAcceleration) {
|
|
||||||
JOYPoint3D point = axes.gUnitsValue;
|
|
||||||
GB_set_accelerometer_values(_gb, point.x, point.z);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)controller:(JOYController *)controller buttonChangedState:(JOYButton *)button
|
|
||||||
{
|
|
||||||
if (!_gb) return;
|
|
||||||
if (![self.window isMainWindow]) 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
|
||||||
[controller setPlayerLEDs:[controller LEDMaskForPlayer:player]];
|
|
||||||
});
|
|
||||||
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]) ?: button.usage;
|
|
||||||
if (!mapping && usage >= JOYButtonUsageGeneric0) {
|
|
||||||
usage = (const JOYButtonUsage[]){JOYButtonUsageY, JOYButtonUsageA, JOYButtonUsageB, JOYButtonUsageX}[(usage - JOYButtonUsageGeneric0) & 3];
|
|
||||||
}
|
|
||||||
|
|
||||||
GB_gameboy_t *effectiveGB = _gb;
|
|
||||||
unsigned effectivePlayer = player;
|
|
||||||
|
|
||||||
if (player && self.document.partner) {
|
|
||||||
effectiveGB = self.document.partner.gb;
|
|
||||||
effectivePlayer = 0;
|
|
||||||
if (controller != self.document.partner.view->lastController) {
|
|
||||||
[self setRumble:0];
|
|
||||||
self.document.partner.view->lastController = controller;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (controller != lastController) {
|
|
||||||
[self setRumble:0];
|
|
||||||
lastController = controller;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (usage) {
|
|
||||||
|
|
||||||
case JOYButtonUsageNone: break;
|
|
||||||
case JOYButtonUsageA: GB_set_key_state_for_player(effectiveGB, GB_KEY_A, effectivePlayer, button.isPressed); break;
|
|
||||||
case JOYButtonUsageB: GB_set_key_state_for_player(effectiveGB, GB_KEY_B, effectivePlayer, button.isPressed); break;
|
|
||||||
case JOYButtonUsageC: break;
|
|
||||||
case JOYButtonUsageStart:
|
|
||||||
case JOYButtonUsageX: GB_set_key_state_for_player(effectiveGB, GB_KEY_START, effectivePlayer, button.isPressed); break;
|
|
||||||
case JOYButtonUsageSelect:
|
|
||||||
case JOYButtonUsageY: GB_set_key_state_for_player(effectiveGB, GB_KEY_SELECT, effectivePlayer, button.isPressed); break;
|
|
||||||
case JOYButtonUsageR2:
|
|
||||||
case JOYButtonUsageL2:
|
|
||||||
case JOYButtonUsageZ: {
|
|
||||||
self.isRewinding = button.isPressed;
|
|
||||||
if (button.isPressed) {
|
|
||||||
if (self.document.isSlave) {
|
|
||||||
GB_set_turbo_mode(self.document.partner.gb, false, false);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
GB_set_turbo_mode(_gb, false, false);
|
GB_set_turbo_mode(_gb, false, false);
|
||||||
}
|
}
|
||||||
_turbo = false;
|
break;
|
||||||
}
|
|
||||||
break;
|
case GBUnderclock:
|
||||||
|
underclockKeyDown = state;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
GB_set_key_state(_gb, (GB_key_t)i, state);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case JOYButtonUsageL1: {
|
|
||||||
if (!analogClockMultiplierValid || analogClockMultiplier == 1.0 || !button.isPressed) {
|
|
||||||
if (self.document.isSlave) {
|
|
||||||
GB_set_turbo_mode(self.document.partner.gb, button.isPressed, false);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
GB_set_turbo_mode(_gb, button.isPressed, button.isPressed && self.isRewinding);
|
|
||||||
}
|
|
||||||
_turbo = button.isPressed;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case JOYButtonUsageR1: underclockKeyDown = button.isPressed; break;
|
|
||||||
case JOYButtonUsageDPadLeft: GB_set_key_state_for_player(effectiveGB, GB_KEY_LEFT, effectivePlayer, button.isPressed); break;
|
|
||||||
case JOYButtonUsageDPadRight: GB_set_key_state_for_player(effectiveGB, GB_KEY_RIGHT, effectivePlayer, button.isPressed); break;
|
|
||||||
case JOYButtonUsageDPadUp: GB_set_key_state_for_player(effectiveGB, GB_KEY_UP, effectivePlayer, button.isPressed); break;
|
|
||||||
case JOYButtonUsageDPadDown: GB_set_key_state_for_player(effectiveGB, GB_KEY_DOWN, effectivePlayer, button.isPressed); break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void) joystick:(NSString *)joystick_name axis: (unsigned)axis movedTo: (signed) value
|
||||||
|
{
|
||||||
|
UpdateSystemActivity(UsrActivity);
|
||||||
|
NSDictionary *mapping = [[NSUserDefaults standardUserDefaults] dictionaryForKey:@"GBJoypadMappings"][joystick_name];
|
||||||
|
NSNumber *x_axis = [mapping objectForKey:@"XAxis"];
|
||||||
|
NSNumber *y_axis = [mapping objectForKey:@"YAxis"];
|
||||||
|
|
||||||
|
if (axis == [x_axis integerValue]) {
|
||||||
|
if (value > JOYSTICK_HIGH) {
|
||||||
|
axisActive[0] = true;
|
||||||
|
GB_set_key_state(_gb, GB_KEY_RIGHT, true);
|
||||||
|
GB_set_key_state(_gb, GB_KEY_LEFT, false);
|
||||||
|
}
|
||||||
|
else if (value < -JOYSTICK_HIGH) {
|
||||||
|
axisActive[0] = true;
|
||||||
|
GB_set_key_state(_gb, GB_KEY_RIGHT, false);
|
||||||
|
GB_set_key_state(_gb, GB_KEY_LEFT, true);
|
||||||
|
}
|
||||||
|
else if (axisActive[0] && value < JOYSTICK_LOW && value > -JOYSTICK_LOW) {
|
||||||
|
axisActive[0] = false;
|
||||||
|
GB_set_key_state(_gb, GB_KEY_RIGHT, false);
|
||||||
|
GB_set_key_state(_gb, GB_KEY_LEFT, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (axis == [y_axis integerValue]) {
|
||||||
|
if (value > JOYSTICK_HIGH) {
|
||||||
|
axisActive[1] = true;
|
||||||
|
GB_set_key_state(_gb, GB_KEY_DOWN, true);
|
||||||
|
GB_set_key_state(_gb, GB_KEY_UP, false);
|
||||||
|
}
|
||||||
|
else if (value < -JOYSTICK_HIGH) {
|
||||||
|
axisActive[1] = true;
|
||||||
|
GB_set_key_state(_gb, GB_KEY_DOWN, false);
|
||||||
|
GB_set_key_state(_gb, GB_KEY_UP, true);
|
||||||
|
}
|
||||||
|
else if (axisActive[1] && value < JOYSTICK_LOW && value > -JOYSTICK_LOW) {
|
||||||
|
axisActive[1] = false;
|
||||||
|
GB_set_key_state(_gb, GB_KEY_DOWN, false);
|
||||||
|
GB_set_key_state(_gb, GB_KEY_UP, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
- (BOOL)acceptsFirstResponder
|
- (BOOL)acceptsFirstResponder
|
||||||
{
|
{
|
||||||
return true;
|
return YES;
|
||||||
}
|
|
||||||
|
|
||||||
- (bool)mouseControlsActive
|
|
||||||
{
|
|
||||||
return _gb && GB_is_inited(_gb) && GB_has_accelerometer(_gb) &&
|
|
||||||
_mouseControlEnabled && [[NSUserDefaults standardUserDefaults] boolForKey:@"GBMBC7AllowMouse"];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)mouseEntered:(NSEvent *)theEvent
|
- (void)mouseEntered:(NSEvent *)theEvent
|
||||||
{
|
{
|
||||||
if (!mouse_hidden) {
|
if (!mouse_hidden) {
|
||||||
mouse_hidden = true;
|
mouse_hidden = true;
|
||||||
if (_mouseHidingEnabled &&
|
if (_mouseHidingEnabled) {
|
||||||
!self.mouseControlsActive) {
|
|
||||||
[NSCursor hide];
|
[NSCursor hide];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -668,47 +322,7 @@ static const uint8_t workboy_vk_to_key[] = {
|
|||||||
[super mouseExited:theEvent];
|
[super mouseExited:theEvent];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)mouseDown:(NSEvent *)event
|
- (void)setMouseHidingEnabled:(BOOL)mouseHidingEnabled
|
||||||
{
|
|
||||||
_mouseControlEnabled = true;
|
|
||||||
if (self.mouseControlsActive) {
|
|
||||||
if (event.type == NSEventTypeLeftMouseDown) {
|
|
||||||
GB_set_key_state(_gb, GB_KEY_A, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)mouseUp:(NSEvent *)event
|
|
||||||
{
|
|
||||||
if (self.mouseControlsActive) {
|
|
||||||
if (event.type == NSEventTypeLeftMouseUp) {
|
|
||||||
GB_set_key_state(_gb, GB_KEY_A, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)mouseMoved:(NSEvent *)event
|
|
||||||
{
|
|
||||||
if (self.mouseControlsActive) {
|
|
||||||
NSPoint point = [self convertPoint:[event locationInWindow] toView:nil];
|
|
||||||
|
|
||||||
point.x /= self.frame.size.width;
|
|
||||||
point.x *= 2;
|
|
||||||
point.x -= 1;
|
|
||||||
|
|
||||||
point.y /= self.frame.size.height;
|
|
||||||
point.y *= 2;
|
|
||||||
point.y -= 1;
|
|
||||||
|
|
||||||
if (GB_get_screen_width(_gb) != 160) { // has border
|
|
||||||
point.x *= 256 / 160.0;
|
|
||||||
point.y *= 224 / 114.0;
|
|
||||||
}
|
|
||||||
GB_set_accelerometer_values(_gb, -point.x, point.y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setMouseHidingEnabled:(bool)mouseHidingEnabled
|
|
||||||
{
|
{
|
||||||
if (mouseHidingEnabled == _mouseHidingEnabled) return;
|
if (mouseHidingEnabled == _mouseHidingEnabled) return;
|
||||||
|
|
||||||
@ -723,7 +337,7 @@ static const uint8_t workboy_vk_to_key[] = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (bool)isMouseHidingEnabled
|
- (BOOL)isMouseHidingEnabled
|
||||||
{
|
{
|
||||||
return _mouseHidingEnabled;
|
return _mouseHidingEnabled;
|
||||||
}
|
}
|
||||||
@ -750,35 +364,4 @@ static const uint8_t workboy_vk_to_key[] = {
|
|||||||
return image_buffers[(current_buffer + 2) % self.numberOfBuffers];
|
return image_buffers[(current_buffer + 2) % self.numberOfBuffers];
|
||||||
}
|
}
|
||||||
|
|
||||||
-(NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender
|
|
||||||
{
|
|
||||||
NSPasteboard *pboard = [sender draggingPasteboard];
|
|
||||||
|
|
||||||
if ( [[pboard types] containsObject:NSURLPboardType] ) {
|
|
||||||
NSURL *fileURL = [NSURL URLFromPasteboard:pboard];
|
|
||||||
if (GB_is_save_state(fileURL.fileSystemRepresentation)) {
|
|
||||||
return NSDragOperationGeneric;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return NSDragOperationNone;
|
|
||||||
}
|
|
||||||
|
|
||||||
-(BOOL)performDragOperation:(id<NSDraggingInfo>)sender
|
|
||||||
{
|
|
||||||
NSPasteboard *pboard = [sender draggingPasteboard];
|
|
||||||
|
|
||||||
if ( [[pboard types] containsObject:NSURLPboardType] ) {
|
|
||||||
NSURL *fileURL = [NSURL URLFromPasteboard:pboard];
|
|
||||||
return [_document loadStateFile:fileURL.fileSystemRepresentation noErrorOnNotFound:false];
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSImage *)renderToImage;
|
|
||||||
{
|
|
||||||
/* Not going to support this on OpenGL, OpenGL is too much of a terrible API for me
|
|
||||||
to bother figuring out how the hell something so trivial can be done. */
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
@end
|
@end
|
||||||
|
@ -19,17 +19,8 @@
|
|||||||
NSOpenGLContext *context = [[NSOpenGLContext alloc] initWithFormat:pf shareContext:nil];
|
NSOpenGLContext *context = [[NSOpenGLContext alloc] initWithFormat:pf shareContext:nil];
|
||||||
|
|
||||||
self.internalView = [[GBOpenGLView alloc] initWithFrame:self.frame pixelFormat:pf];
|
self.internalView = [[GBOpenGLView alloc] initWithFrame:self.frame pixelFormat:pf];
|
||||||
((GBOpenGLView *)self.internalView).wantsBestResolutionOpenGLSurface = true;
|
((GBOpenGLView *)self.internalView).wantsBestResolutionOpenGLSurface = YES;
|
||||||
((GBOpenGLView *)self.internalView).openGLContext = context;
|
((GBOpenGLView *)self.internalView).openGLContext = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)flip
|
|
||||||
{
|
|
||||||
[super flip];
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
|
||||||
[self.internalView setNeedsDisplay:true];
|
|
||||||
[self setNeedsDisplay:true];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -1,7 +1,13 @@
|
|||||||
#import <CoreImage/CoreImage.h>
|
|
||||||
#import "GBViewMetal.h"
|
#import "GBViewMetal.h"
|
||||||
#pragma clang diagnostic ignored "-Wpartial-availability"
|
|
||||||
|
|
||||||
|
#define WIDTH 160
|
||||||
|
#define HEIGHT 144
|
||||||
|
#define PITCH (160 * 4)
|
||||||
|
|
||||||
|
static const MTLRegion region = {
|
||||||
|
{0, 0, 0}, // MTLOrigin
|
||||||
|
{WIDTH, HEIGHT, 1} // MTLSize
|
||||||
|
};
|
||||||
|
|
||||||
static const vector_float2 rect[] =
|
static const vector_float2 rect[] =
|
||||||
{
|
{
|
||||||
@ -18,7 +24,7 @@ static const vector_float2 rect[] =
|
|||||||
id<MTLBuffer> vertices;
|
id<MTLBuffer> vertices;
|
||||||
id<MTLRenderPipelineState> pipeline_state;
|
id<MTLRenderPipelineState> pipeline_state;
|
||||||
id<MTLCommandQueue> command_queue;
|
id<MTLCommandQueue> command_queue;
|
||||||
id<MTLBuffer> frame_blending_mode_buffer;
|
id<MTLBuffer> mix_previous_buffer;
|
||||||
id<MTLBuffer> output_resolution_buffer;
|
id<MTLBuffer> output_resolution_buffer;
|
||||||
vector_float2 output_resolution;
|
vector_float2 output_resolution;
|
||||||
}
|
}
|
||||||
@ -31,39 +37,30 @@ static const vector_float2 rect[] =
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) allocateTextures
|
|
||||||
{
|
|
||||||
if (!device) return;
|
|
||||||
|
|
||||||
MTLTextureDescriptor *texture_descriptor = [[MTLTextureDescriptor alloc] init];
|
|
||||||
|
|
||||||
texture_descriptor.pixelFormat = MTLPixelFormatRGBA8Unorm;
|
|
||||||
|
|
||||||
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];
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)createInternalView
|
- (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;
|
view.delegate = self;
|
||||||
self.internalView = view;
|
self.internalView = view;
|
||||||
view.paused = true;
|
|
||||||
view.enableSetNeedsDisplay = true;
|
MTLTextureDescriptor *texture_descriptor = [[MTLTextureDescriptor alloc] init];
|
||||||
view.framebufferOnly = false;
|
|
||||||
|
texture_descriptor.pixelFormat = MTLPixelFormatRGBA8Unorm;
|
||||||
|
|
||||||
|
texture_descriptor.width = WIDTH;
|
||||||
|
texture_descriptor.height = HEIGHT;
|
||||||
|
|
||||||
|
texture = [device newTextureWithDescriptor:texture_descriptor];
|
||||||
|
previous_texture = [device newTextureWithDescriptor:texture_descriptor];
|
||||||
|
|
||||||
vertices = [device newBufferWithBytes:rect
|
vertices = [device newBufferWithBytes:rect
|
||||||
length:sizeof(rect)
|
length:sizeof(rect)
|
||||||
options:MTLResourceStorageModeShared];
|
options:MTLResourceStorageModeShared];
|
||||||
|
|
||||||
static const GB_frame_blending_mode_t default_blending_mode = GB_FRAME_BLENDING_MODE_DISABLED;
|
static const bool default_mix_value = false;
|
||||||
frame_blending_mode_buffer = [device newBufferWithBytes:&default_blending_mode
|
mix_previous_buffer = [device newBufferWithBytes:&default_mix_value
|
||||||
length:sizeof(default_blending_mode)
|
length:sizeof(default_mix_value)
|
||||||
options:MTLResourceStorageModeShared];
|
options:MTLResourceStorageModeShared];
|
||||||
|
|
||||||
output_resolution_buffer = [device newBufferWithBytes:&output_resolution
|
output_resolution_buffer = [device newBufferWithBytes:&output_resolution
|
||||||
length:sizeof(output_resolution)
|
length:sizeof(output_resolution)
|
||||||
@ -94,7 +91,7 @@ static const vector_float2 rect[] =
|
|||||||
withString:scaler_source];
|
withString:scaler_source];
|
||||||
|
|
||||||
MTLCompileOptions *options = [[MTLCompileOptions alloc] init];
|
MTLCompileOptions *options = [[MTLCompileOptions alloc] init];
|
||||||
options.fastMathEnabled = true;
|
options.fastMathEnabled = YES;
|
||||||
id<MTLLibrary> library = [device newLibraryWithSource:shader_source
|
id<MTLLibrary> library = [device newLibraryWithSource:shader_source
|
||||||
options:options
|
options:options
|
||||||
error:&error];
|
error:&error];
|
||||||
@ -125,44 +122,30 @@ static const vector_float2 rect[] =
|
|||||||
command_queue = [device newCommandQueue];
|
command_queue = [device newCommandQueue];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)mtkView:(MTKView *)view drawableSizeWillChange:(CGSize)size
|
- (void)mtkView:(nonnull MTKView *)view drawableSizeWillChange:(CGSize)size
|
||||||
{
|
{
|
||||||
output_resolution = (vector_float2){size.width, size.height};
|
output_resolution = (vector_float2){size.width, size.height};
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
|
||||||
[(MTKView *)self.internalView draw];
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)drawInMTKView:(MTKView *)view
|
- (void)drawInMTKView:(nonnull MTKView *)view
|
||||||
{
|
{
|
||||||
if (!(view.window.occlusionState & NSWindowOcclusionStateVisible)) return;
|
|
||||||
if (!self.gb) return;
|
|
||||||
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 replaceRegion:region
|
[texture replaceRegion:region
|
||||||
mipmapLevel:0
|
mipmapLevel:0
|
||||||
withBytes:[self currentBuffer]
|
withBytes:[self currentBuffer]
|
||||||
bytesPerRow:texture.width * 4];
|
bytesPerRow:PITCH];
|
||||||
if ([self frameBlendingMode]) {
|
if ([self shouldBlendFrameWithPrevious]) {
|
||||||
[previous_texture replaceRegion:region
|
[previous_texture replaceRegion:region
|
||||||
mipmapLevel:0
|
mipmapLevel:0
|
||||||
withBytes:[self previousBuffer]
|
withBytes:[self previousBuffer]
|
||||||
bytesPerRow:texture.width * 4];
|
bytesPerRow:PITCH];
|
||||||
}
|
}
|
||||||
|
|
||||||
MTLRenderPassDescriptor *render_pass_descriptor = view.currentRenderPassDescriptor;
|
MTLRenderPassDescriptor *render_pass_descriptor = view.currentRenderPassDescriptor;
|
||||||
id<MTLCommandBuffer> command_buffer = [command_queue commandBuffer];
|
id<MTLCommandBuffer> command_buffer = [command_queue commandBuffer];
|
||||||
|
|
||||||
if (render_pass_descriptor != nil) {
|
if(render_pass_descriptor != nil)
|
||||||
*(GB_frame_blending_mode_t *)[frame_blending_mode_buffer contents] = [self frameBlendingMode];
|
{
|
||||||
|
*(bool *)[mix_previous_buffer contents] = [self shouldBlendFrameWithPrevious];
|
||||||
*(vector_float2 *)[output_resolution_buffer contents] = output_resolution;
|
*(vector_float2 *)[output_resolution_buffer contents] = output_resolution;
|
||||||
|
|
||||||
id<MTLRenderCommandEncoder> render_encoder =
|
id<MTLRenderCommandEncoder> render_encoder =
|
||||||
@ -179,7 +162,7 @@ static const vector_float2 rect[] =
|
|||||||
offset:0
|
offset:0
|
||||||
atIndex:0];
|
atIndex:0];
|
||||||
|
|
||||||
[render_encoder setFragmentBuffer:frame_blending_mode_buffer
|
[render_encoder setFragmentBuffer:mix_previous_buffer
|
||||||
offset:0
|
offset:0
|
||||||
atIndex:0];
|
atIndex:0];
|
||||||
|
|
||||||
@ -205,28 +188,4 @@ static const vector_float2 rect[] =
|
|||||||
|
|
||||||
[command_buffer commit];
|
[command_buffer commit];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)flip
|
|
||||||
{
|
|
||||||
[super flip];
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
|
||||||
[(MTKView *)self.internalView setNeedsDisplay:true];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSImage *)renderToImage
|
|
||||||
{
|
|
||||||
CIImage *ciImage = [CIImage imageWithMTLTexture:[[(MTKView *)self.internalView currentDrawable] texture]
|
|
||||||
options:@{
|
|
||||||
kCIImageColorSpace: (__bridge_transfer id)CGColorSpaceCreateDeviceRGB()
|
|
||||||
}];
|
|
||||||
ciImage = [ciImage imageByApplyingTransform:CGAffineTransformTranslate(CGAffineTransformMakeScale(1, -1),
|
|
||||||
0, ciImage.extent.size.height)];
|
|
||||||
CIContext *context = [CIContext context];
|
|
||||||
CGImageRef cgImage = [context createCGImage:ciImage fromRect:ciImage.extent];
|
|
||||||
NSImage *ret = [[NSImage alloc] initWithCGImage:cgImage size:self.internalView.bounds.size];
|
|
||||||
CGImageRelease(cgImage);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
#import <Cocoa/Cocoa.h>
|
|
||||||
#include <Core/gb.h>
|
|
||||||
|
|
||||||
@interface GBVisualizerView : NSView
|
|
||||||
- (void)addSample:(GB_sample_t *)sample;
|
|
||||||
@end
|
|
@ -1,71 +0,0 @@
|
|||||||
#import "GBVisualizerView.h"
|
|
||||||
#import "GBPaletteEditorController.h"
|
|
||||||
#include <Core/gb.h>
|
|
||||||
|
|
||||||
#define SAMPLE_COUNT 1024
|
|
||||||
|
|
||||||
static NSColor *color_to_effect_color(typeof(GB_PALETTE_DMG.colors[0]) color)
|
|
||||||
{
|
|
||||||
if (@available(macOS 10.10, *)) {
|
|
||||||
double tint = MAX(color.r, MAX(color.g, color.b)) + 64;
|
|
||||||
|
|
||||||
return [NSColor colorWithRed:color.r / tint
|
|
||||||
green:color.g / tint
|
|
||||||
blue:color.b / tint
|
|
||||||
alpha:tint/(255 + 64)];
|
|
||||||
|
|
||||||
}
|
|
||||||
return [NSColor colorWithRed:color.r / 255.0
|
|
||||||
green:color.g / 255.0
|
|
||||||
blue:color.b / 255.0
|
|
||||||
alpha:1.0];
|
|
||||||
}
|
|
||||||
|
|
||||||
@implementation GBVisualizerView
|
|
||||||
{
|
|
||||||
GB_sample_t _samples[SAMPLE_COUNT];
|
|
||||||
size_t _position;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)drawRect:(NSRect)dirtyRect
|
|
||||||
{
|
|
||||||
const GB_palette_t *palette = [GBPaletteEditorController userPalette];
|
|
||||||
NSSize size = self.bounds.size;
|
|
||||||
|
|
||||||
[color_to_effect_color(palette->colors[0]) setFill];
|
|
||||||
NSRectFill(self.bounds);
|
|
||||||
|
|
||||||
NSBezierPath *line = [NSBezierPath bezierPath];
|
|
||||||
[line moveToPoint:NSMakePoint(0, size.height / 2)];
|
|
||||||
|
|
||||||
for (unsigned i = 0; i < SAMPLE_COUNT; i++) {
|
|
||||||
GB_sample_t *sample = _samples + ((i + _position) % SAMPLE_COUNT);
|
|
||||||
double volume = ((signed)sample->left + (signed)sample->right) / 32768.0;
|
|
||||||
[line lineToPoint:NSMakePoint(size.width * (i + 0.5) / SAMPLE_COUNT,
|
|
||||||
(volume + 1) * size.height / 2)];
|
|
||||||
}
|
|
||||||
|
|
||||||
[line lineToPoint:NSMakePoint(size.width, size.height / 2)];
|
|
||||||
[line setLineWidth:1.0];
|
|
||||||
|
|
||||||
[color_to_effect_color(palette->colors[2]) setFill];
|
|
||||||
[line fill];
|
|
||||||
|
|
||||||
[color_to_effect_color(palette->colors[1]) setFill];
|
|
||||||
NSRectFill(NSMakeRect(0, size.height / 2 - 0.5, size.width, 1));
|
|
||||||
|
|
||||||
[color_to_effect_color(palette->colors[3]) setStroke];
|
|
||||||
[line stroke];
|
|
||||||
|
|
||||||
[super drawRect:dirtyRect];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)addSample:(GB_sample_t *)sample
|
|
||||||
{
|
|
||||||
_samples[_position++] = *sample;
|
|
||||||
if (_position == SAMPLE_COUNT) {
|
|
||||||
_position = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
@ -10,7 +10,7 @@ static GBWarningPopover *lastPopover;
|
|||||||
lastPopover = [[self alloc] init];
|
lastPopover = [[self alloc] init];
|
||||||
|
|
||||||
[lastPopover setBehavior:NSPopoverBehaviorApplicationDefined];
|
[lastPopover setBehavior:NSPopoverBehaviorApplicationDefined];
|
||||||
[lastPopover setAnimates:true];
|
[lastPopover setAnimates:YES];
|
||||||
lastPopover.contentViewController = [[NSViewController alloc] initWithNibName:@"PopoverView" bundle:nil];
|
lastPopover.contentViewController = [[NSViewController alloc] initWithNibName:@"PopoverView" bundle:nil];
|
||||||
NSTextField *field = (NSTextField *)lastPopover.contentViewController.view;
|
NSTextField *field = (NSTextField *)lastPopover.contentViewController.view;
|
||||||
[field setStringValue:contents];
|
[field setStringValue:contents];
|
||||||
@ -20,7 +20,7 @@ static GBWarningPopover *lastPopover;
|
|||||||
[lastPopover setContentSize:textSize];
|
[lastPopover setContentSize:textSize];
|
||||||
|
|
||||||
if (!view.window.isVisible) {
|
if (!view.window.isVisible) {
|
||||||
[view.window setIsVisible:true];
|
[view.window setIsVisible:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
[lastPopover showRelativeToRect:view.bounds
|
[lastPopover showRelativeToRect:view.bounds
|
||||||
|
@ -2,8 +2,6 @@
|
|||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>CFBundleDisplayName</key>
|
|
||||||
<string>SameBoy</string>
|
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
<string>en</string>
|
<string>en</string>
|
||||||
<key>CFBundleDocumentTypes</key>
|
<key>CFBundleDocumentTypes</key>
|
||||||
@ -16,7 +14,7 @@
|
|||||||
<key>CFBundleTypeIconFile</key>
|
<key>CFBundleTypeIconFile</key>
|
||||||
<string>Cartridge</string>
|
<string>Cartridge</string>
|
||||||
<key>CFBundleTypeName</key>
|
<key>CFBundleTypeName</key>
|
||||||
<string>Game Boy Game</string>
|
<string>GameBoy Game</string>
|
||||||
<key>CFBundleTypeRole</key>
|
<key>CFBundleTypeRole</key>
|
||||||
<string>Viewer</string>
|
<string>Viewer</string>
|
||||||
<key>LSItemContentTypes</key>
|
<key>LSItemContentTypes</key>
|
||||||
@ -36,7 +34,7 @@
|
|||||||
<key>CFBundleTypeIconFile</key>
|
<key>CFBundleTypeIconFile</key>
|
||||||
<string>ColorCartridge</string>
|
<string>ColorCartridge</string>
|
||||||
<key>CFBundleTypeName</key>
|
<key>CFBundleTypeName</key>
|
||||||
<string>Game Boy Color Game</string>
|
<string>GameBoy Color Game</string>
|
||||||
<key>CFBundleTypeRole</key>
|
<key>CFBundleTypeRole</key>
|
||||||
<string>Viewer</string>
|
<string>Viewer</string>
|
||||||
<key>LSItemContentTypes</key>
|
<key>LSItemContentTypes</key>
|
||||||
@ -48,46 +46,6 @@
|
|||||||
<key>NSDocumentClass</key>
|
<key>NSDocumentClass</key>
|
||||||
<string>Document</string>
|
<string>Document</string>
|
||||||
</dict>
|
</dict>
|
||||||
<dict>
|
|
||||||
<key>CFBundleTypeExtensions</key>
|
|
||||||
<array>
|
|
||||||
<string>isx</string>
|
|
||||||
</array>
|
|
||||||
<key>CFBundleTypeIconFile</key>
|
|
||||||
<string>ColorCartridge</string>
|
|
||||||
<key>CFBundleTypeName</key>
|
|
||||||
<string>Game Boy ISX File</string>
|
|
||||||
<key>CFBundleTypeRole</key>
|
|
||||||
<string>Viewer</string>
|
|
||||||
<key>LSItemContentTypes</key>
|
|
||||||
<array>
|
|
||||||
<string>com.github.liji32.sameboy.isx</string>
|
|
||||||
</array>
|
|
||||||
<key>LSTypeIsPackage</key>
|
|
||||||
<integer>0</integer>
|
|
||||||
<key>NSDocumentClass</key>
|
|
||||||
<string>Document</string>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleTypeExtensions</key>
|
|
||||||
<array>
|
|
||||||
<string>gbs</string>
|
|
||||||
</array>
|
|
||||||
<key>CFBundleTypeIconFile</key>
|
|
||||||
<string>ColorCartridge</string>
|
|
||||||
<key>CFBundleTypeName</key>
|
|
||||||
<string>Game Boy Sound File</string>
|
|
||||||
<key>CFBundleTypeRole</key>
|
|
||||||
<string>Viewer</string>
|
|
||||||
<key>LSItemContentTypes</key>
|
|
||||||
<array>
|
|
||||||
<string>com.github.liji32.sameboy.gbs</string>
|
|
||||||
</array>
|
|
||||||
<key>LSTypeIsPackage</key>
|
|
||||||
<integer>0</integer>
|
|
||||||
<key>NSDocumentClass</key>
|
|
||||||
<string>Document</string>
|
|
||||||
</dict>
|
|
||||||
</array>
|
</array>
|
||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key>
|
||||||
<string>SameBoy</string>
|
<string>SameBoy</string>
|
||||||
@ -112,7 +70,7 @@
|
|||||||
<key>LSMinimumSystemVersion</key>
|
<key>LSMinimumSystemVersion</key>
|
||||||
<string>10.9</string>
|
<string>10.9</string>
|
||||||
<key>NSHumanReadableCopyright</key>
|
<key>NSHumanReadableCopyright</key>
|
||||||
<string>Copyright © 2015-2021 Lior Halphon</string>
|
<string>Copyright © 2015-2018 Lior Halphon</string>
|
||||||
<key>NSMainNibFile</key>
|
<key>NSMainNibFile</key>
|
||||||
<string>MainMenu</string>
|
<string>MainMenu</string>
|
||||||
<key>NSPrincipalClass</key>
|
<key>NSPrincipalClass</key>
|
||||||
@ -125,7 +83,7 @@
|
|||||||
<string>public.data</string>
|
<string>public.data</string>
|
||||||
</array>
|
</array>
|
||||||
<key>UTTypeDescription</key>
|
<key>UTTypeDescription</key>
|
||||||
<string>Game Boy Game</string>
|
<string>GameBoy Game</string>
|
||||||
<key>UTTypeIconFile</key>
|
<key>UTTypeIconFile</key>
|
||||||
<string>Cartridge</string>
|
<string>Cartridge</string>
|
||||||
<key>UTTypeIdentifier</key>
|
<key>UTTypeIdentifier</key>
|
||||||
@ -144,7 +102,7 @@
|
|||||||
<string>public.data</string>
|
<string>public.data</string>
|
||||||
</array>
|
</array>
|
||||||
<key>UTTypeDescription</key>
|
<key>UTTypeDescription</key>
|
||||||
<string>Game Boy Color Game</string>
|
<string>GameBoy Color Game</string>
|
||||||
<key>UTTypeIconFile</key>
|
<key>UTTypeIconFile</key>
|
||||||
<string>ColorCartridge</string>
|
<string>ColorCartridge</string>
|
||||||
<key>UTTypeIdentifier</key>
|
<key>UTTypeIdentifier</key>
|
||||||
@ -157,47 +115,7 @@
|
|||||||
</array>
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
<dict>
|
|
||||||
<key>UTTypeConformsTo</key>
|
|
||||||
<array>
|
|
||||||
<string>public.data</string>
|
|
||||||
</array>
|
|
||||||
<key>UTTypeDescription</key>
|
|
||||||
<string>Game Boy ISX File</string>
|
|
||||||
<key>UTTypeIconFile</key>
|
|
||||||
<string>ColorCartridge</string>
|
|
||||||
<key>UTTypeIdentifier</key>
|
|
||||||
<string>com.github.liji32.sameboy.isx</string>
|
|
||||||
<key>UTTypeTagSpecification</key>
|
|
||||||
<dict>
|
|
||||||
<key>public.filename-extension</key>
|
|
||||||
<array>
|
|
||||||
<string>isx</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>UTTypeConformsTo</key>
|
|
||||||
<array>
|
|
||||||
<string>public.data</string>
|
|
||||||
</array>
|
|
||||||
<key>UTTypeDescription</key>
|
|
||||||
<string>Game Boy Sound File</string>
|
|
||||||
<key>UTTypeIconFile</key>
|
|
||||||
<string>ColorCartridge</string>
|
|
||||||
<key>UTTypeIdentifier</key>
|
|
||||||
<string>com.github.liji32.sameboy.gbs</string>
|
|
||||||
<key>UTTypeTagSpecification</key>
|
|
||||||
<dict>
|
|
||||||
<key>public.filename-extension</key>
|
|
||||||
<array>
|
|
||||||
<string>gbs</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
</dict>
|
|
||||||
</array>
|
</array>
|
||||||
<key>NSCameraUsageDescription</key>
|
|
||||||
<string>SameBoy needs to access your camera to emulate the Game Boy Camera</string>
|
|
||||||
<key>NSSupportsAutomaticGraphicsSwitching</key>
|
<key>NSSupportsAutomaticGraphicsSwitching</key>
|
||||||
<true/>
|
<true/>
|
||||||
</dict>
|
</dict>
|
||||||
|
BIN
Cocoa/Joypad.png
Before Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 6.1 KiB |
Before Width: | Height: | Size: 7.0 KiB |
@ -30,7 +30,7 @@
|
|||||||
|
|
||||||
<h1>SameBoy</h1>
|
<h1>SameBoy</h1>
|
||||||
<h2>MIT License</h2>
|
<h2>MIT License</h2>
|
||||||
<h3>Copyright © 2015-2021 Lior Halphon</h3>
|
<h3>Copyright © 2015-2018 Lior Halphon</h3>
|
||||||
|
|
||||||
<p>Permission is hereby granted, free of charge, to any person obtaining a copy
|
<p>Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
<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="9531" systemVersion="14F1713" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<deployment identifier="macosx"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="9531"/>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14868"/>
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<objects>
|
<objects>
|
||||||
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
|
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
|
||||||
@ -12,11 +11,7 @@
|
|||||||
</customObject>
|
</customObject>
|
||||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||||
<customObject id="Voe-Tx-rLC" customClass="AppDelegate">
|
<customObject id="Voe-Tx-rLC" customClass="AppDelegate"/>
|
||||||
<connections>
|
|
||||||
<outlet property="linkCableMenuItem" destination="V4S-Fo-xJK" id="KL9-3K-64i"/>
|
|
||||||
</connections>
|
|
||||||
</customObject>
|
|
||||||
<customObject id="YLy-65-1bz" customClass="NSFontManager"/>
|
<customObject id="YLy-65-1bz" customClass="NSFontManager"/>
|
||||||
<menu title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
|
<menu title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
|
||||||
<items>
|
<items>
|
||||||
@ -316,36 +311,7 @@
|
|||||||
</menu>
|
</menu>
|
||||||
</menuItem>
|
</menuItem>
|
||||||
<menuItem isSeparatorItem="YES" id="5GS-tt-E0a"/>
|
<menuItem isSeparatorItem="YES" id="5GS-tt-E0a"/>
|
||||||
<menuItem title="Save Screenshot" keyEquivalent="s" id="0J3-yf-iXs">
|
<menuItem title="Game Boy" tag="1" id="vc7-yy-ARW">
|
||||||
<connections>
|
|
||||||
<action selector="saveScreenshot:" target="-1" id="gJd-ml-J8p"/>
|
|
||||||
</connections>
|
|
||||||
</menuItem>
|
|
||||||
<menuItem title="Save Screenshot As…" alternate="YES" keyEquivalent="s" id="98X-Fp-Uny">
|
|
||||||
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
|
|
||||||
<connections>
|
|
||||||
<action selector="saveScreenshotAs:" target="-1" id="Cxc-Gx-ql1"/>
|
|
||||||
</connections>
|
|
||||||
</menuItem>
|
|
||||||
<menuItem title="Copy Screenshot" keyEquivalent="S" id="vbX-pB-QC8">
|
|
||||||
<connections>
|
|
||||||
<action selector="copyScreenshot:" target="-1" id="XJC-EB-HNl"/>
|
|
||||||
</connections>
|
|
||||||
</menuItem>
|
|
||||||
<menuItem isSeparatorItem="YES" id="zk7-gf-LXN"/>
|
|
||||||
<menuItem title="Game Boy" tag="1" id="g7C-LA-VAr">
|
|
||||||
<modifierMask key="keyEquivalentModifierMask"/>
|
|
||||||
<connections>
|
|
||||||
<action selector="reset:" target="-1" id="rxG-cz-s1S"/>
|
|
||||||
</connections>
|
|
||||||
</menuItem>
|
|
||||||
<menuItem title="Game Boy Pocket/Light" tag="5" id="1bM-CT-hoW">
|
|
||||||
<modifierMask key="keyEquivalentModifierMask"/>
|
|
||||||
<connections>
|
|
||||||
<action selector="reset:" target="-1" id="U7l-BM-kB1"/>
|
|
||||||
</connections>
|
|
||||||
</menuItem>
|
|
||||||
<menuItem title="Super Game Boy" tag="4" id="vc7-yy-ARW">
|
|
||||||
<modifierMask key="keyEquivalentModifierMask"/>
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
<connections>
|
<connections>
|
||||||
<action selector="reset:" target="-1" id="E4M-QG-ua9"/>
|
<action selector="reset:" target="-1" id="E4M-QG-ua9"/>
|
||||||
@ -369,22 +335,11 @@
|
|||||||
<action selector="mute:" target="-1" id="YE5-mi-Yzd"/>
|
<action selector="mute:" target="-1" id="YE5-mi-Yzd"/>
|
||||||
</connections>
|
</connections>
|
||||||
</menuItem>
|
</menuItem>
|
||||||
</items>
|
<menuItem isSeparatorItem="YES" id="YIZ-pz-N4V"/>
|
||||||
</menu>
|
<menuItem title="Blend Frames" id="AWj-r8-L6U">
|
||||||
</menuItem>
|
|
||||||
<menuItem title="Cheats" id="8ld-Ad-nvc">
|
|
||||||
<modifierMask key="keyEquivalentModifierMask"/>
|
|
||||||
<menu key="submenu" title="Cheats" id="Ucc-Hm-TVA">
|
|
||||||
<items>
|
|
||||||
<menuItem title="Enable Cheats" keyEquivalent="C" id="vtx-LG-v6y">
|
|
||||||
<connections>
|
|
||||||
<action selector="toggleCheats:" target="-1" id="gsw-UY-fhu"/>
|
|
||||||
</connections>
|
|
||||||
</menuItem>
|
|
||||||
<menuItem title="Show Cheats" id="LZV-QK-YXi">
|
|
||||||
<modifierMask key="keyEquivalentModifierMask"/>
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
<connections>
|
<connections>
|
||||||
<action selector="showCheats:" target="-1" id="tfr-qM-q8X"/>
|
<action selector="toggleBlend:" target="-1" id="TjO-ce-UxL"/>
|
||||||
</connections>
|
</connections>
|
||||||
</menuItem>
|
</menuItem>
|
||||||
</items>
|
</items>
|
||||||
@ -400,35 +355,18 @@
|
|||||||
<action selector="disconnectAllAccessories:" target="-1" id="5hY-9U-nRn"/>
|
<action selector="disconnectAllAccessories:" target="-1" id="5hY-9U-nRn"/>
|
||||||
</connections>
|
</connections>
|
||||||
</menuItem>
|
</menuItem>
|
||||||
<menuItem title="Game Link Cable & Infrared" id="V4S-Fo-xJK">
|
|
||||||
<modifierMask key="keyEquivalentModifierMask"/>
|
|
||||||
<menu key="submenu" title="Game Link Cable & Infrared" id="6sJ-Wz-QLj">
|
|
||||||
<connections>
|
|
||||||
<outlet property="delegate" destination="Voe-Tx-rLC" id="PMY-5j-25T"/>
|
|
||||||
</connections>
|
|
||||||
</menu>
|
|
||||||
<connections>
|
|
||||||
<action selector="nop:" target="Voe-Tx-rLC" id="Bpa-0C-lkN"/>
|
|
||||||
</connections>
|
|
||||||
</menuItem>
|
|
||||||
<menuItem title="Game Boy Printer" id="zHR-Ha-pOR">
|
<menuItem title="Game Boy Printer" id="zHR-Ha-pOR">
|
||||||
<modifierMask key="keyEquivalentModifierMask"/>
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
<connections>
|
<connections>
|
||||||
<action selector="connectPrinter:" target="-1" id="tl1-CL-tAw"/>
|
<action selector="connectPrinter:" target="-1" id="tl1-CL-tAw"/>
|
||||||
</connections>
|
</connections>
|
||||||
</menuItem>
|
</menuItem>
|
||||||
<menuItem title="Workboy" id="lo9-CX-BJj">
|
|
||||||
<modifierMask key="keyEquivalentModifierMask"/>
|
|
||||||
<connections>
|
|
||||||
<action selector="connectWorkboy:" target="-1" id="6vS-bq-wAX"/>
|
|
||||||
</connections>
|
|
||||||
</menuItem>
|
|
||||||
</items>
|
</items>
|
||||||
</menu>
|
</menu>
|
||||||
</menuItem>
|
</menuItem>
|
||||||
<menuItem title="Develop" id="IwX-DJ-dBk">
|
<menuItem title="Developer" id="IwX-DJ-dBk">
|
||||||
<modifierMask key="keyEquivalentModifierMask"/>
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
<menu key="submenu" title="Develop" id="UVb-cc-at0">
|
<menu key="submenu" title="Developer" id="UVb-cc-at0">
|
||||||
<items>
|
<items>
|
||||||
<menuItem title="Developer Mode" id="Qx6-Tt-zZR">
|
<menuItem title="Developer Mode" id="Qx6-Tt-zZR">
|
||||||
<modifierMask key="keyEquivalentModifierMask"/>
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
@ -456,19 +394,6 @@
|
|||||||
</connections>
|
</connections>
|
||||||
</menuItem>
|
</menuItem>
|
||||||
<menuItem isSeparatorItem="YES" id="M6n-8G-LZS"/>
|
<menuItem isSeparatorItem="YES" id="M6n-8G-LZS"/>
|
||||||
<menuItem title="Show Background and Window" state="on" id="yfD-Qd-zoz">
|
|
||||||
<modifierMask key="keyEquivalentModifierMask"/>
|
|
||||||
<connections>
|
|
||||||
<action selector="toggleDisplayBackground:" target="-1" id="p5b-1n-SPR"/>
|
|
||||||
</connections>
|
|
||||||
</menuItem>
|
|
||||||
<menuItem title="Show Objects" state="on" id="OWx-a0-vQk">
|
|
||||||
<modifierMask key="keyEquivalentModifierMask"/>
|
|
||||||
<connections>
|
|
||||||
<action selector="toggleDisplayObjects:" target="-1" id="8ie-ey-739"/>
|
|
||||||
</connections>
|
|
||||||
</menuItem>
|
|
||||||
<menuItem isSeparatorItem="YES" id="afI-BR-65k"/>
|
|
||||||
<menuItem title="Show Memory" id="UIa-n7-LSa">
|
<menuItem title="Show Memory" id="UIa-n7-LSa">
|
||||||
<modifierMask key="keyEquivalentModifierMask"/>
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
<connections>
|
<connections>
|
||||||
@ -522,7 +447,6 @@
|
|||||||
</menu>
|
</menu>
|
||||||
</menuItem>
|
</menuItem>
|
||||||
</items>
|
</items>
|
||||||
<point key="canvasLocation" x="140" y="260"/>
|
|
||||||
</menu>
|
</menu>
|
||||||
</objects>
|
</objects>
|
||||||
</document>
|
</document>
|
||||||
|
@ -1,42 +0,0 @@
|
|||||||
#import <AppKit/AppKit.h>
|
|
||||||
#import <objc/runtime.h>
|
|
||||||
|
|
||||||
@interface NSImageRep(PrivateAPI)
|
|
||||||
@property(setter=_setAppearanceName:) NSString *_appearanceName;
|
|
||||||
@end
|
|
||||||
|
|
||||||
static NSImage * (*imageNamed)(Class self, SEL _cmd, NSString *name);
|
|
||||||
|
|
||||||
@implementation NSImage(DarkHooks)
|
|
||||||
|
|
||||||
+ (NSImage *)imageNamedWithDark:(NSImageName)name
|
|
||||||
{
|
|
||||||
NSImage *light = imageNamed(self, _cmd, name);
|
|
||||||
if (@available(macOS 10.14, *)) {
|
|
||||||
NSImage *dark = imageNamed(self, _cmd, [name stringByAppendingString:@"~dark"]);
|
|
||||||
if (!dark) {
|
|
||||||
return light;
|
|
||||||
}
|
|
||||||
NSImage *ret = [[NSImage alloc] initWithSize:light.size];
|
|
||||||
for (NSImageRep *rep in light.representations) {
|
|
||||||
[rep _setAppearanceName:NSAppearanceNameAqua];
|
|
||||||
[ret addRepresentation:rep];
|
|
||||||
}
|
|
||||||
for (NSImageRep *rep in dark.representations) {
|
|
||||||
[rep _setAppearanceName:NSAppearanceNameDarkAqua];
|
|
||||||
[ret addRepresentation:rep];
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
return light;
|
|
||||||
}
|
|
||||||
|
|
||||||
+(void)load
|
|
||||||
{
|
|
||||||
if (@available(macOS 10.14, *)) {
|
|
||||||
imageNamed = (void *)[self methodForSelector:@selector(imageNamed:)];
|
|
||||||
method_setImplementation(class_getClassMethod(self, @selector(imageNamed:)),
|
|
||||||
[self methodForSelector:@selector(imageNamedWithDark:)]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@end
|
|
@ -1,7 +0,0 @@
|
|||||||
#import <AppKit/AppKit.h>
|
|
||||||
@implementation NSObject (MavericksCompat)
|
|
||||||
- (instancetype)initWithCoder:(NSCoder *)coder
|
|
||||||
{
|
|
||||||
return [self init];
|
|
||||||
}
|
|
||||||
@end
|
|
BIN
Cocoa/Next.png
Before Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 1.7 KiB |
BIN
Cocoa/Pause.png
Before Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 4.3 KiB |
BIN
Cocoa/Play.png
Before Width: | Height: | Size: 4.3 KiB |
Before Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 4.5 KiB |
BIN
Cocoa/Rewind.png
Before Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 4.5 KiB |
Before Width: | Height: | Size: 5.9 KiB |
@ -1,139 +0,0 @@
|
|||||||
<?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"/>
|
|
||||||
<plugIn identifier="com.apple.WebKitIBPlugin" version="14868"/>
|
|
||||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
|
||||||
</dependencies>
|
|
||||||
<objects>
|
|
||||||
<customObject id="-2" userLabel="File's Owner" customClass="AppDelegate">
|
|
||||||
<connections>
|
|
||||||
<outlet property="updateChanges" destination="ATt-VM-cb5" id="hJj-Nd-FBv"/>
|
|
||||||
<outlet property="updateProgressButton" destination="7nO-AA-WmG" id="wTa-9l-cOG"/>
|
|
||||||
<outlet property="updateProgressLabel" destination="wIm-GX-c6B" id="URp-JG-6wR"/>
|
|
||||||
<outlet property="updateProgressSpinner" destination="fqq-Nb-THz" id="4vC-m5-ysO"/>
|
|
||||||
<outlet property="updateProgressWindow" destination="2Gy-QG-FoA" id="RXw-50-DQh"/>
|
|
||||||
<outlet property="updateWindow" destination="QvC-M9-y7g" id="iwP-kC-tmG"/>
|
|
||||||
</connections>
|
|
||||||
</customObject>
|
|
||||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
|
||||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
|
||||||
<window title="Update Available" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" restorable="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="QvC-M9-y7g">
|
|
||||||
<windowStyleMask key="styleMask" titled="YES" closable="YES" resizable="YES"/>
|
|
||||||
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
|
||||||
<rect key="contentRect" x="196" y="240" width="480" height="360"/>
|
|
||||||
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1417"/>
|
|
||||||
<view key="contentView" wantsLayer="YES" id="EiT-Mj-1SZ">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="480" height="360"/>
|
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
|
||||||
<subviews>
|
|
||||||
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="xZt-UI-Wl2">
|
|
||||||
<rect key="frame" x="338" y="13" width="128" height="32"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
|
|
||||||
<buttonCell key="cell" type="push" title="Install Update" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="Mav-rY-sJo">
|
|
||||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
|
||||||
<font key="font" metaFont="system"/>
|
|
||||||
<string key="keyEquivalent" base64-UTF8="YES">
|
|
||||||
DQ
|
|
||||||
</string>
|
|
||||||
</buttonCell>
|
|
||||||
<connections>
|
|
||||||
<action selector="installUpdate:" target="-2" id="jJc-CY-4vz"/>
|
|
||||||
</connections>
|
|
||||||
</button>
|
|
||||||
<button verticalHuggingPriority="750" id="b3S-YN-iDZ">
|
|
||||||
<rect key="frame" x="242" y="13" width="96" height="32"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
|
|
||||||
<buttonCell key="cell" type="push" title="Not Now" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="QsJ-cv-hwF">
|
|
||||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
|
||||||
<font key="font" metaFont="system"/>
|
|
||||||
</buttonCell>
|
|
||||||
<connections>
|
|
||||||
<action selector="performClose:" target="QvC-M9-y7g" id="8qO-ac-k7U"/>
|
|
||||||
</connections>
|
|
||||||
</button>
|
|
||||||
<button verticalHuggingPriority="750" id="uOb-4Q-S8o">
|
|
||||||
<rect key="frame" x="14" y="13" width="128" height="32"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
|
||||||
<buttonCell key="cell" type="push" title="Skip Version" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="Ai5-6n-dvH">
|
|
||||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
|
||||||
<font key="font" metaFont="system"/>
|
|
||||||
</buttonCell>
|
|
||||||
<connections>
|
|
||||||
<action selector="skipVersion:" target="-2" id="Jf8-Qe-6X0"/>
|
|
||||||
</connections>
|
|
||||||
</button>
|
|
||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Swh-S6-6XA">
|
|
||||||
<rect key="frame" x="18" y="313" width="444" height="27"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
|
||||||
<textFieldCell key="cell" lineBreakMode="clipping" title="A new version of SameBoy is available with the following changes:" id="WsO-pC-VO7">
|
|
||||||
<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>
|
|
||||||
<box verticalHuggingPriority="750" fixedFrame="YES" boxType="separator" translatesAutoresizingMaskIntoConstraints="NO" id="9Tu-Q3-l40">
|
|
||||||
<rect key="frame" x="0.0" y="302" width="480" height="5"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
|
||||||
</box>
|
|
||||||
<box verticalHuggingPriority="750" fixedFrame="YES" boxType="separator" translatesAutoresizingMaskIntoConstraints="NO" id="dva-Ay-nnl">
|
|
||||||
<rect key="frame" x="0.0" y="58" width="480" height="4"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
|
||||||
</box>
|
|
||||||
<webView maintainsBackForwardList="NO" id="ATt-VM-cb5">
|
|
||||||
<rect key="frame" x="0.0" y="61" width="480" height="243"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
|
||||||
<webPreferences key="preferences" defaultFontSize="13" defaultFixedFontSize="13" minimumFontSize="0" plugInsEnabled="NO" javaEnabled="NO" javaScriptEnabled="NO" javaScriptCanOpenWindowsAutomatically="NO" loadsImagesAutomatically="NO" allowsAnimatedImages="NO" allowsAnimatedImageLooping="NO">
|
|
||||||
<nil key="identifier"/>
|
|
||||||
</webPreferences>
|
|
||||||
<connections>
|
|
||||||
<outlet property="UIDelegate" destination="-2" id="xQ1-eY-1hu"/>
|
|
||||||
<outlet property="frameLoadDelegate" destination="-2" id="BOf-df-5LR"/>
|
|
||||||
</connections>
|
|
||||||
</webView>
|
|
||||||
</subviews>
|
|
||||||
</view>
|
|
||||||
<point key="canvasLocation" x="217" y="267"/>
|
|
||||||
</window>
|
|
||||||
<window title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" restorable="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" id="2Gy-QG-FoA">
|
|
||||||
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
|
||||||
<rect key="contentRect" x="283" y="305" width="512" height="61"/>
|
|
||||||
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1417"/>
|
|
||||||
<view key="contentView" id="jjT-Z5-15Q">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="512" height="61"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
|
||||||
<subviews>
|
|
||||||
<progressIndicator wantsLayer="YES" fixedFrame="YES" maxValue="100" displayedWhenStopped="NO" indeterminate="YES" style="spinning" translatesAutoresizingMaskIntoConstraints="NO" id="fqq-Nb-THz">
|
|
||||||
<rect key="frame" x="20" y="15" width="32" height="32"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
|
||||||
</progressIndicator>
|
|
||||||
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="7nO-AA-WmG">
|
|
||||||
<rect key="frame" x="417" y="13" width="82" height="32"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
|
|
||||||
<buttonCell key="cell" type="push" title="Cancel" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="6l6-qX-gsr">
|
|
||||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
|
||||||
<font key="font" metaFont="system"/>
|
|
||||||
<string key="keyEquivalent" base64-UTF8="YES">
|
|
||||||
Gw
|
|
||||||
</string>
|
|
||||||
</buttonCell>
|
|
||||||
<connections>
|
|
||||||
<action selector="updateAction:" target="-2" id="geO-Gk-xrs"/>
|
|
||||||
</connections>
|
|
||||||
</button>
|
|
||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="wIm-GX-c6B">
|
|
||||||
<rect key="frame" x="58" y="15" width="359" height="24"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
|
||||||
<textFieldCell key="cell" lineBreakMode="clipping" title="Downloading update..." id="qmF-X1-v5B">
|
|
||||||
<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>
|
|
||||||
</view>
|
|
||||||
<point key="canvasLocation" x="11" y="-203.5"/>
|
|
||||||
</window>
|
|
||||||
</objects>
|
|
||||||
</document>
|
|
684
Cocoa/joypad.m
Executable file
@ -0,0 +1,684 @@
|
|||||||
|
/*
|
||||||
|
Joypad support is based on a stripped-down version of SDL's Darwin implementation
|
||||||
|
of the Joystick API, under the following license:
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Simple DirectMedia Layer
|
||||||
|
Copyright (C) 1997-2017 Sam Lantinga <slouken@libsdl.org>
|
||||||
|
|
||||||
|
This software is provided 'as-is', without any express or implied
|
||||||
|
warranty. In no event will the authors be held liable for any damages
|
||||||
|
arising from the use of this software.
|
||||||
|
|
||||||
|
Permission is granted to anyone to use this software for any purpose,
|
||||||
|
including commercial applications, and to alter it and redistribute it
|
||||||
|
freely, subject to the following restrictions:
|
||||||
|
|
||||||
|
1. The origin of this software must not be misrepresented; you must not
|
||||||
|
claim that you wrote the original software. If you use this software
|
||||||
|
in a product, an acknowledgment in the product documentation would be
|
||||||
|
appreciated but is not required.
|
||||||
|
2. Altered source versions must be plainly marked as such, and must not be
|
||||||
|
misrepresented as being the original software.
|
||||||
|
3. This notice may not be removed or altered from any source distribution.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <AppKit/AppKit.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <IOKit/hid/IOHIDLib.h>
|
||||||
|
#include "GBJoystickListener.h"
|
||||||
|
|
||||||
|
typedef signed SDL_JoystickID;
|
||||||
|
typedef struct _SDL_Joystick SDL_Joystick;
|
||||||
|
|
||||||
|
typedef struct _SDL_JoystickAxisInfo
|
||||||
|
{
|
||||||
|
int16_t initial_value; /* Initial axis state */
|
||||||
|
int16_t value; /* Current axis state */
|
||||||
|
int16_t zero; /* Zero point on the axis (-32768 for triggers) */
|
||||||
|
bool has_initial_value; /* Whether we've seen a value on the axis yet */
|
||||||
|
bool sent_initial_value; /* Whether we've sent the initial axis value */
|
||||||
|
} SDL_JoystickAxisInfo;
|
||||||
|
|
||||||
|
struct _SDL_Joystick
|
||||||
|
{
|
||||||
|
SDL_JoystickID instance_id; /* Device instance, monotonically increasing from 0 */
|
||||||
|
char *name; /* Joystick name - system dependent */
|
||||||
|
|
||||||
|
int naxes; /* Number of axis controls on the joystick */
|
||||||
|
SDL_JoystickAxisInfo *axes;
|
||||||
|
|
||||||
|
int nbuttons; /* Number of buttons on the joystick */
|
||||||
|
uint8_t *buttons; /* Current button states */
|
||||||
|
|
||||||
|
struct joystick_hwdata *hwdata; /* Driver dependent information */
|
||||||
|
|
||||||
|
int ref_count; /* Reference count for multiple opens */
|
||||||
|
|
||||||
|
bool is_game_controller;
|
||||||
|
bool force_recentering; /* SDL_TRUE if this device needs to have its state reset to 0 */
|
||||||
|
struct _SDL_Joystick *next; /* pointer to next joystick we have allocated */
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t data[16];
|
||||||
|
} SDL_JoystickGUID;
|
||||||
|
|
||||||
|
struct recElement
|
||||||
|
{
|
||||||
|
IOHIDElementRef elementRef;
|
||||||
|
IOHIDElementCookie cookie;
|
||||||
|
uint32_t usagePage, usage; /* HID usage */
|
||||||
|
SInt32 min; /* reported min value possible */
|
||||||
|
SInt32 max; /* reported max value possible */
|
||||||
|
|
||||||
|
/* runtime variables used for auto-calibration */
|
||||||
|
SInt32 minReport; /* min returned value */
|
||||||
|
SInt32 maxReport; /* max returned value */
|
||||||
|
|
||||||
|
struct recElement *pNext; /* next element in list */
|
||||||
|
};
|
||||||
|
typedef struct recElement recElement;
|
||||||
|
|
||||||
|
struct joystick_hwdata
|
||||||
|
{
|
||||||
|
IOHIDDeviceRef deviceRef; /* HIDManager device handle */
|
||||||
|
io_service_t ffservice; /* Interface for force feedback, 0 = no ff */
|
||||||
|
|
||||||
|
char product[256]; /* name of product */
|
||||||
|
uint32_t usage; /* usage page from IOUSBHID Parser.h which defines general usage */
|
||||||
|
uint32_t usagePage; /* usage within above page from IOUSBHID Parser.h which defines specific usage */
|
||||||
|
|
||||||
|
int axes; /* number of axis (calculated, not reported by device) */
|
||||||
|
int buttons; /* number of buttons (calculated, not reported by device) */
|
||||||
|
int elements; /* number of total elements (should be total of above) (calculated, not reported by device) */
|
||||||
|
|
||||||
|
recElement *firstAxis;
|
||||||
|
recElement *firstButton;
|
||||||
|
|
||||||
|
bool removed;
|
||||||
|
|
||||||
|
int instance_id;
|
||||||
|
SDL_JoystickGUID guid;
|
||||||
|
|
||||||
|
SDL_Joystick joystick;
|
||||||
|
};
|
||||||
|
typedef struct joystick_hwdata recDevice;
|
||||||
|
|
||||||
|
/* The base object of the HID Manager API */
|
||||||
|
static IOHIDManagerRef hidman = NULL;
|
||||||
|
|
||||||
|
/* static incrementing counter for new joystick devices seen on the system. Devices should start with index 0 */
|
||||||
|
static int s_joystick_instance_id = -1;
|
||||||
|
|
||||||
|
#define SDL_JOYSTICK_AXIS_MAX 32767
|
||||||
|
|
||||||
|
void SDL_PrivateJoystickAxis(SDL_Joystick * joystick, uint8_t axis, int16_t value)
|
||||||
|
{
|
||||||
|
/* Make sure we're not getting garbage or duplicate events */
|
||||||
|
if (axis >= joystick->naxes) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!joystick->axes[axis].has_initial_value) {
|
||||||
|
joystick->axes[axis].initial_value = value;
|
||||||
|
joystick->axes[axis].value = value;
|
||||||
|
joystick->axes[axis].zero = value;
|
||||||
|
joystick->axes[axis].has_initial_value = true;
|
||||||
|
}
|
||||||
|
if (value == joystick->axes[axis].value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!joystick->axes[axis].sent_initial_value) {
|
||||||
|
/* Make sure we don't send motion until there's real activity on this axis */
|
||||||
|
const int MAX_ALLOWED_JITTER = SDL_JOYSTICK_AXIS_MAX / 80; /* ShanWan PS3 controller needed 96 */
|
||||||
|
if (abs(value - joystick->axes[axis].value) <= MAX_ALLOWED_JITTER) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
joystick->axes[axis].sent_initial_value = true;
|
||||||
|
joystick->axes[axis].value = value; /* Just so we pass the check above */
|
||||||
|
SDL_PrivateJoystickAxis(joystick, axis, joystick->axes[axis].initial_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update internal joystick state */
|
||||||
|
joystick->axes[axis].value = value;
|
||||||
|
|
||||||
|
NSResponder<GBJoystickListener> *responder = (typeof(responder)) [[NSApp keyWindow] firstResponder];
|
||||||
|
while (responder) {
|
||||||
|
if ([responder respondsToSelector:@selector(joystick:axis:movedTo:)]) {
|
||||||
|
[responder joystick:@(joystick->name) axis:axis movedTo:value];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
responder = (typeof(responder)) [responder nextResponder];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SDL_PrivateJoystickButton(SDL_Joystick *joystick, uint8_t button, uint8_t state)
|
||||||
|
{
|
||||||
|
|
||||||
|
/* Make sure we're not getting garbage or duplicate events */
|
||||||
|
if (button >= joystick->nbuttons) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (state == joystick->buttons[button]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update internal joystick state */
|
||||||
|
joystick->buttons[button] = state;
|
||||||
|
|
||||||
|
NSResponder<GBJoystickListener> *responder = (typeof(responder)) [[NSApp keyWindow] firstResponder];
|
||||||
|
while (responder) {
|
||||||
|
if ([responder respondsToSelector:@selector(joystick:button:changedState:)]) {
|
||||||
|
[responder joystick:@(joystick->name) button:button changedState:state];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
responder = (typeof(responder)) [responder nextResponder];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
FreeElementList(recElement *pElement)
|
||||||
|
{
|
||||||
|
while (pElement) {
|
||||||
|
recElement *pElementNext = pElement->pNext;
|
||||||
|
free(pElement);
|
||||||
|
pElement = pElementNext;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static recDevice *
|
||||||
|
FreeDevice(recDevice *removeDevice)
|
||||||
|
{
|
||||||
|
recDevice *pDeviceNext = NULL;
|
||||||
|
if (removeDevice) {
|
||||||
|
if (removeDevice->deviceRef) {
|
||||||
|
IOHIDDeviceUnscheduleFromRunLoop(removeDevice->deviceRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
|
||||||
|
removeDevice->deviceRef = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* free element lists */
|
||||||
|
FreeElementList(removeDevice->firstAxis);
|
||||||
|
FreeElementList(removeDevice->firstButton);
|
||||||
|
|
||||||
|
free(removeDevice);
|
||||||
|
}
|
||||||
|
return pDeviceNext;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SInt32
|
||||||
|
GetHIDElementState(recDevice *pDevice, recElement *pElement)
|
||||||
|
{
|
||||||
|
SInt32 value = 0;
|
||||||
|
|
||||||
|
if (pDevice && pElement) {
|
||||||
|
IOHIDValueRef valueRef;
|
||||||
|
if (IOHIDDeviceGetValue(pDevice->deviceRef, pElement->elementRef, &valueRef) == kIOReturnSuccess) {
|
||||||
|
value = (SInt32) IOHIDValueGetIntegerValue(valueRef);
|
||||||
|
|
||||||
|
/* record min and max for auto calibration */
|
||||||
|
if (value < pElement->minReport) {
|
||||||
|
pElement->minReport = value;
|
||||||
|
}
|
||||||
|
if (value > pElement->maxReport) {
|
||||||
|
pElement->maxReport = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SInt32
|
||||||
|
GetHIDScaledCalibratedState(recDevice * pDevice, recElement * pElement, SInt32 min, SInt32 max)
|
||||||
|
{
|
||||||
|
const float deviceScale = max - min;
|
||||||
|
const float readScale = pElement->maxReport - pElement->minReport;
|
||||||
|
const SInt32 value = GetHIDElementState(pDevice, pElement);
|
||||||
|
if (readScale == 0) {
|
||||||
|
return value; /* no scaling at all */
|
||||||
|
}
|
||||||
|
return ((value - pElement->minReport) * deviceScale / readScale) + min;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
JoystickDeviceWasRemovedCallback(void *ctx, IOReturn result, void *sender)
|
||||||
|
{
|
||||||
|
recDevice *device = (recDevice *) ctx;
|
||||||
|
device->removed = true;
|
||||||
|
device->deviceRef = NULL; // deviceRef was invalidated due to the remove
|
||||||
|
FreeDevice(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void AddHIDElement(const void *value, void *parameter);
|
||||||
|
|
||||||
|
/* Call AddHIDElement() on all elements in an array of IOHIDElementRefs */
|
||||||
|
static void
|
||||||
|
AddHIDElements(CFArrayRef array, recDevice *pDevice)
|
||||||
|
{
|
||||||
|
const CFRange range = { 0, CFArrayGetCount(array) };
|
||||||
|
CFArrayApplyFunction(array, range, AddHIDElement, pDevice);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
ElementAlreadyAdded(const IOHIDElementCookie cookie, const recElement *listitem) {
|
||||||
|
while (listitem) {
|
||||||
|
if (listitem->cookie == cookie) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
listitem = listitem->pNext;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* See if we care about this HID element, and if so, note it in our recDevice. */
|
||||||
|
static void
|
||||||
|
AddHIDElement(const void *value, void *parameter)
|
||||||
|
{
|
||||||
|
recDevice *pDevice = (recDevice *) parameter;
|
||||||
|
IOHIDElementRef refElement = (IOHIDElementRef) value;
|
||||||
|
const CFTypeID elementTypeID = refElement ? CFGetTypeID(refElement) : 0;
|
||||||
|
|
||||||
|
if (refElement && (elementTypeID == IOHIDElementGetTypeID())) {
|
||||||
|
const IOHIDElementCookie cookie = IOHIDElementGetCookie(refElement);
|
||||||
|
const uint32_t usagePage = IOHIDElementGetUsagePage(refElement);
|
||||||
|
const uint32_t usage = IOHIDElementGetUsage(refElement);
|
||||||
|
recElement *element = NULL;
|
||||||
|
recElement **headElement = NULL;
|
||||||
|
|
||||||
|
/* look at types of interest */
|
||||||
|
switch (IOHIDElementGetType(refElement)) {
|
||||||
|
case kIOHIDElementTypeInput_Misc:
|
||||||
|
case kIOHIDElementTypeInput_Button:
|
||||||
|
case kIOHIDElementTypeInput_Axis: {
|
||||||
|
switch (usagePage) { /* only interested in kHIDPage_GenericDesktop and kHIDPage_Button */
|
||||||
|
case kHIDPage_GenericDesktop:
|
||||||
|
switch (usage) {
|
||||||
|
case kHIDUsage_GD_X:
|
||||||
|
case kHIDUsage_GD_Y:
|
||||||
|
case kHIDUsage_GD_Z:
|
||||||
|
case kHIDUsage_GD_Rx:
|
||||||
|
case kHIDUsage_GD_Ry:
|
||||||
|
case kHIDUsage_GD_Rz:
|
||||||
|
case kHIDUsage_GD_Slider:
|
||||||
|
case kHIDUsage_GD_Dial:
|
||||||
|
case kHIDUsage_GD_Wheel:
|
||||||
|
if (!ElementAlreadyAdded(cookie, pDevice->firstAxis)) {
|
||||||
|
element = (recElement *) calloc(1, sizeof (recElement));
|
||||||
|
if (element) {
|
||||||
|
pDevice->axes++;
|
||||||
|
headElement = &(pDevice->firstAxis);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case kHIDUsage_GD_DPadUp:
|
||||||
|
case kHIDUsage_GD_DPadDown:
|
||||||
|
case kHIDUsage_GD_DPadRight:
|
||||||
|
case kHIDUsage_GD_DPadLeft:
|
||||||
|
case kHIDUsage_GD_Start:
|
||||||
|
case kHIDUsage_GD_Select:
|
||||||
|
case kHIDUsage_GD_SystemMainMenu:
|
||||||
|
if (!ElementAlreadyAdded(cookie, pDevice->firstButton)) {
|
||||||
|
element = (recElement *) calloc(1, sizeof (recElement));
|
||||||
|
if (element) {
|
||||||
|
pDevice->buttons++;
|
||||||
|
headElement = &(pDevice->firstButton);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case kHIDPage_Simulation:
|
||||||
|
switch (usage) {
|
||||||
|
case kHIDUsage_Sim_Rudder:
|
||||||
|
case kHIDUsage_Sim_Throttle:
|
||||||
|
case kHIDUsage_Sim_Accelerator:
|
||||||
|
case kHIDUsage_Sim_Brake:
|
||||||
|
if (!ElementAlreadyAdded(cookie, pDevice->firstAxis)) {
|
||||||
|
element = (recElement *) calloc(1, sizeof (recElement));
|
||||||
|
if (element) {
|
||||||
|
pDevice->axes++;
|
||||||
|
headElement = &(pDevice->firstAxis);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case kHIDPage_Button:
|
||||||
|
case kHIDPage_Consumer: /* e.g. 'pause' button on Steelseries MFi gamepads. */
|
||||||
|
if (!ElementAlreadyAdded(cookie, pDevice->firstButton)) {
|
||||||
|
element = (recElement *) calloc(1, sizeof (recElement));
|
||||||
|
if (element) {
|
||||||
|
pDevice->buttons++;
|
||||||
|
headElement = &(pDevice->firstButton);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case kIOHIDElementTypeCollection: {
|
||||||
|
CFArrayRef array = IOHIDElementGetChildren(refElement);
|
||||||
|
if (array) {
|
||||||
|
AddHIDElements(array, pDevice);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (element && headElement) { /* add to list */
|
||||||
|
recElement *elementPrevious = NULL;
|
||||||
|
recElement *elementCurrent = *headElement;
|
||||||
|
while (elementCurrent && usage >= elementCurrent->usage) {
|
||||||
|
elementPrevious = elementCurrent;
|
||||||
|
elementCurrent = elementCurrent->pNext;
|
||||||
|
}
|
||||||
|
if (elementPrevious) {
|
||||||
|
elementPrevious->pNext = element;
|
||||||
|
} else {
|
||||||
|
*headElement = element;
|
||||||
|
}
|
||||||
|
|
||||||
|
element->elementRef = refElement;
|
||||||
|
element->usagePage = usagePage;
|
||||||
|
element->usage = usage;
|
||||||
|
element->pNext = elementCurrent;
|
||||||
|
|
||||||
|
element->minReport = element->min = (SInt32) IOHIDElementGetLogicalMin(refElement);
|
||||||
|
element->maxReport = element->max = (SInt32) IOHIDElementGetLogicalMax(refElement);
|
||||||
|
element->cookie = IOHIDElementGetCookie(refElement);
|
||||||
|
|
||||||
|
pDevice->elements++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
GetDeviceInfo(IOHIDDeviceRef hidDevice, recDevice *pDevice)
|
||||||
|
{
|
||||||
|
const uint16_t BUS_USB = 0x03;
|
||||||
|
const uint16_t BUS_BLUETOOTH = 0x05;
|
||||||
|
int32_t vendor = 0;
|
||||||
|
int32_t product = 0;
|
||||||
|
int32_t version = 0;
|
||||||
|
CFTypeRef refCF = NULL;
|
||||||
|
CFArrayRef array = NULL;
|
||||||
|
uint16_t *guid16 = (uint16_t *)pDevice->guid.data;
|
||||||
|
|
||||||
|
/* get usage page and usage */
|
||||||
|
refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDPrimaryUsagePageKey));
|
||||||
|
if (refCF) {
|
||||||
|
CFNumberGetValue(refCF, kCFNumberSInt32Type, &pDevice->usagePage);
|
||||||
|
}
|
||||||
|
if (pDevice->usagePage != kHIDPage_GenericDesktop) {
|
||||||
|
return false; /* Filter device list to non-keyboard/mouse stuff */
|
||||||
|
}
|
||||||
|
|
||||||
|
refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDPrimaryUsageKey));
|
||||||
|
if (refCF) {
|
||||||
|
CFNumberGetValue(refCF, kCFNumberSInt32Type, &pDevice->usage);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((pDevice->usage != kHIDUsage_GD_Joystick &&
|
||||||
|
pDevice->usage != kHIDUsage_GD_GamePad &&
|
||||||
|
pDevice->usage != kHIDUsage_GD_MultiAxisController)) {
|
||||||
|
return false; /* Filter device list to non-keyboard/mouse stuff */
|
||||||
|
}
|
||||||
|
|
||||||
|
pDevice->deviceRef = hidDevice;
|
||||||
|
|
||||||
|
/* get device name */
|
||||||
|
refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDProductKey));
|
||||||
|
if (!refCF) {
|
||||||
|
/* Maybe we can't get "AwesomeJoystick2000", but we can get "Logitech"? */
|
||||||
|
refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDManufacturerKey));
|
||||||
|
}
|
||||||
|
if ((!refCF) || (!CFStringGetCString(refCF, pDevice->product, sizeof (pDevice->product), kCFStringEncodingUTF8))) {
|
||||||
|
strlcpy(pDevice->product, "Unidentified joystick", sizeof (pDevice->product));
|
||||||
|
}
|
||||||
|
|
||||||
|
refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDVendorIDKey));
|
||||||
|
if (refCF) {
|
||||||
|
CFNumberGetValue(refCF, kCFNumberSInt32Type, &vendor);
|
||||||
|
}
|
||||||
|
|
||||||
|
refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDProductIDKey));
|
||||||
|
if (refCF) {
|
||||||
|
CFNumberGetValue(refCF, kCFNumberSInt32Type, &product);
|
||||||
|
}
|
||||||
|
|
||||||
|
refCF = IOHIDDeviceGetProperty(hidDevice, CFSTR(kIOHIDVersionNumberKey));
|
||||||
|
if (refCF) {
|
||||||
|
CFNumberGetValue(refCF, kCFNumberSInt32Type, &version);
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(pDevice->guid.data, 0, sizeof(pDevice->guid.data));
|
||||||
|
|
||||||
|
if (vendor && product) {
|
||||||
|
*guid16++ = BUS_USB;
|
||||||
|
*guid16++ = 0;
|
||||||
|
*guid16++ = vendor;
|
||||||
|
*guid16++ = 0;
|
||||||
|
*guid16++ = product;
|
||||||
|
*guid16++ = 0;
|
||||||
|
*guid16++ = version;
|
||||||
|
*guid16++ = 0;
|
||||||
|
} else {
|
||||||
|
*guid16++ = BUS_BLUETOOTH;
|
||||||
|
*guid16++ = 0;
|
||||||
|
strlcpy((char*)guid16, pDevice->product, sizeof(pDevice->guid.data) - 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
array = IOHIDDeviceCopyMatchingElements(hidDevice, NULL, kIOHIDOptionsTypeNone);
|
||||||
|
if (array) {
|
||||||
|
AddHIDElements(array, pDevice);
|
||||||
|
CFRelease(array);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)
|
||||||
|
{
|
||||||
|
recDevice *device = joystick->hwdata;
|
||||||
|
recElement *element;
|
||||||
|
SInt32 value;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!device) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (device->removed) { /* device was unplugged; ignore it. */
|
||||||
|
if (joystick->hwdata) {
|
||||||
|
joystick->force_recentering = true;
|
||||||
|
joystick->hwdata = NULL;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
element = device->firstAxis;
|
||||||
|
i = 0;
|
||||||
|
while (element) {
|
||||||
|
value = GetHIDScaledCalibratedState(device, element, -32768, 32767);
|
||||||
|
SDL_PrivateJoystickAxis(joystick, i, value);
|
||||||
|
element = element->pNext;
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
|
||||||
|
element = device->firstButton;
|
||||||
|
i = 0;
|
||||||
|
while (element) {
|
||||||
|
value = GetHIDElementState(device, element);
|
||||||
|
if (value > 1) { /* handle pressure-sensitive buttons */
|
||||||
|
value = 1;
|
||||||
|
}
|
||||||
|
SDL_PrivateJoystickButton(joystick, i, value);
|
||||||
|
element = element->pNext;
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void JoystickInputCallback(
|
||||||
|
SDL_Joystick * joystick,
|
||||||
|
IOReturn result,
|
||||||
|
void * _Nullable sender,
|
||||||
|
IOHIDReportType type,
|
||||||
|
uint32_t reportID,
|
||||||
|
uint8_t * report,
|
||||||
|
CFIndex reportLength)
|
||||||
|
{
|
||||||
|
SDL_SYS_JoystickUpdate(joystick);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
JoystickDeviceWasAddedCallback(void *ctx, IOReturn res, void *sender, IOHIDDeviceRef ioHIDDeviceObject)
|
||||||
|
{
|
||||||
|
recDevice *device;
|
||||||
|
io_service_t ioservice;
|
||||||
|
|
||||||
|
if (res != kIOReturnSuccess) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
device = (recDevice *) calloc(1, sizeof(recDevice));
|
||||||
|
|
||||||
|
if (!device) {
|
||||||
|
abort();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!GetDeviceInfo(ioHIDDeviceObject, device)) {
|
||||||
|
free(device);
|
||||||
|
return; /* not a device we care about, probably. */
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_Joystick *joystick = &device->joystick;
|
||||||
|
|
||||||
|
joystick->instance_id = device->instance_id;
|
||||||
|
joystick->hwdata = device;
|
||||||
|
joystick->name = device->product;
|
||||||
|
|
||||||
|
joystick->naxes = device->axes;
|
||||||
|
joystick->nbuttons = device->buttons;
|
||||||
|
|
||||||
|
if (joystick->naxes > 0) {
|
||||||
|
joystick->axes = (SDL_JoystickAxisInfo *) calloc(joystick->naxes, sizeof(SDL_JoystickAxisInfo));
|
||||||
|
}
|
||||||
|
if (joystick->nbuttons > 0) {
|
||||||
|
joystick->buttons = (uint8_t *) calloc(joystick->nbuttons, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get notified when this device is disconnected. */
|
||||||
|
IOHIDDeviceRegisterRemovalCallback(ioHIDDeviceObject, JoystickDeviceWasRemovedCallback, device);
|
||||||
|
static uint8_t junk[80];
|
||||||
|
IOHIDDeviceRegisterInputReportCallback(ioHIDDeviceObject, junk, sizeof(junk), (IOHIDReportCallback) JoystickInputCallback, joystick);
|
||||||
|
IOHIDDeviceScheduleWithRunLoop(ioHIDDeviceObject, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
|
||||||
|
|
||||||
|
/* Allocate an instance ID for this device */
|
||||||
|
device->instance_id = ++s_joystick_instance_id;
|
||||||
|
|
||||||
|
/* We have to do some storage of the io_service_t for SDL_HapticOpenFromJoystick */
|
||||||
|
ioservice = IOHIDDeviceGetService(ioHIDDeviceObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
ConfigHIDManager(CFArrayRef matchingArray)
|
||||||
|
{
|
||||||
|
CFRunLoopRef runloop = CFRunLoopGetCurrent();
|
||||||
|
|
||||||
|
if (IOHIDManagerOpen(hidman, kIOHIDOptionsTypeNone) != kIOReturnSuccess) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
IOHIDManagerSetDeviceMatchingMultiple(hidman, matchingArray);
|
||||||
|
IOHIDManagerRegisterDeviceMatchingCallback(hidman, JoystickDeviceWasAddedCallback, NULL);
|
||||||
|
IOHIDManagerScheduleWithRunLoop(hidman, runloop, kCFRunLoopDefaultMode);
|
||||||
|
|
||||||
|
return true; /* good to go. */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static CFDictionaryRef
|
||||||
|
CreateHIDDeviceMatchDictionary(const UInt32 page, const UInt32 usage, int *okay)
|
||||||
|
{
|
||||||
|
CFDictionaryRef retval = NULL;
|
||||||
|
CFNumberRef pageNumRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &page);
|
||||||
|
CFNumberRef usageNumRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usage);
|
||||||
|
const void *keys[2] = { (void *) CFSTR(kIOHIDDeviceUsagePageKey), (void *) CFSTR(kIOHIDDeviceUsageKey) };
|
||||||
|
const void *vals[2] = { (void *) pageNumRef, (void *) usageNumRef };
|
||||||
|
|
||||||
|
if (pageNumRef && usageNumRef) {
|
||||||
|
retval = CFDictionaryCreate(kCFAllocatorDefault, keys, vals, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pageNumRef) {
|
||||||
|
CFRelease(pageNumRef);
|
||||||
|
}
|
||||||
|
if (usageNumRef) {
|
||||||
|
CFRelease(usageNumRef);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!retval) {
|
||||||
|
*okay = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
CreateHIDManager(void)
|
||||||
|
{
|
||||||
|
bool retval = false;
|
||||||
|
int okay = 1;
|
||||||
|
const void *vals[] = {
|
||||||
|
(void *) CreateHIDDeviceMatchDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick, &okay),
|
||||||
|
(void *) CreateHIDDeviceMatchDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad, &okay),
|
||||||
|
(void *) CreateHIDDeviceMatchDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_MultiAxisController, &okay),
|
||||||
|
};
|
||||||
|
const size_t numElements = sizeof(vals) / sizeof(vals[0]);
|
||||||
|
CFArrayRef array = okay ? CFArrayCreate(kCFAllocatorDefault, vals, numElements, &kCFTypeArrayCallBacks) : NULL;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < numElements; i++) {
|
||||||
|
if (vals[i]) {
|
||||||
|
CFRelease((CFTypeRef) vals[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (array) {
|
||||||
|
hidman = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
|
||||||
|
if (hidman != NULL) {
|
||||||
|
retval = ConfigHIDManager(array);
|
||||||
|
}
|
||||||
|
CFRelease(array);
|
||||||
|
}
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void __attribute__((constructor)) SDL_SYS_JoystickInit(void)
|
||||||
|
{
|
||||||
|
if (!CreateHIDManager()) {
|
||||||
|
fprintf(stderr, "Joystick: Couldn't initialize HID Manager");
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,5 @@
|
|||||||
#import <Cocoa/Cocoa.h>
|
#import <Cocoa/Cocoa.h>
|
||||||
|
|
||||||
int main(int argc, const char * argv[])
|
int main(int argc, const char * argv[]) {
|
||||||
{
|
|
||||||
return NSApplicationMain(argc, argv);
|
return NSApplicationMain(argc, argv);
|
||||||
}
|
}
|
||||||
|