From: <man...@us...> - 2013-06-01 19:51:25
|
Revision: 2271 http://sourceforge.net/p/modplug/code/2271 Author: manxorist Date: 2013-06-01 19:51:19 +0000 (Sat, 01 Jun 2013) Log Message: ----------- Merged revision(s) 2203-2238 from branches/manx/instrument-fields-fixes: [Fix] Make instrument fields reading and writing endian aware. [Ref] Add ReadTruncatedIntLE to class FileReader. ........ [Fix] Correctly calculate array size in GET_MPTHEADER_array_member. (broke in r2203) ........ [Fix] GCC build fix. ........ [Ref] Fix a bunch of gcc warnings in WriteInstrumentHeaderStructOrField(). ........ [Ref] Use Util::MaxValueOfType instead of ~0u. ........ [Ref] Silence a stupid GCC warning about unsigned type comparison against 0 in WriteInstrumentHeaderStructOrField() in cases where a smaller type needs to be sign or zero extended. ........ Revision Links: -------------- http://sourceforge.net/p/modplug/code/2203 Modified Paths: -------------- trunk/OpenMPT/soundlib/Endianness.h trunk/OpenMPT/soundlib/FileReader.h trunk/OpenMPT/soundlib/Load_it.cpp trunk/OpenMPT/soundlib/Load_itp.cpp trunk/OpenMPT/soundlib/SampleFormats.cpp trunk/OpenMPT/soundlib/Sndfile.cpp trunk/OpenMPT/soundlib/Sndfile.h Property Changed: ---------------- trunk/OpenMPT/ Index: trunk/OpenMPT =================================================================== --- trunk/OpenMPT 2013-06-01 19:30:14 UTC (rev 2270) +++ trunk/OpenMPT 2013-06-01 19:51:19 UTC (rev 2271) Property changes on: trunk/OpenMPT ___________________________________________________________________ Modified: svn:mergeinfo ## -1,6 +1,7 ## /branches/manx/build-speedup:1586-1589 /branches/manx/gcc-fixes:2018-2022,2024-2048,2052-2071,2075-2077,2080,2087-2089 /branches/manx/header-dependencies-cleanups:1394-1397,1401-1402,1405-1406 +/branches/manx/instrument-fields-fixes:2203-2238 /branches/manx/mptstring-stdstring-support:2204,2208,2212,2214,2217,2220,2224,2259,2261-2262,2264,2267 /branches/manx/nonglobal-mixer:1715-1841 /branches/manx/profiler:1813 \ No newline at end of property Modified: trunk/OpenMPT/soundlib/Endianness.h =================================================================== --- trunk/OpenMPT/soundlib/Endianness.h 2013-06-01 19:30:14 UTC (rev 2270) +++ trunk/OpenMPT/soundlib/Endianness.h 2013-06-01 19:51:19 UTC (rev 2271) @@ -79,6 +79,16 @@ inline int32 SwapBytesLE_(unaligned_int32 *value) { return *value; } inline int16 SwapBytesLE_(unaligned_int16 *value) { return *value; } #endif +// Do NOT remove these overloads, even if they seem useless. +// We do not want risking to extend 8bit integers to int and then endian-converting and casting back to int. +// Thus these overloads. +inline uint8 SwapBytesLE_(uint8 *value) { return *value; } +inline int8 SwapBytesLE_(int8 *value) { return *value; } +inline char SwapBytesLE_(char *value) { return *value; } +inline uint8 SwapBytesBE_(uint8 *value) { return *value; } +inline int8 SwapBytesBE_(int8 *value) { return *value; } +inline char SwapBytesBE_(char *value) { return *value; } + // GCC will not bind references to members of packed structures, workaround it by using a raw pointer. // This is a temporary solution as this pointer might of course be unaligned which GCC seems to not care about in that case. #define SwapBytesBE(value) SwapBytesBE_(&(value)) Modified: trunk/OpenMPT/soundlib/FileReader.h =================================================================== --- trunk/OpenMPT/soundlib/FileReader.h 2013-06-01 19:30:14 UTC (rev 2270) +++ trunk/OpenMPT/soundlib/FileReader.h 2013-06-01 19:51:19 UTC (rev 2271) @@ -22,6 +22,7 @@ #endif #include <limits> #include <vector> +#include <cstring> // change to show warnings for functions which trigger pre-caching the whole file for unseekable streams @@ -566,7 +567,8 @@ return true; } -protected: +public: + // Read some kind of integer in little-endian format. // If successful, the file cursor is advanced by the size of the integer. template <typename T> @@ -599,7 +601,46 @@ } } -public: + // Read a integer in little-endian format which has some of its higher bytes not stored in file. + // If successful, the file cursor is advanced by the given size. + template <typename T> + T ReadTruncatedIntLE(off_t size) + { + static_assert(std::numeric_limits<T>::is_integer == true, "Target type is a not an integer"); + ASSERT(sizeof(T) >= size); + if(size == 0) + { + return 0; + } + if(!CanRead(size)) + { + return 0; + } + uint8 buf[sizeof(T)]; + for(std::size_t i = 0; i < size; ++i) + { + Read(buf[i]); + } + if(std::numeric_limits<T>::is_signed) + { + for(std::size_t i = size; i < sizeof(T); ++i) + { + // sign extend + buf[i] = (buf[size-1] & 0x80) ? 0xff : 0x00; + } + } else + { + for(std::size_t i = size; i < sizeof(T); ++i) + { + // zero extend + buf[i] = 0x00; + } + } + T target; + std::memcpy(&target, buf, sizeof(T)); + return SwapBytesLE(target); + } + // Read unsigned 32-Bit integer in little-endian format. // If successful, the file cursor is advanced by the size of the integer. uint32 ReadUint32LE() Modified: trunk/OpenMPT/soundlib/Load_it.cpp =================================================================== --- trunk/OpenMPT/soundlib/Load_it.cpp 2013-06-01 19:30:14 UTC (rev 2270) +++ trunk/OpenMPT/soundlib/Load_it.cpp 2013-06-01 19:51:19 UTC (rev 2271) @@ -1955,17 +1955,15 @@ fwrite(&size, 1, sizeof(int16), f); //write size for(UINT nins=1; nins<=nInstruments; nins++) //for all instruments... { - char *pField; if (Instruments[nins]) { - pField = GetInstrumentHeaderFieldPointer(Instruments[nins], code, size); //get ptr to field + WriteInstrumentHeaderStructOrField(Instruments[nins], f, code, size); } else { ModInstrument *emptyInstrument = new ModInstrument(); - pField = GetInstrumentHeaderFieldPointer(emptyInstrument, code, size); //get ptr to field + WriteInstrumentHeaderStructOrField(emptyInstrument, f, code, size); delete emptyInstrument; } - fwrite(pField, 1, size, f); //write field data } } Modified: trunk/OpenMPT/soundlib/Load_itp.cpp =================================================================== --- trunk/OpenMPT/soundlib/Load_itp.cpp 2013-06-01 19:30:14 UTC (rev 2270) +++ trunk/OpenMPT/soundlib/Load_itp.cpp 2013-06-01 19:51:19 UTC (rev 2271) @@ -504,7 +504,7 @@ // instruments' header for(i=0; i<m_nInstruments; i++) { - if(Instruments[i+1]) WriteInstrumentHeaderStruct(Instruments[i+1], f); + if(Instruments[i+1]) WriteInstrumentHeaderStructOrField(Instruments[i+1], f); // write separator tag code = 'SEP@'; fwrite(&code, 1, sizeof(uint32), f); Modified: trunk/OpenMPT/soundlib/SampleFormats.cpp =================================================================== --- trunk/OpenMPT/soundlib/SampleFormats.cpp 2013-06-01 19:30:14 UTC (rev 2270) +++ trunk/OpenMPT/soundlib/SampleFormats.cpp 2013-06-01 19:51:19 UTC (rev 2271) @@ -1122,7 +1122,7 @@ int32 code = MULTICHAR4_LE_MSVC('M','P','T','X'); fwrite(&code, 1, sizeof(int32), f); // Write extension tag - WriteInstrumentHeaderStruct(pIns, f); // Write full extended header. + WriteInstrumentHeaderStructOrField(pIns, f); // Write full extended header. fclose(f); return true; @@ -1708,7 +1708,7 @@ int32 code = MULTICHAR4_LE_MSVC('M','P','T','X'); SwapBytesLE(code); fwrite(&code, 1, sizeof(int32), f); // Write extension tag - WriteInstrumentHeaderStruct(pIns, f); // Write full extended header. + WriteInstrumentHeaderStructOrField(pIns, f); // Write full extended header. fclose(f); return true; @@ -1720,18 +1720,27 @@ void ReadInstrumentExtensionField(ModInstrument* pIns, const uint32 code, const uint16 size, FileReader &file) //------------------------------------------------------------------------------------------------------------ { - // get field's address in instrument's header - char *fadr = GetInstrumentHeaderFieldPointer(pIns, code, size); - - if(fadr && code != MULTICHAR4_LE_MSVC('K','[','.','.')) // copy field data in instrument's header - memcpy(fadr, file.GetRawData(), size); // (except for keyboard mapping) - if(fadr && code == MULTICHAR4_LE_MSVC('n','[','.','.')) + if(code == MULTICHAR4_LE_MSVC('K','[','.','.')) + { + // skip keyboard mapping + file.Skip(size); + return; + } + + bool success = ReadInstrumentHeaderField(pIns, code, size, file); + + if(!success) + { + file.Skip(size); + return; + } + + if(code == MULTICHAR4_LE_MSVC('n','[','.','.')) mpt::String::SetNullTerminator(pIns->name); - if(fadr && code == MULTICHAR4_LE_MSVC('f','n','[','.')) + if(code == MULTICHAR4_LE_MSVC('f','n','[','.')) mpt::String::SetNullTerminator(pIns->filename); - file.Skip(size); - if(code == MULTICHAR4_LE_MSVC('d','F','.','.') && fadr != nullptr) // 'dF..' field requires additional processing. + if(code == MULTICHAR4_LE_MSVC('d','F','.','.')) // 'dF..' field requires additional processing. ConvertReadExtendedFlags(pIns); } Modified: trunk/OpenMPT/soundlib/Sndfile.cpp =================================================================== --- trunk/OpenMPT/soundlib/Sndfile.cpp 2013-06-01 19:30:14 UTC (rev 2270) +++ trunk/OpenMPT/soundlib/Sndfile.cpp 2013-06-01 19:51:19 UTC (rev 2271) @@ -44,8 +44,8 @@ - both following functions need to be updated when adding a new member in ModInstrument : -void WriteInstrumentHeaderStruct(ModInstrument * input, FILE * file); -BYTE * GetInstrumentHeaderFieldPointer(ModInstrument * input, uint32 fcode, int16 fsize); +void WriteInstrumentHeaderStructOrField(ModInstrument * input, FILE * file, uint32 only_this_code, int16 fixedsize); +bool ReadInstrumentHeaderField(ModInstrument * input, uint32 fcode, int16 fsize, FileReader &file); - see below for body declaration. @@ -166,27 +166,73 @@ #define MULTICHAR_STRING_TO_INT(str) MULTICHAR4_LE_MSVC((str)[0],(str)[1],(str)[2],(str)[3]) +template<typename T, bool is_signed> struct IsNegativeFunctor { bool operator()(T val) const { return val < 0; } }; +template<typename T> struct IsNegativeFunctor<T, true> { bool operator()(T val) const { return val < 0; } }; +template<typename T> struct IsNegativeFunctor<T, false> { bool operator()(T /*val*/) const { return false; } }; + +template<typename T> +bool IsNegative(const T &val) +{ + return IsNegativeFunctor<T, std::numeric_limits<T>::is_signed>()(val); +} + // -------------------------------------------------------------------------------------------- // Convenient macro to help WRITE_HEADER declaration for single type members ONLY (non-array) // -------------------------------------------------------------------------------------------- #define WRITE_MPTHEADER_sized_member(name,type,code) \ -static_assert(sizeof(input->name) >= sizeof(type), "Instrument property does not fit into specified type!");\ -fcode = MULTICHAR_STRING_TO_INT(#code);\ -fwrite(& fcode , 1 , sizeof( uint32 ) , file);\ -fsize = sizeof( type );\ -fwrite(& fsize , 1 , sizeof( int16 ) , file);\ -fwrite(&input-> name , 1 , fsize , file); + static_assert(sizeof(input->name) >= sizeof(type), "Instrument property does not fit into specified type!");\ + fcode = MULTICHAR_STRING_TO_INT(#code);\ + fsize = sizeof( type );\ + if(only_this_code == Util::MaxValueOfType(only_this_code)) \ + { \ + fwrite(& fcode , 1 , sizeof( uint32 ) , file);\ + fwrite(& fsize , 1 , sizeof( int16 ) , file);\ + fwrite(&input-> name , 1 , fsize , file); \ + } else if(only_this_code == fcode)\ + { \ + /* hackish workaround to resolve mismatched size values: */ \ + /* nResampling was a long time declared as uint32 but these macro tables used uint16 and UINT. */ \ + /* This worked fine on little-endian, on big-endian not so much. Thus support writing size-mismatched fields. */ \ + ASSERT(fixedsize >= fsize); /* ASSERT(fixedsize == fsize); */ \ + type tmp = input-> name; \ + tmp = SwapBytesLE(tmp); \ + fwrite(&tmp , 1 , fsize , file); \ + if(fixedsize > fsize) \ + { \ + for(int16 i = 0; i < fixedsize - fsize; ++i) \ + { \ + uint8 fillbyte = !IsNegative(tmp) ? 0 : 0xff; /* sign extend */ \ + fwrite(&fillbyte, 1, 1, file); \ + } \ + } \ + } \ +/**/ // -------------------------------------------------------------------------------------------- // Convenient macro to help WRITE_HEADER declaration for array members ONLY // -------------------------------------------------------------------------------------------- #define WRITE_MPTHEADER_array_member(name,type,code,arraysize) \ -ASSERT(sizeof(input->name) >= sizeof(type) * arraysize);\ -fcode = MULTICHAR_STRING_TO_INT(#code);\ -fwrite(& fcode , 1 , sizeof( uint32 ) , file);\ -fsize = sizeof( type ) * arraysize;\ -fwrite(& fsize , 1 , sizeof( int16 ) , file);\ -fwrite(&input-> name , 1 , fsize , file); + ASSERT(sizeof(input->name) >= sizeof(type) * arraysize);\ + fcode = MULTICHAR_STRING_TO_INT(#code);\ + fsize = sizeof( type ) * arraysize;\ + if(only_this_code == Util::MaxValueOfType(only_this_code)) \ + { \ + fwrite(& fcode , 1 , sizeof( uint32 ) , file);\ + fwrite(& fsize , 1 , sizeof( int16 ) , file);\ + fwrite(&input-> name , 1 , fsize , file); \ + } else if(only_this_code == fcode)\ + { \ + /* ASSERT(fixedsize <= fsize); */ \ + fsize = fixedsize; /* just trust the size we got passed */ \ + for(std::size_t i = 0; i < fsize/sizeof(type); ++i) \ + { \ + type tmp; \ + tmp = input-> name [i]; \ + tmp = SwapBytesLE(tmp); \ + fwrite(&tmp, 1, sizeof(type), file); \ + } \ + } \ +/**/ namespace { // Create 'dF..' entry. @@ -214,19 +260,29 @@ } // unnamed namespace. // Write (in 'file') 'input' ModInstrument with 'code' & 'size' extra field infos for each member -void WriteInstrumentHeaderStruct(ModInstrument * input, FILE * file) +void WriteInstrumentHeaderStructOrField(ModInstrument * input, FILE * file, uint32 only_this_code, int16 fixedsize) { uint32 fcode; int16 fsize; + +if(only_this_code != Util::MaxValueOfType(only_this_code)) +{ + ASSERT(fixedsize > 0); +} + WRITE_MPTHEADER_sized_member( nFadeOut , UINT , FO.. ) -{ // dwFlags needs to be constructed so write it manually. +if(only_this_code == Util::MaxValueOfType(only_this_code) || only_this_code == MULTICHAR_STRING_TO_INT("dF..")){ // dwFlags needs to be constructed so write it manually. //WRITE_MPTHEADER_sized_member( dwFlags , DWORD , dF.. ) - const DWORD dwFlags = CreateExtensionFlags(*input); + uint32 dwFlags = CreateExtensionFlags(*input); fcode = MULTICHAR_STRING_TO_INT("dF.."); - fwrite(&fcode, 1, sizeof(int32), file); fsize = sizeof(dwFlags); - fwrite(&fsize, 1, sizeof(int16), file); + if(!only_this_code) + { + fwrite(&fcode, 1, sizeof(int32), file); + fwrite(&fsize, 1, sizeof(int16), file); + } + dwFlags = SwapBytesLE(dwFlags); fwrite(&dwFlags, 1, fsize, file); } @@ -288,30 +344,52 @@ WRITE_MPTHEADER_sized_member( midiPWD , int8 , MPWD ) } + // -------------------------------------------------------------------------------------------- // Convenient macro to help GET_HEADER declaration for single type members ONLY (non-array) // -------------------------------------------------------------------------------------------- #define GET_MPTHEADER_sized_member(name,type,code) \ -if(fcode == MULTICHAR_STRING_TO_INT(#code)) {\ -if( fsize <= sizeof( type ) ) pointer = (char *)(&input-> name);\ -} else + if(fcode == MULTICHAR_STRING_TO_INT(#code)) {\ + if( fsize <= sizeof( type ) ) \ + { \ + /* hackish workaround to resolve mismatched size values: */ \ + /* nResampling was a long time declared as uint32 but these macro tables used uint16 and UINT. */ \ + /* This worked fine on little-endian, on big-endian not so much. Thus support reading size-mismatched fields. */ \ + type tmp; \ + if(!file.CanRead(fsize)) return false; \ + tmp = file.ReadTruncatedIntLE<type>(fsize); \ + STATIC_ASSERT(sizeof(tmp) == sizeof(input-> name )); \ + memcpy(&(input-> name ), &tmp, sizeof(type)); \ + return true; \ + } \ + } else \ +/**/ // -------------------------------------------------------------------------------------------- // Convenient macro to help GET_HEADER declaration for array members ONLY // -------------------------------------------------------------------------------------------- #define GET_MPTHEADER_array_member(name,type,code,arraysize) \ -if(fcode == MULTICHAR_STRING_TO_INT(#code)) {\ -if( fsize <= sizeof( type ) * arraysize ) pointer = (char *)(&input-> name);\ -} else + if(fcode == MULTICHAR_STRING_TO_INT(#code)) {\ + if( fsize <= sizeof( type ) * arraysize ) \ + { \ + if(!file.CanRead(sizeof(type) * arraysize)) return false; \ + for(std::size_t i = 0; i < arraysize; ++i) \ + { \ + input-> name [i] = file.ReadIntLE<type>(); \ + } \ + return true; \ + } \ + } else \ +/**/ + // Return a pointer on the wanted field in 'input' ModInstrument given field code & size -char *GetInstrumentHeaderFieldPointer(const ModInstrument *input, uint32 fcode, uint16 fsize) +bool ReadInstrumentHeaderField(ModInstrument *input, uint32 fcode, uint16 fsize, FileReader &file) { -if(input == nullptr) return nullptr; -char *pointer = nullptr; +if(input == nullptr) return false; GET_MPTHEADER_sized_member( nFadeOut , UINT , FO.. ) -GET_MPTHEADER_sized_member( dwFlags , DWORD , dF.. ) +GET_MPTHEADER_sized_member( dwFlags , uint32 , dF.. ) GET_MPTHEADER_sized_member( nGlobalVol , UINT , GV.. ) GET_MPTHEADER_sized_member( nPan , UINT , P... ) GET_MPTHEADER_sized_member( VolEnv.nNodes , UINT , VE.. ) @@ -370,7 +448,8 @@ GET_MPTHEADER_sized_member( midiPWD , int8 , MPWD ) {} -return pointer; +return false; + } // -! NEW_FEATURE#0027 Modified: trunk/OpenMPT/soundlib/Sndfile.h =================================================================== --- trunk/OpenMPT/soundlib/Sndfile.h 2013-06-01 19:30:14 UTC (rev 2270) +++ trunk/OpenMPT/soundlib/Sndfile.h 2013-06-01 19:51:19 UTC (rev 2271) @@ -40,11 +40,13 @@ #include "../sounddsp/DSP.h" #include "../sounddsp/EQ.h" + +class FileReader; // ----------------------------------------------------------------------------------------- // MODULAR ModInstrument FIELD ACCESS : body content at the (near) top of Sndfile.cpp !!! // ----------------------------------------------------------------------------------------- -extern void WriteInstrumentHeaderStruct(ModInstrument * input, FILE * file); -extern char *GetInstrumentHeaderFieldPointer(const ModInstrument * input, uint32 fcode, uint16 fsize); +extern void WriteInstrumentHeaderStructOrField(ModInstrument * input, FILE * file, uint32 only_this_code = -1 /* -1 for all */, int16 fixedsize = 0); +extern bool ReadInstrumentHeaderField(ModInstrument * input, uint32 fcode, uint16 fsize, FileReader &file); // -------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------- This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |