Clean up all the system shutdown calls

I added Sys_RegisterShutdown years ago and never really did anything
with it: now any system that needs to be shutdown can ensure it gets
shutdown on program exit, and in the correct order (ie, reverse to init
order).
This commit is contained in:
Bill Currie 2019-07-12 23:15:26 +09:00
parent b2e12d701b
commit 4e4d1b99b4
40 changed files with 135 additions and 176 deletions

View file

@ -471,11 +471,8 @@ SV_WriteFilterList (void)
static void
SV_Shutdown (void)
{
NET_Shutdown ();
// write filter list
SV_WriteFilterList ();
Con_Shutdown ();
}
static void

View file

@ -35,7 +35,6 @@ void CDAudio_Play(int track, qboolean looping);
void CDAudio_Stop(void);
void CDAudio_Pause(void);
void CDAudio_Resume(void);
void CDAudio_Shutdown(void);
void CDAudio_Update(void);
#endif // _CDAUDIO_H

View file

@ -119,7 +119,6 @@ void Con_BufferAddText (con_buffer_t *buf, const char *text);
// init/shutdown functions
void Con_Init (const char *plugin_name);
void Con_Shutdown (void);
void Con_ExecLine (const char *line);
void Con_ProcessInput (void);

View file

@ -44,8 +44,6 @@ struct cvar_s;
void IN_Init (struct cbuf_s *cbuf);
void IN_Init_Cvars (void);
void IN_Shutdown (void);
void IN_ProcessEvents (void);
void IN_UpdateGrab (struct cvar_s *);

View file

@ -81,10 +81,6 @@ void S_Init (int *viewentity, double *host_frametime);
*/
void S_Init_Cvars (void);
/** Shutdown the sound engine. Allows audio output modules to shutdown
gracefully.
*/
void S_Shutdown (void);
//@}
/** \defgroup sound_stuff Unclassified

View file

@ -76,7 +76,6 @@ void VID_Init_Cvars (void);
// the palette data will go away after the call, so it must be copied off if
// the video driver will need it again
void VID_Init (byte *palette, byte *colormap);
void VID_Shutdown (void);
void VID_SetCaption (const char *text);
void VID_ClearMemory (void);

View file

@ -90,10 +90,6 @@ extern struct cvar_s *net_packetlog;
*/
void NET_Init (int port);
/** Shutdown the UDP network interface.
*/
void NET_Shutdown (void);
/** Read a single packet from the network into net_message.
\return True if successfully read, otherwise false.

View file

@ -273,10 +273,6 @@ extern int net_activeconnections;
*/
void NET_Init (void);
/** Shutdown the networking sub-system.
*/
void NET_Shutdown (void);
/** Check for new connections.
\return Pointer to the qsocket for the new connection if there

View file

@ -69,8 +69,8 @@ CDAudio_Resume (void)
cdmodule->functions->cd->pCDAudio_Resume ();
}
VISIBLE void
CDAudio_Shutdown (void)
static void
CDAudio_shutdown (void)
{
if (cdmodule)
cdmodule->functions->general->p_Shutdown ();
@ -93,6 +93,8 @@ CD_f (void)
VISIBLE int
CDAudio_Init (void)
{
Sys_RegisterShutdown (CDAudio_shutdown);
PI_RegisterPlugins (cd_plugin_list);
cd_plugin = Cvar_Get ("cd_plugin", CD_DEFAULT, CVAR_ROM, NULL,
"CD Plugin to use");

View file

@ -53,6 +53,19 @@ static plugin_list_t snd_render_list[] = {
SND_RENDER_PLUGIN_LIST
};
static void
S_shutdown (void)
{
if (snd_render_module) {
PI_UnloadPlugin (snd_render_module);
snd_render_module = NULL;
snd_render_funcs = NULL;
}
if (snd_output_module) {
PI_UnloadPlugin (snd_output_module);
snd_output_module = NULL;
}
}
VISIBLE void
S_Init (int *viewentity, double *host_frametime)
@ -65,6 +78,8 @@ S_Init (int *viewentity, double *host_frametime)
return;
}
Sys_RegisterShutdown (S_shutdown);
PI_RegisterPlugins (snd_output_list);
PI_RegisterPlugins (snd_render_list);
snd_output_module = PI_LoadPlugin ("snd_output", snd_output->string);
@ -120,20 +135,6 @@ S_AmbientOn (void)
snd_render_funcs->pS_AmbientOn ();
}
VISIBLE void
S_Shutdown (void)
{
if (snd_render_module) {
PI_UnloadPlugin (snd_render_module);
snd_render_module = NULL;
snd_render_funcs = NULL;
}
if (snd_output_module) {
PI_UnloadPlugin (snd_output_module);
snd_output_module = NULL;
}
}
VISIBLE void
S_StaticSound (sfx_t *sfx, const vec3_t origin, float vol, float attenuation)
{

View file

@ -463,7 +463,7 @@ SNDDMA_GetDMAPos (void)
}
static void
SNDDMA_Shutdown (void)
SNDDMA_shutdown (void)
{
if (snd_inited) {
qfsnd_pcm_close (pcm);
@ -549,7 +549,7 @@ PLUGIN_INFO(snd_output, alsa)
plugin_info_general_funcs.p_Init = SNDDMA_Init_Cvars;
plugin_info_general_funcs.p_Shutdown = NULL;
plugin_info_snd_output_funcs.pS_O_Init = SNDDMA_Init;
plugin_info_snd_output_funcs.pS_O_Shutdown = SNDDMA_Shutdown;
plugin_info_snd_output_funcs.pS_O_Shutdown = SNDDMA_shutdown;
plugin_info_snd_output_funcs.pS_O_GetDMAPos = SNDDMA_GetDMAPos;
plugin_info_snd_output_funcs.pS_O_Submit = SNDDMA_Submit;
plugin_info_snd_output_funcs.pS_O_BlockSound = SNDDMA_BlockSound;

View file

@ -108,7 +108,7 @@ SNDDMA_GetDMAPos (void)
}
static void
SNDDMA_Shutdown (void)
SNDDMA_shutdown (void)
{
if (snd_inited) {
Qclose (snd_file);
@ -174,7 +174,7 @@ PLUGIN_INFO(snd_output, disk)
plugin_info_general_funcs.p_Init = SNDDMA_Init_Cvars;
plugin_info_general_funcs.p_Shutdown = NULL;
plugin_info_snd_output_funcs.pS_O_Init = SNDDMA_Init;
plugin_info_snd_output_funcs.pS_O_Shutdown = SNDDMA_Shutdown;
plugin_info_snd_output_funcs.pS_O_Shutdown = SNDDMA_shutdown;
plugin_info_snd_output_funcs.pS_O_GetDMAPos = SNDDMA_GetDMAPos;
plugin_info_snd_output_funcs.pS_O_Submit = SNDDMA_Submit;
plugin_info_snd_output_funcs.pS_O_BlockSound = SNDDMA_BlockSound;

View file

@ -417,13 +417,8 @@ SNDDMA_Submit (void)
DSOUND_LockBuffer (false);
}
/*
SNDDMA_Shutdown
Reset the sound device for exiting
*/
static void
SNDDMA_Shutdown (void)
SNDDMA_shutdown (void)
{
FreeSound ();
}
@ -450,7 +445,7 @@ DSOUND_LockBuffer (qboolean lockit)
if (hresult != DSERR_BUFFERLOST) {
Sys_Printf
("S_TransferStereo16: DS::Lock Sound Buffer Failed\n");
SNDDMA_Shutdown ();
SNDDMA_shutdown ();
SNDDMA_Init ();
return NULL;
}
@ -458,7 +453,7 @@ DSOUND_LockBuffer (qboolean lockit)
if (++reps > 10000) {
Sys_Printf
("S_TransferStereo16: DS: couldn't restore buffer\n");
SNDDMA_Shutdown ();
SNDDMA_shutdown ();
SNDDMA_Init ();
return NULL;
}
@ -529,7 +524,7 @@ PLUGIN_INFO(snd_output, dx)
plugin_info_general_funcs.p_Init = SNDDMA_Init_Cvars;
plugin_info_general_funcs.p_Shutdown = NULL;
plugin_info_snd_output_funcs.pS_O_Init = SNDDMA_Init;
plugin_info_snd_output_funcs.pS_O_Shutdown = SNDDMA_Shutdown;
plugin_info_snd_output_funcs.pS_O_Shutdown = SNDDMA_shutdown;
plugin_info_snd_output_funcs.pS_O_GetDMAPos = SNDDMA_GetDMAPos;
plugin_info_snd_output_funcs.pS_O_Submit = SNDDMA_Submit;
plugin_info_snd_output_funcs.pS_O_BlockSound = SNDDMA_BlockSound;

View file

@ -331,7 +331,7 @@ SNDDMA_GetDMAPos (void)
}
static void
SNDDMA_Shutdown (void)
SNDDMA_shutdown (void)
{
if (snd_inited) {
if (mmaped_io)
@ -413,7 +413,7 @@ PLUGIN_INFO(snd_output, oss)
plugin_info_general_funcs.p_Init = SNDDMA_Init_Cvars;
plugin_info_general_funcs.p_Shutdown = NULL;
plugin_info_snd_output_funcs.pS_O_Init = SNDDMA_Init;
plugin_info_snd_output_funcs.pS_O_Shutdown = SNDDMA_Shutdown;
plugin_info_snd_output_funcs.pS_O_Shutdown = SNDDMA_shutdown;
plugin_info_snd_output_funcs.pS_O_GetDMAPos = SNDDMA_GetDMAPos;
plugin_info_snd_output_funcs.pS_O_Submit = SNDDMA_Submit;
plugin_info_snd_output_funcs.pS_O_BlockSound = SNDDMA_BlockSound;

View file

@ -206,7 +206,7 @@ SNDDMA_GetDMAPos (void)
}
static void
SNDDMA_Shutdown (void)
SNDDMA_shutdown (void)
{
if (snd_inited) {
SDL_CloseAudio ();
@ -297,7 +297,7 @@ PLUGIN_INFO(snd_output, sdl)
plugin_info_general_funcs.p_Init = SNDDMA_Init_Cvars;
plugin_info_general_funcs.p_Shutdown = NULL;
plugin_info_snd_output_funcs.pS_O_Init = SNDDMA_Init;
plugin_info_snd_output_funcs.pS_O_Shutdown = SNDDMA_Shutdown;
plugin_info_snd_output_funcs.pS_O_Shutdown = SNDDMA_shutdown;
plugin_info_snd_output_funcs.pS_O_GetDMAPos = SNDDMA_GetDMAPos;
plugin_info_snd_output_funcs.pS_O_Submit = SNDDMA_Submit;
plugin_info_snd_output_funcs.pS_O_BlockSound = SNDDMA_BlockSound;

View file

@ -269,7 +269,7 @@ SNDDMA_GetDMAPos (void)
}
static void
SNDDMA_Shutdown (void)
SNDDMA_shutdown (void)
{
if (snd_inited) {
free (write_buffer);
@ -357,7 +357,7 @@ PLUGIN_INFO(snd_output, sgi)
plugin_info_general_funcs.p_Init = SNDDMA_Init_Cvars;
plugin_info_general_funcs.p_Shutdown = NULL;
plugin_info_snd_output_funcs.pS_O_Init = SNDDMA_Init;
plugin_info_snd_output_funcs.pS_O_Shutdown = SNDDMA_Shutdown;
plugin_info_snd_output_funcs.pS_O_Shutdown = SNDDMA_shutdown;
plugin_info_snd_output_funcs.pS_O_GetDMAPos = SNDDMA_GetDMAPos;
plugin_info_snd_output_funcs.pS_O_Submit = SNDDMA_Submit;
plugin_info_snd_output_funcs.pS_O_BlockSound = SNDDMA_BlockSound;

View file

@ -189,7 +189,7 @@ SNDDMA_GetSamples (void)
}
#endif
static void
SNDDMA_Shutdown (void)
SNDDMA_shutdown (void)
{
if (snd_inited) {
close (audio_fd);
@ -281,7 +281,7 @@ PLUGIN_INFO(snd_output, sun)
plugin_info_general_funcs.p_Shutdown = NULL;
plugin_info_sound_funcs.pS_O_Init = SNDDMA_Init;
plugin_info_sound_funcs.pS_O_Shutdown = SNDDMA_Shutdown;
plugin_info_sound_funcs.pS_O_Shutdown = SNDDMA_shutdown;
plugin_info_sound_funcs.pS_O_GetDMAPos = SNDDMA_GetDMAPos;
plugin_info_sound_funcs.pS_O_Submit = SNDDMA_Submit;
plugin_info_sound_funcs.pS_O_BlockSound = SNDDMA_BlockSound;

View file

@ -344,13 +344,8 @@ SNDDMA_Submit (void)
}
}
/*
SNDDMA_Shutdown
Reset the sound device for exiting
*/
static void
SNDDMA_Shutdown (void)
SNDDMA_shutdown (void)
{
FreeSound ();
}
@ -379,7 +374,7 @@ PLUGIN_INFO(snd_output, win)
plugin_info_general_funcs.p_Init = SNDDMA_Init_Cvars;
plugin_info_general_funcs.p_Shutdown = NULL;
plugin_info_snd_output_funcs.pS_O_Init = SNDDMA_Init;
plugin_info_snd_output_funcs.pS_O_Shutdown = SNDDMA_Shutdown;
plugin_info_snd_output_funcs.pS_O_Shutdown = SNDDMA_shutdown;
plugin_info_snd_output_funcs.pS_O_GetDMAPos = SNDDMA_GetDMAPos;
plugin_info_snd_output_funcs.pS_O_Submit = SNDDMA_Submit;
plugin_info_snd_output_funcs.pS_O_BlockSound = SNDDMA_BlockSound;

View file

@ -931,13 +931,13 @@ C_Init (void)
}
static void
C_Shutdown (void)
C_shutdown (void)
{
}
static general_funcs_t plugin_info_general_funcs = {
C_Init,
C_Shutdown,
C_shutdown,
};
static console_funcs_t plugin_info_console_funcs = {

View file

@ -84,9 +84,20 @@ Con_Interp_f (cvar_t *var)
}
}
static void
Con_shutdown (void)
{
if (con_module) {
con_module->functions->general->p_Shutdown ();
PI_UnloadPlugin (con_module);
}
}
VISIBLE void
Con_Init (const char *plugin_name)
{
Sys_RegisterShutdown (Con_shutdown);
con_module = PI_LoadPlugin ("console", plugin_name);
if (con_module) {
con_module->functions->general->p_Init ();
@ -123,15 +134,6 @@ Con_ExecLine (const char *line)
Sys_Printf ("%s\n", line);
}
VISIBLE void
Con_Shutdown (void)
{
if (con_module) {
con_module->functions->general->p_Shutdown ();
PI_UnloadPlugin (con_module);
}
}
VISIBLE void
Con_Printf (const char *fmt, ...)
{

View file

@ -696,7 +696,7 @@ C_Init (void)
}
static void
C_Shutdown (void)
C_shutdown (void)
{
if (log_file) {
Qclose (log_file);
@ -780,7 +780,7 @@ C_NewMap (void)
static general_funcs_t plugin_info_general_funcs = {
C_Init,
C_Shutdown,
C_shutdown,
};
static general_data_t plugin_info_general_data;

View file

@ -403,6 +403,17 @@ NET_GetLocalAddress (void)
Sys_Printf ("IP address %s\n", NET_AdrToString (net_local_adr));
}
static void
NET_shutdown (void)
{
#ifdef _WIN32
closesocket (net_socket);
WSACleanup ();
#else
close (net_socket);
#endif
}
void
NET_Init (int port)
{
@ -416,6 +427,7 @@ NET_Init (int port)
if (r)
Sys_Error ("Winsock initialization failed.");
#endif /* _WIN32 */
Sys_RegisterShutdown (NET_shutdown);
net_socket = UDP_OpenSocket (port);
@ -431,14 +443,3 @@ NET_Init (int port)
Sys_Printf ("UDP (IPv4) Initialized\n");
}
void
NET_Shutdown (void)
{
#ifdef _WIN32
closesocket (net_socket);
WSACleanup ();
#else
close (net_socket);
#endif
}

View file

@ -600,8 +600,8 @@ NET_Init (int port)
Sys_Printf ("UDP (IPv6) Initialized\n");
}
void
NET_Shutdown (void)
static void
NET_shutdown (void)
{
#ifdef _WIN32
closesocket (net_socket);

View file

@ -718,6 +718,33 @@ NET_SendToAll (sizebuf_t *data, double blocktime)
//=============================================================================
static void
NET_shutdown (void)
{
qsocket_t *sock;
SetNetTime ();
for (sock = net_activeSockets; sock; sock = sock->next)
NET_Close (sock);
//
// shutdown the drivers
//
for (net_driverlevel = 0; net_driverlevel < net_numdrivers;
net_driverlevel++) {
if (net_drivers[net_driverlevel].initialized == true) {
net_drivers[net_driverlevel].Shutdown ();
net_drivers[net_driverlevel].initialized = false;
}
}
if (vcrFile) {
Sys_Printf ("Closing vcrfile.\n");
Qclose (vcrFile);
}
}
void
NET_Init (void)
{
@ -725,6 +752,8 @@ NET_Init (void)
int controlSocket;
qsocket_t *s;
Sys_RegisterShutdown (NET_shutdown);
if (COM_CheckParm ("-playback")) {
net_numdrivers = 1;
net_drivers[0].Init = VCR_Init;
@ -790,33 +819,6 @@ NET_Init (void)
Sys_MaskPrintf (SYS_NET, "TCP/IP address %s\n", my_tcpip_address);
}
void
NET_Shutdown (void)
{
qsocket_t *sock;
SetNetTime ();
for (sock = net_activeSockets; sock; sock = sock->next)
NET_Close (sock);
//
// shutdown the drivers
//
for (net_driverlevel = 0; net_driverlevel < net_numdrivers;
net_driverlevel++) {
if (net_drivers[net_driverlevel].initialized == true) {
net_drivers[net_driverlevel].Shutdown ();
net_drivers[net_driverlevel].initialized = false;
}
}
if (vcrFile) {
Sys_Printf ("Closing vcrfile.\n");
Qclose (vcrFile);
}
}
static PollProcedure *pollProcedureList = NULL;

View file

@ -53,12 +53,6 @@ VID_SetGamma (double gamma)
return SDL_SetGamma((float) gamma, (float) gamma, (float) gamma);
}
void
VID_Shutdown (void)
{
SDL_Quit ();
}
static void
VID_UpdateFullscreen (cvar_t *vid_fullscreen)
{

View file

@ -128,8 +128,8 @@ IN_Move (void)
}
/* Called at shutdown */
void
IN_Shutdown (void)
static void
IN_shutdown (void)
{
JOY_Shutdown ();
@ -142,6 +142,8 @@ IN_Shutdown (void)
void
IN_Init (cbuf_t *cbuf)
{
Sys_RegisterShutdown (IN_shutdown);
IE_Init ();
IN_LL_Init ();
Key_Init (cbuf);

View file

@ -150,8 +150,8 @@ QFGL_LoadLibrary (void)
#endif // HAVE_DLOPEN
void
VID_Shutdown (void)
static void
VID_shutdown (void)
{
if (!fc)
return;
@ -271,6 +271,8 @@ VID_Init (byte *palette, byte *colormap)
{
GLint attribs[32];
Sys_RegisterShutdown (VID_shutdown);
GLF_Init ();
qf_fxMesaCreateContext = QFGL_ProcAddress (libgl_handle,

View file

@ -209,8 +209,8 @@ static unsigned long fb_map_length = 0;
static struct fb_var_screeninfo orig_var;
void
VID_Shutdown (void)
static void
VID_shutdown (void)
{
Sys_MaskPrintf (SYS_VID, "VID_Shutdown\n");
@ -406,6 +406,8 @@ VID_Init (byte *palette, byte *colormap)
if (fbdev_inited)
return;
Sys_RegisterShutdown (VID_shutdown);
R_LoadModule (0, VID_SetPalette);
if (COM_CheckParm ("-novideo")) {

View file

@ -64,9 +64,17 @@ static vid_internal_t vid_internal;
uint32_t sdl_flags;
static void
VID_shutdown (void)
{
SDL_Quit ();
}
void
VID_Init (byte *palette, byte *colormap)
{
Sys_RegisterShutdown (VID_shutdown);
vid_internal.gl_context = SDL_GL_Context;
vid_internal.sw_context = SDL_SW_Context;
// Load the SDL library

View file

@ -239,8 +239,8 @@ get_mode (int width, int height, int depth)
return i;
}
void
VID_Shutdown (void)
static void
VID_shutdown (void)
{
Sys_MaskPrintf (SYS_VID, "VID_Shutdown\n");
@ -358,6 +358,8 @@ VID_Init (byte *palette, byte *colormap)
if (svgalib_inited)
return;
Sys_RegisterShutdown (VID_shutdown);
err = vga_init ();
if (err)
Sys_Error ("SVGALib failed to allocate a new VC");

View file

@ -372,8 +372,8 @@ GL_EndRendering (void)
}
}
void
VID_Shutdown (void)
static void
VID_shutdown (void)
{
HGLRC hRC;
HDC hDC;
@ -454,6 +454,8 @@ VID_Init (byte *palette, byte *colormap)
DWORD lasterror;
WNDCLASS wc;
Sys_RegisterShutdown (VID_shutdown);
vid_fullscreen = Cvar_Get ("vid_fullscreen", "0", CVAR_ROM | CVAR_ARCHIVE,
NULL, "Run WGL client at fullscreen");
GLF_Init ();

View file

@ -83,6 +83,13 @@ D_EndDirectRect (int x, int y, int width, int height)
// direct drawing of the "accessing disk" icon isn't supported
}
static void
VID_shutdown (void)
{
Sys_MaskPrintf (SYS_VID, "VID_shutdown\n");
X11_CloseDisplay ();
}
/*
Set up color translation tables and the window. Takes a 256-color 8-bit
palette. Palette data will go away after the call, so copy it if you'll
@ -91,6 +98,8 @@ D_EndDirectRect (int x, int y, int width, int height)
void
VID_Init (byte *palette, byte *colormap)
{
Sys_RegisterShutdown (VID_shutdown);
vid_internal.gl_context = X11_GL_Context;
vid_internal.sw_context = X11_SW_Context;
#ifdef HAVE_VULKAN
@ -133,18 +142,6 @@ VID_Init_Cvars ()
X11_GL_Init_Cvars ();
}
/*
VID_Shutdown
Restore video mode
*/
void
VID_Shutdown (void)
{
Sys_MaskPrintf (SYS_VID, "VID_Shutdown\n");
X11_CloseDisplay ();
}
#if 0
static int config_notify = 0;
static int config_notify_width;

View file

@ -286,7 +286,6 @@ extern void (*write_angles) (sizebuf_t *sb, const vec3_t angles);
struct cbuf_s;
void CL_Init (struct cbuf_s *cbuf);
void CL_InitCvars (void);
void CL_Shutdown (void);
void CL_ClearMemory (void);
void CL_EstablishConnection (const char *host);

View file

@ -120,14 +120,10 @@ CL_WriteConfiguration (void)
}
}
void
static void
CL_Shutdown (void)
{
CL_WriteConfiguration ();
CDAudio_Shutdown ();
S_Shutdown ();
IN_Shutdown ();
VID_Shutdown ();
}
void
@ -537,6 +533,8 @@ CL_Init (cbuf_t *cbuf)
{
byte *basepal, *colormap;
Sys_RegisterShutdown (CL_Shutdown);
basepal = (byte *) QFS_LoadHunkFile (QFS_FOpenFile ("gfx/palette.lmp"));
if (!basepal)
Sys_Error ("Couldn't load gfx/palette.lmp");

View file

@ -968,11 +968,4 @@ Host_Shutdown (void)
return;
}
isdown = true;
NET_Shutdown ();
if (cls.state != ca_dedicated) {
CL_Shutdown ();
}
Con_Shutdown ();
}

View file

@ -98,11 +98,6 @@ CL_EstablishConnection (const char *host)
{
}
void
CL_Shutdown ()
{
}
void
CL_Init (struct cbuf_s *cbuf)
{

View file

@ -222,8 +222,6 @@ qtv_memory_init (void)
static void
qtv_shutdown (void)
{
NET_Shutdown ();
Con_Shutdown ();
Cbuf_Delete (qtv_cbuf);
Cbuf_ArgsDelete (qtv_args);
}

View file

@ -1882,10 +1882,5 @@ Host_Shutdown (void)
Host_WriteConfiguration ();
CDAudio_Shutdown ();
CL_HTTP_Shutdown ();
NET_Shutdown ();
S_Shutdown ();
IN_Shutdown ();
VID_Shutdown ();
}

View file

@ -246,9 +246,6 @@ SV_Shutdown (void)
}
if (sv.recording_demo)
SV_Stop (0);
NET_Shutdown ();
Con_Shutdown ();
}
/*

View file

@ -161,9 +161,6 @@ static builtin_t builtins[] = {
static void
bi_shutdown (void)
{
S_Shutdown ();
IN_Shutdown ();
VID_Shutdown ();
}
void