1 /* ScummVM Tools
2  *
3  * ScummVM Tools is the legal property of its developers, whose
4  * names are too numerous to list here. Please refer to the
5  * COPYRIGHT file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  */
21 
22 #include <stdlib.h>
23 
24 #include "compress.h"
25 #include "common/endian.h"
26 #include "common/str.h"
27 #include "compress_tony_vdb.h"
28 
29 #define TEMP_RAW "tempfile.raw"
30 #define TEMP_ENC "tempfile.enc"
31 
CLIP(T v,T amin,T amax)32 template<typename T> inline T CLIP (T v, T amin, T amax) {
33 	if (v < amin)
34 		return amin;
35 	else if (v > amax)
36 		return amax;
37 	else
38 		return v;
39 }
40 
CompressTonyVDB(const std::string & name)41 CompressTonyVDB::CompressTonyVDB(const std::string &name) : CompressionTool(name, TOOLTYPE_COMPRESSION) {
42 	_supportsProgressBar = true;
43 
44 	ToolInput input1;
45 	input1.format = "*.vdb";
46 	_inputPaths.push_back(input1);
47 
48 	_shorthelp = "Used to compress Tony Tough's .vdb files.";
49 	_helptext = "\nUsage: " + getName() + " [mode-params] <infile.vdb>\n";
50 }
51 
52 // This table is used to adjust the step for use on the next sample.
53 // We could half the table, but since the lookup index used is always
54 // a 4-bit nibble, it's more efficient to just keep it as it is.
55 const int16 _stepAdjustTable[16] = {
56 	-1, -1, -1, -1, 2, 4, 6, 8,
57 	-1, -1, -1, -1, 2, 4, 6, 8
58 };
59 
60 const int16 _imaTable[89] = {
61 	7,    8,    9,   10,   11,   12,   13,   14,
62 	16,   17,   19,   21,   23,   25,   28,   31,
63 	34,   37,   41,   45,   50,   55,   60,   66,
64 	73,   80,   88,   97,  107,  118,  130,  143,
65 	157,  173,  190,  209,  230,  253,  279,  307,
66 	337,  371,  408,  449,  494,  544,  598,  658,
67 	724,  796,  876,  963, 1060, 1166, 1282, 1411,
68 	1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024,
69 	3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484,
70 	7132, 7845, 8630, 9493,10442,11487,12635,13899,
71 	15289,16818,18500,20350,22385,24623,27086,29794,
72 	32767
73 };
74 
decodeIMA(byte code,int channel)75 int16 CompressTonyVDB::decodeIMA(byte code, int channel) {
76 	int32 E = (2 * (code & 0x7) + 1) * _imaTable[_status.ima_ch[channel].stepIndex] / 8;
77 	int32 diff = (code & 0x08) ? -E : E;
78 	int32 samp = CLIP<int32>(_status.ima_ch[channel].last + diff, -32768, 32767);
79 
80 	_status.ima_ch[channel].last = samp;
81 	_status.ima_ch[channel].stepIndex += _stepAdjustTable[code];
82 	_status.ima_ch[channel].stepIndex = CLIP<int32>(_status.ima_ch[channel].stepIndex, 0, 88);
83 
84 	return samp;
85 }
86 
87 /* Converts ADPCM-data sample in input_adp to requested dataformat and writes to output_smp */
88 /* Quick hack together from adpcm.cpp */
convertTonyADPCMSample()89 bool CompressTonyVDB::convertTonyADPCMSample() {
90 	Common::File curFileHandle;
91 
92 	int decodedSampleCount = 0;
93 	int16 decodedSamples[2];
94 	uint32 samples;
95 	byte data;
96 	memset(&_status, 0, sizeof(_status));
97 
98 	for (samples = 0; samples < _uncompressedSize; samples++) {
99 		if (decodedSampleCount == 0) {
100 			data = _inBuffer[samples >> 1];
101 			decodedSamples[0] = decodeIMA((data >> 4) & 0x0f, 0);
102 			decodedSamples[1] = decodeIMA((data >> 0) & 0x0f, 0); // 1 channel, hardcoded
103 			decodedSampleCount = 2;
104 		}
105 		// (1 - (count - 1)) ensures that _decodedSamples acts as a FIFO of depth 2
106 		_outBuffer[samples] = decodedSamples[1 - (decodedSampleCount - 1)];
107 		decodedSampleCount--;
108 	}
109 
110 	Common::removeFile(TEMP_RAW);
111 	Common::removeFile(TEMP_ENC);
112 
113 	curFileHandle.open(TEMP_RAW, "wb");
114 	for (uint32 i = 0; i < _uncompressedSize; i++)
115 		curFileHandle.writeUint16LE(_outBuffer[i]);
116 	curFileHandle.close();
117 
118 	// Encode this raw data...
119 	setRawAudioType(true, false, 16); // LE, stereo, 16-bit
120 	encodeAudio(TEMP_RAW, true, _rate, TEMP_ENC, _format);
121 
122 	return true;
123 }
124 
125 static const char vdb_hdr[] = {
126 	'V', 'D', 'B', '1', 0
127 };
128 
execute()129 void CompressTonyVDB::execute() {
130 	Common::Filename inpath_adp = _inputPaths[0].path;
131 	Common::Filename &outpath = _outputPath;
132 
133 	if (outpath.empty())
134 		outpath = inpath_adp.getPath();
135 
136 	char buf[2048];
137 	_input_vdb.open(inpath_adp, "rb");
138 	_input_vdb.seek(-8, SEEK_END);
139 	int numFiles = _input_vdb.readUint32LE();
140 	_input_vdb.read_throwsOnError(buf, 4);
141 
142 	if (strncmp(buf, vdb_hdr, 4)) {
143 		error("Unexpected file signature\n");
144 	}
145 
146 	_input_vdb.seek(-8 - (numFiles * 12), SEEK_END);
147 	VoiceHeader *vh = new VoiceHeader[numFiles];
148 
149 	for (int i = 0; i < numFiles; i++) {
150 		vh[i]._offset = _input_vdb.readUint32LE();
151 		vh[i]._code = _input_vdb.readUint32LE();
152 		vh[i]._parts = _input_vdb.readUint32LE();
153 	}
154 
155 	Common::Filename outpath_enc = outpath;
156 	outpath_enc.setFullName(inpath_adp.getFullName());
157 	switch(_format) {
158 	case AUDIO_MP3:
159 		outpath_enc.setExtension(".MDB");
160 		break;
161 	case AUDIO_VORBIS:
162 		outpath_enc.setExtension(".ODB");
163 		break;
164 	case AUDIO_FLAC:
165 		outpath_enc.setExtension(".FDB");
166 		break;
167 	default:
168 		delete[] vh;
169 		throw ToolException("Unknown audio format");
170 		break;
171 	}
172 
173 	Common::removeFile(outpath_enc.getFullPath().c_str());
174 	_output_enc.open(outpath_enc, "wb");
175 
176 	for (int i = 0; i < numFiles; i++) {
177 		// Update progress
178 		updateProgress(i, numFiles);
179 
180 		_input_vdb.seek(vh[i]._offset, SEEK_SET);
181 
182 		for (int j = 0; j < vh[i]._parts; j++) {
183 			_sampleSize = _input_vdb.readUint32LE();
184 			_rate = _input_vdb.readUint32LE();
185 			_inBuffer = new byte[_sampleSize];
186 			printf("%d\t%d\t%d/%d\n", _input_vdb.pos() - 8, _sampleSize + 8, j + 1, vh[i]._parts);
187 			_input_vdb.read_throwsOnError(_inBuffer, _sampleSize);
188 			_uncompressedSize = _sampleSize * 2;
189 			_outBuffer = new int16[_uncompressedSize];
190 			if (convertTonyADPCMSample()) {
191 				if (j == 0)
192 					vh[i]._offset = _output_enc.pos();
193 
194 				Common::File curFileHandle;
195 				curFileHandle.open(TEMP_ENC, "rb");
196 				curFileHandle.seek(0, SEEK_END);
197 				uint32 copyLeft = curFileHandle.pos();
198 				curFileHandle.seek(0, SEEK_SET);
199 				_output_enc.writeUint32LE(copyLeft);
200 				uint32 doneRead = 0;
201 				char buffer[2048];
202 				while (copyLeft > 0) {
203 					doneRead = curFileHandle.read_noThrow(buffer, copyLeft > sizeof(buffer) ? sizeof(buffer) : copyLeft);
204 					if (doneRead <= 0)
205 						break;
206 					copyLeft -= (int)doneRead;
207 					_output_enc.write(buffer, doneRead);
208 				}
209 				curFileHandle.close();
210 			}
211 			delete[](_inBuffer);
212 			delete[](_outBuffer);
213 		}
214 	}
215 	for (int i = 0; i < numFiles; i++) {
216 		_output_enc.writeUint32LE(vh[i]._offset);
217 		_output_enc.writeUint32LE(vh[i]._code);
218 		_output_enc.writeUint32LE(vh[i]._parts);
219 	}
220 
221 	_output_enc.writeUint32LE(numFiles);
222 	switch(_format) {
223 	case AUDIO_MP3:
224 		_output_enc.writeByte('M');
225 		break;
226 	case AUDIO_VORBIS:
227 		_output_enc.writeByte('O');
228 		break;
229 	case AUDIO_FLAC:
230 		_output_enc.writeByte('F');
231 		break;
232 	default:
233 		break;
234 	}
235 
236 	for (int i = 1; i < 4; i++)
237 		_output_enc.writeByte(vdb_hdr[i]);
238 
239 	_output_enc.close();
240 
241 	/* And some clean-up :-) */
242 	Common::removeFile(TEMP_RAW);
243 	Common::removeFile(TEMP_ENC);
244 
245 	delete[] vh;
246 }
247 
248 
249 #ifdef STANDALONE_MAIN
main(int argc,char * argv[])250 int main(int argc, char *argv[]) {
251 	CompressTonyVDB tony(argv[0]);
252 	return tony.run(argc, argv);
253 }
254 #endif
255