-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This is the first commit. Examples: - Game Boy - Game Boy + SDL 2 - SNES - SNES + SDL 2 - GTK 3 - Java - PHP Plus - Text to Speech
- Loading branch information
0 parents
commit a999e48
Showing
37 changed files
with
4,993 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
*.bin | ||
.DS_Store | ||
*.dylib | ||
*.gb | ||
*.gbc | ||
*.smc |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
# Awesome PHP FFI | ||
|
||
[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat)](http://gabrielrcouto.mit-license.org/) | ||
|
||
This is a repository showing some use cases of [PHP FFI](https://wiki.php.net/rfc/ffi). | ||
|
||
Examples in this repository: | ||
|
||
- [Game Boy Emulator](gameboy) | ||
- [Game Boy Emulator with SDL2 rendering](gameboy-sdl) | ||
- [GTK3 window with a button](gtk3) | ||
- [Calling a function from .class Java file](java-itaucripto) | ||
- [Modifying the PHP Core to auto inject PHP Open Tag](php-plus) | ||
- [Simple printf example](printf) | ||
- [Creating window and rendering pixels with SDL2](sdl) | ||
- [SNES Emulator](snes) | ||
- [SNES Emulator with SDL2 rendering](snes-sdl) | ||
- [text-to-speech using Mac library](speech) | ||
|
||
Other repositories about PHP FFI: | ||
|
||
- [DBus access via FFI and PHP 7.4 POC](https://github.com/paxal/php-dbus) | ||
- [Driving webview using PHP 7.4 FFI](https://github.com/andyvanee/php-webview-ffi) | ||
- [PHP wrapper for libsass using FFI](https://github.com/shyim/php-sass) | ||
- [PHP TensorFlow Binding](https://github.com/dstogov/php-tensorflow) | ||
- [FFIMe](https://github.com/ircmaxell/FFIMe) | ||
- [A compiler. For PHP](https://github.com/ircmaxell/php-compiler) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
# Game Boy Emulator + SDL2 | ||
|
||
![sameboy-sdl](https://user-images.githubusercontent.com/2197005/65930680-fab53280-e3dc-11e9-98bb-f02c6cd94368.gif) | ||
|
||
This example uses the sameboy library for Game Boy emulation + SDL2 library for rendering. | ||
|
||
You'll need to compile the [sameboy library](https://github.com/LIJI32/SameBoy) and change the `FFI_LIB` on `sameboy_libretro.h` file. | ||
|
||
You'll need to install the [SDL2 library](https://www.libsdl.org/download-2.0.php) and change the `FFI_LIB` on `sdl.h` file. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,181 @@ | ||
<?php | ||
const ROM = 'pokemon.gbc'; | ||
|
||
const RETRO_ENVIRONMENT_SET_PIXEL_FORMAT = 10; | ||
const RETRO_ENVIRONMENT_GET_VARIABLE = 15; | ||
const RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE = 17; | ||
const RETRO_ENVIRONMENT_GET_RUMBLE_INTERFACE = 23; | ||
const RETRO_ENVIRONMENT_GET_LOG_INTERFACE = 27; | ||
|
||
const RETRO_DEVICE_ID_JOYPAD_B = 0; | ||
const RETRO_DEVICE_ID_JOYPAD_Y = 1; | ||
const RETRO_DEVICE_ID_JOYPAD_SELECT = 2; | ||
const RETRO_DEVICE_ID_JOYPAD_START = 3; | ||
const RETRO_DEVICE_ID_JOYPAD_UP = 4; | ||
const RETRO_DEVICE_ID_JOYPAD_DOWN = 5; | ||
const RETRO_DEVICE_ID_JOYPAD_LEFT = 6; | ||
const RETRO_DEVICE_ID_JOYPAD_RIGHT = 7; | ||
const RETRO_DEVICE_ID_JOYPAD_A = 8; | ||
const RETRO_DEVICE_ID_JOYPAD_X = 9; | ||
const RETRO_DEVICE_ID_JOYPAD_L = 10; | ||
const RETRO_DEVICE_ID_JOYPAD_R = 11; | ||
const RETRO_DEVICE_ID_JOYPAD_L2 = 12; | ||
const RETRO_DEVICE_ID_JOYPAD_R2 = 13; | ||
const RETRO_DEVICE_ID_JOYPAD_L3 = 14; | ||
const RETRO_DEVICE_ID_JOYPAD_R3 = 15; | ||
|
||
const KEYBOARD_MAPPING = [ | ||
RETRO_DEVICE_ID_JOYPAD_RIGHT => 'd', | ||
RETRO_DEVICE_ID_JOYPAD_LEFT => 'a', | ||
RETRO_DEVICE_ID_JOYPAD_UP => 'w', | ||
RETRO_DEVICE_ID_JOYPAD_DOWN => 's', | ||
RETRO_DEVICE_ID_JOYPAD_A => ',', | ||
RETRO_DEVICE_ID_JOYPAD_B => '.', | ||
RETRO_DEVICE_ID_JOYPAD_SELECT => 'n', | ||
RETRO_DEVICE_ID_JOYPAD_START => 'm' | ||
]; | ||
|
||
const SDL_INIT_VIDEO = 0x00000020; | ||
const SDL_RENDERER_ACCELERATED = 0x00000002; | ||
const SDL_RENDERER_PRESENTVSYNC = 0x00000004; | ||
const SDL_QUIT = 0x100; | ||
const SDL_WINDOW_SHOWN = 0x00000004; | ||
|
||
const ZOOM_LEVEL = 4; | ||
const SCREEN_HEIGHT = 144; | ||
const SCREEN_WIDTH = 160; | ||
|
||
$currentSecond = 0; | ||
$fps = 0; | ||
$framesInSecond = 0; | ||
|
||
$sdl = FFI::load('sdl.h'); | ||
$sameboy = FFI::load('sameboy_libretro.h'); | ||
|
||
if ($sdl->SDL_Init(SDL_INIT_VIDEO) != 0) { | ||
throw new Exception('SDL_Init Error: ' . $sdl->SDL_GetError(), 1); | ||
} | ||
|
||
$win = $sdl->SDL_CreateWindow('PHP FFI Boy', 0, 0, SCREEN_WIDTH * ZOOM_LEVEL, SCREEN_HEIGHT * ZOOM_LEVEL, SDL_WINDOW_SHOWN); | ||
|
||
if ($win == null){ | ||
throw new Exception('SDL_CreateWindow Error: ' . $sdl->SDL_GetError(), 1); | ||
$sdl->SDL_Quit(); | ||
} | ||
|
||
$ren = $sdl->SDL_CreateRenderer($win, -1, 0); | ||
|
||
$sdl->SDL_SetRenderDrawColor($ren, 0, 0, 0, 255); | ||
$sdl->SDL_RenderClear($ren); | ||
|
||
$event = $sdl->new('SDL_Event', false); | ||
|
||
$gameinfo = $sameboy->new('struct retro_game_info', false); | ||
$gameinfo->path = $sameboy->strdup(ROM); | ||
|
||
$sameboy->retro_set_environment(function($cmd, $data) use ($sameboy) { | ||
if ($cmd === RETRO_ENVIRONMENT_SET_PIXEL_FORMAT) { | ||
return true; | ||
} | ||
|
||
if ($cmd === RETRO_ENVIRONMENT_GET_VARIABLE) { | ||
$argument = $sameboy->cast('struct retro_variable', $data); | ||
|
||
if ($argument->key === 'sameboy_model') { | ||
$argument->value = $sameboy->strdup('Game Boy'); | ||
$data = FFI::addr($argument); | ||
} | ||
|
||
return true; | ||
} | ||
|
||
if ($cmd === RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE) { | ||
$argument = $sameboy->cast('bool', $data); | ||
$data = FFI::addr($argument); | ||
return false; | ||
} | ||
|
||
if ($cmd === RETRO_ENVIRONMENT_GET_RUMBLE_INTERFACE) { | ||
$argument = $sameboy->cast('struct retro_rumble_interface', $data); | ||
$argument->set_rumble_state = function($port, $effect, $strength) { | ||
return; | ||
}; | ||
$data = FFI::addr($argument); | ||
return false; | ||
} | ||
|
||
if ($cmd === RETRO_ENVIRONMENT_GET_LOG_INTERFACE) { | ||
return false; | ||
} | ||
|
||
return; | ||
}); | ||
|
||
$sameboy->retro_set_audio_sample(function($a, $b) { | ||
return; | ||
}); | ||
|
||
$sameboy->retro_set_input_poll(function () { | ||
return; | ||
}); | ||
|
||
$sameboy->retro_set_input_state(function ($port, $device, $index, $id) use (&$key) { | ||
if ($key === KEYBOARD_MAPPING[$id]) { | ||
return true; | ||
} | ||
|
||
return false; | ||
}); | ||
|
||
$sameboy->retro_set_video_refresh(function ($data, $width, $height, $pitch) use (&$ren, $sdl) { | ||
$pixels = FFI::cast('uint32_t[' . ($width * $height) . ']', $data); | ||
|
||
$sdl->SDL_SetRenderDrawColor($ren, 142, 134, 9, 255); | ||
$sdl->SDL_RenderClear($ren); | ||
$sdl->SDL_SetRenderDrawColor($ren, 37, 57, 61, 255); | ||
|
||
for ($y = 0; $y < SCREEN_HEIGHT; $y++) { | ||
for ($x = 0; $x < SCREEN_WIDTH; $x++) { | ||
$pixel = $pixels[$x + (SCREEN_WIDTH * $y)]; | ||
|
||
if ($pixel === 0) { | ||
$rect = $sdl->new('SDL_Rect', true); | ||
$rect->x = $x * ZOOM_LEVEL; | ||
$rect->y = $y * ZOOM_LEVEL; | ||
$rect->w = $rect->h = ZOOM_LEVEL; | ||
$sdl->SDL_RenderFillRect($ren, FFI::addr($rect)); | ||
$sdl->SDL_RenderDrawRect($ren, FFI::addr($rect)); | ||
} | ||
} | ||
} | ||
|
||
$sdl->SDL_RenderPresent($ren); | ||
|
||
return; | ||
}); | ||
|
||
$sameboy->retro_init(); | ||
$sameboy->retro_load_game(FFI::addr($gameinfo)); | ||
|
||
$isRunning = true; | ||
|
||
while ($isRunning) { | ||
//Calculate current FPS | ||
if ($currentSecond != time()) { | ||
$fps = $framesInSecond; | ||
$currentSecond = time(); | ||
$framesInSecond = 1; | ||
} else { | ||
++$framesInSecond; | ||
} | ||
|
||
$sameboy->retro_run(); | ||
|
||
if ($sdl->SDL_PollEvent(FFI::addr($event))) { | ||
if ($event->type == SDL_QUIT) { | ||
$isRunning = false; | ||
} | ||
} | ||
|
||
$sdl->SDL_SetWindowTitle($win, 'PHP FFI Boy - FPS: ' . $fps); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
#define FFI_LIB "sameboy_libretro.dylib" | ||
|
||
int sprintf(char *str, const char *format, ...); | ||
char *strdup(const char *s); | ||
|
||
typedef void (*retro_audio_sample_t)(int16_t left, int16_t right); | ||
typedef bool (*retro_environment_t)(unsigned cmd, void *data); | ||
typedef void (*retro_input_poll_t)(void); | ||
typedef int16_t (*retro_input_state_t)(unsigned port, unsigned device, unsigned index, unsigned id); | ||
typedef void (*retro_log_printf_t)(enum retro_log_level level, const char *fmt, ...); | ||
typedef bool (*retro_set_rumble_state_t)(unsigned port, enum retro_rumble_effect effect, uint16_t strength); | ||
typedef void (*retro_video_refresh_t)(const void *data, unsigned width, unsigned height, size_t pitch); | ||
|
||
bool vblank1_occurred = false, vblank2_occurred = false; | ||
|
||
void retro_init(void); | ||
unsigned retro_api_version(void); | ||
bool retro_load_game(const struct retro_game_info *info); | ||
void retro_run(void); | ||
|
||
void retro_set_audio_sample(retro_audio_sample_t cb); | ||
|
||
void retro_set_environment(retro_environment_t cb); | ||
|
||
void retro_set_input_poll(retro_input_poll_t cb); | ||
void retro_set_input_state(retro_input_state_t cb); | ||
|
||
void retro_set_video_refresh(retro_video_refresh_t cb); | ||
|
||
struct retro_game_info | ||
{ | ||
const char *path; /* Path to game, UTF-8 encoded. | ||
* Usually used as a reference. | ||
* May be NULL if rom was loaded from stdin | ||
* or similar. | ||
* retro_system_info::need_fullpath guaranteed | ||
* that this path is valid. */ | ||
const void *data; /* Memory buffer of loaded game. Will be NULL | ||
* if need_fullpath was set. */ | ||
size_t size; /* Size of memory buffer. */ | ||
const char *meta; /* String of implementation specific meta-data. */ | ||
}; | ||
|
||
struct retro_rumble_interface | ||
{ | ||
retro_set_rumble_state_t set_rumble_state; | ||
}; | ||
|
||
struct retro_variable | ||
{ | ||
/* Variable to query in RETRO_ENVIRONMENT_GET_VARIABLE. | ||
* If NULL, obtains the complete environment string if more | ||
* complex parsing is necessary. | ||
* The environment string is formatted as key-value pairs | ||
* delimited by semicolons as so: | ||
* "key1=value1;key2=value2;..." | ||
*/ | ||
const char *key; | ||
|
||
/* Value to be obtained. If key does not exist, it is set to NULL. */ | ||
const char *value; | ||
}; |
Oops, something went wrong.