Initial commit
This commit is contained in:
272
engines/darkseed/nsp.cpp
Normal file
272
engines/darkseed/nsp.cpp
Normal file
@@ -0,0 +1,272 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "darkseed/darkseed.h"
|
||||
#include "darkseed/nsp.h"
|
||||
#include "common/debug.h"
|
||||
|
||||
namespace Darkseed {
|
||||
|
||||
Sprite::Sprite(uint16 width, uint16 height, uint16 pitch) : _width(width), _height(height), _pitch(pitch) {
|
||||
_pixels.resize(pitch * height, 0);
|
||||
}
|
||||
|
||||
bool Sprite::loadData(Common::SeekableReadStream &readStream) {
|
||||
g_engine->waitForSpeech();
|
||||
if (_width == 1 && _height == 1) {
|
||||
byte b = readStream.readByte();
|
||||
_pixels[0] = b >> 4;
|
||||
} else {
|
||||
bool hasReadByte = false;
|
||||
int currentDataByte = 0;
|
||||
for (int i = 0; i < _pitch * _height; i++) {
|
||||
if (!hasReadByte) {
|
||||
currentDataByte = readStream.readByte();
|
||||
if (readStream.eos()) {
|
||||
debug("Argh!");
|
||||
return false;
|
||||
}
|
||||
hasReadByte = true;
|
||||
_pixels[i] = currentDataByte >> 4;
|
||||
} else {
|
||||
hasReadByte = false;
|
||||
_pixels[i] = currentDataByte & 0xf;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Sprite::clipToScreen(int x, int y, uint16 frameBottom, uint16 *clippedWidth, uint16 *clippedHeight) const {
|
||||
*clippedWidth = _width;
|
||||
*clippedHeight = _height;
|
||||
if (x + _width > g_engine->_screen->w) {
|
||||
*clippedWidth = g_engine->_screen->w - x;
|
||||
}
|
||||
if (x + _width > 570) {
|
||||
*clippedWidth = 570 - x;
|
||||
}
|
||||
if (frameBottom != 0 && y + _height > g_engine->_frameBottom) {
|
||||
if (y >= frameBottom) {
|
||||
return;
|
||||
}
|
||||
*clippedHeight = frameBottom - y;
|
||||
}
|
||||
}
|
||||
|
||||
void Sprite::draw(int x, int y, uint16 frameBottom) const {
|
||||
uint16 clippedWidth = _width;
|
||||
uint16 clippedHeight = _height;
|
||||
clipToScreen(x, y, frameBottom, &clippedWidth, &clippedHeight);
|
||||
|
||||
// clip to left side of frame.
|
||||
if (x + _width <= 70) {
|
||||
return;
|
||||
}
|
||||
int dX = 0;
|
||||
if (x < 70) {
|
||||
dX = 70 - x;
|
||||
x = 70;
|
||||
clippedWidth -= dX;
|
||||
}
|
||||
const uint8 *pixels = _pixels.data() + dX;
|
||||
g_engine->_screen->copyRectToSurfaceWithKey(pixels, _pitch, x, y, clippedWidth, clippedHeight, 0xf);
|
||||
}
|
||||
|
||||
void Sprite::draw(Graphics::Surface *dst, int x, int y, uint16 frameBottom) const {
|
||||
uint16 clippedWidth = _width;
|
||||
uint16 clippedHeight = _height;
|
||||
clipToScreen(x, y, frameBottom, &clippedWidth, &clippedHeight);
|
||||
dst->copyRectToSurfaceWithKey(_pixels.data(), _pitch, x, y, clippedWidth, clippedHeight, 0xf);
|
||||
}
|
||||
|
||||
void Sprite::drawScaled(int destX, int destY, int destWidth, int destHeight, bool flipX) const {
|
||||
//TODO image isn't exactly the same when not scaling. It seems larger by about a pixel.
|
||||
//TODO this logic is pretty messy. It should probably be re-written. It is trying to scale, clip and flip at once.
|
||||
Graphics::ManagedSurface * destSurface = g_engine->_screen;
|
||||
// Based on the GNAP engine scaling code
|
||||
if (destWidth == 0 || destHeight == 0) {
|
||||
return;
|
||||
}
|
||||
const byte *source = _pixels.data();
|
||||
const int xs = ((_width - 1) << 16) / destWidth;
|
||||
const int ys = ((_height - 1) << 16) / destHeight;
|
||||
int clipX = 0, clipY = 0;
|
||||
const int destPitch = destSurface->pitch;
|
||||
if (destX < 0) {
|
||||
clipX = -destX;
|
||||
destX = 0;
|
||||
destWidth -= clipX;
|
||||
}
|
||||
|
||||
if (destY < 0) {
|
||||
clipY = -destY;
|
||||
destY = 0;
|
||||
destHeight -= clipY;
|
||||
}
|
||||
if (destY + destHeight >= destSurface->h) {
|
||||
destHeight = destSurface->h - destY;
|
||||
}
|
||||
if (destWidth < 0 || destHeight < 0)
|
||||
return;
|
||||
byte *dst = (byte *)destSurface->getBasePtr(destX, destY);
|
||||
int yi = ys * clipY;
|
||||
const byte *hsrc = source + _pitch * ((yi + 0x8000) >> 16);
|
||||
int16 currY = destY;
|
||||
for (int yc = 0; yc < destHeight && currY < g_engine->_frameBottom; ++yc) {
|
||||
byte *wdst = flipX ? dst + (destWidth - 1) : dst;
|
||||
int16 currX = flipX ? destX + (destWidth - 1) : destX;
|
||||
int xi = flipX ? xs : xs * clipX;
|
||||
const byte *wsrc = hsrc + ((xi + 0x8000) >> 16);
|
||||
for (int xc = 0; xc < destWidth; ++xc) {
|
||||
if (currX > 69 && currX < 570 && currX < destSurface->w) { // clip to game window. TODO pass clip rect into method.
|
||||
byte colorIndex = *wsrc;
|
||||
// uint16 c = READ_LE_UINT16(&palette[colorIndex * 2]);
|
||||
if (colorIndex != 0xf) {
|
||||
*wdst = colorIndex;
|
||||
// if (!(c & 0x8000u) || alpha == NONE) {
|
||||
// // only copy opaque pixels
|
||||
// WRITE_SCREEN(wdst, c & ~0x8000);
|
||||
// } else {
|
||||
// WRITE_SCREEN(wdst, alphaBlendRGB555(c & 0x7fffu, READ_SCREEN(wdst) & 0x7fffu, 128));
|
||||
// // semi-transparent pixels.
|
||||
// }
|
||||
}
|
||||
}
|
||||
currX += (flipX ? -1 : 1);
|
||||
wdst += (flipX ? -1 : 1);
|
||||
xi += xs;
|
||||
wsrc = hsrc + ((xi + 0x8000) >> 16);
|
||||
}
|
||||
dst += destPitch;
|
||||
yi += ys;
|
||||
hsrc = source + _pitch * ((yi + 0x8000) >> 16);
|
||||
currY++;
|
||||
}
|
||||
}
|
||||
|
||||
bool Nsp::load(const Common::Path &filename) {
|
||||
Common::File file;
|
||||
Common::Path filePath = g_engine->getRoomFilePath(filename);
|
||||
if (!file.open(filePath)) {
|
||||
return false;
|
||||
}
|
||||
bool ret = load(file);
|
||||
file.close();
|
||||
if (ret) {
|
||||
Common::String filePathStr = filePath.toString();
|
||||
debug("Loaded %s", filePathStr.c_str());
|
||||
Common::Path obtFilename = Common::Path(filePathStr.substr(0, filePathStr.size() - 4) + ".obt");
|
||||
if (!loadObt(obtFilename)) {
|
||||
debug("failed to load %s", obtFilename.toString().c_str());
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Nsp::load(Common::SeekableReadStream &readStream) {
|
||||
_frames.clear();
|
||||
for (int i = 0; i < 96; i++) {
|
||||
int w = readStream.readByte();
|
||||
int h = readStream.readByte();
|
||||
int p = w + (w & 1);
|
||||
_frames.push_back(Sprite(w, h, p));
|
||||
}
|
||||
|
||||
for (int i = 0; i < 96; i++) {
|
||||
if (!_frames[i].loadData(readStream)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const Sprite &Nsp::getSpriteAt(int index) const {
|
||||
if (index >= (int)_frames.size()) {
|
||||
error("getSpriteAt: Invalid sprite index. %d", index);
|
||||
}
|
||||
return _frames[index];
|
||||
}
|
||||
|
||||
bool Nsp::loadObt(const Common::Path &filename) {
|
||||
Common::File file;
|
||||
if (!file.open(filename)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
_animations.clear();
|
||||
_animations.resize(20);
|
||||
for (int i = 0; i < 20; i++) {
|
||||
_animations[i]._numFrames = file.readByte();
|
||||
|
||||
for (int j = 0; j < 20; j++) {
|
||||
if (file.readByte()) {
|
||||
_animations[i]._deltaX.push_back(-(file.readUint16LE() / 100));
|
||||
} else {
|
||||
_animations[i]._deltaX.push_back(file.readUint16LE() / 100);
|
||||
}
|
||||
if (file.readByte()) {
|
||||
_animations[i]._deltaY.push_back(-(file.readUint16LE() / 100));
|
||||
} else {
|
||||
_animations[i]._deltaY.push_back(file.readUint16LE() / 100);
|
||||
}
|
||||
_animations[i]._frameNo.push_back(file.readByte());
|
||||
_animations[i]._frameDuration.push_back(file.readByte());
|
||||
}
|
||||
}
|
||||
|
||||
file.close();
|
||||
|
||||
debug("Loaded %s", filename.toString().c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
const Obt &Nsp::getAnimAt(int index) {
|
||||
return _animations[index];
|
||||
}
|
||||
|
||||
int16 Nsp::getMaxSpriteWidth() {
|
||||
int maxWidth = 0;
|
||||
for (auto &frame : _frames) {
|
||||
if (frame._width > maxWidth) {
|
||||
maxWidth = frame._width;
|
||||
}
|
||||
}
|
||||
return maxWidth;
|
||||
}
|
||||
|
||||
Obt::Obt() {
|
||||
_numFrames = 0;
|
||||
_deltaX.reserve(20);
|
||||
_deltaY.reserve(20);
|
||||
_frameNo.reserve(20);
|
||||
_frameDuration.reserve(20);
|
||||
}
|
||||
|
||||
Obt::~Obt() {
|
||||
_deltaX.clear();
|
||||
_deltaY.clear();
|
||||
_frameNo.clear();
|
||||
_frameDuration.clear();
|
||||
}
|
||||
|
||||
} // End of namespace Darkseed
|
||||
Reference in New Issue
Block a user