Ticket #9131: compress_bs1_mac.patch

File compress_bs1_mac.patch, 9.9 KB (added by criezy, 11 years ago)
  • compress.h

     
    125125        void extractAndEncodeVOC(const char *outName, Common::File &input, AudioFormat compMode);
    126126        void extractAndEncodeWAV(const char *outName, Common::File &input, AudioFormat compMode);
    127127
     128        void encodeAIF(const char *inName, const char *outName, AudioFormat compMode);
     129
    128130        void encodeAudio(const char *inname, bool rawInput, int rawSamplerate, const char *outname, AudioFormat compmode);
    129131        void encodeRaw(char *rawData, int length, int samplerate, const char *outname, AudioFormat compmode);
    130132        void setRawAudioType(bool isLittleEndian, bool isStereo, uint8 bitsPerSample);
  • engines/sword1/compress_sword1.h

     
    3030        CompressSword1(const std::string &name = "compress_sword1");
    3131
    3232        virtual void execute();
     33       
     34        virtual InspectionMatch inspectInput(const Common::Filename &filename);
    3335
    3436        bool _compSpeech;
    3537        bool _compMusic;
     
    4547        void compressSpeech(const Common::Filename *inpath, const Common::Filename *outpath);
    4648        void compressMusic(const Common::Filename *inpath, const Common::Filename *outpath);
    4749        void checkFilesExist(bool checkSpeech, bool checkMusic, const Common::Filename *inpath);
     50        void guessEndianness(int16 *data, int16 length);
     51       
     52private:
     53        bool _macVersion;
     54       
     55        enum Endianness { BigEndian , LittleEndian , UnknownEndian } ;
     56        Endianness _speechEndianness;
    4857};
    4958
    5059#endif
  • engines/sword1/compress_sword1.cpp

     
    343343                }
    344344                free(fBuf);
    345345                *returnSize = resSize * 2;
     346                if (_speechEndianness == UnknownEndian)
     347                        guessEndianness(dstData, length);
    346348                return dstData;
    347349        } else {
    348350                free(fBuf);
     
    352354        }
    353355}
    354356
     357void CompressSword1::guessEndianness(int16 *data, int16 length) {
     358        // Compute average of difference between two consecutive samples for both the given
     359        // data array and the byte swapped array.
     360        double bs_diff_sum = 0., diff_sum = 0.;
     361        if (length > 2000)
     362                length = 2000;
     363
     364        int16 prev_bs_value = (int16)SWAP_16(*((uint16*)data));
     365        for (int16 i = 1 ; i < length ; ++i) {
     366                diff_sum += data[i] > data[i-1] ? data[i] - data[i-1] : data[i-1] - data[i];
     367                int16 bs_value = (int16)SWAP_16(*((uint16*)(data + i)));
     368                bs_diff_sum += bs_value > prev_bs_value ? bs_value - prev_bs_value : prev_bs_value - bs_value;
     369                prev_bs_value = bs_value;
     370        }
     371        // Set the little/big endian flags
     372        if (diff_sum < bs_diff_sum)
     373                _speechEndianness = LittleEndian;
     374        else
     375                _speechEndianness = BigEndian;
     376        setRawAudioType(_speechEndianness == LittleEndian, false, 16);
     377}
     378
    355379uint8 *CompressSword1::convertData(uint8 *rawData, uint32 rawSize, uint32 *resSize) {
    356380        uint8 *resBuf;
    357381
     
    441465        int i;
    442466        char cluName[256], outName[256];
    443467
    444         setRawAudioType(true, false, 16);
     468        if (_speechEndianness != UnknownEndian)
     469                setRawAudioType(_speechEndianness == LittleEndian, false, 16);
    445470
    446471        for (i = 1; i <= 2; i++) {
    447472                // Updates the progress bar, add music files if we compress those too
     
    491516                // Update the progress bar, we add 2 if we compress speech to, for those files
    492517                updateProgress(i, TOTAL_TUNES +(_compSpeech? 2 : 0));
    493518
    494                 sprintf(fNameIn, "%s/MUSIC/%s.WAV", inpath->getPath().c_str(), musicNames[i].fileName);
     519                if (!_macVersion)
     520                        sprintf(fNameIn, "%s/MUSIC/%s.WAV", inpath->getPath().c_str(), musicNames[i].fileName);
     521                else
     522                        sprintf(fNameIn, "%s/MUSIC/%s.AIF", inpath->getPath().c_str(), musicNames[i].fileName);
    495523                try {
    496524                        Common::File inf(fNameIn, "rb");
    497525
     
    510538                        }
    511539
    512540                        print("encoding file (%3d/%d) %s -> %s\n", i + 1, TOTAL_TUNES, musicNames[i].fileName, fNameOut);
    513                         encodeAudio(fNameIn, false, -1, fNameOut, _format);
     541                        if (!_macVersion)
     542                                encodeAudio(fNameIn, false, -1, fNameOut, _format);
     543                        else
     544                                encodeAIF(fNameIn, fNameOut, _format);
    514545                } catch (Common::FileException& err) {
    515546                        print(err.what());
    516547                }
     
    544575                }
    545576        }
    546577
     578        /* The PC Version uses LittleEndian speech files.
     579         The Mac version can be either with little endian speech files or big endian speech files.
     580         We detect if we have a PC or Mac version with the music files (WAV for PC and AIF for Mac).
     581         If we have the Mac version or if we don't check the music files, an heuristic will be used
     582         when first accessing the speech file to detect if it is little endian or big endian */
     583         
    547584        if (checkMusic) {
    548585                for (i = 0; i < 20; i++) { /* Check the first 20 music files */
     586                        // Check WAV file
    549587                        sprintf(fileName, "%s/MUSIC/%s.WAV", inpath->getPath().c_str(), musicNames[i].fileName);
    550588                        testFile = fopen(fileName, "rb");
    551589
    552590                        if (testFile) {
    553591                                musicFound = true;
    554592                                fclose(testFile);
     593                                break;
    555594                        }
     595                       
     596                        // Check AIF file
     597                        sprintf(fileName, "%s/MUSIC/%s.AIF", inpath->getPath().c_str(), musicNames[i].fileName);
     598                        testFile = fopen(fileName, "rb");
     599                       
     600                        if (testFile) {
     601                                musicFound = true;
     602                                _macVersion = true;
     603                                _speechEndianness = UnknownEndian;
     604                                fclose(testFile);
     605                                break;
     606                        }
    556607                }
    557608
    558609                if (!musicFound) {
     
    562613                        print("If your OS is case-sensitive, make sure the filenames\n");
    563614                        print("and directorynames are all upper-case.\n");
    564615                }
     616        } else {
     617                _speechEndianness = UnknownEndian;
    565618        }
    566619
    567620        if ((checkSpeech && (!speechFound)) || (checkMusic && (!musicFound))) {
     
    569622        }
    570623}
    571624
     625InspectionMatch CompressSword1::inspectInput(const Common::Filename &filename) {
     626        // Wildcard matching as implemented in Tools is too restrictive (e.g. it doesn't
     627        // work with *.cl? or even *.cl*).
     628        // This is the reason why this function is reimplemented there.
     629        if (
     630                scumm_stricmp(filename.getExtension().c_str(), "clu") == 0 ||
     631                scumm_stricmp(filename.getExtension().c_str(), "clm") == 0
     632        )
     633                return IMATCH_PERFECT;
     634        return IMATCH_AWFUL;
     635}
     636
    572637CompressSword1::CompressSword1(const std::string &name) : CompressionTool(name, TOOLTYPE_COMPRESSION) {
    573638        _compSpeech = true;
    574639        _compMusic = true;
     640        _macVersion = false;
     641        _speechEndianness = LittleEndian;
    575642
    576643        _supportsProgressBar = true;
    577644
    578645        ToolInput input;
    579         input.format = "*.clu";
     646        input.format = "*.*";
    580647        _inputPaths.push_back(input);
    581648
    582649        _shorthelp = "Used to compress the Broken Sword 1 data files.";
  • compress.cpp

     
    2626#include <sstream>
    2727#include <stdio.h>
    2828
    2929#include "compress.h"
     30#include "common/endian.h"
    3031
    3132#ifdef USE_VORBIS
    3233#include <vorbis/vorbisenc.h>
     
    593594        encodeAudio(outName, false, -1, tempEncoded, compMode);
    594595}
    595596
     597void CompressionTool::encodeAIF(const char *inName, const char *outName, AudioFormat compmode) {
     598        // Get sound definition (length, frequency, stereo, ...)
     599        char buf[4];
     600        Common::File inFile(inName, "rb");
     601        inFile.read_throwsOnError(buf, 4);
     602        if (memcmp(buf, "FORM", 4) != 0)
     603                error("Error: AIFF file has no 'FORM' header");
     604        inFile.readUint32BE();
     605        // Only AIFF (uncompressed) is supported, not AIFC
     606        inFile.read_throwsOnError(buf, 4);
     607        if (memcmp(buf, "AIFF", 4) != 0)
     608                error("Error: AIFF file has no 'AIFF' header");
     609       
     610        bool foundCOMM = false;
     611        bool foundSSND = false;
     612        uint16 numChannels = 0, bitsPerSample = 0;
     613        uint32 numSampleFrames = 0, offset = 0, blockSize = 0, soundOffset = 0;
     614        uint32 sampleRate = 0;
     615       
     616        while ((!foundCOMM || !foundSSND) && !inFile.err() && !inFile.eos()) {
     617               
     618                inFile.read_throwsOnError(buf, 4);
     619                uint32 length = inFile.readUint32BE();
     620                uint32 pos = inFile.pos();
     621               
     622                if (memcmp(buf, "COMM", 4) == 0) {
     623                        foundCOMM = true;
     624                        numChannels = inFile.readUint16BE();
     625                        numSampleFrames = inFile.readUint32BE();
     626                        bitsPerSample = inFile.readUint16BE();
     627                        // The sample rate is stored as an "80 bit IEEE Standard 754 floating
     628                        // point number (Standard Apple Numeric Environment [SANE] data type
     629                        // Extended).
     630                        byte rate_buf[10];
     631                        uint32 last = 0;
     632                        inFile.read_throwsOnError(rate_buf, 10);
     633                        sampleRate = READ_BE_UINT32(rate_buf + 2);
     634                        byte exp = 30 - rate_buf[1];
     635                       
     636                        while (exp--) {
     637                                last = sampleRate;
     638                                sampleRate >>= 1;
     639                        }
     640                       
     641                        if (last & 0x00000001)
     642                                sampleRate++;
     643                } else if (memcmp(buf, "SSND", 4) == 0) {
     644                        foundSSND = true;
     645                        offset = inFile.readUint32BE();
     646                        blockSize = inFile.readUint32BE();
     647                        soundOffset = inFile.pos();
     648                }
     649               
     650                inFile.seek(pos + length, SEEK_SET);
     651        }
     652        if (!foundCOMM)
     653                error("Error: AIFF file has no 'COMM' chunk in 'AIFF' header");
     654       
     655        if (!foundSSND)
     656                error("Error: AIFF file has no 'COMM' chunk in 'SSND' header");
     657       
     658        // Only a subset of the AIFF format is supported
     659        if (numChannels < 1 || numChannels > 2)
     660                error("Error: AIFF file has an unsupported number of channels");
     661       
     662        if (bitsPerSample != 8 && bitsPerSample != 16)
     663                error("Error: AIFF file has an unsupported number of bits per sample");
     664       
     665        if (offset != 0 || blockSize != 0)
     666                error("Error: AIFF file has block-aligned data, which is not supported");
     667       
     668        // Get data and write to temporary file
     669        uint32 size = numSampleFrames * numChannels * (bitsPerSample / 8);
     670        inFile.seek(soundOffset, SEEK_SET);
     671        char *aifData = (char *)malloc(size);
     672        inFile.read_throwsOnError(aifData, size);
     673        Common::File tmpFile(TEMP_RAW, "wb");
     674        tmpFile.write(aifData, size);
     675        tmpFile.close();
     676       
     677        // Convert the temporary raw file to MP3/OGG/FLAC
     678        // Samples are always signed, and big endian.
     679        setRawAudioType(false, numChannels == 2, bitsPerSample);
     680        encodeAudio(TEMP_RAW, true, sampleRate, outName, compmode);
     681       
     682        // Delete temporary file
     683        unlink(TEMP_RAW);
     684}
     685
    596686void CompressionTool::extractAndEncodeVOC(const char *outName, Common::File &input, AudioFormat compMode) {
    597687        int bits;
    598688        int blocktype;