5151 </div >
5252 </div >
5353
54- <style >
55- /* Only keep basic container styling */
56- #game-wrapper {
57- padding-top : 0 ;
58- background : #222 ;
59- border-radius : 8px ;
60- overflow : hidden ;
61- margin-bottom : 20px ;
62- max-width : 1024px ;
63- margin-left : auto ;
64- margin-right : auto ;
65- }
66-
67- /* Keep only the aspect ratio for the game container */
68- #game {
69- aspect-ratio : 4 /3 ;
70- max-height : 700px ;
71- }
72-
73- /* Keep alert styling for consistency */
74- .alert-secondary {
75- background-color : #2a2a2a ;
76- border-color : #3a3a3a ;
77- color : #999 ;
78- display : inline-block ;
79- margin : 0 auto ;
80- padding : 0.75rem 1.25rem ;
81- }
82-
83- .alert-secondary a .alert-link {
84- color : #bbb ;
85- text-decoration : underline ;
86- }
87-
88- .alert-secondary a .alert-link :hover {
89- color : #fff ;
90- }
91-
92- .progress-overlay {
93- position : absolute ;
94- top : 0 ;
95- left : 0 ;
96- right : 0 ;
97- bottom : 0 ;
98- display : none ; /* Hidden by default */
99- justify-content : center ;
100- align-items : center ;
101- background-color : rgba (0 , 0 , 0 , 0.8 );
102- z-index : 1000 ;
103- }
104-
105- .progress {
106- box-shadow : 0 0 10px rgba (0 , 0 , 0 , 0.5 );
107- }
108-
109- #progress-text {
110- font-size : 14px ;
111- font-weight : bold ;
112- }
113-
114- .alert-warning {
115- display : inline-block ;
116- margin : 0 auto ;
117- padding : 0.75rem 1.25rem ;
118- }
119-
120- /* Security Warning Styles */
121- .security-alert {
122- text-align : left ;
123- border : none ;
124- border-radius : 8px ;
125- margin-bottom : 1rem ;
126- padding : 0.75rem 1rem ;
127- box-shadow : 0 2px 10px rgba (220 , 53 , 69 , 0.3 );
128- position : relative ;
129- overflow : hidden ;
130- font-size : 0.9rem ;
131- display : inline-block ;
132- line-height : 1.4 ;
133- }
134-
135- .security-alert ::before {
136- content : ' ' ;
137- position : absolute ;
138- top : 0 ;
139- left : 0 ;
140- width : 4px ;
141- height : 100% ;
142- background : currentColor ;
143- opacity : 0.7 ;
144- }
145-
146- .security-alert.alert-danger {
147- background-color : rgba (220 , 53 , 69 , 0.1 );
148- color : #dc3545 ;
149- }
150-
151- .security-alert.alert-warning {
152- background-color : rgba (255 , 193 , 7 , 0.1 );
153- color : #ffc107 ;
154- }
155-
156- .security-alert i {
157- vertical-align : middle ;
158- }
159-
160- .security-alert strong {
161- vertical-align : middle ;
162- }
163-
164- .security-alert ul {
165- margin-top : 0.75rem ;
166- margin-bottom : 0 ;
167- padding-left : 2.5rem ;
168- color : #e9ecef ;
169- }
170-
171- .security-alert ul li {
172- margin : 0.5rem 0 ;
173- line-height : 1.4 ;
174- }
175-
176- .security-alert small {
177- display : block ;
178- margin-top : 1rem ;
179- padding-top : 1rem ;
180- border-top : 1px solid rgba (255 , 255 , 255 , 0.1 );
181- color : #e9ecef ;
182- }
183-
184- .security-alert pre {
185- background : rgba (0 , 0 , 0 , 0.2 );
186- border-radius : 4px ;
187- padding : 0.75rem ;
188- margin : 0.75rem 0 0 ;
189- color : #e9ecef ;
190- font-size : 0.9rem ;
191- }
192- </style >
193-
54+ <link rel =" stylesheet" href =" /public/css/emulator.css" >
19455 <script src =" https://code.jquery.com/jquery-3.7.1.min.js" crossorigin =" anonymous" ></script >
19556 <script src =" https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" crossorigin =" anonymous" ></script >
19657 <script src =" https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.2/js/bootstrap.min.js" crossorigin =" anonymous" ></script >
19758 <script src =" https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/js/all.min.js" crossorigin =" anonymous" ></script >
19859 <
script src =
" https://cdn.jsdelivr.net/npm/[email protected] /dist/jszip.min.js" ></
script >
19960 <script >
200- // Check if in a context that supports SharedArrayBuffer
201- const isHttps = window .location .protocol === ' https:' ;
202- const hasSharedArrayBuffer = typeof SharedArrayBuffer !== ' undefined' ;
203- const isCrossOriginIsolated = window .crossOriginIsolated === true ;
204- const canUseThreads = hasSharedArrayBuffer && isCrossOriginIsolated;
205-
206- // Display security warnings
207- const warningsDiv = document .getElementById (' security-warnings' );
208-
209- if (! isHttps) {
210- warningsDiv .innerHTML += `
211- <div class =" alert security-alert alert-danger py-2" >
212- <i class =" fas fa-exclamation-triangle mr-2" ></i >
213- <strong ><%= __('emulator.warning.https').split(':')[0] %>:</strong >
214- <%- __('emulator.warning.https').split(':')[1] %>
215- </div >
216- ` ;
217- }
218-
219- // Display important notice immediately
220- console .log (' %cAbout this Page' , ' font-size: 20px; font-weight: bold; color: #4CAF50;' );
221- console .log (
222- ' %c<%= __("emulator.console.about") %>\n ' +
223- ' <%= __("emulator.console.disclaimer") %>\n ' +
224- ' <%= __("emulator.console.more_info") %>' ,
225- ' font-size: 14px; color: #90CAF9;'
226- );
227- console .log (` %c${ window .location .origin } /about` , ' font-size: 14px; color: #90CAF9;' );
228-
229- // Configure EmulatorJS
230- console .log (' [Emulator] Starting emulator configuration' );
231- console .log (' [Emulator] System:' , ' <%= emulatorConfig.system %>' );
232- console .log (' [Emulator] Core:' , ' <%= emulatorConfig.core %>' );
233-
234- console .log (' [Emulator] SharedArrayBuffer available:' , hasSharedArrayBuffer);
235- console .log (' [Emulator] Cross-Origin-Isolation status:' , isCrossOriginIsolated);
236- console .log (' [Emulator] Can use threads:' , canUseThreads);
237-
238- window .EJS_player = ' #game' ;
239-
240- window .EJS_core = ' <%= emulatorConfig.core %>' ;
241- window .EJS_gameUrl = ' /proxy-rom/<%= romFile.id %>' ;
242- window .EJS_pathtodata = ' /emulatorjs/data/' ;
243- window .EJS_startOnLoaded = true ;
244- window .EJS_gameID = 1
245-
246- // Using threads improves performance by a lot
247- // But also creates freezes, crashes and some emulators need to be reconfigured to work
248- // This should be revisited in the future.
249- // We're using threads only on PSP for now
250- window .EJS_threads = ' <%= emulatorConfig.system %>' === ' Sony PlayStation Portable' ? (navigator .hardwareConcurrency || 4 ) : false ;
251- window .EJS_gameName = ' <%= romFile.filename.replace(/\. [^/.]+$/, "") %>' ;
252- window .EJS_backgroundBlur = true ;
253- window .EJS_defaultOptions = {
254- ' save-state-slot' : 1 ,
255- ' save-state-location' : ' local'
256- };
257-
258- // BIOS configuration
259- window .EJS_biosUrl = < % if (emulatorConfig .bios ) { % >
260- ' /proxy-bios?url=' + encodeURIComponent (< %- JSON .stringify (Object .values (emulatorConfig .bios .files )[0 ].url ) % > )
261- < % } else { % >
262- undefined
263- < % } % > ;
264-
265- console .log (' [Emulator] BIOS configuration:' , window .EJS_biosUrl );
266-
267- // Required for Sega CD ??
268- window .EJS_loadStateURL = window .location .href ;
269- window .EJS_saveStateURL = window .location .href ;
270- window .EJS_cheats = true ;
271-
272- // Add error event listener for the emulator
273- window .EJS_onGameStart = () => {
274- console .log (' [Emulator] Game started successfully' );
275- };
276-
277- window .EJS_onLoadState = (state ) => {
278- console .log (' [Emulator] Load state:' , state);
279- };
280-
281- window .EJS_onSaveState = (state ) => {
282- console .log (' [Emulator] Save state:' , state);
283- };
284-
285- window .EJS_onLoadError = (error ) => {
286- console .error (' [Emulator] Load error:' , error);
287- };
288-
289- async function loadRom () {
290- try {
291- console .log (' [Emulator] Starting ROM load process' );
292- const progressContainer = document .getElementById (' progress-container' );
293- const progressBar = document .getElementById (' download-progress' );
294- const progressText = document .getElementById (' progress-text' );
295- progressContainer .style .display = ' flex' ;
296-
297- const isCompressed = / \. (zip| 7z)$ / i .test (' <%= romFile.filename %>' );
298- const shouldUnpack = < %= emulatorConfig .unpackRoms % > ;
299- console .log (` [Emulator] ROM compression status: ${ isCompressed ? ' compressed' : ' uncompressed' } ` );
300- console .log (` [Emulator] Should unpack: ${ shouldUnpack} ` );
301-
302- progressText .textContent = ' <%= __("emulator.loading.downloading") %> (0%)' ;
303- console .log (' [Emulator] Initiating ROM download' );
304-
305- const response = await fetch (EJS_gameUrl);
306- if (! response .ok ) {
307- throw new Error (' <%= __("emulator.error.http_error", { status: "response.status" }) %>' .replace (' response.status' , response .status ));
308- }
309-
310- // If we're not unpacking, still show download progress but return direct URL
311- if (! isCompressed || ! shouldUnpack) {
312- const contentLength = response .headers .get (' content-length' );
313- const total = parseInt (contentLength, 10 );
314- let loaded = 0 ;
315-
316- const reader = response .body .getReader ();
317- const chunks = [];
318-
319- while (true ) {
320- const { done , value } = await reader .read ();
321- if (done) break ;
322-
323- chunks .push (value);
324- loaded += value .length ;
325-
326- const percent = Math .round ((loaded / total) * 100 );
327- progressBar .style .width = percent + ' %' ;
328- progressText .textContent = ` <%= __("emulator.loading.downloading") %> (${ percent} %)` ;
329- }
330-
331- console .log (' [Emulator] Using direct URL for ROM' );
332- progressContainer .style .display = ' none' ;
333-
334- // Create blob from chunks for direct loading
335- const blob = new Blob (chunks);
336- return URL .createObjectURL (blob);
337- }
338-
339- // For compressed files that need unpacking, continue with decompression
340- const contentLength = response .headers .get (' content-length' );
341- const total = parseInt (contentLength, 10 );
342- let loaded = 0 ;
343-
344- const reader = response .body .getReader ();
345- const chunks = [];
346-
347- while (true ) {
348- const { done , value } = await reader .read ();
349- if (done) break ;
350-
351- chunks .push (value);
352- loaded += value .length ;
353-
354- const percent = Math .round ((loaded / total) * 100 );
355- progressBar .style .width = percent + ' %' ;
356- progressText .textContent = ` <%= __("emulator.loading.downloading") %> (${ percent} %)` ;
357- }
358-
359- // Decompression phase
360- progressText .textContent = ' <%= __("emulator.loading.decompressing") %>' ;
361- console .log (' [Emulator] Starting ZIP extraction' );
362-
363- const blob = new Blob (chunks);
364- const zip = await JSZip .loadAsync (blob);
365- const files = Object .keys (zip .files );
366- console .log (' [Emulator] ZIP contents:' , files);
367-
368- const romFile = files .find (f => ! zip .files [f].dir );
369- if (! romFile) {
370- throw new Error (' <%= __("emulator.error.no_rom") %>' );
371- }
372- console .log (' [Emulator] Found ROM file in ZIP:' , romFile);
373-
374- const romData = await zip .files [romFile].async (' blob' );
375- console .log (' [Emulator] ROM extraction complete' );
376- progressContainer .style .display = ' none' ;
377- return URL .createObjectURL (romData);
378- } catch (error) {
379- console .error (' [Emulator] Error in loadRom:' , error);
380- throw error;
61+ const emulatorConfig = < %- JSON .stringify (emulatorConfig) % >
62+ const romFile = < %- JSON .stringify (romFile) % >
63+
64+ const emuStrings = {
65+ console: {
66+ about: " <%= __(" emulator .console .about " ) %>" ,
67+ disclaimer: " <%= __(" emulator .console .disclaimer " ) %>" ,
68+ more_info: " <%= __(" emulator .console .more_info " ) %>"
69+ },
70+ warning: {
71+ https: " <%= __(" emulator .warning .https " ) %>"
72+ },
73+ loading: {
74+ downloading: " <%= __(" emulator .loading .downloading " ) %>" ,
75+ decompressing: " <%= __(" emulator .loading .decompressing " ) %>"
76+ },
77+ error: {
78+ http_error: " <%= __(" emulator .error .http_error " ) %>" ,
79+ no_rom: " <%= __(" emulator .error .no_rom " ) %>" ,
80+ loading: " <%= __(" emulator .error .loading " ) %>"
38181 }
38282 }
383-
384- loadRom ()
385- .then (romUrl => {
386- console .log (' [Emulator] ROM loaded successfully, initializing EmulatorJS' );
387- window .EJS_gameUrl = romUrl;
388-
389- // We need to wait a moment to ensure cross-origin isolation is properly applied
390- setTimeout (() => {
391- const script = document .createElement (' script' );
392- script .src = ` ${ window .EJS_pathtodata } loader.js` ;
393- script .onerror = (error ) => {
394- const gameDiv = document .getElementById (' game' );
395- gameDiv .innerHTML = ` <div class =" alert alert-danger" >
396- Failed to load EmulatorJS. Please refresh the page or try again later.
397- </div >` ;
398- };
399- document .body .appendChild (script);
400- }, 500 );
401- })
402- .catch (error => {
403- const gameDiv = document .getElementById (' game' );
404- gameDiv .innerHTML = ` <div class =" alert alert-danger" >
405- <%= __("emulator.error.loading") %>: ${ error .message }
406- </div >` ;
407- });
40883 </script >
84+ <script src =" /public/js/emulator.js" defer ></script >
40985</body >
41086</html >
0 commit comments