diff --git a/BootROMs/cgb_boot_fast.asm b/BootROMs/cgb_boot_fast.asm
new file mode 100644
index 00000000..60d82c11
--- /dev/null
+++ b/BootROMs/cgb_boot_fast.asm
@@ -0,0 +1,785 @@
+; Sameboy CGB bootstrap ROM
+; 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
+
+; Select RAM bank
+ ld a, 2
+ ldh [$70], a
+ 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
\ No newline at end of file
diff --git a/Cocoa/Info.plist b/Cocoa/Info.plist
index 149f6789..72375fa9 100644
--- a/Cocoa/Info.plist
+++ b/Cocoa/Info.plist
@@ -14,9 +14,13 @@
CFBundleTypeIconFile
Cartridge
CFBundleTypeName
- Gameboy Game
+ GameBoy Game
CFBundleTypeRole
Viewer
+ LSItemContentTypes
+
+ com.github.liji32.sameboy.gb
+
LSTypeIsPackage
0
NSDocumentClass
@@ -30,9 +34,13 @@
CFBundleTypeIconFile
ColorCartridge
CFBundleTypeName
- Gameboy Color Game
+ GameBoy Color Game
CFBundleTypeRole
Viewer
+ LSItemContentTypes
+
+ com.github.liji32.sameboy.gbc
+
LSTypeIsPackage
0
NSDocumentClass
@@ -44,7 +52,7 @@
CFBundleIconFile
AppIcon.icns
CFBundleIdentifier
- com.github.LIJI32.SameBoy
+ com.github.liji32.sameboy
CFBundleInfoDictionaryVersion
6.0
CFBundleName
@@ -61,11 +69,52 @@
LSMinimumSystemVersion
10.9
+ NSHumanReadableCopyright
+ Copyright © 2015-2017 Lior Halphon
NSMainNibFile
MainMenu
- NSHumanReadableCopyright
- Copyright © 2015-2016 Lior Halphon
NSPrincipalClass
NSApplication
+ UTExportedTypeDeclarations
+
+
+ UTTypeConformsTo
+
+ public.data
+
+ UTTypeDescription
+ GameBoy Game
+ UTTypeIconFile
+ Cartridge
+ UTTypeIdentifier
+ com.github.liji32.sameboy.gb
+ UTTypeTagSpecification
+
+ public.filename-extension
+
+ gb
+
+
+
+
+ UTTypeConformsTo
+
+ public.data
+
+ UTTypeDescription
+ GameBoy Color Game
+ UTTypeIconFile
+ ColorCartridge
+ UTTypeIdentifier
+ com.github.liji32.sameboy.gbc
+ UTTypeTagSpecification
+
+ public.filename-extension
+
+ gbc
+
+
+
+
diff --git a/Makefile b/Makefile
index f91fc8dc..ffe52cc7 100755
--- a/Makefile
+++ b/Makefile
@@ -82,11 +82,12 @@ SDL_TARGET := $(BIN)/sdl/sameboy
TESTER_TARGET := $(BIN)/tester/sameboy_tester
endif
-cocoa: $(BIN)/Sameboy.app
+cocoa: $(BIN)/SameBoy.app
+quicklook: $(BIN)/SameBoy.qlgenerator
sdl: $(SDL_TARGET) $(BIN)/sdl/dmg_boot.bin $(BIN)/sdl/cgb_boot.bin $(BIN)/sdl/LICENSE
bootroms: $(BIN)/BootROMs/cgb_boot.bin $(BIN)/BootROMs/dmg_boot.bin
tester: $(TESTER_TARGET) $(BIN)/tester/dmg_boot.bin $(BIN)/tester/cgb_boot.bin
-
+all: cocoa sdl tester
# Get a list of our source files and their respective object file targets
@@ -96,11 +97,13 @@ TESTER_SOURCES := $(shell ls Tester/*.c)
ifeq ($(PLATFORM),Darwin)
COCOA_SOURCES := $(shell ls Cocoa/*.m) $(shell ls HexFiend/*.m)
+QUICKLOOK_SOURCES := $(shell ls QuickLook/*.m) $(shell ls QuickLook/*.c)
SDL_SOURCES += $(shell ls SDL/*.m)
endif
CORE_OBJECTS := $(patsubst %,$(OBJ)/%.o,$(CORE_SOURCES))
COCOA_OBJECTS := $(patsubst %,$(OBJ)/%.o,$(COCOA_SOURCES))
+QUICKLOOK_OBJECTS := $(patsubst %,$(OBJ)/%.o,$(QUICKLOOK_SOURCES))
SDL_OBJECTS := $(patsubst %,$(OBJ)/%.o,$(SDL_SOURCES))
TESTER_OBJECTS := $(patsubst %,$(OBJ)/%.o,$(TESTER_SOURCES))
@@ -142,33 +145,61 @@ $(OBJ)/%.m.o: %.m
Shaders:$(shell ls Shaders/*.fsh)
-$(BIN)/Sameboy.app: $(BIN)/Sameboy.app/Contents/MacOS/Sameboy \
- $(shell ls Cocoa/*.icns) \
- Cocoa/License.html \
- Cocoa/info.plist \
- $(BIN)/Sameboy.app/Contents/Resources/dmg_boot.bin \
- $(BIN)/Sameboy.app/Contents/Resources/cgb_boot.bin \
- $(BIN)/Sameboy.app/Contents/Resources/Base.lproj/Document.nib \
- $(BIN)/Sameboy.app/Contents/Resources/Base.lproj/MainMenu.nib \
- $(BIN)/Sameboy.app/Contents/Resources/Base.lproj/Preferences.nib \
- Shaders
- $(MKDIR) -p $(BIN)/Sameboy.app/Contents/Resources
- cp Cocoa/*.icns $(BIN)/Sameboy.app/Contents/Resources/
- sed s/@VERSION/$(VERSION)/ < Cocoa/info.plist > $(BIN)/Sameboy.app/Contents/info.plist
- cp Cocoa/License.html $(BIN)/Sameboy.app/Contents/Resources/Credits.html
- $(MKDIR) -p $(BIN)/Sameboy.app/Contents/Resources/Shaders
- cp Shaders/*.fsh $(BIN)/Sameboy.app/Contents/Resources/Shaders
+$(BIN)/SameBoy.app: $(BIN)/SameBoy.app/Contents/MacOS/SameBoy \
+ $(shell ls Cocoa/*.icns) \
+ Cocoa/License.html \
+ Cocoa/Info.plist \
+ $(BIN)/SameBoy.app/Contents/Resources/dmg_boot.bin \
+ $(BIN)/SameBoy.app/Contents/Resources/cgb_boot.bin \
+ $(BIN)/SameBoy.app/Contents/Resources/Base.lproj/Document.nib \
+ $(BIN)/SameBoy.app/Contents/Resources/Base.lproj/MainMenu.nib \
+ $(BIN)/SameBoy.app/Contents/Resources/Base.lproj/Preferences.nib \
+ $(BIN)/SameBoy.qlgenerator \
+ Shaders
+ $(MKDIR) -p $(BIN)/SameBoy.app/Contents/Resources
+ cp Cocoa/*.icns $(BIN)/SameBoy.app/Contents/Resources/
+ sed s/@VERSION/$(VERSION)/ < Cocoa/Info.plist > $(BIN)/SameBoy.app/Contents/Info.plist
+ cp Cocoa/License.html $(BIN)/SameBoy.app/Contents/Resources/Credits.html
+ $(MKDIR) -p $(BIN)/SameBoy.app/Contents/Resources/Shaders
+ cp Shaders/*.fsh $(BIN)/SameBoy.app/Contents/Resources/Shaders
+ $(MKDIR) -p $(BIN)/SameBoy.app/Contents/Library/QuickLook/
+ cp -rf $(BIN)/SameBoy.qlgenerator $(BIN)/SameBoy.app/Contents/Library/QuickLook/
-$(BIN)/Sameboy.app/Contents/MacOS/Sameboy: $(CORE_OBJECTS) $(COCOA_OBJECTS)
+$(BIN)/SameBoy.app/Contents/MacOS/SameBoy: $(CORE_OBJECTS) $(COCOA_OBJECTS)
-@$(MKDIR) -p $(dir $@)
$(CC) $^ -o $@ $(LDFLAGS) -framework OpenGL -framework AudioUnit -framework AVFoundation -framework CoreVideo -framework CoreMedia
ifeq ($(CONF), release)
strip $@
endif
-$(BIN)/Sameboy.app/Contents/Resources/Base.lproj/%.nib: Cocoa/%.xib
+$(BIN)/SameBoy.app/Contents/Resources/Base.lproj/%.nib: Cocoa/%.xib
ibtool --compile $@ $^
+# Quick Look generator
+
+$(BIN)/SameBoy.qlgenerator: $(BIN)/SameBoy.qlgenerator/Contents/MacOS/SameBoyQL \
+ $(shell ls QuickLook/*.png) \
+ QuickLook/Info.plist \
+ $(BIN)/SameBoy.qlgenerator/Contents/Resources/cgb_boot_fast.bin
+ $(MKDIR) -p $(BIN)/SameBoy.qlgenerator/Contents/Resources
+ cp QuickLook/*.png $(BIN)/SameBoy.qlgenerator/Contents/Resources/
+ sed s/@VERSION/$(VERSION)/ < QuickLook/Info.plist > $(BIN)/SameBoy.qlgenerator/Contents/Info.plist
+
+# Currently, SameBoy.app includes two "copies" of each Core .o file once in the app itself and
+# once in the QL Generator. It should probably become a dylib instead.
+$(BIN)/SameBoy.qlgenerator/Contents/MacOS/SameBoyQL: $(CORE_OBJECTS) $(QUICKLOOK_OBJECTS)
+ -@$(MKDIR) -p $(dir $@)
+ $(CC) $^ -o $@ $(LDFLAGS) -bundle -framework Cocoa -framework Quicklook
+ifeq ($(CONF), release)
+ strip -u -r -s QuickLook/exports.sym $@
+endif
+
+# cgb_boot_fast.bin is not a standard boot ROM, we don't expect it to exist in the user-provided
+# boot ROM directory.
+$(BIN)/SameBoy.qlgenerator/Contents/Resources/cgb_boot_fast.bin: $(BIN)/BootROMs/cgb_boot_fast.bin
+ -@$(MKDIR) -p $(dir $@)
+ cp -f $^ $@
+
# SDL Port
# Unix versions build only one binary
@@ -220,7 +251,7 @@ $(BIN)/sdl/%.bin $(BIN)/tester/%.bin: $(BOOTROMS_DIR)/%.bin
-@$(MKDIR) -p $(dir $@)
cp -f $^ $@
-$(BIN)/Sameboy.app/Contents/Resources/%.bin: $(BOOTROMS_DIR)/%.bin
+$(BIN)/SameBoy.app/Contents/Resources/%.bin: $(BOOTROMS_DIR)/%.bin
-@$(MKDIR) -p $(dir $@)
cp -f $^ $@
diff --git a/QuickLook/CartridgeTemplate.png b/QuickLook/CartridgeTemplate.png
new file mode 100644
index 00000000..bd65b5d6
Binary files /dev/null and b/QuickLook/CartridgeTemplate.png differ
diff --git a/QuickLook/ColorCartridgeTemplate.png b/QuickLook/ColorCartridgeTemplate.png
new file mode 100644
index 00000000..ddc27c9e
Binary files /dev/null and b/QuickLook/ColorCartridgeTemplate.png differ
diff --git a/QuickLook/GenerateThumbnailForURL.m b/QuickLook/GenerateThumbnailForURL.m
new file mode 100644
index 00000000..13e9e2fa
--- /dev/null
+++ b/QuickLook/GenerateThumbnailForURL.m
@@ -0,0 +1,96 @@
+#include
+#include
+#include "get_image_for_rom.h"
+
+/* -----------------------------------------------------------------------------
+ Generate a thumbnail for file
+
+ This function's job is to create thumbnail for designated file as fast as possible
+ ----------------------------------------------------------------------------- */
+
+OSStatus GenerateThumbnailForURL(void *thisInterface, QLThumbnailRequestRef thumbnail, CFURLRef url, CFStringRef contentTypeUTI, CFDictionaryRef options, CGSize maxSize)
+{
+ @autoreleasepool {
+ /* Load the template NSImages when generating the first thumbnail */
+ static NSImage *template = nil;
+ static NSImage *templateUniversal = nil;
+ static NSImage *templateColor = nil;
+ static NSBundle *bundle = nil;
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ bundle = [NSBundle bundleWithIdentifier:@"com.github.liji32.sameboy.previewer"];
+ template = [[NSImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CartridgeTemplate" ofType:@"png"]];
+ templateUniversal = [[NSImage alloc] initWithContentsOfFile:[bundle pathForResource:@"UniversalCartridgeTemplate" ofType:@"png"]];
+ templateColor = [[NSImage alloc] initWithContentsOfFile:[bundle pathForResource:@"ColorCartridgeTemplate" ofType:@"png"]];
+ });
+
+ uint32_t bitmap[160*144];
+ uint8_t cgbFlag = 0;
+
+ /* The cgb_boot_fast boot ROM skips the boot animation */
+ if (get_image_for_rom([[(__bridge NSURL *)url path] UTF8String],
+ [[bundle pathForResource:@"cgb_boot_fast" ofType:@"bin"] UTF8String],
+ bitmap, &cgbFlag, (cancel_callback_t)QLThumbnailRequestIsCancelled, thumbnail)) {
+ return noErr;
+ }
+
+ /* Convert the screenshot to a CGImageRef */
+ CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, bitmap, sizeof(bitmap), NULL);
+ CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
+ CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault;
+ CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;
+
+ CGImageRef iref = CGImageCreate(160,
+ 144,
+ 8,
+ 32,
+ 4 * 160,
+ colorSpaceRef,
+ bitmapInfo,
+ provider,
+ NULL,
+ YES,
+ renderingIntent);
+
+
+ /* Draw the icon */
+ CGContextRef cgContext = QLThumbnailRequestCreateContext(thumbnail, [template size], false, (__bridge CFDictionaryRef)(@{@"IconFlavor" : @(0)}));
+ if (cgContext) {
+ NSGraphicsContext *context = [NSGraphicsContext graphicsContextWithGraphicsPort:(void *)cgContext flipped:NO];
+ if (context) {
+ [NSGraphicsContext setCurrentContext:context];
+
+ /* Use the CGB flag to determine the cartrdige "look":
+ - DMG cartridges are grey
+ - CGB cartrdiges are transparent
+ - CGB cartridges that support DMG systems are black
+ */
+ NSImage *effectiveTemplate = nil;
+ switch (cgbFlag) {
+ case 0xC0:
+ effectiveTemplate = templateColor;
+ break;
+ case 0x80:
+ effectiveTemplate = templateUniversal;
+ break;
+ default:
+ effectiveTemplate = template;
+ }
+
+ /* Convert the screenshot to a magnified NSImage */
+ NSImage *screenshot = [[NSImage alloc] initWithCGImage:iref size:NSMakeSize(640, 576)];
+ /* Draw the screenshot */
+ [screenshot drawInRect:NSMakeRect(192, 150, 640, 576)];
+ /* Mask it with the template (The middle part of the template image is transparent) */
+ [effectiveTemplate drawInRect:(NSRect){{0,0},template.size}];
+ }
+ QLThumbnailRequestFlushContext(thumbnail, cgContext);
+ CFRelease(cgContext);
+ }
+
+ CGColorSpaceRelease(colorSpaceRef);
+ CGDataProviderRelease(provider);
+ CGImageRelease(iref);
+ }
+ return noErr;
+}
\ No newline at end of file
diff --git a/QuickLook/Info.plist b/QuickLook/Info.plist
new file mode 100644
index 00000000..da833754
--- /dev/null
+++ b/QuickLook/Info.plist
@@ -0,0 +1,62 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ en
+ CFBundleDocumentTypes
+
+
+ CFBundleTypeRole
+ QLGenerator
+ LSItemContentTypes
+
+ com.github.liji32.sameboy.gb
+ com.github.liji32.sameboy.gbc
+
+
+
+ CFBundleExecutable
+ SameBoyQL
+ CFBundleIdentifier
+ com.github.liji32.sameboy.previewer
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ SameBoy
+ CFBundleShortVersionString
+ Version @VERSION
+ CFBundleSignature
+ ????
+ CFPlugInDynamicRegisterFunction
+
+ CFPlugInDynamicRegistration
+ NO
+ CFPlugInFactories
+
+ 48BC750C-2BB9-49B1-AE80-786E22B3DEB4
+ QuickLookGeneratorPluginFactory
+
+ CFPlugInTypes
+
+ 5E2D9680-5022-40FA-B806-43349622E5B9
+
+ 48BC750C-2BB9-49B1-AE80-786E22B3DEB4
+
+
+ CFPlugInUnloadFunction
+
+ NSHumanReadableCopyright
+ Copyright © 2015-2017 Lior Halphon
+ QLNeedsToBeRunInMainThread
+
+ QLPreviewHeight
+ 144
+ QLPreviewWidth
+ 160
+ QLSupportsConcurrentRequests
+
+ QLThumbnailMinimumSize
+ 64
+
+
diff --git a/QuickLook/UniversalCartridgeTemplate.png b/QuickLook/UniversalCartridgeTemplate.png
new file mode 100644
index 00000000..45267330
Binary files /dev/null and b/QuickLook/UniversalCartridgeTemplate.png differ
diff --git a/QuickLook/exports.sym b/QuickLook/exports.sym
new file mode 100644
index 00000000..778b2031
--- /dev/null
+++ b/QuickLook/exports.sym
@@ -0,0 +1,3 @@
+_DeallocQuickLookGeneratorPluginType
+_QuickLookGeneratorQueryInterface
+_QuickLookGeneratorPluginFactory
\ No newline at end of file
diff --git a/QuickLook/get_image_for_rom.c b/QuickLook/get_image_for_rom.c
new file mode 100755
index 00000000..b9a5d0a1
--- /dev/null
+++ b/QuickLook/get_image_for_rom.c
@@ -0,0 +1,95 @@
+#include
+#include
+#include
+#include
+
+#include "get_image_for_rom.h"
+#include "gb.h"
+
+#define LENGTH 60 * 10
+
+struct local_data {
+ unsigned long frames;
+ bool running;
+ cancel_callback_t cancel_callback;
+ void *callback_data;
+};
+
+static char *async_input_callback(GB_gameboy_t *gb)
+{
+ return NULL;
+}
+
+static void log_callback(GB_gameboy_t *gb, const char *string, GB_log_attributes attributes)
+{
+
+}
+
+
+static void vblank(GB_gameboy_t *gb)
+{
+
+ struct local_data *local_data = (struct local_data *)gb->user_data;
+
+ if (local_data->cancel_callback(local_data->callback_data)) {
+ local_data->running = false;
+ return;
+ }
+
+ if (local_data->frames == LENGTH) {
+ local_data->running = false;
+ }
+ else if (local_data->frames == LENGTH - 1) {
+ gb->disable_rendering = false;
+ }
+
+ local_data->frames++;
+}
+
+static uint32_t rgb_encode(GB_gameboy_t *gb, uint8_t r, uint8_t g, uint8_t b)
+{
+ return (b << 16) | (g << 8) | (r) | 0xFF000000;
+}
+
+int get_image_for_rom(const char *filename, const char *boot_path, uint32_t *output, uint8_t *cgb_flag,
+ cancel_callback_t cancel_callback, void *callback_data)
+{
+ GB_gameboy_t gb;
+ GB_init_cgb(&gb);
+ if (GB_load_boot_rom(&gb, boot_path)) {
+ GB_free(&gb);
+ return 1;
+ }
+
+ GB_set_vblank_callback(&gb, (GB_vblank_callback_t) vblank);
+ GB_set_pixels_output(&gb, output);
+ GB_set_rgb_encode_callback(&gb, rgb_encode);
+ GB_set_async_input_callback(&gb, async_input_callback);
+ GB_set_log_callback(&gb, log_callback);
+
+ if (GB_load_rom(&gb, filename)) {
+ GB_free(&gb);
+ return 1;
+ }
+
+ /* Run emulation */
+ struct local_data local_data = {0,};
+ gb.user_data = &local_data;
+ local_data.running = true;
+ local_data.frames = 0;
+ local_data.cancel_callback = cancel_callback;
+ local_data.callback_data = callback_data;
+ gb.turbo = gb.turbo_dont_skip = gb.disable_rendering = true;
+
+ while (local_data.running) {
+ GB_run(&gb);
+ }
+
+
+ *cgb_flag = gb.rom[0x143] & 0xC0;
+
+ GB_free(&gb);
+ /* Report failure if cancelled */
+ return local_data.frames != LENGTH + 1;
+}
+
diff --git a/QuickLook/get_image_for_rom.h b/QuickLook/get_image_for_rom.h
new file mode 100644
index 00000000..a92fbd90
--- /dev/null
+++ b/QuickLook/get_image_for_rom.h
@@ -0,0 +1,11 @@
+#ifndef get_image_for_rom_h
+#define get_image_for_rom_h
+#include
+
+typedef bool (*cancel_callback_t)(void*);
+
+int get_image_for_rom(const char *filename, const char *boot_path, uint32_t *output, uint8_t *cgb_flag,
+ cancel_callback_t cancel_callback, void *callback_data);
+
+
+#endif
diff --git a/QuickLook/main.c b/QuickLook/main.c
new file mode 100644
index 00000000..7998fb58
--- /dev/null
+++ b/QuickLook/main.c
@@ -0,0 +1,198 @@
+/* This is an Apple-provided "bootstrap" code for Quick Look generators, nothing intersting here. */
+
+#include
+#include
+#include
+#include
+
+// -----------------------------------------------------------------------------
+// constants
+// -----------------------------------------------------------------------------
+
+// Don't modify this line
+#define PLUGIN_ID "48BC750C-2BB9-49B1-AE80-786E22B3DEB4"
+
+//
+// Below is the generic glue code for all plug-ins.
+//
+// You should not have to modify this code aside from changing
+// names if you decide to change the names defined in the Info.plist
+//
+
+
+// -----------------------------------------------------------------------------
+// typedefs
+// -----------------------------------------------------------------------------
+
+// The thumbnail generation function to be implemented in GenerateThumbnailForURL.c
+OSStatus GenerateThumbnailForURL(void *thisInterface, QLThumbnailRequestRef thumbnail, CFURLRef url, CFStringRef contentTypeUTI, CFDictionaryRef options, CGSize maxSize);
+
+// The layout for an instance of QuickLookGeneratorPlugIn
+typedef struct __QuickLookGeneratorPluginType
+{
+ void *conduitInterface;
+ CFUUIDRef factoryID;
+ UInt32 refCount;
+} QuickLookGeneratorPluginType;
+
+// -----------------------------------------------------------------------------
+// prototypes
+// -----------------------------------------------------------------------------
+// Forward declaration for the IUnknown implementation.
+//
+
+QuickLookGeneratorPluginType *AllocQuickLookGeneratorPluginType(CFUUIDRef inFactoryID);
+void DeallocQuickLookGeneratorPluginType(QuickLookGeneratorPluginType *thisInstance);
+HRESULT QuickLookGeneratorQueryInterface(void *thisInstance,REFIID iid,LPVOID *ppv);
+void *QuickLookGeneratorPluginFactory(CFAllocatorRef allocator,CFUUIDRef typeID);
+ULONG QuickLookGeneratorPluginAddRef(void *thisInstance);
+ULONG QuickLookGeneratorPluginRelease(void *thisInstance);
+
+// -----------------------------------------------------------------------------
+// myInterfaceFtbl definition
+// -----------------------------------------------------------------------------
+// The QLGeneratorInterfaceStruct function table.
+//
+static QLGeneratorInterfaceStruct myInterfaceFtbl = {
+ NULL,
+ QuickLookGeneratorQueryInterface,
+ QuickLookGeneratorPluginAddRef,
+ QuickLookGeneratorPluginRelease,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+
+// -----------------------------------------------------------------------------
+// AllocQuickLookGeneratorPluginType
+// -----------------------------------------------------------------------------
+// Utility function that allocates a new instance.
+// You can do some initial setup for the generator here if you wish
+// like allocating globals etc...
+//
+QuickLookGeneratorPluginType *AllocQuickLookGeneratorPluginType(CFUUIDRef inFactoryID)
+{
+ QuickLookGeneratorPluginType *theNewInstance;
+
+ theNewInstance = (QuickLookGeneratorPluginType *)malloc(sizeof(QuickLookGeneratorPluginType));
+ memset(theNewInstance,0,sizeof(QuickLookGeneratorPluginType));
+
+ /* Point to the function table Malloc enough to store the stuff and copy the filler from myInterfaceFtbl over */
+ theNewInstance->conduitInterface = malloc(sizeof(QLGeneratorInterfaceStruct));
+ memcpy(theNewInstance->conduitInterface,&myInterfaceFtbl,sizeof(QLGeneratorInterfaceStruct));
+
+ /* Retain and keep an open instance refcount for each factory. */
+ theNewInstance->factoryID = CFRetain(inFactoryID);
+ CFPlugInAddInstanceForFactory(inFactoryID);
+
+ /* This function returns the IUnknown interface so set the refCount to one. */
+ theNewInstance->refCount = 1;
+ return theNewInstance;
+}
+
+// -----------------------------------------------------------------------------
+// DeallocQuickLookGeneratorPluginType
+// -----------------------------------------------------------------------------
+// Utility function that deallocates the instance when
+// the refCount goes to zero.
+// In the current implementation generator interfaces are never deallocated
+// but implement this as this might change in the future
+//
+void DeallocQuickLookGeneratorPluginType(QuickLookGeneratorPluginType *thisInstance)
+{
+ CFUUIDRef theFactoryID;
+
+ theFactoryID = thisInstance->factoryID;
+ /* Free the conduitInterface table up */
+ free(thisInstance->conduitInterface);
+
+ /* Free the instance structure */
+ free(thisInstance);
+ if (theFactoryID){
+ CFPlugInRemoveInstanceForFactory(theFactoryID);
+ CFRelease(theFactoryID);
+ }
+}
+
+// -----------------------------------------------------------------------------
+// QuickLookGeneratorQueryInterface
+// -----------------------------------------------------------------------------
+// Implementation of the IUnknown QueryInterface function.
+//
+HRESULT QuickLookGeneratorQueryInterface(void *thisInstance,REFIID iid,LPVOID *ppv)
+{
+ CFUUIDRef interfaceID;
+
+ interfaceID = CFUUIDCreateFromUUIDBytes(kCFAllocatorDefault,iid);
+
+ if (CFEqual(interfaceID,kQLGeneratorCallbacksInterfaceID)){
+ /* If the Right interface was requested, bump the ref count,
+ * set the ppv parameter equal to the instance, and
+ * return good status.
+ */
+ ((QLGeneratorInterfaceStruct *)((QuickLookGeneratorPluginType *)thisInstance)->conduitInterface)->GenerateThumbnailForURL = GenerateThumbnailForURL;
+ ((QLGeneratorInterfaceStruct *)((QuickLookGeneratorPluginType*)thisInstance)->conduitInterface)->AddRef(thisInstance);
+ *ppv = thisInstance;
+ CFRelease(interfaceID);
+ return S_OK;
+ }else{
+ /* Requested interface unknown, bail with error. */
+ *ppv = NULL;
+ CFRelease(interfaceID);
+ return E_NOINTERFACE;
+ }
+}
+
+// -----------------------------------------------------------------------------
+// QuickLookGeneratorPluginAddRef
+// -----------------------------------------------------------------------------
+// Implementation of reference counting for this type. Whenever an interface
+// is requested, bump the refCount for the instance. NOTE: returning the
+// refcount is a convention but is not required so don't rely on it.
+//
+ULONG QuickLookGeneratorPluginAddRef(void *thisInstance)
+{
+ ((QuickLookGeneratorPluginType *)thisInstance )->refCount += 1;
+ return ((QuickLookGeneratorPluginType*) thisInstance)->refCount;
+}
+
+// -----------------------------------------------------------------------------
+// QuickLookGeneratorPluginRelease
+// -----------------------------------------------------------------------------
+// When an interface is released, decrement the refCount.
+// If the refCount goes to zero, deallocate the instance.
+//
+ULONG QuickLookGeneratorPluginRelease(void *thisInstance)
+{
+ ((QuickLookGeneratorPluginType*)thisInstance)->refCount -= 1;
+ if (((QuickLookGeneratorPluginType*)thisInstance)->refCount == 0){
+ DeallocQuickLookGeneratorPluginType((QuickLookGeneratorPluginType*)thisInstance );
+ return 0;
+ }else{
+ return ((QuickLookGeneratorPluginType*) thisInstance )->refCount;
+ }
+}
+
+// -----------------------------------------------------------------------------
+// QuickLookGeneratorPluginFactory
+// -----------------------------------------------------------------------------
+void *QuickLookGeneratorPluginFactory(CFAllocatorRef allocator,CFUUIDRef typeID)
+{
+ QuickLookGeneratorPluginType *result;
+ CFUUIDRef uuid;
+
+ /* If correct type is being requested, allocate an
+ * instance of kQLGeneratorTypeID and return the IUnknown interface.
+ */
+ if (CFEqual(typeID,kQLGeneratorTypeID)){
+ uuid = CFUUIDCreateFromString(kCFAllocatorDefault,CFSTR(PLUGIN_ID));
+ result = AllocQuickLookGeneratorPluginType(uuid);
+ CFRelease(uuid);
+ return result;
+ }
+ /* If the requested type is incorrect, return NULL. */
+ return NULL;
+}
+