Update DS emu

This commit is contained in:
2023-01-19 13:07:46 +01:00
parent c8326e953b
commit ba33a4e087
3 changed files with 442 additions and 587 deletions

View File

@ -1,263 +1,139 @@
<!doctype html> <!doctype html>
<html> <html>
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black"> <meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="viewport" <meta name="viewport"
content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height"> content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height">
<link rel="manifest" href="manifest.json"> <link rel="manifest" href="manifest.json">
<link rel="apple-touch-icon" href="https://ds.44670.org/icon.png"> <link rel="apple-touch-icon" href="https://ds.44670.org/icon.png">
<link href="dark.css" rel="stylesheet"> <link href="dark.css" rel="stylesheet">
<title>DS Player</title> <title>DeSmuME-WASM Fork</title>
<!-- Add hint for search engine --> <!-- Add hint for search engine -->
<meta name="description" <meta name="description"
content="DS Player is a web emulator for playing NDS and GBA games, designed for iOS(iPhone and iPad) and also workable for other devices."> content="This is a personal fork of DeSmuME-WASM, a web emulator for playing NDS games, designed for iOS (iPhone and iPad) and also workable for other devices.">
<meta name="keywords" content="NDS, GBA, iOS, iPhone, iPad, emulator, DS, Safari, online, PWA"> <meta name="keywords" content="NDS, GBA, iOS, iPhone, iPad, emulator, DS, Safari, online, PWA">
<meta name="author" content="44670"> <meta name="author" content="44670">
</head> </head>
<body> <body>
<style> <style>
html, html,
body { body {
overflow-x: hidden; overflow-x: hidden;
-webkit-user-select: none; -webkit-user-select: none;
user-select: none; user-select: none;
-webkit-touch-callout: none; -webkit-touch-callout: none;
cursor: inherit; cursor: inherit;
} }
body { body {
background-color: black; background-color: black;
color: white; color: white;
padding: 0; padding: 0;
margin: 0; margin: 0;
width: 100vw; width: 100vw;
height: 100vh; height: 100vh;
font-family: 'Myriad Set Pro', 'Helvetica Neue', Helvetica, Arial, sans-serif; font-family: 'Myriad Set Pro', 'Helvetica Neue', Helvetica, Arial, sans-serif;
} }
canvas { canvas {
position: absolute; position: absolute;
z-index: 1; z-index: 1;
} }
#msg-layer { #msg-layer {
position: absolute; position: absolute;
left: 0; left: 0;
width: 100%; width: 100%;
top: 40vh; top: 40vh;
background: rgba(0, 0, 0, 0.5); background: rgba(0, 0, 0, 0.5);
z-index: 3; z-index: 3;
backdrop-filter: blur(3px); backdrop-filter: blur(3px);
-webkit-backdrop-filter: blur(3px); -webkit-backdrop-filter: blur(3px);
} }
#vk-layer { #vk-layer {
position: absolute; position: absolute;
left: 0; left: 0;
top: 0; top: 0;
width: 100%; width: 100%;
height: 100%; height: 100%;
z-index: 2; z-index: 2;
touch-action: none; touch-action: none;
opacity: 0.3; opacity: 0.3;
} }
#menu { #menu {
position: absolute; position: absolute;
left: 0; left: 0;
top: 0; top: 0;
width: 100%; width: 100%;
height: 100%; height: 100%;
z-index: 4; z-index: 4;
overflow: hidden scroll; overflow: hidden scroll;
background: rgba(0, 0, 0, 0.4); background: rgba(0, 0, 0, 0.4);
backdrop-filter: blur(3px); backdrop-filter: blur(3px);
-webkit-backdrop-filter: blur(3px); -webkit-backdrop-filter: blur(3px);
} }
#menu button { #menu button {
background: transparent; background: transparent;
} }
#menu button:active { #menu button:active {
background: rgba(255, 255, 255, 0.5); background: rgba(255, 255, 255, 0.5);
} }
a, a,
a:visited { a:visited {
color: white; color: white;
} }
.vk-round { .vk-round {
text-align: center; text-align: center;
vertical-align: middle; vertical-align: middle;
border-radius: 50%; border-radius: 50%;
display: inline-block; display: inline-block;
} }
.vk-round-rect { .vk-round-rect {
border-radius: 0.5em; border-radius: 0.5em;
display: inline-block; display: inline-block;
} }
.vk { .vk {
color: #000; color: #000;
background-color: #fff; background-color: #fff;
position: absolute; position: absolute;
z-index: 1; z-index: 1;
text-align: center; text-align: center;
vertical-align: middle; vertical-align: middle;
display: inline-block; display: inline-block;
} }
.vk-touched {} .vk-touched {}
.link { .link {
text-decoration: underline; text-decoration: underline;
} }
hr { hr {
border: 1px solid #fff; border: 1px solid #fff;
height: 0; height: 0;
} }
</style> </style>
<div id="welcome" class="menu"> <div id="welcome" class="menu">
<h1>DS Player</h1> <h1>Redirecting</h1>
<div id="loading">Loading...</div> <div id="loading">Click <a href="./">here</a> if not redirected automatically</div>
<div id="loadrom" hidden> </div>
<input id="rom" type="file" hidden> <script src="localforage.js"></script>
<button style="width:calc(100% - 2em);margin: 1em;" id="btn-choose-file">Choose File (or <script src="pako.min.js"></script>
drag/drop)...</button><br> <script src="app.js"></script>
</div> <script>
<p style="color: #888;"> window.location = "https://octospacc.gitlab.io/Web-Archives-Misc/Repo/DeSmuME" + window.location.hash;
Your files are processed locally and won't be uploaded to any server.<br> </script>
This software should not be used to play games you have not legally obtained.<br> </body>
"Nintendo DS" is a trademark of Nintendo Co., Ltd. This site is not associated with Nintendo in any way. </html>
</p>
<hr>
<p><span id="ver-info"></span><a onclick="whatsNew()" id="whats-new" href="index.html#">What's New</a>&nbsp;|&nbsp;<a
href="index.html#" onclick="uiSwitchTo('menu')">⚙ Settings...</a></p>
<p id="p-sns">
<a href="https://ds.44670.org/gba/">GBA Player</a>
</p>
<p>
<a href="https://github.com/44670/desmume-wasm">Help</a>&nbsp;|&nbsp;<a href="index.html#" id="a-gamepad">No
Gamepad</a>
</p>
<p>
Powered by <a
href="https://github.com/44670/desmume-wasm">desmume-wasm.</a>&nbsp;|&nbsp;https://ds.44670.org<br>
</p>
<div id="pro" style="background-color: #333">
</div>
<p id="ios-power-hint" hidden>
Please <b>turn off "Low Power Mode"</b> in iOS Control Center for better performance.
</p>
<div id="mac-warning" hidden>
WARNING:<br>
It looks like you are using macOS.<br>
Due to macOS Safari <a href="https://webkit.org/tracking-prevention/">limitations</a>, ALL of you save data
will be LOST after 7 days of inactivity.<br>
For this reason, it is highly recommended to use a different browser. (For example: <a
href="https://www.google.com/chrome/">Chrome</a>)
</div>
</div>
<div id="ios-hint" hidden>
<h1>DS Player</h1>
Due to iOS limitations, please open this site(https://ds.44670.org) in <b>Safari</b>, and add it to your
<b>Home Screen</b> by <b>Share Menu</b> to continue.
<p style="text-align: center;">⬇⬇⬇</p>
</div>
<div id="vk-layer" hidden>
<div class="vk-rect vk" data-k="menu" id="vk-menu">M</div>
<div class="vk-rect vk" data-k="l">L</div>
<div class="vk-rect vk" data-k="r">R</div>
<div class="vk-round vk" data-k="a">A</div>
<div class="vk-round vk" data-k="b">B</div>
<div class="vk-round vk" data-k="x">X</div>
<div class="vk-round vk" data-k="y">Y</div>
<div class="vk-rect vk" data-k="select">SE</div>
<div class="vk-rect vk" data-k="start">ST</div>
<div class="vk-round vk" data-k="stick" id="vk-stick"></div>
<div id="vk-dpad-1" class="vk vk-round-rect"></div>
<div id="vk-dpad-2" class="vk vk-round-rect"></div>
</div>
<div style="z-index: 2;position: absolute;bottom: 20px;" id="fps"></div>
<div id="msg-layer" hidden>
<p id="msg-text"></p>
</div>
<div id="menu" hidden>
<button onclick="uiMenuBack()"> Back</button>
<hr>
<div id="cfg-ea" hidden>
<b>Early Access features</b><br>
Please note that these features are experimental and may not be stable.<br>
<input type="checkbox" id="cfg-turbo">
<label for="cfg-turbo">Turbo mode</label><br>
<br>
<label for="cfg-ls-layout">Landscape Screen Layout</label>
<select id="cfg-ls-layout" value="">
<option value="0">TB</option>
<option value="1">LR 1:1</option>
<option value="2">LR X:1</option>
</select><br>
<label for="cfg-scale-mode">Screen filter(restart required)</label>
<select id="cfg-scale-mode" value="">
<option value="0">Pixelated</option>
<option value="1">Smooth</option>
<option value="2">XBRZ</option>
</select><br>
</div>
<hr>
<input type="checkbox" id="power-save">
<label for="power-save">30FPS limit</label><br>
<input type="checkbox" id="vk-enabled">
<label for="vk-enabled">Virtual gamepad</label><br>
<input type="checkbox" id="cfg-mute-sound">
<label for="cfg-mute-sound">Mute sound</label><br>
<label for="vk-direction">Virtual gamepad Style</label>
<select id="vk-direction" value="">
<option value="0">Circle-Pad</option>
<option value="1">D-Pad</option>
</select><br>
<label for="cfg-lang">Firmware Language</label>
<select id="cfg-lang">
<option value="0">Japanese</option>
<option value="1">English</option>
<option value="2">French</option>
<option value="3">German</option>
<option value="4">Italian</option>
<option value="5">Spanish</option>
</select><br>
<input type="checkbox" id="cfg-swap-abxy">
<label for="cfg-swap-abxy">Gamepad: Swap A/B and X/Y</label><br>
<input type="checkbox" id="lid-closed" onchange="window.lidClosed = this.checked">
<label for="lid-closed">Close the lid</label><br>
<hr>
<div id="menu-savegame" hidden>
<input type="file" id="restore-file" onchange="uiSaveRestore()" hidden>
Save Data: <button onclick="uiSaveExport()">Backup</button>|<button
onclick="$id('restore-file').click()">Restore</button><br>
</div>
<hr>
Cloud Save:<span id="span-cloud-id"></span><br>
<button onclick="dpOnConnectButtonClicked()" id="btn-dp-connect">Connect Dropbox</button><br>
<button onclick="dpManualBtn(true)">↑ Upload</button>|<button
onclick="dpManualBtn(false)">↓ Download</button><br>
</div>
<div id="player" hidden>
<canvas id="top" width="256" height="192"></canvas>
<canvas id="bottom" width="256" height="192"></canvas>
</div>
<script src="localforage.js"></script>
<script src="pako.min.js"></script>
<script src="app.js"></script>
<script src="build/nds.js"></script>
</body>
</html>

View File

@ -1,5 +1,20 @@
var VER = 'v20230106' var VER = 'v2023-01-19'
const whatsNew = _ => {
alert(`\
{{ DeSmuME-WASM - OctoSpacc Fork }}
< Changelog >
[ 2023-01-19 ]
- Add this changelog and copyright notice
- Fix URL loading happening prematurely
- Change notices and visible options in the HTML
[ 2023-01-18 ]
- Add support for loading ROMs from URL
`)};
var EngineIsReady = false;
var isIOS = !!navigator.platform && /iPad|iPhone|iPod/.test(navigator.platform); var isIOS = !!navigator.platform && /iPad|iPhone|iPod/.test(navigator.platform);
var isMacOS = !!navigator.platform && /Mac/.test(navigator.platform); var isMacOS = !!navigator.platform && /Mac/.test(navigator.platform);
if (isMacOS) { if (isMacOS) {
@ -197,7 +212,6 @@ async function uiSaveRestore() {
}) })
} }
var gameID = '' var gameID = ''
var emuKeyState = new Array(14) var emuKeyState = new Array(14)
const emuKeyNames = ["right", "left", "down", "up", "select", "start", "b", "a", "y", "x", "l", "r", "debug", "lid"] const emuKeyNames = ["right", "left", "down", "up", "select", "start", "b", "a", "y", "x", "l", "r", "debug", "lid"]
var vkMap = {} var vkMap = {}
@ -209,20 +223,16 @@ for (var i = 0; i < emuKeyNames.length; i++) {
keyNameToKeyId[emuKeyNames[i]] = i keyNameToKeyId[emuKeyNames[i]] = i
} }
var isLandscape = false var isLandscape = false
const emuKeyboardMapping = [39, 37, 40, 38, 16, 13, 90, 88, 65, 83, 81, 87, -1, 8] const emuKeyboardMapping = [39, 37, 40, 38, 16, 13, 90, 88, 65, 83, 81, 87, -1, 8]
var emuIsRunning = false var emuIsRunning = false
var emuIsGameLoaded = false var emuIsGameLoaded = false
var fps = 0 var fps = 0
var divFPS = $id('fps') var divFPS = $id('fps')
var fileInput = $id('rom') var fileInput = $id('rom')
var romSize = 0 var romSize = 0
var FB = [0, 0] var FB = [0, 0]
var screenCanvas = [document.getElementById('top'), document.getElementById('bottom')] var screenCanvas = [document.getElementById('top'), document.getElementById('bottom')]
var ctx2d; var ctx2d;
var audioContext var audioContext
var audioBuffer var audioBuffer
var scriptNode var scriptNode
@ -231,8 +241,6 @@ var audioFifoL = new Int16Array(audioFifoCap)
var audioFifoR = new Int16Array(audioFifoCap) var audioFifoR = new Int16Array(audioFifoCap)
var audioFifoHead = 0 var audioFifoHead = 0
var audioFifoLen = 0 var audioFifoLen = 0
var frameCount = 0 var frameCount = 0
var prevCalcFPSTime = 0 var prevCalcFPSTime = 0
var touched = 0 var touched = 0
@ -242,7 +250,6 @@ var prevSaveFlag = 0
var lastTwoFrameTime = 10 var lastTwoFrameTime = 10
var fbSize var fbSize
function callPlugin(type, arg) { function callPlugin(type, arg) {
for (var k in plugins) { for (var k in plugins) {
if (plugins[k].handler) { if (plugins[k].handler) {
@ -299,10 +306,8 @@ function emuRunFrame() {
Module._runFrame(0, keyMask, touched, touchX, touchY) Module._runFrame(0, keyMask, touched, touchX, touchY)
emuRunAudio() emuRunAudio()
} }
Module._runFrame(1, keyMask, touched, touchX, touchY) Module._runFrame(1, keyMask, touched, touchX, touchY)
emuRunAudio() emuRunAudio()
if (optScaleMode < 2) { if (optScaleMode < 2) {
ctx2d[0].putImageData(FB[0], 0, 0) ctx2d[0].putImageData(FB[0], 0, 0)
ctx2d[1].putImageData(FB[1], 0, 0) ctx2d[1].putImageData(FB[1], 0, 0)
@ -310,8 +315,6 @@ function emuRunFrame() {
gpuDraw(screenCanvas[0], FB[0]) gpuDraw(screenCanvas[0], FB[0])
gpuDraw(screenCanvas[1], FB[1]) gpuDraw(screenCanvas[1], FB[1])
} }
frameCount += 1 frameCount += 1
if (frameCount % 120 == 0) { if (frameCount % 120 == 0) {
var time = performance.now() var time = performance.now()
@ -324,7 +327,6 @@ function emuRunFrame() {
} }
} }
function wasmReady() { function wasmReady() {
Module._setSampleRate(47856) Module._setSampleRate(47856)
setTimeout(() => { setTimeout(() => {
@ -347,6 +349,7 @@ function wasmReady() {
} else { } else {
gpuInit() gpuInit()
} }
EngineIsReady = true;
} }
function emuCopySavBuffer() { function emuCopySavBuffer() {
@ -481,7 +484,6 @@ function makeVKStyle(top, left, w, h, fontSize) {
return 'top:' + top + 'px;left:' + left + 'px;width:' + w + 'px;height:' + h + 'px;' + 'font-size:' + fontSize + 'px;line-height:' + h + 'px;' return 'top:' + top + 'px;left:' + left + 'px;width:' + w + 'px;height:' + h + 'px;' + 'font-size:' + fontSize + 'px;line-height:' + h + 'px;'
} }
function uiAdjustVKLayout() { function uiAdjustVKLayout() {
var baseSize = Math.min(window.innerWidth, window.innerHeight) * 0.14 * config.vkScale var baseSize = Math.min(window.innerWidth, window.innerHeight) * 0.14 * config.vkScale
var fontSize = baseSize * 0.7 var fontSize = baseSize * 0.7
@ -491,7 +493,6 @@ function uiAdjustVKLayout() {
var abxyHeight = baseSize * 3 var abxyHeight = baseSize * 3
var vkw = baseSize var vkw = baseSize
var vkh = baseSize var vkh = baseSize
vkw = baseSize * 1.5 vkw = baseSize * 1.5
vkh = baseSize * 0.6 vkh = baseSize * 0.6
fontSize = baseSize * 0.5 fontSize = baseSize * 0.5
@ -500,8 +501,6 @@ function uiAdjustVKLayout() {
vkw = baseSize * 0.4 vkw = baseSize * 0.4
vkh = baseSize * 0.4 vkh = baseSize * 0.4
$id('vk-menu').style = makeVKStyle(offTop, window.innerWidth / 2 - vkw / 2, vkw, vkh, fontSize) $id('vk-menu').style = makeVKStyle(offTop, window.innerWidth / 2 - vkw / 2, vkw, vkh, fontSize)
offTop += baseSize * 0.62 offTop += baseSize * 0.62
vkw = baseSize vkw = baseSize
vkh = baseSize vkh = baseSize
@ -510,13 +509,11 @@ function uiAdjustVKLayout() {
vkMap['b'].style = makeVKStyle(offTop + abxyHeight - vkh, offLeft + abxyWidth / 2 - vkw / 2, vkw, vkh, fontSize) vkMap['b'].style = makeVKStyle(offTop + abxyHeight - vkh, offLeft + abxyWidth / 2 - vkw / 2, vkw, vkh, fontSize)
vkMap['x'].style = makeVKStyle(offTop, offLeft + abxyWidth / 2 - vkw / 2, vkw, vkh, fontSize) vkMap['x'].style = makeVKStyle(offTop, offLeft + abxyWidth / 2 - vkw / 2, vkw, vkh, fontSize)
vkMap['y'].style = makeVKStyle(offTop + abxyHeight / 2 - vkh / 2, offLeft, vkw, vkh, fontSize) vkMap['y'].style = makeVKStyle(offTop + abxyHeight / 2 - vkh / 2, offLeft, vkw, vkh, fontSize)
vkw = baseSize * 1.0 vkw = baseSize * 1.0
vkh = baseSize * 1.0 vkh = baseSize * 1.0
offLeft = 0 offLeft = 0
$id('vk-stick').style = config.useDPad ? 'display:none;' : makeVKStyle(offTop + abxyHeight / 2 - vkh / 2, offLeft + abxyHeight / 2 - vkw / 2, vkw, vkh, fontSize) $id('vk-stick').style = config.useDPad ? 'display:none;' : makeVKStyle(offTop + abxyHeight / 2 - vkh / 2, offLeft + abxyHeight / 2 - vkw / 2, vkw, vkh, fontSize)
vkStickPos = [offTop + abxyHeight / 2, offLeft + abxyHeight / 2, vkw, vkh, fontSize] vkStickPos = [offTop + abxyHeight / 2, offLeft + abxyHeight / 2, vkw, vkh, fontSize]
var dpadW = abxyWidth var dpadW = abxyWidth
var dpadH = abxyHeight var dpadH = abxyHeight
var dpadX = offLeft var dpadX = offLeft
@ -554,7 +551,6 @@ function setScreenPos(c, left, top, w, h) {
} }
} }
function uiUpdateLayout() { function uiUpdateLayout() {
isLandscape = isScreenLandscape() isLandscape = isScreenLandscape()
if ((!isLandscape) || (config.lsLayout == 0)) { if ((!isLandscape) || (config.lsLayout == 0)) {
@ -597,11 +593,9 @@ function uiUpdateLayout() {
} }
} }
uiAdjustVKLayout() uiAdjustVKLayout()
} }
function uiSwitchTo(mode) { function uiSwitchTo(mode) {
if (mode == uiCurrentMode) { if (mode == uiCurrentMode) {
return return
@ -614,7 +608,6 @@ function uiSwitchTo(mode) {
body.style = '' body.style = ''
html.style = '' html.style = ''
emuIsRunning = false emuIsRunning = false
if (mode == 'player') { if (mode == 'player') {
body.style = 'touch-action: none;' body.style = 'touch-action: none;'
html.style = 'position: fixed;overflow:hidden;touch-action: none;' html.style = 'position: fixed;overflow:hidden;touch-action: none;'
@ -709,7 +702,6 @@ function tryInitSound() {
function emuLoop() { function emuLoop() {
window.requestAnimationFrame(emuLoop) window.requestAnimationFrame(emuLoop)
if (emuIsRunning && (!emuUseTimer33)) { if (emuIsRunning && (!emuUseTimer33)) {
prevRunFrameTime = performance.now() prevRunFrameTime = performance.now()
emuRunFrame() emuRunFrame()
@ -753,32 +745,25 @@ function handleTouch(event) {
} }
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
var isDown = false var isDown = false
var x = 0 var x = 0
var y = 0 var y = 0
var needUpdateStick = false var needUpdateStick = false
var stickY = vkStickPos[0] var stickY = vkStickPos[0]
var stickX = vkStickPos[1] var stickX = vkStickPos[1]
var stickW = vkStickPos[2] var stickW = vkStickPos[2]
var stickH = vkStickPos[3] var stickH = vkStickPos[3]
var stickPressed = false var stickPressed = false
var stickDeadZone = stickW * 0.2 var stickDeadZone = stickW * 0.2
var nextStickTouchID = null var nextStickTouchID = null
var nextTpadTouchID = null var nextTpadTouchID = null
var tsRect = screenCanvas[1].getBoundingClientRect() var tsRect = screenCanvas[1].getBoundingClientRect()
for (var i = 0; i < emuKeyState.length; i++) { for (var i = 0; i < emuKeyState.length; i++) {
emuKeyState[i] = false emuKeyState[i] = false
} }
for (var k in vkState) { for (var k in vkState) {
vkState[k][1] = 0 vkState[k][1] = 0
} }
for (var i = 0; i < event.touches.length; i++) { for (var i = 0; i < event.touches.length; i++) {
var t = event.touches[i]; var t = event.touches[i];
var tid = t.identifier var tid = t.identifier
@ -805,7 +790,6 @@ function handleTouch(event) {
} else { } else {
if ((tid === stickTouchID) || ((dom == vkMap['stick']) && (tid != tpadTouchID))) { if ((tid === stickTouchID) || ((dom == vkMap['stick']) && (tid != tpadTouchID))) {
stickPressed = true stickPressed = true
vkState['stick'][1] = 1 vkState['stick'][1] = 1
var sx = t.clientX var sx = t.clientX
var sy = t.clientY var sy = t.clientY
@ -844,11 +828,9 @@ function handleTouch(event) {
continue continue
} }
} }
touched = isDown ? 1 : 0; touched = isDown ? 1 : 0;
touchX = x touchX = x
touchY = y touchY = y
for (var k in vkState) { for (var k in vkState) {
if (vkState[k][0] != vkState[k][1]) { if (vkState[k][0] != vkState[k][1]) {
var dom = vkMap[k] var dom = vkMap[k]
@ -867,7 +849,6 @@ function handleTouch(event) {
} }
} }
for (var i = 0; i < emuKeyState.length; i++) { for (var i = 0; i < emuKeyState.length; i++) {
var k = emuKeyNames[i] var k = emuKeyNames[i]
if (vkState[k]) { if (vkState[k]) {
@ -876,11 +857,9 @@ function handleTouch(event) {
} }
} }
} }
if (needUpdateStick) { if (needUpdateStick) {
vkMap['stick'].style = makeVKStyle(stickY - stickW / 2, stickX - stickW / 2, stickW, stickH, vkStickPos[4]) vkMap['stick'].style = makeVKStyle(stickY - stickW / 2, stickX - stickW / 2, stickW, stickH, vkStickPos[4])
} }
stickTouchID = nextStickTouchID stickTouchID = nextStickTouchID
tpadTouchID = nextTpadTouchID tpadTouchID = nextTpadTouchID
} }
@ -888,9 +867,6 @@ function handleTouch(event) {
window.addEventListener(val, handleTouch) window.addEventListener(val, handleTouch)
}) })
window.onmousedown = window.onmouseup = window.onmousemove = (e) => { window.onmousedown = window.onmouseup = window.onmousemove = (e) => {
if (!emuIsRunning) { if (!emuIsRunning) {
return return
@ -981,7 +957,6 @@ if (isSaveSupported) {
}); });
} }
function processGamepadInput() { function processGamepadInput() {
var padMap = gamePadKeyMap var padMap = gamePadKeyMap
if (config.swapABXY) { if (config.swapABXY) {
@ -1023,8 +998,6 @@ function processGamepadInput() {
} }
} }
var isMicrophoneEnabled = false var isMicrophoneEnabled = false
var micPtr var micPtr
var micBuf var micBuf
@ -1080,7 +1053,6 @@ function enableMicrophone() {
} }
} }
micScriptNode.connect(audioContext.destination); micScriptNode.connect(audioContext.destination);
}); });
} }
@ -1088,8 +1060,6 @@ function isScreenLandscape() {
return (window.innerWidth / window.innerHeight) > 1.2 return (window.innerWidth / window.innerHeight) > 1.2
} }
if (location.origin == 'https://ds.44670.org') { if (location.origin == 'https://ds.44670.org') {
if (isSaveSupported) { if (isSaveSupported) {
// Register Service Worker // Register Service Worker
@ -1110,7 +1080,6 @@ if (location.origin == 'https://ds.44670.org') {
} }
} }
(function () { (function () {
var cnt = 0; var cnt = 0;
// Prompt to install PWA // Prompt to install PWA
window.onbeforeinstallprompt = function (e) { window.onbeforeinstallprompt = function (e) {
@ -1497,7 +1466,6 @@ function gpuDraw(canvas, idata) {
gl.drawArrays(gl.TRIANGLES, 0, 6); gl.drawArrays(gl.TRIANGLES, 0, 6);
} }
function gpuInit() { function gpuInit() {
if (!gpuInitWithCanvas(screenCanvas[0])) { if (!gpuInitWithCanvas(screenCanvas[0])) {
return return
@ -1505,7 +1473,6 @@ function gpuInit() {
gpuInitWithCanvas(screenCanvas[1]); gpuInitWithCanvas(screenCanvas[1]);
} }
var DP_BASE_PATH = "/dssav" var DP_BASE_PATH = "/dssav"
var DP_EXT = ".4dsaz" var DP_EXT = ".4dsaz"
@ -1519,8 +1486,6 @@ function dpGetCurrentDayInt() {
return retInt; return retInt;
} }
function dpIsConnected() { function dpIsConnected() {
return localStorage['d-token'] ? true : false return localStorage['d-token'] ? true : false
} }
@ -1536,7 +1501,6 @@ async function dpIDHash(gameID) {
return digestHex.substring(0, 8) return digestHex.substring(0, 8)
} }
async function dpGameLoaded() { async function dpGameLoaded() {
if (dpIsConnected()) { if (dpIsConnected()) {
var hash = await dpIDHash(gameID) var hash = await dpIDHash(gameID)
@ -1544,7 +1508,6 @@ async function dpGameLoaded() {
} }
} }
async function dpConnect() { async function dpConnect() {
var redirectUri = encodeURIComponent(location.origin) var redirectUri = encodeURIComponent(location.origin)
var url = "https://www.dropbox.com/oauth2/authorize?client_id=zro5k6xlnsxu4gz&response_type=code&token_access_type=offline" var url = "https://www.dropbox.com/oauth2/authorize?client_id=zro5k6xlnsxu4gz&response_type=code&token_access_type=offline"
@ -1705,9 +1668,6 @@ async function dpTryUploadCloudSave(gameID, tag, u8Arr, mode) {
return false return false
} }
async function dpTryAutoBackup() { async function dpTryAutoBackup() {
if (!dpIsConnected()) { if (!dpIsConnected()) {
return false return false
@ -1724,9 +1684,6 @@ async function dpTryAutoBackup() {
return false return false
} }
function dpOnConnectButtonClicked() { function dpOnConnectButtonClicked() {
if (dpIsConnected()) { if (dpIsConnected()) {
if (confirm("Are you sure to disconnect from Dropbox?")) { if (confirm("Are you sure to disconnect from Dropbox?")) {
@ -1742,7 +1699,6 @@ function dpOnConnectButtonClicked() {
} }
async function dpManualBtn(isUpload) { async function dpManualBtn(isUpload) {
if (!dpIsConnected()) { if (!dpIsConnected()) {
alert("Please connect to Dropbox first.") alert("Please connect to Dropbox first.")
return return
@ -1792,11 +1748,25 @@ const LoadRomFromUrl = _ => {
Req.open('GET', RomUrl, true); Req.open('GET', RomUrl, true);
Req.responseType = 'blob'; Req.responseType = 'blob';
Req.onload = function() { Req.onload = function() {
while (!EngineIsReady) {};
tryLoadROM(Req.response); tryLoadROM(Req.response);
}; };
Req.send(); Req.send();
}; };
}; };
/*
const RomUrlBoxChange = _ => {
if (RomUrlBox.value) {
$id('btn-choose-file').innerHTML = 'Load from URL!';
} else {
$id('btn-choose-file').innerHTML = 'Choose File (or drag/drop)...';
};
};
['onchange', 'oninput', 'onpaste'].forEach(function (i) {
RomUrlBox[i] = RomUrlBoxChange;
});
*/
dpOnLoad(); dpOnLoad();
LoadRomFromUrl(); LoadRomFromUrl();

View File

@ -1,263 +1,272 @@
<!doctype html> <!doctype html>
<html> <html>
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black"> <meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="viewport" <meta name="viewport"
content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height"> content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height">
<link rel="manifest" href="manifest.json"> <link rel="manifest" href="manifest.json">
<link rel="apple-touch-icon" href="https://ds.44670.org/icon.png"> <link rel="apple-touch-icon" href="https://ds.44670.org/icon.png">
<link href="dark.css" rel="stylesheet"> <link href="dark.css" rel="stylesheet">
<title>DS Player</title> <title>DeSmuME-WASM Fork</title>
<!-- Add hint for search engine --> <!-- Add hint for search engine -->
<meta name="description" <meta name="description"
content="DS Player is a web emulator for playing NDS and GBA games, designed for iOS(iPhone and iPad) and also workable for other devices."> content="This is a personal fork of DeSmuME-WASM, a web emulator for playing NDS games, designed for iOS (iPhone and iPad) and also workable for other devices.">
<meta name="keywords" content="NDS, GBA, iOS, iPhone, iPad, emulator, DS, Safari, online, PWA"> <meta name="keywords" content="NDS, GBA, iOS, iPhone, iPad, emulator, DS, Safari, online, PWA">
<meta name="author" content="44670"> <meta name="author" content="44670">
</head> </head>
<body> <body>
<style> <style>
html, html,
body { body {
overflow-x: hidden; overflow-x: hidden;
-webkit-user-select: none; -webkit-user-select: none;
user-select: none; user-select: none;
-webkit-touch-callout: none; -webkit-touch-callout: none;
cursor: inherit; cursor: inherit;
} }
body { body {
background-color: black; background-color: black;
color: white; color: white;
padding: 0; padding: 0;
margin: 0; margin: 0;
width: 100vw; width: 100vw;
height: 100vh; height: 100vh;
font-family: 'Myriad Set Pro', 'Helvetica Neue', Helvetica, Arial, sans-serif; font-family: 'Myriad Set Pro', 'Helvetica Neue', Helvetica, Arial, sans-serif;
} }
canvas { canvas {
position: absolute; position: absolute;
z-index: 1; z-index: 1;
} }
#msg-layer { #msg-layer {
position: absolute; position: absolute;
left: 0; left: 0;
width: 100%; width: 100%;
top: 40vh; top: 40vh;
background: rgba(0, 0, 0, 0.5); background: rgba(0, 0, 0, 0.5);
z-index: 3; z-index: 3;
backdrop-filter: blur(3px); backdrop-filter: blur(3px);
-webkit-backdrop-filter: blur(3px); -webkit-backdrop-filter: blur(3px);
} }
#vk-layer { #vk-layer {
position: absolute; position: absolute;
left: 0; left: 0;
top: 0; top: 0;
width: 100%; width: 100%;
height: 100%; height: 100%;
z-index: 2; z-index: 2;
touch-action: none; touch-action: none;
opacity: 0.3; opacity: 0.3;
} }
#menu { #menu {
position: absolute; position: absolute;
left: 0; left: 0;
top: 0; top: 0;
width: 100%; width: 100%;
height: 100%; height: 100%;
z-index: 4; z-index: 4;
overflow: hidden scroll; overflow: hidden scroll;
background: rgba(0, 0, 0, 0.4); background: rgba(0, 0, 0, 0.4);
backdrop-filter: blur(3px); backdrop-filter: blur(3px);
-webkit-backdrop-filter: blur(3px); -webkit-backdrop-filter: blur(3px);
} }
#menu button { #menu button {
background: transparent; background: transparent;
} }
#menu button:active { #menu button:active {
background: rgba(255, 255, 255, 0.5); background: rgba(255, 255, 255, 0.5);
} }
a, a,
a:visited { a:visited {
color: white; color: white;
} }
.vk-round { .vk-round {
text-align: center; text-align: center;
vertical-align: middle; vertical-align: middle;
border-radius: 50%; border-radius: 50%;
display: inline-block; display: inline-block;
} }
.vk-round-rect { .vk-round-rect {
border-radius: 0.5em; border-radius: 0.5em;
display: inline-block; display: inline-block;
} }
.vk { .vk {
color: #000; color: #000;
background-color: #fff; background-color: #fff;
position: absolute; position: absolute;
z-index: 1; z-index: 1;
text-align: center; text-align: center;
vertical-align: middle; vertical-align: middle;
display: inline-block; display: inline-block;
} }
.vk-touched {} .vk-touched {}
.link { .link {
text-decoration: underline; text-decoration: underline;
} }
hr { hr {
border: 1px solid #fff; border: 1px solid #fff;
height: 0; height: 0;
} }
</style> </style>
<div id="welcome" class="menu"> <div id="welcome" class="menu">
<h1>DS Player</h1> <h1>DeSmuME-WASM Fork</h1>
<div id="loading">Loading...</div> <div id="loading">Loading...</div>
<div id="loadrom" hidden> <div id="loadrom" hidden>
<input id="rom" type="file" hidden> <!--<input id="RomUrlBox" type="text" placeholder="Paste a ROM URL to load from that...">-->
<button style="width:calc(100% - 2em);margin: 1em;" id="btn-choose-file">Choose File (or <input id="rom" type="file" hidden>
drag/drop)...</button><br> <button style="width:calc(100% - 2em);margin: 1em;" id="btn-choose-file">Choose File (or
</div> drag/drop)...</button><br>
<p style="color: #888;"> </div>
Your files are processed locally and won't be uploaded to any server.<br> <p style="color: #888; font-size: smaller;">
This software should not be used to play games you have not legally obtained.<br> Your files are processed locally and won't be uploaded to any server.<br>
"Nintendo DS" is a trademark of Nintendo Co., Ltd. This site is not associated with Nintendo in any way. This software should not be used to play games you have not legally obtained.<br>
</p> This software is released under the GPL-2 license. Visit the original repo to read the terms.
<hr> <br>Changes in this fork from the original version are stated in the changelog.
<p><span id="ver-info"></span><a onclick="whatsNew()" id="whats-new" href="index.html#">What's New</a>&nbsp;|&nbsp;<a <br>Modified parts of the source are "index.html" and "app.js" of this website, you can view their code directly.
href="index.html#" onclick="uiSwitchTo('menu')">⚙ Settings...</a></p> <br>Compiled parts ("nds.js", "nds.wasm") are not modified and you can find their source on the original repo.
<p id="p-sns"> </p>
<a href="https://ds.44670.org/gba/">GBA Player</a> <hr>
</p> <p><span id="ver-info"></span><a onclick="whatsNew()" id="whats-new" href="index.html#">What's New</a>&nbsp;|&nbsp;<a
<p> href="index.html#" onclick="uiSwitchTo('menu')">⚙ Settings...</a></p>
<a href="https://github.com/44670/desmume-wasm">Help</a>&nbsp;|&nbsp;<a href="index.html#" id="a-gamepad">No <p id="p-sns">
Gamepad</a> <a href="https://gba.44670.org">GBA Player</a>
</p> </p>
<p> <p>
Powered by <a <a href="https://github.com/44670/desmume-wasm">Help</a><!--&nbsp;|&nbsp;<a href="index.html#" id="a-gamepad">No
href="https://github.com/44670/desmume-wasm">desmume-wasm.</a>&nbsp;|&nbsp;https://ds.44670.org<br> Gamepad--></a>
</p> </p>
<p>
Powered by <a
<div id="pro" style="background-color: #333"> href="https://github.com/44670/desmume-wasm">desmume-wasm.</a>&nbsp;|&nbsp;OctoSpacc Fork <span style="font-size: smaller;">(Click on "What's New" for more information - DO NOT report issues with this fork to the original developer!)</span><br>
</div> </p>
<p id="ios-power-hint" hidden> <!--
Please <b>turn off "Low Power Mode"</b> in iOS Control Center for better performance. <p>
</p> Powered by <a
<div id="mac-warning" hidden> href="https://github.com/octospacc/DeSmuME-WASM">DeSmuME-WASM - OctoSpacc Fork</a> (GPLv2)--&nbsp;|&nbsp;OctoSpacc fork--<br>
WARNING:<br> </p>
It looks like you are using macOS.<br> -->
Due to macOS Safari <a href="https://webkit.org/tracking-prevention/">limitations</a>, ALL of you save data
will be LOST after 7 days of inactivity.<br> <div id="pro" style="background-color: #333">
For this reason, it is highly recommended to use a different browser. (For example: <a </div>
href="https://www.google.com/chrome/">Chrome</a>) <p id="ios-power-hint" hidden>
</div> Please <b>turn off "Low Power Mode"</b> in iOS Control Center for better performance.
</div> </p>
<div id="ios-hint" hidden> <div id="mac-warning" hidden>
<h1>DS Player</h1> WARNING:<br>
Due to iOS limitations, please open this site(https://ds.44670.org) in <b>Safari</b>, and add it to your It looks like you are using macOS.<br>
<b>Home Screen</b> by <b>Share Menu</b> to continue. Due to macOS Safari <a href="https://webkit.org/tracking-prevention/">limitations</a>, ALL of you save data
<p style="text-align: center;">⬇⬇⬇</p> will be LOST after 7 days of inactivity.<br>
</div> For this reason, it is highly recommended to use a different browser. (For example: <a
<div id="vk-layer" hidden> href="https://www.google.com/chrome/">Chrome</a>)
<div class="vk-rect vk" data-k="menu" id="vk-menu">M</div> </div>
<div class="vk-rect vk" data-k="l">L</div> </div>
<div class="vk-rect vk" data-k="r">R</div> <div id="ios-hint" hidden>
<div class="vk-round vk" data-k="a">A</div> <h1>DS Player</h1>
<div class="vk-round vk" data-k="b">B</div> Due to iOS limitations, please open this site(https://ds.44670.org) in <b>Safari</b>, and add it to your
<div class="vk-round vk" data-k="x">X</div> <b>Home Screen</b> by <b>Share Menu</b> to continue.
<div class="vk-round vk" data-k="y">Y</div> <p style="text-align: center;">⬇⬇⬇</p>
<div class="vk-rect vk" data-k="select">SE</div> </div>
<div class="vk-rect vk" data-k="start">ST</div> <div id="vk-layer" hidden>
<div class="vk-round vk" data-k="stick" id="vk-stick"></div> <div class="vk-rect vk" data-k="menu" id="vk-menu">M</div>
<div id="vk-dpad-1" class="vk vk-round-rect"></div> <div class="vk-rect vk" data-k="l">L</div>
<div id="vk-dpad-2" class="vk vk-round-rect"></div> <div class="vk-rect vk" data-k="r">R</div>
</div> <div class="vk-round vk" data-k="a">A</div>
<div style="z-index: 2;position: absolute;bottom: 20px;" id="fps"></div> <div class="vk-round vk" data-k="b">B</div>
<div id="msg-layer" hidden> <div class="vk-round vk" data-k="x">X</div>
<p id="msg-text"></p> <div class="vk-round vk" data-k="y">Y</div>
</div> <div class="vk-rect vk" data-k="select">SE</div>
<div id="menu" hidden> <div class="vk-rect vk" data-k="start">ST</div>
<button onclick="uiMenuBack()"> Back</button> <div class="vk-round vk" data-k="stick" id="vk-stick"></div>
<hr> <div id="vk-dpad-1" class="vk vk-round-rect"></div>
<div id="cfg-ea" hidden> <div id="vk-dpad-2" class="vk vk-round-rect"></div>
<b>Early Access features</b><br> </div>
Please note that these features are experimental and may not be stable.<br> <div style="z-index: 2;position: absolute;bottom: 20px;" id="fps"></div>
<input type="checkbox" id="cfg-turbo"> <div id="msg-layer" hidden>
<label for="cfg-turbo">Turbo mode</label><br> <p id="msg-text"></p>
<br> </div>
<label for="cfg-ls-layout">Landscape Screen Layout</label> <div id="menu" hidden>
<select id="cfg-ls-layout" value=""> <button onclick="uiMenuBack()"> Back</button>
<option value="0">TB</option> <hr>
<option value="1">LR 1:1</option> <div id="cfg-ea">
<option value="2">LR X:1</option> <b>Early Access features</b><br>
</select><br> Please note that these features are experimental and may not be stable.<br>
<label for="cfg-scale-mode">Screen filter(restart required)</label> <input type="checkbox" id="cfg-turbo">
<select id="cfg-scale-mode" value=""> <label for="cfg-turbo">Turbo mode</label><br>
<option value="0">Pixelated</option> <br>
<option value="1">Smooth</option> <label for="cfg-ls-layout">Landscape Screen Layout</label>
<option value="2">XBRZ</option> <select id="cfg-ls-layout" value="">
</select><br> <option value="0">TB</option>
</div> <option value="1">LR 1:1</option>
<hr> <option value="2">LR X:1</option>
<input type="checkbox" id="power-save"> </select><br>
<label for="power-save">30FPS limit</label><br> <label for="cfg-scale-mode">Screen filter(restart required)</label>
<input type="checkbox" id="vk-enabled"> <select id="cfg-scale-mode" value="">
<label for="vk-enabled">Virtual gamepad</label><br> <option value="0">Pixelated</option>
<input type="checkbox" id="cfg-mute-sound"> <option value="1">Smooth</option>
<label for="cfg-mute-sound">Mute sound</label><br> <option value="2">XBRZ</option>
<label for="vk-direction">Virtual gamepad Style</label> </select><br>
<select id="vk-direction" value=""> </div>
<option value="0">Circle-Pad</option> <hr>
<option value="1">D-Pad</option> <input type="checkbox" id="power-save">
</select><br> <label for="power-save">30FPS limit</label><br>
<label for="cfg-lang">Firmware Language</label> <input type="checkbox" id="vk-enabled">
<select id="cfg-lang"> <label for="vk-enabled">Virtual gamepad</label><br>
<option value="0">Japanese</option> <input type="checkbox" id="cfg-mute-sound">
<option value="1">English</option> <label for="cfg-mute-sound">Mute sound</label><br>
<option value="2">French</option> <label for="vk-direction">Virtual gamepad Style</label>
<option value="3">German</option> <select id="vk-direction" value="">
<option value="4">Italian</option> <option value="0">Circle-Pad</option>
<option value="5">Spanish</option> <option value="1">D-Pad</option>
</select><br> </select><br>
<input type="checkbox" id="cfg-swap-abxy"> <label for="cfg-lang">Firmware Language</label>
<label for="cfg-swap-abxy">Gamepad: Swap A/B and X/Y</label><br> <select id="cfg-lang">
<input type="checkbox" id="lid-closed" onchange="window.lidClosed = this.checked"> <option value="0">Japanese</option>
<label for="lid-closed">Close the lid</label><br> <option value="1">English</option>
<hr> <option value="2">French</option>
<div id="menu-savegame" hidden> <option value="3">German</option>
<input type="file" id="restore-file" onchange="uiSaveRestore()" hidden> <option value="4">Italian</option>
Save Data: <button onclick="uiSaveExport()">Backup</button>|<button <option value="5">Spanish</option>
onclick="$id('restore-file').click()">Restore</button><br> </select><br>
</div> <input type="checkbox" id="cfg-swap-abxy">
<hr> <label for="cfg-swap-abxy">Gamepad: Swap A/B and X/Y</label><br>
Cloud Save:<span id="span-cloud-id"></span><br> <input type="checkbox" id="lid-closed" onchange="window.lidClosed = this.checked">
<button onclick="dpOnConnectButtonClicked()" id="btn-dp-connect">Connect Dropbox</button><br> <label for="lid-closed">Close the lid</label><br>
<button onclick="dpManualBtn(true)">↑ Upload</button>|<button <hr>
onclick="dpManualBtn(false)">↓ Download</button><br> <div id="menu-savegame" hidden>
</div> <input type="file" id="restore-file" onchange="uiSaveRestore()" hidden>
<div id="player" hidden> Save Data: <button onclick="uiSaveExport()">Backup</button>|<button
<canvas id="top" width="256" height="192"></canvas> onclick="$id('restore-file').click()">Restore</button><br>
<canvas id="bottom" width="256" height="192"></canvas> </div>
</div> <hr>
<script src="localforage.js"></script> Cloud Save:<span id="span-cloud-id"></span><br>
<script src="pako.min.js"></script> <button onclick="dpOnConnectButtonClicked()" id="btn-dp-connect">Connect Dropbox</button><br>
<script src="app.js"></script> <button onclick="dpManualBtn(true)">↑ Upload</button>|<button
<script src="build/nds.js"></script> onclick="dpManualBtn(false)">↓ Download</button><br>
</body> </div>
</html> <div id="player" hidden>
<canvas id="top" width="256" height="192"></canvas>
<canvas id="bottom" width="256" height="192"></canvas>
</div>
<script src="localforage.js"></script>
<script src="pako.min.js"></script>
<script src="app.js"></script>
<script src="build/nds.js"></script>
</body>
</html>