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:
parent
8b46072ca3
commit
27243323fd
|
@ -34,6 +34,13 @@ if( NOT ENABLE_DEBUGGER AND ENABLE_SDL )
|
|||
message( SEND_ERROR "The SDL port can't be built without debugging support" )
|
||||
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
|
||||
IF( NOT VERSION )
|
||||
SET( VERSION "https://github.com/EmperorArthur/VBA-M" )
|
||||
|
@ -71,7 +78,7 @@ endif( ENABLE_ASM_SCALERS )
|
|||
# Look for some dependencies using CMake scripts
|
||||
FIND_PACKAGE ( ZLIB REQUIRED )
|
||||
FIND_PACKAGE ( PNG REQUIRED )
|
||||
FIND_PACKAGE ( OpenGL REQUIRED )
|
||||
FIND_PACKAGE ( OpenGL )
|
||||
FIND_PACKAGE ( SDL REQUIRED )
|
||||
|
||||
if( ENABLE_LINK )
|
||||
|
@ -87,7 +94,10 @@ SET(VBAMCORE_LIBS
|
|||
${ZLIB_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
|
||||
# so that pkg-config is not required
|
||||
|
@ -131,8 +141,12 @@ IF( NOT SYSCONFDIR )
|
|||
SET( SYSCONFDIR "/etc" )
|
||||
ENDIF( NOT SYSCONFDIR )
|
||||
|
||||
if ( OpenGL_FOUND )
|
||||
set( USE_OPENGL_FLAG "-DUSE_OPENGL " )
|
||||
endif ( OpenGL_FOUND )
|
||||
|
||||
# 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='')
|
||||
|
||||
if( ENABLE_LINK )
|
||||
|
@ -142,13 +156,13 @@ if( ENABLE_LINK )
|
|||
SET(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} ${RT_LIB})
|
||||
SET(VBAMCORE_LIBS ${VBAMCORE_LIBS} ${RT_LIB})
|
||||
ENDIF(RT_LIB)
|
||||
|
||||
|
||||
FIND_LIBRARY(PTHREAD_LIB pthread)
|
||||
IF(PTHREAD_LIB)
|
||||
SET(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} ${PTHREAD_LIB})
|
||||
SET(VBAMCORE_LIBS ${VBAMCORE_LIBS} ${PTHREAD_LIB})
|
||||
ENDIF(PTHREAD_LIB)
|
||||
|
||||
|
||||
INCLUDE(CheckFunctionExists)
|
||||
CHECK_FUNCTION_EXISTS(sem_timedwait SEM_TIMEDWAIT)
|
||||
IF( SEM_TIMEDWAIT)
|
||||
|
@ -304,6 +318,10 @@ SET(SRC_SDL
|
|||
src/sdl/expr-lex.cpp
|
||||
)
|
||||
|
||||
SET(SRC_PNACL
|
||||
src/pnacl/nacl_glue.cpp
|
||||
)
|
||||
|
||||
SET(SRC_FILTERS
|
||||
src/filters/2xSaI.cpp
|
||||
src/filters/admame.cpp
|
||||
|
@ -376,8 +394,8 @@ IF( ENABLE_SDL )
|
|||
vbam
|
||||
WIN32
|
||||
${SRC_SDL}
|
||||
${SRC_PNACL}
|
||||
)
|
||||
|
||||
IF( WIN32 )
|
||||
SET( WIN32_LIBRARIES wsock32 )
|
||||
ENDIF( WIN32 )
|
||||
|
@ -392,11 +410,14 @@ IF( ENABLE_SDL )
|
|||
${WIN32_LIBRARIES}
|
||||
${LIRC_CLIENT_LIBRARY}
|
||||
)
|
||||
|
||||
INSTALL(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/vbam DESTINATION bin)
|
||||
INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/src/sdl/vbam.cfg-example
|
||||
DESTINATION ${SYSCONFDIR}
|
||||
RENAME vbam.cfg)
|
||||
IF ( PNACL )
|
||||
build_to_app( vbam )
|
||||
ELSE ( PNACL )
|
||||
INSTALL(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/vbam DESTINATION bin)
|
||||
INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/src/sdl/vbam.cfg-example
|
||||
DESTINATION ${SYSCONFDIR}
|
||||
RENAME vbam.cfg)
|
||||
ENDIF ( PNACL )
|
||||
ENDIF( ENABLE_SDL )
|
||||
|
||||
IF( ENABLE_GTK )
|
||||
|
|
|
@ -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 )
|
|
@ -7,13 +7,13 @@
|
|||
|
||||
|
||||
#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. */
|
||||
#define fseeko64 fseeko
|
||||
#define ftello64 ftello
|
||||
#else
|
||||
typedef off64_t __off64_t;
|
||||
#endif /* __APPLE__ || BSD */
|
||||
#endif /* __APPLE__ || BSD || __native_client__ */
|
||||
#endif /* __GNUC__ */
|
||||
|
||||
#ifndef _MSC_VER
|
||||
|
|
|
@ -5,7 +5,7 @@ extern int armExecute();
|
|||
extern int thumbExecute();
|
||||
|
||||
#ifdef __GNUC__
|
||||
#ifndef __APPLE__
|
||||
#if !defined(__APPLE__) && !defined(__native_client__)
|
||||
# define INSN_REGPARM __attribute__((regparm(1)))
|
||||
#else
|
||||
# define INSN_REGPARM /*nothing*/
|
||||
|
|
|
@ -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
|
||||
});
|
||||
});
|
|
@ -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(🔍) + 1-9</div>
|
||||
<div>Load state: Search(🔍) + 1-9</div>
|
||||
</div>
|
||||
<div>Vol down/up: -/=</div>
|
||||
</div>
|
||||
<script src="index.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"program": {
|
||||
"portable" : {
|
||||
"pnacl-translate" : {
|
||||
"url" : "vbam.pexe",
|
||||
"optlevel": 2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 21 KiB |
|
@ -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);
|
|
@ -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"
|
||||
]
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
captureDir=/store/states
|
||||
saveDir=/store/states
|
||||
batteryDir=/store/states
|
||||
showSpeed=2
|
||||
rtcEnabled=1
|
|
@ -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
|
|
@ -23,6 +23,7 @@
|
|||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <cmath>
|
||||
#ifdef USE_OPENGL
|
||||
#ifdef __APPLE__
|
||||
#include <OpenGL/glu.h>
|
||||
#include <OpenGL/glext.h>
|
||||
|
@ -30,6 +31,7 @@
|
|||
#include <GL/glu.h>
|
||||
#include <GL/glext.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <time.h>
|
||||
|
||||
|
@ -134,9 +136,13 @@ int sdlPrintUsage = 0;
|
|||
int cartridgeType = 3;
|
||||
int captureFormat = 0;
|
||||
|
||||
int openGL = 1;
|
||||
int textureSize = 256;
|
||||
#ifdef USE_OPENGL
|
||||
int openGL = 1;
|
||||
GLuint screenTexture = 0;
|
||||
#else
|
||||
int openGL = 0;
|
||||
#endif
|
||||
u8 *filterPix = 0;
|
||||
|
||||
int pauseWhenInactive = 0;
|
||||
|
@ -810,6 +816,7 @@ void sdlReadPreferences(FILE *f)
|
|||
|
||||
void sdlOpenGLInit(int w, int h)
|
||||
{
|
||||
#ifdef USE_OPENGL
|
||||
float screenAspect = (float) srcWidth / srcHeight,
|
||||
windowAspect = (float) w / h;
|
||||
|
||||
|
@ -861,6 +868,7 @@ void sdlOpenGLInit(int w, int h)
|
|||
|
||||
glClearColor(0.0,0.0,0.0,1.0);
|
||||
glClear( GL_COLOR_BUFFER_BIT );
|
||||
#endif
|
||||
}
|
||||
|
||||
void sdlReadPreferences()
|
||||
|
@ -1277,6 +1285,9 @@ static void sdlHandleSavestateKey(int num, int shifted)
|
|||
// 0: load
|
||||
// 1: save
|
||||
int backuping = 1; // controls whether we are doing savestate backups
|
||||
#ifdef __native_client__
|
||||
backuping = 0;
|
||||
#endif
|
||||
|
||||
if ( sdlSaveKeysSwitch == 2 )
|
||||
{
|
||||
|
@ -1475,9 +1486,11 @@ void sdlPollEvents()
|
|||
}
|
||||
break;
|
||||
case SDLK_KP_DIVIDE:
|
||||
case SDLK_MINUS:
|
||||
sdlChangeVolume(-0.1);
|
||||
break;
|
||||
case SDLK_KP_MULTIPLY:
|
||||
case SDLK_EQUALS:
|
||||
sdlChangeVolume(0.1);
|
||||
break;
|
||||
case SDLK_KP_MINUS:
|
||||
|
@ -1543,15 +1556,19 @@ void sdlPollEvents()
|
|||
case SDLK_g:
|
||||
if(!(event.key.keysym.mod & MOD_NOCTRL) &&
|
||||
(event.key.keysym.mod & KMOD_CTRL)) {
|
||||
filterFunction = 0;
|
||||
while (!filterFunction)
|
||||
{
|
||||
filter = (Filter)((filter + 1) % kInvalidFilter);
|
||||
filterFunction = initFilter(filter, systemColorDepth, srcWidth);
|
||||
}
|
||||
if (getFilterEnlargeFactor(filter) != filter_enlarge)
|
||||
sdlInitVideo();
|
||||
systemScreenMessage(getFilterName(filter));
|
||||
filterFunction = 0;
|
||||
while (!filterFunction) {
|
||||
filter = (Filter)((filter + 1) % kInvalidFilter);
|
||||
#ifdef __native_client__
|
||||
if (getFilterEnlargeFactor(filter) != filter_enlarge)
|
||||
continue;
|
||||
#endif
|
||||
filterFunction = initFilter(filter, systemColorDepth, srcWidth);
|
||||
}
|
||||
if (getFilterEnlargeFactor(filter) != filter_enlarge) {
|
||||
sdlInitVideo();
|
||||
}
|
||||
systemScreenMessage(getFilterName(filter));
|
||||
}
|
||||
break;
|
||||
case SDLK_F11:
|
||||
|
@ -2486,6 +2503,7 @@ void systemDrawScreen()
|
|||
drawSpeed(screen, destPitch, 10, 20);
|
||||
|
||||
if (openGL) {
|
||||
#ifdef USE_OPENGL
|
||||
glClear( GL_COLOR_BUFFER_BIT );
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, destWidth);
|
||||
if (systemColorDepth == 16)
|
||||
|
@ -2508,6 +2526,7 @@ void systemDrawScreen()
|
|||
glEnd();
|
||||
|
||||
SDL_GL_SwapBuffers();
|
||||
#endif
|
||||
} else {
|
||||
SDL_UnlockSurface(surface);
|
||||
SDL_Flip(surface);
|
||||
|
|
|
@ -85,7 +85,7 @@ static int sensorY = 2047;
|
|||
static uint32_t sdlGetHatCode(const SDL_Event &event)
|
||||
{
|
||||
if (!event.jhat.value) return 0;
|
||||
|
||||
|
||||
return (
|
||||
((event.jhat.which + 1) << 16) |
|
||||
32 |
|
||||
|
@ -249,7 +249,6 @@ static void sdlUpdateJoyButton(int which,
|
|||
int b = joypad[j][i] & 0xffff;
|
||||
if(dev) {
|
||||
dev--;
|
||||
|
||||
if((dev == which) && (b >= 128) && (b == (button+128))) {
|
||||
sdlButtons[j][i] = pressed;
|
||||
}
|
||||
|
@ -399,7 +398,11 @@ void inputInitJoysticks()
|
|||
joypad[PAD_MAIN][i] = joypad[PAD_DEFAULT][i];
|
||||
}
|
||||
|
||||
#if defined (__native_client__)
|
||||
sdlNumDevices = 1;
|
||||
#else
|
||||
sdlNumDevices = SDL_NumJoysticks();
|
||||
#endif
|
||||
|
||||
if(sdlNumDevices)
|
||||
sdlDevices = (SDL_Joystick **)calloc(1,sdlNumDevices *
|
||||
|
@ -418,8 +421,11 @@ void inputInitJoysticks()
|
|||
if(sdlDevices[dev] == NULL) {
|
||||
sdlDevices[dev] = SDL_JoystickOpen(dev);
|
||||
}
|
||||
|
||||
#if defined (__native_client__)
|
||||
ok = true; // Force gamepad to be useable.
|
||||
#else
|
||||
ok = sdlCheckJoyKey(joypad[j][i]);
|
||||
#endif
|
||||
} else
|
||||
ok = false;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue