+ return ioctl(fd, request, argp);
+}
+
+int emu_do_ioctl(int fd, int request, void *argp)
+{
+ if (fd == emu_interesting_fds[IFD_SOUND].fd)
+ return emu_sound_ioctl(fd, request, argp);
+
+ switch (fd) {
+ /* *********************** */
+ case FAKEDEV_FB0:
+ case FAKEDEV_FB1:
+ if (argp == NULL)
+ goto fail;
+
+ switch (request) {
+ case FBIOGET_FSCREENINFO: {
+ struct fb_fix_screeninfo *fix = argp;
+
+ memset(fix, 0, sizeof(*fix));
+ strcpy(fix->id, "mmsp2_RGB0");
+ fix->type = FB_TYPE_PACKED_PIXELS;
+ fix->accel = FB_ACCEL_NONE;
+ fix->visual = FB_VISUAL_TRUECOLOR;
+ fix->line_length = 320*2;
+ fix->smem_start = (fd == FAKEDEV_FB0) ? 0x03101000 : 0x03381000;
+ fix->smem_len = 320*240*2;
+ return 0;
+ }
+ case FBIOGET_VSCREENINFO: {
+ struct fb_var_screeninfo *var = argp;
+ static const struct fb_bitfield fbb_red = { offset: 11, length: 5, };
+ static const struct fb_bitfield fbb_green = { offset: 5, length: 6, };
+ static const struct fb_bitfield fbb_blue = { offset: 0, length: 5, };
+
+ memset(var, 0, sizeof(*var));
+ var->activate = FB_ACTIVATE_NOW;
+ var->xres =
+ var->xres_virtual = 320;
+ var->yres =
+ var->yres_virtual = 240;
+ var->width =
+ var->height = -1;
+ var->vmode = FB_VMODE_NONINTERLACED;
+ var->bits_per_pixel = 16;
+ var->red = fbb_red;
+ var->green = fbb_green;
+ var->blue = fbb_blue;
+ return 0;
+ }
+ case FBIOPUT_VSCREENINFO: {
+ struct fb_var_screeninfo *var = argp;
+ dbg(" put vscreen: %dx%d@%d\n", var->xres, var->yres, var->bits_per_pixel);
+ if (var->xres != 320 || var->yres != 240 || var->bits_per_pixel != 16)
+ return -1;
+ return 0;
+ }
+ }
+
+ /* *********************** */
+ case FAKEDEV_TTY0:
+ // fake tty0 to make GPH SDL happy
+ if (request == 0x4b46) // KDGKBENT
+ return -1;
+ return 0;
+ }
+
+fail:
+ err("bad/ni ioctl(%d, %08x, %p)\n", fd, request, argp);
+ errno = EINVAL;
+ return -1;
+}
+
+struct dev_fd_t emu_interesting_fds[] = {
+ [IFD_SOUND] = { "/dev/dsp", -1, emu_sound_open },
+ { NULL, 0, NULL },
+};
+
+static const struct {
+ const char *from;
+ const char *to;
+} path_map[] = {
+ { "/mnt/tmp/", "/tmp/" },
+};
+
+static const char *wrap_path(const char *path)
+{
+ char *buff;
+ size_t size;
+ int i, len;
+
+ // do only path mapping for now
+ for (i = 0; i < ARRAY_SIZE(path_map); i++) {
+ len = strlen(path_map[i].from);
+ if (strncmp(path, path_map[i].from, len) == 0) {
+ size = strlen(path) + strlen(path_map[i].to) + 1;
+ buff = malloc(size);
+ if (buff == NULL)
+ break;
+ snprintf(buff, size, "%s%s", path_map[i].to, path + len);
+ dbg("mapped path \"%s\" -> \"%s\"\n", path, buff);
+ return buff;
+ }
+ }
+
+ return path;
+}
+
+static void wrap_path_free(const char *w_path, const char *old_path)
+{
+ if (w_path != old_path)
+ free((void *)w_path);
+}
+
+void *emu_do_fopen(const char *path, const char *mode)
+{
+ const char *w_path;
+ FILE *ret;
+
+ w_path = wrap_path(path);
+ ret = fopen(w_path, mode);
+ wrap_path_free(w_path, path);
+
+ return ret;
+}
+
+// FIXME: threads..
+int emu_do_system(const char *command)
+{
+ static char tmp_path[512];
+ const char *p2;
+ char *p;
+ int ret;
+
+ if (command == NULL)
+ return -1;
+
+ // pass through stuff in PATH
+ p = strchr(command, ' ');
+ p2 = strchr(command, '/');
+ if (p2 == NULL || (p != NULL && p2 > p))
+ return system(command);
+
+ make_local_path(tmp_path, sizeof(tmp_path), "ginge_prep");
+ p = tmp_path + strlen(tmp_path);
+
+ p2 = wrap_path(command);
+ snprintf(p, sizeof(tmp_path) - (p - tmp_path), " --nomenu %s", p2);
+ wrap_path_free(p2, command);
+
+ dbg("system: \"%s\"\n", tmp_path);
+
+ // the app might want the screen too..
+ fb_thread_pause();
+ ret = system(tmp_path);
+ fb_thread_resume();
+ return ret;
+}
+
+int emu_do_execve(const char *filename, char *const argv[], char *const envp[])
+{
+ const char **new_argv;
+ char *prep_path;
+ int i, ret, argc;
+
+ if (filename == NULL)
+ return -1;
+
+ if (strstr(filename, "/gp2xmenu") != NULL)
+ exit(0);
+
+ for (i = 0; argv[i] != NULL; i++)
+ ;
+ argc = i + 1;
+
+ new_argv = calloc(argc + 2, sizeof(new_argv[0]));
+ if (new_argv == NULL)
+ return -1;
+
+ prep_path = malloc(512);
+ if (prep_path == NULL)
+ return -1;
+
+ make_local_path(prep_path, 512, "ginge_prep");
+ new_argv[0] = prep_path;
+ new_argv[1] = "--nomenu";
+ new_argv[2] = wrap_path(filename);
+
+ if (argv[0] != NULL)
+ for (i = 1; argv[i] != NULL; i++)
+ new_argv[i + 2] = argv[i];
+
+ dbg("execve \"%s\" %s \"%s\"\n", new_argv[0], new_argv[1], new_argv[2]);
+ ret = execve(new_argv[0], (char **)new_argv, envp);
+ perror("execve");
+ return ret;