mirror of
https://github.com/jkdansereau/goldeneye-pc-port.git
synced 2026-10-24 00:02:45 +02:00
Env-gated per-frame timing: render phases, tris/batches, game-thread busy, GL_TIME_ELAPSED GPU timer. Measured ~2.1 ms/frame total CPU on the heaviest levels here; the GPU timer is only meaningful when GPU-bound. Target-hardware numbers needed before a low-end preset. Golden 3/3; Win + Linux builds.
957 lines
35 KiB
C
957 lines
35 KiB
C
#include <os_extension.h>
|
|
#include <PR/os.h>
|
|
#ifdef PORT
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#endif
|
|
#include "bondview.h"
|
|
#include <bondconstants.h>
|
|
#include <bondgame.h>
|
|
#include "boss.h"
|
|
#include "deb.h"
|
|
#include "debugmenu.h"
|
|
#include "game/dyn.h"
|
|
#include "game/game_debug.h"
|
|
#include "game/file.h"
|
|
#include "game/file2.h"
|
|
#include "game/indy_comms.h"
|
|
#include "init.h"
|
|
#include "joy.h"
|
|
#include "game/lv.h"
|
|
#include "game/front.h"
|
|
#include "mema.h"
|
|
#include "memp.h"
|
|
#include "music.h"
|
|
#include "game/ob.h"
|
|
#include "ramrom.h"
|
|
#include "random.h"
|
|
#include "rmon.h" /*<PR/rmon.h>*/
|
|
#include "sched.h"
|
|
#include "speed_graph.h"
|
|
#include "token.h"
|
|
#include "tlb_manage.h"
|
|
#include "fr.h"
|
|
#include "game/image.h"
|
|
#include "vi.h"
|
|
#include "game/bg.h"
|
|
#include "game/debugmenu_handler.h"
|
|
#include "game/lv.h"
|
|
#include "game/ramromreplay.h"
|
|
#include "game/rsp.h"
|
|
#include "game/stan.h"
|
|
#include "game/textrelated.h"
|
|
#include "game/player.h"
|
|
#include "game/frametiming.h"
|
|
#include "PR/R4300.h"
|
|
|
|
/**
|
|
* @file boss.c
|
|
* @brief Main game loop and initialization functions.
|
|
*
|
|
* This file contains the core functions that drive the game loop, manage stages, and handle debug operations.
|
|
*/
|
|
|
|
|
|
// #define OS_CLOCK_RATE 62500000LL
|
|
// #define OS_CPU_COUNTER (OS_CLOCK_RATE * 3 / 4)
|
|
|
|
#ifdef REFRESH_PAL
|
|
|
|
#define CYCLES_PER_FRAME ((u32) OS_CPU_COUNTER / 50U) // 937,500
|
|
#define INTERVAL_INTER_MATH (CYCLES_PER_FRAME / 2U) // 468,750
|
|
|
|
// this is: frameDelay * 931050 - 465525
|
|
#define MAIN_LOOP_TICK_INTERVAL frameDelay * (CYCLES_PER_FRAME - 6450U) - (INTERVAL_INTER_MATH - 3225U)
|
|
|
|
#else
|
|
|
|
#define CYCLES_PER_FRAME ((u32) OS_CPU_COUNTER / 60U) // 781,250
|
|
#define INTERVAL_INTER_MATH (CYCLES_PER_FRAME / 2U) // 390,625
|
|
|
|
// note: 3225U * 5/6 = 2687.5
|
|
#define MAIN_LOOP_TICK_INTERVAL (INTERVAL_INTER_MATH - 2688U) // 387,937
|
|
|
|
#endif
|
|
|
|
/**
|
|
* Copied from n64devkit\ultra\usr\src\pr\demos_old\simple\gfx.h
|
|
*/
|
|
typedef union {
|
|
|
|
struct {
|
|
short type;
|
|
} gen;
|
|
|
|
struct {
|
|
short type;
|
|
} done;
|
|
|
|
OSScMsg app;
|
|
|
|
} GFXMsg;
|
|
|
|
// forward declarations
|
|
void bossMainloop(void);
|
|
|
|
/* data */
|
|
u32 g_BossDebugNoticeEntry = 0;
|
|
s32 g_DebugAndUpdateStageFlag = FALSE;
|
|
s32 g_StageNum = LEVELID_TITLE;
|
|
u32 g_CurentMMallocValue = 0x234800;
|
|
u32 g_CurentMaMallocValue = 0x4B000;
|
|
s32 g_ShowMemUseFlag = FALSE;
|
|
s32 g_ShowMemBarsFlag = FALSE;
|
|
|
|
struct memallocstring memallocstringtable[] = {
|
|
{ LEVELID_DAM, "-ml0 -me0 -mgfx70 -mvtx50 -mt625 -ma275"},
|
|
{ LEVELID_FACILITY, "-ml0 -me0 -mgfx70 -mvtx50 -mt650 -ma160"},
|
|
{ LEVELID_RUNWAY, "-ml0 -me0 -mgfx100 -mvtx50 -mt610 -ma300"},
|
|
{ LEVELID_SURFACE, "-ml0 -me0 -mgfx70 -mvtx50 -mt600 -ma300"},
|
|
{ LEVELID_BUNKER1, "-ml0 -me0 -mgfx100 -mvtx50 -mt700 -ma150"},
|
|
{ LEVELID_SILO, "-ml0 -me0 -mgfx70 -mvtx50 -mt660 -ma150"},
|
|
{ LEVELID_FRIGATE, "-ml0 -me0 -mgfx70 -mvtx50 -mt750 -ma225"},
|
|
{ LEVELID_SURFACE2, "-ml0 -me0 -mgfx100 -mvtx50 -mt550 -ma350"},
|
|
{ LEVELID_BUNKER2, "-ml0 -me0 -mgfx100 -mvtx50 -mt725 -ma150"},
|
|
{ LEVELID_STATUE, "-ml0 -me0 -mgfx70 -mvtx50 -mt750 -ma220"},
|
|
{ LEVELID_ARCHIVES, "-ml0 -me0 -mgfx70 -mvtx50 -mt600 -ma250"},
|
|
{ LEVELID_STREETS, "-ml0 -me0 -mgfx60 -mvtx40 -mt635 -ma290"},
|
|
{ LEVELID_DEPOT, "-ml0 -me0 -mgfx60 -mvtx50 -mt710 -ma300"},
|
|
{ LEVELID_TRAIN, "-ml0 -me0 -mgfx100 -mvtx50 -mt600 -ma200"},
|
|
{ LEVELID_JUNGLE, "-ml0 -me0 -mgfx70 -mvtx50 -mt500 -ma200"},
|
|
{ LEVELID_CONTROL, "-ml0 -me0 -mgfx70 -mvtx50 -mt671 -ma200"},
|
|
{ LEVELID_CRADLE, "-ml0 -me0 -mgfx100 -mvtx50 -mt650 -ma250"},
|
|
{ LEVELID_CAVERNS, "-ml0 -me0 -mgfx100 -mvtx50 -mt530 -ma250"},
|
|
{ LEVELID_AZTEC, "-ml0 -me0 -mgfx60 -mvtx40 -mt855 -ma135"},
|
|
{ LEVELID_EGYPT, "-ml0 -me0 -mgfx100 -mvtx50 -mt600 -ma250"},
|
|
{ LEVELID_CITADEL, "-ml0 -me0 -mgfx100 -mvtx50 -mt650 -ma150"},
|
|
{ LEVELID_CUBA, "-ml0 -me0 -mgfx100 -mvtx50 -mt300 -ma300"},
|
|
{ LEVELID_TITLE, "-ml0 -me0 -mgfx80 -mvtx20 -mt646 -ma001"},
|
|
{ 0x5B, "-ml0 -me0 -mgfx60 -mvtx20 -mt500 -ma001"},
|
|
{ 0x63, "-ml0 -me0 -mgfx60 -mvtx20 -mt500 -ma001"},
|
|
{ LEVELID_TEMPLE, "-ml0 -me0 -mgfx130 -mvtx100 -mt390 -ma100"},
|
|
{ LEVELID_COMPLEX, "-ml0 -me0 -mgfx130 -mvtx100 -mt400 -ma100"},
|
|
{ LEVELID_LIBRARY, "-ml0 -me0 -mgfx130 -mvtx100 -mt400 -ma100"},
|
|
{ LEVELID_BASEMENT, "-ml0 -me0 -mgfx130 -mvtx100 -mt400 -ma100"},
|
|
{ LEVELID_STACK, "-ml0 -me0 -mgfx130 -mvtx100 -mt400 -ma100"},
|
|
{ LEVELID_CAVES, "-ml0 -me0 -mgfx130 -mvtx100 -mt400 -ma300"},
|
|
{ LEVELID_BUNKER2_MP , "-ml0 -me0 -mgfx130 -mvtx100 -mt550 -ma170"},
|
|
{ LEVELID_ARCHIVES_MP , "-ml0 -me0 -mgfx80 -mvtx100 -mt550 -ma250"},
|
|
{ LEVELID_CAVERNS_MP , "-ml0 -me0 -mgfx130 -mvtx100 -mt440 -ma220"},
|
|
{ LEVELID_FACILITY_MP , "-ml0 -me0 -mgfx90 -mvtx100 -mt550 -ma230"},
|
|
{ LEVELID_EGYPT_MP , "-ml0 -me0 -mgfx110 -mvtx100 -mt350 -ma400"},
|
|
{ LEVELID_DEFAULT, "-ml0 -me0 -mgfx100 -mvtx50 -mt700 -ma400"},
|
|
{ 0x0, },
|
|
{ 0x0, },
|
|
{ 0x0, }
|
|
};
|
|
|
|
s32 g_MainStageNum = LEVELID_NONE;
|
|
#ifdef PORT
|
|
static s32 g_d235ReloadStage = -1, g_d235ReloadWait = 0; /* TEMP D235 verification */
|
|
#endif
|
|
s32 g_BossIsDebugMenuOpen = FALSE;
|
|
|
|
OSScMsg g_bossGfxDoneMsg = { OS_SC_DONE_MSG };
|
|
|
|
|
|
// extern declarations
|
|
extern struct player *g_CurrentPlayer;
|
|
|
|
/**
|
|
* 6930 70005D30
|
|
* ??? - uses "-level_", "-m" strings
|
|
*/
|
|
|
|
|
|
void bossInitMainthreadData(void)
|
|
{
|
|
OSMesg bossmsg;
|
|
OSTimer bosstimer;
|
|
OSMesgQueue bossmq;
|
|
u32 start;
|
|
u32 unused;
|
|
s32 i;
|
|
|
|
debInit();
|
|
romCreateMesgQueue();
|
|
tlbmanageEstablishManagementTable();
|
|
image_entries_load();
|
|
mempInit();
|
|
memaInit();
|
|
bgInit();
|
|
viInit();
|
|
viInitVideoSettings();
|
|
indycommInit();
|
|
g_DebugAndUpdateStageFlag = rmonGetToken();
|
|
obInit();
|
|
rspInit();
|
|
dynInit();
|
|
joyInit();
|
|
osCreateMesgQueue(&bossmq, &bossmsg, 1);
|
|
|
|
for (i = 0; i != MAXCONTROLLERS; i++)
|
|
{
|
|
osSetTimer(&bosstimer, OS_USEC_TO_CYCLES(100000), 0, &bossmq, &bossmsg);
|
|
osRecvMesg(&bossmq, &bossmsg, OS_MESG_BLOCK);
|
|
if (i == 1)
|
|
{
|
|
joyCheckStatusThreadSafe();
|
|
}
|
|
else if (i >= 2)
|
|
{
|
|
joyConsumeSamplesWrapper();
|
|
}
|
|
}
|
|
|
|
if (tokenFind(1, "-level_") == 0)
|
|
{
|
|
g_DebugAndUpdateStageFlag = 1;
|
|
}
|
|
|
|
stanInit();
|
|
gameInit();
|
|
|
|
if (g_DebugAndUpdateStageFlag != 0)
|
|
{
|
|
tokenSetString(" -ml0 -me0 -mgfx100 -mvtx50 -mt700 -ma400");
|
|
}
|
|
#ifdef PORT
|
|
// D121 (WS1): a bare `-level_XX` on PC otherwise boots with default
|
|
// pools and OOM-crashes. On N64 the per-stage memallocstringtable[]
|
|
// loop (boss.c bossMainloop) supplies the right `-m*` row, but it is
|
|
// gated on g_DebugAndUpdateStageFlag, which `-level_` leaves to
|
|
// rmonGetToken() -> 0 on PC. Enabling the flag here instead is NOT
|
|
// equivalent (it routes boot through the title stage). So inject the
|
|
// stage's row directly, only when no `-m*` args were supplied by hand.
|
|
// Same lookup the N64 loop does; no control-flow / flag change.
|
|
// tokenSetString() REPLACES the whole token buffer, so the injected
|
|
// string must carry `-level_XX` (and `-hard N`) forward or bossMainloop
|
|
// loses them and boots the title stage instead.
|
|
else if (tokenFind(1, "-level_") != 0 && tokenFind(1, "-m") == 0)
|
|
{
|
|
const unsigned char *lvl = (const unsigned char *)tokenFind(1, "-level_");
|
|
const char *hard = tokenFind(1, "-hard");
|
|
s32 wantId = (((s32)(lvl[0] * 10) + (s32)lvl[1]) - 0x210);
|
|
s32 k;
|
|
|
|
for (k = 0; memallocstringtable[k].id != 0; k++)
|
|
{
|
|
if (memallocstringtable[k].id == wantId)
|
|
{
|
|
static char portTokenBuf[128];
|
|
|
|
if (hard != NULL && hard[0] != '\0')
|
|
{
|
|
sprintf(portTokenBuf, "-level_%c%c -hard%c %s",
|
|
lvl[0], lvl[1], hard[0],
|
|
memallocstringtable[k].string);
|
|
}
|
|
else
|
|
{
|
|
sprintf(portTokenBuf, "-level_%c%c %s",
|
|
lvl[0], lvl[1], memallocstringtable[k].string);
|
|
}
|
|
|
|
tokenSetString(portTokenBuf);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (tokenFind(1, "-m") != 0)
|
|
{
|
|
g_CurentMMallocValue = (s32) (strtol(tokenFind(1, "-m"), 0, 0) << 0xa);
|
|
}
|
|
|
|
start = (PHYS_TO_K0(osVirtualToPhysical(&_bssSegmentEnd)));
|
|
mempCheckMemflagTokens(start, ((u32)tlbmanageGetTlbAllocatedBlock() - (u32)start));
|
|
mempResetBank(MEMPOOL_PERMANENT);
|
|
langInit();
|
|
lvInit();
|
|
bossInit();
|
|
textInit();
|
|
debmenuInit();
|
|
default_player_perspective_and_height();
|
|
store_osgetcount();
|
|
null_init_main_1();
|
|
speedgraphInit();
|
|
set_gu_scale();
|
|
null_init_main_2();
|
|
casingsInit();
|
|
alloc_load_expand_ani_table();
|
|
init_weapon_animation_groups_maybe();
|
|
reset_counter_rand_body_head();
|
|
null_init_main_3();
|
|
init_player_gait_object();
|
|
initGameData();
|
|
fileResetRamRomSave();
|
|
clear_ramrom_block_buffer_heading_ptrs();
|
|
}
|
|
|
|
/**
|
|
* 6BF4 70005FF4
|
|
* 1 ->"show mem use" debug memory display [800241B4]; fry AT,T6
|
|
*/
|
|
void bossEnableShowMemUseFlag(void) {
|
|
g_ShowMemUseFlag=TRUE;
|
|
}
|
|
|
|
/**
|
|
* 6C04 70006004
|
|
* toggle "show mem bars" [800241B8]; fries V0,T6,T7
|
|
*/
|
|
void bossMemBarsFlagToggle(void) {
|
|
g_ShowMemBarsFlag = g_ShowMemBarsFlag ^ 1;
|
|
}
|
|
|
|
/**
|
|
* 6C1C 7000601C
|
|
* loads primary resources and starts main program loop
|
|
* this is infinite. Loops unconditionally: JAL 70006060
|
|
*/
|
|
void bossEntry(void) {
|
|
bossInitMainthreadData();
|
|
rspAllocateBuffers();
|
|
musicSeqPlayerInit();
|
|
while(1){
|
|
bossMainloop();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Main program loop.
|
|
*
|
|
* 6C60 70006060
|
|
*
|
|
* Seems to have been based on devkit example at one point,
|
|
* n64devkit\ultra\usr\src\pr\demos_old\simple\simple.c
|
|
*
|
|
* loop:
|
|
* 70006090 tests memstring for "-level_##"
|
|
* 700060DC if not title, tests memstring for "-hard#"
|
|
* 70006160 follows...
|
|
* 700061FC test if debug console unconnected [800241A4]
|
|
* 700062EC follows...
|
|
* 700062FC tests memstring for "-ma"
|
|
* 7000633C allocates "-ma" bytes to mem bank 4
|
|
* 7000635C reset player data pointers
|
|
* 70006364 offsets stage number based on number of players unless main menu
|
|
* 700063A0 parses and sets memory allocation, loads stage, etc.
|
|
* ...
|
|
* 70006708 displays memory usage when active
|
|
* 70006724 displays in-game debugger when active
|
|
* 7000674C writes a full sync, end display list combo
|
|
* 7000676C display mem use when active [800241B4]
|
|
* 700067A8 display mem bars when active [800241B8]
|
|
* 700067C0 follows...
|
|
* 700067D8 tests if "u64.taskgrab.#.core" activated and dumps memory
|
|
* 70006854 follows... (700068BC - stop demos)
|
|
*/
|
|
void bossMainloop(void)
|
|
{
|
|
// declarations
|
|
|
|
s32 done;
|
|
const unsigned char *tokenFindLevel;
|
|
GFXMsg *localGfxFrameMsg; // sp 468
|
|
OSScMsg localGfxDoneMsg; // sp 436
|
|
s32 stringIndex;
|
|
s32 toggleFlag; // sp 428
|
|
Gfx *gdl; // sp424
|
|
Gfx *firstGdl;
|
|
u32 nowCount;
|
|
s32 i;
|
|
s32 count;
|
|
s8 joyStickXPos;
|
|
s8 joyStickYPos;
|
|
u16 joyButtons;
|
|
struct player *localPlayer;
|
|
s32 localSelectedNumPlayers;
|
|
u32 pendingGfx = 0;
|
|
s32 freeGfx;
|
|
s32 mainTickElapsed;
|
|
s32 rspReplyMsg;
|
|
|
|
u32 unused_stackpadding_[56];
|
|
|
|
// end declarations
|
|
|
|
done = 0;
|
|
reset_mem_bank_5();
|
|
|
|
if (tokenFind(1, "-level_") != NULL)
|
|
{
|
|
tokenFindLevel = (const unsigned char *)tokenFind(1, "-level_");
|
|
|
|
// quick hack strltolon, converts the two digit ASCII level to 32bit int
|
|
// eg, "09" = 48*10 + 57 - 528 = 9
|
|
g_StageNum = (((s32)(tokenFindLevel[0] * 10) + (s32)tokenFindLevel[1]) - 0x210);
|
|
}
|
|
|
|
if (g_StageNum != LEVELID_TITLE)
|
|
{
|
|
fileValidateSaves();
|
|
fileSetCurrentFolder(FOLDER1);
|
|
set_selected_difficulty(DIFFICULTY_AGENT);
|
|
set_solo_and_ptr_briefing(g_StageNum);
|
|
|
|
if (tokenFind(1, "-hard"))
|
|
{
|
|
// convert ASCII difficulty value to int in set difficulty calls eg '1' = 49, 49-48 = 1
|
|
set_selected_difficulty(*(const unsigned char*)tokenFind(1, "-hard") - '0');
|
|
lvlSetSelectedDifficulty(*(const unsigned char*)tokenFind(1, "-hard") - '0');
|
|
}
|
|
}
|
|
|
|
nowCount = osGetCount();
|
|
randomSetSeed(nowCount);
|
|
|
|
// 'done' value never changes, and control never breaks -- infinite loop
|
|
while (!done)
|
|
{
|
|
localGfxFrameMsg = NULL;
|
|
localGfxDoneMsg = g_bossGfxDoneMsg;
|
|
toggleFlag = 0;
|
|
pendingGfx = 0;
|
|
|
|
test_if_recording_demos_this_stage_load(g_StageNum, lvlGetSelectedDifficulty());
|
|
if (g_DebugAndUpdateStageFlag)
|
|
{
|
|
stringIndex = -1;
|
|
|
|
if (g_StageNum != LEVELID_TITLE && get_selected_num_players() >= 2)
|
|
{
|
|
stringIndex = 0;
|
|
|
|
while(memallocstringtable[stringIndex].id)
|
|
{
|
|
if (memallocstringtable[stringIndex].id == (g_StageNum + 400))
|
|
{
|
|
break;
|
|
}
|
|
|
|
stringIndex++;
|
|
}
|
|
|
|
if (memallocstringtable[stringIndex].id == 0)
|
|
{
|
|
stringIndex = -1;
|
|
}
|
|
}
|
|
|
|
if (stringIndex)
|
|
{
|
|
// empty
|
|
}
|
|
|
|
if (stringIndex < 0)
|
|
{
|
|
stringIndex = 0;
|
|
|
|
while(memallocstringtable[stringIndex].id)
|
|
{
|
|
if (memallocstringtable[stringIndex].id == g_StageNum)
|
|
{
|
|
break;
|
|
}
|
|
|
|
stringIndex++;
|
|
}
|
|
}
|
|
|
|
tokenSetString(memallocstringtable[stringIndex].string);
|
|
}
|
|
|
|
mempResetBank(MEMPOOL_STAGE);
|
|
obBlankResourcesLoadedInBank(MEMPOOL_STAGE);
|
|
if (tokenFind(1, "-ma"))
|
|
{
|
|
g_CurentMaMallocValue = (s32) (strtol(tokenFind(1, "-ma"), NULL, 0) * 1024);
|
|
}
|
|
#ifdef PORT
|
|
/* D294: PC visibility settings (Video.FovScale / WidescreenAuto /
|
|
* DrawDistance / LodDistance) keep more rooms on screen at once than
|
|
* the N64's fixed view, so the authored `-ma` room pool can exhaust;
|
|
* memaAlloc() then fails and undrawn rooms render as solid black.
|
|
* Scale the pool by portRoomPoolScale() (1.0 at all-N64-faithful
|
|
* settings, capped 2.0x) and never let it eat more than the STAGE
|
|
* bank minus 128 KB headroom for zbuf/object resources. */
|
|
{
|
|
extern f32 portRoomPoolScale(void);
|
|
f32 scale = portRoomPoolScale();
|
|
s32 avail;
|
|
u32 scaled;
|
|
|
|
if (scale < 0.9999f || scale > 1.0001f) {
|
|
scaled = (u32)(((s64)g_CurentMaMallocValue * (s64)(scale * 4.0f) + 3) / 4);
|
|
avail = mempGetBankSizeLeft(MEMPOOL_STAGE) - 0x20000;
|
|
if ((s32)scaled > avail && avail >= (s32)g_CurentMaMallocValue) {
|
|
scaled = (u32)avail;
|
|
}
|
|
g_CurentMaMallocValue = scaled;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef PORT
|
|
if (getenv("GE_D294")) { osSyncPrintf("D294 stage=%d ma=%d\n", (s32)g_StageNum, (s32)g_CurentMaMallocValue); }
|
|
#endif
|
|
memaReset(mempAllocBytesInBank(g_CurentMaMallocValue, MEMPOOL_STAGE), g_CurentMaMallocValue);
|
|
reset_play_data_ptrs();
|
|
#ifdef PORT
|
|
/* D235 (wrong textures after dying/restarting a level): the port-side
|
|
* GL texture cache (port/fast3d/gfx_pc.cpp TextureCacheMap) is keyed by
|
|
* (source address, palette addrs, fmt, siz, size_bytes, palette_hash)
|
|
* with NO content check. The MEMPOOL_STAGE arena those stage-texture
|
|
* source addresses live in was just reset above, so every entry
|
|
* pointing into it is stale. On a death/restart the texpool can place
|
|
* a DIFFERENT image at an address that held another one during the
|
|
* previous run of this level -> cache hit returns the old image =
|
|
* "wrong textures" that only ever appear on the reload path, never on
|
|
* a cold boot. The N64 has no such cache (the RSP re-reads DRAM every
|
|
* frame), so clearing it here is a port-only correctness fix; the
|
|
* re-uploads are pixel-identical and cost one-time during the loading
|
|
* screen. videoResetTextureCache() existed for exactly this and had
|
|
* zero callers -- this is the wiring. GE_D235=1 logs how many stale
|
|
* entries each transition actually purged (evidence for the finding;
|
|
* a cold boot into a level should log 0, a death/restart >0). */
|
|
{
|
|
static int ge_d235 = -1;
|
|
if (ge_d235 < 0) ge_d235 = getenv("GE_D235") != NULL;
|
|
extern void videoResetTextureCache(void);
|
|
extern int gfx_texture_cache_count(void);
|
|
if (ge_d235)
|
|
osSyncPrintf("D235: stage=%d texcache entries purged at load=%d\n",
|
|
(s32)g_StageNum, gfx_texture_cache_count());
|
|
if (!getenv("GE_D235_OFF")) /* TEMP: A/B switch for verification */
|
|
videoResetTextureCache();
|
|
|
|
/* TEMP (D235 verification, remove with the probe):
|
|
* GE_D235_RELOAD=<frames> re-enters the first level once, <frames>
|
|
* retraces after the title stage loads following it (e.g. after a
|
|
* watch Abort) -- so the front end has rendered its own textures
|
|
* into the reset stage arena first, like a real death/restart --
|
|
* via the same bossSetLoadedStage() call the front end's mission
|
|
* start makes (front.c). Inert when unset. */
|
|
{
|
|
static int s_reload = -2, s_firstLevel = -1;
|
|
if (s_reload == -2) {
|
|
const char *e = getenv("GE_D235_RELOAD");
|
|
s_reload = e ? atoi(e) : -1;
|
|
}
|
|
if (s_reload >= 0) {
|
|
if (g_StageNum != LEVELID_TITLE && s_firstLevel < 0)
|
|
s_firstLevel = g_StageNum;
|
|
else if (g_StageNum == LEVELID_TITLE && s_firstLevel >= 0 &&
|
|
g_d235ReloadStage < 0 && g_d235ReloadWait == 0) {
|
|
/* GE_D235_NEXT=<stage id>: load a DIFFERENT stage
|
|
* instead (level A -> title -> level B leaks). */
|
|
const char *nx = getenv("GE_D235_NEXT");
|
|
g_d235ReloadStage = (nx && *nx) ? (s32)strtol(nx, NULL, 0) : s_firstLevel;
|
|
g_d235ReloadWait = s_reload + 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
localSelectedNumPlayers = 0;
|
|
if (g_StageNum != LEVELID_TITLE)
|
|
{
|
|
localSelectedNumPlayers = 1;
|
|
if (get_selected_num_players() >= 2)
|
|
{
|
|
localSelectedNumPlayers = get_selected_num_players();
|
|
}
|
|
}
|
|
|
|
init_player_data_ptrs_construct_viewports(localSelectedNumPlayers);
|
|
dynInitMemory();
|
|
joyCheckStatusThreadSafe();
|
|
lvlStageLoad(g_StageNum);
|
|
viInitBuffers();
|
|
debmenuRefresh();
|
|
waitForNextFrame();
|
|
speedgraphMarkerCommit();
|
|
|
|
if(1); // regalloc
|
|
if(1);
|
|
|
|
while (osRecvMesg(&gfxFrameMsgQ, (OSMesg *)&localGfxFrameMsg, OS_MESG_NOBLOCK) == 0)
|
|
{
|
|
// empty
|
|
}
|
|
|
|
if(1); // regalloc
|
|
if(1);
|
|
|
|
while (g_MainStageNum < 0 || pendingGfx != 0)
|
|
{
|
|
#ifdef PORT
|
|
/* #92 perf probe (GE_PERFSTAT=1): game-thread busy time between
|
|
* waking for a message and blocking again (logic + DL build). */
|
|
{
|
|
static int pon = -1; static u32 tw = 0; static f64 busy = 0; static s32 n = 0;
|
|
if (pon < 0) pon = getenv("GE_PERFSTAT") != NULL;
|
|
if (pon) {
|
|
u32 now = osGetCount();
|
|
if (tw) { busy += (f64)(u32)(now - tw); n++; }
|
|
if (n == 600) {
|
|
fprintf(stderr, "PERFSTAT game-thread busy=%.2fms/msg (avg over 600 msgs)\n",
|
|
busy / (f64)n / (f64)(OS_CPU_COUNTER / 1000));
|
|
busy = 0; n = 0;
|
|
}
|
|
}
|
|
osRecvMesg(&gfxFrameMsgQ, (OSMesg *)&localGfxFrameMsg, OS_MESG_BLOCK);
|
|
if (pon) tw = osGetCount();
|
|
}
|
|
#else
|
|
osRecvMesg(&gfxFrameMsgQ, (OSMesg *)&localGfxFrameMsg, OS_MESG_BLOCK);
|
|
#endif
|
|
|
|
switch (localGfxFrameMsg->gen.type)
|
|
{
|
|
case (OS_SC_RETRACE_MSG):
|
|
{
|
|
#ifdef DEBUG
|
|
/* debug logging from simple.c, I think this requires #include <ultralog.h>
|
|
* // if (logging)
|
|
* // osLogEvent(log, LOG_RETRACE, 1, pendingGfx);
|
|
*/
|
|
#endif
|
|
|
|
mainTickElapsed = (u32) (osGetCount() - copy_of_osgetcount_value_1);
|
|
if (mainTickElapsed < MAIN_LOOP_TICK_INTERVAL)
|
|
{
|
|
// nothing to do.
|
|
}
|
|
else
|
|
{
|
|
if (g_MainStageNum < 0 && pendingGfx < 2U)
|
|
{
|
|
#ifdef PORT
|
|
/* TEMP D235 verification (see GE_D235_RELOAD above) */
|
|
if (g_d235ReloadWait > 0 && --g_d235ReloadWait == 0) {
|
|
osSyncPrintf("D235: RELOAD re-entering stage %d\n", g_d235ReloadStage);
|
|
bossSetLoadedStage(g_d235ReloadStage);
|
|
}
|
|
#endif
|
|
if (get_is_ramrom_flag())
|
|
{
|
|
iterate_ramrom_entries_handle_camera_out();
|
|
}
|
|
else
|
|
{
|
|
waitForNextFrame();
|
|
}
|
|
|
|
speedgraphRenderGraph();
|
|
speedgraphMarkerCommit();
|
|
speedgraphMarkerHandler(0x20000);
|
|
joyConsumeSamplesWrapper();
|
|
permit_stderr(0);
|
|
|
|
gdl = firstGdl = dynGetMasterDisplayList();
|
|
|
|
#if defined(DEBUGMENU)
|
|
//ported from pd beta, official way to open debug menu
|
|
// If menu is open (?) or player has pressed C down + C up
|
|
if (g_BossIsDebugMenuOpen || joyGetButtons(0, U_CBUTTONS | D_CBUTTONS) == (U_CBUTTONS | D_CBUTTONS)) {
|
|
joyStickXPos = joyGetStickX(0);
|
|
joyStickYPos = joyGetStickY(0);
|
|
joyButtons = joyGetButtons(0, ANY_BUTTON);
|
|
g_BossIsDebugMenuOpen = debug_menu_processor(joyStickXPos, joyStickYPos, joyButtons, joyGetButtonsPressedThisFrame(0, ANY_BUTTON));
|
|
} else if (joyGetButtons(0, START_BUTTON) == 0) {
|
|
g_DebugMode = g_DebugHighlightedOption;
|
|
} else
|
|
{
|
|
joyStickXPos = joyGetStickX(0);
|
|
joyStickYPos = joyGetStickY(0);
|
|
joyButtons = joyGetButtons(0, ANY_BUTTON);
|
|
g_BossIsDebugMenuOpen = debug_menu_processor(joyStickXPos, joyStickYPos, joyButtons, joyGetButtonsPressedThisFrame(0, ANY_BUTTON));
|
|
}
|
|
#elif defined(PORT)
|
|
// GE_DEBUGMENU=1 -- diagnostic-only: enable leftover debug-menu open
|
|
// path (D243). Unset (default) reproduces the #ifndef DEBUGMENU
|
|
// behavior below exactly -- no behavior change.
|
|
static int ge_debugmenu = -1;
|
|
if (ge_debugmenu < 0) ge_debugmenu = getenv("GE_DEBUGMENU") != NULL;
|
|
if (ge_debugmenu && (g_BossIsDebugMenuOpen || joyGetButtons(0, U_CBUTTONS | D_CBUTTONS) == (U_CBUTTONS | D_CBUTTONS))) {
|
|
joyStickXPos = joyGetStickX(0);
|
|
joyStickYPos = joyGetStickY(0);
|
|
joyButtons = joyGetButtons(0, ANY_BUTTON);
|
|
g_BossIsDebugMenuOpen = debug_menu_processor(joyStickXPos, joyStickYPos, joyButtons, joyGetButtonsPressedThisFrame(0, ANY_BUTTON));
|
|
} else if (ge_debugmenu && joyGetButtons(0, START_BUTTON) == 0) {
|
|
g_DebugMode = g_DebugHighlightedOption;
|
|
} else if (ge_debugmenu) {
|
|
joyStickXPos = joyGetStickX(0);
|
|
joyStickYPos = joyGetStickY(0);
|
|
joyButtons = joyGetButtons(0, ANY_BUTTON);
|
|
g_BossIsDebugMenuOpen = debug_menu_processor(joyStickXPos, joyStickYPos, joyButtons, joyGetButtonsPressedThisFrame(0, ANY_BUTTON));
|
|
} else if (g_BossIsDebugMenuOpen)
|
|
{
|
|
joyStickXPos = joyGetStickX(0);
|
|
joyStickYPos = joyGetStickY(0);
|
|
joyButtons = joyGetButtons(0, ANY_BUTTON);
|
|
g_BossIsDebugMenuOpen = debug_menu_processor(joyStickXPos, joyStickYPos, joyButtons, joyGetButtonsPressedThisFrame(0, ANY_BUTTON));
|
|
}
|
|
#else
|
|
if (g_BossIsDebugMenuOpen)
|
|
{
|
|
joyStickXPos = joyGetStickX(0);
|
|
joyStickYPos = joyGetStickY(0);
|
|
joyButtons = joyGetButtons(0, ANY_BUTTON);
|
|
g_BossIsDebugMenuOpen = debug_menu_processor(joyStickXPos, joyStickYPos, joyButtons, joyGetButtonsPressedThisFrame(0, ANY_BUTTON));
|
|
}
|
|
#endif
|
|
|
|
lvlManageMpGame();
|
|
shuffle_player_ids();
|
|
|
|
if (g_StageNum != LEVELID_TITLE)
|
|
{
|
|
for (i = 0; i < getPlayerCount(); i++)
|
|
{
|
|
set_cur_player(get_nth_player_from_shuffled(i));
|
|
|
|
localPlayer = g_CurrentPlayer;
|
|
viSetViewSize(localPlayer->viewx, localPlayer->viewy);
|
|
|
|
localPlayer = g_CurrentPlayer;
|
|
viSetViewPosition(localPlayer->viewleft, localPlayer->viewtop);
|
|
|
|
lvlViewMoveTick();
|
|
}
|
|
}
|
|
|
|
#ifdef PORT
|
|
/* D250: isolate DL-building cost (culling,
|
|
* animation, scene traversal) from gfx_run's
|
|
* software-RSP interpretation cost, measured
|
|
* separately by libultra.c's "frame rendered in
|
|
* us" (D193 already showed that one stays well
|
|
* under budget while real fps still sags -- this
|
|
* finds out whether lvlRender is where the rest
|
|
* of the missing time goes). Env-gated, no-op
|
|
* unless GE_D250 is set. */
|
|
{
|
|
static int gd250 = -1;
|
|
static OSTime d250Sum = 0, d250WallStart = 0;
|
|
static int d250N = 0;
|
|
OSTime d250T0 = 0;
|
|
if (gd250 < 0) gd250 = getenv("GE_D250") != NULL;
|
|
if (gd250) d250T0 = osGetTime();
|
|
#endif
|
|
gdl = lvlRender(gdl);
|
|
#ifdef PORT
|
|
if (gd250) {
|
|
OSTime now = osGetTime();
|
|
d250Sum += now - d250T0;
|
|
d250N++;
|
|
if (!d250WallStart) d250WallStart = now;
|
|
if (now - d250WallStart >= 1000000) {
|
|
fprintf(stderr,
|
|
"[D250] lvlRender avg=%lluus n=%d\n",
|
|
(unsigned long long)(d250Sum / (d250N ? d250N : 1)),
|
|
d250N);
|
|
d250Sum = 0; d250N = 0; d250WallStart = now;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Lets Visualise the Coverage Value used for Scilohete Anti-Ailising (edges)
|
|
// (done on the VI), also produces a cool looking linemode - providing AA is working.
|
|
if (get_debug_VisCVG_flag())
|
|
{
|
|
gDPPipeSync(gdl++); // 0xe7000000, 0x00000000
|
|
gDPSetCycleType(gdl++, G_CYC_1CYCLE); // 0xba001402, 0x00000000
|
|
gDPSetBlendColor(gdl++, 0xff, 0xff, 0xff, 0xff); // 0xf9000000, 0xffffffff
|
|
gDPSetPrimDepth(gdl++, 0xffff, 0xffff); // 0xee000000, 0xffffffff
|
|
gDPSetDepthSource(gdl++, G_ZS_PRIM); // 0xb9000201, 0x00000004
|
|
gDPSetRenderMode(gdl++, G_RM_VISCVG, G_RM_VISCVG2); // 0xb900031d, 0x0fa54040
|
|
gDPFillRectangle(gdl++, 0, 0, viGetX() - 1, viGetY() - 1);
|
|
}
|
|
|
|
gdl = debmenuDraw(gdl);
|
|
|
|
if (get_memusage_display_flag())
|
|
{
|
|
gdl = speedgraphDisplayMetrics(gdl);
|
|
}
|
|
|
|
if (g_BossIsDebugMenuOpen)
|
|
{
|
|
debugmenuUpdate();
|
|
gdl = debugmenuRender(gdl);
|
|
}
|
|
|
|
gDPFullSync(gdl++); // 0xe9000000, 0x00000000
|
|
gSPEndDisplayList(gdl++); // 0xb8000000, 0x00000000
|
|
|
|
if (g_ShowMemUseFlag)
|
|
{
|
|
nulled_mempLoopAllMemBanks();
|
|
memaDumpPrePostMerge();
|
|
dynRemovedFunc(gdl);
|
|
nullsub_41(0);
|
|
g_ShowMemUseFlag = 0;
|
|
}
|
|
|
|
if (g_ShowMemBarsFlag)
|
|
{
|
|
dynDrawMembars(gdl);
|
|
}
|
|
|
|
freeGfx = dynGetFreeGfx2(gdl);
|
|
dynSwapBuffers();
|
|
video_related_8();
|
|
|
|
if ((get_debug_taskgrab_val())
|
|
&& (joyGetButtonsPressedThisFrame(0, (A_BUTTON | B_BUTTON)))
|
|
&& (joyGetButtons(0, (A_BUTTON | B_BUTTON)) == (A_BUTTON | B_BUTTON)))
|
|
{
|
|
static s32 taskgrab_ramdump_num = 1;
|
|
u8 taskGrabBuffer[28];
|
|
s32 taskGrabFileSize;
|
|
|
|
while (1)
|
|
{
|
|
s32 unusedSprintf = sprintf(taskGrabBuffer, "u64.taskgrab.%d.core", taskgrab_ramdump_num);
|
|
|
|
if (indycommHostCheckFileExists(taskGrabBuffer, &taskGrabFileSize) != NULL)
|
|
{
|
|
taskgrab_ramdump_num++;
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
indycommHostSendDump(taskGrabBuffer, (u8*)0x80000000, 0x400000);
|
|
}
|
|
|
|
rspReplyMsg = (s32)(&localGfxDoneMsg);
|
|
rspGfxTaskStart(firstGdl, gdl, 0, (s32*)rspReplyMsg);
|
|
|
|
pendingGfx++;
|
|
memaSingleDefragPass();
|
|
#ifdef VERSION_EU
|
|
eu_sub_7f0c00a4();
|
|
#endif
|
|
toggleFlag ^= 1;
|
|
|
|
speedgraphMarkerHandler(0x10000);
|
|
if(1);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case (OS_SC_DONE_MSG):
|
|
pendingGfx--;
|
|
break;
|
|
|
|
case OS_SC_PRE_NMI_MSG: // message when the console's power is being cut off
|
|
pendingGfx = 4U; // this stops any further graphics processing
|
|
break;
|
|
}
|
|
}
|
|
|
|
lvlUnloadStageTextData();
|
|
stop_demo_playback();
|
|
mempNullNextEntryInBank(MEMPOOL_STAGE);
|
|
obBlankResourcesLoadedInBank(MEMPOOL_STAGE);
|
|
|
|
#if defined(VERSION_EU)
|
|
if(1);
|
|
#endif
|
|
|
|
g_StageNum = g_MainStageNum;
|
|
g_MainStageNum = LEVELID_NONE;
|
|
}
|
|
|
|
// if(gdl) also works here.
|
|
if (!gdl)
|
|
// removed ... or ido?
|
|
;
|
|
|
|
sub_GAME_7F0D1A7C();
|
|
}
|
|
|
|
|
|
/**
|
|
* 7530 70006930
|
|
* run title [0x5A->loaded stage#]; fry AT
|
|
* redirect to 70006950: A0=0x5A
|
|
*/
|
|
void bossRunTitleStage(void) {
|
|
bossSetLoadedStage(LEVELID_TITLE);
|
|
}
|
|
|
|
/**
|
|
* 7550 70006950
|
|
* A0->loaded stage# [800242FC]; fry AT
|
|
* 0x5A jumps to folder select
|
|
* 0x5B
|
|
* 0x63
|
|
*/
|
|
void bossSetLoadedStage(LEVELID stage){
|
|
g_MainStageNum = stage;
|
|
}
|
|
|
|
/**
|
|
* 755C 7000695C
|
|
* V0= stage# [800241A8]
|
|
*/
|
|
LEVELID bossGetStageNum() {
|
|
return g_StageNum;
|
|
}
|
|
|
|
/**
|
|
* 7568 70006968
|
|
* return to title screen from stage
|
|
*/
|
|
void bossReturnTitleStage(void) {
|
|
#ifdef BUGFIX_R1
|
|
display_objective_status_text_on_status_change();
|
|
objectivestatusDisableStatusDisplay();
|
|
#endif
|
|
#ifdef PORT
|
|
if (getenv("GE_D160")) {
|
|
osSyncPrintf("D160: bossReturnTitleStage ENTER stage=%d allComplete=%d caller=%p\n",
|
|
(int)bossGetStageNum(), (int)objectiveIsAllComplete(),
|
|
__builtin_return_address(0));
|
|
}
|
|
if (getenv("GE_SAVELOG")) {
|
|
extern int g_savelogObjOnce;
|
|
g_savelogObjOnce = 1;
|
|
osSyncPrintf("SAVELOG: bossReturnTitleStage stage=%d objectiveIsAllComplete=%d\n",
|
|
(int)bossGetStageNum(), (int)objectiveIsAllComplete());
|
|
g_savelogObjOnce = 0;
|
|
}
|
|
#endif
|
|
if ((bossGetStageNum() != LEVELID_CUBA) && (objectiveIsAllComplete() != 0x0)) {
|
|
end_of_mission_briefing();
|
|
}
|
|
bossRunTitleStage();
|
|
}
|
|
|
|
/**
|
|
* 75B4 700069B4
|
|
* V0=state of debug menu (1:on; 0:off) [80024300]
|
|
*/
|
|
s32 bossGetDebugParseFlag(void) {
|
|
return g_BossIsDebugMenuOpen;
|
|
}
|
|
|
|
|
|
/**
|
|
* 75C0 700069C0
|
|
* V0= p->debug.notice.list entry for boss_c_debug using data at 800241A0
|
|
*/
|
|
void bossInit(void) {
|
|
debTryAdd(&g_BossDebugNoticeEntry, "boss_c_debug");
|
|
}
|