439 lines
14 KiB
C
439 lines
14 KiB
C
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <math.h>
|
|
#include <SDL2/SDL.h>
|
|
#include <SDL2/SDL_ttf.h>
|
|
#include "helpers.h"
|
|
|
|
#define HEAP_POOL_SIZE 4096
|
|
|
|
static Object pool[HEAP_POOL_SIZE];
|
|
static Object *free_list = NULL;
|
|
static int initialized = 0;
|
|
|
|
static void heap_init(void) {
|
|
for (int i = 0; i < HEAP_POOL_SIZE - 1; i++)
|
|
pool[i].next_free = &pool[i + 1];
|
|
pool[HEAP_POOL_SIZE - 1].next_free = NULL;
|
|
free_list = &pool[0];
|
|
initialized = 1;
|
|
}
|
|
|
|
Object *heap_alloc(CPEntry *cls, uint16_t fields_count) {
|
|
if (!initialized) heap_init();
|
|
|
|
if (!free_list) {
|
|
fprintf(stderr, "heap: out of memory\n");
|
|
return NULL;
|
|
}
|
|
|
|
Object *obj = free_list;
|
|
free_list = obj->next_free;
|
|
obj->next_free = NULL;
|
|
obj->cls = cls;
|
|
obj->fields_count = fields_count;
|
|
obj->fields = calloc(fields_count, sizeof(intptr_t));
|
|
return obj;
|
|
}
|
|
|
|
void heap_free(Object *obj) {
|
|
if (!obj) return;
|
|
free(obj->fields);
|
|
obj->fields = NULL;
|
|
obj->next_free = free_list;
|
|
free_list = obj;
|
|
}
|
|
|
|
void heap_destroy(void) {
|
|
for (int i = 0; i < HEAP_POOL_SIZE; i++)
|
|
if (pool[i].next_free == NULL && pool[i].fields)
|
|
free(pool[i].fields);
|
|
}
|
|
|
|
|
|
|
|
/* SDL2 globals */
|
|
static SDL_Window *g_win = NULL;
|
|
static SDL_Renderer *g_ren = NULL;
|
|
static TTF_Font *g_font = NULL;
|
|
|
|
extern int execute(ClassFile *cf, Method *m, intptr_t *args, int argc);
|
|
|
|
/* Fake "Display" object slot — just a sentinel int, not a real Object */
|
|
#define DISPLAY_SENTINEL 0xD15F1A7
|
|
|
|
void midp_sdl_init(int w, int h) {
|
|
SDL_Init(SDL_INIT_VIDEO);
|
|
if (TTF_Init() != 0) {
|
|
fprintf(stderr, "SDL_ttf init failed: %s\n", TTF_GetError());
|
|
}
|
|
|
|
g_win = SDL_CreateWindow("MIDlet", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, w, h, 0);
|
|
g_ren = SDL_CreateRenderer(g_win, -1, SDL_RENDERER_ACCELERATED);
|
|
|
|
if (!g_font) {
|
|
const char *font_candidates[] = {
|
|
"/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf",
|
|
"/usr/share/fonts/truetype/liberation2/LiberationSans-Regular.ttf",
|
|
"/usr/share/fonts/truetype/freefont/FreeSans.ttf"
|
|
};
|
|
for (size_t i = 0; i < sizeof(font_candidates) / sizeof(font_candidates[0]); i++) {
|
|
g_font = TTF_OpenFont(font_candidates[i], 16);
|
|
if (g_font) break;
|
|
}
|
|
if (!g_font) {
|
|
fprintf(stderr, "SDL_ttf font load failed: %s\n", TTF_GetError());
|
|
}
|
|
}
|
|
|
|
SDL_SetRenderDrawColor(g_ren, 0, 0, 0, 255);
|
|
SDL_RenderClear(g_ren);
|
|
SDL_RenderPresent(g_ren);
|
|
}
|
|
|
|
void midp_sdl_destroy(void) {
|
|
if (g_font) {
|
|
TTF_CloseFont(g_font);
|
|
g_font = NULL;
|
|
}
|
|
if (TTF_WasInit()) TTF_Quit();
|
|
SDL_DestroyRenderer(g_ren);
|
|
SDL_DestroyWindow(g_win);
|
|
SDL_Quit();
|
|
}
|
|
|
|
void midp_sdl_wait(int milliseconds) {
|
|
Uint32 start = SDL_GetTicks();
|
|
SDL_Event ev;
|
|
|
|
while ((int)(SDL_GetTicks() - start) < milliseconds) {
|
|
while (SDL_PollEvent(&ev)) {
|
|
if (ev.type == SDL_QUIT) {
|
|
return;
|
|
}
|
|
}
|
|
SDL_Delay(16);
|
|
}
|
|
}
|
|
|
|
/* helper: match class/name/desc triple */
|
|
static int is(const char *a, const char *b) { return strcmp(a, b) == 0; }
|
|
|
|
static void draw_text_ttf(const char *text, int x, int y) {
|
|
if (!text || !*text || !g_font || !g_ren) return;
|
|
|
|
Uint8 r = 255, g = 255, b = 255, a = 255;
|
|
SDL_GetRenderDrawColor(g_ren, &r, &g, &b, &a);
|
|
|
|
SDL_Color color = { r, g, b, a };
|
|
SDL_Surface *surface = TTF_RenderUTF8_Blended(g_font, text, color);
|
|
if (!surface) return;
|
|
|
|
SDL_Texture *texture = SDL_CreateTextureFromSurface(g_ren, surface);
|
|
if (!texture) {
|
|
SDL_FreeSurface(surface);
|
|
return;
|
|
}
|
|
|
|
SDL_Rect dst = { x, y, surface->w, surface->h };
|
|
SDL_FreeSurface(surface);
|
|
SDL_RenderCopy(g_ren, texture, NULL, &dst);
|
|
SDL_DestroyTexture(texture);
|
|
}
|
|
|
|
static void draw_arc_outline(int cx, int cy, int rx, int ry, int start_deg, int arc_deg) {
|
|
int end_deg = start_deg + arc_deg;
|
|
for (int deg = start_deg; deg <= end_deg; deg++) {
|
|
double rad = deg * 3.14159265358979323846 / 180.0;
|
|
int x = cx + (int)(rx * cos(rad));
|
|
int y = cy + (int)(ry * sin(rad));
|
|
SDL_RenderDrawPoint(g_ren, x, y);
|
|
}
|
|
}
|
|
|
|
static int invoke_canvas_paint(ClassFile *caller_cf, intptr_t canvas_slot) {
|
|
Object *canvas = SLOT_TO_OBJ(canvas_slot);
|
|
if (!canvas || !canvas->cls || canvas->cls->tag != CP_CLASS) return 0;
|
|
|
|
const char *canvas_class = cp_utf8(caller_cf, canvas->cls->cls_or_str.index);
|
|
if (!canvas_class || !canvas_class[0]) return 0;
|
|
|
|
char class_path[512];
|
|
snprintf(class_path, sizeof(class_path), "%s.class", canvas_class);
|
|
|
|
ClassFile *canvas_cf = classfile_load(class_path);
|
|
if (!canvas_cf) {
|
|
fprintf(stderr, "cannot load canvas class file: %s\n", class_path);
|
|
return 0;
|
|
}
|
|
|
|
Method *paint = classfile_find_method(canvas_cf, "paint",
|
|
"(Ljavax/microedition/lcdui/Graphics;)V");
|
|
if (!paint) {
|
|
classfile_free(canvas_cf);
|
|
return 0;
|
|
}
|
|
|
|
intptr_t args[2] = { canvas_slot, 0xC0FFEE };
|
|
int rc = execute(canvas_cf, paint, args, 2);
|
|
classfile_free(canvas_cf);
|
|
|
|
SDL_RenderPresent(g_ren);
|
|
return rc >= 0;
|
|
}
|
|
|
|
int midp_native_call(ClassFile *cf, const char *class_name, const char *name, const char *desc, intptr_t *stack, int *sp, int opcode __attribute__((unused)))
|
|
{
|
|
if (is(desc, "()I") && is(name, "getWidth")) {
|
|
int w, h;
|
|
(void)h;
|
|
SDL_GetWindowSize(g_win, &w, &h);
|
|
(*sp)--; /* pop 'this' */
|
|
stack[(*sp)++] = w;
|
|
return 1;
|
|
}
|
|
if (is(desc, "()I") && is(name, "getHeight")) {
|
|
int w, h;
|
|
(void)w;
|
|
SDL_GetWindowSize(g_win, &w, &h);
|
|
(*sp)--; /* pop 'this' */
|
|
stack[(*sp)++] = h;
|
|
return 1;
|
|
}
|
|
|
|
/* javax.microedition.lcdui.Display */
|
|
|
|
if (is(class_name, "javax/microedition/lcdui/Display")) {
|
|
|
|
/* Display.getDisplay(MIDlet) → Display */
|
|
if (is(name, "getDisplay") &&
|
|
is(desc, "(Ljavax/microedition/midlet/MIDlet;)Ljavax/microedition/lcdui/Display;")) {
|
|
(*sp)--; /* pop MIDlet ref */
|
|
stack[(*sp)++] = DISPLAY_SENTINEL;
|
|
return 1;
|
|
}
|
|
|
|
/* Display.setCurrent(Displayable) → void */
|
|
if (is(name, "setCurrent") &&
|
|
is(desc, "(Ljavax/microedition/lcdui/Displayable;)V")) {
|
|
intptr_t displayable = stack[--(*sp)];
|
|
intptr_t display_ref = stack[--(*sp)]; /* 'this' for virtual */
|
|
(void)displayable; (void)display_ref;
|
|
|
|
/* Clear and show a frame so emulator window is visibly active. */
|
|
SDL_SetRenderDrawColor(g_ren, 0, 0, 0, 255);
|
|
SDL_RenderClear(g_ren);
|
|
|
|
/* Try to run original Canvas.paint(Graphics). */
|
|
invoke_canvas_paint(cf, displayable);
|
|
|
|
SDL_RenderPresent(g_ren);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
/* javax.microedition.lcdui.Graphics ─ */
|
|
|
|
if (is(class_name, "javax/microedition/lcdui/Graphics")) {
|
|
|
|
/* setColor(int) */
|
|
if (is(name, "setColor") && is(desc, "(I)V")) {
|
|
int rgb = stack[--(*sp)];
|
|
(*sp)--; /* pop 'this' */
|
|
SDL_SetRenderDrawColor(g_ren,
|
|
(rgb >> 16) & 0xFF,
|
|
(rgb >> 8) & 0xFF,
|
|
rgb & 0xFF, 255);
|
|
return 1;
|
|
}
|
|
|
|
/* setColor(r, g, b) */
|
|
if (is(name, "setColor") && is(desc, "(III)V")) {
|
|
int b = stack[--(*sp)] & 0xFF;
|
|
int g = stack[--(*sp)] & 0xFF;
|
|
int r = stack[--(*sp)] & 0xFF;
|
|
(*sp)--; /* pop 'this' */
|
|
SDL_SetRenderDrawColor(g_ren, r, g, b, 255);
|
|
return 1;
|
|
}
|
|
|
|
/* fillRect(x, y, w, h) */
|
|
if (is(name, "fillRect") && is(desc, "(IIII)V")) {
|
|
int h2 = stack[--(*sp)];
|
|
int w2 = stack[--(*sp)];
|
|
int y = stack[--(*sp)];
|
|
int x = stack[--(*sp)];
|
|
(*sp)--; /* pop 'this' */
|
|
SDL_Rect r = { x, y, w2, h2 };
|
|
SDL_RenderFillRect(g_ren, &r);
|
|
return 1;
|
|
}
|
|
|
|
/* drawRect(x, y, w, h) */
|
|
if (is(name, "drawRect") && is(desc, "(IIII)V")) {
|
|
int h2 = stack[--(*sp)];
|
|
int w2 = stack[--(*sp)];
|
|
int y = stack[--(*sp)];
|
|
int x = stack[--(*sp)];
|
|
(*sp)--;
|
|
SDL_Rect r = { x, y, w2, h2 };
|
|
SDL_RenderDrawRect(g_ren, &r);
|
|
return 1;
|
|
}
|
|
|
|
/* drawLine(x1, y1, x2, y2) */
|
|
if (is(name, "drawLine") && is(desc, "(IIII)V")) {
|
|
int y2 = stack[--(*sp)];
|
|
int x2 = stack[--(*sp)];
|
|
int y1 = stack[--(*sp)];
|
|
int x1 = stack[--(*sp)];
|
|
(*sp)--;
|
|
SDL_RenderDrawLine(g_ren, x1, y1, x2, y2);
|
|
return 1;
|
|
}
|
|
|
|
/* drawString(String, x, y, anchor) - anchor ignored for now */
|
|
if (is(name, "drawString") && is(desc, "(Ljava/lang/String;III)V")) {
|
|
int anchor = stack[--(*sp)];
|
|
int y = stack[--(*sp)];
|
|
int x = stack[--(*sp)];
|
|
int sidx = stack[--(*sp)];
|
|
(*sp)--;
|
|
(void)anchor;
|
|
if (sidx > 0 && sidx < cf->cp_count && cf->cp[sidx].tag == CP_STRING) {
|
|
const char *text = cp_utf8(cf, cf->cp[sidx].cls_or_str.index);
|
|
draw_text_ttf(text, x, y);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
if (is(name, "drawArc") && is(desc, "(IIIIII)V")) {
|
|
int arc_deg = stack[--(*sp)];
|
|
int start = stack[--(*sp)];
|
|
int h2 = stack[--(*sp)];
|
|
int w2 = stack[--(*sp)];
|
|
int y = stack[--(*sp)];
|
|
int x = stack[--(*sp)];
|
|
(*sp)--;
|
|
|
|
int cx = x + (w2 / 2);
|
|
int cy = y + (h2 / 2);
|
|
int rx = w2 / 2;
|
|
int ry = h2 / 2;
|
|
draw_arc_outline(cx, cy, rx, ry, start, arc_deg);
|
|
return 1;
|
|
}
|
|
|
|
/* flushGraphics / repaint stubs — just present */
|
|
if (is(name, "flushGraphics") || is(name, "repaint")) {
|
|
/* pop whatever args there are, then 'this' */
|
|
/* for safety just drain back to a known depth — caller handles */
|
|
SDL_RenderPresent(g_ren);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
/* javax.microedition.lcdui.Canvas */
|
|
|
|
if (is(class_name, "javax/microedition/lcdui/Canvas")) {
|
|
|
|
/* Canvas.repaint() */
|
|
if (is(name, "repaint") && is(desc, "()V")) {
|
|
(*sp)--; /* pop 'this' */
|
|
SDL_RenderPresent(g_ren);
|
|
return 1;
|
|
}
|
|
|
|
/* Canvas.getGraphics() → Graphics (stub: return sentinel) */
|
|
if (is(name, "getGraphics") &&
|
|
is(desc, "()Ljavax/microedition/lcdui/Graphics;")) {
|
|
(*sp)--; /* pop 'this' */
|
|
stack[(*sp)++] = 0xC0FFEE; /* fake Graphics handle */
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
/* java.io.PrintStream ─ */
|
|
|
|
if (is(class_name, "java/io/PrintStream")) {
|
|
if (is(name, "println")) {
|
|
if (is(desc, "(I)V")) {
|
|
int val = stack[--(*sp)]; (*sp)--;
|
|
printf("%d\n", val);
|
|
return 1;
|
|
}
|
|
if (is(desc, "(Ljava/lang/String;)V")) {
|
|
int str_idx = stack[--(*sp)]; (*sp)--;
|
|
CPEntry *e = &cf->cp[str_idx];
|
|
if (e->tag == CP_STRING)
|
|
printf("%s\n", cp_utf8(cf, e->cls_or_str.index));
|
|
return 1;
|
|
}
|
|
if (is(desc, "(Z)V")) {
|
|
int val = stack[--(*sp)]; (*sp)--;
|
|
printf("%s\n", val ? "true" : "false");
|
|
return 1;
|
|
}
|
|
if (is(desc, "()V")) { (*sp)--; printf("\n"); return 1; }
|
|
}
|
|
if (is(name, "print")) {
|
|
if (is(desc, "(I)V")) {
|
|
int val = stack[--(*sp)]; (*sp)--;
|
|
printf("%d", val);
|
|
return 1;
|
|
}
|
|
if (is(desc, "(Ljava/lang/String;)V")) {
|
|
int str_idx = stack[--(*sp)]; (*sp)--;
|
|
CPEntry *e = &cf->cp[str_idx];
|
|
if (e->tag == CP_STRING)
|
|
printf("%s", cp_utf8(cf, e->cls_or_str.index));
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* java.lang.Thread (minimal stubs) */
|
|
|
|
if (is(class_name, "java/lang/Thread")) {
|
|
if (is(name, "sleep")) {
|
|
long long ms = stack[--(*sp)];
|
|
(*sp)--; /* long takes 2 slots */
|
|
SDL_Delay((uint32_t)ms);
|
|
return 1;
|
|
}
|
|
if (is(name, "start") || is(name, "join")) {
|
|
(*sp)--;
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
/* javax.microedition.midlet.MIDlet (common stubs) */
|
|
|
|
if (is(name, "getAppProperty") && is(desc, "(Ljava/lang/String;)Ljava/lang/String;")) {
|
|
/* this, key -> null */
|
|
(*sp) -= 2;
|
|
stack[(*sp)++] = 0;
|
|
return 1;
|
|
}
|
|
if (is(name, "notifyDestroyed") && is(desc, "()V")) {
|
|
(*sp)--; /* this */
|
|
return 1;
|
|
}
|
|
if (is(name, "notifyPaused") && is(desc, "()V")) {
|
|
(*sp)--; /* this */
|
|
return 1;
|
|
}
|
|
if (is(name, "platformRequest") && is(desc, "(Ljava/lang/String;)Z")) {
|
|
(*sp) -= 2; /* this, url */
|
|
stack[(*sp)++] = 0;
|
|
return 1;
|
|
}
|
|
if (is(name, "checkPermission") && is(desc, "(Ljava/lang/String;)I")) {
|
|
(*sp) -= 2; /* this, permission */
|
|
stack[(*sp)++] = 1; /* allowed */
|
|
return 1;
|
|
}
|
|
|
|
return 0; /* not handled */
|
|
} |