mirror of
https://github.com/libretro/scummvm.git
synced 2025-02-01 16:35:20 +00:00
Patch #1735773: "WAG file parsing patch to AGI's fallback detector".
Slightly modified, split into separate files. svn-id: r27401
This commit is contained in:
parent
24daa86bdd
commit
4d4cd489f5
@ -31,6 +31,7 @@
|
||||
#include "common/file.h"
|
||||
|
||||
#include "agi/agi.h"
|
||||
#include "agi/wagparser.h"
|
||||
|
||||
|
||||
namespace Agi {
|
||||
@ -1848,6 +1849,10 @@ Common::EncapsulatedADGameDesc fallbackDetector(const FSList *fslist) {
|
||||
typedef Common::HashMap<Common::String, int32, Common::CaseSensitiveString_Hash, Common::CaseSensitiveString_EqualTo> IntMap;
|
||||
IntMap allFiles;
|
||||
bool matchedUsingFilenames = false;
|
||||
bool matchedUsingWag = false;
|
||||
int wagFileCount = 0;
|
||||
WagFileParser wagFileParser;
|
||||
Common::String wagFilePath;
|
||||
Common::String gameid("agi-fanmade"), description, extra; // Set the defaults for gameid, description and extra
|
||||
FSList fslistCurrentDir; // Only used if fslist == NULL
|
||||
|
||||
@ -1868,12 +1873,18 @@ Common::EncapsulatedADGameDesc fallbackDetector(const FSList *fslist) {
|
||||
g_fallbackDesc.features = GF_FANMADE;
|
||||
g_fallbackDesc.version = 0x2917;
|
||||
|
||||
// First grab all filenames
|
||||
// First grab all filenames and at the same time count the number of *.wag files
|
||||
for (FSList::const_iterator file = fslist->begin(); file != fslist->end(); ++file) {
|
||||
if (file->isDirectory()) continue;
|
||||
Common::String filename = file->name();
|
||||
filename.toLowercase();
|
||||
allFiles[filename] = true; // Save the filename in a hash table
|
||||
|
||||
if (filename.hasSuffix(".wag")) {
|
||||
// Save latest found *.wag file's path (Can be used to open the file, the name can't)
|
||||
wagFilePath = file->path();
|
||||
wagFileCount++; // Count found *.wag files
|
||||
}
|
||||
}
|
||||
|
||||
if (allFiles.contains("logdir") && allFiles.contains("object") &&
|
||||
@ -1909,7 +1920,55 @@ Common::EncapsulatedADGameDesc fallbackDetector(const FSList *fslist) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// WinAGI produces *.wag file with interpreter version, game name and other parameters.
|
||||
// If there's exactly one *.wag file and it parses successfully then we'll use its information.
|
||||
if (wagFileCount == 1 && wagFileParser.parse(wagFilePath.c_str())) {
|
||||
matchedUsingWag = true;
|
||||
|
||||
const WagProperty *wagAgiVer = wagFileParser.getProperty(WagProperty::PC_INTVERSION);
|
||||
const WagProperty *wagGameID = wagFileParser.getProperty(WagProperty::PC_GAMEID);
|
||||
const WagProperty *wagGameDesc = wagFileParser.getProperty(WagProperty::PC_GAMEDESC);
|
||||
const WagProperty *wagGameVer = wagFileParser.getProperty(WagProperty::PC_GAMEVERSION);
|
||||
const WagProperty *wagGameLastEdit = wagFileParser.getProperty(WagProperty::PC_GAMELAST);
|
||||
|
||||
// If there is an AGI version number in the *.wag file then let's use it
|
||||
if (wagAgiVer != NULL && wagFileParser.checkAgiVersionProperty(*wagAgiVer)) {
|
||||
// TODO/FIXME: Check that version number is something we support before trying to use it.
|
||||
// If the version number is unsupported then it'll get switched to 0x2917 later.
|
||||
// But there's the possibility that file based detection has detected something else
|
||||
// than a v2 AGI game. So there's a possibility for conflicting information.
|
||||
g_fallbackDesc.version = wagFileParser.convertToAgiVersionNumber(*wagAgiVer);
|
||||
}
|
||||
|
||||
// Set gameid according to *.wag file information if it's present and it doesn't contain whitespace.
|
||||
if (wagGameID != NULL && !Common::String(wagGameID->getData()).contains(" ")) {
|
||||
gameid = wagGameID->getData();
|
||||
debug(3, "Agi::fallbackDetector: Using game id (%s) from WAG file", gameid.c_str());
|
||||
}
|
||||
|
||||
// Set game description and extra according to *.wag file information if they're present
|
||||
if (wagGameDesc != NULL) {
|
||||
description = wagGameDesc->getData();
|
||||
debug(3, "Agi::fallbackDetector: Game description (%s) from WAG file", wagGameDesc->getData());
|
||||
|
||||
// If there's game version in the *.wag file, set extra to it
|
||||
if (wagGameVer != NULL) {
|
||||
extra = wagGameVer->getData();
|
||||
debug(3, "Agi::fallbackDetector: Game version (%s) from WAG file", wagGameVer->getData());
|
||||
}
|
||||
|
||||
// If there's game last edit date in the *.wag file, add it to extra
|
||||
if (wagGameLastEdit != NULL) {
|
||||
if (!extra.empty() ) extra += " ";
|
||||
extra += wagGameLastEdit->getData();
|
||||
debug(3, "Agi::fallbackDetector: Game's last edit date (%s) from WAG file", wagGameLastEdit->getData());
|
||||
}
|
||||
}
|
||||
} else if (wagFileCount > 1) { // More than one *.wag file, confusing! So let's not use them.
|
||||
warning("More than one (%d) *.wag files found. WAG files ignored", wagFileCount);
|
||||
}
|
||||
|
||||
// Check that the AGI interpreter version is a supported one
|
||||
if (!(g_fallbackDesc.version >= 0x2000 && g_fallbackDesc.version < 0x4000)) {
|
||||
warning("Unsupported AGI interpreter version 0x%x in AGI's fallback detection. Using default 0x2917", g_fallbackDesc.version);
|
||||
@ -1924,7 +1983,7 @@ Common::EncapsulatedADGameDesc fallbackDetector(const FSList *fslist) {
|
||||
|
||||
// Check if we found a match with any of the fallback methods
|
||||
Common::EncapsulatedADGameDesc result;
|
||||
if (matchedUsingFilenames) {
|
||||
if (matchedUsingWag || matchedUsingFilenames) {
|
||||
extra = description + " " + extra; // Let's combine the description and extra
|
||||
result = Common::EncapsulatedADGameDesc((const Common::ADGameDescription *)&g_fallbackDesc, gameid, extra);
|
||||
|
||||
|
@ -28,6 +28,7 @@ MODULE_OBJS = \
|
||||
sprite.o \
|
||||
text.o \
|
||||
view.o \
|
||||
wagparser.o \
|
||||
words.o
|
||||
|
||||
|
||||
|
229
engines/agi/wagparser.cpp
Normal file
229
engines/agi/wagparser.cpp
Normal file
@ -0,0 +1,229 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 "common/stdafx.h"
|
||||
|
||||
#include "common/file.h"
|
||||
#include "common/util.h"
|
||||
|
||||
#include "agi/wagparser.h"
|
||||
|
||||
namespace Agi {
|
||||
|
||||
WagProperty::WagProperty() {
|
||||
setDefaults();
|
||||
}
|
||||
|
||||
WagProperty::~WagProperty() {
|
||||
deleteData();
|
||||
}
|
||||
|
||||
WagProperty::WagProperty(const WagProperty &other) {
|
||||
deepCopy(other);
|
||||
}
|
||||
|
||||
WagProperty &WagProperty::operator=(const WagProperty &other) {
|
||||
if (&other != this) deepCopy(other); // Don't do self-assignment
|
||||
return *this;
|
||||
}
|
||||
|
||||
void WagProperty::deepCopy(const WagProperty &other) {
|
||||
_readOk = other._readOk;
|
||||
_propCode = other._propCode;
|
||||
_propType = other._propType;
|
||||
_propNum = other._propNum;
|
||||
_propSize = other._propSize;
|
||||
|
||||
deleteData(); // Delete old data (If any) and set _propData to NULL
|
||||
if (other._propData != NULL) {
|
||||
_propData = new char[other._propSize + 1UL]; // Allocate space for property's data plus trailing zero
|
||||
memcpy(_propData, other._propData, other._propSize + 1UL); // Copy the whole thing
|
||||
}
|
||||
}
|
||||
|
||||
bool WagProperty::read(Common::SeekableReadStream &stream) {
|
||||
// First read the property's header
|
||||
_propCode = (enum WagPropertyCode) stream.readByte();
|
||||
_propType = (enum WagPropertyType) stream.readByte();
|
||||
_propNum = stream.readByte();
|
||||
_propSize = stream.readUint16LE();
|
||||
|
||||
if (stream.ioFailed()) { // Check that we got the whole header
|
||||
_readOk = false;
|
||||
return _readOk;
|
||||
}
|
||||
|
||||
// Then read the property's data
|
||||
deleteData(); // Delete old data (If any)
|
||||
_propData = new char[_propSize + 1UL]; // Allocate space for property's data plus trailing zero
|
||||
uint32 readBytes = stream.read(_propData, _propSize); // Read the data in
|
||||
_propData[_propSize] = 0; // Set the trailing zero for easy C-style string access
|
||||
|
||||
_readOk = (_propData != NULL && readBytes == _propSize); // Check that we got the whole data
|
||||
return _readOk;
|
||||
}
|
||||
|
||||
void WagProperty::clear() {
|
||||
deleteData();
|
||||
setDefaults();
|
||||
}
|
||||
|
||||
void WagProperty::setDefaults() {
|
||||
_readOk = false;
|
||||
_propCode = PC_UNDEFINED;
|
||||
_propType = PT_UNDEFINED;
|
||||
_propNum = 0;
|
||||
_propSize = 0;
|
||||
_propData = NULL;
|
||||
}
|
||||
|
||||
void WagProperty::deleteData() {
|
||||
if (_propData != NULL) {
|
||||
delete _propData;
|
||||
_propData = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
WagFileParser::WagFileParser() :
|
||||
_parsedOk(false) {
|
||||
}
|
||||
|
||||
WagFileParser::~WagFileParser() {
|
||||
}
|
||||
|
||||
bool WagFileParser::checkAgiVersionProperty(const WagProperty &version) const {
|
||||
if (version.getCode() == WagProperty::PC_INTVERSION && // Must be AGI interpreter version property
|
||||
version.getSize() >= 3 && // Need at least three characters for a version number like "X.Y"
|
||||
isdigit(version.getData()[0]) && // And the first character must be a digit
|
||||
(version.getData()[1] == ',' || version.getData()[1] == '.')) { // And the second a comma or a period
|
||||
|
||||
for (int i = 2; i < version.getSize(); i++) // And the rest must all be digits
|
||||
if (!isdigit(version.getData()[i]))
|
||||
return false; // Bail out if found a non-digit after the decimal point
|
||||
|
||||
return true;
|
||||
} else // Didn't pass the preliminary test so fails
|
||||
return false;
|
||||
}
|
||||
|
||||
uint16 WagFileParser::convertToAgiVersionNumber(const WagProperty &version) {
|
||||
// Examples of the conversion: "2.44" -> 0x2440, "2.917" -> 0x2917, "3.002086" -> 0x3086.
|
||||
if (checkAgiVersionProperty(version)) { // Check that the string is a valid AGI interpreter version string
|
||||
// Convert first ascii digit to an integer and put it in the fourth nibble (Bits 12...15) of the version number
|
||||
// and at the same time set all other nibbles to zero.
|
||||
uint16 agiVerNum = ((uint16) (version.getData()[0] - '0')) << (3 * 4);
|
||||
|
||||
// Convert at most three least significant digits of the version number's minor part
|
||||
// (i.e. the part after the decimal point) and put them in order to the third, second
|
||||
// and the first nibble of the version number. Just to clarify version.getSize() - 2
|
||||
// is the number of digits after the decimal point.
|
||||
int32 digitCount = MIN<int32>(3, ((int32) version.getSize()) - 2); // How many digits left to convert
|
||||
for (int i = 0; i < digitCount; i++)
|
||||
agiVerNum |= ((uint16) (version.getData()[version.getSize() - digitCount + i] - '0')) << ((2 - i) * 4);
|
||||
|
||||
debug(3, "WagFileParser: Converted AGI version from string %s to number 0x%x", version.getData(), agiVerNum);
|
||||
return agiVerNum;
|
||||
} else // Not a valid AGI interpreter version string
|
||||
return 0; // Can't convert, so failure
|
||||
}
|
||||
|
||||
bool WagFileParser::checkWagVersion(Common::SeekableReadStream &stream) {
|
||||
if (stream.size() >= WINAGI_VERSION_LENGTH) { // Stream has space to contain the WinAGI version string
|
||||
// Read the last WINAGI_VERSION_LENGTH bytes of the stream and make a string out of it
|
||||
char str[WINAGI_VERSION_LENGTH+1]; // Allocate space for the trailing zero also
|
||||
uint32 oldStreamPos = stream.pos(); // Save the old stream position
|
||||
stream.seek(stream.size() - WINAGI_VERSION_LENGTH);
|
||||
uint32 readBytes = stream.read(str, WINAGI_VERSION_LENGTH);
|
||||
stream.seek(oldStreamPos); // Seek back to the old stream position
|
||||
str[readBytes] = 0; // Set the trailing zero to finish the C-style string
|
||||
if (readBytes != WINAGI_VERSION_LENGTH) { // Check that we got the whole version string
|
||||
debug(3, "WagFileParser::checkWagVersion: Error reading WAG file version from stream");
|
||||
return false;
|
||||
}
|
||||
debug(3, "WagFileParser::checkWagVersion: Read WinAGI version string (\"%s\")", str);
|
||||
|
||||
// Check that the WinAGI version string is one of the two version strings
|
||||
// WinAGI 1.1.21 recognizes as acceptable in the end of a *.wag file.
|
||||
// Note that they are all of length 16 and are padded with spaces to be that long.
|
||||
return scumm_stricmp(str, "WINAGI v1.0 ") == 0 ||
|
||||
scumm_stricmp(str, "1.0 BETA ") == 0;
|
||||
} else { // Stream is too small to contain the WinAGI version string
|
||||
debug(3, "WagFileParser::checkWagVersion: Stream is too small to contain a valid WAG file");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool WagFileParser::parse(const char *filename) {
|
||||
Common::File file;
|
||||
WagProperty property; // Temporary property used for reading
|
||||
Common::MemoryReadStream *stream = NULL; // The file is to be read fully into memory and handled using this
|
||||
|
||||
_parsedOk = false; // We haven't parsed the file yet
|
||||
|
||||
if (file.open(filename)) { // Open the file
|
||||
stream = file.readStream(file.size()); // Read the file into memory
|
||||
if (stream != NULL && stream->size() == file.size()) { // Check that the whole file was read into memory
|
||||
if (checkWagVersion(*stream)) { // Check that WinAGI version string is valid
|
||||
// It seems we've got a valid *.wag file so let's parse its properties from the start.
|
||||
stream->seek(0); // Rewind the stream
|
||||
if (!_propList.empty()) _propList.clear(); // Clear out old properties (If any)
|
||||
|
||||
do { // Parse the properties
|
||||
if (property.read(*stream)) { // Read the property and check it was read ok
|
||||
_propList.push_back(property); // Add read property to properties list
|
||||
debug(4, "WagFileParser::parse: Read property with code %d, type %d, number %d, size %d, data \"%s\"",
|
||||
property.getCode(), property.getType(), property.getNumber(), property.getSize(), property.getData());
|
||||
} else // Reading failed, let's bail out
|
||||
break;
|
||||
} while (!endOfProperties(*stream)); // Loop until the end of properties
|
||||
|
||||
// File was parsed successfully only if we got to the end of properties
|
||||
// and all the properties were read successfully (Also the last).
|
||||
_parsedOk = endOfProperties(*stream) && property.readOk();
|
||||
|
||||
if (!_parsedOk) // Error parsing stream
|
||||
warning("Error parsing WAG file (%s). WAG file ignored", filename);
|
||||
} else // Invalid WinAGI version string or it couldn't be read
|
||||
warning("Invalid WAG file (%s) version or error reading it. WAG file ignored", filename);
|
||||
} else // Couldn't fully read file into memory
|
||||
warning("Error reading WAG file (%s) into memory. WAG file ignored", filename);
|
||||
} else // Couldn't open file
|
||||
warning("Couldn't open WAG file (%s). WAG file ignored", filename);
|
||||
|
||||
if (stream != NULL) delete stream; // If file was read into memory, deallocate that buffer
|
||||
return _parsedOk;
|
||||
}
|
||||
|
||||
const WagProperty *WagFileParser::getProperty(const WagProperty::WagPropertyCode code) const {
|
||||
for (PropertyList::const_iterator iter = _propList.begin(); iter != _propList.end(); iter++)
|
||||
if (iter->getCode() == code) return iter;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool WagFileParser::endOfProperties(const Common::SeekableReadStream &stream) const {
|
||||
return stream.pos() >= (stream.size() - WINAGI_VERSION_LENGTH);
|
||||
}
|
||||
|
||||
} // End of namespace Agi
|
288
engines/agi/wagparser.h
Normal file
288
engines/agi/wagparser.h
Normal file
@ -0,0 +1,288 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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$
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Agi {
|
||||
|
||||
/**
|
||||
* WagProperty represents a single property from WinAGI's *.wag file.
|
||||
* A property consists of a header and of data.
|
||||
* The header consists of the following:
|
||||
* - Property code (Integer/Enumeration, 1 byte)
|
||||
* - Property type (Integer/Enumeration, 1 byte)
|
||||
* - Property number (Integer, 1 byte)
|
||||
* - Property size (Little endian integer, 2 bytes)
|
||||
* And then there's the data with as many bytes as defined in the header's property size variable.
|
||||
*/
|
||||
class WagProperty {
|
||||
// Constants, enumerations etc
|
||||
public:
|
||||
/**
|
||||
* Property codes taken from WinAGI 1.1.21's source code file WinAGI/AGIObjects.bas.
|
||||
*/
|
||||
enum WagPropertyCode {
|
||||
PC_GAMEDESC = 129, ///< Game description (WinAGI 1.1.21 limits these to 4096 bytes)
|
||||
PC_GAMEAUTHOR, ///< Game author (WinAGI 1.1.21 limits these to 256 bytes)
|
||||
PC_GAMEID, ///< Game ID
|
||||
PC_INTVERSION, ///< Interpreter version (WinAGI 1.1.21 defaults to version 2.917)
|
||||
PC_GAMELAST, ///< Last edit date
|
||||
PC_GAMEVERSION, ///< Game version (WinAGI 1.1.21 limits these to 256 bytes)
|
||||
PC_GAMEABOUT, ///< About game (WinAGI 1.1.21 limits these to 4096 bytes)
|
||||
PC_GAMEEXEC, ///< Game executable
|
||||
PC_RESDIR, ///< Resource directory name
|
||||
PC_DEFSYNTAX, ///< Default syntax
|
||||
PC_INVOBJDESC = 144,
|
||||
PC_VOCABWORDDESC = 160,
|
||||
PC_PALETTE = 172,
|
||||
PC_USERESNAMES = 180,
|
||||
PC_LOGIC = 192,
|
||||
PC_PICTURE = 208,
|
||||
PC_SOUND = 224,
|
||||
PC_VIEW = 240,
|
||||
PC_UNDEFINED = 0x100 ///< An undefined property code (Added for ScummVM).
|
||||
};
|
||||
|
||||
/**
|
||||
* Property types taken from WinAGI 1.1.21's source code file WinAGI/AGIObjects.bas.
|
||||
* At the moment these aren't really at all needed by ScummVM. Just here if anyone decides to use them.
|
||||
*/
|
||||
enum WagPropertyType {
|
||||
PT_ID,
|
||||
PT_DESC,
|
||||
PT_SYNTAX,
|
||||
PT_CRC32,
|
||||
PT_KEY,
|
||||
PT_INST0,
|
||||
PT_INST1,
|
||||
PT_INST2,
|
||||
PT_MUTE0,
|
||||
PT_MUTE1,
|
||||
PT_MUTE2,
|
||||
PT_MUTE3,
|
||||
PT_TPQN,
|
||||
PT_ROOM,
|
||||
PT_VIS0,
|
||||
PT_VIS1,
|
||||
PT_VIS2,
|
||||
PT_VIS3,
|
||||
PT_ALL = 0xff,
|
||||
PT_UNDEFINED = 0x100 ///< An undefined property type (Added for ScummVM).
|
||||
};
|
||||
|
||||
// Constructors, destructors, operators etc
|
||||
public:
|
||||
/**
|
||||
* Creates an empty WagProperty object.
|
||||
* No property header or property data in it.
|
||||
*/
|
||||
WagProperty();
|
||||
|
||||
/**
|
||||
* Destructor. Releases allocated memory if any etc. The usual.
|
||||
*/
|
||||
~WagProperty();
|
||||
|
||||
/**
|
||||
* Copy constructor. Deep copies the variables.
|
||||
*/
|
||||
WagProperty(const WagProperty &other);
|
||||
|
||||
/**
|
||||
* Assignment operator. Deep copies the variables.
|
||||
*/
|
||||
WagProperty &operator=(const WagProperty &other);
|
||||
|
||||
// Non-public helper methods
|
||||
protected:
|
||||
/**
|
||||
* Sets the default values for member variables.
|
||||
*/
|
||||
void setDefaults();
|
||||
|
||||
/**
|
||||
* Delete's the property's data from memory if we have it, otherwise does nothing.
|
||||
*/
|
||||
void deleteData();
|
||||
|
||||
/**
|
||||
* Deep copies the parameter object to this object.
|
||||
* @param other The object to be deep copied to this object.
|
||||
*/
|
||||
void deepCopy(const WagProperty &other);
|
||||
|
||||
// Public methods that have side-effects
|
||||
public:
|
||||
/**
|
||||
* Read in a property (Header and data).
|
||||
* @return True if reading was a success, false otherwise.
|
||||
*/
|
||||
bool read(Common::SeekableReadStream &stream);
|
||||
|
||||
/**
|
||||
* Clears the property.
|
||||
* After this the property is empty. No header or data.
|
||||
*/
|
||||
void clear();
|
||||
|
||||
// Public access functions
|
||||
public:
|
||||
/**
|
||||
* Was the property read ok from the source stream?
|
||||
*/
|
||||
bool readOk() const { return _readOk; };
|
||||
|
||||
/**
|
||||
* Return the property's code.
|
||||
* @return The property's code if readOk(), PC_UNDEFINED otherwise.
|
||||
*/
|
||||
enum WagPropertyCode getCode() const { return _propCode; };
|
||||
|
||||
/**
|
||||
* Return the property's type.
|
||||
* @return The property's type if readOk(), PT_UNDEFINED otherwise.
|
||||
*/
|
||||
enum WagPropertyType getType() const { return _propType; };
|
||||
|
||||
/**
|
||||
* Return the property's number.
|
||||
* @return The property's number if readOk(), 0 otherwise.
|
||||
*/
|
||||
byte getNumber() const { return _propNum; };
|
||||
|
||||
/**
|
||||
* Return the property's data's length.
|
||||
* @return The property's data's length if readOk(), 0 otherwise.
|
||||
*/
|
||||
uint16 getSize() const { return _propSize; }
|
||||
|
||||
/**
|
||||
* Return property's data. Constant access version.
|
||||
* Can be used as a C-style string (i.e. this is guaranteed to have a trailing zero).
|
||||
* @return The property's data if readOk(), NULL otherwise.
|
||||
*/
|
||||
const char *getData() const { return _propData; };
|
||||
|
||||
// Member variables
|
||||
protected:
|
||||
bool _readOk; ///< Was the property read ok from the source stream?
|
||||
enum WagPropertyCode _propCode; ///< Property code (Part of the property's header)
|
||||
enum WagPropertyType _propType; ///< Property type (Part of the property's header)
|
||||
byte _propNum; ///< Property number (Part of the property's header)
|
||||
uint16 _propSize; ///< Property's size (Part of the property's header)
|
||||
char *_propData; ///< The property's data (Plus a trailing zero for C-style string access)
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Class for parsing *.wag files created by WinAGI.
|
||||
* Using this class you can get information about fanmade AGI games if they have provided a *.wag file with them.
|
||||
*/
|
||||
class WagFileParser {
|
||||
// Constants, type definitions, enumerations etc.
|
||||
public:
|
||||
static const int WINAGI_VERSION_LENGTH = 16; ///< WinAGI's version string's length (Always 16)
|
||||
typedef Common::Array<WagProperty> PropertyList; ///< A type definition for an array of *.wag file properties
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructor. Creates a WagFileParser object in a default state.
|
||||
*/
|
||||
WagFileParser();
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
~WagFileParser();
|
||||
|
||||
/**
|
||||
* Loads a *.wag file and parses it.
|
||||
* @note After this you can access the loaded properties using getProperty() and getProperties() etc.
|
||||
* @param filename Name of the file to be parsed.
|
||||
* @return True if parsed successfully, false otherwise.
|
||||
*/
|
||||
bool parse(const char *filename);
|
||||
|
||||
/**
|
||||
* Get list of the loaded properties.
|
||||
* @note Use only after a call to parse() first.
|
||||
* @return The list of loaded properties.
|
||||
*/
|
||||
const PropertyList &getProperties() const { return _propList; };
|
||||
|
||||
/**
|
||||
* Get property with the given property code.
|
||||
* @note Use only after a call to parse() first.
|
||||
* @return Pointer to the property if its found in memory, NULL otherwise.
|
||||
*
|
||||
* TODO/FIXME: Handle cases where several properties with the given property code are found.
|
||||
* At the moment we don't need this functionality because the properties we use
|
||||
* for fallback detection probably don't have multiples in the WAG-file.
|
||||
* TODO: Make this faster than linear time if desired/needed.
|
||||
*/
|
||||
const WagProperty *getProperty(const WagProperty::WagPropertyCode code) const;
|
||||
|
||||
/**
|
||||
* Tests if the given property contains a valid AGI interpreter version string.
|
||||
* A valid AGI interpreter version string is of the form "X.Y" or "X,Y" where
|
||||
* X is a single decimal digit and Y is a string of decimal digits (At least one digit).
|
||||
* @param version The property to be tested.
|
||||
* @return True if the given property contains a valid AGI interpreter version string, false otherwise.
|
||||
*/
|
||||
bool checkAgiVersionProperty(const WagProperty &version) const;
|
||||
|
||||
/**
|
||||
* Convert property's data to an AGI interpreter version number.
|
||||
* @param version The property to be converted (Property code should be PC_INTVERSION).
|
||||
* @return AGI interpreter version number if successful, 0 otherwise.
|
||||
*/
|
||||
uint16 convertToAgiVersionNumber(const WagProperty &version);
|
||||
|
||||
/**
|
||||
* Was the file parsed successfully?
|
||||
* @return True if file was parsed successfully, false otherwise.
|
||||
*/
|
||||
bool parsedOk() const { return _parsedOk; };
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Checks if stream has a valid WinAGI version string in its end.
|
||||
* @param stream The stream to be checked.
|
||||
* @return True if reading was successful and stream contains a valid WinAGI version string, false otherwise.
|
||||
*/
|
||||
bool checkWagVersion(Common::SeekableReadStream &stream);
|
||||
|
||||
/**
|
||||
* Checks if we're at or past the end of the properties stored in the stream.
|
||||
* @param stream The stream whose seeking position is to be checked.
|
||||
* @return True if stream's seeking position is at or past the end of the properties, false otherwise.
|
||||
*/
|
||||
bool endOfProperties(const Common::SeekableReadStream &stream) const;
|
||||
|
||||
// Member variables
|
||||
protected:
|
||||
PropertyList _propList; ///< List of loaded properties from the file.
|
||||
bool _parsedOk; ///< Did the parsing of the file go ok?
|
||||
};
|
||||
|
||||
} // End of namespace Agi
|
Loading…
x
Reference in New Issue
Block a user