vfs: check userland buffers before reading them.
[haiku.git] / src / add-ons / media / plugins / ape_reader / MAClib / APECompressCreate.cpp
blob21eafcb2987701af87588b8758f8e965825f3b7e
1 #include "All.h"
2 #include "IO.h"
3 #include "APECompressCreate.h"
5 #include "APECompressCore.h"
7 CAPECompressCreate::CAPECompressCreate()
9 m_nMaxFrames = 0;
12 CAPECompressCreate::~CAPECompressCreate()
16 int CAPECompressCreate::Start(CIO * pioOutput, const WAVEFORMATEX * pwfeInput, int nMaxAudioBytes, int nCompressionLevel, const void * pHeaderData, int nHeaderBytes)
18 // verify the parameters
19 if (pioOutput == NULL || pwfeInput == NULL)
20 return ERROR_BAD_PARAMETER;
22 // verify the wave format
23 if ((pwfeInput->nChannels != 1) && (pwfeInput->nChannels != 2))
25 return ERROR_INPUT_FILE_UNSUPPORTED_CHANNEL_COUNT;
27 if ((pwfeInput->wBitsPerSample != 8) && (pwfeInput->wBitsPerSample != 16) && (pwfeInput->wBitsPerSample != 24))
29 return ERROR_INPUT_FILE_UNSUPPORTED_BIT_DEPTH;
32 // initialize (creates the base classes)
33 m_nSamplesPerFrame = 73728;
34 if (nCompressionLevel == COMPRESSION_LEVEL_EXTRA_HIGH)
35 m_nSamplesPerFrame *= 4;
36 else if (nCompressionLevel == COMPRESSION_LEVEL_INSANE)
37 m_nSamplesPerFrame *= 16;
39 m_spIO.Assign(pioOutput, FALSE, FALSE);
40 m_spAPECompressCore.Assign(new CAPECompressCore(m_spIO, pwfeInput, m_nSamplesPerFrame, nCompressionLevel));
42 // copy the format
43 memcpy(&m_wfeInput, pwfeInput, sizeof(WAVEFORMATEX));
45 // the compression level
46 m_nCompressionLevel = nCompressionLevel;
47 m_nFrameIndex = 0;
48 m_nLastFrameBlocks = m_nSamplesPerFrame;
50 // initialize the file
51 if (nMaxAudioBytes < 0)
52 nMaxAudioBytes = 2147483647;
54 uint32 nMaxAudioBlocks = nMaxAudioBytes / pwfeInput->nBlockAlign;
55 int nMaxFrames = nMaxAudioBlocks / m_nSamplesPerFrame;
56 if ((nMaxAudioBlocks % m_nSamplesPerFrame) != 0) nMaxFrames++;
58 InitializeFile(m_spIO, &m_wfeInput, nMaxFrames,
59 m_nCompressionLevel, pHeaderData, nHeaderBytes);
61 return ERROR_SUCCESS;
64 int CAPECompressCreate::GetFullFrameBytes()
66 return m_nSamplesPerFrame * m_wfeInput.nBlockAlign;
69 int CAPECompressCreate::EncodeFrame(const void * pInputData, int nInputBytes)
71 int nInputBlocks = nInputBytes / m_wfeInput.nBlockAlign;
73 if ((nInputBlocks < m_nSamplesPerFrame) && (m_nLastFrameBlocks < m_nSamplesPerFrame))
75 return -1; // can only pass a smaller frame for the very last time
78 // update the seek table
79 m_spAPECompressCore->GetBitArray()->AdvanceToByteBoundary();
80 int nRetVal = SetSeekByte(m_nFrameIndex, m_spIO->GetPosition() + (m_spAPECompressCore->GetBitArray()->GetCurrentBitIndex() / 8));
81 if (nRetVal != ERROR_SUCCESS)
82 return nRetVal;
84 // compress
85 nRetVal = m_spAPECompressCore->EncodeFrame(pInputData, nInputBytes);
87 // update stats
88 m_nLastFrameBlocks = nInputBlocks;
89 m_nFrameIndex++;
91 return nRetVal;
94 int CAPECompressCreate::Finish(const void * pTerminatingData, int nTerminatingBytes, int nWAVTerminatingBytes)
96 // clear the bit array
97 RETURN_ON_ERROR(m_spAPECompressCore->GetBitArray()->OutputBitArray(TRUE));
99 // finalize the file
100 RETURN_ON_ERROR(FinalizeFile(m_spIO, m_nFrameIndex, m_nLastFrameBlocks,
101 pTerminatingData, nTerminatingBytes, nWAVTerminatingBytes, m_spAPECompressCore->GetPeakLevel()));
103 return ERROR_SUCCESS;
106 int CAPECompressCreate::SetSeekByte(int nFrame, int nByteOffset)
108 if (nFrame >= m_nMaxFrames) return ERROR_APE_COMPRESS_TOO_MUCH_DATA;
109 m_spSeekTable[nFrame] = nByteOffset;
110 return ERROR_SUCCESS;
113 int CAPECompressCreate::InitializeFile(CIO * pIO, const WAVEFORMATEX * pwfeInput, int nMaxFrames, int nCompressionLevel, const void * pHeaderData, int nHeaderBytes)
115 // error check the parameters
116 if (pIO == NULL || pwfeInput == NULL || nMaxFrames <= 0)
117 return ERROR_BAD_PARAMETER;
119 APE_DESCRIPTOR APEDescriptor; memset(&APEDescriptor, 0, sizeof(APEDescriptor));
120 APE_HEADER APEHeader; memset(&APEHeader, 0, sizeof(APEHeader));
122 // create the descriptor (only fill what we know)
123 APEDescriptor.cID[0] = 'M';
124 APEDescriptor.cID[1] = 'A';
125 APEDescriptor.cID[2] = 'C';
126 APEDescriptor.cID[3] = ' ';
127 APEDescriptor.nVersion = MAC_VERSION_NUMBER;
129 APEDescriptor.nDescriptorBytes = sizeof(APEDescriptor);
130 APEDescriptor.nHeaderBytes = sizeof(APEHeader);
131 APEDescriptor.nSeekTableBytes = nMaxFrames * sizeof(unsigned int);
132 APEDescriptor.nHeaderDataBytes = (nHeaderBytes == CREATE_WAV_HEADER_ON_DECOMPRESSION) ? 0 : nHeaderBytes;
134 // create the header (only fill what we know now)
135 APEHeader.nBitsPerSample = pwfeInput->wBitsPerSample;
136 APEHeader.nChannels = pwfeInput->nChannels;
137 APEHeader.nSampleRate = pwfeInput->nSamplesPerSec;
139 APEHeader.nCompressionLevel = (uint16) nCompressionLevel;
140 APEHeader.nFormatFlags = (nHeaderBytes == CREATE_WAV_HEADER_ON_DECOMPRESSION) ? MAC_FORMAT_FLAG_CREATE_WAV_HEADER : 0;
142 APEHeader.nBlocksPerFrame = m_nSamplesPerFrame;
144 // write the data to the file
145 unsigned int nBytesWritten = 0;
146 RETURN_ON_ERROR(pIO->Write(&APEDescriptor, sizeof(APEDescriptor), &nBytesWritten))
147 RETURN_ON_ERROR(pIO->Write(&APEHeader, sizeof(APEHeader), &nBytesWritten))
149 // write an empty seek table
150 m_spSeekTable.Assign(new uint32 [nMaxFrames], TRUE);
151 if (m_spSeekTable == NULL) { return ERROR_INSUFFICIENT_MEMORY; }
152 ZeroMemory(m_spSeekTable, nMaxFrames * 4);
153 RETURN_ON_ERROR(pIO->Write(m_spSeekTable, (nMaxFrames * 4), &nBytesWritten))
154 m_nMaxFrames = nMaxFrames;
156 // write the WAV data
157 if ((pHeaderData != NULL) && (nHeaderBytes > 0) && (nHeaderBytes != CREATE_WAV_HEADER_ON_DECOMPRESSION))
159 m_spAPECompressCore->GetBitArray()->GetMD5Helper().AddData(pHeaderData, nHeaderBytes);
160 RETURN_ON_ERROR(pIO->Write((void *) pHeaderData, nHeaderBytes, &nBytesWritten))
163 return ERROR_SUCCESS;
167 int CAPECompressCreate::FinalizeFile(CIO * pIO, int nNumberOfFrames, int nFinalFrameBlocks, const void * pTerminatingData, int nTerminatingBytes, int nWAVTerminatingBytes, int nPeakLevel)
169 // store the tail position
170 int nTailPosition = pIO->GetPosition();
172 // append the terminating data
173 unsigned int nBytesWritten = 0;
174 unsigned int nBytesRead = 0;
175 int nRetVal = 0;
176 if (nTerminatingBytes > 0)
178 m_spAPECompressCore->GetBitArray()->GetMD5Helper().AddData(pTerminatingData, nTerminatingBytes);
179 if (pIO->Write((void *) pTerminatingData, nTerminatingBytes, &nBytesWritten) != 0) { return ERROR_IO_WRITE; }
182 // go to the beginning and update the information
183 nRetVal = pIO->Seek(0, FILE_BEGIN);
185 // get the descriptor
186 APE_DESCRIPTOR APEDescriptor;
187 nRetVal = pIO->Read(&APEDescriptor, sizeof(APEDescriptor), &nBytesRead);
188 if ((nRetVal != 0) || (nBytesRead != sizeof(APEDescriptor))) { return ERROR_IO_READ; }
190 // get the header
191 APE_HEADER APEHeader;
192 nRetVal = pIO->Read(&APEHeader, sizeof(APEHeader), &nBytesRead);
193 if (nRetVal != 0 || nBytesRead != sizeof(APEHeader)) { return ERROR_IO_READ; }
195 // update the header
196 APEHeader.nFinalFrameBlocks = nFinalFrameBlocks;
197 APEHeader.nTotalFrames = nNumberOfFrames;
199 // update the descriptor
200 APEDescriptor.nAPEFrameDataBytes = nTailPosition - (APEDescriptor.nDescriptorBytes + APEDescriptor.nHeaderBytes + APEDescriptor.nSeekTableBytes + APEDescriptor.nHeaderDataBytes);
201 APEDescriptor.nAPEFrameDataBytesHigh = 0;
202 APEDescriptor.nTerminatingDataBytes = nTerminatingBytes;
204 // update the MD5
205 m_spAPECompressCore->GetBitArray()->GetMD5Helper().AddData(&APEHeader, sizeof(APEHeader));
206 m_spAPECompressCore->GetBitArray()->GetMD5Helper().AddData(m_spSeekTable, m_nMaxFrames * 4);
207 m_spAPECompressCore->GetBitArray()->GetMD5Helper().GetResult(APEDescriptor.cFileMD5);
209 // set the pointer and re-write the updated header and peak level
210 nRetVal = pIO->Seek(0, FILE_BEGIN);
211 if (pIO->Write(&APEDescriptor, sizeof(APEDescriptor), &nBytesWritten) != 0) { return ERROR_IO_WRITE; }
212 if (pIO->Write(&APEHeader, sizeof(APEHeader), &nBytesWritten) != 0) { return ERROR_IO_WRITE; }
214 // write the updated seek table
215 if (pIO->Write(m_spSeekTable, m_nMaxFrames * 4, &nBytesWritten) != 0) { return ERROR_IO_WRITE; }
217 return ERROR_SUCCESS;