From b08d9bbec5eeb931e7e3342f9b1640ac5ce6292a Mon Sep 17 00:00:00 2001 From: Daniel Brooks Date: Mon, 3 Apr 2017 22:08:46 -0700 Subject: [PATCH] add support for PCE emulators, particularly macplus PCE supports several other personal computers of the era, but only macplus is tested so far. --- example_macplus.html | 35 ++++++++ loader.js | 184 ++++++++++++++++++++++++++++++++++--------- other_logos/pce.png | Bin 0 -> 5131 bytes 3 files changed, 181 insertions(+), 38 deletions(-) create mode 100644 example_macplus.html create mode 100644 other_logos/pce.png diff --git a/example_macplus.html b/example_macplus.html new file mode 100644 index 00000000..896a410c --- /dev/null +++ b/example_macplus.html @@ -0,0 +1,35 @@ + + + + example computer program + + + + + + + + + diff --git a/loader.js b/loader.js index 069922ab..b7d99d40 100644 --- a/loader.js +++ b/loader.js @@ -51,19 +51,22 @@ var Module = null; } // yea, this is a hack + var images; if (/archive\.org$/.test(document.location.hostname)) { - var images = { ia: img("/images/ialogo.png"), - mame: img("/images/mame.png"), - mess: img("/images/mame.png"), - dosbox: img("/images/dosbox.png"), - sae: img("/images/sae.png") - }; + images = { ia: img("/images/ialogo.png"), + mame: img("/images/mame.png"), + mess: img("/images/mame.png"), + dosbox: img("/images/dosbox.png"), + sae: img("/images/sae.png"), + pce: img("/images/pce.png") + }; } else { images = { ia: img("other_logos/ia-logo-150x150.png"), mame: img("other_logos/mame.png"), mess: img("other_logos/mame.png"), dosbox: img("other_logos/dosbox.png"), - sae: img("other_logos/sae.png") + sae: img("other_logos/sae.png"), + pce: img("other_logos/pce.png") }; } @@ -109,7 +112,7 @@ var Module = null; }) .then(function (data) { if (splash.failed_loading) { - return; + return null; } filelist = data; splash.setTitle("Downloading emulator metadata..."); @@ -130,7 +133,7 @@ var Module = null; }) .then(function (data) { if (splash.failed_loading) { - return; + return null; } modulecfg = JSON.parse(data); @@ -146,6 +149,11 @@ var Module = null; cfgr = SAELoader; get_files = get_sae_files; } + else if (module && module.indexOf("pce-") === 0) { + emulator_logo = images.pce; + cfgr = PCELoader; + get_files = get_pce_files; + } else if (module) { emulator_logo = images.mame; cfgr = MAMELoader; @@ -164,7 +172,7 @@ var Module = null; cfgr.sampleRate(SAMPLE_RATE)]; if (/archive\.org$/.test(document.location.hostname)) { - cfgr.muted(!(typeof $ !== 'undefined' && $.cookie && $.cookie('unmute'))) + cfgr.muted(!(typeof $ !== 'undefined' && $.cookie && $.cookie('unmute'))); } if (module && module.indexOf("dosbox") === 0) { @@ -174,7 +182,9 @@ var Module = null; } else if (module && module.indexOf("sae-") === 0) { config_args.push(cfgr.model(modulecfg.driver), cfgr.rom(modulecfg.bios_filenames)); - } else if (module) { + } else if (module && module.indexOf("pce-") === 0) { + config_args.push(cfgr.model(modulecfg.driver)); + } else if (module) { // MAME config_args.push(cfgr.driver(modulecfg.driver), cfgr.extraArgs(modulecfg.extra_args)); } @@ -232,7 +242,9 @@ var Module = null; var node = urls[i], drive = node.nodeName.split('_')[2], title = 'Game File ('+ (i+1) +' of '+ (game ? len+1 : len) +')', - url = get_zip_url(node.textContent); + filename = node.textContent, + url = (filename.includes("/")) ? get_zip_url(filename) + : get_zip_url(filename, get_item_name(game)); files.push(cfgr.mountZip(drive, cfgr.fetchFile(title, url))); } @@ -285,16 +297,16 @@ var Module = null; var periph = k.match(/^mame_peripheral_([a-zA-Z0-9]+)$/)[1]; peripherals[periph] = meta[k]; game_files_counter[meta[k]] = 1; - }) + }); var game_files = Object.keys(game_files_counter), len = game_files.length; game_files.forEach(function (filename, i) { - var title = "Game File ("+ (i+1) +" of "+ len +")"; + var title = "Game File ("+ (i+1) +" of "+ len +")", + url = (filename.includes("/")) ? get_zip_url(filename) + : get_zip_url(filename, get_item_name(game)); files.push(cfgr.mountFile('/'+ filename, - cfgr.fetchFile(title, - get_zip_url(filename, - get_item_name(game))))); + cfgr.fetchFile(title, url))); }); Object.keys(peripherals).forEach(function (periph) { files.push(cfgr.peripheral(periph, // we're not pushing a 'file' here, @@ -333,21 +345,75 @@ var Module = null; }); game_files.forEach(function (file, i) { if (file) { - var title = "Game File ("+ (i+1) +" of "+ game_files.length +")"; + var title = "Game File ("+ (i+1) +" of "+ game_files.length +")", + url = (file.name.includes("/")) ? get_zip_url(file.name) + : get_zip_url(file.name, get_item_name(game)); files.push(cfgr.mountFile('/'+ file.name, - cfgr.fetchFile(title, - get_zip_url(file.name, - get_item_name(game))))); + cfgr.fetchFile(title, url))); files.push(cfgr.floppy(0, // we're not pushing a file here file.name)); // but that's ok } }); files.push(cfgr.mountFile('/'+ modulecfg['driver'] + '.cfg', - cfgr.fetchOptionalFile("CFG File", + cfgr.fetchOptionalFile("Config File", get_other_emulator_config_url(module)))); return files; } + function get_pce_files(cfgr, metadata, modulecfg, filelist) { + var files = [], + bios_files = modulecfg['bios_filenames']; + bios_files.forEach(function (fname, i) { + if (fname) { + var title = "ROM File ("+ (i+1) +" of "+ bios_files.length +")"; + files.push(cfgr.mountFile('/'+ fname, + cfgr.fetchFile(title, + get_bios_url(fname)))); + } + }); + + var meta = dict_from_xml(metadata), + game_files_counter = {}; + list_from_xml(filelist).filter(function (node) { + return "getAttribute" in node; + }) + .map(function (node) { + var file = dict_from_xml(node); + file.name = node.getAttribute("name"); + return file; + }) + .filter(function (file) { + return file.name.endsWith("." + meta.emulator_ext); + }) + .forEach(function (file, i) { + if (modulecfg.peripherals && modulecfg.peripherals[i]) { + game_files_counter[file.name] = modulecfg.peripherals[i]; + } + }); + Object.keys(meta).filter(function (k) { + return k.startsWith("pce_drive_"); + }) + .forEach(function (k) { + var periph = k.match(/^pce_drive_([a-zA-Z0-9]+)$/)[1]; + game_files_counter[meta[k]] = periph; + }); + + var game_files = Object.keys(game_files_counter), + len = game_files.length; + game_files.forEach(function (filename, i) { + var title = "Game File ("+ (i+1) +" of "+ len +")", + ext = filename.match(/\.([^.]*)$/)[1], + url = (filename.includes("/")) ? get_zip_url(filename) + : get_zip_url(filename, get_item_name(game)); + files.push(cfgr.mountFile('/'+ game_files_counter[filename] +'.'+ ext, + cfgr.fetchFile(title, url))); + }); + + files.push(cfgr.mountFile('/pce-'+ modulecfg['driver'] + '.cfg', + cfgr.fetchOptionalFile("Config File", + get_other_emulator_config_url("pce-"+ modulecfg['driver'])))); + return files; + } var get_item_name = function (game_path) { return game_path.split('/').shift(); }; @@ -503,7 +569,7 @@ var Module = null; }; MAMELoader.peripheral = function (peripheral, game) { - var p = {} + var p = {}; p[peripheral] = [game]; return { peripheral: p }; }; @@ -525,11 +591,11 @@ var Module = null; SAELoader.model = function (model) { return { amigaModel: model }; - } + }; SAELoader.fastMemory = function (megabytes) { return { fast_memory: megabytes << 20 }; - } + }; SAELoader.rom = function (filenames) { if (typeof filenames == "string") @@ -538,14 +604,29 @@ var Module = null; }; SAELoader.floppy = function (index, filename) { - var f = {} + var f = {}; f[index] = filename; return { floppy: f }; }; SAELoader.ntsc = function (v) { return { ntsc: !!v }; + }; + + /** + * PCELoader + */ + + function PCELoader() { + var config = Array.prototype.reduce.call(arguments, extend); + config.emulator_arguments = ["-c", "/emulator/pce-"+ config.pceModel +".cfg"]; + return config; } + PCELoader.__proto__ = BaseLoader; + + PCELoader.model = function (model) { + return { pceModel: model }; + }; var build_mame_arguments = function (muted, driver, native_resolution, sample_rate, peripheral, extra_args) { var args = [driver, @@ -568,7 +649,7 @@ var Module = null; for (var p in peripheral) { if (Object.prototype.propertyIsEnumerable.call(peripheral, p)) { args.push('-' + p, - '/emulator/'+ (peripheral[p][0].replace(/\//g,'_'))) + '/emulator/'+ (peripheral[p][0].replace(/\//g,'_'))); } } } @@ -664,29 +745,29 @@ var Module = null; SAERunner.prototype.start = function () { var err = this._sae.start(); - } + }; SAERunner.prototype.pause = function () { this._sae.pause(); - } + }; SAERunner.prototype.stop = function () { this._sae.stop(); - } + }; SAERunner.prototype.mute = function () { var err = this._sae.mute(true); if (err) { - console.warn("unable to mute; SAE error number", err) + console.warn("unable to mute; SAE error number", err); } - } + }; SAERunner.prototype.unmute = function () { var err = this._sae.mute(false); if (err) { - console.warn("unable to unmute; SAE error number", err) + console.warn("unable to unmute; SAE error number", err); } - } + }; SAERunner.prototype.onStarted = function (func) { this._cfg.hook.event.started = func; @@ -728,10 +809,10 @@ var Module = null; var muted = false; var SDL_PauseAudio; - this.isMuted = function () { return muted; } - this.mute = function () { return this.setMute(true); } - this.unmute = function () { return this.setMute(false); } - this.toggleMute = function () { return this.setMute(!muted); } + this.isMuted = function () { return muted; }; + this.mute = function () { return this.setMute(true); }; + this.unmute = function () { return this.setMute(false); }; + this.toggleMute = function () { return this.setMute(!muted); }; this.setMute = function (state) { muted = state; if (runner) { @@ -1462,13 +1543,40 @@ var Module = null; return Array.prototype.slice.call(xml.childNodes); } + function _SDL_CreateRGBSurfaceFrom(pixels, width, height, depth, pitch, rmask, gmask, bmask, amask) { + // TODO: Actually fill pixel data to created surface. + // TODO: Take into account depth and pitch parameters. + // console.log('TODO: Partially unimplemented SDL_CreateRGBSurfaceFrom called!'); + var surface = SDL.makeSurface(width, height, 0, false, 'CreateRGBSurfaceFrom', rmask, gmask, bmask, amask); + + var surfaceData = SDL.surfaces[surface]; + var surfaceImageData = surfaceData.ctx.getImageData(0, 0, width, height); + var surfacePixelData = surfaceImageData.data; + + // Fill pixel data to created surface. + // Supports SDL_PIXELFORMAT_RGBA8888 and SDL_PIXELFORMAT_RGB888 + var channels = amask ? 4 : 3; // RGBA8888 or RGB888 + for (var pixelOffset = 0; pixelOffset < width*height; pixelOffset++) { + surfacePixelData[pixelOffset*4+0] = HEAPU8[pixels + (pixelOffset*channels+0)]; // R + surfacePixelData[pixelOffset*4+1] = HEAPU8[pixels + (pixelOffset*channels+1)]; // G + surfacePixelData[pixelOffset*4+2] = HEAPU8[pixels + (pixelOffset*channels+2)]; // B + surfacePixelData[pixelOffset*4+3] = amask ? HEAPU8[pixels + (pixelOffset*channels+3)] : 0xff; // A + }; + + surfaceData.ctx.putImageData(surfaceImageData, 0, 0); + + return surface; + } + window.IALoader = IALoader; window.DosBoxLoader = DosBoxLoader; window.JSMESSLoader = MAMELoader; // depreciated; just for backwards compatibility window.JSMAMELoader = MAMELoader; // ditto window.MAMELoader = MAMELoader; window.SAELoader = SAELoader; + window.PCELoader = PCELoader; window.Emulator = Emulator; + window._SDL_CreateRGBSurfaceFrom = _SDL_CreateRGBSurfaceFrom; })(typeof Promise === 'undefined' ? ES6Promise.Promise : Promise); // legacy diff --git a/other_logos/pce.png b/other_logos/pce.png new file mode 100644 index 0000000000000000000000000000000000000000..d51b7e19c1b163c6602ccbf06724995fc73be581 GIT binary patch literal 5131 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 literal 0 HcmV?d00001