--- libgig/trunk/src/gig.cpp 2020/02/01 15:51:54 3732 +++ libgig/trunk/src/gig.cpp 2021/06/15 11:38:38 3928 @@ -2,7 +2,7 @@ * * * libgig - C++ cross-platform Gigasampler format file access library * * * - * Copyright (C) 2003-2020 by Christian Schoenebeck * + * Copyright (C) 2003-2021 by Christian Schoenebeck * * * * * * This library is free software; you can redistribute it and/or modify * @@ -482,8 +482,8 @@ ScanCompressedSample(); } - // we use a buffer for decompression and for truncating 24 bit samples to 16 bit - if ((Compressed || BitDepth == 24) && !InternalDecompressionBuffer.Size) { + // we use a buffer for decompression only + if (Compressed && !InternalDecompressionBuffer.Size) { InternalDecompressionBuffer.pStart = new unsigned char[INITIAL_SAMPLE_BUFFER_SIZE]; InternalDecompressionBuffer.Size = INITIAL_SAMPLE_BUFFER_SIZE; } @@ -3498,13 +3498,14 @@ RIFF::List* _3prg = rgn->GetSubList(LIST_TYPE_3PRG); if (_3prg) { int dimensionRegionNr = 0; - RIFF::List* _3ewl = _3prg->GetFirstSubList(); - while (_3ewl) { + size_t i = 0; + for (RIFF::List* _3ewl = _3prg->GetSubListAt(i); _3ewl; + _3ewl = _3prg->GetSubListAt(++i)) + { if (_3ewl->GetListType() == LIST_TYPE_3EWL) { pDimensionRegions[dimensionRegionNr] = new DimensionRegion(this, _3ewl); dimensionRegionNr++; } - _3ewl = _3prg->GetNextSubList(); } if (dimensionRegionNr == 0) throw gig::Exception("No dimension region found."); } @@ -3807,6 +3808,8 @@ * @throws gig::Exception if requested zone could not be deleted */ void Region::DeleteDimensionZone(dimension_t type, int zone) { + if (!Dimensions) + throw gig::Exception("Could not delete dimension zone, because there is no dimension at all."); dimension_def_t* oldDef = GetDimensionDefinition(type); if (!oldDef) throw gig::Exception("Could not delete dimension zone, no such dimension of given type"); @@ -3835,7 +3838,7 @@ // requested by the arguments of this method call) to the temporary // region, and don't use Region::CopyAssign() here for this task, since // it would also alter fast lookup helper variables here and there - dimension_def_t newDef; + dimension_def_t newDef = {}; for (int i = 0; i < Dimensions; ++i) { dimension_def_t def = pDimensionDefinitions[i]; // copy, don't reference // is this the dimension requested by the method arguments? ... @@ -3846,6 +3849,9 @@ } tempRgn->AddDimension(&def); } + // silence clang sanitizer warning + if (newDef.dimension == dimension_none) + throw gig::Exception("Unexpected internal failure resolving dimension in DeleteDimensionZone() [this is a bug]."); // find the dimension index in the tempRegion which is the dimension // type passed to this method (paranoidly expecting different order) @@ -3946,6 +3952,8 @@ * @throws gig::Exception if requested zone could not be splitted */ void Region::SplitDimensionZone(dimension_t type, int zone) { + if (!Dimensions) + throw gig::Exception("Could not split dimension zone, because there is no dimension at all."); dimension_def_t* oldDef = GetDimensionDefinition(type); if (!oldDef) throw gig::Exception("Could not split dimension zone, no such dimension of given type"); @@ -3972,7 +3980,7 @@ // requested by the arguments of this method call) to the temporary // region, and don't use Region::CopyAssign() here for this task, since // it would also alter fast lookup helper variables here and there - dimension_def_t newDef; + dimension_def_t newDef = {}; for (int i = 0; i < Dimensions; ++i) { dimension_def_t def = pDimensionDefinitions[i]; // copy, don't reference // is this the dimension requested by the method arguments? ... @@ -3983,6 +3991,9 @@ } tempRgn->AddDimension(&def); } + // silence clang sanitizer warning + if (newDef.dimension == dimension_none) + throw gig::Exception("Unexpected internal failure resolving dimension in SplitDimensionZone() [this is a bug]."); // find the dimension index in the tempRegion which is the dimension // type passed to this method (paranoidly expecting different order) @@ -4896,8 +4907,9 @@ pScripts = new std::list; if (!pList) return; - for (RIFF::Chunk* ck = pList->GetFirstSubChunk(); ck; - ck = pList->GetNextSubChunk()) + size_t i = 0; + for (RIFF::Chunk* ck = pList->GetSubChunkAt(i); ck; + ck = pList->GetSubChunkAt(++i)) { if (ck->GetChunkID() == CHUNK_ID_SCRI) { pScripts->push_back(new Script(this, ck)); @@ -4977,14 +4989,15 @@ if (!pRegions) pRegions = new RegionList; RIFF::List* lrgn = insList->GetSubList(LIST_TYPE_LRGN); if (lrgn) { - RIFF::List* rgn = lrgn->GetFirstSubList(); - while (rgn) { + size_t i = 0; + for (RIFF::List* rgn = lrgn->GetSubListAt(i); rgn; + rgn = lrgn->GetSubListAt(++i)) + { if (rgn->GetListType() == LIST_TYPE_RGN) { if (pProgress) __notify_progress(pProgress, (float) pRegions->size() / (float) Regions); pRegions->push_back(new Region(this, rgn)); } - rgn = lrgn->GetNextSubList(); } // Creating Region Key Table for fast lookup UpdateRegionKeyTable(); @@ -5288,11 +5301,28 @@ } /** + * Returns Region at supplied @a pos position within the region list of + * this instrument. If supplied @a pos is out of bounds then @c NULL is + * returned. + * + * @param pos - position of sought Region in region list + * @returns pointer address to requested region or @c NULL if @a pos is + * out of bounds + */ + Region* Instrument::GetRegionAt(size_t pos) { + if (!pRegions) return NULL; + if (pos >= pRegions->size()) return NULL; + return static_cast( (*pRegions)[pos] ); + } + + /** * Returns the first Region of the instrument. You have to call this * method once before you use GetNextRegion(). * * @returns pointer address to first region or NULL if there is none * @see GetNextRegion() + * @deprecated This method is not reentrant-safe, use GetRegionAt() + * instead. */ Region* Instrument::GetFirstRegion() { if (!pRegions) return NULL; @@ -5307,6 +5337,8 @@ * * @returns pointer address to the next region or NULL if end reached * @see GetFirstRegion() + * @deprecated This method is not reentrant-safe, use GetRegionAt() + * instead. */ Region* Instrument::GetNextRegion() { if (!pRegions) return NULL; @@ -5958,7 +5990,11 @@ PianoReleaseMode = orig->PianoReleaseMode; DimensionKeyRange = orig->DimensionKeyRange; scriptPoolFileOffsets = orig->scriptPoolFileOffsets; - pScriptRefs = orig->pScriptRefs; + // deep copy of pScriptRefs required (to avoid undefined behaviour) + if (pScriptRefs) delete pScriptRefs; + pScriptRefs = new std::vector<_ScriptPooolRef>; + if (orig->pScriptRefs) + *pScriptRefs = *orig->pScriptRefs; scriptVars = orig->scriptVars; // free old midi rules @@ -5969,7 +6005,7 @@ pMidiRules[0] = NULL; // delete all old regions - while (Regions) DeleteRegion(GetFirstRegion()); + while (Regions) DeleteRegion(GetRegionAt(0)); // create new regions and copy them from original { RegionList::const_iterator it = orig->pRegions->begin(); @@ -6069,7 +6105,8 @@ if (!pNameChunk && pFile->pVersion && pFile->pVersion->major > 2) { // v3 has a fixed list of 128 strings, find a free one - for (RIFF::Chunk* ck = _3gnl->GetFirstSubChunk() ; ck ; ck = _3gnl->GetNextSubChunk()) { + size_t i = 0; + for (RIFF::Chunk* ck = _3gnl->GetSubChunkAt(i); ck; ck = _3gnl->GetSubChunkAt(++i)) { if (strcmp(static_cast(ck->LoadChunkData()), "") == 0) { pNameChunk = ck; break; @@ -6233,6 +6270,14 @@ } } + /** + * Returns a pointer to the first Sample object of the file, + * NULL otherwise. + * + * @param pProgress - optional: callback function for progress notification + * @deprecated This method is not reentrant-safe, use GetSample() + * instead. + */ Sample* File::GetFirstSample(progress_t* pProgress) { if (!pSamples) LoadSamples(pProgress); if (!pSamples) return NULL; @@ -6240,6 +6285,13 @@ return static_cast( (SamplesIterator != pSamples->end()) ? *SamplesIterator : NULL ); } + /** + * Returns a pointer to the next Sample object of the file, + * NULL otherwise. + * + * @deprecated This method is not reentrant-safe, use GetSample() + * instead. + */ Sample* File::GetNextSample() { if (!pSamples) return NULL; SamplesIterator++; @@ -6249,18 +6301,15 @@ /** * Returns Sample object of @a index. * + * @param index - position of sample in sample list (0..n) + * @param pProgress - optional: callback function for progress notification * @returns sample object or NULL if index is out of bounds */ - Sample* File::GetSample(uint index) { - if (!pSamples) LoadSamples(); + Sample* File::GetSample(size_t index, progress_t* pProgress) { + if (!pSamples) LoadSamples(pProgress); if (!pSamples) return NULL; - DLS::File::SampleList::iterator it = pSamples->begin(); - for (int i = 0; i < index; ++i) { - ++it; - if (it == pSamples->end()) return NULL; - } - if (it == pSamples->end()) return NULL; - return static_cast( *it ); + if (index >= pSamples->size()) return NULL; + return static_cast( (*pSamples)[index] ); } /** @@ -6322,9 +6371,10 @@ // remove all references to the sample for (Instrument* instrument = GetFirstInstrument() ; instrument ; instrument = GetNextInstrument()) { - for (Region* region = instrument->GetFirstRegion() ; region ; - region = instrument->GetNextRegion()) { - + size_t iRgn = 0; + for (Region* region = instrument->GetRegionAt(iRgn); region; + region = instrument->GetRegionAt(++iRgn)) + { if (region->GetSample() == pSample) region->SetSample(NULL); for (int i = 0 ; i < region->DimensionRegions ; i++) { @@ -6347,8 +6397,6 @@ if (!pSamples) pSamples = new SampleList; - RIFF::File* file = pRIFF; - // just for progress calculation int iSampleIndex = 0; int iTotalSamples = WavePoolCount; @@ -6429,8 +6477,10 @@ if (wvpl) { file_offset_t wvplFileOffset = wvpl->GetFilePos() - wvpl->GetPos(); // should be zero, but just to be sure - RIFF::List* wave = wvpl->GetFirstSubList(); - while (wave) { + size_t i = 0; + for (RIFF::List* wave = wvpl->GetSubListAt(i); wave; + wave = wvpl->GetSubListAt(++i)) + { if (wave->GetListType() == LIST_TYPE_WAVE) { // notify current progress if (pProgress) { @@ -6443,7 +6493,6 @@ iSampleIndex++; } - wave = wvpl->GetNextSubList(); } } } @@ -6676,8 +6725,10 @@ RIFF::List* lstInstruments = pRIFF->GetSubList(LIST_TYPE_LINS); if (lstInstruments) { int iInstrumentIndex = 0; - RIFF::List* lstInstr = lstInstruments->GetFirstSubList(); - while (lstInstr) { + size_t i = 0; + for (RIFF::List* lstInstr = lstInstruments->GetSubListAt(i); + lstInstr; lstInstr = lstInstruments->GetSubListAt(++i)) + { if (lstInstr->GetListType() == LIST_TYPE_INS) { if (pProgress) { // notify current progress @@ -6695,7 +6746,6 @@ iInstrumentIndex++; } - lstInstr = lstInstruments->GetNextSubList(); } if (pProgress) __notify_progress(pProgress, 1.0); // notify done @@ -6961,15 +7011,16 @@ if (lst3gri) { RIFF::List* lst3gnl = lst3gri->GetSubList(LIST_TYPE_3GNL); if (lst3gnl) { - RIFF::Chunk* ck = lst3gnl->GetFirstSubChunk(); - while (ck) { + size_t i = 0; + for (RIFF::Chunk* ck = lst3gnl->GetSubChunkAt(i); ck; + ck = lst3gnl->GetSubChunkAt(++i)) + { if (ck->GetChunkID() == CHUNK_ID_3GNM) { if (pVersion && pVersion->major > 2 && strcmp(static_cast(ck->LoadChunkData()), "") == 0) break; pGroups->push_back(new Group(this, ck)); } - ck = lst3gnl->GetNextSubChunk(); } } } @@ -7059,8 +7110,9 @@ pScriptGroups = new std::list; RIFF::List* lstLS = pRIFF->GetSubList(LIST_TYPE_3LS); if (lstLS) { - for (RIFF::List* lst = lstLS->GetFirstSubList(); lst; - lst = lstLS->GetNextSubList()) + size_t i = 0; + for (RIFF::List* lst = lstLS->GetSubListAt(i); lst; + lst = lstLS->GetSubListAt(++i)) { if (lst->GetListType() == LIST_TYPE_RTIS) { pScriptGroups->push_back(new ScriptGroup(this, lst)); @@ -7121,7 +7173,7 @@ // INFO was added by Resource::UpdateChunks - make sure it // is placed first in file RIFF::Chunk* info = pRIFF->GetSubList(LIST_TYPE_INFO); - RIFF::Chunk* first = pRIFF->GetFirstSubChunk(); + RIFF::Chunk* first = pRIFF->GetSubChunkAt(0); if (first != info) { pRIFF->MoveSubChunk(info, first); } @@ -7142,12 +7194,13 @@ // v3: make sure the file has 128 3gnm chunks // (before updating the Group chunks) if (pVersion && pVersion->major > 2) { - RIFF::Chunk* _3gnm = _3gnl->GetFirstSubChunk(); - for (int i = 0 ; i < 128 ; i++) { + size_t i = 0; + for (RIFF::Chunk* _3gnm = _3gnl->GetSubChunkAt(i); i < 128; + _3gnm = _3gnl->GetSubChunkAt(++i)) + { // create 128 empty placeholder strings which will either // be filled by Group::UpdateChunks below or left empty. ::SaveString(CHUNK_ID_3GNM, _3gnm, _3gnl, "", "", true, 64); - if (_3gnm) _3gnm = _3gnl->GetNextSubChunk(); } } @@ -7213,8 +7266,10 @@ memset(&pData[(instrumentIdx + 1) * sublen + 48], 0, sublen - 48); - for (Region* region = instrument->GetFirstRegion() ; region ; - region = instrument->GetNextRegion()) { + size_t iRgn = 0; + for (Region* region = instrument->GetRegionAt(iRgn); region; + region = instrument->GetRegionAt(++iRgn)) + { for (int i = 0 ; i < region->DimensionRegions ; i++) { gig::DimensionRegion *d = region->pDimensionRegions[i]; if (d->pSample) {