mirror of
https://github.com/scummvm/scummvm-tools.git
synced 2026-05-21 05:40:44 +00:00
168e91e076
*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
329 lines
8.4 KiB
C++
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
|
|
|