0x47 stub
[scummvm-innocent.git] / engines / agi / wagparser.cpp
blobd243439dff3d462b7fcca20721977fb21934a21b
1 /* ScummVM - Graphic Adventure Engine
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 * $URL$
22 * $Id$
26 #include "common/file.h"
27 #include "common/util.h"
28 #include "common/fs.h"
29 #include "common/debug.h"
31 #include "agi/wagparser.h"
33 namespace Agi {
35 WagProperty::WagProperty() {
36 setDefaults();
39 WagProperty::~WagProperty() {
40 deleteData();
43 WagProperty::WagProperty(const WagProperty &other) {
44 deepCopy(other);
47 WagProperty &WagProperty::operator=(const WagProperty &other) {
48 if (&other != this) deepCopy(other); // Don't do self-assignment
49 return *this;
52 void WagProperty::deepCopy(const WagProperty &other) {
53 _readOk = other._readOk;
54 _propCode = other._propCode;
55 _propType = other._propType;
56 _propNum = other._propNum;
57 _propSize = other._propSize;
59 deleteData(); // Delete old data (If any) and set _propData to NULL
60 if (other._propData != NULL) {
61 _propData = new char[other._propSize + 1UL]; // Allocate space for property's data plus trailing zero
62 memcpy(_propData, other._propData, other._propSize + 1UL); // Copy the whole thing
66 bool WagProperty::read(Common::SeekableReadStream &stream) {
67 // First read the property's header
68 _propCode = (enum WagPropertyCode) stream.readByte();
69 _propType = (enum WagPropertyType) stream.readByte();
70 _propNum = stream.readByte();
71 _propSize = stream.readUint16LE();
73 if (stream.eos() || stream.err()) { // Check that we got the whole header
74 _readOk = false;
75 return _readOk;
78 // Then read the property's data
79 deleteData(); // Delete old data (If any)
80 _propData = new char[_propSize + 1UL]; // Allocate space for property's data plus trailing zero
81 uint32 readBytes = stream.read(_propData, _propSize); // Read the data in
82 _propData[_propSize] = 0; // Set the trailing zero for easy C-style string access
84 _readOk = (_propData != NULL && readBytes == _propSize); // Check that we got the whole data
85 return _readOk;
88 void WagProperty::clear() {
89 deleteData();
90 setDefaults();
93 void WagProperty::setDefaults() {
94 _readOk = false;
95 _propCode = PC_UNDEFINED;
96 _propType = PT_UNDEFINED;
97 _propNum = 0;
98 _propSize = 0;
99 _propData = NULL;
102 void WagProperty::deleteData() {
103 if (_propData != NULL) {
104 delete _propData;
105 _propData = NULL;
109 WagFileParser::WagFileParser() :
110 _parsedOk(false) {
113 WagFileParser::~WagFileParser() {
116 bool WagFileParser::checkAgiVersionProperty(const WagProperty &version) const {
117 if (version.getCode() == WagProperty::PC_INTVERSION && // Must be AGI interpreter version property
118 version.getSize() >= 3 && // Need at least three characters for a version number like "X.Y"
119 isdigit(version.getData()[0]) && // And the first character must be a digit
120 (version.getData()[1] == ',' || version.getData()[1] == '.')) { // And the second a comma or a period
122 for (int i = 2; i < version.getSize(); i++) // And the rest must all be digits
123 if (!isdigit(version.getData()[i]))
124 return false; // Bail out if found a non-digit after the decimal point
126 return true;
127 } else // Didn't pass the preliminary test so fails
128 return false;
131 uint16 WagFileParser::convertToAgiVersionNumber(const WagProperty &version) {
132 // Examples of the conversion: "2.44" -> 0x2440, "2.917" -> 0x2917, "3.002086" -> 0x3086.
133 if (checkAgiVersionProperty(version)) { // Check that the string is a valid AGI interpreter version string
134 // Convert first ascii digit to an integer and put it in the fourth nibble (Bits 12...15) of the version number
135 // and at the same time set all other nibbles to zero.
136 uint16 agiVerNum = ((uint16) (version.getData()[0] - '0')) << (3 * 4);
138 // Convert at most three least significant digits of the version number's minor part
139 // (i.e. the part after the decimal point) and put them in order to the third, second
140 // and the first nibble of the version number. Just to clarify version.getSize() - 2
141 // is the number of digits after the decimal point.
142 int32 digitCount = MIN<int32>(3, ((int32) version.getSize()) - 2); // How many digits left to convert
143 for (int i = 0; i < digitCount; i++)
144 agiVerNum |= ((uint16) (version.getData()[version.getSize() - digitCount + i] - '0')) << ((2 - i) * 4);
146 debug(3, "WagFileParser: Converted AGI version from string %s to number 0x%x", version.getData(), agiVerNum);
147 return agiVerNum;
148 } else // Not a valid AGI interpreter version string
149 return 0; // Can't convert, so failure
152 bool WagFileParser::checkWagVersion(Common::SeekableReadStream &stream) {
153 if (stream.size() >= WINAGI_VERSION_LENGTH) { // Stream has space to contain the WinAGI version string
154 // Read the last WINAGI_VERSION_LENGTH bytes of the stream and make a string out of it
155 char str[WINAGI_VERSION_LENGTH+1]; // Allocate space for the trailing zero also
156 uint32 oldStreamPos = stream.pos(); // Save the old stream position
157 stream.seek(stream.size() - WINAGI_VERSION_LENGTH);
158 uint32 readBytes = stream.read(str, WINAGI_VERSION_LENGTH);
159 stream.seek(oldStreamPos); // Seek back to the old stream position
160 str[readBytes] = 0; // Set the trailing zero to finish the C-style string
161 if (readBytes != WINAGI_VERSION_LENGTH) { // Check that we got the whole version string
162 debug(3, "WagFileParser::checkWagVersion: Error reading WAG file version from stream");
163 return false;
165 debug(3, "WagFileParser::checkWagVersion: Read WinAGI version string (\"%s\")", str);
167 // Check that the WinAGI version string is one of the two version strings
168 // WinAGI 1.1.21 recognizes as acceptable in the end of a *.wag file.
169 // Note that they are all of length 16 and are padded with spaces to be that long.
170 return scumm_stricmp(str, "WINAGI v1.0 ") == 0 ||
171 scumm_stricmp(str, "1.0 BETA ") == 0;
172 } else { // Stream is too small to contain the WinAGI version string
173 debug(3, "WagFileParser::checkWagVersion: Stream is too small to contain a valid WAG file");
174 return false;
178 bool WagFileParser::parse(const Common::FSNode &node) {
179 WagProperty property; // Temporary property used for reading
180 Common::SeekableReadStream *stream = NULL; // The file stream
182 _parsedOk = false; // We haven't parsed the file yet
184 stream = node.createReadStream(); // Open the file
185 if (stream) { // Check that opening the file was succesful
186 if (checkWagVersion(*stream)) { // Check that WinAGI version string is valid
187 // It seems we've got a valid *.wag file so let's parse its properties from the start.
188 stream->seek(0); // Rewind the stream
189 if (!_propList.empty()) _propList.clear(); // Clear out old properties (If any)
191 do { // Parse the properties
192 if (property.read(*stream)) { // Read the property and check it was read ok
193 _propList.push_back(property); // Add read property to properties list
194 debug(4, "WagFileParser::parse: Read property with code %d, type %d, number %d, size %d, data \"%s\"",
195 property.getCode(), property.getType(), property.getNumber(), property.getSize(), property.getData());
196 } else // Reading failed, let's bail out
197 break;
198 } while (!endOfProperties(*stream)); // Loop until the end of properties
200 // File was parsed successfully only if we got to the end of properties
201 // and all the properties were read successfully (Also the last).
202 _parsedOk = endOfProperties(*stream) && property.readOk();
204 if (!_parsedOk) // Error parsing stream
205 warning("Error parsing WAG file (%s). WAG file ignored", node.getPath().c_str());
206 } else // Invalid WinAGI version string or it couldn't be read
207 warning("Invalid WAG file (%s) version or error reading it. WAG file ignored", node.getPath().c_str());
208 } else // Couldn't open file
209 warning("Couldn't open WAG file (%s). WAG file ignored", node.getPath().c_str());
211 delete stream;
212 return _parsedOk;
215 const WagProperty *WagFileParser::getProperty(const WagProperty::WagPropertyCode code) const {
216 for (PropertyList::const_iterator iter = _propList.begin(); iter != _propList.end(); iter++)
217 if (iter->getCode() == code) return iter;
218 return NULL;
221 bool WagFileParser::endOfProperties(const Common::SeekableReadStream &stream) const {
222 return stream.pos() >= (stream.size() - WINAGI_VERSION_LENGTH);
225 } // End of namespace Agi