/** Toggles a console variable. Useful for on/off values. * * This works on on/off, yes/no values only */ static void COM_Toggle_f(void) { consvar_t *cvar; if (COM_Argc() != 2) { CONS_Printf(M_GetText("Toggle <cvar_name>: Toggle the value of a cvar\n")); return; } cvar = CV_FindVar(COM_Argv(1)); if (!cvar) { CONS_Alert(CONS_NOTICE, M_GetText("%s is not a cvar\n"), COM_Argv(1)); return; } if (!(cvar->PossibleValue == CV_YesNo || cvar->PossibleValue == CV_OnOff)) { CONS_Alert(CONS_NOTICE, M_GetText("%s is not a boolean value\n"), COM_Argv(1)); return; } // netcvar don't change imediately cvar->flags |= CV_SHOWMODIFONETIME; CV_AddValue(cvar, +1); }
// Compile a script by name and dump it back to disk. void LUA_DumpFile(const char *filename) { FILE *handle; char filenamebuf[MAX_WADPATH]; if (!gL) // Lua needs to be initialized LUA_ClearState(false); // find the file the SRB2 way strncpy(filenamebuf, filename, MAX_WADPATH); filenamebuf[MAX_WADPATH - 1] = '\0'; filename = filenamebuf; if ((handle = fopen(filename, "rb")) == NULL) { // If we failed to load the file with the path as specified by // the user, strip the directories and search for the file. nameonly(filenamebuf); // If findfile finds the file, the full path will be returned // in filenamebuf == filename. if (findfile(filenamebuf, NULL, true)) { if ((handle = fopen(filename, "rb")) == NULL) { CONS_Alert(CONS_ERROR, M_GetText("Can't open %s\n"), filename); return; } } else { CONS_Alert(CONS_ERROR, M_GetText("File %s not found.\n"), filename); return; } } fclose(handle); // pass the path we found to Lua // luaL_loadfile will open and read the file in as a Lua function if (luaL_loadfile(gL, filename)) { CONS_Alert(CONS_ERROR,"%s\n",lua_tostring(gL,-1)); lua_pop(gL, 1); return; } // dump it back to disk if ((handle = fopen(filename, "wb")) == NULL) CONS_Alert(CONS_ERROR, M_GetText("Can't write to %s\n"), filename); if (lua_dump(gL, dumpWriter, handle)) CONS_Printf("Failed while writing %s to disk... Sorry!\n", filename); else CONS_Printf("Successfully compiled %s into bytecode.\n", filename); fclose(handle); lua_pop(gL, 1); // function is still on stack after lua_dump lua_gc(gL, LUA_GCCOLLECT, 0); return; }
boolean LUAh_PlayerHook(player_t *plr, enum hook which) { hook_p hookp; boolean hooked = false; if (!gL || !(hooksAvailable[which/8] & (1<<(which%8)))) return false; lua_settop(gL, 0); for (hookp = roothook; hookp; hookp = hookp->next) if (hookp->type == which) { if (lua_gettop(gL) == 0) LUA_PushUserdata(gL, plr, META_PLAYER); lua_pushfstring(gL, FMT_HOOKID, hookp->id); lua_gettable(gL, LUA_REGISTRYINDEX); lua_pushvalue(gL, -2); if (lua_pcall(gL, 1, 1, 0)) { if (!hookp->error || cv_debug & DBG_LUA) CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); lua_pop(gL, 1); hookp->error = true; continue; } if (lua_toboolean(gL, -1)) hooked = true; lua_pop(gL, 1); } lua_settop(gL, 0); return hooked; }
// Add a new sound fx into a free sfx slot. // sfxenum_t S_AddSoundFx(const char *name, boolean singular, INT32 flags, boolean skinsound) { sfxenum_t i, slot; if (skinsound) slot = sfx_skinsoundslot0; else slot = sfx_freeslot0; for (i = slot; i < NUMSFX; i++) { if (!S_sfx[i].priority) { strncpy(freeslotnames[i-sfx_freeslot0], name, 6); S_sfx[i].singularity = singular; S_sfx[i].priority = 60; S_sfx[i].pitch = flags; S_sfx[i].volume = -1; S_sfx[i].lumpnum = LUMPERROR; S_sfx[i].skinsound = -1; S_sfx[i].usefulness = -1; /// \todo if precached load it here S_sfx[i].data = NULL; return i; } } CONS_Alert(CONS_WARNING, M_GetText("No more free sound slots\n")); return 0; }
static void ArchiveTables(void) { int TABLESINDEX; UINT16 i, n; UINT8 e; if (!gL) return; TABLESINDEX = lua_gettop(gL); n = (UINT16)lua_objlen(gL, TABLESINDEX); for (i = 1; i <= n; i++) { lua_rawgeti(gL, TABLESINDEX, i); lua_pushnil(gL); while (lua_next(gL, -2)) { ArchiveValue(TABLESINDEX, -2); // key should be either a number or a string, ArchiveValue can handle this. e = ArchiveValue(TABLESINDEX, -1); if (e == 1) n++; // the table contained a new table we'll have to archive. :( else if (e == 2) { lua_pushvalue(gL, -2); CONS_Alert(CONS_ERROR, "Type of value for table %d entry '%s' (%s) could not be archived!\n", i, lua_tostring(gL, -1), luaL_typename(gL, -1)); lua_pop(gL, 1); } lua_pop(gL, 1); } lua_pop(gL, 1); WRITEUINT8(save_p, ARCH_TEND); } }
// Hook for B_BuildTailsTiccmd by skin name boolean LUAh_BotAI(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd) { hook_p hookp; boolean hooked = false; if (!gL || !(hooksAvailable[hook_BotAI/8] & (1<<(hook_BotAI%8)))) return false; lua_settop(gL, 0); for (hookp = roothook; hookp; hookp = hookp->next) if (hookp->type == hook_BotAI && (hookp->s.skinname == NULL || !strcmp(hookp->s.skinname, ((skin_t*)tails->skin)->name))) { if (lua_gettop(gL) == 0) { LUA_PushUserdata(gL, sonic, META_MOBJ); LUA_PushUserdata(gL, tails, META_MOBJ); } lua_pushfstring(gL, FMT_HOOKID, hookp->id); lua_gettable(gL, LUA_REGISTRYINDEX); lua_pushvalue(gL, -3); lua_pushvalue(gL, -3); if (lua_pcall(gL, 2, 8, 0)) { if (!hookp->error || cv_debug & DBG_LUA) CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); lua_pop(gL, 1); hookp->error = true; continue; } // This turns forward, backward, left, right, jump, and spin into a proper ticcmd for tails. if (lua_istable(gL, 2+1)) { boolean forward=false, backward=false, left=false, right=false, strafeleft=false, straferight=false, jump=false, spin=false; #define CHECKFIELD(field) \ lua_getfield(gL, 2+1, #field);\ if (lua_toboolean(gL, -1))\ field = true;\ lua_pop(gL, 1); CHECKFIELD(forward) CHECKFIELD(backward) CHECKFIELD(left) CHECKFIELD(right) CHECKFIELD(strafeleft) CHECKFIELD(straferight) CHECKFIELD(jump) CHECKFIELD(spin) #undef CHECKFIELD B_KeysToTiccmd(tails, cmd, forward, backward, left, right, strafeleft, straferight, jump, spin); } else B_KeysToTiccmd(tails, cmd, lua_toboolean(gL, 2+1), lua_toboolean(gL, 2+2), lua_toboolean(gL, 2+3), lua_toboolean(gL, 2+4), lua_toboolean(gL, 2+5), lua_toboolean(gL, 2+6), lua_toboolean(gL, 2+7), lua_toboolean(gL, 2+8)); lua_pop(gL, 8); hooked = true; } lua_settop(gL, 0); return hooked; }
// Panic function Lua calls when there's an unprotected error. // This function cannot return. Lua would kill the application anyway if it did. FUNCNORETURN static int LUA_Panic(lua_State *L) { CONS_Alert(CONS_ERROR,"LUA PANIC! %s\n",lua_tostring(L,-1)); I_Error("An unfortunate Lua processing error occurred in the exe itself. This is not a scripting error on your part."); #ifndef __GNUC__ return -1; #endif }
/** Adds text into the command buffer for later execution. * * \param ptext The text to add. * \sa COM_BufInsertText */ void COM_BufAddText(const char *ptext) { size_t l; l = strlen(ptext); if (com_text.cursize + l >= com_text.maxsize) { CONS_Alert(CONS_WARNING, M_GetText("Command buffer full!\n")); return; } VS_Write(&com_text, ptext, l); }
// Hook for player chat boolean LUAh_PlayerMsg(int source, int target, int flags, char *msg) { hook_p hookp; boolean hooked = false; if (!gL || !(hooksAvailable[hook_PlayerMsg/8] & (1<<(hook_PlayerMsg%8)))) return false; lua_settop(gL, 0); for (hookp = roothook; hookp; hookp = hookp->next) if (hookp->type == hook_PlayerMsg) { if (lua_gettop(gL) == 0) { LUA_PushUserdata(gL, &players[source], META_PLAYER); // Source player if (flags & 2 /*HU_CSAY*/) { // csay TODO: make HU_CSAY accessible outside hu_stuff.c lua_pushinteger(gL, 3); // type lua_pushnil(gL); // target } else if (target == -1) { // sayteam lua_pushinteger(gL, 1); // type lua_pushnil(gL); // target } else if (target == 0) { // say lua_pushinteger(gL, 0); // type lua_pushnil(gL); // target } else { // sayto lua_pushinteger(gL, 2); // type LUA_PushUserdata(gL, &players[target-1], META_PLAYER); // target } lua_pushstring(gL, msg); // msg } lua_pushfstring(gL, FMT_HOOKID, hookp->id); lua_gettable(gL, LUA_REGISTRYINDEX); lua_pushvalue(gL, -5); lua_pushvalue(gL, -5); lua_pushvalue(gL, -5); lua_pushvalue(gL, -5); if (lua_pcall(gL, 4, 1, 0)) { if (!hookp->error || cv_debug & DBG_LUA) CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); lua_pop(gL, 1); hookp->error = true; continue; } if (lua_toboolean(gL, -1)) hooked = true; lua_pop(gL, 1); } lua_settop(gL, 0); return hooked; }
boolean LUA_CallAction(const char *csaction, mobj_t *actor) { I_Assert(csaction != NULL); I_Assert(actor != NULL); if (!gL) // Lua isn't loaded, return false; // action not called. if (superstack && fasticmp(csaction, superactions[superstack-1])) // the action is calling itself, return false; // let it call the hardcoded function instead. // grab function by uppercase name. lua_getfield(gL, LUA_REGISTRYINDEX, LREG_ACTIONS); { char *action = Z_StrDup(csaction); strupr(action); lua_getfield(gL, -1, action); Z_Free(action); } lua_remove(gL, -2); // pop LREG_ACTIONS if (lua_isnil(gL, -1)) // no match { lua_pop(gL, 1); // pop nil return false; // action not called. } if (superstack == MAXRECURSION) { CONS_Alert(CONS_WARNING, "Max Lua Action recursion reached! Cool it on the calling A_Action functions from inside A_Action functions!\n"); return true; } // Found a function. // Call it with (actor, var1, var2) I_Assert(lua_isfunction(gL, -1)); LUA_PushUserdata(gL, actor, META_MOBJ); lua_pushinteger(gL, var1); lua_pushinteger(gL, var2); superactions[superstack] = csaction; ++superstack; LUA_Call(gL, 3); --superstack; superactions[superstack] = NULL; return true; // action successfully called. }
// Load a script from a MYFILE static inline void LUA_LoadFile(MYFILE *f, char *name) { if (!name) name = wadfiles[f->wad]->filename; CONS_Printf("Loading Lua script from %s\n", name); if (!gL) // Lua needs to be initialized LUA_ClearState(); lua_pushinteger(gL, f->wad); lua_setfield(gL, LUA_REGISTRYINDEX, "WAD"); if (luaL_loadbuffer(gL, f->data, f->size, va("@%s",name)) || lua_pcall(gL, 0, 0, 0)) { CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL,-1)); lua_pop(gL,1); } lua_gc(gL, LUA_GCCOLLECT, 0); }
/** Parses a single line of text into arguments and tries to execute it. * The text can come from the command buffer, a remote client, or stdin. * * \param ptext A single line of text. */ static void COM_ExecuteString(char *ptext) { xcommand_t *cmd; cmdalias_t *a; static INT32 recursion = 0; // detects recursion and stops it if it goes too far COM_TokenizeString(ptext); // execute the command line if (COM_Argc() == 0) return; // no tokens // check functions for (cmd = com_commands; cmd; cmd = cmd->next) { if (!stricmp(com_argv[0], cmd->name)) //case insensitive now that we have lower and uppercase! { recursion = 0; cmd->function(); return; } } // check aliases for (a = com_alias; a; a = a->next) { if (!stricmp(com_argv[0], a->name)) { if (recursion > MAX_ALIAS_RECURSION) { CONS_Alert(CONS_WARNING, M_GetText("Alias recursion cycle detected!\n")); recursion = 0; return; } recursion++; COM_BufInsertText(a->value); return; } } recursion = 0; // check cvars // Hurdler: added at Ebola's request ;) // (don't flood the console in software mode with bad gr_xxx command) if (!CV_Command() && con_destlines) CONS_Printf(M_GetText("Unknown command '%s'\n"), COM_Argv(0)); }
static void ArchiveExtVars(void *pointer, const char *ptype) { int TABLESINDEX; UINT16 i; if (!gL) { if (fastcmp(ptype,"player")) // players must always be included, even if no vars WRITEUINT16(save_p, 0); return; } TABLESINDEX = lua_gettop(gL); lua_getfield(gL, LUA_REGISTRYINDEX, LREG_EXTVARS); I_Assert(lua_istable(gL, -1)); lua_pushlightuserdata(gL, pointer); lua_rawget(gL, -2); lua_remove(gL, -2); // pop LREG_EXTVARS if (!lua_istable(gL, -1)) { // no extra values table lua_pop(gL, 1); if (fastcmp(ptype,"player")) // players must always be included, even if no vars WRITEUINT16(save_p, 0); return; } lua_pushnil(gL); for (i = 0; lua_next(gL, -2); i++) lua_pop(gL, 1); if (i == 0 && !fastcmp(ptype,"player")) // skip anything that has an empty table and isn't a player. return; if (fastcmp(ptype,"mobj")) // mobjs must write their mobjnum as a header WRITEUINT32(save_p, ((mobj_t *)pointer)->mobjnum); WRITEUINT16(save_p, i); lua_pushnil(gL); while (lua_next(gL, -2)) { I_Assert(lua_type(gL, -2) == LUA_TSTRING); WRITESTRING(save_p, lua_tostring(gL, -2)); if (ArchiveValue(TABLESINDEX, -1) == 2) CONS_Alert(CONS_ERROR, "Type of value for %s entry '%s' (%s) could not be archived!\n", ptype, lua_tostring(gL, -2), luaL_typename(gL, -1)); lua_pop(gL, 1); } lua_pop(gL, 1); }
// Hook for P_DamageMobj by mobj type (Should mobj take damage?) UINT8 LUAh_ShouldDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage) { hook_p hookp; UINT8 shouldDamage = 0; // 0 = default, 1 = force yes, 2 = force no. if (!gL || !(hooksAvailable[hook_ShouldDamage/8] & (1<<(hook_ShouldDamage%8)))) return 0; lua_settop(gL, 0); for (hookp = roothook; hookp; hookp = hookp->next) if (hookp->type == hook_ShouldDamage && (hookp->s.mt == MT_NULL || hookp->s.mt == target->type)) { if (lua_gettop(gL) == 0) { LUA_PushUserdata(gL, target, META_MOBJ); LUA_PushUserdata(gL, inflictor, META_MOBJ); LUA_PushUserdata(gL, source, META_MOBJ); lua_pushinteger(gL, damage); } lua_pushfstring(gL, FMT_HOOKID, hookp->id); lua_gettable(gL, LUA_REGISTRYINDEX); lua_pushvalue(gL, -5); lua_pushvalue(gL, -5); lua_pushvalue(gL, -5); lua_pushvalue(gL, -5); if (lua_pcall(gL, 4, 1, 0)) { if (!hookp->error || cv_debug & DBG_LUA) CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); lua_pop(gL, 1); hookp->error = true; continue; } if (!lua_isnil(gL, -1)) { if (lua_toboolean(gL, -1)) shouldDamage = 1; // Force yes else shouldDamage = 2; // Force no } lua_pop(gL, 1); } lua_settop(gL, 0); return shouldDamage; }
/** Checks the server to see if we CAN download all the files, * before starting to create them and requesting. * * \return True if we can download all the files * */ boolean CL_CheckDownloadable(void) { UINT8 i,dlstatus = 0; for (i = 0; i < fileneedednum; i++) if (fileneeded[i].status != FS_FOUND && fileneeded[i].status != FS_OPEN && fileneeded[i].important) { if (fileneeded[i].willsend == 1) continue; if (fileneeded[i].willsend == 0) dlstatus = 1; else //if (fileneeded[i].willsend == 2) dlstatus = 2; } // Downloading locally disabled if (!dlstatus && M_CheckParm("-nodownload")) dlstatus = 3; if (!dlstatus) return true; // not downloadable, put reason in console CONS_Alert(CONS_NOTICE, M_GetText("You need additional files to connect to this server:\n")); for (i = 0; i < fileneedednum; i++) if (fileneeded[i].status != FS_FOUND && fileneeded[i].status != FS_OPEN && fileneeded[i].important) { CONS_Printf(" * \"%s\" (%dK)", fileneeded[i].filename, fileneeded[i].totalsize >> 10); if (fileneeded[i].status == FS_NOTFOUND) CONS_Printf(M_GetText(" not found, md5: ")); else if (fileneeded[i].status == FS_MD5SUMBAD) CONS_Printf(M_GetText(" wrong version, md5: ")); { INT32 j; char md5tmp[33]; for (j = 0; j < 16; j++) sprintf(&md5tmp[j*2], "%02x", fileneeded[i].md5sum[j]); CONS_Printf("%s", md5tmp); } CONS_Printf("\n"); }
fixed_t LUA_EvalMath(const char *word) { lua_State *L = NULL; char buf[1024], *b; const char *p; fixed_t res = 0; // make a new state so SOC can't interefere with scripts // allocate state L = lua_newstate(LUA_Alloc, NULL); lua_atpanic(L, LUA_Panic); // open only enum lib lua_pushcfunction(L, LUA_EnumLib); lua_pushboolean(L, true); lua_call(L, 1, 0); // change ^ into ^^ for Lua. strcpy(buf, "return "); b = buf+strlen(buf); for (p = word; *p && b < &buf[1022]; p++) { *b++ = *p; if (*p == '^') *b++ = '^'; } *b = '\0'; // eval string. lua_pop(L, -1); if (luaL_dostring(L, buf)) { p = lua_tostring(L, -1); while (*p++ != ':' && *p) ; p += 3; // "1: " CONS_Alert(CONS_WARNING, "%s\n", p); } else res = lua_tointeger(L, -1); // clean up and return. lua_close(L); return res; }
boolean I_SetSongTrack(INT32 track) { if (track != current_track) // If the track's already playing, then why bother? { FMOD_RESULT e; #ifdef HAVE_LIBGME // If the specified track is within the number of tracks playing, then change it if (gme) { if (track >= 0 && track < gme_track_count(gme)) { gme_err_t gme_e = gme_start_track(gme,track); if (gme_e == NULL) { current_track = track; return true; } else CONS_Alert(CONS_ERROR, "Encountered GME error: %s\n", gme_e); } return false; } #endif // HAVE_LIBGME // Try to set it via FMOD e = FMOD_Channel_SetPosition(music_channel, (UINT32)track, FMOD_TIMEUNIT_MODORDER); if (e == FMOD_OK) // We good { current_track = track; return true; } else if (e == FMOD_ERR_UNSUPPORTED // Only music modules, numbnuts! || e == FMOD_ERR_INVALID_POSITION) // Out-of-bounds! return false; else // Congrats, you horribly broke it somehow { FMR_MUSIC(e); return false; } } return false; }
// Hook for frame (after mobj and player thinkers) void LUAh_ThinkFrame(void) { hook_p hookp; if (!gL || !(hooksAvailable[hook_ThinkFrame/8] & (1<<(hook_ThinkFrame%8)))) return; for (hookp = roothook; hookp; hookp = hookp->next) if (hookp->type == hook_ThinkFrame) { lua_pushfstring(gL, FMT_HOOKID, hookp->id); lua_gettable(gL, LUA_REGISTRYINDEX); if (lua_pcall(gL, 0, 0, 0)) { if (!hookp->error || cv_debug & DBG_LUA) CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); lua_pop(gL, 1); hookp->error = true; } } }
// Hook for mobj collisions UINT8 LUAh_MobjCollideHook(mobj_t *thing1, mobj_t *thing2, enum hook which) { hook_p hookp; UINT8 shouldCollide = 0; // 0 = default, 1 = force yes, 2 = force no. if (!gL || !(hooksAvailable[which/8] & (1<<(which%8)))) return 0; lua_settop(gL, 0); for (hookp = roothook; hookp; hookp = hookp->next) if (hookp->type == which && (hookp->s.mt == MT_NULL || hookp->s.mt == thing1->type)) { if (lua_gettop(gL) == 0) { LUA_PushUserdata(gL, thing1, META_MOBJ); LUA_PushUserdata(gL, thing2, META_MOBJ); } lua_pushfstring(gL, FMT_HOOKID, hookp->id); lua_gettable(gL, LUA_REGISTRYINDEX); lua_pushvalue(gL, -3); lua_pushvalue(gL, -3); if (lua_pcall(gL, 2, 1, 0)) { if (!hookp->error || cv_debug & DBG_LUA) CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); lua_pop(gL, 1); hookp->error = true; continue; } if (!lua_isnil(gL, -1)) { // if nil, leave shouldCollide = 0. if (lua_toboolean(gL, -1)) shouldCollide = 1; // Force yes else shouldCollide = 2; // Force no } lua_pop(gL, 1); } lua_settop(gL, 0); return shouldCollide; }
// Hook for P_DamageMobj by mobj type (Mobj actually takes damage!) boolean LUAh_MobjDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage) { hook_p hookp; boolean hooked = false; if (!gL || !(hooksAvailable[hook_MobjDamage/8] & (1<<(hook_MobjDamage%8)))) return 0; lua_settop(gL, 0); for (hookp = roothook; hookp; hookp = hookp->next) if (hookp->type == hook_MobjDamage && (hookp->s.mt == MT_NULL || hookp->s.mt == target->type)) { if (lua_gettop(gL) == 0) { LUA_PushUserdata(gL, target, META_MOBJ); LUA_PushUserdata(gL, inflictor, META_MOBJ); LUA_PushUserdata(gL, source, META_MOBJ); lua_pushinteger(gL, damage); } lua_pushfstring(gL, FMT_HOOKID, hookp->id); lua_gettable(gL, LUA_REGISTRYINDEX); lua_pushvalue(gL, -5); lua_pushvalue(gL, -5); lua_pushvalue(gL, -5); lua_pushvalue(gL, -5); if (lua_pcall(gL, 4, 1, 0)) { if (!hookp->error || cv_debug & DBG_LUA) CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); lua_pop(gL, 1); hookp->error = true; continue; } if (lua_toboolean(gL, -1)) hooked = true; lua_pop(gL, 1); } lua_settop(gL, 0); return hooked; }
// Helper function for "objects" search static UINT8 lib_searchBlockmap_Objects(lua_State *L, INT32 x, INT32 y, mobj_t *thing) { mobj_t *mobj, *bnext = NULL; if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight) return 0; // Check interaction with the objects in the blockmap. for (mobj = blocklinks[y*bmapwidth + x]; mobj; mobj = bnext) { P_SetTarget(&bnext, mobj->bnext); // We want to note our reference to bnext here incase it is MF_NOTHINK and gets removed! if (mobj == thing) continue; // our thing just found itself, so move on lua_pushvalue(L, 1); // push function LUA_PushUserdata(L, thing, META_MOBJ); LUA_PushUserdata(L, mobj, META_MOBJ); if (lua_pcall(gL, 2, 1, 0)) { if (!blockfuncerror || cv_debug & DBG_LUA) CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); lua_pop(gL, 1); blockfuncerror = true; return 0; // *shrugs* } if (!lua_isnil(gL, -1)) { // if nil, continue if (lua_toboolean(gL, -1)) return 2; // stop whole search else return 1; // stop block search } lua_pop(gL, 1); if (P_MobjWasRemoved(thing) // func just popped our thing, cannot continue. || (bnext && P_MobjWasRemoved(bnext))) // func just broke blockmap chain, cannot continue. { P_SetTarget(&bnext, NULL); return (P_MobjWasRemoved(thing)) ? 2 : 1; } } return 0; }
// Hook for hurt messages boolean LUAh_HurtMsg(player_t *player, mobj_t *inflictor, mobj_t *source) { hook_p hookp; boolean hooked = false; if (!gL || !(hooksAvailable[hook_HurtMsg/8] & (1<<(hook_HurtMsg%8)))) return false; lua_settop(gL, 0); for (hookp = roothook; hookp; hookp = hookp->next) if (hookp->type == hook_HurtMsg && (hookp->s.mt == MT_NULL || (inflictor && hookp->s.mt == inflictor->type))) { if (lua_gettop(gL) == 0) { LUA_PushUserdata(gL, player, META_PLAYER); LUA_PushUserdata(gL, inflictor, META_MOBJ); LUA_PushUserdata(gL, source, META_MOBJ); } lua_pushfstring(gL, FMT_HOOKID, hookp->id); lua_gettable(gL, LUA_REGISTRYINDEX); lua_pushvalue(gL, -4); lua_pushvalue(gL, -4); lua_pushvalue(gL, -4); if (lua_pcall(gL, 3, 1, 0)) { if (!hookp->error || cv_debug & DBG_LUA) CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); lua_pop(gL, 1); hookp->error = true; continue; } if (lua_toboolean(gL, -1)) hooked = true; lua_pop(gL, 1); } lua_settop(gL, 0); return hooked; }
// Hook for P_TouchSpecialThing by mobj type boolean LUAh_TouchSpecial(mobj_t *special, mobj_t *toucher) { hook_p hookp; boolean hooked = false; if (!gL || !(hooksAvailable[hook_TouchSpecial/8] & (1<<(hook_TouchSpecial%8)))) return 0; lua_settop(gL, 0); for (hookp = roothook; hookp; hookp = hookp->next) if (hookp->type == hook_TouchSpecial && (hookp->s.mt == MT_NULL || hookp->s.mt == special->type)) { if (lua_gettop(gL) == 0) { LUA_PushUserdata(gL, special, META_MOBJ); LUA_PushUserdata(gL, toucher, META_MOBJ); } lua_pushfstring(gL, FMT_HOOKID, hookp->id); lua_gettable(gL, LUA_REGISTRYINDEX); lua_pushvalue(gL, -3); lua_pushvalue(gL, -3); if (lua_pcall(gL, 2, 1, 0)) { if (!hookp->error || cv_debug & DBG_LUA) CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); lua_pop(gL, 1); hookp->error = true; continue; } if (lua_toboolean(gL, -1)) hooked = true; lua_pop(gL, 1); } lua_settop(gL, 0); return hooked; }
// Hook for B_BuildTiccmd boolean LUAh_BotTiccmd(player_t *bot, ticcmd_t *cmd) { hook_p hookp; boolean hooked = false; if (!gL || !(hooksAvailable[hook_BotTiccmd/8] & (1<<(hook_BotTiccmd%8)))) return false; lua_settop(gL, 0); for (hookp = roothook; hookp; hookp = hookp->next) if (hookp->type == hook_BotTiccmd) { if (lua_gettop(gL) == 0) { LUA_PushUserdata(gL, bot, META_PLAYER); LUA_PushUserdata(gL, cmd, META_TICCMD); } lua_pushfstring(gL, FMT_HOOKID, hookp->id); lua_gettable(gL, LUA_REGISTRYINDEX); lua_pushvalue(gL, -3); lua_pushvalue(gL, -3); if (lua_pcall(gL, 2, 1, 0)) { if (!hookp->error || cv_debug & DBG_LUA) CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); lua_pop(gL, 1); hookp->error = true; continue; } if (lua_toboolean(gL, -1)) hooked = true; lua_pop(gL, 1); } lua_settop(gL, 0); return hooked; }
// // D_CheckNetGame // Works out player numbers among the net participants // boolean D_CheckNetGame(void) { boolean ret = false; InitAck(); rebound_tail = rebound_head = 0; statstarttic = I_GetTime(); I_NetGet = Internal_Get; I_NetSend = Internal_Send; I_NetCanSend = NULL; I_NetCloseSocket = NULL; I_NetFreeNodenum = Internal_FreeNodenum; I_NetMakeNodewPort = NULL; hardware_MAXPACKETLENGTH = MAXPACKETLENGTH; net_bandwidth = 30000; // I_InitNetwork sets doomcom and netgame // check and initialize the network driver multiplayer = false; // only dos version with external driver will return true netgame = I_InitNetwork(); if (!netgame && !I_NetOpenSocket) { D_SetDoomcom(); netgame = I_InitTcpNetwork(); } if (netgame) ret = true; if (!server && netgame) netgame = false; server = true; // WTF? server always true??? // no! The deault mode is server. Client is set elsewhere // when the client executes connect command. doomcom->ticdup = 1; if (M_CheckParm("-extratic")) { if (M_IsNextParm()) doomcom->extratics = (INT16)atoi(M_GetNextParm()); else doomcom->extratics = 1; CONS_Printf(M_GetText("Set extratics to %d\n"), doomcom->extratics); } if (M_CheckParm("-bandwidth")) { if (M_IsNextParm()) { net_bandwidth = atoi(M_GetNextParm()); if (net_bandwidth < 1000) net_bandwidth = 1000; if (net_bandwidth > 100000) hardware_MAXPACKETLENGTH = MAXPACKETLENGTH; CONS_Printf(M_GetText("Network bandwidth set to %d\n"), net_bandwidth); } else I_Error("usage: -bandwidth <byte_per_sec>"); } software_MAXPACKETLENGTH = hardware_MAXPACKETLENGTH; if (M_CheckParm("-packetsize")) { if (M_IsNextParm()) { INT32 p = atoi(M_GetNextParm()); if (p < 75) p = 75; if (p > hardware_MAXPACKETLENGTH) p = hardware_MAXPACKETLENGTH; software_MAXPACKETLENGTH = (UINT16)p; } else I_Error("usage: -packetsize <bytes_per_packet>"); } if (netgame) multiplayer = true; if (doomcom->id != DOOMCOM_ID) I_Error("Doomcom buffer invalid!"); if (doomcom->numnodes > MAXNETNODES) I_Error("Too many nodes (%d), max:%d", doomcom->numnodes, MAXNETNODES); netbuffer = (doomdata_t *)(void *)&doomcom->data; #ifdef DEBUGFILE #ifdef _arch_dreamcast //debugfile = stderr; if (debugfile) CONS_Printf(M_GetText("debug output to: %s\n"), "STDERR"); #else if (M_CheckParm("-debugfile")) { char filename[20]; INT32 k = doomcom->consoleplayer - 1; if (M_IsNextParm()) k = atoi(M_GetNextParm()) - 1; while (!debugfile && k < MAXPLAYERS) { k++; sprintf(filename, "debug%d.txt", k); debugfile = fopen(filename, "w"); } if (debugfile) CONS_Printf(M_GetText("debug output to: %s\n"), filename); else CONS_Alert(CONS_WARNING, M_GetText("cannot debug output to file %s!\n"), filename); } #endif #endif D_ClientServerInit(); return ret; }
void Got_Luacmd(UINT8 **cp, INT32 playernum) { UINT8 i, argc, flags; char buf[256]; // don't use I_Assert here, goto the deny code below // to clean up and kick people who try nefarious exploits // like sending random junk lua commands to crash the server if (!gL) goto deny; lua_getfield(gL, LUA_REGISTRYINDEX, "COM_Command"); // push COM_Command if (!lua_istable(gL, -1)) goto deny; argc = READUINT8(*cp); READSTRINGN(*cp, buf, 255); strlwr(buf); // must lowercase buffer lua_getfield(gL, -1, buf); // push command info table if (!lua_istable(gL, -1)) goto deny; lua_remove(gL, -2); // pop COM_Command lua_rawgeti(gL, -1, 2); // push flags from command info table if (lua_isboolean(gL, -1)) flags = (lua_toboolean(gL, -1) ? 1 : 0); else flags = (UINT8)lua_tointeger(gL, -1); lua_pop(gL, 1); // pop flags // requires server/admin and the player is not one of them if ((flags & 1) && playernum != serverplayer && !IsPlayerAdmin(playernum)) goto deny; lua_rawgeti(gL, -1, 1); // push function from command info table // although honestly this should be true anyway // BUT GODDAMNIT I SAID NO I_ASSERTS SO NO I_ASSERTS IT IS if (!lua_isfunction(gL, -1)) goto deny; lua_remove(gL, -2); // pop command info table LUA_PushUserdata(gL, &players[playernum], META_PLAYER); for (i = 1; i < argc; i++) { READSTRINGN(*cp, buf, 255); lua_pushstring(gL, buf); } LUA_Call(gL, (int)argc); // argc is 1-based, so this will cover the player we passed too. return; deny: //must be hacked/buggy client if (gL) // check if Lua is actually turned on first, you dummmy -- Monster Iestyn 04/07/18 lua_settop(gL, 0); // clear stack CONS_Alert(CONS_WARNING, M_GetText("Illegal lua command received from %s\n"), player_names[playernum]); if (server) { XBOXSTATIC UINT8 bufn[2]; bufn[0] = (UINT8)playernum; bufn[1] = KICK_MSG_CON_FAIL; SendNetXCmd(XD_KICK, &bufn, 2); } }
// allocate a socket static SOCKET_TYPE UDP_Bind(int family, struct sockaddr *addr, socklen_t addrlen) { SOCKET_TYPE s = socket(family, SOCK_DGRAM, IPPROTO_UDP); int opt; socklen_t opts; #ifdef FIONBIO #ifdef WATTCP char trueval = true; #else unsigned long trueval = true; #endif #endif mysockaddr_t straddr; if (s == (SOCKET_TYPE)ERRSOCKET || s == BADSOCKET) return (SOCKET_TYPE)ERRSOCKET; #ifdef USE_WINSOCK { // Alam_GBC: disable the new UDP connection reset behavior for Win2k and up #ifdef USE_WINSOCK2 DWORD dwBytesReturned = 0; BOOL bfalse = FALSE; WSAIoctl(s, SIO_UDP_CONNRESET, &bfalse, sizeof(bfalse), NULL, 0, &dwBytesReturned, NULL, NULL); #else unsigned long falseval = false; ioctl(s, SIO_UDP_CONNRESET, &falseval); #endif } #endif straddr.any = *addr; I_OutputMsg("Binding to %s\n", SOCK_AddrToStr(&straddr)); if (family == AF_INET) { mysockaddr_t tmpaddr; tmpaddr.any = *addr ; if (tmpaddr.ip4.sin_addr.s_addr == htonl(INADDR_ANY)) { opt = true; opts = (socklen_t)sizeof(opt); setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, opts); } // make it broadcastable opt = true; opts = (socklen_t)sizeof(opt); if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, (char *)&opt, opts)) { CONS_Alert(CONS_WARNING, M_GetText("Could not get broadcast rights\n")); // I do not care anymore } } #ifdef HAVE_IPV6 else if (family == AF_INET6) { if (memcmp(addr, &in6addr_any, sizeof(in6addr_any)) == 0) //IN6_ARE_ADDR_EQUAL { opt = true; opts = (socklen_t)sizeof(opt); setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, opts); } #ifdef IPV6_V6ONLY // make it IPv6 ony opt = true; opts = (socklen_t)sizeof(opt); if (setsockopt(s, SOL_SOCKET, IPV6_V6ONLY, (char *)&opt, opts)) { CONS_Alert(CONS_WARNING, M_GetText("Could not limit IPv6 bind\n")); // I do not care anymore } #endif } #endif if (bind(s, addr, addrlen) == ERRSOCKET) { close(s); I_OutputMsg("Binding failed\n"); return (SOCKET_TYPE)ERRSOCKET; } #ifdef FIONBIO // make it non blocking opt = true; if (ioctl(s, FIONBIO, &trueval) != 0) { close(s); I_OutputMsg("Seting FIOBIO on failed\n"); return (SOCKET_TYPE)ERRSOCKET; } #endif opts = (socklen_t)sizeof(opt); getsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *)&opt, &opts); CONS_Printf(M_GetText("Network system buffer: %dKb\n"), opt>>10); if (opt < 64<<10) // 64k { opt = 64<<10; opts = (socklen_t)sizeof(opt); setsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *)&opt, opts); getsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *)&opt, &opts); if (opt < 64<<10) CONS_Alert(CONS_WARNING, M_GetText("Can't set buffer length to 64k, file transfer will be bad\n")); else CONS_Printf(M_GetText("Network system buffer set to: %dKb\n"), opt>>10); }
void *I_GetSfx(sfxinfo_t *sfx) { FMOD_SOUND *sound; char *lump; FMOD_CREATESOUNDEXINFO fmt; #ifdef HAVE_LIBGME Music_Emu *emu; gme_info_t *info; #endif if (sfx->lumpnum == LUMPERROR) sfx->lumpnum = S_GetSfxLumpNum(sfx); sfx->length = W_LumpLength(sfx->lumpnum); lump = W_CacheLumpNum(sfx->lumpnum, PU_SOUND); sound = ds2fmod(lump); if (sound) { Z_Free(lump); return sound; } // It's not a doom sound. // Try to read it as an FMOD sound? memset(&fmt, 0, sizeof(FMOD_CREATESOUNDEXINFO)); fmt.cbsize = sizeof(FMOD_CREATESOUNDEXINFO); fmt.length = sfx->length; #ifdef HAVE_LIBGME // VGZ format if ((UINT8)lump[0] == 0x1F && (UINT8)lump[1] == 0x8B) { #ifdef HAVE_ZLIB UINT8 *inflatedData; size_t inflatedLen; z_stream stream; int zErr; // Somewhere to handle any error messages zlib tosses out memset(&stream, 0x00, sizeof (z_stream)); // Init zlib stream // Begin the inflation process inflatedLen = *(UINT32 *)(lump + (sfx->length-4)); // Last 4 bytes are the decompressed size, typically inflatedData = (UINT8 *)Z_Malloc(inflatedLen, PU_SOUND, NULL); // Make room for the decompressed data stream.total_in = stream.avail_in = sfx->length; stream.total_out = stream.avail_out = inflatedLen; stream.next_in = (UINT8 *)lump; stream.next_out = inflatedData; zErr = inflateInit2(&stream, 32 + MAX_WBITS); if (zErr == Z_OK) // We're good to go { zErr = inflate(&stream, Z_FINISH); if (zErr == Z_STREAM_END) { // Run GME on new data if (!gme_open_data(inflatedData, inflatedLen, &emu, 44100)) { Z_Free(inflatedData); // GME supposedly makes a copy for itself, so we don't need this lying around Z_Free(lump); // We're done with the uninflated lump now, too. gme_start_track(emu, 0); gme_track_info(emu, &info, 0); fmt.format = FMOD_SOUND_FORMAT_PCM16; fmt.defaultfrequency = 44100; fmt.numchannels = 2; fmt.length = ((UINT32)info->play_length * 441 / 10) * 4; fmt.decodebuffersize = (44100 * 2) / 35; fmt.pcmreadcallback = GMEReadCallback; fmt.userdata = emu; FMR(FMOD_System_CreateSound(fsys, NULL, FMOD_CREATESAMPLE|FMOD_OPENUSER|FMOD_SOFTWARE|FMOD_LOWMEM, &fmt, &sound)); return sound; } } else { const char *errorType; switch (zErr) { case Z_ERRNO: errorType = "Z_ERRNO"; break; case Z_STREAM_ERROR: errorType = "Z_STREAM_ERROR"; break; case Z_DATA_ERROR: errorType = "Z_DATA_ERROR"; break; case Z_MEM_ERROR: errorType = "Z_MEM_ERROR"; break; case Z_BUF_ERROR: errorType = "Z_BUF_ERROR"; break; case Z_VERSION_ERROR: errorType = "Z_VERSION_ERROR"; break; default: errorType = "unknown error"; } CONS_Alert(CONS_ERROR,"Encountered %s when running inflate: %s\n", errorType, stream.msg); } (void)inflateEnd(&stream); } else // Hold up, zlib's got a problem { const char *errorType; switch (zErr) { case Z_ERRNO: errorType = "Z_ERRNO"; break; case Z_STREAM_ERROR: errorType = "Z_STREAM_ERROR"; break; case Z_DATA_ERROR: errorType = "Z_DATA_ERROR"; break; case Z_MEM_ERROR: errorType = "Z_MEM_ERROR"; break; case Z_BUF_ERROR: errorType = "Z_BUF_ERROR"; break; case Z_VERSION_ERROR: errorType = "Z_VERSION_ERROR"; break; default: errorType = "unknown error"; } CONS_Alert(CONS_ERROR,"Encountered %s when running inflateInit: %s\n", errorType, stream.msg); } Z_Free(inflatedData); // GME didn't open jack, but don't let that stop us from freeing this up #else //CONS_Alert(CONS_ERROR,"Cannot decompress VGZ; no zlib support\n"); #endif } // Try to read it as a GME sound else if (!gme_open_data(lump, sfx->length, &emu, 44100)) { Z_Free(lump); gme_start_track(emu, 0); gme_track_info(emu, &info, 0); fmt.format = FMOD_SOUND_FORMAT_PCM16; fmt.defaultfrequency = 44100; fmt.numchannels = 2; fmt.length = ((UINT32)info->play_length * 441 / 10) * 4; fmt.decodebuffersize = (44100 * 2) / 35; fmt.pcmreadcallback = GMEReadCallback; fmt.userdata = emu; gme_free_info(info); FMR(FMOD_System_CreateSound(fsys, NULL, FMOD_CREATESAMPLE|FMOD_OPENUSER|FMOD_SOFTWARE|FMOD_LOWMEM, &fmt, &sound)); return sound; } #endif // Ogg, Mod, Midi, etc. FMR(FMOD_System_CreateSound(fsys, lump, FMOD_CREATESAMPLE|FMOD_OPENMEMORY|FMOD_SOFTWARE|FMOD_LOWMEM, &fmt, &sound)); Z_Free(lump); // We're done with the lump now, at least. return sound; }
boolean I_StartDigSong(const char *musicname, boolean looping) { char *data; size_t len; FMOD_CREATESOUNDEXINFO fmt; lumpnum_t lumpnum = W_CheckNumForName(va("O_%s",musicname)); if (lumpnum == LUMPERROR) { lumpnum = W_CheckNumForName(va("D_%s",musicname)); if (lumpnum == LUMPERROR) return false; midimode = true; } else midimode = false; data = (char *)W_CacheLumpNum(lumpnum, PU_MUSIC); len = W_LumpLength(lumpnum); memset(&fmt, 0, sizeof(FMOD_CREATESOUNDEXINFO)); fmt.cbsize = sizeof(FMOD_CREATESOUNDEXINFO); #ifdef HAVE_LIBGME if ((UINT8)data[0] == 0x1F && (UINT8)data[1] == 0x8B) { #ifdef HAVE_ZLIB UINT8 *inflatedData; size_t inflatedLen; z_stream stream; int zErr; // Somewhere to handle any error messages zlib tosses out memset(&stream, 0x00, sizeof (z_stream)); // Init zlib stream // Begin the inflation process inflatedLen = *(UINT32 *)(data + (len-4)); // Last 4 bytes are the decompressed size, typically inflatedData = (UINT8 *)Z_Calloc(inflatedLen, PU_MUSIC, NULL); // Make room for the decompressed data stream.total_in = stream.avail_in = len; stream.total_out = stream.avail_out = inflatedLen; stream.next_in = (UINT8 *)data; stream.next_out = inflatedData; zErr = inflateInit2(&stream, 32 + MAX_WBITS); if (zErr == Z_OK) // We're good to go { zErr = inflate(&stream, Z_FINISH); if (zErr == Z_STREAM_END) { // Run GME on new data if (!gme_open_data(inflatedData, inflatedLen, &gme, 44100)) { gme_equalizer_t gmeq = {GME_TREBLE, GME_BASS, 0,0,0,0,0,0,0,0}; Z_Free(inflatedData); // GME supposedly makes a copy for itself, so we don't need this lying around Z_Free(data); // We don't need this, either. gme_start_track(gme, 0); current_track = 0; gme_set_equalizer(gme,&gmeq); fmt.format = FMOD_SOUND_FORMAT_PCM16; fmt.defaultfrequency = 44100; fmt.numchannels = 2; fmt.length = -1; fmt.decodebuffersize = (44100 * 2) / 35; fmt.pcmreadcallback = GMEReadCallback; fmt.userdata = gme; FMR(FMOD_System_CreateStream(fsys, NULL, FMOD_OPENUSER | (looping ? FMOD_LOOP_NORMAL : 0), &fmt, &music_stream)); FMR(FMOD_System_PlaySound(fsys, FMOD_CHANNEL_FREE, music_stream, false, &music_channel)); FMR(FMOD_Channel_SetVolume(music_channel, music_volume / 31.0)); FMR(FMOD_Channel_SetPriority(music_channel, 0)); return true; } } else { const char *errorType; switch (zErr) { case Z_ERRNO: errorType = "Z_ERRNO"; break; case Z_STREAM_ERROR: errorType = "Z_STREAM_ERROR"; break; case Z_DATA_ERROR: errorType = "Z_DATA_ERROR"; break; case Z_MEM_ERROR: errorType = "Z_MEM_ERROR"; break; case Z_BUF_ERROR: errorType = "Z_BUF_ERROR"; break; case Z_VERSION_ERROR: errorType = "Z_VERSION_ERROR"; break; default: errorType = "unknown error"; } CONS_Alert(CONS_ERROR,"Encountered %s when running inflate: %s\n", errorType, stream.msg); } (void)inflateEnd(&stream); } else // Hold up, zlib's got a problem { const char *errorType; switch (zErr) { case Z_ERRNO: errorType = "Z_ERRNO"; break; case Z_STREAM_ERROR: errorType = "Z_STREAM_ERROR"; break; case Z_DATA_ERROR: errorType = "Z_DATA_ERROR"; break; case Z_MEM_ERROR: errorType = "Z_MEM_ERROR"; break; case Z_BUF_ERROR: errorType = "Z_BUF_ERROR"; break; case Z_VERSION_ERROR: errorType = "Z_VERSION_ERROR"; break; default: errorType = "unknown error"; } CONS_Alert(CONS_ERROR,"Encountered %s when running inflateInit: %s\n", errorType, stream.msg); } Z_Free(inflatedData); // GME didn't open jack, but don't let that stop us from freeing this up #else //CONS_Alert(CONS_ERROR,"Cannot decompress VGZ; no zlib support\n"); #endif } else if (!gme_open_data(data, len, &gme, 44100)) { gme_equalizer_t gmeq = {GME_TREBLE, GME_BASS, 0,0,0,0,0,0,0,0}; Z_Free(data); // We don't need this anymore. gme_start_track(gme, 0); current_track = 0; gme_set_equalizer(gme,&gmeq); fmt.format = FMOD_SOUND_FORMAT_PCM16; fmt.defaultfrequency = 44100; fmt.numchannels = 2; fmt.length = -1; fmt.decodebuffersize = (44100 * 2) / 35; fmt.pcmreadcallback = GMEReadCallback; fmt.userdata = gme; FMR(FMOD_System_CreateStream(fsys, NULL, FMOD_OPENUSER | (looping ? FMOD_LOOP_NORMAL : 0), &fmt, &music_stream)); FMR(FMOD_System_PlaySound(fsys, FMOD_CHANNEL_FREE, music_stream, false, &music_channel)); FMR(FMOD_Channel_SetVolume(music_channel, music_volume / 31.0)); FMR(FMOD_Channel_SetPriority(music_channel, 0)); return true; } #endif fmt.length = len; { FMOD_RESULT e = FMOD_System_CreateStream(fsys, data, FMOD_OPENMEMORY_POINT|(looping ? FMOD_LOOP_NORMAL : 0), &fmt, &music_stream); if (e != FMOD_OK) { if (e == FMOD_ERR_FORMAT) CONS_Alert(CONS_WARNING, "Failed to play music lump %s due to invalid format.\n", W_CheckNameForNum(lumpnum)); else FMR(e); return false; } } FMR(FMOD_System_PlaySound(fsys, FMOD_CHANNEL_FREE, music_stream, false, &music_channel)); if (midimode) FMR(FMOD_Channel_SetVolume(music_channel, midi_volume / 31.0)); else FMR(FMOD_Channel_SetVolume(music_channel, music_volume / 31.0)); FMR(FMOD_Channel_SetPriority(music_channel, 0)); current_track = 0; // Try to find a loop point in streaming music formats (ogg, mp3) if (looping) { FMOD_RESULT e; FMOD_TAG tag; unsigned int loopstart, loopend; // A proper LOOPPOINT is its own tag, stupid. e = FMOD_Sound_GetTag(music_stream, "LOOPPOINT", 0, &tag); if (e != FMOD_ERR_TAGNOTFOUND) { FMR(e); loopstart = atoi((char *)tag.data); // assumed to be a string data tag. FMR(FMOD_Sound_GetLoopPoints(music_stream, NULL, FMOD_TIMEUNIT_PCM, &loopend, FMOD_TIMEUNIT_PCM)); if (loopstart > 0) FMR(FMOD_Sound_SetLoopPoints(music_stream, loopstart, FMOD_TIMEUNIT_PCM, loopend, FMOD_TIMEUNIT_PCM)); return true; } // Use LOOPMS for time in miliseconds. e = FMOD_Sound_GetTag(music_stream, "LOOPMS", 0, &tag); if (e != FMOD_ERR_TAGNOTFOUND) { FMR(e); loopstart = atoi((char *)tag.data); // assumed to be a string data tag. FMR(FMOD_Sound_GetLoopPoints(music_stream, NULL, FMOD_TIMEUNIT_MS, &loopend, FMOD_TIMEUNIT_PCM)); if (loopstart > 0) FMR(FMOD_Sound_SetLoopPoints(music_stream, loopstart, FMOD_TIMEUNIT_MS, loopend, FMOD_TIMEUNIT_PCM)); return true; } // Try to fetch it from the COMMENT tag, like A.J. Freda e = FMOD_Sound_GetTag(music_stream, "COMMENT", 0, &tag); if (e != FMOD_ERR_TAGNOTFOUND) { char *loopText; // Handle any errors that arose, first FMR(e); // Figure out where the number starts loopText = strstr((char *)tag.data,"LOOPPOINT="); if (loopText != NULL) { // Skip the "LOOPPOINT=" part. loopText += 10; // Convert it to our looppoint // FMOD seems to ensure the tag is properly NULL-terminated. // atoi will stop when it reaches anything that's not a number. loopstart = atoi(loopText); // Now do the rest like above FMR(FMOD_Sound_GetLoopPoints(music_stream, NULL, FMOD_TIMEUNIT_PCM, &loopend, FMOD_TIMEUNIT_PCM)); if (loopstart > 0) FMR(FMOD_Sound_SetLoopPoints(music_stream, loopstart, FMOD_TIMEUNIT_PCM, loopend, FMOD_TIMEUNIT_PCM)); } return true; } } // No special loop point, but we're playing so it's all good. return true; }
// spew console messages for general errors. static void FMR_Debug(FMOD_RESULT e, int line) { if (e != FMOD_OK) CONS_Alert(CONS_ERROR, "FMOD:%d - %s\n", line, FMOD_ErrorString(e)); }