Files
scummvm-tools/compress_queen.cpp
T
Hampus Nilsson 168e91e076 *Started on CLI interface, already works but there are many edges to sort out.
*Added a common 'Tools' class to be used by both the CLI and the GUI, right now it's NOT in use by the GUI, so there is some code clutter.
*Rename gui/tools.h to gui/tools.cpp
*Tools now know their own type.
*Some other small changes.

svn-id: r42670
2009-07-23 01:41:14 +00:00

329 lines
8.4 KiB
C++

/* compress_queen - Rebuild QUEEN.1 file to contain Resource Table (and optionally compress sound & speech)
* Copyright (C) 2009 The ScummVM Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#include "compress.h"
#include "compress_queen.h"
const uint32 QTBL = 'QTBL';
#define INPUT_TBL "queen.tbl"
#define FINAL_OUT "queen.1c"
#define TEMP_DAT "tempfile.dat"
#define TEMP_TBL "tempfile.tbl"
#define TEMP_SB "tempfile.sb"
#define CURRENT_TBL_VERSION 2
#define EXTRA_TBL_HEADER 8
#define SB_HEADER_SIZE_V104 110
#define SB_HEADER_SIZE_V110 122
enum {
VER_ENG_FLOPPY = 0,
VER_ENG_TALKIE = 1,
VER_FRE_FLOPPY = 2,
VER_FRE_TALKIE = 3,
VER_GER_FLOPPY = 4,
VER_GER_TALKIE = 5,
VER_ITA_FLOPPY = 6,
VER_ITA_TALKIE = 7,
VER_SPA_TALKIE = 8,
VER_HEB_TALKIE = 9,
VER_DEMO_PCGAMES = 10,
VER_DEMO = 11,
VER_INTERVIEW = 12,
VER_AMI_ENG_FLOPPY = 13,
VER_AMI_DEMO = 14,
VER_AMI_INTERVIEW = 15,
VER_PC_COUNT = 13 /* PC versions */
};
struct PatchFile {
const char *filename;
char lang;
};
const struct CompressQueen::GameVersion gameVersions[] = {
{ "PEM10", 1, 0, 0x00000008, 22677657 },
{ "CEM10", 0, 0, 0x0000584E, 190787021 },
{ "PFM10", 1, 0, 0x0002CD93, 22157304 },
{ "CFM10", 0, 0, 0x00032585, 186689095 },
{ "PGM10", 1, 0, 0x00059ACA, 22240013 },
{ "CGM10", 0, 0, 0x0005F2A7, 217648975 },
{ "PIM10", 1, 0, 0x000866B1, 22461366 },
{ "CIM10", 0, 0, 0x0008BEE2, 190795582 },
{ "CSM10", 0, 0, 0x000B343C, 190730602 },
{ "CHM10", 0, 0, 0x000DA981, 190705558 },
{ "PE100", 1, 1, 0x00101EC6, 3724538 },
{ "PE100", 1, 1, 0x00102B7F, 3732177 },
{ "PEint", 1, 1, 0x00103838, 1915913 },
{ "aEM10", 1, 0, 0x00103F1E, 351775 },
{ "CE101", 1, 1, 0x00107D8D, 563335 },
{ "PE100", 1, 1, 0x001086D4, 597032 }
};
const struct PatchFile patchFiles[] = {
{ "CHIEF1.DOG", 'F' },
{ "CHIEF2.DOG", 'F' },
{ "BUD1.DOG", 'I' }
};
CompressQueen::CompressQueen(const std::string &name) : CompressionTool(name, TOOLTYPE_COMPRESSION) {
_outputToDirectory = false;
_supportsProgressBar = true;
ToolInput input;
input.format = "queen.1";
_inputPaths.push_back(input);
_helptext = "\nUsage: %s [mode] [mode params] [-o outputfile] <inputfile (queen.1)>\n" kCompressionAudioHelp;
}
const CompressQueen::GameVersion *CompressQueen::detectGameVersion(uint32 size) {
const struct GameVersion *pgv = gameVersions;
int i;
/* Compressing/rebuiling an Amiga version is not supported */
for (i = 0; i < VER_PC_COUNT; ++i, ++pgv) {
if (pgv->dataFileSize == size) {
return pgv;
}
}
error("Unknown/unsupported FOTAQ version");
return NULL;
}
void CompressQueen::fromFileToFile(File &in, File &out, uint32 amount) {
char fBuf[2048];
uint32 numRead;
while (amount > 0) {
numRead = in.readN(fBuf, 1, amount > 2048 ? 2048 : amount);
if (numRead <= 0) {
break;
}
amount -= numRead;
out.write(fBuf, 1, numRead);
}
}
void CompressQueen::createFinalFile(Filename *outPath) {
int i;
uint32 dataStartOffset;
uint32 dataSize;
File inTbl(TEMP_TBL, "rb");
File inData(TEMP_DAT, "rb");
File outFinal(*outPath, "wb");
dataStartOffset = inTbl.size() + EXTRA_TBL_HEADER;
dataSize = inData.size();
inTbl.seek(7, SEEK_SET); /* Skip past header */
/* Write new header */
outFinal.writeUint32BE(QTBL);
outFinal.write(_version->versionString, 6, 1);
outFinal.writeByte(_version->isFloppy);
outFinal.writeByte(_version->isDemo);
outFinal.writeByte(_versionExtra.compression);
outFinal.writeUint16BE(_versionExtra.entries);
for (i = 0; i < _versionExtra.entries; i++) {
fromFileToFile(inTbl, outFinal, 12);
outFinal.writeByte(inTbl.readByte());
outFinal.writeUint32BE(dataStartOffset + inTbl.readUint32BE());
outFinal.writeUint32BE(inTbl.readUint32BE());
}
/* Append contents of temporary datafile to final datafile */
fromFileToFile(inData, outFinal, dataSize);
/* Cleanup */
unlink(TEMP_TBL);
unlink(TEMP_DAT);
}
void CompressQueen::execute() {
File inputData, inputTbl, outputTbl, outputData, compFile;
char tmp[5];
int size, i = 1;
uint32 prevOffset;
Filename inpath(_inputPaths[0].path);
Filename &outpath = _outputPath;
if (outpath.empty()) {
outpath = inpath;
outpath.setFullName(FINAL_OUT);
}
/* Open input file (QUEEN.1) */
inputData.open(inpath, "rb");
/* Open TBL file (QUEEN.TBL) */
inpath.setFullName(INPUT_TBL);
inputTbl.open(inpath, "rb");
size = inputData.size();
inputTbl.read(tmp, 1, 4);
tmp[4] = '\0';
if (memcmp(tmp, "QTBL", 4)) {
error("Invalid TBL file");
}
if (inputTbl.readUint32BE() != CURRENT_TBL_VERSION) {
error("You are using an incorrect (outdated?) version of the queen.tbl file");
}
_version = detectGameVersion(size);
inputTbl.seek(_version->tableOffset, SEEK_SET);
_versionExtra.compression = compression_format(_format);
_versionExtra.entries = inputTbl.readUint16BE();
outputTbl.open(TEMP_TBL, "wb");
outputData.open(TEMP_DAT, "wb");
/* Write tablefile header */
outputTbl.writeUint32BE(QTBL);
outputTbl.writeByte(_versionExtra.compression);
outputTbl.writeUint16BE(_versionExtra.entries);
for (i = 0; i < _versionExtra.entries; i++) {
/* Update progress */
updateProgress(i, _versionExtra.entries);
prevOffset = outputData.pos();
/* Read entry */
inputTbl.read(_entry.filename, 1, 12);
_entry.filename[12] = '\0';
_entry.bundle = inputTbl.readByte();
_entry.offset = inputTbl.readUint32BE();
_entry.size = inputTbl.readUint32BE();
print("Processing entry: %s\n", _entry.filename);
inputData.seek(_entry.offset, SEEK_SET);
if (_versionExtra.compression && strstr(_entry.filename, ".SB")) { /* Do we want to compress? */
uint16 sbVersion;
int headerSize;
/* Read in .SB */
File tmpFile(TEMP_SB, "wb");
inputData.seek(_entry.offset, SEEK_SET);
inputData.seek(2, SEEK_CUR);
sbVersion = inputData.readUint16LE();
switch (sbVersion) {
case 104:
headerSize = SB_HEADER_SIZE_V104;
break;
case 110:
headerSize = SB_HEADER_SIZE_V110;
break;
default:
warning("Unhandled SB file version %d, defaulting to 104\n", sbVersion);
headerSize = SB_HEADER_SIZE_V104;
break;
}
inputData.seek(headerSize - 4, SEEK_CUR);
_entry.size -= headerSize;
fromFileToFile(inputData, tmpFile, _entry.size);
tmpFile.close();
/* Invoke encoder */
setRawAudioType(false, false, 8);
encodeAudio(TEMP_SB, true, 11840, tempEncoded, _format);
/* Append MP3/OGG to data file */
compFile.open(tempEncoded, "rb");
_entry.size = compFile.size();
fromFileToFile(compFile, outputData, _entry.size);
compFile.close();
/* Delete temporary files */
unlink(TEMP_SB);
unlink(tempEncoded);
} else {
/* Non .SB file */
bool patched = false;
/* Check for external files */
uint8 j;
for (j = 0; j < ARRAYSIZE(patchFiles); ++j) {
const struct PatchFile *pf = &patchFiles[j];
if (_version->versionString[1] == pf->lang && strcmp(pf->filename, _entry.filename) == 0) {
/* XXX patched data files are supposed to be in cwd */
File fpPatch(pf->filename, "rb");
if (fpPatch.isOpen()) {
_entry.size = fpPatch.size();
print("Patching entry, new size = %d bytes\n", _entry.size);
fromFileToFile(fpPatch, outputData, _entry.size);
fpPatch.close();
patched = true;
}
break;
}
}
if (!patched) {
fromFileToFile(inputData, outputData, _entry.size);
}
}
/* Write entry to table */
outputTbl.write(_entry.filename, 12, 1);
outputTbl.writeByte(_entry.bundle);
outputTbl.writeUint32BE(prevOffset);
outputTbl.writeUint32BE(_entry.size);
}
outputTbl.close();
outputData.close();
/* Merge the temporary table and temporary datafile to create final file */
createFinalFile(&outpath);
}
#ifdef STANDALONE_MAIN
int main(int argc, char *argv[]) {
CompressQueen queen(argv[0]);
return queen.run(argc, argv);
}
#endif