+
+static int shift = 0, ctrl = 0, alt = 0;
+
+static void keyboard_sdl(int down, enum keycode keycode)
+{
+ switch (keycode) {
+ case KEYCODE_LCTRL:
+ case KEYCODE_RCTRL:
+ ctrl = down;
+ break;
+ default: break;
+ }
+
+ if (ctrl && down) {
+ switch (keycode) {
+ case KEYCODE_h:
+ toggle_help();
+ break;
+ case KEYCODE_v:
+ config_video_filter ^= 1;
+ osd_info("video filter", (config_video_filter) ? "on" : "off");
+ break;
+ case KEYCODE_a:
+ config_audio_filter ^= 1;
+ osd_info("audio filter", (config_audio_filter) ? "on" : "off");
+ break;
+ case KEYCODE_s:
+ config_amiga_speed ^= 1;
+ osd_info("render speed", (config_amiga_speed) ? "original" : "fast");
+ break;
+ case KEYCODE_r:
+ config_render ^= 1;
+ osd_info("render mode", (config_render) ? "OpenGL" : "original");
+ break;
+ case KEYCODE_b:
+ if (!config_render && !config_debug_opengl) {
+ osd_info("", "not applicable");
+ break;
+ }
+ if (config_benson_size == 0.5) {
+ config_benson_size = 1.0;
+ config_fov = FOV_NOVAGEN;
+ } else {
+ config_benson_size = 0.5;
+ config_fov = FOV_JOLLY;
+ }
+ osd_info("Benson size", (config_benson_size == 0.5) ? "half" : "normal");
+ border_timer = ticks_sdl() + 1500;
+ break;
+ case KEYCODE_i:
+ if (!intro_skipped)
+ skip_intro();
+ else
+ osd_info("", "not applicable");
+ break;
+ case KEYCODE_1:
+ if (get_mission_disk(1, 1)) { /* just to check support */
+ mission_disk = 1;
+ osd_info("mission disk", "insert 1");
+ } else
+ osd_info("", "not applicable");
+ break;
+ case KEYCODE_2:
+ if (get_mission_disk(2, 1)) { /* just to check support */
+ mission_disk = 2;
+ osd_info("mission disk", "insert 2");
+ } else
+ osd_info("", "not applicable");
+ break;
+ case KEYCODE_0:
+ if (get_mission_disk(1, 1)) { /* just to check support */
+ mission_disk = 0;
+ osd_info("mission disk", "removed");
+ } else
+ osd_info("", "not applicable");
+ break;
+ case KEYCODE_c:
+ if (config_ctrl_c)
+ quit = 1;
+ break;
+ case KEYCODE_o:
+#ifdef HAVE_OVR
+ reset_observer_ovr();
+ osd_info("", "reset observer");
+#else
+ osd_info("", "not applicable");
+#endif
+ break;
+ case KEYCODE_KP_PLUS:
+#ifdef HAVE_OVR
+ osd_info("", "not applicable");
+#else
+ if (config_fov / 1.2 >= FOV_MIN)
+ config_fov /= 1.2;
+ disp_fov:
+ {
+ char text[16];
+ if (config_fov < 63.5 || config_fov > 64.5)
+ sprintf(text, "%.2f", config_fov);
+ else
+ sprintf(text, "%.2f (default)", config_fov);
+ osd_info("FOV", text);
+ border_timer = ticks_sdl() + 1500;
+ }
+#endif
+ break;
+ case KEYCODE_KP_MINUS:
+#ifdef HAVE_OVR
+ osd_info("", "not applicable");
+#else
+ if (config_fov * 1.2 <= FOV_MAX)
+ config_fov *= 1.2;
+ goto disp_fov;
+#endif
+ break;
+ default: break;
+ }
+ /* do not pass keys to game while holding CTRL */
+ return;
+ }
+
+ if (help_view == 1 && down) {
+ help_view = 2;
+ return;
+ }
+
+ if (keycode == KEYCODE_PAUSE && down) {
+ toggle_help();
+ return;
+ }
+
+ /* in help view we must not forward keypresses */
+ if (help_view)
+ return;
+
+ switch (keycode) {
+ case KEYCODE_LSHIFT:
+ set_amiga_key(keycode, down);
+ shift = down;
+ break;
+ case KEYCODE_RSHIFT:
+ set_amiga_key(keycode, down);
+ shift = down;
+ break;
+ case KEYCODE_LALT:
+ set_amiga_key(keycode, down);
+ alt = down;
+ break;
+ case KEYCODE_RALT:
+ set_amiga_key(keycode, down);
+ alt = down;
+ break;
+ case KEYCODE_LEFT:
+ if (shift && down) {
+ set_amiga_key(keycode, down);
+ set_joystick(-1, -1, -1, -1, -1);
+ break;
+ }
+ set_amiga_key(keycode, 0);
+ set_joystick(down, -1, -1, -1, -1);
+ break;
+ case KEYCODE_RIGHT:
+ if (shift && down) {
+ set_amiga_key(keycode, down);
+ set_joystick(-1, -1, -1, -1, -1);
+ break;
+ }
+ set_amiga_key(keycode, 0);
+ set_joystick(-1, down, -1, -1, -1);
+ break;
+ case KEYCODE_UP:
+ if (shift && down) {
+ set_amiga_key(keycode, down);
+ set_joystick(-1, -1, -1, -1, -1);
+ break;
+ }
+ set_amiga_key(keycode, 0);
+ if (!alt || down)
+ set_joystick(-1, -1, down, 0, -1);
+ break;
+ case KEYCODE_DOWN:
+ if (shift && down) {
+ set_amiga_key(keycode, down);
+ set_joystick(-1, -1, -1, -1, -1);
+ break;
+ }
+ set_amiga_key(keycode, 0);
+ if (!alt || down)
+ set_joystick(-1, -1, 0, down, -1);
+ break;
+ case KEYCODE_END:
+ set_joystick(-1, -1, -1, -1, down);
+ break;
+ case KEYCODE_INSERT:
+ set_amiga_key(KEYCODE_HELP, down);
+ break;
+ default:
+ /* reset vr thrust */
+#ifdef HAVE_OVR
+ thrust_last = 0;
+#endif
+ set_amiga_key(keycode, down);
+ }
+}
+
+static double joystick_last_x = 0, joystick_last_y = 0;
+static int joystick_last_fire = 0;
+
+static void joystick_sdl(double x, double y, int fire)
+{
+ int left = -1, right = -1, up = -1, down = -1;
+
+ if (x <= -0.5 && joystick_last_x > -0.5)
+ left = 1;
+ if (x > -0.5 && joystick_last_x <= -0.5)
+ left = 0;
+ if (x >= 0.5 && joystick_last_x < 0.5)
+ right = 1;
+ if (x < 0.5 && joystick_last_x >= 0.5)
+ right = 0;
+ if (y <= -0.5 && joystick_last_y > -0.5)
+ up = 1;
+ if (y > -0.5 && joystick_last_y <= -0.5)
+ up = 0;
+ if (y >= 0.5 && joystick_last_y < 0.5)
+ down = 1;
+ if (y < 0.5 && joystick_last_y >= 0.5)
+ down = 0;
+ set_joystick(left, right, up, down, fire);
+ joystick_last_x = x;
+ joystick_last_y = y;
+ joystick_last_fire = fire;
+}
+
+void audio_sdl(float *data, int length)
+{
+ int fill, s;
+
+ /* read sound from sound buffer
+ * buffer pointer read and write is atomic, so no locking required!
+ */
+ fill = (sound_buffer_writep - sound_buffer_readp + sound_buffer_size) % sound_buffer_size;
+ if (fill < length) {
+#ifdef DEBUG_SOUND_BUFFERING
+ fprintf(stderr, "sound buffer underrun\n");
+#endif
+ /* correct read pointer as if the buffer would have 'length' of samples stored inside */
+ sound_buffer_readp = (sound_buffer_readp + fill - length + sound_buffer_size) % sound_buffer_size;
+ }
+ for (s = 0; s < length; s++) {
+ *data++ = sound_buffer[(sound_buffer_readp + s) % sound_buffer_size].left;
+ *data++ = sound_buffer[(sound_buffer_readp + s) % sound_buffer_size].right;
+ }
+#ifdef DEBUG_SOUND_BUFFERING
+ printf("fill %d = %.4f\n", length, sound_buffer[sound_buffer_readp][0]);
+#endif
+ sound_buffer_readp =(sound_buffer_readp + length) % sound_buffer_size;
+}
+
+/* used for geier counter */
+void sound_irq(void)
+{
+ /* trigger and execute IRQ 4 = sound */
+ execute_cpu(4, NULL);
+}
+
+extern uint8_t mercenary_splash_palette[][3];
+extern char mercenary_splash_pixels[];
+
+int main(int argc, char *argv[])
+{
+ int rc;
+ int sdl_sound_chunk;
+
+ home_dir = getenv("HOME");
+ if (!home_dir)
+ home_dir = "";
+
+ rc = parse_args(argc, argv);
+ if (rc)
+ return 0;
+
+ /* allocate image */
+ image = calloc(IMAGE_WIDTH * IMAGE_HEIGHT * ((double_pixel_size) ? 4 : 1), 3);
+ if (!image) {
+ print_error("Failed to allocate image buffer\n");
+ goto done;
+ }
+
+ if ((SOUND_SAMPLERATE % IRQ_RATE)) {
+ print_error("Sample rate must be a multiple of IRQ rate, please fix!\n");
+ goto done;
+ }
+ if ((1000 % IRQ_RATE)) {
+ print_error("1000 (Ticks per second) rate must be a multiple of IRQ rate, please fix!\n");
+ goto done;
+ }
+
+ /* calculate SDL chunk size for audio
+ * it must be a power of two, but not more than the chunk size for each IRQ!
+ */
+ for (sdl_sound_chunk = 2; sdl_sound_chunk <= (SOUND_SAMPLERATE / IRQ_RATE); sdl_sound_chunk <<= 1)
+ ;
+ sdl_sound_chunk >>= 1;
+// printf("samples per IRQ = %d, samples per SDL audio = %d\n", SOUND_SAMPLERATE / IRQ_RATE, sdl_sound_chunk); exit(0);
+
+ /* allocate sound buffers */
+ sound_buffer_size = SOUND_SAMPLERATE / IRQ_RATE * SOUND_CHUNKS;
+ sound_buffer = calloc(sound_buffer_size, sizeof(*sound_buffer));
+ if (!sound_buffer) {
+ print_error("Failed to allocate image buffer\n");
+ goto done;
+ }
+
+ /* allocate memory */
+ memory = calloc(MEMORY_SIZE, 1);
+ if (!memory) {
+ print_error("Failed to allocate cpu's memory\n");
+ goto done;
+ }
+ stop_event = calloc(MEMORY_SIZE, 1);
+ if (!stop_event) {
+ print_error("Failed to allocate cpu's stop_event memory\n");
+ goto done;
+ }
+ chipreg = calloc(IOSIZE, 1);
+ if (!chipreg) {
+ print_error("Failed to allocate chip register\n");
+ goto done;
+ }
+
+ /* init cpu code */
+ execute_init(MEMORY_SIZE, memory, stop_event, chipreg, io_read, io_write, mercenary_stop_at);
+
+ /* init disk emulation */
+ disk_init(disk_read, disk_write);
+
+ /* load binary */
+ mercenary_load();
+
+ /* patch some stuff */
+ mercenary_patch();
+
+ /* init SDL and OpenGL */
+#ifdef HAVE_OVR
+ int vbl_sync = 0;
+ int vr = 1;
+ int multisampling = 0;
+ window_width = SCREEN_WIDTH;
+ window_height = SCREEN_HEIGHT;
+#else
+ int vbl_sync = 1;
+ int vr = 0;
+ int multisampling = config_multisampling;
+ window_width = (config_debug_opengl) ? SCREEN_WIDTH / 3 * 2 : SCREEN_WIDTH;
+ window_height = (config_debug_opengl) ? SCREEN_HEIGHT / 3 * 4 : SCREEN_HEIGHT;
+#endif
+ rc = init_sdl(mercenary_name, window_width, window_height, SOUND_SAMPLERATE, sdl_sound_chunk, keyboard_sdl, config_joystick, config_joystick_x, config_joystick_y, config_joystick_fire, joystick_sdl, audio_sdl, resize_window, multisampling, vbl_sync, vr);
+ if (rc < 0)
+ goto done;
+#ifdef HAVE_OVR
+ rc = init_ovr(config_multisampling);
+ if (rc < 0)
+ goto done;
+ rc = init_vr_keyboard(config_keyboard_height, config_keyboard_distance);
+ if (rc < 0)
+ goto done;
+#endif
+ rc = init_opengl_image((double_pixel_size) ? IMAGE_WIDTH * 2 : IMAGE_WIDTH, (double_pixel_size) ? IMAGE_HEIGHT * 2 : IMAGE_HEIGHT);
+ if (rc < 0)
+ goto done;
+
+ /* init osd */
+ rc = init_opengl_osd(0, IMAGE_WIDTH * 2, IMAGE_HEIGHT * 2);
+ if (rc < 0)
+ goto done;
+ rc = init_opengl_osd(1, OSD_WIDTH, OSD_HEIGHT);
+ if (rc < 0)
+ goto done;
+ help_osd[0] = text_alloc(IMAGE_WIDTH * 2, IMAGE_HEIGHT * 2, 0x00);
+ if (!help_osd[0])
+ goto done;
+ text_insert_image(help_osd[0], mercenary_splash_palette, mercenary_splash_pixels, 0xff);
+ help_views++;
+ help_osd[1] = text_alloc(IMAGE_WIDTH * 2, IMAGE_HEIGHT * 2, HELP_ALPHA);
+ if (!help_osd[1])
+ goto done;
+ text_render(help_osd[1], IMAGE_WIDTH * 2, IMAGE_HEIGHT * 2, mercenary_name, HELP_ALPHA, 3, (double)(80 - strlen(mercenary_name)) / 2.0, 1, 1);
+ text_render(help_osd[1], IMAGE_WIDTH * 2, IMAGE_HEIGHT * 2,
+ "Emulation:\n"
+ " Press `PAUSE' to toggle this help screen on or off.\n"
+ " Press `CTRL' + `F' to toggle between full screen / window mode.\n"
+ " Press `CTRL' + `R' to toggle between original / OpenGL rendering.\n"
+ " Press `CTRL' + `S' to toggle between original / fast rendering.\n"
+ " Press `CTRL' + `B' to toggle between large / small Benson.\n"
+ " Press `CTRL' + `V' to toggle video filter on / off.\n"
+ " Press `CTRL' + `A' to toggle audio filter on / off.\n"
+ " Press `CTRL' + `+' or `-' to change field-of-view (OpenGL).\n"
+ " Press `CTRL' + `I' to skip intro (approaching to Eris).\n"
+ " Press `CTRL' + `1' or `2' to insert or `0' to remove mission disk.\n"
+#ifdef HAVE_OVR
+ " Press `CTRL' + `O' to reset observer position.\n"
+#endif
+ "\n"
+ "Answer to a Question:\n"
+ " Press `O' (not Zero) for OK and other key for NO.\n"
+ "\n"
+ "Walking / Driving / Flying:\n"
+ " Use cursor keys as joystick, to move player / craft.\n"
+ " Press `R' for running, 'W' for walking speed.\n"
+ " Press `B' to board, `L' to leave.\n"
+ " Press `1'...`0' to drive / fly (slow...fast), `+' or `-' to adjust.\n"
+ " Press `F1'...`F0' to drive / fly backwards (slow...fast).\n"
+ " Press `SPACE' to stop craft.\n"
+ " Press `ESCAPE' to activate escape sequence on crafts.\n"
+ " Press `T' for turbo on certain craft.\n"
+ " Press `END' as joystick's fire button.\n"
+ "\n"
+ "Elevator:\n"
+ " Press `1'...`9' to select floor, 'G' for ground, `B' for basement.\n"
+ "\n"
+ "Inside a transporter:\n"
+ " Press `1'...`0' to select destination.\n"
+ "\n"
+ "Items:\n"
+ " Press `SHFIT' + cursor left / right keys to choose item.\n"
+ " Press `SHFIT' + cursor up / down keys to pickup / drop item.\n"
+ " Press `NUMPAD Enter' to select certain items.\n"
+ " Press `NUMPAD +' / `NUMPAD -' to select entries.\n"
+ " Press `NUMPAD *' to read entry\n"
+ "Saving / Loading / Pause:\n"
+ " Press `INSERT' to loading and saving options.\n"
+ " Press `ENTER' to pause game, other key to continue.\n"
+ ,HELP_ALPHA, 1, 2, 5, 0);
+ help_views++;
+#ifdef HAVE_OVR
+ help_osd[2] = text_alloc(IMAGE_WIDTH * 2, IMAGE_HEIGHT * 2, HELP_ALPHA);
+ if (!help_osd[2])
+ goto done;
+ text_render(help_osd[2], IMAGE_WIDTH * 2, IMAGE_HEIGHT * 2, mercenary_name, HELP_ALPHA, 3, (double)(80 - strlen(mercenary_name)) / 2.0, 1, 1);
+ text_render(help_osd[2], IMAGE_WIDTH * 2, IMAGE_HEIGHT * 2,
+ "Emulation using Controller:\n"
+ " Press `A' or 'X' button to toggle this help screen on or off.\n"
+ " Press 'B' or 'Y' button to reset observer position. (importaint!)\n"
+ " To have a virtual keyboard, point right hand below control pannel.\n"
+ " Point to a key on keyboard. The key will highlight.\n"
+ " Pull `Trigger' on right controller to enter the highlighted key.\n"
+ "\n"
+ "Walking / Driving / Flying using Controller:\n"
+ " Use thumb stick on right controller, to move player / craft.\n"
+ " Move thumb stick forth and back to walk, left and right to rotate.\n"
+ " Pull `Trigger' on right controller to change orientation.\n"
+ " Press `X' button to board, `Y' button to leave.\n"
+ " Move thumb stick on left controller to drive/fly forward or backward.\n"
+ " Pull `Trigger' on left controller stop craft.\n"
+ " Press thumb stick on left controller for escape sequence and stop it.\n"
+ " Pull `Trigger' on right controller to fire.\n"
+ "\n"
+ "For all other game function, use the virtual or real keyboard!\n"
+ ,HELP_ALPHA, 1, 2, 5, 0);
+ help_views++;
+#endif
+ info_osd = text_alloc(OSD_WIDTH, OSD_HEIGHT, 0x00);
+ if (!info_osd)
+ goto done;
+
+ /* init audio */
+ sound_init(SOUND_SAMPLERATE, sound_irq);
+
+ /* start cpu */
+ reset_cpu();
+
+ if (config_skip_intro)
+ skip_intro();
+
+ /* run game */
+ main_loop();
+
+done:
+ exit_opengl();
+#ifdef HAVE_OVR
+ exit_ovr();
+#endif
+ exit_sdl();
+
+ if (chipreg)
+ free(chipreg);
+ if (stop_event)
+ free(stop_event);
+ if (memory)
+ free(memory);
+ if (sound_buffer)
+ free(sound_buffer);
+ if (image)
+ free(image);
+ while (help_views) {
+ help_views--;
+ if (help_osd[help_views])
+ free(help_osd[help_views]);
+ }
+ if (info_osd)
+ free(info_osd);
+
+ return 0;
+}
+