Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

View File

@@ -0,0 +1,252 @@
/* 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 "graphics/framelimiter.h"
#include "graphics/palette.h"
#include "graphics/paletteman.h"
#include "video/smk_decoder.h"
#include "bagel/spacebar/baglib/bagel.h"
#include "bagel/spacebar/boflib/debug.h"
#include "bagel/spacebar/boflib/app.h"
#include "bagel/spacebar/boflib/timer.h"
#include "bagel/spacebar/boflib/gfx/text.h"
#include "bagel/boflib/sound.h"
#include "bagel/bagel.h"
namespace Bagel {
namespace SpaceBar {
#define DEBUG_LOG "DEBUG.LOG"
#define DEBUG_INI "BOFFO.INI"
#define BOFDISP 0
CBofApp *CBofApp::_pBofApp;
CBofApp::CBofApp() {
StartupCode();
}
CBofApp::CBofApp(const char *pszAppName) {
StartupCode();
setAppName(pszAppName);
}
CBofApp::~CBofApp() {
ShutDownCode();
_szAppName[0] = '\0';
_pMainWnd = nullptr;
_pPalette = nullptr;
_pBofApp = nullptr;
}
void CBofApp::StartupCode() {
_pBofApp = this;
// Open the Boffo debug options file (BOFFO.INI)
g_pDebugOptions = new CBofDebugOptions(DEBUG_INI);
g_pDebugOptions->readSetting("DebugOptions", "MainLoops", &_nIterations, DEFAULT_MAINLOOPS);
//
// Initialize the boffo libraries
//
// Init the Window library
CBofWindow::initialize();
// Init the text library
CBofText::initialize();
}
void CBofApp::ShutDownCode() {
// Un-initialize the text library
CBofText::shutdown();
// Shut down the Window library
CBofWindow::shutdown();
// Kill any shared palette
CBofPalette::setSharedPalette(nullptr);
if (g_pDebugOptions != nullptr) {
delete g_pDebugOptions;
g_pDebugOptions = nullptr;
}
}
ErrorCode CBofApp::preInit() {
if ((_pPalette == nullptr) && (_pDefPalette == nullptr)) {
_pDefPalette = new CBofPalette();
_pDefPalette->createDefault();
setPalette(_pDefPalette);
}
return _errCode;
}
ErrorCode CBofApp::initialize() {
return _errCode;
}
ErrorCode CBofApp::runApp() {
int nCount = _nIterations;
// Acquire and dispatch messages until we need to quit, or too many errors
Graphics::FrameLimiter limiter(g_system, 60, false);
while (!g_engine->shouldQuit() && CBofError::getErrorCount() < MAX_ERRORS) {
// Support for playing videos via the console
if (_consoleVideo && _consoleVideo->isPlaying()) {
if (_consoleVideo->needsUpdate()) {
const Graphics::Surface *s = _consoleVideo->decodeNextFrame();
Graphics::Palette pal(_consoleVideo->getPalette(), 256);
g_engine->getScreen()->blitFrom(*s, Common::Point(0, 0), &pal);
}
limiter.delayBeforeSwap();
g_engine->getScreen()->update();
limiter.startFrame();
continue;
}
delete _consoleVideo;
_consoleVideo = nullptr;
// Handle sounds and timers
CBofSound::audioTask();
CBofTimer::handleTimers();
if (nCount < 0) {
nCount++;
if (nCount == 0)
nCount = 1;
} else {
for (int i = 0; i < nCount; i++) {
// Give each window it's own main loop (sort-of)
CBofWindow *pWindow = CBofWindow::getWindowList();
while (pWindow != nullptr) {
if (shouldQuit())
return ERR_NONE;
if (pWindow->isCreated()) {
pWindow->onMainLoop();
}
pWindow = (CBofWindow *)pWindow->getNext();
}
}
nCount = _nIterations;
}
// Handle events
_pMainWnd->handleEvents();
limiter.delayBeforeSwap();
g_engine->getScreen()->update();
limiter.startFrame();
}
return _errCode;
}
ErrorCode CBofApp::shutdown() {
return _errCode;
}
void CBofApp::postShutDown() {
delete _pWindow;
_pWindow = nullptr;
// No more palettes
_pPalette = nullptr;
delete _pDefPalette;
_pDefPalette = nullptr;
}
void CBofApp::setPalette(CBofPalette *pPalette) {
_pPalette = pPalette;
if (pPalette != nullptr) {
if (g_system->getScreenFormat().bytesPerPixel == 1) {
const auto &pal = pPalette->getPalette();
g_system->getPaletteManager()->setPalette(pal._data, 0, pal._numColors);
}
} else {
// Use default palette
_pPalette = _pDefPalette;
}
}
void CBofApp::addCursor(CBofCursor &cCursor) {
_cCursorList.addToTail(cCursor);
}
void CBofApp::delCursor(int nIndex) {
_cCursorList.remove(nIndex);
}
bool CBofApp::consolePlayVideo(const Common::Path &path) {
delete _consoleVideo;
_consoleVideo = new Video::SmackerDecoder();
_consoleVideo->setSoundType(Audio::Mixer::kSFXSoundType);
if (_consoleVideo->loadFile(path)) {
_consoleVideo->start();
return true;
} else {
delete _consoleVideo;
_consoleVideo = nullptr;
return false;
}
}
///////////////////////////////////////////////////////////////////////////
// Global routines
///////////////////////////////////////////////////////////////////////////
CBofPoint getMousePos() {
return CBofWindow::getMousePos();
}
void bofMessageBox(const char *pszTitle, const char *pszMessage) {
Common::String msg = Common::String::format("%s - %s", pszTitle, pszMessage);
g_engine->errorDialog(msg.c_str());
}
} // namespace SpaceBar
} // namespace Bagel

View File

@@ -0,0 +1,165 @@
/* 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/>.
*
*/
#ifndef BAGEL_BOFLIB_APP_H
#define BAGEL_BOFLIB_APP_H
#include "video/video_decoder.h"
#include "bagel/spacebar/boflib/gui/window.h"
#include "bagel/spacebar/boflib/gfx/cursor.h"
#include "bagel/boflib/error.h"
#include "bagel/spacebar/boflib/list.h"
namespace Bagel {
namespace SpaceBar {
#define MAX_APP_NAME 128
#define DEFAULT_MAINLOOPS 1
#define kReallyFastPPC 50
#define kReallySlowPPC 200
class CBofApp : public CBofError {
private:
CBofWindow *_pWindow = nullptr;
CBofWindow *_captureControl = nullptr;
CBofWindow *_focusControl = nullptr;
Video::VideoDecoder *_consoleVideo = nullptr;
protected:
void StartupCode();
void ShutDownCode();
char _szAppName[MAX_APP_NAME] = { 0 };
CBofList<CBofCursor> _cCursorList;
CBofCursor _cDefaultCursor;
CBofWindow *_pMainWnd = nullptr;
CBofPalette *_pPalette = nullptr;
CBofPalette *_pDefPalette = nullptr;
int _nScreenDX = 0;
int _nScreenDY = 0;
int _nColorDepth = 0;
int _nIterations = DEFAULT_MAINLOOPS;
static CBofApp *_pBofApp;
virtual bool shouldQuit() const = 0;
public:
CBofApp();
CBofApp(const char *pszAppName);
virtual ~CBofApp();
ErrorCode preInit();
void postShutDown();
// These functions can be overridden by the child class
virtual ErrorCode initialize();
virtual ErrorCode runApp();
virtual ErrorCode shutdown();
virtual void setAppName(const char *pszNewAppName) {
Common::strcpy_s(_szAppName, pszNewAppName);
}
const char *getAppName() const {
return (const char *)_szAppName;
}
void setMainWindow(CBofWindow *pWnd) {
_pMainWnd = pWnd;
}
CBofWindow *getMainWindow() const {
return _pMainWnd;
}
CBofWindow *getActualWindow() const {
return _pWindow;
}
void setPalette(CBofPalette *pPalette);
CBofPalette *getPalette() const {
return _pPalette;
}
int screenWidth() const {
return _nScreenDX;
}
int screenHeight() const {
return _nScreenDY;
}
int screenDepth() const {
return _nColorDepth;
}
CBofCursor getDefaultCursor() const {
return _cDefaultCursor;
}
void setDefaultCursor(CBofCursor &cCursor) {
_cDefaultCursor = cCursor;
}
void addCursor(CBofCursor &cCursor);
void delCursor(int nIndex);
CBofCursor getCursor(int nIndex) {
return _cCursorList[nIndex];
}
int getNumberOfCursors() const {
return _cCursorList.getCount();
}
void setCaptureControl(CBofWindow *ctl) {
_captureControl = ctl;
}
CBofWindow *getCaptureControl() const {
return _captureControl;
}
void setFocusControl(CBofWindow *ctl) {
_focusControl = ctl;
}
CBofWindow *getFocusControl() const {
return _focusControl;
}
bool consolePlayVideo(const Common::Path &path);
static uint32 getMachineSpeed() {
return kReallyFastPPC;
}
static CBofApp *getApp() {
return _pBofApp;
}
};
// Global routines
//
void bofMessageBox(const char *pszTitle, const char *pszMessage);
CBofPoint getMousePos();
} // namespace SpaceBar
} // namespace Bagel
#endif

View File

@@ -0,0 +1,57 @@
/* 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/>.
*
*/
#ifndef BAGEL_BOFLIB_ARRAY_H
#define BAGEL_BOFLIB_ARRAY_H
#include "common/array.h"
namespace Bagel {
namespace SpaceBar {
template<class T>
class Array : public Common::Array<T> {
public:
int indexOf(T t) {
for (int i = 0; i < (int)this->size(); ++i) {
if (this->operator[](i) == t)
return i;
}
return -1;
}
bool contains(T t) const {
return this->indexOf(t) != -1;
}
void remove(T t) {
int idx = this->indexOf(t);
if (idx != -1)
this->remove_at(idx);
}
};
} // namespace SpaceBar
} // namespace Bagel
#endif

View File

@@ -0,0 +1,132 @@
/* 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 "bagel/spacebar/boflib/crc.h"
namespace Bagel {
namespace SpaceBar {
static const uint16 crc32tabLo[256] = {
0x0000, 0x3096, 0x612c, 0x51ba, 0xc419, 0xf48f, 0xa535, 0x95a3,
0x8832, 0xb8a4, 0xe91e, 0xd988, 0x4c2b, 0x7cbd, 0x2d07, 0x1d91,
0x1064, 0x20f2, 0x7148, 0x41de, 0xd47d, 0xe4eb, 0xb551, 0x85c7,
0x9856, 0xa8c0, 0xf97a, 0xc9ec, 0x5c4f, 0x6cd9, 0x3d63, 0x0df5,
0x20c8, 0x105e, 0x41e4, 0x7172, 0xe4d1, 0xd447, 0x85fd, 0xb56b,
0xa8fa, 0x986c, 0xc9d6, 0xf940, 0x6ce3, 0x5c75, 0x0dcf, 0x3d59,
0x30ac, 0x003a, 0x5180, 0x6116, 0xf4b5, 0xc423, 0x9599, 0xa50f,
0xb89e, 0x8808, 0xd9b2, 0xe924, 0x7c87, 0x4c11, 0x1dab, 0x2d3d,
0x4190, 0x7106, 0x20bc, 0x102a, 0x8589, 0xb51f, 0xe4a5, 0xd433,
0xc9a2, 0xf934, 0xa88e, 0x9818, 0x0dbb, 0x3d2d, 0x6c97, 0x5c01,
0x51f4, 0x6162, 0x30d8, 0x004e, 0x95ed, 0xa57b, 0xf4c1, 0xc457,
0xd9c6, 0xe950, 0xb8ea, 0x887c, 0x1ddf, 0x2d49, 0x7cf3, 0x4c65,
0x6158, 0x51ce, 0x0074, 0x30e2, 0xa541, 0x95d7, 0xc46d, 0xf4fb,
0xe96a, 0xd9fc, 0x8846, 0xb8d0, 0x2d73, 0x1de5, 0x4c5f, 0x7cc9,
0x713c, 0x41aa, 0x1010, 0x2086, 0xb525, 0x85b3, 0xd409, 0xe49f,
0xf90e, 0xc998, 0x9822, 0xa8b4, 0x3d17, 0x0d81, 0x5c3b, 0x6cad,
0x8320, 0xb3b6, 0xe20c, 0xd29a, 0x4739, 0x77af, 0x2615, 0x1683,
0x0b12, 0x3b84, 0x6a3e, 0x5aa8, 0xcf0b, 0xff9d, 0xae27, 0x9eb1,
0x9344, 0xa3d2, 0xf268, 0xc2fe, 0x575d, 0x67cb, 0x3671, 0x06e7,
0x1b76, 0x2be0, 0x7a5a, 0x4acc, 0xdf6f, 0xeff9, 0xbe43, 0x8ed5,
0xa3e8, 0x937e, 0xc2c4, 0xf252, 0x67f1, 0x5767, 0x06dd, 0x364b,
0x2bda, 0x1b4c, 0x4af6, 0x7a60, 0xefc3, 0xdf55, 0x8eef, 0xbe79,
0xb38c, 0x831a, 0xd2a0, 0xe236, 0x7795, 0x4703, 0x16b9, 0x262f,
0x3bbe, 0x0b28, 0x5a92, 0x6a04, 0xffa7, 0xcf31, 0x9e8b, 0xae1d,
0xc2b0, 0xf226, 0xa39c, 0x930a, 0x06a9, 0x363f, 0x6785, 0x5713,
0x4a82, 0x7a14, 0x2bae, 0x1b38, 0x8e9b, 0xbe0d, 0xefb7, 0xdf21,
0xd2d4, 0xe242, 0xb3f8, 0x836e, 0x16cd, 0x265b, 0x77e1, 0x4777,
0x5ae6, 0x6a70, 0x3bca, 0x0b5c, 0x9eff, 0xae69, 0xffd3, 0xcf45,
0xe278, 0xd2ee, 0x8354, 0xb3c2, 0x2661, 0x16f7, 0x474d, 0x77db,
0x6a4a, 0x5adc, 0x0b66, 0x3bf0, 0xae53, 0x9ec5, 0xcf7f, 0xffe9,
0xf21c, 0xc28a, 0x9330, 0xa3a6, 0x3605, 0x0693, 0x5729, 0x67bf,
0x7a2e, 0x4ab8, 0x1b02, 0x2b94, 0xbe37, 0x8ea1, 0xdf1b, 0xef8d
};
static const uint16 crc32tabHi[256] = {
0x0000, 0x7707, 0xee0e, 0x9909, 0x076d, 0x706a, 0xe963, 0x9e64,
0x0edb, 0x79dc, 0xe0d5, 0x97d2, 0x09b6, 0x7eb1, 0xe7b8, 0x90bf,
0x1db7, 0x6ab0, 0xf3b9, 0x84be, 0x1ada, 0x6ddd, 0xf4d4, 0x83d3,
0x136c, 0x646b, 0xfd62, 0x8a65, 0x1401, 0x6306, 0xfa0f, 0x8d08,
0x3b6e, 0x4c69, 0xd560, 0xa267, 0x3c03, 0x4b04, 0xd20d, 0xa50a,
0x35b5, 0x42b2, 0xdbbb, 0xacbc, 0x32d8, 0x45df, 0xdcd6, 0xabd1,
0x26d9, 0x51de, 0xc8d7, 0xbfd0, 0x21b4, 0x56b3, 0xcfba, 0xb8bd,
0x2802, 0x5f05, 0xc60c, 0xb10b, 0x2f6f, 0x5868, 0xc161, 0xb666,
0x76dc, 0x01db, 0x98d2, 0xefd5, 0x71b1, 0x06b6, 0x9fbf, 0xe8b8,
0x7807, 0x0f00, 0x9609, 0xe10e, 0x7f6a, 0x086d, 0x9164, 0xe663,
0x6b6b, 0x1c6c, 0x8565, 0xf262, 0x6c06, 0x1b01, 0x8208, 0xf50f,
0x65b0, 0x12b7, 0x8bbe, 0xfcb9, 0x62dd, 0x15da, 0x8cd3, 0xfbd4,
0x4db2, 0x3ab5, 0xa3bc, 0xd4bb, 0x4adf, 0x3dd8, 0xa4d1, 0xd3d6,
0x4369, 0x346e, 0xad67, 0xda60, 0x4404, 0x3303, 0xaa0a, 0xdd0d,
0x5005, 0x2702, 0xbe0b, 0xc90c, 0x5768, 0x206f, 0xb966, 0xce61,
0x5ede, 0x29d9, 0xb0d0, 0xc7d7, 0x59b3, 0x2eb4, 0xb7bd, 0xc0ba,
0xedb8, 0x9abf, 0x03b6, 0x74b1, 0xead5, 0x9dd2, 0x04db, 0x73dc,
0xe363, 0x9464, 0x0d6d, 0x7a6a, 0xe40e, 0x9309, 0x0a00, 0x7d07,
0xf00f, 0x8708, 0x1e01, 0x6906, 0xf762, 0x8065, 0x196c, 0x6e6b,
0xfed4, 0x89d3, 0x10da, 0x67dd, 0xf9b9, 0x8ebe, 0x17b7, 0x60b0,
0xd6d6, 0xa1d1, 0x38d8, 0x4fdf, 0xd1bb, 0xa6bc, 0x3fb5, 0x48b2,
0xd80d, 0xaf0a, 0x3603, 0x4104, 0xdf60, 0xa867, 0x316e, 0x4669,
0xcb61, 0xbc66, 0x256f, 0x5268, 0xcc0c, 0xbb0b, 0x2202, 0x5505,
0xc5ba, 0xb2bd, 0x2bb4, 0x5cb3, 0xc2d7, 0xb5d0, 0x2cd9, 0x5bde,
0x9b64, 0xec63, 0x756a, 0x026d, 0x9c09, 0xeb0e, 0x7207, 0x0500,
0x95bf, 0xe2b8, 0x7bb1, 0x0cb6, 0x92d2, 0xe5d5, 0x7cdc, 0x0bdb,
0x86d3, 0xf1d4, 0x68dd, 0x1fda, 0x81be, 0xf6b9, 0x6fb0, 0x18b7,
0x8808, 0xff0f, 0x6606, 0x1101, 0x8f65, 0xf862, 0x616b, 0x166c,
0xa00a, 0xd70d, 0x4e04, 0x3903, 0xa767, 0xd060, 0x4969, 0x3e6e,
0xaed1, 0xd9d6, 0x40df, 0x37d8, 0xa9bc, 0xdebb, 0x47b2, 0x30b5,
0xbdbd, 0xcaba, 0x53b3, 0x24b4, 0xbad0, 0xcdd7, 0x54de, 0x23d9,
0xb366, 0xc461, 0x5d68, 0x2a6f, 0xb40b, 0xc30c, 0x5a05, 0x2d02
};
uint32 calculateCRC(const void *pBuffer, int32 lBufLen, uint32 lCrcValue) {
assert(pBuffer != nullptr);
assert(lBufLen > 0);
const byte *p = (const byte *)pBuffer;
int32 i = -1;
while (++i < lBufLen) {
byte c = (byte)(*p ^ (byte)lCrcValue);
lCrcValue = (lCrcValue >> 8) ^ crc32tabLo[c] ^ ((uint32)crc32tabHi[c] << 16);
p++;
}
return lCrcValue;
}
uint32 calculateCRC(const int32 *pBuffer, int32 lBufLen, uint32 lCrcValue) {
assert(pBuffer != nullptr);
assert(lBufLen > 0);
const int32 *p = pBuffer;
for (int i = 0; i < lBufLen; ++i) {
uint32 val = *(const uint32 *)p++;
for (int j = 0; j < 4; ++j, val >>= 8) {
byte c = (byte)((val & 0xff) ^ (byte)lCrcValue);
lCrcValue = (lCrcValue >> 8) ^ crc32tabLo[c] ^ ((uint32)crc32tabHi[c] << 16);
}
}
return lCrcValue;
}
} // namespace SpaceBar
} // namespace Bagel

View File

@@ -0,0 +1,44 @@
/* 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/>.
*
*/
#ifndef BAGEL_BOFLIB_CRC_H
#define BAGEL_BOFLIB_CRC_H
#include "bagel/boflib/stdinc.h"
namespace Bagel {
namespace SpaceBar {
/**
* Calculates the CRC (Cyclic Redundancy Check) for a buffer
* @param pBuffer Pointer to buffer
* @param lBufLen Length of this buffer
* @param lCrcValue Previous CRC value (if running CRC)
* @return New CRC value
*/
extern uint32 calculateCRC(const void *pBuffer, int32 lBufLen, uint32 lCrcValue = 0);
extern uint32 calculateCRC(const int32 *pBuffer, int32 lBufLen, uint32 lCrcValue = 0);
} // namespace SpaceBar
} // namespace Bagel
#endif

View File

@@ -0,0 +1,820 @@
/* 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 "bagel/spacebar/boflib/dat_file.h"
#include "bagel/spacebar/boflib/crc.h"
#include "bagel/spacebar/boflib/debug.h"
#include "bagel/boflib/log.h"
#include "bagel/boflib/misc.h"
#include "bagel/boflib/stdinc.h"
#include "bagel/boflib/file_functions.h"
namespace Bagel {
namespace SpaceBar {
// Local prototypes
static uint32 CreateHashCode(const byte *);
void HeaderRec::synchronize(Common::Serializer &s) {
s.syncAsSint32LE(_lOffset);
s.syncAsSint32LE(_lLength);
s.syncAsUint32LE(_lCrc);
s.syncAsUint32LE(_lKey);
}
void HeadInfo::synchronize(Common::Serializer &s) {
s.syncAsSint32LE(_lNumRecs);
s.syncAsSint32LE(_lAddress);
s.syncAsUint32LE(_lFlags);
s.syncAsUint32LE(_lFootCrc);
}
CBofDataFile::CBofDataFile() {
_szFileName[0] = '\0';
_szPassWord[0] = '\0';
_lHeaderLength = 0;
_lNumRecs = 0;
_pHeader = nullptr;
_bHeaderDirty = false;
}
ErrorCode CBofDataFile::setFile(const char *pszFileName, uint32 lFlags) {
assert(isValidObject(this));
// Validate input
assert(pszFileName != nullptr);
assert(strlen(pszFileName) < MAX_FNAME);
// Release any previous data-file
releaseFile();
// All data files are binary, so force it
lFlags |= CBF_BINARY;
// Remember the flags
_lFlags = lFlags;
if (fileGetFullPath(_szFileName, pszFileName) != nullptr) {
if (open() == ERR_NONE) {
// Read header block
readHeader();
// Close data file if we are not keeping it open
if (!(_lFlags & CDF_KEEPOPEN)) {
close();
}
} else
reportError(ERR_FOPEN, "Could not open file %s", _szFileName);
} else {
reportError(ERR_FFIND, "Could not build full path to %s", pszFileName);
}
return _errCode;
}
CBofDataFile::~CBofDataFile() {
assert(isValidObject(this));
releaseFile();
}
ErrorCode CBofDataFile::releaseFile() {
assert(isValidObject(this));
// If header was modified
if (_bHeaderDirty) {
// Write header to disk
writeHeader();
}
close();
// Free header buffer
delete[] _pHeader;
_pHeader = nullptr;
return _errCode;
}
ErrorCode CBofDataFile::create() {
assert(isValidObject(this));
HeadInfo stHeaderInfo;
// Only continue if there is no current error
if (_errCode == ERR_NONE) {
if (_stream != nullptr) {
close();
}
// Re-initialize
delete[] _pHeader;
_pHeader = nullptr;
_stream = nullptr;
_lHeaderLength = 0;
_bHeaderDirty = false;
stHeaderInfo._lNumRecs = _lNumRecs = 0;
stHeaderInfo._lAddress = HeadInfo::size();
// Create the file
if (CBofFile::create(_szFileName, _lFlags) == ERR_NONE) {
// Write empty header info
if (write(stHeaderInfo) != ERR_NONE) {
_errCode = ERR_FWRITE;
}
seek(0);
} else {
_errCode = ERR_FOPEN;
}
}
return _errCode;
}
ErrorCode CBofDataFile::open() {
assert(isValidObject(this));
// Only continue if there is no current error
if (_errCode == ERR_NONE && _stream == nullptr) {
if (!(_lFlags & CDF_READONLY)) {
if (_lFlags & CDF_SAVEFILE) {
if (_lFlags & CDF_CREATE)
create();
} else if (!fileExists(_szFileName))
create();
}
if (_stream == nullptr) {
// Open data file
CBofFile::open(_szFileName, _lFlags);
}
}
return _errCode;
}
ErrorCode CBofDataFile::close() {
assert(isValidObject(this));
if (_stream != nullptr) {
if (_bHeaderDirty) {
writeHeader();
}
CBofFile::close();
}
return _errCode;
}
ErrorCode CBofDataFile::readHeader() {
assert(isValidObject(this));
// Only continue if there is no current error
if (_errCode == ERR_NONE) {
if (_stream == nullptr) {
open();
}
if (!errorOccurred()) {
// Determine number of records in file
HeadInfo stHeaderInfo;
if (read(stHeaderInfo) == ERR_NONE) {
_lNumRecs = stHeaderInfo._lNumRecs;
_lHeaderStart = stHeaderInfo._lAddress;
// Length of header is number of records * header-record size
_lHeaderLength = _lNumRecs * HeaderRec::size();
Common::SeekableReadStream *rs = dynamic_cast<Common::SeekableReadStream *>(_stream);
assert(rs);
int32 lfileLength = rs->size();
// Make sure header contains valid info
if ((_lHeaderStart >= HeadInfo::size()) &&
(_lHeaderStart <= lfileLength) && (_lHeaderLength >= 0) &&
(_lHeaderLength < lfileLength)) {
// Force Encrypted, and Compress if existing file has them
_lFlags |= stHeaderInfo._lFlags & CDF_ENCRYPT;
_lFlags |= stHeaderInfo._lFlags & CDF_COMPRESSED;
if (_lHeaderLength != 0) {
// Allocate buffer to hold header
_pHeader = new HeaderRec[(int)_lNumRecs];
// Seek to start of header
seek(_lHeaderStart);
// Read header
ErrorCode errorCode = ERR_NONE;
for (int i = 0; i < _lNumRecs && errorCode == ERR_NONE; ++i) {
errorCode = read(_pHeader[i]);
}
if (errorCode == ERR_NONE) {
uint32 lCrc = calculateCRC(&_pHeader->_lOffset, 4 * _lNumRecs);
if (lCrc != stHeaderInfo._lFootCrc) {
logError(buildString("Error: '%s' has invalid footer", _szFileName));
_errCode = ERR_CRC;
}
} else {
logError(buildString("Error: Could not read footer in file '%s'", _szFileName));
_errCode = ERR_FREAD;
}
}
} else {
logError(buildString("Error: '%s' has invalid header", _szFileName));
_errCode = ERR_FTYPE;
}
} else {
logError(buildString("Error: Could not read header in file '%s'", _szFileName));
_errCode = ERR_FREAD;
}
}
}
return _errCode;
}
ErrorCode CBofDataFile::writeHeader() {
assert(isValidObject(this));
// Only continue if there is no current error
if (_errCode == ERR_NONE) {
// Open the data file if it's not already open
if (_stream == nullptr) {
open();
}
if (_errCode == ERR_NONE) {
// Header starts at the end of the last record
HeaderRec *pRec = &_pHeader[_lNumRecs - 1];
_lHeaderStart = pRec->_lOffset + pRec->_lLength;
HeadInfo stHeaderInfo;
stHeaderInfo._lNumRecs = _lNumRecs;
stHeaderInfo._lAddress = _lHeaderStart;
stHeaderInfo._lFlags = _lFlags;
stHeaderInfo._lFootCrc = calculateCRC(&_pHeader->_lOffset, 4 * _lNumRecs);
// Seek to front of file to write header info
seekToBeginning();
if (write(stHeaderInfo) == ERR_NONE) {
// Seek to start of where header is to be written
seek(_lHeaderStart);
// Write header to data file
ErrorCode errorCode = ERR_NONE;
for (int i = 0; i < _lNumRecs && errorCode == ERR_NONE; ++i) {
errorCode = write(_pHeader[i]);
}
if (errorCode == ERR_NONE) {
// Header is now clean
_bHeaderDirty = false;
} else {
logError(buildString("Error writing footer to file '%s'", _szFileName));
_errCode = ERR_FWRITE;
}
} else {
logError(buildString("Error writing header to file '%s'", _szFileName));
_errCode = ERR_FWRITE;
}
}
}
return _errCode;
}
ErrorCode CBofDataFile::readRecord(int32 lRecNum, void *pBuf) {
assert(isValidObject(this));
// Only continue if there is no current error
if (_errCode == ERR_NONE) {
// Can't write to nullptr pointers
assert(pBuf != nullptr);
// Validate record number
assert(lRecNum >= 0 && lRecNum < _lNumRecs);
// Make sure we have a valid header
assert(_pHeader != nullptr);
// Get info about address of where record starts
// and how large the record is.
HeaderRec *pRecInfo = &_pHeader[(int)lRecNum];
// Open the data file if it's not already open
if (_stream == nullptr) {
open();
}
if (_errCode == ERR_NONE) {
// Seek to that point in the file
seek(pRecInfo->_lOffset);
// Read in the record
if (read(pBuf, pRecInfo->_lLength) == ERR_NONE) {
// If this file is encrypted, then decrypt it
if (_lFlags & CDF_ENCRYPT) {
decrypt(pBuf, (int)pRecInfo->_lLength, _szPassWord);
}
// Calculate and verify this record's CRC value
uint32 lCrc = calculateCRC(pBuf, (int)pRecInfo->_lLength);
if (lCrc != pRecInfo->_lCrc) {
_errCode = ERR_CRC;
}
} else {
logError(buildString("Error reading record %d in file '%s'", lRecNum, _szFileName));
_errCode = ERR_FREAD;
}
}
}
return _errCode;
}
ErrorCode CBofDataFile::readFromFile(int32 lRecNum, void *pBuf, int32 lBytes) {
assert(isValidObject(this));
// Only continue if there is no current error
if (_errCode == ERR_NONE) {
// Can't write to nullptr pointers
assert(pBuf != nullptr);
// Validate record number
assert(lRecNum >= 0 && lRecNum < _lNumRecs);
// Make sure we have a valid header
assert(_pHeader != nullptr);
// Get info about address of where record starts
// and how large the record is.
HeaderRec *pRecInfo = &_pHeader[(int)lRecNum];
// Open the data file if it's not already open
if (_stream == nullptr) {
open();
}
if (_errCode == ERR_NONE) {
// Seek to that point in the file
seek(pRecInfo->_lOffset);
// Read in the requested bytes...
if (read(pBuf, lBytes) == ERR_NONE) {
// If this file is encrypted, then decrypt it
if (_lFlags & CDF_ENCRYPT) {
decryptPartial(pBuf, (int32)pRecInfo->_lLength, (int32)lBytes, _szPassWord);
}
// Don't bother with a CRC as this chunk of input won't generate a proper
// CRC anyway.
} else {
logError(buildString("Error reading record %u in file '%s'", lRecNum, _szFileName));
_errCode = ERR_FREAD;
}
}
}
return _errCode;
}
ErrorCode CBofDataFile::writeRecord(int32 lRecNum, void *pBuf, int32 lSize, bool bUpdateHeader, uint32 lKey) {
assert(isValidObject(this));
// Only continue if there is no current error
if (_errCode == ERR_NONE) {
// Validate record number
assert(lRecNum >= 0 && lRecNum < _lNumRecs);
// Validate input buffer
assert(pBuf != nullptr);
// There must already be a valid header
assert(_pHeader != nullptr);
if (lSize == -1)
lSize = _pHeader[(int)lRecNum]._lLength;
int32 lPrevOffset = HeadInfo::size();
int32 lPrevLength = 0;
if (lRecNum != 0) {
lPrevOffset = _pHeader[(int)lRecNum - 1]._lOffset;
lPrevLength = _pHeader[(int)lRecNum - 1]._lLength;
}
HeaderRec *pRecInfo = &_pHeader[(int)lRecNum];
// Header needs to updated
_bHeaderDirty = true;
if (_stream == nullptr) {
open();
}
// This record starts at the end of the last record
pRecInfo->_lOffset = lPrevOffset + lPrevLength;
// Seek to where we want to write this record
seek(pRecInfo->_lOffset);
// Calculate new hash code based on this records key
pRecInfo->_lKey = lKey;
if (lKey == 0xFFFFFFFF) {
pRecInfo->_lKey = CreateHashCode((const byte *)pBuf);
}
// Calculate this record's CRC value
pRecInfo->_lCrc = calculateCRC(pBuf, lSize);
if (_lFlags & CDF_ENCRYPT) {
encrypt(pBuf, lSize, _szPassWord);
}
// If new record is larger then original
if (lSize > pRecInfo->_lLength) {
// How many bytes back do we have to write?
int32 lDiff = lSize - pRecInfo->_lLength;
//
// Move the rest of file back that many bytes
//
// Read the rest of the file in chunks (of 200k or less),
// and write each chunk back in it's new position.
//
int32 lBufLength = getLength() - (pRecInfo->_lOffset + pRecInfo->_lLength);
int32 lChunkSize = MIN(lBufLength, (int32)200000);
// Allocate a buffer big enough for one chunk
byte *pTmpBuf = (byte *)bofAlloc(lChunkSize);
// While there is data to move
while (lBufLength > 0) {
// Seek to beginning of the source for this chunk
setPosition(pRecInfo->_lOffset + pRecInfo->_lLength + lBufLength - lChunkSize);
// Read the chunk
read(pTmpBuf, lChunkSize);
// Seek to this chunks new position (offset by 'lDiff' bytes)
setPosition(pRecInfo->_lOffset + pRecInfo->_lLength + lBufLength - lChunkSize + lDiff);
// Write chunk to new position
write(pTmpBuf, lChunkSize);
// That much less to do next time through
lBufLength -= lChunkSize;
// Last chunk is lBufLength
lChunkSize = MIN(lBufLength, lChunkSize);
}
// Don't need that temp buffer anymore
bofFree(pTmpBuf);
// Tell the rest of the records that they moved
for (int i = lRecNum + 1; i < getNumberOfRecs(); i++) {
_pHeader[i]._lOffset += lDiff;
}
// Remember it's new length
pRecInfo->_lLength = lSize;
// Seek to where we want to write this record
seek(pRecInfo->_lOffset);
// Write this record
write(pBuf, lSize);
// If we are to update the header now
if (bUpdateHeader) {
writeHeader();
}
} else {
// Write this record
if (write(pBuf, lSize) == ERR_NONE) {
// If this record got smaller
if (pRecInfo->_lLength > lSize) {
// Remember it's length
pRecInfo->_lLength = lSize;
int bufferSize = getMaxRecSize();
if (bufferSize <= 0)
fatalError(ERR_FREAD, "Invalid size read in header data");
// Allocate a buffer that could hold the largest record
byte *pTmpBuf = (byte *)bofAlloc(bufferSize);
for (int i = (int)lRecNum + 1; i < (int)_lNumRecs - 1; i++) {
_errCode = readRecord(i, pTmpBuf);
if (_errCode != ERR_NONE)
break;
_errCode = writeRecord(i + 1, pTmpBuf);
if (_errCode != ERR_NONE)
break;
}
bofFree(pTmpBuf);
}
// If we are to update the header now
if (bUpdateHeader) {
writeHeader();
}
} else {
_errCode = ERR_FWRITE;
}
}
// If this record is encrypted the decrypt it
if (_lFlags & CDF_ENCRYPT) {
decrypt(pBuf, (int)pRecInfo->_lLength, _szPassWord);
}
}
return _errCode;
}
ErrorCode CBofDataFile::verifyRecord(int32 lRecNum) {
assert(isValidObject(this));
if (_errCode == ERR_NONE) {
// Validate record number
assert(lRecNum >= 0 && lRecNum < _lNumRecs);
// Allocate space to hold this record
void *pBuf = bofAlloc((int)getRecSize(lRecNum));
_errCode = readRecord(lRecNum, pBuf);
bofFree(pBuf);
}
return _errCode;
}
ErrorCode CBofDataFile::verifyAllRecords() {
assert(isValidObject(this));
if (_errCode == ERR_NONE) {
int32 n = getNumberOfRecs();
for (int32 i = 0; i < n; i++) {
_errCode = verifyRecord(i);
if (_errCode != ERR_NONE) {
break;
}
}
}
return _errCode;
}
ErrorCode CBofDataFile::addRecord(void *pBuf, int32 lLength, bool bUpdateHeader, uint32 lKey) {
assert(isValidObject(this));
// Only continue if there is no current error
if (_errCode == ERR_NONE) {
// Validate input
assert(pBuf != nullptr);
assert(lLength > 0);
if (lLength > 0) {
if (_stream == nullptr) {
open();
}
if (_errCode == ERR_NONE) {
_lNumRecs++;
HeaderRec *pTmpHeader = new HeaderRec[(int)_lNumRecs];
for (int i = 0; i < _lNumRecs; ++i) {
pTmpHeader[i]._lOffset = pTmpHeader[i]._lLength = 0;
pTmpHeader[i]._lCrc = pTmpHeader[i]._lKey = 0;
}
if (_pHeader != nullptr) {
memcpy(pTmpHeader, _pHeader, (size_t)(HeaderRec::size() * (_lNumRecs - 1)));
delete[] _pHeader;
}
_pHeader = pTmpHeader;
int32 lRecNum = _lNumRecs - 1;
HeaderRec *pCurRec = &_pHeader[lRecNum];
int32 lPrevLength = HeadInfo::size();
int32 lPrevOffset = 0;
if (lRecNum != 0) {
lPrevLength = _pHeader[lRecNum - 1]._lLength;
lPrevOffset = _pHeader[lRecNum - 1]._lOffset;
}
pCurRec->_lLength = lLength;
pCurRec->_lOffset = lPrevOffset + lPrevLength;
writeRecord(lRecNum, pBuf, lLength, bUpdateHeader, lKey);
}
}
}
return _errCode;
}
int32 CBofDataFile::findRecord(uint32 lKey) {
assert(isValidObject(this));
// Assume no match
int32 lRecNum = -1;
// Only continue if there is no current error
if (_errCode == ERR_NONE) {
// Scan the header for the key matching the hash code
for (int32 i = 0; i < _lNumRecs; i++) {
// Header records must be valid
assert(_pHeader != nullptr);
if (_pHeader[i]._lKey == lKey) {
lRecNum = i;
break;
}
}
}
return lRecNum;
}
int32 CBofDataFile::getRecSize(int32 lRecNum) {
assert(isValidObject(this));
int32 lSize = -1;
// Only continue if there is no current error
if (_errCode == ERR_NONE) {
// Validate record number
assert(lRecNum >= 0 && lRecNum < _lNumRecs);
assert(_pHeader != nullptr);
lSize = _pHeader[lRecNum]._lLength;
}
return lSize;
}
int32 CBofDataFile::getMaxRecSize() const {
assert(isValidObject(this));
int32 lLargest = -1;
// Only continue if there is no current error
if (_errCode == ERR_NONE) {
// Validate header
assert(_pHeader != nullptr);
for (int i = 0; i < (int)_lNumRecs; i++) {
lLargest = MAX(lLargest, _pHeader[i]._lLength);
}
}
return lLargest;
}
void CBofDataFile::setPassword(const char *pszPassword) {
assert(isValidObject(this));
_szPassWord[0] = '\0';
if (pszPassword != nullptr) {
assert(strlen(pszPassword) < MAX_PW_LEN);
Common::strcpy_s(_szPassWord, pszPassword);
}
}
ErrorCode CBofDataFile::read(void *pDestBuf, int32 lBytes) {
return CBofFile::read(pDestBuf, lBytes);
}
ErrorCode CBofDataFile::read(HeadInfo &rec) {
byte buf[16];
ErrorCode errorCode = read(&buf[0], 16);
Common::MemoryReadStream mem(buf, 16);
Common::Serializer s(&mem, nullptr);
rec.synchronize(s);
return errorCode;
}
ErrorCode CBofDataFile::read(HeaderRec &rec) {
byte buf[16];
ErrorCode errorCode = read(&buf[0], 16);
Common::MemoryReadStream mem(buf, 16);
Common::Serializer s(&mem, nullptr);
rec.synchronize(s);
return errorCode;
}
ErrorCode CBofDataFile::write(const void *pSrcBuf, int32 lBytes) {
return CBofFile::write(pSrcBuf, lBytes);
}
ErrorCode CBofDataFile::write(HeadInfo &rec) {
byte buf[16];
Common::MemoryWriteStream mem(buf, 16);
Common::Serializer s(nullptr, &mem);
rec.synchronize(s);
return write(&buf[0], 16);
}
ErrorCode CBofDataFile::write(HeaderRec &rec) {
byte buf[16];
Common::MemoryWriteStream mem(buf, 16);
Common::Serializer s(nullptr, &mem);
rec.synchronize(s);
return write(&buf[0], 16);
}
/**
* Builds a Hash code based on a key.
* @param pKey Key
* @return Hash code
*/
uint32 CreateHashCode(const byte *pKey) {
// validate input
assert(pKey != nullptr);
uint32 lCode = ((uint32) * pKey << 24) | ((uint32) * (pKey + 1) << 16) | ((uint32) * (pKey + 2) << 8) | *(pKey + 3);
return lCode;
}
void SwapHeadInfo(HeadInfo *stHI) {
// Macintosh is big endian, so we must swap our bytes
stHI->_lNumRecs = SWAPLONG(stHI->_lNumRecs);
stHI->_lAddress = SWAPLONG(stHI->_lAddress);
stHI->_lFlags = SWAPLONG(stHI->_lFlags);
stHI->_lFootCrc = SWAPLONG(stHI->_lFootCrc);
}
void SwapHeaderRec(HeaderRec *stHR, int nRecords) {
HeaderRec *p = stHR;
for (int i = 0; i < nRecords; i++) {
p->_lOffset = SWAPLONG(p->_lOffset);
p->_lLength = SWAPLONG(p->_lLength);
p->_lCrc = SWAPLONG(p->_lCrc);
p->_lKey = SWAPLONG(p->_lKey);
p++;
}
}
} // namespace SpaceBar
} // namespace Bagel

View File

@@ -0,0 +1,245 @@
/* 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/>.
*
*/
#ifndef BAGEL_BOFLIB_DAT_FILE_H
#define BAGEL_BOFLIB_DAT_FILE_H
#include "common/serializer.h"
#include "bagel/spacebar/boflib/file.h"
namespace Bagel {
namespace SpaceBar {
#define CDF_NOFLAGS 0x00000000
#define CDF_READONLY CBF_READONLY // Open for Read-only access
#define CDF_OVERWRITE CBF_OVERWRITE // *Overwrite any existing data-file
#define CDF_SHARED CBF_SHARED // *Open for Shared access
#define CDF_CREATE CBF_CREATE // *Create new file if not exist
#define CDF_SAVEFILE CBF_SAVEFILE
#define CDF_MEMORY 0x00010000 // *header/footer should stay in memory
#define CDF_ENCRYPT 0x00020000 // Specifies if data should use encryption
#define CDF_KEEPOPEN 0x00040000 // File should be kept open after construction
#define CDF_COMPRESSED 0x00080000 // *Specifies if data should be compressed
// * = indicates feature not yet implemented
#define CDF_DEFAULT (CDF_MEMORY | CDF_ENCRYPT | CDF_SHARED | CDF_KEEPOPEN | CDF_READONLY)
#define MAX_PW_LEN 32 // Max Password length
struct HeaderRec {
public:
int32 _lOffset;
int32 _lLength;
uint32 _lCrc;
uint32 _lKey;
void synchronize(Common::Serializer &s);
static int size() {
return 16;
}
};
struct HeadInfo {
int32 _lNumRecs; // Number of records in this file
int32 _lAddress; // starting address of footer
uint32 _lFlags; // contains flags for this file
uint32 _lFootCrc; // CRC of the footer
void synchronize(Common::Serializer &s);
static int size() {
return 16;
}
};
class CBofDataFile : public CBofFile {
private:
char _szPassWord[MAX_PW_LEN];
int32 _lHeaderLength = 0;
int32 _lHeaderStart = 0;
int32 _lNumRecs = 0;
HeaderRec *_pHeader = nullptr;
bool _bHeaderDirty;
protected:
/**
* Read the header (actually a footer) from the data-file.
* @return Error return code
*/
ErrorCode readHeader();
/**
* Writes the header (actually a footer) to the data-file.
* @return Error return code
*/
ErrorCode writeHeader();
public:
/**
* Constructor
*/
CBofDataFile();
/**
* Destructor
*/
virtual ~CBofDataFile();
/**
* Initializes a CBofDataFile with specified info
* @param pszFileName Name of .DAT file
* @param lFlags Flags for open, and encryption, etc.
* @return Error return code
*/
ErrorCode setFile(const char *pszFileName, uint32 lFlags);
/**
* Free memory used by this object
* @return Error return code
*/
ErrorCode releaseFile();
/**
* Retrieves size of specified record.
* @param lRecNum Index of record to get size of
* @return Size of specified record
*/
int32 getRecSize(int32 lRecNum);
int32 getNumberOfRecs() const {
return _lNumRecs;
}
/**
* Retrieves size of the largest record.
* @return Size of largest record in the data-file
*/
int32 getMaxRecSize() const;
/**
* Opens an existing data-file, or creates a new one.
* @return Error return code
*/
ErrorCode open();
/**
* Closes current data-file, if it's not already closed
* @return Error return code
*/
ErrorCode close() override;
/**
* Destroys current data-file, if any, and starts a new empty one
* @return Error return code
*/
ErrorCode create();
/**
* Reads specified record from data-file.
* @param lRecNum Record number to read
* @param pBuf Buffer to store record
* @return Error return code
*/
ErrorCode readRecord(int32 lRecNum, void *pBuf);
/**
* Read a set number of bytes from the beginning of a file,
* don't bother with a CRC, but decrypt if necessary. This is dependent upon
* the decryption being based on a single byte ordering scheme.
*/
ErrorCode readFromFile(int32 lRecNum, void *pBuf, int32 lBytes);
/**
* Writes specified to data-file.
* @param lRecNum Record number to read
* @param pBuf Buffer to write data from
* @param lSize Size of buffer
* @param bUpdateHeader True if header is to be committed to disk
* @param lKey Hash key
* @return Error return code
*/
ErrorCode writeRecord(int32 lRecNum, void *pBuf, int32 lSize = -1, bool bUpdateHeader = false, uint32 lKey = 0xFFFFFFFF);
/**
* Verifies specified record in data-file.
* @param lRecNum Record number to verify
* @return Error return code
*/
ErrorCode verifyRecord(int32 lRecNum);
/**
* Verifies all records in this file
* @return Error return code
*/
ErrorCode verifyAllRecords();
/**
* Adds a new record to the data-file.
* @param pBuf Buffer to write data from
* @param lLength Size of buffer
* @param bUpdateHeader true if header is to be committed to disk
* @param lKey hash Key
* @return Error return code
*/
ErrorCode addRecord(void *pBuf, int32 lLength, bool bUpdateHeader = false, uint32 lKey = 0xFFFFFFFF);
/**
* Finds record by it's key.
* @param lKey Key to search records with
* @return Index of record matching key, or -1
*/
int32 findRecord(uint32 lKey);
/**
* Sets encryption password
* @param pszPassword New password
*/
void setPassword(const char *pszPassword);
const char *getPassword() const {
return _szPassWord;
}
/**
* Read from a currently open file
* @param pDestBuf Destination buffer
* @param lBytes Number of bytes
* @return Error code
*/
ErrorCode read(void *pDestBuf, int32 lBytes) override;
ErrorCode read(HeaderRec &rec);
ErrorCode read(HeadInfo &rec);
/**
* Write to a currently open file
* @param pSrcBuf Source buffer
* @param lBytes Number of bytes
* @return Error code
*/
ErrorCode write(const void *pSrcBuf, int32 lBytes) override;
ErrorCode write(HeaderRec &rec);
ErrorCode write(HeadInfo &rec);
};
} // namespace SpaceBar
} // namespace Bagel
#endif

View File

@@ -0,0 +1,51 @@
/* 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 "common/config-manager.h"
#include "common/debug.h"
#include "bagel/spacebar/boflib/debug.h"
#include "bagel/bagel.h"
namespace Bagel {
namespace SpaceBar {
CBofDebugOptions *g_pDebugOptions = nullptr;
CBofDebugOptions::CBofDebugOptions(const char *pszFileName) : CBofOptions(pszFileName) {
// Add programmer definable debug options here
ConfMan.registerDefault("AbortsOn", true);
ConfMan.registerDefault("MessageBoxOn", true);
ConfMan.registerDefault("RandomOn", true);
ConfMan.registerDefault("DebugLevel", gDebugLevel);
ConfMan.registerDefault("ShowIO", false);
ConfMan.registerDefault("MessageSpy", false);
readSetting("DebugOptions", "AbortsOn", &_bAbortsOn, ConfMan.getBool("AbortsOn"));
readSetting("DebugOptions", "MessageBoxOn", &_bMessageBoxOn, ConfMan.getBool("MessageBoxOn"));
readSetting("DebugOptions", "RandomOn", &_bRandomOn, ConfMan.getBool("RandomOn"));
readSetting("DebugOptions", "DebugLevel", &_nDebugLevel, ConfMan.getInt("DebugLevel"));
readSetting("DebugOptions", "ShowIO", &_bShowIO, ConfMan.getBool("ShowIO"));
readSetting("DebugOptions", "MessageSpy", &_bShowMessages, ConfMan.getBool("MessageSpy"));
}
} // namespace SpaceBar
} // namespace Bagel

View File

@@ -0,0 +1,55 @@
/* 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/>.
*
*/
#ifndef BAGEL_BOFLIB_DEBUG_H
#define BAGEL_BOFLIB_DEBUG_H
#include "bagel/spacebar/boflib/options.h"
namespace Bagel {
namespace SpaceBar {
/**
* Declare a debug-options (.INI) file
*/
class CBofDebugOptions : public CBofOptions {
public:
/**
* Constructor
* @param pszFileName Name of debug options file
*/
CBofDebugOptions(const char *pszFileName);
int _nDebugLevel;
bool _bAbortsOn;
bool _bMessageBoxOn;
bool _bRandomOn;
bool _bShowIO;
bool _bShowMessages;
};
extern CBofDebugOptions *g_pDebugOptions;
} // namespace SpaceBar
} // namespace Bagel
#endif

View File

@@ -0,0 +1,44 @@
/* 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/>.
*
*/
#ifndef BAGEL_BOFLIB_EVENTS_H
#define BAGEL_BOFLIB_EVENTS_H
#include "common/events.h"
namespace Bagel {
namespace SpaceBar {
/**
* Enum for custom ScummVM events
*/
enum BagelEventType {
// EVENT user stores a message num in mouse.x, and param in mouse.y
EVENT_USER = 1001,
// TIMER events
EVENT_TIMER = 1002
};
} // namespace SpaceBar
} // namespace Bagel
#endif

View File

@@ -0,0 +1,297 @@
/* 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 "common/macresman.h"
#include "common/stream.h"
#include "common/system.h"
#include "common/savefile.h"
#include "bagel/bagel.h"
#include "bagel/spacebar/boflib/file.h"
#include "bagel/boflib/file_functions.h"
#include "bagel/spacebar/boflib/debug.h"
#include "bagel/boflib/log.h"
namespace Bagel {
namespace SpaceBar {
#define CHUNK_SIZE 0x00007FFF
CBofFile::CBofFile() {
_szFileName[0] = '\0';
}
CBofFile::CBofFile(const char *pszFileName, uint32 lFlags) {
_szFileName[0] = '\0';
assert(pszFileName != nullptr);
// Open now?
if (pszFileName != nullptr) {
open(pszFileName, lFlags);
}
}
CBofFile::~CBofFile() {
assert(isValidObject(this));
close();
}
ErrorCode CBofFile::create(const char *pszFileName, uint32 lFlags) {
assert(isValidObject(this));
assert(pszFileName != nullptr);
assert(strlen(pszFileName) < MAX_DIRPATH);
assert(*pszFileName != '\0');
// Can't create a read-only file
assert(!(lFlags & CBF_READONLY));
_lFlags = lFlags;
// Remember this files name
Common::strcpy_s(_szFileName, pszFileName);
// Create the file
Common::OutSaveFile *save = g_system->getSavefileManager()->openForSaving(pszFileName, false);
if (save != nullptr) {
_stream = new SaveReadWriteStream(save);
if (g_pDebugOptions != nullptr && g_pDebugOptions->_bShowIO) {
logInfo(buildString("Creating file '%s'", _szFileName));
}
} else {
reportError(ERR_FOPEN, "Unable to create %s", _szFileName);
}
return _errCode;
}
ErrorCode CBofFile::open(const char *pszFileName, uint32 lFlags) {
assert(isValidObject(this));
assert(pszFileName != nullptr);
assert(strlen(pszFileName) < MAX_DIRPATH);
assert(*pszFileName != '\0');
// Can't open for both Text and Binary modes
assert(!((lFlags & CBF_TEXT) && (lFlags & CBF_BINARY)));
// Can't overwrite a readonly file
assert(!((lFlags & CBF_READONLY) && (lFlags & CBF_OVERWRITE)));
// Can't create a new file to be readonly (there would be nothing to read!)
assert(!((lFlags & CBF_READONLY) && (lFlags & CBF_CREATE)));
// Keep a copy of these flags
_lFlags = lFlags;
if (_stream)
return _errCode;
if ((lFlags & CBF_CREATE) && ((lFlags & CBF_SAVEFILE) ||
!fileExists(pszFileName))) {
create(pszFileName, lFlags);
} else {
// Remember this files' name
Common::strcpy_s(_szFileName, pszFileName);
if (lFlags & CBF_SAVEFILE) {
_stream = g_system->getSavefileManager()->openForLoading(pszFileName);
if (!_stream)
reportError(ERR_FOPEN, "Could not open %s", pszFileName);
} else {
if (g_engine->getPlatform() == Common::kPlatformMacintosh) {
_stream = Common::MacResManager::openFileOrDataFork(pszFileName);
} else {
_stream = SearchMan.createReadStreamForMember(pszFileName);
}
if (_stream) {
if (g_pDebugOptions != nullptr && g_pDebugOptions->_bShowIO) {
logInfo(buildString("Opened file '%s'", _szFileName));
}
} else {
reportError(ERR_FOPEN, "Could not open %s", pszFileName);
}
}
}
return _errCode;
}
ErrorCode CBofFile::close() {
assert(isValidObject(this));
if (_stream != nullptr) {
if (g_pDebugOptions != nullptr && g_pDebugOptions->_bShowIO) {
logInfo(buildString("Closed file '%s'", _szFileName));
}
delete _stream;
_stream = nullptr;
}
return ERR_NONE;
}
ErrorCode CBofFile::read(void *pDestBuf, int32 lBytes) {
assert(isValidObject(this));
assert(pDestBuf != nullptr);
assert(lBytes >= 0);
Common::SeekableReadStream *rs = dynamic_cast<Common::SeekableReadStream *>(_stream);
assert(rs);
if (!errorOccurred()) {
if (rs != nullptr) {
byte *pBuf = (byte *)pDestBuf;
while (lBytes > 0) {
int nLength = (int)MIN(lBytes, (int32)CHUNK_SIZE);
lBytes -= CHUNK_SIZE;
if ((int)rs->read(pBuf, nLength) != nLength) {
reportError(ERR_FREAD, "Unable to read %d bytes from %s", nLength, _szFileName);
}
pBuf += nLength;
}
} else {
error("Attempt to read from a file that is not open for reading: %s", _szFileName);
}
}
return _errCode;
}
ErrorCode CBofFile::write(const void *pSrcBuf, int32 lBytes) {
assert(isValidObject(this));
Common::WriteStream *ws = dynamic_cast<Common::WriteStream *>(_stream);
if (ws != nullptr) {
// As long as this file is not set for readonly, then write the buffer
if (!(_lFlags & CBF_READONLY)) {
const byte *pBuf = (const byte *)pSrcBuf;
while (lBytes > 0) {
int nLength = (int)MIN(lBytes, (int32)CHUNK_SIZE);
lBytes -= CHUNK_SIZE;
if ((int)ws->write(pBuf, nLength) != nLength) {
reportError(ERR_FWRITE, "Unable to write %d bytes to %s", nLength, _szFileName);
}
pBuf += nLength;
}
// Flush this file's buffer back out right now
commit();
} else {
logWarning(buildString("Attempted to write to the READONLY file '%s'", _szFileName));
}
} else {
logWarning("Attempt to write to a file that is not open");
}
return _errCode;
}
ErrorCode CBofFile::setPosition(uint32 lPos) {
assert(isValidObject(this));
// Only supports files up to 2Gig
assert(lPos < 0x80000000);
Common::SeekableReadStream *rs = dynamic_cast<Common::SeekableReadStream *>(_stream);
Common::SeekableWriteStream *ws = dynamic_cast<Common::SeekableWriteStream *>(_stream);
if (rs && !rs->seek(lPos)) {
reportError(ERR_FSEEK, "Unable to seek to %u in rs", lPos);
}
if (ws && !ws->seek(lPos)) {
reportError(ERR_FSEEK, "Unable to seek to %u in ws", lPos);
}
return _errCode;
}
uint32 CBofFile::getPosition() {
assert(isValidObject(this));
Common::SeekableReadStream *rs = dynamic_cast<Common::SeekableReadStream *>(_stream);
Common::SeekableWriteStream *ws = dynamic_cast<Common::SeekableWriteStream *>(_stream);
if (rs)
return rs->pos();
if (ws)
return ws->pos();
error("getPosition on closed file");
}
ErrorCode CBofFile::seekToEnd() {
assert(isValidObject(this));
Common::SeekableReadStream *rs = dynamic_cast<Common::SeekableReadStream *>(_stream);
Common::SeekableWriteStream *ws = dynamic_cast<Common::SeekableWriteStream *>(_stream);
if (rs)
rs->seek(0, SEEK_END);
else if (ws)
ws->seek(0, SEEK_END);
else
error("Seek in closed file");
return _errCode;
}
uint32 CBofFile::getLength() {
assert(isValidObject(this));
Common::SeekableReadStream *rs = dynamic_cast<Common::SeekableReadStream *>(_stream);
Common::SeekableWriteStream *ws = dynamic_cast<Common::SeekableWriteStream *>(_stream);
if (rs)
return rs->size();
if (ws)
return ws->size();
error("getLength in closed file");
}
void CBofFile::commit() {
assert(isValidObject(this));
Common::SeekableWriteStream *ws = dynamic_cast<Common::SeekableWriteStream *>(_stream);
if (ws)
ws->finalize();
}
} // namespace SpaceBar
} // namespace Bagel

View File

@@ -0,0 +1,210 @@
/* 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/>.
*
*/
#ifndef BAGEL_BOFLIB_FILE_H
#define BAGEL_BOFLIB_FILE_H
#include "common/algorithm.h"
#include "common/memstream.h"
#include "common/stream.h"
#include "bagel/boflib/stdinc.h"
#include "bagel/boflib/object.h"
#include "bagel/boflib/error.h"
namespace Bagel {
namespace SpaceBar {
#define CBF_TEXT 0x00000001
#define CBF_BINARY 0x00000002
#define CBF_READONLY 0x00000004
#define CBF_OVERWRITE 0x00000008
#define CBF_SHARED 0x00000010
#define CBF_CREATE 0x00000020
#define CBF_SAVEFILE 0x100
#define CBF_DEFAULT (CBF_BINARY | CBF_READONLY)
#define CBOFFILE_TEXT CBF_TEXT
#define CBOFFILE_READONLY CBF_READONLY
#define CBOFFILE_OVERWRITE CBF_OVERWRITE
#define CBOFFILE_DEFAULT CBF_DEFAULT
class SaveReadStream : public Common::SeekableReadStream {
private:
Common::MemoryWriteStreamDynamic *_owner;
public:
SaveReadStream(Common::MemoryWriteStreamDynamic *owner) : _owner(owner) {
}
bool eos() const override {
return _owner->pos() >= _owner->size();
}
uint32 read(void *dataPtr, uint32 dataSize) override {
int bytesToCopy = MIN<int>(dataSize, _owner->size() - _owner->pos());
const byte *src = _owner->getData() + _owner->pos();
Common::copy(src, src + bytesToCopy, (byte *)dataPtr);
seek(bytesToCopy, SEEK_CUR);
return bytesToCopy;
}
};
/**
* Used as a wrapper for writing out original saves using the console,
* since it also does reads from the stream whilst open
*/
class SaveReadWriteStream : public Common::MemoryWriteStreamDynamic, SaveReadStream {
private:
Common::WriteStream *_save;
public:
SaveReadWriteStream(Common::WriteStream *save) :
Common::MemoryWriteStreamDynamic(DisposeAfterUse::YES),
SaveReadStream(this), _save(save) {
}
~SaveReadWriteStream() {
_save->write(getData(), Common::MemoryWriteStreamDynamic::size());
delete _save;
}
int64 pos() const override {
return Common::MemoryWriteStreamDynamic::pos();
}
bool seek(int64 offset, int whence = SEEK_SET) override {
return Common::MemoryWriteStreamDynamic::seek(offset, whence);
}
int64 size() const override {
return Common::MemoryWriteStreamDynamic::size();
}
};
class CBofFile : public CBofObject, public CBofError {
protected:
char _szFileName[MAX_FNAME];
Common::Stream *_stream = nullptr;
uint32 _lFlags = CBF_DEFAULT;
public:
/**
* Default constructor
*/
CBofFile();
/**
* Open a specified file for access
* @param pszFileName Filename
* @param lFlags Access flags
*/
CBofFile(const char *pszFileName, uint32 lFlags = CBF_DEFAULT);
/**
* Destructor
*/
virtual ~CBofFile();
/**
* Open specified file into this object
* @param pszFileName Filename
* @param lFlags Access flags
*/
ErrorCode open(const char *pszFileName, uint32 lFlags = CBF_DEFAULT);
/**
* Creates specified file
* @param pszFileName Filename
* @param lFlags Access flags
*/
ErrorCode create(const char *pszFileName, uint32 lFlags = CBF_DEFAULT | CBF_CREATE);
/**
* Close a currently open file
*/
virtual ErrorCode close();
/**
* Read from a currently open file
* @param pDestBuf Destination buffer
* @param lBytes Number of bytes
* @return Error code
*/
virtual ErrorCode read(void *pDestBuf, int32 lBytes);
/**
* Write to a currently open file
* @param pSrcBuf Source buffer
* @param lBytes Number of bytes
* @return Error code
*/
virtual ErrorCode write(const void *pSrcBuf, int32 lBytes);
/**
* Flushes I/O stream
*/
void commit();
/**
* Seek to a specified location in the file
* @return Error code
*/
ErrorCode seek(uint32 lPos) {
return (setPosition(lPos));
}
/**
* Sets the file pointer to the beginning of the file
* @return Error code
*/
ErrorCode seekToBeginning() {
return (setPosition(0));
}
/**
* Sets the file pointer to the end of the file
* @return Error code
*/
ErrorCode seekToEnd();
/**
* Sets the current file-seek position to that specified
* @param lPos New position
*/
ErrorCode setPosition(uint32 lPos);
/**
* Retrieves the current seek position
*/
uint32 getPosition();
/**
* Get the length of a file
*/
uint32 getLength();
operator Common::SeekableReadStream *() const {
return dynamic_cast<Common::SeekableReadStream *>(_stream);
}
};
} // namespace SpaceBar
} // namespace Bagel
#endif

View File

@@ -0,0 +1,107 @@
/* 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/>.
*
*/
#ifndef BAGEL_BOFLIB_FIXED_H
#define BAGEL_BOFLIB_FIXED_H
#include "bagel/boflib/object.h"
#include "bagel/boflib/misc.h"
namespace Bagel {
namespace SpaceBar {
class CBofFixed : public CBofObject {
private:
Fixed _lVal;
CBofFixed(const Fixed Arg) {
_lVal = Arg;
}
public:
// Constructors
CBofFixed() {
_lVal = 0L;
}
CBofFixed(const CBofFixed &Arg) {
_lVal = Arg._lVal;
}
CBofFixed(const int Arg) {
_lVal = (Fixed)(((long)(Arg)) << 16);
}
CBofFixed(const double Arg) {
_lVal = (Fixed)(Arg * (1 << 16));
}
// Operators
// inline CBofFixed operator =(const CBofFixed& Arg) const;
CBofFixed operator+(const CBofFixed &Arg) const {
return _lVal + Arg._lVal;
}
CBofFixed operator-(const CBofFixed &Arg) const {
return _lVal - Arg._lVal;
}
CBofFixed operator*(const CBofFixed &Arg) const {
return fixedMultiply(_lVal, Arg._lVal);
}
CBofFixed operator/(const CBofFixed &Arg) const {
return fixedDivide(_lVal, Arg._lVal);
}
CBofFixed operator-=(const CBofFixed &Arg) {
_lVal -= Arg._lVal;
return *this;
}
CBofFixed operator+=(const CBofFixed &Arg) {
_lVal += Arg._lVal;
return *this;
}
CBofFixed operator*=(const CBofFixed &Arg) {
_lVal = _lVal * Arg._lVal;
return *this;
}
CBofFixed operator/=(const CBofFixed &Arg) {
_lVal = _lVal / Arg._lVal;
return *this;
}
CBofFixed operator=(const CBofFixed &Arg) {
_lVal = Arg._lVal;
return *this;
}
// Conversion operators
operator int() {
return (int)(((int32)_lVal) >> 16);
}
};
} // namespace SpaceBar
} // namespace Bagel
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,446 @@
/* 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/>.
*
*/
#ifndef BAGEL_BOFLIB_GFX_BMP_H
#define BAGEL_BOFLIB_GFX_BMP_H
#include "graphics/managed_surface.h"
#include "bagel/boflib/palette.h"
#include "bagel/boflib/cache.h"
#include "bagel/boflib/error.h"
#include "bagel/boflib/object.h"
#include "bagel/boflib/point.h"
#include "bagel/boflib/rect.h"
#include "bagel/boflib/size.h"
#include "bagel/boflib/stdinc.h"
namespace Bagel {
namespace SpaceBar {
// Color constants
//
enum {
NOT_TRANSPARENT = -1,
COLOR_WHITE = 255,
COLOR_BLACK = 0
};
#define CBMP_FADE_SPEED 10
#define CBMP_FADE_SIZE 4
#define CBMP_CURT_SPEED 8 // Must be a power of 2
#define CBMP_LINE_SPEED 32 // Should be a power of 4
// forward declare CBofWindow
class CBofWindow;
class CBofBitmap : public CBofError, public CBofObject, public CCache {
protected:
/**
* Does the actual allocation for this bitmap
* @return true is this bitmap was successfully loaded into the cache
*/
bool alloc() override;
/**
* Frees the data used by this bitmap (removes from cache)
*/
void free() override;
//
// data members
//
static bool _bUseBackdrop;
char _szFileName[MAX_FNAME];
Graphics::ManagedSurface _bitmap;
byte *_pBits = nullptr;
CBofPalette *_pPalette = nullptr;
int _nScanDX = 0;
int _nDX = 0;
int _nDY = 0;
bool _bTopDown = false;
bool _bOwnPalette = false;
bool _bReadOnly = false;
bool _bInitialized = false;
public:
/**
* Default constructor
*/
CBofBitmap();
/**
* Constructs a CBofBitmap
* @param dx Width of new bitmap
* @param dy Height of new bitmap
* @param pPalette Palette to use for this bitmap
* @param bOwnPalette true if destructor should delete palette
* @param pPrivateBuff
*/
CBofBitmap(int dx, int dy, CBofPalette *pPalette, bool bOwnPalette = false, byte *pPrivateBuff = nullptr);
/**
* Constructs a CBofBitmap
* @param pszFileName Path and Filename for Bitmap on disk
* @param pPalette Palette to use for this bitmap
* @param bOwnPalette true if destructor should delete palette
*/
CBofBitmap(const char *pszFileName, CBofPalette *pPalette = nullptr, bool bOwnPalette = false);
/**
* Destructor
*/
virtual ~CBofBitmap();
/**
* Allocates the structures needed for a CBofBitmap
* @param pPalette Palette to be assigned into this bitmap
*/
ErrorCode buildBitmap(CBofPalette *pPalette);
/**
* Loads the specified bitmap from disk
* @param pszFileName Filename
* @param pPalette Palette
* @return Error return code
*/
ErrorCode loadBitmap(const char *pszFileName, CBofPalette *pPalette);
/**
* Frees the data used by this bitmap
*/
void releaseBitmap();
//
// Palette routines
//
/**
* Assigns specified palette to this bitmap
* @param pPalette Pointer to CBofPalette to be assigned
* @param bOwnPalette true if bitmap is to own this palette
*/
void setPalette(CBofPalette *pPalette, bool bOwnPalette = false);
CBofPalette *getPalette() {
return _pPalette;
}
void setIsOwnPalette(bool own) {
_bOwnPalette = own;
}
//
// Misc routines
//
/**
* Returns the bit address of the (x, y) location in this bmp
* @param x Column in _pBits
* @param y Row in _pBits
* @return Address of (x,y) in bitmap surface
*/
byte *getPixelAddress(int x, int y);
byte *getPixelAddress(CBofPoint *pPoint) {
return getPixelAddress(pPoint->x, pPoint->y);
}
CBofSize getSize() {
return CBofSize(_nDX, _nDY);
}
CBofRect getRect() {
return CBofRect(0, 0, _nDX - 1, _nDY - 1);
}
void setReadOnly(bool bReadOnly) {
_bReadOnly = bReadOnly;
}
bool getReadOnly() {
return _bReadOnly;
}
bool isTopDown() {
return _bTopDown;
}
int width() {
return _nDX;
}
int widthBytes() {
return _nScanDX;
}
int height() {
return _nDY;
}
operator Graphics::ManagedSurface &() {
return _bitmap;
}
Graphics::ManagedSurface getSurface();
/**
* Returns current bitmap's filename (if any)
* @return Pointer to bitmap's filename
*/
const char *getFileName();
//
// Drawing routines
//
/**
* Paints some or all of the bitmap directly to the screen
* @param pWnd Destination device for painting
* @param x Destination column
* @param y Destination row
* @param pSrcRect Source rectangle from bitmap
* @param nMaskColor Transparency color
* @return error return code
*/
ErrorCode paint(CBofWindow *pWnd, int x, int y, CBofRect *pSrcRect = nullptr, int nMaskColor = NOT_TRANSPARENT);
/**
* Paints some or all of the bitmap directly to the screen
* @param pWnd Destination Device to paint to
* @param pDstRect Destination rectangle (for stretching)
* @param pSrcRect Source rectangle from bitmap
* @param nMaskColor transparency color
* @return Error return code
*/
ErrorCode paint(CBofWindow *pWnd, CBofRect *pDstRect = nullptr, CBofRect *pSrcRect = nullptr, int nMaskColor = NOT_TRANSPARENT);
/**
* Paints some or all of the bitmap directly to the screen
* @param pWnd Destination Device to paint to
* @param pDstRect Destination rectangle (for stretching)
* @param pSrcRect Source rectangle from bitmap
* @param nMaskColor Transparency color
* @return Error return code
*/
ErrorCode paintMaskBackdrop(CBofWindow *pWnd, CBofRect *pDstRect = nullptr, CBofRect *pSrcRect = nullptr, int nMaskColor = NOT_TRANSPARENT);
/**
* Paints some or all of the bitmap directly to the screen
* @param pBmp Destination bitmap to paint to
* @param x Destination column
* @param y Destination row
* @param pSrcRect Source rectangle from bitmap
* @param nMaskColor Transparency color
* @return Error return code
*/
ErrorCode paint(CBofBitmap *pBmp, int x, int y, CBofRect *pSrcRect = nullptr, int nMaskColor = NOT_TRANSPARENT);
/**
* Paints some or all of the bitmap directly to the screen
* @param pBmp Destination bitmap to paint to
* @param pDstRect Destination rectangle (for stretching)
* @param pSrcRect Source rectangle from bitmap
* @param nMaskColor Transparency color
* @return Error return code.
*/
ErrorCode paint(CBofBitmap *pBmp, CBofRect *pDstRect = nullptr, CBofRect *pSrcRect = nullptr, int nMaskColor = NOT_TRANSPARENT);
//
// Special Paint routines Optimized for specific tasks
//
// Stretches 4 pixel wide
/**
* Stretches 4 pixel wide strips from source to destination
* @brief The Destination rectangle MUST be divisible by 4.
* Both bitmaps must be Bottom-Up. The Source must be smaller than the Destination.
*
* @param pBmp Destination bitmap to paint to
* @param pDstRect Destination rectangle (for stretching)
* @param pSrcRect Source rectangle from bitmap
* @return Error return code
*/
ErrorCode paintStretch4(CBofBitmap *pBmp, CBofRect *pDstRect, CBofRect *pSrcRect);
/**
* Stretches a multiple of 4 pixel wide strips from source to destination
* @param pBmp Destination bitmap to paint to
* @param pDstRect Destination rectangle (for stretching)
* @param pSrcRect Source rectangle from bitmap
* @param nOptSize
* @return Error return code
*/
ErrorCode paintStretchOpt(CBofBitmap *pBmp, CBofRect *pDstRect, CBofRect *pSrcRect, int nOptSize);
/**
* Paints some or all of the bitmap directly to the screen
* @param pBmp Destination bitmap to paint to
* @return Error return code
*/
ErrorCode paint1To1(CBofBitmap *pBmp);
/** Copy specified section of screen (or window) to bitmap.
* @param pWnd Window to capture
* @param pSrcRect Source rectangle in window
* @param pDstRect Destination area to copy image to
* @return Error return code
*/
ErrorCode captureScreen(CBofWindow *pWnd, CBofRect *pSrcRect, CBofRect *pDstRect = nullptr);
/**
* Performs a "Fade" onto the specified window
* @param pWnd Pointer to window to fade into
* @param x Fade upper left X
* @param y Fade upper left Y
* @param nMaskColor Transparency color (if any)
* @param nBlockSize Size of Fade Blocks
* @param nSpeed Speed for fade (not implemented yet)
* @return Error return code
*/
ErrorCode fadeIn(CBofWindow *pWnd, int x = 0, int y = 0, int nMaskColor = NOT_TRANSPARENT, int nBlockSize = CBMP_FADE_SIZE, int nSpeed = CBMP_FADE_SPEED);
ErrorCode curtain(CBofWindow *pWnd, int nSpeed = CBMP_CURT_SPEED, int nMaskColor = NOT_TRANSPARENT);
ErrorCode fadeLines(CBofWindow *pWnd, int nSpeed = CBMP_LINE_SPEED, int nMaskColor = NOT_TRANSPARENT);
/**
* Returns the color at the (x, y) location in this bmp
* @param x X position
* @param y Y position
* @return Color Index of specified (x,y) location in _pBits
*/
byte readPixel(int x, int y);
/**
* Assigns the specified color to the (x, y) location
* @param x X position
* @param y Y position
* @param iColor Pixel value
*/
void writePixel(int x, int y, byte iColor);
/**
* Writes a circle into this bitmap
* @param x X center position
* @param y Y center position
* @param nRadius Radius of circle
* @param iColor Pixel value
*/
void circle(int x, int y, uint16 nRadius, byte iColor);
/**
* Writes a line into this bitmap
* @param nSrcX Endpoint 1 x
* @param nSrcY Endpoint 1 y
* @param nDstX Endpoint 2 x
* @param nDstY Endpoint 2 y
* @param iColor Pixel value
*/
void line(int nSrcX, int nSrcY, int nDstX, int nDstY, byte iColor);
/**
* Writes a line into this bitmap
* @param pSrc Endpoint 1
* @param pDest Endpoint 2
* @param iColor Pixel value
*/
void line(CBofPoint *pSrc, CBofPoint *pDest, byte iColor);
/**
* Writes a Rectangle into this bitmap
* @param cRect Pointer to rectangle Coordinates
* @param iColor Color of rectangle
*/
void drawRect(CBofRect *cRect, byte iColor);
/**
* Writes a filled in Rectangle to this bitmap
* @param cRect Pointer to rectangle Coordinates
* @param iColor Color of rectangle
*/
void fillRect(CBofRect *cRect, byte iColor);
/**
* Scrolls current bitmap horizontally
* @param nPixels Number of pixels to scroll by
* @param pRect Section of bitmap to scroll
* @return Error return code
*/
ErrorCode scrollRight(int nPixels, CBofRect *pRect = nullptr);
ErrorCode scrollLeft(int nPixels, CBofRect *pRect = nullptr) {
return scrollRight(-nPixels, pRect);
}
/**
* Scrolls current bitmap vertically
* @param nPixels Number of pixels to scroll by
* @return Error return code
*/
ErrorCode scrollUp(int nPixels);
static void setUseBackdrop(bool b) {
_bUseBackdrop = b;
}
static bool getUseBackdrop() {
return _bUseBackdrop;
}
};
//////////////////////////////////////////////////////////////////////////////
//
// Misc graphics routines
//
//////////////////////////////////////////////////////////////////////////////
/**
* Loads specified bitmap (and possibly re-maps to palette)
* @param pszFileName Bitmap to open
* @param pPalette Palette for re-mapping
* @param bSharedPal Shared palette flag
* @return Pointer to bitmap
*/
extern CBofBitmap *loadBitmap(const char *pszFileName, CBofPalette *pPalette = nullptr, bool bSharedPal = false);
/**
* Paints specified bitmap to specified window
* @param pWindow Window to paint to
* @param pszFileName Bitmap filename
* @param pDstRect Destination area to paint to
* @param pSrcRect Source area to paint from
* @param pPalette Optional palette to re-map with
* @param nMaskColor Optional transparent color
* @return Error return code
*/
extern ErrorCode paintBitmap(CBofWindow *pWindow, const char *pszFileName, CBofRect *pDstRect = nullptr,
CBofRect *pSrcRect = nullptr, CBofPalette *pPalette = nullptr, int nMaskColor = NOT_TRANSPARENT);
} // namespace SpaceBar
} // namespace Bagel
#endif

View File

@@ -0,0 +1,72 @@
/* 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 "graphics/cursorman.h"
#include "bagel/spacebar/boflib/gfx/cursor.h"
#include "bagel/boflib/stdinc.h"
namespace Bagel {
namespace SpaceBar {
void CBofCursor::initialize() {
show();
}
CBofCursor::~CBofCursor() {
assert(isValidObject(this));
unLoad();
}
ErrorCode CBofCursor::load() {
assert(isValidObject(this));
// kill any previous cursor
unLoad();
return _errCode;
}
ErrorCode CBofCursor::unLoad() {
assert(isValidObject(this));
return _errCode;
}
ErrorCode CBofCursor::set() {
assert(isValidObject(this));
return _errCode;
}
// TODO: This controlled the "Windows cursor" in the original game
// ScummVM doesn't have one, so just show the arrow cursor or
// use CursorMan accordingly
void CBofCursor::hide() {
//CursorMan.showMouse(false);
}
void CBofCursor::show() {
CursorMan.showMouse(true);
}
} // namespace SpaceBar
} // namespace Bagel

View File

@@ -0,0 +1,51 @@
/* 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/>.
*
*/
#ifndef BAGEL_BOFLIB_GFX_CURSOR_H
#define BAGEL_BOFLIB_GFX_CURSOR_H
#include "bagel/boflib/error.h"
#include "bagel/boflib/object.h"
namespace Bagel {
namespace SpaceBar {
class CBofCursor : public CBofObject, public CBofError {
public:
CBofCursor() {
}
~CBofCursor();
static void initialize();
ErrorCode load();
ErrorCode unLoad();
ErrorCode set();
static void show();
static void hide();
};
} // namespace SpaceBar
} // namespace Bagel
#endif

View File

@@ -0,0 +1,729 @@
/* 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 "bagel/spacebar/boflib/gfx/sprite.h"
#include "bagel/boflib/misc.h"
namespace Bagel {
namespace SpaceBar {
CBofRect *CBofSprite::_cDirtyRect;
CBofSprite *CBofSprite::_pSpriteChain = nullptr; // Pointer to chain of linked sprites
CBofSprite *CBofSprite::_pTouchedSprite = nullptr; // Pointer to sprite overlapped during painting
CBofBitmap *CBofSprite::_pWorkBmp = nullptr; // Offscreen work area
CBofPalette *CBofSprite::_pSharedPalette = nullptr; // Shared palette for ALL sprites
int CBofSprite::_nWorkDX = 0;
int CBofSprite::_nWorkDY = 0;
void CBofSprite::initialize() {
_cDirtyRect = new CBofRect();
_pSpriteChain = nullptr;
_pTouchedSprite = nullptr;
_pWorkBmp = nullptr;
_pSharedPalette = nullptr;
_nWorkDX = 0;
_nWorkDY = 0;
}
void CBofSprite::shutdown() {
delete _cDirtyRect;
}
void CBofSprite::openLibrary(CBofPalette *pPal) {
// Must have a valid palette to do any sprite related stuff
assert(pPal != nullptr);
clearDirtyRect();
setSharedPalette(pPal);
// Set up a default work area
setupWorkArea(200, 200);
}
void CBofSprite::closeLibrary() {
flushSpriteChain();
tearDownWorkArea();
_pSharedPalette = nullptr;
}
CBofSprite::CBofSprite() {
_pImage = nullptr; // No initial bitmap image for the sprite
_cSize = CBofSize(0, 0); // There is no size to the sprite image
_cRect.setRectEmpty(); // Rectangular bounds not yet defined
_cImageRect = _cRect; // Image rectangle starts same as display bounds
_cPosition = CBofPoint(0, 0); // Default position to upper left corner of display
_bPositioned = false; // Not yet positioned
_bDuplicated = false; // Not sharing resources with other sprites
_nZOrder = SPRITE_TOPMOST; // Default to top most in fore/back ground order
_nCelCount = 1; // Number of frames in animated cel strip
_nCelID = _nCelCount - 1; // Cel identifier not pointing at a cel
_bAnimated = false; // Not initially animated
_bLinked = false; // Not initially linked into the sprite chain
_nMaskColor = NOT_TRANSPARENT; // Default to NO transparency
_bReadOnly = true;
setBlockAdvance(false); // Default always advance next sprite
}
CBofSprite::~CBofSprite() {
assert(isValidObject(this));
unlinkSprite();
clearImage(); // Clear the sprite image bitmap and context
}
void CBofSprite::linkSprite() {
assert(isValidObject(this));
if (!_bLinked) {
// Set for linked into chain
_bLinked = true;
if (_pSpriteChain != nullptr) {
switch (_nZOrder) {
case SPRITE_TOPMOST:
_pSpriteChain->addToTail(this);
break;
case SPRITE_HINDMOST:
_pSpriteChain->addToHead(this);
_pSpriteChain = this;
break;
default: {
CBofSprite *pSprite;
CBofSprite *pLastSprite = pSprite = _pSpriteChain;
while (pSprite != nullptr && pSprite->_nZOrder > _nZOrder) {
pLastSprite = pSprite;
pSprite = (CBofSprite *)pSprite->_pNext;
}
pLastSprite->Insert(this);
break;
}
}
} else {
_pSpriteChain = this;
}
// _pSpriteChain must always point to the head of the linked list
assert(_pSpriteChain == (CBofSprite *)_pSpriteChain->getHead());
}
}
void CBofSprite::unlinkSprite() {
assert(isValidObject(this));
if (_bLinked) {
// Set for not linked into chain
_bLinked = false;
if (_pSpriteChain == this)
_pSpriteChain = (CBofSprite *)_pNext;
Delete();
}
}
void CBofSprite::flushSpriteChain() {
CBofSprite *pSprite = getSpriteChain();
// Cycle getting head of chain, un-linking it and then deleting it
while (pSprite != nullptr) {
pSprite->unlinkSprite();
delete pSprite;
pSprite = getSpriteChain();
}
}
void CBofSprite::setupWorkArea(int dx, int dy) {
// Do we already have a work area?
if (_pWorkBmp != nullptr) {
// Yes, so lets tear it down before we start a new one
tearDownWorkArea();
}
// Create an offscreen bitmap where we do all the work;
_pWorkBmp = new CBofBitmap(dx, dy, _pSharedPalette);
_nWorkDX = dx;
_nWorkDY = dy;
}
void CBofSprite::tearDownWorkArea() {
delete _pWorkBmp;
_pWorkBmp = nullptr;
}
CBofSprite *CBofSprite::duplicateSprite() {
assert(isValidObject(this));
// Create an object for the sprite
CBofSprite *pSprite = new CBofSprite;
duplicateSprite(pSprite);
return pSprite;
}
void CBofSprite::duplicateSprite(CBofSprite *pSprite) {
if (!isValidObject(this) || (pSprite == nullptr))
error("duplicateSprite - Invalid source or destination sprite");
pSprite->_pImage = _pImage;
pSprite->_cRect = _cRect;
pSprite->_cImageRect = _cImageRect;
pSprite->_cSize = _cSize;
pSprite->_cPosition = _cPosition;
pSprite->_nZOrder = _nZOrder;
pSprite->_nCelID = _nCelID;
pSprite->_nCelCount = _nCelCount;
pSprite->_bAnimated = _bAnimated;
pSprite->_nMaskColor = _nMaskColor;
pSprite->_bDuplicated = true; // Mark it as a sprite with shared resources
}
bool CBofSprite::loadSprite(const char *pszPathName, int nCels) {
assert(isValidObject(this));
assert(pszPathName != nullptr);
assert(nCels >= 1);
// Create an object for the sprite's image
CBofBitmap *pBitmap = new CBofBitmap(pszPathName, _pSharedPalette);
return loadSprite(pBitmap, nCels);
}
bool CBofSprite::loadSprite(CBofBitmap *pBitmap, int nCels) {
assert(isValidObject(this));
// Can't load an invalid bitmap
assert(pBitmap != nullptr);
assert(nCels >= 1);
clearImage(); // Clear out any/all existing bitmaps, palettes,
_pImage = pBitmap; // Save pointer to bitmap
pBitmap->setReadOnly(_bReadOnly);
_cSize = pBitmap->getSize();
_cRect.setRect(0, 0, _cSize.cx - 1, _cSize.cy - 1);
_cImageRect.setRect(0, 0, _cSize.cx - 1, _cSize.cy - 1);
_nCelCount = 1;
_nCelID = _nCelCount - 1;
if (nCels != 1) {
setupCels(nCels);
// Assume it's animated
_bAnimated = true;
}
return true; // Return success
}
bool CBofSprite::setupCels(const int nCels) {
assert(isValidObject(this));
assert(nCels > 0);
_nCelCount = nCels; // Set cel count
_nCelID = _nCelCount - 1; // No current cel
int nStripWidth = _cSize.cx; // Temp place toRetain cell strip pixel length
_cSize.cx /= nCels; // Calculate width of a cel
if (_cSize.cx * nCels == nStripWidth) { // Verify we have an even multiple
_cRect.right = _cRect.left + _cSize.cx; // Reset sprite rectangular bounds
_cRect.bottom = _cRect.top + _cSize.cy; // ... based on cel dimensions
_cImageRect.setRect(0, 0, _cSize.cx - 1, _cSize.cy - 1); // Set bounds for first cel in strip
return true;
}
return false;
}
void CBofSprite::nextCel() {
assert(isValidObject(this));
// verify old cel id
assert(_nCelID >= 0 && _nCelID < _nCelCount);
if (getBlockAdvance() == false) {
if (++_nCelID >= _nCelCount)
_nCelID = 0;
setCel(_nCelID);
}
}
void CBofSprite::prevCel() {
assert(isValidObject(this));
// verify old cel id
assert(_nCelID >= 0 && _nCelID < _nCelCount);
if (--_nCelID < 0)
_nCelID = _nCelCount - 1;
setCel(_nCelID);
}
bool CBofSprite::paintSprite(CBofWindow *pWnd, const int x, const int y) {
assert(isValidObject(this));
// Can't paint to a non-existent window
assert(pWnd != nullptr);
// The window MUST have a backdrop
assert(pWnd->getBackdrop() != nullptr);
batchPaint(x, y);
updateDirtyRect(pWnd, this);
return !errorOccurred();
}
bool CBofSprite::paintSprite(CBofBitmap *pBmp, const int x, const int y) {
assert(isValidObject(this));
// Can't paint to a non-existent window
assert(pBmp != nullptr);
batchPaint(x, y);
updateDirtyRect(pBmp, this);
return !errorOccurred();
}
bool CBofSprite::paintCel(CBofWindow *pWnd, int nCelId, const int x, const int y) {
setCel(nCelId - 1);
return paintSprite(pWnd, x, y);
}
bool CBofSprite::paintCel(CBofBitmap *pBmp, int nCelId, const int x, const int y) {
setCel(nCelId - 1);
return paintSprite(pBmp, x, y);
}
void CBofSprite::batchPaint(const int x, const int y) {
assert(isValidObject(this));
CBofRect cDstRect;
// Default to no sprite being overlapped by this painting operation
_pTouchedSprite = nullptr;
// Calculate destination rectangle
cDstRect.setRect(x, y, x + _cSize.cx - 1, y + _cSize.cy - 1);
// Add the destination position to the dirty rectangle list
addToDirtyRect(&cDstRect);
// If the sprite is already on screen, then we must also add it's old
// current location to the dirty rect list so that it is erase properly
if (_bPositioned) {
addToDirtyRect(&_cRect);
}
// Now establish the sprite's new position
setPosition(x, y);
if (_bAnimated && (_nCelCount > 1))
// Advance to the next cel in the strip
nextCel();
}
bool CBofSprite::updateDirtyRect(CBofWindow *pWnd, CBofSprite *pPrimarySprite) {
assert(pWnd != nullptr);
// The window MUST have a backdrop associated with it. If that's not feasible, then
// use CSprites instead of CBofSprites
assert(pWnd->getBackdrop() != nullptr);
//
// Repaint the contents of the specified rectangle
//
CBofBitmap *pBackdrop = pWnd->getBackdrop();
if (pBackdrop != nullptr) {
CBofRect *pRect = _cDirtyRect;
if (pRect->width() != 0 && pRect->height() != 0) {
// Need a work area
CBofBitmap *pWork = _pWorkBmp;
int dx = pRect->width();
int dy = pRect->height();
bool bTempWorkArea = false;
if ((pWork == nullptr) || (dx > _nWorkDX) || (dy > _nWorkDY)) {
bTempWorkArea = true;
pWork = new CBofBitmap(dx, dy, _pSharedPalette);
}
pWork->lock();
// Paint the background into the work area
pBackdrop->paint(pWork, 0, 0, pRect);
// Only need to search the sprite list if current sprite is linked
CBofSprite *pSprite = pPrimarySprite;
if (pPrimarySprite == nullptr || pPrimarySprite->_bLinked) {
pSprite = _pSpriteChain;
}
CBofRect cRect, cSrcRect;
// Run through the sprite list
while (pSprite != nullptr) {
// and paint each partial sprite overlap to the work area
if (pSprite->_bPositioned && cRect.intersectRect(&pSprite->_cRect, pRect)) {
if (pPrimarySprite != pSprite)
_pTouchedSprite = pSprite;
cSrcRect = cRect - pSprite->_cRect.topLeft();
cSrcRect += pSprite->_cImageRect.topLeft();
cRect -= pRect->topLeft();
pSprite->_pImage->paint(pWork, &cRect, &cSrcRect, pSprite->_nMaskColor);
}
pSprite = (CBofSprite *)pSprite->_pNext;
}
// Paint final outcome to the screen
cSrcRect.setRect(0, 0, pRect->width() - 1, pRect->height() - 1);
pWork->paint(pWnd, pRect, &cSrcRect);
pWork->unlock();
if (bTempWorkArea) {
delete pWork;
}
}
}
clearDirtyRect();
return true;
}
bool CBofSprite::updateDirtyRect(CBofBitmap *pBmp, CBofSprite *pPrimarySprite) {
assert(pBmp != nullptr);
//
// Repaint the contents of the specified rectangle
//
CBofRect *pRect = getDirtyRect();
// Only need to search the sprite list if current sprite is linked
CBofSprite *pSprite = pPrimarySprite;
if (pPrimarySprite == nullptr || pPrimarySprite->_bLinked) {
pSprite = _pSpriteChain;
}
CBofRect cRect;
// Run through the sprite list
while (pSprite != nullptr) {
// and paint each partial sprite overlap to the work area
if (pSprite->_bPositioned && cRect.intersectRect(&pSprite->_cRect, pRect)) {
if (pPrimarySprite != pSprite)
_pTouchedSprite = pSprite;
CBofRect cSrcRect = cRect - pSprite->_cRect.topLeft();
cSrcRect += pSprite->_cImageRect.topLeft();
pSprite->_pImage->paint(pBmp, &cRect, &cSrcRect, pSprite->_nMaskColor);
}
pSprite = (CBofSprite *)pSprite->_pNext;
}
clearDirtyRect();
return true;
}
void CBofSprite::addToDirtyRect(CBofRect *pRect) {
assert(pRect != nullptr);
CBofRect cRect;
if (_cDirtyRect->isRectEmpty()) {
cRect = *pRect;
} else {
cRect.unionRect(_cDirtyRect, pRect);
}
*_cDirtyRect = cRect;
}
void CBofSprite::setCel(const int nCelID) {
assert(isValidObject(this));
// All sprites must have at least 1 frame
assert(_nCelCount > 0);
if (_nCelID != nCelID) {
_nCelID = nCelID % _nCelCount;
if ((_nCelID != 0) && (nCelID < 0)) {
_nCelID = _nCelCount + _nCelID;
}
}
// Verify new cel id
assert(_nCelID >= 0 && _nCelID < _nCelCount);
_cImageRect.left = _nCelID * _cSize.cx;
_cImageRect.right = _cImageRect.left + _cSize.cx;
}
bool CBofSprite::eraseSprite(CBofWindow *pWnd) {
assert(isValidObject(this));
assert(pWnd != nullptr);
batchErase();
updateDirtyRect(pWnd);
return !errorOccurred();
}
void CBofSprite::batchErase() {
if (_bPositioned) {
_bPositioned = false;
addToDirtyRect(&_cRect);
}
}
bool CBofSprite::testInterception(CBofSprite *pTestSprite, CBofPoint *pPoint) {
assert(isValidObject(this));
assert(pTestSprite != nullptr);
// Punt if no interception allowed
if (pTestSprite != nullptr) {
// be sure to not test against ourself
if (this != pTestSprite) {
CBofRect overlapRect; // Area of overlap between rectangles
// Use simple rectangle screening first
if (overlapRect.intersectRect(&_cRect, &pTestSprite->_cRect)) {
// ... and if that succeeds, see if we
// ... have image masks that overlap
if ((_nMaskColor == NOT_TRANSPARENT) || (pTestSprite->_nMaskColor == NOT_TRANSPARENT) || spritesOverlap(pTestSprite, pPoint)) {
return true;
}
}
}
}
return false;
}
CBofSprite *CBofSprite::interception(CBofRect *pNewRect, CBofSprite *pTestSprite) {
assert(isValidObject(this));
assert(pNewRect != nullptr);
// Get first sprite to be tested
CBofSprite *pSprite = pTestSprite;
// Thumb through the sprite chain
while (pSprite != nullptr) {
// be sure to not test against ourself
// ... and only test against overlapping sprites
if (this != pSprite) {
CBofRect overlapRect; // Area of overlap between rectangles
// Sprites touch if their rectangles intersect.
// does our sprite overlap another?
if (overlapRect.intersectRect(pNewRect, &pSprite->_cRect))
// ... if so return a pointer to it
return pSprite;
}
// Fetch next sprite in chain for testing
pSprite = (CBofSprite *)pSprite->_pNext;
}
return nullptr;
}
CBofSprite *CBofSprite::interception(CBofSprite *pTestSprite) {
assert(isValidObject(this));
CBofSprite *pSprite = pTestSprite; // Get first sprite to be tested
while (pSprite != nullptr) { // Thumb through the entire sprite collection
if (testInterception(pSprite, nullptr)) // ... testing against each sprite in turn
return pSprite; // found an interception
pSprite = (CBofSprite *)pSprite->_pNext; // fetch next sprite in chain for testing
}
return nullptr;
}
bool CBofSprite::spritesOverlap(CBofSprite *pSprite, CBofPoint *pPoint) {
assert(isValidObject(this));
assert(pSprite != nullptr);
// Assume no overlap
bool bHit = false;
// If the sprite's rectangles overlap
CBofRect overlapRect;
if (overlapRect.intersectRect(&_cRect, &pSprite->_cRect)) {
int32 dx = overlapRect.width();
int32 dy = overlapRect.height();
int32 x1 = overlapRect.left - _cRect.left + _cImageRect.left;
int32 y1 = overlapRect.top - _cRect.top + _cImageRect.top;
int32 x2 = overlapRect.left - pSprite->_cRect.left + pSprite->_cImageRect.left;
int32 y2 = overlapRect.top - pSprite->_cRect.top + pSprite->_cImageRect.top;
int32 dx1 = _pImage->widthBytes();
int32 dx2 = pSprite->_pImage->widthBytes();
byte m1 = (byte)_nMaskColor;
byte m2 = (byte)pSprite->_nMaskColor;
// Lock down these bitmaps
_pImage->lock();
pSprite->_pImage->lock();
byte *pDib1 = (byte *)_pImage->getPixelAddress((int)x1, (int)y1);
byte *pDib2 = (byte *)pSprite->_pImage->getPixelAddress((int)x2, (int)y2);
if (!_pImage->isTopDown()) {
dx1 = -dx1;
}
if (!pSprite->_pImage->isTopDown()) {
dx2 = -dx2;
}
for (int32 y = 0; y < dy; y++) {
byte *pPtr1 = pDib1;
byte *pPtr2 = pDib2;
for (int32 x = 0; x < dx; x++) {
if ((*pPtr1 != m1) && (*pPtr2 != m2)) {
if (pPoint != nullptr) {
pPoint->x = (int)x;
pPoint->y = (int)y;
}
bHit = true;
goto endroutine;
}
pPtr1++;
pPtr2++;
}
pDib1 += dx1;
pDib2 += dx2;
}
}
endroutine:
// Don't need access to these bitmaps any more
pSprite->_pImage->unlock();
_pImage->unlock();
return bHit;
}
void CBofSprite::setPosition(int x, int y) {
assert(isValidObject(this));
// Now have a real location establish the new location of the sprite
// and setup the bitmap's bounding rectangle
_bPositioned = true;
_cPosition.x = x;
_cPosition.y = y;
_cRect.setRect(_cPosition.x, _cPosition.y, _cPosition.x + _cSize.cx - 1, _cPosition.y + _cSize.cy - 1);
}
void CBofSprite::clearImage() {
assert(isValidObject(this));
if (!_bDuplicated && (_pImage != nullptr)) {
delete _pImage;
}
_pImage = nullptr;
}
void CBofSprite::setSharedPalette(CBofPalette *pPal) {
assert(pPal != nullptr);
_pSharedPalette = pPal;
}
void CBofSprite::setZOrder(int nValue) {
assert(isValidObject(this));
assert(nValue >= SPRITE_TOPMOST && nValue <= SPRITE_HINDMOST);
_nZOrder = nValue;
// Relinking this sprite after setting it's new Z-Order will
// add the sprite to the correct Z-Order sorted location (Insertion Sort)
if (_bLinked) {
unlinkSprite();
linkSprite();
}
}
} // namespace SpaceBar
} // namespace Bagel

View File

@@ -0,0 +1,237 @@
/* 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/>.
*
*/
#ifndef BAGEL_BOFLIB_GFX_SPRITE_H
#define BAGEL_BOFLIB_GFX_SPRITE_H
#include "bagel/spacebar/boflib/gfx/bitmap.h"
#include "bagel/boflib/object.h"
#include "bagel/boflib/palette.h"
#include "bagel/spacebar/boflib/gui/window.h"
namespace Bagel {
namespace SpaceBar {
#define SPRITE_TOPMOST 0
#define SPRITE_FOREGROUND 64
#define SPRITE_MIDDLE 128
#define SPRITE_BACKGROUND 192
#define SPRITE_HINDMOST 255
class CBofSprite : public CBofError, public CBofObject, public CLList {
public:
static void initialize();
static void shutdown();
// Constructors
CBofSprite();
// Destructors
virtual ~CBofSprite();
//////////////////////////////////////////
// Implementation
CBofSprite *duplicateSprite();
void duplicateSprite(CBofSprite *pSprite);
bool loadSprite(const char *pszPathName, int nCels = 1);
bool loadSprite(CBofBitmap *pBitmap, int nCels = 1);
bool paintSprite(CBofBitmap *pBmp, int x, int y);
bool paintSprite(CBofBitmap *pBmp, CBofPoint point) {
return paintSprite(pBmp, point.x, point.y);
}
bool paintSprite(CBofWindow *pWnd, int x, int y);
bool paintSprite(CBofWindow *pWnd, CBofPoint point) {
return paintSprite(pWnd, point.x, point.y);
}
bool paintCel(CBofWindow *pWnd, int nCelId, int x, int y);
bool paintCel(CBofBitmap *pBmp, int nCelId, int x, int y);
void batchPaint(int, int y);
void batchErase();
bool setupCels(int nCels);
void setCel(int nCelID);
void nextCel();
void prevCel();
bool refreshSprite(CBofBitmap *pBmp) {
return paintSprite(pBmp, _cPosition.x, _cPosition.y);
}
bool refreshSprite(CBofWindow *pWnd) {
return paintSprite(pWnd, _cPosition.x, _cPosition.y);
}
bool eraseSprite(CBofWindow *pWnd);
// Notice how there is no eraseSprite for a CBofBitmap - that's because
// sprites no longer retain their background, so there would be no way
// to restore the background, and that's all eraseSprite does.
CBofSprite *interception(CBofRect *newRect, CBofSprite *pTestSprite);
CBofSprite *interception(CBofSprite *pTestSprite);
CBofSprite *interception() {
return interception(_pSpriteChain);
}
CBofSprite *interception(CBofRect *newRect) {
return interception(newRect, _pSpriteChain);
}
bool testInterception(CBofSprite *pTestSprite, CBofPoint *pPoint = nullptr);
void setPosition(int x, int y);
CBofPoint getPosition() const {
return _cPosition;
}
CBofSize getSize() const {
return _cSize;
}
CBofRect getRect() const {
return _cRect;
}
int height() const {
return _cRect.height();
}
int width() const {
return _cRect.width();
}
void setMaskColor(int nColor) {
_nMaskColor = nColor;
}
int getMaskColor() const {
return _nMaskColor;
}
byte readPixel(int x, int y) const {
return _pImage->readPixel(x, y);
}
void setZOrder(int nValue);
int getCelCount() const {
return _nCelCount;
}
int getCelIndex() const {
return _nCelID;
}
void setAnimated(bool bAnimated) {
_bAnimated = bAnimated;
}
bool getAnimated() const {
return _bAnimated;
}
void linkSprite();
void unlinkSprite();
const char *getFileName() const {
return _pImage->getFileName();
}
static void openLibrary(CBofPalette *pPal);
static void closeLibrary();
static void setSharedPalette(CBofPalette *pPalette);
static CBofSprite *getSpriteChain() {
return _pSpriteChain;
}
static bool updateDirtyRect(CBofWindow *pWnd, CBofSprite *pPrimarySprite = nullptr);
static bool updateDirtyRect(CBofBitmap *pBmp, CBofSprite *pPrimarySprite = nullptr);
static void addToDirtyRect(CBofRect *pRect);
static void clearDirtyRect() {
_cDirtyRect->setRectEmpty();
}
static CBofRect *getDirtyRect() {
return _cDirtyRect;
}
static void flushSpriteChain();
static void setupWorkArea(int dx, int dy);
static void tearDownWorkArea();
// Add a method for allowing callers of this object to block
// next cell advancement
void setBlockAdvance(bool b = true) {
_bBlockAdvance = b;
}
bool getBlockAdvance() const {
return _bBlockAdvance;
}
private:
void clearImage();
bool spritesOverlap(CBofSprite *pSprite, CBofPoint *pPoint = nullptr);
bool _bBlockAdvance; // Allow block next cell.
public:
CBofBitmap *_pImage; // Bitmap for the sprite
protected:
CBofPoint _cPosition; // Upper left corner of sprite on display
CBofSize _cSize; // dx/dy size of the sprite bitmap
CBofRect _cRect; // Bounding rectangle on display
CBofRect _cImageRect; // Bounding rectangle within image bitmap
int _nMaskColor; // Transparent color index for this sprite
int _nZOrder; // Foreground / background order
int _nCelID; // Index of current cel image
int _nCelCount; // Number of cels in the animation strip
bool _bDuplicated : 1; // Shares bitmaps with some other sprite
bool _bPositioned : 1; // Whether sprite has been positioned yet
bool _bAnimated : 1; // Whether cel advance occurs when painting
bool _bLinked : 1; // Whether sprite is linked into the chain
bool _bReadOnly : 1; // Whether image is read only or not
static CBofRect *_cDirtyRect;
static CBofSprite *_pSpriteChain; // Pointer to linked chain of sprites
static CBofSprite *_pTouchedSprite; // Sprite touched during painting operation
static CBofBitmap *_pWorkBmp; // Offscreen work area
static CBofPalette *_pSharedPalette; // Shared palette for ALL sprites
static int _nWorkDX;
static int _nWorkDY;
};
} // namespace SpaceBar
} // namespace Bagel
#endif

View File

@@ -0,0 +1,476 @@
/* 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 "graphics/fonts/ttf.h"
#include "bagel/spacebar/boflib/app.h"
#include "bagel/spacebar/boflib/gfx/text.h"
namespace Bagel {
namespace SpaceBar {
#define START_SIZE 8
#define MONO_FONT "LiberationMono-Regular.ttf"
#define SERIF_FONT_REGULAR "LiberationSans-Regular.ttf"
#define SERIF_FONT_BOLD "LiberationSans-Bold.ttf"
#define TAB_SIZE 50
int CBofText::_tabStop;
bool CBofText::_initialized;
Graphics::Font *CBofText::_defaultFonts[NUM_POINT_SIZES];
Graphics::Font *CBofText::_fixedFonts[NUM_POINT_SIZES];
ErrorCode CBofText::initialize() {
_initialized = true;
_tabStop = 20; // tabstops every 20 pixels
Common::fill(_defaultFonts, _defaultFonts + NUM_POINT_SIZES,
(Graphics::Font *)nullptr);
Common::fill(_fixedFonts, _fixedFonts + NUM_POINT_SIZES,
(Graphics::Font *)nullptr);
return ERR_NONE;
}
ErrorCode CBofText::shutdown() {
for (int i = 0; i < NUM_POINT_SIZES; i++) {
delete _defaultFonts[i];
delete _fixedFonts[i];
}
_initialized = false;
return ERR_NONE;
}
CBofText::CBofText() {
initializeFields(); // Initialize stuff
}
CBofText::CBofText(const CBofRect *pRect, int nJustify, uint32 nFormatFlags) {
// Can't access null pointers
assert(pRect != nullptr);
// Initialize stuff
initializeFields();
// Build the work areas
setupText(pRect, nJustify, nFormatFlags);
}
CBofText::~CBofText() {
delete _pWork;
_pWork = nullptr;
delete _pBackground;
_pBackground = nullptr;
}
void CBofText::initializeFields() {
_pBackground = nullptr;
_pWork = nullptr;
_bSaved = false;
_cPosition = CBofPoint(0, 0);
_cSize = CBofSize(0, 0);
_cRect.setRect(0, 0, 0, 0);
_cShadowColor = RGB(0, 0, 0);
_nShadow_DX = 0;
_nShadow_DY = 0;
_nJustify = JUSTIFY_LEFT;
_nFormatFlags = FORMAT_DEFAULT;
_bMultiLine = false;
_nCurSize = 10;
_nCurWeight = TEXT_DONTCARE;
_cTextColor = CTEXT_COLOR;
}
ErrorCode CBofText::setupText(const CBofRect *pRect, int nJustify, uint32 nFormatFlags) {
// Can't access null pointers
assert(pRect != nullptr);
_nJustify = nJustify;
// Setup the fields for location and size of the text area
_cRect = *pRect;
_cSize.cx = _cRect.width();
_cSize.cy = _cRect.height();
delete _pWork;
_pWork = nullptr;
delete _pBackground;
_pBackground = nullptr;
CBofPalette *pPalette = CBofApp::getApp()->getPalette();
// Create a bitmap to serve as our work area as we output text
_pWork = new CBofBitmap(_cSize.cx, _cSize.cy, pPalette);
// Create a bitmap to hold the background we overwrite
_pBackground = new CBofBitmap(_cSize.cx, _cSize.cy, pPalette);
return _errCode;
}
ErrorCode CBofText::setupTextOpt(const CBofRect *pRect, int nJustify, uint32 nFormatFlags) {
// Can't access null pointers
assert(pRect != nullptr);
_nJustify = nJustify;
_nFormatFlags = nFormatFlags;
// Setup the fields for location and size of the text area
_cRect = *pRect;
_cSize.cx = _cRect.width();
_cSize.cy = _cRect.height();
return _errCode;
}
ErrorCode CBofText::erase(CBofWindow *pWnd) {
// Can't access null pointers
assert(pWnd != nullptr);
if (_pBackground != nullptr && _bSaved) {
// Simply splat the background art back where it came from
_errCode = _pBackground->paint(pWnd, &_cRect);
}
return _errCode;
}
ErrorCode CBofText::erase(CBofBitmap *pBmp) {
// Can't access null pointers
assert(pBmp != nullptr);
if (_pBackground != nullptr && _bSaved) {
// Simply splat the background art back where it came from
_errCode = _pBackground->paint(pBmp, &_cRect);
}
return _errCode;
}
ErrorCode CBofText::display(CBofWindow *pWnd, const char *pszText, const int nSize, const int nWeight, const COLORREF cColor, int nFont) {
assert(isValidObject(this));
// Can't access null pointers
assert(pWnd != nullptr);
_cTextColor = cColor;
return displayText(pWnd, pszText, &_cRect, nSize, nWeight, false, nFont);
}
ErrorCode CBofText::display(CBofWindow *pWnd) {
assert(isValidObject(this));
assert(pWnd != nullptr);
return display(pWnd, _cCurString, _nCurSize, _nCurWeight, _cTextColor);
}
ErrorCode CBofText::display(CBofBitmap *pBmp) {
assert(isValidObject(this));
assert(pBmp != nullptr);
return display(pBmp, _cCurString, _nCurSize, _nCurWeight, _cTextColor);
}
ErrorCode CBofText::display(CBofBitmap *pBmp, const char *pszText, const int nSize, const int nWeight, const COLORREF cColor, int nFont) {
// Can't access null pointers
assert(pBmp != nullptr);
_cTextColor = cColor;
return displayText(pBmp, pszText, &_cRect, nSize, nWeight, false, nFont);
}
ErrorCode CBofText::displayShadowed(CBofWindow *pWnd, const char *pszText, const int nSize, const int nWeight, const COLORREF cColor, const COLORREF cShadow, const int nDX, const int nDY, int nFont) {
// Can't access null pointers
assert(pWnd != nullptr);
_cTextColor = cColor;
_cShadowColor = cShadow;
_nShadow_DX = nDX;
_nShadow_DY = nDY;
return displayText(pWnd, pszText, &_cRect, nSize, nWeight, true, nFont);
}
ErrorCode CBofText::displayText(CBofWindow *pWnd, const char *pszText, CBofRect *pRect, const int nSize, const int nWeight, const bool bShadowed, int nFont) {
assert(isValidObject(this));
assert(pWnd != nullptr);
assert(pszText != nullptr);
assert(pRect != nullptr);
CBofRect cRect(0, 0, pRect->width() - 1, pRect->height() - 1);
assert(_pBackground != nullptr);
assert(_pWork != nullptr);
if (!_bSaved) {
CBofBitmap::setUseBackdrop(true);
_pBackground->captureScreen(pWnd, pRect);
CBofBitmap::setUseBackdrop(false);
_bSaved = true;
}
_pBackground->paint(_pWork, 0, 0);
displayTextEx(_pWork, pszText, &cRect, nSize, nWeight, bShadowed, nFont);
_pWork->paint(pWnd, pRect);
return _errCode;
}
ErrorCode CBofText::displayText(CBofBitmap *pBmp, const char *pszText, CBofRect *pRect, const int nSize, const int nWeight, const bool bShadowed, int nFont) {
assert(isValidObject(this));
assert(pBmp != nullptr);
assert(pszText != nullptr);
assert(pRect != nullptr);
CBofRect cRect(0, 0, pRect->width() - 1, pRect->height() - 1);
assert(_pWork != nullptr);
assert(_pBackground != nullptr);
if (!_bSaved) {
CBofRect r = _pBackground->getRect();
pBmp->paint(_pBackground, &r, pRect);
_bSaved = true;
}
_pBackground->paint(_pWork, 0, 0);
displayTextEx(_pWork, pszText, &cRect, nSize, nWeight, bShadowed, nFont);
_pWork->paint(pBmp, pRect);
return _errCode;
}
Graphics::Font *CBofText::getFont(int nFont, int nSize, int nWeight) {
Graphics::Font *font;
// Attempt to use one of the fonts that we pre-allocated
if (nFont != FONT_MONO) {
font = _defaultFonts[nSize - START_SIZE];
} else {
font = _fixedFonts[nSize - START_SIZE];
}
// Last resort - create the font now
if (font == nullptr) {
if (nFont != FONT_MONO) {
font = Graphics::loadTTFFontFromArchive(SERIF_FONT_REGULAR, nSize, Graphics::kTTFSizeModeCell);
_defaultFonts[nSize - START_SIZE] = font;
} else {
font = Graphics::loadTTFFontFromArchive(MONO_FONT, nSize, Graphics::kTTFSizeModeCell);
_fixedFonts[nSize - START_SIZE] = font;
}
}
return font;
}
ErrorCode CBofText::displayTextEx(CBofBitmap *pBmp, const char *pszText, CBofRect *pRect, const int nSize, const int nWeight, const bool bShadowed, int nFont) {
assert(isValidObject(this));
// can't access null pointers
assert(pBmp != nullptr);
assert(pszText != nullptr);
assert(pRect != nullptr);
Graphics::ManagedSurface surface = pBmp->getSurface();
Graphics::Font *font = getFont(nFont, nSize, nWeight);
int color;
// Split lines
Common::U32StringArray lines;
font->wordWrapText(Common::U32String(pszText, Common::kWindows1252), pRect->width(), lines);
// Iterate the lines to get the maximum width
int maxWidth = 0;
for (uint i = 0; i < lines.size(); ++i)
maxWidth = MAX(maxWidth, font->getStringWidth(lines[i]));
Common::Point textInfo(maxWidth, (int)lines.size() * font->getFontHeight());
_cPosition.y = (_cSize.cy - textInfo.y) >> 1;
Graphics::TextAlign align = Graphics::kTextAlignLeft;
switch (_nJustify) {
case JUSTIFY_CENTER:
_cPosition.x = (_cSize.cx - textInfo.x) >> 1;
align = Graphics::kTextAlignCenter;
break;
case JUSTIFY_LEFT:
// align left
_cPosition.x = 0;
break;
case JUSTIFY_RIGHT:
_cPosition.x = _cSize.cx - textInfo.x;
align = Graphics::kTextAlignRight;
break;
case JUSTIFY_WRAP:
// Align left
_bMultiLine = true;
break;
default:
break;
}
// text starts relative to area for painting
_cPosition += pRect->topLeft();
// Note: Under ScummVM, even single line drawing uses the multiLine code
Common::Rect newRect = *pRect;
if ((_nFormatFlags & FORMAT_TOP_CENTER) == FORMAT_TOP_CENTER) {
int h = lines.size() * font->getFontHeight();
newRect.top = (newRect.top + newRect.bottom) / 2 - h / 2;
newRect.bottom = newRect.top + h;
}
Common::Rect shadowRect = newRect;
shadowRect.translate(_nShadow_DX, _nShadow_DY);
for (uint i = 0; i < lines.size(); ++i) {
const Common::U32String &line = lines[i];
if (bShadowed) {
color = CBofApp::getApp()->getPalette()->getNearestIndex(_cShadowColor);
displayLine(font, surface, line, shadowRect.left, shadowRect.top,
shadowRect.width(), color, align);
}
color = CBofApp::getApp()->getPalette()->getNearestIndex(_cTextColor);
displayLine(font, surface, line, newRect.left, newRect.top,
newRect.width(), color, align);
newRect.top += font->getFontHeight();
shadowRect.top += font->getFontHeight();
}
return _errCode;
}
void CBofText::displayLine(Graphics::Font *font, Graphics::ManagedSurface &surface,
const Common::U32String &line, int left, int top, int width, int color, Graphics::TextAlign align) {
if (!line.contains('\t')) {
font->drawString(&surface, line, left, top, width, color, align);
} else {
// Special rendering of tabbed text
Common::U32String str = line;
while (!str.empty()) {
if (str[0] == '\t') {
// Move to next tab stop
left = (left + TAB_SIZE) / TAB_SIZE * TAB_SIZE;
str.deleteChar(0);
} else {
Common::U32String fragment;
size_t tab = str.findFirstOf('\t');
if (tab == Common::U32String::npos) {
fragment = str;
str.clear();
} else {
fragment = Common::U32String(str.c_str(), str.c_str() + tab);
str = Common::U32String(str.c_str() + tab);
}
int fragmentWidth = font->getStringWidth(fragment);
font->drawString(&surface, fragment, left, top, width, color, align);
left += fragmentWidth;
width -= fragmentWidth;
}
}
}
}
ErrorCode paintText(CBofWindow *pWnd, CBofRect *pRect, const char *pszString, const int nSize, const int nWeight, const COLORREF cColor, int nJustify, uint32 nFormatFlags, int nFont) {
assert(pWnd != nullptr);
assert(pRect != nullptr);
CBofText cText(pRect, nJustify, nFormatFlags);
return cText.display(pWnd, pszString, nSize, nWeight, cColor, nFont);
}
ErrorCode paintText(CBofBitmap *pBmp, CBofRect *pRect, const char *pszString, const int nSize, const int nWeight, const COLORREF cColor, int nJustify, uint32 nFormatFlags, int nFont) {
assert(pBmp != nullptr);
assert(pRect != nullptr);
CBofText cText;
cText.setupTextOpt(pRect, nJustify, nFormatFlags);
cText.setColor(cColor);
return cText.displayTextEx(pBmp, pszString, pRect, nSize, nWeight, false, nFont);
}
ErrorCode paintShadowedText(CBofBitmap *pBmp, CBofRect *pRect, const char *pszString, const int nSize, const int nWeight, const COLORREF cColor, int nJustify, uint32 nFormatFlags, int nFont) {
assert(pBmp != nullptr);
assert(pRect != nullptr);
CBofText cText;
cText.setupTextOpt(pRect, nJustify, nFormatFlags);
cText.setColor(cColor);
cText.setShadowColor(CTEXT_SHADOW_COLOR);
cText.setShadowSize(CTEXT_SHADOW_DX, CTEXT_SHADOW_DY);
return cText.displayTextEx(pBmp, pszString, pRect, nSize, nWeight, true, nFont);
}
CBofRect calculateTextRect(CBofWindow *pWnd, const CBofString *pStr, int nSize, int nFont) {
return calculateTextRect(pWnd->getRect(), pStr, nSize, nFont);
}
CBofRect calculateTextRect(CBofRect rect, const CBofString *pStr, int nSize, int nFont) {
// Get the font to use
Graphics::Font *font = CBofText::getFont(nFont, nSize, TEXT_NORMAL);
// Wrap the text as necessary
Common::U32StringArray lines;
font->wordWrapText(Common::U32String(pStr->getBuffer(), Common::kWindows1252), rect.width(), lines);
// Iterate the lines to get the maximum width
int maxWidth = 0;
for (uint i = 0; i < lines.size(); ++i)
maxWidth = MAX(maxWidth, font->getStringWidth(lines[i]));
return CBofRect(0, 0, maxWidth, (int)lines.size() * font->getFontHeight());
}
} // namespace SpaceBar
} // namespace Bagel

View File

@@ -0,0 +1,340 @@
/* 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/>.
*
*/
#ifndef BAGEL_BOFLIB_GFX_TEXT_H
#define BAGEL_BOFLIB_GFX_TEXT_H
#include "graphics/font.h"
#include "bagel/boflib/object.h"
#include "bagel/spacebar/boflib/gfx/bitmap.h"
#include "bagel/boflib/error.h"
#include "bagel/boflib/string.h"
namespace Bagel {
namespace SpaceBar {
#define NUM_POINT_SIZES 32
// Text color and offset definitions
#define CTEXT_COLOR RGB(0,0,0)
#define CTEXT_SHADOW_COLOR RGB(0,0,0)
#define CTEXT_YELLOW RGB(255, 255, 0)
#define CTEXT_WHITE RGB(255, 255, 255)
#define CTEXT_SHADOW_DX 2
#define CTEXT_SHADOW_DY 2
// Text justification definitions
enum {
JUSTIFY_CENTER = 0,
JUSTIFY_LEFT = 1,
JUSTIFY_RIGHT = 2,
JUSTIFY_WRAP = 3
};
// Text weight definitions
//
/*
* DrawText Format Flags
*/
#define DT_TOP 0x00000000
#define DT_LEFT 0x00000000
#define DT_CENTER 0x00000001
#define DT_RIGHT 0x00000002
#define DT_VCENTER 0x00000004
#define DT_BOTTOM 0x00000008
#define DT_WORDBREAK 0x00000010
#define DT_SINGLELINE 0x00000020
#define DT_EXPANDTABS 0x00000040
#define DT_TABSTOP 0x00000080
#define DT_NOCLIP 0x00000100
#define DT_EXTERNALLEADING 0x00000200
#define DT_CALCRECT 0x00000400
#define DT_NOPREFIX 0x00000800
#define DT_intERNAL 0x00001000
enum {
FW_NORMAL,
FW_BOLD,
FW_MEDIUM,
};
#define FONT_DEFAULT 0
#define FONT_MONO 1
#define TEXT_DONTCARE 0
#define TEXT_THIN FW_THIN
#define TEXT_EXTRALIGHT FW_EXTRALIGHT
#define TEXT_ULTRALIGHT FW_ULTRALIGHT
#define TEXT_LIGHT FW_LIGHT
#define TEXT_NORMAL FW_NORMAL
#define TEXT_REGULAR FW_REGULAR
#define TEXT_MEDIUM FW_MEDIUM
#define TEXT_SEMIBOLD FW_SEMIBOLD
#define TEXT_DEMIBOLD FW_DEMIBOLD
#define TEXT_BOLD FW_BOLD
#define TEXT_EXTRABOLD FW_EXTRABOLD
#define TEXT_ULTRABOLD FW_ULTRABOLD
#define TEXT_BLACK FW_BLACK
#define TEXT_HEAVY FW_HEAVY
#define FORMAT_TOP_LEFT ( DT_TOP | DT_LEFT )
#define FORMAT_TOP_RIGHT ( DT_TOP | DT_RIGHT )
#define FORMAT_TOP_CENTER ( DT_TOP | DT_CENTER )
#define FORMAT_BOT_LEFT ( DT_BOTTOM | DT_LEFT )
#define FORMAT_BOT_RIGHT ( DT_BOTTOM | DT_RIGHT )
#define FORMAT_BOT_CENTER ( DT_BOTTOM | DT_CENTER )
#define FORMAT_CENTER_LEFT ( DT_VCENTER | DT_LEFT )
#define FORMAT_CENTER_RIGHT ( DT_VCENTER | DT_RIGHT )
#define FORMAT_CENTER_CENTER ( DT_VCENTER | DT_CENTER )
#define FORMAT_SINGLE_LINE DT_SINGLELINE
#define FORMAT_MULTI_LINE DT_WORDBREAK
#define FORMAT_DEFAULT ( FORMAT_TOP_LEFT | FORMAT_MULTI_LINE )
#define FONT_DEFAULT_SIZE (-14)
#define FONT_8POINT 8
#define FONT_10POINT 10
#define FONT_12POINT 12
#define FONT_14POINT 14
#define FONT_15POINT 15
#define FONT_18POINT 18
#define FONT_20POINT 20
#define TEXT_DEFAULT_FACE TEXT_BOLD
class CBofText : public CBofObject, public CBofError {
public:
// Constructors
CBofText();
CBofText(const CBofRect *pRect, int nJustify = JUSTIFY_CENTER, uint32 nFormatFlags = FORMAT_DEFAULT);
virtual ~CBofText();
// Implementation
//
/**
* Build primary data objects and work areas; text will be displayed
* centered within the defined rectangular area, hence it is up to
* the caller to ensure that the text fits (excess is cropped).
* @param pRect Rectangular area encompassed by the text object
* @param nJustify Alignment of text in the rectangle
* @param nFormatFlags Format flag
*/
ErrorCode setupText(const CBofRect *pRect, int nJustify = JUSTIFY_CENTER, uint32 nFormatFlags = FORMAT_DEFAULT);
ErrorCode setupTextOpt(const CBofRect *pRect, int nJustify = JUSTIFY_CENTER, uint32 nFormatFlags = FORMAT_DEFAULT);
void setText(const CBofString &cString) {
_cCurString = cString;
}
void setColor(const COLORREF cColor) {
_cTextColor = cColor;
}
void SetSize(const int nSize) {
_nCurSize = nSize;
}
void setWeight(const int nWeight) {
_nCurWeight = nWeight;
}
void setShadowColor(const COLORREF cColor) {
_cShadowColor = cColor;
}
void setShadowSize(int nDX, int nDY) {
_nShadow_DX = nDX;
_nShadow_DY = nDY;
}
CBofString getText() const {
return _cCurString;
}
COLORREF getColor() const {
return _cTextColor;
}
int getSize() const {
return _nCurSize;
}
int getWeight() const {
return _nCurWeight;
}
/**
* Restores the background behind current text on screen
* @param pWnd Window to erase text from
* @return Error return Code
*/
ErrorCode erase(CBofWindow *pWnd);
/**
* Restores the background behind current text offscreen
* @param pBmp Offscreen bitmap to erase text from
* @return Error return Code
*/
ErrorCode erase(CBofBitmap *pBmp);
/**
* Re-displays current text, formatted with current attribs
* @param pWnd Window to paint into
* @return Error return Code
*/
ErrorCode display(CBofWindow *pWnd);
/**
* Re-displays current text, formatted with current attribs
* @param pBmp Bitmap to paint into
* @return Error return Code
*/
ErrorCode display(CBofBitmap *pBmp);
/**
* Display a text string, formatted in the current text area
* @param pWnd Window to paint into
* @param pszText Point to text string to be displayed
* @param nSize Point size of the text to be used
* @param nWeight Weighting of the font (FW_ identifier)
* @param cColor Color that the text will be
* @param nFont Font used (default or mono)
* @return Error return Code
*/
ErrorCode display(CBofWindow *pWnd, const char *pszText, int nSize, int nWeight, COLORREF cColor = CTEXT_COLOR, int nFont = FONT_DEFAULT);
/**
* Display a text string, formatted in the current text area
* @param pBmp Bitmap to paint into
* @param pszText Point to text string to be displayed
* @param nSize Point size of the text to be used
* @param nWeight Weighting of the font (FW_ identifier)
* @param cColor Color that the text will be
* @param nFont Font used (default or mono)
* @return Error return Code
*/
ErrorCode display(CBofBitmap *pBmp, const char *pszText, int nSize, int nWeight, COLORREF cColor = CTEXT_COLOR, int nFont = FONT_DEFAULT);
/**
* Display a shadowed text string into the current text area
* @param pWnd Window to paint into
* @param pszText Point to text string to be displayed
* @param nSize Point size of the text to be used
* @param nWeight Weighting of the font (FW_ identifier)
* @param cColor Color that the text will be
* @param cShadow Color that the text's shadow will be
* @param nDX Shadow DX
* @param nDY Shadow DY
* @param nFont Font used (default or mono)
* @return Error return Code
*/
ErrorCode displayShadowed(CBofWindow *pWnd, const char *pszText, int nSize,
int nWeight, COLORREF cColor, COLORREF cShadow = CTEXT_SHADOW_COLOR,
int nDX = CTEXT_SHADOW_DX, int nDY = CTEXT_SHADOW_DY, int nFont = FONT_DEFAULT);
void flushBackground() {
_bSaved = false;
}
static ErrorCode initialize();
static ErrorCode shutdown();
/**
* Displays specified text onto specified bitmap
* @param pBmp Bitmap to paint text onto
* @param pszText Pointer to text string to be displayed
* @param pRect Area to paint text to
* @param nSize Point size of the text to be used
* @param nWeight Weighting of the font (FW_ identifier)
* @param bShadowed Whether the text is shadowed
* @param nFont Font used (default or mono)
* @return Error return Code
*/
ErrorCode displayTextEx(CBofBitmap *pBmp, const char *pszText, CBofRect *pRect, int nSize, int nWeight, bool bShadowed, int nFont = FONT_DEFAULT);
static Graphics::Font *getFont(int nFont, int nSize, int nWeight);
private:
/**
* Initializes key fields to zero or nullptr states.
*/
void initializeFields();
/**
* Displays specified text onto specified bitmap
* @param pWnd Window to paint text onto
* @param pszText Pointer to text string to be displayed
* @param pRect Area to paint text to
* @param nSize Point size of the text to be used
* @param nWeight Weighting of the font (FW_ identifier)
* @param bShadowed Whether the text is shadowed
* @param nFont Font used (default or mono)
* @return Error return Code
*/
ErrorCode displayText(CBofWindow *pWnd, const char *pszText, CBofRect *pRect, int nSize, int nWeight, bool bShadowed, int nFont = FONT_DEFAULT);
ErrorCode displayText(CBofBitmap *pBmp, const char *pszText, CBofRect *pRect, int nSize, int nWeight, bool bShadowed, int nFont = FONT_DEFAULT);
void displayLine(Graphics::Font *font, Graphics::ManagedSurface &surface, const Common::U32String &line,
int left, int top, int width, int color, Graphics::TextAlign align);
protected:
CBofString _cCurString; // text to be displayed
CBofRect _cRect; // bounding rectangle of text area
CBofPoint _cPosition; // upper left corner of text displayed
CBofSize _cSize; // dx/dy size of the text bitmap
COLORREF _cTextColor; // color to use for the text itself
COLORREF _cShadowColor; // color to use for the text's shadow
CBofBitmap *_pBackground; // bitmap for the text's background
CBofBitmap *_pWork; // bitmap for the work area
int _nCurSize; // point size of current text
int _nCurWeight; // style of current text
int _nJustify; // positioning within the rectangle
int _nShadow_DX; // horizontal offset for shadow
int _nShadow_DY; // vertical offset for shadow
uint32 _nFormatFlags; // multi line formatting flags
bool _bMultiLine; // multi vs single line formatting
bool _bSaved;
static Graphics::Font *_defaultFonts[NUM_POINT_SIZES];
static Graphics::Font *_fixedFonts[NUM_POINT_SIZES];
static bool _initialized;
static int _tabStop; // tabstop table
};
// Global text functions
//
ErrorCode paintText(CBofWindow *pWnd, CBofRect *pRect, const char *, int nSize, int nWeight, COLORREF cColor = CTEXT_COLOR, int nJustify = JUSTIFY_CENTER, uint32 nFormat = FORMAT_DEFAULT, int nFont = FONT_DEFAULT);
ErrorCode paintText(CBofBitmap *pBmp, CBofRect *pRect, const char *, int nSize, int nWeight, COLORREF cColor = CTEXT_COLOR, int nJustify = JUSTIFY_CENTER, uint32 nFormat = FORMAT_DEFAULT, int nFont = FONT_DEFAULT);
ErrorCode paintShadowedText(CBofBitmap *, CBofRect *pRect, const char *, int nSize, int nWeight, COLORREF cColor = CTEXT_COLOR, int nJustify = JUSTIFY_CENTER, uint32 n = FORMAT_DEFAULT, int nFont = FONT_DEFAULT);
/**
* Utility routine that will calculate the rectangle that a text string
* will fit in, given point size and font.
*/
CBofRect calculateTextRect(CBofWindow *pWnd, const CBofString *pStr, int nSize, int nFont);
CBofRect calculateTextRect(CBofRect rect, const CBofString *pStr, int nSize, int nFont);
} // namespace SpaceBar
} // namespace Bagel
#endif

View File

@@ -0,0 +1,621 @@
/* 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 "bagel/spacebar/boflib/gui/button.h"
#include "bagel/spacebar/boflib/app.h"
#include "bagel/spacebar/boflib/gfx/text.h"
namespace Bagel {
namespace SpaceBar {
#define SELECTED_TEXT_OFFSET_DX 1
#define SELECTED_TEXT_OFFSET_DY 1
#define BUTTON_TEXT_SIZE 10
#define CHECK_BOX_SIZE 14
#define CHECK_BOX_OFFSET_DX 4
#define CHECK_BOX_OFFSET_DY 4
#define CHECK_BOX_TEXT_SIZE 10
#define RADIO_BOX_SIZE 13
#define RADIO_BOX_OFFSET_DX 3
#define RADIO_BOX_OFFSET_DY 3
#define RADIO_BOX_TEXT_SIZE 10
static ST_COLORSCHEME g_stDefaultColors = {
RGB(171, 151, 127),
RGB(207, 199, 183),
RGB(131, 111, 91),
RGB(0, 0, 0),
RGB(0, 0, 0),
RGB(0, 0, 0)
};
CBofButton::CBofButton() {
// Inits
_nState = BUTTON_UP;
// Load a default color scheme until another is loaded
loadColorScheme(&g_stDefaultColors);
}
CBofButton::CBofButton(ST_COLORSCHEME *pColorScheme) {
assert(pColorScheme != nullptr);
// Inits
_nState = BUTTON_UP;
loadColorScheme(pColorScheme);
}
CBofButton::~CBofButton() {
assert(isValidObject(this));
}
ErrorCode CBofButton::paint(CBofRect *) {
assert(isValidObject(this));
// Only continue if this button is visible
if (isVisible() && (_parent != nullptr) && _parent->isVisible()) {
CBofPalette *pPalette = CBofApp::getApp()->getPalette();
int nWidth = _cRect.width();
int nHeight = _cRect.height();
// Create our off-screen buffer
CBofBitmap cBmp(nWidth, nHeight, pPalette);
cBmp.fillRect(&_cRect, pPalette->getNearestIndex(_cFaceColor));
int left = _cRect.left;
int right = _cRect.right;
int top = _cRect.top;
int bottom = _cRect.bottom;
byte iShadow = pPalette->getNearestIndex(_cShadowColor);
byte iHighlight = pPalette->getNearestIndex(_cHighlightColor);
if (_nState == BUTTON_DOWN) {
byte iTemp = iShadow;
iShadow = iHighlight;
iHighlight = iTemp;
}
byte c1 = iShadow;
byte c2 = iHighlight;
int i;
for (i = 1; i <= 3; i++) {
cBmp.line(left + i, bottom - i, right - i, bottom - i, c1);
cBmp.line(right - i, bottom - i, right - i, top + i - 1, c1);
}
for (i = 1; i <= 3; i++) {
cBmp.line(left + i, bottom - i, left + i, top + i - 1, c2);
cBmp.line(left + i, top + i - 1, right - i, top + i - 1, c2);
}
cBmp.drawRect(&_cRect, pPalette->getNearestIndex(_cOutlineColor));
// Create a temporary text object
CBofRect cTempRect(3, 3, _cRect.right - 3, _cRect.bottom - 3);
if (_nState == BUTTON_DOWN) {
cTempRect += CBofPoint(1, 1);
}
CBofText cText(&cTempRect);
// Print text into button
COLORREF cTextColor = _cTextColor;
if (_nState == BUTTON_DISABLED)
cTextColor = _cTextDisabledColor;
cText.display(&cBmp, _szTitle, BUTTON_TEXT_SIZE, TEXT_NORMAL, cTextColor);
if (_nState == BUTTON_FOCUS) {
cBmp.drawRect(&cTempRect, pPalette->getNearestIndex(_cOutlineColor));
}
// Now we can update the window
cBmp.paint(this, 0, 0);
}
return _errCode;
}
void CBofButton::loadColorScheme(ST_COLORSCHEME *pColorScheme) {
assert(isValidObject(this));
assert(pColorScheme != nullptr);
// Save all of the color info we need to build a button
_cFaceColor = pColorScheme->_cFace;
_cHighlightColor = pColorScheme->_cHighlight;
_cShadowColor = pColorScheme->_cShadow;
_cTextColor = pColorScheme->_cText;
_cTextDisabledColor = pColorScheme->_cTextDisabled;
_cOutlineColor = pColorScheme->_cOutline;
}
void CBofButton::enable() {
assert(isValidObject(this));
CBofWindow::enable();
setState(BUTTON_UP);
}
void CBofButton::disable() {
assert(isValidObject(this));
setState(BUTTON_DISABLED);
CBofWindow::disable();
}
ErrorCode CBofButton::setState(int nNewState, bool bRepaintNow) {
assert(isValidObject(this));
assert(nNewState >= BUTTON_UP && nNewState <= BUTTON_DISABLED);
// Remember last button state
int nOldState = _nState;
_nState = nNewState;
// Update the window if forced to or if button state has changed
if (bRepaintNow || (nOldState != nNewState)) {
paint();
}
// I must have a valid parent
assert(_parent != nullptr);
// Tell parent the new state of this button
if (_parent != nullptr) {
_parent->onBofButton(this, _nState);
}
return _errCode;
}
void CBofButton::onPaint(CBofRect *pRect) {
assert(isValidObject(this));
assert(pRect != nullptr);
paint(pRect);
}
void CBofButton::onLButtonDown(uint32, CBofPoint *pPoint, void *) {
assert(isValidObject(this));
assert(pPoint != nullptr);
if (!_bCaptured && _nState != BUTTON_DISABLED) {
setCapture();
setState(BUTTON_DOWN, true);
}
}
void CBofButton::onLButtonUp(uint32, CBofPoint *pPoint, void *) {
assert(isValidObject(this));
assert(pPoint != nullptr);
if (_bCaptured) {
releaseCapture();
setState(BUTTON_UP, true);
if (_cRect.ptInRect(*pPoint) && (_parent != nullptr)) {
_parent->onBofButton(this, BUTTON_CLICKED);
}
}
}
/*****************************************************************************/
/* CBofRadioButton */
/*****************************************************************************/
void CBofRadioButton::onLButtonDown(uint32, CBofPoint *pPoint, void *) {
assert(isValidObject(this));
assert(pPoint != nullptr);
if (_nState == BUTTON_UP) {
setState(BUTTON_DOWN, true);
}
}
void CBofRadioButton::onLButtonUp(uint32, CBofPoint *pPoint, void *) {
assert(isValidObject(this));
assert(pPoint != nullptr);
}
ErrorCode CBofRadioButton::paint(CBofRect *) {
assert(isValidObject(this));
// Only continue if this button is visible
if (isVisible() && (_parent != nullptr) && _parent->isVisible()) {
CBofPalette *pPalette = CBofApp::getApp()->getPalette();
int nWidth = _cRect.width();
int nHeight = _cRect.height();
// Create a temporary off-screen buffer
CBofBitmap cBmp(nWidth, nHeight, pPalette);
// Fill in the background color
cBmp.fillRect(&_cRect, pPalette->getNearestIndex(_cFaceColor));
COLORREF cTextColor = _cTextColor;
if (_nState == BUTTON_DISABLED)
cTextColor = _cTextDisabledColor;
byte iShadow = pPalette->getNearestIndex(cTextColor);
byte iHighlight = pPalette->getNearestIndex(cTextColor);
// Paint the radio button circle
int nRadius = 7;
int x = nRadius + RADIO_BOX_OFFSET_DX;
int y = nHeight / 2;
cBmp.circle(x, y, (uint16)nRadius, iShadow);
nRadius--;
cBmp.circle(x, y, (uint16)nRadius, iShadow);
// Create a temporary text object
CBofRect cTempRect(20, RADIO_BOX_OFFSET_DY, _cRect.right, _cRect.bottom - RADIO_BOX_OFFSET_DY);
if (_nState == BUTTON_DOWN) {
nRadius = 1;
cBmp.circle(x, y, (uint16)nRadius, iHighlight);
nRadius = 2;
cBmp.circle(x, y, (uint16)nRadius, iHighlight);
nRadius = 3;
cBmp.circle(x, y, (uint16)nRadius, iHighlight);
nRadius = 4;
cBmp.circle(x, y, (uint16)nRadius, iHighlight);
}
CBofText cText(&cTempRect, JUSTIFY_LEFT);
// Put a box around the whole button
cBmp.drawRect(&_cRect, pPalette->getNearestIndex(_cOutlineColor));
// Show text disabled if button is disabled
// Print text into button
cText.display(&cBmp, _szTitle, RADIO_BOX_TEXT_SIZE, TEXT_NORMAL, cTextColor);
// If button has focus, then put a box around the text
if (_nState == BUTTON_FOCUS) {
cBmp.drawRect(&cTempRect, pPalette->getNearestIndex(_cOutlineColor));
}
// Now we can update the window
cBmp.paint(this, 0, 0);
}
return _errCode;
}
/*****************************************************************************/
/* CBofCheckButton */
/*****************************************************************************/
void CBofCheckButton::onLButtonDown(uint32, CBofPoint *pPoint, void *) {
assert(isValidObject(this));
assert(pPoint != nullptr);
if (_nState == BUTTON_DISABLED)
return;
if (_nState == BUTTON_UP) {
setState(BUTTON_DOWN, true);
} else if (_nState == BUTTON_DOWN) {
setState(BUTTON_UP, true);
}
}
void CBofCheckButton::onLButtonUp(uint32, CBofPoint *pPoint, void *) {
assert(isValidObject(this));
assert(pPoint != nullptr);
// Do nothing, and don't call the base class version of this function
}
ErrorCode CBofCheckButton::paint(CBofRect *) {
assert(isValidObject(this));
// Only continue if this button is visible
if (isVisible() && (_parent != nullptr) && _parent->isVisible()) {
CBofPalette *pPalette = CBofApp::getApp()->getPalette();
int nWidth = _cRect.width();
int nHeight = _cRect.height();
// Create a temporary off-screen buffer
CBofBitmap cBmp(nWidth, nHeight, pPalette);
// Fill in the background color
cBmp.fillRect(&_cRect, pPalette->getNearestIndex(_cFaceColor));
// Show text disabled if button is disabled
COLORREF cTextColor = _cTextColor;
if (_nState == BUTTON_DISABLED)
cTextColor = _cTextDisabledColor;
byte iShadow = pPalette->getNearestIndex(cTextColor);
// Draw the check box (centered vertically)
int y = ((nHeight - CHECK_BOX_SIZE) / 2);
CBofRect cTempRect(CHECK_BOX_OFFSET_DX, y, CHECK_BOX_SIZE + CHECK_BOX_OFFSET_DX - 1, y + CHECK_BOX_SIZE - 1);
cBmp.drawRect(&cTempRect, iShadow);
// if button is in DOWN/ON state, then put an X in the box
//
if (_nState == BUTTON_DOWN) {
cBmp.line(cTempRect.left, cTempRect.top, cTempRect.right, cTempRect.bottom, iShadow);
cBmp.line(cTempRect.left, cTempRect.bottom, cTempRect.right, cTempRect.top, iShadow);
}
// Create a temporary text object
cTempRect.setRect(CHECK_BOX_SIZE + CHECK_BOX_OFFSET_DX, CHECK_BOX_OFFSET_DX, _cRect.right, _cRect.bottom - CHECK_BOX_OFFSET_DX);
CBofText cText(&cTempRect, JUSTIFY_LEFT);
// Put a box around the whole button
cBmp.drawRect(&_cRect, pPalette->getNearestIndex(_cOutlineColor));
// Print text into button
//
cText.display(&cBmp, _szTitle, CHECK_BOX_TEXT_SIZE, TEXT_NORMAL, cTextColor);
// If button has focus, then put a box around the text
if (_nState == BUTTON_FOCUS) {
cBmp.drawRect(&cTempRect, pPalette->getNearestIndex(_cOutlineColor));
}
// Now we can update the window
cBmp.paint(this, 0, 0);
}
return _errCode;
}
ErrorCode CBofCheckButton::SetCheck(bool bChecked) {
assert(isValidObject(this));
setState(bChecked ? BUTTON_CHECKED : BUTTON_UNCHECKED, false);
return _errCode;
}
/*****************************************************************************/
/* CBofBmpButton */
/*****************************************************************************/
CBofBmpButton::CBofBmpButton() {
_pButtonUp = nullptr;
_pButtonDown = nullptr;
_pButtonFocus = nullptr;
_pButtonDisabled = nullptr;
_pBackground = nullptr;
_nState = BUTTON_UP;
_nMaskColor = NOT_TRANSPARENT;
}
CBofBmpButton::~CBofBmpButton() {
delete _pButtonUp;
_pButtonUp = nullptr;
delete _pButtonDown;
_pButtonDown = nullptr;
delete _pButtonDisabled;
_pButtonDisabled = nullptr;
delete _pButtonFocus;
_pButtonFocus = nullptr;
delete _pBackground;
_pBackground = nullptr;
}
ErrorCode CBofBmpButton::paint(CBofRect *) {
assert(isValidObject(this));
// loadBitmaps must be called before the button can be painted
assert(_pButtonUp != nullptr);
assert(_pButtonDown != nullptr);
assert(_pButtonFocus != nullptr);
assert(_pButtonDisabled != nullptr);
// Only continue if this button is visible
if (isVisible() && (_parent != nullptr) && _parent->isVisible()) {
CBofPalette *pPalette = _pButtonUp->getPalette();
int nWidth = _cRect.width();
int nHeight = _cRect.height();
// Do all painting off-screen
CBofBitmap cOffScreen(nWidth, nHeight, pPalette);
if (_pBackground == nullptr) {
_pBackground = new CBofBitmap(nWidth, nHeight, pPalette);
} else {
_pBackground->paint(&cOffScreen, 0, 0);
}
// Assume UP state
CBofBitmap *pBitmap = _pButtonUp;
// Display the correct bitmap based on state
if (_nState == BUTTON_DOWN) {
pBitmap = _pButtonDown;
} else if (_nState == BUTTON_FOCUS) {
pBitmap = _pButtonFocus;
} else if (_nState == BUTTON_DISABLED) {
pBitmap = _pButtonDisabled;
}
// Paint button offscreen
pBitmap->paint(&cOffScreen, 0, 0, nullptr, _nMaskColor);
// Now we can update the window
cOffScreen.paint(this, 0, 0);
}
return _errCode;
}
ErrorCode CBofBmpButton::loadBitmaps(CBofBitmap *pUp, CBofBitmap *pDown, CBofBitmap *pFocus, CBofBitmap *pDisabled, int nMaskColor) {
assert(isValidObject(this));
assert(pUp != nullptr);
assert(pDown != nullptr);
// Use the bitmaps passed in
_pButtonUp = pUp;
_pButtonDown = pDown;
_pButtonFocus = pFocus;
_pButtonDisabled = pDisabled;
// Remember the transparent color for these bitmaps
_nMaskColor = nMaskColor;
return _errCode;
}
ErrorCode CBofBmpButton::loadBitmaps(CBofPalette *pPalette, const char *pszUp, const char *pszDown, const char *pszFocus, const char *pszDisabled, int nMaskColor) {
assert(isValidObject(this));
assert(pPalette != nullptr);
assert(pszUp != nullptr);
assert(pszDown != nullptr);
assert(pszFocus != nullptr);
assert(pszDisabled != nullptr);
// Remember the button transparent color
_nMaskColor = nMaskColor;
// Load each of the bitmaps that represent the button state
_pButtonUp = new CBofBitmap(pszUp, pPalette);
_pButtonUp->setReadOnly(true);
_pButtonDown = new CBofBitmap(pszDown, pPalette);
_pButtonDown->setReadOnly(true);
_pButtonFocus = new CBofBitmap(pszFocus, pPalette);
_pButtonFocus->setReadOnly(true);
_pButtonDisabled = new CBofBitmap(pszDisabled, pPalette);
_pButtonDisabled->setReadOnly(true);
return _errCode;
}
ErrorCode CBofBmpButton::setState(int nNewState, bool bRepaintNow) {
assert(isValidObject(this));
assert(nNewState >= BUTTON_UP && nNewState <= BUTTON_DISABLED);
// Remember last button state
int nOldState = _nState;
_nState = nNewState;
// Update the window if forced to or if button state has changed
if (bRepaintNow || (nOldState != nNewState)) {
paint();
}
// I MUST have a valid parent
assert(_parent != nullptr);
// Tell parent the new state of this button
if (_parent != nullptr) {
_parent->onBofButton(this, _nState);
}
return _errCode;
}
void CBofBmpButton::onPaint(CBofRect *pRect) {
assert(isValidObject(this));
assert(pRect != nullptr);
paint(pRect);
}
void CBofBmpButton::onLButtonDown(uint32, CBofPoint *pPoint, void *) {
assert(isValidObject(this));
assert(pPoint != nullptr);
if (!_bCaptured && _nState != BUTTON_DISABLED) {
setCapture();
setState(BUTTON_DOWN, true);
}
}
void CBofBmpButton::onLButtonUp(uint32, CBofPoint *pPoint, void *) {
assert(isValidObject(this));
assert(pPoint != nullptr);
if (_bCaptured) {
releaseCapture();
setState(BUTTON_UP, true);
if (_cRect.ptInRect(*pPoint) && (_parent != nullptr)) {
_parent->onBofButton(this, BUTTON_CLICKED);
}
}
}
} // namespace SpaceBar
} // namespace Bagel

View File

@@ -0,0 +1,151 @@
/* 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/>.
*
*/
#ifndef BAGEL_BOFLIB_GUI_BUTTON_H
#define BAGEL_BOFLIB_GUI_BUTTON_H
#include "bagel/spacebar/boflib/gui/window.h"
#include "bagel/boflib/palette.h"
namespace Bagel {
namespace SpaceBar {
// Button states
//
#define BUTTON_UP 0
#define BUTTON_DOWN 1
#define BUTTON_FOCUS 2
#define BUTTON_DISABLED 3
#define BUTTON_CLICKED 4
#define BUTTON_ON BUTTON_DOWN
#define BUTTON_OFF BUTTON_UP
#define BUTTON_CHECKED BUTTON_ON
#define BUTTON_UNCHECKED BUTTON_OFF
struct ST_COLORSCHEME {
COLORREF _cFace;
COLORREF _cHighlight;
COLORREF _cShadow;
COLORREF _cText;
COLORREF _cTextDisabled;
COLORREF _cOutline;
};
class CBofButton : public CBofWindow {
public:
CBofButton();
CBofButton(ST_COLORSCHEME *pColorScheme);
virtual ~CBofButton();
void loadColorScheme(ST_COLORSCHEME *pColorScheme);
virtual ErrorCode paint(CBofRect *pRect = nullptr);
void enable() override;
void disable() override;
ErrorCode setState(int nNewState, bool bRepaintNow = true);
int getState() {
return _nState;
}
protected:
void onPaint(CBofRect *pRect) override;
void onLButtonDown(uint32 nFlags, CBofPoint *pPoint, void * = nullptr) override;
void onLButtonUp(uint32 nFlags, CBofPoint *pPoint, void * = nullptr) override;
COLORREF _cFaceColor;
COLORREF _cHighlightColor;
COLORREF _cShadowColor;
COLORREF _cTextColor;
COLORREF _cTextDisabledColor;
COLORREF _cOutlineColor;
int _nState;
};
class CBofRadioButton : public CBofButton {
public:
virtual ErrorCode paint(CBofRect *pRect = nullptr);
protected:
virtual void onLButtonDown(uint32 nFlags, CBofPoint *pPoint, void * = nullptr);
virtual void onLButtonUp(uint32 nFlags, CBofPoint *pPoint, void * = nullptr);
};
class CBofCheckButton : public CBofButton {
public:
ErrorCode SetCheck(bool bChecked);
bool GetCheck() {
return (_nState == BUTTON_CHECKED);
}
ErrorCode paint(CBofRect *pRect = nullptr) override;
protected:
void onLButtonDown(uint32 nFlags, CBofPoint *pPoint, void * = nullptr) override;
void onLButtonUp(uint32 nFlags, CBofPoint *pPoint, void * = nullptr) override;
};
class CBofBmpButton : public CBofWindow {
public:
// Constructors
CBofBmpButton();
virtual ~CBofBmpButton();
// NOTE: CBofBmpButton takes control of these bitmaps, so there's
// no need for the callers to free them afterwards
ErrorCode loadBitmaps(CBofBitmap *pUp, CBofBitmap *pDown, CBofBitmap *pFocus, CBofBitmap *pDisabled, int nMaskColor = NOT_TRANSPARENT);
ErrorCode loadBitmaps(CBofPalette *pPalette, const char *pszUp, const char *pszDown = nullptr, const char *pszFocus = nullptr, const char *pszDisabled = nullptr, int nMaskColor = NOT_TRANSPARENT);
ErrorCode paint(CBofRect *pRect = nullptr);
ErrorCode setState(int nNewState, bool bRepaintNow = true);
int getState() {
return _nState;
}
CBofBitmap *getButtonBmp() {
return _pButtonUp;
}
protected:
void onPaint(CBofRect *pRect) override;
void onLButtonDown(uint32 nFlags, CBofPoint *pPoint, void * = nullptr) override;
void onLButtonUp(uint32 nFlags, CBofPoint *pPoint, void * = nullptr) override;
CBofBitmap *_pButtonUp;
CBofBitmap *_pButtonDown;
CBofBitmap *_pButtonFocus;
CBofBitmap *_pButtonDisabled;
CBofBitmap *_pBackground;
int _nState;
int _nMaskColor;
};
} // namespace SpaceBar
} // namespace Bagel
#endif

View File

@@ -0,0 +1,294 @@
/* 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 "common/system.h"
#include "common/events.h"
#include "graphics/framelimiter.h"
#include "bagel/spacebar/boflib/gui/dialog.h"
#include "bagel/spacebar/boflib/app.h"
#include "bagel/spacebar/boflib/timer.h"
#include "bagel/boflib/sound.h"
#include "bagel/bagel.h"
namespace Bagel {
namespace SpaceBar {
CBofDialog::CBofDialog() {
// Inits
_pDlgBackground = nullptr;
_bFirstTime = true;
_bTempBitmap = false;
_lFlags = BOFDLG_DEFAULT;
_bEndDialog = false;
_bHavePainted = false;
}
CBofDialog::CBofDialog(const char *pszFileName, CBofWindow *pParent, const uint32 nID, const uint32 lFlags) {
assert(pszFileName != nullptr);
assert(pParent != nullptr);
// Inits
_pDlgBackground = nullptr;
_bFirstTime = true;
_bTempBitmap = false;
_lFlags = lFlags;
_bEndDialog = false;
_bHavePainted = false;
CBofBitmap *pBmp = loadBitmap(pszFileName);
if (pBmp != nullptr) {
// Use specified bitmap as this dialog's image
setBackdrop(pBmp);
}
assert(_pBackdrop != nullptr);
CBofRect cRect = _pBackdrop->getRect();
// Create the dialog box
create("DialogBox", cRect.left, cRect.top, cRect.width(), cRect.height(), pParent, nID);
}
CBofDialog::~CBofDialog() {
assert(isValidObject(this));
delete _pDlgBackground;
_pDlgBackground = nullptr;
}
ErrorCode CBofDialog::create(const char *pszName, int x, int y, int nWidth, int nHeight, CBofWindow *pParent, uint32 nControlID) {
assert(isValidObject(this));
assert(pszName != nullptr);
// Dialog boxes must have parent windows
assert(pParent != nullptr);
// Inits
_parent = pParent;
_nID = nControlID;
// Remember the name of this window
Common::strlcpy(_szTitle, pszName, MAX_TITLE);
// Calculate effective bounds
Common::Rect stRect(x, y, x + nWidth, y + nHeight);
if (pParent != nullptr)
stRect.translate(pParent->getWindowRect().left, pParent->getWindowRect().top);
_cRect = stRect;
delete _surface;
_surface = new Graphics::ManagedSurface(*g_engine->getScreen(), stRect);
return _errCode;
}
ErrorCode CBofDialog::create(const char *pszName, CBofRect *pRect, CBofWindow *pParent, uint32 nControlID) {
assert(isValidObject(this));
assert(pszName != nullptr);
CBofRect cRect;
int x = 0;
int y = 0;
int nWidth = USE_DEFAULT;
int nHeight = USE_DEFAULT;
if ((pRect == nullptr) && (_pBackdrop != nullptr)) {
cRect = _pBackdrop->getRect();
pRect = &cRect;
}
if (pRect != nullptr) {
x = pRect->left;
y = pRect->top;
nWidth = pRect->width();
nHeight = pRect->height();
}
return create(pszName, x, y, nWidth, nHeight, pParent, nControlID);
}
void CBofDialog::onClose() {
assert(isValidObject(this));
// Release any capture/focus that was active
CBofApp *app = CBofApp::getApp();
app->setCaptureControl(nullptr);
app->setFocusControl(nullptr);
if (_parent != nullptr) {
CBofWindow *pParent = _parent;
pParent->enable();
// The parent window MUST now be enabled
assert(pParent->isEnabled());
}
// If we saved the background, then paint it
if (_lFlags & BOFDLG_SAVEBACKGND) {
paintBackground();
} else if (_parent != nullptr) {
// Need to validate the portion of the parent window that we obscured
// (but that we also have already repainted)
// Otherwise, we need to cause the parent to repaint itself
_parent->invalidateRect(nullptr);
}
CBofWindow::onClose();
// Stop our personal message loop
_bEndDialog = true;
}
ErrorCode CBofDialog::paint(CBofRect *pRect) {
assert(isValidObject(this));
assert(pRect != nullptr);
// Repaint the background behind the dialog
if (!_bFirstTime) {
paintBackground();
}
_bFirstTime = false;
// Paint the dialog (uses bitmap instead of standard windows dialog)
if (hasBackdrop()) {
paintBackdrop(pRect, COLOR_WHITE);
}
return _errCode;
}
ErrorCode CBofDialog::paintBackground() {
assert(isValidObject(this));
// Paint back the background
if (_pDlgBackground != nullptr) {
_errCode = _pDlgBackground->paint(this, 0, 0);
}
return _errCode;
}
ErrorCode CBofDialog::saveBackground() {
assert(isValidObject(this));
if (_lFlags & BOFDLG_SAVEBACKGND) {
CBofPalette *pPalette = CBofApp::getApp()->getPalette();
// Remove any previous background
delete _pDlgBackground;
// Save a copy of the background
_pDlgBackground = new CBofBitmap(width(), height(), pPalette);
_pDlgBackground->captureScreen(this, &_cRect);
_pDlgBackground->setReadOnly(true);
}
_bFirstTime = false;
return _errCode;
}
ErrorCode CBofDialog::killBackground() {
delete _pDlgBackground;
_pDlgBackground = nullptr;
return _errCode;
}
void CBofDialog::onPaint(CBofRect *pRect) {
assert(isValidObject(this));
assert(pRect != nullptr);
if (_bFirstTime) {
saveBackground();
}
paint(pRect);
_bHavePainted = true;
}
int CBofDialog::doModal() {
assert(isValidObject(this));
// The dialog box must have been successfully created first
assert(isCreated());
CBofWindow *pLastActive = getActiveWindow();
setActive();
onInitDialog();
// Display the window
show();
updateWindow();
// Start our own message loop (simulate Modal)
_bEndDialog = false;
// Acquire and dispatch messages until quit message is received,
// or until there are too many errors.
Graphics::FrameLimiter limiter(g_system, 60, false);
while (!_bEndDialog && !g_engine->shouldQuit() && (CBofError::getErrorCount() < MAX_ERRORS)) {
CBofSound::audioTask();
CBofTimer::handleTimers();
if (isCreated()) {
onMainLoop();
}
handleEvents();
limiter.delayBeforeSwap();
g_engine->getScreen()->update();
limiter.startFrame();
}
if (pLastActive != nullptr) {
pLastActive->setActive();
} else {
_pActiveWindow = nullptr;
}
return _nReturnValue;
}
///////////////////////////////////////////////////////////////////
// Virtual functions that the user can override if they want to
///////////////////////////////////////////////////////////////////
void CBofDialog::onInitDialog() {}
} // namespace SpaceBar
} // namespace Bagel

View File

@@ -0,0 +1,144 @@
/* 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/".
*
*/
#ifndef BAGEL_BOFLIB_GUI_DIALOG_H
#define BAGEL_BOFLIB_GUI_DIALOG_H
#include "bagel/spacebar/boflib/gui/window.h"
#include "bagel/spacebar/boflib/gfx/bitmap.h"
namespace Bagel {
namespace SpaceBar {
#define IDOK 1
#define IDCANCEL 2
#define BOFDLG_TRANSPARENT 0x00000001
#define BOFDLG_SAVEBACKGND 0x00000002
#define BOFDLG_DEFAULT (BOFDLG_TRANSPARENT /* | BOFDLG_SAVEBACKGND*/)
class CBofDialog : public CBofWindow {
protected:
CBofBitmap *_pDlgBackground = nullptr;
uint32 _lFlags = 0;
int _nReturnValue = 0;
bool _bFirstTime = false;
bool _bTempBitmap = false;
bool _bEndDialog = false;
bool _bHavePainted = false;
protected:
virtual ErrorCode paint(CBofRect *pRect);
virtual ErrorCode paintBackground();
virtual ErrorCode saveBackground();
virtual ErrorCode killBackground();
void onPaint(CBofRect *pRect) override;
void onClose() override;
virtual void onInitDialog();
public:
/**
* Constructor
*/
CBofDialog();
/**
* Constructor
*/
CBofDialog(const char *pszFileName, CBofWindow *pParent, uint32 nID = 0, uint32 lFlags = BOFDLG_DEFAULT);
/**
* Destructor
*/
virtual ~CBofDialog();
/**
* Creates the dialog
* @param pszName Dialog name
* @param x Top-left X position
* @param y Top-left Y position
* @param nWidth Width
* @param nHeight Height
* @param pParent Parent window
* @param nControlID Control Id
* @return Error return code
*/
ErrorCode create(const char *pszName, int x, int y, int nWidth, int nHeight, CBofWindow *pParent, uint32 nControlID = 0) override;
/**
* Creates the dialog
* @param pszName Dialog name
* @param pRect Dialog bounds
* @param pParent Parent window
* @param nControlID Control Id
* @return Error return code
*/
ErrorCode create(const char *pszName, CBofRect *pRect, CBofWindow *pParent, uint32 nControlID = 0) override;
/**
* Set the dialog flags
*/
void setFlags(uint32 lFlags) {
_lFlags = lFlags;
}
/**
* Return the dialog's flags
*/
uint32 getFlags() const {
return _lFlags;
}
/**
* Show the dialog as a modal
*/
int doModal();
/**
* End the dialog modal display
*/
void endModal() {
_bEndDialog = true;
}
/**
* Set the dialog's return value
*/
void setReturnValue(int nValue) {
_nReturnValue = nValue;
}
/**
* Get the dialog's return value
* @return
*/
int getReturnValue() const {
return _nReturnValue;
}
};
} // namespace SpaceBar
} // namespace Bagel
#endif

View File

@@ -0,0 +1,141 @@
/* 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 "bagel/spacebar/boflib/app.h"
#include "bagel/spacebar/boflib/gui/edit_text.h"
#include "bagel/spacebar/boflib/gfx/text.h"
#include "bagel/spacebar/boflib/std_keys.h"
namespace Bagel {
namespace SpaceBar {
CBofEditText::CBofEditText(const char *pszName, int x, int y, int nWidth,
int nHeight, CBofWindow *pParent)
: CBofWindow(pszName, x, y, nWidth, nHeight, pParent) {
create(pszName, x, y, nWidth, nHeight, pParent);
}
ErrorCode CBofEditText::create(const char *pszName, CBofRect *pRect,
CBofWindow *pParent, uint32 nControlID) {
assert(isValidObject(this));
assert(pszName != nullptr);
// Remember who our parent is
_parent = pParent;
int x = 0;
int y = 0;
int nWidth = USE_DEFAULT;
int nHeight = USE_DEFAULT;
if (pRect != nullptr) {
x = pRect->left;
y = pRect->top;
nWidth = pRect->width();
nHeight = pRect->height();
}
return create(pszName, x, y, nWidth, nHeight, pParent, nControlID);
}
ErrorCode CBofEditText::create(const char *pszName, int x, int y,
int nWidth, int nHeight, CBofWindow *pParent, uint32 nControlID) {
assert(isValidObject(this));
assert(pszName != nullptr);
// Remember who our parent is
_parent = pParent;
_nID = nControlID;
// Remember the name of this window
Common::strcpy_s(_szTitle, pszName);
// Retain screen coordinates for this window
_cWindowRect.setRect(x, y, x + nWidth - 1, y + nHeight - 1);
CBofPalette *pPalette = CBofApp::getApp()->getPalette();
if (pPalette != nullptr) {
selectPalette(pPalette);
}
// Retain local coordinates (based on own window)
_cRect.setRect(0, 0, _cWindowRect.width() - 1, _cWindowRect.height() - 1);
return _errCode;
}
void CBofEditText::setText(const char *pszString) {
assert(isValidObject(this));
assert(isCreated());
assert(pszString != nullptr);
_text = pszString;
updateWindow();
}
void CBofEditText::onPaint(CBofRect *pRect) {
assert(isValidObject(this));
assert(pRect != nullptr);
if (hasFocus())
fillRect(nullptr, 255);
// Draw the text, if any
if (!_text.isEmpty()) {
CBofString tmp = _text + "|";
paintText(this, &_cRect, tmp.getBuffer(),
12, 0, CTEXT_COLOR,
JUSTIFY_LEFT,
FORMAT_TOP_LEFT | FORMAT_SINGLE_LINE);
}
}
void CBofEditText::onLButtonDown(uint32 nFlags, CBofPoint *pPoint, void *) {
// First click focuses text input
setFocus();
_cursorPos = _text.getBufferSize();
updateWindow();
}
void CBofEditText::onKeyHit(uint32 lKey, uint32 lRepCount) {
if (lKey >= 32 && lKey <= 127) {
CBofString tmp = _text + lKey;
CBofRect rect = calculateTextRect(this, &tmp, 12, 0);
if ((_cRect.width() - rect.width()) > 10) {
setText(tmp);
}
} else if (lKey == BKEY_BACK && !_text.isEmpty()) {
_text.deleteLastChar();
updateWindow();
}
}
} // namespace SpaceBar
} // namespace Bagel

View File

@@ -0,0 +1,62 @@
/* 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/>.
*
*/
#ifndef BAGEL_BOFLIB_GUI_EDIT_TEXT_H
#define BAGEL_BOFLIB_GUI_EDIT_TEXT_H
#include "bagel/spacebar/boflib/gui/window.h"
#include "bagel/boflib/string.h"
namespace Bagel {
namespace SpaceBar {
class CBofEditText : public CBofWindow {
private:
CBofString _text;
size_t _cursorPos = 0;
protected:
void onLButtonDown(uint32 nFlags, CBofPoint *pPoint, void * = nullptr) override;
void onKeyHit(uint32 lKey, uint32 lRepCount) override;
public:
CBofEditText() {
}
CBofEditText(const char *pszName, int x, int y, int nWidth, int nHeight, CBofWindow *pParent);
ErrorCode create(const char *pszName, int x, int y, int nWidth, int nHeight, CBofWindow *pParent, uint32 nControlID = 0) override;
ErrorCode create(const char *pszName, CBofRect *pRect, CBofWindow *pParent, uint32 nControlID = 0) override;
CBofString getText() const {
return _text;
}
void setText(const char *pszString);
void onPaint(CBofRect *pRect) override;
};
} // namespace SpaceBar
} // namespace Bagel
#endif

View File

@@ -0,0 +1,549 @@
/* 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 "bagel/spacebar/boflib/app.h"
#include "bagel/spacebar/boflib/gfx/text.h"
#include "bagel/spacebar/boflib/gui/list_box.h"
#include "bagel/spacebar/boflib/std_keys.h"
namespace Bagel {
namespace SpaceBar {
#define TEXT_ITEM_HEIGHT 24
CBofListBox::CBofListBox() {
_cTextColor = CTEXT_COLOR;
_cHighColor = CTEXT_COLOR;
_nTextSize = 10;
_nTextWeight = TEXT_NORMAL;
_nNumItems = 0;
_n1stVisible = 0;
_nPageSize = 0;
_pWork = nullptr;
_nItemHeight = TEXT_ITEM_HEIGHT;
_nState = LISTBOX_NORMAL;
_nTextFont = FONT_DEFAULT;
// Initialized the selected item
CBofListBox::clearSelection();
}
CBofListBox::~CBofListBox() {
// Kill the temporary work area
delete _pWork;
_pWork = nullptr;
deleteAll(false);
}
void CBofListBox::clearSelection() {
_nSelectedItem = -1;
}
void CBofListBox::insertBefore(int nIndex, const CBofString &cString, bool bRepaint) {
assert(isValidObject(this));
ListBoxItem lbi;
lbi._pTextStr = new CBofString(cString);
lbi._nTextLineColor = COLOR_USE_DEFAULT;
_cTextItems.insertBefore(nIndex, lbi);
// One more item
_nNumItems++;
clearSelection();
if (bRepaint && isCreated() && isVisible()) {
repaintAll();
}
}
void CBofListBox::insertAfter(int nIndex, const CBofString &cString, bool bRepaint) {
assert(isValidObject(this));
ListBoxItem lbi;
lbi._pTextStr = new CBofString(cString);
lbi._nTextLineColor = COLOR_USE_DEFAULT;
_cTextItems.insertAfter(nIndex, lbi);
// One more item
_nNumItems++;
if (bRepaint && isCreated() && isVisible()) {
repaintAll();
}
}
void CBofListBox::addToHead(const CBofString &cString, bool bRepaint) {
assert(isValidObject(this));
ListBoxItem lbi;
lbi._pTextStr = new CBofString(cString);
lbi._nTextLineColor = COLOR_USE_DEFAULT;
_cTextItems.addToHead(lbi);
// One more item
_nNumItems++;
clearSelection();
if (bRepaint && isCreated() && isVisible()) {
repaintAll();
}
}
void CBofListBox::addToTail(const CBofString &cString, bool bRepaint) {
assert(isValidObject(this));
ListBoxItem lbi;
lbi._pTextStr = new CBofString(cString);
lbi._nTextLineColor = COLOR_USE_DEFAULT;
_cTextItems.addToTail(lbi);
// One more item
_nNumItems++;
clearSelection();
if (bRepaint && isCreated() && isVisible()) {
repaintAll();
}
}
ErrorCode CBofListBox::delItem(int nIndex, bool bRepaint) {
assert(isValidObject(this));
assert(nIndex >= 0 && nIndex < _nNumItems);
_cTextItems.remove(nIndex);
// One less item
_nNumItems--;
if (_n1stVisible >= _nNumItems) {
_n1stVisible = _nNumItems - 1;
if (_n1stVisible < 0)
_n1stVisible = 0;
}
clearSelection();
if (bRepaint && isCreated() && isVisible()) {
repaintAll();
}
return _errCode;
}
ErrorCode CBofListBox::deleteAll(bool bRepaint) {
assert(isValidObject(this));
// Switch item to be pointer to cbofstring instead of the item itself
int nCount = _cTextItems.getCount();
for (int i = 0; i < nCount; i++) {
ListBoxItem lbi = _cTextItems.getNodeItem(i);
delete lbi._pTextStr;
}
_cTextItems.removeAll();
_nNumItems = 0;
_n1stVisible = 0;
clearSelection();
if (bRepaint && isCreated() && isVisible()) {
repaintAll();
}
return _errCode;
}
void CBofListBox::onLButtonDown(uint32 /*nFlags*/, CBofPoint *pPoint, void *) {
assert(isValidObject(this));
assert(pPoint != nullptr);
int nIndex = (pPoint->y / _nItemHeight) + _n1stVisible;
if (nIndex < _nNumItems) {
_nSelectedItem = nIndex; // Set the selected item
_nState = LISTBOX_SELECT;
if (_parent != nullptr) {
_parent->setPrevMouseDown(*pPoint);
_parent->onBofListBox(this, nIndex);
}
}
}
void CBofListBox::onLButtonDblClk(uint32 /*nFlags*/, CBofPoint *pPoint) {
assert(isValidObject(this));
assert(pPoint != nullptr);
int nIndex = (pPoint->y / _nItemHeight) + _n1stVisible;
if (nIndex < _nNumItems) {
_nSelectedItem = nIndex; // Set the selected item
_nState = LISTBOX_USENOW;
if (_parent != nullptr) {
_parent->setPrevMouseDown(*pPoint);
_parent->onBofListBox(this, nIndex);
}
}
setFocus();
}
void CBofListBox::onKeyHit(uint32 lKey, uint32 lRepCount) {
assert(isValidObject(this));
switch (lKey) {
case BKEY_HOME:
scrollTo(0);
break;
case BKEY_END:
scrollTo(_nNumItems);
break;
case BKEY_UP:
lineUp();
break;
case BKEY_DOWN:
lineDown();
break;
case BKEY_PAGEUP:
pageUp();
break;
case BKEY_PAGEDOWN:
pageDown();
break;
default:
// Call the previous windows onkeyhit
CBofWindow *pParent = getParent();
if (pParent && pParent != this) {
pParent->onKeyHit(lKey, lRepCount);
}
break;
}
}
ErrorCode CBofListBox::scrollUp(const int nLines) {
assert(isValidObject(this));
// If all the items fit on a single page, make this operation a no-op.
if (_nNumItems <= _nPageSize) {
return scrollTo(_n1stVisible);
}
int nNewLine = _n1stVisible - nLines;
if (nNewLine < 0) {
nNewLine = 0;
} else if (nNewLine > (_nNumItems - _nPageSize)) {
// If the line requested to be the top of the page
// would cause fewer than _nPageSize lines to be displayed,
// snap nNewLine to be equal to the top of the last full page.
nNewLine = (_nNumItems - _nPageSize);
}
return scrollTo(nNewLine);
}
ErrorCode CBofListBox::scrollTo(const int nLine) {
assert(isValidObject(this));
assert(nLine >= 0 && nLine <= _nNumItems);
// Only update the screen if the list actually moved
if (_n1stVisible != nLine) {
_n1stVisible = nLine;
if (nLine >= _nNumItems) {
assert(_nNumItems > 0);
_n1stVisible--;
}
// Show the text box
repaintAll();
}
return _errCode;
}
void CBofListBox::onPaint(CBofRect * /*pRect*/) {
assert(isValidObject(this));
_nPageSize = height() / _nItemHeight;
if (_pBackdrop == nullptr) {
saveBackground();
}
repaintAll();
}
void CBofListBox::killBackground() {
assert(isValidObject(this));
delete _pBackdrop;
_pBackdrop = nullptr;
}
ErrorCode CBofListBox::saveBackground() {
assert(isValidObject(this));
killBackground();
_pBackdrop = new CBofBitmap(width(), height(), CBofApp::getApp()->getPalette());
if ((_parent != nullptr) && (_parent->getBackdrop() != nullptr)) {
CBofRect cRect = _pBackdrop->getRect();
_parent->getBackdrop()->paint(_pBackdrop, &cRect, &_cWindowRect);
} else {
_pBackdrop->captureScreen(this, &_cRect);
}
return _errCode;
}
ErrorCode CBofListBox::createWorkArea() {
assert(isValidObject(this));
if (_pBackdrop == nullptr) {
saveBackground();
}
if (_pWork == nullptr) {
assert(_pBackdrop != nullptr);
_pWork = new CBofBitmap(width(), height(), _pBackdrop->getPalette());
}
return _errCode;
}
ErrorCode CBofListBox::repaintAll() {
assert(isValidObject(this));
if (!errorOccurred()) {
assert(isCreated());
int nCurFont = getFont();
setFont(_nTextFont);
createWorkArea();
if (_pWork != nullptr) {
_pWork->lock();
int nIndexedColor = _pWork->getPalette()->getNearestIndex(_cTextColor);
// prepare the background
//
assert(_pBackdrop != nullptr);
_pBackdrop->paint(_pWork);
for (int i = 0; i < _nPageSize; i++) {
CBofRect cRect;
cRect.setRect(0, i * _nItemHeight, width() - 1, (i + 1) * _nItemHeight - 1);
if (i + _n1stVisible < _nNumItems) {
// If this item is currently selected and we have a high color
if ((i + _n1stVisible == _nSelectedItem) && (_cHighColor != _cTextColor)) {
// display text highlighted
paintText(_pWork,
&cRect,
*(_cTextItems.getNodeItem(i + _n1stVisible)._pTextStr),
_nTextSize,
_nTextWeight,
_cHighColor,
JUSTIFY_LEFT,
FORMAT_TOP_LEFT | FORMAT_SINGLE_LINE,
getFont());
} else {
// Display text
// Allow list items of different colors.
COLORREF rgbTextColor = _cTextColor;
if (_cTextItems.getNodeItem(i + _n1stVisible)._nTextLineColor != COLOR_USE_DEFAULT) {
rgbTextColor = _cTextItems.getNodeItem(i + _n1stVisible)._nTextLineColor;
}
paintText(_pWork,
&cRect,
*(_cTextItems.getNodeItem(i + _n1stVisible)._pTextStr),
_nTextSize,
_nTextWeight,
rgbTextColor,
JUSTIFY_LEFT,
FORMAT_TOP_LEFT | FORMAT_SINGLE_LINE,
getFont());
}
CBofPoint bl(cRect.bottomLeft()), br(cRect.bottomRight());
_pWork->line(&bl, &br, (byte)nIndexedColor);
}
}
// Show final image on screen
_pWork->paint(this);
_pWork->unlock();
}
// Reset the font
setFont(nCurFont);
}
return _errCode;
}
ErrorCode CBofListBox::repaintItem(int nIndex) {
assert(isValidObject(this));
if (!errorOccurred()) {
assert(nIndex >= 0 && nIndex < _nNumItems);
int nCurFont = getFont();
setFont(_nTextFont); // Set the proper font
// If this item is visible, then repaint it.
if (nIndex >= _n1stVisible && nIndex <= _n1stVisible + _nPageSize) {
int i = nIndex - _n1stVisible;
createWorkArea();
int nIndexedColor = _pWork->getPalette()->getNearestIndex(_cTextColor);
_pWork->lock();
// Calculate area for this text item
CBofRect cRect;
cRect.setRect(0, i * _nItemHeight, width() - 1, (i + 1) * _nItemHeight - 1);
// Prepare the background
assert(_pBackdrop != nullptr);
_pBackdrop->paint(_pWork, &cRect, &cRect);
// If this item is currently selected and we have a high color
if ((nIndex == _nSelectedItem) && (_cHighColor != _cTextColor)) {
// Display text highlighted
paintText(_pWork,
&cRect,
*(_cTextItems.getNodeItem(nIndex)._pTextStr),
_nTextSize,
_nTextWeight,
_cHighColor,
JUSTIFY_LEFT,
FORMAT_TOP_LEFT | FORMAT_SINGLE_LINE,
getFont());
} else {
// Display text
// Allow list items of different colors.
COLORREF rgbTextColor = _cTextColor;
if (_cTextItems.getNodeItem(i + _n1stVisible)._nTextLineColor != COLOR_USE_DEFAULT) {
rgbTextColor = _cTextItems.getNodeItem(i + _n1stVisible)._nTextLineColor;
}
paintText(_pWork,
&cRect,
*(_cTextItems.getNodeItem(nIndex)._pTextStr),
_nTextSize,
_nTextWeight,
rgbTextColor,
JUSTIFY_LEFT,
FORMAT_TOP_LEFT | FORMAT_SINGLE_LINE,
getFont());
}
CBofPoint bl(cRect.bottomLeft()), br(cRect.bottomRight());
_pWork->line(&bl, &br, (byte)nIndexedColor);
// Show final image on screen
_pWork->paint(this, &cRect, &cRect);
_pWork->unlock();
}
// Reset the font
setFont(nCurFont);
}
return _errCode;
}
void CBofListBox::setSelectedItem(int nItem, bool bRepaint) {
assert(isValidObject(this));
// Set highlighted item
_nSelectedItem = nItem;
if (bRepaint) {
repaintAll();
}
}
CBofString CBofListBox::getText(int nIndex) {
return *(_cTextItems.getNodeItem(nIndex)._pTextStr);
}
void CBofListBox::setText(int nIndex, const CBofString &cStr) {
ListBoxItem lbi = _cTextItems.getNodeItem(nIndex);
*lbi._pTextStr = cStr;
_cTextItems.setNodeItem(nIndex, lbi);
}
void CBofListBox::setTextLineColor(int nIndex, COLORREF rgbColor) {
ListBoxItem lbi = _cTextItems.getNodeItem(nIndex);
lbi._nTextLineColor = rgbColor;
_cTextItems.setNodeItem(nIndex, lbi);
}
} // namespace SpaceBar
} // namespace Bagel

View File

@@ -0,0 +1,179 @@
/* 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/>.
*
*/
#ifndef BAGEL_BOFLIB_GUI_LIST_BOX_H
#define BAGEL_BOFLIB_GUI_LIST_BOX_H
#include "bagel/spacebar/boflib/gui/window.h"
#include "bagel/spacebar/boflib/list.h"
#include "bagel/boflib/string.h"
namespace Bagel {
namespace SpaceBar {
#define LISTBOX_NORMAL 0
#define LISTBOX_SELECT 1
#define LISTBOX_USENOW 2
#define COLOR_USE_DEFAULT 0xFFFFFFFF
class ListBoxItem {
public:
ListBoxItem() {
_pTextStr = nullptr;
_nTextLineColor = COLOR_USE_DEFAULT;
}
CBofString *_pTextStr;
COLORREF _nTextLineColor;
};
class CBofListBox : public CBofWindow {
public:
CBofListBox();
~CBofListBox();
void setSelectedItem(int nItem, bool bRepaint = true);
void insertBefore(int nIndex, const CBofString &cString, bool bRepaint = true);
void insertAfter(int nIndex, const CBofString &cString, bool bRepaint = true);
void addToHead(const CBofString &cString, bool bRepaint = true);
void addToTail(const CBofString &cString, bool bRepaint = true);
ErrorCode delItem(int nIndex, bool bRepaint = true);
ErrorCode deleteAll(bool bRepaint = true);
int getNumItems() {
return _nNumItems;
}
CBofString getText(int nIndex);
void setText(int nIndex, const CBofString &cStr);
void setTextLineColor(int nIndex, COLORREF rgbColor);
ErrorCode lineUp() {
return scrollUp(1);
}
ErrorCode lineDown() {
return scrollDown(1);
}
ErrorCode pageUp() {
return scrollUp(_nPageSize);
}
ErrorCode pageDown() {
return scrollDown(_nPageSize);
}
ErrorCode scrollUp(int nLines);
ErrorCode scrollDown(const int nLines) {
return scrollUp(-nLines);
}
ErrorCode scrollTo(int nLine);
ErrorCode createWorkArea();
ErrorCode saveBackground();
void killBackground();
void setHighlightColor(COLORREF cHighColor) {
_cHighColor = cHighColor;
}
COLORREF getHighlightColor() {
return _cHighColor;
}
void setTextColor(COLORREF cColor) {
_cTextColor = cColor;
}
COLORREF getTextColor() {
return _cTextColor;
}
void setPointSize(int nSize) {
_nTextSize = nSize;
}
int getPointSize() {
return _nTextSize;
}
void setWeight(int nWeight) {
_nTextWeight = nWeight;
}
int getWeight() {
return _nTextWeight;
}
void setItemHeight(int nHeight) {
_nItemHeight = nHeight;
}
int getItemHeight() {
return _nItemHeight;
}
void setFont(int nFont) {
_nTextFont = nFont;
}
int getFont() {
return _nTextFont;
}
int getState() {
return _nState;
}
virtual ErrorCode repaintItem(int nIndex);
virtual ErrorCode repaintAll();
protected:
virtual void onLButtonDown(uint32 nFlags, CBofPoint *pPoint, void * = nullptr);
virtual void onLButtonDblClk(uint32 nFlags, CBofPoint *pPoint);
virtual void onKeyHit(uint32 lKey, uint32 lRepCount);
virtual void onPaint(CBofRect *pRect);
/**
* Clears the currently selected item
*/
virtual void clearSelection();
CBofList<ListBoxItem> _cTextItems;
CBofBitmap *_pWork;
int _nNumItems;
int _n1stVisible;
int _nPageSize;
int _nTextSize;
int _nTextWeight;
COLORREF _cTextColor;
COLORREF _cHighColor;
int _nSelectedItem;
int _nItemHeight;
int _nTextFont;
int _nState;
};
} // namespace SpaceBar
} // namespace Bagel
#endif

View File

@@ -0,0 +1,376 @@
/* 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 "graphics/cursorman.h"
#include "bagel/boflib/string.h"
#include "bagel/spacebar/boflib/gui/movie.h"
#include "bagel/spacebar/boflib/gfx/cursor.h"
#include "bagel/spacebar/boflib/std_keys.h"
namespace Bagel {
namespace SpaceBar {
CBofMovie::CBofMovie(CBofWindow *pParent, const char *pszFilename, CBofRect *pBounds, bool bStretch, bool bUseNewPalette, bool bBlackOutWindow) {
_bStretch = bStretch;
// Allow movie to not shift to new palette.
_bUseNewPalette = bUseNewPalette;
// Black out first and last frame of flythroughs and examine movies
_bBlackOutWindow = bBlackOutWindow;
initialize(pParent);
open(pszFilename, pBounds);
}
CBofMovie::~CBofMovie() {
closeMovie();
}
ErrorCode CBofMovie::initialize(CBofWindow *pParent) {
// Movie Stuff
_eMovStatus = STOPPED;
_bEscCanStop = true;
// Smacker Stuff
_pSbuf = nullptr;
_pSmk = nullptr;
_bLoop = false;
// Call dialog box creates
if (create("MovieWin", 0, 0, 1, 1, pParent, 1) == ERR_NONE) {
setCapture();
}
return ERR_NONE;
}
bool CBofMovie::open(const char *sFilename, CBofRect *pBounds) {
if (sFilename == nullptr) {
assert(sFilename);
return false;
}
if (pBounds != nullptr) {
_cRect = *pBounds;
}
if (openMovie(sFilename)) {
// We were given specific rect for movie
if (pBounds)
reSize(pBounds, true);
else
// Center the movie to the parent window
centerRect();
return true;
}
return false;
}
bool CBofMovie::openMovie(const char *sFilename) {
assert(sFilename[0] != '\0');
if (_pSmk) {
closeMovie();
}
_pSmk = new Video::SmackerDecoder();
_pSmk->setSoundType(Audio::Mixer::kSFXSoundType);
if (!_pSmk->loadFile(sFilename)) {
// Opened failed
error("Movie not found=%s", sFilename);
}
// If supposed to stretch into specified window
if (_bStretch) {
_pSbuf = new Graphics::ManagedSurface(width(), height(), _pSmk->getPixelFormat());
} else {
_pSbuf = new Graphics::ManagedSurface(_pSmk->getWidth(), _pSmk->getHeight(), _pSmk->getPixelFormat());
}
_srcRect = Common::Rect(_pSmk->getWidth(), _pSmk->getHeight());
_dstRect = Common::Rect(_pSbuf->w, _pSbuf->h);
if (!_bStretch) {
_dstRect.moveTo((_pSbuf->w - _pSmk->getWidth()) / 2, (_pSbuf->h - _pSmk->getHeight()) / 2);
}
CBofRect MovieBounds(0, 0, (uint16)_pSbuf->w - 1, (uint16)_pSbuf->h - 1);
reSize(&MovieBounds, true);
// If we have a window that is going to cause a single frame
// palette shift, then black it out here.
if (_bBlackOutWindow) {
fillWindow(COLOR_BLACK);
}
// Smack the current frame into the buffer
const Graphics::Surface *frame = _pSmk->decodeNextFrame();
if (frame) {
_pSbuf->setPalette(_pSmk->getPalette(), 0, 256);
_pSbuf->blitFrom(*frame, _srcRect, _dstRect);
}
return true;
}
void CBofMovie::onKeyHit(uint32 lKey, uint32 /*lRepCount*/) {
if (_bEscCanStop && lKey == BKEY_ESC) {
// Clean up and exit
_bLoop = false;
stop();
onMovieDone();
}
}
void CBofMovie::onMainLoop() {
if (!_pSmk->needsUpdate() || _eMovStatus == STOPPED)
return;
// Smack the current frame into the buffer
const Graphics::Surface *frame = _pSmk->decodeNextFrame();
if (_pSmk->hasDirtyPalette()) {
_pSbuf->setPalette(_pSmk->getPalette(), 0, 256);
}
if (frame) {
_pSbuf->blitFrom(*frame, _srcRect, _dstRect);
updateWindow();
}
if (_eMovStatus == FORWARD) {
if (_pSmk->getCurFrame() == (int)_pSmk->getFrameCount() - 1) {
if (_bLoop == false) {
onMovieDone();
} else {
seekToStart();
_pSmk->start();
}
}
} else if (_eMovStatus == REVERSE) {
if ((_pSmk->getCurFrame() == 0) || (_pSmk->getCurFrame() == 1)) {
if (_bLoop == false) {
onMovieDone();
} else {
seekToEnd();
}
} else {
setFrame(_pSmk->getCurFrame() - 2); // HACK: Reverse playback
}
}// MOVIE_REVERSE
}
void CBofMovie::onPaint(CBofRect *) {
if (_pSbuf) {
getSurface()->blitFrom(*_pSbuf);
}
}
void CBofMovie::closeMovie() {
delete _pSbuf;
_pSbuf = nullptr;
delete _pSmk;
_pSmk = nullptr;
}
void CBofMovie::onClose() {
closeMovie();
CBofDialog::onClose();
}
void CBofMovie::onMovieDone() {
if (!_bLoop) {
if (_bCaptured)
releaseCapture();
getParent()->enable();
_bEndDialog = true;
}
}
bool CBofMovie::play(bool bLoop, bool bEscCanStop) {
_bEscCanStop = bEscCanStop;
_bLoop = bLoop;
bool bSuccess = play();
getParent()->disable();
getParent()->flushAllMessages();
CursorMan.showMouse(false);
doModal();
CursorMan.showMouse(true);
return bSuccess;
}
bool CBofMovie::play() {
if (_pSmk) {
_pSmk->pauseVideo(false);
//_pSmk->setReverse(false); // TODO: Not supported by SMK
_pSmk->start();
_eMovStatus = FORWARD;
return true;
}
return false;
}
bool CBofMovie::reverse(bool bLoop, bool bEscCanStop) {
_bEscCanStop = bEscCanStop;
_bLoop = bLoop;
bool bSuccess = reverse();
getParent()->disable();
getParent()->flushAllMessages();
doModal();
return bSuccess;
}
bool CBofMovie::reverse() {
if (_pSmk) {
_pSmk->pauseVideo(false);
//_smk->setReverse(true); // TODO: Not supported by SMK
_pSmk->start();
_eMovStatus = REVERSE;
return true;
}
return false;
}
bool CBofMovie::stop() {
if (_pSmk) {
_pSmk->stop();
_eMovStatus = STOPPED;
return true;
}
return false;
}
bool CBofMovie::pause() {
if (_pSmk) {
_pSmk->pauseVideo(true);
_eMovStatus = PAUSED;
return true;
}
return false;
}
bool CBofMovie::seekToStart() {
if (_pSmk) {
_pSmk->rewind();
return true;
}
return false;
}
bool CBofMovie::seekToEnd() {
if (_pSmk) {
setFrame(_pSmk->getFrameCount() - 2); // HACK: Reverse rewind
return true;
}
return false;
}
uint32 CBofMovie::getFrame() {
if (_pSmk) {
return _pSmk->getCurFrame();
}
return (uint32) -1;
}
bool CBofMovie::setFrame(uint32 dwFrameNum) {
if (_pSmk) {
dwFrameNum = CLIP<uint32>(dwFrameNum, 0, _pSmk->getFrameCount() - 1);
_pSmk->forceSeekToFrame(dwFrameNum);
return true;
}
return false;
}
void CBofMovie::onReSize(CBofSize *pSize) {
}
bool CBofMovie::centerRect() {
CBofRect cBofRect = getParent()->getClientRect();
RECT rcParentRect = cBofRect.getWinRect();
int ClientWidth = rcParentRect.right - rcParentRect.left;
int ClientHeight = rcParentRect.bottom - rcParentRect.top;
// Get Movies width and height
int MovieWidth = _pSmk->getWidth();
int MovieHeight = _pSmk->getHeight();
RECT rcMovieBounds;
rcMovieBounds.left = (ClientWidth - MovieWidth) / 2;
rcMovieBounds.top = (ClientHeight - MovieHeight) / 2;
rcMovieBounds.right = rcMovieBounds.left + MovieWidth;
rcMovieBounds.bottom = rcMovieBounds.top + MovieHeight;
// Reposition the playback window
cBofRect = rcMovieBounds;
reSize(&cBofRect, true);
return true;
}
void CBofMovie::onButtonUp(uint32 /*nFlags*/, CBofPoint */*pPoint*/) {
}
ErrorCode bofPlayMovie(CBofWindow *pParent, const char *pszMovieFile, CBofRect *pRect) {
assert(pParent != nullptr);
assert(pszMovieFile != nullptr);
CBofMovie cMovie(pParent, pszMovieFile, pRect);
if (!cMovie.errorOccurred()) {
cMovie.play(false, true);
}
return cMovie.getErrorCode();
}
} // namespace SpaceBar
} // namespace Bagel

View File

@@ -0,0 +1,111 @@
/* 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/".
*
*/
#ifndef BAGEL_BOFLIB_GUI_MOVIE_H
#define BAGEL_BOFLIB_GUI_MOVIE_H
#include "graphics/managed_surface.h"
#include "video/smk_decoder.h"
#include "bagel/boflib/error.h"
#include "bagel/spacebar/boflib/gui/dialog.h"
#include "bagel/boflib/rect.h"
namespace Bagel {
namespace SpaceBar {
class CBofMovie : public CBofDialog {
public:
enum MVSTATUS {
STOPPED, PAUSED, FORWARD, REVERSE
};
protected:
Graphics::ManagedSurface *_pSbuf;
Video::SmackerDecoder *_pSmk;
bool _bEscCanStop;
bool _bLoop;
bool _bStretch;
bool _bUseNewPalette;
bool _bBlackOutWindow;
MVSTATUS _eMovStatus;
Common::Rect _srcRect, _dstRect;
virtual ErrorCode initialize(CBofWindow *pParent);
virtual bool openMovie(const char *sFilename);
virtual void closeMovie();
virtual void onReSize(CBofSize *pSize);
virtual bool play();
virtual bool reverse();
virtual void onLButtonUp(uint32 nFlags, CBofPoint *pPoint, void * = nullptr) {
onButtonUp(nFlags, pPoint);
}
virtual void onRButtonUp(uint32 nFlags, CBofPoint *pPoint) {
onButtonUp(nFlags, pPoint);
}
virtual void onButtonUp(uint32 nFlags, CBofPoint *pPoint);
virtual void onPaint(CBofRect *pRect);
virtual void onMovieDone();
virtual void onClose();
virtual void onMainLoop();
virtual void onKeyHit(uint32 lKey, uint32 lRepCount);
public:
CBofMovie(CBofWindow *pParent = nullptr, const char *pszFilename = nullptr, CBofRect *pBounds = nullptr, bool bStretch = false, bool bUseNewPalette = true, bool bBlackOutWindow = false);
~CBofMovie();
virtual bool open(const char *sFilename = nullptr, CBofRect *pBounds = nullptr);
virtual bool play(bool bLoop, bool bEscCanStop = true);
virtual bool reverse(bool bLoop, bool bEscCanStop = true);
virtual bool pause();
virtual bool stop();
virtual MVSTATUS status() {
return _eMovStatus;
}
virtual bool seekToStart();
virtual bool seekToEnd();
virtual uint32 getFrame();
virtual bool setFrame(uint32 dwFrameNum);
virtual bool centerRect();
Graphics::ManagedSurface *getSmackBuffer() {
return _pSbuf;
}
Video::SmackerDecoder *getSmackMovie() {
return _pSmk;
}
};
ErrorCode bofPlayMovie(CBofWindow *pParent, const char *pszMovieFile, CBofRect *pRect = nullptr);
} // namespace SpaceBar
} // namespace Bagel
#endif

View File

@@ -0,0 +1,489 @@
/* 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 "bagel/spacebar/boflib/app.h"
#include "bagel/spacebar/boflib/gui/scroll_bar.h"
namespace Bagel {
namespace SpaceBar {
#define BMP_SCROLL_TIMER 9999
#define DEF_TIMER_INTERVAL 100
CBofScrollBar::CBofScrollBar() {
_pLeftBtnUp = nullptr;
_pRightBtnUp = nullptr;
_pLeftBtnDn = nullptr;
_pRightBtnDn = nullptr;
_pThumb = nullptr;
_nMin = 0;
_nMax = 10;
_nPos = 0;
_nLineDelta = 1;
_nPageDelta = 1;
_pScrollText = nullptr;
_szScrollText[0] = '\0';
_cThumbSize.cx = 0;
_cThumbSize.cy = 0;
_cBkSize.cx = 0;
_cBkSize.cy = 0;
_cCurPoint.x = 0;
_cCurPoint.y = 0;
_cThumbPos.x = 0;
_cThumbPos.y = 0;
_nOffset = 0;
_nScrollWidth = 0;
_nRange = 0;
_bMouseCaptured = false;
_nScrollState = 0;
_nTimerCount = DEF_TIMER_INTERVAL;
}
CBofScrollBar::~CBofScrollBar() {
assert(isValidObject(this));
_szScrollText[0] = '\0';
delete _pScrollText;
_pScrollText = nullptr;
delete _pThumb;
_pThumb = nullptr;
delete _pLeftBtnUp;
_pLeftBtnUp = nullptr;
delete _pRightBtnUp;
_pRightBtnUp = nullptr;
delete _pLeftBtnDn;
_pLeftBtnDn = nullptr;
delete _pRightBtnDn;
_pRightBtnDn = nullptr;
}
void CBofScrollBar::onPaint(CBofRect *pDirtyRect) {
assert(isValidObject(this));
paint(pDirtyRect);
}
ErrorCode CBofScrollBar::setText(const char *pszText, int nJustify) {
assert(isValidObject(this));
_szScrollText[0] = '\0';
if ((pszText != nullptr) && (_parent != nullptr)) {
Common::strlcpy(_szScrollText, pszText, MAX_TEXT);
if (_pScrollText == nullptr) {
CBofPoint cPoint = _parent->getWindowRect().topLeft();
CBofRect cTempRect = _cWindowRect - cPoint;
cTempRect -= CBofPoint(0, 20);
cTempRect.right += 20;
_pScrollText = new CBofText(&cTempRect, nJustify);
}
if (_pScrollText != nullptr) {
_pScrollText->display(_parent, _szScrollText, FONT_DEFAULT_SIZE, TEXT_DEFAULT_FACE);
}
}
return _errCode;
}
ErrorCode CBofScrollBar::setPos(const int nPos, bool bRepaint, bool isInitial) {
assert(isValidObject(this));
// Save old position
int nOriginalPos = _nPos;
_nPos = nPos;
if (nPos < _nMin)
_nPos = _nMin;
if (nPos > _nMax)
_nPos = _nMax;
assert(_nRange != 0);
_cThumbPos.x = (int)(((int32)(_nScrollWidth - _cThumbSize.cx) * _nPos) / (_nRange - 1)) + _nOffset;
_cThumbPos.y = (int)(_cBkSize.cy / 2) - (int)(_cThumbSize.cy / 2);
if (_cThumbPos.x < 0)
_cThumbPos.x = 0;
if (_cThumbPos.x > (_nScrollWidth - _cThumbSize.cx + _nOffset))
_cThumbPos.x = _nScrollWidth - _cThumbSize.cx + _nOffset;
// If forced to repaint
if (bRepaint) {
paint();
} else if (_nPos != nOriginalPos) {
// Otherwise, only paint the thumb if it's position changed
if (_pThumb != nullptr) {
if (_pThumb->paintSprite(this, _cThumbPos) == false) {
reportError(ERR_UNKNOWN, "_pThumb->paintSprite() failed");
}
}
}
// If the thumb actually moved, then tell our parent about it
if (_nPos != nOriginalPos && !isInitial) {
_parent->onBofScrollBar(this, _nPos);
}
return _errCode;
}
void CBofScrollBar::getScrollRange(int &nMin, int &nMax) {
assert(isValidObject(this));
nMin = _nMin;
nMax = _nMax;
}
void CBofScrollBar::setScrollRange(int nMin, int nMax, bool bRepaint) {
assert(isValidObject(this));
_nMin = nMin;
_nMax = nMax;
_nRange = _nMax - _nMin + 1;
// Should we repaint the scroll bar now?
if (bRepaint) {
paint();
}
}
ErrorCode CBofScrollBar::loadBitmaps(const char *pszBack, const char *pszThumb, const char *pszLeftBtnUp, const char *pszRightBtnUp, const char *pszLeftBtnDn, const char *pszRightBtnDn) {
assert(isValidObject(this));
if ((pszBack == nullptr) || (pszThumb == nullptr))
return _errCode;
_cLeftBtnRect.setRect(0, 0, 0, 0);
_cRightBtnRect.setRect(0, 0, 0, 0);
if (_pThumb != nullptr) {
_pThumb->eraseSprite(this);
delete _pThumb;
_pThumb = nullptr;
}
killBackdrop();
setBackdrop(pszBack);
CBofPalette *pPalette = CBofApp::getApp()->getPalette();
_cBkSize = _pBackdrop->getSize();
_nScrollWidth = _cBkSize.cx;
_pThumb = new CBofSprite;
if (_pThumb->loadSprite(pszThumb) != false) {
_pThumb->setMaskColor(COLOR_WHITE);
_cThumbSize = _pThumb->getSize();
}
delete _pLeftBtnUp;
_pLeftBtnUp = nullptr;
CBofPoint cPoint;
if (pszLeftBtnUp != nullptr) {
_pLeftBtnUp = new CBofBitmap(pszLeftBtnUp, pPalette);
cPoint.x = 0;
cPoint.y = (_pBackdrop->height() / 2) - (_pLeftBtnUp->height() / 2);
_cLeftBtnRect = _pLeftBtnUp->getRect() + cPoint;
_nOffset = _pLeftBtnUp->width();
_nScrollWidth -= _nOffset;
}
delete _pRightBtnUp;
_pRightBtnUp = nullptr;
if (pszRightBtnUp != nullptr) {
_pRightBtnUp = new CBofBitmap(pszRightBtnUp, pPalette);
cPoint.x = _pBackdrop->width() - _pRightBtnUp->width();
cPoint.y = (_pBackdrop->height() / 2) - (_pRightBtnUp->height() / 2);
_cRightBtnRect = _pLeftBtnUp->getRect() + cPoint;
_nScrollWidth -= _cRightBtnRect.width();
}
delete _pLeftBtnDn;
_pLeftBtnDn = nullptr;
if (pszLeftBtnDn != nullptr) {
_pLeftBtnDn = new CBofBitmap(pszLeftBtnDn, pPalette);
}
delete _pRightBtnDn;
_pRightBtnDn = nullptr;
if (pszRightBtnDn != nullptr) {
_pRightBtnDn = new CBofBitmap(pszRightBtnDn, pPalette);
}
return _errCode;
}
ErrorCode CBofScrollBar::paint(CBofRect *pDirtyRect) {
assert(isValidObject(this));
if (!errorOccurred()) {
CBofPalette *pPalette = CBofApp::getApp()->getPalette();
//
// This function needs to be optimized to paint only the section that is
// invalidated. Right now it just repaints the entire scroll bar each time.
//
if ((_pBackdrop != nullptr) && (_pThumb != nullptr)) {
// Do all painting offscreen
CBofBitmap *pBmp = new CBofBitmap(_cBkSize.cx, _cBkSize.cy, pPalette);
_pBackdrop->paint(pBmp, 0, 0, nullptr, COLOR_WHITE);
if ((_nScrollState == 1) && (_pLeftBtnDn != nullptr)) {
CBofPoint cPoint = _cLeftBtnRect.topLeft();
_pLeftBtnDn->paint(pBmp, cPoint.x, cPoint.y, nullptr, COLOR_WHITE);
} else if (_pLeftBtnUp != nullptr) {
CBofPoint cPoint = _cLeftBtnRect.topLeft();
_pLeftBtnUp->paint(pBmp, cPoint.x, cPoint.y, nullptr, COLOR_WHITE);
}
if ((_nScrollState == 4) && (_pRightBtnDn != nullptr)) {
CBofPoint cPoint = _cRightBtnRect.topLeft();
_pRightBtnDn->paint(pBmp, cPoint.x, cPoint.y, nullptr, COLOR_WHITE);
} else if (_pRightBtnUp != nullptr) {
CBofPoint cPoint = _cRightBtnRect.topLeft();
_pRightBtnUp->paint(pBmp, cPoint.x, cPoint.y, nullptr, COLOR_WHITE);
}
_cThumbPos.x = (int)(((int32)(_nScrollWidth - _cThumbSize.cx) * _nPos) / (_nRange - 1)) + _nOffset;
_cThumbPos.y = (int)(_cBkSize.cy / 2) - (int)(_cThumbSize.cy / 2);
if (_cThumbPos.x < 0)
_cThumbPos.x = 0;
if (_cThumbPos.x > (_nScrollWidth - _cThumbSize.cx + _nOffset))
_cThumbPos.x = _nScrollWidth - _cThumbSize.cx + _nOffset;
_pThumb->paintSprite(pBmp, _cThumbPos);
// now we can paint the offscreen buffer to the screen
pBmp->paint(this, 0, 0);
delete pBmp;
}
if ((_pScrollText != nullptr) && (_parent != nullptr)) {
_pScrollText->display(_parent, _szScrollText, FONT_DEFAULT_SIZE, TEXT_DEFAULT_FACE);
}
}
return _errCode;
}
void CBofScrollBar::onLButtonDown(uint32 nFlags, CBofPoint *pPoint, void *) {
assert(isValidObject(this));
CBofRect cLeftPageRect, cRightPageRect;
bool bDoNothing = false;
cLeftPageRect.setRect(_nOffset, 0, (_nScrollWidth / _nRange) * _nPos + _nOffset - 1, _cBkSize.cy - 1);
cRightPageRect.setRect(((_nScrollWidth / _nRange) * _nPos) + _nOffset + _cThumbSize.cx, 0, _nOffset + _nScrollWidth - 1, _cBkSize.cy - 1);
_cCurPoint = *pPoint;
if (_pLeftBtnUp != nullptr && _cLeftBtnRect.ptInRect(*pPoint)) {
// Let timer know what happened
_nScrollState = 1;
// Set new thumb position
setPos(_nPos - _nLineDelta, true);
} else if (_pThumb->getRect().ptInRect(*pPoint)) {
_nScrollState = 5;
} else if (cLeftPageRect.ptInRect(*pPoint)) {
_nScrollState = 2;
// Set new thumb position
setPos(_nPos - _nPageDelta, true);
} else if (cRightPageRect.ptInRect(*pPoint)) {
_nScrollState = 3;
// Set new thumb position
setPos(_nPos + _nPageDelta, true);
} else if (_pRightBtnUp != nullptr && _cRightBtnRect.ptInRect(*pPoint)) {
// Let timer know what happened
_nScrollState = 4;
// Set new thumb position
setPos(_nPos + _nLineDelta, true);
} else {
bDoNothing = true;
}
if (!bDoNothing) {
_bMouseCaptured = true;
setCapture();
if (_nScrollState != 5)
setTimer(BMP_SCROLL_TIMER, _nTimerCount);
}
CBofWindow::onLButtonDown(nFlags, pPoint);
}
int CBofScrollBar::pointToPos(CBofPoint *pPoint) {
assert(isValidObject(this));
assert(pPoint != nullptr);
int nPos = _nPos;
if (_cRect.ptInRect(*pPoint)) {
nPos = (pPoint->x - _nOffset) / (int)(_nScrollWidth / _nRange);
}
return nPos;
}
void CBofScrollBar::onLButtonUp(uint32 nFlags, CBofPoint *pPoint, void *) {
assert(isValidObject(this));
if (_bMouseCaptured) {
killTimer(BMP_SCROLL_TIMER);
_bMouseCaptured = false;
releaseCapture();
int x, y;
switch (_nScrollState) {
case 5:
setPos(pointToPos(pPoint));
break;
case 1:
if (_pLeftBtnUp != nullptr) {
x = 0;
y = (int)(_cBkSize.cy / 2) - (int)(_cLeftBtnRect.height() / 2);
_pLeftBtnUp->paint(this, x, y, nullptr, COLOR_WHITE);
}
break;
case 4:
if (_pRightBtnUp != nullptr) {
x = _cBkSize.cx - _cRightBtnRect.width();
y = (int)(_cBkSize.cy / 2) - (int)(_cRightBtnRect.height() / 2);
_pRightBtnUp->paint(this, x, y, nullptr, COLOR_WHITE);
}
break;
default:
break;
}
_nScrollState = 0;
}
CBofWindow::onLButtonUp(nFlags, pPoint);
}
void CBofScrollBar::onMouseMove(uint32 nFlags, CBofPoint *pPoint, void *) {
assert(isValidObject(this));
if (_bMouseCaptured) {
_cCurPoint = *pPoint;
if (_nScrollState == 5) {
setPos(pointToPos(pPoint));
}
}
CBofWindow::onMouseMove(nFlags, pPoint);
}
void CBofScrollBar::setRepeatTimer(uint32 nTimerInt) {
assert(isValidObject(this));
_nTimerCount = nTimerInt;
}
void CBofScrollBar::onTimer(uint32 nWhichTimer) {
assert(isValidObject(this));
CBofRect cLeftPageRect, cRightPageRect;
cLeftPageRect.setRect(_nOffset, 0, (_nScrollWidth / _nRange) * _nPos + _nOffset - 1, _cBkSize.cy - 1);
cRightPageRect.setRect(((_nScrollWidth / _nRange) * _nPos) + _nOffset + _cThumbSize.cx, 0, _nOffset + _nScrollWidth - 1, _cBkSize.cy - 1);
if (nWhichTimer == BMP_SCROLL_TIMER) {
if ((_nScrollState == 1) && _cLeftBtnRect.ptInRect(_cCurPoint)) {
lineLeft();
} else if ((_nScrollState == 2) && cLeftPageRect.ptInRect(_cCurPoint)) {
pageLeft();
} else if ((_nScrollState == 3) && cRightPageRect.ptInRect(_cCurPoint)) {
pageRight();
} else if ((_nScrollState == 4) && _cRightBtnRect.ptInRect(_cCurPoint)) {
lineRight();
}
}
}
} // namespace SpaceBar
} // namespace Bagel

View File

@@ -0,0 +1,155 @@
/* 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/>.
*
*/
#ifndef BAGEL_BOFLIB_GUI_SCROLL_BAR_H
#define BAGEL_BOFLIB_GUI_SCROLL_BAR_H
#include "bagel/spacebar/boflib/gui/window.h"
#include "bagel/spacebar/boflib/gfx/sprite.h"
#include "bagel/spacebar/boflib/gfx/text.h"
namespace Bagel {
namespace SpaceBar {
#define BSB_LEFT 800
#define BSB_RIGHT 801
#define BSB_LINE_LEFT 802
#define BSB_LINE_RIGHT 803
#define BSB_PAGE_LEFT 804
#define BSB_PAGE_RIGHT 805
#define BSB_THUMB_POS 806
#define BSB_THUMB_TRACK 807
#define MAX_TEXT 128
class CBofScrollBar : public CBofWindow {
public:
CBofScrollBar();
virtual ~CBofScrollBar();
// Implementation
//
ErrorCode loadBitmaps(const char *pszBack, const char *pszThumb, const char *pszLeftUp = nullptr, const char *pszRightUp = nullptr, const char *pszLeftDown = nullptr, const char *pszRightDown = nullptr);
ErrorCode setPos(int nPos, bool bRepaint = true, bool isInitial = false);
int getPos() const {
return _nPos;
}
ErrorCode lineLeft() {
return setPos(_nPos - _nLineDelta);
}
ErrorCode lineRight() {
return setPos(_nPos + _nLineDelta);
}
ErrorCode pageLeft() {
return setPos(_nPos - _nPageDelta);
}
ErrorCode pageRight() {
return setPos(_nPos + _nPageDelta);
}
ErrorCode home() {
return setPos(_nMin);
}
ErrorCode end() {
return setPos(_nMax);
}
int getScrollMin() const {
return _nMin;
}
int getScrollMax() const {
return _nMax;
}
void setLineDelta(const int nDelta) {
_nLineDelta = nDelta;
}
int getLineDelta() const {
return _nLineDelta;
}
void setPageDelta(const int nDelta) {
_nPageDelta = nDelta;
}
int getPageDelta() const {
return _nPageDelta;
}
void getScrollRange(int &nMin, int &nMax);
void setScrollRange(int nMin, int nMax, bool bRepaint = true);
ErrorCode setText(const char *pszText, int nFlags = JUSTIFY_CENTER);
void setRepeatTimer(uint32 nMilliSeconds);
ErrorCode paint(CBofRect *pRect = nullptr);
protected:
int pointToPos(CBofPoint *pPoint);
void onPaint(CBofRect *pDirtyRect) override;
void onLButtonDown(uint32 nFlags, CBofPoint *pPoint, void * = nullptr) override;
void onLButtonUp(uint32 nFlags, CBofPoint *pPoint, void * = nullptr) override;
void onMouseMove(uint32 nFlags, CBofPoint *pPoint, void * = nullptr) override;
void onTimer(uint32) override;
//
// Data members
//
CBofBitmap *_pLeftBtnUp;
CBofBitmap *_pRightBtnUp;
CBofBitmap *_pLeftBtnDn;
CBofBitmap *_pRightBtnDn;
CBofSprite *_pThumb;
CBofRect _cLeftBtnRect;
CBofRect _cRightBtnRect;
int _nMin;
int _nMax;
int _nPos;
int _nLineDelta;
int _nPageDelta;
CBofText *_pScrollText;
char _szScrollText[MAX_TEXT];
CBofSize _cThumbSize;
CBofSize _cBkSize;
int _nOffset;
int _nScrollWidth;
int _nRange;
bool _bMouseCaptured;
CBofPoint _cCurPoint;
CBofPoint _cThumbPos;
int _nScrollState;
uint32 _nTimerCount;
};
} // namespace SpaceBar
} // namespace Bagel
#endif

View File

@@ -0,0 +1,273 @@
/* 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 "bagel/spacebar/boflib/gui/text_box.h"
namespace Bagel {
namespace SpaceBar {
#define DEFAULT_PAGE_SIZE 10
#define DEFAULT_WEIGHT TEXT_NORMAL
#define DEFAULT_POINT_SIZE FONT_12POINT
#define DEFAULT_COLOR CTEXT_COLOR
CBofTextBox::CBofTextBox() {
// Inits
_pDestWindow = nullptr;
_pDestBitmap = nullptr;
_pTextField = nullptr;
_nWeight = DEFAULT_WEIGHT;
_nPointSize = DEFAULT_POINT_SIZE;
_cTextColor = DEFAULT_COLOR;
_nTextFont = FONT_DEFAULT;
_nPageSize = DEFAULT_PAGE_SIZE;
_nCurrentLine = 0;
_nCurrentIndex = 0;
_nNumLines = 0;
}
CBofTextBox::CBofTextBox(CBofWindow *pWindow, const CBofRect *pRect, const CBofString &cText) {
assert(pWindow != nullptr);
assert(pRect != nullptr);
// Inits
_pDestWindow = nullptr;
_pDestBitmap = nullptr;
_pTextField = nullptr;
_nWeight = DEFAULT_WEIGHT;
_nPointSize = DEFAULT_POINT_SIZE;
_cTextColor = DEFAULT_COLOR;
_nPageSize = DEFAULT_PAGE_SIZE;
_nCurrentLine = 0;
_nCurrentIndex = 0;
_nNumLines = 0;
_nTextFont = FONT_DEFAULT;
setText(cText);
setBox(pRect);
setDisplay(pWindow);
}
CBofTextBox::CBofTextBox(CBofBitmap *pBitmap, const CBofRect *pRect, const CBofString &cText) {
assert(pBitmap != nullptr);
assert(pRect != nullptr);
// Inits
_pDestWindow = nullptr;
_pDestBitmap = nullptr;
_pTextField = nullptr;
_nWeight = DEFAULT_WEIGHT;
_nPointSize = DEFAULT_POINT_SIZE;
_cTextColor = DEFAULT_COLOR;
_nPageSize = DEFAULT_PAGE_SIZE;
_nCurrentLine = 0;
_nCurrentIndex = 0;
_nNumLines = 0;
_nTextFont = FONT_DEFAULT;
setText(cText);
setBox(pRect);
setDisplay(pBitmap);
}
CBofTextBox::~CBofTextBox() {
assert(isValidObject(this));
if (_pTextField != nullptr) {
delete _pTextField;
_pTextField = nullptr;
}
_pDestWindow = nullptr;
_pDestBitmap = nullptr;
}
ErrorCode CBofTextBox::setBox(const CBofRect *pRect) {
assert(isValidObject(this));
assert(pRect != nullptr);
// Remove previous text field (if any)
delete _pTextField;
_pTextField = nullptr;
// Create a new text field the size of the box we want
_pTextField = new CBofText(pRect, JUSTIFY_WRAP);
return _errCode;
}
void CBofTextBox::setDisplay(CBofWindow *pWindow) {
assert(isValidObject(this));
assert(pWindow != nullptr);
_pDestWindow = pWindow;
_pDestBitmap = nullptr;
}
void CBofTextBox::setDisplay(CBofBitmap *pBitmap) {
assert(isValidObject(this));
assert(pBitmap != nullptr);
_pDestBitmap = pBitmap;
_pDestWindow = nullptr;
}
void CBofTextBox::setTextAttribs(const int nSize, const int nWeight, const COLORREF cColor, const int nFont) {
assert(isValidObject(this));
_nPointSize = nSize;
_nWeight = nWeight;
_cTextColor = cColor;
_nTextFont = nFont;
}
void CBofTextBox::setText(const CBofString &cString) {
_cBuffer = cString;
assert(_cBuffer.getLength() != 0);
_cBuffer.replaceStr("\r\n", "\n");
_cBuffer.replaceStr("\r", "\n");
_nCurrentLine = 0;
_nCurrentIndex = 0;
_nNumLines = _cBuffer.findNumOccurrences("\n");
}
int CBofTextBox::getIndex(const int nLine) {
assert(nLine >= 0 && nLine <= _nNumLines);
// Find the index into our buffer that represents the top left of the
// buffer that is nLine from current the beginning of the buffer.
const char *pszCur, *pszBuffer;
const char *pszLast = pszCur = pszBuffer = _cBuffer;
for (int i = 0; i < nLine; i++) {
pszLast = pszCur;
pszCur = strstr(pszCur, "\n");
// Make sure we don't go too far (nLines is invalid)
assert(pszCur != nullptr);
pszCur++;
}
int nChars = pszCur - pszBuffer;
if (nLine == _nNumLines) {
nChars = pszLast - pszBuffer;
_nCurrentLine--;
}
return nChars;
}
ErrorCode CBofTextBox::scrollUp(const int nLines) {
// Make scroll a no-op if all the lines in the box appear on one screen.
if (_nNumLines <= _nPageSize) {
return scrollTo(_nCurrentLine);
}
int nNewLine = _nCurrentLine - nLines;
if (nNewLine < 0) {
nNewLine = 0;
} else if (nNewLine > (_nNumLines - _nPageSize)) {
// If the line requested to be the top of the page
// would cause fewer than _nPageSize lines to be displayed,
// snap nNewLine to be the top of the last full page.
//
nNewLine = (_nNumLines - _nPageSize);
}
return scrollTo(nNewLine);
}
ErrorCode CBofTextBox::scrollTo(const int nLine) {
assert(isValidObject(this));
assert(nLine >= 0 && nLine <= _nNumLines);
_nCurrentIndex = getIndex(nLine);
_nCurrentLine = nLine;
// Show the text box
display();
return _errCode;
}
ErrorCode CBofTextBox::display() {
assert(isValidObject(this));
assert(_nCurrentLine >= 0 && _nCurrentLine <= _nNumLines);
assert(_nCurrentIndex >= 0 && _nCurrentIndex < _cBuffer.getLength());
// The actual text box must have been created before it can be displayed
assert(_pTextField != nullptr);
// If painting to a window
if (_pDestWindow != nullptr) {
_pTextField->display(_pDestWindow, _cBuffer.mid(_nCurrentIndex), _nPointSize, _nWeight, _cTextColor, _nTextFont);
} else {
// Otherwise, must be painting to a bitmap
assert(_pDestBitmap != nullptr);
_pTextField->display(_pDestBitmap, _cBuffer.mid(_nCurrentIndex), _nPointSize, _nWeight, _cTextColor, _nTextFont);
}
return _errCode;
}
ErrorCode CBofTextBox::erase() {
assert(isValidObject(this));
// The actual text box must have been created before it can be displayed
assert(_pTextField != nullptr);
if (_pDestWindow != nullptr) {
_errCode = _pTextField->erase(_pDestWindow);
} else {
assert(_pDestBitmap != nullptr);
_errCode = _pTextField->erase(_pDestBitmap);
}
return _errCode;
}
void CBofTextBox::flushBackground() {
assert(isValidObject(this));
if (_pTextField != nullptr) {
_pTextField->flushBackground();
}
}
} // namespace SpaceBar
} // namespace Bagel

View File

@@ -0,0 +1,140 @@
/* 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/>.
*
*/
#ifndef BAGEL_BOFLIB_GUI_TEXT_BOX_H
#define BAGEL_BOFLIB_GUI_TEXT_BOX_H
#include "bagel/spacebar/boflib/gfx/bitmap.h"
#include "bagel/boflib/error.h"
#include "bagel/boflib/string.h"
#include "bagel/spacebar/boflib/gfx/text.h"
#include "bagel/spacebar/boflib/gui/window.h"
namespace Bagel {
namespace SpaceBar {
class CBofTextBox : public CBofObject, public CBofError {
public:
CBofTextBox();
CBofTextBox(CBofWindow *pWindow, const CBofRect *pRect, const CBofString &cText);
CBofTextBox(CBofBitmap *pBitmap, const CBofRect *pRect, const CBofString &cText);
virtual ~CBofTextBox();
void setText(const CBofString &cText);
ErrorCode setBox(const CBofRect *pRect);
void setDisplay(CBofWindow *pWindow);
void setDisplay(CBofBitmap *pBitmap);
void setTextAttribs(int nSize, int nWeight, COLORREF cColor = CTEXT_COLOR, int nFont = FONT_DEFAULT);
void setPointSize(const int nSize) {
_nPointSize = nSize;
}
int getPointSize() const {
return _nPointSize;
}
void setWeight(const int nWeight) {
_nWeight = nWeight;
}
int getWeight() const {
return _nWeight;
}
void setPageLength(const int nSize) {
_nPageSize = nSize;
}
int getPageLength() const {
return _nPageSize;
}
void setColor(const COLORREF cColor) {
_cTextColor = cColor;
}
COLORREF getColor() const {
return _cTextColor;
}
void setFont(int nFont) {
_nTextFont = nFont;
}
int getFont() const {
return _nTextFont;
}
ErrorCode lineUp() {
return scrollUp(1);
}
ErrorCode lineDown() {
return scrollDown(1);
}
ErrorCode pageUp() {
return scrollUp(_nPageSize);
}
ErrorCode pageDown() {
return scrollDown(_nPageSize);
}
ErrorCode scrollUp(int nLines);
ErrorCode scrollDown(const int nLines) {
return scrollUp(-nLines);
}
ErrorCode scrollTo(int nLine);
ErrorCode display();
ErrorCode erase();
void flushBackground();
int getCurrLine() {
return _nCurrentLine;
}
ErrorCode setCurrLine(const int nLine) {
return scrollTo(nLine);
}
protected:
int getIndex(int nLines);
// Data
CBofString _cBuffer;
CBofText *_pTextField;
CBofWindow *_pDestWindow;
CBofBitmap *_pDestBitmap;
int _nCurrentLine;
int _nCurrentIndex;
int _nNumLines;
int _nPageSize;
COLORREF _cTextColor;
int _nPointSize;
int _nWeight;
int _nTextFont;
};
} // namespace SpaceBar
} // namespace Bagel
#endif

View File

@@ -0,0 +1,859 @@
/* 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 "bagel/spacebar/boflib/gui/window.h"
#include "bagel/spacebar/boflib/app.h"
#include "bagel/spacebar/boflib/events.h"
#include "bagel/boflib/sound.h"
#include "bagel/spacebar/boflib/std_keys.h"
#include "bagel/metaengine.h"
#include "bagel/bagel.h"
namespace Bagel {
namespace SpaceBar {
#define DOUBLE_CLICK_TIME 250
// Static members defined here
CBofWindow *CBofWindow::_pWindowList = nullptr;
CBofWindow *CBofWindow::_pActiveWindow = nullptr;
CBofTimerPacket *CBofWindow::_pTimerList = nullptr;
int CBofWindow::_mouseX = 0;
int CBofWindow::_mouseY = 0;
CBofWindow::CBofWindow() {
if (_pActiveWindow == nullptr)
_pActiveWindow = this;
if (_pWindowList == nullptr) {
_pWindowList = this;
} else {
_pWindowList->Insert(this);
}
}
CBofWindow::CBofWindow(const char *pszName, int x, int y, int nWidth, int nHeight, CBofWindow *pParent) {
if (_pWindowList == nullptr) {
_pWindowList = this;
} else {
_pWindowList->Insert(this);
}
create(pszName, x, y, nWidth, nHeight, pParent);
}
CBofWindow::~CBofWindow() {
assert(isValidObject(this));
delete _surface;
_surface = nullptr;
killMyTimers();
// Remove it from any parent
if (_parent != nullptr)
setParent(nullptr);
// Remove this window from the list
if (_pWindowList == this) {
_pWindowList = (CBofWindow *)getNext();
}
killBackdrop();
CBofWindow::destroy();
}
ErrorCode CBofWindow::initialize() {
_pWindowList = nullptr;
_pActiveWindow = nullptr;
_pTimerList = nullptr;
return ERR_NONE;
}
ErrorCode CBofWindow::shutdown() {
return ERR_NONE;
}
Common::Point CBofWindow::getMousePos() {
return Common::Point(_mouseX, _mouseY);
}
void CBofWindow::destroy() {
releaseCapture();
delete _surface;
_surface = nullptr;
// When gui elements are destroyed, remove them
// from the _children array of their parent
setParent(nullptr);
}
void CBofWindow::validateAnscestors(CBofRect *pRect) {
assert(isValidObject(this));
// Validate all anscestors
CBofWindow *pParent = _parent;
while (pParent != nullptr) {
pParent->validateRect(pRect);
pParent = pParent->getParent();
}
}
ErrorCode CBofWindow::create(const char *pszName, int x, int y, int nWidth, int nHeight, CBofWindow *pParent, uint32 nControlID) {
assert(isValidObject(this));
assert(pszName != nullptr);
assert(pParent != this);
// Remember who our parent is
if (pParent != nullptr)
setParent(pParent);
_nID = nControlID;
// Remember the name of this window
Common::strlcpy(_szTitle, pszName, MAX_TITLE);
// Retain screen coordinates for this window
_cWindowRect.setRect(x, y, x + nWidth - 1, y + nHeight - 1);
// Calculate effective bounds
Common::Rect stRect(x, y, x + nWidth, y + nHeight);
if (pParent != nullptr)
stRect.translate(pParent->getWindowRect().left,
pParent->getWindowRect().top);
delete _surface;
_surface = new Graphics::ManagedSurface(*g_engine->getScreen(), stRect);
if (!errorOccurred()) {
CBofPalette *pPalette = CBofApp::getApp()->getPalette();
if (pPalette != nullptr) {
selectPalette(pPalette);
}
// Retain local coordinates (based on own window)
_cRect.setRect(0, 0, _cWindowRect.width() - 1, _cWindowRect.height() - 1);
}
return _errCode;
}
void CBofWindow::updateWindow() {
if (_visible) {
if (isVisible())
onPaint(&_cRect);
for (uint i = 0; i < _children.size(); ++i)
_children[i]->updateWindow();
}
}
void CBofWindow::setParent(CBofWindow *parent) {
if (_parent != nullptr)
_parent->_children.remove(this);
_parent = parent;
if (parent)
parent->_children.push_back(this);
}
ErrorCode CBofWindow::create(const char *pszName, CBofRect *pRect, CBofWindow *pParent, uint32 nControlID) {
assert(isValidObject(this));
assert(pszName != nullptr);
int x = 0;
int y = 0;
int nWidth = USE_DEFAULT;
int nHeight = USE_DEFAULT;
if (pRect != nullptr) {
x = pRect->left;
y = pRect->top;
nWidth = pRect->width();
nHeight = pRect->height();
}
return create(pszName, x, y, nWidth, nHeight, pParent, nControlID);
}
void CBofWindow::releaseCapture() {
_bCaptured = false;
if (hasCapture())
CBofApp::getApp()->setCaptureControl(nullptr);
}
void CBofWindow::setCapture() {
_bCaptured = true;
CBofApp::getApp()->setCaptureControl(this);
}
bool CBofWindow::hasCapture() const {
return CBofApp::getApp()->getCaptureControl() == this;
}
void CBofWindow::releaseFocus() {
CBofApp::getApp()->setFocusControl(nullptr);
}
void CBofWindow::setFocus() {
CBofApp::getApp()->setFocusControl(this);
}
bool CBofWindow::hasFocus() const {
return CBofApp::getApp()->getFocusControl() == this;
}
void CBofWindow::center() {
assert(isValidObject(this));
CBofWindow *pParent = _parent;
int x, y;
if (pParent != nullptr) {
CBofRect cWindowRect = pParent->getWindowRect();
x = cWindowRect.left + (pParent->width() - width()) / 2;
y = cWindowRect.top + (pParent->height() - height()) / 2;
} else {
x = (CBofApp::getApp()->screenWidth() - width()) / 2;
y = (CBofApp::getApp()->screenHeight() - height()) / 2;
}
move(x, y);
}
void CBofWindow::move(const int x, const int y, bool bRepaint) {
assert(isValidObject(this));
assert(isCreated());
// We now have a new position (in screen coordinates)
_cWindowRect.setRect(x, y, x + _cRect.width() - 1, y + _cRect.height() - 1);
// Recreate the surface at the new screen position
delete _surface;
_surface = new Graphics::ManagedSurface(*g_engine->getScreen(), _cWindowRect);
}
void CBofWindow::reSize(CBofRect *pRect, bool bRepaint) {
assert(isValidObject(this));
assert(isCreated());
assert(pRect != nullptr);
// We now have a new position (in screen coordinates)
_cWindowRect = *pRect;
_cRect.setRect(0, 0, _cWindowRect.width() - 1, _cWindowRect.height() - 1);
// Recreate the surface at the new screen position
delete _surface;
_surface = new Graphics::ManagedSurface(*g_engine->getScreen(), _cWindowRect);
}
void CBofWindow::select() {
// No implementation in ScummVM
}
void CBofWindow::show() {
assert(isValidObject(this));
if (!errorOccurred()) {
assert(isCreated());
if (isCreated()) {
_visible = true;
invalidateRect(&_cRect);
}
}
}
void CBofWindow::hide() {
assert(isValidObject(this));
if (!errorOccurred()) {
assert(isCreated());
_visible = false;
}
}
void CBofWindow::postMessage(uint32 nMessage, uint32 lParam1, uint32 lParam2) {
assert(isValidObject(this));
assert(isCreated());
}
void CBofWindow::setTimer(uint32 nID, uint32 nInterval, BofCallback pCallBack) {
assert(isValidObject(this));
assert(isCreated());
// Don't add it if there's already a timer there with the same id.
CBofTimerPacket *pPacket = _pTimerList;
while (pPacket != nullptr) {
if (pPacket->_nID == nID)
return;
pPacket = (CBofTimerPacket *)pPacket->getNext();
}
pPacket = new CBofTimerPacket;
pPacket->_nID = nID;
pPacket->_nInterval = nInterval;
pPacket->_pCallBack = pCallBack;
pPacket->_pOwnerWindow = this;
// Add this timer to the list of current timers
if (_pTimerList != nullptr) {
_pTimerList->addToHead(pPacket);
}
_pTimerList = pPacket;
// Add the timer to the window
_timers.push_back(WindowTimer(nInterval, nID, pCallBack));
}
void CBofWindow::killTimer(uint32 nID) {
assert(isValidObject(this));
// Remove the timer from the window timer list
for (Common::List<WindowTimer>::iterator it = _timers.begin(); it != _timers.end(); ++it) {
if (it->_id == nID) {
_timers.erase(it);
break;
}
}
// Find and remove the timer packet for this timer
CBofTimerPacket *pPacket = _pTimerList;
while (pPacket != nullptr) {
if (pPacket->_nID == nID) {
if (pPacket == _pTimerList) {
_pTimerList = (CBofTimerPacket *)_pTimerList->getNext();
}
delete pPacket;
break;
}
pPacket = (CBofTimerPacket *)pPacket->getNext();
}
}
void CBofWindow::killMyTimers() {
assert(isValidObject(this));
CBofTimerPacket *pTimer = _pTimerList;
while (pTimer != nullptr) {
CBofTimerPacket *pNextTimer = (CBofTimerPacket *)pTimer->getNext();
if (pTimer->_pOwnerWindow == this) {
killTimer(pTimer->_nID);
}
pTimer = pNextTimer;
}
}
void CBofWindow::checkTimers() {
for (uint i = 0; i < _children.size(); ++i)
_children[i]->checkTimers();
for (bool timersChanged = true; timersChanged;) {
timersChanged = false;
uint32 currTime = g_system->getMillis();
// Iterate over the timers looking for any that have expired
for (auto &timer : _timers) {
if (currTime >= (timer._lastExpiryTime + timer._interval)) {
// Timer has expired
timer._lastExpiryTime = currTime;
if (timer._callback) {
(timer._callback)(timer._id, this);
} else {
onTimer(timer._id);
}
// Flag to restart scanning through the timer list
// for any other expired timers, since the timer call
// may have modified the existing list
timersChanged = true;
break;
}
}
}
}
void CBofWindow::screenToClient(CBofPoint *pPoint) {
// Not needed in ScummVM
}
CBofRect CBofWindow::getClientRect() {
assert(isValidObject(this));
CBofRect cRect(0, 0, _cRect.width() - 1, _cRect.height() - 1);
return cRect;
}
void CBofWindow::postUserMessage(uint32 lMessage, uint32 lExtraInfo) {
Common::Event e;
e.type = (Common::EventType)EVENT_USER;
e.mouse.x = lMessage;
e.mouse.y = lExtraInfo;
g_system->getEventManager()->pushEvent(e);
}
void CBofWindow::flushAllMessages() {
// Make sure this is a valid window
assert(isValidObject(this));
assert(isCreated());
}
void CBofWindow::validateRect(const CBofRect *pRect) {
// No implementation in ScummVM
}
void CBofWindow::invalidateRect(const CBofRect *pRect) {
}
ErrorCode CBofWindow::setBackdrop(CBofBitmap *pNewBitmap, bool bRefresh) {
assert(isValidObject(this));
assert(pNewBitmap != nullptr);
// Destroy old backdrop (if any)
killBackdrop();
// We take ownership of this bitmap!
_pBackdrop = pNewBitmap;
if (bRefresh) {
_pBackdrop->paint(this, 0, 0);
}
return _errCode;
}
ErrorCode CBofWindow::setBackdrop(const char *pszFileName, bool bRefresh) {
assert(isValidObject(this));
assert(pszFileName != nullptr);
// Use Application's palette if none supplied
CBofPalette *pPalette = CBofApp::getApp()->getPalette();
CBofBitmap *pBmp = new CBofBitmap(pszFileName, pPalette);
return setBackdrop(pBmp, bRefresh);
}
void CBofWindow::killBackdrop() {
assert(isValidObject(this));
delete _pBackdrop;
_pBackdrop = nullptr;
}
ErrorCode CBofWindow::paintBackdrop(CBofRect *pRect, int nTransparentColor) {
assert(isValidObject(this));
if (_pBackdrop != nullptr) {
if (pRect == nullptr) {
_errCode = _pBackdrop->paint(this, &_cRect, nullptr, nTransparentColor);
} else {
_errCode = _pBackdrop->paint(this, pRect, pRect, nTransparentColor);
}
}
return _errCode;
}
void CBofWindow::selectPalette(CBofPalette *pPal) {
assert(isValidObject(this));
assert(isCreated());
}
Graphics::ManagedSurface *CBofWindow::getSurface() {
return _surface;
}
// Default version of these virtual functions don't do anything
//
void CBofWindow::onMouseMove(uint32, CBofPoint *, void *) {
}
void CBofWindow::onLButtonDown(uint32, CBofPoint *, void *) {
}
void CBofWindow::onLButtonUp(uint32, CBofPoint *, void *) {
}
void CBofWindow::onLButtonDblClk(uint32, CBofPoint *) {
}
void CBofWindow::onRButtonDown(uint32, CBofPoint *) {
}
void CBofWindow::onRButtonUp(uint32, CBofPoint *) {
}
void CBofWindow::onRButtonDblClk(uint32, CBofPoint *) {
}
void CBofWindow::onKeyHit(uint32, uint32) {
}
void CBofWindow::onReSize(CBofSize *) {
}
void CBofWindow::onPaint(CBofRect *) {
}
void CBofWindow::onTimer(uint32) {
}
void CBofWindow::onClose() {
}
void CBofWindow::onBofButton(CBofObject *, int) {
}
void CBofWindow::onBofScrollBar(CBofObject *, int) {
}
void CBofWindow::onBofListBox(CBofObject *, int) {
}
void CBofWindow::onUserMessage(uint32, uint32) {
}
void CBofWindow::onMainLoop() {
}
void CBofWindow::onSoundNotify(CBofObject *, uint32) {
}
void CBofWindow::onMovieNotify(uint32, uint32) {
}
void CBofWindow::onActivate() {
}
void CBofWindow::onDeActivate() {
}
void CBofWindow::onMCINotify(uint32 wParam, uint32 lParam) {
assert(isValidObject(this));
}
void CBofWindow::handleEvents() {
Common::Event e;
CBofWindow *capture = CBofApp::getApp()->getCaptureControl();
CBofWindow *focus = CBofApp::getApp()->getFocusControl();
bool eventsPresent = false;
while (g_system->getEventManager()->pollEvent(e)) {
if (capture)
capture->handleEvent(e);
else if (e.type == Common::EVENT_KEYDOWN && focus)
focus->handleEvent(e);
else
handleEvent(e);
if (e.type >= Common::EVENT_MOUSEMOVE && e.type <= Common::EVENT_MBUTTONUP) {
_mouseX = e.mouse.x;
_mouseY = e.mouse.y;
}
if (e.type != Common::EVENT_MOUSEMOVE) {
eventsPresent = true;
break;
}
}
// Only do timer checks when not processing other pending events.
// This simulates Windows behaviour, where the WM_TIMER events
// would be added at the end of the event queue
if (!eventsPresent)
// Check for expired timers
checkTimers();
}
void CBofWindow::handleEvent(const Common::Event &event) {
assert(isValidObject(this));
if (!_enabled || !_visible)
// Window is disabled or hidden
return;
CBofPoint mousePos(event.mouse.x - _cWindowRect.left,
event.mouse.y - _cWindowRect.top);
for (auto parent = _parent; parent; parent = parent->_parent) {
mousePos.x -= parent->_cWindowRect.left;
mousePos.y -= parent->_cWindowRect.top;
}
switch (event.type) {
case Common::EVENT_MOUSEMOVE:
case Common::EVENT_LBUTTONDOWN:
case Common::EVENT_LBUTTONUP:
case Common::EVENT_RBUTTONDOWN:
case Common::EVENT_RBUTTONUP: {
// Check if the mouse is within the area of a child control
for (uint i = 0; i < _children.size(); ++i) {
auto &child = *_children[i];
if (child.isVisible() && child.isEnabled() &&
child.getWindowRect().ptInRect(mousePos)) {
child.handleEvent(event);
return;
}
}
break;
}
default:
break;
}
uint32 currTime = g_system->getMillis();
switch ((int)event.type) {
case Common::EVENT_MOUSEMOVE:
onMouseMove(0, &mousePos);
break;
case Common::EVENT_LBUTTONDOWN:
if ((currTime - _lastLButtonTime) <= DOUBLE_CLICK_TIME) {
_lastLButtonTime = 0;
onLButtonDblClk(1, &mousePos);
} else {
onLButtonDown(1, &mousePos);
_lastLButtonTime = currTime;
}
break;
case Common::EVENT_LBUTTONUP:
onLButtonUp(0, &mousePos);
break;
case Common::EVENT_RBUTTONDOWN:
if ((currTime - _lastRButtonTime) <= DOUBLE_CLICK_TIME) {
_lastRButtonTime = 0;
onRButtonDblClk(2, &mousePos);
} else {
onRButtonDown(2, &mousePos);
_lastRButtonTime = currTime;
}
break;
case Common::EVENT_RBUTTONUP:
onRButtonUp(0, &mousePos);
break;
case Common::EVENT_KEYDOWN:
uint32 lNewKey;
if ((lNewKey = translateKey(event)) != BKEY_UNKNOWN) {
onKeyHit(lNewKey, event.kbdRepeat ? 1 : 0);
}
break;
case Common::EVENT_CUSTOM_ENGINE_ACTION_START:
if (event.customType != KEYBIND_NONE)
onKeyHit((event.customType == KEYBIND_WAIT)
? BKEY_SPACE : BKEY_SCRL_LOCK, 0);
break;
case EVENT_USER:
// Message type and param are stored in mouse x/y
onUserMessage(event.mouse.x, event.mouse.y);
break;
case Common::EVENT_QUIT:
onClose();
break;
default:
break;
}
}
uint32 CBofWindow::translateKey(const Common::Event &event) const {
uint32 nCode = BKEY_UNKNOWN;
switch (event.kbd.keycode) {
case Common::KEYCODE_F1:
nCode = BKEY_F1;
break;
case Common::KEYCODE_F2:
nCode = BKEY_SAVE;
break;
case Common::KEYCODE_F3:
nCode = BKEY_RESTORE;
break;
case Common::KEYCODE_F4:
nCode = BKEY_F4;
break;
case Common::KEYCODE_F5:
nCode = BKEY_SAVE;
break;
case Common::KEYCODE_F6:
nCode = BKEY_F6;
break;
case Common::KEYCODE_F7:
nCode = BKEY_RESTORE;
break;
case Common::KEYCODE_F8:
nCode = BKEY_F8;
break;
case Common::KEYCODE_F9:
nCode = BKEY_F9;
break;
case Common::KEYCODE_F10:
nCode = BKEY_F10;
break;
case Common::KEYCODE_F11:
nCode = BKEY_F11;
break;
case Common::KEYCODE_F12:
nCode = BKEY_F12;
break;
case Common::KEYCODE_END:
nCode = BKEY_END;
break;
case Common::KEYCODE_HOME:
nCode = BKEY_HOME;
break;
case Common::KEYCODE_LEFT:
nCode = BKEY_LEFT;
break;
case Common::KEYCODE_RIGHT:
nCode = BKEY_RIGHT;
break;
case Common::KEYCODE_UP:
nCode = BKEY_UP;
break;
case Common::KEYCODE_DOWN:
nCode = BKEY_DOWN;
break;
case Common::KEYCODE_RETURN:
nCode = BKEY_ENTER;
break;
case Common::KEYCODE_INSERT:
nCode = BKEY_INS;
break;
case Common::KEYCODE_BACKSPACE:
nCode = BKEY_BACK;
break;
case Common::KEYCODE_DELETE:
nCode = BKEY_DEL;
break;
case Common::KEYCODE_SCROLLOCK:
nCode = BKEY_SCRL_LOCK;
break;
case Common::KEYCODE_PAGEUP:
nCode = BKEY_PAGEUP;
break;
case Common::KEYCODE_PAGEDOWN:
nCode = BKEY_PAGEDOWN;
break;
case Common::KEYCODE_ESCAPE:
nCode = BKEY_ESC;
break;
default:
// No translation for this key
if (event.kbd.ascii >= 32 && event.kbd.ascii <= 127)
nCode = event.kbd.ascii;
break;
}
if (nCode != BKEY_UNKNOWN) {
if (event.kbd.flags & Common::KBD_ALT) {
nCode = tolower(nCode) | BKF_ALT;
}
}
return nCode;
}
void CBofWindow::fillWindow(byte iColor) {
fillRect(nullptr, iColor);
}
void CBofWindow::fillRect(CBofRect *pRect, byte iColor) {
CBofBitmap cBmp(width(), height(), CBofApp::getApp()->getPalette());
cBmp.fillRect(pRect, iColor);
cBmp.paint(this, 0, 0);
}
ErrorCode CBofWindow::paintBeveledText(CBofRect *rect, const CBofString &cString, const int size, const int weight, const COLORREF color, int justify, uint32 format) {
assert(rect != nullptr);
CBofBitmap bmp(rect->width(), rect->height(), nullptr, false);
// Assume no error
ErrorCode errorCode = ERR_NONE;
CBofRect r = bmp.getRect();
CBofPalette *palette = nullptr;
CBofApp *app = CBofApp::getApp();
if (app != nullptr) {
palette = app->getPalette();
}
if (palette != nullptr) {
bmp.fillRect(nullptr, palette->getNearestIndex(RGB(92, 92, 92)));
bmp.drawRect(&r, palette->getNearestIndex(RGB(0, 0, 0)));
} else {
bmp.fillRect(nullptr, COLOR_BLACK);
}
byte c1 = 3;
byte c2 = 9;
CBofRect cBevel = r;
int left = cBevel.left;
int top = cBevel.top;
int right = cBevel.right;
int bottom = cBevel.bottom;
r.left += 6;
r.top += 3;
r.right -= 5;
r.bottom -= 5;
for (int i = 1; i <= 3; i++) {
bmp.line(left + i, bottom - i, right - i, bottom - i, c1);
bmp.line(right - i, bottom - i, right - i, top + i - 1, c1);
}
for (int i = 1; i <= 3; i++) {
bmp.line(left + i, bottom - i, left + i, top + i - 1, c2);
bmp.line(left + i, top + i - 1, right - i, top + i - 1, c2);
}
paintText(&bmp, &r, cString, size, weight, color, justify, format, FONT_DEFAULT);
bmp.paint(this, rect);
return errorCode;
}
} // namespace SpaceBar
} // namespace Bagel

View File

@@ -0,0 +1,480 @@
/* 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/>.
*
*/
#ifndef BAGEL_BOFLIB_GUI_WINDOW_H
#define BAGEL_BOFLIB_GUI_WINDOW_H
#include "common/list.h"
#include "common/events.h"
#include "bagel/spacebar/boflib/array.h"
#include "bagel/boflib/stdinc.h"
#include "bagel/boflib/error.h"
#include "bagel/boflib/object.h"
#include "bagel/boflib/point.h"
#include "bagel/boflib/rect.h"
#include "bagel/boflib/size.h"
#include "bagel/spacebar/boflib/timer.h"
#include "bagel/boflib/llist.h"
#include "bagel/spacebar/boflib/gfx/bitmap.h"
#include "bagel/boflib/palette.h"
#include "bagel/spacebar/boflib/gfx/text.h"
namespace Bagel {
class CBofString;
namespace SpaceBar {
#define MAX_TITLE 64
#define USE_DEFAULT (-1)
class CBofBitmap;
class CBofTimerPacket;
class CBofWindow : public CLList, public CBofObject, public CBofError {
private:
bool _visible = true;
bool _enabled = true;
Common::List<WindowTimer> _timers;
uint32 _lastLButtonTime = 0, _lastRButtonTime = 0;
/**
* Handles translating from a ScummVM event structure to
* a code used by the game engine
*/
uint32 translateKey(const Common::Event &event) const;
protected:
/**
* Checks window timers for expiry
*/
void checkTimers();
public:
/**
* Default constructor
*/
CBofWindow();
/**
* Constructor for CBofWindow
* @param pszName Name of window
* @param x X position
* @param y Y position
* @param nWidth Width of window to create (optional)
* @param nHeight Height of window to create (optional)
* @param pParent Parent of this window (optional)
*/
CBofWindow(const char *pszName, int x, int y, int nWidth, int nHeight, CBofWindow *pParent);
/**
* Destructor
*/
virtual ~CBofWindow();
static ErrorCode initialize();
static ErrorCode shutdown();
static Common::Point getMousePos();
/**
* Creates a window
* @param pszName Name of window
* @param x X position of upper-left corner
* @param y Y position of upper-left corner
* @param nWidth Width of window to create (optional)
* @param nHeight Height of window to create (optional)
* @param pParent Parent of this window (optional)
* @param nControlID User defined ID of this window
* @return Error return code
*/
virtual ErrorCode create(const char *pszName, int x, int y, int nWidth, int nHeight, CBofWindow *pParent, uint32 nControlID = 0);
/**
* Creates a window
* @param pszName Name of window
* @param pRect Rectangle for window placement
* @param pParent Parent of this window (optional)
* @param nControlID User defined ID of this window
* @return Error return code
*/
virtual ErrorCode create(const char *pszName, CBofRect *pRect, CBofWindow *pParent, uint32 nControlID = 0);
/**
* Destroys the Window attached to this CBofWindow (if any)
*/
virtual void destroy();
virtual void destroyWindow() {
destroy();
}
/**
* Shows current window (if hidden)
*/
void show();
void select();
/**
* Hides current window (if shown)
*/
void hide();
/**
* Centers current window in parent window or in screen
*/
void center();
/**
* Moves current window to specified location in parent
* @param x New upper left corner X position
* @param y New upper left corner Y position
* @param bRepaint true if should update the window
*/
void move(int x, int y, bool bRepaint = false);
/**
* Resizes current window to specified area
* @param pRect New area for window
* @param bRepaint Optional repaint after resize
*/
void reSize(CBofRect *pRect, bool bRepaint = false);
virtual ErrorCode close() {
onClose();
return ERR_NONE;
}
/**
* Posts a message
* @param nMessage Message to post
* @param lParam1 User info
* @param lParam2 More user info
*/
void postMessage(uint32 nMessage, uint32 lParam1, uint32 lParam2);
/**
* Posts a user defined message
*/
void postUserMessage(uint32 lMessage, uint32 lExtraInfo);
/**
* Sets a timer which calls specified callback (or onTimer)
* @param nID ID of timer to set
* @param nInterval Number of milliseconds till event
* @param pCallBack Function to call when time is up
*/
void setTimer(uint32 nID, uint32 nInterval, BofCallback pCallBack = nullptr);
/**
* Stops specified timer
* @param nID ID of timer to stop
*/
void killTimer(uint32 nID);
/**
* Stops all timers associated with current window
*/
void killMyTimers();
/**
* Returns the parent window element, if any
*/
CBofWindow *getParent() const {
return _parent;
}
/**
* Causes all parent windows to have valid paint regions
* @param pRect Area to validate
*/
void validateAnscestors(CBofRect *pRect = nullptr);
static CBofWindow *getActiveWindow() {
return _pActiveWindow;
}
void setActive() {
_pActiveWindow = this;
}
static CBofWindow *getWindowList() {
return _pWindowList;
}
CBofRect getWindowRect() const {
return _cWindowRect;
}
CBofRect getClientRect();
CBofRect getRect() const {
return _cRect;
}
int width() const {
return _cRect.width();
}
int height() const {
return _cRect.height();
}
void screenToClient(CBofPoint *pPoint);
/**
* Selects and Realizes specified palette into current DC
* @param pPal Palette to select
*/
void selectPalette(CBofPalette *pPal);
/**
* Associates a new background bitmap to this window
* @param pNewBitmap New background bitmap
* @param bRefresh true if should repaint now
* @return Error return code
*/
ErrorCode setBackdrop(CBofBitmap *pNewBitmap, bool bRefresh = false);
/**
* Associates a new background bitmap to this window
* @param pszFileName new background bitmap from file
* @param bRefresh true if should repaint now
* @return Error return code
*/
ErrorCode setBackdrop(const char *pszFileName, bool bRefresh = false);
void clearBackdrop() {
_pBackdrop = nullptr;
}
CBofBitmap *getBackdrop() const {
return _pBackdrop;
}
bool hasBackdrop() const {
return _pBackdrop != nullptr;
}
/**
* Deletes the background bitmap associated with this window
*/
void killBackdrop();
/**
* Updates the specified section of the background bitmap
* @param pRect Area of bitmap to update on screen
* @param nTransparentColor Color index used for transparency (-1 = none)
* @return Error return code
*/
ErrorCode paintBackdrop(CBofRect *pRect = nullptr, int nTransparentColor = -1);
void setControlID(uint32 nID) {
_nID = nID;
}
uint32 getControlID() const {
return _nID;
}
void setBkColor(COLORREF cColor) {
_cBkColor = cColor;
}
COLORREF getBkColor() const {
return _cBkColor;
}
void setFgColor(COLORREF cColor) {
_cFgColor = cColor;
}
COLORREF getFgColor() const {
return _cFgColor;
}
void setPrevMouseDown(CBofPoint p) {
_cPrevMouseDown = p;
}
CBofPoint getPrevMouseDown() const {
return _cPrevMouseDown;
}
/**
* Sets mouse capture for this window
*/
void setCapture();
/**
* Release mouse capture for this window
*/
void releaseCapture();
/**
* Returns true if the control is capturing mouse events
*/
bool hasCapture() const;
/**
* Sets the focus on a control for keyboard input
*/
void setFocus();
/**
* Releases focus from an edit control
*/
void releaseFocus();
/**
* Returns true if the control has focus
*/
bool hasFocus() const;
void flushAllMessages();
/**
* Adds specified rectangle to dirty rect list for this window
* @param pRect Rectangle to add to dirty list
*/
void validateRect(const CBofRect *pRect);
/**
* Removes specified rectangle from dirty rect for this window
* @param pRect Rectangle to remove from dirty list
*/
void invalidateRect(const CBofRect *pRect);
virtual void onBofButton(CBofObject *pButton, int nExtraInfo);
virtual void onBofScrollBar(CBofObject *pButton, int nNewPos);
virtual void onBofListBox(CBofObject *pListBox, int nItemIndex);
virtual void onMainLoop();
virtual void onSoundNotify(CBofObject *pObject, uint32 lParam2);
virtual void onMovieNotify(uint32 lParam1, uint32 lParam2);
virtual void onMCINotify(uint32 wParam, uint32 lParam);
virtual void onTimer(uint32 nTimerId);
/**
* Handles a pending ScummVM event
* @param event Event to process
*/
virtual void handleEvent(const Common::Event &event);
Graphics::ManagedSurface *getSurface();
bool isCreated() const {
return _surface != nullptr;
}
virtual void enable() {
_enabled = true;
updateWindow();
}
virtual void disable() {
_enabled = false;
updateWindow();
}
bool isVisible() const {
return _visible;
}
bool isEnabled() const {
return _enabled;
}
void updateWindow();
void setParent(CBofWindow *parent);
/**
* Handle all pending ScummVM events
*/
void handleEvents();
virtual void onKeyHit(uint32 lKey, uint32 lRepCount);
void fillWindow(byte iColor);
void fillRect(CBofRect *pRect, byte iColor);
ErrorCode paintBeveledText(CBofRect *rect, const CBofString &string, int size, int weight, COLORREF color, int justify, uint32 format);
protected:
CBofWindow *_parent = nullptr; // Pointer to parent window
Array<CBofWindow *> _children; // Child element pointers
virtual void onMouseMove(uint32 nFlags, CBofPoint *pPoint, void * = nullptr);
virtual void onLButtonDown(uint32 nFlags, CBofPoint *pPoint, void * = nullptr);
virtual void onLButtonUp(uint32 nFlags, CBofPoint *pPoint, void * = nullptr);
virtual void onLButtonDblClk(uint32 nFlags, CBofPoint *pPoint);
virtual void onRButtonDown(uint32 nFlags, CBofPoint *pPoint);
virtual void onRButtonUp(uint32 nFlags, CBofPoint *pPoint);
virtual void onRButtonDblClk(uint32 nFlags, CBofPoint *pPoint);
virtual void onReSize(CBofSize *pSize);
virtual void onPaint(CBofRect *pRect);
virtual void onClose();
virtual void onUserMessage(uint32 nMessage, uint32 lParam);
virtual void onActivate();
virtual void onDeActivate();
// Window Data
char _szTitle[MAX_TITLE] = { 0 }; // Title of window
CBofRect _cWindowRect; // Screen based area of this window
CBofRect _cRect; // Window-based area of this window
CBofBitmap *_pBackdrop = nullptr; // Backdrop bitmap
uint32 _nID = 0; // ID of this window
COLORREF _cBkColor = RGB(255, 255, 255);
COLORREF _cFgColor = RGB(0, 0, 0);
bool _bCaptured = false;
Graphics::ManagedSurface *_surface = nullptr;
static CBofWindow *_pWindowList;
static CBofWindow *_pActiveWindow;
static CBofTimerPacket *_pTimerList;
CBofPoint _cPrevMouseDown;
static int _mouseX;
static int _mouseY;
};
class CBofMessage : public CBofObject {
public:
CBofWindow *_pWindow; // destination window for message
uint32 _nMessage; // message to send (usually WM_USER)
uint32 _lParam1; // user defined info
uint32 _lParam2; // more user defined info
};
class CBofTimerPacket : public CBofObject, public CLList {
public:
CBofWindow *_pOwnerWindow;
BofCallback _pCallBack;
uint32 _nID;
uint32 _nInterval;
};
extern CBofWindow *g_hackWindow;
} // namespace SpaceBar
} // namespace Bagel
#endif

View File

@@ -0,0 +1,496 @@
/* 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/>.
*
*/
#ifndef BAGEL_BOFLIB_LIST_H
#define BAGEL_BOFLIB_LIST_H
#include "common/scummsys.h"
#include "bagel/boflib/misc.h"
namespace Bagel {
namespace SpaceBar {
#define MIN_NODES 5 // Minimum # of pre-allocated nodes in node array
template<class T>
class CBofListNode {
protected:
T _cItem; // Data contained at this node
public:
CBofListNode() {
_pNext = _pPrev = nullptr;
}
CBofListNode(T cItem) {
_pNext = _pPrev = nullptr;
_cItem = cItem;
}
T getNodeItem() {
return _cItem;
}
void setNodeItem(T cItem) {
_cItem = cItem;
}
CBofListNode *_pNext; // Next node in list
CBofListNode *_pPrev; // Previous node in list
};
template<class T>
class CBofList {
private:
void newItemList() {
if (_pItemList != nullptr) {
bofFree(_pItemList);
_pItemList = nullptr;
}
if (_nNumItems != 0) {
_pItemList = (void **)bofAlloc(_nNumItems * sizeof(void *));
}
}
void killItemList() {
if (_pItemList != nullptr) {
bofFree(_pItemList);
_pItemList = nullptr;
}
}
void recalcItemList() {
// We only want to recalc if we're about to overflow what we have
if (_nNumItems >= _nItemsAllocated) {
if (_pItemList != nullptr) {
bofFree(_pItemList);
_pItemList = nullptr;
}
if (_nNumItems != 0) {
assert(_nItemsAllocated < 0x8000);
_nItemsAllocated *= 2;
if (_nItemsAllocated == 0)
_nItemsAllocated = MIN_NODES;
_pItemList = (void **)bofAlloc(_nItemsAllocated * sizeof(void *));
}
}
if (_nNumItems != 0) {
assert(_pItemList != nullptr);
int i = 0;
CBofListNode<T> *pNode = _pHead;
while (pNode != nullptr) {
*(_pItemList + i++) = pNode;
pNode = pNode->_pNext;
}
}
}
/**
* Allocates a new CBofListNode with specified data
* @param cItem Data to store in new node
* @returns Pointer to new node
*/
CBofListNode<T> *newNode(T cItem) {
CBofListNode<T> *pNewNode = new CBofListNode<T>(cItem);
return pNewNode;
}
/**
* Calculates the actual head of this linked list
* @remarks This function is used for debugging to verify that _pHead
* is still pointing to the 1st node in the list.
* @returns Pointer to head of list
*/
CBofListNode<T> *getActualHead() {
CBofListNode<T> *pNode;
CBofListNode<T> *pLast = pNode = _pHead;
while (pNode != nullptr) {
pLast = pNode;
pNode = pNode->_pPrev;
}
return pLast;
}
/**
* Calculates the actual tail of this linked list
* @remarks This function is used for debugging to verify that _pTail
* is still pointing to the last node in the list.
* @returns Pointer to tail of list
*/
CBofListNode<T> *getActualTail() {
CBofListNode<T> *pNode;
CBofListNode<T> *pLast = pNode = _pTail;
while (pNode != nullptr) {
pLast = pNode;
pNode = pLast->_pNext;
}
return pLast;
}
protected:
uint32 _nNumItems;
uint32 _nItemsAllocated;
CBofListNode<T> *_pHead; // pointer to head of list
CBofListNode<T> *_pTail; // pointer to tail of list
void **_pItemList; // pointer to secondary node list
public:
/*
* Constructor
*/
CBofList() {
_nNumItems = 0;
_nItemsAllocated = 0;
_pHead = _pTail = nullptr;
_pItemList = nullptr;
}
/**
* Destructor
*/
virtual ~CBofList() {
removeAll();
killItemList();
assert(_nNumItems == 0);
}
int getCount() const {
return _nNumItems;
}
/**
* Retrieves the number of items in this list
* @returns Returns the number of linked items in this linked list.
*/
int getActualCount() const {
uint32 nCount = 0;
CBofListNode<T> *pNode = _pHead;
while (pNode != nullptr) {
nCount++;
pNode = pNode->_pNext;
}
// There should be no discrepancy
assert(_nNumItems == nCount);
return _nNumItems;
}
/**
* Returns true if the list is empty
* @return
*/
bool isEmpty() const {
return _pHead == nullptr;
}
/**
* Retrieves the item at the specified location
* @returns Returns the item located at the node with given index.
* @param nNodeIndex Index of node to retrieve
*/
inline T getNodeItem(int nNodeIndex) {
CBofListNode<T> *pNode = getNode(nNodeIndex);
assert(pNode != nullptr);
return pNode->getNodeItem();
}
void setNodeItem(int nNodeIndex, T tNewItem) {
CBofListNode<T> *pNode = getNode(nNodeIndex);
assert(pNode != nullptr);
pNode->setNodeItem(tNewItem);
}
T operator[](int nIndex) {
return getNodeItem(nIndex);
}
/**
* Retrieves the node at the specified location
* @returns Returns the node located at the given index.
* @param nNodeIndex Index of node to retrieve
*/
CBofListNode<T> *getNode(int nNodeIndex) {
assert(nNodeIndex >= 0 && nNodeIndex < getCount());
CBofListNode<T> *pNode;
if (_pItemList == nullptr) {
pNode = _pHead;
while (pNode != nullptr) {
if (nNodeIndex-- == 0)
break;
pNode = pNode->_pNext;
}
} else {
pNode = (CBofListNode<T> *)(*(_pItemList + nNodeIndex));
}
return pNode;
}
/**
* Inserts a new node as the previous node to the one specified
* @param nNodeIndex Index of node to insert before
* @param cNewItem Data to store at new node
*/
void insertBefore(int nNodeIndex, T cNewItem) {
assert(!isEmpty());
insertBefore(getNode(nNodeIndex), cNewItem);
}
/**
* Inserts a new node as the previous node to the one specified
* @param pNode Node to insert before
* @param cNewItem Data to store at new node
*/
void insertBefore(CBofListNode<T> *pNode, T cNewItem) {
assert(pNode != nullptr);
assert(!isEmpty());
if (pNode == _pHead) {
addToHead(cNewItem);
} else {
CBofListNode<T> *pNewNode = newNode(cNewItem);
pNewNode->_pPrev = pNode->_pPrev;
pNewNode->_pNext = pNode;
if (pNode->_pPrev != nullptr)
pNode->_pPrev->_pNext = pNewNode;
pNode->_pPrev = pNewNode;
}
// one more item in list
assert(_nNumItems != 0xFFFF);
_nNumItems++;
recalcItemList();
}
/**
* Inserts a new node as the next node to the one specified
* @param nNodeIndex Index of node to insert after
* @param cNewItem Data to store at new node
*/
void insertAfter(int nNodeIndex, T cNewItem) {
assert(!isEmpty());
insertAfter(getNode(nNodeIndex), cNewItem);
}
/**
* Inserts a new node as the next node to the one specified
* @param pNode Node to insert after
* @param cNewItem Data to store at new node
*/
void insertAfter(CBofListNode<T> *pNode, T cNewItem) {
assert(pNode != nullptr);
assert(!isEmpty());
if (pNode == _pTail) {
addToTail(cNewItem);
} else {
CBofListNode<T> *pNewNode = newNode(cNewItem);
pNewNode->_pPrev = pNode;
pNewNode->_pNext = pNode->_pNext;
if (pNode->_pNext != nullptr)
pNode->_pNext->_pPrev = pNewNode;
pNode->_pNext = pNewNode;
}
// one more item in list
assert(_nNumItems != 0xFFFF);
_nNumItems++;
recalcItemList();
}
/**
* Removes specified node from the list
* @param pNode Node to remove
* @returns Item stored at specified location
*/
T remove(CBofListNode<T> *pNode) {
assert(pNode != nullptr);
// One less item in list
_nNumItems--;
//assert(_nNumItems >= 0);
if (pNode != nullptr) {
T retVal = pNode->getNodeItem();
if (_pHead == pNode)
_pHead = _pHead->_pNext;
if (_pTail == pNode)
_pTail = _pTail->_pPrev;
if (pNode->_pPrev != nullptr)
pNode->_pPrev->_pNext = pNode->_pNext;
if (pNode->_pNext != nullptr)
pNode->_pNext->_pPrev = pNode->_pPrev;
delete pNode;
recalcItemList();
return retVal;
} else {
return T();
}
}
/**
* Removes specified node (by index) from the list
* @param nNodeIndex Index of node to remove
* @returns Item stored at specified location
*/
T remove(int nNodeIndex) {
return remove(getNode(nNodeIndex));
}
/**
* Removes all nodes from this list
* @remarks Deletes all memory used by the nodes in this list
*/
void removeAll() {
int i = getCount();
while (i-- != 0)
remove(0);
}
/**
* Removes specified node (by index) from the list
* @returns Item stored at specified location
*/
inline T removeHead() {
assert(_pHead != nullptr);
return remove(_pHead);
}
/**
* Removes specified node (by index) from the list
* @returns Item stored at specified location
*/
inline T removeTail() {
assert(_pTail != nullptr);
return remove(_pTail);
}
/**
* Adds specified node as the new head of this list
* @param pNewNode Pointer to node to add to the list
*/
inline void addToHead(CBofListNode<T> *pNewNode) {
assert(pNewNode != nullptr);
pNewNode->_pNext = _pHead;
pNewNode->_pPrev = nullptr;
if (_pHead != nullptr)
_pHead->_pPrev = pNewNode;
_pHead = pNewNode;
if (_pTail == nullptr)
_pTail = _pHead;
// one less item in list
assert(_nNumItems != 0xFFFF);
_nNumItems++;
recalcItemList();
}
/**
* Adds specified item as the new head of this list
* @param cItem Item to add to the list
*/
inline void addToHead(T cItem) {
addToHead(newNode(cItem));
}
/**
* Adds specified node as the new tail of this list
* @param pNewNode Pointer to node to add to the list
*/
void addToTail(CBofListNode<T> *pNewNode) {
assert(pNewNode != nullptr);
pNewNode->_pPrev = _pTail;
pNewNode->_pNext = nullptr;
if (_pTail != nullptr)
_pTail->_pNext = pNewNode;
_pTail = pNewNode;
if (_pHead == nullptr)
_pHead = _pTail;
// One more item in list
assert(_nNumItems != 0xFFFF);
_nNumItems++;
recalcItemList();
}
/**
* Adds specified item as the new tail of this list
* @param cItem Item to add to the list
*/
void addToTail(T cItem) {
addToTail(newNode(cItem));
}
CBofListNode<T> *getHead() const {
return _pHead;
}
CBofListNode<T> *getTail() const {
return _pTail;
}
};
} // namespace SpaceBar
} // namespace Bagel
#endif

View File

@@ -0,0 +1,361 @@
/* 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 "common/config-manager.h"
#include "common/file.h"
#include "bagel/spacebar/boflib/options.h"
#include "bagel/boflib/misc.h"
#include "bagel/boflib/log.h"
#include "bagel/boflib/string_functions.h"
namespace Bagel {
namespace SpaceBar {
COption::COption(const char *pszInit) {
_szBuf[0] = '\0';
if (pszInit != nullptr) {
assert(strlen(pszInit) < MAX_OPTION_LEN);
Common::strcpy_s(_szBuf, pszInit);
}
}
CBofOptions::CBofOptions(const char *pszOptionFile) {
_szFileName[0] = '\0';
_pOptionList = nullptr;
_bDirty = false;
if (pszOptionFile != nullptr) {
loadOptionFile(pszOptionFile);
}
}
CBofOptions::~CBofOptions() {
assert(isValidObject(this));
release();
_szFileName[0] = '\0';
}
ErrorCode CBofOptions::loadOptionFile(const char *pszOptionFile) {
assert(isValidObject(this));
assert(pszOptionFile != nullptr);
assert(*pszOptionFile != '\0');
assert(strlen(pszOptionFile) < MAX_FNAME);
release();
Common::strcpy_s(_szFileName, pszOptionFile);
return load();
}
ErrorCode CBofOptions::load() {
assert(isValidObject(this));
// Assume no error
ErrorCode errorCode = ERR_NONE;
// Free any previous option info
release();
Common::File f;
if (Common::File::exists(_szFileName) && f.open(_szFileName)) {
char szBuf[MAX_OPTION_LEN];
assert(_pOptionList == nullptr);
while (readLine(&f, szBuf)) {
COption *pNewOption = new COption(szBuf);
if (_pOptionList != nullptr) {
_pOptionList->addToTail(pNewOption);
} else {
_pOptionList = pNewOption;
}
}
if (_pOptionList != nullptr) {
// _pOptionList must always be the head of the list!
assert(_pOptionList == _pOptionList->getHead());
}
f.close();
} else {
errorCode = ERR_FOPEN;
}
return errorCode;
}
void CBofOptions::release() {
assert(isValidObject(this));
commit();
// Release each item in the list
while (_pOptionList != nullptr) {
COption *pNextItem = (COption *)_pOptionList->getNext();
delete _pOptionList;
_pOptionList = pNextItem;
}
}
ErrorCode CBofOptions::commit() {
assert(isValidObject(this));
ErrorCode errorCode = ERR_NONE;
if ((_pOptionList != nullptr) && _bDirty) {
// _pOptionList must always be the head of the list!
assert(_pOptionList == _pOptionList->getHead());
warning("TODO: Look into refactoring options to ConfMan if needed");
}
return errorCode;
}
ErrorCode CBofOptions::writeSetting(const char *pszSection, const char *pszVar, const char *pszNewValue) {
// Can't access nullptr pointers
assert(pszSection != nullptr);
assert(pszVar != nullptr);
assert(pszNewValue != nullptr);
char szValueBuf[MAX_OPTION_LEN];
// Assume no error
ErrorCode errorCode = ERR_NONE;
// Indicate that the options file needs to be updated
_bDirty = true;
Common::sprintf_s(szValueBuf, "%s=%s", pszVar, pszNewValue);
// Find this option based on it's section
COption *pOption = findOption(pszSection, pszVar);
if (pOption != nullptr) {
// Update option with new value
Common::strcpy_s(pOption->_szBuf, szValueBuf);
} else {
// Did not find option (or possibly also did not find section)
// If this section is not in the file
COption *pSection = findSection(pszSection);
if (pSection == nullptr) {
char szSectionBuf[MAX_OPTION_LEN];
// Then create a new section
Common::sprintf_s(szSectionBuf, "[%s]", pszSection);
pSection = new COption(szSectionBuf);
if (_pOptionList != nullptr) {
_pOptionList->addToTail(pSection);
} else {
_pOptionList = pSection;
}
}
// Add this option to the specified section
pOption = new COption(szValueBuf);
pSection->Insert(pOption);
}
return errorCode;
}
ErrorCode CBofOptions::writeSetting(const char *pszSection, const char *pszVar, int nNewValue) {
// Can't access nullptr pointers
assert(pszSection != nullptr);
assert(pszVar != nullptr);
char szBuf[20];
Common::sprintf_s(szBuf, "%d", nNewValue);
ErrorCode errorCode = writeSetting(pszSection, pszVar, szBuf);
return errorCode;
}
ErrorCode CBofOptions::readSetting(const char *section, const char *option, char *stringValue, const char *defaultValue, uint32 maxLen) {
// Can't access nullptr pointers
assert(section != nullptr);
assert(option != nullptr);
assert(stringValue != nullptr);
assert(defaultValue != nullptr);
// If ConfMan has a key of a given name, no matter the section,
// than it takes precedence over any INI file value
if (ConfMan.hasKey(option)) {
Common::String str = ConfMan.get(option);
Common::strcpy_s(stringValue, maxLen, str.c_str());
return ERR_NONE;
}
char szBuf[MAX_OPTION_LEN];
// Assume no error
ErrorCode errorCode = ERR_NONE;
// Assume we will need to use the default setting
Common::strcpy_s(stringValue, maxLen, defaultValue);
// Try to find this option
COption *pOption = findOption(section, option);
if (pOption != nullptr) {
assert(strlen(pOption->_szBuf) < MAX_OPTION_LEN);
Common::strcpy_s(szBuf, pOption->_szBuf);
// Strip out any comments
strreplaceChar(szBuf, ';', '\0');
// Find 1st equal sign
char *p = strchr(szBuf, '=');
// Error in .INI file if we can't find the equal sign
if (p != nullptr) {
p++;
if (strlen(p) > 0)
Common::strcpy_s(stringValue, maxLen, p);
} else {
logError(buildString("Error in %s, section: %s, entry: %s", _szFileName, section, option));
errorCode = ERR_FTYPE;
}
}
return errorCode;
}
ErrorCode CBofOptions::readSetting(const char *section, const char *option, int *intValue, int defaultValue) {
assert(section != nullptr);
assert(option != nullptr);
assert(intValue != nullptr);
// If ConfMan has a key of a given name, no matter the section,
// than it takes precedence over any INI file value
if (ConfMan.hasKey(option)) {
*intValue = ConfMan.getInt(option);
return ERR_NONE;
}
char szDefault[20], szBuf[20];
Common::sprintf_s(szDefault, "%d", defaultValue);
ErrorCode errorCode = readSetting(section, option, szBuf, szDefault, 20);
*intValue = atoi(szBuf);
return errorCode;
}
ErrorCode CBofOptions::readSetting(const char *section, const char *option, bool *boolValue, bool defaultValue) {
assert(section != nullptr);
assert(option != nullptr);
assert(boolValue != nullptr);
// If ConfMan has a key of a given name, no matter the section,
// than it takes precedence over any INI file value
if (ConfMan.hasKey(option)) {
*boolValue = ConfMan.getBool(option);
return ERR_NONE;
}
int v;
ErrorCode errorCode = readSetting(section, option, &v, defaultValue);
*boolValue = v != 0;
return errorCode;
}
COption *CBofOptions::findSection(const char *pszSection) {
assert(isValidObject(this));
assert(pszSection != nullptr);
assert(*pszSection != '\0');
char szSectionBuf[MAX_OPTION_LEN];
Common::sprintf_s(szSectionBuf, "[%s]", pszSection);
int nLength = strlen(szSectionBuf);
COption *pOption = _pOptionList;
while (pOption != nullptr) {
if (!scumm_strnicmp(pOption->_szBuf, szSectionBuf, nLength)) {
break;
}
pOption = (COption *)pOption->getNext();
}
return pOption;
}
COption *CBofOptions::findOption(const char *pszSection, const char *pszVar) {
assert(isValidObject(this));
assert(pszSection != nullptr);
assert(pszVar != nullptr);
assert(*pszSection != '\0');
assert(*pszVar != '\0');
// Assume we won't find the option
COption *pFound = nullptr;
int nLength = strlen(pszVar);
COption *pStart = findSection(pszSection);
if (pStart != nullptr) {
COption *pOption = (COption *)pStart->getNext();
while (pOption != nullptr) {
if (pOption->_szBuf[0] == '[') {
// this option was not found
pFound = nullptr;
break;
}
if (!scumm_strnicmp(pOption->_szBuf, pszVar, nLength)) {
pFound = pOption;
break;
}
pOption = (COption *)pOption->getNext();
}
}
return pFound;
}
bool CBofOptions::readLine(Common::SeekableReadStream *pFile, char *pszBuf) {
assert(pFile != nullptr);
assert(pszBuf != nullptr);
if (pFile->eos())
return false;
Common::String line = pFile->readLine();
Common::strcpy_s(pszBuf, 256, line.c_str());
return true;
}
} // namespace SpaceBar
} // namespace Bagel

View File

@@ -0,0 +1,169 @@
/* 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/>.
*
*/
#ifndef BAGEL_BOFLIB_OPTIONS_H
#define BAGEL_BOFLIB_OPTIONS_H
#include "common/stream.h"
#include "bagel/boflib/stdinc.h"
#include "bagel/boflib/llist.h"
#include "bagel/boflib/object.h"
#include "bagel/boflib/error.h"
namespace Bagel {
namespace SpaceBar {
#define MAX_OPTION_LEN 100
class COption : public CLList, public CBofObject {
public:
COption(const char *pszInit = nullptr);
char _szBuf[MAX_OPTION_LEN];
};
class CBofOptions : public CBofObject { // CCache
private:
/**
* Finds the specified INI section in current file
* @param pszSection INI section
* @return Pointer to option where this section starts
*/
COption *findSection(const char *pszSection);
/**
* Finds the variable (option) in specified section
* @param pszSection INI section
* @param pszVar Variable name for this option
* @return Pointer to option containing this variable
*/
COption *findOption(const char *pszSection, const char *pszVar);
/**
* Reads one line of text from specified file
* @param pFile Pointer to open file for reading
* @param pszBuf Buffer to fill with text read
*/
bool readLine(Common::SeekableReadStream *pFile, char *pszBuf);
protected:
char _szFileName[MAX_FNAME];
COption *_pOptionList;
bool _bDirty;
public:
/**
* Constructor
* @brief Loads specified .INI file which contains user options.
* @param pszOptionsFile Name of .INI settings file
**/
CBofOptions(const char *pszOptionsFile = nullptr);
/**
* Destructor
*/
virtual ~CBofOptions();
/**
* Updates current option list file
* @return Error return code
*/
ErrorCode commit();
/**
* Loads specified .INI options file
* @brief Loads and builds Option list
* @param pszFile Name of .INI file to load
* @return Error return code
**/
ErrorCode loadOptionFile(const char *pszFile);
const char *getFileName() const {
return ((const char *)_szFileName);
}
/**
* Adds or modifies 1 option in list
* @param pszSection Section in .INI file
* @param pszOption Option to update
* @param pszValue New value
* @return Error return code
*/
ErrorCode writeSetting(const char *pszSection, const char *pszOption, const char *pszValue);
/**
* Adds or modifies 1 option in list
* @param pszSection Section in .INI file
* @param pszOption Option to update
* @param nValue New value
* @return Error return code
*/
ErrorCode writeSetting(const char *pszSection, const char *pszOption, int nValue);
/**
* Reads value for the specified option
* @param section Section in .INI file
* @param option Option to update
* @param stringValue Destination buffer for read value
* @param defaultValue Default value if not exists
* @param nSize Max length of stringValue buffer
* @return Error return code
*/
ErrorCode readSetting(const char *section, const char *option, char *stringValue, const char *defaultValue, uint32 nSize);
/**
* Reads value for the specified option
* @param section Section in .INI file
* @param option Option to update
* @param nValue Pointer to write value to
* @param defaultValue Default value if not exists
* @return Error return code
*/
ErrorCode readSetting(const char *section, const char *option, int *nValue, int defaultValue);
/**
* Reads value for the specified option
* @param section Section in .INI file
* @param option Option to update
* @param boolValue Pointer to write value to
* @param defaultValue Default value if not exists
* @return Error return code
*/
ErrorCode readSetting(const char *section, const char *option, bool *boolValue, bool defaultValue);
/**
* Loads current .INI options file
* @return Error return code
*/
ErrorCode load();
/**
* Updates and Releases current option list
*/
void release();
};
} // namespace SpaceBar
} // namespace Bagel
#endif

View File

@@ -0,0 +1,163 @@
/* 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 "bagel/spacebar/boflib/res.h"
#include "bagel/spacebar/boflib/debug.h"
#include "bagel/boflib/misc.h"
#include "bagel/boflib/string_functions.h"
#include "bagel/boflib/log.h"
namespace Bagel {
namespace SpaceBar {
CBofStringTable::CBofStringTable(const char *pszFileName) : CBofFile(nullptr) {
assert(pszFileName != nullptr);
load(pszFileName);
}
CBofStringTable::~CBofStringTable() {
assert(isValidObject(this));
release();
}
ErrorCode CBofStringTable::load(const char *pszFileName) {
assert(isValidObject(this));
// Deallocate any previous data
release();
// Open this string file
open(pszFileName);
_lBufSize = getLength();
assert(_lBufSize > 0);
// Allocate a buffer to hold entire file and fill it with 0s
_pBuf = (byte *)bofCleanAlloc(_lBufSize + 1);
// Read in entire file
read(_pBuf, _lBufSize);
buildTable();
// Don't need this file open anymore
close();
return _errCode;
}
void CBofStringTable::release() {
assert(isValidObject(this));
killTable();
if (_pBuf != nullptr) {
bofFree(_pBuf);
_pBuf = nullptr;
}
}
void CBofStringTable::killTable() {
assert(isValidObject(this));
CResString *pString = _pStringTable;
while (pString != nullptr) {
CResString *pNextString = (CResString *)pString->getNext();
delete pString;
pString = pNextString;
}
_pStringTable = nullptr;
}
ErrorCode CBofStringTable::buildTable() {
assert(isValidObject(this));
// Deallocate any previous table
killTable();
assert(_pStringTable == nullptr);
assert(_pBuf != nullptr);
memreplaceChar(_pBuf, '\r', '\0', _lBufSize);
memreplaceChar(_pBuf, '\n', '\0', _lBufSize);
const byte *pBuf = _pBuf;
while (pBuf < _pBuf + _lBufSize) {
int nId = atoi((const char *)pBuf);
pBuf = (const byte *)strchr((const char *)pBuf, '=');
if (pBuf == nullptr) {
reportError(ERR_NONE, "Parsing error in buildTable()");
break;
}
pBuf++;
CResString *pString = new CResString(nId, (const char *)pBuf);
// Add this string to the table
if (_pStringTable == nullptr) {
_pStringTable = pString;
} else {
_pStringTable->addToTail(pString);
}
while (*pBuf++ != '\0') {
if (pBuf > _pBuf + _lBufSize)
break;
}
while (*pBuf == '\0') {
pBuf++;
if (pBuf > _pBuf + _lBufSize)
break;
}
}
return _errCode;
}
const char *CBofStringTable::getString(int nId) {
assert(isValidObject(this));
CResString *pCurString = _pStringTable;
const char *pszString = nullptr;
while (pCurString != nullptr) {
if (pCurString->_nId == nId) {
pszString = (const char *)pCurString->_pszString;
break;
}
pCurString = (CResString *)pCurString->getNext();
}
if (pCurString == nullptr) {
logWarning(buildString("Resource String %d not found in %s", nId, _szFileName));
}
return pszString;
}
} // namespace SpaceBar
} // namespace Bagel

View File

@@ -0,0 +1,96 @@
/* 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/>.
*
*/
#ifndef BAGEL_BOFLIB_RES_H
#define BAGEL_BOFLIB_RES_H
#include "bagel/spacebar/boflib/file.h"
#include "bagel/boflib/llist.h"
#include "bagel/boflib/object.h"
namespace Bagel {
namespace SpaceBar {
class CResString : public CLList, public CBofObject {
public:
CResString(int nId, const char *pszString) {
_nId = nId;
_pszString = pszString;
}
int _nId;
const char *_pszString;
};
class CBofStringTable : public CBofFile {
private:
CResString *_pStringTable = nullptr;
byte *_pBuf = nullptr;
uint32 _lBufSize = 0;
protected:
/**
* Loads string table from specified res file
* @param pszFileName Name of file containing resources
* @return Error return code
*/
ErrorCode load(const char *pszFileName);
/**
* De-allocates the current Resource String Table
*/
void release();
/**
* Allocates the current Resource String Table
* @return Error return code
*/
ErrorCode buildTable();
/**
* De-allocates the current Resource String Table
*/
void killTable();
public:
/**
* Constructor for Boffo Resource String Table
* @param pszFileName Name of file containing resources
**/
CBofStringTable(const char *pszFileName);
/**
* Destructor
*/
virtual ~CBofStringTable();
/**
* Retrieves the specified resource string
* @param nId Res ID for string to be retrieved
*/
const char *getString(int nId);
};
} // namespace SpaceBar
} // namespace Bagel
#endif

View File

@@ -0,0 +1,167 @@
/* 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/>.
*
*/
#ifndef BAGEL_BOFLIB_STD_KEYS_H
#define BAGEL_BOFLIB_STD_KEYS_H
#include "bagel/boflib/stdinc.h"
namespace Bagel {
namespace SpaceBar {
// Boffo Key defs
//
#define BKEY_UNKNOWN 0x00000000
#define BKF_ALT 0x10000000
#define BKF_CMD 0x10000000 // Mac's Flower/Clover/Command key
#define BKF_CTRL 0x20000000
#define BKF_SHIFT 0x40000000
#define BKEY_0 '0'
#define BKEY_1 '1'
#define BKEY_2 '2'
#define BKEY_3 '3'
#define BKEY_4 '4'
#define BKEY_5 '5'
#define BKEY_6 '6'
#define BKEY_7 '7'
#define BKEY_8 '8'
#define BKEY_9 '9'
#define BKEY_a (uint32)'a'
#define BKEY_b (uint32)'b'
#define BKEY_c (uint32)'c'
#define BKEY_d (uint32)'d'
#define BKEY_e (uint32)'e'
#define BKEY_f (uint32)'f'
#define BKEY_g (uint32)'g'
#define BKEY_h (uint32)'h'
#define BKEY_i (uint32)'i'
#define BKEY_j (uint32)'j'
#define BKEY_k (uint32)'k'
#define BKEY_l (uint32)'l'
#define BKEY_m (uint32)'m'
#define BKEY_n (uint32)'n'
#define BKEY_o (uint32)'o'
#define BKEY_p (uint32)'p'
#define BKEY_q (uint32)'q'
#define BKEY_r (uint32)'r'
#define BKEY_s (uint32)'s'
#define BKEY_t (uint32)'t'
#define BKEY_u (uint32)'u'
#define BKEY_v (uint32)'v'
#define BKEY_w (uint32)'w'
#define BKEY_x (uint32)'x'
#define BKEY_y (uint32)'y'
#define BKEY_z (uint32)'z'
#define BKEY_PLUS (uint32)'+'
#define BKEY_MINUS (uint32)'-'
#define BKEY_PERIOD (uint32)'.'
#define BKEY_ALT_a (BKEY_a | BKF_ALT)
#define BKEY_ALT_b (BKEY_b | BKF_ALT)
#define BKEY_ALT_c (BKEY_c | BKF_ALT)
#define BKEY_ALT_d (BKEY_d | BKF_ALT)
#define BKEY_ALT_e (BKEY_e | BKF_ALT)
#define BKEY_ALT_f (BKEY_f | BKF_ALT)
#define BKEY_ALT_g (BKEY_g | BKF_ALT)
#define BKEY_ALT_h (BKEY_h | BKF_ALT)
#define BKEY_ALT_i (BKEY_i | BKF_ALT)
#define BKEY_ALT_j (BKEY_j | BKF_ALT)
#define BKEY_ALT_k (BKEY_k | BKF_ALT)
#define BKEY_ALT_l (BKEY_l | BKF_ALT)
#define BKEY_ALT_m (BKEY_m | BKF_ALT)
#define BKEY_ALT_n (BKEY_n | BKF_ALT)
#define BKEY_ALT_o (BKEY_o | BKF_ALT)
#define BKEY_ALT_p (BKEY_p | BKF_ALT)
#define BKEY_ALT_q (BKEY_q | BKF_ALT)
#define BKEY_ALT_r (BKEY_r | BKF_ALT)
#define BKEY_ALT_s (BKEY_s | BKF_ALT)
#define BKEY_ALT_t (BKEY_t | BKF_ALT)
#define BKEY_ALT_u (BKEY_u | BKF_ALT)
#define BKEY_ALT_v (BKEY_v | BKF_ALT)
#define BKEY_ALT_w (BKEY_w | BKF_ALT)
#define BKEY_ALT_x (BKEY_x | BKF_ALT)
#define BKEY_ALT_y (BKEY_y | BKF_ALT)
#define BKEY_ALT_z (BKEY_z | BKF_ALT)
#define BKEY_BASE 0x00000100
#define BKEY_F1 (BKEY_BASE + 1)
#define BKEY_F2 (BKEY_BASE + 2)
#define BKEY_F3 (BKEY_BASE + 3)
#define BKEY_F4 (BKEY_BASE + 4)
#define BKEY_SAVE (BKEY_BASE + 5)
#define BKEY_F6 (BKEY_BASE + 6)
#define BKEY_RESTORE (BKEY_BASE + 7)
#define BKEY_F8 (BKEY_BASE + 8)
#define BKEY_F9 (BKEY_BASE + 9)
#define BKEY_F10 (BKEY_BASE + 10)
#define BKEY_F11 (BKEY_BASE + 11)
#define BKEY_F12 (BKEY_BASE + 12)
#define BKEY_ALT_F1 (BKEY_F1 | BKF_ALT)
#define BKEY_ALT_F2 (BKEY_F2 | BKF_ALT)
#define BKEY_ALT_F3 (BKEY_F3 | BKF_ALT)
#define BKEY_ALT_F4 (BKEY_F4 | BKF_ALT)
#define BKEY_ALT_F5 (BKEY_F5 | BKF_ALT)
#define BKEY_ALT_F6 (BKEY_F6 | BKF_ALT)
#define BKEY_ALT_F7 (BKEY_F7 | BKF_ALT)
#define BKEY_ALT_F8 (BKEY_F8 | BKF_ALT)
#define BKEY_ALT_F9 (BKEY_F9 | BKF_ALT)
#define BKEY_ALT_F10 (BKEY_F10 | BKF_ALT)
#define BKEY_ALT_F11 (BKEY_F11 | BKF_ALT)
#define BKEY_ALT_F12 (BKEY_F12 | BKF_ALT)
#define BKEY_ESC (uint32)0x1B
#define BKEY_BACK (uint32)0x08
#define BKEY_ENTER (uint32)0x0D
#define BKEY_SPACE (uint32)' '
#define BKEY_PERIOD (uint32)'.'
#define BKEY_END (BKEY_BASE + 14)
#define BKEY_HOME (BKEY_BASE + 15)
#define BKEY_LEFT (BKEY_BASE + 16)
#define BKEY_RIGHT (BKEY_BASE + 17)
#define BKEY_UP (BKEY_BASE + 18)
#define BKEY_DOWN (BKEY_BASE + 19)
#define BKEY_INS (BKEY_BASE + 20)
#define BKEY_DEL (BKEY_BASE + 21)
#define BKEY_ALT_UP (BKEY_UP | BKF_ALT)
#define BKEY_ALT_DOWN (BKEY_DOWN | BKF_ALT)
#define BKEY_PAGEUP (BKEY_BASE + 22)
#define BKEY_PAGEDOWN (BKEY_BASE + 23)
#define BKEY_SCRL_LOCK (BKEY_BASE + 24)
#define BKEY_SHIFT BKF_SHIFT
#define BKEY_CTRL BKF_CTRL
#define BKEY_ALT BKF_ALT
} // namespace SpaceBar
} // namespace Bagel
#endif

View File

@@ -0,0 +1,126 @@
/* 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 "common/system.h"
#include "bagel/spacebar/boflib/timer.h"
#include "bagel/bagel.h"
#include "bagel/boflib/log.h"
namespace Bagel {
namespace SpaceBar {
bool CBofTimer::_bModified = false;
CBofTimer *CBofTimer::_pTimerList = nullptr;
WindowTimer::WindowTimer(uint32 interval, uint32 id, BofCallback callback) :
_interval(interval), _id(id), _callback(callback) {
_lastExpiryTime = g_system->getMillis();
}
CBofTimer::CBofTimer() {
_lLastTime = 0;
_nID = 0;
_nInterval = 0;
_pCallBack = nullptr;
_lUserInfo = 0;
_bActive = false;
// Another item for the list
if (_pTimerList == nullptr) {
_pTimerList = this;
} else {
_pTimerList->addToTail(this);
}
// Creating a new timer object modifies the timer list
_bModified = true;
}
CBofTimer::CBofTimer(uint32 nID, uint32 nInterval, void *lUserInfo, BofCallback pCallBack) {
_lLastTime = 0;
_nID = nID;
_nInterval = nInterval;
_pCallBack = pCallBack;
_lUserInfo = lUserInfo;
_bActive = false;
// Another item for the list
if (_pTimerList == nullptr) {
_pTimerList = this;
} else {
_pTimerList->addToTail(this);
}
// Creating a new timer object modifies the timer list
_bModified = true;
}
CBofTimer::~CBofTimer() {
assert(isValidObject(this));
if (_pTimerList == this) {
_pTimerList = (CBofTimer *)_pTimerList->getNext();
}
// Removing a timer object modifies the timer list
_bModified = true;
}
void CBofTimer::handleTimers() {
CBofTimer *pTimer = _pTimerList;
while (pTimer != nullptr) {
if (pTimer->isActive()) {
uint32 lCurrentTime = g_system->getMillis();
if ((uint32)(lCurrentTime - pTimer->_lLastTime) >= pTimer->_nInterval) {
// Remember for next time
pTimer->_lLastTime = lCurrentTime;
if (pTimer->_pCallBack != nullptr) {
// Execute call back
(*pTimer->_pCallBack)(pTimer->_nID, pTimer->_lUserInfo);
// If callback modifies the timer list, then we must start over
if (_bModified) {
pTimer = _pTimerList;
continue;
}
} else {
// Otherwise, something is wrong
logWarning(buildString("Timer without a callback: %d", pTimer->_nID));
}
}
}
pTimer = (CBofTimer *)pTimer->getNext();
}
}
} // namespace SpaceBar
} // namespace Bagel

View File

@@ -0,0 +1,112 @@
/* 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/".
*
*/
#ifndef BAGEL_BOFLIB_TIMER_H
#define BAGEL_BOFLIB_TIMER_H
#include "bagel/spacebar/boflib/timer.h"
#include "bagel/boflib/llist.h"
#include "bagel/boflib/palette.h"
namespace Bagel {
namespace SpaceBar {
struct WindowTimer {
uint32 _interval = 0;
uint32 _lastExpiryTime = 0;
uint32 _id = 0;
BofCallback _callback = nullptr;
WindowTimer() { }
WindowTimer(uint32 interval, uint32 id, BofCallback callback);
};
class CBofTimer: public CBofObject, public CLList {
public:
CBofTimer();
CBofTimer(uint32 nID, uint32 nInterval, void *lUserInfo, BofCallback pCallBack);
~CBofTimer();
void start() {
_bActive = true;
}
void stop() {
_bActive = false;
}
bool isActive() {
return _bActive;
}
void setID(uint32 nID) {
_nID = nID;
}
uint32 getID() {
return _nID;
}
void setInterval(uint32 nInterval) {
_nInterval = nInterval;
}
uint32 getInterval() {
return _nInterval;
}
void setUserInfo(void *lUserInfo) {
_lUserInfo = lUserInfo;
}
void *getUserInfo() {
return _lUserInfo;
}
void setCallBack(BofCallback pCallBack) {
_pCallBack = pCallBack;
}
BofCallback getCallBack() {
return _pCallBack;
}
static void handleTimers();
//
// members
//
protected:
static CBofTimer *_pTimerList;
static bool _bModified;
public:
uint32 _lLastTime;
uint32 _nID;
uint32 _nInterval;
BofCallback _pCallBack;
void *_lUserInfo;
bool _bActive;
};
} // namespace SpaceBar
} // namespace Bagel
#endif

View File

@@ -0,0 +1,230 @@
/* 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 "bagel/spacebar/boflib/vector.h"
namespace Bagel {
namespace SpaceBar {
CVector::CVector() {
this->x = 0;
this->y = 0;
this->z = 0;
}
CVector::CVector(const Vector &stVector) {
x = stVector.x;
y = stVector.y;
z = stVector.z;
}
CVector::CVector(double xx, double yy, double zz) {
x = xx;
y = yy;
z = zz;
}
double CVector::length() {
// Make sure this object is not used after it is destructed
assert(isValidObject(this));
return sqrt(x * x + y * y);
}
double CVector::angleBetween(const Vector &vector) {
// Make sure this object is not used after it is destructed
assert(isValidObject(this));
CVector vTmp(vector);
// Get the angle by getting the arc-cosine of the cosine of the
// angle between the 2 vectors.
double fCos = this->dotProduct(vTmp) / (this->length() * vTmp.length());
if (fCos > 1.0) {
fCos = 1.0;
} else if (fCos < -1.0) {
fCos = -1.0;
}
double angle = acos(fCos);
return angle;
}
double CVector::dotProduct(const Vector &vector) {
// Make sure this object is not used after it is destructed
assert(isValidObject(this));
return (this->x * vector.x) + (this->y * vector.y);
}
void CVector::rotate(double angle) {
// Make sure this object is not used after it is destructed
assert(isValidObject(this));
// Get the sine and cosine of the angle
double co = cos(angle);
double si = sin(angle);
double xx = this->x * co - this->y * si;
double yy = this->y * co + this->x * si;
this->x = xx;
this->y = yy;
}
double CVector::realAngle(const Vector &vector) {
// Make sure this object is not used after it is destructed
assert(isValidObject(this));
CVector vTmp = *this;
double angle = vTmp.angleBetween(vector);
if (angle != (double)0.0) {
vTmp.rotate(angle);
// Determine if the angle is greater then 180 degrees
if (((int)(vTmp.angleBetween(vector) * 1000) == 0)) {
angle = 2 * PI - angle;
}
}
return angle;
}
CVector CVector::operator+(Vector vector) {
// Make sure this object is not used after it is destructed
assert(isValidObject(this));
CVector vSum(this->x + vector.x, this->y + vector.y, this->z + vector.z);
return vSum;
}
CVector CVector::operator+(double offset) {
// Make sure this object is not used after it is destructed
assert(isValidObject(this));
CVector vSum(this->x + offset, this->y + offset, this->z + offset);
return vSum;
}
CVector CVector::operator-(Vector vector) {
// Make sure this object is not used after it is destructed
assert(isValidObject(this));
CVector vDif(this->x - vector.x, this->y - vector.y, this->z - vector.z);
return vDif;
}
CVector CVector::operator-(double offset) {
// Make sure this object is not used after it is destructed
assert(isValidObject(this));
CVector vDif(this->x - offset, this->y - offset, this->z - offset);
return vDif;
}
void CVector::operator+=(Vector vector) {
// Make sure this object is not used after it is destructed
assert(isValidObject(this));
this->x += vector.x;
this->y += vector.y;
this->z += vector.z;
}
void CVector::operator-=(Vector vector) {
// Make sure this object is not used after it is destructed
assert(isValidObject(this));
this->x -= vector.x;
this->y -= vector.y;
this->z -= vector.z;
}
CVector CVector::operator*(double scalar) {
// Make sure this object is not used after it is destructed
assert(isValidObject(this));
CVector vProduct(this->x * scalar, this->y * scalar, this->z * scalar);
return vProduct;
}
CVector CVector::operator/(double scalar) {
// Make sure this object is not used after it is destructed
assert(isValidObject(this));
// Can't divide by 0
assert(scalar != (double)0.0);
CVector vDividend;
if (scalar != (double)0.0) {
vDividend.x = this->x / scalar;
vDividend.y = this->y / scalar;
vDividend.z = this->z / scalar;
}
return vDividend;
}
void CVector::operator*=(double scalar) {
// Make sure this object is not used after it is destructed
assert(isValidObject(this));
this->x *= scalar;
this->y *= scalar;
this->z *= scalar;
}
void CVector::operator/=(double scalar) {
// Make sure this object is not used after it is destructed
assert(isValidObject(this));
// can't divide by 0
assert(scalar != (double)0.0);
if (scalar != (double)0.0) {
this->x /= scalar;
this->y /= scalar;
this->z /= scalar;
}
}
bool CVector::operator==(Vector v) {
// Make sure this object is not used after it is destructed
assert(isValidObject(this));
bool bReturn = false;
if ((this->x == v.x) && (this->y == v.y))
bReturn = true;
return bReturn;
}
} // namespace SpaceBar
} // namespace Bagel

View File

@@ -0,0 +1,105 @@
/* 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/>.
*
*/
#ifndef BAGEL_BOFLIB_VECTOR_H
#define BAGEL_BOFLIB_VECTOR_H
#include "bagel/boflib/stdinc.h"
#include "bagel/boflib/object.h"
namespace Bagel {
namespace SpaceBar {
#define PI (double)3.141592653
#define RADCNVT ((double)180 / PI) // PI is 180 degrees
#define Deg2Rad(d) ((d) / RADCNVT) // Converts degrees to radians
#define Rad2Deg(r) ((r) * RADCNVT) // Converts radians to degrees
class CVector : public CBofObject, public Vector {
public:
/**
* Default constructor
*/
CVector();
/**
* Copy constructor
*/
CVector(const Vector &stVector);
/**
* Constructor based on passed figures
*/
CVector(double xx, double yy, double zz = 0);
/**
* Calculates the dot-product of the 2 specified vectors
* @param vector Second vector
* @return Dot-product
*/
double dotProduct(const Vector &vector);
/**
* Rotates this vector the specified number of degrees
* @param fAngle Rotation angle
*/
void rotate(double fAngle);
/**
* Gets the angle between this vector and specified vector
* @param vector Second vector
* @return Angle
*/
double angleBetween(const Vector &vector);
/**
* Calculates the positive or negative angle between 2 vectors
* @param vector Second vector
* @return Angle
*/
double realAngle(const Vector &vector);
/**
* Gets the length of this vector
* @return Vector length
*/
double length();
// Generic operations
CVector operator+(Vector);
CVector operator+(double);
CVector operator-(Vector);
CVector operator-(double);
CVector operator*(double);
CVector operator/(double);
void operator+=(Vector);
void operator-=(Vector);
void operator*=(double);
void operator/=(double);
bool operator==(Vector);
};
} // namespace SpaceBar
} // namespace Bagel
#endif

View File

@@ -0,0 +1,125 @@
/* 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/>.
*
*/
#ifndef BAGEL_BOFLIB_VHASH_TABLE_H
#define BAGEL_BOFLIB_VHASH_TABLE_H
#include "bagel/spacebar/boflib/list.h"
namespace Bagel {
namespace SpaceBar {
template<class T, int S>
class CBofVHashTable {
public:
CBofVHashTable(unsigned(*hashFun)(const T &));
virtual ~CBofVHashTable();
bool contains(const T &val);
void insert(const T &val);
private:
// Default constructor is not allowed. A declaration is provided,
// but an implementation is not. A link error will result if an
// attempt is made to use this constructor. The location which
// is attempting to reference the default constructor should be changed
// so that it calls the class constructor which takes a pointer to a
// function and a table size.
CBofVHashTable();
// Member which holds the hash table itself.
CBofList<T *> _xHashTable[S];
// Member which holds a pointer to the table's hashing
// function.
unsigned(*_pHashFunction)(const T &);
// Member which holds the count of buckets in the hash table.
int _nHashTableSize;
// Boolean which affords a light-weight test of whether the hash
// table is empty.
//
bool _bisEmpty;
};
// CBofVHashTable::CBofVHashTable - class constructor.
//
template<class T, int S>
CBofVHashTable<T, S>::CBofVHashTable(unsigned(*hashFun)(const T &)) : _nHashTableSize(S),
_pHashFunction(hashFun), _bisEmpty(true) {
}
// CBofVHashTable::~CBofVHashTable - class destructor.
template<class T, int S>
CBofVHashTable<T, S>::~CBofVHashTable() {
for (int x = 0; x < _nHashTableSize; x++) {
CBofList<T *> *pHashBucket = &_xHashTable[x];
int nListEntries = pHashBucket->getActualCount();
for (int i = 0; i < nListEntries; i++) {
T *pListItem = pHashBucket->getNodeItem(i);
delete pListItem;
pHashBucket->setNodeItem(i, (T *)nullptr);
}
}
}
// CBofVHashTable<T, S>::insert - add a value to the hash table.
template<class T, int S>
void CBofVHashTable<T, S>::insert(const T &val) {
T *pNodeValue = new T(val);
CBofListNode<T *> *pNode = new CBofListNode<T *>(pNodeValue);
assert(pNode != nullptr);
int nHashBucketIndex = ((*_pHashFunction)(val)) % _nHashTableSize;
assert(nHashBucketIndex < _nHashTableSize);
CBofList<T *> *pHashBucket = &_xHashTable[nHashBucketIndex];
assert(pHashBucket != nullptr);
pHashBucket->addToTail(pNode);
}
// CBofVHashTable<T, S>contains - predicate to test whether a value is stored in the hash table.
template<class T, int S>
bool CBofVHashTable<T, S>::contains(const T &val) {
bool returnValue = false;
int nHashBucketIndex = ((*_pHashFunction)(val)) % _nHashTableSize;
assert(nHashBucketIndex < _nHashTableSize);
CBofVHashTable<T, S> *const fakeThis = (CBofVHashTable<T, S> *const)this;
CBofList<T *> *pHashBucket = &(fakeThis->_xHashTable[nHashBucketIndex]);
assert(pHashBucket != nullptr);
int nItemsInBucket = pHashBucket->getCount();
for (int i = 0; i < nItemsInBucket; i++) {
T *TableEntry = pHashBucket->getNodeItem(i);
if (TableEntry->compareNoCase((const char *)val) == 0) {
returnValue = true;
break;
}
}
return returnValue;
}
} // namespace SpaceBar
} // namespace Bagel
#endif