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