/* * steghide 0.5.1 - a steganography program * Copyright (C) 1999-2003 Stefan Hetzl * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ #include #include #include #include "CvrStgFile.h" #include "DFSAPHeuristic.h" #include "SampleValueAdjacencyList.h" #include "SMDConstructionHeuristic.h" #include "WavFormatChunk.h" #include "WavChunkHeader.h" #include "WavChunkUnused.h" #include "WavFile.h" #include "WavPCMSampleValue.h" #include "common.h" #include "error.h" #include "msg.h" WavFile::WavFile (BinaryIO *io) : CvrStgFile() { setSamplesPerVertex (SamplesPerVertex) ; setEmbValueModulus (EmbValueModulus) ; read (io) ; } WavFile::~WavFile () { delete riffchhdr ; delete FormatChunk ; delete datachhdr ; for (std::vector::iterator i = UnusedBeforeData.begin() ; i != UnusedBeforeData.end() ; i++) { delete (*i) ; } } void WavFile::read (BinaryIO *io) { CvrStgFile::read (io) ; readheaders () ; readdata () ; } void WavFile::write () { CvrStgFile::write () ; writeheaders () ; writedata () ; } std::list WavFile::getProperties () const { std::list retval ; // format std::string formatstring = "wave audio" ; if (FormatChunk->getFormatTag() == WavFormatChunk::FormatPCM) { formatstring += ", PCM encoding" ; } retval.push_back (CvrStgFile::Property (_("format"), formatstring)) ; return retval ; } std::vector WavFile::getMatchingAlgorithms (Graph* g, Matching* m) const { std::vector retval ; retval.push_back (new SMDConstructionHeuristic (g, m)) ; retval.push_back (new DFSAPHeuristic (g, m)) ; return retval ; } unsigned long WavFile::getNumSamples() const { unsigned long retval = 0 ; if (FormatChunk->getBitsPerSample() <= 8) { retval = data_small.size() ; } else { retval = data_large.size() ; } return retval ; } void WavFile::replaceSample (const SamplePos pos, const SampleValue* s) { const WavPCMSampleValue* sample = dynamic_cast (s) ; myassert (sample != NULL) ; if (FormatChunk->getBitsPerSample() <= 8) { data_small[pos] = (unsigned char) sample->getValue() ; } else { data_large[pos] = sample->getValue() ; } } SampleValue *WavFile::getSampleValue (SamplePos pos) const { int value = 0 ; if (FormatChunk->getBitsPerSample() <= 8) { value = (int) data_small[pos] ; } else { value = data_large[pos] ; } return ((SampleValue *) new WavPCMSampleValue (value)) ; } std::vector WavFile::calcSVAdjacencyLists (const std::vector& svs) const { EmbValue m = getEmbValueModulus() ; std::vector lists (svs.size()) ; for (EmbValue i = 0 ; i < m ; i++) { lists[i] = new SampleValueAdjacencyList (svs.size()) ; } std::vector svs_sorted (svs.size()) ; for (unsigned long i = 0 ; i < svs.size() ; i++) { svs_sorted[i] = (WavPCMSampleValue*) svs[i] ; } WavPCMSmaller smaller ; sort (svs_sorted.begin(), svs_sorted.end(), smaller) ; // fill the lists int r = Globs.TheCvrStgFile->getRadius() ; unsigned long dstart = 0 ; for (unsigned long si = 0 ; si < svs_sorted.size() ; si++) { while ((dstart < svs_sorted.size()) && (svs_sorted[dstart]->getValue() < svs_sorted[si]->getValue() - r)) { dstart++ ; } // dstart is the index of the first sample in svs_sorted that is >= (svs_sorted[si]->getValue() - r) unsigned long di = dstart ; while ((di < svs_sorted.size()) && (svs_sorted[di]->getValue() <= svs_sorted[si]->getValue() + r)) { if (si != di) { (*(lists[svs_sorted[di]->getEmbeddedValue()]))[svs_sorted[si]].push_back (svs_sorted[di]) ; } di++ ; } } // sort rows for (EmbValue i = 0 ; i < m ; i++) { lists[i]->sort() ; } return lists ; } unsigned short WavFile::getBitsPerSample() const { return FormatChunk->getBitsPerSample() ; } unsigned short WavFile::getBytesPerSample() { unsigned short retval = 0 ; if (FormatChunk->getBitsPerSample() % 8 == 0) { retval = FormatChunk->getBitsPerSample() / 8 ; } else { retval = (FormatChunk->getBitsPerSample() / 8) + 1 ; } return retval ; } unsigned short WavFile::getFirstBitPosinSample() { unsigned short retval = 0 ; if (FormatChunk->getBitsPerSample() % 8 == 0) { retval = 0 ; } else { retval = 8 - (FormatChunk->getBitsPerSample() % 8) ; } return retval ; } /* reads the wav file data from disk */ void WavFile::readdata (void) { try { data_small.clear() ; data_large.clear() ; unsigned short bitspersample = FormatChunk->getBitsPerSample() ; unsigned short bytespersample = getBytesPerSample() ; unsigned short firstbitpos = getFirstBitPosinSample() ; UWORD32 mask = 0 ; for (unsigned short i = 0 ; i < bitspersample ; i++) { mask <<= 1 ; mask |= 1 ; } unsigned long readpos = 0 ; while (readpos < datachhdr->getChunkLength()) { if (bitspersample <= 8) { data_small.push_back ((getBinIO()->read8()) >> firstbitpos) ; } else { // decode two's complement UWORD32 value = (UWORD32) getBinIO()->read_le (bytespersample) ; value = value >> firstbitpos ; int sign = 0 ; if (((value & (1 << (bitspersample - 1))) >> (bitspersample - 1)) == 0) { sign = 1 ; } else { sign = -1 ; value = ~value ; value++ ; value &= mask ; } data_large.push_back (sign * ((int) value)) ; } readpos += bytespersample ; } UnusedAfterData.clear() ; while (!getBinIO()->eof()) { UnusedAfterData.push_back (getBinIO()->read8()) ; } } catch (BinaryInputError e) { switch (e.getType()) { case BinaryInputError::FILE_ERR: { throw SteghideError (_("an error occured while reading the audio data from the file \"%s\"."), getBinIO()->getName().c_str()) ; break ; } case BinaryInputError::FILE_EOF: { throw SteghideError (_("premature end of file \"%s\" while reading audio data."), getBinIO()->getName().c_str()) ; break ; } case BinaryInputError::STDIN_ERR: { throw SteghideError (_("an error occured while reading the audio data from standard input.")) ; break ; } case BinaryInputError::STDIN_EOF: { throw SteghideError (_("premature end of data from standard input while reading audio data.")) ; break ; } } } } void WavFile::writedata (void) { try { unsigned short bitspersample = FormatChunk->getBitsPerSample() ; unsigned short bytespersample = getBytesPerSample() ; unsigned short firstbitpos = getFirstBitPosinSample() ; unsigned long writepos = 0 ; while (writepos < datachhdr->getChunkLength()) { if (bitspersample <= 8) { getBinIO()->write8 (data_small[writepos] << firstbitpos) ; } else { unsigned long value = 0 ; if (data_large[writepos / bytespersample] >= 0) { value = (unsigned long) data_large[writepos / bytespersample] ; } else { value = (unsigned long) -data_large[writepos / bytespersample] ; // value is now |sample| value = ~value ; value++ ; } value <<= firstbitpos ; getBinIO()->write_le (value, bytespersample) ; } writepos += bytespersample ; } for (std::vector::const_iterator it = UnusedAfterData.begin() ; it != UnusedAfterData.end() ; it++) { getBinIO()->write8(*it) ; } } catch (BinaryOutputError e) { switch (e.getType()) { case BinaryOutputError::FILE_ERR: { throw SteghideError (_("an error occured while writing the audio data to the file \"%s\"."), getBinIO()->getName().c_str()) ; break ; } case BinaryOutputError::STDOUT_ERR: { throw SteghideError (_("an error occured while writing the audio data to standard output.")) ; break ; } } } } /* reads the headers of a wav file from disk */ void WavFile::readheaders () { try { riffchhdr = new WavChunkHeader ("RIFF", getBinIO()->read32_le()) ; for (unsigned int i = 0 ; i < 4 ; i++) { id_wave[i] = getBinIO()->read8() ; } if (strncmp (id_wave, "WAVE", 4) != 0) { throw UnSupFileFormat (getBinIO()) ; } FormatChunk = new WavFormatChunk (new WavChunkHeader (getBinIO()), getBinIO()) ; if (FormatChunk->getBitsPerSample() <= 8) { setRadius (Radius_small) ; } else { setRadius (Radius_large) ; } UnusedBeforeData.clear() ; WavChunkHeader *chhdr = new WavChunkHeader (getBinIO()) ; while (strncmp (chhdr->getChunkId(), "data", 4) != 0) { UnusedBeforeData.push_back (new WavChunkUnused (chhdr, getBinIO())) ; chhdr = new WavChunkHeader (getBinIO()) ; } datachhdr = chhdr ; } catch (BinaryInputError e) { switch (e.getType()) { case BinaryInputError::FILE_ERR: { throw SteghideError (_("an error occured while reading the wav headers from the file \"%s\"."), getBinIO()->getName().c_str()) ; break ; } case BinaryInputError::FILE_EOF: { throw SteghideError (_("premature end of file \"%s\" while reading wav headers."), getBinIO()->getName().c_str()) ; break ; } case BinaryInputError::STDIN_ERR: { throw SteghideError (_("an error occured while reading the wav headers from standard input.")) ; break ; } case BinaryInputError::STDIN_EOF: { throw SteghideError (_("premature end of data from standard input while reading wav headers.")) ; break ; } } } return ; } /* writes the headers of a wav file to disk */ void WavFile::writeheaders () { try { riffchhdr->write (getBinIO()) ; for (int i = 0 ; i < 4 ; i++) { getBinIO()->write8 (id_wave[i]) ; } FormatChunk->write (getBinIO()) ; for (std::vector::const_iterator i = UnusedBeforeData.begin() ; i != UnusedBeforeData.end() ; i++) { (*i)->write (getBinIO()) ; } datachhdr->write (getBinIO()) ; } catch (BinaryOutputError e) { switch (e.getType()) { case BinaryOutputError::FILE_ERR: { throw SteghideError (_("an error occured while writing the wav headers to the file \"%s\"."), getBinIO()->getName().c_str()) ; break ; } case BinaryOutputError::STDOUT_ERR: { throw SteghideError (_("an error occured while writing the wav headers to standard output.")) ; break ; } } } }