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 /* Compress Touche Speech Data Files */
23
24 #include <string.h>
25 #include <stdio.h>
26
27 #include "compress.h"
28 #include "compress_touche.h"
29
30 #define CURRENT_VER 1
31 #define HEADER_SIZE 4
32 #define MAX_OFFSETS 140
33 #define OBJ_HDR_LEN 200
34 #define Vxx_HDR_LEN 1024
35
36 #define OUTPUT_MP3 "TOUCHE.SO3"
37 #define OUTPUT_OGG "TOUCHE.SOG"
38 #define OUTPUT_FLA "TOUCHE.SOF"
39
40 static uint32 input_OBJ_offs[OBJ_HDR_LEN];
41 static uint32 input_OBJ_size[OBJ_HDR_LEN];
42 static uint32 input_Vxx_offs[Vxx_HDR_LEN];
43 static uint32 input_Vxx_size[Vxx_HDR_LEN];
44
CompressTouche(const std::string & name)45 CompressTouche::CompressTouche(const std::string &name) : CompressionTool(name, TOOLTYPE_COMPRESSION) {
46 _supportsProgressBar = true;
47 _outputToDirectory = false;
48
49 ToolInput input;
50 input.format = "/";
51 input.file = false;
52 _inputPaths.push_back(input);
53
54 _shorthelp = "Used to compress Touche speech files (Vxxx and OBJ).";
55 _helptext = "\nUsage: " + getName() + " [params] [-o outputfile] <inputdir>\n* differs with compression type.\n" + _shorthelp + "\n";
56 }
57
inspectInput(const Common::Filename & filename)58 InspectionMatch CompressTouche::inspectInput(const Common::Filename &filename) {
59 if (!filename.directory())
60 return IMATCH_AWFUL;
61
62 Common::Filename probe(filename);
63 probe.setFullName("TOUCHE.DAT");
64 if (probe.exists())
65 return IMATCH_PERFECT;
66
67 return IMATCH_AWFUL;
68 }
69
compress_sound_data_file(uint32 current_offset,Common::File & output,Common::File & input,uint32 * offs_table,uint32 * size_table,int len)70 uint32 CompressTouche::compress_sound_data_file(uint32 current_offset, Common::File &output, Common::File &input, uint32 *offs_table, uint32 *size_table, int len) {
71 int i, size;
72 uint8 buf[2048];
73 uint32 start_offset = current_offset;
74
75 /* write 0 offsets/sizes table */
76 for (i = 0; i < len; ++i) {
77 offs_table[i] = input.readUint32LE();
78 size_table[i] = input.readUint32LE();
79 output.writeUint32LE(0);
80 output.writeUint32LE(0);
81 current_offset += 8;
82 }
83 for (i = 0; i < len; ++i) {
84 if (size_table[i] == 0) {
85 offs_table[i] = 0;
86 } else {
87 input.seek(offs_table[i], SEEK_SET);
88 input.read_throwsOnError(buf, 8);
89
90 if (memcmp(buf, "Creative", 8) != 0) {
91 error("Invalid VOC data found");
92 }
93
94 print("VOC found (pos = %d) :", offs_table[i]);
95 input.seek(18, SEEK_CUR);
96 extractAndEncodeVOC(TEMP_RAW, input, _format);
97
98 /* append converted data to output file */
99 Common::File temp(tempEncoded, "rb");
100
101 size_table[i] = 0;
102
103 while ((size = temp.read_noThrow(buf, 2048)) > 0) {
104 output.write(buf, size);
105 size_table[i] += size;
106 }
107
108 offs_table[i] = current_offset;
109 current_offset += size_table[i];
110 }
111 }
112
113 /* fix data offsets table */
114 output.seek(start_offset, SEEK_SET);
115 for (i = 0; i < len; ++i) {
116 output.writeUint32LE(offs_table[i]);
117 output.writeUint32LE(size_table[i]);
118 }
119 output.seek(0, SEEK_END);
120
121 return current_offset;
122 }
123
compress_sound_data(Common::Filename * inpath,Common::Filename * outpath)124 void CompressTouche::compress_sound_data(Common::Filename *inpath, Common::Filename *outpath) {
125 int i;
126 uint32 current_offset;
127 uint32 offsets_table[MAX_OFFSETS];
128
129 Common::File output(*outpath, "wb");
130
131 output.writeUint16LE(1); /* current version */
132 output.writeUint16LE(0); /* flags */
133
134 current_offset = HEADER_SIZE;
135
136 /* write 0 offsets table */
137 for (i = 0; i < MAX_OFFSETS; ++i) {
138 offsets_table[i] = 0;
139 output.writeUint32LE(offsets_table[i]);
140 current_offset += 4;
141 }
142
143 /* process 'OBJ' file */
144 inpath->setFullName("OBJ");
145 Common::File input(*inpath, "rb");
146
147 offsets_table[0] = current_offset;
148 current_offset = compress_sound_data_file(current_offset, output, input, input_OBJ_offs, input_OBJ_size, OBJ_HDR_LEN);
149 input.close();
150 print("Processed '%s'.", inpath->getFullPath().c_str());
151
152 /* process Vxx files */
153 for (i = 1; i < MAX_OFFSETS; ++i) {
154 updateProgress(i, MAX_OFFSETS);
155
156 char d[16];
157 sprintf(d, "V%d", i);
158 inpath->setFullName(d);
159
160 try {
161 input.open(*inpath, "rb");
162 offsets_table[i] = current_offset;
163 current_offset = compress_sound_data_file(current_offset, output, input, input_Vxx_offs, input_Vxx_size, Vxx_HDR_LEN);
164 input.close();
165 print("Processed '%s'.", inpath->getFullPath().c_str());
166 } catch (...) {
167 //print("Skipping '%s'.", inpath->getFullPath().c_str());
168 }
169 }
170
171 /* fix global offsets table at the beginning of the file */
172 output.seek(HEADER_SIZE, SEEK_SET);
173 for (i = 0; i < MAX_OFFSETS; ++i) {
174 output.writeUint32LE(offsets_table[i]);
175 }
176
177 output.close();
178
179 /* cleanup */
180 Common::removeFile(TEMP_RAW);
181 Common::removeFile(tempEncoded);
182
183 print("Done.");
184 }
185
execute()186 void CompressTouche::execute() {
187 Common::Filename inpath(_inputPaths[0].path);
188 Common::Filename &outpath = _outputPath;
189
190 if (outpath.getFullName().empty()) {
191 switch(_format) {
192 case AUDIO_MP3:
193 outpath.setFullName(OUTPUT_MP3);
194 break;
195 case AUDIO_VORBIS:
196 outpath.setFullName(OUTPUT_OGG);
197 break;
198 case AUDIO_FLAC:
199 outpath.setFullName(OUTPUT_FLA);
200 break;
201 default:
202 throw ToolException("Unknown audio format");
203 break;
204 }
205 }
206
207 compress_sound_data(&inpath, &outpath);
208 }
209
210 #ifdef STANDALONE_MAIN
main(int argc,char * argv[])211 int main(int argc, char *argv[]) {
212 CompressTouche touche(argv[0]);
213 return touche.run(argc, argv);
214 }
215 #endif
216
217