From 87c0029b2bc7e22a3fe31eec5d4d04621e32265f Mon Sep 17 00:00:00 2001 From: Denis Pauk Date: Mon, 29 Jul 2024 22:50:06 +0300 Subject: [PATCH] client: play arbitrary CDTRACK ogg file https://github.com/yquake2/yquake2remaster/issues/18 --- src/client/cl_parse.c | 4 +- src/client/cl_view.c | 46 +++++----- src/client/menu/menu.c | 3 +- src/client/sound/header/vorbis.h | 2 +- src/client/sound/ogg.c | 137 ++++++++++++++++++++++++------ src/game/g_spawn.c | 9 +- src/game/header/local.h | 1 + src/game/savegame/tables/fields.h | 1 + 8 files changed, 145 insertions(+), 58 deletions(-) diff --git a/src/client/cl_parse.c b/src/client/cl_parse.c index a5729f8e..57993d57 100644 --- a/src/client/cl_parse.c +++ b/src/client/cl_parse.c @@ -1095,9 +1095,7 @@ CL_ParseConfigString(void) { if (cl.refresh_prepped) { - int track = (int)strtol(cl.configstrings[CS_CDTRACK], (char **)NULL, 10); - - OGG_PlayTrack(track, true, true); + OGG_PlayTrack(cl.configstrings[CS_CDTRACK], true, true); } } else if ((i >= CS_MODELS) && (i < CS_MODELS + MAX_MODELS)) diff --git a/src/client/cl_view.c b/src/client/cl_view.c index 559c1292..8aaef407 100644 --- a/src/client/cl_view.c +++ b/src/client/cl_view.c @@ -46,21 +46,21 @@ cvar_t *crosshair_3d_glow_b; cvar_t *cl_stats; -int r_numdlights; -dlight_t r_dlights[MAX_DLIGHTS]; +static int r_numdlights; +static dlight_t r_dlights[MAX_DLIGHTS]; -int r_numentities; -entity_t r_entities[MAX_ENTITIES]; +static int r_numentities; +static entity_t r_entities[MAX_ENTITIES]; -int r_numparticles; -particle_t r_particles[MAX_PARTICLES]; +static int r_numparticles; +static particle_t r_particles[MAX_PARTICLES]; -lightstyle_t r_lightstyles[MAX_LIGHTSTYLES]; +static lightstyle_t r_lightstyles[MAX_LIGHTSTYLES]; char cl_weaponmodels[MAX_CLIENTWEAPONMODELS][MAX_QPATH]; int num_cl_weaponmodels; -void V_Render3dCrosshair(void); +static void V_Render3dCrosshair(void); /* * Specifies the model that will be used as the world @@ -139,7 +139,7 @@ V_AddLightStyle(int style, float r, float g, float b) /* *If cl_testparticles is set, create 4096 particles in the view */ -void +static void V_TestParticles(void) { particle_t *p; @@ -169,7 +169,7 @@ V_TestParticles(void) /* * If cl_testentities is set, create 32 player models */ -void +static void V_TestEntities(void) { int i, j; @@ -200,18 +200,20 @@ V_TestEntities(void) /* * If cl_testlights is set, create 32 lights models */ -void +static void V_TestLights(void) { - int i, j; - float f, r; - dlight_t *dl; + int i; r_numdlights = 32; memset(r_dlights, 0, sizeof(r_dlights)); for (i = 0; i < r_numdlights; i++) { + dlight_t *dl; + float f, r; + int j; + dl = &r_dlights[i]; r = 64 * ((i % 4) - 1.5f); @@ -362,9 +364,7 @@ CL_PrepRefresh(void) cl.force_refdef = true; /* make sure we have a valid refdef */ /* start the cd track */ - int track = (int)strtol(cl.configstrings[CS_CDTRACK], (char **)NULL, 10); - - OGG_PlayTrack(track, true, true); + OGG_PlayTrack(cl.configstrings[CS_CDTRACK], true, true); } float @@ -388,14 +388,14 @@ CalcFov(float fov_x, float width, float height) } /* gun frame debugging functions */ -void +static void V_Gun_Next_f(void) { gun_frame++; Com_Printf("frame %i\n", gun_frame); } -void +static void V_Gun_Prev_f(void) { gun_frame--; @@ -408,7 +408,7 @@ V_Gun_Prev_f(void) Com_Printf("frame %i\n", gun_frame); } -void +static void V_Gun_Model_f(void) { char name[MAX_QPATH]; @@ -423,7 +423,7 @@ V_Gun_Model_f(void) gun_model = R_RegisterModel(name); } -int +static int entitycmpfnc(const entity_t *a, const entity_t *b) { /* all other models are sorted by model then skin */ @@ -600,7 +600,7 @@ V_RenderView(float stereo_separation) SCR_DrawCrosshair(); } -void +static void V_Render3dCrosshair(void) { trace_t crosshair_trace; @@ -646,7 +646,7 @@ V_Render3dCrosshair(void) } } -void +static void V_Viewpos_f(void) { Com_Printf("position: %i %i %i, angles: %i %i %i\n", diff --git a/src/client/menu/menu.c b/src/client/menu/menu.c index 83b4835f..3ba01529 100644 --- a/src/client/menu/menu.c +++ b/src/client/menu/menu.c @@ -2299,8 +2299,7 @@ EnableOGGMusic(void *unused) if (cls.state == ca_active) { - int track = (int)strtol(cl.configstrings[CS_CDTRACK], (char **)NULL, 10); - OGG_PlayTrack(track, true, true); + OGG_PlayTrack(cl.configstrings[CS_CDTRACK], true, true); } } else diff --git a/src/client/sound/header/vorbis.h b/src/client/sound/header/vorbis.h index 5389a5a8..aee01d33 100644 --- a/src/client/sound/header/vorbis.h +++ b/src/client/sound/header/vorbis.h @@ -37,7 +37,7 @@ typedef enum int OGG_Status(void); void OGG_InitTrackList(void); void OGG_Init(void); -void OGG_PlayTrack(int trackNo, qboolean cdtrack, qboolean immediate); +void OGG_PlayTrack(const char* track, qboolean cdtrack, qboolean immediate); void OGG_RecoverState(void); void OGG_SaveState(void); void OGG_Shutdown(void); diff --git a/src/client/sound/ogg.c b/src/client/sound/ogg.c index 7d588f2e..c51f5cd9 100644 --- a/src/client/sound/ogg.c +++ b/src/client/sound/ogg.c @@ -64,7 +64,7 @@ enum GameType { rogue }; -struct { +static struct { qboolean saved; int curfile; int numsamples; @@ -88,15 +88,24 @@ OGG_TogglePlayback(void); static int getMappedGOGtrack(int track, enum GameType gameType) { if(track <= 0) + { return 0; + } if(track == 1) + { return 0; // 1 is illegal (=> data track on CD), 0 means "no track" + } if(gameType == other) + { return track; + } + if(gameType == rogue) + { return track + 10; + } // apparently it's xatrix => map the track to the corresponding TrackXX.ogg from GOG switch(track) @@ -185,14 +194,16 @@ OGG_InitTrackList(void) char testFileName[MAX_OSPATH]; - // the simple case (like before: $mod/music/02.ogg - 11.ogg or whatever) + /* the simple case (like before: $mod/music/02.ogg - 11.ogg or whatever) */ snprintf(testFileName, MAX_OSPATH, "%s02.ogg", fullMusicPath); if(Sys_IsFile(testFileName)) { + int i; + ogg_tracks[2] = strdup(testFileName); - for(int i=3; imodified = false; - OGG_PlayTrack(ogg_mapcdtrack, true, true); + OGG_PlayTrack(va("%d", ogg_mapcdtrack), true, true); } } @@ -367,8 +404,11 @@ OGG_Stream(void) * play the ogg file that corresponds to the CD track with the given number */ void -OGG_PlayTrack(int trackNo, qboolean cdtrack, qboolean immediate) +OGG_PlayTrack(const char *track, qboolean cdtrack, qboolean immediate) { + char name[MAX_OSPATH], *path = NULL; + int trackNo; + if (sound_started == SS_NOT) { return; // sound is not initialized @@ -379,6 +419,53 @@ OGG_PlayTrack(int trackNo, qboolean cdtrack, qboolean immediate) return; } + while (1) + { + int res = 0; + FILE* f; + + path = FS_NextPath(path); + + if (!path) + { + break; + } + + Com_sprintf(name, sizeof(name), "%s/music/%s", path, track); + + /* Open ogg vorbis file. */ + f = Q_fopen(name, "rb"); + if (f == NULL) + { + continue; + } + + /* Check running music. */ + if (ogg_status == PLAY) + { + OGG_Stop(); + } + + // fclose is not required on error with close_on_free=true + ogg_file = stb_vorbis_open_file(f, true, &res, NULL); + + if (res != 0) + { + Com_Printf("%s: '%s' is not a valid Ogg Vorbis file (error %i).\n", + __func__, name, res); + return; + } + + /* Play file. */ + ogg_curfile = 0; + ogg_numsamples = 0; + ogg_status = PLAY; + + return; + } + + trackNo = (int)strtol(track, (char **)NULL, 10); + // Track 0 means "stop music". if (trackNo == 0) { @@ -439,7 +526,7 @@ OGG_PlayTrack(int trackNo, qboolean cdtrack, qboolean immediate) return; } break; case 2: // sequential - { + { newtrack = (curtrack + 1) % (ogg_maxfileindex + 1) != 0 ? (curtrack + 1) : 2; } break; case 3: // random @@ -474,7 +561,8 @@ OGG_PlayTrack(int trackNo, qboolean cdtrack, qboolean immediate) if (ogg_tracks[trackNo] == NULL) { - Com_Printf("%s: Don't have a .ogg file for track %d\n", __func__, trackNo); + Com_Printf("%s: Don't have a .ogg file for track %d\n", + __func__, trackNo); } /* Check running music. */ @@ -492,7 +580,8 @@ OGG_PlayTrack(int trackNo, qboolean cdtrack, qboolean immediate) if (ogg_tracks[trackNo] == NULL) { - Com_Printf("OGG_PlayTrack: I don't have a file for track %d!\n", trackNo); + Com_Printf("%s: I don't have a file for track %d!\n", + __func__, trackNo); return; } @@ -502,7 +591,8 @@ OGG_PlayTrack(int trackNo, qboolean cdtrack, qboolean immediate) if (f == NULL) { - Com_Printf("%s: could not open file %s for track %d: %s.\n", __func__, ogg_tracks[trackNo], trackNo, strerror(errno)); + Com_Printf("%s: could not open file %s for track %d: %s.\n", + __func__, ogg_tracks[trackNo], trackNo, strerror(errno)); ogg_tracks[trackNo] = NULL; return; @@ -515,7 +605,8 @@ OGG_PlayTrack(int trackNo, qboolean cdtrack, qboolean immediate) if (res != 0) { - Com_Printf("%s: '%s' is not a valid Ogg Vorbis file (error %i).\n", __func__, ogg_tracks[trackNo], res); + Com_Printf("%s: '%s' is not a valid Ogg Vorbis file (error %i).\n", + __func__, ogg_tracks[trackNo], res); return; } @@ -626,7 +717,7 @@ OGG_TogglePlayback(void) /* * Prints a help message for the 'ogg' cmd. */ -void +static void OGG_HelpMsg(void) { Com_Printf("Unknown sub command %s\n\n", Cmd_Argv(1)); @@ -641,7 +732,7 @@ OGG_HelpMsg(void) /* * The 'ogg' cmd. Gives some control and information about the playback state. */ -void +static void OGG_Cmd(void) { if (Cmd_Argc() < 2) @@ -662,17 +753,7 @@ OGG_Cmd(void) return; } - int track = (int)strtol(Cmd_Argv(2), NULL, 10); - - if (track < 2 || track > ogg_maxfileindex) - { - Com_Printf("invalid track %s, must be an number between 2 and %d\n", Cmd_Argv(1), ogg_maxfileindex); - return; - } - else - { - OGG_PlayTrack(track, false, true); - } + OGG_PlayTrack(Cmd_Argv(2), false, true); } else if (Q_stricmp(Cmd_Argv(1), "stop") == 0) { @@ -727,7 +808,7 @@ OGG_RecoverState(void) int shuffle_state = ogg_shuffle->value; Cvar_SetValue("ogg_shuffle", 0); - OGG_PlayTrack(ogg_saved_state.curfile, false, true); + OGG_PlayTrack(va("%d", ogg_saved_state.curfile), false, true); stb_vorbis_seek_frame(ogg_file, ogg_saved_state.numsamples); ogg_numsamples = ogg_saved_state.numsamples; diff --git a/src/game/g_spawn.c b/src/game/g_spawn.c index 0e787e7a..bd44a686 100644 --- a/src/game/g_spawn.c +++ b/src/game/g_spawn.c @@ -908,7 +908,14 @@ SP_worldspawn(edict_t *ent) gi.configstring(CS_SKYAXIS, va("%f %f %f", st.skyaxis[0], st.skyaxis[1], st.skyaxis[2])); - gi.configstring(CS_CDTRACK, va("%i", ent->sounds)); + if (st.music && st.music[0]) + { + gi.configstring(CS_CDTRACK, st.music); + } + else + { + gi.configstring(CS_CDTRACK, va("%i", ent->sounds)); + } gi.configstring(CS_MAXCLIENTS, va("%i", (int)(maxclients->value))); diff --git a/src/game/header/local.h b/src/game/header/local.h index 85d94f3d..3985a019 100644 --- a/src/game/header/local.h +++ b/src/game/header/local.h @@ -344,6 +344,7 @@ typedef struct float skyrotate; vec3_t skyaxis; char *nextmap; + char *music; int lip; int distance; diff --git a/src/game/savegame/tables/fields.h b/src/game/savegame/tables/fields.h index e4de6885..74ba1534 100644 --- a/src/game/savegame/tables/fields.h +++ b/src/game/savegame/tables/fields.h @@ -104,5 +104,6 @@ {"maxyaw", STOFS(maxyaw), F_FLOAT, FFL_SPAWNTEMP}, {"minpitch", STOFS(minpitch), F_FLOAT, FFL_SPAWNTEMP}, {"maxpitch", STOFS(maxpitch), F_FLOAT, FFL_SPAWNTEMP}, +{"music", STOFS(music), F_LSTRING, FFL_SPAWNTEMP}, {"nextmap", STOFS(nextmap), F_LSTRING, FFL_SPAWNTEMP}, {0, 0, 0, 0}