| 1 | /* sword2mp3 - Compress Broken Sword II sound clusters into MP3/Ogg Vorbis |
| 2 | * Copyright (C) 2002, 2003 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
| 17 | * |
| 18 | * $Header$ |
| 19 | * |
| 20 | */ |
| 21 | |
| 22 | #include "extract.h" |
| 23 | |
| 24 | #define TEMP_IDX "tempfile.idx" |
| 25 | #define TEMP_DAT "tempfile.dat" |
| 26 | |
| 27 | static FILE *input, *output_idx, *output_snd; |
| 28 | |
| 29 | static CompressMode gCompMode = kMP3Mode; |
| 30 | |
| 31 | void showhelp(char *exename) |
| 32 | { |
| 33 | printf("\nUsage: %s <params> monster.sou\n", exename); |
| 34 | |
| 35 | printf("\nParams:\n"); |
| 36 | printf(" --mp3 encode to MP3 format (default)\n"); |
| 37 | printf(" --vorbis encode to Vorbis format\n"); |
| 38 | printf(" --flac encode to Flac format\n"); |
| 39 | printf("(If one of these is specified, it must be the first parameter.)\n"); |
| 40 | |
| 41 | printf("\nMP3 mode params:\n"); |
| 42 | printf(" -b <rate> <rate> is the target bitrate(ABR)/minimal bitrate(VBR) (default:%i)\n", minBitrDef); |
| 43 | printf(" -B <rate> <rate> is the maximum VBR/ABR bitrate (default:%i)\n", maxBitrDef); |
| 44 | printf(" --vbr LAME uses the VBR mode (default)\n"); |
| 45 | printf(" --abr LAME uses the ABR mode\n"); |
| 46 | printf(" -V <value> specifies the value (0 - 9) of VBR quality (0=best) (default:%i)\n", vbrqualDef); |
| 47 | printf(" -q <value> specifies the MPEG algorithm quality (0-9; 0=best) (default:%i)\n", algqualDef); |
| 48 | printf(" --silent the output of LAME is hidden (default:disabled)\n"); |
| 49 | |
| 50 | printf("\nVorbis mode params:\n"); |
| 51 | printf(" -b <rate> <rate> is the nominal bitrate (default:unset)\n"); |
| 52 | printf(" -m <rate> <rate> is the minimum bitrate (default:unset)\n"); |
| 53 | printf(" -M <rate> <rate> is the maximum bitrate (default:unset)\n"); |
| 54 | printf(" -q <value> specifies the value (0 - 10) of VBR quality (10=best) (default:%i)\n", oggqualDef); |
| 55 | printf(" --silent the output of oggenc is hidden (default:disabled)\n"); |
| 56 | |
| 57 | printf("\nFlac mode params:\n"); |
| 58 | printf(" [params] optional Arguments passed directly to the Encoder\n"); |
| 59 | printf(" recommended is: --best -b 1152\n"); |
| 60 | |
| 61 | printf("\n --help this help message\n"); |
| 62 | |
| 63 | printf("\n\nIf a parameter is not given the default value is used\n"); |
| 64 | printf("If using VBR mode for MP3 -b and -B must be multiples of 8; the maximum is 160!\n"); |
| 65 | exit(2); |
| 66 | } |
| 67 | |
| 68 | uint32 append_to_file(FILE *f1, const char *filename) { |
| 69 | FILE *f2; |
| 70 | uint32 length, orig_length; |
| 71 | size_t size; |
| 72 | char fbuf[2048]; |
| 73 | |
| 74 | f2 = fopen(filename, "rb"); |
| 75 | if (!f2) { |
| 76 | printf("Can't open file %s for reading!\n", filename); |
| 77 | exit(-1); |
| 78 | } |
| 79 | |
| 80 | orig_length = length = fileSize(f2); |
| 81 | |
| 82 | while (length > 0) { |
| 83 | size = fread(fbuf, 1, length > sizeof(fbuf) ? sizeof(fbuf) : length, f2); |
| 84 | if (size <= 0) |
| 85 | break; |
| 86 | length -= size; |
| 87 | fwrite(fbuf, 1, size, f1); |
| 88 | } |
| 89 | |
| 90 | fclose(f2); |
| 91 | return orig_length; |
| 92 | } |
| 93 | |
| 94 | #define GetCompressedShift(n) ((n) >> 4) |
| 95 | #define GetCompressedSign(n) (((n) >> 3) & 1) |
| 96 | #define GetCompressedAmplitude(n) ((n) & 7) |
| 97 | |
| 98 | int main(int argc, char *argv[]) { |
| 99 | char output_filename[40]; |
| 100 | FILE *output, *f; |
| 101 | char *ptr; |
| 102 | int i, j; |
| 103 | uint32 indexSize; |
| 104 | uint32 totalSize; |
| 105 | uint32 length; |
| 106 | |
| 107 | if (argc < 2) |
| 108 | showhelp(argv[0]); |
| 109 | i = 1; |
| 110 | if (strcmp(argv[1], "--mp3") == 0) { |
| 111 | gCompMode = kMP3Mode; |
| 112 | i++; |
| 113 | } |
| 114 | else if (strcmp(argv[1], "--vorbis") == 0) { |
| 115 | gCompMode = kVorbisMode; |
| 116 | i++; |
| 117 | } else if (strcmp(argv[1], "--flac") == 0) { |
| 118 | gCompMode = kFlacMode; |
| 119 | i++; |
| 120 | } |
| 121 | |
| 122 | switch (gCompMode) { |
| 123 | case kMP3Mode: |
| 124 | tempEncoded = TEMP_MP3; |
| 125 | process_mp3_parms(argc, argv, i); |
| 126 | break; |
| 127 | case kVorbisMode: |
| 128 | tempEncoded = TEMP_OGG; |
| 129 | process_ogg_parms(argc, argv, i); |
| 130 | break; |
| 131 | case kFlacMode: |
| 132 | tempEncoded = TEMP_FLAC; |
| 133 | process_flac_parms(argc, argv, i); |
| 134 | break; |
| 135 | } |
| 136 | |
| 137 | i = argc - 1; |
| 138 | |
| 139 | input = fopen(argv[i], "rb"); |
| 140 | if (!input) { |
| 141 | printf("Cannot open file: %s\n", argv[i]); |
| 142 | return EXIT_FAILURE; |
| 143 | } |
| 144 | |
| 145 | indexSize = readUint32LE(input); |
| 146 | totalSize = 8 * (indexSize + 1); |
| 147 | |
| 148 | if (readUint32BE(input) != 0xfff0fff0) { |
| 149 | printf("This doesn't look like a cluster file\n"); |
| 150 | return EXIT_FAILURE; |
| 151 | } |
| 152 | |
| 153 | output_idx = fopen(TEMP_IDX, "wb"); |
| 154 | if (!output_idx) { |
| 155 | printf("Can't open file " TEMP_IDX " for writing!\n"); |
| 156 | return EXIT_FAILURE; |
| 157 | } |
| 158 | |
| 159 | output_snd = fopen(TEMP_DAT, "wb"); |
| 160 | if (!output_snd) { |
| 161 | printf("Can't open file " TEMP_DAT " for writing!\n"); |
| 162 | return EXIT_FAILURE; |
| 163 | } |
| 164 | |
| 165 | writeUint32LE(output_idx, indexSize); |
| 166 | writeUint32BE(output_idx, 0xfff0fff0); |
| 167 | |
| 168 | for (j = strlen(argv[i]) - 1; j >= 0; j--) { |
| 169 | if (argv[i][j] == '/' || argv[i][j] == '\\' || argv[i][j] == ':') { |
| 170 | j++; |
| 171 | break; |
| 172 | } |
| 173 | } |
| 174 | |
| 175 | if (j < 0) |
| 176 | j = 0; |
| 177 | |
| 178 | strncpy(output_filename, argv[i] + j, sizeof(output_filename) - 1); |
| 179 | output_filename[sizeof(output_filename) - 1] = 0; |
| 180 | |
| 181 | ptr = output_filename + strlen(output_filename) - 1; |
| 182 | |
| 183 | switch (gCompMode) { |
| 184 | case kMP3Mode: |
| 185 | *ptr = '3'; |
| 186 | break; |
| 187 | case kVorbisMode: |
| 188 | *ptr = 'g'; |
| 189 | break; |
| 190 | case kFlacMode: |
| 191 | *ptr = 'f'; |
| 192 | break; |
| 193 | } |
| 194 | |
| 195 | for (i = 0; i < indexSize; i++) { |
| 196 | uint32 pos; |
| 197 | uint32 enc_length; |
| 198 | |
| 199 | fseek(input, 8 * (i + 1), SEEK_SET); |
| 200 | |
| 201 | pos = readUint32LE(input); |
| 202 | length = readUint32LE(input); |
| 203 | |
| 204 | if (pos != 0 && length != 0) { |
| 205 | uint16 prev; |
| 206 | |
| 207 | fseek(input, pos, SEEK_SET); |
| 208 | |
| 209 | /* |
| 210 | * The first sample is stored uncompressed. Subsequent |
| 211 | * samples are stored as some sort of 8-bit delta. |
| 212 | */ |
| 213 | |
| 214 | prev = readUint16LE(input); |
| 215 | |
| 216 | f = fopen(TEMP_WAV, "wb"); |
| 217 | if (!f) { |
| 218 | printf("Can't open file %s for writing!\n", TEMP_WAV); |
| 219 | return EXIT_FAILURE; |
| 220 | } |
| 221 | |
| 222 | /* |
| 223 | * Our encoding function assumes that raw data means |
| 224 | * 8-bit data. Rather than going through the trouble of |
| 225 | * adding support for 16-bit data at various byte |
| 226 | * orders, let's just prepend a simple WAV header. |
| 227 | */ |
| 228 | |
| 229 | writeUint32BE(f, 0x52494646); /* "RIFF" */ |
| 230 | writeUint32LE(f, 2 * length + 36); |
| 231 | writeUint32BE(f, 0x57415645); /* "WAVE" */ |
| 232 | writeUint32BE(f, 0x666d7420); /* "fmt " */ |
| 233 | writeUint32LE(f, 16); |
| 234 | writeUint16LE(f, 1); /* PCM */ |
| 235 | writeUint16LE(f, 1); /* mono */ |
| 236 | writeUint32LE(f, 22050); /* sample rate */ |
| 237 | writeUint32LE(f, 44100); /* bytes per second */ |
| 238 | writeUint16LE(f, 2); /* basic block size */ |
| 239 | writeUint16LE(f, 16); /* sample width */ |
| 240 | writeUint32BE(f, 0x64617461); /* "data" */ |
| 241 | writeUint32LE(f, 2 * length); |
| 242 | |
| 243 | writeUint16LE(f, prev); |
| 244 | |
| 245 | for (j = 1; j < length; j++) { |
| 246 | byte data; |
| 247 | uint16 out; |
| 248 | |
| 249 | data = readByte(input); |
| 250 | if (GetCompressedSign(data)) |
| 251 | out = prev - (GetCompressedAmplitude(data) << GetCompressedShift(data)); |
| 252 | else |
| 253 | out = prev + (GetCompressedAmplitude(data) << GetCompressedShift(data)); |
| 254 | |
| 255 | writeUint16LE(f, out); |
| 256 | prev = out; |
| 257 | } |
| 258 | fclose(f); |
| 259 | |
| 260 | encodeAudio(TEMP_WAV, false, -1, tempEncoded, gCompMode); |
| 261 | enc_length = append_to_file(output_snd, tempEncoded); |
| 262 | |
| 263 | writeUint32LE(output_idx, totalSize); |
| 264 | writeUint32LE(output_idx, length); |
| 265 | totalSize = totalSize + enc_length; |
| 266 | } else { |
| 267 | writeUint32LE(output_idx, 0); |
| 268 | writeUint32LE(output_idx, 0); |
| 269 | } |
| 270 | } |
| 271 | |
| 272 | fclose(output_idx); |
| 273 | fclose(output_snd); |
| 274 | |
| 275 | output = fopen(output_filename, "wb"); |
| 276 | if (!output) { |
| 277 | printf("Can't open file %s for writing!\n", output_filename); |
| 278 | return EXIT_FAILURE; |
| 279 | } |
| 280 | |
| 281 | append_to_file(output, TEMP_IDX); |
| 282 | append_to_file(output, TEMP_DAT); |
| 283 | |
| 284 | fclose(output); |
| 285 | unlink(TEMP_DAT); |
| 286 | unlink(TEMP_IDX); |
| 287 | unlink(TEMP_MP3); |
| 288 | unlink(TEMP_OGG); |
| 289 | unlink(TEMP_FLAC); |
| 290 | unlink(TEMP_WAV); |
| 291 | |
| 292 | return EXIT_SUCCESS; |
| 293 | } |