[Scummvm-cvs-logs] SF.net SVN: scummvm:[46991] tools/trunk

sev at users.sourceforge.net sev at users.sourceforge.net
Mon Jan 4 22:23:02 CET 2010


Revision: 46991
          http://scummvm.svn.sourceforge.net/scummvm/?rev=46991&view=rev
Author:   sev
Date:     2010-01-04 21:23:02 +0000 (Mon, 04 Jan 2010)

Log Message:
-----------
Patch #2925828: "Tools: add BS1 mac compression in compress_sword1"

Modified Paths:
--------------
    tools/trunk/compress.cpp
    tools/trunk/compress.h
    tools/trunk/engines/sword1/compress_sword1.cpp
    tools/trunk/engines/sword1/compress_sword1.h

Modified: tools/trunk/compress.cpp
===================================================================
--- tools/trunk/compress.cpp	2010-01-04 20:58:31 UTC (rev 46990)
+++ tools/trunk/compress.cpp	2010-01-04 21:23:02 UTC (rev 46991)
@@ -31,6 +31,7 @@
 #endif /* HAVE_CONFIG_H */
 
 #include "compress.h"
+#include "common/endian.h"
 
 #ifdef USE_VORBIS
 #include <vorbis/vorbisenc.h>
@@ -596,6 +597,95 @@
 	encodeAudio(outName, false, -1, tempEncoded, compMode);
 }
 
+void CompressionTool::encodeAIF(const char *inName, const char *outName, AudioFormat compmode) {
+	// Get sound definition (length, frequency, stereo, ...)
+	char buf[4];
+	Common::File inFile(inName, "rb");
+	inFile.read_throwsOnError(buf, 4);
+	if (memcmp(buf, "FORM", 4) != 0)
+		error("Error: AIFF file has no 'FORM' header");
+	inFile.readUint32BE();
+	// Only AIFF (uncompressed) is supported, not AIFC
+	inFile.read_throwsOnError(buf, 4);
+	if (memcmp(buf, "AIFF", 4) != 0)
+		error("Error: AIFF file has no 'AIFF' header");
+	
+	bool foundCOMM = false;
+	bool foundSSND = false;
+	uint16 numChannels = 0, bitsPerSample = 0;
+	uint32 numSampleFrames = 0, offset = 0, blockSize = 0, soundOffset = 0;
+	uint32 sampleRate = 0;
+	
+	while ((!foundCOMM || !foundSSND) && !inFile.err() && !inFile.eos()) {
+		
+		inFile.read_throwsOnError(buf, 4);
+		uint32 length = inFile.readUint32BE();
+		uint32 pos = inFile.pos();
+		
+		if (memcmp(buf, "COMM", 4) == 0) {
+			foundCOMM = true;
+			numChannels = inFile.readUint16BE();
+			numSampleFrames = inFile.readUint32BE();
+			bitsPerSample = inFile.readUint16BE();
+			// The sample rate is stored as an "80 bit IEEE Standard 754 floating
+			// point number (Standard Apple Numeric Environment [SANE] data type
+			// Extended).
+			byte rate_buf[10];
+			uint32 last = 0;
+			inFile.read_throwsOnError(rate_buf, 10);
+			sampleRate = READ_BE_UINT32(rate_buf + 2);
+			byte exp = 30 - rate_buf[1];
+			
+			while (exp--) {
+				last = sampleRate;
+				sampleRate >>= 1;
+			}
+			
+			if (last & 0x00000001)
+				sampleRate++;
+		} else if (memcmp(buf, "SSND", 4) == 0) {
+			foundSSND = true;
+			offset = inFile.readUint32BE();
+			blockSize = inFile.readUint32BE();
+			soundOffset = inFile.pos();
+		}
+		
+		inFile.seek(pos + length, SEEK_SET);
+	}
+	if (!foundCOMM)
+		error("Error: AIFF file has no 'COMM' chunk in 'AIFF' header");
+	
+	if (!foundSSND)
+		error("Error: AIFF file has no 'COMM' chunk in 'SSND' header");
+	
+	// Only a subset of the AIFF format is supported
+	if (numChannels < 1 || numChannels > 2)
+		error("Error: AIFF file has an unsupported number of channels");
+	
+	if (bitsPerSample != 8 && bitsPerSample != 16)
+		error("Error: AIFF file has an unsupported number of bits per sample");
+	
+	if (offset != 0 || blockSize != 0)
+		error("Error: AIFF file has block-aligned data, which is not supported");
+	
+	// Get data and write to temporary file
+	uint32 size = numSampleFrames * numChannels * (bitsPerSample / 8);
+	inFile.seek(soundOffset, SEEK_SET);
+	char *aifData = (char *)malloc(size);
+	inFile.read_throwsOnError(aifData, size);
+	Common::File tmpFile(TEMP_RAW, "wb");
+	tmpFile.write(aifData, size);
+	tmpFile.close();
+	
+	// Convert the temporary raw file to MP3/OGG/FLAC
+	// Samples are always signed, and big endian.
+	setRawAudioType(false, numChannels == 2, bitsPerSample);
+	encodeAudio(TEMP_RAW, true, sampleRate, outName, compmode);
+	
+	// Delete temporary file
+	unlink(TEMP_RAW);
+}
+
 void CompressionTool::extractAndEncodeVOC(const char *outName, Common::File &input, AudioFormat compMode) {
 	int bits;
 	int blocktype;

Modified: tools/trunk/compress.h
===================================================================
--- tools/trunk/compress.h	2010-01-04 20:58:31 UTC (rev 46990)
+++ tools/trunk/compress.h	2010-01-04 21:23:02 UTC (rev 46991)
@@ -115,6 +115,8 @@
 	void extractAndEncodeVOC(const char *outName, Common::File &input, AudioFormat compMode);
 	void extractAndEncodeWAV(const char *outName, Common::File &input, AudioFormat compMode);
 
+	void encodeAIF(const char *inName, const char *outName, AudioFormat compMode);
+
 	void encodeAudio(const char *inname, bool rawInput, int rawSamplerate, const char *outname, AudioFormat compmode);
 	void setRawAudioType(bool isLittleEndian, bool isStereo, uint8 bitsPerSample);
 

Modified: tools/trunk/engines/sword1/compress_sword1.cpp
===================================================================
--- tools/trunk/engines/sword1/compress_sword1.cpp	2010-01-04 20:58:31 UTC (rev 46990)
+++ tools/trunk/engines/sword1/compress_sword1.cpp	2010-01-04 21:23:02 UTC (rev 46991)
@@ -343,6 +343,8 @@
 		}
 		free(fBuf);
 		*returnSize = resSize * 2;
+		if (_speechEndianness == UnknownEndian)
+			guessEndianness(dstData, length);
 		return dstData;
 	} else {
 		free(fBuf);
@@ -352,6 +354,28 @@
 	}
 }
 
+void CompressSword1::guessEndianness(int16 *data, int16 length) {
+	// Compute average of difference between two consecutive samples for both the given
+	// data array and the byte swapped array.
+	double bs_diff_sum = 0., diff_sum = 0.;
+	if (length > 2000)
+		length = 2000;
+
+	int16 prev_bs_value = (int16)SWAP_16(*((uint16*)data));
+	for (int16 i = 1 ; i < length ; ++i) {
+		diff_sum += data[i] > data[i-1] ? data[i] - data[i-1] : data[i-1] - data[i];
+		int16 bs_value = (int16)SWAP_16(*((uint16*)(data + i)));
+		bs_diff_sum += bs_value > prev_bs_value ? bs_value - prev_bs_value : prev_bs_value - bs_value;
+		prev_bs_value = bs_value;
+	}
+	// Set the little/big endian flags
+	if (diff_sum < bs_diff_sum)
+		_speechEndianness = LittleEndian;
+	else
+		_speechEndianness = BigEndian;
+	setRawAudioType(_speechEndianness == LittleEndian, false, 16);
+}
+
 uint8 *CompressSword1::convertData(uint8 *rawData, uint32 rawSize, uint32 *resSize) {
 	uint8 *resBuf;
 
@@ -441,7 +465,8 @@
 	int i;
 	char cluName[256], outName[256];
 
-	setRawAudioType(true, false, 16);
+	if (_speechEndianness != UnknownEndian)
+		setRawAudioType(_speechEndianness == LittleEndian, false, 16);
 
 	for (i = 1; i <= 2; i++) {
 		// Updates the progress bar, add music files if we compress those too
@@ -491,7 +516,10 @@
 		// Update the progress bar, we add 2 if we compress speech to, for those files
 		updateProgress(i, TOTAL_TUNES +(_compSpeech? 2 : 0));
 
-		sprintf(fNameIn, "%s/MUSIC/%s.WAV", inpath->getPath().c_str(), musicNames[i].fileName);
+		if (!_macVersion)
+			sprintf(fNameIn, "%s/MUSIC/%s.WAV", inpath->getPath().c_str(), musicNames[i].fileName);
+		else
+			sprintf(fNameIn, "%s/MUSIC/%s.AIF", inpath->getPath().c_str(), musicNames[i].fileName);
 		try {
 			Common::File inf(fNameIn, "rb");
 
@@ -510,7 +538,10 @@
 			}
 
 			print("encoding file (%3d/%d) %s -> %s\n", i + 1, TOTAL_TUNES, musicNames[i].fileName, fNameOut);
-			encodeAudio(fNameIn, false, -1, fNameOut, _format);
+			if (!_macVersion)
+				encodeAudio(fNameIn, false, -1, fNameOut, _format);
+			else
+				encodeAIF(fNameIn, fNameOut, _format);
 		} catch (Common::FileException& err) {
 			print(err.what());
 		}
@@ -544,15 +575,35 @@
 		}
 	}
 
+	/* The PC Version uses LittleEndian speech files.
+	 The Mac version can be either with little endian speech files or big endian speech files.
+	 We detect if we have a PC or Mac version with the music files (WAV for PC and AIF for Mac).
+	 If we have the Mac version or if we don't check the music files, an heuristic will be used
+	 when first accessing the speech file to detect if it is little endian or big endian */
+	 
 	if (checkMusic) {
 		for (i = 0; i < 20; i++) { /* Check the first 20 music files */
+			// Check WAV file
 			sprintf(fileName, "%s/MUSIC/%s.WAV", inpath->getPath().c_str(), musicNames[i].fileName);
 			testFile = fopen(fileName, "rb");
 
 			if (testFile) {
 				musicFound = true;
 				fclose(testFile);
+				break;
 			}
+			
+			// Check AIF file
+			sprintf(fileName, "%s/MUSIC/%s.AIF", inpath->getPath().c_str(), musicNames[i].fileName);
+			testFile = fopen(fileName, "rb");
+			
+			if (testFile) {
+				musicFound = true;
+				_macVersion = true;
+				_speechEndianness = UnknownEndian;
+				fclose(testFile);
+				break;
+			}
 		}
 
 		if (!musicFound) {
@@ -562,6 +613,8 @@
 			print("If your OS is case-sensitive, make sure the filenames\n");
 			print("and directorynames are all upper-case.\n");
 		}
+	} else {
+		_speechEndianness = UnknownEndian;
 	}
 
 	if ((checkSpeech && (!speechFound)) || (checkMusic && (!musicFound))) {
@@ -569,14 +622,28 @@
 	}
 }
 
+InspectionMatch CompressSword1::inspectInput(const Common::Filename &filename) {
+	// Wildcard matching as implemented in Tools is too restrictive (e.g. it doesn't
+	// work with *.cl? or even *.cl*).
+	// This is the reason why this function is reimplemented there.
+	if (
+		scumm_stricmp(filename.getExtension().c_str(), "clu") == 0 ||
+		scumm_stricmp(filename.getExtension().c_str(), "clm") == 0
+	)
+		return IMATCH_PERFECT;
+	return IMATCH_AWFUL;
+}
+
 CompressSword1::CompressSword1(const std::string &name) : CompressionTool(name, TOOLTYPE_COMPRESSION) {
 	_compSpeech = true;
 	_compMusic = true;
+	_macVersion = false;
+	_speechEndianness = LittleEndian;
 
 	_supportsProgressBar = true;
 
 	ToolInput input;
-	input.format = "*.clu";
+	input.format = "*.*";
 	_inputPaths.push_back(input);
 
 	_shorthelp = "Used to compress the Broken Sword 1 data files.";

Modified: tools/trunk/engines/sword1/compress_sword1.h
===================================================================
--- tools/trunk/engines/sword1/compress_sword1.h	2010-01-04 20:58:31 UTC (rev 46990)
+++ tools/trunk/engines/sword1/compress_sword1.h	2010-01-04 21:23:02 UTC (rev 46991)
@@ -30,6 +30,8 @@
 	CompressSword1(const std::string &name = "compress_sword1");
 
 	virtual void execute();
+	
+	virtual InspectionMatch inspectInput(const Common::Filename &filename);
 
 	bool _compSpeech;
 	bool _compMusic;
@@ -45,6 +47,13 @@
 	void compressSpeech(const Common::Filename *inpath, const Common::Filename *outpath);
 	void compressMusic(const Common::Filename *inpath, const Common::Filename *outpath);
 	void checkFilesExist(bool checkSpeech, bool checkMusic, const Common::Filename *inpath);
+	void guessEndianness(int16 *data, int16 length);
+	
+private:
+	bool _macVersion;
+	
+	enum Endianness { BigEndian , LittleEndian , UnknownEndian } ;
+	Endianness _speechEndianness;
 };
 
 #endif


This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.




More information about the Scummvm-git-logs mailing list