Render game graphics using OpenGL
[mercenary-reloaded.git] / src / mercenary / mercenary2.c
1 /* game specials
2  *
3  * (C) 2018 by Andreas Eversberg <jolly@eversberg.eu>
4  * All Rights Reserved
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <stdio.h>
21 #include <stdint.h>
22 #include <stdlib.h>
23 #include <math.h>
24 #include "../libsdl/print.h"
25 #include "../libcpu/m68k.h"
26 #include "../libcpu/m68kcpu.h"
27 #include "../libcpu/execute.h"
28 #include "mercenary.h"
29
30 #define INITIAL_STACK   0x7fffa
31 #define RESET_VECTOR    0x59484
32
33 /* interrupt CPU execution at special break points and tell emulation what to do */
34 const struct cpu_stop mercenary_stop_at[] = {
35         { 0x59a4e,      STOP_AT_WAIT_VBL },                     /* done with rendering, waiting for VBL */
36         { 0x54c26,      STOP_AT_WAIT_VBL },                     /* after pressing 'HELP' key before showing menu line on benson */
37         { 0x55438,      STOP_AT_WAIT_VBL },                     /* waiting for menu command */
38         { 0x54c2e,      STOP_AT_WAIT_VBL },                     /* after pressing 'HELP' key while showing menu line on benson */
39         { 0x55446,      STOP_AT_WAIT_VBL },                     /* after pressing 'RETURN' while game waits for other key to resume */
40         { 0x51620,      STOP_AT_WAIT_VBL },                     /* after dying, waiting for VBL to fade out palette */
41         { 0x596BC,      STOP_AT_CLEAR_SCREEN1 },                /* clear the screen (here we know the color 8, this is wy we cannot do it earlier) */
42         { 0x596F4,      STOP_AT_CLEAR_SCREEN1 },
43         { 0x59720,      STOP_AT_CLEAR_SCREEN1 },
44         { 0x598A8,      STOP_AT_CLEAR_SCREEN1 },
45         { 0x598E6,      STOP_AT_CLEAR_SCREEN2 },                /* special case where we use color index 15 when we are flying (no raster split for ground color) */
46         { 0x59982,      STOP_AT_CLEAR_SCREEN3 },                /* special case where we are in universe and do not have any ground */
47         { 0x55F2E,      STOP_AT_DRAW_GROUND },                  /* the ground is rendered. the color index is captured at CLEAR_SCREEN */
48         { 0x59710,      STOP_AT_DRAW_GROUND },                  /* at this point there is ground rendered, because game uses raster split for ground color, but we do render opengl */
49         { 0x5346E,      STOP_AT_COORD_OBJECT },                 /* object coordinates are ready */
50         { 0x534F6,      STOP_AT_POLY_OBJECT_M2 },               /* object polygon is rendered */
51         { 0x534F0,      STOP_AT_LINE_OBJECT },                  /* object line is rendered */
52         { 0x5324A,      STOP_AT_COORD_BEACON },                 /* beacon's point coordinates are ready */
53         { 0x53284,      STOP_AT_POINT_BEACON },                 /* becon point is rendered */
54         { 0x53A00,      STOP_AT_COORD_BUILDING_EXTERIOR },      /* building (house) coordinates are ready */
55         { 0x53A5C,      STOP_AT_POLY_BUILDING_EXTERIOR },       /* building polygons are rendered */
56         { 0x53A54,      STOP_AT_LINE_BUILDING_EXTERIOR },       /* lines of building, like radio tower on icarus */
57         { 0x5AA10,      STOP_AT_COORD_BUILDING_INTERIOR },      /* building coordinates for interrior */
58         { 0x5B218,      STOP_AT_POLY_BUILDING_INTERIOR1 },      /* floor of building will be rendered */
59         { 0x5B1BE,      STOP_AT_POLY_BUILDING_INTERIOR2 },      /* ceiling of building will be rendered */
60         { 0x5B154,      STOP_AT_POLY_BUILDING_INTERIOR3 },      /* ceiling of window/door will be rendered */
61         { 0x5B0E0,      STOP_AT_POLY_BUILDING_INTERIOR4 },      /* floor of window will be rendered */
62         { 0x5B2DE,      STOP_AT_POLY_BUILDING_INTERIOR1to4 },   /* ceiling/floor of building is rendered */
63         { 0x5AFF4,      STOP_AT_POLY_BUILDING_INTERIOR5 },      /* part above window/door will be rendered */
64         { 0x5AF8A,      STOP_AT_POLY_BUILDING_INTERIOR6 },      /* part below window will be rendered */
65         { 0x5B0BE,      STOP_AT_POLY_BUILDING_INTERIOR5to6 },   /* part below/above window/door of building is rendered */
66         { 0x5B36C,      STOP_AT_WALL_BUILDING },                /* a wall (between floor and ceiling/window/door) is rendered) */
67         { 0x4F462,      STOP_AT_COORD_COMET },                  /* comet's coordinates are ready */
68         { 0x4F496,      STOP_AT_MATRIX_COMET },                 /* what rotation matrix to use */
69         { 0x4F4B8,      STOP_AT_POLY_COMET },                   /* comet's horizontal polygon (without culling no need to render 0x4F4BC) */
70         { 0x4F4C0,      STOP_AT_POLY_COMET },                   /* comet's vertival polygon (without culling no need to render 0x4F4C4) */
71         { 0x54634,      STOP_AT_COORD_LINE_ROADS },             /* road's line coordinates are ready */
72         { 0x54676,      STOP_AT_LINE_ROADS },                   /* road's and ground surface's polygon */
73         { 0x5469C,      STOP_AT_LINE_ROADS },
74         { 0x546B2,      STOP_AT_LINE_ROADS },
75         { 0x56496,      STOP_AT_LINE_ROADS_CENTER },            /* center line of roads */
76         { 0x56442,      STOP_AT_COORD_POLY_ROADS },             /* road's and ground surface's coordinates are ready */
77         { 0x5649C,      STOP_AT_POLY_ROADS },                   /* road's and ground surface's polygon */
78         { 0x53CB6,      STOP_AT_COORD_TAGS },                   /* coordinates for tags, like key's marking are ready */
79         /* note: there are no coordinates for large tags in this game (no faces) */
80         /* note: there are no STOP_AT_LINE_TAGS1 and STOP_AT_POLY_TAGS1 in this game(given color) */
81         { 0x53CFA,      STOP_AT_LINE_TAGS2 },                   /* tag's line is rendered (use last color) */
82         { 0x53CF0,      STOP_AT_POLY_TAGS2 },                   /* tag's polygon is rendered (use last color) */
83         { 0x52BF4,      STOP_AT_COORD_PLANET },                 /* planet's coordinates are ready (viewed from ground) */
84         { 0x52C1A,      STOP_AT_MATRIX_COMET },                 /* what rotation matrix to use */
85         { 0x52756,      STOP_AT_COORD_PLANET },                 /* planet's coordinates are ready (viewed from universe) */
86         { 0x5277C,      STOP_AT_MATRIX_COMET },                 /* what rotation matrix to use */
87         { 0x52C70,      STOP_AT_DRAW_PLANET },                  /* planet's sphere is rendered, D0 is color (viewed from ground) */
88         { 0x52B36,      STOP_AT_DRAW_PLANET },                  /* planet's sphere is rendered, D0 is color (viewed from universe) */
89         { 0x4F4E6,      STOP_AT_DRAW_COMET },                   /* comet's sphere is rendered */
90         { 0x50006,      STOP_AT_DRAW_STARS_SPACE },             /* stars are rendered (viewed from universe) */
91         { 0x4FF4C,      STOP_AT_DRAW_STARS_GROUND },            /* stars are rendered (viewed from ground) */
92         { 0x4FE24,      STOP_AT_DRAW_STARS_FLYING },            /* stars are rendered (viewed from planet when flying) */
93         { 0x4FCAC,      STOP_AT_DRAW_STARS_FLYING2 },           /* same as above, but stars when upside down (above zenit) */
94         { 0x50FC2,      STOP_AT_DRAW_STARS_INTERSTELLAR },      /* interstellar star flight */
95         { 0x50BF4,      STOP_AT_DRAW_SUN_INTERSTELLAR },        /* draw sun dot while flying interstellar */
96         { 0x50BB8,      STOP_AT_CLEAR_SCREEN3 },                /* clear while flying interstellar */
97         { 0x563B8,      STOP_AT_COORD_ISLANDS },                /* island's coordinates are ready */
98         { 0x56416,      STOP_AT_POLY_ISLANDS },                 /* island's polygon is rendered */
99         { 0x56410,      STOP_AT_LINE_ISLANDS },
100         { 0x511FE,      STOP_AT_DRAW_SIGHTS },                  /* when sights are rendered */
101         { 0x0,          STOP_AT_END },                          /* end */
102 };
103
104 extern const uint32_t mercenary2_hex[];
105 extern int mercenary2_hex_size;
106
107 void mercenary_load(void)
108 {
109         int i;
110
111         /* load game binary from constant to volatile memory */
112         for (i = 0; i < mercenary2_hex_size; i += 4) {
113                 m68k_write_memory_32(i, mercenary2_hex[i / 4]);
114         }
115 }
116
117 void mercenary_patch(void)
118 {
119         uint32_t address;
120
121         /* initial stack */
122         m68k_write_memory_32(0x00000, INITIAL_STACK);
123
124         /* reset vector */
125         m68k_write_memory_32(0x00004, RESET_VECTOR);
126
127         /* remove function that checks what stars are rendered when flying above plante
128          * instead of just rendering the necessary parts, both parts are always rendered:
129          * 1. stars from horizont up to zenith
130          * 2. stars from zenith up to horizon (upside down)
131          * we need that, so opengl rendering can use wider FOV without missing stars.
132          * the game will actually have no problem with it, except that it requires more cpu cycles
133          */
134         for (address = 0x4FD90; address < 0x4FDB6; address += 2)
135                 m68k_write_memory_16(address, 0x4e71); /* nop */
136
137         /* remove wait for VBL */
138         m68k_write_memory_16(0x59a54, 0x4e71); /* nop */
139
140         /* reduce loop that waits for disk stepper to move */
141         if (m68k_read_memory_32(0x55398) != 0x0000091b) {
142                 print_error("expecting loop counter of 0x0000091b here, please fix!\n");
143                 exit(0);
144         }
145         m68k_write_memory_32(0x55398, 1);
146         /* reduce loop that waits for disk side change */
147         if (m68k_read_memory_32(0x54ffc) != 0x00000d02) {
148                 print_error("expecting loop counter of 0x00000d02 here, please fix!\n");
149                 exit(0);
150         }
151         m68k_write_memory_32(0x54ffc, 1);
152 }
153
154 uint32_t mercenary_palette_view(void)
155 {
156         return m68k_read_memory_32(0x007c14);
157 }
158
159 uint32_t mercenary_palette_render(void)
160 {
161         return m68k_read_memory_32(0x007c18);
162 }
163
164 uint32_t mercenary_palette_predefined(void)
165 {
166         return m68k_read_memory_32(0x007a0e);
167 }
168
169 uint32_t mercenary_palette_stars(void)
170 {
171         return 0x500C4+66;
172 }
173
174 void mercenary_get_orientation(double *roll, double *pitch, double *yaw)
175 {
176         int16_t r;
177
178         /* get observer's tilt, pitch, yaw */
179         r = (int16_t)(m68k_read_memory_16(0x007A9E) & 0x3ff);
180         *roll = (double)r / 1024.0 * 2 * M_PI;
181         r = (int16_t)((m68k_read_memory_16(0x007AA0) + 0x201) & 0x3ff); /* add one extra to make view leveled to ground */
182         *pitch = -(double)r / 1024.0 * 2 * M_PI;
183         r = (int16_t)((m68k_read_memory_16(0x007AA2) + 0x200) & 0x3ff);
184         *yaw = -(double)r / 1024.0 * 2 * M_PI;
185 }
186
187 void mercenary_get_orientation_raw(int16_t *pitch, uint16_t *yaw)
188 {
189         *pitch = m68k_read_memory_16(0x007AA0);
190         *yaw = m68k_read_memory_16(0x007AA2);
191 }
192
193 void mercenary_get_orientation_planet(double *inclination, double *rotation)
194 {
195         int16_t r;
196
197         /* get plant's inclination and rotation */
198         r = (int16_t)((m68k_read_memory_16(0x42C70)) & 0x3ff);
199         *inclination = (double)r / 1024.0 * 2 * M_PI;
200         r = (int16_t)((m68k_read_memory_16(0x42C6c)) & 0x3ff);
201         *rotation = -(double)r / 1024.0 * 2 * M_PI;
202 }
203
204 void mercenary_coord_building_interior(int16_t *east, int32_t *height1, int32_t *height2, int32_t *height3, int32_t *height4, int16_t *north)
205 {
206         *east = (int16_t)m68k_read_memory_16(5698+REG_A[0]) - (int16_t)REG_A[2];
207         *north = (int16_t)m68k_read_memory_16(6722+REG_A[0]) - (int16_t)REG_A[3];
208         *height1 = -(int16_t)m68k_read_memory_16(0x79AC);
209         *height2 = (int16_t)m68k_read_memory_16(0x7B26) - (int16_t)m68k_read_memory_16(0x79AC);
210         *height3 = (int16_t)m68k_read_memory_16(0x7B28) - (int16_t)m68k_read_memory_16(0x79AC);
211         *height4 = (int16_t)m68k_read_memory_16(0x7B2A) - (int16_t)m68k_read_memory_16(0x79AC);
212 }
213
214 void mercenary_get_height(int32_t *height)
215 {
216         *height = (int32_t)m68k_read_memory_32(0x7A30);
217 }
218
219 int mercenary_street_color_index(void)
220 {
221         return (m68k_read_memory_16(0x7BE2) >> 5) & 0xf;
222 }
223
224 int mercenary_line_tags_index(void)
225 {
226         return (m68k_read_memory_16(0x7BE2) >> 5) & 0xf;
227 }
228
229 uint16_t mercenary_poly_tags_color(void)
230 {
231         return m68k_read_memory_16(0x7B06);
232 }
233
234 int mercenary_background_index(void)
235 {
236         return m68k_read_memory_16(0x7AEA) >> 2;
237 }
238
239 uint32_t mercenary_planet_scale_index(void)
240 {
241         return 24640;
242 }
243
244 uint32_t mercenary_star_table(void)
245 {
246         return 0x005D8C0;
247 }
248
249 const char *mercenary_gamesavesuffix = ".m2save";
250