Would you like to play Nokia (J2ME) games on Retropie?
-
I guess I'm not done...
So I converted everything to AWT, as I had a lot of work that would have gone wasted otherwise. I figured that someone might want a new J2ME "emulator" either to tinker with the source or just to play their old games. Out of curiosity, I wondered if the JavaFx bit would be as slow as I thought.
While the JavaFx version plays fine on the desktop, it's absolutely awful on my pi 3. It's okay for turn-based games like Doom RPG and Orcs and Elves, but games like ShadoWalker are just too slow to be fun.
Give it a try if you want.
You'll need the JavaFx binaries (see Hex's post above) and Oracle's JDK. Run as root from the command line, e.g.:
java -jar freej2me_javafx.jar file:/home/pi/RetroPie/roms/j2me/ShadoWalker.jar 240 320Q and E for soft 1 and 2, numbers, arrow keys, wasd (depending on the game). Mouse for touch screen (for games like Doom II RPG). M to quit. Experiment with display sizes if a game isn't working correctly (128x128, 176x208, 240x320 are common, 360x640 for some later games, notably Doom II RPG)
Yes, the controls are stupid and there isn't any controller support. This was just a test, after all. It's not like it runs fast enough to be fun anyway. I offer it here for novelty only.
Maybe it'll work okay on the pi 4, should it ever appear.
-
@recompile Excellent work. I will give it a try. How is that old phones were better at doing this. Since this is technically not an emulator, more like an interface I was under the impression that performance would be least of the worries. Is it the graphics conversion that makes it slow?
-
Out of curiosity, when you're running it and run
top
in a separate SSH session, what's the bottleneck? CPU? Memory? Something else? -
@hex said in Would you like to play Nokia (J2ME) games on Retropie?:
Is it the graphics conversion that makes it slow?
That seems to be a large part of it.
It wasn't much of a test, but this is what I did. Using a watch, I measured how far I could walk forward in ShadoWalker in 5 seconds on my desktop. I did the same on my pi3 both with and without drawing the display. (I added a feature that skips the pixel-by-pixel copy in javafx if a flag is set, which I can turn on and off with a keypress) Without drawing, I move a bit further than I do with drawing though not nearly as far as on my desktop.
Doing the same experiment, but only drawing 25% of the pixels, I move farther than full drawing, but not quite as far as not drawing at all.
Changing repaint/flushgraphics to a faster (but incorrect) version that doesn't make a subimage doesn't seem to make any difference. That manual pixel-by-pixel copy for JavaFx really is significantly slower than other image copying.
Now, removing the excessive once-per-frame gc call I had (which I added to remove irritating pauses in Orcs and Elves) made a big difference. I gained about as much distance while drawing as between without and with drawing before. Without drawing, I was able to go the same distance as with drawing on my desktop!
There's some clear room for improvement, and a clear path to victory. This could actually be doable with some smarter memory management and something faster than JavaFx.
This is what I'm thinking: If you can make a small demo with JNI and SDL that draws a scaled image on screen (you'll want to pass a byte array), I'll turn that in to a usable J2ME "emulator". I've never used SDL or JNI, and the bulk of the code is likely to be in C. This part might be right up your alley.
The JNI part doesn't look like it'll be difficult:
https://www3.ntu.edu.sg/home/ehchua/programming/java/JavaNativeInterface.htmlAs a bonus, by ditching JavaFx, we'll make it much easier for others to get this running.
-
@recompile It is awesome. Basically I have to draw frames from java using C/C++. Can you tell me what type of frame data you have so I can start from that.
I shall get to it once PS gets updated. There were some issues with Video Screensaver and PS that caused ES to be unusable if VideoScreensaver was disabled by setting the timeout to zero. Ignorance on my part.
-
@hex You'll have a byte array with pixel data in ARGB (32bpp) from BufferedImage.getRGB and two int32s, for width and height.
-
@recompile Sounds good. I will get back to you once I have something working.
-
@recompile Would the following work by any chance. I have no experience with JNI and it is not ideal for me at present.
You can open a process from java with HxW parameters. Then pipe each frame to it.
My application can read HxW from passed parameters, create SDL surface and draw incoming frames. Once you finish and app needs closing just close the pipe which will cleanly exit my application too.
Alternatively I can create a FIFO and you can pipe to it if that makes it easier.
Also I am interested in knowing if your frame will handle black background and scaling on the buffer or should I handle that in c++. both options are acceptable.
-
@recompile This is what I have been able to accomplish. Since I am not fluent in Java at present I opted to show the example using python. It will mostly be similar for java.
I dont have a stream of frames so I am sending over a single raw image stream from python to c++/SDL. Then sleeping for 10 secs and exiting.
You will need to make some changes for ARGB mode
unsigned int bytes = 3; //Make this 4 // For RGB :: Comment next line SDL_PIXELFORMAT_RGB24, // For ARGB32 :: Uncomment next line // SDL_PIXELFORMAT_ARGB8888,
You will need to compile the c++ file. Instructions in MD file.
https://www.dropbox.com/sh/1mr9qcm5qxqow63/AAAzd5tHb-JmysaF79UpB5Wja?dl=0
Let me know if you need any more info or have questions.
-
@hex said in Would you like to play Nokia (J2ME) games on Retropie?:
I am not fluent in Java
Neither am I. I don't even like Java. But look how far we've managed to get anyway.
I like the idea of a named pipe. We'll need to get input from SDL as well, so that may be the easiest way. Though I wonder if sockets would be easier.
@hex said in Would you like to play Nokia (J2ME) games on Retropie?:
if your frame will handle black background and scaling on the buffer
It will not because it should not. It would be slower and we'd be sending a lot more data on every frame.
-
@recompile SDL handles scaling and letterboxing by default so that s not an issue. I shall check if I can get JNI working
I dont think we need a named pipe. Writing to STDIN of the c++ file is sufficient. As soon of you close the pipe SDL cleanly exits. What input are you considering from SDL? Keyboard Events?
-
@hex said in Would you like to play Nokia (J2ME) games on Retropie?:
s. What input are you considering from SDL? Keyboard Events?
Yes. Joystick and mouse input as well. We won't have access to those without a java ui otherwise for what I can only assume are incredibly stupid reasons.
@hex said in Would you like to play Nokia (J2ME) games on Retropie?:
Writing to STDIN of the c++ file is sufficient.
Games are surprisingly noisy. That Prince of Persia game you mentioned, for example, writes to stdout every time you grab a ledge.
-
Ok I get the controls thing. We cans see what can be done for inter process communications.
@recompile said in Would you like to play Nokia (J2ME) games on Retropie?:
Games are surprisingly noisy. That Prince of Persia game you mentioned, for example, writes to stdout every time you grab a ledge.
That shouldn't make a difference. So what is happening is from JAVA application (J) you open the C++ application (C) with source W and H as parameters. Then send video frames to C.stdin. Thus you are only sending frames on this channel. You can also read from C.stdout which shall send back only key/Joystick events . Thus the entire communication is controlled.
/* Pipes used for J2ME emu ,---- Frame out ---- >> -------- STDIN -----C |,--- Event in ----- << ------ SDTOUT ----' J --- SDTOUT -, |`--- STDERR |- Free do do what ever needed `---- STDIN -' */
-
I'll give it a shot, I guess. You'll need to send keydown and keyup events.
Ed: We may want to be able to change WxH at will for configuration.
-
This actually eliminates the need for JNI and is very fast. Just make changes to the c++ code i mentioned above and remove all print statements so stdout is clean for events while video testing. I shall make way for event passing till then.
Things to Note:
- Frame should always be completely passed else Sync will be lost.
- C is handling scaling and letterboxing
Also do you have a communication standard in mind for Events or am I free to select?
Edit based on your edit: Well J can start C once resolution is know. Is there a way for J to know resolution once a jar is loaded?
-
@hex said in Would you like to play Nokia (J2ME) games on Retropie?:
do you have a communication standard in mind for Events or am I free to select?
Feel free to use whatever you'd like. It could be as simple as a byte for the event type and a byte for event data for keyboard/joystick, maybe a pair of shorts for mouse events. I like simple.
-
@hex said in Would you like to play Nokia (J2ME) games on Retropie?:
Is there a way for J to know resolution once a jar is loaded?
Sadly, no. Most games get the display size from the phone, and either adapt to it or fail. A few games have a preferred display size in the manifest, but it's not reliable. This is why I was passing the display resolution in as a parameter.
I had planned to add a configuration editor that would start and ask for things like display size, control configuration, etc. that starts if a configuration for a game can't be found on load, or that can be manually started to modify a games configuration.
This is what I'm thinking. I could pass a byte along with the frame that signals that what follows is either a frame or a change in resolution. Something like [0, ...frame data...] or [1, shortW, shortH]
-
@recompile is is ok for me to check for incomming events between frames? I dont know if incoming frames can be guaranteed. So will something like this work for you in "C"? Else I will need to make it multi threaded so that polling and drawing can be done independently.
//wait for incomming frame, blocking call while true { read_frame() //Render frame show_frame() // Check if input event occurred if SDL_PollEvent() send_event() }
-
@recompile Regarding the resolution problem, I would like to suggest this :
If config found
- Read config
- Read resolution from config
- Start C based on resolution
If config not found
- Get screen resolution
- Start C based on resolution
- Send frames for config.
- Get map of all required info
- Write config file
- End C by closing C.stdin pipe
- GOTO : if config found
Will this work ?
I would like to keep communications to a minimum. Killing and restarting is not much of a big deal.
-
@hex That'll work. If freely killing and restarting 'C' isn't going to be a problem then the problem is solved.
@hex said in Would you like to play Nokia (J2ME) games on Retropie?:
@recompile is is ok for me to check for incomming events between frames? I dont know if incoming frames can be guaranteed.
There's no guarantee at all. Some games will just sit and not repaint while waiting for an event. Other games will draw frames as fast as they can, even if nothing changes.
Contributions to the project are always appreciated, so if you would like to support us with a donation you can do so here.
Hosting provided by Mythic-Beasts. See the Hosting Information page for more information.