Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
matthewbauer
GitHub Repository: matthewbauer/gametime-player
Path: blob/master/index.coffee
1044 views
1
sparkmd5 = require 'sparkmd5'
2
JSZip = require 'jszip'
3
localForage = require 'localforage'
4
require 'x-retro'
5
6
settings = require './settings.json!'
7
utils = require './utils'
8
9
draghint = document.getElementById 'draghint'
10
loading = document.getElementById 'loading'
11
12
if location.search? and location.search.substr(1)
13
window.url = location.search.substr(1)
14
if window.url.startsWith 'http'
15
window.url = settings.urlPrefix + window.url
16
[..., window.filename] = location.search.substr(1).split('/')
17
18
if window.url and window.filename
19
xhr = new XMLHttpRequest()
20
xhr.open 'GET', window.url, true
21
xhr.responseType = 'arraybuffer'
22
xhr.onload = (e) ->
23
loadData window.filename, new Uint8Array this.response if this.status == 200
24
xhr.send()
25
else
26
loading.classList.add 'hidden'
27
draghint.classList.remove 'hidden'
28
29
navigator.serviceWorker.register 'worker.js' if navigator.serviceWorker
30
31
retro = null
32
33
onkey = (event) ->
34
if retro.player and settings.keys.hasOwnProperty event.which
35
pressed = event.type == 'keydown'
36
retro.player.inputs[0].buttons[settings.keys[event.which]] ?= {}
37
retro.player.inputs[0].buttons[settings.keys[event.which]].pressed = pressed
38
event.preventDefault()
39
40
autosaver = 0
41
42
createOverlay = (buttons, prefix) ->
43
buttons.forEach (button) ->
44
el = null
45
if button.src
46
el = document.createElement 'img'
47
el.setAttribute('src', prefix + button.src)
48
else
49
el = document.createElement 'div'
50
el.style['z-index'] = 1
51
el.style.position = 'absolute'
52
el.style.transform = 'translate(-50%, -50%)'
53
el.style.left = 100 * button.x + '%'
54
el.style.top = 100 * button.y + '%'
55
el.style.width = 100 * button.width + '%'
56
el.style.height = 100 * button.height + '%'
57
if button.circle
58
el.style['border-radius'] = '100%'
59
if button.id?
60
el.style['z-index'] = 2
61
press = (event) ->
62
if retro.player
63
retro.player.inputs[0].buttons[button.id] ?= {}
64
retro.player.inputs[0].buttons[button.id].pressed = (event.type == 'mousedown' || event.type == 'touchstart')
65
event.preventDefault()
66
el.addEventListener 'mousedown', press
67
el.addEventListener 'mousemove', press
68
el.addEventListener 'mouseup', press
69
el.addEventListener 'touchstart', press
70
el.addEventListener 'touchmove', press
71
el.addEventListener 'touchend', press
72
document.getElementById('overlay').appendChild(el)
73
74
error = (e) ->
75
loading.classList.add 'hidden'
76
alert 'Sorry, an error occured. If this happened while loading, you may have a corrupted file'
77
draghint.classList.remove 'hidden'
78
# document.getElementById('error').classList.remove 'hidden'
79
console.error e
80
81
play_error = (e) ->
82
loading.classList.add 'hidden'
83
document.getElementById('error').classList.remove 'hidden'
84
if retro?
85
document.getElementById('clear-save').classList.remove 'hidden'
86
console.error e
87
88
writeSave = (retro) ->
89
try
90
return localForage.setItem retro.md5, new Uint8Array retro.core.serialize()
91
catch err
92
console.error err
93
94
loadSave = (retro) ->
95
try
96
return localForage.getItem retro.md5
97
catch err
98
console.error err
99
100
window.removeSave = () ->
101
try
102
return localForage.removeItem window.retro.md5
103
catch err
104
console.error err
105
106
play = (rom, extension) ->
107
Promise.resolve()
108
.then ->
109
throw new Error 'no rom!' if not rom
110
window.retro = retro = document.createElement 'canvas', 'x-retro'
111
document.body.appendChild retro
112
retro.md5 = sparkmd5.ArrayBuffer.hash rom
113
retro.name = settings.extensions[extension]
114
Promise.all([
115
System.import settings.extensions[extension]
116
loadSave retro
117
System.import settings.overlays[retro.name] + 'index.json!' if settings.overlays[retro.name] and 'ontouchstart' in window
118
]).then ([core, save, _overlay]) ->
119
createOverlay _overlay, settings.overlays[retro.name] if _overlay?
120
retro.core = core
121
retro.game = rom
122
core.unserialize new Uint8Array save if save?
123
core.set_input_poll ->
124
gamepads = navigator.getGamepads() if navigator.getGamepads
125
retro.player.inputs = gamepads if gamepads and gamepads[0]
126
retro.player.inputs = [
127
buttons: {}
128
]
129
loading.classList.add 'hidden'
130
overlay.classList.remove 'hidden'
131
autosaver = setInterval ->
132
writeSave retro
133
, 1000
134
window.addEventListener 'keydown', onkey
135
window.addEventListener 'keyup', onkey
136
retro.start()
137
138
loadData = (filename, buffer) ->
139
draghint.classList.add 'hidden'
140
extension = utils.getExtension filename
141
rom = null
142
if extension is 'zip'
143
zip = new JSZip buffer
144
for file in zip.file /.*/ # any way to predict name of file?
145
extension = utils.getExtension file.name
146
if settings.extensions[extension]
147
rom = new Uint8Array file.asArrayBuffer()
148
break
149
else if settings.extensions[extension]
150
rom = buffer
151
play rom, extension
152
.catch play_error
153
154
load = (file) ->
155
return if not file instanceof Blob
156
draghint.classList.add 'hidden'
157
reader = new FileReader()
158
reader.addEventListener 'load', (event) ->
159
loadData file.name, new Uint8Array reader.result
160
reader.readAsArrayBuffer file
161
162
window.addEventListener 'drop', (event) ->
163
return if draghint.classList.contains 'hidden'
164
loading.classList.remove 'hidden'
165
event.preventDefault()
166
draghint.classList.remove 'hover'
167
if event.dataTransfer.files.length > 0
168
load event.dataTransfer.files[0]
169
false
170
171
window.addEventListener 'dragover', (event) ->
172
event.preventDefault()
173
draghint.classList.add 'hover'
174
false
175
176
window.addEventListener 'dragleave', (event) ->
177
event.preventDefault()
178
draghint.classList.remove 'hover'
179
false
180
181
window.addEventListener 'focus', ->
182
draghint.classList.remove 'hover'
183
184
menu = document.getElementById 'menu'
185
window.addEventListener 'contextmenu', (event) ->
186
if retro?
187
if retro.classList.contains 'hidden'
188
retro.start()
189
else
190
retro.stop()
191
retro.classList.toggle 'hidden'
192
overlay.classList.toggle 'hidden'
193
menu.classList.toggle 'hidden'
194
event.preventDefault()
195
196
window.resume = ->
197
retro.classList.remove 'hidden'
198
overlay.classList.toggle 'hidden'
199
menu.classList.add 'hidden'
200
retro.start()
201
202
window.reset = ->
203
retro.stop()
204
retro.core.reset()
205
window.resume()
206
207
window.mute = ->
208
if retro.player.destination.gain.value == 0
209
retro.player.destination.gain.value = 1
210
document.getElementById('mute').textContent = 'mute'
211
else
212
retro.player.destination.gain.value = 0
213
document.getElementById('mute').textContent = 'unmute'
214
window.resume()
215
216
window.save = ->
217
a = document.createElement 'a'
218
document.body.appendChild a
219
a.classList.add 'hidden'
220
blob = new Blob [new Uint8Array retro.core.serialize()],
221
type: 'application/octet-binary'
222
url = URL.createObjectURL blob
223
a.href = url
224
a.download = retro.md5 + '.' + retro.name + '.sav'
225
a.click()
226
URL.revokeObjectURL url
227
228
savechooser = document.getElementById 'savechooser'
229
savechooser.addEventListener 'change', ->
230
file = this.files[0]
231
return if not file instanceof Blob
232
draghint.classList.add 'hidden'
233
reader = new FileReader()
234
reader.addEventListener 'load', (event) ->
235
retro.core.unserialize new Uint8Array reader.result
236
window.resume()
237
reader.readAsArrayBuffer file
238
239
window.load = ->
240
savechooser.click()
241
242
chooser = document.getElementById 'chooser'
243
chooser.addEventListener 'change', ->
244
draghint.classList.remove 'hover'
245
loading.classList.remove 'hidden'
246
load this.files[0]
247
248
window.addEventListener 'click', (event) ->
249
if not draghint.classList.contains 'hidden'
250
draghint.classList.add 'hover'
251
chooser.click()
252
253
window.addEventListener 'touchstart', (e) ->
254
e.preventDefault()
255
256
window.addEventListener 'error', error
257
258