OVR: Add patch to make game render planets behind observer
[mercenary-reloaded.git] / src / mercenary / mercenary3.c
index ba9cba1..7fe20c2 100644 (file)
 #include <stdio.h>
 #include <stdint.h>
 #include <stdlib.h>
+#include <math.h>
+#include "../libsdl/print.h"
 #include "../libcpu/m68k.h"
+#include "../libcpu/m68kcpu.h"
+#include "../libcpu/execute.h"
 #include "mercenary.h"
 
+#define DS_0 0x530c
+#define DS_41 0x505E2
+#define DS_64 0x53d34
+#define DS_84 0x5D856
+
 #define        INITIAL_STACK   0x7fffa
 #define RESET_VECTOR   0x5a16c
 // #define RESET_VECTOR        0x5a1a4 /* strange reset vector that causes the player to fly around */
-const uint32_t mercenary_stop_at[] = {
-       0x5a826,        /* done with rendering, waiting for VBL */
-       0x55d8c,        /* after pressing 'HELP' key before showing menu line on benson */
-       0x56398,        /* waiting for menu command */
-       0x55d94,        /* after pressing 'HELP' key while showing menu line on benson */
-       0x563a6,        /* after pressing 'RETURN' while game waits for other key to resume */
-       0x52946,        /* after dying, waiting for VBL to fade out palette */
-       0x0 /* end */
-}; /* break points for rendering and continue with processing loop */
+
+/* interrupt CPU execution at special break points and tell emulation what to do */
+const struct cpu_stop mercenary_stop_at[] = {
+       { 0x5a826,      STOP_AT_WAIT_VBL },                     /* done with rendering, waiting for VBL */
+       { 0x55d8c,      STOP_AT_WAIT_INPUT },                   /* after pressing 'HELP' key before showing menu line on benson */
+       { 0x56398,      STOP_AT_WAIT_INPUT },                   /* waiting for menu command */
+       { 0x55d94,      STOP_AT_WAIT_INPUT },                   /* after pressing 'HELP' key while showing menu line on benson */
+       { 0x563a6,      STOP_AT_WAIT_INPUT },                   /* after pressing 'RETURN' while game waits for other key to resume */
+       { 0x52946,      STOP_AT_WAIT_VBL },                     /* after dying, waiting for VBL to fade out palette */
+       { 0x5A456,      STOP_AT_CLEAR_SCREEN1 },                /* clear the screen (here we know the color 8, this is wy we cannot do it earlier) */
+       { 0x5A48E,      STOP_AT_CLEAR_SCREEN1 },
+       { 0x5A4BA,      STOP_AT_CLEAR_SCREEN1 },
+       { 0x5A672,      STOP_AT_CLEAR_SCREEN1 },
+       { 0x5A6B0,      STOP_AT_CLEAR_SCREEN2 },                /* special case where we use color index 15 when we are flying (no raster split for ground color) */
+       { 0x5A770,      STOP_AT_CLEAR_SCREEN3 },                /* special case where we are in universe and do not have any ground */
+       { 0x56E00,      STOP_AT_DRAW_GROUND },                  /* the ground is rendered. the color index is captured at CLEAR_SCREEN */
+       { 0x5A4AA,      STOP_AT_DRAW_GROUND },                  /* at this point there is ground rendered, because game uses raster split for ground color, but we do render opengl */
+       { 0x53FE2,      STOP_AT_INFO_OBJECT_MOVING },           /* get ID and position of next object */
+       { 0x53E42,      STOP_AT_INFO_OBJECT_FIX },              /* next object is FIX (taxi/bus/intercity) */
+       { 0x53E8E,      STOP_AT_INFO_OBJECT_FIX },
+       { 0x54330,      STOP_AT_TAG_IS_OBJECT_1 },              /* indicates that the next tag is an object */
+       { 0x54334,      STOP_AT_TAG_IS_OBJECT_0 },              /* indicates that the tag of object was rendered */
+       { 0x542B2,      STOP_AT_COORD_OBJECT },                 /* object coordinates are ready */
+       { 0x5431a,      STOP_AT_POLY_OBJECT_M3 },               /* object polygon is rendered */
+       { 0x54342,      STOP_AT_LINE_OBJECT },                  /* object line is rendered */
+       { 0x54042,      STOP_AT_COORD_BEACON },                 /* beacon's point coordinates are ready */
+       { 0x54088,      STOP_AT_POINT_BEACON },                 /* becon point is rendered */
+       { 0x54848,      STOP_AT_COORD_BUILDING_EXTERIOR },      /* building (house) coordinates are ready */
+       { 0x548a4,      STOP_AT_POLY_BUILDING_EXTERIOR },       /* building polygons are rendered */
+       { 0x5489C,      STOP_AT_LINE_BUILDING_EXTERIOR },       /* lines of building, like radio tower on icarus */
+       { 0x5B810,      STOP_AT_COORD_BUILDING_INTERIOR },      /* building coordinates for interrior */
+       { 0x5C01E,      STOP_AT_POLY_BUILDING_INTERIOR1 },      /* floor of building will be rendered */
+       { 0x5BFc4,      STOP_AT_POLY_BUILDING_INTERIOR2 },      /* ceiling of building will be rendered */
+       { 0x5BF5a,      STOP_AT_POLY_BUILDING_INTERIOR3 },      /* ceiling of window/door will be rendered */
+       { 0x5BEE6,      STOP_AT_POLY_BUILDING_INTERIOR4 },      /* floor of window will be rendered */
+       { 0x5C0E4,      STOP_AT_POLY_BUILDING_INTERIOR1to4 },   /* ceiling/floor of building is rendered */
+       { 0x5BDFA,      STOP_AT_POLY_BUILDING_INTERIOR5 },      /* part above window/door will be rendered */
+       { 0x5BD90,      STOP_AT_POLY_BUILDING_INTERIOR6 },      /* part below window will be rendered */
+       { 0x5BEC4,      STOP_AT_POLY_BUILDING_INTERIOR5to6 },   /* part below/above window/door of building is rendered */
+       { 0x5C172,      STOP_AT_WALL_BUILDING },                /* a wall (between floor and ceiling/window/door) is rendered) */
+       { 0x4FB08,      STOP_AT_COORD_COMET },                  /* comet's coordinates are ready */
+       { 0x4FB3C,      STOP_AT_MATRIX_COMET },                 /* what rotation matrix to use */
+       { 0x4FB5E,      STOP_AT_POLY_COMET },                   /* comet's horizontal polygon (without culling no need to render 0x4FB62) */
+       { 0x4FB66,      STOP_AT_POLY_COMET },                   /* comet's vertival polygon (without culling no need to render 0x4FB6A) */
+       { 0x5573E,      STOP_AT_COORD_LINE_ROADS },             /* road's line coordinates are ready */
+       { 0x55780,      STOP_AT_LINE_ROADS },                   /* road's and ground surface's polygon */
+       { 0x557A6,      STOP_AT_LINE_ROADS },
+       { 0x557BC,      STOP_AT_LINE_ROADS },
+       { 0x57370,      STOP_AT_LINE_ROADS_CENTER },            /* center line of roads */
+       { 0x5731C,      STOP_AT_COORD_POLY_ROADS },             /* road's and ground surface's coordinates are ready */
+       { 0x57376,      STOP_AT_POLY_ROADS },                   /* road's and ground surface's polygon */
+       { 0x54BF4,      STOP_AT_COORD_TAGS },                   /* coordinates for tags, like key's marking are ready */
+       { 0x54BAC,      STOP_AT_COORD_TAGS2 },                  /* coordinates for large tags are ready, like bill's face */
+       { 0x54C40,      STOP_AT_LINE_TAGS1 },                   /* tag's line is rendered (new color D0) */
+       { 0x54C3A,      STOP_AT_LINE_TAGS2 },                   /* tag's line is rendered (use last color) */
+       { 0x54C54,      STOP_AT_POLY_TAGS1 },                   /* tag's polygon is rendered (new color D0) */
+       { 0x54C30,      STOP_AT_POLY_TAGS2 },                   /* tag's polygon is rendered (use last color) */
+       { 0x53A1E,      STOP_AT_COORD_PLANET },                 /* planet's coordinates are ready (viewed from ground) */
+       { 0x53A44,      STOP_AT_MATRIX_PLANET },                /* what rotation matrix to use */
+       { 0x53712,      STOP_AT_COORD_PLANET },                 /* planet's coordinates are ready (viewed from universe) */
+       { 0x537A4,      STOP_AT_MATRIX_PLANET },                /* what rotation matrix to use */
+       { 0x53A9A,      STOP_AT_DRAW_PLANET },                  /* planet's sphere is rendered, D0 is color (viewed from ground) */
+       { 0x53960,      STOP_AT_DRAW_PLANET },                  /* planet's sphere is rendered, D0 is color (viewed from universe) */
+       { 0x4FB8C,      STOP_AT_DRAW_COMET },                   /* comet's sphere is rendered */
+       { 0x50524,      STOP_AT_DRAW_STARS_SPACE },             /* stars are rendered (viewed from universe) */
+       { 0x5046A,      STOP_AT_DRAW_STARS_GROUND },            /* stars are rendered (viewed from ground) */
+       { 0x50342,      STOP_AT_DRAW_STARS_FLYING },            /* stars are rendered (viewed from planet when flying) */
+       { 0x501CA,      STOP_AT_DRAW_STARS_FLYING2 },           /* same as above, but stars when upside down (above zenit) */
+       { 0x57290,      STOP_AT_COORD_ISLANDS },                /* island's coordinates are ready */
+       { 0x572EE,      STOP_AT_POLY_ISLANDS },                 /* island's polygon is rendered */
+       { 0x572E8,      STOP_AT_LINE_ISLANDS },
+       { 0x5214E,      STOP_AT_DRAW_SIGHTS },                  /* when sights are rendered */
+       { 0x4F748,      STOP_AT_POLY_UKN2 },
+       { 0x54362,      STOP_AT_EXPLOSION },                    /* explosion debris */
+       { 0x4CA18,      STOP_AT_EXPLOSION },
+       { 0x53A6C,      STOP_AT_PATCH_RENDER },                 /* patch away planet check (behind observer) */
+       { 0x537B0,      STOP_AT_PATCH_RENDER },                 /* patch away planet check (behind observer) */
+       { 0x45806,      STOP_AT_PATCH_RENDER },                 /* patch away planet rendering (would crash without check above) */
+       { 0x0,          STOP_AT_END },                          /* end */
+};
 
 extern const uint32_t mercenary3_hex[];
 extern int mercenary3_hex_size;
@@ -51,6 +131,8 @@ void mercenary_load(void)
 
 void mercenary_patch(void)
 {
+       uint32_t address;
+
        /* initial stack */
        m68k_write_memory_32(0x00000, INITIAL_STACK);
 
@@ -64,25 +146,180 @@ void mercenary_patch(void)
        m68k_write_memory_16(0x5a17e, 0x4e71); /* nop */
        m68k_write_memory_16(0x5a180, 0x4e71); /* nop */
 
+       /* remove function that checks what stars are rendered when flying above plante
+        * instead of just rendering the necessary parts, both parts are always rendered:
+        * 1. stars from horizont up to zenith
+        * 2. stars from zenith up to horizon (upside down)
+        * we need that, so opengl rendering can use wider FOV without missing stars.
+        * the game will actually have no problem with it, except that it requires more cpu cycles
+        */
+       for (address = 0x502AE; address < 0x502D4; address += 2)
+               m68k_write_memory_16(address, 0x4e71); /* nop */
+
+//m68k_write_memory_16(0x53A42, 0x2C20);
+
+//m68k_write_memory_16(0x58388, 0x4E75); /* rts */
+//m68k_write_memory_16(0x4FE3E, 0x4E75); /* rts */
+
+//m68k_write_memory_16(0x53A6C, 0x6008); /* bra */
+//m68k_write_memory_16(0x537B0, 0x6008); /* bra */
+//m68k_write_memory_16(0x45806, 0x4E75); /* rts */
+
+//m68k_write_memory_16(0x54342, 0x4e71); /* nop */
+//m68k_write_memory_16(0x54344, 0x4e71); /* nop */
+//m68k_write_memory_16(0x5059e, 0x3f0); /* nop */
+#if 0
+m68k_write_memory_16(0x4FB5E, 0x4e71); /* nop */
+m68k_write_memory_16(0x4FB60, 0x4e71); /* nop */
+m68k_write_memory_16(0x4FB62, 0x4e71); /* nop */
+m68k_write_memory_16(0x4FB64, 0x4e71); /* nop */
+m68k_write_memory_16(0x4FB66, 0x4e71); /* nop */
+m68k_write_memory_16(0x4FB68, 0x4e71); /* nop */
+m68k_write_memory_16(0x4FB6a, 0x4e71); /* nop */
+m68k_write_memory_16(0x4FB6c, 0x4e71); /* nop */
+#endif
+
        /* remove wait for VBL */
        m68k_write_memory_16(0x5a82C, 0x4e71); /* nop */
 
        /* reduce loop that waits for disk stepper to move */
        if (m68k_read_memory_32(0x562f8) != 0x000091b0) {
-               fprintf(stderr, "expecting loop counter of 0x000091b0 here, please fix!\n");
+               print_error("expecting loop counter of 0x000091b0 here, please fix!\n");
                exit(0);
        }
        m68k_write_memory_32(0x562f8, 1);
        /* reduce loop that waits for disk side change */
        if (m68k_read_memory_32(0x55f5c) != 0x0000d020) {
-               fprintf(stderr, "expecting loop counter of 0x0000d020 here, please fix!\n");
+               print_error("expecting loop counter of 0x0000d020 here, please fix!\n");
                exit(0);
        }
        m68k_write_memory_32(0x55f5c, 1);
 }
 
-uint32_t mercenary_palette(void)
+/* skip certain parts when rendering improved graphics */
+void mercenary_patch_render(void)
+{
+       switch (REG_PC) {
+       case 0x53A6C: /* take that branch, so planets get rendered behind obersver */
+               REG_PC += 10;
+               break;
+       case 0x537B0: /* take that branch, so planets get rendered behind obersver */
+               REG_PC += 10;
+               break;
+       case 0x45806: /* just RTS to avoid crashing of rendering functions when planets are behind obersver */
+               REG_PC -= 2;
+               break;
+       }
+}
+
+uint32_t mercenary_palette_view(void)
 {
        return m68k_read_memory_32(0x0072b0);
 }
 
+uint32_t mercenary_palette_render(void)
+{
+       return m68k_read_memory_32(0x0072b4);
+}
+
+uint32_t mercenary_palette_predefined(void)
+{
+       return m68k_read_memory_32(0x0070a4);
+}
+
+uint32_t mercenary_palette_stars(void)
+{
+       return DS_41+66;
+}
+
+void mercenary_get_orientation(double *roll, double *pitch, double *yaw)
+{
+       int16_t r;
+
+       /* we could use 0x1e4*, but then "floating on the water" is not included */
+
+       /* get observer's tilt, pitch, yaw */
+       r = (int16_t)(m68k_read_memory_16(DS_0+0x1E26) & 0x3ff);
+       *roll = (double)r / 1024.0 * 2 * M_PI;
+       r = (int16_t)((m68k_read_memory_16(DS_0+0x1E28) + 0x201) & 0x3ff); /* add one extra to make view leveled to ground */
+       *pitch = -(double)r / 1024.0 * 2 * M_PI;
+       r = (int16_t)((m68k_read_memory_16(DS_0+0x1E2a) + 0x200) & 0x3ff);
+       *yaw = -(double)r / 1024.0 * 2 * M_PI;
+}
+
+void mercenary_get_orientation_raw(int16_t *pitch, uint16_t *yaw)
+{
+       *pitch = m68k_read_memory_16(DS_0+0x1E28);
+       *yaw = m68k_read_memory_16(DS_0+0x1E2a);
+}
+
+void mercenary_get_orientation_planet(double *inclination, double *azimuth)
+{
+       int16_t r;
+
+       /* get plant's inclination and rotation */
+       r = (int16_t)((m68k_read_memory_16(0x42C70)) & 0x3ff);
+       *inclination = (double)r / 1024.0 * 2 * M_PI;
+       r = (int16_t)((m68k_read_memory_16(0x42C6c)) & 0x3ff);
+       *azimuth = -(double)r / 1024.0 * 2 * M_PI;
+}
+
+void mercenary_get_location(int32_t *east, int32_t *height, int32_t *north)
+{
+       *east = (int32_t)m68k_read_memory_32(DS_0+0x1E1A);
+       *height = (int32_t)m68k_read_memory_32(DS_0+0x1DBA);
+       *north = (int32_t)m68k_read_memory_32(DS_0+0x1E22);
+}
+
+void mercenary_get_object_info(int *id, int32_t *east, int32_t *height, int32_t *north)
+{
+       *id = REG_A[0];
+       *east = (int32_t)m68k_read_memory_32(REG_A[0] + 22556);
+       *height = (int32_t)m68k_read_memory_32(REG_A[0] + 23580);
+       *north = (int32_t)m68k_read_memory_32(REG_A[0] + 24604);
+}
+
+void mercenary_coord_building_interior(int16_t *east, int32_t *height1, int32_t *height2, int32_t *height3, int32_t *height4, int16_t *north)
+{
+       *east = (int16_t)m68k_read_memory_16(5698+REG_A[0]) - (int16_t)REG_A[2];
+       *north = (int16_t)m68k_read_memory_16(6298+REG_A[0]) - (int16_t)REG_A[3];
+       *height1 = -(int16_t)m68k_read_memory_16(DS_0+0x1D30);
+       *height2 = (int16_t)m68k_read_memory_16(DS_0+0x1EB2) - (int16_t)m68k_read_memory_16(DS_0+0x1D30);
+       *height3 = (int16_t)m68k_read_memory_16(DS_0+0x1EB4) - (int16_t)m68k_read_memory_16(DS_0+0x1D30);
+       *height4 = (int16_t)m68k_read_memory_16(DS_0+0x1EB6) - (int16_t)m68k_read_memory_16(DS_0+0x1D30);
+
+}
+
+int mercenary_street_color_index(void)
+{
+       return (m68k_read_memory_16(DS_0+0x1f70) >> 5) & 0xf;
+}
+
+int mercenary_line_tags_index(void)
+{
+       return (m68k_read_memory_16(DS_0+0x1f70) >> 5) & 0xf;
+}
+
+uint16_t mercenary_poly_tags_color(void)
+{
+       return m68k_read_memory_16(DS_0+0x1e92);
+}
+
+int mercenary_background_index(void)
+{
+       return m68k_read_memory_16(DS_0+0x1E76) >> 2;
+}
+
+uint32_t mercenary_planet_scale_index(void)
+{
+       return 21584;
+}
+
+uint32_t mercenary_star_table(void)
+{
+       return DS_84+0x6A;
+}
+
+const char *mercenary_name = "Mercenary III - The Dion Crisis";
+const char *mercenary_gamesavesuffix = ".m3save";
+