Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
MorsGames
GitHub Repository: MorsGames/sm64plus
Path: blob/master/src/game/game_init.c
7858 views
1
#include <ultra64.h>
2
3
#include "sm64.h"
4
#include "gfx_dimensions.h"
5
#include "audio/external.h"
6
#include "buffers/buffers.h"
7
#include "buffers/gfx_output_buffer.h"
8
#include "buffers/framebuffers.h"
9
#include "buffers/zbuffer.h"
10
#include "engine/level_script.h"
11
#include "game_init.h"
12
#include "main.h"
13
#include "memory.h"
14
#include "profiler.h"
15
#include "save_file.h"
16
#include "seq_ids.h"
17
#include "sound_init.h"
18
#include "print.h"
19
#include "segment2.h"
20
#include "segment_symbols.h"
21
#include "rumble_init.h"
22
#include <prevent_bss_reordering.h>
23
24
#include "settings.h"
25
26
// First 3 controller slots
27
struct Controller gControllers[3];
28
29
// Gfx handlers
30
struct SPTask *gGfxSPTask;
31
#ifdef USE_SYSTEM_MALLOC
32
struct AllocOnlyPool *gGfxAllocOnlyPool;
33
Gfx *gDisplayListHeadInChunk;
34
Gfx *gDisplayListEndInChunk;
35
#else
36
Gfx *gDisplayListHead;
37
u8 *gGfxPoolEnd;
38
#endif
39
struct GfxPool *gGfxPool;
40
41
// OS Controllers
42
OSContStatus gControllerStatuses[4];
43
OSContPad gControllerPads[4];
44
u8 gControllerBits;
45
s8 gEepromProbe; // Save Data Probe
46
47
// OS Messages
48
OSMesgQueue gGameVblankQueue;
49
OSMesgQueue gGfxVblankQueue;
50
OSMesg gGameMesgBuf[1];
51
OSMesg gGfxMesgBuf[1];
52
53
// Vblank Handler
54
struct VblankHandler gGameVblankHandler;
55
56
// Buffers
57
uintptr_t gPhysicalFrameBuffers[3];
58
uintptr_t gPhysicalZBuffer;
59
60
// Stuff exclusive to 64Plus
61
s8 gCanMirror = 0;
62
s8 gReimportTextures = 0;
63
s8 gBooDialogueWasSaid = 0;
64
65
// Mario Anims and Demo allocation
66
void *gMarioAnimsMemAlloc;
67
void *gDemoInputsMemAlloc;
68
struct DmaHandlerList gMarioAnimsBuf;
69
struct DmaHandlerList gDemoInputsBuf;
70
71
// fillers
72
UNUSED static u8 sfillerGameInit[0x90];
73
static s32 sUnusedGameInitValue = 0;
74
75
// General timer that runs as the game starts
76
u32 gGlobalTimer = 0;
77
78
// Framebuffer rendering values (max 3)
79
u16 sRenderedFramebuffer = 0;
80
u16 sRenderingFrameBuffer = 0;
81
82
// Goddard Vblank Function Caller
83
void (*gGoddardVblankCallback)(void) = NULL;
84
85
// Defined controller slots
86
struct Controller *gPlayer1Controller = &gControllers[0];
87
struct Controller *gPlayer2Controller = &gControllers[1];
88
struct Controller *gPlayer3Controller = &gControllers[2]; // Probably debug only, see note below
89
90
// Title Screen Demo Handler
91
struct DemoInput *gCurrDemoInput = NULL;
92
u16 gDemoInputListID = 0;
93
struct DemoInput gRecordedDemoInput = { 0 };
94
95
// Display
96
// ----------------------------------------------------------------------------------------------------
97
98
s8 get_mirror() {
99
if (configEncoreMode == 0 || configEncoreMode == 2 || gCurrCourseNum == COURSE_CAKE_END) {
100
return 0;
101
}
102
103
return gCanMirror;
104
}
105
106
s16 get_palette() {
107
if (configEncoreMode == 0 || configEncoreMode == 3 || !gCanMirror) {
108
return 0;
109
}
110
111
return gCurrCourseNum+1;
112
}
113
114
/**
115
* Sets the initial RDP (Reality Display Processor) rendering settings.
116
*/
117
void init_rdp(void) {
118
gDPPipeSync(gDisplayListHead++);
119
gDPPipelineMode(gDisplayListHead++, G_PM_1PRIMITIVE);
120
121
gDPSetScissor(gDisplayListHead++, G_SC_NON_INTERLACE, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
122
gDPSetCombineMode(gDisplayListHead++, G_CC_SHADE, G_CC_SHADE);
123
124
gDPSetTextureLOD(gDisplayListHead++, G_TL_TILE);
125
gDPSetTextureLUT(gDisplayListHead++, G_TT_NONE);
126
gDPSetTextureDetail(gDisplayListHead++, G_TD_CLAMP);
127
gDPSetTexturePersp(gDisplayListHead++, G_TP_PERSP);
128
if (configTextureFiltering > -1) {
129
gDPSetTextureFilter(gDisplayListHead++, G_TF_BILERP);
130
}
131
else {
132
gDPSetTextureFilter(gDisplayListHead++, G_TF_POINT);
133
}
134
gDPSetTextureConvert(gDisplayListHead++, G_TC_FILT);
135
136
gDPSetCombineKey(gDisplayListHead++, G_CK_NONE);
137
gDPSetAlphaCompare(gDisplayListHead++, G_AC_NONE);
138
gDPSetRenderMode(gDisplayListHead++, G_RM_OPA_SURF, G_RM_OPA_SURF2);
139
gDPSetColorDither(gDisplayListHead++, G_CD_MAGICSQ);
140
gDPSetCycleType(gDisplayListHead++, G_CYC_FILL);
141
142
#ifdef VERSION_SH
143
gDPSetAlphaDither(gDisplayListHead++, G_AD_PATTERN);
144
#endif
145
gDPPipeSync(gDisplayListHead++);
146
}
147
148
/**
149
* Sets the initial RSP (Reality Signal Processor) settings.
150
*/
151
void init_rsp(void) {
152
gSPClearGeometryMode(gDisplayListHead++, G_SHADE | G_SHADING_SMOOTH | G_CULL_BOTH | G_FOG
153
| G_LIGHTING | G_TEXTURE_GEN | G_TEXTURE_GEN_LINEAR | G_LOD);
154
155
gSPSetGeometryMode(gDisplayListHead++, G_SHADE | G_SHADING_SMOOTH | G_CULL_BACK | G_LIGHTING);
156
157
gSPNumLights(gDisplayListHead++, NUMLIGHTS_1);
158
gSPTexture(gDisplayListHead++, 0, 0, 0, G_TX_RENDERTILE, G_OFF);
159
160
// @bug Failing to set the clip ratio will result in warped triangles in F3DEX2
161
// without this change: https://jrra.zone/n64/doc/n64man/gsp/gSPClipRatio.htm
162
#ifdef F3DEX_GBI_2
163
gSPClipRatio(gDisplayListHead++, FRUSTRATIO_1);
164
#endif
165
}
166
167
/**
168
* Initialize the z buffer for the current frame.
169
*/
170
void init_z_buffer(void) {
171
gDPPipeSync(gDisplayListHead++);
172
173
gDPSetDepthSource(gDisplayListHead++, G_ZS_PIXEL);
174
gDPSetDepthImage(gDisplayListHead++, gPhysicalZBuffer);
175
176
gDPSetColorImage(gDisplayListHead++, G_IM_FMT_RGBA, G_IM_SIZ_16b, SCREEN_WIDTH, gPhysicalZBuffer);
177
gDPSetFillColor(gDisplayListHead++,
178
GPACK_ZDZ(G_MAXFBZ, 0) << 16 | GPACK_ZDZ(G_MAXFBZ, 0));
179
180
gDPFillRectangle(gDisplayListHead++, 0, BORDER_HEIGHT, SCREEN_WIDTH - 1,
181
SCREEN_HEIGHT - 1 - BORDER_HEIGHT);
182
}
183
184
/**
185
* Tells the RDP which of the three framebuffers it shall draw to.
186
*/
187
void select_frame_buffer(void) {
188
gDPPipeSync(gDisplayListHead++);
189
190
gDPSetCycleType(gDisplayListHead++, G_CYC_1CYCLE);
191
gDPSetColorImage(gDisplayListHead++, G_IM_FMT_RGBA, G_IM_SIZ_16b, SCREEN_WIDTH,
192
gPhysicalFrameBuffers[sRenderingFrameBuffer]);
193
gDPSetScissor(gDisplayListHead++, G_SC_NON_INTERLACE, 0, BORDER_HEIGHT, SCREEN_WIDTH,
194
SCREEN_HEIGHT - BORDER_HEIGHT);
195
}
196
197
/**
198
* Clear the framebuffer and fill it with a 32-bit color.
199
* Information about the color argument: https://jrra.zone/n64/doc/n64man/gdp/gDPSetFillColor.htm
200
*/
201
void clear_frame_buffer(s32 color) {
202
gDPPipeSync(gDisplayListHead++);
203
204
gDPSetRenderMode(gDisplayListHead++, G_RM_OPA_SURF, G_RM_OPA_SURF2);
205
gDPSetCycleType(gDisplayListHead++, G_CYC_FILL);
206
207
gDPSetFillColor(gDisplayListHead++, color);
208
gDPFillRectangle(gDisplayListHead++,
209
GFX_DIMENSIONS_RECT_FROM_LEFT_EDGE(0), BORDER_HEIGHT,
210
GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(0) - 1, SCREEN_HEIGHT - BORDER_HEIGHT - 1);
211
212
gDPPipeSync(gDisplayListHead++);
213
214
gDPSetCycleType(gDisplayListHead++, G_CYC_1CYCLE);
215
}
216
217
/**
218
* Resets the viewport, readying it for the final image.
219
*/
220
void clear_viewport(Vp *viewport, s32 color) {
221
s16 vpUlx = (viewport->vp.vtrans[0] - viewport->vp.vscale[0]) / 4 + 1;
222
s16 vpUly = (viewport->vp.vtrans[1] - viewport->vp.vscale[1]) / 4 + 1;
223
s16 vpLrx = (viewport->vp.vtrans[0] + viewport->vp.vscale[0]) / 4 - 2;
224
s16 vpLry = (viewport->vp.vtrans[1] + viewport->vp.vscale[1]) / 4 - 2;
225
226
#ifdef WIDESCREEN
227
if (configAspectRatio != 1) {
228
vpUlx = GFX_DIMENSIONS_RECT_FROM_LEFT_EDGE(vpUlx);
229
vpLrx = GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(SCREEN_WIDTH - vpLrx);
230
}
231
#endif
232
233
gDPPipeSync(gDisplayListHead++);
234
235
gDPSetRenderMode(gDisplayListHead++, G_RM_OPA_SURF, G_RM_OPA_SURF2);
236
gDPSetCycleType(gDisplayListHead++, G_CYC_FILL);
237
238
gDPSetFillColor(gDisplayListHead++, color);
239
gDPFillRectangle(gDisplayListHead++, vpUlx, vpUly, vpLrx, vpLry);
240
241
gDPPipeSync(gDisplayListHead++);
242
243
gDPSetCycleType(gDisplayListHead++, G_CYC_1CYCLE);
244
}
245
246
/**
247
* Draw the horizontal screen borders.
248
*/
249
void draw_screen_borders(void) {
250
gDPPipeSync(gDisplayListHead++);
251
252
gDPSetScissor(gDisplayListHead++, G_SC_NON_INTERLACE, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
253
gDPSetRenderMode(gDisplayListHead++, G_RM_OPA_SURF, G_RM_OPA_SURF2);
254
gDPSetCycleType(gDisplayListHead++, G_CYC_FILL);
255
256
gDPSetFillColor(gDisplayListHead++, GPACK_RGBA5551(0, 0, 0, 0) << 16 | GPACK_RGBA5551(0, 0, 0, 0));
257
258
#if BORDER_HEIGHT != 0
259
gDPFillRectangle(gDisplayListHead++, GFX_DIMENSIONS_RECT_FROM_LEFT_EDGE(0), 0,
260
GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(0) - 1, BORDER_HEIGHT - 1);
261
gDPFillRectangle(gDisplayListHead++,
262
GFX_DIMENSIONS_RECT_FROM_LEFT_EDGE(0), SCREEN_HEIGHT - BORDER_HEIGHT,
263
GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(0) - 1, SCREEN_HEIGHT - 1);
264
#endif
265
}
266
267
/**
268
* Defines the viewport scissoring rectangle.
269
* Scissoring: https://jrra.zone/n64/doc/pro-man/pro12/12-03.htm#01
270
*/
271
void make_viewport_clip_rect(Vp *viewport) {
272
s16 vpUlx = (viewport->vp.vtrans[0] - viewport->vp.vscale[0]) / 4 + 1;
273
s16 vpPly = (viewport->vp.vtrans[1] - viewport->vp.vscale[1]) / 4 + 1;
274
s16 vpLrx = (viewport->vp.vtrans[0] + viewport->vp.vscale[0]) / 4 - 1;
275
s16 vpLry = (viewport->vp.vtrans[1] + viewport->vp.vscale[1]) / 4 - 1;
276
277
gDPSetScissor(gDisplayListHead++, G_SC_NON_INTERLACE, vpUlx, vpPly, vpLrx, vpLry);
278
}
279
280
/**
281
* Initializes the Fast3D OSTask structure.
282
* If you plan on using gSPLoadUcode, make sure to add OS_TASK_LOADABLE to the flags member.
283
*/
284
void create_gfx_task_structure(void) {
285
s32 entries = gDisplayListHead - gGfxPool->buffer;
286
287
gGfxSPTask->msgqueue = &gGfxVblankQueue;
288
gGfxSPTask->msg = (OSMesg) 2;
289
gGfxSPTask->task.t.type = M_GFXTASK;
290
#ifdef TARGET_N64
291
gGfxSPTask->task.t.ucode_boot = rspF3DBootStart;
292
gGfxSPTask->task.t.ucode_boot_size = ((u8 *) rspF3DBootEnd - (u8 *) rspF3DBootStart);
293
gGfxSPTask->task.t.flags = 0;
294
gGfxSPTask->task.t.ucode = rspF3DStart;
295
gGfxSPTask->task.t.ucode_data = rspF3DDataStart;
296
#endif
297
gGfxSPTask->task.t.ucode_size = SP_UCODE_SIZE; // (this size is ignored)
298
gGfxSPTask->task.t.ucode_data_size = SP_UCODE_DATA_SIZE;
299
gGfxSPTask->task.t.dram_stack = (u64 *) gGfxSPTaskStack;
300
gGfxSPTask->task.t.dram_stack_size = SP_DRAM_STACK_SIZE8;
301
gGfxSPTask->task.t.output_buff = gGfxSPTaskOutputBuffer;
302
gGfxSPTask->task.t.output_buff_size =
303
(u64 *)((u8 *) gGfxSPTaskOutputBuffer + sizeof(gGfxSPTaskOutputBuffer));
304
gGfxSPTask->task.t.data_ptr = (u64 *) &gGfxPool->buffer;
305
gGfxSPTask->task.t.data_size = entries * sizeof(Gfx);
306
gGfxSPTask->task.t.yield_data_ptr = (u64 *) gGfxSPTaskYieldBuffer;
307
gGfxSPTask->task.t.yield_data_size = OS_YIELD_DATA_SIZE;
308
}
309
310
/**
311
* Set default RCP (Reality Co-Processor) settings.
312
*/
313
void init_rcp(void) {
314
move_segment_table_to_dmem();
315
init_rdp();
316
init_rsp();
317
init_z_buffer();
318
select_frame_buffer();
319
}
320
321
/**
322
* End the master display list and initialize the graphics task structure for the next frame to be rendered.
323
*/
324
void end_master_display_list(void) {
325
draw_screen_borders();
326
if (gShowProfiler) {
327
draw_profiler();
328
}
329
330
gDPFullSync(gDisplayListHead++);
331
gSPEndDisplayList(gDisplayListHead++);
332
333
create_gfx_task_structure();
334
}
335
336
/**
337
* Draw the bars that appear when the N64 is soft reset.
338
*/
339
void draw_reset_bars(void) {
340
s32 width, height;
341
s32 fbNum;
342
u64 *fbPtr;
343
344
if (gResetTimer != 0 && gNmiResetBarsTimer < 15) {
345
if (sRenderedFramebuffer == 0) {
346
fbNum = 2;
347
} else {
348
fbNum = sRenderedFramebuffer - 1;
349
}
350
351
fbPtr = (u64 *) PHYSICAL_TO_VIRTUAL(gPhysicalFrameBuffers[fbNum]);
352
fbPtr += gNmiResetBarsTimer++ * (SCREEN_WIDTH / 4);
353
354
for (width = 0; width < ((SCREEN_HEIGHT / 16) + 1); width++) {
355
// Loop must be one line to match on -O2
356
for (height = 0; height < (SCREEN_WIDTH / 4); height++) *fbPtr++ = 0;
357
fbPtr += ((SCREEN_WIDTH / 4) * 14);
358
}
359
}
360
361
osWritebackDCacheAll();
362
osRecvMesg(&gGameVblankQueue, &gMainReceivedMesg, OS_MESG_BLOCK);
363
osRecvMesg(&gGameVblankQueue, &gMainReceivedMesg, OS_MESG_BLOCK);
364
}
365
366
#ifdef TARGET_N64
367
/**
368
* Initial settings for the first rendered frame.
369
*/
370
void render_init(void) {
371
gGfxPool = &gGfxPools[0];
372
set_segment_base_addr(1, gGfxPool->buffer);
373
gGfxSPTask = &gGfxPool->spTask;
374
gDisplayListHead = gGfxPool->buffer;
375
gGfxPoolEnd = (u8 *)(gGfxPool->buffer + GFX_POOL_SIZE);
376
init_rcp();
377
clear_frame_buffer(0);
378
end_master_display_list();
379
exec_display_list(&gGfxPool->spTask);
380
381
sRenderingFrameBuffer++;
382
gGlobalTimer++;
383
}
384
#endif
385
386
#ifdef USE_SYSTEM_MALLOC
387
Gfx **alloc_next_dl(void) {
388
u32 size = 10000;
389
Gfx *new_chunk = alloc_only_pool_alloc(gGfxAllocOnlyPool, size * sizeof(Gfx));
390
gSPBranchList(gDisplayListHeadInChunk++, new_chunk);
391
gDisplayListHeadInChunk = new_chunk;
392
gDisplayListEndInChunk = new_chunk + size;
393
return &gDisplayListHeadInChunk;
394
}
395
#endif
396
397
/**
398
* Selects the location of the F3D output buffer (gDisplayListHead).
399
*/
400
void select_gfx_pool(void) {
401
gGfxPool = &gGfxPools[gGlobalTimer % ARRAY_COUNT(gGfxPools)];
402
set_segment_base_addr(1, gGfxPool->buffer);
403
gGfxSPTask = &gGfxPool->spTask;
404
#ifdef USE_SYSTEM_MALLOC
405
gDisplayListHeadInChunk = gGfxPool->buffer;
406
gDisplayListEndInChunk = gDisplayListHeadInChunk + 1;
407
alloc_only_pool_clear(gGfxAllocOnlyPool);
408
#else
409
gDisplayListHead = gGfxPool->buffer;
410
gGfxPoolEnd = (u8 *) (gGfxPool->buffer + GFX_POOL_SIZE);
411
#endif
412
}
413
414
/**
415
* This function:
416
* - Sends the current master display list out to be rendered.
417
* - Tells the VI which color framebuffer to be displayed.
418
* - Yields to the VI framerate twice, locking the game at 30 FPS.
419
* - Selects which framebuffer will be rendered and displayed to next time.
420
*/
421
void display_and_vsync(void) {
422
profiler_log_thread5_time(BEFORE_DISPLAY_LISTS);
423
osRecvMesg(&gGfxVblankQueue, &gMainReceivedMesg, OS_MESG_BLOCK);
424
if (gGoddardVblankCallback != NULL) {
425
gGoddardVblankCallback();
426
gGoddardVblankCallback = NULL;
427
}
428
exec_display_list(&gGfxPool->spTask);
429
profiler_log_thread5_time(AFTER_DISPLAY_LISTS);
430
osRecvMesg(&gGameVblankQueue, &gMainReceivedMesg, OS_MESG_BLOCK);
431
osViSwapBuffer((void *) PHYSICAL_TO_VIRTUAL(gPhysicalFrameBuffers[sRenderedFramebuffer]));
432
profiler_log_thread5_time(THREAD5_END);
433
osRecvMesg(&gGameVblankQueue, &gMainReceivedMesg, OS_MESG_BLOCK);
434
if (++sRenderedFramebuffer == 3) {
435
sRenderedFramebuffer = 0;
436
}
437
if (++sRenderingFrameBuffer == 3) {
438
sRenderingFrameBuffer = 0;
439
}
440
gGlobalTimer++;
441
}
442
443
// Controls
444
// ----------------------------------------------------------------------------------------------------
445
446
/**
447
* This function records distinct inputs over a 255-frame interval to RAM locations and was likely
448
* used to record the demo sequences seen in the final game. This function is unused.
449
*/
450
UNUSED static void record_demo(void) {
451
// Record the player's button mask and current rawStickX and rawStickY.
452
u8 buttonMask =
453
((gPlayer1Controller->buttonDown & (A_BUTTON | B_BUTTON | Z_TRIG | START_BUTTON)) >> 8)
454
| (gPlayer1Controller->buttonDown & (U_CBUTTONS | D_CBUTTONS | L_CBUTTONS | R_CBUTTONS));
455
s8 rawStickX = gPlayer1Controller->rawStickX;
456
s8 rawStickY = gPlayer1Controller->rawStickY;
457
458
// If the stick is in deadzone, set its value to 0 to
459
// nullify the effects. We do not record deadzone inputs.
460
if (rawStickX > -8 && rawStickX < 8) {
461
rawStickX = 0;
462
}
463
464
if (rawStickY > -8 && rawStickY < 8) {
465
rawStickY = 0;
466
}
467
468
// Rrecord the distinct input and timer so long as they are unique.
469
// If the timer hits 0xFF, reset the timer for the next demo input.
470
if (gRecordedDemoInput.timer == 0xFF || buttonMask != gRecordedDemoInput.buttonMask
471
|| rawStickX != gRecordedDemoInput.rawStickX || rawStickY != gRecordedDemoInput.rawStickY) {
472
gRecordedDemoInput.timer = 0;
473
gRecordedDemoInput.buttonMask = buttonMask;
474
gRecordedDemoInput.rawStickX = rawStickX;
475
gRecordedDemoInput.rawStickY = rawStickY;
476
}
477
gRecordedDemoInput.timer++;
478
}
479
480
/**
481
* Take the updated controller struct and calculate the new x, y, and distance floats.
482
*/
483
void adjust_analog_stick(struct Controller *controller) {
484
UNUSED u8 pad[8];
485
486
// Reset the controller's x and y floats.
487
controller->stickX = 0;
488
controller->stickY = 0;
489
controller->stick2X = 0;
490
controller->stick2Y = 0;
491
492
// Modulate the rawStickX and rawStickY to be the new f32 values by adding/subtracting 6.
493
if (controller->rawStickX <= -8) {
494
controller->stickX = controller->rawStickX + 6;
495
}
496
497
if (controller->rawStickX >= 8) {
498
controller->stickX = controller->rawStickX - 6;
499
}
500
501
if (controller->rawStickY <= -8) {
502
controller->stickY = controller->rawStickY + 6;
503
}
504
505
if (controller->rawStickY >= 8) {
506
controller->stickY = controller->rawStickY - 6;
507
}
508
509
if (controller->rawStick2X <= -8) {
510
controller->stick2X = controller->rawStick2X + 6;
511
}
512
513
if (controller->rawStick2X >= 8) {
514
controller->stick2X = controller->rawStick2X - 6;
515
}
516
517
if (controller->rawStick2Y <= -8) {
518
controller->stick2Y = controller->rawStick2Y + 6;
519
}
520
521
if (controller->rawStick2Y >= 8) {
522
controller->stick2Y = controller->rawStick2Y - 6;
523
}
524
525
// Calculate f32 magnitude from the center by vector length.
526
controller->stickMag =
527
sqrtf(controller->stickX * controller->stickX + controller->stickY * controller->stickY);
528
controller->stick2Mag =
529
sqrtf(controller->stick2X * controller->stick2X + controller->stick2Y * controller->stick2Y);
530
531
532
// Magnitude cannot exceed 64.0f: if it does, modify the values
533
// appropriately to flatten the values down to the allowed maximum value.
534
if (controller->stickMag > 64) {
535
controller->stickX *= 64 / controller->stickMag;
536
controller->stickY *= 64 / controller->stickMag;
537
controller->stickMag = 64;
538
}
539
// The value is much higher for the second stick since we want the camera to move faster with mouse in use.
540
// Not too fast though...
541
if (controller->stick2Mag > 768) {
542
controller->stick2X *= 768 / controller->stick2Mag;
543
controller->stick2Y *= 768 / controller->stick2Mag;
544
controller->stick2Mag = 768;
545
}
546
}
547
548
/**
549
* If a demo sequence exists, this will run the demo input list until it is complete.
550
*/
551
void run_demo_inputs(void) {
552
// Eliminate the unused bits.
553
gControllers[0].controllerData->button &= VALID_BUTTONS;
554
555
// Check if a demo inputs list exists and if so,
556
// run the active demo input list.
557
if (gCurrDemoInput != NULL) {
558
// Clear player 2's inputs if they exist. Player 2's controller
559
// cannot be used to influence a demo. At some point, Nintendo
560
// may have planned for there to be a demo where 2 players moved
561
// around instead of just one, so clearing player 2's influence from
562
// the demo had to have been necessary to perform this. Co-op mode, perhaps?
563
if (gControllers[1].controllerData != NULL) {
564
gControllers[1].controllerData->stick_x = 0;
565
gControllers[1].controllerData->stick_y = 0;
566
gControllers[1].controllerData->button = 0;
567
}
568
569
// The timer variable being 0 at the current input means the demo is over.
570
// Set the button to the END_DEMO mask to end the demo.
571
if (gCurrDemoInput->timer == 0) {
572
gControllers[0].controllerData->stick_x = 0;
573
gControllers[0].controllerData->stick_y = 0;
574
gControllers[0].controllerData->button = END_DEMO;
575
} else {
576
// Backup the start button if it is pressed, since we don't want the
577
// demo input to override the mask where start may have been pressed.
578
u16 startPushed = gControllers[0].controllerData->button & START_BUTTON;
579
580
// Perform the demo inputs by assigning the current button mask and the stick inputs.
581
gControllers[0].controllerData->stick_x = gCurrDemoInput->rawStickX;
582
gControllers[0].controllerData->stick_y = gCurrDemoInput->rawStickY;
583
584
// To assign the demo input, the button information is stored in
585
// an 8-bit mask rather than a 16-bit mask. this is because only
586
// A, B, Z, Start, and the C-Buttons are used in a demo, as bits
587
// in that order. In order to assign the mask, we need to take the
588
// upper 4 bits (A, B, Z, and Start) and shift then left by 8 to
589
// match the correct input mask. We then add this to the masked
590
// lower 4 bits to get the correct button mask.
591
gControllers[0].controllerData->button =
592
((gCurrDemoInput->buttonMask & 0xF0) << 8) + ((gCurrDemoInput->buttonMask & 0xF));
593
594
// If start was pushed, put it into the demo sequence being input to end the demo.
595
gControllers[0].controllerData->button |= startPushed;
596
597
// Run the current demo input's timer down. if it hits 0, advance the demo input list.
598
if (--gCurrDemoInput->timer == 0) {
599
gCurrDemoInput++;
600
}
601
}
602
}
603
}
604
605
/**
606
* Update the controller struct with available inputs if present.
607
*/
608
void read_controller_inputs(void) {
609
s32 i;
610
611
// If any controllers are plugged in, update the controller information.
612
if (gControllerBits) {
613
osRecvMesg(&gSIEventMesgQueue, &gMainReceivedMesg, OS_MESG_BLOCK);
614
osContGetReadData(&gControllerPads[0]);
615
#if ENABLE_RUMBLE
616
release_rumble_pak_control();
617
#endif
618
}
619
run_demo_inputs();
620
621
for (i = 0; i < 2; i++) {
622
struct Controller *controller = &gControllers[i];
623
624
// if we're receiving inputs, update the controller struct with the new button info.
625
if (controller->controllerData != NULL) {
626
627
controller->rawStickX = controller->controllerData->stick_x;
628
controller->rawStickY = controller->controllerData->stick_y;
629
630
if (configDpadControls && !configDebugMovementMode) {
631
controller->rawStickX += (((controller->buttonDown & R_JPAD) > 0) - ((controller->buttonDown & L_JPAD) > 0))*80;
632
controller->rawStickY += (((controller->buttonDown & U_JPAD) > 0) - ((controller->buttonDown & D_JPAD) > 0))*80;
633
}
634
635
controller->rawStick2X = controller->controllerData->stick2_x;
636
controller->rawStick2Y = controller->controllerData->stick2_y;
637
638
if (get_mirror()) {
639
controller->rawStickX *= -1;
640
controller->rawStick2X *= -1;
641
}
642
643
controller->buttonPressed = controller->controllerData->button
644
& (controller->controllerData->button ^ controller->buttonDown);
645
// 0.5x A presses are a good meme
646
controller->buttonDown = controller->controllerData->button;
647
adjust_analog_stick(controller);
648
} else // otherwise, if the controllerData is NULL, 0 out all of the inputs.
649
{
650
controller->rawStickX = 0;
651
controller->rawStickY = 0;
652
controller->rawStick2X = 0;
653
controller->rawStick2Y = 0;
654
controller->buttonPressed = 0;
655
controller->buttonDown = 0;
656
controller->stickX = 0;
657
controller->stickY = 0;
658
controller->stick2X = 0;
659
controller->stick2Y = 0;
660
controller->stickMag = 0;
661
}
662
}
663
664
// For some reason, player 1's inputs are copied to player 3's port.
665
// This potentially may have been a way the developers "recorded"
666
// the inputs for demos, despite record_demo existing.
667
gPlayer3Controller->rawStickX = gPlayer1Controller->rawStickX;
668
gPlayer3Controller->rawStickY = gPlayer1Controller->rawStickY;
669
gPlayer3Controller->stickX = gPlayer1Controller->stickX;
670
gPlayer3Controller->stickY = gPlayer1Controller->stickY;
671
gPlayer3Controller->stickMag = gPlayer1Controller->stickMag;
672
gPlayer3Controller->buttonPressed = gPlayer1Controller->buttonPressed;
673
gPlayer3Controller->buttonDown = gPlayer1Controller->buttonDown;
674
}
675
676
/**
677
* Initialize the controller structs to point at the OSCont information.
678
*/
679
void init_controllers(void) {
680
s16 port, cont;
681
682
// Set controller 1 to point to the set of status/pads for input 1 and
683
// init the controllers.
684
gControllers[0].statusData = &gControllerStatuses[0];
685
gControllers[0].controllerData = &gControllerPads[0];
686
osContInit(&gSIEventMesgQueue, &gControllerBits, &gControllerStatuses[0]);
687
688
// Strangely enough, the EEPROM probe for save data is done in this function.
689
// Save Pak detection?
690
gEepromProbe = osEepromProbe(&gSIEventMesgQueue);
691
692
// Loop over the 4 ports and link the controller structs to the appropriate
693
// status and pad. Interestingly, although there are pointers to 3 controllers,
694
// only 2 are connected here. The third seems to have been reserved for debug
695
// purposes and was never connected in the retail ROM, thus gPlayer3Controller
696
// cannot be used, despite being referenced in various code.
697
for (cont = 0, port = 0; port < 4 && cont < 2; port++) {
698
// Is controller plugged in?
699
if (gControllerBits & (1 << port)) {
700
// The game allows you to have just 1 controller plugged
701
// into any port in order to play the game. this was probably
702
// so if any of the ports didn't work, you can have controllers
703
// plugged into any of them and it will work.
704
#if ENABLE_RUMBLE
705
gControllers[cont].port = port;
706
#endif
707
gControllers[cont].statusData = &gControllerStatuses[port];
708
gControllers[cont++].controllerData = &gControllerPads[port];
709
}
710
}
711
}
712
713
// Game thread core
714
// ----------------------------------------------------------------------------------------------------
715
716
/**
717
* Setup main segments and framebuffers.
718
*/
719
void setup_game_memory(void) {
720
UNUSED u64 padding;
721
722
// Setup general Segment 0
723
set_segment_base_addr(0, (void *) 0x80000000);
724
// Create Mesg Queues
725
osCreateMesgQueue(&gGfxVblankQueue, gGfxMesgBuf, ARRAY_COUNT(gGfxMesgBuf));
726
osCreateMesgQueue(&gGameVblankQueue, gGameMesgBuf, ARRAY_COUNT(gGameMesgBuf));
727
// Setup z buffer and framebuffer
728
gPhysicalZBuffer = VIRTUAL_TO_PHYSICAL(gZBuffer);
729
gPhysicalFrameBuffers[0] = VIRTUAL_TO_PHYSICAL(gFrameBuffer0);
730
gPhysicalFrameBuffers[1] = VIRTUAL_TO_PHYSICAL(gFrameBuffer1);
731
gPhysicalFrameBuffers[2] = VIRTUAL_TO_PHYSICAL(gFrameBuffer2);
732
// Setup Mario Animations
733
gMarioAnimsMemAlloc = main_pool_alloc(0x4000, MEMORY_POOL_LEFT);
734
set_segment_base_addr(17, (void *) gMarioAnimsMemAlloc);
735
setup_dma_table_list(&gMarioAnimsBuf, gMarioAnims, gMarioAnimsMemAlloc);
736
// Setup Demo Inputs List
737
gDemoInputsMemAlloc = main_pool_alloc(0x800, MEMORY_POOL_LEFT);
738
set_segment_base_addr(24, (void *) gDemoInputsMemAlloc);
739
setup_dma_table_list(&gDemoInputsBuf, gDemoInputs, gDemoInputsMemAlloc);
740
// Setup Level Script Entry
741
load_segment(0x10, _entrySegmentRomStart, _entrySegmentRomEnd, MEMORY_POOL_LEFT);
742
// Setup Segment 2 (Fonts, Text, etc)
743
load_segment_decompress(2, _segment2_mio0SegmentRomStart, _segment2_mio0SegmentRomEnd);
744
}
745
746
#ifndef TARGET_N64
747
static struct LevelCommand *levelCommandAddr;
748
#endif
749
750
/**
751
* Main game loop thread. Runs forever as long as the game continues.
752
*/
753
void thread5_game_loop(UNUSED void *arg) {
754
#ifdef TARGET_N64
755
struct LevelCommand *levelCommandAddr;
756
#endif
757
758
setup_game_memory();
759
#if ENABLE_RUMBLE
760
init_rumble_pak_scheduler_queue();
761
#endif
762
init_controllers();
763
#if ENABLE_RUMBLE
764
create_thread_6();
765
#endif
766
save_file_load_all();
767
768
set_vblank_handler(2, &gGameVblankHandler, &gGameVblankQueue, (OSMesg) 1);
769
770
// Point levelCommandAddr to the entry point into the level script data.
771
levelCommandAddr = segmented_to_virtual(level_script_entry);
772
773
play_music(SEQ_PLAYER_SFX, SEQUENCE_ARGS(0, SEQ_SOUND_PLAYER), 0);
774
set_sound_mode(save_file_get_sound_mode());
775
776
#ifdef TARGET_N64
777
render_init();
778
779
while (TRUE) {
780
#else
781
gGlobalTimer++;
782
}
783
784
void game_loop_one_iteration(void) {
785
#endif
786
// If the reset timer is active, run the process to reset the game.
787
if (gResetTimer) {
788
draw_reset_bars();
789
#ifdef TARGET_N64
790
continue;
791
#else
792
return;
793
#endif
794
}
795
profiler_log_thread5_time(THREAD5_START);
796
797
// If any controllers are plugged in, start read the data for when
798
// read_controller_inputs is called later.
799
if (gControllerBits) {
800
#if ENABLE_RUMBLE
801
block_until_rumble_pak_free();
802
#endif
803
osContStartReadData(&gSIEventMesgQueue);
804
}
805
806
audio_game_loop_tick();
807
select_gfx_pool();
808
read_controller_inputs();
809
levelCommandAddr = level_script_execute(levelCommandAddr);
810
811
display_and_vsync();
812
813
// when debug info is enabled, print the "BUF %d" information.
814
if (gShowDebugText) {
815
#ifndef USE_SYSTEM_MALLOC
816
// subtract the end of the gfx pool with the display list to obtain the
817
// amount of free space remaining.
818
print_text_fmt_int(180, 20, "BUF %d", gGfxPoolEnd - (u8 *) gDisplayListHead);
819
#endif
820
}
821
#ifdef TARGET_N64
822
}
823
#endif
824
}
825
826