Ticket #9031: compress_tinsel.cpp

File compress_tinsel.cpp, 13.0 KB (added by m-kiewitz, 15 years ago)

new compress utility for tinsel smp/idx files

Line 
1/* compress_scumm_sou - monster.sou to MP3-compressed monster.so3 converter
2 * Copyright (C) 2002-2006 The ScummVM Team
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 *
18 * $URL: https://scummvm.svn.sourceforge.net/svnroot/scummvm/tools/trunk/compress_scumm_sou.cpp $
19 * $Id: compress_scumm_sou.cpp 32595 2008-06-07 17:35:09Z fingolfin $
20 *
21 */
22
23#include "compress.h"
24#include "util.h"
25
26// compress_tinsel - by Jimi (m [underline] kiewitz [AT] users.sourceforge.net)
27
28// data-format of index-file:
29// [pointer to data file DWORD] [pointer to data file DWORD] [pointer to data file DWORD]
30// we use index[0] to signal the engine what data format it's supposed to expect. It may be 'MP3 ', 'OGG ' or 'FLAC'
31
32// data-format of sample-file:
33// [sample-length DWORD] [sample-data]
34// or
35// [subsamplecount DWORD] [sample-length DWORD] [sample-data]
36// where subsamplecount has upmost bit 31 set (that's how one differentiates sample-length and subsamplecount)
37// It seems that data-format 1 is used by DiscWorld 1 and data-format 2 is used by DiscWorld 2. Also DiscWorld 1 uses
38// raw-data as samples and DiscWorld 2 uses ADPCM 6-bit encoded data as samples. We suppose that we will need to do ADPCM
39// decoding on all multi-sample data.
40
41// We also copy over the first 5000 bytes of the .smp file, because otherwise we would trash ScummVM detection.
42
43#define TEMP_IDX "compressed.idx"
44#define TEMP_SMP "compressed.smp"
45#define TEMP_RAW "tempfile.raw"
46#define TEMP_ENC "tempfile.enc"
47
48static FILE *input_idx, *input_smp, *output_idx, *output_smp;
49static CompressMode gCompMode = kMP3Mode;
50static char INPUT_IDX[256], INPUT_SMP[256];
51
52/* Converts raw-data sample in input_smp of size SampleSize to requested dataformat and writes to output_smp */
53void convertTinselRawSample (uint32 sampleSize) {
54 uint32 copyLeft = 0;
55 uint32 doneRead = 0;
56 char buffer[2048];
57 FILE *curFileHandle;
58
59 printf("Assuming DW1 sample being 8-bit raw...\n");
60
61 unlink(TEMP_RAW); unlink(TEMP_ENC);
62 curFileHandle = fopen(TEMP_RAW, "wb");
63 copyLeft = sampleSize;
64 while (copyLeft > 0) {
65 doneRead = fread(buffer, 1, copyLeft > sizeof(buffer) ? sizeof(buffer) : copyLeft, input_smp);
66 if (doneRead <= 0)
67 break;
68 copyLeft -= (int)doneRead;
69 fwrite(buffer, 1, doneRead, curFileHandle);
70 }
71 fclose(curFileHandle);
72
73 // Encode this raw data...
74 setRawAudioType(true, false, 8); // LE, mono, 8-bit (??)
75 encodeAudio(TEMP_RAW, true, 22050, TEMP_ENC, gCompMode);
76
77 // Append compressed data to output_smp
78 curFileHandle = fopen(TEMP_ENC, "rb");
79 fseek(curFileHandle, 0, SEEK_END);
80 copyLeft = ftell(curFileHandle);
81 fseek(curFileHandle, 0, SEEK_SET);
82 // Write size of compressed data
83 writeUint32LE(output_smp, copyLeft);
84 // Write actual data
85 while (copyLeft > 0) {
86 doneRead = fread(buffer, 1, copyLeft > sizeof(buffer) ? sizeof(buffer) : copyLeft, curFileHandle);
87 if (doneRead <= 0)
88 break;
89 copyLeft -= (int)doneRead;
90 fwrite(buffer, 1, doneRead, output_smp);
91 }
92 fclose(curFileHandle);
93}
94
95static const double TinselFilterTable[4][2] = {
96 {0, 0 },
97 {0.9375, 0},
98 {1.796875, -0.8125},
99 {1.53125, -0.859375}
100};
101
102template<typename T> inline T CLIP (T v, T amin, T amax)
103 { if (v < amin) return amin; else if (v > amax) return amax; else return v; }
104
105/* Converts ADPCM-data sample in input_smp of size SampleSize to requested dataformat and writes to output_smp */
106/* Quick hack together from adpcm.cpp */
107void convertTinselADPCMSample (uint32 sampleSize) {
108 byte *inBuffer, *inPos;
109 int16 *outBuffer, *outPos;
110 double predictor = 0;
111 double k0 = 0, k1 = 0;
112 double d0 = 0, d1 = 0;
113 uint32 blockAlign, blockPos;
114 uint16 chunkData;
115 int16 chunkWord;
116 uint8 headerByte, filterVal, chunkPos;
117 const double eVal = 1.032226562;
118 uint32 decodeLeft = 0, decodedCount = 0;
119 uint32 uncompressedSize;
120 double sample;
121
122 uint32 copyLeft = 0;
123 uint32 doneRead = 0;
124 char buffer[2048];
125 FILE *curFileHandle;
126
127 printf("Assuming DW2 sample using ADPCM 6-bit, decoding to 16-bit raw...\n");
128
129 // Allocate buffer for the ADPCM-compressed sample
130 inBuffer = (byte *)malloc(sampleSize);
131 if (!inBuffer) {
132 printf("malloc failed!\n");
133 return;
134 }
135
136 // Allocate buffer for uncompressed sample data (3 bytes will be uncompressed to 8 bytes)
137 uncompressedSize = (sampleSize/3)*4*2+16;
138 outBuffer = (int16 *)malloc(uncompressedSize);
139 if (!outBuffer) {
140 printf("malloc failed!\n");
141 return;
142 }
143
144 fread(inBuffer, 1, sampleSize, input_smp);
145
146 // 1 channel, 22050 rate, block align 24,
147 blockAlign = 24; // Fixed for Tinsel 6-bit
148 blockPos = blockAlign; // To make sure first header is read
149
150 inPos = inBuffer; outPos = outBuffer;
151 decodeLeft = sampleSize;
152 while (decodeLeft > 0) {
153 if (blockPos == blockAlign) {
154 // read Tinsel header
155 headerByte = *inPos; inPos++; decodeLeft--;
156 filterVal = (headerByte & 0xC0) >> 6;
157
158 if ((headerByte & 0x20) != 0) {
159 //Lower 6 bit are negative
160 // Negate
161 headerByte = ~(headerByte | 0xC0) + 1;
162 predictor = 1 << headerByte;
163 } else {
164 // Lower 6 bit are positive
165 // Truncate
166 headerByte &= 0x1F;
167 predictor = ((double) 1.0) / (1 << headerByte);
168 }
169 k0 = TinselFilterTable[filterVal][0];
170 k1 = TinselFilterTable[filterVal][1];
171 blockPos = 0;
172 chunkPos = 0;
173 }
174
175 switch (chunkPos) {
176 case 0:
177 chunkData = *inPos; inPos++; decodeLeft--;
178 chunkWord = (chunkData << 8) & 0xFC00;
179 break;
180 case 1:
181 chunkData = (chunkData << 8) | *inPos; inPos++; decodeLeft--;
182 blockPos++;
183 chunkWord = (chunkData << 6) & 0xFC00;
184 break;
185 case 2:
186 chunkData = (chunkData << 8) | *inPos; inPos++; decodeLeft--;
187 blockPos++;
188 chunkWord = (chunkData << 4) & 0xFC00;
189 break;
190 case 3:
191 chunkData = chunkData << 8;
192 blockPos++;
193 chunkWord = (chunkData << 2) & 0xFC00;
194 break;
195 }
196 sample = chunkWord;
197 sample *= eVal * predictor;
198 sample += (d0 * k0) + (d1 * k1);
199 d1 = d0;
200 d0 = sample;
201 *outPos = (int16) CLIP<double>(sample, -32768.0, 32767.0); outPos++;
202 decodedCount++;
203 chunkPos = (chunkPos + 1) % 4;
204 }
205
206 unlink(TEMP_RAW); unlink(TEMP_ENC);
207 curFileHandle = fopen(TEMP_RAW, "wb");
208 fwrite(outBuffer, 1, decodedCount*2, curFileHandle);
209 fclose(curFileHandle);
210
211 free(inBuffer); free(outBuffer);
212
213 // Encode this raw data...
214 setRawAudioType(true, false, 16); // LE, mono, 16-bit
215 encodeAudio(TEMP_RAW, true, 22050, TEMP_ENC, gCompMode);
216
217 // Append compressed data to output_smp
218 curFileHandle = fopen(TEMP_ENC, "rb");
219 fseek(curFileHandle, 0, SEEK_END);
220 copyLeft = ftell(curFileHandle);
221 fseek(curFileHandle, 0, SEEK_SET);
222 // Write size of compressed data
223 writeUint32LE(output_smp, copyLeft);
224 // Write actual data
225 while (copyLeft > 0) {
226 doneRead = fread(buffer, 1, copyLeft > sizeof(buffer) ? sizeof(buffer) : copyLeft, curFileHandle);
227 if (doneRead <= 0)
228 break;
229 copyLeft -= (int)doneRead;
230 fwrite(buffer, 1, doneRead, output_smp);
231 }
232 fclose(curFileHandle);
233}
234
235void showhelp(char *exename) {
236 printf("\nUsage: %s [params] [file]\n", exename);
237
238 printf("\nParams:\n");
239 printf(" --mp3 encode to MP3 format (default)\n");
240 printf(" --vorbis encode to Vorbis format\n");
241 printf(" --flac encode to Flac format\n");
242 printf("(If one of these is specified, it must be the first parameter.)\n");
243
244 printf("\nMP3 mode params:\n");
245 printf(" -b <rate> <rate> is the target bitrate(ABR)/minimal bitrate(VBR) (default:%d)\n", minBitrDef);
246 printf(" -B <rate> <rate> is the maximum VBR/ABR bitrate (default:%d)\n", maxBitrDef);
247 printf(" --vbr LAME uses the VBR mode (default)\n");
248 printf(" --abr LAME uses the ABR mode\n");
249 printf(" -V <value> specifies the value (0 - 9) of VBR quality (0=best) (default:%d)\n", vbrqualDef);
250 printf(" -q <value> specifies the MPEG algorithm quality (0-9; 0=best) (default:%d)\n", algqualDef);
251 printf(" --silent the output of LAME is hidden (default:disabled)\n");
252
253 printf("\nVorbis mode params:\n");
254 printf(" -b <rate> <rate> is the nominal bitrate (default:unset)\n");
255 printf(" -m <rate> <rate> is the minimum bitrate (default:unset)\n");
256 printf(" -M <rate> <rate> is the maximum bitrate (default:unset)\n");
257 printf(" -q <value> specifies the value (0 - 10) of VBR quality (10=best) (default:%d)\n", oggqualDef);
258 printf(" --silent the output of oggenc is hidden (default:disabled)\n");
259
260 printf("\nFlac mode params:\n");
261 printf(" --fast FLAC uses compression level 0\n");
262 printf(" --best FLAC uses compression level 8\n");
263 printf(" -<value> specifies the value (0 - 8) of compression (8=best)(default:%d)\n", flacCompressDef);
264 printf(" -b <value> specifies a blocksize of <value> samples (default:%d)\n", flacBlocksizeDef);
265 printf(" --verify files are encoded and then decoded to check accuracy\n");
266 printf(" --silent the output of FLAC is hidden (default:disabled)\n");
267
268 printf("\n --help this help message\n");
269
270 printf("\n\nIf a parameter is not given the default value is used\n");
271 printf("If using VBR mode for MP3 -b and -B must be multiples of 8; the maximum is 160!\n");
272 exit(2);
273}
274
275int main(int argc, char *argv[]) {
276 char inputPath[768];
277 int i;
278 uint32 indexNo = 0;
279 uint32 indexCount = 0;
280 uint32 indexOffset = 0;
281 uint32 loopCount = 0;
282 uint32 sampleSize = 0;
283 uint32 sampleCount = 0;
284
285 if (argc < 2) {
286 showhelp(argv[0]);
287 }
288
289 /* Compression mode */
290 gCompMode = kMP3Mode;
291 i = 1;
292
293 if (strcmp(argv[1], "--mp3") == 0) {
294 gCompMode = kMP3Mode;
295 i++;
296 } else if (strcmp(argv[1], "--vorbis") == 0) {
297 gCompMode = kVorbisMode;
298 i++;
299 } else if (strcmp(argv[1], "--flac") == 0) {
300 gCompMode = kFlacMode;
301 i++;
302 }
303
304 switch (gCompMode) {
305 case kMP3Mode:
306 if (!process_mp3_parms(argc, argv, i))
307 showhelp(argv[0]);
308 break;
309 case kVorbisMode:
310 if (!process_ogg_parms(argc, argv, i))
311 showhelp(argv[0]);
312 break;
313 case kFlacMode:
314 if (!process_flac_parms(argc, argv, i))
315 showhelp(argv[0]);
316 break;
317 }
318
319 getPath(argv[argc - 1], inputPath);
320
321 sprintf(INPUT_IDX, "%s.idx", argv[argc - 1]);
322 sprintf(INPUT_SMP, "%s.smp", argv[argc - 1]);
323
324 input_idx = fopen(INPUT_IDX, "rb");
325 if (!input_idx) {
326 printf("Cannot open file: %s\n", INPUT_IDX);
327 exit(-1);
328 }
329
330 input_smp = fopen(INPUT_SMP, "rb");
331 if (!input_smp) {
332 printf("Cannot open file: %s\n", INPUT_SMP);
333 exit(-1);
334 }
335
336 unlink(TEMP_IDX);
337 output_idx = fopen(TEMP_IDX, "wb");
338 if (!output_idx) {
339 printf("Can't open file " TEMP_IDX " for write!\n" );
340 exit(-1);
341 }
342 unlink(TEMP_SMP);
343 output_smp = fopen(TEMP_SMP, "wb");
344 if (!output_smp) {
345 printf("Can't open file " TEMP_SMP " for write!\n");
346 exit(-1);
347 }
348
349 fseek(input_idx, 0, SEEK_END);
350 indexCount = ftell(input_idx) / sizeof(uint32);
351 fseek(input_idx, 0, SEEK_SET);
352
353 loopCount = indexCount;
354 while (loopCount>0) {
355 indexOffset = readUint32LE(input_idx);
356 if (indexOffset) {
357 if (indexNo==0) {
358 printf("The sourcefiles are already compressed, aborting...\n");
359 return 1;
360 }
361 // Got sample(s), so convert...
362 printf("Converting sample %d of %d\n", indexNo, indexCount);
363
364 // Seek to Sample in input-file and read SampleSize
365 fseek(input_smp, indexOffset, SEEK_SET);
366 sampleSize = readUint32LE(input_smp);
367
368 // Write offset of new data to new index file
369 writeUint32LE(output_idx, ftell(output_smp));
370
371 if (sampleSize & 0x80000000) {
372 // multiple samples in ADPCM format
373 sampleCount = sampleSize & ~0x80000000;
374 // Write sample count to new sample file
375 writeUint32LE(output_smp, sampleSize);
376 while (sampleCount>0) {
377 sampleSize = readUint32LE(input_smp);
378 convertTinselADPCMSample(sampleSize);
379 sampleCount--;
380 }
381 } else {
382 // just one sample in raw format
383 convertTinselRawSample(sampleSize);
384 }
385 } else {
386 if (indexNo==0) {
387 // Write signature as index 0
388 switch (gCompMode) {
389 case kMP3Mode: writeUint32BE(output_idx, MKID_BE('MP3 ')); break;
390 case kVorbisMode: writeUint32BE(output_idx, MKID_BE('OGG ')); break;
391 case kFlacMode: writeUint32BE(output_idx, MKID_BE('FLAC')); break;
392 }
393 } else {
394 writeUint32LE(output_idx, 0);
395 }
396 }
397 loopCount--; indexNo++;
398 }
399 fclose(output_smp);
400 fclose(output_idx);
401 fclose(input_smp);
402 fclose(input_idx);
403
404 /* And some clean-up :-) */
405 unlink(TEMP_RAW);
406 unlink(TEMP_ENC);
407
408 return 0;
409}