thenick775 / gbajs3

Gbajs3 is a full Game Boy Advance emulator online in the browser supporting the mGBA WASM core. It is freely licensed and works in any modern browser.
Other
41 stars 26 forks source link

Module.screenShot and Module.autoLoadCheats, Pls help me (bow) #55

Closed sonht0555 closed 8 months ago

sonht0555 commented 8 months ago

How to apply and call functions Module.screenShot and Module.autoLoadCheats on java script. I'm trying to use the screenShot feature to take a snapshot after each save state, but it seems like I'm not using the Module.screenShot function correctly. I tested it like this, and it doesn't seem to return any data.

function domToPng(){
  return Promise.resolve('dataurl');
}
document.getElementById("convertBtn").addEventListener("click", function () {
const canvas = document.getElementById("canvas");
  if (!canvas) return;
  Module?.screenShot(() =>
  domToPng(canvas)
      .then((dataUrl) => {
        const link = document.createElement('a');
        link.download = 'screenshot.png;'
        link.href = dataUrl;
        link.click();
        link.remove();
      })
      .catch(() => {
        toast.error('Screenshot has failed');
      })
  );
});
thenick775 commented 8 months ago

If you have issues reaching into the wasm implementation, please feel free to open additional issues in my mGBA fork!

Do you have domToPng installed? Your implementation wont return any data because you're not operating on a data url that contains the contents of the canvas, which can be easily downloaded as a file. That's what I'm using domToPng for in this case.

Here's a bit about how you can get a data url but I know it doesn't always work reliably on iOS, hence my use of the above library.

But in reference to autoLoadCheats: That function returns true/false (1/0 in c) if the auto loading of cheats was successful. This function does load cheats, but it loads the cheat files in the cores cheatsPath from IDBFS supporting the following formats:

// loads all cheats files located in the cores cheatsPath,
// cheat files must match the name of the rom they are
// to be applied to, and must end with the extension .cheats
// supported cheat formats: 
//  - mGBA custom format
//  - libretro format
//  - EZFCht format

You can follow my full implementation here and here. And can use Module.uploadCheats if you want to upload a cheats file.

For the screenshot, its designed to give you just an image (which save states also are, but there a better function to call for that).

The screenShot function is designed to allow you to get a full canvas buffer to then be operated on and copied in the DOM due to the way webgl clears the canvas buffers, otherwise you would always get a black canvas to be saved to an image. It takes a callback that will be executed when the canvas has been painted to, see this file for a bit more information.

If you want to save save state (which is also a screenshot for mGBA), use Module.saveState/Module.loadState/Module.uploadSaveOrSaveState

As always, happy to answer any questions 😎

sonht0555 commented 8 months ago

function domToPng(){ return Promise.resolve('dataurl'); }

I just declared like this, how to fully declare domtopng :(( I tried referencing your code. But I don't understand too much about TS

sonht0555 commented 8 months ago

Do you have any chat channels? I want to be asked more

thenick775 commented 8 months ago

You'd need to install it with NPM, but in your case try rendering a canvas with painted contents and use something like this:

const canvas = document.getElementById("canvas");
const dataURL = canvas.toDataURL();
console.log(dataURL);
<canvas id="canvas" width="5" height="5"></canvas>
thenick775 commented 8 months ago

In reference to chat channels, feel free to open discussions here or here!

Or ping me in issues you've created within your own emulator frontend repository!

sonht0555 commented 8 months ago

That's right, I don't know why the return value of module.screenshot is black. even if it went through the doomtopng filter

thenick775 commented 8 months ago

Feel free to send me a link to a branch if your repo, happy to take a look if you have targeted pieces of code and a branch with your screenshot code committed.

But with the frontend here, I only run the screenshot function when there is a running game, and the canvas is controlled by mGBA/emscripten in webGL. Otherwise it'll always be black, as there's nothing in the canvas buffers

sonht0555 commented 8 months ago

You'd need to install it with NPM, but in your case try rendering a canvas with painted contents and use something like this:

i tried, it returns black, actually it is still showing gaming emulator, i don't understand why

sonht0555 commented 8 months ago

ok, let me tag you in my repo. I haven't pushed it up yet

thenick775 commented 8 months ago

In js, heres how I used to do it with a 2d canvas context with the older gbajs that didn't use emscripten/webGL:

screenShot() {
    var resizedCanvas = document.createElement('canvas');
    var resizedContext = resizedCanvas.getContext('2d');
    resizedContext.mozImageSmoothingEnabled = false;
    resizedContext.webkitImageSmoothingEnabled = false;
    resizedContext.msImageSmoothingEnabled = false;
    resizedContext.imageSmoothingEnabled = false;

    resizedCanvas.height = $('#screenwrapper').height();
    resizedCanvas.width = $('#screenwrapper').width();

    var screen = document.getElementById('screen');
    resizedContext.drawImage(
        screen,
        0,
        0,
        resizedCanvas.width,
        resizedCanvas.height
    );

    let data = resizedCanvas.toDataURL();
    let image = new Image();
    image.src = data;
    let w = window.open('');
    w.document.write(image.outerHTML);
}
thenick775 commented 8 months ago

You'd need to install it with NPM, but in your case try rendering a canvas with painted contents and use something like this:

i tried, it returns black, actually it is still showing gaming emulator, i don't understand why

In your case you probably need to get the data url like:

Module.screenShot(() =>
        var resizedCanvas = document.createElement('canvas');
    var resizedContext = resizedCanvas.getContext('2d');
    resizedContext.mozImageSmoothingEnabled = false;
    resizedContext.webkitImageSmoothingEnabled = false;
    resizedContext.msImageSmoothingEnabled = false;
    resizedContext.imageSmoothingEnabled = false;

    resizedCanvas.height = $('#screenwrapper').height();
    resizedCanvas.width = $('#screenwrapper').width();

    var screen = document.getElementById('screen'); // your emulator webgl canvas element by ID
    resizedContext.drawImage(
        screen,
        0,
        0,
        resizedCanvas.width,
        resizedCanvas.height
    );

    let data = resizedCanvas.toDataURL();
    let image = new Image();
    image.src = data;
    let w = window.open('');
    w.document.write(image.outerHTML);
);

As my mGBA fork as well as endrift's uses emscripten with a webgl canvas context.

Then you can open it in a new tab like I did here, or download the screenshot as a file

sonht0555 commented 8 months ago

I have understood. let me try. Most likely, my problem lies in the step of not initializing the getContext('webgl') environment, so the frontend canvas has no data, causing the results to return completely black.

thenick775 commented 8 months ago

Sure, I also made some mistakes and have edited them in relation to the canvas contexts.

This code uses the mGBA WASM function Module.screenShot to fill the webgl canvas buffers so we can grab the image.

It executes the function callback provided as a parameter after the buffers are loaded.

I create a 2d canvas context where I am going to copy the webgl canvas buffers to for easier manipulation:

var resizedCanvas = document.createElement('canvas'); // canvas to copy webgl canvas to
var resizedContext = resizedCanvas.getContext('2d');
resizedContext.mozImageSmoothingEnabled = false;
resizedContext.webkitImageSmoothingEnabled = false;
resizedContext.msImageSmoothingEnabled = false;
resizedContext.imageSmoothingEnabled = false;

// make this new copy canvas the same width and height as the original
resizedCanvas.height = $('#screenwrapper').height();
resizedCanvas.width = $('#screenwrapper').width();

Now we want to copy our emulator webgl canvas into our new 2d "copy" canvas (resizedCanvas):

var screen = document.getElementById('screen'); // your canvas element by ID, what we are copying from
resizedContext.drawImage( // use the copy canvas contexts drawImage to copy your emulator canvas's buffers (the image)
  screen,
  0,
  0,
  resizedCanvas.width,
  resizedCanvas.height
);
// grab the data url, write this to an image, then open the image in a new tab of the browser
let data = resizedCanvas.toDataURL();
let image = new Image();
image.src = data;
let w = window.open('');
w.document.write(image.outerHTML);
sonht0555 commented 8 months ago

I did itπŸ˜›. There's a little more. I'm trying to create a function to import cheat codes and load them into the game. Can you guide me through the steps to achieve that function?

thenick775 commented 8 months ago

Sure, you can use Module.uploadCheats to load a cheat file object in any of the following formats supported here

sonht0555 commented 8 months ago

How to enter cheat file or cheat code here. As I understand it, this is simply a function that calls the cheat file.

If my fontend has an input box and a save cheat button. How can I use your other module?

thenick775 commented 8 months ago

How to enter cheat file or cheat code here. As I understand it, this is simply a function that calls the cheat file.

If my fontend has an input box and a save cheat button. How can I use your other module?

In this case, Module.uploadCheats uploads the cheat file object from your file input to the cores cheatsPath in IndexedDB, and takes a callback you can use to load the cheat file in mGBA by calling Module.autoLoadCheats. You'll want to do this on the submit when you press the button

See here for typings, and here for more information how I implemented saving the current cheat file with a running emulator.

Edited: added some small details

sonht0555 commented 8 months ago

ya, I feel you. I have seen all the functions to call out