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 "compress_tony.h"
27
28 #define TEMP_RAW "tempfile.raw"
29 #define TEMP_ENC "tempfile.enc"
30
CLIP(T v,T amin,T amax)31 template<typename T> inline T CLIP (T v, T amin, T amax) {
32 if (v < amin)
33 return amin;
34 else if (v > amax)
35 return amax;
36 else
37 return v;
38 }
39
CompressTony(const std::string & name)40 CompressTony::CompressTony(const std::string &name) : CompressionTool(name, TOOLTYPE_COMPRESSION) {
41 _supportsProgressBar = false;
42
43 ToolInput input1;
44 input1.format = "*.adp";
45 _inputPaths.push_back(input1);
46
47 _shorthelp = "Used to compress Tony Tough's .adp files.";
48 _helptext = "\nUsage: " + getName() + " [mode-params] [-o outputname] <infile.adp>\n";
49 }
50
51 // This table is used to adjust the step for use on the next sample.
52 // We could half the table, but since the lookup index used is always
53 // a 4-bit nibble, it's more efficient to just keep it as it is.
54 const int16 _stepAdjustTable[16] = {
55 -1, -1, -1, -1, 2, 4, 6, 8,
56 -1, -1, -1, -1, 2, 4, 6, 8
57 };
58
59 const int16 _imaTable[89] = {
60 7, 8, 9, 10, 11, 12, 13, 14,
61 16, 17, 19, 21, 23, 25, 28, 31,
62 34, 37, 41, 45, 50, 55, 60, 66,
63 73, 80, 88, 97, 107, 118, 130, 143,
64 157, 173, 190, 209, 230, 253, 279, 307,
65 337, 371, 408, 449, 494, 544, 598, 658,
66 724, 796, 876, 963, 1060, 1166, 1282, 1411,
67 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024,
68 3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484,
69 7132, 7845, 8630, 9493,10442,11487,12635,13899,
70 15289,16818,18500,20350,22385,24623,27086,29794,
71 32767
72 };
73
decodeIMA(byte code,int channel)74 int16 CompressTony::decodeIMA(byte code, int channel) {
75 int32 E = (2 * (code & 0x7) + 1) * _imaTable[_status.ima_ch[channel].stepIndex] / 8;
76 int32 diff = (code & 0x08) ? -E : E;
77 int32 samp = CLIP<int32>(_status.ima_ch[channel].last + diff, -32768, 32767);
78
79 _status.ima_ch[channel].last = samp;
80 _status.ima_ch[channel].stepIndex += _stepAdjustTable[code];
81 _status.ima_ch[channel].stepIndex = CLIP<int32>(_status.ima_ch[channel].stepIndex, 0, 88);
82
83 return samp;
84 }
85
86 /* Converts ADPCM-data sample in input_adp to requested dataformat and writes to output_smp */
87 /* Quick hack together from adpcm.cpp */
convertTonyADPCMSample()88 void CompressTony::convertTonyADPCMSample() {
89 byte *inBuffer;
90 int16 *outBuffer;
91
92 uint32 doneRead = 0;
93 char buffer[2048];
94 Common::File curFileHandle;
95
96 uint32 rate = _input_adp.readUint32LE();
97 uint32 channels = _input_adp.readUint32LE();
98
99 printf("rate %d channels %d\n", rate, channels);
100 printf("original size %d\n", _input_adp.size());
101
102 int sampleSize = _input_adp.size() - 12; // 4 (signature) + 4 (rate) + 4 (channels)
103
104 // Allocate buffer for the ADPCM-compressed sample
105 inBuffer = (byte *)malloc(sampleSize);
106 if (!inBuffer) {
107 print("malloc failed!");
108 return;
109 }
110 _input_adp.read_throwsOnError(inBuffer, sampleSize);
111
112 // Allocate buffer for uncompressed sample data (3 bytes will be uncompressed to 8 bytes)
113 uint32 uncompressedSize = sampleSize * 2;
114 printf("uncompressed %d bytes\n", uncompressedSize * 2);
115
116 outBuffer = (int16 *)malloc(uncompressedSize * sizeof(int16));
117 if (!outBuffer) {
118 print("malloc failed!");
119 free(inBuffer);
120 return;
121 }
122
123 int decodedSampleCount = 0;
124 int16 decodedSamples[2];
125 uint32 samples;
126 byte data;
127 memset(&_status, 0, sizeof(_status));
128
129 for (samples = 0; samples < uncompressedSize; samples++) {
130 if (decodedSampleCount == 0) {
131 data = inBuffer[samples >> 1];
132 decodedSamples[0] = decodeIMA((data >> 4) & 0x0f, 0);
133 decodedSamples[1] = decodeIMA((data >> 0) & 0x0f, channels == 2 ? 1 : 0);
134 decodedSampleCount = 2;
135 }
136 // (1 - (count - 1)) ensures that _decodedSamples acts as a FIFO of depth 2
137 outBuffer[samples] = decodedSamples[1 - (decodedSampleCount - 1)];
138 decodedSampleCount--;
139 }
140
141 Common::removeFile("TEMP.RAW");
142 Common::removeFile(TEMP_RAW);
143 Common::removeFile(TEMP_ENC);
144
145 curFileHandle.open(TEMP_RAW, "wb");
146 for (uint32 i = 0; i < uncompressedSize; i++)
147 curFileHandle.writeUint16LE(outBuffer[i]);
148 curFileHandle.close();
149
150 free(inBuffer);
151 free(outBuffer);
152
153 // Encode this raw data...
154 setRawAudioType(true, true, 16); // LE, stereo, 16-bit
155 encodeAudio(TEMP_RAW, true, rate, TEMP_ENC, _format);
156
157 // Append compressed data to output_smp
158 curFileHandle.open(TEMP_ENC, "rb");
159 curFileHandle.seek(0, SEEK_END);
160 uint32 copyLeft = curFileHandle.pos();
161 curFileHandle.seek(0, SEEK_SET);
162
163 // Write size of compressed data
164 // Write actual data
165 while (copyLeft > 0) {
166 doneRead = curFileHandle.read_noThrow(buffer, copyLeft > sizeof(buffer) ? sizeof(buffer) : copyLeft);
167 if (doneRead <= 0)
168 break;
169 copyLeft -= (int)doneRead;
170 _output_enc.write(buffer, doneRead);
171 }
172 }
173
174 static const char f_hdr[] = {
175 'A', 'D', 'P', 0x10, 0
176 };
177
execute()178 void CompressTony::execute() {
179 Common::Filename inpath_adp = _inputPaths[0].path;
180 Common::Filename &outpath = _outputPath;
181
182 if (outpath.empty())
183 outpath = inpath_adp.getPath();
184
185
186 char buf[2048];
187 _input_adp.open(inpath_adp, "rb");
188 _input_adp.read_throwsOnError(buf, 4);
189 if (strncmp(buf, f_hdr, 4)) {
190 error("Bad ADP");
191 }
192
193 Common::Filename outpath_enc = outpath;
194 outpath_enc.setFullName(inpath_adp.getFullName());
195 switch(_format) {
196 case AUDIO_MP3:
197 outpath_enc.setExtension(".MP3");
198 break;
199 case AUDIO_VORBIS:
200 outpath_enc.setExtension(".OGG");
201 break;
202 case AUDIO_FLAC:
203 outpath_enc.setExtension(".FLA");
204 break;
205 default:
206 throw ToolException("Unknown audio format");
207 break;
208 }
209
210 Common::removeFile(outpath_enc.getFullPath().c_str());
211 _output_enc.open(outpath_enc, "wb");
212
213 convertTonyADPCMSample();
214
215 /* And some clean-up :-) */
216 Common::removeFile(TEMP_RAW);
217 Common::removeFile(TEMP_ENC);
218 }
219
220
221 #ifdef STANDALONE_MAIN
main(int argc,char * argv[])222 int main(int argc, char *argv[]) {
223 CompressTony tony(argv[0]);
224 return tony.run(argc, argv);
225 }
226 #endif
227