aboutsummaryrefslogtreecommitdiff
path: root/common/winexe_ne.cpp
diff options
context:
space:
mode:
authorMatthew Hoops2011-02-22 20:01:19 -0500
committerMatthew Hoops2011-02-22 20:03:18 -0500
commita2a0b13de2cd6fb28787b2821466f3ef7c2210ea (patch)
tree07982f6f3efef63b3380699f7c0bcae48e6ee4ff /common/winexe_ne.cpp
parent3a3fd9f009e253b9943dcbc56324a0c935324d04 (diff)
downloadscummvm-rg350-a2a0b13de2cd6fb28787b2821466f3ef7c2210ea.tar.gz
scummvm-rg350-a2a0b13de2cd6fb28787b2821466f3ef7c2210ea.tar.bz2
scummvm-rg350-a2a0b13de2cd6fb28787b2821466f3ef7c2210ea.zip
COMMON: Begin to merge some NE/PE code
The ID classes are now common to both. The files have been renamed to better illustrate their purpose.
Diffstat (limited to 'common/winexe_ne.cpp')
-rw-r--r--common/winexe_ne.cpp559
1 files changed, 559 insertions, 0 deletions
diff --git a/common/winexe_ne.cpp b/common/winexe_ne.cpp
new file mode 100644
index 0000000000..8a2b07b4d0
--- /dev/null
+++ b/common/winexe_ne.cpp
@@ -0,0 +1,559 @@
+/* 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/debug.h"
+#include "common/file.h"
+#include "common/memstream.h"
+#include "common/str.h"
+#include "common/stream.h"
+#include "common/winexe_ne.h"
+
+namespace Common {
+
+NECursor::NECursor() {
+ _width = 0;
+ _height = 0;
+ _hotspotX = 0;
+ _hotspotY = 0;
+ _surface = 0;
+ memset(_palette, 0, 256 * 3);
+}
+
+NECursor::~NECursor() {
+ clear();
+}
+
+uint16 NECursor::getWidth() const {
+ return _width;
+}
+
+uint16 NECursor::getHeight() const {
+ return _height;
+}
+
+uint16 NECursor::getHotspotX() const {
+ return _hotspotX;
+}
+
+uint16 NECursor::getHotspotY() const {
+ return _hotspotY;
+}
+
+void NECursor::setDimensions(uint16 width, uint16 height) {
+ _width = width;
+ _height = height;
+}
+
+void NECursor::setHotspot(uint16 x, uint16 y) {
+ _hotspotX = x;
+ _hotspotY = y;
+}
+
+bool NECursor::readCursor(SeekableReadStream &stream, uint32 count) {
+ clear();
+
+ SeekableReadStream *bitmap = stream.readStream(count);
+ _surface = new byte[_width * _height];
+
+ uint32 width = _width;
+ uint32 height = _height * 2;
+
+ // Sanity checks
+ assert((width > 0) && (height > 0));
+
+ // Check header size
+ if (bitmap->readUint32LE() != 40)
+ return false;
+
+ // Check dimensions
+ if (bitmap->readUint32LE() != width)
+ return false;
+ if (bitmap->readUint32LE() != height)
+ return false;
+
+ // Color planes
+ if (bitmap->readUint16LE() != 1)
+ return false;
+ // Bits per pixel
+ if (bitmap->readUint16LE() != 1)
+ return false;
+ // Compression
+ if (bitmap->readUint32LE() != 0)
+ return false;
+
+ // Image size + X resolution + Y resolution
+ bitmap->skip(12);
+
+ uint32 numColors = bitmap->readUint32LE();
+
+ if (numColors == 0)
+ numColors = 2;
+ else if (numColors > 2)
+ return false;
+
+ // Assert that enough data is there for the whole cursor
+ if ((uint32)bitmap->size() < 40 + numColors * 4 + width * height / 8)
+ return false;
+
+ // Height includes AND-mask and XOR-mask
+ height /= 2;
+
+ // Standard palette: transparent, black, white
+ _palette[6] = 0xff;
+ _palette[7] = 0xff;
+ _palette[8] = 0xff;
+
+ // Reading the palette
+ bitmap->seek(40);
+ for (uint32 i = 0 ; i < numColors; i++) {
+ _palette[(i + 1) * 3 + 2] = bitmap->readByte();
+ _palette[(i + 1) * 3 + 1] = bitmap->readByte();
+ _palette[(i + 1) * 3 + 0] = bitmap->readByte();
+ bitmap->readByte();
+ }
+
+ // Reading the bitmap data
+ uint32 dataSize = bitmap->size() - 40 - numColors * 4;
+ byte *initialSource = new byte[dataSize];
+ bitmap->read(initialSource, dataSize);
+ const byte *srcP = initialSource;
+ const byte *srcM = srcP + ((width * height) / 8);
+ byte *dest = _surface + width * (height - 1);
+
+ for (uint32 i = 0; i < height; i++) {
+ byte *rowDest = dest;
+
+ for (uint32 j = 0; j < (width / 8); j++) {
+ byte p = srcP[j];
+ byte m = srcM[j];
+
+ for (int k = 0; k < 8; k++, rowDest++, p <<= 1, m <<= 1) {
+ if ((m & 0x80) != 0x80) {
+ if ((p & 0x80) == 0x80)
+ *rowDest = 2;
+ else
+ *rowDest = 1;
+ } else
+ *rowDest = 0;
+ }
+ }
+
+ dest -= width;
+ srcP += width / 8;
+ srcM += width / 8;
+ }
+
+ delete bitmap;
+ delete[] initialSource;
+ return true;
+}
+
+void NECursor::clear() {
+ delete[] _surface; _surface = 0;
+}
+
+NEResources::NEResources() {
+ _exe = 0;
+}
+
+NEResources::~NEResources() {
+ clear();
+}
+
+void NEResources::clear() {
+ if (_exe) {
+ delete _exe;
+ _exe = 0;
+ }
+
+ _resources.clear();
+
+ for (uint32 i = 0; i < _cursors.size(); i++)
+ for (uint32 j = 0; j < _cursors[i].cursors.size(); j++)
+ delete _cursors[i].cursors[j];
+
+ _cursors.clear();
+}
+
+const Array<NECursorGroup> &NEResources::getCursors() const {
+ return _cursors;
+}
+
+bool NEResources::loadFromEXE(const String &fileName) {
+ if (fileName.empty())
+ return false;
+
+ File *file = new File();
+
+ if (!file->open(fileName)) {
+ delete file;
+ return false;
+ }
+
+ return loadFromEXE(file);
+}
+
+bool NEResources::loadFromEXE(SeekableReadStream *stream) {
+ clear();
+
+ if (!stream)
+ return false;
+
+ _exe = stream;
+
+ uint32 offsetResourceTable = getResourceTableOffset();
+ if (offsetResourceTable == 0xFFFFFFFF)
+ return false;
+ if (offsetResourceTable == 0)
+ return true;
+
+ if (!readResourceTable(offsetResourceTable))
+ return false;
+
+ if (!readCursors())
+ return false;
+
+ return true;
+}
+
+bool NEResources::loadFromCompressedEXE(const String &fileName) {
+ // Based on http://www.cabextract.org.uk/libmspack/doc/szdd_kwaj_format.html
+
+ File file;
+
+ if (!file.open(fileName))
+ return false;
+
+ // First part of the signature
+ if (file.readUint32BE() != MKID_BE('SZDD'))
+ return false;
+
+ // Second part of the signature
+ if (file.readUint32BE() != 0x88F02733)
+ return false;
+
+ // Compression mode must be 'A'
+ if (file.readByte() != 'A')
+ return false;
+
+ file.readByte(); // file name character change
+ uint32 unpackedLength = file.readUint32LE();
+
+ byte *window = new byte[0x1000];
+ int pos = 0x1000 - 16;
+ memset(window, 0x20, 0x1000); // Initialize to all spaces
+
+ byte *unpackedData = (byte *)malloc(unpackedLength);
+ byte *dataPos = unpackedData;
+
+ // Apply simple LZSS decompression
+ for (;;) {
+ byte controlByte = file.readByte();
+
+ if (file.eos())
+ break;
+
+ for (byte i = 0; i < 8; i++) {
+ if (controlByte & (1 << i)) {
+ *dataPos++ = window[pos++] = file.readByte();
+ pos &= 0xFFF;
+ } else {
+ int matchPos = file.readByte();
+ int matchLen = file.readByte();
+ matchPos |= (matchLen & 0xF0) << 4;
+ matchLen = (matchLen & 0xF) + 3;
+ while (matchLen--) {
+ *dataPos++ = window[pos++] = window[matchPos++];
+ pos &= 0xFFF;
+ matchPos &= 0xFFF;
+ }
+ }
+
+ }
+ }
+
+ delete[] window;
+ SeekableReadStream *stream = new MemoryReadStream(unpackedData, unpackedLength);
+
+ return loadFromEXE(stream);
+}
+
+uint32 NEResources::getResourceTableOffset() {
+ if (!_exe)
+ return 0xFFFFFFFF;
+
+ if (!_exe->seek(0))
+ return 0xFFFFFFFF;
+
+ // 'MZ'
+ if (_exe->readUint16BE() != 0x4D5A)
+ return 0xFFFFFFFF;
+
+ if (!_exe->seek(60))
+ return 0xFFFFFFFF;
+
+ uint32 offsetSegmentEXE = _exe->readUint16LE();
+ if (!_exe->seek(offsetSegmentEXE))
+ return 0xFFFFFFFF;
+
+ // 'NE'
+ if (_exe->readUint16BE() != 0x4E45)
+ return 0xFFFFFFFF;
+
+ if (!_exe->seek(offsetSegmentEXE + 36))
+ return 0xFFFFFFFF;
+
+ uint32 offsetResourceTable = _exe->readUint16LE();
+ if (offsetResourceTable == 0)
+ // No resource table
+ return 0;
+
+ // Offset relative to the segment _exe header
+ offsetResourceTable += offsetSegmentEXE;
+ if (!_exe->seek(offsetResourceTable))
+ return 0xFFFFFFFF;
+
+ return offsetResourceTable;
+}
+
+static const char *s_resTypeNames[] = {
+ "", "cursor", "bitmap", "icon", "menu", "dialog", "string",
+ "font_dir", "font", "accelerator", "rc_data", "msg_table",
+ "group_cursor", "group_icon", "version", "dlg_include",
+ "plug_play", "vxd", "ani_cursor", "ani_icon", "html",
+ "manifest"
+};
+
+bool NEResources::readResourceTable(uint32 offset) {
+ if (!_exe)
+ return false;
+
+ if (!_exe->seek(offset))
+ return false;
+
+ uint32 align = 1 << _exe->readUint16LE();
+
+ uint16 typeID = _exe->readUint16LE();
+ while (typeID != 0) {
+ uint16 resCount = _exe->readUint16LE();
+
+ _exe->skip(4); // reserved
+
+ for (int i = 0; i < resCount; i++) {
+ Resource res;
+
+ // Resource properties
+ res.offset = _exe->readUint16LE() * align;
+ res.size = _exe->readUint16LE() * align;
+ res.flags = _exe->readUint16LE();
+ uint16 id = _exe->readUint16LE();
+ res.handle = _exe->readUint16LE();
+ res.usage = _exe->readUint16LE();
+
+ res.type = typeID;
+
+ if ((id & 0x8000) == 0)
+ res.id = getResourceString(*_exe, offset + id);
+ else
+ res.id = id & 0x7FFF;
+
+ if (typeID & 0x8000 && ((typeID & 0x7FFF) < ARRAYSIZE(s_resTypeNames)))
+ debug(2, "Found resource %s %s", s_resTypeNames[typeID & 0x7FFF], res.id.toString().c_str());
+ else
+ debug(2, "Found resource %04x %s", typeID, res.id.toString().c_str());
+
+ _resources.push_back(res);
+ }
+
+ typeID = _exe->readUint16LE();
+ }
+
+ return true;
+}
+
+String NEResources::getResourceString(SeekableReadStream &exe, uint32 offset) {
+ uint32 curPos = exe.pos();
+
+ if (!exe.seek(offset)) {
+ exe.seek(curPos);
+ return "";
+ }
+
+ uint8 length = exe.readByte();
+
+ String string;
+ for (uint16 i = 0; i < length; i++)
+ string += (char)exe.readByte();
+
+ exe.seek(curPos);
+ return string;
+}
+
+const NEResources::Resource *NEResources::findResource(uint16 type, WinResourceID id) const {
+ for (List<Resource>::const_iterator it = _resources.begin(); it != _resources.end(); ++it)
+ if (it->type == type && it->id == id)
+ return &*it;
+
+ return 0;
+}
+
+SeekableReadStream *NEResources::getResource(uint16 type, WinResourceID id) {
+ const Resource *res = findResource(type, id);
+
+ if (!res)
+ return 0;
+
+ _exe->seek(res->offset);
+ return _exe->readStream(res->size);
+}
+
+const Array<WinResourceID> NEResources::getIDList(uint16 type) const {
+ Array<WinResourceID> idArray;
+
+ for (List<Resource>::const_iterator it = _resources.begin(); it != _resources.end(); ++it)
+ if (it->type == type)
+ idArray.push_back(it->id);
+
+ return idArray;
+}
+
+bool NEResources::readCursors() {
+ uint32 cursorCount = 0;
+
+ for (List<Resource>::const_iterator it = _resources.begin(); it != _resources.end(); ++it)
+ if (it->type == kNEGroupCursor)
+ cursorCount++;
+
+ if (cursorCount == 0) {
+ _cursors.clear();
+ return true;
+ }
+
+ _cursors.resize(cursorCount);
+
+ Array<NECursorGroup>::iterator cursorGroup = _cursors.begin();
+ for (List<Resource>::const_iterator it = _resources.begin(); it != _resources.end(); ++it) {
+ if (it->type == kNEGroupCursor) {
+ if (!readCursorGroup(*cursorGroup, *it))
+ return false;
+
+ ++cursorGroup;
+ }
+ }
+
+ return true;
+}
+
+bool NEResources::readCursorGroup(NECursorGroup &group, const Resource &resource) {
+ if (!_exe)
+ return false;
+
+ if (resource.size <= 6)
+ return false;
+
+ if (!_exe->seek(resource.offset))
+ return false;
+
+ byte *data = new byte[resource.size];
+
+ if (!_exe->read(data, resource.size)) {
+ delete[] data;
+ return false;
+ }
+
+ uint32 cursorCount = READ_LE_UINT16(data + 4);
+ if (resource.size < (6 + cursorCount * 16)) {
+ delete[] data;
+ return false;
+ }
+
+ group.cursors.resize(cursorCount);
+
+ uint32 offset = 6;
+ for (uint32 i = 0; i < cursorCount; i++) {
+ group.cursors[i] = new NECursor();
+ NECursor *cursor = group.cursors[i];
+
+ // Plane count
+ if (READ_LE_UINT16(data + offset + 4) != 1) {
+ delete[] data;
+ return false;
+ }
+
+ // Bit count
+ if (READ_LE_UINT16(data + offset + 6) != 1) {
+ delete[] data;
+ return false;
+ }
+
+ uint32 id = READ_LE_UINT32(data + offset + 12);
+ const Resource *cursorResource = findResource(kNECursor, id);
+ if (!cursorResource) {
+ delete[] data;
+ return false;
+ }
+
+ cursor->setDimensions(READ_LE_UINT16(data + offset), READ_LE_UINT16(data + offset + 2) / 2);
+
+ uint32 dataSize = READ_LE_UINT32(data + offset + 8);
+ if (!readCursor(*cursor, *cursorResource, dataSize)) {
+ delete[] data;
+ return false;
+ }
+
+ offset += 16;
+ }
+
+ group.id = resource.id;
+
+ delete[] data;
+ return true;
+}
+
+bool NEResources::readCursor(NECursor &cursor, const Resource &resource, uint32 size) {
+ if (!_exe)
+ return false;
+
+ if (size <= 4)
+ return false;
+ if (resource.size < size)
+ return false;
+
+ if (!_exe->seek(resource.offset))
+ return false;
+
+ uint32 hotspotX = _exe->readUint16LE();
+ uint32 hotspotY = _exe->readUint16LE();
+ cursor.setHotspot(hotspotX, hotspotY);
+
+ size -= 4;
+
+ if (!cursor.readCursor(*_exe, size))
+ return false;
+
+ return true;
+}
+
+} // End of namespace Common