Add PNaCl build of VBA-M.

This CL adds a PNaCl target for VBA-M which builds a Chrome App
into the src/pnacl/app directory.
This commit is contained in:
Christopher Lam 2014-12-12 21:21:48 +11:00
parent 8b46072ca3
commit 27243323fd
15 changed files with 866 additions and 27 deletions

View File

@ -34,6 +34,13 @@ if( NOT ENABLE_DEBUGGER AND ENABLE_SDL )
message( SEND_ERROR "The SDL port can't be built without debugging support" ) message( SEND_ERROR "The SDL port can't be built without debugging support" )
endif( NOT ENABLE_DEBUGGER AND ENABLE_SDL ) endif( NOT ENABLE_DEBUGGER AND ENABLE_SDL )
if ( PNACL )
set ( ENABLE_SDL ON )
set ( ENABLE_GTK OFF )
set ( ENABLE_LINK OFF )
set ( ENABLE_WX OFF )
endif ( PNACL )
# Set the version number with -DVERSION=X.X.X-uber # Set the version number with -DVERSION=X.X.X-uber
IF( NOT VERSION ) IF( NOT VERSION )
SET( VERSION "https://github.com/EmperorArthur/VBA-M" ) SET( VERSION "https://github.com/EmperorArthur/VBA-M" )
@ -71,7 +78,7 @@ endif( ENABLE_ASM_SCALERS )
# Look for some dependencies using CMake scripts # Look for some dependencies using CMake scripts
FIND_PACKAGE ( ZLIB REQUIRED ) FIND_PACKAGE ( ZLIB REQUIRED )
FIND_PACKAGE ( PNG REQUIRED ) FIND_PACKAGE ( PNG REQUIRED )
FIND_PACKAGE ( OpenGL REQUIRED ) FIND_PACKAGE ( OpenGL )
FIND_PACKAGE ( SDL REQUIRED ) FIND_PACKAGE ( SDL REQUIRED )
if( ENABLE_LINK ) if( ENABLE_LINK )
@ -87,7 +94,10 @@ SET(VBAMCORE_LIBS
${ZLIB_LIBRARY} ${ZLIB_LIBRARY}
${PNG_LIBRARY}) ${PNG_LIBRARY})
if ( PNACL )
# Add core NaCl libraries.
SET(VBAMCORE_LIBS ${VBAMCORE_LIBS} ppapi ppapi_cpp nacl_io)
endif ( PNACL )
# Disable looking for GTK if not going to build the GTK frontend # Disable looking for GTK if not going to build the GTK frontend
# so that pkg-config is not required # so that pkg-config is not required
@ -131,8 +141,12 @@ IF( NOT SYSCONFDIR )
SET( SYSCONFDIR "/etc" ) SET( SYSCONFDIR "/etc" )
ENDIF( NOT SYSCONFDIR ) ENDIF( NOT SYSCONFDIR )
if ( OpenGL_FOUND )
set( USE_OPENGL_FLAG "-DUSE_OPENGL " )
endif ( OpenGL_FOUND )
# C defines # C defines
ADD_DEFINITIONS (-DHAVE_NETINET_IN_H -DHAVE_ARPA_INET_H -DHAVE_ZLIB_H -DFINAL_VERSION -DSDL -DUSE_OPENGL -DSYSCONFDIR='"${SYSCONFDIR}"' -DWITH_LIRC='${WITHLIRC}') ADD_DEFINITIONS (-DHAVE_NETINET_IN_H -DHAVE_ARPA_INET_H -DHAVE_ZLIB_H -DFINAL_VERSION -DSDL ${USE_OPENGL_FLAG} -DSYSCONFDIR='"${SYSCONFDIR}"' -DWITH_LIRC='${WITHLIRC}')
ADD_DEFINITIONS (-DVERSION='"${VERSION}"' -DPKGDATADIR='"${PKGDATADIR}"' -DPACKAGE='') ADD_DEFINITIONS (-DVERSION='"${VERSION}"' -DPKGDATADIR='"${PKGDATADIR}"' -DPACKAGE='')
if( ENABLE_LINK ) if( ENABLE_LINK )
@ -304,6 +318,10 @@ SET(SRC_SDL
src/sdl/expr-lex.cpp src/sdl/expr-lex.cpp
) )
SET(SRC_PNACL
src/pnacl/nacl_glue.cpp
)
SET(SRC_FILTERS SET(SRC_FILTERS
src/filters/2xSaI.cpp src/filters/2xSaI.cpp
src/filters/admame.cpp src/filters/admame.cpp
@ -376,8 +394,8 @@ IF( ENABLE_SDL )
vbam vbam
WIN32 WIN32
${SRC_SDL} ${SRC_SDL}
${SRC_PNACL}
) )
IF( WIN32 ) IF( WIN32 )
SET( WIN32_LIBRARIES wsock32 ) SET( WIN32_LIBRARIES wsock32 )
ENDIF( WIN32 ) ENDIF( WIN32 )
@ -392,11 +410,14 @@ IF( ENABLE_SDL )
${WIN32_LIBRARIES} ${WIN32_LIBRARIES}
${LIRC_CLIENT_LIBRARY} ${LIRC_CLIENT_LIBRARY}
) )
IF ( PNACL )
build_to_app( vbam )
ELSE ( PNACL )
INSTALL(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/vbam DESTINATION bin) INSTALL(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/vbam DESTINATION bin)
INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/src/sdl/vbam.cfg-example INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/src/sdl/vbam.cfg-example
DESTINATION ${SYSCONFDIR} DESTINATION ${SYSCONFDIR}
RENAME vbam.cfg) RENAME vbam.cfg)
ENDIF ( PNACL )
ENDIF( ENABLE_SDL ) ENDIF( ENABLE_SDL )
IF( ENABLE_GTK ) IF( ENABLE_GTK )

View File

@ -0,0 +1,56 @@
# How to build for PNaCl on Linux.
#
# Download the Native Client SDK.
# Run naclsdk update pepper_XX. The pepper version must be pepper_37.
# Set NACL_SDK_ROOT in your environment to nacl_sdk/pepper_XX.
#
# Check out the naclports repository. This was built with
# naclports@b0aaa9899316e157883ee4b54f70affbebd2c3e1.
# Set NACL_ARCH to 'pnacl' in your environment and run 'make sdl'
# from the naclports root.
#
# Come back to the root of vba-m and run
# cmake -DCMAKE_TOOLCHAIN_FILE=CMakeScripts/PNaCl.Toolchain.cmake CMakeLists.txt
# make
#
# This will build a non-finalized PNaCl port to ./vbam and a finalized version
# to src/pnacl/app/vbam.pexe.
#
# The src/pnacl/app folder can be loaded as an unpacked extension into Chrome
# which will run vba-m as a packaged app.
include( CMakeForceCompiler )
set( PNACL ON )
set( PLATFORM_PREFIX "$ENV{NACL_SDK_ROOT}/toolchain/linux_pnacl" )
set( FINALIZED_TARGET "src/pnacl/app/vbam.pexe" )
set( CMAKE_SYSTEM_NAME "Linux" CACHE STRING "Target system." )
set( CMAKE_SYSTEM_PROCESSOR "LLVM-IR" CACHE STRING "Target processor." )
set( CMAKE_FIND_ROOT_PATH "${PLATFORM_PREFIX}/usr" )
set( CMAKE_AR "${PLATFORM_PREFIX}/bin64/pnacl-ar" CACHE STRING "")
set( CMAKE_RANLIB "${PLATFORM_PREFIX}/bin64/pnacl-ranlib" CACHE STRING "")
set( CMAKE_C_COMPILER "${PLATFORM_PREFIX}/bin64/pnacl-clang" )
set( CMAKE_CXX_COMPILER "${PLATFORM_PREFIX}/bin64/pnacl-clang++" )
set( CMAKE_C_FLAGS "-Wno-non-literal-null-conversion -Wno-deprecated-writable-strings -U__STRICT_ANSI__" CACHE STRING "" )
set( CMAKE_CXX_FLAGS "-Wno-non-literal-null-conversion -Wno-deprecated-writable-strings -U__STRICT_ANSI__" CACHE STRING "" )
cmake_force_c_compiler( ${CMAKE_C_COMPILER} Clang )
cmake_force_cxx_compiler( ${CMAKE_CXX_COMPILER} Clang )
set( CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER )
set( CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY )
set( CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY )
set( CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY )
macro( build_to_app _target )
add_custom_command( TARGET ${_target}
POST_BUILD
COMMAND "${PLATFORM_PREFIX}/bin64/pnacl-finalize"
"-o" "${FINALIZED_TARGET}"
"$<TARGET_FILE:${_target}>" )
endmacro()
set( ENV{SDLDIR} "${PLATFORM_PREFIX}/usr" )
include_directories( SYSTEM $ENV{NACL_SDK_ROOT}/include )
link_directories( $ENV{NACL_SDK_ROOT}/lib/pnacl/Release )

View File

@ -7,13 +7,13 @@
#ifdef __GNUC__ #ifdef __GNUC__
#if defined(__APPLE__) || defined (BSD) || defined (__NetBSD__) #if defined(__APPLE__) || defined (BSD) || defined (__NetBSD__) || defined (__native_client__)
typedef off_t __off64_t; /* off_t is 64 bits on BSD. */ typedef off_t __off64_t; /* off_t is 64 bits on BSD. */
#define fseeko64 fseeko #define fseeko64 fseeko
#define ftello64 ftello #define ftello64 ftello
#else #else
typedef off64_t __off64_t; typedef off64_t __off64_t;
#endif /* __APPLE__ || BSD */ #endif /* __APPLE__ || BSD || __native_client__ */
#endif /* __GNUC__ */ #endif /* __GNUC__ */
#ifndef _MSC_VER #ifndef _MSC_VER

View File

@ -5,7 +5,7 @@ extern int armExecute();
extern int thumbExecute(); extern int thumbExecute();
#ifdef __GNUC__ #ifdef __GNUC__
#ifndef __APPLE__ #if !defined(__APPLE__) && !defined(__native_client__)
# define INSN_REGPARM __attribute__((regparm(1))) # define INSN_REGPARM __attribute__((regparm(1)))
#else #else
# define INSN_REGPARM /*nothing*/ # define INSN_REGPARM /*nothing*/

View File

@ -0,0 +1,11 @@
chrome.app.runtime.onLaunched.addListener(function() {
chrome.app.window.create('game.html', {
bounds: {
width: 480,
height: 320
},
resizable: true,
minWidth: 480,
minHeight: 320
});
});

63
src/pnacl/app/game.html Normal file
View File

@ -0,0 +1,63 @@
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="listener">
<embed name="nacl_module"
id="naclModule"
width=480 height=320
src="game.nmf"
type="application/x-pnacl" />
</div>
<div id="loadingMessage" class="centered">
<div>Translating PNaCl to Native Architecture</div>
<div id="loadingBar">
<div id="progress"></div>
</div>
</div>
<div id="openMessage" class="centered helptext">
<div>Press Ctrl+O to open a rom</div>
<div id="filenameErrorMessage" class="errorMessage">
The filename cannot have spaces. Please rename the file and try again.
</div>
</div>
<div id="topLeftHelptext" class="helptext">
</div>
<div id="topRightHelptext" class="helptext">
<div><a href='#' id="advancedButton">Advanced</a></div>
<div class="advancedSetting">
<div><a href="#" id="importSaveButton">Import save</a></div>
<div>
<label>
<input type="checkbox" id="gamepadCheckbox">
Use gamepad
</label>
</div>
</div>
</div>
<div id="leftHelptext" class="helptext">
<div>A: Z</div>
<div>B: X</div>
<div>L: A</div>
<div>R: S</div>
<div>D-pad: Arrow keys</div>
</div>
<div id="rightHelptext" class="helptext">
<div>Start: Enter</div>
<div>Select: Backspace</div>
<div id="normalSaveStateKeys">
<div>Save state: Shift + F1-9</div>
<div>Load state: F1-9</div>
</div>
<div id="crosSaveStateKeys">
<div>Save state: Shift + Search(&#128269) + 1-9</div>
<div>Load state: Search(&#128269) + 1-9</div>
</div>
<div>Vol down/up: -/=</div>
</div>
<script src="index.js"></script>
</body>
</html>

10
src/pnacl/app/game.nmf Normal file
View File

@ -0,0 +1,10 @@
{
"program": {
"portable" : {
"pnacl-translate" : {
"url" : "vbam.pexe",
"optlevel": 2
}
}
}
}

BIN
src/pnacl/app/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

237
src/pnacl/app/index.js Normal file
View File

@ -0,0 +1,237 @@
var plugin = document.getElementById('naclModule');
var firstLoad = false;
var naclVisible = false;
var localfs = null;
var syncfs = null;
var saveDirEntry = null;
var gamepadCheckbox = document.getElementById('gamepadCheckbox');
var postFileCallback = function() {};
function setFilenameErrorVisible(visible) {
document.getElementById('filenameErrorMessage').style.display =
visible ? 'block' : 'none';
}
function setHelptextsVisible(visible) {
var helptexts = document.getElementsByClassName('helptext');
for (var i = 0; i < helptexts.length; i++) {
helptexts[i].style.display = visible ? 'block' : 'none';
}
}
function setNaclVisible(visible) {
naclVisible = visible;
document.getElementsByTagName('body')[0].className =
visible ? 'lightsOff' : 'lightsOn';
setHelptextsVisible(!visible);
scaleNacl();
}
function scaleNacl() {
var bounds = chrome.app.window.current().getBounds();
var scaleX = bounds.width / plugin.width;
var scaleY = bounds.height / plugin.height;
var scale = naclVisible ? Math.min(scaleX, scaleY) : 0;
plugin.style.webkitTransform = 'scale(' + scale + ')';
}
function makeNewPlugin() {
var newNode = plugin.cloneNode(true);
plugin.parentNode.appendChild(newNode);
plugin.parentNode.removeChild(plugin);
plugin = document.getElementById('naclModule');
setNaclVisible(false);
}
function showOpenFileDialog() {
setFilenameErrorVisible(false);
chrome.fileSystem.chooseEntry({
'type': 'openFile',
'accepts': [{'description': 'GBA Roms', 'extensions': ['zip', 'gba']}]
},
function(entry) {
if (!entry)
return;
if (entry.fullPath.indexOf(' ') != -1) {
setFilenameErrorVisible(true);
return;
}
postFileCallback = function() {
setNaclVisible(true);
plugin.postMessage({
'path': entry.fullPath,
'filesystem': entry.filesystem,
'gamepad' : gamepadCheckbox.checked
});
postFileCallback = function() {};
};
makeNewPlugin();
});
}
function copyToSyncFileSystem() {
if (!syncfs || !localfs) {
setTimeout(copyToSyncFileSystem, 1000);
return;
}
reader = saveDirEntry.createReader();
var copyIfNewer = function(entries) {
if (entries.length == 0)
return;
for (i in entries) {
var entry = entries[i];
if (!entry.isFile)
return;
(function() {
var e = entry;
var doCopy = function() {
console.log('Upsyncing ' + e.name);
e.copyTo(syncfs.root);
};
e.getMetadata(function(localMeta) {
syncfs.root.getFile(e.name, {}, function(syncEntry) {
syncEntry.getMetadata(function(syncMeta) {
// syncEntry.remove(function() {
if (syncMeta.modificationTime < localMeta.modificationTime)
doCopy();
// });
}, doCopy);
}, doCopy);
});
})();
}
reader.readEntries(copyIfNewer);
};
reader.readEntries(copyIfNewer);
setTimeout(copyToSyncFileSystem, 30000);
}
function initSyncFileSystem() {
syncfs = null;
chrome.syncFileSystem.getServiceStatus(function(status) {
if (status == 'running') {
chrome.syncFileSystem.requestFileSystem(function(fs) {
syncfs = fs;
});
} else {
setTimeout(initSyncFileSystem, 5000);
}
});
}
window.webkitRequestFileSystem(window.PERSISTENT, 0, function(fs) {
localfs = fs;
fs.root.getDirectory(
'states', { create: true }, function(d) { saveDirEntry = d; });
});
initSyncFileSystem();
copyToSyncFileSystem();
chrome.syncFileSystem.onServiceStatusChanged.addListener(initSyncFileSystem);
chrome.syncFileSystem.onFileStatusChanged.addListener(function(detail) {
/*
if (saveDirEntry && detail.status == 'synced' &&
detail.direction == 'remote_to_local') {
if (detail.action == 'deleted') {
saveDirEntry.getFile(detail.fileEntry.name, {}, function(entry) {
console.log('Deleting' + detail.fileEntry.name);
entry.remove(function() {});
});
detail.fileEntry.remove(function() {});
} else {
detail.fileEntry.copyTo(saveDirEntry);
console.log('Downsyncing ' + detail.fileEntry.name);
}
}*/
});
var is_cros = window.navigator.userAgent.toLowerCase().indexOf('cros') != -1;
document.getElementById('normalSaveStateKeys').style.display = is_cros ? 'none' : 'block';
document.getElementById('crosSaveStateKeys').style.display = is_cros ? 'block' : 'none';
document.getElementById('importSaveButton').onclick = function() {
chrome.fileSystem.chooseEntry({
'type': 'openFile',
'accepts': [{'description': 'VBA-M Save Files', 'extensions': ['sav', 'sgm']}]
},
function(entry) {
if (!entry)
return;
if (entry.fullPath.indexOf(' ') != -1) {
setFilenameErrorVisible(true);
return;
}
entry.copyTo(saveDirEntry);
console.log('Copied ' + entry.name + ' to HTML5fs');
});
};
// Gamepad checkbox.
gamepadCheckbox.onclick = function() {
chrome.storage.local.set({'gamepad': gamepadCheckbox.checked});
}
chrome.storage.local.get('gamepad', function(items) {
gamepadCheckbox.checked = items['gamepad'];
});
// Advanced settings
document.getElementById('advancedButton').onclick = function() {
document.getElementById('advancedButton').style.display = 'none';
var advancedSettings = document.getElementsByClassName('advancedSetting');
for (var i = 0; i < advancedSettings.length; i++)
advancedSettings[i].style.display = 'block';
};
// Add window scaling.
chrome.app.window.current().onBoundsChanged.addListener(scaleNacl);
// NaCl plugin listener
var listener = document.getElementById('listener');
listener.addEventListener(
'message',
function(e) {
console.log(e);
}, true);
listener.addEventListener(
'crash',
function(e) {
makeNewPlugin();
}, true);
listener.addEventListener(
'load',
function(e) {
setHelptextsVisible(true);
document.getElementById('loadingMessage').style.display = 'none';
postFileCallback();
firstLoad = true;
scaleNacl();
}, true);
listener.addEventListener(
'progress',
function(e) {
var percent = e.loaded / e.total;
document.getElementById('progress').style.width = (percent * 100) + '%';
}, true);
document.addEventListener('keydown', function(e) {
// Ctrl + o
if (firstLoad && e.ctrlKey && e.keyCode == 79) {
showOpenFileDialog();
}
// ESC
if (e.keyCode == 27) {
// makeNewPlugin();
e.preventDefault();
}
}, true);

View File

@ -0,0 +1,22 @@
{
"name": "VBA-M",
"description": "A native GBA emulator for Chrome",
"version": "1.3.6",
"manifest_version": 2,
"minimum_chrome_version": "34",
"offline_enabled": true,
"icons": {
"128": "icon.png"
},
"app": {
"background": {
"scripts": ["background.js"]
}
},
"permissions": [
"syncFileSystem",
"fileSystem",
"storage",
"unlimitedStorage"
]
}

89
src/pnacl/app/style.css Normal file
View File

@ -0,0 +1,89 @@
/* Structure */
html {
height: 100%;
}
body {
align-items: center;
display: flex;
height: 100%;
justify-content: center;
margin: 0;
}
.lightsOn {
background-color: rgb(251, 251, 251);
}
.lightsOff {
background-color: black;
}
.centered {
left: 0;
position: absolute;
text-align: center;
top: 40%;
width: 100%;
z-index: -1;
}
.helptext {
display: none;
}
.errorMessage {
display: none;
color: red;
}
#loadingBar {
background-color: black;
border-radius: 13px;
margin: 0 auto;
padding: 3px;
width: 200px;
}
#loadingBar > div {
background-color: blue;
border-radius: 10px;
height: 10px;
width: 0;
}
#naclModule {
-webkit-transform-origin: 50% 50%;
}
#leftHelptext {
bottom: 5px;
left: 5px;
position: fixed;
}
#rightHelptext {
bottom: 5px;
position: fixed;
right: 5px;
text-align: right;
}
#topLeftHelptext {
left: 5px;
position: fixed;
text-align: left;
top: 5px;
}
#topRightHelptext {
top: 5px;
position: fixed;
right: 5px;
text-align: right;
}
.advancedSetting {
display: none;
}

5
src/pnacl/app/vbam.cfg Normal file
View File

@ -0,0 +1,5 @@
captureDir=/store/states
saveDir=/store/states
batteryDir=/store/states
showSpeed=2
rtcEnabled=1

300
src/pnacl/nacl_glue.cpp Normal file
View File

@ -0,0 +1,300 @@
#include <unistd.h>
#include <SDL/SDL.h>
#include <SDL/SDL_events.h>
#include <SDL/SDL_nacl.h>
#include <nacl_io/nacl_io.h>
#include <ppapi/c/pp_errors.h>
#include <ppapi/c/pp_instance.h>
#include <ppapi/c/ppb_gamepad.h>
#include <ppapi/c/ppb_instance.h>
#include <ppapi/cpp/completion_callback.h>
#include <ppapi/cpp/file_system.h>
#include <ppapi/cpp/input_event.h>
#include <ppapi/cpp/instance.h>
#include <ppapi/cpp/module.h>
#include <ppapi/cpp/var.h>
#include <ppapi/cpp/var_dictionary.h>
#include <ppapi/utility/completion_callback_factory.h>
#include <sys/mount.h>
#include <sys/stat.h>
#define NUM_BUTTONS 17
#define AXIS_MAX 32767
extern int SDL_main(int argc, char **argv);
class GameInstance : public pp::Instance {
public:
explicit GameInstance(PP_Instance instance)
: pp::Instance(instance),
game_main_thread_(NULL),
num_changed_view_(0),
width_(0),
height_(0),
gamepad_enabled_(false),
cc_factory_(this) {
// Game requires mouse and keyboard events; add more if necessary.
nacl_io_init_ppapi(instance, pp::Module::Get()->get_browser_interface());
RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE |
PP_INPUTEVENT_CLASS_KEYBOARD);
++num_instances_;
assert(num_instances_ == 1);
gamepad_ = static_cast<const PPB_Gamepad*>(
pp::Module::Get()->GetBrowserInterface(PPB_GAMEPAD_INTERFACE));
assert(gamepad_);
}
virtual ~GameInstance() {
// Wait for game thread to finish.
if (game_main_thread_) {
pthread_join(game_main_thread_, NULL);
}
}
// This function is called with the HTML attributes of the embed tag,
// which can be used in lieu of command line arguments.
virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]) {
for (uint32_t i = 0; i < argc; ++i) {
if (argn[i] == std::string("width")) {
width_ = strtol(argv[i], 0, 0);
}
if (argn[i] == std::string("height")) {
height_ = strtol(argv[i], 0, 0);
}
}
return true;
}
// This crucial function forwards PPAPI events to SDL.
virtual bool HandleInputEvent(const pp::InputEvent& event) {
SDL_NACL_PushEvent(event.pp_resource());
return true;
}
virtual void HandleMessage(const pp::Var& message) {
pp::VarDictionary args(message);
pp::Var filesystem_var = args.Get("filesystem");
if (!filesystem_var.is_resource())
return;
pp::Resource filesystem_resource = filesystem_var.AsResource();
if (!pp::FileSystem::IsFileSystem(filesystem_resource))
return;
PP_Resource filesystem_id = filesystem_resource.pp_resource();
if (!filesystem_id)
return;
char buf[BUFSIZ];
if (snprintf(buf, BUFSIZ, "type=PERSISTENT,filesystem_resource=%d", filesystem_id) > BUFSIZ)
return;
rom_mount_string_ = buf;
pp::Var path_var = args.Get("path");
if (!path_var.is_string())
return;
rom_path_string_ = path_var.AsString();
pp::Var gamepad_var = args.Get("gamepad");
if (!gamepad_var.is_bool())
return;
gamepad_enabled_ = gamepad_var.AsBool();
}
// This function is called for various reasons, e.g. visibility and page
// size changes. We ignore these calls except for the first
// invocation, which we use to start the game.
virtual void DidChangeView(const pp::Rect& position, const pp::Rect& clip) {
++num_changed_view_;
if (num_changed_view_ > 1) return;
// NOTE: It is crucial that the two calls below are run here
// and not in a thread.
SDL_NACL_SetInstance(pp_instance(),
pp::Module::Get()->get_browser_interface(),
width_,
height_);
// This is SDL_Init call which used to be in game_main()
int flags = SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER |
SDL_INIT_NOPARACHUTE | SDL_INIT_JOYSTICK;
if(SDL_Init(flags))
exit(-1);
StartGameInNewThread(0);
}
private:
static int num_instances_; // Ensure we only create one instance.
static bool old_pressed_[NUM_BUTTONS];
pthread_t game_main_thread_; // This thread will run game_main().
int num_changed_view_; // Ensure we initialize an instance only once.
// Dimension of the SDL video screen.
int width_;
int height_;
pp::CompletionCallbackFactory<GameInstance> cc_factory_;
bool gamepad_enabled_;
const PPB_Gamepad* gamepad_;
std::string rom_mount_string_;
std::string rom_path_string_;
static SDL_Event *copyEvent(SDL_Event& event) {
SDL_Event *event_copy = (SDL_Event*)malloc(sizeof(SDL_Event));
*event_copy = event;
return event_copy;
}
int GetSDLButton(int i) {
switch (i) {
case 2:
return 1;
case 3:
return 0;
case 4:
return 6;
case 5:
return 7;
}
return i;
}
void GamepadInputLoop(int32_t dummy) {
PP_GamepadsSampleData gamepad_data;
gamepad_->Sample(pp_instance(), &gamepad_data);
if (gamepad_data.length) {
PP_GamepadSampleData& pad = gamepad_data.items[0];
if (pad.connected) {
// Draw axes.
SDL_Event e;
bool pressed[NUM_BUTTONS] = {0};
for (int i = 0; i < pad.buttons_length; i++)
pressed[i] = pad.buttons[i] > 0.3 || pressed[i];
if (old_pressed_[16] != pressed[16]) {
}
for (int i = 0; i < NUM_BUTTONS; i++) {
if (pressed[i] != old_pressed_[i]) {
e.type = SDL_JOYBUTTONDOWN;
e.jbutton.which = 0;
e.jbutton.button = GetSDLButton(i);
e.jbutton.state = pressed[i] ? SDL_PRESSED : SDL_RELEASED;
SDL_PushEvent(copyEvent(e));
old_pressed_[i] = pressed[i];
// Send keyboard SPACE instead of button 16.
if (i == 16) {
e.type = pressed[i] ? SDL_KEYDOWN : SDL_KEYUP;
e.key.type = pressed[i] ? SDL_KEYDOWN : SDL_KEYUP;
e.key.which = 1;
e.key.state = pressed[i] ? SDL_PRESSED : SDL_RELEASED;
e.key.keysym.scancode = SDLK_SPACE;
e.key.keysym.sym = SDLK_SPACE;
SDL_PushEvent(copyEvent(e));
}
}
}
e.type = SDL_JOYAXISMOTION;
e.jaxis.which = 0;
for (int i = 0; i < pad.axes_length; i++) {
e.jaxis.type = SDL_JOYAXISMOTION;
e.jaxis.axis = i;
e.jaxis.value = pad.axes[i] * AXIS_MAX;
SDL_PushEvent(copyEvent(e));
}
if (pressed[12]) {
e.jaxis.axis = 1;
e.jaxis.value = -AXIS_MAX;
SDL_PushEvent(copyEvent(e));
}
if (pressed[14]) {
e.jaxis.axis = 0;
e.jaxis.value = -AXIS_MAX;
SDL_PushEvent(copyEvent(e));
}
if (pressed[13]) {
e.jaxis.axis = 1;
e.jaxis.value = AXIS_MAX;
SDL_PushEvent(copyEvent(e));
}
if (pressed[15]) {
e.jaxis.axis = 0;
e.jaxis.value = AXIS_MAX;
SDL_PushEvent(copyEvent(e));
}
}
}
pp::Module::Get()->core()->CallOnMainThread(
100, cc_factory_.NewCallback(&GameInstance::GamepadInputLoop), 0);
}
void StartGameInNewThread(int32_t dummy) {
if (!rom_mount_string_.empty() && !rom_path_string_.empty()) {
fprintf(stderr, "Mounting %s with %s\n", rom_path_string_.c_str(),
rom_mount_string_.c_str());
pthread_create(&game_main_thread_, NULL, &LaunchGame, this);
if (gamepad_enabled_) {
pp::Module::Get()->core()->CallOnMainThread(
100, cc_factory_.NewCallback(&GameInstance::GamepadInputLoop), 0);
}
} else {
// Wait some more (here: 100ms).
pp::Module::Get()->core()->CallOnMainThread(
100, cc_factory_.NewCallback(&GameInstance::StartGameInNewThread), 0);
}
}
static void* LaunchGame(void* data) {
// Use "thiz" to get access to instance object.
GameInstance* thiz = reinterpret_cast<GameInstance*>(data);
umount("/");
mount("", "/", "memfs", 0, "");
mkdir("/config", 0777);
fprintf(stderr, "mount httpfs = %d\n", mount("/", "/config", "httpfs", 0, ""));
mkdir("/games", 0777);
fprintf(stderr, "mount rom = %d\n", mount("/", "/games", "html5fs", 0, thiz->rom_mount_string_.c_str()));
mkdir("/store", 0777);
fprintf(stderr, "mount html5fs = %d\n", mount("/", "/store", "html5fs", 0, ""));
mkdir("/store/states", 0777);
mkdir("/store/captures", 0777);
// Craft a fake command line.
char rom_path[BUFSIZ];
strncpy(rom_path, ("/games" + thiz->rom_path_string_).c_str(), BUFSIZ);
std::string config_string("--config=/config/vbam.cfg");
if (thiz->gamepad_enabled_)
config_string = "--config=/config/vbam-gamepad.cfg";
char config[BUFSIZ];
strncpy(config, config_string.c_str(), BUFSIZ);
char* argv[] = { "vbam", config, rom_path, NULL};
SDL_main(3, argv);
return 0;
}
};
int GameInstance::num_instances_;
bool GameInstance::old_pressed_[];
class GameModule : public pp::Module {
public:
GameModule() : pp::Module() {}
virtual ~GameModule() {}
virtual pp::Instance* CreateInstance(PP_Instance instance) {
return new GameInstance(instance);
}
};
namespace pp {
Module* CreateModule() {
return new GameModule();
}
} // namespace pp

View File

@ -23,6 +23,7 @@
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <cmath> #include <cmath>
#ifdef USE_OPENGL
#ifdef __APPLE__ #ifdef __APPLE__
#include <OpenGL/glu.h> #include <OpenGL/glu.h>
#include <OpenGL/glext.h> #include <OpenGL/glext.h>
@ -30,6 +31,7 @@
#include <GL/glu.h> #include <GL/glu.h>
#include <GL/glext.h> #include <GL/glext.h>
#endif #endif
#endif
#include <time.h> #include <time.h>
@ -134,9 +136,13 @@ int sdlPrintUsage = 0;
int cartridgeType = 3; int cartridgeType = 3;
int captureFormat = 0; int captureFormat = 0;
int openGL = 1;
int textureSize = 256; int textureSize = 256;
#ifdef USE_OPENGL
int openGL = 1;
GLuint screenTexture = 0; GLuint screenTexture = 0;
#else
int openGL = 0;
#endif
u8 *filterPix = 0; u8 *filterPix = 0;
int pauseWhenInactive = 0; int pauseWhenInactive = 0;
@ -810,6 +816,7 @@ void sdlReadPreferences(FILE *f)
void sdlOpenGLInit(int w, int h) void sdlOpenGLInit(int w, int h)
{ {
#ifdef USE_OPENGL
float screenAspect = (float) srcWidth / srcHeight, float screenAspect = (float) srcWidth / srcHeight,
windowAspect = (float) w / h; windowAspect = (float) w / h;
@ -861,6 +868,7 @@ void sdlOpenGLInit(int w, int h)
glClearColor(0.0,0.0,0.0,1.0); glClearColor(0.0,0.0,0.0,1.0);
glClear( GL_COLOR_BUFFER_BIT ); glClear( GL_COLOR_BUFFER_BIT );
#endif
} }
void sdlReadPreferences() void sdlReadPreferences()
@ -1277,6 +1285,9 @@ static void sdlHandleSavestateKey(int num, int shifted)
// 0: load // 0: load
// 1: save // 1: save
int backuping = 1; // controls whether we are doing savestate backups int backuping = 1; // controls whether we are doing savestate backups
#ifdef __native_client__
backuping = 0;
#endif
if ( sdlSaveKeysSwitch == 2 ) if ( sdlSaveKeysSwitch == 2 )
{ {
@ -1475,9 +1486,11 @@ void sdlPollEvents()
} }
break; break;
case SDLK_KP_DIVIDE: case SDLK_KP_DIVIDE:
case SDLK_MINUS:
sdlChangeVolume(-0.1); sdlChangeVolume(-0.1);
break; break;
case SDLK_KP_MULTIPLY: case SDLK_KP_MULTIPLY:
case SDLK_EQUALS:
sdlChangeVolume(0.1); sdlChangeVolume(0.1);
break; break;
case SDLK_KP_MINUS: case SDLK_KP_MINUS:
@ -1544,13 +1557,17 @@ void sdlPollEvents()
if(!(event.key.keysym.mod & MOD_NOCTRL) && if(!(event.key.keysym.mod & MOD_NOCTRL) &&
(event.key.keysym.mod & KMOD_CTRL)) { (event.key.keysym.mod & KMOD_CTRL)) {
filterFunction = 0; filterFunction = 0;
while (!filterFunction) while (!filterFunction) {
{
filter = (Filter)((filter + 1) % kInvalidFilter); filter = (Filter)((filter + 1) % kInvalidFilter);
#ifdef __native_client__
if (getFilterEnlargeFactor(filter) != filter_enlarge)
continue;
#endif
filterFunction = initFilter(filter, systemColorDepth, srcWidth); filterFunction = initFilter(filter, systemColorDepth, srcWidth);
} }
if (getFilterEnlargeFactor(filter) != filter_enlarge) if (getFilterEnlargeFactor(filter) != filter_enlarge) {
sdlInitVideo(); sdlInitVideo();
}
systemScreenMessage(getFilterName(filter)); systemScreenMessage(getFilterName(filter));
} }
break; break;
@ -2486,6 +2503,7 @@ void systemDrawScreen()
drawSpeed(screen, destPitch, 10, 20); drawSpeed(screen, destPitch, 10, 20);
if (openGL) { if (openGL) {
#ifdef USE_OPENGL
glClear( GL_COLOR_BUFFER_BIT ); glClear( GL_COLOR_BUFFER_BIT );
glPixelStorei(GL_UNPACK_ROW_LENGTH, destWidth); glPixelStorei(GL_UNPACK_ROW_LENGTH, destWidth);
if (systemColorDepth == 16) if (systemColorDepth == 16)
@ -2508,6 +2526,7 @@ void systemDrawScreen()
glEnd(); glEnd();
SDL_GL_SwapBuffers(); SDL_GL_SwapBuffers();
#endif
} else { } else {
SDL_UnlockSurface(surface); SDL_UnlockSurface(surface);
SDL_Flip(surface); SDL_Flip(surface);

View File

@ -249,7 +249,6 @@ static void sdlUpdateJoyButton(int which,
int b = joypad[j][i] & 0xffff; int b = joypad[j][i] & 0xffff;
if(dev) { if(dev) {
dev--; dev--;
if((dev == which) && (b >= 128) && (b == (button+128))) { if((dev == which) && (b >= 128) && (b == (button+128))) {
sdlButtons[j][i] = pressed; sdlButtons[j][i] = pressed;
} }
@ -399,7 +398,11 @@ void inputInitJoysticks()
joypad[PAD_MAIN][i] = joypad[PAD_DEFAULT][i]; joypad[PAD_MAIN][i] = joypad[PAD_DEFAULT][i];
} }
#if defined (__native_client__)
sdlNumDevices = 1;
#else
sdlNumDevices = SDL_NumJoysticks(); sdlNumDevices = SDL_NumJoysticks();
#endif
if(sdlNumDevices) if(sdlNumDevices)
sdlDevices = (SDL_Joystick **)calloc(1,sdlNumDevices * sdlDevices = (SDL_Joystick **)calloc(1,sdlNumDevices *
@ -418,8 +421,11 @@ void inputInitJoysticks()
if(sdlDevices[dev] == NULL) { if(sdlDevices[dev] == NULL) {
sdlDevices[dev] = SDL_JoystickOpen(dev); sdlDevices[dev] = SDL_JoystickOpen(dev);
} }
#if defined (__native_client__)
ok = true; // Force gamepad to be useable.
#else
ok = sdlCheckJoyKey(joypad[j][i]); ok = sdlCheckJoyKey(joypad[j][i]);
#endif
} else } else
ok = false; ok = false;
} }