Skip to content

Commit

Permalink
added emscripten platform
Browse files Browse the repository at this point in the history
  • Loading branch information
inolen committed Feb 23, 2014
1 parent a8f2107 commit 6b58f05
Show file tree
Hide file tree
Showing 8 changed files with 1,227 additions and 4 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,7 @@ profile
!default.mode1v3
!default.mode2v3
!default.perspectivev3

# emscripten
####################
node_modules/
17 changes: 13 additions & 4 deletions code/qcommon/md5.c
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,10 @@ typedef struct MD5Context {
* Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
* initialization constants.
*/
static void MD5Init(struct MD5Context *ctx)
#if !EMSCRIPTEN
static
#endif
void MD5Init(struct MD5Context *ctx)
{
ctx->buf[0] = 0x67452301;
ctx->buf[1] = 0xefcdab89;
Expand Down Expand Up @@ -163,7 +166,10 @@ static void MD5Transform(uint32_t buf[4],
* Update context to reflect the concatenation of another buffer full
* of bytes.
*/
static void MD5Update(struct MD5Context *ctx, unsigned char const *buf,
#if !EMSCRIPTEN
static
#endif
void MD5Update(struct MD5Context *ctx, unsigned char const *buf,
unsigned len)
{
uint32_t t;
Expand Down Expand Up @@ -213,7 +219,10 @@ static void MD5Update(struct MD5Context *ctx, unsigned char const *buf,
* Final wrapup - pad to 64-byte boundary with the bit pattern
* 1 0* (64-bit count of bits processed, MSB-first)
*/
static void MD5Final(struct MD5Context *ctx, unsigned char *digest)
#if !EMSCRIPTEN
static
#endif
void MD5Final(struct MD5Context *ctx, unsigned char *digest)
{
unsigned count;
unsigned char *p;
Expand Down Expand Up @@ -307,4 +316,4 @@ char *Com_MD5File( const char *fn, int length, const char *prefix, int prefix_le
Q_strcat(final, sizeof(final), va("%02X", digest[i]));
}
return final;
}
}
16 changes: 16 additions & 0 deletions code/qcommon/q_platform.h
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,22 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA

#endif

//================================================================== Q3JS ===

#ifdef EMSCRIPTEN

#define OS_STRING "emscripten"
#define ID_INLINE
#define PATH_SEP '/'

#define ARCH_STRING ""

#define DLL_EXT ".js"

#define Q3_LITTLE_ENDIAN

#endif

//===========================================================================

//catch missing defines in above blocks
Expand Down
11 changes: 11 additions & 0 deletions code/qcommon/q_shared.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,16 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
// q_shared.h -- included first by ALL program modules.
// A user mod should never modify this file

#if EMSCRIPTEN
#define PRODUCT_NAME "ioq3"
#define BASEGAME "baseq3"
#define CLIENT_WINDOW_TITLE "quakejs"
#define CLIENT_WINDOW_MIN_TITLE "quakejs"
#define HOMEPATH_NAME_UNIX ".q3a"
#define HOMEPATH_NAME_WIN "Quake3"
#define HOMEPATH_NAME_MACOSX HOMEPATH_NAME_WIN
#define GAMENAME_FOR_MASTER "Quake3Arena"
#else
#ifdef STANDALONE
#define PRODUCT_NAME "iofoo3"
#define BASEGAME "foobar"
Expand All @@ -47,6 +57,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#define GAMENAME_FOR_MASTER "Quake3Arena"
#define LEGACY_PROTOCOL
#endif
#endif

// Heartbeat for dpmaster protocol. You shouldn't change this unless you know what you're doing
#define HEARTBEAT_FOR_MASTER "DarkPlaces"
Expand Down
383 changes: 383 additions & 0 deletions code/sys/sys_browser.js

Large diffs are not rendered by default.

590 changes: 590 additions & 0 deletions code/sys/sys_common.js

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions code/sys/sys_loadlib.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/

#if EMSCRIPTEN
# include <dlfcn.h>
# define Sys_LoadLibrary(f) dlopen(f,RTLD_NOW)
# define Sys_UnloadLibrary(h) dlclose(h)
# define Sys_LoadFunction(h,fn) dlsym(h,fn)
# define Sys_LibraryError() dlerror()
#else
#ifdef DEDICATED
# ifdef _WIN32
# include <windows.h>
Expand Down Expand Up @@ -47,5 +54,6 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
# define Sys_LoadFunction(h,fn) SDL_LoadFunction(h,fn)
# define Sys_LibraryError() SDL_GetError()
#endif
#endif

void * QDECL Sys_LoadDll(const char *name, qboolean useSystemLib);
202 changes: 202 additions & 0 deletions code/sys/sys_node.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
var LibrarySys = {
$SYS__deps: ['$Browser', '$SYSC'],
$SYS: {
timeBase: null,
DoXHR: function (url, opts) {
if (!url) {
return opts.onload(new Error('Must provide a URL'));
}

var http = require('http');

http.get(url, function (res) {
var buf = [];

res.on('data', function (data) {
buf.push(data);
});

res.on('end', function () {
var err = null;
var data;

if (!(res.statusCode >= 200 && res.statusCode < 300)) {
err = new Error('Couldn\'t load ' + url + '. Status: ' + res.statusCode);
} else {
var buffer = Buffer.concat(buf);

// Manually parse out a request expecting a JSON response.
if (opts.dataType === 'json') {
var str = buffer.toString();
try {
data = JSON.parse(str);
} catch (e) {
err = e;
}
} else {
// Convert from node Buffer -> ArrayBuffer.
data = (new Uint8Array(buffer)).buffer;
}
}

if (opts.onload) {
opts.onload(err, data);
}
});
});
},
LoadingDescription: function (desc) {
if (desc) {
console.log(desc);
}
},
LoadingProgress: function (frac) {
console.log('loaded ' + (frac*100).toFixed(2) + '%');
},
PromptEULA: function (callback) {
var readline = require('readline');
var lines = SYSC.eula.split('\n');

console.log('In order to continue, the official Quake3 demo will need to be installed.');
console.log('Please read through the demo\'s EULA and type \'y\' if you agree to it and would like to continue.\n');

console.log(lines.pop());

var rl = readline.createInterface(process.stdin, process.stdout);
rl.prompt();

rl.on('line', function (line) {
line = line.trim();

if (lines.length) {
console.log(lines.pop());
return;
}

if (!line) {
rl.setPrompt('Agree? (y/n): ');
rl.prompt();
return;
}

rl.close();

if (line !== 'y' && line !== 'yes') {
return callback(new Error('You must agree to the EULA to continue'));
}

return callback();
});
}
},
Sys_PlatformInit: function () {
_CON_SetIsTTY(process.stdin.isTTY);
},
Sys_PlatformExit: function () {
},
Sys_FS_Startup__deps: ['$Browser', '$FS', '$PATH', '$SYSC'],
Sys_FS_Startup: function (context) {
// mount a persistable fs into base if not already mounted
var name = allocate(intArrayFromString('fs_homepath'), 'i8', ALLOC_STACK);
var fs_homepath = Pointer_stringify(_Cvar_VariableString(name));
var localPath = PATH.join('.', fs_homepath);

// make sure the local path exists
var mkdirp = function (p) {
try {
fs.mkdirSync(p);
} catch (e) {
// make the subdirectory and then retry
if (e.code === 'ENOENT') {
mkdirp(PATH.dirname(p));
mkdirp(p);
return;
}

// if we got any other error, let's see if the directory already exists
var stat;
try {
stat = fs.statSync(p);
}
catch (e) {
SYSC.Error('fatal', e.message);
return;
}

if (!stat.isDirectory()) {
SYSC.Error('fatal', e.message);
}
}
};
mkdirp(localPath);

// mount up the local filesystem in emscripten
var dir;
try {
dir = FS.mkdir(fs_homepath, 0777);
} catch (e) {
if (!(e instanceof FS.ErrnoError) || e.errno !== ERRNO_CODES.EEXIST) {
SYSC.Error('fatal', e.message);
}
}

try {
FS.mount(NODEFS, { root: localPath }, fs_homepath);
} catch (e) {
if (!(e instanceof FS.ErrnoError) || e.errno !== ERRNO_CODES.EBUSY) {
SYSC.Error('fatal', e.message);
}
}

SYSC.FS_Startup(Browser.safeCallback(function (err) {
if (err) {
// FIXME cb_free_context(context)
SYSC.Error('fatal', err);
return;
}

SYSC.ProxyCallback(context);
}));
},
Sys_FS_Shutdown__deps: ['$Browser', '$SYSC'],
Sys_FS_Shutdown: function (context) {
var name = allocate(intArrayFromString('fs_homepath'), 'i8', ALLOC_STACK);
var fs_homepath = Pointer_stringify(_Cvar_VariableString(name));

SYSC.FS_Shutdown(Browser.safeCallback(function (err) {
if (err) {
// FIXME cb_free_context(context)
SYSC.Error('fatal', err);
return;
}

SYSC.ProxyCallback(context);
}));
},
Sys_Milliseconds: function () {
var time = process.hrtime();

if (!SYS.timeBase) {
SYS.timeBase = time[0] * 1000 + parseInt(time[1] / 1000000, 10);
}

return (time[0] * 1000 + parseInt(time[1] / 1000000, 10)) - SYS.timeBase;
},
Sys_GetCurrentUser: function () {
var stack = Runtime.stackSave();
var ret = allocate(intArrayFromString('player'), 'i8', ALLOC_STACK);
Runtime.stackRestore(stack);
return ret;
},
Sys_Dialog: function (type, message, title) {
SYSC.Error('SYS_Dialog not implemented');
},
Sys_ErrorDialog: function (error) {
error = Pointer_stringify(error);
console.error(error);
process.exit();
}
};

autoAddDeps(LibrarySys, '$SYS');
mergeInto(LibraryManager.library, LibrarySys);

0 comments on commit 6b58f05

Please sign in to comment.