scummvm/engines/saga2/saga2.cpp
2023-12-24 13:19:25 +01:00

425 lines
10 KiB
C++

/* 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/scummsys.h"
#include "common/config-manager.h"
#include "common/debug.h"
#include "common/error.h"
#include "common/events.h"
#include "common/file.h"
#include "common/fs.h"
#include "common/system.h"
#include "engines/util.h"
#include "saga2/saga2.h"
#include "saga2/fta.h"
#include "saga2/actor.h"
#include "saga2/audio.h"
#include "saga2/band.h"
#include "saga2/beegee.h"
#include "saga2/calendar.h"
#include "saga2/console.h"
#include "saga2/contain.h"
#include "saga2/detection.h"
#include "saga2/dispnode.h"
#include "saga2/gdraw.h"
#include "saga2/imagcach.h"
#include "saga2/mouseimg.h"
#include "saga2/motion.h"
#include "saga2/music.h"
#include "saga2/panel.h"
#include "saga2/spelshow.h"
#include "saga2/tilemode.h"
#include "saga2/vpal.h"
namespace Saga2 {
void main_saga2();
Saga2Engine *g_vm;
Saga2Engine::Saga2Engine(OSystem *syst, const SAGA2GameDescription *desc)
: Engine(syst), _gameDescription(desc) {
const Common::FSNode gameDataDir(ConfMan.getPath("path"));
// Don't forget to register your random source
_rnd = new Common::RandomSource("saga2");
g_vm = this;
_console = nullptr;
_renderer = nullptr;
_audio = nullptr;
_pal = nullptr;
_act = nullptr;
_calendar = nullptr;
_tmm = nullptr;
_cnm = nullptr;
_bandList = nullptr;
_mouseInfo = nullptr;
_smkDecoder = nullptr;
_videoX = _videoY = 0;
_loadedWeapons = 0;
_gameRunning = true;
_autoAggression = true;
_autoWeapon = true;
_showNight = true;
_speechText = true;
_speechVoice = true;
_showPosition = false;
_showStats = false;
_showStatusMsg = false;
_teleportOnClick = false;
_teleportOnMap = false;
_indivControlsFlag = false;
_userControlsSetup = false;
_fadeDepth = 1;
_currentMapNum = 0;
SearchMan.addSubDirectoryMatching(gameDataDir, "res");
SearchMan.addSubDirectoryMatching(gameDataDir, "dos/drivers"); // For Miles Sound files
SearchMan.addSubDirectoryMatching(gameDataDir, "drivers");
SearchMan.addSubDirectoryMatching(gameDataDir, "video"); // FTA2 movies
SearchMan.addSubDirectoryMatching(gameDataDir, "smack"); // Dino movies
_loadedWeapons = 0;
_imageCache = new CImageCache;
_mTaskList = new MotionTaskList;
_bandList = new BandList();
_mainDisplayList = new DisplayNodeList;
_activeSpells = new SpellDisplayList(kMaxActiveSpells);
_pointer = new gMousePointer(_mainPort);
_activeRegionList = new ActiveRegion[kPlayerActors];
_toolBase = new gToolBase;
_properties = new Properties;
_aTaskList = new TileActivityTaskList;
_grandMasterFTA = new Deejay;
_edpList = nullptr;
_sdpList = nullptr;
_tileImageBanks = nullptr;
_stackList = nullptr;
_taskList = nullptr;
_frate = nullptr;
_lrate = nullptr;
}
Saga2Engine::~Saga2Engine() {
debug("Saga2Engine::~Saga2Engine");
freeExeResources();
// Dispose your resources here
delete _rnd;
delete _renderer;
delete _pal;
delete _act;
delete _calendar;
delete _tmm;
delete _cnm;
delete _imageCache;
delete _mTaskList;
delete _bandList;
delete _mainDisplayList;
delete _activeSpells;
delete _pointer;
delete[] _activeRegionList;
delete _toolBase;
delete _properties;
delete _aTaskList;
delete _grandMasterFTA;
}
Common::Error Saga2Engine::run() {
// Initialize graphics using following:
initGraphics(640, 480);
_console = new Console(this);
setDebugger(_console);
_renderer = new Renderer();
_pal = new PaletteManager;
_act = new ActorManager;
_calendar = new CalendarTime;
_tmm = new TileModeManager;
_cnm = new ContainerManager;
readConfig();
if (getGameId() == GID_FTA2)
loadExeResources();
// TODO: Load EXE resources for Dino
main_saga2();
return Common::kNoError;
}
bool Saga2Engine::hasFeature(EngineFeature f) const {
return
(f == kSupportsReturnToLauncher) ||
(f == kSupportsLoadingDuringRuntime) ||
(f == kSupportsSavingDuringRuntime) ||
(f == kSupportsSubtitleOptions);
}
int Saga2Engine::getGameId() const {
return _gameDescription->gameId;
}
const ADGameFileDescription *Saga2Engine::getFilesDescriptions() const {
return _gameDescription->desc.filesDescriptions;
}
Common::Error Saga2Engine::loadGameStream(Common::SeekableReadStream *stream) {
Common::Serializer s(stream, nullptr);
syncGameStream(s);
return Common::kNoError;
}
Common::Error Saga2Engine::saveGameStream(Common::WriteStream *stream, bool isAutosave) {
Common::Serializer s(nullptr, stream);
syncGameStream(s);
return Common::kNoError;
}
Common::String Saga2Engine::getSavegameFile(int slot) {
return getMetaEngine()->getSavegameFile(slot, _targetName.c_str());
}
Common::Error Saga2Engine::saveGameState(int slot, const Common::String &desc, bool isAutosave) {
pauseTimer();
Common::OutSaveFile *outS = getSaveFileManager()->openForSaving(getSavegameFile(slot), false);
if (!outS)
return Common::kCreatingFileFailed;
saveGame(outS, desc);
outS->write("SCVM", 4);
CHUNK_BEGIN;
uint32 pos = outS->pos() + 4;
_renderer->saveBackBuffer(kBeforeTakingThumbnail);
if (_renderer->hasSavedBackBuffer(kBeforeOpeningMenu))
_renderer->popSavedBackBuffer(kBeforeOpeningMenu);
getMetaEngine()->appendExtendedSaveToStream(out, g_vm->getTotalPlayTime() / 1000, desc, isAutosave, pos);
_renderer->popSavedBackBuffer(kBeforeTakingThumbnail);
CHUNK_END;
outS->finalize();
delete outS;
resumeTimer();
return Common::kNoError;
}
Common::Error Saga2Engine::loadGameState(int slot) {
loadGame(slot);
return Common::kNoError;
}
void Saga2Engine::syncSoundSettings() {
Engine::syncSoundSettings();
_speechText = true;
if (ConfMan.hasKey("subtitles"))
_speechText = ConfMan.getBool("subtitles");
_speechVoice = true;
if (ConfMan.hasKey("speech_mute"))
_speechVoice = !ConfMan.getBool("speech_mute");
if (_audio)
_audio->_music->syncSoundSettings();
}
void Saga2Engine::syncGameStream(Common::Serializer &s) {
// Use methods of Serializer to save/load fields
int dummy = 0;
s.syncAsUint16LE(dummy);
}
int32 clamp(int32 a, int32 val, int32 c) {
if (val < a) return a;
if (val > c) return c;
return val;
}
gFont Onyx10Font;
gFont Plate18Font;
gFont Helv11Font;
gFont Amber13Font;
gFont ThinFix8Font;
gFont Script10Font;
uint32 loadingWindowWidth = 640;
uint32 loadingWindowHeight = 480;
uint8 *loadingWindowPalette;
uint8 *loadingWindowData;
uint8 *ColorMapRanges;
uint8 *closeBx1ImageData;
uint8 *closeBx2ImageData;
uint8 *usePtrImageData;
uint8 *xPointerImageData;
uint8 *arrowImageData;
uint8 *grabPtrImageData;
uint8 *attakPtrImageData;
uint8 *centerActorIndicatorImageData;
uint8 *pgUpImageData;
uint8 *pgDownImageData;
uint8 *pgLeftImageData;
uint8 *pgRightImageData;
uint8 *autoWalkImageData;
uint8 *gaugeImageData;
static void loadFont(Common::File &file, gFont *font, uint32 offset) {
file.seek(offset);
font->height = file.readUint16LE();
font->baseLine = file.readUint16LE();
font->rowMod = file.readUint16LE();
for (int i = 0; i < 256; i++)
font->charXOffset[i] = file.readUint16LE();
file.read(font->charWidth, 256);
file.read(font->charKern, 256);
file.read(font->charSpace, 256);
uint size = font->height * font->rowMod;
font->fontdata = (byte *)malloc(size);
file.read(font->fontdata, size);
}
struct dataChunks {
uint8 **ptr;
uint32 offset;
uint32 size;
} chunks[] = {
{ (uint8 **)&Onyx10Font, 0x004F7258, 0 },
{ (uint8 **)&Plate18Font, 0x004F7EE0, 0 },
{ (uint8 **)&Helv11Font, 0x004F9F30, 0 },
{ (uint8 **)&Amber13Font, 0x004FAC60, 0 },
{ (uint8 **)&ThinFix8Font, 0x004FC210, 0 },
{ (uint8 **)&Script10Font, 0x004FCD18, 0 },
{ &loadingWindowPalette, 0x004A2600, 1024 },
{ &loadingWindowData, 0x004A2A00, 307200 },
{ &ColorMapRanges, 0x004EDC20, 1584 },
{ &closeBx1ImageData, 0x004EE2B8, 144 },
{ &closeBx2ImageData, 0x004EE348, 144 },
{ &usePtrImageData, 0x004EE3D8, 232 },
{ &xPointerImageData, 0x004EE4C0, 232 },
{ &arrowImageData, 0x004EE5A8, 192 },
{ &grabPtrImageData, 0x004EE668, 208 },
{ &attakPtrImageData, 0x004EE738, 536 },
{ &centerActorIndicatorImageData,0x004EE950, 96 },
{ &pgUpImageData, 0x004EE9B0, 256 },
{ &pgDownImageData, 0x004EEAB0, 256 },
{ &pgLeftImageData, 0x004EEBB0, 256 },
{ &pgRightImageData, 0x004EECB0, 256 },
{ &autoWalkImageData, 0x004EEDB0, 228 },
{ &gaugeImageData, 0x004EF257, 241 },
{ nullptr, 0, 0 }
};
void Saga2Engine::loadExeResources() {
Common::File exe;
const uint32 offset = 0x4F6D90 - 0xF4990;
if (!(exe.open("win/fta2win.exe") || exe.open("fta2win.exe")))
error("FTA2WIN.EXE file is missing");
if (exe.size() != 1093120)
error("Incorrect FTA2WIN.EXE file size. Expected is 1093120");
for (int i = 0; chunks[i].ptr; i++) {
if (chunks[i].size == 0) { // Font
loadFont(exe, (gFont *)chunks[i].ptr, chunks[i].offset - offset);
} else {
*chunks[i].ptr = (uint8 *)malloc(chunks[i].size);
exe.seek(chunks[i].offset - offset);
exe.read(*chunks[i].ptr, chunks[i].size);
}
}
initCursors();
exe.close();
}
void Saga2Engine::freeExeResources() {
for (int i = 0; chunks[i].ptr; i++)
if (chunks[i].size == 0) // Font
free(((gFont *)chunks[i].ptr)->fontdata);
else
free(*chunks[i].ptr);
freeCursors();
}
void Saga2Engine::readConfig() {
_autoWeapon = true;
if (ConfMan.hasKey("auto_weapon"))
_autoWeapon = ConfMan.getBool("auto_weapon");
_autoAggression = true;
if (ConfMan.hasKey("auto_aggression"))
_autoAggression = ConfMan.getBool("auto_aggression");
_showNight = true;
if (ConfMan.hasKey("show_night"))
_showNight = ConfMan.getBool("show_night");
syncSoundSettings();
}
void Saga2Engine::saveConfig() {
ConfMan.flushToDisk();
}
} // End of namespace Saga2