diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5740e3f6e8..6195ac57b1 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -391,6 +391,12 @@ if( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE ) set( CMAKE_CXX_FLAGS "-Wno-unknown-pragmas -Wno-comment -Wno-format ${CMAKE_CXX_FLAGS}" ) endif() + # Detect FreeBSD and add flags + if(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD") + set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC" ) + set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC" ) + endif() + if( NOT NO_STRIP ) set (CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} -s" ) set (CMAKE_EXE_LINKER_FLAGS_MINSIZEREL "${CMAKE_EXE_LINKER_FLAGS_MINSIZEREL} -s" ) diff --git a/src/g_inventory/a_weapons.cpp b/src/g_inventory/a_weapons.cpp index a0458ce3ea..e0d98529be 100644 --- a/src/g_inventory/a_weapons.cpp +++ b/src/g_inventory/a_weapons.cpp @@ -231,7 +231,7 @@ bool AWeapon::CheckAmmo(int fireMode, bool autoSwitch, bool requireAmmo, int amm VMCall(func, params, 5, &ret, 1); return !!retval; } - return CheckAmmo(fireMode, autoSwitch, requireAmmo, ammocount); + return DoCheckAmmo(fireMode, autoSwitch, requireAmmo, ammocount); } bool AWeapon::DoCheckAmmo (int fireMode, bool autoSwitch, bool requireAmmo, int ammocount) diff --git a/src/gl/compatibility/gl_20.cpp b/src/gl/compatibility/gl_20.cpp index d714d3e46d..3321f95613 100644 --- a/src/gl/compatibility/gl_20.cpp +++ b/src/gl/compatibility/gl_20.cpp @@ -459,8 +459,8 @@ bool gl_SetupLight(int group, Plane & p, ADynamicLight * light, FVector3 & nearP bool gl_SetupLightTexture() { - if (GLRenderer->gllight == nullptr) return false; - FMaterial * pat = FMaterial::ValidateTexture(GLRenderer->gllight, false); + if (!GLRenderer->glLight.isValid()) return false; + FMaterial * pat = FMaterial::ValidateTexture(GLRenderer->glLight, false, false); gl_RenderState.SetMaterial(pat, CLAMP_XY_NOMIP, 0, -1, false); return true; } diff --git a/src/gl/renderer/gl_renderer.cpp b/src/gl/renderer/gl_renderer.cpp index 74bd81c177..212fc704c7 100644 --- a/src/gl/renderer/gl_renderer.cpp +++ b/src/gl/renderer/gl_renderer.cpp @@ -104,7 +104,6 @@ FGLRenderer::FGLRenderer(OpenGLFrameBuffer *fb) mSkyVBO = nullptr; gl_spriteindex = 0; mShaderManager = nullptr; - gllight = glpart2 = glpart = mirrortexture = nullptr; mLights = nullptr; m2DDrawer = nullptr; mTonemapPalette = nullptr; @@ -163,6 +162,8 @@ void FGLRenderer::Initialize(int width, int height) mCustomPostProcessShaders = new FCustomPostProcessShaders(); m2DDrawer = new F2DDrawer; + GetSpecialTextures(); + // needed for the core profile, because someone decided it was a good idea to remove the default VAO. if (!gl.legacyMode) { @@ -172,11 +173,6 @@ void FGLRenderer::Initialize(int width, int height) } else mVAOID = 0; - if (gl.legacyMode) gllight = FTexture::CreateTexture(Wads.GetNumForFullName("glstuff/gllight.png"), FTexture::TEX_MiscPatch); - glpart2 = FTexture::CreateTexture(Wads.GetNumForFullName("glstuff/glpart2.png"), FTexture::TEX_MiscPatch); - glpart = FTexture::CreateTexture(Wads.GetNumForFullName("glstuff/glpart.png"), FTexture::TEX_MiscPatch); - mirrortexture = FTexture::CreateTexture(Wads.GetNumForFullName("glstuff/mirror.png"), FTexture::TEX_MiscPatch); - mVBO = new FFlatVertexBuffer(width, height); mSkyVBO = new FSkyVertexBuffer; if (!gl.legacyMode) mLights = new FLightBuffer(); @@ -206,10 +202,6 @@ FGLRenderer::~FGLRenderer() if (mVBO != NULL) delete mVBO; if (mSkyVBO != NULL) delete mSkyVBO; if (mLights != NULL) delete mLights; - if (glpart2) delete glpart2; - if (glpart) delete glpart; - if (gllight) delete gllight; - if (mirrortexture) delete mirrortexture; if (mFBID != 0) glDeleteFramebuffers(1, &mFBID); if (mVAOID != 0) { @@ -241,6 +233,16 @@ FGLRenderer::~FGLRenderer() delete mFXAALumaShader; } + +void FGLRenderer::GetSpecialTextures() +{ + if (gl.legacyMode) glLight = TexMan.CheckForTexture("glstuff/gllight.png", FTexture::TEX_MiscPatch); + glPart2 = TexMan.CheckForTexture("glstuff/glpart2.png", FTexture::TEX_MiscPatch); + glPart = TexMan.CheckForTexture("glstuff/glpart.png", FTexture::TEX_MiscPatch); + mirrorTexture = TexMan.CheckForTexture("glstuff/mirror.png", FTexture::TEX_MiscPatch); + +} + //========================================================================== // // Calculates the viewport values needed for 2D and 3D operations diff --git a/src/gl/renderer/gl_renderer.h b/src/gl/renderer/gl_renderer.h index eb71cd4bed..1f68fe57e4 100644 --- a/src/gl/renderer/gl_renderer.h +++ b/src/gl/renderer/gl_renderer.h @@ -133,10 +133,10 @@ public: FShadowMap mShadowMap; - FTexture *gllight; - FTexture *glpart2; - FTexture *glpart; - FTexture *mirrortexture; + FTextureID glLight; + FTextureID glPart2; + FTextureID glPart; + FTextureID mirrorTexture; float mSky1Pos, mSky2Pos; @@ -190,6 +190,7 @@ public: void CopyToBackbuffer(const GL_IRECT *bounds, bool applyGamma); void DrawPresentTexture(const GL_IRECT &box, bool applyGamma); void Flush(); + void GetSpecialTextures(); bool StartOffscreen(); diff --git a/src/gl/scene/gl_sprite.cpp b/src/gl/scene/gl_sprite.cpp index a1816acd21..0fb7945cec 100644 --- a/src/gl/scene/gl_sprite.cpp +++ b/src/gl/scene/gl_sprite.cpp @@ -1185,19 +1185,20 @@ void GLSprite::ProcessParticle (particle_t *particle, sector_t *sector)//, int s // [BB] Load the texture for round or smooth particles if (gl_particles_style) { - FTexture *lump = NULL; + FTextureID lump; if (gl_particles_style == 1) { - lump = GLRenderer->glpart2; + lump = GLRenderer->glPart2; } else if (gl_particles_style == 2) { - lump = GLRenderer->glpart; + lump = GLRenderer->glPart; } + else lump.SetNull(); - if (lump != NULL) + if (lump.isValid()) { - gltexture = FMaterial::ValidateTexture(lump, true); + gltexture = FMaterial::ValidateTexture(lump, true, false); translation = 0; ul = gltexture->GetUL(); diff --git a/src/gl/scene/gl_walls_draw.cpp b/src/gl/scene/gl_walls_draw.cpp index d69ceeb4dd..beeb103a0f 100644 --- a/src/gl/scene/gl_walls_draw.cpp +++ b/src/gl/scene/gl_walls_draw.cpp @@ -260,7 +260,7 @@ void GLWall::RenderFogBoundary() //========================================================================== void GLWall::RenderMirrorSurface() { - if (GLRenderer->mirrortexture == NULL) return; + if (!GLRenderer->mirrorTexture.isValid()) return; // For the sphere map effect we need a normal of the mirror surface, FVector3 v = glseg.Normal(); @@ -288,7 +288,7 @@ void GLWall::RenderMirrorSurface() gl_RenderState.AlphaFunc(GL_GREATER,0); glDepthFunc(GL_LEQUAL); - FMaterial * pat=FMaterial::ValidateTexture(GLRenderer->mirrortexture, false); + FMaterial * pat=FMaterial::ValidateTexture(GLRenderer->mirrorTexture, false, false); gl_RenderState.SetMaterial(pat, CLAMP_NONE, 0, -1, false); flags &= ~GLWF_GLOW; diff --git a/src/gl/system/gl_extlist.txt b/src/gl/system/gl_extlist.txt index b67d21f10e..d0999b3e2a 100644 --- a/src/gl/system/gl_extlist.txt +++ b/src/gl/system/gl_extlist.txt @@ -3,7 +3,6 @@ -- Generation of gl_load.c and gl_load.h files: -- > lua LoadGen.lua -style=pointer_c -spec=gl -version=3.3 -profile=compatibility -extfile=gl_extlist.txt load -APPLE_client_storage ARB_buffer_storage ARB_shader_storage_buffer_object ARB_texture_compression @@ -14,4 +13,3 @@ EXT_texture_filter_anisotropic EXT_texture_sRGB KHR_debug ARB_invalidate_subdata -EXT_abgr diff --git a/src/gl/system/gl_framebuffer.cpp b/src/gl/system/gl_framebuffer.cpp index d6eb142d80..8549abb7e6 100644 --- a/src/gl/system/gl_framebuffer.cpp +++ b/src/gl/system/gl_framebuffer.cpp @@ -551,6 +551,7 @@ void OpenGLFrameBuffer::GameRestart() UpdatePalette (); ScreenshotBuffer = NULL; gl_GenerateGlobalBrightmapFromColormap(); + GLRenderer->GetSpecialTextures(); } diff --git a/src/gl/system/gl_load.c b/src/gl/system/gl_load.c index b0608ecb52..12734d7153 100644 --- a/src/gl/system/gl_load.c +++ b/src/gl/system/gl_load.c @@ -97,7 +97,6 @@ static PROC WinGetProcAddress(const char *name) #endif #endif -int ogl_ext_APPLE_client_storage = ogl_LOAD_FAILED; int ogl_ext_ARB_buffer_storage = ogl_LOAD_FAILED; int ogl_ext_ARB_shader_storage_buffer_object = ogl_LOAD_FAILED; int ogl_ext_ARB_texture_compression = ogl_LOAD_FAILED; @@ -108,7 +107,6 @@ int ogl_ext_EXT_texture_filter_anisotropic = ogl_LOAD_FAILED; int ogl_ext_EXT_texture_sRGB = ogl_LOAD_FAILED; int ogl_ext_KHR_debug = ogl_LOAD_FAILED; int ogl_ext_ARB_invalidate_subdata = ogl_LOAD_FAILED; -int ogl_ext_EXT_abgr = ogl_LOAD_FAILED; void (CODEGEN_FUNCPTR *_ptrc_glBufferStorage)(GLenum target, GLsizeiptr size, const void * data, GLbitfield flags) = NULL; @@ -2388,8 +2386,7 @@ typedef struct ogl_StrToExtMap_s PFN_LOADFUNCPOINTERS LoadExtension; } ogl_StrToExtMap; -static ogl_StrToExtMap ExtensionMap[12] = { - {"GL_APPLE_client_storage", &ogl_ext_APPLE_client_storage, NULL}, +static ogl_StrToExtMap ExtensionMap[10] = { {"GL_ARB_buffer_storage", &ogl_ext_ARB_buffer_storage, Load_ARB_buffer_storage}, {"GL_ARB_shader_storage_buffer_object", &ogl_ext_ARB_shader_storage_buffer_object, Load_ARB_shader_storage_buffer_object}, {"GL_ARB_texture_compression", &ogl_ext_ARB_texture_compression, Load_ARB_texture_compression}, @@ -2400,10 +2397,9 @@ static ogl_StrToExtMap ExtensionMap[12] = { {"GL_EXT_texture_sRGB", &ogl_ext_EXT_texture_sRGB, NULL}, {"GL_KHR_debug", &ogl_ext_KHR_debug, Load_KHR_debug}, {"GL_ARB_invalidate_subdata", &ogl_ext_ARB_invalidate_subdata, Load_ARB_invalidate_subdata}, - {"GL_EXT_abgr", &ogl_ext_EXT_abgr, NULL}, }; -static int g_extensionMapSize = 12; +static int g_extensionMapSize = 10; static ogl_StrToExtMap *FindExtEntry(const char *extensionName) { @@ -2420,7 +2416,6 @@ static ogl_StrToExtMap *FindExtEntry(const char *extensionName) static void ClearExtensionVars(void) { - ogl_ext_APPLE_client_storage = ogl_LOAD_FAILED; ogl_ext_ARB_buffer_storage = ogl_LOAD_FAILED; ogl_ext_ARB_shader_storage_buffer_object = ogl_LOAD_FAILED; ogl_ext_ARB_texture_compression = ogl_LOAD_FAILED; @@ -2431,7 +2426,6 @@ static void ClearExtensionVars(void) ogl_ext_EXT_texture_sRGB = ogl_LOAD_FAILED; ogl_ext_KHR_debug = ogl_LOAD_FAILED; ogl_ext_ARB_invalidate_subdata = ogl_LOAD_FAILED; - ogl_ext_EXT_abgr = ogl_LOAD_FAILED; } diff --git a/src/gl/system/gl_load.h b/src/gl/system/gl_load.h index eb9b0e321c..ee849aaf96 100644 --- a/src/gl/system/gl_load.h +++ b/src/gl/system/gl_load.h @@ -107,7 +107,6 @@ typedef unsigned int GLhandleARB; extern "C" { #endif /*__cplusplus*/ -extern int ogl_ext_APPLE_client_storage; extern int ogl_ext_ARB_buffer_storage; extern int ogl_ext_ARB_shader_storage_buffer_object; extern int ogl_ext_ARB_texture_compression; @@ -118,9 +117,6 @@ extern int ogl_ext_EXT_texture_filter_anisotropic; extern int ogl_ext_EXT_texture_sRGB; extern int ogl_ext_KHR_debug; extern int ogl_ext_ARB_invalidate_subdata; -extern int ogl_ext_EXT_abgr; - -#define GL_UNPACK_CLIENT_STORAGE_APPLE 0x85B2 #define GL_BUFFER_IMMUTABLE_STORAGE 0x821F #define GL_BUFFER_STORAGE_FLAGS 0x8220 @@ -286,8 +282,6 @@ extern int ogl_ext_EXT_abgr; #define GL_STACK_UNDERFLOW 0x0504 #define GL_VERTEX_ARRAY 0x8074 -#define GL_ABGR_EXT 0x8000 - #define GL_2D 0x0600 #define GL_2_BYTES 0x1407 #define GL_3D 0x0601 diff --git a/src/p_acs.cpp b/src/p_acs.cpp index e36cecde78..1b6c472d80 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -1284,7 +1284,7 @@ int ACSStringPool::InsertString(FString &str, unsigned int h, unsigned int bucke } if (index == Pool.Size()) { // There were no free entries; make a new one. - Pool.Reserve(1); + Pool.Reserve(MIN_GC_SIZE); FirstFreeEntry++; } else diff --git a/src/p_map.cpp b/src/p_map.cpp index 8e445862a3..7d5a79de88 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -6380,6 +6380,11 @@ void P_DoCrunch(AActor *thing, FChangePosition *cpos) mo->Translation = thing->BloodTranslation; } + if (mo->flags5 & MF5_PUFFGETSOWNER) + { + mo->target = thing; + } + if (!(cl_bloodtype <= 1)) mo->renderflags |= RF_INVISIBLE; } diff --git a/src/posix/cocoa/i_main.mm b/src/posix/cocoa/i_main.mm index f5c49eaafb..d528a0f72c 100644 --- a/src/posix/cocoa/i_main.mm +++ b/src/posix/cocoa/i_main.mm @@ -169,10 +169,6 @@ static void I_DetectOS() "32-bit Intel"; #elif defined __x86_64__ "64-bit Intel"; -#elif defined __ppc__ - "32-bit PowerPC"; -#elif defined __ppc64__ - "64-bit PowerPC"; #else "Unknown"; #endif diff --git a/src/posix/cocoa/i_video.mm b/src/posix/cocoa/i_video.mm index a1bc96403c..fdc3522d3b 100644 --- a/src/posix/cocoa/i_video.mm +++ b/src/posix/cocoa/i_video.mm @@ -112,13 +112,7 @@ EXTERN_CVAR(Bool, ticker ) EXTERN_CVAR(Bool, vid_vsync) EXTERN_CVAR(Bool, vid_hidpi) -#if defined __ppc__ || defined __ppc64__ -static const bool TRUECOLOR_DEFAULT = false; -#else // other than PowerPC -static const bool TRUECOLOR_DEFAULT = true; -#endif // PowerPC - -CUSTOM_CVAR(Bool, swtruecolor, TRUECOLOR_DEFAULT, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) +CUSTOM_CVAR(Bool, swtruecolor, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) { // Strictly speaking this doesn't require a mode switch, but it is the easiest // way to force a CreateFramebuffer call without a lot of refactoring. @@ -892,7 +886,6 @@ CocoaFrameBuffer::CocoaFrameBuffer(int width, int height, bool bgra, bool fullsc glGenTextures(1, &m_texture); glBindTexture(GL_TEXTURE_RECTANGLE_ARB, m_texture); - glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE); glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST); @@ -1103,16 +1096,8 @@ void CocoaFrameBuffer::Flip() rbOpts.dirty = false; } -#ifdef __LITTLE_ENDIAN__ - static const GLenum format = GL_RGBA; -#else // __BIG_ENDIAN__ - static const GLenum format = GL_ABGR_EXT; -#endif // __LITTLE_ENDIAN__ - - if (IsBgra()) - glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8, Width, Height, 0, GL_BGRA, GL_UNSIGNED_BYTE, m_pixelBuffer); - else - glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8, Width, Height, 0, format, GL_UNSIGNED_BYTE, m_pixelBuffer); + const GLenum format = IsBgra() ? GL_BGRA : GL_RGBA; + glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8, Width, Height, 0, format, GL_UNSIGNED_BYTE, m_pixelBuffer); glBegin(GL_QUADS); glColor4f(1.0f, 1.0f, 1.0f, 1.0f); diff --git a/src/posix/osx/iwadpicker_cocoa.mm b/src/posix/osx/iwadpicker_cocoa.mm index 2d8b2057c4..2792e86302 100644 --- a/src/posix/osx/iwadpicker_cocoa.mm +++ b/src/posix/osx/iwadpicker_cocoa.mm @@ -381,10 +381,6 @@ static NSString* GetArchitectureString() return @"i386"; #elif defined __x86_64__ return @"x86_64"; -#elif defined __ppc__ - return @"ppc"; -#elif defined __ppc64__ - return @"ppc64"; #endif } diff --git a/src/scripting/backend/codegen.cpp b/src/scripting/backend/codegen.cpp index a9c7e4fcc9..96393ad4c1 100644 --- a/src/scripting/backend/codegen.cpp +++ b/src/scripting/backend/codegen.cpp @@ -5945,7 +5945,7 @@ FxRandomSeed::~FxRandomSeed() FxExpression *FxRandomSeed::Resolve(FCompileContext &ctx) { CHECKRESOLVED(); - RESOLVE(seed, ctx); + SAFE_RESOLVE(seed, ctx); return this; }; diff --git a/src/sound/i_soundinternal.h b/src/sound/i_soundinternal.h index b1ff4efce1..3aced0400c 100644 --- a/src/sound/i_soundinternal.h +++ b/src/sound/i_soundinternal.h @@ -112,6 +112,9 @@ struct FISoundChannel }; +void FindLoopTags(FileReader *fr, uint32_t *start, bool *startass, uint32_t *end, bool *endass); + + enum SampleType { SampleType_UInt8, diff --git a/src/sound/musicformats/music_libsndfile.cpp b/src/sound/musicformats/music_libsndfile.cpp index fed79dcf05..af53ac37f0 100644 --- a/src/sound/musicformats/music_libsndfile.cpp +++ b/src/sound/musicformats/music_libsndfile.cpp @@ -97,80 +97,171 @@ CUSTOM_CVAR(Int, snd_streambuffersize, 64, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) //========================================================================== // -// try to find the LOOP_START/LOOP_END tags +// Try to find the LOOP_START/LOOP_END tags in a Vorbis Comment block // -// This is a brute force implementation, thanks in no snall part -// that no decent documentation of Ogg headers seems to exist and -// all available tag libraries are horrendously bloated. -// So if we want to do this without any new third party dependencies, -// thanks to the lack of anything that would help to do this properly, -// this was the only solution. +// We have to parse through the FLAC or Ogg headers manually, since sndfile +// doesn't provide proper access to the comments and we'd rather not require +// using libFLAC and libvorbisfile directly. // //========================================================================== -void FindLoopTags(FileReader *fr, uint32_t *start, bool *startass, uint32_t *end, bool *endass) +static void ParseVorbisComments(FileReader *fr, uint32_t *start, bool *startass, uint32_t *end, bool *endass) { - unsigned char testbuf[256]; + uint8_t vc_data[4]; - fr->Seek(0, SEEK_SET); - long got = fr->Read(testbuf, 256); - auto eqp = testbuf - 1; - int count; - while(true) - { - unsigned char *c = (unsigned char *)memchr(eqp + 1, '=', 256 - (eqp + 1 - testbuf)); - if (c == nullptr) return; // If there is no '=' in the first 256 bytes there's also no metadata. + // The VC block starts with a 32LE integer for the vendor string length, + // followed by the vendor string + if(fr->Read(vc_data, 4) != 4) + return; + uint32_t vndr_len = vc_data[0] | (vc_data[1]<<8) | (vc_data[2]<<16) | (vc_data[3]<<24); - eqp = c; - while (*c >= 32 && *c < 127) c--; - if (*c != 0) - { - // doesn't look like a valid tag, so try again - continue; - } - c -= 3; - int len = c[0] + 256*c[1] + 65536*c[2]; - if (c[3] || len > 1000000 || len < (eqp - c - 3)) - { - // length looks fishy so retry with the next '=' - continue; - } - c -= 4; - count = c[0] + 256 * c[1]; - if (c[2] || c[3] || count <= 0 || count > 1000) - { - // very unlikely to have 1000 tags - continue; - } - c += 4; - fr->Seek(long(c - testbuf), SEEK_SET); - break; // looks like we found something. - } - for (int i = 0; i < count; i++) + // Skip vendor string + if(fr->Seek(vndr_len, SEEK_CUR) == -1) + return; + + // Following the vendor string is a 32LE integer for the number of + // comments, followed by each comment. + if(fr->Read(vc_data, 4) != 4) + return; + size_t count = vc_data[0] | (vc_data[1]<<8) | (vc_data[2]<<16) | (vc_data[3]<<24); + + for(size_t i = 0; i < count; i++) { - int length = 0; - fr->Read(&length, 4); - length = LittleLong(length); - if (length == 0 || length > 1000000) return; // looks like we lost it... - if (length > 25) + // Each comment is a 32LE integer for the comment length, followed by + // the comment text (not null terminated!) + if(fr->Read(vc_data, 4) != 4) + return; + uint32_t length = vc_data[0] | (vc_data[1]<<8) | (vc_data[2]<<16) | (vc_data[3]<<24); + + if(length >= 128) { - // This tag is too long to be a valid time stamp so don't even bother. - fr->Seek(length, SEEK_CUR); + // If the comment is "big", skip it + if(fr->Seek(length, SEEK_CUR) == -1) + return; continue; } - fr->Read(testbuf, length); - testbuf[length] = 0; - if (strnicmp((char*)testbuf, "LOOP_START=", 11) == 0) - { - S_ParseTimeTag((char*)testbuf + 11, startass, start); - } - else if (strnicmp((char*)testbuf, "LOOP_END=", 9) == 0) - { - S_ParseTimeTag((char*)testbuf + 9, endass, end); - } + + char strdat[128]; + if(fr->Read(strdat, length) != (long)length) + return; + strdat[length] = 0; + + if(strnicmp(strdat, "LOOP_START=", 11) == 0) + S_ParseTimeTag(strdat + 11, startass, start); + else if(strnicmp(strdat, "LOOP_END=", 9) == 0) + S_ParseTimeTag(strdat + 9, endass, end); } } +static void FindFlacComments(FileReader *fr, uint32_t *loop_start, bool *startass, uint32_t *loop_end, bool *endass) +{ + // Already verified the fLaC marker, so we're 4 bytes into the file + bool lastblock = false; + uint8_t header[4]; + + while(!lastblock && fr->Read(header, 4) == 4) + { + // The first byte of the block header contains the type and a flag + // indicating the last metadata block + char blocktype = header[0]&0x7f; + lastblock = !!(header[0]&0x80); + // Following the type is a 24BE integer for the size of the block + uint32_t blocksize = (header[1]<<16) | (header[2]<<8) | header[3]; + + // FLAC__METADATA_TYPE_VORBIS_COMMENT is 4 + if(blocktype == 4) + { + ParseVorbisComments(fr, loop_start, startass, loop_end, endass); + return; + } + + if(fr->Seek(blocksize, SEEK_CUR) == -1) + break; + } +} + +static void FindOggComments(FileReader *fr, uint32_t *loop_start, bool *startass, uint32_t *loop_end, bool *endass) +{ + uint8_t ogghead[27]; + + // We already read and verified the OggS marker, so skip the first 4 bytes + // of the Ogg page header. + while(fr->Read(ogghead+4, 23) == 23) + { + // The 19th byte of the Ogg header is a 32LE integer for the page + // number, and the 27th is a uint8 for the number of segments in the + // page. + uint32_t ogg_pagenum = ogghead[18] | (ogghead[19]<<8) | (ogghead[20]<<16) | + (ogghead[21]<<24); + uint8_t ogg_segments = ogghead[26]; + + // Following the Ogg page header is a series of uint8s for the length of + // each segment in the page. The page segment data follows contiguously + // after. + uint8_t segsizes[256]; + if(fr->Read(segsizes, ogg_segments) != ogg_segments) + break; + + // Find the segment with the Vorbis Comment packet (type 3) + for(int i = 0; i < ogg_segments; ++i) + { + uint8_t segsize = segsizes[i]; + + if(segsize > 16) + { + uint8_t vorbhead[7]; + if(fr->Read(vorbhead, 7) != 7) + return; + + if(vorbhead[0] == 3 && memcmp(vorbhead+1, "vorbis", 6) == 0) + { + // If the packet is 'laced', it spans multiple segments (a + // segment size of 255 indicates the next segment continues + // the packet, ending with a size less than 255). Vorbis + // packets always start and end on segment boundaries. A + // packet that's an exact multiple of 255 ends with a + // segment of 0 size. + while(segsize == 255 && ++i < ogg_segments) + segsize = segsizes[i]; + + // TODO: A Vorbis packet can theoretically span multiple + // Ogg pages (e.g. start in the last segment of one page + // and end in the first segment of a following page). That + // will require extra logic to decode as the VC block will + // be broken up with non-Vorbis data in-between. For now, + // just handle the common case where it's all in one page. + if(i < ogg_segments) + ParseVorbisComments(fr, loop_start, startass, loop_end, endass); + return; + } + + segsize -= 7; + } + if(fr->Seek(segsize, SEEK_CUR) == -1) + return; + } + + // Don't keep looking after the third page + if(ogg_pagenum >= 2) + break; + + if(fr->Read(ogghead, 4) != 4 || memcmp(ogghead, "OggS", 4) != 0) + break; + } +} + +void FindLoopTags(FileReader *fr, uint32_t *start, bool *startass, uint32_t *end, bool *endass) +{ + uint8_t signature[4]; + + fr->Read(signature, 4); + if(memcmp(signature, "fLaC", 4) == 0) + FindFlacComments(fr, start, startass, end, endass); + else if(memcmp(signature, "OggS", 4) == 0) + FindOggComments(fr, start, startass, end, endass); +} + + //========================================================================== // // SndFile_OpenSong @@ -179,18 +270,12 @@ void FindLoopTags(FileReader *fr, uint32_t *start, bool *startass, uint32_t *end MusInfo *SndFile_OpenSong(FileReader &fr) { - uint8_t signature[4]; - fr.Seek(0, SEEK_SET); - fr.Read(signature, 4); + uint32_t loop_start = 0, loop_end = ~0u; bool startass = false, endass = false; - - if (!memcmp(signature, "OggS", 4) || !memcmp(signature, "fLaC", 4)) - { - // Todo: Read loop points from metadata - FindLoopTags(&fr, &loop_start, &startass, &loop_end, &endass); - } + FindLoopTags(&fr, &loop_start, &startass, &loop_end, &endass); + fr.Seek(0, SEEK_SET); auto decoder = SoundRenderer::CreateDecoder(&fr); if (decoder == nullptr) return nullptr; diff --git a/src/sound/oalsound.cpp b/src/sound/oalsound.cpp index f0cf6342fa..d234b0b050 100644 --- a/src/sound/oalsound.cpp +++ b/src/sound/oalsound.cpp @@ -1276,8 +1276,6 @@ std::pair OpenALSoundRenderer::LoadSoundRaw(uint8_t *sfxdata, return std::make_pair(retval, AL.SOFT_source_spatialize || channels==1); } -void FindLoopTags(FileReader *fr, uint32_t *start, bool *startass, uint32_t *end, bool *endass); - std::pair OpenALSoundRenderer::LoadSound(uint8_t *sfxdata, int length, bool monoize, FSoundLoadBuffer *pBuffer) { SoundHandle retval = { NULL }; @@ -1292,12 +1290,9 @@ std::pair OpenALSoundRenderer::LoadSound(uint8_t *sfxdata, int /* Only downmix to mono if we can't spatialize multi-channel sounds. */ monoize = monoize && !AL.SOFT_source_spatialize; - if (!memcmp(sfxdata, "OggS", 4) || !memcmp(sfxdata, "FLAC", 4)) - { - MemoryReader mr((char*)sfxdata, length); - FindLoopTags(&mr, &loop_start, &startass, &loop_end, &endass); - } + FindLoopTags(&reader, &loop_start, &startass, &loop_end, &endass); + reader.Seek(0, SEEK_SET); std::unique_ptr decoder(CreateDecoder(&reader)); if (!decoder) return std::make_pair(retval, true);