Initial commit
This commit is contained in:
55
engines/ags/shared/ac/audio_clip_type.cpp
Normal file
55
engines/ags/shared/ac/audio_clip_type.cpp
Normal 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ags/shared/ac/audio_clip_type.h"
|
||||
#include "ags/shared/util/stream.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
using AGS::Shared::Stream;
|
||||
|
||||
void AudioClipType::ReadFromFile(Stream *in) {
|
||||
id = in->ReadInt32();
|
||||
reservedChannels = in->ReadInt32();
|
||||
volume_reduction_while_speech_playing = in->ReadInt32();
|
||||
crossfadeSpeed = in->ReadInt32();
|
||||
reservedForFuture = in->ReadInt32();
|
||||
}
|
||||
|
||||
void AudioClipType::WriteToFile(Stream *out) {
|
||||
out->WriteInt32(id);
|
||||
out->WriteInt32(reservedChannels);
|
||||
out->WriteInt32(volume_reduction_while_speech_playing);
|
||||
out->WriteInt32(crossfadeSpeed);
|
||||
out->WriteInt32(reservedForFuture);
|
||||
}
|
||||
|
||||
void AudioClipType::ReadFromSavegame(Shared::Stream *in) {
|
||||
volume_reduction_while_speech_playing = in->ReadInt32();
|
||||
crossfadeSpeed = in->ReadInt32();
|
||||
}
|
||||
|
||||
void AudioClipType::WriteToSavegame(Shared::Stream *out) const {
|
||||
out->WriteInt32(volume_reduction_while_speech_playing);
|
||||
out->WriteInt32(crossfadeSpeed);
|
||||
}
|
||||
|
||||
} // namespace AGS3
|
||||
52
engines/ags/shared/ac/audio_clip_type.h
Normal file
52
engines/ags/shared/ac/audio_clip_type.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/* 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 AGS_SHARED_AC_AUDIO_CLIP_TYPE_H
|
||||
#define AGS_SHARED_AC_AUDIO_CLIP_TYPE_H
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
// Forward declaration
|
||||
namespace AGS {
|
||||
namespace Shared {
|
||||
class Stream;
|
||||
} // namespace Shared
|
||||
} // namespace AGS
|
||||
|
||||
using namespace AGS; // FIXME later
|
||||
|
||||
#define AUDIO_CLIP_TYPE_SOUND 1
|
||||
struct AudioClipType {
|
||||
int id;
|
||||
int reservedChannels;
|
||||
int volume_reduction_while_speech_playing;
|
||||
int crossfadeSpeed;
|
||||
int reservedForFuture;
|
||||
|
||||
void ReadFromFile(Shared::Stream *in);
|
||||
void WriteToFile(Shared::Stream *out);
|
||||
void ReadFromSavegame(Shared::Stream *in);
|
||||
void WriteToSavegame(Shared::Stream *out) const;
|
||||
};
|
||||
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
214
engines/ags/shared/ac/character_info.cpp
Normal file
214
engines/ags/shared/ac/character_info.cpp
Normal file
@@ -0,0 +1,214 @@
|
||||
/* 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/str.h"
|
||||
#include "ags/shared/ac/character_info.h"
|
||||
#include "ags/shared/ac/game_version.h"
|
||||
#include "ags/shared/util/stream.h"
|
||||
#include "ags/shared/util/string_utils.h"
|
||||
#include "ags/globals.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
using namespace AGS::Shared;
|
||||
|
||||
void CharacterInfo::ReadBaseFields(Stream *in) {
|
||||
defview = in->ReadInt32();
|
||||
talkview = in->ReadInt32();
|
||||
view = in->ReadInt32();
|
||||
room = in->ReadInt32();
|
||||
prevroom = in->ReadInt32();
|
||||
x = in->ReadInt32();
|
||||
y = in->ReadInt32();
|
||||
wait = in->ReadInt32();
|
||||
flags = in->ReadInt32();
|
||||
following = in->ReadInt16();
|
||||
followinfo = in->ReadInt16();
|
||||
idleview = in->ReadInt32();
|
||||
idletime = in->ReadInt16();
|
||||
idleleft = in->ReadInt16();
|
||||
transparency = in->ReadInt16();
|
||||
baseline = in->ReadInt16();
|
||||
activeinv = in->ReadInt32();
|
||||
talkcolor = in->ReadInt32();
|
||||
thinkview = in->ReadInt32();
|
||||
blinkview = in->ReadInt16();
|
||||
blinkinterval = in->ReadInt16();
|
||||
blinktimer = in->ReadInt16();
|
||||
blinkframe = in->ReadInt16();
|
||||
walkspeed_y = in->ReadInt16();
|
||||
pic_yoffs = in->ReadInt16();
|
||||
z = in->ReadInt32();
|
||||
walkwait = in->ReadInt32();
|
||||
speech_anim_speed = in->ReadInt16();
|
||||
idle_anim_speed = in->ReadInt16();
|
||||
blocking_width = in->ReadInt16();
|
||||
blocking_height = in->ReadInt16();
|
||||
index_id = in->ReadInt32();
|
||||
pic_xoffs = in->ReadInt16();
|
||||
walkwaitcounter = in->ReadInt16();
|
||||
loop = in->ReadInt16();
|
||||
frame = in->ReadInt16();
|
||||
walking = in->ReadInt16();
|
||||
animating = in->ReadInt16();
|
||||
walkspeed = in->ReadInt16();
|
||||
animspeed = in->ReadInt16();
|
||||
in->ReadArrayOfInt16(inv, MAX_INV);
|
||||
actx = in->ReadInt16();
|
||||
acty = in->ReadInt16();
|
||||
}
|
||||
|
||||
void CharacterInfo::WriteBaseFields(Stream *out) const {
|
||||
out->WriteInt32(defview);
|
||||
out->WriteInt32(talkview);
|
||||
out->WriteInt32(view);
|
||||
out->WriteInt32(room);
|
||||
out->WriteInt32(prevroom);
|
||||
out->WriteInt32(x);
|
||||
out->WriteInt32(y);
|
||||
out->WriteInt32(wait);
|
||||
out->WriteInt32(flags);
|
||||
out->WriteInt16(following);
|
||||
out->WriteInt16(followinfo);
|
||||
out->WriteInt32(idleview);
|
||||
out->WriteInt16(idletime);
|
||||
out->WriteInt16(idleleft);
|
||||
out->WriteInt16(transparency);
|
||||
out->WriteInt16(baseline);
|
||||
out->WriteInt32(activeinv);
|
||||
out->WriteInt32(talkcolor);
|
||||
out->WriteInt32(thinkview);
|
||||
out->WriteInt16(blinkview);
|
||||
out->WriteInt16(blinkinterval);
|
||||
out->WriteInt16(blinktimer);
|
||||
out->WriteInt16(blinkframe);
|
||||
out->WriteInt16(walkspeed_y);
|
||||
out->WriteInt16(pic_yoffs);
|
||||
out->WriteInt32(z);
|
||||
out->WriteInt32(walkwait);
|
||||
out->WriteInt16(speech_anim_speed);
|
||||
out->WriteInt16(idle_anim_speed);
|
||||
out->WriteInt16(blocking_width);
|
||||
out->WriteInt16(blocking_height);
|
||||
out->WriteInt32(index_id);
|
||||
out->WriteInt16(pic_xoffs);
|
||||
out->WriteInt16(walkwaitcounter);
|
||||
out->WriteInt16(loop);
|
||||
out->WriteInt16(frame);
|
||||
out->WriteInt16(walking);
|
||||
out->WriteInt16(animating);
|
||||
out->WriteInt16(walkspeed);
|
||||
out->WriteInt16(animspeed);
|
||||
out->WriteArrayOfInt16(inv, MAX_INV);
|
||||
out->WriteInt16(actx);
|
||||
out->WriteInt16(acty);
|
||||
}
|
||||
|
||||
void CharacterInfo::ReadFromFile(Stream *in, CharacterInfo2 &chinfo2, GameDataVersion data_ver) {
|
||||
ReadBaseFields(in);
|
||||
StrUtil::ReadCStrCount(name, in, LEGACY_MAX_CHAR_NAME_LEN);
|
||||
StrUtil::ReadCStrCount(scrname, in, LEGACY_MAX_SCRIPT_NAME_LEN);
|
||||
on = in->ReadInt8();
|
||||
in->ReadInt8(); // alignment padding to int32
|
||||
|
||||
// Upgrade data
|
||||
if (data_ver < kGameVersion_360_16) {
|
||||
idle_anim_speed = animspeed + 5;
|
||||
}
|
||||
// Assign unrestricted names from legacy fields
|
||||
chinfo2.name_new = name;
|
||||
chinfo2.scrname_new = scrname;
|
||||
}
|
||||
|
||||
void CharacterInfo::WriteToFile(Stream *out) const {
|
||||
WriteBaseFields(out);
|
||||
out->Write(name, LEGACY_MAX_CHAR_NAME_LEN);
|
||||
out->Write(scrname, LEGACY_MAX_SCRIPT_NAME_LEN);
|
||||
out->WriteInt8(on);
|
||||
out->WriteInt8(0); // alignment padding to int32
|
||||
}
|
||||
|
||||
void CharacterInfo::ReadFromSavegame(Stream *in, CharacterInfo2 &chinfo2, CharacterSvgVersion save_ver) {
|
||||
ReadBaseFields(in);
|
||||
if (save_ver < kCharSvgVersion_36115) { // Fixed-size name and scriptname
|
||||
chinfo2.name_new.ReadCount(in, LEGACY_MAX_CHAR_NAME_LEN);
|
||||
in->Seek(LEGACY_MAX_SCRIPT_NAME_LEN); // skip legacy scriptname
|
||||
// (don't overwrite static data from save!)
|
||||
} else {
|
||||
chinfo2.name_new = StrUtil::ReadString(in);
|
||||
}
|
||||
on = in->ReadInt8();
|
||||
|
||||
//
|
||||
// Upgrade restored data
|
||||
if (save_ver < kCharSvgVersion_36025) {
|
||||
idle_anim_speed = animspeed + 5;
|
||||
}
|
||||
// Fill legacy name fields, for compatibility with old scripts and plugins
|
||||
snprintf(name, LEGACY_MAX_CHAR_NAME_LEN, "%s", chinfo2.name_new.GetCStr());
|
||||
}
|
||||
|
||||
void CharacterInfo::WriteToSavegame(Stream *out, const CharacterInfo2 &chinfo2) const {
|
||||
WriteBaseFields(out);
|
||||
StrUtil::WriteString(chinfo2.name_new, out); // kCharSvgVersion_36115
|
||||
out->WriteInt8(on);
|
||||
}
|
||||
|
||||
#if defined (OBSOLETE)
|
||||
#define COPY_CHAR_VAR(name) ci->name = oci->name
|
||||
|
||||
void ConvertOldCharacterToNew(OldCharacterInfo *oci, CharacterInfo *ci) {
|
||||
COPY_CHAR_VAR(defview);
|
||||
COPY_CHAR_VAR(talkview);
|
||||
COPY_CHAR_VAR(view);
|
||||
COPY_CHAR_VAR(room);
|
||||
COPY_CHAR_VAR(prevroom);
|
||||
COPY_CHAR_VAR(x);
|
||||
COPY_CHAR_VAR(y);
|
||||
COPY_CHAR_VAR(wait);
|
||||
COPY_CHAR_VAR(flags);
|
||||
COPY_CHAR_VAR(following);
|
||||
COPY_CHAR_VAR(followinfo);
|
||||
COPY_CHAR_VAR(idleview);
|
||||
COPY_CHAR_VAR(idletime);
|
||||
COPY_CHAR_VAR(idleleft);
|
||||
COPY_CHAR_VAR(transparency);
|
||||
COPY_CHAR_VAR(baseline);
|
||||
COPY_CHAR_VAR(activeinv);
|
||||
COPY_CHAR_VAR(loop);
|
||||
COPY_CHAR_VAR(frame);
|
||||
COPY_CHAR_VAR(walking);
|
||||
COPY_CHAR_VAR(animating);
|
||||
COPY_CHAR_VAR(walkspeed);
|
||||
COPY_CHAR_VAR(animspeed);
|
||||
COPY_CHAR_VAR(actx);
|
||||
COPY_CHAR_VAR(acty);
|
||||
COPY_CHAR_VAR(on);
|
||||
snprintf(ci->name, sizeof(CharacterInfo::name), "%s", oci->name);
|
||||
snprintf(ci->scrname, sizeof(CharacterInfo::scrname), "%s", oci->scrname);
|
||||
memcpy(&ci->inv[0], &oci->inv[0], sizeof(short) * 100);
|
||||
// move the talking colour into the struct and remove from flags
|
||||
ci->talkcolor = (oci->flags & OCHF_SPEECHCOL) >> OCHF_SPEECHCOLSHIFT;
|
||||
ci->flags = ci->flags & (~OCHF_SPEECHCOL);
|
||||
}
|
||||
#endif // OBSOLETE
|
||||
|
||||
} // namespace AGS3
|
||||
263
engines/ags/shared/ac/character_info.h
Normal file
263
engines/ags/shared/ac/character_info.h
Normal file
@@ -0,0 +1,263 @@
|
||||
/* 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 AGS_SHARED_AC_CHARACTER_INFO_H
|
||||
#define AGS_SHARED_AC_CHARACTER_INFO_H
|
||||
|
||||
#include "common/std/vector.h"
|
||||
#include "ags/shared/ac/common_defines.h" // constants
|
||||
#include "ags/shared/ac/game_version.h"
|
||||
#include "ags/shared/core/types.h"
|
||||
#include "ags/shared/util/bbop.h"
|
||||
#include "ags/shared/util/string.h"
|
||||
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
namespace AGS {
|
||||
namespace Shared {
|
||||
class Stream;
|
||||
} // namespace Shared
|
||||
} // namespace AGS
|
||||
|
||||
using namespace AGS; // FIXME later
|
||||
|
||||
#define MAX_INV 301
|
||||
// Character flags
|
||||
#define CHF_MANUALSCALING 1
|
||||
#define CHF_FIXVIEW 2 // between SetCharView and ReleaseCharView
|
||||
#define CHF_NOINTERACT 4
|
||||
#define CHF_NODIAGONAL 8
|
||||
#define CHF_ALWAYSIDLE 0x10
|
||||
#define CHF_NOLIGHTING 0x20
|
||||
#define CHF_NOTURNING 0x40
|
||||
#define CHF_NOWALKBEHINDS 0x80
|
||||
#define CHF_FLIPSPRITE 0x100 // ?? Is this used??
|
||||
#define CHF_NOBLOCKING 0x200
|
||||
#define CHF_SCALEMOVESPEED 0x400
|
||||
#define CHF_NOBLINKANDTHINK 0x800
|
||||
#define CHF_SCALEVOLUME 0x1000
|
||||
#define CHF_HASTINT 0x2000 // engine only
|
||||
#define CHF_BEHINDSHEPHERD 0x4000 // engine only
|
||||
#define CHF_AWAITINGMOVE 0x8000 // engine only
|
||||
#define CHF_MOVENOTWALK 0x10000 // engine only - do not do walk anim
|
||||
#define CHF_ANTIGLIDE 0x20000
|
||||
#define CHF_HASLIGHT 0x40000
|
||||
#define CHF_TINTLIGHTMASK (CHF_NOLIGHTING | CHF_HASTINT | CHF_HASLIGHT)
|
||||
// Speechcol is no longer part of the flags as of v2.5
|
||||
#define OCHF_SPEECHCOL 0xff000000
|
||||
#define OCHF_SPEECHCOLSHIFT 24
|
||||
#define UNIFORM_WALK_SPEED 0
|
||||
#define FOLLOW_ALWAYSONTOP 0x7ffe
|
||||
|
||||
// Character's internal flags, packed in CharacterInfo::animating
|
||||
#define CHANIM_MASK 0xFF
|
||||
#define CHANIM_ON 0x01
|
||||
#define CHANIM_REPEAT 0x02
|
||||
#define CHANIM_BACKWARDS 0x04
|
||||
|
||||
// Converts character flags (CHF_*) to matching RoomObject flags (OBJF_*)
|
||||
inline int CharFlagsToObjFlags(int chflags) {
|
||||
using namespace AGS::Shared;
|
||||
return FlagToFlag(chflags, CHF_NOINTERACT, OBJF_NOINTERACT) |
|
||||
FlagToFlag(chflags, CHF_NOWALKBEHINDS, OBJF_NOWALKBEHINDS) |
|
||||
FlagToFlag(chflags, CHF_HASTINT, OBJF_HASTINT) |
|
||||
FlagToFlag(chflags, CHF_HASLIGHT, OBJF_HASLIGHT) |
|
||||
// following flags are inverse
|
||||
FlagToNoFlag(chflags, CHF_NOLIGHTING, OBJF_USEREGIONTINTS) |
|
||||
FlagToNoFlag(chflags, CHF_MANUALSCALING, OBJF_USEROOMSCALING) |
|
||||
FlagToNoFlag(chflags, CHF_NOBLOCKING, OBJF_SOLID);
|
||||
}
|
||||
|
||||
// Length of deprecated character name field, in bytes
|
||||
#define LEGACY_MAX_CHAR_NAME_LEN 40
|
||||
|
||||
enum CharacterSvgVersion {
|
||||
kCharSvgVersion_Initial = 0, // [UNSUPPORTED] from 3.5.0 pre-alpha
|
||||
kCharSvgVersion_350 = 1, // new movelist format (along with pathfinder)
|
||||
kCharSvgVersion_36025 = 2, // animation volume
|
||||
kCharSvgVersion_36109 = 3, // removed movelists, save externally
|
||||
kCharSvgVersion_36115 = 4, // no limit on character name's length
|
||||
};
|
||||
|
||||
|
||||
// Predeclare a design-time Character extension
|
||||
struct CharacterInfo2;
|
||||
// Predeclare a runtime Character extension (TODO: refactor and remove this from here)
|
||||
struct CharacterExtras;
|
||||
|
||||
// CharacterInfo is a design-time Character data.
|
||||
// Contains original set of character fields.
|
||||
// IMPORTANT: exposed to script API, and plugin API as AGSCharacter!
|
||||
// For older script compatibility the struct also has to maintain its size,
|
||||
// and be stored in a plain array to keep the relative memory address offsets
|
||||
// between the Character objects!
|
||||
// Do not add or change existing fields, unless planning breaking compatibility.
|
||||
// Prefer to use CharacterInfo2 and CharacterExtras structs for any extensions.
|
||||
//
|
||||
// TODO: must refactor, some parts of it should be in a runtime Character class.
|
||||
struct CharacterInfo {
|
||||
int defview;
|
||||
int talkview;
|
||||
int view;
|
||||
int room, prevroom;
|
||||
int x, y, wait;
|
||||
int flags;
|
||||
short following;
|
||||
short followinfo;
|
||||
int idleview; // the loop will be randomly picked
|
||||
short idletime, idleleft; // num seconds idle before playing anim
|
||||
short transparency; // if character is transparent
|
||||
short baseline;
|
||||
int activeinv;
|
||||
int talkcolor;
|
||||
int thinkview;
|
||||
short blinkview, blinkinterval; // design time
|
||||
short blinktimer, blinkframe; // run time
|
||||
short walkspeed_y;
|
||||
short pic_yoffs; // this is fixed in screen coordinates
|
||||
int z; // z-location, for flying etc
|
||||
int walkwait;
|
||||
short speech_anim_speed, idle_anim_speed;
|
||||
short blocking_width, blocking_height;
|
||||
int index_id; // used for object functions to know the id
|
||||
short pic_xoffs; // this is fixed in screen coordinates
|
||||
short walkwaitcounter;
|
||||
uint16_t loop, frame;
|
||||
short walking; // stores movelist index, optionally +TURNING_AROUND
|
||||
short animating; // stores CHANIM_* flags in lower byte and delay in upper byte
|
||||
short walkspeed, animspeed;
|
||||
short inv[MAX_INV];
|
||||
short actx, acty;
|
||||
// These two name fields are deprecated, but must stay here
|
||||
// for compatibility with old scripts and plugin API
|
||||
char name[LEGACY_MAX_CHAR_NAME_LEN];
|
||||
char scrname[LEGACY_MAX_SCRIPT_NAME_LEN];
|
||||
int8 on;
|
||||
|
||||
int get_effective_y() const; // return Y - Z
|
||||
int get_baseline() const; // return baseline, or Y if not set
|
||||
int get_blocking_top() const; // return Y - BlockingHeight/2
|
||||
int get_blocking_bottom() const; // return Y + BlockingHeight/2
|
||||
|
||||
// Returns effective x/y walkspeeds for this character
|
||||
void get_effective_walkspeeds(int &walk_speed_x, int &walk_speed_y) const {
|
||||
walk_speed_x = walkspeed;
|
||||
walk_speed_y = ((walkspeed_y == UNIFORM_WALK_SPEED) ? walkspeed : walkspeed_y);
|
||||
}
|
||||
|
||||
inline bool has_explicit_light() const {
|
||||
return (flags & CHF_HASLIGHT) != 0;
|
||||
}
|
||||
inline bool has_explicit_tint() const {
|
||||
return (flags & CHF_HASTINT) != 0;
|
||||
}
|
||||
inline bool is_animating() const {
|
||||
return (animating & CHANIM_ON) != 0;
|
||||
}
|
||||
inline int get_anim_repeat() const {
|
||||
return (animating & CHANIM_REPEAT) ? ANIM_REPEAT : ANIM_ONCE;
|
||||
}
|
||||
inline bool get_anim_forwards() const {
|
||||
return (animating & CHANIM_BACKWARDS) == 0;
|
||||
}
|
||||
inline int get_anim_delay() const {
|
||||
return (animating >> 8) & 0xFF;
|
||||
}
|
||||
inline void set_animating(bool repeat, bool forwards, int delay) {
|
||||
animating = CHANIM_ON |
|
||||
(CHANIM_REPEAT * repeat) |
|
||||
(CHANIM_BACKWARDS * !forwards) |
|
||||
((delay & 0xFF) << 8);
|
||||
}
|
||||
|
||||
// [IKM] 2012-06-28: I still have to pass char_index to some of those functions
|
||||
// either because they use it to set some variables with it,
|
||||
// or because they pass it further to other functions, that are called from various places
|
||||
// and it would be too much to change them all simultaneously
|
||||
//
|
||||
// [IKM] 2016-08-26: these methods should NOT be in CharacterInfo class,
|
||||
// bit in distinct runtime character class!
|
||||
void UpdateMoveAndAnim(int &char_index, CharacterExtras *chex, std::vector<int> &followingAsSheep);
|
||||
void UpdateFollowingExactlyCharacter();
|
||||
|
||||
int update_character_walkturning(CharacterExtras *chex);
|
||||
void update_character_moving(int &char_index, CharacterExtras *chex, int &doing_nothing);
|
||||
int update_character_animating(int &char_index, int &doing_nothing);
|
||||
void update_character_idle(CharacterExtras *chex, int &doing_nothing);
|
||||
void update_character_follower(int &char_index, std::vector<int> &followingAsSheep, int &doing_nothing);
|
||||
|
||||
void ReadFromFile(Shared::Stream *in, CharacterInfo2 &chinfo2, GameDataVersion data_ver);
|
||||
void WriteToFile(Shared::Stream *out) const;
|
||||
// TODO: move to runtime-only class (?)
|
||||
void ReadFromSavegame(Shared::Stream *in, CharacterInfo2 &chinfo2, CharacterSvgVersion save_ver);
|
||||
void WriteToSavegame(Shared::Stream *out, const CharacterInfo2 &chinfo2) const;
|
||||
|
||||
private:
|
||||
// Fixups loop and frame values, in case any of them are set to a value out of the valid range
|
||||
void FixupCurrentLoopAndFrame();
|
||||
|
||||
// Helper functions that read and write first data fields,
|
||||
// common for both game file and save.
|
||||
void ReadBaseFields(Shared::Stream *in);
|
||||
void WriteBaseFields(Shared::Stream *out) const;
|
||||
};
|
||||
|
||||
|
||||
// Design-time Character extended fields
|
||||
struct CharacterInfo2 {
|
||||
// Unrestricted scriptname and name fields
|
||||
AGS::Shared::String scrname_new;
|
||||
AGS::Shared::String name_new;
|
||||
};
|
||||
|
||||
|
||||
#if defined (OBSOLETE)
|
||||
struct OldCharacterInfo {
|
||||
int defview;
|
||||
int talkview;
|
||||
int view;
|
||||
int room, prevroom;
|
||||
int x, y, wait;
|
||||
int flags;
|
||||
short following;
|
||||
short followinfo;
|
||||
int idleview; // the loop will be randomly picked
|
||||
short idletime, idleleft; // num seconds idle before playing anim
|
||||
short transparency; // if character is transparent
|
||||
short baseline;
|
||||
int activeinv; // this is an INT to support SeeR (no signed shorts)
|
||||
short loop, frame;
|
||||
short walking, animating;
|
||||
short walkspeed, animspeed;
|
||||
short inv[100];
|
||||
short actx, acty;
|
||||
char name[30];
|
||||
char scrname[16];
|
||||
int8 on;
|
||||
};
|
||||
|
||||
void ConvertOldCharacterToNew(OldCharacterInfo *oci, CharacterInfo *ci);
|
||||
#endif // OBSOLETE
|
||||
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
54
engines/ags/shared/ac/common.cpp
Normal file
54
engines/ags/shared/ac/common.cpp
Normal file
@@ -0,0 +1,54 @@
|
||||
/* 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 "ags/shared/ac/common.h"
|
||||
#include "ags/shared/util/string.h"
|
||||
#include "ags/globals.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
using namespace AGS::Shared;
|
||||
|
||||
void quit(const String &str) {
|
||||
quit(str.GetCStr());
|
||||
}
|
||||
|
||||
void quitprintf(const char *fmt, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
String text = String::FromFormatV(fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
// WORKAROUND: In ScummVM we have to make this an error, because
|
||||
// too many places calling it presume it doesn't return,
|
||||
// and will throw a wobbly if does
|
||||
error("%s", text.GetCStr());
|
||||
}
|
||||
|
||||
void set_our_eip(int eip) {
|
||||
_G(our_eip) = eip;
|
||||
}
|
||||
|
||||
int get_our_eip() {
|
||||
return _G(our_eip);
|
||||
}
|
||||
|
||||
} // namespace AGS3
|
||||
40
engines/ags/shared/ac/common.h
Normal file
40
engines/ags/shared/ac/common.h
Normal file
@@ -0,0 +1,40 @@
|
||||
/* 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 AGS_SHARED_AC_COMMON_H
|
||||
#define AGS_SHARED_AC_COMMON_H
|
||||
|
||||
#include "ags/shared/util/string.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
// These are the project-dependent functions, they are defined both in Engine.App and AGS.Native.
|
||||
void quit(const AGS::Shared::String &str);
|
||||
void quit(const char *);
|
||||
void quitprintf(const char *fmt, ...);
|
||||
void set_our_eip(int eip);
|
||||
int get_our_eip();
|
||||
|
||||
extern const char *game_file_sig;
|
||||
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
131
engines/ags/shared/ac/common_defines.h
Normal file
131
engines/ags/shared/ac/common_defines.h
Normal file
@@ -0,0 +1,131 @@
|
||||
/* 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 AGS_SHARED_AC_COMMON_DEFINES_H
|
||||
#define AGS_SHARED_AC_COMMON_DEFINES_H
|
||||
|
||||
#include "ags/shared/core/platform.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
// Some arbitrary return values, should be replaced with either
|
||||
// simple boolean, or HError
|
||||
#define EXIT_NORMAL 0
|
||||
#define EXIT_CRASH 92
|
||||
#define EXIT_ERROR 93
|
||||
|
||||
// Legacy (UNSUPPORTED!) interaction script constants
|
||||
//
|
||||
// NUMCONDIT : whataction[0]: Char walks off left
|
||||
// [1]: Char walks off right
|
||||
// [2]: Char walks off bottom
|
||||
// [3]: Char walks off top
|
||||
// [4]: First enters screen
|
||||
// [5]: Every time enters screen
|
||||
// [6]: execute every loop
|
||||
// [5]...[19]: Char stands on lookat type
|
||||
// [20]...[35]: Look at type
|
||||
// [36]...[49]: Action on type
|
||||
// [50]...[65]: Use inv on type
|
||||
// [66]...[75]: Look at object
|
||||
// [76]...[85]: Action on object
|
||||
// [86]...[95]: Speak to object
|
||||
// [96]...[105]: Use inv on object
|
||||
// [106]...[124]: Misc conditions 1-20
|
||||
|
||||
// game ver whataction[]=
|
||||
// v1.00 0 : Go to screen
|
||||
// 1 : Don't do anything
|
||||
// 2 : Can't walk
|
||||
// 3 : Man dies
|
||||
// 4 : Run animation
|
||||
// 5 : Display message
|
||||
// 6 : Remove an object (set object.on=0)
|
||||
// 7 : Remove object & add Val2 to inventory
|
||||
// 8 : Add Val1 to inventory (Val2=num times)
|
||||
// 9 : Run a script
|
||||
// v1.00 SR-1 10 : Run graphical script
|
||||
// v1.1 11 : Play sound effect SOUND%d.WAV
|
||||
// v1.12 12 : Play FLI/FLC animation FLIC%d.FLC or FLIC%d.FLI
|
||||
// 13 : Turn object on
|
||||
// v2.00 14 : Run conversation
|
||||
#if defined(OBSOLETE)
|
||||
#define NUM_MISC 20
|
||||
#define NUMOTCON 7 // number of conditions before standing on
|
||||
#define NUM_CONDIT (120 + NUMOTCON)
|
||||
#define MISC_COND (MAX_WALK_BEHINDS * 4 + NUMOTCON + MAX_ROOM_OBJECTS * 4)
|
||||
#define NUMRESPONSE 14
|
||||
#define NUMCOMMANDS 15
|
||||
#define GO_TO_SCREEN 0
|
||||
#define NO_ACTION 1
|
||||
#define NO_WALK 2
|
||||
#define MAN_DIES 3
|
||||
#define RUN_ANIMATE 4
|
||||
#define SHOW_MESSAGE 5
|
||||
#define OBJECT_OFF 6
|
||||
#define OBJECT_INV 7
|
||||
#define ADD_INV 8
|
||||
#define RUNSCRIPT 9
|
||||
#define GRAPHSCRIPT 10
|
||||
#define PLAY_SOUND 11
|
||||
#define PLAY_FLI 12
|
||||
#define OBJECT_ON 13
|
||||
#define RUN_DIALOG 14
|
||||
#endif
|
||||
|
||||
// Script name length limit for some game objects
|
||||
#define LEGACY_MAX_SCRIPT_NAME_LEN 20
|
||||
// Number of state-saved rooms
|
||||
#define MAX_ROOMS 300
|
||||
// Some obsolete room data, likely pre-2.5
|
||||
#define MAX_LEGACY_ROOM_FLAGS 15
|
||||
// Old object name limit
|
||||
#define LEGACY_MAXOBJNAMELEN 30
|
||||
// Max number of sprites in older versions
|
||||
#define LEGACY_MAX_SPRITES_V25 6000
|
||||
#define LEGACY_MAX_SPRITES 30000
|
||||
|
||||
// The game to screen coordinate conversion multiplier, was used in older high-res games
|
||||
#define HIRES_COORD_MULTIPLIER 2
|
||||
|
||||
// Room object flags (currently limited by a byte)
|
||||
#define OBJF_NOINTERACT 0x01 // not clickable
|
||||
#define OBJF_NOWALKBEHINDS 0x02 // ignore walk-behinds
|
||||
#define OBJF_HASTINT 0x04 // the tint_* members are valid
|
||||
#define OBJF_USEREGIONTINTS 0x08 // obey region tints/light areas
|
||||
#define OBJF_USEROOMSCALING 0x10 // obey room scaling areas
|
||||
#define OBJF_SOLID 0x20 // blocks characters from moving
|
||||
#define OBJF_LEGACY_LOCKED 0x40 // object position is locked in the editor (OBSOLETE since 3.5.0)
|
||||
#define OBJF_HASLIGHT 0x80 // the tint_light is valid and treated as brightness
|
||||
#define OBJF_TINTLIGHTMASK (OBJF_HASTINT | OBJF_HASLIGHT | OBJF_USEREGIONTINTS)
|
||||
|
||||
// Animation flow mode
|
||||
// NOTE: had to move to common_defines, because used by CharacterInfo
|
||||
// Animates once and stops at the *last* frame
|
||||
#define ANIM_ONCE 0
|
||||
// Animates infinitely until stopped by command
|
||||
#define ANIM_REPEAT 1
|
||||
// Animates once and stops, resetting to the very first frame
|
||||
#define ANIM_ONCERESET 2
|
||||
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
48
engines/ags/shared/ac/dialog_topic.cpp
Normal file
48
engines/ags/shared/ac/dialog_topic.cpp
Normal file
@@ -0,0 +1,48 @@
|
||||
/* 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 "ags/shared/ac/dialog_topic.h"
|
||||
#include "ags/shared/util/stream.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
using AGS::Shared::Stream;
|
||||
|
||||
void DialogTopic::ReadFromFile(Stream *in) {
|
||||
in->ReadArray(optionnames, 150 * sizeof(char), MAXTOPICOPTIONS);
|
||||
in->ReadArrayOfInt32(optionflags, MAXTOPICOPTIONS);
|
||||
in->ReadInt32(); // optionscripts 32-bit pointer
|
||||
in->ReadArrayOfInt16(entrypoints, MAXTOPICOPTIONS);
|
||||
startupentrypoint = in->ReadInt16();
|
||||
codesize = in->ReadInt16();
|
||||
numoptions = in->ReadInt32();
|
||||
topicFlags = in->ReadInt32();
|
||||
}
|
||||
|
||||
void DialogTopic::ReadFromSavegame(Shared::Stream *in) {
|
||||
in->ReadArrayOfInt32(optionflags, MAXTOPICOPTIONS);
|
||||
}
|
||||
|
||||
void DialogTopic::WriteToSavegame(Shared::Stream *out) const {
|
||||
out->WriteArrayOfInt32(optionflags, MAXTOPICOPTIONS);
|
||||
}
|
||||
|
||||
} // namespace AGS3
|
||||
86
engines/ags/shared/ac/dialog_topic.h
Normal file
86
engines/ags/shared/ac/dialog_topic.h
Normal file
@@ -0,0 +1,86 @@
|
||||
/* 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 AGS_SHARED_AC_DIALOG_TOPIC_H
|
||||
#define AGS_SHARED_AC_DIALOG_TOPIC_H
|
||||
|
||||
#include "ags/shared/core/types.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
namespace AGS {
|
||||
namespace Shared {
|
||||
class Stream;
|
||||
} // namespace Shared
|
||||
} // namespace AGS
|
||||
|
||||
using namespace AGS; // FIXME later
|
||||
|
||||
// [IKM] This is *conversation* dialog, not *gui* dialog, mind you!
|
||||
|
||||
#define MAXTOPICOPTIONS 30
|
||||
#define DFLG_ON 1 // currently enabled
|
||||
#define DFLG_OFFPERM 2 // off forever (can't be trurned on)
|
||||
#define DFLG_NOREPEAT 4 // character doesn't repeat it when clicked
|
||||
#define DFLG_HASBEENCHOSEN 8 // dialog option is 'read'
|
||||
#define DTFLG_SHOWPARSER 1 // show parser in this topic
|
||||
#define DCMD_SAY 1
|
||||
#define DCMD_OPTOFF 2
|
||||
#define DCMD_OPTON 3
|
||||
#define DCMD_RETURN 4
|
||||
#define DCMD_STOPDIALOG 5
|
||||
#define DCMD_OPTOFFFOREVER 6
|
||||
#define DCMD_RUNTEXTSCRIPT 7
|
||||
#define DCMD_GOTODIALOG 8
|
||||
#define DCMD_PLAYSOUND 9
|
||||
#define DCMD_ADDINV 10
|
||||
#define DCMD_SETSPCHVIEW 11
|
||||
#define DCMD_NEWROOM 12
|
||||
#define DCMD_SETGLOBALINT 13
|
||||
#define DCMD_GIVESCORE 14
|
||||
#define DCMD_GOTOPREVIOUS 15
|
||||
#define DCMD_LOSEINV 16
|
||||
#define DCMD_ENDSCRIPT 0xff
|
||||
#define DCHAR_NARRATOR 999
|
||||
#define DCHAR_PLAYER 998
|
||||
|
||||
struct DialogTopic {
|
||||
char optionnames[MAXTOPICOPTIONS][150];
|
||||
int32_t optionflags[MAXTOPICOPTIONS];
|
||||
short entrypoints[MAXTOPICOPTIONS];
|
||||
short startupentrypoint;
|
||||
short codesize;
|
||||
int numoptions;
|
||||
int topicFlags;
|
||||
// NOTE: optionscripts is an unknown data from before AGS 2.5
|
||||
#ifdef OBSOLETE
|
||||
std::vector<uint8_t> optionscripts;
|
||||
#endif
|
||||
|
||||
void ReadFromFile(Shared::Stream *in);
|
||||
|
||||
void ReadFromSavegame(Shared::Stream *in);
|
||||
void WriteToSavegame(Shared::Stream *out) const;
|
||||
};
|
||||
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
44
engines/ags/shared/ac/dynobj/script_audio_clip.cpp
Normal file
44
engines/ags/shared/ac/dynobj/script_audio_clip.cpp
Normal 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ags/shared/ac/dynobj/script_audio_clip.h"
|
||||
#include "ags/shared/util/stream.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
using namespace AGS::Shared;
|
||||
|
||||
void ScriptAudioClip::ReadFromFile(Stream *in) {
|
||||
id = in->ReadInt32();
|
||||
scriptName.ReadCount(in, LEGACY_AUDIOCLIP_SCRIPTNAMELENGTH);
|
||||
fileName.ReadCount(in, LEGACY_AUDIOCLIP_FILENAMELENGTH);
|
||||
bundlingType = static_cast<uint8_t>(in->ReadInt8());
|
||||
type = static_cast<uint8_t>(in->ReadInt8());
|
||||
fileType = static_cast<AudioFileType>(in->ReadInt8());
|
||||
defaultRepeat = in->ReadInt8();
|
||||
in->ReadInt8(); // alignment padding to int16
|
||||
defaultPriority = in->ReadInt16();
|
||||
defaultVolume = in->ReadInt16();
|
||||
in->ReadInt16(); // alignment padding to int32
|
||||
in->ReadInt32(); // reserved
|
||||
}
|
||||
|
||||
} // namespace AGS3
|
||||
68
engines/ags/shared/ac/dynobj/script_audio_clip.h
Normal file
68
engines/ags/shared/ac/dynobj/script_audio_clip.h
Normal file
@@ -0,0 +1,68 @@
|
||||
/* 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 AGS_SHARED_AC_DYNOBJ_SCRIPT_AUDIO_CLIP_H
|
||||
#define AGS_SHARED_AC_DYNOBJ_SCRIPT_AUDIO_CLIP_H
|
||||
|
||||
#include "ags/shared/util/string.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
namespace AGS {
|
||||
namespace Shared {
|
||||
class Stream;
|
||||
} // namespace Shared
|
||||
} // namespace AGS
|
||||
|
||||
using namespace AGS; // FIXME later
|
||||
|
||||
enum AudioFileType {
|
||||
eAudioFileOGG = 1,
|
||||
eAudioFileMP3 = 2,
|
||||
eAudioFileWAV = 3,
|
||||
eAudioFileVOC = 4,
|
||||
eAudioFileMIDI = 5,
|
||||
eAudioFileMOD = 6
|
||||
};
|
||||
|
||||
#define AUCL_BUNDLE_EXE 1
|
||||
#define AUCL_BUNDLE_VOX 2
|
||||
|
||||
#define LEGACY_AUDIOCLIP_SCRIPTNAMELENGTH 30
|
||||
#define LEGACY_AUDIOCLIP_FILENAMELENGTH 15
|
||||
|
||||
struct ScriptAudioClip {
|
||||
int id = 0;
|
||||
Shared::String scriptName;
|
||||
Shared::String fileName;
|
||||
uint8_t bundlingType = AUCL_BUNDLE_EXE;
|
||||
uint8_t type = 0;
|
||||
AudioFileType fileType = eAudioFileOGG;
|
||||
int8 defaultRepeat = 0;
|
||||
short defaultPriority = 50;
|
||||
short defaultVolume = 100;
|
||||
|
||||
void ReadFromFile(Shared::Stream *in);
|
||||
};
|
||||
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
422
engines/ags/shared/ac/game_setup_struct.cpp
Normal file
422
engines/ags/shared/ac/game_setup_struct.cpp
Normal file
@@ -0,0 +1,422 @@
|
||||
/* 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 "ags/shared/ac/audio_clip_type.h"
|
||||
#include "ags/shared/ac/game_setup_struct.h"
|
||||
#include "ags/shared/ac/old_game_setup_struct.h"
|
||||
#include "ags/shared/ac/words_dictionary.h"
|
||||
#include "ags/shared/ac/dynobj/script_audio_clip.h"
|
||||
#include "ags/shared/game/interactions.h"
|
||||
#include "ags/shared/util/string_utils.h"
|
||||
#include "ags/globals.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
using namespace AGS::Shared;
|
||||
|
||||
GameSetupStruct::GameSetupStruct()
|
||||
: filever(0)
|
||||
, roomCount(0)
|
||||
, scoreClipID(0) {
|
||||
memset(lipSyncFrameLetters, 0, sizeof(lipSyncFrameLetters));
|
||||
memset(guid, 0, sizeof(guid));
|
||||
memset(saveGameFileExtension, 0, sizeof(saveGameFileExtension));
|
||||
}
|
||||
|
||||
GameSetupStruct::~GameSetupStruct() {
|
||||
Free();
|
||||
}
|
||||
|
||||
void GameSetupStruct::Free() {
|
||||
GameSetupStructBase::Free();
|
||||
|
||||
fonts.clear();
|
||||
mcurs.clear();
|
||||
|
||||
intrChar.clear();
|
||||
charScripts.clear();
|
||||
charProps.clear();
|
||||
|
||||
// TODO: find out if it really needs to begin with 1 here?
|
||||
for (size_t i = 1; i < (size_t)MAX_INV; i++) {
|
||||
intrInv[i].reset();
|
||||
invProps[i].clear();
|
||||
}
|
||||
invScripts.clear();
|
||||
numinvitems = 0;
|
||||
|
||||
viewNames.clear();
|
||||
dialogScriptNames.clear();
|
||||
|
||||
roomNames.clear();
|
||||
roomNumbers.clear();
|
||||
roomCount = 0;
|
||||
|
||||
audioClips.clear();
|
||||
audioClipTypes.clear();
|
||||
|
||||
SpriteInfos.clear();
|
||||
}
|
||||
|
||||
// Assigns font info parameters using legacy flags value read from the game data
|
||||
void SetFontInfoFromLegacyFlags(FontInfo &finfo, const uint8_t data) {
|
||||
finfo.Flags = (data >> 6) & 0xFF;
|
||||
finfo.Size = data & FFLG_LEGACY_SIZEMASK;
|
||||
}
|
||||
|
||||
void AdjustFontInfoUsingFlags(FontInfo &finfo, const uint32_t flags) {
|
||||
finfo.Flags = flags;
|
||||
if ((flags & FFLG_SIZEMULTIPLIER) != 0) {
|
||||
finfo.SizeMultiplier = finfo.Size;
|
||||
finfo.Size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
ScriptAudioClip *GetAudioClipForOldStyleNumber(GameSetupStruct &game, bool is_music, int num) {
|
||||
String clip_name;
|
||||
if (is_music)
|
||||
clip_name.Format("aMusic%d", num);
|
||||
else
|
||||
clip_name.Format("aSound%d", num);
|
||||
|
||||
for (size_t i = 0; i < _GP(game).audioClips.size(); ++i) {
|
||||
if (clip_name.CompareNoCase(_GP(game).audioClips[i].scriptName) == 0)
|
||||
return &_GP(game).audioClips[i];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Reading Part 1
|
||||
|
||||
void GameSetupStruct::read_savegame_info(Shared::Stream *in, GameDataVersion data_ver) {
|
||||
if (data_ver > kGameVersion_272) { // only 3.x
|
||||
StrUtil::ReadCStrCount(guid, in, MAX_GUID_LENGTH);
|
||||
StrUtil::ReadCStrCount(saveGameFileExtension, in, MAX_SG_EXT_LENGTH);
|
||||
saveGameFolderName.ReadCount(in, LEGACY_MAX_SG_FOLDER_LEN);
|
||||
}
|
||||
}
|
||||
|
||||
void GameSetupStruct::read_font_infos(Shared::Stream *in, GameDataVersion data_ver) {
|
||||
fonts.resize(numfonts);
|
||||
if (data_ver < kGameVersion_350) {
|
||||
for (int i = 0; i < numfonts; ++i)
|
||||
SetFontInfoFromLegacyFlags(fonts[i], in->ReadInt8());
|
||||
for (int i = 0; i < numfonts; ++i)
|
||||
fonts[i].Outline = in->ReadInt8(); // size of char
|
||||
if (data_ver < kGameVersion_341)
|
||||
return;
|
||||
for (int i = 0; i < numfonts; ++i) {
|
||||
fonts[i].YOffset = in->ReadInt32();
|
||||
if (data_ver >= kGameVersion_341_2)
|
||||
fonts[i].LineSpacing = MAX<int32_t>(0, in->ReadInt32());
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < numfonts; ++i) {
|
||||
uint32_t flags = in->ReadInt32();
|
||||
fonts[i].Size = in->ReadInt32();
|
||||
fonts[i].Outline = in->ReadInt32();
|
||||
fonts[i].YOffset = in->ReadInt32();
|
||||
fonts[i].LineSpacing = MAX<int32_t>(0, in->ReadInt32());
|
||||
AdjustFontInfoUsingFlags(fonts[i], flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GameSetupStruct::ReadInvInfo(Stream *in) {
|
||||
for (int i = 0; i < numinvitems; ++i) {
|
||||
invinfo[i].ReadFromFile(in);
|
||||
}
|
||||
}
|
||||
|
||||
void GameSetupStruct::WriteInvInfo(Stream *out) {
|
||||
for (int i = 0; i < numinvitems; ++i) {
|
||||
invinfo[i].WriteToFile(out);
|
||||
}
|
||||
}
|
||||
|
||||
HGameFileError GameSetupStruct::read_cursors(Shared::Stream *in) {
|
||||
mcurs.resize(numcursors);
|
||||
ReadMouseCursors(in);
|
||||
return HGameFileError::None();
|
||||
}
|
||||
|
||||
void GameSetupStruct::read_interaction_scripts(Shared::Stream *in, GameDataVersion data_ver) {
|
||||
_G(numGlobalVars) = 0;
|
||||
|
||||
if (data_ver > kGameVersion_272) { // 3.x
|
||||
charScripts.resize(numcharacters);
|
||||
invScripts.resize(numinvitems);
|
||||
for (size_t i = 0; i < (size_t)numcharacters; ++i)
|
||||
charScripts[i].reset(InteractionScripts::CreateFromStream(in));
|
||||
// NOTE: new inventory items' events are loaded starting from 1 for some reason
|
||||
for (size_t i = 1; i < (size_t)numinvitems; ++i)
|
||||
invScripts[i].reset(InteractionScripts::CreateFromStream(in));
|
||||
} else { // 2.x
|
||||
intrChar.resize(numcharacters);
|
||||
for (size_t i = 0; i < (size_t)numcharacters; ++i)
|
||||
intrChar[i].reset(Interaction::CreateFromStream(in));
|
||||
for (size_t i = 0; i < (size_t)numinvitems; ++i)
|
||||
intrInv[i].reset(Interaction::CreateFromStream(in));
|
||||
|
||||
_G(numGlobalVars) = in->ReadInt32();
|
||||
for (size_t i = 0; i < (size_t)_G(numGlobalVars); ++i)
|
||||
_G(globalvars)[i].Read(in);
|
||||
}
|
||||
}
|
||||
|
||||
void GameSetupStruct::read_words_dictionary(Shared::Stream *in) {
|
||||
dict.reset(new WordsDictionary());
|
||||
read_dictionary(dict.get(), in);
|
||||
}
|
||||
|
||||
void GameSetupStruct::ReadMouseCursors(Stream *in) {
|
||||
for (int i = 0; i < numcursors; ++i) {
|
||||
mcurs[i].ReadFromFile(in);
|
||||
}
|
||||
}
|
||||
|
||||
void GameSetupStruct::WriteMouseCursors(Stream *out) {
|
||||
for (int i = 0; i < numcursors; ++i) {
|
||||
mcurs[i].WriteToFile(out);
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Reading Part 2
|
||||
|
||||
void GameSetupStruct::read_characters(Shared::Stream *in) {
|
||||
chars.resize(numcharacters);
|
||||
chars2.resize(numcharacters);
|
||||
ReadCharacters(in);
|
||||
}
|
||||
|
||||
void GameSetupStruct::read_lipsync(Shared::Stream *in, GameDataVersion data_ver) {
|
||||
if (data_ver >= kGameVersion_254) // lip syncing was introduced in 2.54
|
||||
in->ReadArray(&lipSyncFrameLetters[0][0], MAXLIPSYNCFRAMES, 50);
|
||||
}
|
||||
|
||||
void GameSetupStruct::read_messages(Shared::Stream *in, const std::array<int32_t> &load_messages, GameDataVersion data_ver) {
|
||||
char mbuf[GLOBALMESLENGTH];
|
||||
for (int i = 0; i < MAXGLOBALMES; ++i) {
|
||||
if (!load_messages[i])
|
||||
continue;
|
||||
if (data_ver < kGameVersion_261) { // Global messages are not encrypted on < 2.61
|
||||
char *nextchar = mbuf;
|
||||
|
||||
// TODO: probably this is same as fgetstring
|
||||
while (1) {
|
||||
*nextchar = in->ReadInt8();
|
||||
if (*nextchar == 0)
|
||||
break;
|
||||
nextchar++;
|
||||
}
|
||||
} else {
|
||||
read_string_decrypt(in, mbuf, GLOBALMESLENGTH);
|
||||
}
|
||||
messages[i] = mbuf;
|
||||
}
|
||||
}
|
||||
|
||||
void GameSetupStruct::ReadCharacters(Stream *in) {
|
||||
for (int i = 0; i < numcharacters; ++i) {
|
||||
chars[i].ReadFromFile(in, chars2[i], _G(loaded_game_file_version));
|
||||
}
|
||||
}
|
||||
|
||||
void GameSetupStruct::WriteCharacters(Stream *out) {
|
||||
for (int i = 0; i < numcharacters; ++i) {
|
||||
chars[i].WriteToFile(out);
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Reading Part 3
|
||||
|
||||
HGameFileError GameSetupStruct::read_customprops(Shared::Stream *in, GameDataVersion data_ver) {
|
||||
dialogScriptNames.resize(numdialog);
|
||||
viewNames.resize(numviews);
|
||||
if (data_ver >= kGameVersion_260) { // >= 2.60
|
||||
if (Properties::ReadSchema(propSchema, in) != kPropertyErr_NoError)
|
||||
return new MainGameFileError(kMGFErr_InvalidPropertySchema);
|
||||
|
||||
int errors = 0;
|
||||
|
||||
charProps.resize(numcharacters);
|
||||
for (int i = 0; i < numcharacters; ++i) {
|
||||
errors += Properties::ReadValues(charProps[i], in);
|
||||
}
|
||||
for (int i = 0; i < numinvitems; ++i) {
|
||||
errors += Properties::ReadValues(invProps[i], in);
|
||||
}
|
||||
|
||||
if (errors > 0)
|
||||
return new MainGameFileError(kMGFErr_InvalidPropertyValues);
|
||||
|
||||
for (int i = 0; i < numviews; ++i)
|
||||
viewNames[i] = String::FromStream(in);
|
||||
|
||||
if (data_ver >= kGameVersion_270) {
|
||||
for (int i = 0; i < numinvitems; ++i)
|
||||
invScriptNames[i] = String::FromStream(in);
|
||||
|
||||
if (data_ver >= kGameVersion_272) {
|
||||
for (int i = 0; i < numdialog; ++i)
|
||||
dialogScriptNames[i] = String::FromStream(in);
|
||||
}
|
||||
}
|
||||
}
|
||||
return HGameFileError::None();
|
||||
}
|
||||
|
||||
HGameFileError GameSetupStruct::read_audio(Shared::Stream *in, GameDataVersion data_ver) {
|
||||
if (data_ver >= kGameVersion_320) {
|
||||
size_t audiotype_count = in->ReadInt32();
|
||||
audioClipTypes.resize(audiotype_count);
|
||||
for (size_t i = 0; i < audiotype_count; ++i) {
|
||||
audioClipTypes[i].ReadFromFile(in);
|
||||
}
|
||||
|
||||
size_t audioclip_count = in->ReadInt32();
|
||||
audioClips.resize(audioclip_count);
|
||||
ReadAudioClips(in, audioclip_count);
|
||||
|
||||
scoreClipID = in->ReadInt32();
|
||||
}
|
||||
return HGameFileError::None();
|
||||
}
|
||||
|
||||
// Temporarily copied this from acruntim.h;
|
||||
// it is unknown if this should be defined for all solution, or only runtime
|
||||
#define STD_BUFFER_SIZE 3000
|
||||
|
||||
void GameSetupStruct::read_room_names(Stream *in, GameDataVersion data_ver) {
|
||||
if ((data_ver >= kGameVersion_301) && (options[OPT_DEBUGMODE] != 0)) {
|
||||
roomCount = in->ReadInt32();
|
||||
roomNumbers.resize(roomCount);
|
||||
roomNames.resize(roomCount);
|
||||
for (int i = 0; i < roomCount; ++i) {
|
||||
roomNumbers[i] = in->ReadInt32();
|
||||
roomNames[i].Read(in);
|
||||
}
|
||||
} else {
|
||||
roomCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void GameSetupStruct::ReadAudioClips(Shared::Stream *in, size_t count) {
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
audioClips[i].ReadFromFile(in);
|
||||
}
|
||||
}
|
||||
|
||||
void GameSetupStruct::ReadFromSaveGame_v321(Stream *in) {
|
||||
// NOTE: the individual object data is read from legacy saves
|
||||
// same way as if it were from a game file
|
||||
ReadInvInfo(in);
|
||||
ReadMouseCursors(in);
|
||||
|
||||
if (_G(loaded_game_file_version) <= kGameVersion_272) {
|
||||
for (int i = 0; i < numinvitems; ++i)
|
||||
intrInv[i]->ReadTimesRunFromSave_v321(in);
|
||||
for (int i = 0; i < numcharacters; ++i)
|
||||
intrChar[i]->ReadTimesRunFromSave_v321(in);
|
||||
}
|
||||
|
||||
in->ReadArrayOfInt32(&options[0], OPT_HIGHESTOPTION_321 + 1);
|
||||
options[OPT_LIPSYNCTEXT] = in->ReadInt8();
|
||||
|
||||
ReadCharacters(in);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
#if defined (OBSOLETE)
|
||||
|
||||
void ConvertOldGameStruct(OldGameSetupStruct *ogss, GameSetupStruct *gss) {
|
||||
snprintf(gss->gamename, sizeof(GameSetupStruct::gamename), "%s", ogss->gamename);
|
||||
for (int i = 0; i < 20; i++)
|
||||
gss->options[i] = ogss->options[i];
|
||||
memcpy(&gss->paluses[0], &ogss->paluses[0], 256);
|
||||
memcpy(&gss->defpal[0], &ogss->defpal[0], 256 * sizeof(RGB));
|
||||
gss->numviews = ogss->numviews;
|
||||
gss->numcharacters = ogss->numcharacters;
|
||||
gss->playercharacter = ogss->playercharacter;
|
||||
gss->totalscore = ogss->totalscore;
|
||||
gss->numinvitems = ogss->numinvitems;
|
||||
gss->numdialog = ogss->numdialog;
|
||||
gss->numdlgmessage = ogss->numdlgmessage;
|
||||
gss->numfonts = ogss->numfonts;
|
||||
gss->color_depth = ogss->color_depth;
|
||||
gss->target_win = ogss->target_win;
|
||||
gss->dialog_bullet = ogss->dialog_bullet;
|
||||
gss->hotdot = ogss->hotdot;
|
||||
gss->hotdotouter = ogss->hotdotouter;
|
||||
gss->uniqueid = ogss->uniqueid;
|
||||
gss->numgui = ogss->numgui;
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
SetFontInfoFromLegacyFlags(gss->fonts[i], ogss->fontflags[i]);
|
||||
gss->fonts[i].Outline = ogss->fontoutline[i];
|
||||
}
|
||||
|
||||
for (int i = 0; i < LEGACY_MAX_SPRITES_V25; ++i) {
|
||||
gss->SpriteInfos[i].Flags = ogss->spriteflags[i];
|
||||
}
|
||||
|
||||
memcpy(&gss->invinfo[0], &ogss->invinfo[0], 100 * sizeof(InventoryItemInfo));
|
||||
for (int i = 0; i < 10; ++i)
|
||||
gss->mcurs[i] = ogss->mcurs[i];
|
||||
for (int i = 0; i < MAXGLOBALMES; i++)
|
||||
gss->messages[i] = ogss->messages[i];
|
||||
gss->dict = ogss->dict;
|
||||
gss->globalscript = ogss->globalscript;
|
||||
gss->chars = nullptr; //ogss->chars;
|
||||
gss->compiled_script = ogss->compiled_script;
|
||||
gss->numcursors = 10;
|
||||
}
|
||||
#endif // OBSOLETE
|
||||
|
||||
void GameSetupStruct::ReadFromSavegame(Stream *in) {
|
||||
// of GameSetupStruct
|
||||
in->ReadArrayOfInt32(options, OPT_HIGHESTOPTION_321 + 1);
|
||||
options[OPT_LIPSYNCTEXT] = in->ReadInt32();
|
||||
// of GameSetupStructBase
|
||||
playercharacter = in->ReadInt32();
|
||||
dialog_bullet = in->ReadInt32();
|
||||
hotdot = static_cast<uint16_t>(in->ReadInt16());
|
||||
hotdotouter = static_cast<uint16_t>(in->ReadInt16());
|
||||
invhotdotsprite = in->ReadInt32();
|
||||
default_lipsync_frame = in->ReadInt32();
|
||||
}
|
||||
|
||||
void GameSetupStruct::WriteForSavegame(Stream *out) {
|
||||
// of GameSetupStruct
|
||||
out->WriteArrayOfInt32(options, OPT_HIGHESTOPTION_321 + 1);
|
||||
out->WriteInt32(options[OPT_LIPSYNCTEXT]);
|
||||
// of GameSetupStructBase
|
||||
out->WriteInt32(playercharacter);
|
||||
out->WriteInt32(dialog_bullet);
|
||||
out->WriteInt16(static_cast<uint16_t>(hotdot));
|
||||
out->WriteInt16(static_cast<uint16_t>(hotdotouter));
|
||||
out->WriteInt32(invhotdotsprite);
|
||||
out->WriteInt32(default_lipsync_frame);
|
||||
}
|
||||
|
||||
} // namespace AGS3
|
||||
189
engines/ags/shared/ac/game_setup_struct.h
Normal file
189
engines/ags/shared/ac/game_setup_struct.h
Normal file
@@ -0,0 +1,189 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// GameSetupStruct is a contemporary main game data.
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef AGS_SHARED_AC_GAME_SETUP_STRUCT_H
|
||||
#define AGS_SHARED_AC_GAME_SETUP_STRUCT_H
|
||||
|
||||
#include "common/std/array.h"
|
||||
#include "common/std/vector.h"
|
||||
#include "ags/shared/ac/audio_clip_type.h"
|
||||
#include "ags/shared/ac/character_info.h" // TODO: constants to separate header
|
||||
#include "ags/shared/ac/game_setup_struct_base.h"
|
||||
#include "ags/shared/ac/inventory_item_info.h"
|
||||
#include "ags/shared/ac/mouse_cursor.h"
|
||||
#include "ags/shared/ac/dynobj/script_audio_clip.h"
|
||||
#include "ags/shared/game/custom_properties.h"
|
||||
#include "ags/shared/game/main_game_file.h" // TODO: constants to separate header or split out reading functions
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
namespace AGS {
|
||||
namespace Shared {
|
||||
struct AssetLibInfo;
|
||||
struct Interaction;
|
||||
struct InteractionScripts;
|
||||
typedef std::shared_ptr<Interaction> PInteraction;
|
||||
typedef std::shared_ptr<InteractionScripts> PInteractionScripts;
|
||||
} // namespace Shared
|
||||
} // namespace AGS
|
||||
|
||||
using AGS::Shared::PInteraction;
|
||||
using AGS::Shared::PInteractionScripts;
|
||||
using AGS::Shared::HGameFileError;
|
||||
|
||||
|
||||
// TODO: split GameSetupStruct into struct used to hold loaded game data, and actual runtime object
|
||||
struct GameSetupStruct : public GameSetupStructBase {
|
||||
// This array is used only to read data into;
|
||||
// font parameters are then put and queried in the fonts module
|
||||
// TODO: split into installation params (used only when reading) and runtime params
|
||||
std::vector<FontInfo> fonts;
|
||||
InventoryItemInfo invinfo[MAX_INV]{};
|
||||
std::vector<MouseCursor> mcurs;
|
||||
std::vector<PInteraction> intrChar;
|
||||
PInteraction intrInv[MAX_INV];
|
||||
std::vector<PInteractionScripts> charScripts;
|
||||
std::vector<PInteractionScripts> invScripts;
|
||||
// TODO: why we do not use this in the engine instead of
|
||||
// _G(loaded_game_file_version)?
|
||||
int filever; // just used by editor
|
||||
Shared::String compiled_with; // version of AGS this data was created by
|
||||
char lipSyncFrameLetters[MAXLIPSYNCFRAMES][50];
|
||||
AGS::Shared::PropertySchema propSchema;
|
||||
std::vector<AGS::Shared::StringIMap> charProps;
|
||||
AGS::Shared::StringIMap invProps[MAX_INV];
|
||||
// NOTE: although the view names are stored in game data, they are never
|
||||
// used, nor registered as script exports; numeric IDs are used to
|
||||
// reference views instead.
|
||||
std::vector<Shared::String> viewNames;
|
||||
Shared::String invScriptNames[MAX_INV];
|
||||
std::vector<Shared::String> dialogScriptNames;
|
||||
char guid[MAX_GUID_LENGTH];
|
||||
char saveGameFileExtension[MAX_SG_EXT_LENGTH];
|
||||
// NOTE: saveGameFolderName is generally used to create game subdirs in common user directories
|
||||
Shared::String saveGameFolderName;
|
||||
int roomCount;
|
||||
std::vector<int> roomNumbers;
|
||||
std::vector<Shared::String> roomNames;
|
||||
std::vector<ScriptAudioClip> audioClips;
|
||||
std::vector<AudioClipType> audioClipTypes;
|
||||
// A clip to play when player gains score in game
|
||||
// TODO: find out why OPT_SCORESOUND option cannot be used to store this in >=3.2 games
|
||||
int scoreClipID;
|
||||
// number of accessible game audio channels (the ones under direct user control)
|
||||
int numGameChannels = 0;
|
||||
// backward-compatible channel limit that may be exported to script and reserved by audiotypes
|
||||
int numCompatGameChannels = 0;
|
||||
|
||||
// TODO: I converted original array of sprite infos to vector here, because
|
||||
// statistically in most games sprites go in long continious sequences with minimal
|
||||
// gaps, and standard hash-map will have relatively big memory overhead compared.
|
||||
// Of course vector will not behave very well if user has created e.g. only
|
||||
// sprite #1 and sprite #1000000. For that reason I decided to still limit static
|
||||
// sprite count to some reasonable number for the time being. Dynamic sprite IDs are
|
||||
// added in sequence, so there won't be any issue with these.
|
||||
// There could be other collection types, more optimal for this case. For example,
|
||||
// we could use a kind of hash map containing fixed-sized arrays, where size of
|
||||
// array is calculated based on key spread factor.
|
||||
std::vector<SpriteInfo> SpriteInfos;
|
||||
|
||||
// Get game's native color depth (bits per pixel)
|
||||
inline int GetColorDepth() const {
|
||||
return color_depth * 8;
|
||||
}
|
||||
|
||||
|
||||
GameSetupStruct();
|
||||
GameSetupStruct(GameSetupStruct &&gss) = default;
|
||||
~GameSetupStruct();
|
||||
|
||||
GameSetupStruct &operator=(GameSetupStruct &&gss) = default;
|
||||
|
||||
void Free();
|
||||
|
||||
// [IKM] Game struct loading code is moved here from Engine's load_game_file
|
||||
// function; for now it is not supposed to be called by Editor; although it
|
||||
// is possible that eventually will be.
|
||||
//
|
||||
// Since reading game data is made in a bit inconvenient way I had to
|
||||
// a) divide process into three functions (there's some extra stuff
|
||||
// being read between them;
|
||||
// b) use a helper struct to pass some arguments
|
||||
//
|
||||
// I also had to move BuildAudioClipArray from the engine and make it
|
||||
// GameSetupStruct member.
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Do not call these directly
|
||||
//------------------------------
|
||||
// Part 1
|
||||
void read_savegame_info(Shared::Stream *in, GameDataVersion data_ver);
|
||||
void read_font_infos(Shared::Stream *in, GameDataVersion data_ver);
|
||||
HGameFileError read_cursors(Shared::Stream *in);
|
||||
void read_interaction_scripts(Shared::Stream *in, GameDataVersion data_ver);
|
||||
void read_words_dictionary(Shared::Stream *in);
|
||||
|
||||
void ReadInvInfo(Shared::Stream *in);
|
||||
void WriteInvInfo(Shared::Stream *out);
|
||||
void ReadMouseCursors(Shared::Stream *in);
|
||||
void WriteMouseCursors(Shared::Stream *out);
|
||||
//------------------------------
|
||||
// Part 2
|
||||
void read_characters(Shared::Stream *in);
|
||||
void read_lipsync(Shared::Stream *in, GameDataVersion data_ver);
|
||||
void read_messages(Shared::Stream *in, const std::array<int32_t> &load_messages, GameDataVersion data_ver);
|
||||
|
||||
void ReadCharacters(Shared::Stream *in);
|
||||
void WriteCharacters(Shared::Stream *out);
|
||||
//------------------------------
|
||||
// Part 3
|
||||
HGameFileError read_customprops(Shared::Stream *in, GameDataVersion data_ver);
|
||||
HGameFileError read_audio(Shared::Stream *in, GameDataVersion data_ver);
|
||||
void read_room_names(Shared::Stream *in, GameDataVersion data_ver);
|
||||
|
||||
void ReadAudioClips(Shared::Stream *in, size_t count);
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
// Functions for reading and writing appropriate data from/to save game
|
||||
void ReadFromSaveGame_v321(Shared::Stream *in);
|
||||
|
||||
void ReadFromSavegame(Shared::Stream *in);
|
||||
void WriteForSavegame(Shared::Stream *out);
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
#if defined (OBSOLETE)
|
||||
struct OldGameSetupStruct;
|
||||
void ConvertOldGameStruct(OldGameSetupStruct *ogss, GameSetupStruct *gss);
|
||||
#endif // OBSOLETE
|
||||
|
||||
// Finds an audio clip using legacy convention index
|
||||
ScriptAudioClip *GetAudioClipForOldStyleNumber(GameSetupStruct &game, bool is_music, int num);
|
||||
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
270
engines/ags/shared/ac/game_setup_struct_base.cpp
Normal file
270
engines/ags/shared/ac/game_setup_struct_base.cpp
Normal file
@@ -0,0 +1,270 @@
|
||||
/* 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 "ags/shared/ac/character_info.h"
|
||||
#include "ags/shared/ac/game_setup_struct_base.h"
|
||||
#include "ags/shared/ac/game_setup_struct.h"
|
||||
#include "ags/shared/ac/game_version.h"
|
||||
#include "ags/shared/ac/words_dictionary.h"
|
||||
#include "ags/shared/script/cc_script.h"
|
||||
#include "ags/shared/util/stream.h"
|
||||
#include "ags/shared/util/string_utils.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
using namespace AGS::Shared;
|
||||
|
||||
GameSetupStructBase::GameSetupStructBase()
|
||||
: numviews(0)
|
||||
, numcharacters(0)
|
||||
, playercharacter(-1)
|
||||
, totalscore(0)
|
||||
, numinvitems(0)
|
||||
, numdialog(0)
|
||||
, numdlgmessage(0)
|
||||
, numfonts(0)
|
||||
, color_depth(0)
|
||||
, target_win(0)
|
||||
, dialog_bullet(0)
|
||||
, hotdot(0)
|
||||
, hotdotouter(0)
|
||||
, uniqueid(0)
|
||||
, numgui(0)
|
||||
, numcursors(0)
|
||||
, default_lipsync_frame(0)
|
||||
, invhotdotsprite(0)
|
||||
, dict(nullptr)
|
||||
, _resolutionType(kGameResolution_Undefined)
|
||||
, _dataUpscaleMult(1)
|
||||
, _screenUpscaleMult(1) {
|
||||
memset(options, 0, sizeof(options));
|
||||
memset(paluses, 0, sizeof(paluses));
|
||||
memset(defpal, 0, sizeof(defpal));
|
||||
memset(reserved, 0, sizeof(reserved));
|
||||
}
|
||||
|
||||
GameSetupStructBase::~GameSetupStructBase() {
|
||||
Free();
|
||||
}
|
||||
|
||||
void GameSetupStructBase::Free() {
|
||||
for (int i = 0; i < MAXGLOBALMES; ++i) {
|
||||
messages[i].Free();
|
||||
}
|
||||
dict.reset();
|
||||
chars.clear();
|
||||
|
||||
numcharacters = 0;
|
||||
}
|
||||
|
||||
void GameSetupStructBase::SetDefaultResolution(GameResolutionType type) {
|
||||
SetDefaultResolution(type, Size());
|
||||
}
|
||||
|
||||
void GameSetupStructBase::SetDefaultResolution(Size size) {
|
||||
SetDefaultResolution(kGameResolution_Custom, size);
|
||||
}
|
||||
|
||||
void GameSetupStructBase::SetDefaultResolution(GameResolutionType type, Size size) {
|
||||
// Calculate native res first then remember it
|
||||
SetNativeResolution(type, size);
|
||||
_defGameResolution = _gameResolution;
|
||||
// Setup data resolution according to legacy settings (if set)
|
||||
_dataResolution = _defGameResolution;
|
||||
if (IsLegacyHiRes() && options[OPT_NATIVECOORDINATES] == 0) {
|
||||
_dataResolution = _defGameResolution / HIRES_COORD_MULTIPLIER;
|
||||
}
|
||||
OnResolutionSet();
|
||||
}
|
||||
|
||||
void GameSetupStructBase::SetNativeResolution(GameResolutionType type, Size game_res) {
|
||||
if (type == kGameResolution_Custom) {
|
||||
_resolutionType = kGameResolution_Custom;
|
||||
_gameResolution = game_res;
|
||||
_letterboxSize = _gameResolution;
|
||||
} else {
|
||||
_resolutionType = type;
|
||||
_gameResolution = ResolutionTypeToSize(_resolutionType, IsLegacyLetterbox());
|
||||
_letterboxSize = ResolutionTypeToSize(_resolutionType, false);
|
||||
}
|
||||
}
|
||||
|
||||
void GameSetupStructBase::SetGameResolution(GameResolutionType type) {
|
||||
SetNativeResolution(type, Size());
|
||||
OnResolutionSet();
|
||||
}
|
||||
|
||||
void GameSetupStructBase::SetGameResolution(Size game_res) {
|
||||
SetNativeResolution(kGameResolution_Custom, game_res);
|
||||
OnResolutionSet();
|
||||
}
|
||||
|
||||
void GameSetupStructBase::OnResolutionSet() {
|
||||
// The final data-to-game multiplier is always set after actual game resolution (not default one)
|
||||
if (!_dataResolution.IsNull())
|
||||
_dataUpscaleMult = _gameResolution.Width / _dataResolution.Width;
|
||||
else
|
||||
_dataUpscaleMult = 1;
|
||||
if (!_defGameResolution.IsNull())
|
||||
_screenUpscaleMult = _gameResolution.Width / _defGameResolution.Width;
|
||||
else
|
||||
_screenUpscaleMult = 1;
|
||||
_relativeUIMult = IsLegacyHiRes() ? HIRES_COORD_MULTIPLIER : 1;
|
||||
}
|
||||
|
||||
void GameSetupStructBase::ReadFromFile(Stream *in, GameDataVersion game_ver, SerializeInfo &info) {
|
||||
// NOTE: historically the struct was saved by dumping whole memory
|
||||
// into the file stream, which added padding from memory alignment;
|
||||
// here we mark the padding bytes, as they do not belong to actual data.
|
||||
gamename.ReadCount(in, LEGACY_GAME_NAME_LENGTH);
|
||||
in->ReadInt16(); // alignment padding to int32 (gamename: 50 -> 52 bytes)
|
||||
in->ReadArrayOfInt32(options, MAX_OPTIONS);
|
||||
if (game_ver < kGameVersion_340_4) { // TODO: this should probably be possible to deduce script API level
|
||||
// using game data version and other options like OPT_STRICTSCRIPTING
|
||||
options[OPT_BASESCRIPTAPI] = kScriptAPI_Undefined;
|
||||
options[OPT_SCRIPTCOMPATLEV] = kScriptAPI_Undefined;
|
||||
}
|
||||
in->Read(&paluses[0], sizeof(paluses));
|
||||
// colors are an array of chars
|
||||
in->Read(&defpal[0], sizeof(defpal));
|
||||
numviews = in->ReadInt32();
|
||||
numcharacters = in->ReadInt32();
|
||||
playercharacter = in->ReadInt32();
|
||||
totalscore = in->ReadInt32();
|
||||
numinvitems = in->ReadInt16();
|
||||
in->ReadInt16(); // alignment padding to int32
|
||||
numdialog = in->ReadInt32();
|
||||
numdlgmessage = in->ReadInt32();
|
||||
numfonts = in->ReadInt32();
|
||||
color_depth = in->ReadInt32();
|
||||
target_win = in->ReadInt32();
|
||||
dialog_bullet = in->ReadInt32();
|
||||
hotdot = static_cast<uint16_t>(in->ReadInt16());
|
||||
hotdotouter = static_cast<uint16_t>(in->ReadInt16());
|
||||
uniqueid = in->ReadInt32();
|
||||
numgui = in->ReadInt32();
|
||||
numcursors = in->ReadInt32();
|
||||
GameResolutionType resolution_type = (GameResolutionType)in->ReadInt32();
|
||||
Size game_size;
|
||||
if (resolution_type == kGameResolution_Custom && game_ver >= kGameVersion_330) {
|
||||
game_size.Width = in->ReadInt32();
|
||||
game_size.Height = in->ReadInt32();
|
||||
}
|
||||
SetDefaultResolution(resolution_type, game_size);
|
||||
|
||||
default_lipsync_frame = in->ReadInt32();
|
||||
invhotdotsprite = in->ReadInt32();
|
||||
in->ReadArrayOfInt32(reserved, NUM_INTS_RESERVED);
|
||||
|
||||
info.ExtensionOffset = static_cast<uint32_t>(in->ReadInt32());
|
||||
in->ReadArrayOfInt32(&info.HasMessages.front(), MAXGLOBALMES);
|
||||
|
||||
info.HasWordsDict = in->ReadInt32() != 0;
|
||||
in->ReadInt32(); // globalscript (dummy 32-bit pointer value)
|
||||
in->ReadInt32(); // chars (dummy 32-bit pointer value)
|
||||
info.HasCCScript = in->ReadInt32() != 0;
|
||||
}
|
||||
|
||||
void GameSetupStructBase::WriteToFile(Stream *out, const SerializeInfo &info) const {
|
||||
// NOTE: historically the struct was saved by dumping whole memory
|
||||
// into the file stream, which added padding from memory alignment;
|
||||
// here we mark the padding bytes, as they do not belong to actual data.
|
||||
gamename.WriteCount(out, LEGACY_GAME_NAME_LENGTH);
|
||||
out->WriteInt16(0); // alignment padding to int32
|
||||
out->WriteArrayOfInt32(options, MAX_OPTIONS);
|
||||
out->Write(&paluses[0], sizeof(paluses));
|
||||
// colors are an array of chars
|
||||
out->Write(&defpal[0], sizeof(defpal));
|
||||
out->WriteInt32(numviews);
|
||||
out->WriteInt32(numcharacters);
|
||||
out->WriteInt32(playercharacter);
|
||||
out->WriteInt32(totalscore);
|
||||
out->WriteInt16(numinvitems);
|
||||
out->WriteInt16(0); // alignment padding to int32
|
||||
out->WriteInt32(numdialog);
|
||||
out->WriteInt32(numdlgmessage);
|
||||
out->WriteInt32(numfonts);
|
||||
out->WriteInt32(color_depth);
|
||||
out->WriteInt32(target_win);
|
||||
out->WriteInt32(dialog_bullet);
|
||||
out->WriteInt16(static_cast<uint16_t>(hotdot));
|
||||
out->WriteInt16(static_cast<uint16_t>(hotdotouter));
|
||||
out->WriteInt32(uniqueid);
|
||||
out->WriteInt32(numgui);
|
||||
out->WriteInt32(numcursors);
|
||||
out->WriteInt32(_resolutionType);
|
||||
if (_resolutionType == kGameResolution_Custom) {
|
||||
out->WriteInt32(_defGameResolution.Width);
|
||||
out->WriteInt32(_defGameResolution.Height);
|
||||
}
|
||||
out->WriteInt32(default_lipsync_frame);
|
||||
out->WriteInt32(invhotdotsprite);
|
||||
out->WriteArrayOfInt32(reserved, 17);
|
||||
for (int i = 0; i < MAXGLOBALMES; ++i) {
|
||||
out->WriteInt32(!messages[i].IsEmpty() ? 1 : 0);
|
||||
}
|
||||
out->WriteInt32(dict ? 1 : 0);
|
||||
out->WriteInt32(0); // globalscript (dummy 32-bit pointer value)
|
||||
out->WriteInt32(0); // chars (dummy 32-bit pointer value)
|
||||
out->WriteInt32(info.HasCCScript ? 1 : 0);
|
||||
}
|
||||
|
||||
Size ResolutionTypeToSize(GameResolutionType resolution, bool letterbox) {
|
||||
switch (resolution) {
|
||||
case kGameResolution_Default:
|
||||
case kGameResolution_320x200:
|
||||
return letterbox ? Size(320, 240) : Size(320, 200);
|
||||
case kGameResolution_320x240:
|
||||
return Size(320, 240);
|
||||
case kGameResolution_640x400:
|
||||
return letterbox ? Size(640, 480) : Size(640, 400);
|
||||
case kGameResolution_640x480:
|
||||
return Size(640, 480);
|
||||
case kGameResolution_800x600:
|
||||
return Size(800, 600);
|
||||
case kGameResolution_1024x768:
|
||||
return Size(1024, 768);
|
||||
case kGameResolution_1280x720:
|
||||
return Size(1280, 720);
|
||||
default:
|
||||
return Size();
|
||||
}
|
||||
}
|
||||
|
||||
const char *GetScriptAPIName(ScriptAPIVersion v) {
|
||||
switch (v) {
|
||||
case kScriptAPI_v321: return "v3.2.1";
|
||||
case kScriptAPI_v330: return "v3.3.0";
|
||||
case kScriptAPI_v334: return "v3.3.4";
|
||||
case kScriptAPI_v335: return "v3.3.5";
|
||||
case kScriptAPI_v340: return "v3.4.0";
|
||||
case kScriptAPI_v341: return "v3.4.1";
|
||||
case kScriptAPI_v350: return "v3.5.0-alpha";
|
||||
case kScriptAPI_v3507: return "v3.5.0-final";
|
||||
case kScriptAPI_v351: return "v3.5.1";
|
||||
case kScriptAPI_v360: return "v3.6.0-alpha";
|
||||
case kScriptAPI_v36026: return "v3.6.0-final";
|
||||
case kScriptAPI_v361: return "v3.6.1";
|
||||
default: return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace AGS3
|
||||
283
engines/ags/shared/ac/game_setup_struct_base.h
Normal file
283
engines/ags/shared/ac/game_setup_struct_base.h
Normal file
@@ -0,0 +1,283 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
//
|
||||
//=============================================================================
|
||||
//
|
||||
// GameSetupStructBase is a base class for main game data.
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef AGS_SHARED_AC_GAME_SETUP_STRUCT_BASE_H
|
||||
#define AGS_SHARED_AC_GAME_SETUP_STRUCT_BASE_H
|
||||
|
||||
#include "ags/lib/allegro.h" // RGB
|
||||
#include "common/std/array.h"
|
||||
#include "common/std/memory.h"
|
||||
#include "common/std/vector.h"
|
||||
#include "ags/shared/ac/game_version.h"
|
||||
#include "ags/shared/ac/game_struct_defines.h"
|
||||
#include "ags/shared/ac/words_dictionary.h"
|
||||
#include "ags/shared/util/string.h"
|
||||
#include "ags/globals.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
// Forward declaration
|
||||
namespace AGS {
|
||||
namespace Shared {
|
||||
class Stream;
|
||||
} // namespace Shared
|
||||
} // namespace AGS
|
||||
|
||||
using namespace AGS; // FIXME later
|
||||
|
||||
struct CharacterInfo;
|
||||
struct ccScript;
|
||||
|
||||
|
||||
struct GameSetupStructBase {
|
||||
static const int LEGACY_GAME_NAME_LENGTH = 50;
|
||||
static const int MAX_OPTIONS = 100;
|
||||
static const int NUM_INTS_RESERVED = 16;
|
||||
|
||||
Shared::String gamename;
|
||||
int32_t options[MAX_OPTIONS];
|
||||
uint8_t paluses[256];
|
||||
RGB defpal[256];
|
||||
int numviews;
|
||||
int numcharacters;
|
||||
int playercharacter;
|
||||
int totalscore;
|
||||
int numinvitems;
|
||||
int numdialog;
|
||||
int numdlgmessage; // [DEPRECATED]
|
||||
int numfonts;
|
||||
int color_depth; // in bytes per pixel (ie. 1 or 2)
|
||||
int target_win;
|
||||
int dialog_bullet; // 0 for none, otherwise slot num of bullet point
|
||||
int hotdot; // inv cursor hotspot dot color
|
||||
int hotdotouter;
|
||||
int uniqueid; // random key identifying the game
|
||||
int numgui;
|
||||
int numcursors;
|
||||
int default_lipsync_frame; // used for unknown chars
|
||||
int invhotdotsprite;
|
||||
int32_t reserved[NUM_INTS_RESERVED];
|
||||
String messages[MAXGLOBALMES];
|
||||
std::unique_ptr<WordsDictionary> dict;
|
||||
std::vector<CharacterInfo> chars;
|
||||
std::vector<CharacterInfo2> chars2; // extended character fields
|
||||
|
||||
GameSetupStructBase();
|
||||
GameSetupStructBase(GameSetupStructBase &&gss) = default;
|
||||
~GameSetupStructBase();
|
||||
|
||||
GameSetupStructBase &operator=(GameSetupStructBase &&gss) = default;
|
||||
|
||||
void Free();
|
||||
void SetDefaultResolution(GameResolutionType type);
|
||||
void SetDefaultResolution(Size game_res);
|
||||
void SetGameResolution(GameResolutionType type);
|
||||
void SetGameResolution(Size game_res);
|
||||
|
||||
// Tells whether the serialized game data contains certain components
|
||||
struct SerializeInfo {
|
||||
bool HasCCScript = false;
|
||||
bool HasWordsDict = false;
|
||||
std::array<int32_t> HasMessages;
|
||||
// File offset at which game data extensions begin
|
||||
uint32_t ExtensionOffset = 0u;
|
||||
|
||||
SerializeInfo() {
|
||||
HasMessages.resize(MAXGLOBALMES);
|
||||
}
|
||||
};
|
||||
|
||||
void ReadFromFile(Shared::Stream *in, GameDataVersion game_ver, SerializeInfo &info);
|
||||
void WriteToFile(Shared::Stream *out, const SerializeInfo &info) const;
|
||||
|
||||
//
|
||||
// ** On game resolution.
|
||||
//
|
||||
// Game resolution is a size of a native game screen in pixels.
|
||||
// This is the "game resolution" that developer sets up in AGS Editor.
|
||||
// It is in the same units in which sprite and font sizes are defined.
|
||||
//
|
||||
// Graphic renderer may scale and stretch game's frame as requested by
|
||||
// player or system, which will not affect native coordinates in any way.
|
||||
//
|
||||
// ** Legacy upscale mode.
|
||||
//
|
||||
// In the past engine had a separation between logical and native screen
|
||||
// coordinates and supported running games "upscaled". E.g. 320x200 games
|
||||
// could be run as 640x400. This was not done by simply stretching final
|
||||
// game's drawn frame to the larger window, but by multiplying all data
|
||||
// containing coordinates and graphics either on load or real-time.
|
||||
// Games of 640x400 and above were scripted and set up in coordinate units
|
||||
// that were always x2 times smaller than the one developer chose.
|
||||
// For example, choosing a 640x400 resolution would make game draw itself
|
||||
// as 640x400, but all the game logic (object properties, script commands)
|
||||
// would work in 320x200 (this also let run 640x400 downscaled to 320x200).
|
||||
// Ignoring the obvious complications, the known benefit from such approach
|
||||
// was that developers could supply separate sets of fonts and sprites for
|
||||
// low-res and high-res modes.
|
||||
// The 3rd generation of AGS still allows to achieve same effect by using
|
||||
// backwards-compatible option (although it is not recommended except when
|
||||
// importing and continuing old projects).
|
||||
//
|
||||
// In order to support this legacy behavior we have a set of functions for
|
||||
// coordinate conversion. They are required to move from "data" resolution
|
||||
// to "final game" resolution and back.
|
||||
//
|
||||
// Some of the script commands, as well as some internal engine data use
|
||||
// coordinates in "game resolution" instead (this should be documented).
|
||||
// In such case there's another conversion which translates these from
|
||||
// default to actual resolution; e.g. when 320x200 game is run as 640x400
|
||||
// they should be multiplied by 2.
|
||||
//
|
||||
// ** TODO.
|
||||
//
|
||||
// Truth be told, all this is still implemented incorrectly, because no one
|
||||
// found time to rewrite the thing. The correct way would perhaps be:
|
||||
// 1) treat old games as x2 lower resolution than they say.
|
||||
// 2) support drawing particular sprites and texts in x2 higher resolution
|
||||
// (assuming display resolution allows). The latter is potentially enabled
|
||||
// by "sprite batches" system in the engine and will benefit new games too.
|
||||
|
||||
inline GameResolutionType GetResolutionType() const {
|
||||
return _resolutionType;
|
||||
}
|
||||
|
||||
// Get actual game's resolution
|
||||
const Size &GetGameRes() const {
|
||||
return _gameResolution;
|
||||
}
|
||||
// Get default resolution the game was created for;
|
||||
// this is usually equal to GetGameRes except for legacy modes.
|
||||
const Size &GetDefaultRes() const {
|
||||
return _defGameResolution;
|
||||
}
|
||||
// Get data & script resolution;
|
||||
// this is usually equal to GetGameRes except for legacy modes.
|
||||
const Size &GetDataRes() const {
|
||||
return _dataResolution;
|
||||
}
|
||||
// Get game data-->final game resolution coordinate multiplier
|
||||
inline int GetDataUpscaleMult() const {
|
||||
return _dataUpscaleMult;
|
||||
}
|
||||
// Get multiplier for various default UI sizes, meant to keep UI looks
|
||||
// more or less readable in any game resolution.
|
||||
// TODO: find a better solution for UI sizes, perhaps make variables.
|
||||
inline int GetRelativeUIMult() const {
|
||||
return _relativeUIMult;
|
||||
}
|
||||
// Get game default res-->final game resolution coordinate multiplier;
|
||||
// used to convert coordinates from original game res to actual one
|
||||
inline int GetScreenUpscaleMult() const {
|
||||
return _screenUpscaleMult;
|
||||
}
|
||||
// Tells if game allows assets defined in relative resolution;
|
||||
// that is - have to be converted to this game resolution type
|
||||
inline bool AllowRelativeRes() const {
|
||||
return options[OPT_RELATIVEASSETRES] != 0;
|
||||
}
|
||||
// Legacy definition of high and low game resolution.
|
||||
// Used to determine certain hardcoded coordinate conversion logic, but
|
||||
// does not make much sense today when the resolution is arbitrary.
|
||||
inline bool IsLegacyHiRes() const {
|
||||
if (_resolutionType == kGameResolution_Custom)
|
||||
return (_gameResolution.Width * _gameResolution.Height) > (320 * 240);
|
||||
return ::AGS3::IsLegacyHiRes(_resolutionType);
|
||||
}
|
||||
// Tells if data has coordinates in default game resolution
|
||||
inline bool IsDataInNativeCoordinates() const {
|
||||
return options[OPT_NATIVECOORDINATES] != 0;
|
||||
}
|
||||
|
||||
// Tells if game runs in native letterbox mode (legacy option)
|
||||
inline bool IsLegacyLetterbox() const {
|
||||
return options[OPT_LETTERBOX] != 0;
|
||||
}
|
||||
// Get letterboxed frame size
|
||||
const Size &GetLetterboxSize() const {
|
||||
return _letterboxSize;
|
||||
}
|
||||
|
||||
// Room region/hotspot masks are traditionally 1:1 of the room's size in
|
||||
// low-resolution games and 1:2 of the room size in high-resolution games.
|
||||
// This also means that mask relation to data resolution is 1:1 if the
|
||||
// game uses low-res coordinates in script and 1:2 if high-res.
|
||||
|
||||
// Test if the game is built around old audio system
|
||||
inline bool IsLegacyAudioSystem() const {
|
||||
return _G(loaded_game_file_version) < kGameVersion_320;
|
||||
}
|
||||
|
||||
// Returns the expected filename of a digital audio package
|
||||
inline AGS::Shared::String GetAudioVOXName() const {
|
||||
return IsLegacyAudioSystem() ? "music.vox" : "audio.vox";
|
||||
}
|
||||
|
||||
// Returns a list of game options that are forbidden to change at runtime
|
||||
inline static Common::Array<int> GetRestrictedOptions() {
|
||||
return Common::Array<int> {{
|
||||
OPT_DEBUGMODE, OPT_LETTERBOX, OPT_HIRES_FONTS, OPT_SPLITRESOURCES,
|
||||
OPT_STRICTSCRIPTING, OPT_LEFTTORIGHTEVAL, OPT_COMPRESSSPRITES, OPT_STRICTSTRINGS,
|
||||
OPT_NATIVECOORDINATES, OPT_SAFEFILEPATHS, OPT_DIALOGOPTIONSAPI, OPT_BASESCRIPTAPI,
|
||||
OPT_SCRIPTCOMPATLEV, OPT_RELATIVEASSETRES, OPT_GAMETEXTENCODING, OPT_KEYHANDLEAPI,
|
||||
OPT_CUSTOMENGINETAG
|
||||
}};
|
||||
}
|
||||
|
||||
private:
|
||||
void SetDefaultResolution(GameResolutionType type, Size game_res);
|
||||
void SetNativeResolution(GameResolutionType type, Size game_res);
|
||||
void OnResolutionSet();
|
||||
|
||||
// Game's native resolution ID, used to init following values.
|
||||
GameResolutionType _resolutionType;
|
||||
|
||||
// Determines game's default screen resolution. Use for the reference
|
||||
// when comparing with actual screen resolution, which may be modified
|
||||
// by certain overriding game modes.
|
||||
Size _defGameResolution;
|
||||
// Determines game's actual resolution.
|
||||
Size _gameResolution;
|
||||
// Determines resolution in which loaded data and script define coordinates
|
||||
// and sizes (with very little exception).
|
||||
Size _dataResolution;
|
||||
// Letterboxed frame size. Used when old game is run in native letterbox
|
||||
// mode. In all other situations is equal to game's resolution.
|
||||
Size _letterboxSize;
|
||||
|
||||
// Game logic to game resolution coordinate factor
|
||||
int _dataUpscaleMult;
|
||||
// Multiplier for various UI drawin sizes, meant to keep UI elements readable
|
||||
int _relativeUIMult;
|
||||
// Game default resolution to actual game resolution factor
|
||||
int _screenUpscaleMult;
|
||||
};
|
||||
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
296
engines/ags/shared/ac/game_struct_defines.h
Normal file
296
engines/ags/shared/ac/game_struct_defines.h
Normal file
@@ -0,0 +1,296 @@
|
||||
/* 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 AGS_SHARED_AC_GAME_STRUCT_DEFINES_H
|
||||
#define AGS_SHARED_AC_GAME_STRUCT_DEFINES_H
|
||||
|
||||
#include "ags/shared/util/geometry.h"
|
||||
#include "ags/shared/core/types.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
#define PAL_GAMEWIDE 0
|
||||
#define PAL_LOCKED 1
|
||||
#define PAL_BACKGROUND 2
|
||||
#define MAXGLOBALMES 500
|
||||
#define GLOBALMESLENGTH 500
|
||||
#define MAXLANGUAGE 5
|
||||
#define LEGACY_MAX_FONTS 30
|
||||
|
||||
// General game options
|
||||
#define OPT_DEBUGMODE 0
|
||||
#define OPT_SCORESOUND 1
|
||||
#define OPT_WALKONLOOK 2
|
||||
#define OPT_DIALOGIFACE 3
|
||||
#define OPT_ANTIGLIDE 4
|
||||
#define OPT_TWCUSTOM 5
|
||||
#define OPT_DIALOGGAP 6
|
||||
#define OPT_NOSKIPTEXT 7
|
||||
#define OPT_DISABLEOFF 8
|
||||
#define OPT_ALWAYSSPCH 9
|
||||
#define OPT_SPEECHTYPE 10
|
||||
#define OPT_PIXPERFECT 11
|
||||
#define OPT_NOWALKMODE 12
|
||||
#define OPT_LETTERBOX 13
|
||||
#define OPT_FIXEDINVCURSOR 14
|
||||
#define OPT_NOLOSEINV 15
|
||||
#define OPT_HIRES_FONTS 16
|
||||
#define OPT_SPLITRESOURCES 17
|
||||
#define OPT_ROTATECHARS 18
|
||||
#define OPT_FADETYPE 19
|
||||
#define OPT_HANDLEINVCLICKS 20
|
||||
#define OPT_MOUSEWHEEL 21
|
||||
#define OPT_DIALOGNUMBERED 22
|
||||
#define OPT_DIALOGUPWARDS 23
|
||||
#define OPT_CROSSFADEMUSIC 24
|
||||
#define OPT_ANTIALIASFONTS 25
|
||||
#define OPT_THOUGHTGUI 26
|
||||
#define OPT_TURNTOFACELOC 27
|
||||
#define OPT_RIGHTLEFTWRITE 28 // right-to-left text writing
|
||||
#define OPT_DUPLICATEINV 29 // if they have 2 of the item, draw it twice
|
||||
#define OPT_SAVESCREENSHOT 30
|
||||
#define OPT_PORTRAITSIDE 31
|
||||
#define OPT_STRICTSCRIPTING 32 // don't allow MoveCharacter-style commands
|
||||
#define OPT_LEFTTORIGHTEVAL 33 // left-to-right operator evaluation
|
||||
#define OPT_COMPRESSSPRITES 34 // sprite compression type (None, RLE, LZW, Deflate)
|
||||
#define OPT_STRICTSTRINGS 35 // don't allow old-style strings, for reference only
|
||||
#define OPT_NEWGUIALPHA 36 // alpha blending method when drawing GUI and controls
|
||||
#define OPT_RUNGAMEDLGOPTS 37
|
||||
#define OPT_NATIVECOORDINATES 38 // defines coordinate relation between game logic and game screen
|
||||
#define OPT_GLOBALTALKANIMSPD 39
|
||||
#define OPT_HIGHESTOPTION_321 39
|
||||
#define OPT_SPRITEALPHA 40 // alpha blending method when drawing images on DrawingSurface
|
||||
#define OPT_SAFEFILEPATHS 41 // restricted file path in script (not writing to the game dir, etc)
|
||||
#define OPT_DIALOGOPTIONSAPI 42 // version of dialog options API (-1 for pre-3.4.0 API)
|
||||
#define OPT_BASESCRIPTAPI 43 // version of the Script API (ScriptAPIVersion) used to compile game script
|
||||
#define OPT_SCRIPTCOMPATLEV 44 // level of API compatibility (ScriptAPIVersion) used to compile game script
|
||||
#define OPT_RENDERATSCREENRES 45 // scale sprites at the (final) screen resolution
|
||||
#define OPT_RELATIVEASSETRES 46 // relative asset resolution mode (where sprites are resized to match game type)
|
||||
#define OPT_WALKSPEEDABSOLUTE 47 // if movement speeds are independent of walkable mask resolution
|
||||
#define OPT_CLIPGUICONTROLS 48 // clip drawn gui control contents to the control's rectangle
|
||||
#define OPT_GAMETEXTENCODING 49 // how the text in the game data should be interpreted
|
||||
#define OPT_KEYHANDLEAPI 50 // key handling mode (old/new)
|
||||
#define OPT_CUSTOMENGINETAG 51 // custom engine tag (for overriding behavior)
|
||||
#define OPT_SCALECHAROFFSETS 52 // apply character scaling to the sprite offsets (z, locked offs)
|
||||
#define OPT_HIGHESTOPTION OPT_SCALECHAROFFSETS
|
||||
#define OPT_NOMODMUSIC 98 // [DEPRECATED]
|
||||
#define OPT_LIPSYNCTEXT 99
|
||||
|
||||
#define CUSTOMENG_NONE 0
|
||||
#define CUSTOMENG_DRACONIAN 1 // Draconian Edition
|
||||
#define CUSTOMENG_CLIFFTOP 2 // Clifftop Games
|
||||
|
||||
// Sierra-style portrait position style
|
||||
#define PORTRAIT_LEFT 0
|
||||
#define PORTRAIT_RIGHT 1
|
||||
#define PORTRAIT_ALTERNATE 2
|
||||
#define PORTRAIT_XPOSITION 3
|
||||
|
||||
// Room transition style
|
||||
#define FADE_NORMAL 0
|
||||
#define FADE_INSTANT 1
|
||||
#define FADE_DISSOLVE 2
|
||||
#define FADE_BOXOUT 3
|
||||
#define FADE_CROSSFADE 4
|
||||
#define FADE_LAST 4 // this should equal the last one
|
||||
|
||||
// Legacy font flags
|
||||
//#define FFLG_LEGACY_NOSCALE 0x01 // TODO: is this from legacy format, ever used?
|
||||
#define FFLG_LEGACY_SIZEMASK 0x3f
|
||||
#define MAX_LEGACY_FONT_SIZE 63
|
||||
// Contemporary font flags
|
||||
#define FFLG_SIZEMULTIPLIER 0x01 // size data means multiplier
|
||||
#define FFLG_DEFLINESPACING 0x02 // linespacing derived from the font height
|
||||
// Font load flags, primarily for backward compatibility:
|
||||
// REPORTNOMINALHEIGHT: get_font_height should return nominal font's height,
|
||||
// eq to "font size" parameter, otherwise returns real pixel height.
|
||||
#define FFLG_REPORTNOMINALHEIGHT 0x04
|
||||
// ASCENDFIXUP: do the TTF ascender fixup, where font's ascender is resized
|
||||
// to the nominal font's height.
|
||||
#define FFLG_ASCENDERFIXUP 0x08
|
||||
// Collection of flags defining fully backward compatible TTF fixup
|
||||
#define FFLG_TTF_BACKCOMPATMASK (FFLG_REPORTNOMINALHEIGHT | FFLG_ASCENDERFIXUP)
|
||||
// Collection of flags defining font's load mode
|
||||
#define FFLG_LOADMODEMASK (FFLG_REPORTNOMINALHEIGHT | FFLG_ASCENDERFIXUP)
|
||||
// Font outline types
|
||||
#define FONT_OUTLINE_NONE -1
|
||||
#define FONT_OUTLINE_AUTO -10
|
||||
|
||||
#define DIALOG_OPTIONS_HIGHLIGHT_COLOR_DEFAULT 14 // Yellow
|
||||
|
||||
// MAXVIEWNAMELENGTH comes from unknown old engine version
|
||||
#define LEGACY_MAXVIEWNAMELENGTH 15
|
||||
#define MAXLIPSYNCFRAMES 20
|
||||
#define MAX_GUID_LENGTH 40
|
||||
#define MAX_SG_EXT_LENGTH 20
|
||||
#define LEGACY_MAX_SG_FOLDER_LEN 50
|
||||
|
||||
enum GameResolutionType {
|
||||
kGameResolution_Undefined = -1,
|
||||
// definition of 320x200 in very old versions of the engine (somewhere pre-2.56)
|
||||
kGameResolution_Default = 0,
|
||||
kGameResolution_320x200 = 1,
|
||||
kGameResolution_320x240 = 2,
|
||||
kGameResolution_640x400 = 3,
|
||||
kGameResolution_640x480 = 4,
|
||||
kGameResolution_800x600 = 5,
|
||||
kGameResolution_1024x768 = 6,
|
||||
kGameResolution_1280x720 = 7,
|
||||
kGameResolution_Custom = 8,
|
||||
kNumGameResolutions,
|
||||
|
||||
kGameResolution_LastLoRes = kGameResolution_320x240,
|
||||
kGameResolution_FirstHiRes = kGameResolution_640x400
|
||||
};
|
||||
|
||||
inline bool IsLegacyHiRes(GameResolutionType resolution) {
|
||||
return resolution > kGameResolution_LastLoRes;
|
||||
}
|
||||
|
||||
Size ResolutionTypeToSize(GameResolutionType resolution, bool letterbox = false);
|
||||
|
||||
// Automatic numbering of dialog options (OPT_DIALOGNUMBERED)
|
||||
enum DialogOptionNumbering {
|
||||
kDlgOptNoNumbering = -1,
|
||||
kDlgOptKeysOnly = 0, // implicit key shortcuts
|
||||
kDlgOptNumbering = 1 // draw option indices and use key shortcuts
|
||||
};
|
||||
|
||||
// Version of the script api (OPT_BASESCRIPTAPI and OPT_SCRIPTCOMPATLEV).
|
||||
// If the existing script function meaning had changed, that may be
|
||||
// possible to find out which implementation to use by checking one of those
|
||||
// two options.
|
||||
// NOTE: please remember that those values are valid only for games made with
|
||||
// 3.4.0 final and above.
|
||||
enum ScriptAPIVersion {
|
||||
kScriptAPI_Undefined = INT32_MIN,
|
||||
kScriptAPI_v321 = 0,
|
||||
kScriptAPI_v330 = 1,
|
||||
kScriptAPI_v334 = 2,
|
||||
kScriptAPI_v335 = 3,
|
||||
kScriptAPI_v340 = 4,
|
||||
kScriptAPI_v341 = 5,
|
||||
kScriptAPI_v350 = 6,
|
||||
kScriptAPI_v3507 = 7,
|
||||
kScriptAPI_v351 = 8,
|
||||
kScriptAPI_v360 = 3060000,
|
||||
kScriptAPI_v36026 = 3060026,
|
||||
kScriptAPI_v361 = 3060100,
|
||||
kScriptAPI_Current = kScriptAPI_v361
|
||||
};
|
||||
|
||||
extern const char *GetScriptAPIName(ScriptAPIVersion v);
|
||||
|
||||
// Determines whether the graphics renderer should scale sprites at the final
|
||||
// screen resolution, as opposed to native resolution
|
||||
enum RenderAtScreenRes {
|
||||
kRenderAtScreenRes_UserDefined = 0,
|
||||
kRenderAtScreenRes_Enabled = 1,
|
||||
kRenderAtScreenRes_Disabled = 2,
|
||||
};
|
||||
|
||||
// Method to use when blending two sprites with alpha channel
|
||||
enum GameSpriteAlphaRenderingStyle {
|
||||
kSpriteAlphaRender_Legacy = 0,
|
||||
kSpriteAlphaRender_Proper
|
||||
};
|
||||
|
||||
// Method to use when blending two GUI elements with alpha channel
|
||||
enum GameGuiAlphaRenderingStyle {
|
||||
kGuiAlphaRender_Legacy = 0,
|
||||
kGuiAlphaRender_AdditiveAlpha,
|
||||
kGuiAlphaRender_Proper
|
||||
};
|
||||
|
||||
// Sprite flags
|
||||
// SERIALIZATION NOTE: serialized as 8-bit in game data and legacy saves
|
||||
// serialized as 32-bit in new saves (for dynamic sprites only).
|
||||
#define SPF_HIRES 0x01 // sized for high native resolution (legacy option)
|
||||
#define SPF_HICOLOR 0x02 // is 16-bit (UNUSED)
|
||||
#define SPF_DYNAMICALLOC 0x04 // created by runtime script
|
||||
#define SPF_TRUECOLOR 0x08 // is 32-bit (UNUSED)
|
||||
#define SPF_ALPHACHANNEL 0x10 // has alpha-channel
|
||||
#define SPF_VAR_RESOLUTION 0x20 // variable resolution (refer to SPF_HIRES)
|
||||
#define SPF_HADALPHACHANNEL 0x80 // the saved sprite on disk has one
|
||||
#define SPF_OBJECTOWNED 0x0100 // owned by a game object (not created in user script)
|
||||
|
||||
// General information about sprite (properties, size)
|
||||
struct SpriteInfo {
|
||||
int Width = 0;
|
||||
int Height = 0;
|
||||
uint32_t Flags = 0u; // SPF_* flags
|
||||
|
||||
SpriteInfo() = default;
|
||||
SpriteInfo(int w, int h, uint32_t flags) : Width(w), Height(h), Flags(flags) {}
|
||||
|
||||
inline Size GetResolution() const { return Size(Width, Height); }
|
||||
// Gets if sprite is created at runtime (by engine, or a script command)
|
||||
inline bool IsDynamicSprite() const { return (Flags & SPF_DYNAMICALLOC) != 0; }
|
||||
|
||||
//
|
||||
// Legacy game support
|
||||
//
|
||||
// Gets if sprite should adjust its base size depending on game's resolution
|
||||
inline bool IsRelativeRes() const {
|
||||
return (Flags & SPF_VAR_RESOLUTION) != 0;
|
||||
}
|
||||
// Gets if sprite belongs to high resolution; hi-res sprites should be
|
||||
// downscaled in low-res games, and low-res sprites should be upscaled
|
||||
// in hi-res games
|
||||
inline bool IsLegacyHiRes() const {
|
||||
return (Flags & SPF_HIRES) != 0;
|
||||
}
|
||||
};
|
||||
|
||||
// Various font parameters, defining and extending font rendering behavior.
|
||||
// While FontRenderer object's main goal is to render single line of text at
|
||||
// the strictly determined position on canvas, FontInfo may additionally
|
||||
// provide instructions on adjusting drawing position, as well as arranging
|
||||
// multiple lines, and similar cases.
|
||||
struct FontInfo {
|
||||
enum AutoOutlineStyle : int {
|
||||
kSquared = 0,
|
||||
kRounded = 1,
|
||||
};
|
||||
|
||||
// General font's loading and rendering flags
|
||||
uint32_t Flags;
|
||||
// Nominal font import size (in pixels)
|
||||
int Size;
|
||||
// Factor to multiply base font size by
|
||||
int SizeMultiplier;
|
||||
// Outlining font index, or auto-outline flag
|
||||
int Outline;
|
||||
// Custom vertical render offset, used mainly for fixing broken fonts
|
||||
int YOffset;
|
||||
// Custom line spacing between two lines of text (0 = use font height)
|
||||
int LineSpacing;
|
||||
// When automatic outlining, thickness of the outline (0 = no auto outline)
|
||||
int AutoOutlineThickness;
|
||||
// When automatic outlining, style of the outline
|
||||
AutoOutlineStyle AutoOutlineStyle;
|
||||
|
||||
FontInfo();
|
||||
};
|
||||
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
175
engines/ags/shared/ac/game_version.h
Normal file
175
engines/ags/shared/ac/game_version.h
Normal file
@@ -0,0 +1,175 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// Game version constants and information
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef AGS_SHARED_AC_GAME_VERSION_H
|
||||
#define AGS_SHARED_AC_GAME_VERSION_H
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
/*
|
||||
|
||||
Game data versions and changes:
|
||||
-------------------------------
|
||||
|
||||
12 : 2.3 + 2.4
|
||||
|
||||
Versions above are incompatible at the moment.
|
||||
|
||||
18 : 2.5.0
|
||||
19 : 2.5.1 + 2.52
|
||||
20 : 2.5.3
|
||||
|
||||
Lip sync data added.
|
||||
21 : 2.5.4
|
||||
22 : 2.5.5
|
||||
|
||||
Variable number of sprites.
|
||||
24 : 2.5.6
|
||||
25 : 2.6.0
|
||||
|
||||
Encrypted global messages and dialogs.
|
||||
26 : 2.6.1
|
||||
|
||||
Wait() must be called with parameter > 0
|
||||
GetRegionAt() clips the input values to the screen size
|
||||
Color 0 now means transparent instead of black for text windows
|
||||
SetPlayerCharacter() does nothing if the new character is already the player character.
|
||||
27 : 2.6.2
|
||||
|
||||
Script modules. Fixes bug in the inventory display.
|
||||
Clickable GUI is selected with regard for the drawing order.
|
||||
Pointer to the "player" variable is now accessed via a dynamic object.
|
||||
31 : 2.7.0
|
||||
32 : 2.7.2
|
||||
|
||||
35 : 3.0.0
|
||||
|
||||
Room names are serialized when game is compiled in "debug" mode.
|
||||
36 : 3.0.1
|
||||
|
||||
Interactions are now scripts. The number for "not set" changed from 0 to -1 for
|
||||
a lot of variables (views, sounds).
|
||||
Deprecated switch between low-res and high-res native coordinates.
|
||||
37 : 3.1.0
|
||||
|
||||
Dialogs are now scripts. New character animation speed.
|
||||
39 : 3.1.1
|
||||
|
||||
Individual character speech animation speed.
|
||||
40 : 3.1.2
|
||||
|
||||
Audio clips
|
||||
41 : 3.2.0
|
||||
42 : 3.2.1
|
||||
|
||||
43 : 3.3.0
|
||||
Added few more game options.
|
||||
|
||||
44 : 3.3.1
|
||||
Added custom dialog option highlight colour.
|
||||
|
||||
45 : 3.4.0.1
|
||||
Support for custom game resolution.
|
||||
|
||||
46 : 3.4.0.2-.3
|
||||
Audio playback speed.
|
||||
Custom dialog option rendering extension.
|
||||
|
||||
47 : 3.4.0.4
|
||||
Custom properties changed at runtime.
|
||||
Ambient lighting
|
||||
|
||||
48 : 3.4.1
|
||||
OPT_RENDERATSCREENRES, extended engine caps check, font vertical offset.
|
||||
|
||||
49 : 3.4.1.2
|
||||
Font custom line spacing.
|
||||
|
||||
50 : 3.5.0.8
|
||||
Sprites have "real" resolution. Expanded FontInfo data format.
|
||||
Option to allow legacy relative asset resolutions.
|
||||
|
||||
3.6.0 :
|
||||
Format value is defined as AGS version represented as NN,NN,NN,NN.
|
||||
Fonts have adjustable outline
|
||||
3.6.0.11:
|
||||
New font load flags, control backward compatible font behavior
|
||||
3.6.0.16:
|
||||
Idle animation speed, modifiable hotspot names, fixed video frame
|
||||
3.6.0.21:
|
||||
Some adjustments to gui text alignment.
|
||||
3.6.1:
|
||||
In RTL mode all text is reversed, not only wrappable (labels etc).
|
||||
3.6.1.10:
|
||||
Disabled automatic SetRestartPoint.
|
||||
3.6.1.14:
|
||||
Extended game object names, resolving hard length limits.
|
||||
*/
|
||||
|
||||
enum GameDataVersion {
|
||||
kGameVersion_Undefined = 0,
|
||||
kGameVersion_230 = 12,
|
||||
kGameVersion_240 = 12,
|
||||
kGameVersion_250 = 18,
|
||||
kGameVersion_251 = 19, // same as 2.52
|
||||
kGameVersion_253 = 20,
|
||||
kGameVersion_254 = 21,
|
||||
kGameVersion_255 = 22,
|
||||
kGameVersion_256 = 24,
|
||||
kGameVersion_260 = 25,
|
||||
kGameVersion_261 = 26,
|
||||
kGameVersion_262 = 27,
|
||||
kGameVersion_270 = 31,
|
||||
kGameVersion_272 = 32,
|
||||
kGameVersion_300 = 35,
|
||||
kGameVersion_301 = 36,
|
||||
kGameVersion_310 = 37,
|
||||
kGameVersion_311 = 39,
|
||||
kGameVersion_312 = 40,
|
||||
kGameVersion_320 = 41,
|
||||
kGameVersion_321 = 42,
|
||||
kGameVersion_330 = 43,
|
||||
kGameVersion_331 = 44,
|
||||
kGameVersion_340_1 = 45,
|
||||
kGameVersion_340_2 = 46,
|
||||
kGameVersion_340_4 = 47,
|
||||
kGameVersion_341 = 48,
|
||||
kGameVersion_341_2 = 49,
|
||||
kGameVersion_350 = 50,
|
||||
kGameVersion_360 = 3060000,
|
||||
kGameVersion_360_11 = 3060011,
|
||||
kGameVersion_360_16 = 3060016,
|
||||
kGameVersion_360_21 = 3060021,
|
||||
kGameVersion_361 = 3060100,
|
||||
kGameVersion_361_10 = 3060110,
|
||||
kGameVersion_361_14 = 3060114,
|
||||
kGameVersion_Current = kGameVersion_361_14
|
||||
};
|
||||
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
47
engines/ags/shared/ac/interface_button.h
Normal file
47
engines/ags/shared/ac/interface_button.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/* 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 AGS_SHARED_AC_INTERFACE_BUTTON_H
|
||||
#define AGS_SHARED_AC_INTERFACE_BUTTON_H
|
||||
|
||||
#include "ags/shared/core/types.h"
|
||||
|
||||
#if defined (OBSOLETE)
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
#define MAXBUTTON 20
|
||||
#define IBFLG_ENABLED 1
|
||||
#define IBFLG_INVBOX 2
|
||||
|
||||
struct InterfaceButton {
|
||||
int x, y, pic, overpic, pushpic, leftclick;
|
||||
int rightclick; // if inv, then leftclick = wid, rightclick = hit
|
||||
int reserved_for_future;
|
||||
int8 flags;
|
||||
void set(int xx, int yy, int picc, int overpicc, int actionn);
|
||||
};
|
||||
|
||||
} // namespace AGS3
|
||||
|
||||
#endif // OBSOLETE
|
||||
|
||||
#endif
|
||||
51
engines/ags/shared/ac/interface_element.h
Normal file
51
engines/ags/shared/ac/interface_element.h
Normal 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 AGS_SHARED_AC_INTERFACE_ELEMENT_H
|
||||
#define AGS_SHARED_AC_INTERFACE_ELEMENT_H
|
||||
|
||||
#if defined (OBSOLETE)
|
||||
|
||||
#include "ags/shared/ac/interface_button.h" // InterfaceButton
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
// this struct should go in a Game struct, not the room structure.
|
||||
struct InterfaceElement {
|
||||
int x, y, x2, y2;
|
||||
int bgcol, fgcol, bordercol;
|
||||
int vtextxp, vtextyp, vtextalign; // X & Y relative to topleft of interface
|
||||
char vtext[40];
|
||||
int numbuttons;
|
||||
InterfaceButton button[MAXBUTTON];
|
||||
int flags;
|
||||
int reserved_for_future;
|
||||
int popupyp; // pops up when _G(mousey) < this
|
||||
int8 popup; // does it pop up? (like sierra icon bar)
|
||||
int8 on;
|
||||
InterfaceElement();
|
||||
};
|
||||
|
||||
} // namespace AGS3
|
||||
|
||||
#endif // OBSOLETE
|
||||
|
||||
#endif
|
||||
66
engines/ags/shared/ac/inventory_item_info.cpp
Normal file
66
engines/ags/shared/ac/inventory_item_info.cpp
Normal file
@@ -0,0 +1,66 @@
|
||||
/* 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 "ags/shared/ac/inventory_item_info.h"
|
||||
#include "ags/shared/util/stream.h"
|
||||
#include "ags/shared/util/string_utils.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
using namespace AGS::Shared;
|
||||
|
||||
void InventoryItemInfo::ReadFromFile(Stream *in) {
|
||||
name.ReadCount(in, LEGACY_MAX_INVENTORY_NAME_LENGTH);
|
||||
in->Seek(3); // alignment padding to int32
|
||||
pic = in->ReadInt32();
|
||||
cursorPic = in->ReadInt32();
|
||||
hotx = in->ReadInt32();
|
||||
hoty = in->ReadInt32();
|
||||
in->ReadArrayOfInt32(reserved, 5);
|
||||
flags = in->ReadInt8();
|
||||
in->Seek(3); // alignment padding to int32
|
||||
}
|
||||
|
||||
void InventoryItemInfo::WriteToFile(Stream *out) {
|
||||
name.WriteCount(out, LEGACY_MAX_INVENTORY_NAME_LENGTH);
|
||||
out->WriteByteCount(0, 3); // alignment padding to int32
|
||||
out->WriteInt32(pic);
|
||||
out->WriteInt32(cursorPic);
|
||||
out->WriteInt32(hotx);
|
||||
out->WriteInt32(hoty);
|
||||
out->WriteArrayOfInt32(reserved, 5);
|
||||
out->WriteInt8(flags);
|
||||
out->WriteByteCount(0, 3); // alignment padding to int32
|
||||
}
|
||||
|
||||
void InventoryItemInfo::ReadFromSavegame(Stream *in) {
|
||||
name = StrUtil::ReadString(in);
|
||||
pic = in->ReadInt32();
|
||||
cursorPic = in->ReadInt32();
|
||||
}
|
||||
|
||||
void InventoryItemInfo::WriteToSavegame(Stream *out) const {
|
||||
StrUtil::WriteString(name, out);
|
||||
out->WriteInt32(pic);
|
||||
out->WriteInt32(cursorPic);
|
||||
}
|
||||
|
||||
} // namespace AGS3
|
||||
56
engines/ags/shared/ac/inventory_item_info.h
Normal file
56
engines/ags/shared/ac/inventory_item_info.h
Normal file
@@ -0,0 +1,56 @@
|
||||
/* 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 AGS_SHARED_AC_INVENTORY_ITEM_INFO_H
|
||||
#define AGS_SHARED_AC_INVENTORY_ITEM_INFO_H
|
||||
|
||||
#include "ags/shared/core/types.h"
|
||||
#include "ags/shared/util/string.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
namespace AGS {
|
||||
namespace Shared {
|
||||
class Stream;
|
||||
} // namespace Shared
|
||||
} // namespace AGS
|
||||
|
||||
using namespace AGS::Shared;
|
||||
|
||||
#define IFLG_STARTWITH 1
|
||||
#define LEGACY_MAX_INVENTORY_NAME_LENGTH 25
|
||||
|
||||
struct InventoryItemInfo {
|
||||
String name;
|
||||
int pic;
|
||||
int cursorPic, hotx, hoty;
|
||||
int32_t reserved[5];
|
||||
uint8_t flags; // IFLG_STARTWITH
|
||||
|
||||
void ReadFromFile(Stream *in);
|
||||
void WriteToFile(Stream *out);
|
||||
void ReadFromSavegame(Stream *in);
|
||||
void WriteToSavegame(Stream *out) const;
|
||||
};
|
||||
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
39
engines/ags/shared/ac/keycode.cpp
Normal file
39
engines/ags/shared/ac/keycode.cpp
Normal file
@@ -0,0 +1,39 @@
|
||||
/* 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 "ags/shared/ac/keycode.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
eAGSKeyCode AGSKeyToScriptKey(eAGSKeyCode keycode) {
|
||||
// Script API requires strictly capital letters, if this is a small letter - capitalize it
|
||||
return (keycode >= 'a' && keycode <= 'z') ?
|
||||
static_cast<eAGSKeyCode>(keycode - 'a' + 'A') : keycode;
|
||||
}
|
||||
|
||||
char AGSKeyToText(eAGSKeyCode keycode) {
|
||||
// support only printable characters (128-255 are chars from extended fonts)
|
||||
if (keycode >= 32 && keycode < 256)
|
||||
return static_cast<char>(keycode);
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace AGS3
|
||||
323
engines/ags/shared/ac/keycode.h
Normal file
323
engines/ags/shared/ac/keycode.h
Normal file
@@ -0,0 +1,323 @@
|
||||
/* 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 AGS_SHARED_AC_KEYCODE_H
|
||||
#define AGS_SHARED_AC_KEYCODE_H
|
||||
|
||||
#include "ags/shared/core/platform.h"
|
||||
#include "ags/shared/core/types.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
#define EXTENDED_KEY_CODE ('\0')
|
||||
#define EXTENDED_KEY_CODE_MACOS ('?')
|
||||
|
||||
// Constant used to define Alt+Key codes
|
||||
#define AGS_EXT_KEY_SHIFT 300
|
||||
#define AGS_EXT_KEY_ALPHA(key) (AGS_EXT_KEY_SHIFT + (key - eAGSKeyCodeCtrlA) + 1)
|
||||
|
||||
// These are based on eKeyCode values in AGS Script.
|
||||
// The actual values are based on scan codes of the old backend (allegro 3 and/or 4),
|
||||
// which in turn mostly match ASCII values (at least for ones below 128), including
|
||||
// Ctrl + letter combination codes.
|
||||
// More codes are added at much higher ranges, for example Alt + letter combo codes
|
||||
// are defined as 300 + letter's order.
|
||||
// It should be specifically noted that eAGSKeyCode is directly conversible to ASCII
|
||||
// at the range of 1 - 128, and AGS script makes use of this.
|
||||
// Another important thing to note is that letter codes are always sent into script
|
||||
// callbacks (like "on_key_pressed") in capitalized form, and that's how they are
|
||||
// declared in script API (that's why in these callbacks user would have to check
|
||||
// the Shift key state if they want to know if it's A or Shift + A).
|
||||
enum eAGSKeyCode {
|
||||
eAGSKeyCodeNone = 0,
|
||||
|
||||
eAGSKeyCodeCtrlA = 1,
|
||||
eAGSKeyCodeCtrlB = 2,
|
||||
eAGSKeyCodeCtrlC = 3,
|
||||
eAGSKeyCodeCtrlD = 4,
|
||||
eAGSKeyCodeCtrlE = 5,
|
||||
eAGSKeyCodeCtrlF = 6,
|
||||
eAGSKeyCodeCtrlG = 7,
|
||||
eAGSKeyCodeCtrlH = 8,
|
||||
eAGSKeyCodeCtrlI = 9,
|
||||
eAGSKeyCodeCtrlJ = 10,
|
||||
eAGSKeyCodeCtrlK = 11,
|
||||
eAGSKeyCodeCtrlL = 12,
|
||||
eAGSKeyCodeCtrlM = 13,
|
||||
eAGSKeyCodeCtrlN = 14,
|
||||
eAGSKeyCodeCtrlO = 15,
|
||||
eAGSKeyCodeCtrlP = 16,
|
||||
eAGSKeyCodeCtrlQ = 17,
|
||||
eAGSKeyCodeCtrlR = 18,
|
||||
eAGSKeyCodeCtrlS = 19,
|
||||
eAGSKeyCodeCtrlT = 20,
|
||||
eAGSKeyCodeCtrlU = 21,
|
||||
eAGSKeyCodeCtrlV = 22,
|
||||
eAGSKeyCodeCtrlW = 23,
|
||||
eAGSKeyCodeCtrlX = 24,
|
||||
eAGSKeyCodeCtrlY = 25,
|
||||
eAGSKeyCodeCtrlZ = 26,
|
||||
|
||||
eAGSKeyCodeBackspace = 8, // matches Ctrl + H
|
||||
eAGSKeyCodeTab = 9, // matches Ctrl + I
|
||||
eAGSKeyCodeReturn = 13, // matches Ctrl + M
|
||||
eAGSKeyCodeEscape = 27,
|
||||
|
||||
/* printable chars - from eAGSKeyCodeSpace to eAGSKeyCode_z */
|
||||
eAGSKeyCodeSpace = 32,
|
||||
eAGSKeyCodeExclamationMark = 33,
|
||||
eAGSKeyCodeDoubleQuote = 34,
|
||||
eAGSKeyCodeHash = 35,
|
||||
eAGSKeyCodeDollar = 36,
|
||||
eAGSKeyCodePercent = 37,
|
||||
eAGSKeyCodeAmpersand = 38,
|
||||
eAGSKeyCodeSingleQuote = 39,
|
||||
eAGSKeyCodeOpenParenthesis = 40,
|
||||
eAGSKeyCodeCloseParenthesis = 41,
|
||||
eAGSKeyCodeAsterisk = 42,
|
||||
eAGSKeyCodePlus = 43,
|
||||
eAGSKeyCodeComma = 44,
|
||||
eAGSKeyCodeHyphen = 45,
|
||||
eAGSKeyCodePeriod = 46,
|
||||
eAGSKeyCodeForwardSlash = 47,
|
||||
|
||||
eAGSKeyCode0 = 48,
|
||||
eAGSKeyCode1 = 49,
|
||||
eAGSKeyCode2 = 50,
|
||||
eAGSKeyCode3 = 51,
|
||||
eAGSKeyCode4 = 52,
|
||||
eAGSKeyCode5 = 53,
|
||||
eAGSKeyCode6 = 54,
|
||||
eAGSKeyCode7 = 55,
|
||||
eAGSKeyCode8 = 56,
|
||||
eAGSKeyCode9 = 57,
|
||||
|
||||
eAGSKeyCodeColon = 58,
|
||||
eAGSKeyCodeSemiColon = 59,
|
||||
eAGSKeyCodeLessThan = 60,
|
||||
eAGSKeyCodeEquals = 61,
|
||||
eAGSKeyCodeGreaterThan = 62,
|
||||
eAGSKeyCodeQuestionMark = 63,
|
||||
eAGSKeyCodeAt = 64, // '@'
|
||||
|
||||
/* Notice that default letter codes match capital ASCII letters */
|
||||
eAGSKeyCodeA = 65, // 'A'
|
||||
eAGSKeyCodeB = 66, // 'B', etc
|
||||
eAGSKeyCodeC = 67,
|
||||
eAGSKeyCodeD = 68,
|
||||
eAGSKeyCodeE = 69,
|
||||
eAGSKeyCodeF = 70,
|
||||
eAGSKeyCodeG = 71,
|
||||
eAGSKeyCodeH = 72,
|
||||
eAGSKeyCodeI = 73,
|
||||
eAGSKeyCodeJ = 74,
|
||||
eAGSKeyCodeK = 75,
|
||||
eAGSKeyCodeL = 76,
|
||||
eAGSKeyCodeM = 77,
|
||||
eAGSKeyCodeN = 78,
|
||||
eAGSKeyCodeO = 79,
|
||||
eAGSKeyCodeP = 80,
|
||||
eAGSKeyCodeQ = 81,
|
||||
eAGSKeyCodeR = 82,
|
||||
eAGSKeyCodeS = 83,
|
||||
eAGSKeyCodeT = 84,
|
||||
eAGSKeyCodeU = 85,
|
||||
eAGSKeyCodeV = 86,
|
||||
eAGSKeyCodeW = 87,
|
||||
eAGSKeyCodeX = 88,
|
||||
eAGSKeyCodeY = 89,
|
||||
eAGSKeyCodeZ = 90, // 'Z'
|
||||
|
||||
eAGSKeyCodeOpenBracket = 91,
|
||||
eAGSKeyCodeBackSlash = 92,
|
||||
eAGSKeyCodeCloseBracket = 93,
|
||||
eAGSKeyCodeCaret = 94, // '^'
|
||||
eAGSKeyCodeUnderscore = 95,
|
||||
eAGSKeyCodeBackquote = 96, // '`'
|
||||
|
||||
/* Small ASCII letter codes are declared here for consistency, but unused in script callbacks */
|
||||
eAGSKeyCode_a = 97, // 'a'
|
||||
eAGSKeyCode_b = 98, // 'b', etc
|
||||
eAGSKeyCode_c = 99,
|
||||
eAGSKeyCode_d = 100,
|
||||
eAGSKeyCode_e = 101,
|
||||
eAGSKeyCode_f = 102,
|
||||
eAGSKeyCode_g = 103,
|
||||
eAGSKeyCode_h = 104,
|
||||
eAGSKeyCode_i = 105,
|
||||
eAGSKeyCode_j = 106,
|
||||
eAGSKeyCode_k = 107,
|
||||
eAGSKeyCode_l = 108,
|
||||
eAGSKeyCode_m = 109,
|
||||
eAGSKeyCode_n = 110,
|
||||
eAGSKeyCode_o = 111,
|
||||
eAGSKeyCode_p = 112,
|
||||
eAGSKeyCode_q = 113,
|
||||
eAGSKeyCode_r = 114,
|
||||
eAGSKeyCode_s = 115,
|
||||
eAGSKeyCode_t = 116,
|
||||
eAGSKeyCode_u = 117,
|
||||
eAGSKeyCode_v = 118,
|
||||
eAGSKeyCode_w = 119,
|
||||
eAGSKeyCode_x = 120,
|
||||
eAGSKeyCode_y = 121,
|
||||
eAGSKeyCode_z = 122, // 'z'
|
||||
|
||||
/* extended symbol codes */
|
||||
eAGSKeyCodeF1 = AGS_EXT_KEY_SHIFT + 59,
|
||||
eAGSKeyCodeF2 = AGS_EXT_KEY_SHIFT + 60,
|
||||
eAGSKeyCodeF3 = AGS_EXT_KEY_SHIFT + 61,
|
||||
eAGSKeyCodeF4 = AGS_EXT_KEY_SHIFT + 62,
|
||||
eAGSKeyCodeF5 = AGS_EXT_KEY_SHIFT + 63,
|
||||
eAGSKeyCodeF6 = AGS_EXT_KEY_SHIFT + 64,
|
||||
eAGSKeyCodeF7 = AGS_EXT_KEY_SHIFT + 65,
|
||||
eAGSKeyCodeF8 = AGS_EXT_KEY_SHIFT + 66,
|
||||
eAGSKeyCodeF9 = AGS_EXT_KEY_SHIFT + 67,
|
||||
eAGSKeyCodeF10 = AGS_EXT_KEY_SHIFT + 68,
|
||||
eAGSKeyCodeF11 = AGS_EXT_KEY_SHIFT + 133,
|
||||
eAGSKeyCodeF12 = AGS_EXT_KEY_SHIFT + 134,
|
||||
|
||||
eAGSKeyCodeHome = AGS_EXT_KEY_SHIFT + 71,
|
||||
eAGSKeyCodeUpArrow = AGS_EXT_KEY_SHIFT + 72,
|
||||
eAGSKeyCodePageUp = AGS_EXT_KEY_SHIFT + 73,
|
||||
eAGSKeyCodeLeftArrow = AGS_EXT_KEY_SHIFT + 75,
|
||||
eAGSKeyCodeNumPad5 = AGS_EXT_KEY_SHIFT + 76,
|
||||
eAGSKeyCodeRightArrow = AGS_EXT_KEY_SHIFT + 77,
|
||||
eAGSKeyCodeEnd = AGS_EXT_KEY_SHIFT + 79,
|
||||
eAGSKeyCodeDownArrow = AGS_EXT_KEY_SHIFT + 80,
|
||||
eAGSKeyCodePageDown = AGS_EXT_KEY_SHIFT + 81,
|
||||
eAGSKeyCodeInsert = AGS_EXT_KEY_SHIFT + 82,
|
||||
eAGSKeyCodeDelete = AGS_EXT_KEY_SHIFT + 83,
|
||||
|
||||
// [sonneveld] These are only used by debugging and abort keys.
|
||||
// They're based on allegro4 codes ...
|
||||
eAGSKeyCodeAltV = AGS_EXT_KEY_ALPHA(eAGSKeyCodeV),
|
||||
eAGSKeyCodeAltX = AGS_EXT_KEY_ALPHA(eAGSKeyCodeX),
|
||||
eAGSKeyCodeAltY = AGS_EXT_KEY_ALPHA(eAGSKeyCodeY),
|
||||
eAGSKeyCodeAltZ = AGS_EXT_KEY_ALPHA(eAGSKeyCodeZ),
|
||||
|
||||
// The beginning of "service key list": mod keys and other special keys
|
||||
// not normally intended to affect the default game logic
|
||||
eAGSKeyCode_FirstServiceKey = 391,
|
||||
|
||||
// not certain if necessary anymore (and not certain what was the origin of this value)
|
||||
eAGSKeyCodeAltTab = AGS_EXT_KEY_SHIFT + 99,
|
||||
|
||||
// Mod-key codes
|
||||
// *probably* made-up numbers, not derived from allegro scan codes.
|
||||
eAGSKeyCodeLShift = 403,
|
||||
eAGSKeyCodeRShift = 404,
|
||||
eAGSKeyCodeLCtrl = 405,
|
||||
eAGSKeyCodeRCtrl = 406,
|
||||
eAGSKeyCodeLAlt = 407,
|
||||
|
||||
// [sonneveld]
|
||||
// The following are the AGS_EXT_KEY_SHIFT, derived from applying arithmetic to the original keycodes.
|
||||
// These do not have a corresponding ags key enum, do not appear in the manual and may not be accessible because of OS contraints.
|
||||
eAGSKeyCodeRAlt = 420,
|
||||
// TODO: judging that above works (at least on Win), following might also work,
|
||||
// but idk which ones may be necessary; still keeping here this excerpt from an old code
|
||||
// if they'd want to be restored (also add them to script API then!).
|
||||
// Also see allegro 4's keyboard.h, where these were declared.
|
||||
/*
|
||||
case 392: __allegro_KEY_PRTSCR
|
||||
case 393: __allegro_KEY_PAUSE
|
||||
case 394: __allegro_KEY_ABNT_C1 // The ABNT_C1 (Brazilian) key
|
||||
case 395: __allegro_KEY_YEN)
|
||||
case 396: __allegro_KEY_KANA
|
||||
case 397: __allegro_KEY_CONVERT
|
||||
case 398: __allegro_KEY_NOCONVERT
|
||||
case 400: __allegro_KEY_CIRCUMFLEX
|
||||
case 402: __allegro_KEY_KANJI
|
||||
case 421: __allegro_KEY_LWIN
|
||||
case 422: __allegro_KEY_RWIN
|
||||
case 423: __allegro_KEY_MENU
|
||||
case 424: __allegro_KEY_SCRLOCK
|
||||
case 425: __allegro_KEY_NUMLOCK
|
||||
case 426: __allegro_KEY_CAPSLOCK
|
||||
*/
|
||||
|
||||
// Mask defines the key code position if packed in the int32;
|
||||
// takes only 12 bits, as minimal necessary to accommodate historical codes.
|
||||
eAGSKeyMask = 0x0FFF
|
||||
};
|
||||
|
||||
// AGS key modifiers
|
||||
enum eAGSKeyMod {
|
||||
eAGSModLShift = 0x00010000,
|
||||
eAGSModRShift = 0x00020000,
|
||||
eAGSModLCtrl = 0x00040000,
|
||||
eAGSModRCtrl = 0x00080000,
|
||||
eAGSModLAlt = 0x00100000,
|
||||
eAGSModRAlt = 0x00200000,
|
||||
eAGSModNum = 0x00400000,
|
||||
eAGSModCaps = 0x00800000,
|
||||
|
||||
// Mask defines the key mod position if packed in the int32;
|
||||
// the upper 8 bits are reserved for "input type" codes;
|
||||
// potentially may take 4 bits below (4th pos), as KeyMask takes only 12.
|
||||
eAGSModMask = 0x00FF0000
|
||||
};
|
||||
|
||||
// Combined key code and a textual representation in UTF-8
|
||||
struct KeyInput {
|
||||
const static size_t UTF8_ARR_SIZE = 5;
|
||||
|
||||
eAGSKeyCode Key = eAGSKeyCodeNone; // actual key code
|
||||
eAGSKeyCode CompatKey = eAGSKeyCodeNone; // old-style key code, combined with mods
|
||||
int Mod = 0; // key modifiers
|
||||
int UChar = 0; // full character value (supports unicode)
|
||||
char Text[UTF8_ARR_SIZE]{}; // character in a string format
|
||||
|
||||
KeyInput() = default;
|
||||
};
|
||||
|
||||
// AGS own mouse button codes;
|
||||
// These correspond to MouseButton enum in script and plugin API (sans special values)
|
||||
enum eAGSMouseButton
|
||||
{
|
||||
kMouseNone = 0,
|
||||
kMouseLeft = 1,
|
||||
kMouseRight = 2,
|
||||
kMouseMiddle = 3,
|
||||
kNumMouseButtons
|
||||
};
|
||||
|
||||
// Tells if the AGS keycode refers to the modifier key (ctrl, alt, etc)
|
||||
inline bool IsAGSModKey(eAGSKeyCode keycode) {
|
||||
return (keycode >= eAGSKeyCodeLShift && keycode <= eAGSKeyCodeLAlt) || keycode == eAGSKeyCodeRAlt;
|
||||
}
|
||||
|
||||
// Tells if the AGS keycode refers to the service key (modifier, PrintScreen and similar);
|
||||
// this lets distinct keys that normally should not affect the game
|
||||
inline bool IsAGSServiceKey(eAGSKeyCode keycode) {
|
||||
return keycode >= eAGSKeyCode_FirstServiceKey;
|
||||
}
|
||||
|
||||
// Converts eAGSKeyCode to script API code, for "on_key_press" and similar callbacks
|
||||
eAGSKeyCode AGSKeyToScriptKey(eAGSKeyCode keycode);
|
||||
// Converts eAGSKeyCode to ASCII text representation with the range check; returns 0 on failure
|
||||
// Not unicode compatible.
|
||||
char AGSKeyToText(eAGSKeyCode keycode);
|
||||
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
80
engines/ags/shared/ac/mouse_cursor.cpp
Normal file
80
engines/ags/shared/ac/mouse_cursor.cpp
Normal file
@@ -0,0 +1,80 @@
|
||||
/* 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 "ags/shared/ac/mouse_cursor.h"
|
||||
#include "ags/shared/util/stream.h"
|
||||
#include "ags/shared/util/string_utils.h"
|
||||
#include "common/util.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
using namespace AGS::Shared;
|
||||
|
||||
void MouseCursor::clear() {
|
||||
pic = 0;
|
||||
hotx = hoty = 0;
|
||||
view = -1;
|
||||
name.Empty();
|
||||
flags = 0;
|
||||
}
|
||||
|
||||
void MouseCursor::ReadFromFile(Stream *in) {
|
||||
pic = in->ReadInt32();
|
||||
hotx = in->ReadInt16();
|
||||
hoty = in->ReadInt16();
|
||||
view = in->ReadInt16();
|
||||
StrUtil::ReadCStrCount(legacy_name, in, LEGACY_MAX_CURSOR_NAME_LENGTH);
|
||||
flags = in->ReadInt8();
|
||||
in->Seek(3); // alignment padding to int32
|
||||
|
||||
name = legacy_name;
|
||||
}
|
||||
|
||||
void MouseCursor::WriteToFile(Stream *out) {
|
||||
out->WriteInt32(pic);
|
||||
out->WriteInt16(hotx);
|
||||
out->WriteInt16(hoty);
|
||||
out->WriteInt16(view);
|
||||
out->Write(legacy_name, LEGACY_MAX_CURSOR_NAME_LENGTH);
|
||||
out->WriteInt8(flags);
|
||||
out->WriteByteCount(0, 3); // alignment padding to int32
|
||||
}
|
||||
|
||||
void MouseCursor::ReadFromSavegame(Stream *in, int cmp_ver) {
|
||||
pic = in->ReadInt32();
|
||||
hotx = static_cast<int16_t>(in->ReadInt32());
|
||||
hoty = static_cast<int16_t>(in->ReadInt32());
|
||||
view = static_cast<int16_t>(in->ReadInt32());
|
||||
flags = static_cast<int8_t>(in->ReadInt32());
|
||||
if (cmp_ver >= kCursorSvgVersion_36016)
|
||||
animdelay = in->ReadInt32();
|
||||
}
|
||||
|
||||
void MouseCursor::WriteToSavegame(Stream *out) const {
|
||||
out->WriteInt32(pic);
|
||||
out->WriteInt32(hotx);
|
||||
out->WriteInt32(hoty);
|
||||
out->WriteInt32(view);
|
||||
out->WriteInt32(flags);
|
||||
out->WriteInt32(animdelay);
|
||||
}
|
||||
|
||||
} // namespace AGS3
|
||||
76
engines/ags/shared/ac/mouse_cursor.h
Normal file
76
engines/ags/shared/ac/mouse_cursor.h
Normal file
@@ -0,0 +1,76 @@
|
||||
/* 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 AGS_SHARED_AC_MOUSE_CURSOR_H
|
||||
#define AGS_SHARED_AC_MOUSE_CURSOR_H
|
||||
|
||||
#include "ags/shared/core/types.h"
|
||||
#include "ags/shared/util/string.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
namespace AGS {
|
||||
namespace Shared {
|
||||
class Stream;
|
||||
} // namespace Shared
|
||||
} // namespace AGS
|
||||
|
||||
using namespace AGS; // FIXME later
|
||||
|
||||
#define MCF_ANIMMOVE 1
|
||||
#define MCF_DISABLED 2
|
||||
#define MCF_STANDARD 4
|
||||
#define MCF_HOTSPOT 8 // only animate when over hotspot
|
||||
|
||||
#define LEGACY_MAX_CURSOR_NAME_LENGTH 10
|
||||
|
||||
enum CursorSvgVersion {
|
||||
kCursorSvgVersion_Initial = 0,
|
||||
kCursorSvgVersion_36016 = 1, // animation delay
|
||||
};
|
||||
|
||||
// IMPORTANT: exposed to plugin API as AGSCursor!
|
||||
// do not change topmost fields, unless planning breaking compatibility.
|
||||
struct MouseCursor {
|
||||
int pic = 0;
|
||||
short hotx = 0, hoty = 0;
|
||||
short view = -1;
|
||||
// This is a deprecated name field, but must stay here for compatibility
|
||||
// with the plugin API (unless the plugin interface is reworked)
|
||||
char legacy_name[LEGACY_MAX_CURSOR_NAME_LENGTH]{};
|
||||
char flags = 0;
|
||||
|
||||
// Following fields are not part of the plugin API
|
||||
Shared::String name;
|
||||
int animdelay = 5;
|
||||
|
||||
MouseCursor() {}
|
||||
|
||||
void clear();
|
||||
void ReadFromFile(Shared::Stream *in);
|
||||
void WriteToFile(Shared::Stream *out);
|
||||
void ReadFromSavegame(Shared::Stream *in, int cmp_ver);
|
||||
void WriteToSavegame(Shared::Stream *out) const;
|
||||
};
|
||||
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
88
engines/ags/shared/ac/old_game_setup_struct.h
Normal file
88
engines/ags/shared/ac/old_game_setup_struct.h
Normal file
@@ -0,0 +1,88 @@
|
||||
/* 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 AGS_SHARED_AC_OLD_GAME_SETUP_STRUCT_H
|
||||
#define AGS_SHARED_AC_OLD_GAME_SETUP_STRUCT_H
|
||||
|
||||
#if defined (OBSOLETE)
|
||||
|
||||
#include "ags/shared/ac/character_info.h" // OldCharacterInfo, CharacterInfo
|
||||
#include "ags/shared/ac/event_block.h" // EventBlock
|
||||
#include "ags/shared/ac/interface_element.h" // InterfaceElement
|
||||
#include "ags/shared/ac/inventory_item_info.h" // InventoryItemInfo
|
||||
#include "ags/shared/ac/mouse_cursor.h" // MouseCursor
|
||||
#include "ags/shared/ac/words_dictionary.h" // WordsDictionary
|
||||
#include "ags/shared/script/cc_script.h" // ccScript
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
struct OriGameSetupStruct {
|
||||
char gamename[30];
|
||||
int8 options[20];
|
||||
unsigned char paluses[256];
|
||||
RGB defpal[256];
|
||||
InterfaceElement iface[10];
|
||||
int numiface;
|
||||
int numviews;
|
||||
MouseCursor mcurs[10];
|
||||
char *globalscript;
|
||||
int numcharacters;
|
||||
OldCharacterInfo *chars;
|
||||
#if defined (OBSOLETE)
|
||||
EventBlock __charcond[50];
|
||||
EventBlock __invcond[100];
|
||||
#endif
|
||||
ccScript *compiled_script;
|
||||
int playercharacter;
|
||||
unsigned char __old_spriteflags[2100];
|
||||
int totalscore;
|
||||
short numinvitems;
|
||||
InventoryItemInfo invinfo[100];
|
||||
int numdialog, numdlgmessage;
|
||||
int numfonts;
|
||||
int color_depth; // in bytes per pixel (ie. 1 or 2)
|
||||
int target_win;
|
||||
int dialog_bullet; // 0 for none, otherwise slot num of bullet point
|
||||
short hotdot, hotdotouter; // inv cursor hotspot dot
|
||||
int uniqueid; // random key identifying the game
|
||||
int reserved[2];
|
||||
short numlang;
|
||||
char langcodes[MAXLANGUAGE][3];
|
||||
char *messages[MAXGLOBALMES];
|
||||
};
|
||||
|
||||
struct OriGameSetupStruct2 : public OriGameSetupStruct {
|
||||
unsigned char fontflags[10];
|
||||
int8 fontoutline[10];
|
||||
int numgui;
|
||||
WordsDictionary *dict;
|
||||
int reserved2[8];
|
||||
};
|
||||
|
||||
struct OldGameSetupStruct : public OriGameSetupStruct2 {
|
||||
unsigned char spriteflags[LEGACY_MAX_SPRITES_V25];
|
||||
};
|
||||
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
463
engines/ags/shared/ac/sprite_cache.cpp
Normal file
463
engines/ags/shared/ac/sprite_cache.cpp
Normal file
@@ -0,0 +1,463 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// sprite caching system
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#include "common/system.h"
|
||||
#include "ags/shared/core/platform.h"
|
||||
#include "ags/shared/util/stream.h"
|
||||
#include "common/std/algorithm.h"
|
||||
#include "ags/shared/ac/sprite_cache.h"
|
||||
#include "ags/shared/ac/game_struct_defines.h"
|
||||
#include "ags/shared/debugging/out.h"
|
||||
#include "ags/shared/gfx/bitmap.h"
|
||||
#include "ags/globals.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
using namespace AGS::Shared;
|
||||
|
||||
// Tells that the sprite is found in the game resources.
|
||||
#define SPRCACHEFLAG_ISASSET 0x01
|
||||
// Tells that the sprite is assigned externally and cannot be autodisposed.
|
||||
#define SPRCACHEFLAG_EXTERNAL 0x02
|
||||
// Tells that the asset sprite failed to load
|
||||
#define SPRCACHEFLAG_ERROR 0x04
|
||||
// Locked sprites are ones that should not be freed when out of cache space.
|
||||
#define SPRCACHEFLAG_LOCKED 0x08
|
||||
|
||||
// High-verbosity sprite cache log
|
||||
#if DEBUG_SPRITECACHE
|
||||
#define SprCacheLog(...) Debug::Printf(kDbgGroup_SprCache, kDbgMsg_Debug, __VA_ARGS__)
|
||||
#else
|
||||
#define SprCacheLog(...)
|
||||
#endif
|
||||
|
||||
namespace AGS {
|
||||
namespace Shared {
|
||||
|
||||
SpriteCache::SpriteCache(std::vector<SpriteInfo> &sprInfos, const Callbacks &callbacks)
|
||||
: _sprInfos(sprInfos), _maxCacheSize(DEFAULTCACHESIZE_KB * 1024u),
|
||||
_cacheSize(0u), _lockedSize(0u) {
|
||||
_callbacks.AdjustSize = (callbacks.AdjustSize) ? callbacks.AdjustSize : DummyAdjustSize;
|
||||
_callbacks.InitSprite = (callbacks.InitSprite) ? callbacks.InitSprite : DummyInitSprite;
|
||||
_callbacks.PostInitSprite = (callbacks.PostInitSprite) ? callbacks.PostInitSprite : DummyPostInitSprite;
|
||||
_callbacks.PrewriteSprite = (callbacks.PrewriteSprite) ? callbacks.PrewriteSprite : DummyPrewriteSprite;
|
||||
|
||||
// Generate a placeholder sprite: 1x1 transparent bitmap
|
||||
_placeholder.reset(BitmapHelper::CreateTransparentBitmap(1, 1, 8));
|
||||
}
|
||||
|
||||
size_t SpriteCache::GetCacheSize() const {
|
||||
return _cacheSize;
|
||||
}
|
||||
|
||||
size_t SpriteCache::GetLockedSize() const {
|
||||
return _lockedSize;
|
||||
}
|
||||
|
||||
size_t SpriteCache::GetMaxCacheSize() const {
|
||||
return _maxCacheSize;
|
||||
}
|
||||
|
||||
size_t SpriteCache::GetSpriteSlotCount() const {
|
||||
return _spriteData.size();
|
||||
}
|
||||
|
||||
void SpriteCache::SetMaxCacheSize(size_t size) {
|
||||
FreeMem(size);
|
||||
_maxCacheSize = size;
|
||||
}
|
||||
|
||||
bool SpriteCache::HasFreeSlots() const {
|
||||
return !((_spriteData.size() == SIZE_MAX) || (_spriteData.size() > MAX_SPRITE_INDEX));
|
||||
}
|
||||
|
||||
bool SpriteCache::IsAssetSprite(sprkey_t index) const {
|
||||
return index >= 0 && (size_t)index < _spriteData.size() && // in the valid range
|
||||
_spriteData[index].IsAssetSprite(); // found in the game resources
|
||||
}
|
||||
|
||||
void SpriteCache::Reset() {
|
||||
_file.Close();
|
||||
_spriteData.clear();
|
||||
_mru.clear();
|
||||
_cacheSize = 0;
|
||||
_lockedSize = 0;
|
||||
}
|
||||
|
||||
bool SpriteCache::SetSprite(sprkey_t index, std::unique_ptr<Bitmap> image, int flags) {
|
||||
if (index < 0 || EnlargeTo(index) != index) {
|
||||
Debug::Printf(kDbgGroup_SprCache, kDbgMsg_Error, "SetSprite: unable to use index %d", index);
|
||||
return false;
|
||||
}
|
||||
if (!image || image->GetSize().IsNull() || image->GetColorDepth() <= 0) {
|
||||
DeleteSprite(index); // free previous item in this slot anyway
|
||||
Debug::Printf(kDbgGroup_SprCache, kDbgMsg_Error, "SetSprite: attempt to assign an invalid bitmap to index %d", index);
|
||||
return false;
|
||||
}
|
||||
|
||||
const int spf_flags = flags
|
||||
| (SPF_HICOLOR * image->GetColorDepth() > 8)
|
||||
| (SPF_TRUECOLOR * image->GetColorDepth() > 16);
|
||||
_sprInfos[index] = SpriteInfo(image->GetWidth(), image->GetHeight(), spf_flags);
|
||||
// Assign sprite with 0 size, as it will not be included into the cache size
|
||||
_spriteData[index] = SpriteData(image.release(), 0, SPRCACHEFLAG_EXTERNAL | SPRCACHEFLAG_LOCKED);
|
||||
SprCacheLog("SetSprite: (external) %d", index);
|
||||
return true;
|
||||
}
|
||||
|
||||
void SpriteCache::SetEmptySprite(sprkey_t index, bool as_asset) {
|
||||
if (index < 0 || EnlargeTo(index) != index) {
|
||||
Debug::Printf(kDbgGroup_SprCache, kDbgMsg_Error, "SetEmptySprite: unable to use index %d", index);
|
||||
return;
|
||||
}
|
||||
if (as_asset)
|
||||
_spriteData[index].Flags = SPRCACHEFLAG_ISASSET;
|
||||
RemapSpriteToPlaceholder(index);
|
||||
}
|
||||
|
||||
Bitmap *SpriteCache::RemoveSprite(sprkey_t index) {
|
||||
if (index < 0 || (size_t)index >= _spriteData.size())
|
||||
return nullptr;
|
||||
Bitmap *image = _spriteData[index].Image.release();
|
||||
InitNullSprite(index);
|
||||
SprCacheLog("RemoveSprite: %d", index);
|
||||
return image;
|
||||
}
|
||||
|
||||
void SpriteCache::DeleteSprite(sprkey_t index) {
|
||||
assert(index >= 0); // out of positive range indexes are valid to fail
|
||||
if (index < 0 || (size_t)index >= _spriteData.size())
|
||||
return;
|
||||
InitNullSprite(index);
|
||||
SprCacheLog("RemoveAndDispose: %d", index);
|
||||
}
|
||||
|
||||
sprkey_t SpriteCache::EnlargeTo(sprkey_t topmost) {
|
||||
if (topmost < 0 || topmost > MAX_SPRITE_INDEX)
|
||||
return -1;
|
||||
if ((size_t)topmost < _spriteData.size())
|
||||
return topmost;
|
||||
|
||||
size_t newsize = topmost + 1;
|
||||
_sprInfos.resize(newsize);
|
||||
_spriteData.resize(newsize);
|
||||
return topmost;
|
||||
}
|
||||
|
||||
sprkey_t SpriteCache::GetFreeIndex() {
|
||||
// FIXME: inefficient if large number of sprites were created in game;
|
||||
// use "available ids" stack, see managed pool for an example;
|
||||
// NOTE: this is shared with the Editor, which means we cannot rely on the
|
||||
// number of "static" sprites and search for slots after... this may be
|
||||
// resolved by splitting SpriteCache class further on "cache builder" and
|
||||
// "runtime cache".
|
||||
for (size_t i = MIN_SPRITE_INDEX; i < _spriteData.size(); ++i) {
|
||||
// slot empty
|
||||
if (!DoesSpriteExist(i)) {
|
||||
_sprInfos[i] = SpriteInfo();
|
||||
_spriteData[i] = SpriteData();
|
||||
return i;
|
||||
}
|
||||
}
|
||||
// enlarge the sprite bank to find a free slot and return the first new free slot
|
||||
return EnlargeTo(_spriteData.size());
|
||||
}
|
||||
|
||||
bool SpriteCache::SpriteData::DoesSpriteExist() const {
|
||||
return (Image != nullptr) || // HAS loaded bitmap
|
||||
((Flags & SPRCACHEFLAG_ISASSET) != 0); // OR found in the game resources
|
||||
}
|
||||
|
||||
bool SpriteCache::SpriteData::IsAssetSprite() const {
|
||||
return (Flags & SPRCACHEFLAG_ISASSET) != 0;
|
||||
}
|
||||
|
||||
bool SpriteCache::SpriteData::IsError() const {
|
||||
return (Flags & SPRCACHEFLAG_ERROR) != 0;
|
||||
}
|
||||
|
||||
bool SpriteCache::SpriteData::IsExternalSprite() const {
|
||||
return (Flags & SPRCACHEFLAG_EXTERNAL) != 0;
|
||||
}
|
||||
|
||||
bool SpriteCache::SpriteData::IsLocked() const {
|
||||
return (Flags & SPRCACHEFLAG_LOCKED) != 0;
|
||||
}
|
||||
|
||||
bool SpriteCache::DoesSpriteExist(sprkey_t index) const {
|
||||
return (index >= 0 && (size_t)index < _spriteData.size()) && // in the valid range
|
||||
_spriteData[index].IsValid(); // has assigned sprite
|
||||
}
|
||||
|
||||
Size SpriteCache::GetSpriteResolution(sprkey_t index) const {
|
||||
return DoesSpriteExist(index) ? _sprInfos[index].GetResolution() : Size();
|
||||
}
|
||||
|
||||
Bitmap *SpriteCache::operator[](sprkey_t index) {
|
||||
// invalid sprite slot
|
||||
if (!DoesSpriteExist(index) || _spriteData[index].IsError())
|
||||
return _placeholder.get();
|
||||
|
||||
// Externally added sprite or locked sprite, don't put it into MRU list
|
||||
if (_spriteData[index].IsExternalSprite() || _spriteData[index].IsLocked())
|
||||
return _spriteData[index].Image.get();
|
||||
// Either use ready image, or load one from assets
|
||||
if (_spriteData[index].Image) {
|
||||
// Move to the beginning of the MRU list
|
||||
_mru.splice(_mru.begin(), _mru, _spriteData[index].MruIt);
|
||||
return _spriteData[index].Image.get();
|
||||
} else {
|
||||
// Sprite exists in file but is not in mem, load it and add to MRU list
|
||||
if (LoadSprite(index)) {
|
||||
_spriteData[index].MruIt = _mru.insert(_mru.begin(), index);
|
||||
return _spriteData[index].Image.get();
|
||||
}
|
||||
}
|
||||
return _placeholder.get();
|
||||
}
|
||||
|
||||
void SpriteCache::FreeMem(size_t space) {
|
||||
for (int tries = 0; (_mru.size() > 0) && (_cacheSize >= (_maxCacheSize - space)); ++tries) {
|
||||
DisposeOldest();
|
||||
if (tries > 1000) { // ???
|
||||
Debug::Printf(kDbgGroup_SprCache, kDbgMsg_Error, "RUNTIME CACHE ERROR: STUCK IN FREE_UP_MEM; RESETTING CACHE");
|
||||
DisposeAllFreeCached();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SpriteCache::DisposeOldest() {
|
||||
assert(_mru.size() > 0);
|
||||
if (_mru.size() == 0)
|
||||
return;
|
||||
auto it = std::prev(_mru.end());
|
||||
const auto sprnum = *it;
|
||||
// Safety check: must be a sprite from resources
|
||||
// TODO: compare with latest upstream
|
||||
// Commented out the assertion, since it triggers for sprites that are in the list but remapped to the placeholder (sprite 0)
|
||||
// Whispers of a Machine is affected by this issue (see TRAC #14730)
|
||||
|
||||
// assert(_spriteData[sprnum].IsAssetSprite());
|
||||
|
||||
if (!_spriteData[sprnum].IsAssetSprite()) {
|
||||
Debug::Printf(kDbgGroup_SprCache, kDbgMsg_Error, "SpriteCache::DisposeOldest: in MRU list sprite %d is external or does not exist", sprnum);
|
||||
_mru.erase(it);
|
||||
// std::list::erase() invalidates iterators to the erased item.
|
||||
// But our implementation does not.
|
||||
_spriteData[sprnum].MruIt._node = nullptr;
|
||||
return;
|
||||
}
|
||||
// Delete the image, unless is locked
|
||||
// NOTE: locked sprites may still occur in MRU list
|
||||
if (!_spriteData[sprnum].IsLocked()) {
|
||||
_cacheSize -= _spriteData[sprnum].Size;
|
||||
_spriteData[sprnum].Image.reset();
|
||||
SprCacheLog("DisposeOldest: disposed %d, size now %d KB", sprnum, _cacheSize / 1024);
|
||||
}
|
||||
// Remove from the mru list
|
||||
_mru.erase(it);
|
||||
// std::list::erase() invalidates iterators to the erased item.
|
||||
// But our implementation does not.
|
||||
_spriteData[sprnum].MruIt._node = nullptr;
|
||||
}
|
||||
|
||||
void SpriteCache::DisposeCached(sprkey_t index) {
|
||||
if (IsAssetSprite(index)) {
|
||||
_spriteData[index].Flags &= ~SPRCACHEFLAG_LOCKED;
|
||||
_spriteData[index].Image.reset();
|
||||
}
|
||||
_cacheSize = _lockedSize;
|
||||
}
|
||||
|
||||
void SpriteCache::DisposeAllFreeCached() {
|
||||
for (size_t i = 0; i < _spriteData.size(); ++i) {
|
||||
if (!_spriteData[i].IsLocked() && // not locked
|
||||
_spriteData[i].IsAssetSprite()) // sprite from game resource
|
||||
{
|
||||
_spriteData[i].Image.reset();
|
||||
}
|
||||
}
|
||||
_cacheSize = _lockedSize;
|
||||
_mru.clear();
|
||||
}
|
||||
|
||||
void SpriteCache::PrecacheSprite(sprkey_t index) {
|
||||
if (index < 0 || (size_t)index >= _spriteData.size())
|
||||
return;
|
||||
if (!_spriteData[index].IsAssetSprite())
|
||||
return; // cannot precache a non-asset sprite
|
||||
|
||||
size_t size = 0;
|
||||
|
||||
if (_spriteData[index].Image == nullptr) {
|
||||
size = LoadSprite(index);
|
||||
} else if (!_spriteData[index].IsLocked()) {
|
||||
size = _spriteData[index].Size;
|
||||
// Remove locked sprite from the MRU list
|
||||
_mru.erase(_spriteData[index].MruIt);
|
||||
// std::list::erase() invalidates iterators to the erased item.
|
||||
// But our implementation does not.
|
||||
_spriteData[index].MruIt._node = nullptr;
|
||||
}
|
||||
|
||||
// make sure locked sprites can't fill the cache
|
||||
_maxCacheSize += size;
|
||||
_lockedSize += size;
|
||||
_spriteData[index].Flags |= SPRCACHEFLAG_LOCKED;
|
||||
SprCacheLog("Precached %d", index);
|
||||
}
|
||||
|
||||
void SpriteCache::LockSprite(sprkey_t index) {
|
||||
assert(index >= 0); // out of positive range indexes are valid to fail
|
||||
if (index < 0 || (size_t)index >= _spriteData.size())
|
||||
return;
|
||||
if (!_spriteData[index].IsAssetSprite())
|
||||
return; // cannot lock a non-asset sprite
|
||||
|
||||
if (_spriteData[index].DoesSpriteExist()) {
|
||||
_spriteData[index].Flags |= SPRCACHEFLAG_LOCKED;
|
||||
} else {
|
||||
LoadSprite(index, true);
|
||||
}
|
||||
SprCacheLog("Locked %d", index);
|
||||
}
|
||||
|
||||
void SpriteCache::UnlockSprite(sprkey_t index) {
|
||||
assert(index >= 0); // out of positive range indexes are valid to fail
|
||||
if (index < 0 || (size_t)index >= _spriteData.size())
|
||||
return;
|
||||
if (!_spriteData[index].IsAssetSprite() ||
|
||||
!_spriteData[index].IsLocked())
|
||||
return; // cannot unlock a non-asset sprite, or non-locked sprite
|
||||
|
||||
_spriteData[index].Flags &= ~SPRCACHEFLAG_LOCKED;
|
||||
SprCacheLog("Unlocked %d", index);
|
||||
}
|
||||
|
||||
size_t SpriteCache::LoadSprite(sprkey_t index, bool lock) {
|
||||
assert((index >= 0) && ((size_t)index < _spriteData.size()));
|
||||
if (index < 0 || (size_t)index >= _spriteData.size())
|
||||
return 0;
|
||||
assert((_spriteData[index].Flags & SPRCACHEFLAG_ISASSET) != 0);
|
||||
|
||||
Bitmap *image;
|
||||
HError err = _file.LoadSprite(index, image);
|
||||
if (!image) {
|
||||
Debug::Printf(kDbgGroup_SprCache, kDbgMsg_Warn,
|
||||
"LoadSprite: failed to load sprite %d:\n%s\n - remapping to placeholder", index,
|
||||
err ? "Sprite does not exist." : err->FullMessage().GetCStr());
|
||||
RemapSpriteToPlaceholder(index);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Let the external user convert this sprite's image for their needs
|
||||
image = _callbacks.InitSprite(index, image, _sprInfos[index].Flags);
|
||||
if (!image) {
|
||||
Debug::Printf(kDbgGroup_SprCache, kDbgMsg_Warn,
|
||||
"LoadSprite: failed to initialize sprite %d, remapping to placeholder", index);
|
||||
RemapSpriteToPlaceholder(index);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// save the stored sprite info
|
||||
_sprInfos[index].Width = image->GetWidth();
|
||||
_sprInfos[index].Height = image->GetHeight();
|
||||
// Clear up space before adding to cache
|
||||
const size_t size = image->GetWidth() * image->GetHeight() * image->GetBPP();
|
||||
FreeMem(size);
|
||||
// Add to the cache, lock if requested or if it's sprite 0
|
||||
const bool should_lock = lock || (index == 0);
|
||||
_spriteData[index] = SpriteData(image, size, SPRCACHEFLAG_ISASSET);
|
||||
_spriteData[index].Flags |= (SPRCACHEFLAG_LOCKED * should_lock);
|
||||
_cacheSize += size;
|
||||
SprCacheLog("Loaded %d, size now %zu KB", index, _cacheSize / 1024);
|
||||
|
||||
// Let the external user to react to the new sprite;
|
||||
// note that this callback is allowed to modify the sprite's pixels,
|
||||
// but not its size or flags.
|
||||
_callbacks.PostInitSprite(index);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
void SpriteCache::RemapSpriteToPlaceholder(sprkey_t index) {
|
||||
assert((index > 0) && ((size_t)index < _spriteData.size()));
|
||||
_sprInfos[index] = SpriteInfo(_placeholder->GetWidth(), _placeholder->GetHeight(), _placeholder->GetColorDepth());
|
||||
_spriteData[index].Flags |= SPRCACHEFLAG_ERROR;
|
||||
SprCacheLog("RemapSpriteToPlaceholder: %d", index);
|
||||
}
|
||||
|
||||
void SpriteCache::InitNullSprite(sprkey_t index) {
|
||||
assert(index >= 0);
|
||||
_sprInfos[index] = SpriteInfo();
|
||||
_spriteData[index] = SpriteData();
|
||||
}
|
||||
|
||||
int SpriteCache::SaveToFile(const String &filename, int store_flags, SpriteCompression compress, SpriteFileIndex &index) {
|
||||
std::vector<std::pair<bool, Bitmap *>> sprites;
|
||||
for (size_t i = 0; i < _spriteData.size(); ++i) {
|
||||
_callbacks.PrewriteSprite(_spriteData[i].Image.get());
|
||||
sprites.push_back(std::make_pair(DoesSpriteExist(i), _spriteData[i].Image.get()));
|
||||
}
|
||||
return SaveSpriteFile(filename, sprites, &_file, store_flags, compress, index);
|
||||
}
|
||||
|
||||
HError SpriteCache::InitFile(const String &filename, const String &sprindex_filename) {
|
||||
Reset();
|
||||
|
||||
std::vector<Size> metrics;
|
||||
HError err = _file.OpenFile(filename, sprindex_filename, metrics);
|
||||
if (!err)
|
||||
return err;
|
||||
|
||||
// Initialize sprite infos
|
||||
size_t newsize = metrics.size();
|
||||
_sprInfos.resize(newsize);
|
||||
_spriteData.resize(newsize);
|
||||
_mru.clear();
|
||||
for (size_t i = 0; i < metrics.size(); ++i) {
|
||||
if (!metrics[i].IsNull()) {
|
||||
// Existing sprite
|
||||
_spriteData[i].Flags = SPRCACHEFLAG_ISASSET;
|
||||
Size newsz = _callbacks.AdjustSize(Size(metrics[i].Width, metrics[i].Height), _sprInfos[i].Flags);
|
||||
_sprInfos[i].Width = newsz.Width;
|
||||
_sprInfos[i].Height = newsz.Height;
|
||||
} else {
|
||||
// Mark as empty slot
|
||||
InitNullSprite(i);
|
||||
}
|
||||
}
|
||||
return HError::None();
|
||||
}
|
||||
|
||||
void SpriteCache::DetachFile() {
|
||||
_file.Close();
|
||||
}
|
||||
|
||||
} // namespace Shared
|
||||
} // namespace AGS
|
||||
} // namespace AGS3
|
||||
254
engines/ags/shared/ac/sprite_cache.h
Normal file
254
engines/ags/shared/ac/sprite_cache.h
Normal file
@@ -0,0 +1,254 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
//
|
||||
// Sprite caching system.
|
||||
//
|
||||
// SpriteFile handles sprite serialization and streaming.
|
||||
// SpriteCache provides bitmaps by demand; it uses SpriteFile to load sprites
|
||||
// and does MRU (most-recent-use) caching.
|
||||
//
|
||||
// TODO: store sprite data in a specialized container type that is optimized
|
||||
// for having most keys allocated in large continious sequences by default.
|
||||
//
|
||||
// Only for the reference: one of the ideas is for container to have a table
|
||||
// of arrays of fixed size internally. When getting an item the hash would be
|
||||
// first divided on array size to find the array the item resides in, then the
|
||||
// item is taken from item from slot index = (hash - arrsize * arrindex).
|
||||
// TODO: find out if there is already a hash table kind that follows similar
|
||||
// principle.
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef AGS_SHARED_AC_SPRITE_CACHE_H
|
||||
#define AGS_SHARED_AC_SPRITE_CACHE_H
|
||||
|
||||
#include "common/std/memory.h"
|
||||
#include "common/std/vector.h"
|
||||
#include "common/std/list.h"
|
||||
#include "ags/shared/ac/sprite_file.h"
|
||||
#include "ags/shared/core/platform.h"
|
||||
#include "ags/shared/gfx/bitmap.h"
|
||||
#include "ags/shared/util/error.h"
|
||||
#include "ags/shared/util/geometry.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
namespace AGS {
|
||||
namespace Shared {
|
||||
class String;
|
||||
class Stream;
|
||||
class Bitmap;
|
||||
} // namespace AGS3
|
||||
} // namespace AGS
|
||||
|
||||
using namespace AGS; // FIXME later
|
||||
typedef AGS::Shared::HError HAGSError;
|
||||
|
||||
struct SpriteInfo;
|
||||
|
||||
// Max size of the sprite cache, in bytes
|
||||
#if AGS_PLATFORM_OS_ANDROID || AGS_PLATFORM_OS_IOS
|
||||
#define DEFAULTCACHESIZE_KB (32 * 1024)
|
||||
#else
|
||||
#define DEFAULTCACHESIZE_KB (128 * 1024)
|
||||
#endif
|
||||
|
||||
struct SpriteInfo;
|
||||
|
||||
namespace AGS {
|
||||
namespace Shared {
|
||||
|
||||
class SpriteCache {
|
||||
public:
|
||||
static const sprkey_t MIN_SPRITE_INDEX = 1; // 0 is reserved for "empty sprite"
|
||||
static const sprkey_t MAX_SPRITE_INDEX = INT32_MAX - 1;
|
||||
static const size_t MAX_SPRITE_SLOTS = INT32_MAX;
|
||||
|
||||
typedef Size (*PfnAdjustSpriteSize)(const Size &size, const uint32_t sprite_flags);
|
||||
typedef Bitmap *(*PfnInitSprite)(sprkey_t index, Bitmap *image, uint32_t &sprite_flags);
|
||||
typedef void (*PfnPostInitSprite)(sprkey_t index);
|
||||
typedef void (*PfnPrewriteSprite)(Bitmap *image);
|
||||
|
||||
struct Callbacks {
|
||||
PfnAdjustSpriteSize AdjustSize;
|
||||
PfnInitSprite InitSprite;
|
||||
PfnPostInitSprite PostInitSprite;
|
||||
PfnPrewriteSprite PrewriteSprite;
|
||||
};
|
||||
|
||||
SpriteCache(std::vector<SpriteInfo> &sprInfos, const Callbacks &callbacks);
|
||||
~SpriteCache() = default;
|
||||
|
||||
// Loads sprite reference information and inits sprite stream
|
||||
HError InitFile(const String &filename, const String &sprindex_filename);
|
||||
// Saves current cache contents to the file
|
||||
int SaveToFile(const String &filename, int store_flags, SpriteCompression compress, SpriteFileIndex &index);
|
||||
// Closes an active sprite file stream
|
||||
void DetachFile();
|
||||
|
||||
inline int GetStoreFlags() const {
|
||||
return _file.GetStoreFlags();
|
||||
}
|
||||
inline SpriteCompression GetSpriteCompression() const {
|
||||
return _file.GetSpriteCompression();
|
||||
}
|
||||
|
||||
// Tells if there is a sprite registered for the given index;
|
||||
// this includes sprites that were explicitly assigned but failed to init and were remapped
|
||||
bool DoesSpriteExist(sprkey_t index) const;
|
||||
// Returns sprite's resolution; or empty Size if sprite does not exist
|
||||
Size GetSpriteResolution(sprkey_t index) const;
|
||||
// Makes sure sprite cache has allocated slots for all sprites up to the given inclusive limit;
|
||||
// returns requested index on success, or -1 on failure.
|
||||
sprkey_t EnlargeTo(sprkey_t topmost);
|
||||
// Finds a free slot index, if all slots are occupied enlarges sprite bank; returns index
|
||||
sprkey_t GetFreeIndex();
|
||||
// Returns current size of the cache, in bytes; this includes locked size too!
|
||||
size_t GetCacheSize() const;
|
||||
// Gets the total size of the locked sprites, in bytes
|
||||
size_t GetLockedSize() const;
|
||||
// Returns maximal size limit of the cache, in bytes; this includes locked size too!
|
||||
size_t GetMaxCacheSize() const;
|
||||
// Returns number of sprite slots in the bank (this includes both actual sprites and free slots)
|
||||
size_t GetSpriteSlotCount() const;
|
||||
// Tells if the sprite storage still has unoccupied slots to put new sprites in
|
||||
bool HasFreeSlots() const;
|
||||
// Tells if the given slot is reserved for the asset sprite, that is a "static"
|
||||
// sprite cached from the game assets
|
||||
bool IsAssetSprite(sprkey_t index) const;
|
||||
// Loads sprite using SpriteFile if such index is known,
|
||||
// frees the space if cache size reaches the limit
|
||||
void PrecacheSprite(sprkey_t index);
|
||||
// Locks sprite, preventing it from getting removed by the normal cache limit.
|
||||
// If this is a registered sprite from the game assets, then loads it first.
|
||||
// If this is a sprite with SPRCACHEFLAG_EXTERNAL flag, then does nothing,
|
||||
// as these are always "locked".
|
||||
// If such sprite does not exist, then fails silently.
|
||||
void LockSprite(sprkey_t index);
|
||||
// Unlocks sprite, putting it back into the cache logic,
|
||||
// where it counts towards normal limit may be deleted to free space.
|
||||
// NOTE: sprites with SPRCACHEFLAG_EXTERNAL flag cannot be unlocked,
|
||||
// only explicitly removed.
|
||||
// If such sprite was not present in memory, then fails silently.
|
||||
void UnlockSprite(sprkey_t index);
|
||||
// Unregisters sprite from the bank and returns the bitmap
|
||||
Bitmap *RemoveSprite(sprkey_t index);
|
||||
// Deletes particular sprite, marks slot as unused
|
||||
void DeleteSprite(sprkey_t index);
|
||||
// Deletes a loaded asset image (non-external) from memory, ignoring its
|
||||
// locked status; this keeps an auxiliary sprite information intact,
|
||||
// so that the same sprite can be cached back later.
|
||||
void DisposeCached(sprkey_t index);
|
||||
// Deletes all free cached asset images (non-locked, non-external)
|
||||
// from memory; this keeps all the auxiliary sprite information intact,
|
||||
// so that the same sprite(s) can be cached back later.
|
||||
void DisposeAllFreeCached();
|
||||
// Deletes all data and resets cache to the clear state
|
||||
void Reset();
|
||||
// Assigns new sprite for the given index; this sprite won't be auto disposed.
|
||||
// *Deletes* the previous sprite if one was found at the same index.
|
||||
// "flags" are SPF_* constants that define sprite's behavior in game.
|
||||
bool SetSprite(sprkey_t index, std::unique_ptr<Bitmap> image, int flags = 0);
|
||||
// Assigns new dummy for the given index, silently remapping it to placeholder;
|
||||
// optionally marks it as an asset placeholder.
|
||||
// *Deletes* the previous sprite if one was found at the same index.
|
||||
void SetEmptySprite(sprkey_t index, bool as_asset);
|
||||
// Sets max cache size in bytes
|
||||
void SetMaxCacheSize(size_t size);
|
||||
|
||||
// Loads (if it's not in cache yet) and returns bitmap by the sprite index
|
||||
Bitmap *operator[](sprkey_t index);
|
||||
|
||||
private:
|
||||
// Load sprite from game resource
|
||||
size_t LoadSprite(sprkey_t index, bool lock = false);
|
||||
// Remap the given index to the placeholder
|
||||
void RemapSpriteToPlaceholder(sprkey_t index);
|
||||
// Delete the oldest (least recently used) image in cache
|
||||
void DisposeOldest();
|
||||
// Keep disposing oldest elements until cache has at least the given free space
|
||||
void FreeMem(size_t space);
|
||||
// Initialize the empty sprite slot
|
||||
void InitNullSprite(sprkey_t index);
|
||||
//
|
||||
// Dummy no-op variants for callbacks
|
||||
//
|
||||
static Size DummyAdjustSize(const Size &size, const uint32_t) { return size; }
|
||||
static Bitmap *DummyInitSprite(sprkey_t, Bitmap *image, uint32_t &) { return image; }
|
||||
static void DummyPostInitSprite(sprkey_t) { /* do nothing */ }
|
||||
static void DummyPrewriteSprite(Bitmap *) { /* do nothing */ }
|
||||
|
||||
// Information required for the sprite streaming
|
||||
struct SpriteData {
|
||||
size_t Size = 0; // to track cache size, 0 = means don't track
|
||||
uint32_t Flags = 0; // SPRCACHEFLAG* flags
|
||||
std::unique_ptr<Bitmap> Image; // actual bitmap
|
||||
|
||||
// MRU list reference
|
||||
std::list<sprkey_t>::iterator MruIt;
|
||||
|
||||
SpriteData() = default;
|
||||
SpriteData(SpriteData &&other) = default;
|
||||
SpriteData(Bitmap *image, size_t size, uint32_t flags) : Size(size), Flags(flags), Image(image) {}
|
||||
|
||||
SpriteData &operator=(SpriteData &&other) = default;
|
||||
|
||||
// Tells if this slot has a valid sprite assigned (not empty slot)
|
||||
bool IsValid() const { return Flags != 0u; }
|
||||
// Tells if there actually is a registered sprite in this slot
|
||||
bool DoesSpriteExist() const;
|
||||
// Tells if there's a game resource corresponding to this slot
|
||||
bool IsAssetSprite() const;
|
||||
// Tells if a sprite failed to load from assets, and should not be used
|
||||
bool IsError() const;
|
||||
// Tells if sprite was added externally, not loaded from game resources
|
||||
bool IsExternalSprite() const;
|
||||
// Tells if sprite is locked and should not be disposed by cache logic
|
||||
bool IsLocked() const;
|
||||
};
|
||||
|
||||
// Provided map of sprite infos, to fill in loaded sprite properties
|
||||
std::vector<SpriteInfo> &_sprInfos;
|
||||
// Array of sprite references
|
||||
std::vector<SpriteData> _spriteData;
|
||||
// Placeholder sprite, returned from operator[] for a non-existing sprite
|
||||
std::unique_ptr<Bitmap> _placeholder;
|
||||
|
||||
Callbacks _callbacks;
|
||||
SpriteFile _file;
|
||||
|
||||
size_t _maxCacheSize; // cache size limit
|
||||
size_t _lockedSize; // size in bytes of currently locked images
|
||||
size_t _cacheSize; // size in bytes of currently cached images
|
||||
|
||||
// MRU list: the way to track which sprites were used recently.
|
||||
// When clearing up space for new sprites, cache first deletes the sprites
|
||||
// that were last time used long ago.
|
||||
std::list<sprkey_t> _mru;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Shared
|
||||
} // namespace AGS
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
746
engines/ags/shared/ac/sprite_file.cpp
Normal file
746
engines/ags/shared/ac/sprite_file.cpp
Normal file
@@ -0,0 +1,746 @@
|
||||
/* 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 "ags/shared/ac/sprite_file.h"
|
||||
#include "common/std/algorithm.h"
|
||||
#include "ags/shared/core/asset_manager.h"
|
||||
#include "ags/shared/gfx/bitmap.h"
|
||||
#include "ags/shared/util/compress.h"
|
||||
#include "ags/shared/util/file.h"
|
||||
#include "ags/shared/util/memory_stream.h"
|
||||
#include "ags/shared/util/stream.h"
|
||||
|
||||
namespace AGS3 {
|
||||
namespace AGS {
|
||||
namespace Shared {
|
||||
|
||||
static const char *spriteFileSig = " Sprite File ";
|
||||
static const char *spindexid = "SPRINDEX";
|
||||
|
||||
// TODO: should not be part of SpriteFile, but rather some asset management class?
|
||||
const char *SpriteFile::DefaultSpriteFileName = "acsprset.spr";
|
||||
const char *SpriteFile::DefaultSpriteIndexName = "sprindex.dat";
|
||||
|
||||
// Image buffer pointer, a helper struct that eases switching
|
||||
// between intermediate buffers when loading, saving or converting an image.
|
||||
template <typename T> struct ImBufferPtrT {
|
||||
T Buf = nullptr;
|
||||
size_t Size = 0;
|
||||
int BPP = 1; // byte per pixel
|
||||
|
||||
ImBufferPtrT() = default;
|
||||
ImBufferPtrT(T buf, size_t sz, int bpp) : Buf(buf), Size(sz), BPP(bpp) {
|
||||
}
|
||||
};
|
||||
typedef ImBufferPtrT<uint8_t *> ImBufferPtr;
|
||||
typedef ImBufferPtrT<const uint8_t *> ImBufferCPtr;
|
||||
|
||||
|
||||
// Finds the given color's index in the palette, or returns SIZE_MAX if such color is not there
|
||||
static size_t lookup_palette(uint32_t col, uint32_t palette[256], uint32_t ncols) {
|
||||
for (size_t i = 0; i < ncols; ++i)
|
||||
if (palette[i] == col) return i;
|
||||
return SIZE_MAX;
|
||||
}
|
||||
|
||||
// Converts a 16/32-bit image into the indexed 8-bit pixel data with palette;
|
||||
// NOTE: the palette will contain colors in the same format as the source image.
|
||||
// only succeeds if the total number of colors used in the image is < 257.
|
||||
static bool CreateIndexedBitmap(const Bitmap *image, std::vector<uint8_t> &dst_data,
|
||||
uint32_t palette[256], uint32_t &pal_count) {
|
||||
const int src_bpp = image->GetBPP();
|
||||
if (src_bpp < 2) { assert(0); return false; }
|
||||
const size_t src_size = image->GetWidth() * image->GetHeight() * image->GetBPP();
|
||||
const size_t dst_size = image->GetWidth() * image->GetHeight();
|
||||
dst_data.resize(dst_size);
|
||||
const uint8_t *src = image->GetData(), *src_end = src + src_size;
|
||||
uint8_t *dst = &dst_data[0], *dst_end = dst + dst_size;
|
||||
pal_count = 0;
|
||||
|
||||
for (; src < src_end && dst < dst_end; src += src_bpp) {
|
||||
uint32_t col = 0;
|
||||
size_t pal_n = 0;
|
||||
switch (src_bpp) {
|
||||
case 2:
|
||||
col = *((const uint16_t *)src);
|
||||
pal_n = lookup_palette(col, palette, pal_count);
|
||||
break;
|
||||
case 4:
|
||||
col = *((const uint32_t *)src);
|
||||
pal_n = lookup_palette(col, palette, pal_count);
|
||||
break;
|
||||
default: assert(0); return false;
|
||||
}
|
||||
|
||||
if (pal_n == SIZE_MAX) {
|
||||
if (pal_count == 256) return false;
|
||||
pal_n = pal_count;
|
||||
palette[pal_count++] = col;
|
||||
}
|
||||
*(dst++) = (uint8_t)pal_n;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Unpacks an indexed image's pixel data into the 16/32-bit image;
|
||||
// NOTE: the palette is expected to contain colors in the same format as the destination.
|
||||
static void UnpackIndexedBitmap(Bitmap *image, const uint8_t *data, size_t data_size,
|
||||
uint32_t *palette, uint32_t pal_count) {
|
||||
assert(pal_count > 0);
|
||||
if (pal_count == 0) return; // meaningless
|
||||
const uint8_t bpp = image->GetBPP();
|
||||
const size_t dst_size = image->GetWidth() * image->GetHeight() * image->GetBPP();
|
||||
uint8_t *dst = image->GetDataForWriting(), *dst_end = dst + dst_size;
|
||||
|
||||
switch (bpp) {
|
||||
case 2:
|
||||
for (size_t p = 0; (p < data_size) && (dst < dst_end); ++p, dst += bpp) {
|
||||
uint8_t index = data[p];
|
||||
assert(index < pal_count);
|
||||
uint32_t color = palette[(index < pal_count) ? index : 0];
|
||||
*((uint16_t *)dst) = color;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
for (size_t p = 0; (p < data_size) && (dst < dst_end); ++p, dst += bpp) {
|
||||
uint8_t index = data[p];
|
||||
assert(index < pal_count);
|
||||
uint32_t color = palette[(index < pal_count) ? index : 0];
|
||||
*((uint32_t *)dst) = color;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static inline SpriteFormat PaletteFormatForBPP(int bpp) {
|
||||
switch (bpp) {
|
||||
case 1: return kSprFmt_PaletteRgb888;
|
||||
case 2: return kSprFmt_PaletteRgb565;
|
||||
case 4: return kSprFmt_PaletteArgb8888;
|
||||
default: return kSprFmt_Undefined;
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint8_t GetPaletteBPP(SpriteFormat fmt) {
|
||||
switch (fmt) {
|
||||
case kSprFmt_PaletteRgb888: return 3;
|
||||
case kSprFmt_PaletteArgb8888: return 4;
|
||||
case kSprFmt_PaletteRgb565: return 2;
|
||||
default: return 0; // means no palette
|
||||
}
|
||||
}
|
||||
|
||||
SpriteFile::SpriteFile() {
|
||||
_curPos = -2;
|
||||
}
|
||||
|
||||
HError SpriteFile::OpenFile(const String &filename, const String &sprindex_filename,
|
||||
std::vector<Size> &metrics) {
|
||||
Close();
|
||||
|
||||
char buff[20];
|
||||
soff_t spr_initial_offs = 0;
|
||||
int spriteFileID = 0;
|
||||
|
||||
_stream.reset(_GP(AssetMgr)->OpenAsset(filename));
|
||||
if (_stream == nullptr)
|
||||
return new Error(String::FromFormat("Failed to open spriteset file '%s'.", filename.GetCStr()));
|
||||
|
||||
spr_initial_offs = _stream->GetPosition();
|
||||
|
||||
_version = (SpriteFileVersion)_stream->ReadInt16();
|
||||
// read the "Sprite File" signature
|
||||
_stream->ReadArray(&buff[0], 13, 1);
|
||||
|
||||
if (_version < kSprfVersion_Uncompressed || _version > kSprfVersion_Current) {
|
||||
_stream.reset();
|
||||
return new Error(String::FromFormat("Unsupported spriteset format (requested %d, supported %d - %d).", _version,
|
||||
kSprfVersion_Uncompressed, kSprfVersion_Current));
|
||||
}
|
||||
|
||||
// unknown version
|
||||
buff[13] = 0;
|
||||
if (strcmp(buff, spriteFileSig)) {
|
||||
_stream.reset();
|
||||
return new Error("Uknown spriteset format.");
|
||||
}
|
||||
|
||||
_storeFlags = 0;
|
||||
if (_version < kSprfVersion_Compressed) {
|
||||
_compress = kSprCompress_None;
|
||||
// skip the palette
|
||||
_stream->Seek(256 * 3); // sizeof(RGB) * 256
|
||||
} else if (_version == kSprfVersion_Compressed) {
|
||||
_compress = kSprCompress_RLE;
|
||||
} else if (_version >= kSprfVersion_Last32bit) {
|
||||
_compress = (SpriteCompression)_stream->ReadInt8();
|
||||
spriteFileID = _stream->ReadInt32();
|
||||
}
|
||||
|
||||
sprkey_t topmost;
|
||||
if (_version < kSprfVersion_HighSpriteLimit)
|
||||
topmost = (uint16_t)_stream->ReadInt16();
|
||||
else
|
||||
topmost = _stream->ReadInt32();
|
||||
if (_version < kSprfVersion_Uncompressed)
|
||||
topmost = 200;
|
||||
|
||||
_spriteData.resize(topmost + 1);
|
||||
metrics.resize(topmost + 1);
|
||||
|
||||
// Version 12+: read global store flags
|
||||
if (_version >= kSprfVersion_StorageFormats) {
|
||||
_storeFlags = _stream->ReadInt8();
|
||||
_stream->ReadInt8(); // reserved
|
||||
_stream->ReadInt8();
|
||||
_stream->ReadInt8();
|
||||
}
|
||||
|
||||
// if there is a sprite index file, use it
|
||||
if (LoadSpriteIndexFile(sprindex_filename, spriteFileID,
|
||||
spr_initial_offs, topmost, metrics)) {
|
||||
// Succeeded
|
||||
return HError::None();
|
||||
}
|
||||
|
||||
// Failed, index file is invalid; index sprites manually
|
||||
return RebuildSpriteIndex(_stream.get(), topmost, metrics);
|
||||
}
|
||||
|
||||
void SpriteFile::Close() {
|
||||
_stream.reset();
|
||||
_spriteData.clear();
|
||||
_version = kSprfVersion_Undefined;
|
||||
_storeFlags = 0;
|
||||
_compress = kSprCompress_None;
|
||||
_curPos = -2;
|
||||
}
|
||||
|
||||
int SpriteFile::GetStoreFlags() const {
|
||||
return _storeFlags;
|
||||
}
|
||||
|
||||
SpriteCompression SpriteFile::GetSpriteCompression() const {
|
||||
return _compress;
|
||||
}
|
||||
|
||||
sprkey_t SpriteFile::GetTopmostSprite() const {
|
||||
return (sprkey_t)_spriteData.size() - 1;
|
||||
}
|
||||
|
||||
bool SpriteFile::LoadSpriteIndexFile(const String &filename, int expectedFileID,
|
||||
soff_t spr_initial_offs, sprkey_t topmost, std::vector<Size> &metrics) {
|
||||
Stream *fidx = _GP(AssetMgr)->OpenAsset(filename);
|
||||
if (fidx == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char buffer[9];
|
||||
// check "SPRINDEX" id
|
||||
fidx->ReadArray(&buffer[0], strlen(spindexid), 1);
|
||||
buffer[8] = 0;
|
||||
if (strcmp(buffer, spindexid)) {
|
||||
delete fidx;
|
||||
return false;
|
||||
}
|
||||
// check version
|
||||
SpriteIndexFileVersion vers = (SpriteIndexFileVersion)fidx->ReadInt32();
|
||||
if (vers < kSpridxfVersion_Initial || vers > kSpridxfVersion_Current) {
|
||||
delete fidx;
|
||||
return false;
|
||||
}
|
||||
if (vers >= kSpridxfVersion_Last32bit) {
|
||||
if (fidx->ReadInt32() != expectedFileID) {
|
||||
delete fidx;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
sprkey_t topmost_index = fidx->ReadInt32();
|
||||
// end index+1 should be the same as num sprites
|
||||
if (fidx->ReadInt32() != topmost_index + 1) {
|
||||
delete fidx;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (topmost_index != topmost) {
|
||||
delete fidx;
|
||||
return false;
|
||||
}
|
||||
|
||||
sprkey_t numsprits = topmost_index + 1;
|
||||
std::vector<int16_t> rspritewidths; rspritewidths.resize(numsprits);
|
||||
std::vector<int16_t> rspriteheights; rspriteheights.resize(numsprits);
|
||||
std::vector<soff_t> spriteoffs; spriteoffs.resize(numsprits);
|
||||
|
||||
fidx->ReadArrayOfInt16(&rspritewidths[0], numsprits);
|
||||
fidx->ReadArrayOfInt16(&rspriteheights[0], numsprits);
|
||||
if (vers <= kSpridxfVersion_Last32bit) {
|
||||
for (sprkey_t i = 0; i < numsprits; ++i)
|
||||
spriteoffs[i] = fidx->ReadInt32();
|
||||
} else // large file support
|
||||
{
|
||||
fidx->ReadArrayOfInt64(&spriteoffs[0], numsprits);
|
||||
}
|
||||
delete fidx;
|
||||
|
||||
for (sprkey_t i = 0; i <= topmost_index; ++i) {
|
||||
if (spriteoffs[i] != 0) {
|
||||
_spriteData[i].Offset = spriteoffs[i] + spr_initial_offs;
|
||||
metrics[i].Width = rspritewidths[i];
|
||||
metrics[i].Height = rspriteheights[i];
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void ReadSprHeader(SpriteDatHeader &hdr, Stream *in,
|
||||
const SpriteFileVersion ver, SpriteCompression gl_compress) {
|
||||
int bpp = in->ReadInt8();
|
||||
SpriteFormat sformat = (SpriteFormat)in->ReadInt8();
|
||||
// note we MUST read first 2 * int8 before skipping rest
|
||||
if (bpp == 0) {
|
||||
hdr = SpriteDatHeader(); return;
|
||||
} // empty slot
|
||||
int pal_count = 0;
|
||||
SpriteCompression compress = gl_compress;
|
||||
if (ver >= kSprfVersion_StorageFormats) {
|
||||
pal_count = (uint8_t)in->ReadInt8() + 1; // saved as (count - 1)
|
||||
compress = (SpriteCompression)in->ReadInt8();
|
||||
}
|
||||
int w = in->ReadInt16();
|
||||
int h = in->ReadInt16();
|
||||
hdr = SpriteDatHeader(bpp, sformat, pal_count, compress, w, h);
|
||||
}
|
||||
|
||||
HError SpriteFile::RebuildSpriteIndex(Stream *in, sprkey_t topmost,
|
||||
std::vector<Size> &metrics) {
|
||||
topmost = MIN(topmost, (sprkey_t)_spriteData.size() - 1);
|
||||
for (sprkey_t i = 0; !in->EOS() && (i <= topmost); ++i) {
|
||||
_spriteData[i].Offset = in->GetPosition();
|
||||
SpriteDatHeader hdr;
|
||||
ReadSprHeader(hdr, _stream.get(), _version, _compress);
|
||||
if (hdr.BPP == 0) continue; // empty slot, this is normal
|
||||
int pal_bpp = GetPaletteBPP(hdr.SFormat);
|
||||
if (pal_bpp > 0) in->Seek(hdr.PalCount * pal_bpp); // skip palette
|
||||
size_t data_sz =
|
||||
((_version >= kSprfVersion_StorageFormats) || _compress != kSprCompress_None) ?
|
||||
(uint32_t)in->ReadInt32() : hdr.Width * hdr.Height * hdr.BPP;
|
||||
in->Seek(data_sz); // skip image data
|
||||
metrics[i].Width = hdr.Width;
|
||||
metrics[i].Height = hdr.Height;
|
||||
}
|
||||
return HError::None();
|
||||
}
|
||||
|
||||
HError SpriteFile::LoadSprite(sprkey_t index, Shared::Bitmap *&sprite) {
|
||||
sprite = nullptr;
|
||||
if (index < 0 || (size_t)index >= _spriteData.size())
|
||||
return new Error(String::FromFormat("LoadSprite: slot index %d out of bounds (%d - %d).",
|
||||
index, 0, _spriteData.size() - 1));
|
||||
|
||||
if (_spriteData[index].Offset == 0)
|
||||
return HError::None(); // sprite is not in file
|
||||
|
||||
SeekToSprite(index);
|
||||
_curPos = -2; // mark undefined pos
|
||||
|
||||
SpriteDatHeader hdr;
|
||||
ReadSprHeader(hdr, _stream.get(), _version, _compress);
|
||||
if (hdr.BPP == 0) return HError::None(); // empty slot, this is normal
|
||||
int bpp = hdr.BPP, w = hdr.Width, h = hdr.Height;
|
||||
std::unique_ptr<Bitmap> image(BitmapHelper::CreateBitmap(w, h, bpp * 8));
|
||||
if (image == nullptr) {
|
||||
return new Error(String::FromFormat("LoadSprite: failed to allocate bitmap %d (%dx%d%d).",
|
||||
index, w, h, bpp * 8));
|
||||
}
|
||||
ImBufferPtr im_data(image->GetDataForWriting(), w * h * bpp, bpp);
|
||||
// (Optional) Handle storage options, reverse
|
||||
std::vector<uint8_t> indexed_buf;
|
||||
uint32_t palette[256];
|
||||
uint32_t pal_bpp = GetPaletteBPP(hdr.SFormat);
|
||||
if (pal_bpp > 0) { // read palette if format assumes one
|
||||
switch (pal_bpp) {
|
||||
case 2: for (uint32_t i = 0; i < hdr.PalCount; ++i) {
|
||||
palette[i] = _stream->ReadInt16();
|
||||
}
|
||||
break;
|
||||
case 4: for (uint32_t i = 0; i < hdr.PalCount; ++i) {
|
||||
palette[i] = _stream->ReadInt32();
|
||||
}
|
||||
break;
|
||||
default: assert(0); break;
|
||||
}
|
||||
indexed_buf.resize(w * h);
|
||||
im_data = ImBufferPtr(&indexed_buf[0], indexed_buf.size(), 1);
|
||||
}
|
||||
// (Optional) Decompress the image data into the temp buffer
|
||||
size_t in_data_size =
|
||||
((_version >= kSprfVersion_StorageFormats) || _compress != kSprCompress_None) ?
|
||||
(uint32_t)_stream->ReadInt32() : (w * h * bpp);
|
||||
if (hdr.Compress != kSprCompress_None) {
|
||||
// TODO: rewrite this to only make a choice once the SpriteFile is initialized
|
||||
// and use either function ptr or a decompressing stream class object
|
||||
if (in_data_size == 0) {
|
||||
return new Error(String::FromFormat("LoadSprite: bad compressed data for sprite %d.", index));
|
||||
}
|
||||
bool result;
|
||||
switch (hdr.Compress) {
|
||||
case kSprCompress_RLE: result = rle_decompress(im_data.Buf, im_data.Size, im_data.BPP, _stream.get());
|
||||
break;
|
||||
case kSprCompress_LZW: result = lzw_decompress(im_data.Buf, im_data.Size, im_data.BPP, _stream.get(), in_data_size);
|
||||
break;
|
||||
case kSprCompress_Deflate: result = inflate_decompress(im_data.Buf, im_data.Size, im_data.BPP, _stream.get(), in_data_size);
|
||||
break;
|
||||
default: assert(!"Unsupported compression type!"); result = false; break;
|
||||
}
|
||||
// TODO: test that not more than data_size was read!
|
||||
if (!result) {
|
||||
return new Error(String::FromFormat("LoadSprite: failed to decompress pixel array for sprite %d.", index));
|
||||
}
|
||||
}
|
||||
// Otherwise (no compression) read directly
|
||||
else {
|
||||
switch (im_data.BPP) {
|
||||
case 1: _stream->Read(im_data.Buf, im_data.Size);
|
||||
break;
|
||||
case 2: _stream->ReadArrayOfInt16(
|
||||
reinterpret_cast<int16_t *>(im_data.Buf), im_data.Size / sizeof(int16_t));
|
||||
break;
|
||||
case 4: _stream->ReadArrayOfInt32(
|
||||
reinterpret_cast<int32_t *>(im_data.Buf), im_data.Size / sizeof(int32_t));
|
||||
break;
|
||||
default: assert(0); break;
|
||||
}
|
||||
}
|
||||
// Finally revert storage options
|
||||
if (pal_bpp > 0) {
|
||||
UnpackIndexedBitmap(image.get(), im_data.Buf, im_data.Size, palette, hdr.PalCount);
|
||||
}
|
||||
|
||||
sprite = image.release(); // FIXME: pass unique_ptr in this function
|
||||
_curPos = index + 1; // mark correct pos
|
||||
return HError::None();
|
||||
}
|
||||
|
||||
HError SpriteFile::LoadRawData(sprkey_t index, SpriteDatHeader &hdr, std::vector<uint8_t> &data) {
|
||||
hdr = SpriteDatHeader();
|
||||
data.resize(0);
|
||||
if (index < 0 || (size_t)index >= _spriteData.size())
|
||||
return new Error(String::FromFormat("LoadSprite: slot index %d out of bounds (%d - %d).",
|
||||
index, 0, _spriteData.size() - 1));
|
||||
|
||||
if (_spriteData[index].Offset == 0)
|
||||
return HError::None(); // sprite is not in file
|
||||
|
||||
SeekToSprite(index);
|
||||
_curPos = -2; // mark undefined pos
|
||||
|
||||
ReadSprHeader(hdr, _stream.get(), _version, _compress);
|
||||
if (hdr.BPP == 0) return HError::None(); // empty slot, this is normal
|
||||
size_t data_size = 0;
|
||||
soff_t data_pos = _stream->GetPosition();
|
||||
// Optional palette
|
||||
size_t pal_size = hdr.PalCount * GetPaletteBPP(hdr.SFormat);
|
||||
data_size += pal_size;
|
||||
_stream->Seek(pal_size);
|
||||
// Pixel data
|
||||
if ((_version >= kSprfVersion_StorageFormats) || _compress != kSprCompress_None)
|
||||
data_size += (uint32_t)_stream->ReadInt32() + sizeof(uint32_t);
|
||||
else
|
||||
data_size += hdr.Width * hdr.Height * hdr.BPP;
|
||||
// Seek back and read all at once
|
||||
data.resize(data_size);
|
||||
_stream->Seek(data_pos, kSeekBegin);
|
||||
_stream->Read(&data[0], data_size);
|
||||
|
||||
_curPos = index + 1; // mark correct pos
|
||||
return HError::None();
|
||||
}
|
||||
|
||||
void SpriteFile::SeekToSprite(sprkey_t index) {
|
||||
// If we didn't just load the previous sprite, seek to it
|
||||
if (index != _curPos) {
|
||||
_stream->Seek(_spriteData[index].Offset, kSeekBegin);
|
||||
_curPos = index;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Finds the topmost occupied slot index
|
||||
static sprkey_t FindTopmostSprite(const std::vector<std::pair<bool, Bitmap *>> &sprites) {
|
||||
sprkey_t topmost = -1;
|
||||
for (sprkey_t i = 0; i < static_cast<sprkey_t>(sprites.size()); ++i)
|
||||
if (sprites[i].first)
|
||||
topmost = i;
|
||||
return topmost;
|
||||
}
|
||||
|
||||
int SaveSpriteFile(const String &save_to_file,
|
||||
const std::vector<std::pair<bool, Bitmap *> > &sprites,
|
||||
SpriteFile *read_from_file,
|
||||
int store_flags, SpriteCompression compress, SpriteFileIndex &index) {
|
||||
std::unique_ptr<Stream> output(File::CreateFile(save_to_file));
|
||||
if (output == nullptr)
|
||||
return -1;
|
||||
|
||||
sprkey_t lastslot = FindTopmostSprite(sprites);
|
||||
SpriteFileWriter writer(output);
|
||||
writer.Begin(store_flags, compress, lastslot);
|
||||
|
||||
std::unique_ptr<Bitmap> temp_bmp; // for disposing temp sprites
|
||||
std::vector<uint8_t> membuf; // for loading raw sprite data
|
||||
|
||||
const bool diff_compress =
|
||||
read_from_file &&
|
||||
(read_from_file->GetSpriteCompression() != compress ||
|
||||
read_from_file->GetStoreFlags() != store_flags);
|
||||
|
||||
for (sprkey_t i = 0; i <= lastslot; ++i) {
|
||||
if (!sprites[i].first) { // empty slot
|
||||
writer.WriteEmptySlot();
|
||||
continue;
|
||||
}
|
||||
|
||||
Bitmap *image = sprites[i].second;
|
||||
// if compression setting is different, load the sprite into memory
|
||||
// (otherwise we will be able to simply copy bytes from one file to another
|
||||
if ((image == nullptr) && diff_compress) {
|
||||
read_from_file->LoadSprite(i, image);
|
||||
temp_bmp.reset(image);
|
||||
}
|
||||
|
||||
// if managed to load an image - save it according the new compression settings
|
||||
if (image != nullptr) {
|
||||
writer.WriteBitmap(image);
|
||||
continue;
|
||||
} else if (diff_compress) {
|
||||
// sprite doesn't exist
|
||||
writer.WriteEmptySlot();
|
||||
continue;
|
||||
}
|
||||
|
||||
// Not in memory - and same compression option;
|
||||
// Directly copy the sprite bytes from the input file to the output
|
||||
SpriteDatHeader hdr;
|
||||
read_from_file->LoadRawData(i, hdr, membuf);
|
||||
if (hdr.BPP == 0) { // empty slot
|
||||
writer.WriteEmptySlot();
|
||||
continue;
|
||||
}
|
||||
writer.WriteRawData(hdr, &membuf[0], membuf.size());
|
||||
}
|
||||
writer.Finalize();
|
||||
|
||||
index = writer.GetIndex();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SaveSpriteIndex(const String &filename, const SpriteFileIndex &index) {
|
||||
// write the sprite index file
|
||||
Stream *out = File::CreateFile(filename);
|
||||
if (!out)
|
||||
return -1;
|
||||
// write "SPRINDEX" id
|
||||
out->WriteArray(spindexid, strlen(spindexid), 1);
|
||||
// write version
|
||||
out->WriteInt32(kSpridxfVersion_Current);
|
||||
out->WriteInt32(index.SpriteFileIDCheck);
|
||||
// write last sprite number and num sprites, to verify that
|
||||
// it matches the spr file
|
||||
out->WriteInt32(index.GetLastSlot());
|
||||
out->WriteInt32(index.GetCount());
|
||||
if (index.GetCount() > 0) {
|
||||
out->WriteArrayOfInt16(&index.Widths[0], index.Widths.size());
|
||||
out->WriteArrayOfInt16(&index.Heights[0], index.Heights.size());
|
||||
out->WriteArrayOfInt64(&index.Offsets[0], index.Offsets.size());
|
||||
}
|
||||
delete out;
|
||||
return 0;
|
||||
}
|
||||
|
||||
SpriteFileWriter::SpriteFileWriter(std::unique_ptr<Stream> &out) : _out(out) {
|
||||
}
|
||||
|
||||
void SpriteFileWriter::Begin(int store_flags, SpriteCompression compress, sprkey_t last_slot) {
|
||||
if (!_out) return;
|
||||
_index.SpriteFileIDCheck = g_system->getMillis();
|
||||
_storeFlags = store_flags;
|
||||
_compress = compress;
|
||||
|
||||
// sprite file version
|
||||
_out->WriteInt16(kSprfVersion_Current);
|
||||
_out->WriteArray(spriteFileSig, strlen(spriteFileSig), 1);
|
||||
_out->WriteInt8(_compress ? 1 : 0);
|
||||
_out->WriteInt32(_index.SpriteFileIDCheck);
|
||||
|
||||
// Remember and write provided "last slot" index,
|
||||
// but if it's not set (< 0) then we will have to return back later
|
||||
// and write correct one; this is done in Finalize().
|
||||
_lastSlotPos = _out->GetPosition();
|
||||
_out->WriteInt32(last_slot);
|
||||
|
||||
_out->WriteInt8(_storeFlags);
|
||||
_out->WriteInt8(0); // reserved
|
||||
_out->WriteInt8(0);
|
||||
_out->WriteInt8(0);
|
||||
|
||||
if (last_slot >= 0) { // allocate buffers to store the indexing info
|
||||
sprkey_t numsprits = last_slot + 1;
|
||||
_index.Offsets.reserve(numsprits);
|
||||
_index.Widths.reserve(numsprits);
|
||||
_index.Heights.reserve(numsprits);
|
||||
}
|
||||
}
|
||||
|
||||
void SpriteFileWriter::WriteBitmap(Bitmap *image) {
|
||||
if (!_out) return;
|
||||
int bpp = image->GetBPP();
|
||||
int w = image->GetWidth();
|
||||
int h = image->GetHeight();
|
||||
ImBufferCPtr im_data(image->GetData(), w * h * bpp, bpp);
|
||||
|
||||
// (Optional) Handle storage options
|
||||
std::vector<uint8_t> indexed_buf;
|
||||
uint32_t palette[256];
|
||||
uint32_t pal_count = 0;
|
||||
SpriteFormat sformat = kSprFmt_Undefined;
|
||||
|
||||
if ((_storeFlags & kSprStore_OptimizeForSize) != 0 && (image->GetBPP() > 1)) { // Try to store this sprite as an indexed bitmap
|
||||
uint32_t gen_pal_count;
|
||||
if (CreateIndexedBitmap(image, indexed_buf, palette, gen_pal_count) && gen_pal_count > 0) { // Test the resulting size, and switch if the paletted image is less
|
||||
if (im_data.Size > (indexed_buf.size() + gen_pal_count * image->GetBPP())) {
|
||||
im_data = ImBufferCPtr(&indexed_buf[0], indexed_buf.size(), 1);
|
||||
sformat = PaletteFormatForBPP(image->GetBPP());
|
||||
pal_count = gen_pal_count;
|
||||
}
|
||||
}
|
||||
}
|
||||
// (Optional) Compress the image data into the temp buffer
|
||||
SpriteCompression compress = kSprCompress_None;
|
||||
if (_compress != kSprCompress_Deflate)
|
||||
warning("TODO: Deflate not implemented, writing uncompressed BMP");
|
||||
else if (_compress != kSprCompress_None) {
|
||||
// TODO: rewrite this to only make a choice once the SpriteFile is initialized
|
||||
// and use either function ptr or a decompressing stream class object
|
||||
compress = _compress;
|
||||
VectorStream mems(_membuf, kStream_Write);
|
||||
bool result;
|
||||
switch (compress) {
|
||||
case kSprCompress_RLE: result = rle_compress(im_data.Buf, im_data.Size, im_data.BPP, &mems);
|
||||
break;
|
||||
case kSprCompress_LZW: result = lzw_compress(im_data.Buf, im_data.Size, im_data.BPP, &mems);
|
||||
break;
|
||||
case kSprCompress_Deflate: result = deflate_compress(im_data.Buf, im_data.Size, im_data.BPP, &mems);
|
||||
break;
|
||||
default: assert(!"Unsupported compression type!"); result = false; break;
|
||||
}
|
||||
// mark to write as a plain byte array
|
||||
im_data = result ? ImBufferCPtr(&_membuf[0], _membuf.size(), 1) : ImBufferCPtr();
|
||||
}
|
||||
|
||||
// Write the final data
|
||||
SpriteDatHeader hdr(bpp, sformat, pal_count, compress, w, h);
|
||||
WriteSpriteData(hdr, im_data.Buf, im_data.Size, im_data.BPP, palette);
|
||||
_membuf.clear();
|
||||
}
|
||||
|
||||
static inline void WriteSprHeader(const SpriteDatHeader &hdr, Stream *out) {
|
||||
out->WriteInt8(hdr.BPP);
|
||||
out->WriteInt8(hdr.SFormat);
|
||||
out->WriteInt8(hdr.PalCount > 0 ? (uint8_t)(hdr.PalCount - 1) : 0);
|
||||
out->WriteInt8(hdr.Compress);
|
||||
out->WriteInt16(hdr.Width);
|
||||
out->WriteInt16(hdr.Height);
|
||||
}
|
||||
|
||||
void SpriteFileWriter::WriteSpriteData(const SpriteDatHeader &hdr,
|
||||
const uint8_t *im_data, size_t im_data_sz, int im_bpp,
|
||||
const uint32_t palette[256]) {
|
||||
// Add index entry and write resulting data to the stream
|
||||
soff_t sproff = _out->GetPosition();
|
||||
_index.Offsets.push_back(sproff);
|
||||
_index.Widths.push_back(hdr.Width);
|
||||
_index.Heights.push_back(hdr.Height);
|
||||
WriteSprHeader(hdr, _out.get());
|
||||
// write palette, if available
|
||||
int pal_bpp = GetPaletteBPP(hdr.SFormat);
|
||||
if (pal_bpp > 0) {
|
||||
assert(hdr.PalCount > 0);
|
||||
switch (pal_bpp) {
|
||||
case 2: for (uint32_t i = 0; i < hdr.PalCount; ++i) {
|
||||
_out->WriteInt16(palette[i]);
|
||||
}
|
||||
break;
|
||||
case 4: for (uint32_t i = 0; i < hdr.PalCount; ++i) {
|
||||
_out->WriteInt32(palette[i]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
// write the image pixel data
|
||||
_out->WriteInt32(im_data_sz);
|
||||
switch (im_bpp) {
|
||||
case 1: _out->Write(im_data, im_data_sz);
|
||||
break;
|
||||
case 2: _out->WriteArrayOfInt16(reinterpret_cast<const int16_t *>(im_data),
|
||||
im_data_sz / sizeof(int16_t));
|
||||
break;
|
||||
case 4: _out->WriteArrayOfInt32(reinterpret_cast<const int32_t *>(im_data),
|
||||
im_data_sz / sizeof(int32_t));
|
||||
break;
|
||||
default: assert(0); break;
|
||||
}
|
||||
}
|
||||
|
||||
void SpriteFileWriter::WriteEmptySlot() {
|
||||
if (!_out) return;
|
||||
soff_t sproff = _out->GetPosition();
|
||||
_out->WriteInt16(0); // write invalid color depth to mark empty slot
|
||||
_index.Offsets.push_back(sproff);
|
||||
_index.Widths.push_back(0);
|
||||
_index.Heights.push_back(0);
|
||||
}
|
||||
|
||||
void SpriteFileWriter::WriteRawData(const SpriteDatHeader &hdr, const uint8_t *data, size_t data_sz) {
|
||||
if (!_out) return;
|
||||
soff_t sproff = _out->GetPosition();
|
||||
_index.Offsets.push_back(sproff);
|
||||
_index.Widths.push_back(hdr.Width);
|
||||
_index.Heights.push_back(hdr.Height);
|
||||
WriteSprHeader(hdr, _out.get());
|
||||
_out->Write(data, data_sz);
|
||||
}
|
||||
|
||||
void SpriteFileWriter::Finalize() {
|
||||
if (!_out || _lastSlotPos < 0) return;
|
||||
_out->Seek(_lastSlotPos, kSeekBegin);
|
||||
_out->WriteInt32(_index.GetLastSlot());
|
||||
_out.reset();
|
||||
}
|
||||
|
||||
} // namespace Shared
|
||||
} // namespace AGS
|
||||
} // namespace AGS3
|
||||
237
engines/ags/shared/ac/sprite_file.h
Normal file
237
engines/ags/shared/ac/sprite_file.h
Normal 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// SpriteFile class handles sprite file parsing and streaming sprites.
|
||||
// SpriteFileWriter manages writing sprites into the output stream one by one,
|
||||
// accumulating index information, and may therefore be suitable for a variety
|
||||
// of situations.
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef AGS_SHARED_AC_SPRITE_FILE_H
|
||||
#define AGS_SHARED_AC_SPRITE_FILE_H
|
||||
|
||||
#include "ags/shared/core/types.h"
|
||||
#include "common/std/memory.h"
|
||||
#include "common/std/vector.h"
|
||||
#include "ags/shared/util/stream.h"
|
||||
#include "ags/globals.h"
|
||||
|
||||
namespace AGS3 {
|
||||
namespace AGS {
|
||||
namespace Shared {
|
||||
|
||||
class Bitmap;
|
||||
|
||||
// TODO: research old version differences
|
||||
enum SpriteFileVersion {
|
||||
kSprfVersion_Undefined = 0,
|
||||
kSprfVersion_Uncompressed = 4,
|
||||
kSprfVersion_Compressed = 5,
|
||||
kSprfVersion_Last32bit = 6,
|
||||
kSprfVersion_64bit = 10,
|
||||
kSprfVersion_HighSpriteLimit = 11,
|
||||
kSprfVersion_StorageFormats = 12,
|
||||
kSprfVersion_Current = kSprfVersion_StorageFormats
|
||||
};
|
||||
|
||||
enum SpriteIndexFileVersion {
|
||||
kSpridxfVersion_Initial = 1,
|
||||
kSpridxfVersion_Last32bit = 2,
|
||||
kSpridxfVersion_64bit = 10,
|
||||
kSpridxfVersion_HighSpriteLimit = 11,
|
||||
kSpridxfVersion_Current = kSpridxfVersion_HighSpriteLimit
|
||||
};
|
||||
|
||||
// Instructions to how the sprites are allowed to be stored
|
||||
enum SpriteStorage {
|
||||
// When possible convert the sprite into another format for less disk space
|
||||
// e.g. save 16/32-bit images as 8-bit colormaps with palette
|
||||
kSprStore_OptimizeForSize = 0x01
|
||||
};
|
||||
|
||||
// Format in which the sprite's pixel data is stored
|
||||
enum SpriteFormat {
|
||||
kSprFmt_Undefined = 0, // undefined, or keep as-is
|
||||
// Encoded as a 8-bit colormap with palette of 24-bit RGB values
|
||||
kSprFmt_PaletteRgb888 = 32,
|
||||
// Encoded as a 8-bit colormap with palette of 32-bit ARGB values
|
||||
kSprFmt_PaletteArgb8888 = 33,
|
||||
// Encoded as a 8-bit colormap with palette of 16-bit RGB565 values
|
||||
kSprFmt_PaletteRgb565 = 34
|
||||
};
|
||||
|
||||
enum SpriteCompression {
|
||||
kSprCompress_None = 0,
|
||||
kSprCompress_RLE,
|
||||
kSprCompress_LZW,
|
||||
kSprCompress_Deflate
|
||||
};
|
||||
|
||||
typedef int32_t sprkey_t;
|
||||
|
||||
// SpriteFileIndex contains sprite file's table of contents
|
||||
struct SpriteFileIndex {
|
||||
int SpriteFileIDCheck = 0; // tag matching sprite file and index file
|
||||
std::vector<int16_t> Widths;
|
||||
std::vector<int16_t> Heights;
|
||||
std::vector<soff_t> Offsets;
|
||||
|
||||
inline size_t GetCount() const {
|
||||
return Offsets.size();
|
||||
}
|
||||
inline sprkey_t GetLastSlot() const {
|
||||
return (sprkey_t)GetCount() - 1;
|
||||
}
|
||||
};
|
||||
|
||||
// Invidual sprite data header (as read from the file)
|
||||
struct SpriteDatHeader {
|
||||
int BPP = 0; // color depth (bytes per pixel); or input format
|
||||
SpriteFormat SFormat = kSprFmt_Undefined; // storage format
|
||||
uint32_t PalCount = 0; // palette length, if applicable to storage format
|
||||
SpriteCompression Compress = kSprCompress_None; // compression type
|
||||
int Width = 0; // sprite's width
|
||||
int Height = 0; // sprite's height
|
||||
|
||||
SpriteDatHeader() = default;
|
||||
SpriteDatHeader(int bpp, SpriteFormat sformat = kSprFmt_Undefined,
|
||||
uint32_t pal_count = 0, SpriteCompression compress = kSprCompress_None,
|
||||
int w = 0, int h = 0) : BPP(bpp), SFormat(sformat), PalCount(pal_count),
|
||||
Compress(compress), Width(w), Height(h) {
|
||||
}
|
||||
};
|
||||
|
||||
// SpriteFile opens a sprite file for reading, reports general information,
|
||||
// and lets read sprites in any order.
|
||||
class SpriteFile {
|
||||
public:
|
||||
// Standart sprite file and sprite index names
|
||||
static const char *DefaultSpriteFileName;
|
||||
static const char *DefaultSpriteIndexName;
|
||||
|
||||
SpriteFile();
|
||||
// Loads sprite reference information and inits sprite stream
|
||||
HError OpenFile(const String &filename, const String &sprindex_filename,
|
||||
std::vector<Size> &metrics);
|
||||
// Closes stream; no reading will be possible unless opened again
|
||||
void Close();
|
||||
|
||||
int GetStoreFlags() const;
|
||||
// Tells if bitmaps in the file are compressed
|
||||
SpriteCompression GetSpriteCompression() const;
|
||||
// Tells the highest known sprite index
|
||||
sprkey_t GetTopmostSprite() const;
|
||||
|
||||
// Loads sprite index file
|
||||
bool LoadSpriteIndexFile(const String &filename, int expectedFileID,
|
||||
soff_t spr_initial_offs, sprkey_t topmost, std::vector<Size> &metrics);
|
||||
// Rebuilds sprite index from the main sprite file
|
||||
HError RebuildSpriteIndex(Stream *in, sprkey_t topmost,
|
||||
std::vector<Size> &metrics);
|
||||
|
||||
// Loads an image data and creates a ready bitmap
|
||||
HError LoadSprite(sprkey_t index, Bitmap *&sprite);
|
||||
// Loads a raw sprite element data into the buffer, stores header info separately
|
||||
HError LoadRawData(sprkey_t index, SpriteDatHeader &hdr, std::vector<uint8_t> &data);
|
||||
|
||||
private:
|
||||
// Seek stream to sprite
|
||||
void SeekToSprite(sprkey_t index);
|
||||
|
||||
// Internal sprite reference
|
||||
struct SpriteRef {
|
||||
soff_t Offset = 0; // data offset
|
||||
size_t RawSize = 0; // file size of element, in bytes
|
||||
// TODO: RawSize is currently unused, due to incompleteness of spriteindex format
|
||||
};
|
||||
|
||||
// Array of sprite references
|
||||
std::vector<SpriteRef> _spriteData;
|
||||
std::unique_ptr<Stream> _stream; // the sprite stream
|
||||
SpriteFileVersion _version = kSprfVersion_Current;
|
||||
int _storeFlags = 0; // storage flags, specify how sprites may be stored
|
||||
SpriteCompression _compress = kSprCompress_None; // sprite compression typ
|
||||
sprkey_t _curPos; // current stream position (sprite slot)
|
||||
};
|
||||
|
||||
// SpriteFileWriter class writes a sprite file in a requested format.
|
||||
// Start using it by calling Begin, write ready bitmaps or copy raw sprite data
|
||||
// over slot by slot, then call Finalize to let it close the format correctly.
|
||||
class SpriteFileWriter {
|
||||
public:
|
||||
SpriteFileWriter(std::unique_ptr<Stream> &out);
|
||||
~SpriteFileWriter() {
|
||||
}
|
||||
|
||||
// Get the sprite index, accumulated after write
|
||||
const SpriteFileIndex &GetIndex() const {
|
||||
return _index;
|
||||
}
|
||||
|
||||
// Initializes new sprite file format;
|
||||
// store_flags are SpriteStorage;
|
||||
// optionally hint how many sprites will be written.
|
||||
void Begin(int store_flags, SpriteCompression compress, sprkey_t last_slot = -1);
|
||||
// Writes a bitmap into file, compressing if necessary
|
||||
void WriteBitmap(Bitmap *image);
|
||||
// Writes an empty slot marker
|
||||
void WriteEmptySlot();
|
||||
// Writes a raw sprite data without any additional processing
|
||||
void WriteRawData(const SpriteDatHeader &hdr, const uint8_t *data, size_t data_sz);
|
||||
// Finalizes current format; no further writing is possible after this
|
||||
void Finalize();
|
||||
|
||||
private:
|
||||
// Writes prepared image data in a proper file format, following explicit data_bpp rule
|
||||
void WriteSpriteData(const SpriteDatHeader &hdr,
|
||||
const uint8_t *im_data, size_t im_data_sz, int im_bpp,
|
||||
const uint32_t palette[256]);
|
||||
|
||||
std::unique_ptr<Stream> &_out;
|
||||
int _storeFlags = 0;
|
||||
SpriteCompression _compress = kSprCompress_None;
|
||||
soff_t _lastSlotPos = -1; // last slot save position in file
|
||||
// sprite index accumulated on write for reporting back to user
|
||||
SpriteFileIndex _index;
|
||||
// compression buffer
|
||||
std::vector<uint8_t> _membuf;
|
||||
};
|
||||
|
||||
// Saves all sprites to file; fills in index data for external use.
|
||||
// TODO: refactor to be able to save main file and index file separately (separate function for gather data?)
|
||||
// Accepts available sprites as pairs of bool and Bitmap pointer, where boolean value
|
||||
// tells if sprite exists and Bitmap pointer may be null;
|
||||
// If a sprite's bitmap is missing, it will try reading one from the input file stream.
|
||||
int SaveSpriteFile(const String &save_to_file,
|
||||
const std::vector<std::pair<bool, Bitmap *> > &sprites,
|
||||
SpriteFile *read_from_file, // optional file to read missing sprites from
|
||||
int store_flags, SpriteCompression compress, SpriteFileIndex &index);
|
||||
// Saves sprite index table in a separate file
|
||||
extern int SaveSpriteIndex(const String &filename, const SpriteFileIndex &index);
|
||||
|
||||
} // namespace Shared
|
||||
} // namespace AGS
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
179
engines/ags/shared/ac/view.cpp
Normal file
179
engines/ags/shared/ac/view.cpp
Normal 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ags/shared/ac/view.h"
|
||||
#include "ags/shared/util/stream.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
using AGS::Shared::Stream;
|
||||
|
||||
ViewFrame::ViewFrame()
|
||||
: pic(0)
|
||||
, xoffs(0)
|
||||
, yoffs(0)
|
||||
, speed(0)
|
||||
, flags(0)
|
||||
, sound(-1)
|
||||
, audioclip(-1) {
|
||||
reserved_for_future[0] = 0;
|
||||
reserved_for_future[1] = 0;
|
||||
}
|
||||
|
||||
void ViewFrame::ReadFromFile(Stream *in) {
|
||||
pic = in->ReadInt32();
|
||||
xoffs = in->ReadInt16();
|
||||
yoffs = in->ReadInt16();
|
||||
speed = in->ReadInt16();
|
||||
in->ReadInt16(); // alignment padding to int32
|
||||
flags = in->ReadInt32();
|
||||
sound = in->ReadInt32();
|
||||
in->ReadInt32(); // reserved 1
|
||||
in->ReadInt32(); // reserved 1
|
||||
}
|
||||
|
||||
void ViewFrame::WriteToFile(Stream *out) {
|
||||
out->WriteInt32(pic);
|
||||
out->WriteInt16(xoffs);
|
||||
out->WriteInt16(yoffs);
|
||||
out->WriteInt16(speed);
|
||||
out->WriteInt16(0); // alignment padding to int32
|
||||
out->WriteInt32(flags);
|
||||
out->WriteInt32(sound);
|
||||
out->WriteInt32(0);
|
||||
out->WriteInt32(0);
|
||||
}
|
||||
|
||||
ViewLoopNew::ViewLoopNew()
|
||||
: numFrames(0)
|
||||
, flags(0) {
|
||||
}
|
||||
|
||||
bool ViewLoopNew::RunNextLoop() {
|
||||
return (flags & LOOPFLAG_RUNNEXTLOOP);
|
||||
}
|
||||
|
||||
void ViewLoopNew::Initialize(int frameCount) {
|
||||
numFrames = frameCount;
|
||||
flags = 0;
|
||||
// an extra frame is allocated to prevent crashes with empty loops
|
||||
frames.resize(numFrames > 0 ? numFrames : 1);
|
||||
}
|
||||
|
||||
void ViewLoopNew::Dispose() {
|
||||
frames.clear();
|
||||
numFrames = 0;
|
||||
}
|
||||
|
||||
void ViewLoopNew::WriteToFile_v321(Stream *out) {
|
||||
out->WriteInt16(static_cast<uint16_t>(numFrames));
|
||||
out->WriteInt32(flags);
|
||||
WriteFrames(out);
|
||||
}
|
||||
|
||||
void ViewLoopNew::WriteFrames(Stream *out) {
|
||||
for (int i = 0; i < numFrames; ++i) {
|
||||
frames[i].WriteToFile(out);
|
||||
}
|
||||
}
|
||||
|
||||
void ViewLoopNew::ReadFromFile_v321(Stream *in) {
|
||||
Initialize(static_cast<uint16_t>(in->ReadInt16()));
|
||||
flags = in->ReadInt32();
|
||||
ReadFrames(in);
|
||||
}
|
||||
|
||||
void ViewLoopNew::ReadFrames(Stream *in) {
|
||||
for (int i = 0; i < numFrames; ++i) {
|
||||
frames[i].ReadFromFile(in);
|
||||
}
|
||||
}
|
||||
|
||||
ViewStruct::ViewStruct()
|
||||
: numLoops(0) {
|
||||
}
|
||||
|
||||
void ViewStruct::Initialize(int loopCount) {
|
||||
numLoops = loopCount;
|
||||
loops.resize(numLoops);
|
||||
}
|
||||
|
||||
void ViewStruct::Dispose() {
|
||||
loops.clear();
|
||||
numLoops = 0;
|
||||
}
|
||||
|
||||
void ViewStruct::WriteToFile(Stream *out) {
|
||||
out->WriteInt16(static_cast<uint16_t>(numLoops));
|
||||
for (int i = 0; i < numLoops; i++) {
|
||||
loops[i].WriteToFile_v321(out);
|
||||
}
|
||||
}
|
||||
|
||||
void ViewStruct::ReadFromFile(Stream *in) {
|
||||
Initialize(static_cast<uint16_t>(in->ReadInt16()));
|
||||
|
||||
for (int i = 0; i < numLoops; i++) {
|
||||
loops[i].ReadFromFile_v321(in);
|
||||
}
|
||||
}
|
||||
|
||||
ViewStruct272::ViewStruct272()
|
||||
: numloops(0) {
|
||||
memset(numframes, 0, sizeof(numframes));
|
||||
memset(loopflags, 0, sizeof(loopflags));
|
||||
}
|
||||
|
||||
void ViewStruct272::ReadFromFile(Stream *in) {
|
||||
numloops = in->ReadInt16();
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
numframes[i] = in->ReadInt16();
|
||||
}
|
||||
in->ReadInt16(); // alignment padding to int32
|
||||
in->ReadArrayOfInt32(loopflags, 16);
|
||||
for (int j = 0; j < 16; ++j) {
|
||||
for (int i = 0; i < 20; ++i) {
|
||||
frames[j][i].ReadFromFile(in);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Convert272ViewsToNew(const std::vector<ViewStruct272> &oldv, std::vector<ViewStruct> &newv) {
|
||||
for (size_t a = 0; a < oldv.size(); a++) {
|
||||
newv[a].Initialize(oldv[a].numloops);
|
||||
|
||||
for (int b = 0; b < oldv[a].numloops; b++) {
|
||||
newv[a].loops[b].Initialize(oldv[a].numframes[b]);
|
||||
|
||||
if ((oldv[a].numframes[b] > 0) &&
|
||||
(oldv[a].frames[b][oldv[a].numframes[b] - 1].pic == -1)) {
|
||||
newv[a].loops[b].flags = LOOPFLAG_RUNNEXTLOOP;
|
||||
newv[a].loops[b].numFrames--;
|
||||
} else
|
||||
newv[a].loops[b].flags = 0;
|
||||
|
||||
for (int c = 0; c < newv[a].loops[b].numFrames; c++)
|
||||
newv[a].loops[b].frames[c] = oldv[a].frames[b][c];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace AGS3
|
||||
101
engines/ags/shared/ac/view.h
Normal file
101
engines/ags/shared/ac/view.h
Normal file
@@ -0,0 +1,101 @@
|
||||
/* 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 AGS_SHARED_AC_VIEW_H
|
||||
#define AGS_SHARED_AC_VIEW_H
|
||||
|
||||
#include "common/std/vector.h"
|
||||
#include "ags/shared/core/types.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
namespace AGS {
|
||||
namespace Shared {
|
||||
class Stream;
|
||||
} // namespace Shared
|
||||
} // namespace AGS
|
||||
|
||||
using namespace AGS; // FIXME later
|
||||
|
||||
#define VFLG_FLIPSPRITE 1
|
||||
|
||||
struct ViewFrame {
|
||||
int pic;
|
||||
short xoffs, yoffs;
|
||||
short speed;
|
||||
int flags; // VFLG_* flags
|
||||
int sound; // play sound when this frame comes round
|
||||
int reserved_for_future[2]; // kept only for plugin api
|
||||
// not saved, set at runtime only
|
||||
int audioclip; // actual audio clip reference (in case sound is a legacy number)
|
||||
|
||||
ViewFrame();
|
||||
|
||||
void ReadFromFile(Shared::Stream *in);
|
||||
void WriteToFile(Shared::Stream *out);
|
||||
};
|
||||
|
||||
#define LOOPFLAG_RUNNEXTLOOP 1
|
||||
|
||||
struct ViewLoopNew {
|
||||
int numFrames;
|
||||
int flags;
|
||||
std::vector<ViewFrame> frames;
|
||||
// NOTE: we still need numFrames:
|
||||
// as we always allocate at least 1 frame for safety, to avoid crashes,
|
||||
// but have to report "logical" number of frames for the engine API.
|
||||
|
||||
ViewLoopNew();
|
||||
void Initialize(int frameCount);
|
||||
void Dispose();
|
||||
bool RunNextLoop();
|
||||
void WriteToFile_v321(Shared::Stream *out);
|
||||
void ReadFromFile_v321(Shared::Stream *in);
|
||||
void WriteFrames(Shared::Stream *out);
|
||||
void ReadFrames(Shared::Stream *in);
|
||||
};
|
||||
|
||||
struct ViewStruct {
|
||||
int numLoops;
|
||||
std::vector<ViewLoopNew> loops;
|
||||
|
||||
ViewStruct();
|
||||
void Initialize(int loopCount);
|
||||
void Dispose();
|
||||
void WriteToFile(Shared::Stream *out);
|
||||
void ReadFromFile(Shared::Stream *in);
|
||||
};
|
||||
|
||||
struct ViewStruct272 {
|
||||
short numloops;
|
||||
short numframes[16];
|
||||
int32_t loopflags[16];
|
||||
ViewFrame frames[16][20];
|
||||
|
||||
ViewStruct272();
|
||||
void ReadFromFile(Shared::Stream *in);
|
||||
};
|
||||
|
||||
extern void Convert272ViewsToNew(const std::vector<ViewStruct272> &oldv, std::vector<ViewStruct> &newv);
|
||||
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
185
engines/ags/shared/ac/words_dictionary.cpp
Normal file
185
engines/ags/shared/ac/words_dictionary.cpp
Normal file
@@ -0,0 +1,185 @@
|
||||
/* 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/std/algorithm.h"
|
||||
#include "ags/shared/ac/words_dictionary.h"
|
||||
#include "ags/shared/util/stream.h"
|
||||
#include "ags/shared/util/string_compat.h"
|
||||
#include "ags/globals.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
using namespace AGS::Shared;
|
||||
|
||||
WordsDictionary::WordsDictionary()
|
||||
: num_words(0)
|
||||
, word(nullptr)
|
||||
, wordnum(nullptr) {
|
||||
}
|
||||
|
||||
WordsDictionary::~WordsDictionary() {
|
||||
free_memory();
|
||||
}
|
||||
|
||||
void WordsDictionary::allocate_memory(int wordCount) {
|
||||
num_words = wordCount;
|
||||
if (num_words > 0) {
|
||||
word = new char *[wordCount];
|
||||
word[0] = new char[wordCount * MAX_PARSER_WORD_LENGTH];
|
||||
wordnum = new short[wordCount];
|
||||
for (int i = 1; i < wordCount; i++) {
|
||||
word[i] = word[0] + MAX_PARSER_WORD_LENGTH * i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WordsDictionary::free_memory() {
|
||||
if (num_words > 0) {
|
||||
delete[] word[0];
|
||||
delete[] word;
|
||||
delete[] wordnum;
|
||||
word = nullptr;
|
||||
wordnum = nullptr;
|
||||
num_words = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void WordsDictionary::sort() {
|
||||
int aa, bb;
|
||||
for (aa = 0; aa < num_words; aa++) {
|
||||
for (bb = aa + 1; bb < num_words; bb++) {
|
||||
if (((wordnum[aa] == wordnum[bb]) && (ags_stricmp(word[aa], word[bb]) > 0))
|
||||
|| (wordnum[aa] > wordnum[bb])) {
|
||||
short temp = wordnum[aa];
|
||||
char tempst[30];
|
||||
|
||||
wordnum[aa] = wordnum[bb];
|
||||
wordnum[bb] = temp;
|
||||
snprintf(tempst, MAX_PARSER_WORD_LENGTH, "%s", word[aa]);
|
||||
snprintf(word[aa], MAX_PARSER_WORD_LENGTH, "%s", word[bb]);
|
||||
snprintf(word[bb], MAX_PARSER_WORD_LENGTH, "%s", tempst);
|
||||
bb = aa;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int WordsDictionary::find_index(const char *wrem) {
|
||||
int aa;
|
||||
for (aa = 0; aa < num_words; aa++) {
|
||||
if (ags_stricmp(wrem, word[aa]) == 0)
|
||||
return aa;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void decrypt_text(char *toenc, size_t buf_sz) {
|
||||
int adx = 0;
|
||||
const char *p_end = toenc + buf_sz;
|
||||
|
||||
while (toenc < p_end) {
|
||||
toenc[0] -= _G(passwencstring)[adx];
|
||||
if (toenc[0] == 0)
|
||||
break;
|
||||
|
||||
adx++;
|
||||
toenc++;
|
||||
|
||||
if (adx > 10)
|
||||
adx = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void read_string_decrypt(Stream *in, char *buf, size_t buf_sz) {
|
||||
size_t len = in->ReadInt32();
|
||||
size_t slen = MIN(buf_sz - 1, len);
|
||||
in->Read(buf, slen);
|
||||
if (len > slen)
|
||||
in->Seek(len - slen);
|
||||
decrypt_text(buf, slen);
|
||||
buf[slen] = 0;
|
||||
}
|
||||
|
||||
String read_string_decrypt(Stream *in, std::vector<char> &dec_buf) {
|
||||
size_t len = in->ReadInt32();
|
||||
dec_buf.resize(len + 1);
|
||||
in->Read(dec_buf.data(), len);
|
||||
decrypt_text(dec_buf.data(), len);
|
||||
dec_buf.back() = 0; // null terminate in case read string does not have one
|
||||
return String(dec_buf.data());
|
||||
}
|
||||
|
||||
void read_dictionary(WordsDictionary *dict, Stream *out) {
|
||||
int ii;
|
||||
|
||||
dict->allocate_memory(out->ReadInt32());
|
||||
for (ii = 0; ii < dict->num_words; ii++) {
|
||||
read_string_decrypt(out, dict->word[ii], MAX_PARSER_WORD_LENGTH);
|
||||
dict->wordnum[ii] = out->ReadInt16();
|
||||
}
|
||||
}
|
||||
|
||||
#if defined (OBSOLETE)
|
||||
// TODO: not a part of wordsdictionary, move to obsoletes
|
||||
void freadmissout(short *pptr, Stream *in) {
|
||||
in->ReadArrayOfInt16(&pptr[0], 5);
|
||||
in->ReadArrayOfInt16(&pptr[7], NUM_CONDIT - 7);
|
||||
pptr[5] = pptr[6] = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
void encrypt_text(char *toenc) {
|
||||
int adx = 0, tobreak = 0;
|
||||
|
||||
while (tobreak == 0) {
|
||||
if (toenc[0] == 0)
|
||||
tobreak = 1;
|
||||
|
||||
toenc[0] += _G(passwencstring)[adx];
|
||||
adx++;
|
||||
toenc++;
|
||||
|
||||
if (adx > 10)
|
||||
adx = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void write_string_encrypt(Stream *out, const char *s) {
|
||||
int stlent = (int)strlen(s) + 1;
|
||||
|
||||
out->WriteInt32(stlent);
|
||||
char *enc = ags_strdup(s);
|
||||
encrypt_text(enc);
|
||||
out->WriteArray(enc, stlent, 1);
|
||||
free(enc);
|
||||
}
|
||||
|
||||
void write_dictionary(WordsDictionary *dict, Stream *out) {
|
||||
int ii;
|
||||
|
||||
out->WriteInt32(dict->num_words);
|
||||
for (ii = 0; ii < dict->num_words; ii++) {
|
||||
write_string_encrypt(out, dict->word[ii]);
|
||||
out->WriteInt16(dict->wordnum[ii]);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace AGS3
|
||||
76
engines/ags/shared/ac/words_dictionary.h
Normal file
76
engines/ags/shared/ac/words_dictionary.h
Normal file
@@ -0,0 +1,76 @@
|
||||
/* 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 AGS_SHARED_AC_WORDS_DICTIONARY_H
|
||||
#define AGS_SHARED_AC_WORDS_DICTIONARY_H
|
||||
|
||||
#include "common/std/vector.h"
|
||||
#include "ags/shared/core/types.h"
|
||||
#include "ags/shared/util/string.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
namespace AGS {
|
||||
namespace Shared {
|
||||
class Stream;
|
||||
} // namespace Shared
|
||||
} // namespace AGS
|
||||
|
||||
using namespace AGS; // FIXME later
|
||||
|
||||
#define MAX_PARSER_WORD_LENGTH 30
|
||||
#define ANYWORD 29999
|
||||
#define RESTOFLINE 30000
|
||||
|
||||
struct WordsDictionary {
|
||||
int num_words;
|
||||
char **word;
|
||||
short *wordnum;
|
||||
|
||||
WordsDictionary();
|
||||
~WordsDictionary();
|
||||
void allocate_memory(int wordCount);
|
||||
void free_memory();
|
||||
void sort();
|
||||
int find_index(const char *);
|
||||
};
|
||||
|
||||
// Decrypts text found in the given buffer, writes back to the same buffer
|
||||
extern void decrypt_text(char *buf, size_t buf_sz);
|
||||
// Reads an encrypted string from the stream and decrypts into the provided buffer
|
||||
extern void read_string_decrypt(Shared::Stream *in, char *buf, size_t buf_sz);
|
||||
// Reads an encrypted string from the stream and returns as a string;
|
||||
// uses provided vector as a temporary decryption buffer (avoid extra allocs)
|
||||
extern Shared::String read_string_decrypt(Shared::Stream *in, std::vector<char> &dec_buf);
|
||||
extern void read_dictionary(WordsDictionary *dict, Shared::Stream *in);
|
||||
|
||||
#if defined (OBSOLETE)
|
||||
// TODO: not a part of wordsdictionary, move to obsoletes
|
||||
extern void freadmissout(short *pptr, Shared::Stream *in);
|
||||
#endif
|
||||
|
||||
extern void encrypt_text(char *toenc);
|
||||
extern void write_string_encrypt(Shared::Stream *out, const char *s);
|
||||
extern void write_dictionary(WordsDictionary *dict, Shared::Stream *out);
|
||||
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
36
engines/ags/shared/core/asset.cpp
Normal file
36
engines/ags/shared/core/asset.cpp
Normal file
@@ -0,0 +1,36 @@
|
||||
/* 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 "ags/shared/core/asset.h"
|
||||
|
||||
namespace AGS3 {
|
||||
namespace AGS {
|
||||
namespace Shared {
|
||||
|
||||
AssetInfo::AssetInfo()
|
||||
: LibUid(0)
|
||||
, Offset(0)
|
||||
, Size(0) {
|
||||
}
|
||||
|
||||
} // namespace Shared
|
||||
} // namespace AGS
|
||||
} // namespace AGS3
|
||||
64
engines/ags/shared/core/asset.h
Normal file
64
engines/ags/shared/core/asset.h
Normal file
@@ -0,0 +1,64 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// AssetInfo and AssetLibInfo - classes describing generic asset library.
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef AGS_SHARED_CORE_ASSET_H
|
||||
#define AGS_SHARED_CORE_ASSET_H
|
||||
|
||||
#include "common/std/vector.h"
|
||||
#include "ags/shared/util/string.h"
|
||||
|
||||
namespace AGS3 {
|
||||
namespace AGS {
|
||||
namespace Shared {
|
||||
|
||||
// Information on single asset
|
||||
struct AssetInfo {
|
||||
// A pair of filename and libuid is assumed to be unique in game scope
|
||||
String FileName; // filename associated with asset
|
||||
int32_t LibUid; // index of library partition (separate file)
|
||||
soff_t Offset; // asset's position in library file (in bytes)
|
||||
soff_t Size; // asset's size (in bytes)
|
||||
|
||||
AssetInfo();
|
||||
};
|
||||
|
||||
// Information on multifile asset library
|
||||
struct AssetLibInfo {
|
||||
String BasePath; // full path to the base filename
|
||||
String BaseDir; // library's directory
|
||||
String BaseFileName; // library's base (head) filename
|
||||
std::vector<String> LibFileNames; // filename for each library part
|
||||
|
||||
// Library contents
|
||||
std::vector<AssetInfo> AssetInfos; // information on contained assets
|
||||
};
|
||||
|
||||
} // namespace Shared
|
||||
} // namespace AGS
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
304
engines/ags/shared/core/asset_manager.cpp
Normal file
304
engines/ags/shared/core/asset_manager.cpp
Normal file
@@ -0,0 +1,304 @@
|
||||
/* 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/memstream.h"
|
||||
#include "common/std/algorithm.h"
|
||||
#include "common/std/utility.h"
|
||||
#include "ags/shared/core/platform.h"
|
||||
#include "ags/shared/core/asset_manager.h"
|
||||
#include "ags/shared/util/directory.h"
|
||||
#include "ags/shared/util/multi_file_lib.h"
|
||||
#include "ags/shared/util/path.h"
|
||||
#include "ags/shared/util/string_utils.h" // cbuf_to_string_and_free
|
||||
|
||||
namespace AGS3 {
|
||||
namespace AGS {
|
||||
namespace Shared {
|
||||
|
||||
inline static bool IsAssetLibDir(const AssetLibInfo *lib) {
|
||||
return lib->BaseFileName.IsEmpty();
|
||||
}
|
||||
|
||||
bool AssetManager::AssetLibEx::TestFilter(const String &filter) const {
|
||||
return filter == "*" ||
|
||||
(std::find(Filters.begin(), Filters.end(), filter) != Filters.end());
|
||||
}
|
||||
|
||||
// Asset library sorting function, directories have priority
|
||||
bool SortLibsPriorityDir(const AssetLibInfo *lib1, const AssetLibInfo *lib2) {
|
||||
// first element is less if it's a directory while second is a lib
|
||||
return IsAssetLibDir(lib1) && !IsAssetLibDir(lib2);
|
||||
}
|
||||
|
||||
// Asset library sorting function, packages have priority
|
||||
bool SortLibsPriorityLib(const AssetLibInfo *lib1, const AssetLibInfo *lib2) {
|
||||
// first element is less if it's a lib while second is a directory
|
||||
return !IsAssetLibDir(lib1) && IsAssetLibDir(lib2);
|
||||
}
|
||||
|
||||
AssetManager::AssetManager() {
|
||||
SetSearchPriority(kAssetPriorityDir); // ensure lib sorter is initialized
|
||||
}
|
||||
|
||||
/* static */ bool AssetManager::IsDataFile(const String &data_file) {
|
||||
Stream *in = File::OpenFileCI(data_file.GetCStr(), Shared::kFile_Open, Shared::kFile_Read);
|
||||
if (in) {
|
||||
MFLUtil::MFLError err = MFLUtil::TestIsMFL(in, true);
|
||||
delete in;
|
||||
return err == MFLUtil::kMFLNoError;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* static */ AssetError AssetManager::ReadDataFileTOC(const String &data_file, AssetLibInfo &lib) {
|
||||
Stream *in = File::OpenFileCI(data_file.GetCStr(), Shared::kFile_Open, Shared::kFile_Read);
|
||||
if (in) {
|
||||
MFLUtil::MFLError err = MFLUtil::ReadHeader(lib, in);
|
||||
delete in;
|
||||
return (err != MFLUtil::kMFLNoError) ? kAssetErrLibParse : kAssetNoError;
|
||||
}
|
||||
return kAssetErrNoLibFile;
|
||||
}
|
||||
|
||||
void AssetManager::SetSearchPriority(AssetSearchPriority priority) {
|
||||
_libsPriority = priority;
|
||||
_libsSorter = _libsPriority == kAssetPriorityDir ? SortLibsPriorityDir : SortLibsPriorityLib;
|
||||
std::sort(_activeLibs.begin(), _activeLibs.end(), _libsSorter);
|
||||
}
|
||||
|
||||
AssetSearchPriority AssetManager::GetSearchPriority() const {
|
||||
return _libsPriority;
|
||||
}
|
||||
|
||||
AssetError AssetManager::AddLibrary(const String &path, const AssetLibInfo **out_lib) {
|
||||
return AddLibrary(path, "", out_lib);
|
||||
}
|
||||
|
||||
AssetError AssetManager::AddLibrary(const String &path, const String &filters, const AssetLibInfo **out_lib) {
|
||||
if (path.IsEmpty())
|
||||
return kAssetErrNoLibFile;
|
||||
|
||||
for (const auto &lib : _libs) {
|
||||
if (Path::ComparePaths(lib->BasePath, path) == 0) {
|
||||
// already present, only assign new filters
|
||||
lib->Filters = filters.Split(',');
|
||||
if (out_lib)
|
||||
*out_lib = lib;
|
||||
return kAssetNoError;
|
||||
}
|
||||
}
|
||||
|
||||
AssetLibEx *lib;
|
||||
AssetError err = RegisterAssetLib(path, lib);
|
||||
if (err != kAssetNoError)
|
||||
return err;
|
||||
lib->Filters = filters.Split(',');
|
||||
auto place = std::upper_bound(_activeLibs.begin(), _activeLibs.end(), lib, _libsSorter);
|
||||
_activeLibs.insert(place, lib);
|
||||
if (out_lib)
|
||||
*out_lib = lib;
|
||||
return kAssetNoError;
|
||||
}
|
||||
|
||||
void AssetManager::RemoveLibrary(const String &path) {
|
||||
int idx = 0;
|
||||
for (auto it = _libs.begin(); it != _libs.end(); ++it, ++idx) {
|
||||
if (Path::ComparePaths((*it)->BasePath, path) == 0) {
|
||||
_libs.remove_at(idx);
|
||||
_activeLibs.remove(*it);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AssetManager::RemoveAllLibraries() {
|
||||
for (uint i = 0; i < _libs.size(); ++i)
|
||||
delete _libs[i];
|
||||
|
||||
_libs.clear();
|
||||
_activeLibs.clear();
|
||||
}
|
||||
|
||||
size_t AssetManager::GetLibraryCount() const {
|
||||
return _libs.size();
|
||||
}
|
||||
|
||||
const AssetLibInfo *AssetManager::GetLibraryInfo(size_t index) const {
|
||||
return index < _libs.size() ? _libs[index] : nullptr;
|
||||
}
|
||||
|
||||
bool AssetManager::DoesAssetExist(const String &asset_name, const String &filter) const {
|
||||
for (const auto &lib : _activeLibs) {
|
||||
if (!lib->TestFilter(filter))
|
||||
continue; // filter does not match
|
||||
|
||||
if (IsAssetLibDir(lib)) {
|
||||
String filename = File::FindFileCI(lib->BaseDir, asset_name);
|
||||
if (!filename.IsEmpty() && File::IsFile(filename)) return true;
|
||||
} else {
|
||||
for (const auto &a : lib->AssetInfos) {
|
||||
if (a.FileName.CompareNoCase(asset_name) == 0) return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void AssetManager::FindAssets(std::vector<String> &assets, const String &wildcard,
|
||||
const String &filter) const {
|
||||
String pattern = StrUtil::WildcardToRegex(wildcard);
|
||||
|
||||
for (const auto *lib : _activeLibs) {
|
||||
auto match = std::find(lib->Filters.begin(), lib->Filters.end(), filter);
|
||||
if (match == lib->Filters.end())
|
||||
continue; // filter does not match
|
||||
|
||||
if (IsAssetLibDir(lib)) {
|
||||
for (FindFile ff = FindFile::OpenFiles(lib->BaseDir, wildcard);
|
||||
!ff.AtEnd(); ff.Next())
|
||||
assets.push_back(ff.Current());
|
||||
} else {
|
||||
for (const auto &a : lib->AssetInfos) {
|
||||
if (pattern == "*" || (*pattern.GetCStr() &&
|
||||
Common::String(a.FileName.GetCStr()).hasSuffixIgnoreCase(pattern.GetCStr() + 1)))
|
||||
assets.push_back(a.FileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort and remove duplicates
|
||||
std::sort(assets.begin(), assets.end());
|
||||
assets.erase(std::unique(assets.begin(), assets.end()), assets.end());
|
||||
}
|
||||
|
||||
AssetError AssetManager::RegisterAssetLib(const String &path, AssetLibEx *&out_lib) {
|
||||
// Test for a directory
|
||||
std::unique_ptr<AssetLibEx> lib;
|
||||
if (File::IsDirectory(path)) {
|
||||
lib.reset(new AssetLibEx());
|
||||
lib->BasePath = Path::MakeAbsolutePath(path);
|
||||
lib->BaseDir = Path::GetDirectoryPath(lib->BasePath);
|
||||
|
||||
// TODO: maybe parse directory for the file reference? idk if needed
|
||||
}
|
||||
// ...else try open a data library
|
||||
else {
|
||||
Stream *in = File::OpenFileCI(path.GetCStr(), Shared::kFile_Open, Shared::kFile_Read);
|
||||
if (!in)
|
||||
return kAssetErrNoLibFile; // can't be opened, return error code
|
||||
|
||||
lib.reset(new AssetLibEx());
|
||||
MFLUtil::MFLError mfl_err = MFLUtil::ReadHeader(*lib, in);
|
||||
delete in;
|
||||
|
||||
if (mfl_err != MFLUtil::kMFLNoError)
|
||||
return kAssetErrLibParse;
|
||||
|
||||
lib->BasePath = Path::MakeAbsolutePath(path);
|
||||
lib->BaseDir = Path::GetDirectoryPath(lib->BasePath);
|
||||
lib->BaseFileName = Path::GetFilename(lib->BasePath);
|
||||
lib->LibFileNames[0] = lib->BaseFileName;
|
||||
|
||||
// Find out real library files in the current filesystem and save them
|
||||
for (size_t i = 0; i < lib->LibFileNames.size(); ++i) {
|
||||
lib->RealLibFiles.push_back(File::FindFileCI(lib->BaseDir, lib->LibFileNames[i]));
|
||||
}
|
||||
}
|
||||
|
||||
out_lib = lib.release();
|
||||
_libs.push_back(out_lib);
|
||||
|
||||
return kAssetNoError;
|
||||
}
|
||||
|
||||
Stream *AssetManager::OpenAsset(const String &asset_name, const String &filter) const {
|
||||
for (const auto *lib : _activeLibs) {
|
||||
if (!lib->TestFilter(filter)) continue; // filter does not match
|
||||
|
||||
Stream *s = nullptr;
|
||||
if (IsAssetLibDir(lib))
|
||||
s = OpenAssetFromDir(lib, asset_name);
|
||||
else
|
||||
s = OpenAssetFromLib(lib, asset_name);
|
||||
if (s)
|
||||
return s;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Stream *AssetManager::OpenAssetFromLib(const AssetLibEx *lib, const String &asset_name) const {
|
||||
for (const auto &a : lib->AssetInfos) {
|
||||
if (a.FileName.CompareNoCase(asset_name) == 0) {
|
||||
String libfile = lib->RealLibFiles[a.LibUid];
|
||||
if (libfile.IsEmpty())
|
||||
return nullptr;
|
||||
return File::OpenFile(libfile, a.Offset, a.Offset + a.Size);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Stream *AssetManager::OpenAssetFromDir(const AssetLibEx *lib, const String &file_name) const {
|
||||
String found_file = File::FindFileCI(lib->BaseDir, file_name);
|
||||
if (found_file.IsEmpty())
|
||||
return nullptr;
|
||||
return File::OpenFileRead(found_file);
|
||||
}
|
||||
|
||||
Stream *AssetManager::OpenAsset(const String &asset_name) const {
|
||||
return OpenAsset(asset_name, "");
|
||||
}
|
||||
|
||||
Common::SeekableReadStream *AssetManager::OpenAssetStream(const String &asset_name) const {
|
||||
return OpenAssetStream(asset_name, "");
|
||||
}
|
||||
|
||||
Common::SeekableReadStream *AssetManager::OpenAssetStream(const String &asset_name, const String &filter) const {
|
||||
Stream *stream = OpenAsset(asset_name, filter);
|
||||
if (!stream)
|
||||
return nullptr;
|
||||
|
||||
// Get the contents of the asset
|
||||
size_t assetSize = stream->GetLength();
|
||||
byte *data = (byte *)malloc(assetSize);
|
||||
stream->Read(data, assetSize);
|
||||
delete stream;
|
||||
|
||||
return new Common::MemoryReadStream(data, assetSize, DisposeAfterUse::YES);
|
||||
}
|
||||
|
||||
String GetAssetErrorText(AssetError err) {
|
||||
switch (err) {
|
||||
case kAssetNoError:
|
||||
return "No error.";
|
||||
case kAssetErrNoLibFile:
|
||||
return "Asset library file not found or could not be opened.";
|
||||
case kAssetErrLibParse:
|
||||
return "Not an asset library or unsupported format.";
|
||||
case kAssetErrNoManager:
|
||||
return "Asset manager is not initialized.";
|
||||
}
|
||||
return "Unknown error.";
|
||||
}
|
||||
|
||||
} // namespace Shared
|
||||
} // namespace AGS
|
||||
} // namespace AGS3
|
||||
160
engines/ags/shared/core/asset_manager.h
Normal file
160
engines/ags/shared/core/asset_manager.h
Normal file
@@ -0,0 +1,160 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// Asset manager class for reading and writing game resources.
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// The code is based on CLIB32, by Chris Jones (1998-99), DJGPP implementation
|
||||
// of the CLIB reader.
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// TODO: consider replace/merge with PhysFS library in the future.
|
||||
//
|
||||
// TODO: support streams that work on a file subsection, limited by size,
|
||||
// to avoid having to return an asset size separately from a stream.
|
||||
// TODO: return stream as smart pointer.
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef AGS_SHARED_CORE_ASSET_MANAGER_H
|
||||
#define AGS_SHARED_CORE_ASSET_MANAGER_H
|
||||
|
||||
#include "common/stream.h"
|
||||
#include "common/std/functional.h"
|
||||
#include "common/std/memory.h"
|
||||
#include "ags/shared/core/asset.h"
|
||||
#include "ags/shared/util/file.h" // TODO: extract filestream mode constants or introduce generic ones
|
||||
|
||||
namespace AGS3 {
|
||||
namespace AGS {
|
||||
namespace Shared {
|
||||
|
||||
class Stream;
|
||||
struct MultiFileLib;
|
||||
|
||||
enum AssetSearchPriority {
|
||||
kAssetPriorityDir,
|
||||
kAssetPriorityLib
|
||||
};
|
||||
|
||||
enum AssetError {
|
||||
kAssetNoError = 0,
|
||||
kAssetErrNoLibFile = -1, // library file not found or can't be read
|
||||
kAssetErrLibParse = -2, // bad library file format or read error
|
||||
kAssetErrNoManager = -6, // asset manager not initialized
|
||||
};
|
||||
|
||||
/**
|
||||
* AssetPath combines asset name and optional library filter, that serves to narrow down the search
|
||||
*/
|
||||
struct AssetPath {
|
||||
String Name;
|
||||
String Filter;
|
||||
|
||||
AssetPath(const String &name = "", const String &filter = "") : Name(name), Filter(filter) {
|
||||
}
|
||||
};
|
||||
|
||||
class AssetManager {
|
||||
public:
|
||||
AssetManager();
|
||||
~AssetManager() {
|
||||
RemoveAllLibraries();
|
||||
}
|
||||
|
||||
// Test if given file is main data file
|
||||
static bool IsDataFile(const String &data_file);
|
||||
// Read data file table of contents into provided struct
|
||||
static AssetError ReadDataFileTOC(const String &data_file, AssetLibInfo &lib);
|
||||
|
||||
// Sets asset search priority (in which order manager will search available locations)
|
||||
void SetSearchPriority(AssetSearchPriority priority);
|
||||
// Gets current asset search priority
|
||||
AssetSearchPriority GetSearchPriority() const;
|
||||
|
||||
// Add library location to the list of asset locations
|
||||
AssetError AddLibrary(const String &path, const AssetLibInfo **lib = nullptr);
|
||||
// Add library location, specifying comma-separated list of filters;
|
||||
// if library was already added before, this method will overwrite the filters only
|
||||
AssetError AddLibrary(const String &path, const String &filters, const AssetLibInfo **lib = nullptr);
|
||||
// Remove library location from the list of asset locations
|
||||
void RemoveLibrary(const String &path);
|
||||
// Removes all libraries
|
||||
void RemoveAllLibraries();
|
||||
|
||||
size_t GetLibraryCount() const;
|
||||
const AssetLibInfo *GetLibraryInfo(size_t index) const;
|
||||
// Tells whether asset exists in any of the registered search locations
|
||||
bool DoesAssetExist(const String &asset_name, const String &filter = "") const;
|
||||
inline bool DoesAssetExist(const AssetPath &apath) const {
|
||||
return DoesAssetExist(apath.Name, apath.Filter);
|
||||
}
|
||||
// Searches in all the registered locations and collects a list of
|
||||
// assets using given wildcard pattern
|
||||
void FindAssets(std::vector<String> &assets, const String &wildcard,
|
||||
const String &filter = "") const;
|
||||
// Open asset stream in the given work mode; returns null if asset is not found or cannot be opened
|
||||
// This method only searches in libraries that do not have any defined filters
|
||||
Stream *OpenAsset(const String &asset_name) const;
|
||||
// Open asset stream, providing a single filter to search in matching libraries
|
||||
Stream *OpenAsset(const String &asset_name, const String &filter) const;
|
||||
inline Stream *OpenAsset(const AssetPath &apath) const {
|
||||
return OpenAsset(apath.Name, apath.Filter);
|
||||
}
|
||||
// Open asset stream in the given work mode; returns null if asset is not found or cannot be opened
|
||||
// This method only searches in libraries that do not have any defined filters
|
||||
Common::SeekableReadStream *OpenAssetStream(const String &asset_name) const;
|
||||
// Open asset stream, providing a single filter to search in matching libraries
|
||||
Common::SeekableReadStream *OpenAssetStream(const String &asset_name, const String &filter) const;
|
||||
|
||||
private:
|
||||
// AssetLibEx combines library info with extended internal data required for the manager
|
||||
struct AssetLibEx : AssetLibInfo {
|
||||
std::vector<String> Filters; // asset filters this library is matching to
|
||||
std::vector<String> RealLibFiles; // fixed up library filenames
|
||||
|
||||
bool TestFilter(const String &filter) const;
|
||||
};
|
||||
|
||||
// Loads library and registers its contents into the cache
|
||||
AssetError RegisterAssetLib(const String &path, AssetLibEx *&lib);
|
||||
|
||||
// Tries to find asset in the given location, and then opens a stream for reading
|
||||
Stream *OpenAssetFromLib(const AssetLibEx *lib, const String &asset_name) const;
|
||||
Stream *OpenAssetFromDir(const AssetLibEx *lib, const String &asset_name) const;
|
||||
|
||||
std::vector<AssetLibEx *> _libs;
|
||||
std::vector<AssetLibEx *> _activeLibs;
|
||||
|
||||
AssetSearchPriority _libsPriority = kAssetPriorityDir;
|
||||
// Sorting function, depends on priority setting
|
||||
bool (*_libsSorter)(const AssetLibInfo *, const AssetLibInfo *);
|
||||
};
|
||||
|
||||
String GetAssetErrorText(AssetError err);
|
||||
|
||||
} // namespace Shared
|
||||
} // namespace AGS
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
34
engines/ags/shared/core/def_version.h
Normal file
34
engines/ags/shared/core/def_version.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/* 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 AGS_SHARED_CORE_DEFVERSION_H
|
||||
#define AGS_SHARED_CORE_DEFVERSION_H
|
||||
|
||||
#define ACI_VERSION_STR "3.6.1.33"
|
||||
#if defined (RC_INVOKED) // for MSVC resource compiler
|
||||
#define ACI_VERSION_MSRC_DEF 3.6.1.33
|
||||
#endif
|
||||
|
||||
#define SPECIAL_VERSION ""
|
||||
|
||||
#define ACI_COPYRIGHT_YEARS "2011-2025"
|
||||
|
||||
#endif
|
||||
164
engines/ags/shared/core/platform.h
Normal file
164
engines/ags/shared/core/platform.h
Normal file
@@ -0,0 +1,164 @@
|
||||
/* 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 AGS_SHARED_CORE_PLATFORM_H
|
||||
#define AGS_SHARED_CORE_PLATFORM_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
// platform definitions. Not intended for replacing types or checking for libraries.
|
||||
|
||||
// ScummVM implementation is identifying as Linux for now
|
||||
#if 1
|
||||
#define AGS_PLATFORM_SCUMMVM (1)
|
||||
#define AGS_PLATFORM_OS_WINDOWS (0)
|
||||
#define AGS_PLATFORM_OS_LINUX (1)
|
||||
#define AGS_PLATFORM_OS_MACOS (0)
|
||||
#define AGS_PLATFORM_OS_ANDROID (0)
|
||||
#define AGS_PLATFORM_OS_IOS (0)
|
||||
#define AGS_PLATFORM_OS_PSP (0)
|
||||
#define AGS_PLATFORM_OS_EMSCRIPTEN (0)
|
||||
// check Android first because sometimes it can get confused with host OS
|
||||
#elif defined(__ANDROID__) || defined(ANDROID)
|
||||
#define AGS_PLATFORM_SCUMMVM (0)
|
||||
#define AGS_PLATFORM_OS_WINDOWS (0)
|
||||
#define AGS_PLATFORM_OS_LINUX (0)
|
||||
#define AGS_PLATFORM_OS_MACOS (0)
|
||||
#define AGS_PLATFORM_OS_ANDROID (1)
|
||||
#define AGS_PLATFORM_OS_IOS (0)
|
||||
#define AGS_PLATFORM_OS_PSP (0)
|
||||
#elif defined(_WIN32)
|
||||
//define something for Windows (32-bit and 64-bit)
|
||||
#define AGS_PLATFORM_SCUMMVM (0)
|
||||
#define AGS_PLATFORM_OS_WINDOWS (1)
|
||||
#define AGS_PLATFORM_OS_LINUX (0)
|
||||
#define AGS_PLATFORM_OS_MACOS (0)
|
||||
#define AGS_PLATFORM_OS_ANDROID (0)
|
||||
#define AGS_PLATFORM_OS_IOS (0)
|
||||
#define AGS_PLATFORM_OS_PSP (0)
|
||||
#elif defined(__APPLE__)
|
||||
#define AGS_PLATFORM_SCUMMVM (0)
|
||||
#include "ags/shared/ags/shared/TargetConditionals.h"
|
||||
#ifndef TARGET_OS_SIMULATOR
|
||||
#define TARGET_OS_SIMULATOR (0)
|
||||
#endif
|
||||
#ifndef TARGET_OS_IOS
|
||||
#define TARGET_OS_IOS (0)
|
||||
#endif
|
||||
#ifndef TARGET_OS_OSX
|
||||
#define TARGET_OS_OSX (0)
|
||||
#endif
|
||||
|
||||
#if TARGET_OS_SIMULATOR || TARGET_IPHONE_SIMULATOR
|
||||
#define AGS_PLATFORM_OS_WINDOWS (0)
|
||||
#define AGS_PLATFORM_OS_LINUX (0)
|
||||
#define AGS_PLATFORM_OS_MACOS (0)
|
||||
#define AGS_PLATFORM_OS_ANDROID (0)
|
||||
#define AGS_PLATFORM_OS_IOS (1)
|
||||
#define AGS_PLATFORM_OS_PSP (0)
|
||||
#elif TARGET_OS_IOS || TARGET_OS_IPHONE
|
||||
#define AGS_PLATFORM_OS_WINDOWS (0)
|
||||
#define AGS_PLATFORM_OS_LINUX (0)
|
||||
#define AGS_PLATFORM_OS_MACOS (0)
|
||||
#define AGS_PLATFORM_OS_ANDROID (0)
|
||||
#define AGS_PLATFORM_OS_IOS (1)
|
||||
#define AGS_PLATFORM_OS_PSP (0)
|
||||
#elif TARGET_OS_OSX || TARGET_OS_MAC
|
||||
#define AGS_PLATFORM_OS_WINDOWS (0)
|
||||
#define AGS_PLATFORM_OS_LINUX (0)
|
||||
#define AGS_PLATFORM_OS_MACOS (1)
|
||||
#define AGS_PLATFORM_OS_ANDROID (0)
|
||||
#define AGS_PLATFORM_OS_IOS (0)
|
||||
#define AGS_PLATFORM_OS_PSP (0)
|
||||
#else
|
||||
#error "Unknown Apple platform"
|
||||
#endif
|
||||
#elif defined(__linux__)
|
||||
#define AGS_PLATFORM_OS_WINDOWS (0)
|
||||
#define AGS_PLATFORM_OS_LINUX (1)
|
||||
#define AGS_PLATFORM_OS_MACOS (0)
|
||||
#define AGS_PLATFORM_OS_ANDROID (0)
|
||||
#define AGS_PLATFORM_OS_IOS (0)
|
||||
#define AGS_PLATFORM_OS_PSP (0)
|
||||
#else
|
||||
#error "Unknown platform"
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
#define AGS_PLATFORM_WINDOWS_MINGW (1)
|
||||
#else
|
||||
#define AGS_PLATFORM_WINDOWS_MINGW (0)
|
||||
#endif
|
||||
|
||||
#if defined(__LP64__)
|
||||
// LP64 machine, macOS or Linux
|
||||
// int 32bit | long 64bit | long long 64bit | void* 64bit
|
||||
#define AGS_PLATFORM_64BIT (1)
|
||||
#elif defined(_WIN64)
|
||||
// LLP64 machine, Windows
|
||||
// int 32bit | long 32bit | long long 64bit | void* 64bit
|
||||
#define AGS_PLATFORM_64BIT (1)
|
||||
#else
|
||||
// 32-bit machine, Windows or Linux or macOS
|
||||
// int 32bit | long 32bit | long long 64bit | void* 32bit
|
||||
#define AGS_PLATFORM_64BIT (0)
|
||||
#endif
|
||||
|
||||
#if defined(SCUMM_LITTLE_ENDIAN)
|
||||
#define AGS_PLATFORM_ENDIAN_LITTLE (1)
|
||||
#define AGS_PLATFORM_ENDIAN_BIG (0)
|
||||
#elif defined(SCUMM_BIG_ENDIAN)
|
||||
#define AGS_PLATFORM_ENDIAN_LITTLE (0)
|
||||
#define AGS_PLATFORM_ENDIAN_BIG (1)
|
||||
#else
|
||||
#error "No endianness defined"
|
||||
#endif
|
||||
|
||||
#if defined(SCUMM_NEED_ALIGNMENT)
|
||||
#define AGS_STRICT_ALIGNMENT
|
||||
#endif
|
||||
|
||||
#define AGS_PLATFORM_DESKTOP ((AGS_PLATFORM_OS_WINDOWS) || (AGS_PLATFORM_OS_LINUX) || (AGS_PLATFORM_OS_MACOS))
|
||||
|
||||
#define AGS_PLATFORM_MOBILE ((AGS_PLATFORM_OS_ANDROID) || (AGS_PLATFORM_OS_IOS))
|
||||
|
||||
#define AGS_HAS_DIRECT3D (AGS_PLATFORM_OS_WINDOWS)
|
||||
#define AGS_HAS_OPENGL ((AGS_PLATFORM_OS_WINDOWS) || (AGS_PLATFORM_OS_LINUX) || (AGS_PLATFORM_MOBILE))
|
||||
#define AGS_OPENGL_ES2 (AGS_PLATFORM_OS_ANDROID)
|
||||
|
||||
// Only allow searching around for game data on desktop systems;
|
||||
// otherwise use explicit argument either from program wrapper, command-line
|
||||
// or read from default config.
|
||||
#define AGS_SEARCH_FOR_GAME_ON_LAUNCH ((AGS_PLATFORM_OS_WINDOWS) || (AGS_PLATFORM_OS_LINUX) || (AGS_PLATFORM_OS_MACOS))
|
||||
|
||||
#if !defined(DEBUG_MANAGED_OBJECTS)
|
||||
#define DEBUG_MANAGED_OBJECTS (0)
|
||||
#endif
|
||||
|
||||
#if !defined(DEBUG_SPRITECACHE)
|
||||
#define DEBUG_SPRITECACHE (0)
|
||||
#endif
|
||||
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
134
engines/ags/shared/core/types.h
Normal file
134
engines/ags/shared/core/types.h
Normal file
@@ -0,0 +1,134 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// Basic types definition
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef AGS_SHARED_CORE_TYPES_H
|
||||
#define AGS_SHARED_CORE_TYPES_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "ags/lib/std.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
#ifndef NULL
|
||||
#define NULL nullptr
|
||||
#endif
|
||||
|
||||
// Not all compilers have this. Added in clang and gcc followed
|
||||
#ifndef __has_attribute
|
||||
#define __has_attribute(x) 0
|
||||
#endif
|
||||
|
||||
#ifndef FORCEINLINE
|
||||
#ifdef _MSC_VER
|
||||
#define FORCEINLINE __forceinline
|
||||
|
||||
#elif defined (__GNUC__) || __has_attribute(__always_inline__)
|
||||
#define FORCEINLINE inline __attribute__((__always_inline__))
|
||||
|
||||
#else
|
||||
#define FORCEINLINE inline
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
typedef uint8 uint8_t;
|
||||
typedef uint16 uint16_t;
|
||||
typedef uint32 uint32_t;
|
||||
typedef uint64 uint64_t;
|
||||
typedef int8 int8_t;
|
||||
typedef int16 int16_t;
|
||||
typedef int32 int32_t;
|
||||
typedef int64 int64_t;
|
||||
|
||||
typedef int64 soff_t; // Stream offset type
|
||||
typedef int64 intptr_t;
|
||||
typedef uint64 uintptr_t;
|
||||
|
||||
// fixed point type
|
||||
#define fixed_t int32_t
|
||||
#define color_t int
|
||||
|
||||
#undef INT16_MIN
|
||||
#undef INT16_MAX
|
||||
#undef UINT16_MAX
|
||||
#undef INT32_MIN
|
||||
#undef INT32_MAX
|
||||
#undef INT_MIN
|
||||
#undef INT_MAX
|
||||
#undef UINT32_MAX
|
||||
#undef UINT_MAX
|
||||
#undef SIZE_MAX
|
||||
#define INT16_MIN -32768
|
||||
#define INT16_MAX 0x7fff
|
||||
#define UINT16_MAX 0xffff
|
||||
#define INT32_MIN (-2147483647 - 1)
|
||||
#define INT32_MAX 2147483647
|
||||
#define INT_MIN (-2147483647 - 1)
|
||||
#define INT_MAX 2147483647
|
||||
#define UINT_MAX 0xffffffff
|
||||
#define SIZE_MAX 0xffffffff
|
||||
#define UINT32_MAX 0xffffffff
|
||||
|
||||
#undef TRUE
|
||||
#undef FALSE
|
||||
#define TRUE true
|
||||
#define FALSE false
|
||||
|
||||
// TODO: use distinct fixed point class
|
||||
enum {
|
||||
kShift = 16,
|
||||
kUnit = 1 << kShift
|
||||
};
|
||||
|
||||
/**
|
||||
* Basic class that can hold either a number or a pointer. Helps avoid some
|
||||
* of the more nasty casts the codebase does, which was causing issues
|
||||
* on 64-bit systems
|
||||
*/
|
||||
class NumberPtr {
|
||||
intptr_t _value;
|
||||
|
||||
public:
|
||||
NumberPtr() : _value(0) {
|
||||
}
|
||||
NumberPtr(int value) : _value(value) {
|
||||
}
|
||||
NumberPtr(void *ptr) : _value((intptr_t)ptr) {
|
||||
}
|
||||
NumberPtr(const void *ptr) : _value((intptr_t)ptr) {
|
||||
}
|
||||
operator int32_t() const {
|
||||
return (int32_t)_value;
|
||||
}
|
||||
intptr_t full() const { return _value; }
|
||||
void *ptr() const { return (void *)_value; }
|
||||
const void *cptr() const { return (const void *)_value; }
|
||||
};
|
||||
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
238
engines/ags/shared/debugging/debug_manager.cpp
Normal file
238
engines/ags/shared/debugging/debug_manager.cpp
Normal file
@@ -0,0 +1,238 @@
|
||||
/* 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 "ags/shared/debugging/debug_manager.h"
|
||||
#include "ags/shared/util/string_types.h"
|
||||
#include "ags/globals.h"
|
||||
|
||||
namespace AGS3 {
|
||||
namespace AGS {
|
||||
namespace Shared {
|
||||
|
||||
DebugOutput::DebugOutput(const String &id, IOutputHandler *handler, MessageType def_verbosity, bool enabled)
|
||||
: _id(id)
|
||||
, _handler(handler)
|
||||
, _enabled(enabled)
|
||||
, _defaultVerbosity(def_verbosity) {
|
||||
_groupFilter.resize(_GP(DbgMgr)._lastGroupID + 1, _defaultVerbosity);
|
||||
}
|
||||
|
||||
String DebugOutput::GetID() const {
|
||||
return _id;
|
||||
}
|
||||
|
||||
IOutputHandler *DebugOutput::GetHandler() const {
|
||||
return _handler;
|
||||
}
|
||||
|
||||
bool DebugOutput::IsEnabled() const {
|
||||
return _enabled;
|
||||
}
|
||||
|
||||
void DebugOutput::SetEnabled(bool enable) {
|
||||
_enabled = enable;
|
||||
}
|
||||
|
||||
void DebugOutput::SetGroupFilter(DebugGroupID id, MessageType verbosity) {
|
||||
uint32_t key = _GP(DbgMgr).GetGroup(id).UID.ID;
|
||||
if (key != kDbgGroup_None)
|
||||
_groupFilter[key] = verbosity;
|
||||
else
|
||||
_unresolvedGroups.insert(std::make_pair(id.SID, verbosity));
|
||||
}
|
||||
|
||||
void DebugOutput::SetAllGroupFilters(MessageType verbosity) {
|
||||
for (auto &group : _groupFilter)
|
||||
group = verbosity;
|
||||
for (auto &group : _unresolvedGroups)
|
||||
group._value = verbosity;
|
||||
}
|
||||
|
||||
void DebugOutput::ClearGroupFilters() {
|
||||
for (auto &gf : _groupFilter)
|
||||
gf = kDbgMsg_None;
|
||||
_unresolvedGroups.clear();
|
||||
}
|
||||
|
||||
void DebugOutput::ResolveGroupID(DebugGroupID id) {
|
||||
if (!id.IsValid())
|
||||
return;
|
||||
|
||||
DebugGroupID real_id = _GP(DbgMgr).GetGroup(id).UID;
|
||||
if (real_id.IsValid()) {
|
||||
if (_groupFilter.size() <= id.ID)
|
||||
_groupFilter.resize(id.ID + 1, _defaultVerbosity);
|
||||
GroupNameToMTMap::const_iterator it = _unresolvedGroups.find(real_id.SID);
|
||||
if (it != _unresolvedGroups.end()) {
|
||||
_groupFilter[real_id.ID] = it->_value;
|
||||
_unresolvedGroups.erase(it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool DebugOutput::TestGroup(DebugGroupID id, MessageType mt) const {
|
||||
DebugGroupID real_id = _GP(DbgMgr).GetGroup(id).UID;
|
||||
if (real_id.ID == (uint32_t)kDbgGroup_None || real_id.ID >= _groupFilter.size())
|
||||
return false;
|
||||
return (_groupFilter[real_id.ID] >= mt) != 0;
|
||||
}
|
||||
|
||||
DebugManager::DebugManager() {
|
||||
// Add hardcoded groups
|
||||
RegisterGroup(DebugGroup(DebugGroupID(kDbgGroup_Main, "main"), ""));
|
||||
RegisterGroup(DebugGroup(DebugGroupID(kDbgGroup_Game, "game"), "Game"));
|
||||
RegisterGroup(DebugGroup(DebugGroupID(kDbgGroup_Script, "script"), "Script"));
|
||||
RegisterGroup(DebugGroup(DebugGroupID(kDbgGroup_SprCache, "sprcache"), "Sprite cache"));
|
||||
RegisterGroup(DebugGroup(DebugGroupID(kDbgGroup_ManObj, "manobj"), "Managed obj"));
|
||||
_firstFreeGroupID = _groups.size();
|
||||
_lastGroupID = _firstFreeGroupID;
|
||||
}
|
||||
|
||||
DebugGroup DebugManager::GetGroup(DebugGroupID id) {
|
||||
if (id.ID != (uint32_t)kDbgGroup_None) {
|
||||
return id.ID < _groups.size() ? _groups[id.ID] : DebugGroup();
|
||||
} else if (!id.SID.IsEmpty()) {
|
||||
GroupByStringMap::const_iterator it = _groupByStrLookup.find(id.SID);
|
||||
return it != _groupByStrLookup.end() ? _groups[it->_value.ID] : DebugGroup();
|
||||
}
|
||||
return DebugGroup();
|
||||
}
|
||||
|
||||
PDebugOutput DebugManager::GetOutput(const String &id) {
|
||||
OutMap::const_iterator it = _outputs.find(id);
|
||||
return it != _outputs.end() ? it->_value.Target : PDebugOutput();
|
||||
}
|
||||
|
||||
DebugGroup DebugManager::RegisterGroup(const String &id, const String &out_name) {
|
||||
DebugGroup group = GetGroup(id);
|
||||
if (group.UID.IsValid())
|
||||
return group;
|
||||
group = DebugGroup(DebugGroupID(++_GP(DbgMgr)._lastGroupID, id), out_name);
|
||||
_groups.push_back(group);
|
||||
_groupByStrLookup[group.UID.SID] = group.UID;
|
||||
|
||||
// Resolve group reference on every output target
|
||||
for (OutMap::const_iterator it = _outputs.begin(); it != _outputs.end(); ++it) {
|
||||
it->_value.Target->ResolveGroupID(group.UID);
|
||||
}
|
||||
return group;
|
||||
}
|
||||
|
||||
void DebugManager::RegisterGroup(const DebugGroup &group) {
|
||||
if (_groups.size() <= group.UID.ID)
|
||||
_groups.resize(group.UID.ID + 1);
|
||||
_groups[group.UID.ID] = group; _groupByStrLookup[group.UID.SID] = group.UID;
|
||||
}
|
||||
|
||||
PDebugOutput DebugManager::RegisterOutput(const String &id, IOutputHandler *handler, MessageType def_verbosity, bool enabled) {
|
||||
_outputs[id].Target = PDebugOutput(new DebugOutput(id, handler, def_verbosity, enabled));
|
||||
_outputs[id].Suppressed = false;
|
||||
return _outputs[id].Target;
|
||||
}
|
||||
|
||||
void DebugManager::UnregisterAll() {
|
||||
_lastGroupID = _firstFreeGroupID;
|
||||
_groups.clear();
|
||||
_groupByStrLookup.clear();
|
||||
_outputs.clear();
|
||||
}
|
||||
|
||||
void DebugManager::UnregisterGroup(DebugGroupID id) {
|
||||
DebugGroup group = GetGroup(id);
|
||||
if (!group.UID.IsValid())
|
||||
return;
|
||||
_groups[group.UID.ID] = DebugGroup();
|
||||
_groupByStrLookup.erase(group.UID.SID);
|
||||
}
|
||||
|
||||
void DebugManager::UnregisterOutput(const String &id) {
|
||||
_outputs.erase(id);
|
||||
}
|
||||
|
||||
void DebugManager::Print(DebugGroupID group_id, MessageType mt, const String &text) {
|
||||
const DebugGroup &group = GetGroup(group_id);
|
||||
DebugMessage msg(text, group.UID.ID, group.OutputName, mt);
|
||||
|
||||
for (OutMap::iterator it = _outputs.begin(); it != _outputs.end(); ++it) {
|
||||
SendMessage(it->_value, msg);
|
||||
}
|
||||
}
|
||||
|
||||
void DebugManager::SendMessage(const String &out_id, const DebugMessage &msg) {
|
||||
OutMap::iterator it = _outputs.find(out_id);
|
||||
if (it != _outputs.end())
|
||||
SendMessage(it->_value, msg);
|
||||
}
|
||||
|
||||
void DebugManager::SendMessage(OutputSlot &out, const DebugMessage &msg) {
|
||||
IOutputHandler *handler = out.Target->GetHandler();
|
||||
if (!handler || !out.Target->IsEnabled() || out.Suppressed)
|
||||
return;
|
||||
if (!out.Target->TestGroup(msg.GroupID, msg.MT))
|
||||
return;
|
||||
// We suppress current target before the call so that if it makes
|
||||
// a call to output system itself, message would not print to the
|
||||
// same target
|
||||
out.Suppressed = true;
|
||||
handler->PrintMessage(msg);
|
||||
out.Suppressed = false;
|
||||
}
|
||||
|
||||
namespace Debug {
|
||||
|
||||
void Printf(const String &text) {
|
||||
_GP(DbgMgr).Print(kDbgGroup_Main, kDbgMsg_Default, text);
|
||||
}
|
||||
|
||||
void Printf(MessageType mt, const String &text) {
|
||||
_GP(DbgMgr).Print(kDbgGroup_Main, mt, text);
|
||||
}
|
||||
|
||||
void Printf(DebugGroupID group, MessageType mt, const String &text) {
|
||||
_GP(DbgMgr).Print(group, mt, text);
|
||||
}
|
||||
|
||||
void Printf(const char *fmt, ...) {
|
||||
va_list argptr;
|
||||
va_start(argptr, fmt);
|
||||
_GP(DbgMgr).Print(kDbgGroup_Main, kDbgMsg_Default, String::FromFormatV(fmt, argptr));
|
||||
va_end(argptr);
|
||||
}
|
||||
|
||||
void Printf(MessageType mt, const char *fmt, ...) {
|
||||
va_list argptr;
|
||||
va_start(argptr, fmt);
|
||||
_GP(DbgMgr).Print(kDbgGroup_Main, mt, String::FromFormatV(fmt, argptr));
|
||||
va_end(argptr);
|
||||
}
|
||||
|
||||
void Printf(DebugGroupID group, MessageType mt, const char *fmt, ...) {
|
||||
va_list argptr;
|
||||
va_start(argptr, fmt);
|
||||
_GP(DbgMgr).Print(group, mt, String::FromFormatV(fmt, argptr));
|
||||
va_end(argptr);
|
||||
}
|
||||
|
||||
} // namespace Debug
|
||||
|
||||
} // namespace Shared
|
||||
} // namespace AGS
|
||||
} // namespace AGS3
|
||||
168
engines/ags/shared/debugging/debug_manager.h
Normal file
168
engines/ags/shared/debugging/debug_manager.h
Normal file
@@ -0,0 +1,168 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// AGS logging system is built with idea that the engine components should not
|
||||
// be bothered with specifying particular output method. Instead they use
|
||||
// generic logging interface, and the actual message printing is done by one
|
||||
// or more registered handlers.
|
||||
// Firstly this makes logging functions independent of running platform or
|
||||
// back-end, secondly it grants end-users ability to configure output according
|
||||
// to their preference.
|
||||
//
|
||||
// To make the logging work we need to register two sets of "entities":
|
||||
// debug groups and output targets.
|
||||
// Debug group is an arbitrary object with a name that describes message
|
||||
// sender.
|
||||
// Output target defines printing handler and a set of verbosity rules
|
||||
// one per each known group.
|
||||
//
|
||||
// When the message is sent, it is tagged with one of the existing group IDs
|
||||
// and a message type (debug info, warning, error). This message is sent onto
|
||||
// each of the registered output targets, which do checks to find out whether
|
||||
// the message is permitted to be sent further to the printing handler, or not.
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef AGS_SHARED_DEBUGGING_DEBUG_MANAGER_H
|
||||
#define AGS_SHARED_DEBUGGING_DEBUG_MANAGER_H
|
||||
|
||||
#include "common/std/memory.h"
|
||||
#include "common/std/map.h"
|
||||
#include "ags/shared/debugging/out.h"
|
||||
#include "ags/shared/debugging/output_handler.h"
|
||||
#include "ags/shared/util/string.h"
|
||||
#include "ags/shared/util/string_types.h"
|
||||
|
||||
namespace AGS3 {
|
||||
namespace AGS {
|
||||
namespace Shared {
|
||||
|
||||
// DebugGroup is a message sender definition, identified by DebugGroupID
|
||||
// and providing OutputName that could be used when printing its messages.
|
||||
// OutputName may or may not be same as DebugGroupID.SID.
|
||||
struct DebugGroup {
|
||||
DebugGroupID UID;
|
||||
String OutputName;
|
||||
|
||||
DebugGroup() {
|
||||
}
|
||||
DebugGroup(DebugGroupID id, String out_name) : UID(id), OutputName(out_name) {
|
||||
}
|
||||
};
|
||||
|
||||
// DebugOutput is a slot for IOutputHandler with its own group filter
|
||||
class DebugOutput {
|
||||
public:
|
||||
DebugOutput(const String &id, IOutputHandler *handler, MessageType def_verbosity = kDbgMsg_All, bool enabled = true);
|
||||
|
||||
String GetID() const;
|
||||
IOutputHandler *GetHandler() const;
|
||||
|
||||
bool IsEnabled() const;
|
||||
void SetEnabled(bool enable);
|
||||
// Setup group filter: either allow or disallow a group with the given ID
|
||||
void SetGroupFilter(DebugGroupID id, MessageType verbosity);
|
||||
// Assign same verbosity level to all known groups
|
||||
void SetAllGroupFilters(MessageType verbosity);
|
||||
// Clear all group filters; this efficiently disables everything
|
||||
void ClearGroupFilters();
|
||||
// Try to resolve group filter unknown IDs
|
||||
void ResolveGroupID(DebugGroupID id);
|
||||
// Test if given group id is permitted
|
||||
bool TestGroup(DebugGroupID id, MessageType mt) const;
|
||||
|
||||
private:
|
||||
String _id;
|
||||
IOutputHandler *_handler;
|
||||
bool _enabled;
|
||||
MessageType _defaultVerbosity;
|
||||
// Set of permitted groups' numeric IDs
|
||||
std::vector<MessageType> _groupFilter;
|
||||
// Set of unresolved groups, which numeric IDs are not yet known
|
||||
typedef std::unordered_map<String, MessageType, IgnoreCase_Hash, IgnoreCase_EqualTo> GroupNameToMTMap;
|
||||
GroupNameToMTMap _unresolvedGroups;
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<DebugOutput> PDebugOutput;
|
||||
|
||||
|
||||
class DebugManager {
|
||||
friend class DebugOutput;
|
||||
|
||||
public:
|
||||
DebugManager();
|
||||
|
||||
// Gets full group ID for any partial one; if the group is not registered returns unset ID
|
||||
DebugGroup GetGroup(DebugGroupID id);
|
||||
// Gets output control interface for the given ID
|
||||
PDebugOutput GetOutput(const String &id);
|
||||
// Registers debugging group with the given string ID; numeric ID
|
||||
// will be assigned internally. Returns full ID pair.
|
||||
// If the group with such string id already exists, returns existing ID.
|
||||
DebugGroup RegisterGroup(const String &id, const String &out_name);
|
||||
// Registers output delegate for passing debug messages to;
|
||||
// if the output with such id already exists, replaces the old one
|
||||
PDebugOutput RegisterOutput(const String &id, IOutputHandler *handler, MessageType def_verbosity = kDbgMsg_All, bool enabled = true);
|
||||
// Unregisters all groups and all targets
|
||||
void UnregisterAll();
|
||||
// Unregisters debugging group with the given ID
|
||||
void UnregisterGroup(DebugGroupID id);
|
||||
// Unregisters output delegate with the given ID
|
||||
void UnregisterOutput(const String &id);
|
||||
|
||||
// Output message of given group and message type
|
||||
void Print(DebugGroupID group_id, MessageType mt, const String &text);
|
||||
// Send message directly to the output with given id; the message
|
||||
// must pass the output's message filter though
|
||||
void SendMessage(const String &out_id, const DebugMessage &msg);
|
||||
|
||||
private:
|
||||
// OutputSlot struct wraps over output target and adds a flag which indicates
|
||||
// that this target is temporarily disabled (for internal use only)
|
||||
struct OutputSlot {
|
||||
PDebugOutput Target;
|
||||
bool Suppressed;
|
||||
|
||||
OutputSlot() : Suppressed(false) {
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::vector<DebugGroup> GroupVector;
|
||||
typedef std::unordered_map<String, DebugGroupID, IgnoreCase_Hash, IgnoreCase_EqualTo> GroupByStringMap;
|
||||
typedef std::unordered_map<String, OutputSlot, IgnoreCase_Hash, IgnoreCase_EqualTo> OutMap;
|
||||
|
||||
void RegisterGroup(const DebugGroup &id);
|
||||
void SendMessage(OutputSlot &out, const DebugMessage &msg);
|
||||
|
||||
uint32_t _firstFreeGroupID;
|
||||
uint32_t _lastGroupID;
|
||||
GroupVector _groups;
|
||||
GroupByStringMap _groupByStrLookup;
|
||||
OutMap _outputs;
|
||||
};
|
||||
|
||||
} // namespace Shared
|
||||
} // namespace AGS
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
173
engines/ags/shared/debugging/out.h
Normal file
173
engines/ags/shared/debugging/out.h
Normal file
@@ -0,0 +1,173 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// Debug output interface provides functions which send a formatted message
|
||||
// tagged with group ID and message type to the registered output handlers.
|
||||
//
|
||||
// Depending on configuration this message may be printed by any of those
|
||||
// handlers, or none of them. The calling unit should not worry about where the
|
||||
// message goes.
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// On using message types.
|
||||
//
|
||||
// Please keep in mind, that there are different levels of errors. AGS logging
|
||||
// system allows to classify debug message by two parameters: debug group and
|
||||
// message type. Sometimes message type alone is not enough. Debug groups can
|
||||
// also be used to distinct messages that has less (or higher) importance.
|
||||
//
|
||||
// For example, there are engine errors and user (game-dev) mistakes. Script
|
||||
// commands that cannot be executed under given circumstances are user
|
||||
// mistakes, and usually are not as severe as internal engine errors. This is
|
||||
// why it is advised to use a separate debug group for mistakes like that to
|
||||
// distinct them from reports on the internal engine's problems and make
|
||||
// verbosity configuration flexible.
|
||||
//
|
||||
// kDbgMsg_Debug - is the most mundane type of message (if the printing function
|
||||
// argument list does not specify message type, it is probably kDbgMsg_Debug).
|
||||
// You can use it for almost anything, from noting some process steps to
|
||||
// displaying current object state. If certain messages are meant to be printed
|
||||
// very often, consider using another distinct debug group so that it may be
|
||||
// disabled to reduce log verbosity.
|
||||
//
|
||||
// kDbgMsg_Info - is a type for important notifications, such as initialization
|
||||
// and stopping of engine components.
|
||||
//
|
||||
// kDbgMsg_Warn - this is suggested for more significant cases, when you find
|
||||
// out that something is not right, but is not immediately affecting engine
|
||||
// processing. For example: certain object was constructed in a way that
|
||||
// would make them behave unexpectedly, or certain common files are missing but
|
||||
// it is not clear yet whether the game will be accessing them.
|
||||
// In other words: use kDbgMsg_Warn when there is no problem right away, but
|
||||
// you are *anticipating* that one may happen under certain circumstances.
|
||||
//
|
||||
// kDbgMsg_Error - use this kind of message is for actual serious problems.
|
||||
// If certain operation assumes both positive and negative results are
|
||||
// acceptable, it is preferred to report such negative result with simple
|
||||
// kDbgMsg_Debug message. kDbgMsg_Error is for negative results that are not
|
||||
// considered acceptable for normal run.
|
||||
//
|
||||
// kDbgMsg_Fatal - is the message type to be reported when the program or
|
||||
// component abortion is imminent.
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef AGS_SHARED_CORE_DEBUGGING_OUT_H
|
||||
#define AGS_SHARED_CORE_DEBUGGING_OUT_H
|
||||
|
||||
#include "ags/shared/util/string.h"
|
||||
|
||||
namespace AGS3 {
|
||||
namespace AGS {
|
||||
namespace Shared {
|
||||
|
||||
// Message types provide distinction for debug messages by their intent.
|
||||
enum MessageType {
|
||||
kDbgMsg_None = 0,
|
||||
// Alerts may be informative messages with topmost level of importance,
|
||||
// such as reporting engine startup and shutdown.
|
||||
kDbgMsg_Alert,
|
||||
// Fatal errors are ones that make program abort immediately.
|
||||
kDbgMsg_Fatal,
|
||||
// Error messages are about engine not being able to perform requested
|
||||
// operation in a situation when that will affect game playability and
|
||||
// further execution.
|
||||
kDbgMsg_Error,
|
||||
// Warnings are made when unexpected or non-standart behavior
|
||||
// is detected in program, which is not immediately critical,
|
||||
// but may be a symptom of a bigger problem.
|
||||
kDbgMsg_Warn,
|
||||
// General information messages.
|
||||
kDbgMsg_Info,
|
||||
// Debug reason is for arbitrary information about events and current
|
||||
// game state.
|
||||
kDbgMsg_Debug,
|
||||
|
||||
|
||||
// Convenient aliases
|
||||
kDbgMsg_Default = kDbgMsg_Debug,
|
||||
kDbgMsg_All = kDbgMsg_Debug
|
||||
};
|
||||
|
||||
// This enumeration is a list of common hard-coded groups, but more could
|
||||
// be added via debugging configuration interface (see 'debug/debug.h').
|
||||
enum CommonDebugGroup : uint32 {
|
||||
kDbgGroup_None = UINT32_MAX,
|
||||
// Main debug group is for reporting general engine status and issues
|
||||
kDbgGroup_Main = 0,
|
||||
// Game group is for logging game logic state and issues
|
||||
kDbgGroup_Game,
|
||||
// Log from the game script
|
||||
kDbgGroup_Script,
|
||||
// Sprite cache logging
|
||||
kDbgGroup_SprCache,
|
||||
// Group for debugging managed object state (can slow engine down!)
|
||||
kDbgGroup_ManObj
|
||||
};
|
||||
|
||||
// Debug group identifier defining either numeric or string id, or both
|
||||
struct DebugGroupID {
|
||||
uint32_t ID;
|
||||
String SID;
|
||||
|
||||
DebugGroupID() : ID((uint32_t)kDbgGroup_None) {
|
||||
}
|
||||
DebugGroupID(uint32_t id, const String &sid = "") : ID(id), SID(sid) {
|
||||
}
|
||||
DebugGroupID(const String &sid) : ID((uint32_t)kDbgGroup_None), SID(sid) {
|
||||
}
|
||||
// Tells if any of the id components is valid
|
||||
bool IsValid() const {
|
||||
return ID != (uint32_t)kDbgGroup_None || !SID.IsEmpty();
|
||||
}
|
||||
// Tells if both id components are properly set
|
||||
bool IsComplete() const {
|
||||
return ID != (uint32_t)kDbgGroup_None && !SID.IsEmpty();
|
||||
}
|
||||
};
|
||||
|
||||
namespace Debug {
|
||||
//
|
||||
// Debug output
|
||||
//
|
||||
// Output a plain message of default group and default type
|
||||
void Printf(const String &text);
|
||||
// Output a plain message of default group and given type
|
||||
void Printf(MessageType mt, const String &text);
|
||||
// Output a plain message of given group and type
|
||||
void Printf(DebugGroupID group_id, MessageType mt, const String &text);
|
||||
// Output formatted message of default group and default type
|
||||
void Printf(const char *fmt, ...);
|
||||
// Output formatted message of default group and given type
|
||||
void Printf(MessageType mt, const char *fmt, ...);
|
||||
// Output formatted message of given group and type
|
||||
void Printf(DebugGroupID group_id, MessageType mt, const char *fmt, ...);
|
||||
|
||||
} // namespace Debug
|
||||
|
||||
} // namespace Shared
|
||||
} // namespace AGS
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
68
engines/ags/shared/debugging/output_handler.h
Normal file
68
engines/ags/shared/debugging/output_handler.h
Normal file
@@ -0,0 +1,68 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// IOutputHandler is a debug printing interface. Its implementations can be
|
||||
// registered as potential output for the debug log.
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef AGS_SHARED_CORE_DEBUGGING_OUTPUT_HANDLER_H
|
||||
#define AGS_SHARED_CORE_DEBUGGING_OUTPUT_HANDLER_H
|
||||
|
||||
#include "ags/shared/debugging/out.h"
|
||||
#include "ags/shared/util/string.h"
|
||||
|
||||
namespace AGS3 {
|
||||
namespace AGS {
|
||||
namespace Shared {
|
||||
|
||||
struct DebugMessage {
|
||||
String Text;
|
||||
uint32_t GroupID;
|
||||
String GroupName;
|
||||
MessageType MT;
|
||||
|
||||
DebugMessage() : GroupID((uint32_t)kDbgGroup_None), MT(kDbgMsg_None) {
|
||||
}
|
||||
DebugMessage(const String &text, uint32_t group_id, const String &group_name, MessageType mt)
|
||||
: Text(text)
|
||||
, GroupID(group_id)
|
||||
, GroupName(group_name)
|
||||
, MT(mt) {
|
||||
}
|
||||
};
|
||||
|
||||
class IOutputHandler {
|
||||
public:
|
||||
virtual ~IOutputHandler() {}
|
||||
|
||||
// Print the given text sent from the debug group.
|
||||
// Implementations are free to decide which message components are to be printed, and how.
|
||||
virtual void PrintMessage(const DebugMessage &msg) = 0;
|
||||
};
|
||||
|
||||
} // namespace Shared
|
||||
} // namespace AGS
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
126
engines/ags/shared/font/ags_font_renderer.h
Normal file
126
engines/ags/shared/font/ags_font_renderer.h
Normal 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef AGS_SHARED_FONT_AGS_FONT_RENDERER_H
|
||||
#define AGS_SHARED_FONT_AGS_FONT_RENDERER_H
|
||||
|
||||
#include "common/std/utility.h"
|
||||
#include "ags/shared/core/types.h"
|
||||
#include "ags/shared/util/string.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
class BITMAP;
|
||||
|
||||
class IAGSFontRenderer {
|
||||
public:
|
||||
virtual bool LoadFromDisk(int fontNumber, int fontSize) = 0;
|
||||
virtual void FreeMemory(int fontNumber) = 0;
|
||||
virtual bool SupportsExtendedCharacters(int fontNumber) = 0;
|
||||
virtual int GetTextWidth(const char *text, int fontNumber) = 0;
|
||||
// Get actual height of the given line of text
|
||||
virtual int GetTextHeight(const char *text, int fontNumber) = 0;
|
||||
virtual void RenderText(const char *text, int fontNumber, BITMAP *destination, int x, int y, int colour) = 0;
|
||||
virtual void AdjustYCoordinateForFont(int *ycoord, int fontNumber) = 0;
|
||||
virtual void EnsureTextValidForFont(char *text, int fontNumber) = 0;
|
||||
protected:
|
||||
IAGSFontRenderer() {}
|
||||
~IAGSFontRenderer() {}
|
||||
};
|
||||
|
||||
// Extended font renderer interface.
|
||||
// WARNING: this interface is exposed for plugins and declared for the second time in agsplugin.h
|
||||
class IAGSFontRenderer2 : public IAGSFontRenderer {
|
||||
public:
|
||||
// Returns engine API version this font renderer complies to.
|
||||
// Must not be lower than 26 (this interface was added at API v26).
|
||||
virtual int GetVersion() = 0;
|
||||
// Returns an arbitrary renderer name; this is for informational
|
||||
// purposes only.
|
||||
virtual const char *GetRendererName() = 0;
|
||||
// Returns given font's name (if available).
|
||||
virtual const char *GetFontName(int fontNumber) = 0;
|
||||
// Returns the given font's height: that is the maximal vertical size
|
||||
// that the font glyphs may occupy.
|
||||
virtual int GetFontHeight(int fontNumber) = 0;
|
||||
// Returns the given font's linespacing;
|
||||
// is allowed to return 0, telling that no specific linespacing
|
||||
// is assigned for this font.
|
||||
virtual int GetLineSpacing(int fontNumber) = 0;
|
||||
|
||||
protected:
|
||||
IAGSFontRenderer2() {}
|
||||
~IAGSFontRenderer2() {}
|
||||
};
|
||||
|
||||
// Font render params, mainly for dealing with various compatibility issues.
|
||||
struct FontRenderParams {
|
||||
// Font's render multiplier
|
||||
int SizeMultiplier = 1;
|
||||
int LoadMode = 0; // contains font flags from FFLG_LOADMODEMASK
|
||||
};
|
||||
|
||||
// Describes loaded font's properties
|
||||
struct FontMetrics {
|
||||
// Nominal font's height, equals to the game-requested size of the font.
|
||||
// This may or not be equal to font's face height; sometimes a font cannot
|
||||
// be scaled exactly to particular size, and then nominal height appears different
|
||||
// (usually - smaller) than the real one.
|
||||
int NominalHeight = 0;
|
||||
// Real font's height, equals to reported ascender + descender.
|
||||
// This is what you normally think as a font's height.
|
||||
int RealHeight = 0;
|
||||
// Compatible height, equals to either NominalHeight or RealHeight,
|
||||
// selected depending on the game settings.
|
||||
// This property is used in calculating linespace, etc.
|
||||
int CompatHeight = 0;
|
||||
// Maximal vertical extent of a font (top; bottom), this tells the actual
|
||||
// graphical bounds that may be occupied by font's glyphs.
|
||||
// In a "proper" font this extent is (0; RealHeight-1), but "bad" fonts may
|
||||
// have individual glyphs exceeding these bounds, in both directions.
|
||||
// Note that "top" may be negative!
|
||||
std::pair<int, int> VExtent;
|
||||
|
||||
inline int ExtentHeight() const { return VExtent.second - VExtent.first; }
|
||||
};
|
||||
|
||||
// The strictly internal font renderer interface, not to use in plugin API.
|
||||
// Contains methods necessary for built-in font renderers.
|
||||
class IAGSFontRendererInternal : public IAGSFontRenderer2 {
|
||||
public:
|
||||
// Tells if this is a bitmap font (otherwise it's a vector font)
|
||||
virtual bool IsBitmapFont() = 0;
|
||||
// Load font, applying extended font rendering parameters
|
||||
virtual bool LoadFromDiskEx(int fontNumber, int fontSize, AGS::Shared::String *src_filename,
|
||||
const FontRenderParams *params, FontMetrics *metrics) = 0;
|
||||
// Fill FontMetrics struct; note that it may be left cleared if this is not supported
|
||||
virtual void GetFontMetrics(int fontNumber, FontMetrics *metrics) = 0;
|
||||
// Perform any necessary adjustments when the AA mode is toggled
|
||||
virtual void AdjustFontForAntiAlias(int fontNumber, bool aa_mode) = 0;
|
||||
|
||||
protected:
|
||||
IAGSFontRendererInternal() {}
|
||||
~IAGSFontRendererInternal() {}
|
||||
};
|
||||
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
568
engines/ags/shared/font/fonts.cpp
Normal file
568
engines/ags/shared/font/fonts.cpp
Normal file
@@ -0,0 +1,568 @@
|
||||
/* 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/std/algorithm.h"
|
||||
#include "ags/lib/alfont/alfont.h"
|
||||
#include "common/std/vector.h"
|
||||
#include "ags/shared/ac/common.h" // set_our_eip
|
||||
#include "ags/shared/ac/game_struct_defines.h"
|
||||
#include "ags/shared/debugging/out.h"
|
||||
#include "ags/shared/font/fonts.h"
|
||||
#include "ags/shared/font/ttf_font_renderer.h"
|
||||
#include "ags/shared/font/wfn_font_renderer.h"
|
||||
#include "ags/shared/gfx/bitmap.h"
|
||||
#include "ags/shared/gui/gui_defines.h" // MAXLINE
|
||||
#include "ags/shared/util/string_utils.h"
|
||||
#include "ags/globals.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
using namespace AGS::Shared;
|
||||
|
||||
FontInfo::FontInfo()
|
||||
: Flags(0)
|
||||
, Size(0)
|
||||
, SizeMultiplier(1)
|
||||
, Outline(FONT_OUTLINE_NONE)
|
||||
, YOffset(0)
|
||||
, LineSpacing(0)
|
||||
, AutoOutlineStyle(kSquared)
|
||||
, AutoOutlineThickness(0) {
|
||||
}
|
||||
|
||||
|
||||
void init_font_renderer() {
|
||||
alfont_init();
|
||||
alfont_text_mode(-1);
|
||||
}
|
||||
|
||||
void shutdown_font_renderer() {
|
||||
set_our_eip(9919);
|
||||
alfont_exit();
|
||||
}
|
||||
|
||||
void adjust_y_coordinate_for_text(int *ypos, size_t fontnum) {
|
||||
if (fontnum >= _GP(fonts).size() || !_GP(fonts)[fontnum].Renderer)
|
||||
return;
|
||||
_GP(fonts)[fontnum].Renderer->AdjustYCoordinateForFont(ypos, fontnum);
|
||||
}
|
||||
|
||||
bool font_first_renderer_loaded() {
|
||||
return _GP(fonts).size() > 0 && _GP(fonts)[0].Renderer != nullptr;
|
||||
}
|
||||
|
||||
bool is_font_loaded(size_t fontNumber) {
|
||||
return fontNumber < _GP(fonts).size() && _GP(fonts)[fontNumber].Renderer != nullptr;
|
||||
}
|
||||
|
||||
// Finish font's initialization
|
||||
static void font_post_init(size_t fontNumber) {
|
||||
Font &font = _GP(fonts)[fontNumber];
|
||||
// If no font height property was provided, then try several methods,
|
||||
// depending on which interface is available
|
||||
if (font.Metrics.NominalHeight == 0 && font.Renderer) {
|
||||
int height = 0;
|
||||
if (font.Renderer2)
|
||||
height = font.Renderer2->GetFontHeight(fontNumber);
|
||||
if (height <= 0) {
|
||||
// With the old renderer we have to rely on GetTextHeight;
|
||||
// the implementations of GetTextHeight are allowed to return varied
|
||||
// results depending on the text parameter.
|
||||
// We use special line of text to get more or less reliable font height.
|
||||
const char *height_test_string = "ZHwypgfjqhkilIK";
|
||||
height = font.Renderer->GetTextHeight(height_test_string, fontNumber);
|
||||
}
|
||||
|
||||
font.Metrics.NominalHeight = std::max(0, height);
|
||||
font.Metrics.RealHeight = font.Metrics.NominalHeight;
|
||||
font.Metrics.VExtent = std::make_pair(0, font.Metrics.RealHeight);
|
||||
}
|
||||
// Use either nominal or real pixel height to define font's logical height
|
||||
// and default linespacing; logical height = nominal height is compatible with the old games
|
||||
font.Metrics.CompatHeight = (font.Info.Flags & FFLG_REPORTNOMINALHEIGHT) != 0 ?
|
||||
font.Metrics.NominalHeight : font.Metrics.RealHeight;
|
||||
|
||||
if (font.Info.Outline != FONT_OUTLINE_AUTO) {
|
||||
font.Info.AutoOutlineThickness = 0;
|
||||
}
|
||||
|
||||
// If no linespacing property was provided, then try several methods,
|
||||
// depending on which interface is available
|
||||
font.LineSpacingCalc = font.Info.LineSpacing;
|
||||
if (font.Info.LineSpacing == 0) {
|
||||
int linespacing = 0;
|
||||
if (font.Renderer2)
|
||||
linespacing = font.Renderer2->GetLineSpacing(fontNumber);
|
||||
if (linespacing > 0) {
|
||||
font.LineSpacingCalc = linespacing;
|
||||
} else {
|
||||
// Calculate default linespacing from the font height + outline thickness.
|
||||
font.Info.Flags |= FFLG_DEFLINESPACING;
|
||||
font.LineSpacingCalc = font.Metrics.CompatHeight + 2 * font.Info.AutoOutlineThickness;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void font_replace_renderer(size_t fontNumber, IAGSFontRenderer* renderer, IAGSFontRenderer2* renderer2) {
|
||||
_GP(fonts)[fontNumber].Renderer = renderer;
|
||||
_GP(fonts)[fontNumber].Renderer2 = renderer2;
|
||||
// If this is one of our built-in font renderers, then correctly
|
||||
// reinitialize interfaces and font metrics
|
||||
if ((renderer == &_GP(ttfRenderer)) || (renderer == &_GP(wfnRenderer))) {
|
||||
_GP(fonts)[fontNumber].RendererInt = static_cast<IAGSFontRendererInternal*>(renderer);
|
||||
_GP(fonts)[fontNumber].RendererInt->GetFontMetrics(fontNumber, &_GP(fonts)[fontNumber].Metrics);
|
||||
} else {
|
||||
// Otherwise, this is probably coming from plugin
|
||||
_GP(fonts)[fontNumber].RendererInt = nullptr;
|
||||
_GP(fonts)[fontNumber].Metrics = FontMetrics(); // reset to defaults
|
||||
}
|
||||
font_post_init(fontNumber);
|
||||
}
|
||||
|
||||
|
||||
IAGSFontRenderer *font_replace_renderer(size_t fontNumber, IAGSFontRenderer *renderer) {
|
||||
if (fontNumber >= _GP(fonts).size())
|
||||
return nullptr;
|
||||
IAGSFontRenderer* old_render = _GP(fonts)[fontNumber].Renderer;
|
||||
font_replace_renderer(fontNumber, renderer, nullptr);
|
||||
return old_render;
|
||||
}
|
||||
|
||||
IAGSFontRenderer *font_replace_renderer(size_t fontNumber, IAGSFontRenderer2 *renderer) {
|
||||
if (fontNumber >= _GP(fonts).size())
|
||||
return nullptr;
|
||||
IAGSFontRenderer* old_render = _GP(fonts)[fontNumber].Renderer;
|
||||
font_replace_renderer(fontNumber, renderer, renderer);
|
||||
return old_render;
|
||||
}
|
||||
|
||||
void font_recalc_metrics(size_t fontNumber) {
|
||||
if (fontNumber >= _GP(fonts).size())
|
||||
return;
|
||||
_GP(fonts)[fontNumber].Metrics = FontMetrics();
|
||||
font_post_init(fontNumber);
|
||||
}
|
||||
|
||||
bool is_bitmap_font(size_t fontNumber) {
|
||||
if (fontNumber >= _GP(fonts).size() || !_GP(fonts)[fontNumber].RendererInt)
|
||||
return false;
|
||||
return _GP(fonts)[fontNumber].RendererInt->IsBitmapFont();
|
||||
}
|
||||
|
||||
bool font_supports_extended_characters(size_t fontNumber) {
|
||||
if (fontNumber >= _GP(fonts).size() || !_GP(fonts)[fontNumber].Renderer)
|
||||
return false;
|
||||
return _GP(fonts)[fontNumber].Renderer->SupportsExtendedCharacters(fontNumber);
|
||||
}
|
||||
|
||||
const char *get_font_name(size_t fontNumber) {
|
||||
if (fontNumber >= _GP(fonts).size() || !_GP(fonts)[fontNumber].Renderer2)
|
||||
return "";
|
||||
const char *name = _GP(fonts)[fontNumber].Renderer2->GetFontName(fontNumber);
|
||||
return name ? name : "";
|
||||
}
|
||||
|
||||
int get_font_flags(size_t fontNumber) {
|
||||
if (fontNumber >= _GP(fonts).size())
|
||||
return 0;
|
||||
return _GP(fonts)[fontNumber].Info.Flags;
|
||||
}
|
||||
|
||||
void ensure_text_valid_for_font(char *text, size_t fontnum) {
|
||||
if (fontnum >= _GP(fonts).size() || !_GP(fonts)[fontnum].Renderer)
|
||||
return;
|
||||
_GP(fonts)[fontnum].Renderer->EnsureTextValidForFont(text, fontnum);
|
||||
}
|
||||
|
||||
int get_font_scaling_mul(size_t fontNumber) {
|
||||
if (fontNumber >= _GP(fonts).size() || !_GP(fonts)[fontNumber].Renderer)
|
||||
return 0;
|
||||
return _GP(fonts)[fontNumber].Info.SizeMultiplier;
|
||||
}
|
||||
|
||||
int get_text_width(const char *texx, size_t fontNumber) {
|
||||
if (fontNumber >= _GP(fonts).size() || !_GP(fonts)[fontNumber].Renderer)
|
||||
return 0;
|
||||
return _GP(fonts)[fontNumber].Renderer->GetTextWidth(texx, fontNumber);
|
||||
}
|
||||
|
||||
int get_text_width_outlined(const char *text, size_t font_number) {
|
||||
if (font_number >= _GP(fonts).size() || !_GP(fonts)[font_number].Renderer)
|
||||
return 0;
|
||||
if (text == nullptr || text[0] == 0) // we ignore outline width since the text is empty
|
||||
return 0;
|
||||
int self_width = _GP(fonts)[font_number].Renderer->GetTextWidth(text, font_number);
|
||||
int outline = _GP(fonts)[font_number].Info.Outline;
|
||||
if (outline < 0 || static_cast<size_t>(outline) > _GP(fonts).size()) { // FONT_OUTLINE_AUTO or FONT_OUTLINE_NONE
|
||||
return self_width + 2 * _GP(fonts)[font_number].Info.AutoOutlineThickness;
|
||||
}
|
||||
int outline_width = _GP(fonts)[outline].Renderer->GetTextWidth(text, outline);
|
||||
return MAX(self_width, outline_width);
|
||||
}
|
||||
|
||||
int get_text_height(const char *text, size_t font_number) {
|
||||
if (font_number >= _GP(fonts).size() || !_GP(fonts)[font_number].Renderer)
|
||||
return 0;
|
||||
return _GP(fonts)[font_number].Renderer->GetTextHeight(text, font_number);
|
||||
}
|
||||
|
||||
int get_font_outline(size_t font_number) {
|
||||
if (font_number >= _GP(fonts).size())
|
||||
return FONT_OUTLINE_NONE;
|
||||
return _GP(fonts)[font_number].Info.Outline;
|
||||
}
|
||||
|
||||
int get_font_outline_thickness(size_t font_number) {
|
||||
if (font_number >= _GP(fonts).size())
|
||||
return 0;
|
||||
return _GP(fonts)[font_number].Info.AutoOutlineThickness;
|
||||
}
|
||||
|
||||
void set_font_outline(size_t font_number, int outline_type,
|
||||
enum FontInfo::AutoOutlineStyle style, int thickness) {
|
||||
if (font_number >= _GP(fonts).size())
|
||||
return;
|
||||
_GP(fonts)[font_number].Info.Outline = outline_type;
|
||||
_GP(fonts)[font_number].Info.AutoOutlineStyle = style;
|
||||
_GP(fonts)[font_number].Info.AutoOutlineThickness = thickness;
|
||||
}
|
||||
|
||||
bool is_font_antialiased(size_t font_number) {
|
||||
if (font_number >= _GP(fonts).size())
|
||||
return false;
|
||||
return ShouldAntiAliasText() && !is_bitmap_font(font_number);
|
||||
}
|
||||
|
||||
int get_font_height(size_t fontNumber) {
|
||||
if (fontNumber >= _GP(fonts).size() || !_GP(fonts)[fontNumber].Renderer)
|
||||
return 0;
|
||||
return _GP(fonts)[fontNumber].Metrics.CompatHeight;
|
||||
}
|
||||
|
||||
int get_font_height_outlined(size_t fontNumber) {
|
||||
if (fontNumber >= _GP(fonts).size() || !_GP(fonts)[fontNumber].Renderer)
|
||||
return 0;
|
||||
int self_height = _GP(fonts)[fontNumber].Metrics.CompatHeight;
|
||||
int outline = _GP(fonts)[fontNumber].Info.Outline;
|
||||
if (outline < 0 || static_cast<size_t>(outline) > _GP(fonts).size()) { // FONT_OUTLINE_AUTO or FONT_OUTLINE_NONE
|
||||
return self_height + 2 * _GP(fonts)[fontNumber].Info.AutoOutlineThickness;
|
||||
}
|
||||
int outline_height = _GP(fonts)[outline].Metrics.CompatHeight;
|
||||
return MAX(self_height, outline_height);
|
||||
}
|
||||
|
||||
int get_font_surface_height(size_t fontNumber) {
|
||||
if (fontNumber >= _GP(fonts).size() || !_GP(fonts)[fontNumber].Renderer)
|
||||
return 0;
|
||||
return _GP(fonts)[fontNumber].Metrics.ExtentHeight();
|
||||
}
|
||||
|
||||
std::pair<int, int> get_font_surface_extent(size_t fontNumber) {
|
||||
if (fontNumber >= _GP(fonts).size() || !_GP(fonts)[fontNumber].Renderer)
|
||||
return std::make_pair(0, 0);
|
||||
return _GP(fonts)[fontNumber].Metrics.VExtent;
|
||||
}
|
||||
|
||||
int get_font_linespacing(size_t fontNumber) {
|
||||
if (fontNumber >= _GP(fonts).size())
|
||||
return 0;
|
||||
return _GP(fonts)[fontNumber].LineSpacingCalc;
|
||||
}
|
||||
|
||||
void set_font_linespacing(size_t fontNumber, int spacing) {
|
||||
if (fontNumber < _GP(fonts).size()) {
|
||||
_GP(fonts)[fontNumber].Info.Flags &= ~FFLG_DEFLINESPACING;
|
||||
_GP(fonts)[fontNumber].Info.LineSpacing = spacing;
|
||||
_GP(fonts)[fontNumber].LineSpacingCalc = spacing;
|
||||
}
|
||||
}
|
||||
|
||||
int get_text_lines_height(size_t fontNumber, size_t numlines) {
|
||||
if (fontNumber >= _GP(fonts).size() || numlines == 0)
|
||||
return 0;
|
||||
return _GP(fonts)[fontNumber].LineSpacingCalc * (numlines - 1) +
|
||||
(_GP(fonts)[fontNumber].Metrics.CompatHeight +
|
||||
2 * _GP(fonts)[fontNumber].Info.AutoOutlineThickness);
|
||||
}
|
||||
|
||||
int get_text_lines_surf_height(size_t fontNumber, size_t numlines) {
|
||||
if (fontNumber >= _GP(fonts).size() || numlines == 0)
|
||||
return 0;
|
||||
return _GP(fonts)[fontNumber].LineSpacingCalc * (numlines - 1) +
|
||||
(_GP(fonts)[fontNumber].Metrics.RealHeight +
|
||||
2 * _GP(fonts)[fontNumber].Info.AutoOutlineThickness);
|
||||
}
|
||||
|
||||
// Replaces AGS-specific linebreak tags with common '\n'
|
||||
void unescape_script_string(const char *cstr, std::vector<char> &out) {
|
||||
out.clear();
|
||||
// Handle the special case of the first char
|
||||
if (cstr[0] == '[') {
|
||||
out.push_back('\n');
|
||||
cstr++;
|
||||
}
|
||||
// Replace all other occurrences as they're found
|
||||
// NOTE: we do not need to decode utf8 here, because
|
||||
// we are only searching for low-code ascii chars.
|
||||
const char *off;
|
||||
for (off = cstr; *off; ++off) {
|
||||
if (*off != '[') continue;
|
||||
if (*(off - 1) == '\\') {
|
||||
// convert \[ into [
|
||||
out.insert(out.end(), cstr, off - 1);
|
||||
out.push_back('[');
|
||||
} else {
|
||||
// convert [ into \n
|
||||
out.insert(out.end(), cstr, off);
|
||||
out.push_back('\n');
|
||||
}
|
||||
cstr = off + 1;
|
||||
}
|
||||
out.insert(out.end(), cstr, off + 1);
|
||||
}
|
||||
|
||||
// Break up the text into lines
|
||||
size_t split_lines(const char *todis, SplitLines &lines, int wii, int fonnt, size_t max_lines) {
|
||||
// NOTE: following hack accommodates for the legacy math mistake in split_lines.
|
||||
// It's hard to tell how crucial it is for the game looks, so research may be needed.
|
||||
// TODO: IMHO this should rely not on game format, but script API level, because it
|
||||
// defines necessary adjustments to game scripts. If you want to fix this, find a way to
|
||||
// pass this flag here all the way from game.options[OPT_BASESCRIPTAPI] (or game format).
|
||||
//
|
||||
// if (game.options[OPT_BASESCRIPTAPI] < $Your current version$)
|
||||
wii -= 1;
|
||||
|
||||
lines.Reset();
|
||||
unescape_script_string(todis, lines.LineBuf);
|
||||
char *theline = &lines.LineBuf.front();
|
||||
|
||||
char *scan_ptr = theline;
|
||||
char *prev_ptr = theline;
|
||||
char *last_whitespace = nullptr;
|
||||
while (1) {
|
||||
char *split_at = nullptr;
|
||||
|
||||
if (*scan_ptr == 0) {
|
||||
// end of the text, add the last line if necessary
|
||||
if (scan_ptr > theline) {
|
||||
lines.Add(theline);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (*scan_ptr == ' ')
|
||||
last_whitespace = scan_ptr;
|
||||
|
||||
// force end of line with the \n character
|
||||
if (*scan_ptr == '\n') {
|
||||
split_at = scan_ptr;
|
||||
// otherwise, see if we are too wide
|
||||
} else {
|
||||
// temporarily terminate the line in the *next* char and test its width
|
||||
char *next_ptr = scan_ptr;
|
||||
ugetx(&next_ptr);
|
||||
const int next_chwas = ugetc(next_ptr);
|
||||
*next_ptr = 0;
|
||||
|
||||
if (get_text_width_outlined(theline, fonnt) > wii) {
|
||||
// line is too wide, order the split
|
||||
if (last_whitespace)
|
||||
// revert to the last whitespace
|
||||
split_at = last_whitespace;
|
||||
else
|
||||
// single very wide word, display as much as possible
|
||||
split_at = prev_ptr;
|
||||
}
|
||||
|
||||
// restore the character that was there before
|
||||
usetc(next_ptr, next_chwas);
|
||||
}
|
||||
|
||||
if (split_at == nullptr) {
|
||||
prev_ptr = scan_ptr;
|
||||
ugetx(&scan_ptr);
|
||||
} else {
|
||||
// check if even one char cannot fit...
|
||||
if (split_at == theline && !((*theline == ' ') || (*theline == '\n'))) {
|
||||
// cannot split with current width restriction
|
||||
lines.Reset();
|
||||
break;
|
||||
}
|
||||
// add this line; do the temporary terminator trick again
|
||||
const int next_chwas = ugetc(split_at);
|
||||
*split_at = 0;
|
||||
lines.Add(theline);
|
||||
usetc(split_at, next_chwas);
|
||||
// check if too many lines
|
||||
if (lines.Count() >= max_lines) {
|
||||
lines[lines.Count() - 1].Append("...");
|
||||
break;
|
||||
}
|
||||
// the next line starts from the split point
|
||||
theline = split_at;
|
||||
// skip the space or new line that caused the line break
|
||||
if ((*theline == ' ') || (*theline == '\n'))
|
||||
theline++;
|
||||
scan_ptr = theline;
|
||||
prev_ptr = theline;
|
||||
last_whitespace = nullptr;
|
||||
}
|
||||
}
|
||||
return lines.Count();
|
||||
}
|
||||
|
||||
void wouttextxy(Shared::Bitmap *ds, int xxx, int yyy, size_t fontNumber, color_t text_color, const char *texx) {
|
||||
if (fontNumber >= _GP(fonts).size())
|
||||
return;
|
||||
yyy += _GP(fonts)[fontNumber].Info.YOffset;
|
||||
if (yyy > ds->GetClip().Bottom)
|
||||
return; // each char is clipped but this speeds it up
|
||||
|
||||
if (_GP(fonts)[fontNumber].Renderer != nullptr) {
|
||||
if (text_color == makeacol32(255, 0, 255, 255)) { // transparent color (magenta)
|
||||
// WORKAROUND: Some Allegro routines are not implemented and alfont treats some magenta texts as invisible
|
||||
// even if the alpha channel is fully opaque
|
||||
// Slightly change the value if the game uses that color for fonts, so that they don't turn invisible
|
||||
debug(0, "Overriding transparent text color!");
|
||||
text_color--;
|
||||
}
|
||||
_GP(fonts)[fontNumber].Renderer->RenderText(texx, fontNumber, (BITMAP *)ds->GetAllegroBitmap(), xxx, yyy, text_color);
|
||||
}
|
||||
}
|
||||
|
||||
void set_fontinfo(size_t fontNumber, const FontInfo &finfo) {
|
||||
if (fontNumber < _GP(fonts).size() && _GP(fonts)[fontNumber].Renderer) {
|
||||
_GP(fonts)[fontNumber].Info = finfo;
|
||||
font_post_init(fontNumber);
|
||||
}
|
||||
}
|
||||
|
||||
FontInfo get_fontinfo(size_t font_number) {
|
||||
if (font_number < _GP(fonts).size())
|
||||
return _GP(fonts)[font_number].Info;
|
||||
return FontInfo();
|
||||
}
|
||||
|
||||
// Loads a font from disk
|
||||
bool load_font_size(size_t fontNumber, const FontInfo &font_info) {
|
||||
if (_GP(fonts).size() <= fontNumber)
|
||||
_GP(fonts).resize(fontNumber + 1);
|
||||
else
|
||||
wfreefont(fontNumber);
|
||||
FontRenderParams params;
|
||||
params.SizeMultiplier = font_info.SizeMultiplier;
|
||||
params.LoadMode = (font_info.Flags & FFLG_LOADMODEMASK);
|
||||
FontMetrics metrics;
|
||||
|
||||
Font &font = _GP(fonts)[fontNumber];
|
||||
String src_filename;
|
||||
if (_GP(ttfRenderer).LoadFromDiskEx(fontNumber, font_info.Size, &src_filename, ¶ms, &metrics)) {
|
||||
font.Renderer = &_GP(ttfRenderer);
|
||||
font.Renderer2 = &_GP(ttfRenderer);
|
||||
font.RendererInt = &_GP(ttfRenderer);
|
||||
} else if (_GP(wfnRenderer).LoadFromDiskEx(fontNumber, font_info.Size, &src_filename, ¶ms, &metrics)) {
|
||||
font.Renderer = &_GP(wfnRenderer);
|
||||
font.Renderer2 = &_GP(wfnRenderer);
|
||||
font.RendererInt = &_GP(wfnRenderer);
|
||||
}
|
||||
|
||||
if (!font.Renderer)
|
||||
return false;
|
||||
|
||||
font.Info = font_info;
|
||||
font.Metrics = metrics;
|
||||
font_post_init(fontNumber);
|
||||
|
||||
Debug::Printf("Loaded font %d: %s, req size: %d; nominal h: %d, real h: %d, extent: %d,%d",
|
||||
fontNumber, src_filename.GetCStr(), font_info.Size, font.Metrics.NominalHeight, font.Metrics.RealHeight,
|
||||
font.Metrics.VExtent.first, font.Metrics.VExtent.second);
|
||||
return true;
|
||||
}
|
||||
|
||||
void wgtprintf(Shared::Bitmap *ds, int xxx, int yyy, size_t fontNumber, color_t text_color, char *fmt, ...) {
|
||||
if (fontNumber >= _GP(fonts).size())
|
||||
return;
|
||||
|
||||
char tbuffer[2000];
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(tbuffer, sizeof(tbuffer), fmt, ap);
|
||||
va_end(ap);
|
||||
wouttextxy(ds, xxx, yyy, fontNumber, text_color, tbuffer);
|
||||
}
|
||||
|
||||
void alloc_font_outline_buffers(size_t font_number,
|
||||
Bitmap **text_stencil, Bitmap **outline_stencil,
|
||||
int text_width, int text_height, int color_depth) {
|
||||
if (font_number >= _GP(fonts).size())
|
||||
return;
|
||||
Font &f = _GP(fonts)[font_number];
|
||||
const int thick = 2 * f.Info.AutoOutlineThickness;
|
||||
if (f.TextStencil.IsNull() || (f.TextStencil.GetColorDepth() != color_depth) ||
|
||||
(f.TextStencil.GetWidth() < text_width) || (f.TextStencil.GetHeight() < text_height)) {
|
||||
int sw = f.TextStencil.IsNull() ? 0 : f.TextStencil.GetWidth();
|
||||
int sh = f.TextStencil.IsNull() ? 0 : f.TextStencil.GetHeight();
|
||||
sw = MAX(text_width, sw);
|
||||
sh = MAX(text_height, sh);
|
||||
f.TextStencil.Create(sw, sh, color_depth);
|
||||
f.OutlineStencil.Create(sw, sh + thick, color_depth);
|
||||
f.TextStencilSub.CreateSubBitmap(&f.TextStencil, RectWH(Size(text_width, text_height)));
|
||||
f.OutlineStencilSub.CreateSubBitmap(&f.OutlineStencil, RectWH(Size(text_width, text_height + thick)));
|
||||
} else {
|
||||
f.TextStencilSub.ResizeSubBitmap(text_width, text_height);
|
||||
f.OutlineStencilSub.ResizeSubBitmap(text_width, text_height + thick);
|
||||
}
|
||||
*text_stencil = &f.TextStencilSub;
|
||||
*outline_stencil = &f.OutlineStencilSub;
|
||||
}
|
||||
|
||||
void adjust_fonts_for_render_mode(bool aa_mode) {
|
||||
for (size_t i = 0; i < _GP(fonts).size(); ++i) {
|
||||
if (_GP(fonts)[i].RendererInt != nullptr)
|
||||
_GP(fonts)[i].RendererInt->AdjustFontForAntiAlias(i, aa_mode);
|
||||
}
|
||||
}
|
||||
|
||||
void wfreefont(size_t fontNumber) {
|
||||
if (fontNumber >= _GP(fonts).size())
|
||||
return;
|
||||
|
||||
_GP(fonts)[fontNumber].TextStencilSub.Destroy();
|
||||
_GP(fonts)[fontNumber].OutlineStencilSub.Destroy();
|
||||
_GP(fonts)[fontNumber].TextStencil.Destroy();
|
||||
_GP(fonts)[fontNumber].OutlineStencil.Destroy();
|
||||
|
||||
if (_GP(fonts)[fontNumber].Renderer != nullptr)
|
||||
_GP(fonts)[fontNumber].Renderer->FreeMemory(fontNumber);
|
||||
|
||||
_GP(fonts)[fontNumber].Renderer = nullptr;
|
||||
}
|
||||
|
||||
void free_all_fonts() {
|
||||
for (size_t i = 0; i < _GP(fonts).size(); ++i) {
|
||||
if (_GP(fonts)[i].Renderer != nullptr)
|
||||
_GP(fonts)[i].Renderer->FreeMemory(i);
|
||||
}
|
||||
_GP(fonts).clear();
|
||||
}
|
||||
|
||||
} // namespace AGS3
|
||||
194
engines/ags/shared/font/fonts.h
Normal file
194
engines/ags/shared/font/fonts.h
Normal file
@@ -0,0 +1,194 @@
|
||||
/* 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 AGS_SHARED_FONT_FONTS_H
|
||||
#define AGS_SHARED_FONT_FONTS_H
|
||||
|
||||
#include "common/std/vector.h"
|
||||
#include "ags/shared/ac/game_struct_defines.h"
|
||||
#include "ags/shared/util/string.h"
|
||||
#include "ags/shared/ac/game_struct_defines.h"
|
||||
#include "ags/shared/font/ags_font_renderer.h"
|
||||
#include "ags/shared/gfx/allegro_bitmap.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
class IAGSFontRenderer;
|
||||
class IAGSFontRenderer2;
|
||||
class IAGSFontRendererInternal;
|
||||
struct FontInfo;
|
||||
struct FontRenderParams;
|
||||
|
||||
namespace AGS {
|
||||
namespace Shared {
|
||||
|
||||
struct Font {
|
||||
// Classic font renderer interface
|
||||
IAGSFontRenderer *Renderer = nullptr;
|
||||
// Extended font renderer interface (optional)
|
||||
IAGSFontRenderer2 *Renderer2 = nullptr;
|
||||
// Internal interface (only for built-in renderers)
|
||||
IAGSFontRendererInternal *RendererInt = nullptr;
|
||||
FontInfo Info;
|
||||
// Values received from the renderer and saved for the reference
|
||||
FontMetrics Metrics;
|
||||
// Precalculated linespacing, based on font properties and compat settings
|
||||
int LineSpacingCalc = 0;
|
||||
|
||||
// Outline buffers
|
||||
Bitmap TextStencil, TextStencilSub;
|
||||
Bitmap OutlineStencil, OutlineStencilSub;
|
||||
|
||||
Font() {}
|
||||
};
|
||||
|
||||
} // namespace Shared
|
||||
} // namespace AGS
|
||||
|
||||
using namespace AGS;
|
||||
|
||||
void init_font_renderer();
|
||||
void shutdown_font_renderer();
|
||||
void adjust_y_coordinate_for_text(int *ypos, size_t fontnum);
|
||||
IAGSFontRenderer *font_replace_renderer(size_t fontNumber, IAGSFontRenderer *renderer);
|
||||
IAGSFontRenderer *font_replace_renderer(size_t fontNumber, IAGSFontRenderer2 *renderer);
|
||||
void font_recalc_metrics(size_t fontNumber);
|
||||
bool font_first_renderer_loaded();
|
||||
bool is_font_loaded(size_t fontNumber);
|
||||
bool is_bitmap_font(size_t fontNumber);
|
||||
bool font_supports_extended_characters(size_t fontNumber);
|
||||
// Get font's name, if it's available, otherwise returns empty string
|
||||
const char *get_font_name(size_t fontNumber);
|
||||
// Get a collection of FFLG_* flags corresponding to this font
|
||||
int get_font_flags(size_t fontNumber);
|
||||
// TODO: with changes to WFN font renderer that implemented safe rendering of
|
||||
// strings containing invalid chars (since 3.3.1) this function is not
|
||||
// important, except for (maybe) few particular cases.
|
||||
// Furthermore, its use complicated things, because AGS could modify some texts
|
||||
// at random times (usually - drawing routines).
|
||||
// Need to check whether it is safe to completely remove it.
|
||||
void ensure_text_valid_for_font(char *text, size_t fontnum);
|
||||
// Get font's scaling multiplier
|
||||
int get_font_scaling_mul(size_t fontNumber);
|
||||
// Calculate actual width of a line of text
|
||||
int get_text_width(const char *texx, size_t fontNumber);
|
||||
// Get the maximal width of the line of text, with corresponding outlining
|
||||
int get_text_width_outlined(const char *text, size_t font_number);
|
||||
// Get the maximal height of the line of text;
|
||||
// note that this won't be a nominal font's height, but the max of each met glyph's graphical height.
|
||||
int get_text_height(const char *text, size_t font_number);
|
||||
// Get font's height; this value is used for logical arrangement of UI elements;
|
||||
// note that this is a "formal" font height, that may have different value
|
||||
// depending on compatibility mode (used when running old games);
|
||||
int get_font_height(size_t fontNumber);
|
||||
// Get the maximal height of the given font, with corresponding outlining
|
||||
int get_font_height_outlined(size_t fontNumber);
|
||||
// Get font's surface height: this always returns the height enough to accommodate
|
||||
// font letters on a bitmap or a texture; the distinction is needed for compatibility reasons
|
||||
int get_font_surface_height(size_t fontNumber);
|
||||
// Get font's maximal graphical extent: this means the farthest vertical positions of glyphs,
|
||||
// relative to the "pen" position. Besides letting to calculate the surface height,
|
||||
// this information also lets to detect if some of the glyphs may appear above y0.
|
||||
std::pair<int, int> get_font_surface_extent(size_t fontNumber);
|
||||
// Get font's line spacing
|
||||
int get_font_linespacing(size_t fontNumber);
|
||||
// Set font's line spacing
|
||||
void set_font_linespacing(size_t fontNumber, int spacing);
|
||||
// Get font's outline type
|
||||
int get_font_outline(size_t font_number);
|
||||
// Get font's automatic outline thickness (if set)
|
||||
int get_font_outline_thickness(size_t font_number);
|
||||
// Gets the total maximal height of the given number of lines printed with the given font;
|
||||
// note that this uses formal font height, for compatibility purposes
|
||||
int get_text_lines_height(size_t fontNumber, size_t numlines);
|
||||
// Gets the height of a graphic surface enough to accommodate this number of text lines;
|
||||
// note this accounts for the real pixel font height
|
||||
int get_text_lines_surf_height(size_t fontNumber, size_t numlines);
|
||||
// Set font's outline type
|
||||
void set_font_outline(size_t font_number, int outline_type,
|
||||
enum FontInfo::AutoOutlineStyle style = FontInfo::kSquared, int thickness = 1);
|
||||
bool is_font_antialiased(size_t font_number);
|
||||
// Outputs a single line of text on the defined position on bitmap, using defined font, color and parameters
|
||||
void wouttextxy(Shared::Bitmap *ds, int xxx, int yyy, size_t fontNumber, color_t text_color, const char *texx);
|
||||
// Assigns FontInfo to the font
|
||||
void set_fontinfo(size_t fontNumber, const FontInfo &finfo);
|
||||
// Gets full information about the font
|
||||
FontInfo get_fontinfo(size_t font_number);
|
||||
// Loads a font from disk
|
||||
bool load_font_size(size_t fontNumber, const FontInfo &font_info); void wgtprintf(Shared::Bitmap *ds, int xxx, int yyy, size_t fontNumber, color_t text_color, char *fmt, ...);
|
||||
// Allocates two outline stencil buffers, or returns previously creates ones;
|
||||
// these buffers are owned by the font, they should not be deleted by the caller.
|
||||
void alloc_font_outline_buffers(size_t font_number,
|
||||
Shared::Bitmap **text_stencil, Shared::Bitmap **outline_stencil,
|
||||
int text_width, int text_height, int color_depth);
|
||||
// Perform necessary adjustments on all fonts in case the text render mode changed (anti-aliasing etc)
|
||||
void adjust_fonts_for_render_mode(bool aa_mode);
|
||||
// Free particular font's data
|
||||
void wfreefont(size_t fontNumber);
|
||||
// Free all fonts data
|
||||
void free_all_fonts();
|
||||
|
||||
// Tells if the text should be antialiased when possible
|
||||
bool ShouldAntiAliasText();
|
||||
|
||||
// SplitLines class represents a list of lines and is meant to reduce
|
||||
// subsequent memory (de)allocations if used often during game loops
|
||||
// and drawing. For that reason it is not equivalent to std::vector,
|
||||
// but keeps constructed String buffers intact for most time.
|
||||
// TODO: implement proper strings pool.
|
||||
class SplitLines {
|
||||
public:
|
||||
inline size_t Count() const {
|
||||
return _count;
|
||||
}
|
||||
inline const Shared::String &operator[](size_t i) const {
|
||||
return _pool[i];
|
||||
}
|
||||
inline Shared::String &operator[](size_t i) {
|
||||
return _pool[i];
|
||||
}
|
||||
inline void Clear() {
|
||||
_pool.clear();
|
||||
_count = 0;
|
||||
}
|
||||
inline void Reset() {
|
||||
_count = 0;
|
||||
}
|
||||
inline void Add(const char *cstr) {
|
||||
if (_pool.size() == _count) _pool.resize(_count + 1);
|
||||
_pool[_count++].SetString(cstr);
|
||||
}
|
||||
|
||||
// An auxiliary line processing buffer
|
||||
std::vector<char> LineBuf;
|
||||
|
||||
private:
|
||||
std::vector<Shared::String> _pool;
|
||||
size_t _count; // actual number of lines in use
|
||||
};
|
||||
|
||||
// Break up the text into lines restricted by the given width;
|
||||
// returns number of lines, or 0 if text cannot be split well to fit in this width
|
||||
size_t split_lines(const char *texx, SplitLines &lines, int width, int fontNumber, size_t max_lines = -1);
|
||||
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
187
engines/ags/shared/font/ttf_font_renderer.cpp
Normal file
187
engines/ags/shared/font/ttf_font_renderer.cpp
Normal file
@@ -0,0 +1,187 @@
|
||||
/* 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 "ags/shared/font/ttf_font_renderer.h"
|
||||
#include "ags/lib/alfont/alfont.h"
|
||||
#include "ags/shared/core/platform.h"
|
||||
#include "ags/shared/ac/game_version.h"
|
||||
#include "ags/globals.h"
|
||||
#include "ags/shared/core/asset_manager.h"
|
||||
#include "ags/shared/util/stream.h"
|
||||
#include "ags/shared/ac/game_struct_defines.h"
|
||||
#include "ags/shared/font/fonts.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
using namespace AGS::Shared;
|
||||
|
||||
// ***** TTF RENDERER *****
|
||||
void TTFFontRenderer::AdjustYCoordinateForFont(int *ycoord, int /*fontNumber*/) {
|
||||
// TTF fonts already have space at the top, so try to remove the gap
|
||||
// TODO: adding -1 was here before (check the comment above),
|
||||
// but how universal is this "space at the top"?
|
||||
// Also, why is this function used only in one case of text rendering?
|
||||
// Review this after we upgrade the font library.
|
||||
ycoord[0]--;
|
||||
}
|
||||
|
||||
void TTFFontRenderer::EnsureTextValidForFont(char * /*text*/, int /*fontNumber*/) {
|
||||
// do nothing, TTF can handle all characters
|
||||
}
|
||||
|
||||
int TTFFontRenderer::GetTextWidth(const char *text, int fontNumber) {
|
||||
return alfont_text_length(_fontData[fontNumber].AlFont, text);
|
||||
}
|
||||
|
||||
int TTFFontRenderer::GetTextHeight(const char * /*text*/, int fontNumber) {
|
||||
return alfont_get_font_real_height(_fontData[fontNumber].AlFont);
|
||||
}
|
||||
|
||||
void TTFFontRenderer::RenderText(const char *text, int fontNumber, BITMAP *destination, int x, int y, int colour) {
|
||||
if (y > destination->cb) // optimisation
|
||||
return;
|
||||
|
||||
// Y - 1 because it seems to get drawn down a bit
|
||||
if ((ShouldAntiAliasText()) && (bitmap_color_depth(destination) > 8))
|
||||
alfont_textout_aa(destination, _fontData[fontNumber].AlFont, text, x, y - 1, colour);
|
||||
else
|
||||
alfont_textout(destination, _fontData[fontNumber].AlFont, text, x, y - 1, colour);
|
||||
}
|
||||
|
||||
bool TTFFontRenderer::LoadFromDisk(int fontNumber, int fontSize) {
|
||||
return LoadFromDiskEx(fontNumber, fontSize, nullptr, nullptr, nullptr);
|
||||
}
|
||||
|
||||
bool TTFFontRenderer::IsBitmapFont() {
|
||||
return false;
|
||||
}
|
||||
|
||||
static int GetAlfontFlags(int load_mode) {
|
||||
int flags = ALFONT_FLG_FORCE_RESIZE | ALFONT_FLG_SELECT_NOMINAL_SZ;
|
||||
// Compatibility: font ascender is always adjusted to the formal font's height;
|
||||
// EXCEPTION: not if it's a game made before AGS 3.4.1 with TTF anti-aliasing
|
||||
// (the reason is uncertain, but this is to emulate old engine's behavior).
|
||||
if (((load_mode & FFLG_ASCENDERFIXUP) != 0) &&
|
||||
!(ShouldAntiAliasText() && (_G(loaded_game_file_version) < kGameVersion_341)))
|
||||
flags |= ALFONT_FLG_ASCENDER_EQ_HEIGHT;
|
||||
// Precalculate real glyphs extent (will make loading fonts relatively slower)
|
||||
flags |= ALFONT_FLG_PRECALC_MAX_CBOX;
|
||||
return flags;
|
||||
}
|
||||
|
||||
// Loads a TTF font of a certain size
|
||||
static ALFONT_FONT *LoadTTF(const String &filename, int fontSize, int alfont_flags) {
|
||||
std::unique_ptr<Stream> reader(_GP(AssetMgr)->OpenAsset(filename));
|
||||
if (!reader)
|
||||
return nullptr;
|
||||
|
||||
const size_t lenof = reader->GetLength();
|
||||
std::vector<char> buf; buf.resize(lenof);
|
||||
reader->Read(&buf.front(), lenof);
|
||||
reader.reset();
|
||||
|
||||
ALFONT_FONT *alfptr = alfont_load_font_from_mem(&buf.front(), lenof);
|
||||
if (!alfptr)
|
||||
return nullptr;
|
||||
alfont_set_font_size_ex(alfptr, fontSize, alfont_flags);
|
||||
return alfptr;
|
||||
}
|
||||
|
||||
// Fill the FontMetrics struct from the given ALFONT
|
||||
static void FillMetrics(ALFONT_FONT *alfptr, FontMetrics *metrics) {
|
||||
metrics->NominalHeight = alfont_get_font_height(alfptr);
|
||||
metrics->RealHeight = alfont_get_font_real_height(alfptr);
|
||||
metrics->CompatHeight = metrics->NominalHeight; // just set to default here
|
||||
alfont_get_font_real_vextent(alfptr, &metrics->VExtent.first, &metrics->VExtent.second);
|
||||
// fixup vextent to be *not less* than realheight
|
||||
metrics->VExtent.first = std::min(0, metrics->VExtent.first);
|
||||
metrics->VExtent.second = std::max(metrics->RealHeight, metrics->VExtent.second);
|
||||
}
|
||||
|
||||
bool TTFFontRenderer::LoadFromDiskEx(int fontNumber, int fontSize, String *src_filename,
|
||||
const FontRenderParams *params, FontMetrics *metrics) {
|
||||
String filename = String::FromFormat("agsfnt%d.ttf", fontNumber);
|
||||
if (fontSize <= 0)
|
||||
fontSize = 8; // compatibility fix
|
||||
assert(params);
|
||||
FontRenderParams f_params = params ? *params : FontRenderParams();
|
||||
if (f_params.SizeMultiplier > 1)
|
||||
fontSize *= f_params.SizeMultiplier;
|
||||
|
||||
ALFONT_FONT *alfptr = LoadTTF(filename, fontSize,
|
||||
GetAlfontFlags(f_params.LoadMode));
|
||||
if (!alfptr)
|
||||
return false;
|
||||
|
||||
_fontData[fontNumber].AlFont = alfptr;
|
||||
_fontData[fontNumber].Params = f_params;
|
||||
if (src_filename)
|
||||
*src_filename = filename;
|
||||
if (metrics)
|
||||
FillMetrics(alfptr, metrics);
|
||||
return true;
|
||||
}
|
||||
|
||||
const char *TTFFontRenderer::GetFontName(int fontNumber) {
|
||||
return alfont_get_name(_fontData[fontNumber].AlFont);
|
||||
}
|
||||
|
||||
int TTFFontRenderer::GetFontHeight(int fontNumber) {
|
||||
return alfont_get_font_real_height(_fontData[fontNumber].AlFont);
|
||||
}
|
||||
|
||||
void TTFFontRenderer::GetFontMetrics(int fontNumber, FontMetrics *metrics) {
|
||||
FillMetrics(_fontData[fontNumber].AlFont, metrics);
|
||||
}
|
||||
|
||||
void TTFFontRenderer::AdjustFontForAntiAlias(int fontNumber, bool /*aa_mode*/) {
|
||||
if (_G(loaded_game_file_version) < kGameVersion_341) {
|
||||
ALFONT_FONT *alfptr = _fontData[fontNumber].AlFont;
|
||||
const FontRenderParams ¶ms = _fontData[fontNumber].Params;
|
||||
int old_height = alfont_get_font_height(alfptr);
|
||||
alfont_set_font_size_ex(alfptr, old_height, GetAlfontFlags(params.LoadMode));
|
||||
}
|
||||
}
|
||||
|
||||
void TTFFontRenderer::FreeMemory(int fontNumber) {
|
||||
alfont_destroy_font(_fontData[fontNumber].AlFont);
|
||||
_fontData.erase(fontNumber);
|
||||
}
|
||||
|
||||
bool TTFFontRenderer::MeasureFontOfPointSize(const String &filename, int size_pt, FontMetrics *metrics) {
|
||||
ALFONT_FONT *alfptr = LoadTTF(filename, size_pt, ALFONT_FLG_FORCE_RESIZE | ALFONT_FLG_SELECT_NOMINAL_SZ);
|
||||
if (!alfptr)
|
||||
return false;
|
||||
FillMetrics(alfptr, metrics);
|
||||
alfont_destroy_font(alfptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TTFFontRenderer::MeasureFontOfPixelHeight(const String &filename, int pixel_height, FontMetrics *metrics) {
|
||||
ALFONT_FONT *alfptr = LoadTTF(filename, pixel_height, ALFONT_FLG_FORCE_RESIZE);
|
||||
if (!alfptr)
|
||||
return false;
|
||||
FillMetrics(alfptr, metrics);
|
||||
alfont_destroy_font(alfptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace AGS3
|
||||
82
engines/ags/shared/font/ttf_font_renderer.h
Normal file
82
engines/ags/shared/font/ttf_font_renderer.h
Normal file
@@ -0,0 +1,82 @@
|
||||
/* 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 AGS_SHARED_FONT_TTF_FONT_RENDERER_H
|
||||
#define AGS_SHARED_FONT_TTF_FONT_RENDERER_H
|
||||
|
||||
#include "common/std/map.h"
|
||||
#include "ags/shared/font/ags_font_renderer.h"
|
||||
#include "ags/shared/util/string.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
struct ALFONT_FONT;
|
||||
|
||||
class TTFFontRenderer : public IAGSFontRendererInternal {
|
||||
public:
|
||||
virtual ~TTFFontRenderer() {}
|
||||
|
||||
// IAGSFontRenderer implementation
|
||||
bool LoadFromDisk(int fontNumber, int fontSize) override;
|
||||
void FreeMemory(int fontNumber) override;
|
||||
bool SupportsExtendedCharacters(int /*fontNumber*/) override {
|
||||
return true;
|
||||
}
|
||||
int GetTextWidth(const char *text, int fontNumber) override;
|
||||
int GetTextHeight(const char *text, int fontNumber) override;
|
||||
void RenderText(const char *text, int fontNumber, BITMAP *destination, int x, int y, int colour) override;
|
||||
void AdjustYCoordinateForFont(int *ycoord, int fontNumber) override;
|
||||
void EnsureTextValidForFont(char *text, int fontNumber) override;
|
||||
|
||||
// IAGSFontRenderer2 implementation
|
||||
int GetVersion() override { return 26; /* first compatible engine API version */ }
|
||||
const char *GetRendererName() override { return "TTFFontRenderer"; }
|
||||
const char *GetFontName(int fontNumber) override;
|
||||
int GetFontHeight(int fontNumber) override;
|
||||
int GetLineSpacing(int fontNumber) override { return 0; /* no specific spacing */ }
|
||||
|
||||
// IAGSFontRendererInternal implementation
|
||||
bool IsBitmapFont() override;
|
||||
bool LoadFromDiskEx(int fontNumber, int fontSize, AGS::Shared::String *src_filename,
|
||||
const FontRenderParams *params, FontMetrics *metrics) override;
|
||||
void GetFontMetrics(int fontNumber, FontMetrics *metrics) override;
|
||||
void AdjustFontForAntiAlias(int fontNumber, bool aa_mode) override;
|
||||
|
||||
//
|
||||
// Utility functions
|
||||
//
|
||||
// Try load the TTF font using provided point size, and report its metrics
|
||||
static bool MeasureFontOfPointSize(const AGS::Shared::String &filename, int size_pt, FontMetrics *metrics);
|
||||
// Try load the TTF font, find the point size which results in pixel height
|
||||
// as close to the requested as possible; report its metrics
|
||||
static bool MeasureFontOfPixelHeight(const AGS::Shared::String &filename, int pixel_height, FontMetrics *metrics);
|
||||
|
||||
private:
|
||||
struct FontData {
|
||||
ALFONT_FONT *AlFont;
|
||||
FontRenderParams Params;
|
||||
};
|
||||
std::map<int, FontData> _fontData;
|
||||
};
|
||||
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
196
engines/ags/shared/font/wfn_font.cpp
Normal file
196
engines/ags/shared/font/wfn_font.cpp
Normal file
@@ -0,0 +1,196 @@
|
||||
/* 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/std/algorithm.h"
|
||||
#include "ags/shared/font/wfn_font.h"
|
||||
#include "ags/shared/debugging/out.h"
|
||||
#include "ags/shared/util/memory.h"
|
||||
#include "ags/shared/util/stream.h"
|
||||
#include "ags/globals.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
using namespace AGS::Shared;
|
||||
|
||||
static const char *WFN_FILE_SIGNATURE = "WGT Font File ";
|
||||
static const size_t WFN_FILE_SIG_LENGTH = 15;
|
||||
static const size_t MinCharDataSize = sizeof(uint16_t) * 2;
|
||||
|
||||
WFNChar::WFNChar()
|
||||
: Width(0)
|
||||
, Height(0)
|
||||
, Data(nullptr) {
|
||||
}
|
||||
|
||||
void WFNChar::RestrictToBytes(size_t bytes) {
|
||||
if (bytes < GetRequiredPixelSize())
|
||||
Height = static_cast<uint16_t>(bytes / GetRowByteCount());
|
||||
}
|
||||
|
||||
const WFNChar &WFNFont::GetChar(uint16_t code) const {
|
||||
return code < _refs.size() ? *_refs[code] : _G(emptyChar);
|
||||
}
|
||||
|
||||
void WFNFont::Clear() {
|
||||
_refs.clear();
|
||||
_items.clear();
|
||||
_pixelData.clear();
|
||||
}
|
||||
|
||||
WFNError WFNFont::ReadFromFile(Stream *in, const soff_t data_size) {
|
||||
Clear();
|
||||
|
||||
const soff_t used_data_size = data_size > 0 ? data_size : in->GetLength();
|
||||
|
||||
// Read font header
|
||||
char sig[WFN_FILE_SIG_LENGTH];
|
||||
in->Read(sig, WFN_FILE_SIG_LENGTH);
|
||||
if (strncmp(sig, WFN_FILE_SIGNATURE, WFN_FILE_SIG_LENGTH) != 0) {
|
||||
Debug::Printf(kDbgMsg_Error, "\tWFN: bad format signature");
|
||||
return kWFNErr_BadSignature; // bad format
|
||||
}
|
||||
|
||||
const soff_t table_addr = static_cast<uint16_t>(in->ReadInt16()); // offset table relative address
|
||||
if (table_addr < (soff_t)(WFN_FILE_SIG_LENGTH + sizeof(uint16_t)) || table_addr >= used_data_size) {
|
||||
Debug::Printf(kDbgMsg_Error, "\tWFN: bad table address: %llu (%llu - %llu)", static_cast<int64>(table_addr),
|
||||
static_cast<int64>(WFN_FILE_SIG_LENGTH + sizeof(uint16_t)), static_cast<int64>(used_data_size));
|
||||
return kWFNErr_BadTableAddress; // bad table address
|
||||
}
|
||||
|
||||
const soff_t offset_table_size = used_data_size - table_addr;
|
||||
const soff_t raw_data_offset = WFN_FILE_SIG_LENGTH + sizeof(uint16_t);
|
||||
const size_t total_char_data = static_cast<size_t>(table_addr - raw_data_offset);
|
||||
const size_t char_count = static_cast<size_t>(offset_table_size / sizeof(uint16_t));
|
||||
|
||||
// We process character data in three steps:
|
||||
// 1. For every character store offset of character item, excluding
|
||||
// duplicates.
|
||||
// 2. Allocate memory for character items and pixel array and copy
|
||||
// appropriate data; test for possible format corruption.
|
||||
// 3. Create array of references from characters to items; same item may be
|
||||
// referenced by many characters.
|
||||
WFNError err = kWFNErr_NoError;
|
||||
|
||||
if (total_char_data == 0u || char_count == 0u)
|
||||
return kWFNErr_NoError; // no items
|
||||
|
||||
// Read character data array
|
||||
std::vector<uint8_t> raw_data; raw_data.resize(total_char_data);
|
||||
in->Read(&raw_data.front(), total_char_data);
|
||||
|
||||
// Read offset table
|
||||
std::vector<uint16_t> offset_table; offset_table.resize(char_count);
|
||||
in->ReadArrayOfInt16(reinterpret_cast<int16_t *>(&offset_table.front()), char_count);
|
||||
|
||||
// Read all referenced offsets in an unsorted vector
|
||||
std::vector<uint16_t> offs;
|
||||
offs.reserve(char_count); // reserve max possible offsets
|
||||
for (size_t i = 0; i < char_count; ++i) {
|
||||
const uint16_t off = offset_table[i];
|
||||
if (off < raw_data_offset || (soff_t)(off + MinCharDataSize) > table_addr) {
|
||||
Debug::Printf("\tWFN: character %d -- bad item offset: %d (%d - %d, +%d)",
|
||||
i, off, raw_data_offset, table_addr, MinCharDataSize);
|
||||
err = kWFNErr_HasBadCharacters; // warn about potentially corrupt format
|
||||
continue; // bad character offset
|
||||
}
|
||||
offs.push_back(off);
|
||||
}
|
||||
// sort offsets vector and remove any duplicates
|
||||
std::sort(offs.begin(), offs.end());
|
||||
#if AGS_PLATFORM_SCUMMVM
|
||||
// TODO: See if this works correctly
|
||||
std::unique(offs.begin(), offs.end());
|
||||
#else
|
||||
std::vector<uint16_t>(offs.begin(), std::unique(offs.begin(), offs.end())).swap(offs);
|
||||
#endif
|
||||
|
||||
// Now that we know number of valid character items, parse and store character data
|
||||
WFNChar init_ch;
|
||||
_items.resize(offs.size());
|
||||
size_t total_pixel_size = 0;
|
||||
for (size_t i = 0; i < _items.size(); ++i) {
|
||||
const uint8_t *p_data = &raw_data[offs[i] - raw_data_offset];
|
||||
init_ch.Width = Memory::ReadInt16LE(p_data);
|
||||
init_ch.Height = Memory::ReadInt16LE(p_data + sizeof(uint16_t));
|
||||
total_pixel_size += init_ch.GetRequiredPixelSize();
|
||||
_items[i] = init_ch;
|
||||
}
|
||||
|
||||
// Now that we know actual size of pixels in use, create pixel data array;
|
||||
// since the items are sorted, the pixel data will be stored sequentially as well.
|
||||
// At this point offs and _items have related elements in the same order.
|
||||
_pixelData.resize(total_pixel_size);
|
||||
std::vector<uint8_t>::iterator pixel_it = _pixelData.begin(); // write ptr
|
||||
for (size_t i = 0; i < _items.size(); ++i) {
|
||||
const size_t pixel_data_size = _items[i].GetRequiredPixelSize();
|
||||
if (pixel_data_size == 0) {
|
||||
Debug::Printf("\tWFN: item at off %d -- null size", offs[i]);
|
||||
err = kWFNErr_HasBadCharacters;
|
||||
continue; // just an empty character
|
||||
}
|
||||
const uint16_t raw_off = offs[i] - raw_data_offset + MinCharDataSize; // offset in raw array
|
||||
size_t src_size = pixel_data_size;
|
||||
if (i + 1 != _items.size() && (soff_t)(raw_off + src_size) > offs[i + 1] - raw_data_offset) { // character pixel data overlaps next character
|
||||
Debug::Printf("\tWFN: item at off %d -- pixel data overlaps next known item (at %d, +%d)",
|
||||
offs[i], offs[i + 1], MinCharDataSize + src_size);
|
||||
err = kWFNErr_HasBadCharacters; // warn about potentially corrupt format
|
||||
src_size = offs[i + 1] - offs[i] - MinCharDataSize;
|
||||
}
|
||||
|
||||
if (raw_off + src_size > total_char_data) { // character pixel data overflow buffer
|
||||
Debug::Printf("\tWFN: item at off %d -- pixel data exceeds available data (at %d, +%d)",
|
||||
offs[i], table_addr, MinCharDataSize + src_size);
|
||||
err = kWFNErr_HasBadCharacters; // warn about potentially corrupt format
|
||||
src_size = total_char_data - raw_off;
|
||||
}
|
||||
_items[i].RestrictToBytes(src_size);
|
||||
|
||||
assert(pixel_it + pixel_data_size <= _pixelData.end()); // should not normally fail
|
||||
Common::copy(raw_data.begin() + raw_off, raw_data.begin() + (raw_off + src_size), pixel_it);
|
||||
_items[i].Data = &(*pixel_it);
|
||||
pixel_it += pixel_data_size;
|
||||
}
|
||||
|
||||
// Create final reference array
|
||||
_refs.resize(char_count);
|
||||
for (size_t i = 0; i < char_count; ++i) {
|
||||
const uint16_t off = offset_table[i];
|
||||
// if bad character offset - reference empty character
|
||||
if (off < raw_data_offset || (soff_t)(off + MinCharDataSize) > table_addr) {
|
||||
_refs[i] = &_G(emptyChar);
|
||||
} else {
|
||||
// in usual case the offset table references items in strict order
|
||||
if (i < _items.size() && offs[i] == off)
|
||||
_refs[i] = &_items[i];
|
||||
else {
|
||||
// we know beforehand that such item must exist
|
||||
std::vector<uint16_t>::const_iterator at = std::lower_bound(offs.begin(), offs.end(), off);
|
||||
assert(at != offs.end() && *at == off && // should not normally fail
|
||||
at - offs.begin() >= 0 && static_cast<size_t>(at - offs.begin()) < _items.size());
|
||||
_refs[i] = &_items[at - offs.begin()]; // set up reference to item
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
} // namespace AGS3
|
||||
108
engines/ags/shared/font/wfn_font.h
Normal file
108
engines/ags/shared/font/wfn_font.h
Normal file
@@ -0,0 +1,108 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// WFNFont - an immutable AGS font object.
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// WFN format:
|
||||
// - signature ( 15 )
|
||||
// - offsets table offset ( 2 )
|
||||
// - characters table (for unknown number of char items):
|
||||
// - width ( 2 )
|
||||
// - height ( 2 )
|
||||
// - pixel bits ( (width / 8 + 1) * height )
|
||||
// - any unknown data
|
||||
// - offsets table (for X chars):
|
||||
// - character offset ( 2 )
|
||||
//
|
||||
// NOTE: unfortunately, at the moment the format does not provide means to
|
||||
// know the number of supported characters for certain, and the size of the
|
||||
// data (file) is used to determine that.
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef AGS_SHARED_FONT_WFN_FONT_H
|
||||
#define AGS_SHARED_FONT_WFN_FONT_H
|
||||
|
||||
#include "common/std/vector.h"
|
||||
#include "ags/shared/core/types.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
namespace AGS {
|
||||
namespace Shared {
|
||||
class Stream;
|
||||
} // namespace Shared
|
||||
} // namespace AGS
|
||||
|
||||
enum WFNError {
|
||||
kWFNErr_NoError,
|
||||
kWFNErr_BadSignature,
|
||||
kWFNErr_BadTableAddress,
|
||||
kWFNErr_HasBadCharacters
|
||||
};
|
||||
|
||||
struct WFNChar {
|
||||
uint16_t Width;
|
||||
uint16_t Height;
|
||||
const uint8_t *Data;
|
||||
|
||||
WFNChar();
|
||||
|
||||
inline size_t GetRowByteCount() const {
|
||||
return (Width + 7) / 8;
|
||||
}
|
||||
|
||||
inline size_t GetRequiredPixelSize() const {
|
||||
return GetRowByteCount() * Height;
|
||||
}
|
||||
|
||||
// Ensure character's width & height fit in given number of pixel bytes
|
||||
void RestrictToBytes(size_t bytes);
|
||||
};
|
||||
|
||||
|
||||
class WFNFont {
|
||||
public:
|
||||
inline uint16_t GetCharCount() const {
|
||||
return static_cast<uint16_t>(_refs.size());
|
||||
}
|
||||
|
||||
// Get WFN character for the given code; if the character is missing, returns empty character
|
||||
const WFNChar &GetChar(uint16_t code) const;
|
||||
|
||||
void Clear();
|
||||
// Reads WFNFont object, using data_size bytes from stream; if data_size = 0,
|
||||
// the available stream's length is used instead. Returns error code.
|
||||
WFNError ReadFromFile(AGS::Shared::Stream *in, const soff_t data_size = 0);
|
||||
|
||||
protected:
|
||||
std::vector<const WFNChar *> _refs; // reference array, contains pointers to elements of _items
|
||||
std::vector<WFNChar> _items; // actual character items
|
||||
std::vector<uint8_t> _pixelData; // pixel data array
|
||||
};
|
||||
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
163
engines/ags/shared/font/wfn_font_renderer.cpp
Normal file
163
engines/ags/shared/font/wfn_font_renderer.cpp
Normal 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 "ags/shared/font/wfn_font_renderer.h"
|
||||
#include "ags/shared/ac/common.h" // our_eip
|
||||
#include "ags/shared/core/asset_manager.h"
|
||||
#include "ags/shared/debugging/out.h"
|
||||
#include "ags/shared/font/wfn_font.h"
|
||||
#include "ags/shared/gfx/bitmap.h"
|
||||
#include "ags/shared/util/stream.h"
|
||||
#include "ags/globals.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
using namespace AGS::Shared;
|
||||
|
||||
void WFNFontRenderer::AdjustYCoordinateForFont(int *ycoord, int fontNumber) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
void WFNFontRenderer::EnsureTextValidForFont(char *text, int fontNumber) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
int WFNFontRenderer::GetTextWidth(const char *text, int fontNumber) {
|
||||
const WFNFont *font = _fontData[fontNumber].Font;
|
||||
const FontRenderParams ¶ms = _fontData[fontNumber].Params;
|
||||
int text_width = 0;
|
||||
|
||||
for (int code = ugetxc(&text); code; code = ugetxc(&text)) {
|
||||
text_width += font->GetChar(code).Width;
|
||||
}
|
||||
return text_width * params.SizeMultiplier;
|
||||
}
|
||||
|
||||
int WFNFontRenderer::GetTextHeight(const char *text, int fontNumber) {
|
||||
const WFNFont *font = _fontData[fontNumber].Font;
|
||||
const FontRenderParams ¶ms = _fontData[fontNumber].Params;
|
||||
int max_height = 0;
|
||||
|
||||
for (int code = ugetxc(&text); code; code = ugetxc(&text)) {
|
||||
const uint16_t height = font->GetChar(code).Height;
|
||||
max_height = std::max(max_height, static_cast<int>(height));
|
||||
}
|
||||
return max_height * params.SizeMultiplier;
|
||||
}
|
||||
|
||||
static int RenderChar(Bitmap *ds, const int at_x, const int at_y, Rect clip,
|
||||
const WFNChar &wfn_char, const int scale, const color_t text_color);
|
||||
|
||||
void WFNFontRenderer::RenderText(const char *text, int fontNumber, BITMAP *destination, int x, int y, int colour) {
|
||||
int oldeip = get_our_eip();
|
||||
set_our_eip(415);
|
||||
|
||||
const WFNFont *font = _fontData[fontNumber].Font;
|
||||
const FontRenderParams ¶ms = _fontData[fontNumber].Params;
|
||||
Bitmap ds(destination, true);
|
||||
|
||||
// NOTE: allegro's putpixel ignores clipping (optimization),
|
||||
// so we'll have to accommodate for that ourselves
|
||||
Rect clip = ds.GetClip();
|
||||
for (int code = ugetxc(&text); code; code = ugetxc(&text))
|
||||
x += RenderChar(&ds, x, y, clip, font->GetChar(code), params.SizeMultiplier, colour);
|
||||
|
||||
set_our_eip(oldeip);
|
||||
}
|
||||
|
||||
static int RenderChar(Bitmap *ds, const int at_x, const int at_y, Rect clip,
|
||||
const WFNChar &wfn_char, const int scale, const color_t text_color) {
|
||||
const int width = wfn_char.Width;
|
||||
const int height = wfn_char.Height;
|
||||
const unsigned char *actdata = wfn_char.Data;
|
||||
const int bytewid = wfn_char.GetRowByteCount();
|
||||
|
||||
int sx = std::max(at_x, clip.Left), ex = clip.Right + 1;
|
||||
int sy = std::max(at_y, clip.Top), ey = clip.Bottom + 1;
|
||||
int sw = std::max(0, clip.Left - at_x);
|
||||
int sh = std::max(0, clip.Top - at_y);
|
||||
for (int h = sh, y = sy; h < height && y < ey; ++h, y += scale) {
|
||||
for (int w = sw, x = sx; w < width && x < ex; ++w, x += scale) {
|
||||
if (((actdata[h * bytewid + (w / 8)] & (0x80 >> (w % 8))) != 0)) {
|
||||
if (scale > 1) {
|
||||
ds->FillRect(RectWH(x, y, scale, scale), text_color);
|
||||
} else {
|
||||
ds->PutPixel(x, y, text_color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return width * scale;
|
||||
}
|
||||
|
||||
bool WFNFontRenderer::LoadFromDisk(int fontNumber, int fontSize) {
|
||||
return LoadFromDiskEx(fontNumber, fontSize, nullptr, nullptr, nullptr);
|
||||
}
|
||||
|
||||
bool WFNFontRenderer::IsBitmapFont() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WFNFontRenderer::LoadFromDiskEx(int fontNumber, int /*fontSize*/, String *src_filename,
|
||||
const FontRenderParams *params, FontMetrics *metrics) {
|
||||
String file_name;
|
||||
Stream *ffi = nullptr;
|
||||
|
||||
file_name.Format("agsfnt%d.wfn", fontNumber);
|
||||
ffi = _GP(AssetMgr)->OpenAsset(file_name);
|
||||
if (ffi == nullptr) {
|
||||
// actual font not found, try font 0 instead
|
||||
// FIXME: this should not be done here in this font renderer implementation,
|
||||
// but somewhere outside, when whoever calls this method
|
||||
file_name = "agsfnt0.wfn";
|
||||
ffi = _GP(AssetMgr)->OpenAsset(file_name);
|
||||
if (ffi == nullptr)
|
||||
return false;
|
||||
}
|
||||
|
||||
WFNFont *font = new WFNFont();
|
||||
WFNError err = font->ReadFromFile(ffi);
|
||||
delete ffi;
|
||||
if (err == kWFNErr_HasBadCharacters)
|
||||
Debug::Printf(kDbgMsg_Warn, "WARNING: font '%s' has mistakes in data format, some characters may be displayed incorrectly", file_name.GetCStr());
|
||||
else if (err != kWFNErr_NoError) {
|
||||
delete font;
|
||||
return false;
|
||||
}
|
||||
_fontData[fontNumber].Font = font;
|
||||
_fontData[fontNumber].Params = params ? *params : FontRenderParams();
|
||||
if (src_filename)
|
||||
*src_filename = file_name;
|
||||
if (metrics)
|
||||
*metrics = FontMetrics();
|
||||
return true;
|
||||
}
|
||||
|
||||
void WFNFontRenderer::FreeMemory(int fontNumber) {
|
||||
delete _fontData[fontNumber].Font;
|
||||
_fontData.erase(fontNumber);
|
||||
}
|
||||
|
||||
bool WFNFontRenderer::SupportsExtendedCharacters(int fontNumber) {
|
||||
return _fontData[fontNumber].Font->GetCharCount() > 128;
|
||||
}
|
||||
|
||||
} // namespace AGS3
|
||||
71
engines/ags/shared/font/wfn_font_renderer.h
Normal file
71
engines/ags/shared/font/wfn_font_renderer.h
Normal file
@@ -0,0 +1,71 @@
|
||||
/* 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 AGS_SHARED_FONT_WFN_FONT_RENDERER_H
|
||||
#define AGS_SHARED_FONT_WFN_FONT_RENDERER_H
|
||||
|
||||
#include "common/std/map.h"
|
||||
#include "ags/lib/std.h"
|
||||
#include "ags/shared/font/ags_font_renderer.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
class WFNFont;
|
||||
|
||||
class WFNFontRenderer : public IAGSFontRendererInternal {
|
||||
public:
|
||||
// IAGSFontRenderer implementation
|
||||
virtual ~WFNFontRenderer() {}
|
||||
|
||||
bool LoadFromDisk(int fontNumber, int fontSize) override;
|
||||
void FreeMemory(int fontNumber) override;
|
||||
bool SupportsExtendedCharacters(int fontNumber) override;
|
||||
int GetTextWidth(const char *text, int fontNumber) override;
|
||||
int GetTextHeight(const char *text, int fontNumber) override;
|
||||
void RenderText(const char *text, int fontNumber, BITMAP *destination, int x, int y, int colour) override;
|
||||
void AdjustYCoordinateForFont(int *ycoord, int fontNumber) override;
|
||||
void EnsureTextValidForFont(char *text, int fontNumber) override;
|
||||
|
||||
// IAGSFontRenderer2 implementation
|
||||
int GetVersion() override { return 26; /* first compatible engine API version */ }
|
||||
const char *GetRendererName() override { return "WFNFontRenderer"; }
|
||||
const char *GetFontName(int /*fontNumber*/) override { return ""; }
|
||||
int GetFontHeight(int fontNumber) override { return 0; /* TODO? */ }
|
||||
int GetLineSpacing(int fontNumber) override { return 0; /* no specific spacing */ }
|
||||
|
||||
// IAGSFontRendererInternal implementation
|
||||
bool IsBitmapFont() override;
|
||||
bool LoadFromDiskEx(int fontNumber, int fontSize, AGS::Shared::String *src_filename,
|
||||
const FontRenderParams *params, FontMetrics *metrics) override;
|
||||
void GetFontMetrics(int fontNumber, FontMetrics *metrics) override { *metrics = FontMetrics(); }
|
||||
void AdjustFontForAntiAlias(int /*fontNumber*/, bool /*aa_mode*/) override { /* do nothing */ }
|
||||
|
||||
private:
|
||||
struct FontData {
|
||||
WFNFont *Font;
|
||||
FontRenderParams Params;
|
||||
};
|
||||
std::map<int, FontData> _fontData;
|
||||
};
|
||||
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
129
engines/ags/shared/game/custom_properties.cpp
Normal file
129
engines/ags/shared/game/custom_properties.cpp
Normal file
@@ -0,0 +1,129 @@
|
||||
/* 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 "ags/shared/game/custom_properties.h"
|
||||
#include "ags/shared/util/stream.h"
|
||||
#include "ags/shared/util/string_utils.h"
|
||||
|
||||
namespace AGS3 {
|
||||
namespace AGS {
|
||||
namespace Shared {
|
||||
|
||||
PropertyDesc::PropertyDesc() {
|
||||
Type = kPropertyBoolean;
|
||||
}
|
||||
|
||||
PropertyDesc::PropertyDesc(const String &name, PropertyType type, const String &desc, const String &def_value) {
|
||||
Name = name;
|
||||
Type = type;
|
||||
Description = desc;
|
||||
DefaultValue = def_value;
|
||||
}
|
||||
|
||||
|
||||
namespace Properties {
|
||||
|
||||
PropertyError ReadSchema(PropertySchema &schema, Stream *in) {
|
||||
PropertyVersion version = (PropertyVersion)in->ReadInt32();
|
||||
if (version < kPropertyVersion_Initial ||
|
||||
version > kPropertyVersion_Current) {
|
||||
return kPropertyErr_UnsupportedFormat;
|
||||
}
|
||||
|
||||
PropertyDesc prop;
|
||||
int count = in->ReadInt32();
|
||||
if (version == kPropertyVersion_Initial) {
|
||||
for (int i = 0; i < count; ++i) {
|
||||
prop.Name.Read(in, LEGACY_MAX_CUSTOM_PROP_SCHEMA_NAME_LENGTH);
|
||||
prop.Description.Read(in, LEGACY_MAX_CUSTOM_PROP_DESC_LENGTH);
|
||||
prop.DefaultValue.Read(in, LEGACY_MAX_CUSTOM_PROP_VALUE_LENGTH);
|
||||
prop.Type = (PropertyType)in->ReadInt32();
|
||||
schema[prop.Name] = prop;
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < count; ++i) {
|
||||
prop.Name = StrUtil::ReadString(in);
|
||||
prop.Type = (PropertyType)in->ReadInt32();
|
||||
prop.Description = StrUtil::ReadString(in);
|
||||
prop.DefaultValue = StrUtil::ReadString(in);
|
||||
schema[prop.Name] = prop;
|
||||
}
|
||||
}
|
||||
return kPropertyErr_NoError;
|
||||
}
|
||||
|
||||
void WriteSchema(const PropertySchema &schema, Stream *out) {
|
||||
out->WriteInt32(kPropertyVersion_Current);
|
||||
out->WriteInt32(schema.size());
|
||||
for (PropertySchema::const_iterator it = schema.begin();
|
||||
it != schema.end(); ++it) {
|
||||
const PropertyDesc &prop = it->_value;
|
||||
StrUtil::WriteString(prop.Name, out);
|
||||
out->WriteInt32(prop.Type);
|
||||
StrUtil::WriteString(prop.Description, out);
|
||||
StrUtil::WriteString(prop.DefaultValue, out);
|
||||
}
|
||||
}
|
||||
|
||||
PropertyError ReadValues(StringIMap &map, Stream *in, bool aligned) {
|
||||
PropertyVersion version = (PropertyVersion)in->ReadInt32();
|
||||
if (version < kPropertyVersion_Initial ||
|
||||
version > kPropertyVersion_Current) {
|
||||
return kPropertyErr_UnsupportedFormat;
|
||||
}
|
||||
|
||||
int count = in->ReadInt32();
|
||||
if (version == kPropertyVersion_Initial) {
|
||||
for (int i = 0; i < count; ++i) {
|
||||
String name = String::FromStream(in, LEGACY_MAX_CUSTOM_PROP_NAME_LENGTH);
|
||||
map[name] = String::FromStream(in, LEGACY_MAX_CUSTOM_PROP_VALUE_LENGTH);
|
||||
}
|
||||
} else {
|
||||
if (aligned) {
|
||||
for (int i = 0; i < count; ++i) {
|
||||
String name = StrUtil::ReadStringAligned(in);
|
||||
map[name] = StrUtil::ReadStringAligned(in);
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < count; ++i) {
|
||||
String name = StrUtil::ReadString(in);
|
||||
map[name] = StrUtil::ReadString(in);
|
||||
}
|
||||
}
|
||||
}
|
||||
return kPropertyErr_NoError;
|
||||
}
|
||||
|
||||
void WriteValues(const StringIMap &map, Stream *out) {
|
||||
out->WriteInt32(kPropertyVersion_Current);
|
||||
out->WriteInt32(map.size());
|
||||
for (StringIMap::const_iterator it = map.begin();
|
||||
it != map.end(); ++it) {
|
||||
StrUtil::WriteString(it->_key, out);
|
||||
StrUtil::WriteString(it->_value, out);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Properties
|
||||
|
||||
} // namespace Shared
|
||||
} // namespace AGS
|
||||
} // namespace AGS3
|
||||
109
engines/ags/shared/game/custom_properties.h
Normal file
109
engines/ags/shared/game/custom_properties.h
Normal file
@@ -0,0 +1,109 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// Custom property structs
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Custom property schema is kept by GameSetupStruct object as a single
|
||||
// instance and defines property type and default value. Every game entity that
|
||||
// has properties implemented keeps CustomProperties object, which stores
|
||||
// actual property values only if ones are different from defaults.
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef AGS_SHARED_GAME_CUSTOM_PROPERTIES_H
|
||||
#define AGS_SHARED_GAME_CUSTOM_PROPERTIES_H
|
||||
|
||||
#include "common/std/map.h"
|
||||
#include "ags/shared/util/string.h"
|
||||
#include "ags/shared/util/string_types.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
#define LEGACY_MAX_CUSTOM_PROPERTIES 30
|
||||
// NOTE: for some reason the property name stored in schema object was limited
|
||||
// to only 20 characters, while the custom properties map could hold up to 200.
|
||||
// Whether this was an error or design choice is unknown.
|
||||
#define LEGACY_MAX_CUSTOM_PROP_SCHEMA_NAME_LENGTH 20
|
||||
#define LEGACY_MAX_CUSTOM_PROP_NAME_LENGTH 200
|
||||
#define LEGACY_MAX_CUSTOM_PROP_DESC_LENGTH 100
|
||||
#define LEGACY_MAX_CUSTOM_PROP_VALUE_LENGTH 500
|
||||
|
||||
namespace AGS {
|
||||
namespace Shared {
|
||||
|
||||
enum PropertyVersion {
|
||||
kPropertyVersion_Initial = 1,
|
||||
kPropertyVersion_340,
|
||||
kPropertyVersion_Current = kPropertyVersion_340
|
||||
};
|
||||
|
||||
enum PropertyType {
|
||||
kPropertyUndefined = 0,
|
||||
kPropertyBoolean,
|
||||
kPropertyInteger,
|
||||
kPropertyString
|
||||
};
|
||||
|
||||
enum PropertyError {
|
||||
kPropertyErr_NoError,
|
||||
kPropertyErr_UnsupportedFormat
|
||||
};
|
||||
|
||||
//
|
||||
// PropertyDesc - a description of a single custom property
|
||||
//
|
||||
struct PropertyDesc {
|
||||
String Name;
|
||||
PropertyType Type;
|
||||
String Description;
|
||||
String DefaultValue;
|
||||
|
||||
PropertyDesc();
|
||||
PropertyDesc(const String &name, PropertyType type, const String &desc, const String &def_value);
|
||||
};
|
||||
|
||||
// NOTE: AGS has case-insensitive property IDs
|
||||
// Schema - a map of property descriptions
|
||||
typedef std::unordered_map<String, PropertyDesc, IgnoreCase_Hash, IgnoreCase_EqualTo> PropertySchema;
|
||||
|
||||
|
||||
namespace Properties {
|
||||
PropertyError ReadSchema(PropertySchema &schema, Stream *in);
|
||||
void WriteSchema(const PropertySchema &schema, Stream *out);
|
||||
|
||||
// Reads property values from the stream and assign them to map.
|
||||
// The non-matching existing map items, if any, are NOT erased.
|
||||
// NOTE: "aligned" parameter is for legacy saves support only.
|
||||
PropertyError ReadValues(StringIMap &map, Stream *in, bool aligned = false);
|
||||
// Writes property values chunk to the stream
|
||||
void WriteValues(const StringIMap &map, Stream *out);
|
||||
|
||||
} // namespace Properties
|
||||
|
||||
} // namespace Shared
|
||||
} // namespace AGS
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
375
engines/ags/shared/game/interactions.cpp
Normal file
375
engines/ags/shared/game/interactions.cpp
Normal file
@@ -0,0 +1,375 @@
|
||||
/* 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 <string.h>
|
||||
#include "ags/shared/ac/common.h" // quit
|
||||
#include "ags/shared/game/interactions.h"
|
||||
#include "ags/shared/util/stream.h"
|
||||
#include "ags/shared/util/math.h"
|
||||
#include "common/util.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
using namespace AGS::Shared;
|
||||
|
||||
namespace AGS {
|
||||
namespace Shared {
|
||||
|
||||
InteractionValue::InteractionValue() {
|
||||
Type = kInterValLiteralInt;
|
||||
Value = 0;
|
||||
Extra = 0;
|
||||
}
|
||||
|
||||
void InteractionValue::clear() {
|
||||
Type = kInterValInvalid;
|
||||
Value = 0;
|
||||
Extra = 0;
|
||||
}
|
||||
|
||||
void InteractionValue::Read(Stream *in) {
|
||||
Type = (InterValType)in->ReadInt8();
|
||||
in->Seek(3); // alignment padding to int32
|
||||
Value = in->ReadInt32();
|
||||
Extra = in->ReadInt32();
|
||||
}
|
||||
|
||||
void InteractionValue::Write(Stream *out) const {
|
||||
out->WriteInt8(Type);
|
||||
out->WriteByteCount(0, 3); // alignment padding to int32
|
||||
out->WriteInt32(Value);
|
||||
out->WriteInt32(Extra);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
InteractionCommand::InteractionCommand()
|
||||
: Type(0)
|
||||
, Parent(nullptr) {
|
||||
}
|
||||
|
||||
InteractionCommand::InteractionCommand(const InteractionCommand &ic) {
|
||||
*this = ic;
|
||||
}
|
||||
|
||||
void InteractionCommand::Assign(const InteractionCommand &ic, InteractionCommandList *parent) {
|
||||
Type = ic.Type;
|
||||
memcpy(Data, ic.Data, sizeof(Data));
|
||||
Children.reset(ic.Children.get() ? new InteractionCommandList(*ic.Children) : nullptr);
|
||||
Parent = parent;
|
||||
}
|
||||
|
||||
void InteractionCommand::Reset() {
|
||||
Type = 0;
|
||||
for (uint8 i = 0; i < ARRAYSIZE(Data); i++) Data[i].clear();
|
||||
Children.reset();
|
||||
Parent = nullptr;
|
||||
}
|
||||
|
||||
void InteractionCommand::ReadValues(Stream *in) {
|
||||
for (int i = 0; i < MAX_ACTION_ARGS; ++i) {
|
||||
Data[i].Read(in);
|
||||
}
|
||||
}
|
||||
|
||||
void InteractionCommand::Read(Stream *in, bool &has_children) {
|
||||
in->ReadInt32(); // skip the 32-bit vtbl ptr (the old serialization peculiarity)
|
||||
Type = in->ReadInt32();
|
||||
ReadValues(in);
|
||||
has_children = in->ReadInt32() != 0;
|
||||
in->ReadInt32(); // skip 32-bit Parent pointer
|
||||
}
|
||||
|
||||
void InteractionCommand::WriteValues(Stream *out) const {
|
||||
for (int i = 0; i < MAX_ACTION_ARGS; ++i) {
|
||||
Data[i].Write(out);
|
||||
}
|
||||
}
|
||||
|
||||
void InteractionCommand::Write(Stream *out) const {
|
||||
out->WriteInt32(0); // write dummy 32-bit vtbl ptr
|
||||
out->WriteInt32(Type);
|
||||
WriteValues(out);
|
||||
out->WriteInt32(Children.get() ? 1 : 0); // notify that has children
|
||||
out->WriteInt32(0); // skip 32-bit Parent pointer
|
||||
}
|
||||
|
||||
InteractionCommand &InteractionCommand::operator=(const InteractionCommand &ic) {
|
||||
if (this == &ic) {
|
||||
return *this; // prevent self-assignment
|
||||
}
|
||||
|
||||
Type = ic.Type;
|
||||
memcpy(Data, ic.Data, sizeof(Data));
|
||||
Children.reset(ic.Children.get() ? new InteractionCommandList(*ic.Children) : nullptr);
|
||||
Parent = ic.Parent;
|
||||
return *this;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
InteractionCommandList::InteractionCommandList()
|
||||
: TimesRun(0) {
|
||||
}
|
||||
|
||||
InteractionCommandList::InteractionCommandList(const InteractionCommandList &icmd_list) {
|
||||
TimesRun = icmd_list.TimesRun;
|
||||
Cmds.resize(icmd_list.Cmds.size());
|
||||
for (size_t i = 0; i < icmd_list.Cmds.size(); ++i) {
|
||||
Cmds[i].Assign(icmd_list.Cmds[i], this);
|
||||
}
|
||||
}
|
||||
|
||||
void InteractionCommandList::Reset() {
|
||||
Cmds.clear();
|
||||
TimesRun = 0;
|
||||
}
|
||||
|
||||
void InteractionCommandList::ReadCommands(Stream *in, std::vector<bool> &cmd_children) {
|
||||
for (size_t i = 0; i < Cmds.size(); ++i) {
|
||||
bool has_children;
|
||||
Cmds[i].Read(in, has_children);
|
||||
cmd_children[i] = has_children;
|
||||
}
|
||||
}
|
||||
|
||||
void InteractionCommandList::Read(Stream *in) {
|
||||
size_t cmd_count = in->ReadInt32();
|
||||
TimesRun = in->ReadInt32();
|
||||
|
||||
std::vector<bool> cmd_children;
|
||||
Cmds.resize(cmd_count);
|
||||
cmd_children.resize(cmd_count);
|
||||
ReadCommands(in, cmd_children);
|
||||
|
||||
for (size_t i = 0; i < cmd_count; ++i) {
|
||||
if (cmd_children[i]) {
|
||||
Cmds[i].Children.reset(new InteractionCommandList());
|
||||
Cmds[i].Children->Read(in);
|
||||
}
|
||||
Cmds[i].Parent = this;
|
||||
}
|
||||
}
|
||||
|
||||
void InteractionCommandList::WriteCommands(Stream *out) const {
|
||||
for (InterCmdVector::const_iterator it = Cmds.begin(); it != Cmds.end(); ++it) {
|
||||
it->Write(out);
|
||||
}
|
||||
}
|
||||
|
||||
void InteractionCommandList::Write(Stream *out) const {
|
||||
size_t cmd_count = Cmds.size();
|
||||
out->WriteInt32(cmd_count);
|
||||
out->WriteInt32(TimesRun);
|
||||
|
||||
WriteCommands(out);
|
||||
|
||||
for (size_t i = 0; i < cmd_count; ++i) {
|
||||
if (Cmds[i].Children.get() != nullptr)
|
||||
Cmds[i].Children->Write(out);
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
InteractionEvent::InteractionEvent()
|
||||
: Type(0)
|
||||
, TimesRun(0) {
|
||||
}
|
||||
|
||||
InteractionEvent::InteractionEvent(const InteractionEvent &ie) {
|
||||
*this = ie;
|
||||
}
|
||||
|
||||
InteractionEvent &InteractionEvent::operator = (const InteractionEvent &ie) {
|
||||
Type = ie.Type;
|
||||
TimesRun = ie.TimesRun;
|
||||
Response.reset(ie.Response.get() ? new InteractionCommandList(*ie.Response) : nullptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
Interaction::Interaction() {
|
||||
}
|
||||
|
||||
Interaction::Interaction(const Interaction &ni) {
|
||||
*this = ni;
|
||||
}
|
||||
|
||||
Interaction &Interaction::operator =(const Interaction &ni) {
|
||||
Events.resize(ni.Events.size());
|
||||
for (size_t i = 0; i < ni.Events.size(); ++i) {
|
||||
Events[i] = InteractionEvent(ni.Events[i]);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void Interaction::CopyTimesRun(const Interaction &inter) {
|
||||
assert(Events.size() == inter.Events.size());
|
||||
size_t count = MIN(Events.size(), inter.Events.size());
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
Events[i].TimesRun = inter.Events[i].TimesRun;
|
||||
}
|
||||
}
|
||||
|
||||
void Interaction::Reset() {
|
||||
Events.clear();
|
||||
}
|
||||
|
||||
Interaction *Interaction::CreateFromStream(Stream *in) {
|
||||
if (in->ReadInt32() != kInteractionVersion_Initial)
|
||||
return nullptr; // unsupported format
|
||||
|
||||
const size_t evt_count = in->ReadInt32();
|
||||
if (evt_count > MAX_NEWINTERACTION_EVENTS)
|
||||
quit("Can't deserialize interaction: too many events");
|
||||
|
||||
int32_t types[MAX_NEWINTERACTION_EVENTS];
|
||||
int32_t load_response[MAX_NEWINTERACTION_EVENTS];
|
||||
in->ReadArrayOfInt32(types, evt_count);
|
||||
in->ReadArrayOfInt32(load_response, evt_count);
|
||||
|
||||
Interaction *inter = new Interaction();
|
||||
inter->Events.resize(evt_count);
|
||||
for (size_t i = 0; i < evt_count; ++i) {
|
||||
InteractionEvent &evt = inter->Events[i];
|
||||
evt.Type = types[i];
|
||||
if (load_response[i] != 0) {
|
||||
evt.Response.reset(new InteractionCommandList());
|
||||
evt.Response->Read(in);
|
||||
}
|
||||
}
|
||||
return inter;
|
||||
}
|
||||
|
||||
void Interaction::Write(Stream *out) const {
|
||||
out->WriteInt32(kInteractionVersion_Initial); // Version
|
||||
const size_t evt_count = Events.size();
|
||||
out->WriteInt32(evt_count);
|
||||
for (size_t i = 0; i < evt_count; ++i) {
|
||||
out->WriteInt32(Events[i].Type);
|
||||
}
|
||||
|
||||
// The pointer is only checked against NULL to determine whether the event exists
|
||||
for (size_t i = 0; i < evt_count; ++i) {
|
||||
out->WriteInt32(Events[i].Response.get() ? 1 : 0);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < evt_count; ++i) {
|
||||
if (Events[i].Response.get())
|
||||
Events[i].Response->Write(out);
|
||||
}
|
||||
}
|
||||
|
||||
void Interaction::ReadFromSavedgame_v321(Stream *in) {
|
||||
const size_t evt_count = in->ReadInt32();
|
||||
if (evt_count > MAX_NEWINTERACTION_EVENTS)
|
||||
quit("Can't deserialize interaction: too many events");
|
||||
|
||||
Events.resize(evt_count);
|
||||
// Read required amount and skip the remaining placeholders
|
||||
for (size_t i = 0; i < evt_count; ++i) {
|
||||
Events[i].Type = in->ReadInt32();
|
||||
}
|
||||
in->Seek((MAX_NEWINTERACTION_EVENTS - evt_count) * sizeof(int32_t));
|
||||
ReadTimesRunFromSave_v321(in);
|
||||
// Skip an array of dummy 32-bit pointers (nasty legacy format)
|
||||
in->Seek(MAX_NEWINTERACTION_EVENTS * sizeof(int32_t));
|
||||
}
|
||||
|
||||
void Interaction::WriteToSavedgame_v321(Stream *out) const {
|
||||
const size_t evt_count = Events.size();
|
||||
out->WriteInt32(evt_count);
|
||||
|
||||
for (size_t i = 0; i < evt_count; ++i) {
|
||||
out->WriteInt32(Events[i].Type);
|
||||
}
|
||||
out->WriteByteCount(0, (MAX_NEWINTERACTION_EVENTS - evt_count) * sizeof(int32_t));
|
||||
WriteTimesRunToSave_v321(out);
|
||||
// Array of dummy 32-bit pointers (nasty legacy format)
|
||||
out->WriteByteCount(0, MAX_NEWINTERACTION_EVENTS * sizeof(int32_t));
|
||||
}
|
||||
|
||||
void Interaction::ReadTimesRunFromSave_v321(Stream *in) {
|
||||
const size_t evt_count = Events.size();
|
||||
// Read required amount and skip the remaining placeholders
|
||||
for (size_t i = 0; i < evt_count; ++i) {
|
||||
Events[i].TimesRun = in->ReadInt32();
|
||||
}
|
||||
in->Seek((MAX_NEWINTERACTION_EVENTS - evt_count) * sizeof(int32_t));
|
||||
}
|
||||
|
||||
void Interaction::WriteTimesRunToSave_v321(Stream *out) const {
|
||||
const size_t evt_count = Events.size();
|
||||
for (size_t i = 0; i < Events.size(); ++i) {
|
||||
out->WriteInt32(Events[i].TimesRun);
|
||||
}
|
||||
out->WriteByteCount(0, (MAX_NEWINTERACTION_EVENTS - evt_count) * sizeof(int32_t));
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#define INTER_VAR_NAME_LENGTH 23
|
||||
|
||||
InteractionVariable::InteractionVariable()
|
||||
: Type(0)
|
||||
, Value(0) {
|
||||
}
|
||||
|
||||
InteractionVariable::InteractionVariable(const String &name, char type, int val)
|
||||
: Name(name)
|
||||
, Type(type)
|
||||
, Value(val) {
|
||||
}
|
||||
|
||||
void InteractionVariable::Read(Stream *in) {
|
||||
Name.ReadCount(in, INTER_VAR_NAME_LENGTH);
|
||||
Type = in->ReadInt8();
|
||||
Value = in->ReadInt32();
|
||||
}
|
||||
|
||||
void InteractionVariable::Write(Shared::Stream *out) const {
|
||||
out->Write(Name.GetCStr(), INTER_VAR_NAME_LENGTH);
|
||||
out->WriteInt8(Type);
|
||||
out->WriteInt32(Value);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
InteractionScripts *InteractionScripts::CreateFromStream(Stream *in) {
|
||||
const size_t evt_count = in->ReadInt32();
|
||||
if (evt_count > MAX_NEWINTERACTION_EVENTS) {
|
||||
quit("Can't deserialize interaction scripts: too many events");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
InteractionScripts *scripts = new InteractionScripts();
|
||||
for (size_t i = 0; i < evt_count; ++i) {
|
||||
String name = String::FromStream(in);
|
||||
scripts->ScriptFuncNames.push_back(name);
|
||||
}
|
||||
return scripts;
|
||||
}
|
||||
|
||||
} // namespace Shared
|
||||
} // namespace AGS
|
||||
} // namespace AGS3
|
||||
213
engines/ags/shared/game/interactions.h
Normal file
213
engines/ags/shared/game/interactions.h
Normal file
@@ -0,0 +1,213 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// Interaction structs.
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Most of the interaction types here were used before the script and have
|
||||
// very limited capabilities. They were removed from AGS completely in
|
||||
// generation 3.0. The code is left for backwards compatibility.
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
/* THE WAY THIS WORKS:
|
||||
*
|
||||
* Interaction (Hotspot 1)
|
||||
* |
|
||||
* +-- eventTypes [NUM_EVENTS]
|
||||
* +-- InteractionCommandList [NUM_EVENTS] (Look at hotspot)
|
||||
* |
|
||||
* +-- InteractionCommand [NUM_COMMANDS] (Display message)
|
||||
* |
|
||||
* +-- InteractionValue [NUM_ARGUMENTS] (5)
|
||||
*/
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef AGS_SHARED_GAME_INTEREACTIONS_H
|
||||
#define AGS_SHARED_GAME_INTEREACTIONS_H
|
||||
|
||||
#include "common/std/memory.h"
|
||||
#include "ags/shared/util/string_types.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
#define LOCAL_VARIABLE_OFFSET 10000
|
||||
#define MAX_GLOBAL_VARIABLES 100
|
||||
#define MAX_ACTION_ARGS 5
|
||||
#define MAX_NEWINTERACTION_EVENTS 30
|
||||
#define MAX_COMMANDS_PER_LIST 40
|
||||
|
||||
namespace AGS {
|
||||
namespace Shared {
|
||||
|
||||
enum InterValType : int8_t {
|
||||
kInterValInvalid = 0,
|
||||
kInterValLiteralInt = 1,
|
||||
kInterValVariable = 2,
|
||||
kInterValBoolean = 3,
|
||||
kInterValCharnum = 4
|
||||
};
|
||||
|
||||
enum InteractionVersion {
|
||||
kInteractionVersion_Initial = 1
|
||||
};
|
||||
|
||||
// InteractionValue represents an argument of interaction command
|
||||
struct InteractionValue {
|
||||
InterValType Type; // value type
|
||||
int Value; // value definition
|
||||
int Extra;
|
||||
|
||||
InteractionValue();
|
||||
|
||||
void clear();
|
||||
|
||||
void Read(Stream *in);
|
||||
void Write(Stream *out) const;
|
||||
};
|
||||
|
||||
|
||||
struct InteractionCommandList;
|
||||
typedef std::unique_ptr<InteractionCommandList> UInterCmdList;
|
||||
|
||||
// InteractionCommand represents a single command (action), an item of Command List
|
||||
struct InteractionCommand {
|
||||
int Type; // type of action
|
||||
InteractionValue Data[MAX_ACTION_ARGS]; // action arguments
|
||||
UInterCmdList Children; // list of sub-actions
|
||||
InteractionCommandList *Parent; // action parent (optional)
|
||||
|
||||
InteractionCommand();
|
||||
InteractionCommand(const InteractionCommand &ic);
|
||||
|
||||
void Assign(const InteractionCommand &ic, InteractionCommandList *parent);
|
||||
void Reset();
|
||||
|
||||
void Read(Stream *in, bool &has_children);
|
||||
void Write(Stream *out) const;
|
||||
|
||||
InteractionCommand &operator = (const InteractionCommand &ic);
|
||||
|
||||
private:
|
||||
void ReadValues(Stream *in);
|
||||
void WriteValues(Stream *out) const;
|
||||
};
|
||||
|
||||
|
||||
typedef std::vector<InteractionCommand> InterCmdVector;
|
||||
// InteractionCommandList represents a list of commands (actions) that need to be
|
||||
// performed on particular game event
|
||||
struct InteractionCommandList {
|
||||
InterCmdVector Cmds; // actions to run
|
||||
int TimesRun; // used by engine to track score changes
|
||||
|
||||
InteractionCommandList();
|
||||
InteractionCommandList(const InteractionCommandList &icmd_list);
|
||||
|
||||
void Reset();
|
||||
|
||||
void Read(Stream *in);
|
||||
void Write(Stream *out) const;
|
||||
|
||||
protected:
|
||||
void ReadCommands(Shared::Stream *in, std::vector<bool> &cmd_children);
|
||||
void WriteCommands(Shared::Stream *out) const;
|
||||
};
|
||||
|
||||
|
||||
// InteractionEvent is a single event with a list of commands to performed
|
||||
struct InteractionEvent {
|
||||
int Type; // type of event
|
||||
int TimesRun; // used by engine to track score changes
|
||||
UInterCmdList Response; // list of commands to run
|
||||
|
||||
InteractionEvent();
|
||||
InteractionEvent(const InteractionEvent &ie);
|
||||
|
||||
InteractionEvent &operator = (const InteractionEvent &ic);
|
||||
};
|
||||
|
||||
typedef std::vector<InteractionEvent> InterEvtVector;
|
||||
// Interaction is the list of events and responses for a game or game object
|
||||
struct Interaction {
|
||||
// The first few event types depend on the item - ID's of 100+ are
|
||||
// custom events (used for subroutines)
|
||||
InterEvtVector Events;
|
||||
|
||||
Interaction();
|
||||
Interaction(const Interaction &inter);
|
||||
|
||||
// Copy information on number of times events of this interaction were fired
|
||||
void CopyTimesRun(const Interaction &inter);
|
||||
void Reset();
|
||||
|
||||
// Game static data (de)serialization
|
||||
static Interaction *CreateFromStream(Stream *in);
|
||||
void Write(Stream *out) const;
|
||||
|
||||
// Reading and writing runtime data from/to savedgame;
|
||||
// NOTE: these are backwards-compatible methods, that do not always
|
||||
// have practical sense
|
||||
void ReadFromSavedgame_v321(Stream *in);
|
||||
void WriteToSavedgame_v321(Stream *out) const;
|
||||
void ReadTimesRunFromSave_v321(Stream *in);
|
||||
void WriteTimesRunToSave_v321(Stream *out) const;
|
||||
|
||||
Interaction &operator =(const Interaction &inter);
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<Interaction> PInteraction;
|
||||
|
||||
|
||||
// Legacy pre-3.0 kind of global and local room variables
|
||||
struct InteractionVariable {
|
||||
String Name{};
|
||||
char Type{ '\0' };
|
||||
int Value{ 0 };
|
||||
|
||||
InteractionVariable();
|
||||
InteractionVariable(const String &name, char type, int val);
|
||||
|
||||
void Read(Stream *in);
|
||||
void Write(Stream *out) const;
|
||||
};
|
||||
|
||||
typedef std::vector<InteractionVariable> InterVarVector;
|
||||
|
||||
|
||||
// A list of script function names for all supported events
|
||||
struct InteractionScripts {
|
||||
StringV ScriptFuncNames;
|
||||
|
||||
static InteractionScripts *CreateFromStream(Stream *in);
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<InteractionScripts> PInteractionScripts;
|
||||
|
||||
} // namespace Shared
|
||||
} // namespace AGS
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
943
engines/ags/shared/game/main_game_file.cpp
Normal file
943
engines/ags/shared/game/main_game_file.cpp
Normal file
@@ -0,0 +1,943 @@
|
||||
/* 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 "ags/shared/ac/audio_clip_type.h"
|
||||
#include "ags/shared/ac/dialog_topic.h"
|
||||
#include "ags/shared/ac/game_setup_struct.h"
|
||||
#include "ags/shared/ac/sprite_cache.h"
|
||||
#include "ags/shared/ac/view.h"
|
||||
#include "ags/shared/ac/words_dictionary.h"
|
||||
#include "ags/shared/ac/dynobj/script_audio_clip.h"
|
||||
#include "ags/shared/core/asset.h"
|
||||
#include "ags/shared/core/asset_manager.h"
|
||||
#include "ags/shared/debugging/out.h"
|
||||
#include "ags/shared/game/main_game_file.h"
|
||||
#include "ags/shared/gui/gui_button.h"
|
||||
#include "ags/shared/gui/gui_label.h"
|
||||
#include "ags/shared/font/fonts.h"
|
||||
#include "ags/shared/gui/gui_main.h"
|
||||
#include "ags/shared/script/cc_common.h"
|
||||
#include "ags/shared/util/data_ext.h"
|
||||
#include "ags/shared/util/path.h"
|
||||
#include "ags/shared/util/string_compat.h"
|
||||
#include "ags/shared/util/string_utils.h"
|
||||
#include "ags/globals.h"
|
||||
|
||||
namespace AGS3 {
|
||||
namespace AGS {
|
||||
namespace Shared {
|
||||
|
||||
const char *MainGameSource::DefaultFilename_v3 = "game28.dta";
|
||||
const char *MainGameSource::DefaultFilename_v2 = "ac2game.dta";
|
||||
const char *MainGameSource::Signature = "Adventure Creator Game File v2";
|
||||
|
||||
MainGameSource::MainGameSource()
|
||||
: DataVersion(kGameVersion_Undefined) {
|
||||
}
|
||||
|
||||
String GetMainGameFileErrorText(MainGameFileErrorType err) {
|
||||
switch (err) {
|
||||
case kMGFErr_NoError:
|
||||
return "No error.";
|
||||
case kMGFErr_FileOpenFailed:
|
||||
return "Main game file not found or could not be opened.";
|
||||
case kMGFErr_SignatureFailed:
|
||||
return "Not an AGS main game file or unsupported format.";
|
||||
case kMGFErr_FormatVersionTooOld:
|
||||
return "Format version is too old; this engine can only run games made with AGS 2.5 or later.";
|
||||
case kMGFErr_FormatVersionNotSupported:
|
||||
return "Format version not supported.";
|
||||
case kMGFErr_CapsNotSupported:
|
||||
return "The game requires extended capabilities which aren't supported by the engine.";
|
||||
case kMGFErr_InvalidNativeResolution:
|
||||
return "Unable to determine native game resolution.";
|
||||
case kMGFErr_TooManySprites:
|
||||
return "Too many sprites for this engine to handle.";
|
||||
case kMGFErr_InvalidPropertySchema:
|
||||
return "Failed to deserialize custom properties schema.";
|
||||
case kMGFErr_InvalidPropertyValues:
|
||||
return "Errors encountered when reading custom properties.";
|
||||
case kMGFErr_CreateGlobalScriptFailed:
|
||||
return "Failed to load global script.";
|
||||
case kMGFErr_CreateDialogScriptFailed:
|
||||
return "Failed to load dialog script.";
|
||||
case kMGFErr_CreateScriptModuleFailed:
|
||||
return "Failed to load script module.";
|
||||
case kMGFErr_GameEntityFailed:
|
||||
return "Failed to load one or more game entities.";
|
||||
case kMGFErr_PluginDataFmtNotSupported:
|
||||
return "Format version of plugin data is not supported.";
|
||||
case kMGFErr_PluginDataSizeTooLarge:
|
||||
return "Plugin data size is too large.";
|
||||
case kMGFErr_ExtListFailed:
|
||||
return "There was error reading game data extensions.";
|
||||
case kMGFErr_ExtUnknown:
|
||||
return "Unknown extension.";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return "Unknown error.";
|
||||
}
|
||||
|
||||
LoadedGameEntities::LoadedGameEntities(GameSetupStruct &game)
|
||||
: Game(game)
|
||||
, SpriteCount(0) {
|
||||
}
|
||||
|
||||
LoadedGameEntities::~LoadedGameEntities() {}
|
||||
|
||||
bool IsMainGameLibrary(const String &filename) {
|
||||
// We must not only detect if the given file is a correct AGS data library,
|
||||
// we also have to assure that this library contains main game asset.
|
||||
// Library may contain some optional data (digital audio, speech etc), but
|
||||
// that is not what we want.
|
||||
AssetLibInfo lib;
|
||||
if (AssetManager::ReadDataFileTOC(filename, lib) != kAssetNoError)
|
||||
return false;
|
||||
for (size_t i = 0; i < lib.AssetInfos.size(); ++i) {
|
||||
if (lib.AssetInfos[i].FileName.CompareNoCase(MainGameSource::DefaultFilename_v3) == 0 ||
|
||||
lib.AssetInfos[i].FileName.CompareNoCase(MainGameSource::DefaultFilename_v2) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Scans given directory for game data libraries, returns first found or none.
|
||||
// Tracks files with standard AGS package names:
|
||||
// - *.ags is a standart cross-platform file pattern for AGS games,
|
||||
// - ac2game.dat is a legacy file name for very old games,
|
||||
// - agsgame.dat is a legacy file name used in some non-Windows releases,
|
||||
// - *.exe is a MS Win executable; it is included to this case because
|
||||
// users often run AGS ports with Windows versions of games.
|
||||
String FindGameData(const String &path, bool(*fn_testfile)(const String &)) {
|
||||
Common::FSNode folder(path.GetCStr());
|
||||
Common::FSList files;
|
||||
if (folder.getChildren(files, Common::FSNode::kListFilesOnly)) {
|
||||
for (Common::FSList::iterator it = files.begin(); it != files.end(); ++it) {
|
||||
Common::String test_file = it->getName();
|
||||
Common::Path filePath = it->getPath();
|
||||
|
||||
if (test_file.hasSuffixIgnoreCase(".ags") ||
|
||||
test_file.equalsIgnoreCase("ac2game.dat") ||
|
||||
test_file.equalsIgnoreCase("agsgame.dat") ||
|
||||
test_file.hasSuffixIgnoreCase(".exe")) {
|
||||
if (IsMainGameLibrary(test_file.c_str()) && fn_testfile(filePath.toString('/'))) {
|
||||
Debug::Printf("Found game data pak: %s", test_file.c_str());
|
||||
return test_file.c_str();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
static bool comparitor(const String &) {
|
||||
return true;
|
||||
}
|
||||
|
||||
String FindGameData(const String &path) {
|
||||
return FindGameData(path, comparitor);
|
||||
}
|
||||
|
||||
// Begins reading main game file from a generic stream
|
||||
static HGameFileError OpenMainGameFileBase(Stream *in, MainGameSource &src) {
|
||||
// Check data signature
|
||||
String data_sig = String::FromStreamCount(in, strlen(MainGameSource::Signature));
|
||||
if (data_sig.Compare(MainGameSource::Signature))
|
||||
return new MainGameFileError(kMGFErr_SignatureFailed);
|
||||
// Read data format version and requested engine version
|
||||
src.DataVersion = (GameDataVersion)in->ReadInt32();
|
||||
if (src.DataVersion >= kGameVersion_230)
|
||||
src.CompiledWith = StrUtil::ReadString(in);
|
||||
if (src.DataVersion < kGameVersion_250)
|
||||
return new MainGameFileError(kMGFErr_FormatVersionTooOld, String::FromFormat("Required format version: %d, supported %d - %d", src.DataVersion, kGameVersion_250, kGameVersion_Current));
|
||||
if (src.DataVersion > kGameVersion_Current)
|
||||
return new MainGameFileError(kMGFErr_FormatVersionNotSupported,
|
||||
String::FromFormat("Game was compiled with %s. Required format version: %d, supported %d - %d", src.CompiledWith.GetCStr(), src.DataVersion, kGameVersion_250, kGameVersion_Current));
|
||||
// Read required capabilities
|
||||
if (src.DataVersion >= kGameVersion_341) {
|
||||
size_t count = in->ReadInt32();
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
src.Caps.insert(StrUtil::ReadString(in));
|
||||
}
|
||||
// Remember loaded game data version
|
||||
// NOTE: this global variable is embedded in the code too much to get
|
||||
// rid of it too easily; the easy way is to set it whenever the main
|
||||
// game file is opened.
|
||||
_G(loaded_game_file_version) = src.DataVersion;
|
||||
_G(game_compiled_version).SetFromString(src.CompiledWith);
|
||||
return HGameFileError::None();
|
||||
}
|
||||
|
||||
HGameFileError OpenMainGameFile(const String &filename, MainGameSource &src) {
|
||||
// Cleanup source struct
|
||||
src = MainGameSource();
|
||||
// Try to open given file
|
||||
Stream *in = File::OpenFileRead(filename);
|
||||
if (!in)
|
||||
return new MainGameFileError(kMGFErr_FileOpenFailed, String::FromFormat("Tried filename: %s.", filename.GetCStr()));
|
||||
src.Filename = filename;
|
||||
src.InputStream.reset(in);
|
||||
return OpenMainGameFileBase(in, src);
|
||||
}
|
||||
|
||||
HGameFileError OpenMainGameFileFromDefaultAsset(MainGameSource &src, AssetManager *mgr) {
|
||||
// Cleanup source struct
|
||||
src = MainGameSource();
|
||||
// Try to find and open main game file
|
||||
String filename = MainGameSource::DefaultFilename_v3;
|
||||
Stream *in = mgr->OpenAsset(filename);
|
||||
if (!in) {
|
||||
filename = MainGameSource::DefaultFilename_v2;
|
||||
in = mgr->OpenAsset(filename);
|
||||
}
|
||||
if (!in)
|
||||
return new MainGameFileError(kMGFErr_FileOpenFailed,
|
||||
String::FromFormat("Tried filenames: %s, %s.", MainGameSource::DefaultFilename_v3, MainGameSource::DefaultFilename_v2));
|
||||
src.Filename = filename;
|
||||
src.InputStream.reset(in);
|
||||
return OpenMainGameFileBase(in, src);
|
||||
}
|
||||
|
||||
HGameFileError ReadDialogScript(PScript &dialog_script, Stream *in, GameDataVersion data_ver) {
|
||||
if (data_ver > kGameVersion_310) { // 3.1.1+ dialog script
|
||||
dialog_script.reset(ccScript::CreateFromStream(in));
|
||||
if (dialog_script == nullptr)
|
||||
return new MainGameFileError(kMGFErr_CreateDialogScriptFailed, cc_get_error().ErrorString);
|
||||
} else { // 2.x and < 3.1.1 dialog
|
||||
dialog_script.reset();
|
||||
}
|
||||
return HGameFileError::None();
|
||||
}
|
||||
|
||||
HGameFileError ReadScriptModules(std::vector<PScript> &sc_mods, Stream *in, GameDataVersion data_ver) {
|
||||
if (data_ver >= kGameVersion_270) { // 2.7.0+ script modules
|
||||
int count = in->ReadInt32();
|
||||
sc_mods.resize(count);
|
||||
for (int i = 0; i < count; ++i) {
|
||||
sc_mods[i].reset(ccScript::CreateFromStream(in));
|
||||
if (sc_mods[i] == nullptr)
|
||||
return new MainGameFileError(kMGFErr_CreateScriptModuleFailed, cc_get_error().ErrorString);
|
||||
}
|
||||
} else {
|
||||
sc_mods.resize(0);
|
||||
}
|
||||
return HGameFileError::None();
|
||||
}
|
||||
|
||||
void ReadViews(GameSetupStruct &game, std::vector<ViewStruct> &views, Stream *in, GameDataVersion data_ver) {
|
||||
views.resize(game.numviews);
|
||||
if (data_ver > kGameVersion_272) // 3.x views
|
||||
{
|
||||
for (int i = 0; i < game.numviews; ++i) {
|
||||
views[i].ReadFromFile(in);
|
||||
}
|
||||
} else // 2.x views
|
||||
{
|
||||
std::vector<ViewStruct272> oldv(game.numviews);
|
||||
for (int i = 0; i < game.numviews; ++i) {
|
||||
oldv[i].ReadFromFile(in);
|
||||
}
|
||||
Convert272ViewsToNew(oldv, views);
|
||||
}
|
||||
}
|
||||
|
||||
void ReadDialogs(std::vector<DialogTopic> &dialog,
|
||||
std::vector<std::vector<uint8_t>> &old_dialog_scripts,
|
||||
std::vector<String> &old_dialog_src,
|
||||
std::vector<String> &old_speech_lines,
|
||||
Stream *in, GameDataVersion data_ver, int dlg_count) {
|
||||
dialog.resize(dlg_count);
|
||||
for (int i = 0; i < dlg_count; ++i) {
|
||||
dialog[i].ReadFromFile(in);
|
||||
}
|
||||
|
||||
if (data_ver > kGameVersion_310)
|
||||
return;
|
||||
|
||||
old_dialog_scripts.resize(dlg_count);
|
||||
old_dialog_src.resize(dlg_count);
|
||||
for (int i = 0; i < dlg_count; ++i) {
|
||||
// NOTE: originally this was read into dialog[i].optionscripts
|
||||
old_dialog_scripts[i].resize(dialog[i].codesize);
|
||||
in->Read(old_dialog_scripts[i].data(), dialog[i].codesize);
|
||||
|
||||
// Encrypted text script
|
||||
int script_text_len = in->ReadInt32();
|
||||
if (script_text_len > 1) {
|
||||
// Originally in the Editor +20000 bytes more were allocated, with comment:
|
||||
// "add a large buffer because it will get added to if another option is added"
|
||||
// which probably referred to this data used by old editor directly to edit dialogs
|
||||
char *buffer = new char[script_text_len + 1];
|
||||
in->Read(buffer, script_text_len);
|
||||
if (data_ver > kGameVersion_260)
|
||||
decrypt_text(buffer, script_text_len);
|
||||
buffer[script_text_len] = 0;
|
||||
old_dialog_src[i] = buffer;
|
||||
delete[] buffer;
|
||||
} else {
|
||||
in->Seek(script_text_len);
|
||||
}
|
||||
}
|
||||
|
||||
// Read the dialog lines
|
||||
//
|
||||
// TODO: investigate this: these strings were read much simpler in the editor, see code:
|
||||
/*
|
||||
char stringbuffer[1000];
|
||||
for (bb=0;bb<thisgame.numdlgmessage;bb++) {
|
||||
if ((filever >= 26) && (encrypted))
|
||||
read_string_decrypt(iii, stringbuffer);
|
||||
else
|
||||
fgetstring(stringbuffer, iii);
|
||||
}
|
||||
*/
|
||||
//int i = 0;
|
||||
char buffer[1000];
|
||||
if (data_ver <= kGameVersion_260) {
|
||||
// Plain text on <= 2.60
|
||||
bool end_reached = false;
|
||||
|
||||
while (!end_reached) {
|
||||
char *nextchar = buffer;
|
||||
|
||||
while (1) {
|
||||
*nextchar = in->ReadInt8();
|
||||
if (*nextchar == 0)
|
||||
break;
|
||||
|
||||
if ((unsigned char)*nextchar == 0xEF) {
|
||||
end_reached = true;
|
||||
in->Seek(-1);
|
||||
break;
|
||||
}
|
||||
|
||||
nextchar++;
|
||||
}
|
||||
|
||||
if (end_reached)
|
||||
break;
|
||||
|
||||
old_speech_lines.push_back(buffer);
|
||||
//i++;
|
||||
}
|
||||
} else {
|
||||
// Encrypted text on > 2.60
|
||||
while (1) {
|
||||
size_t newlen = static_cast<uint32_t>(in->ReadInt32());
|
||||
if (newlen == 0xCAFEBEEF) { // GUI magic
|
||||
in->Seek(-4);
|
||||
break;
|
||||
}
|
||||
|
||||
newlen = MIN(newlen, sizeof(buffer) - 1);
|
||||
in->Read(buffer, newlen);
|
||||
decrypt_text(buffer, newlen);
|
||||
buffer[newlen] = 0;
|
||||
old_speech_lines.push_back(buffer);
|
||||
//i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HGameFileError ReadPlugins(std::vector<PluginInfo> &infos, Stream *in) {
|
||||
int fmt_ver = in->ReadInt32();
|
||||
if (fmt_ver != 1)
|
||||
return new MainGameFileError(kMGFErr_PluginDataFmtNotSupported, String::FromFormat("Version: %d, supported: %d", fmt_ver, 1));
|
||||
|
||||
int pl_count = in->ReadInt32();
|
||||
for (int i = 0; i < pl_count; ++i) {
|
||||
String name = String::FromStream(in);
|
||||
size_t datasize = in->ReadInt32();
|
||||
// just check for silly datasizes
|
||||
if (datasize > PLUGIN_SAVEBUFFERSIZE)
|
||||
return new MainGameFileError(kMGFErr_PluginDataSizeTooLarge, String::FromFormat("Required: %zu, max: %zu", datasize, (size_t)PLUGIN_SAVEBUFFERSIZE));
|
||||
|
||||
PluginInfo info;
|
||||
info.Name = name;
|
||||
if (datasize > 0) {
|
||||
info.Data.resize(datasize);
|
||||
in->Read(&info.Data.front(), datasize);
|
||||
}
|
||||
infos.push_back(info);
|
||||
}
|
||||
return HGameFileError::None();
|
||||
}
|
||||
|
||||
// Create the missing audioClips data structure for 3.1.x games.
|
||||
// This is done by going through the data files and adding all music*.*
|
||||
// and sound*.* files to it.
|
||||
void BuildAudioClipArray(const std::vector<String> &assets, std::vector<ScriptAudioClip> &audioclips) {
|
||||
char temp_name[30];
|
||||
int temp_number;
|
||||
char temp_extension[10];
|
||||
|
||||
for (const String &asset : assets) {
|
||||
if (sscanf(asset.GetCStr(), "%5s%d.%3s", temp_name, &temp_number, temp_extension) != 3)
|
||||
continue;
|
||||
|
||||
ScriptAudioClip clip;
|
||||
if (ags_stricmp(temp_extension, "mp3") == 0)
|
||||
clip.fileType = eAudioFileMP3;
|
||||
else if (ags_stricmp(temp_extension, "wav") == 0)
|
||||
clip.fileType = eAudioFileWAV;
|
||||
else if (ags_stricmp(temp_extension, "voc") == 0)
|
||||
clip.fileType = eAudioFileVOC;
|
||||
else if (ags_stricmp(temp_extension, "mid") == 0)
|
||||
clip.fileType = eAudioFileMIDI;
|
||||
else if ((ags_stricmp(temp_extension, "mod") == 0) || (ags_stricmp(temp_extension, "xm") == 0)
|
||||
|| (ags_stricmp(temp_extension, "s3m") == 0) || (ags_stricmp(temp_extension, "it") == 0))
|
||||
clip.fileType = eAudioFileMOD;
|
||||
else if (ags_stricmp(temp_extension, "ogg") == 0)
|
||||
clip.fileType = eAudioFileOGG;
|
||||
else
|
||||
continue;
|
||||
|
||||
if (ags_stricmp(temp_name, "music") == 0) {
|
||||
clip.scriptName.Format("aMusic%d", temp_number);
|
||||
clip.fileName.Format("music%d.%s", temp_number, temp_extension);
|
||||
clip.bundlingType = (ags_stricmp(temp_extension, "mid") == 0) ? AUCL_BUNDLE_EXE : AUCL_BUNDLE_VOX;
|
||||
clip.type = 2;
|
||||
clip.defaultRepeat = 1;
|
||||
} else if (ags_stricmp(temp_name, "sound") == 0) {
|
||||
clip.scriptName.Format("aSound%d", temp_number);
|
||||
clip.fileName.Format("sound%d.%s", temp_number, temp_extension);
|
||||
clip.bundlingType = AUCL_BUNDLE_EXE;
|
||||
clip.type = 3;
|
||||
clip.defaultRepeat = 0;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
clip.defaultVolume = 100;
|
||||
clip.defaultPriority = 50;
|
||||
clip.id = audioclips.size();
|
||||
audioclips.push_back(clip);
|
||||
}
|
||||
}
|
||||
|
||||
void ApplySpriteData(GameSetupStruct &game, const LoadedGameEntities &ents, GameDataVersion data_ver) {
|
||||
if (ents.SpriteCount == 0)
|
||||
return;
|
||||
|
||||
// Apply sprite flags read from original format (sequential array)
|
||||
_GP(spriteset).EnlargeTo(ents.SpriteCount - 1);
|
||||
for (size_t i = 0; i < ents.SpriteCount; ++i) {
|
||||
_GP(game).SpriteInfos[i].Flags = ents.SpriteFlags[i];
|
||||
}
|
||||
|
||||
// Promote sprite resolutions and mark legacy resolution setting
|
||||
if (data_ver < kGameVersion_350) {
|
||||
for (size_t i = 0; i < ents.SpriteCount; ++i) {
|
||||
SpriteInfo &info = _GP(game).SpriteInfos[i];
|
||||
if (_GP(game).IsLegacyHiRes() == info.IsLegacyHiRes())
|
||||
info.Flags &= ~(SPF_HIRES | SPF_VAR_RESOLUTION);
|
||||
else
|
||||
info.Flags |= SPF_VAR_RESOLUTION;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UpgradeFonts(GameSetupStruct &game, GameDataVersion data_ver) {
|
||||
if (data_ver < kGameVersion_350) {
|
||||
for (int i = 0; i < _GP(game).numfonts; ++i) {
|
||||
FontInfo &finfo = _GP(game).fonts[i];
|
||||
// If the game is hi-res but font is designed for low-res, then scale it up
|
||||
if (_GP(game).IsLegacyHiRes() && _GP(game).options[OPT_HIRES_FONTS] == 0) {
|
||||
finfo.SizeMultiplier = HIRES_COORD_MULTIPLIER;
|
||||
} else {
|
||||
finfo.SizeMultiplier = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (data_ver < kGameVersion_360) {
|
||||
for (int i = 0; i < game.numfonts; ++i) {
|
||||
FontInfo &finfo = game.fonts[i];
|
||||
if (finfo.Outline == FONT_OUTLINE_AUTO) {
|
||||
finfo.AutoOutlineStyle = FontInfo::kSquared;
|
||||
finfo.AutoOutlineThickness = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (data_ver < kGameVersion_360_11) { // use global defaults for the font load flags
|
||||
for (int i = 0; i < game.numfonts; ++i) {
|
||||
game.fonts[i].Flags |= FFLG_TTF_BACKCOMPATMASK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Convert audio data to the current version
|
||||
void UpgradeAudio(GameSetupStruct &game, LoadedGameEntities &ents, GameDataVersion data_ver) {
|
||||
if (data_ver >= kGameVersion_320)
|
||||
return;
|
||||
|
||||
// An explanation of building audio clips array for pre-3.2 games.
|
||||
//
|
||||
// When AGS version 3.2 was released, it contained new audio system.
|
||||
// In the nutshell, prior to 3.2 audio files had to be manually put
|
||||
// to game project directory and their IDs were taken out of filenames.
|
||||
// Since 3.2 this information is stored inside the game data.
|
||||
// To make the modern engine compatible with pre-3.2 games, we have
|
||||
// to scan game data packages for audio files, and enumerate them
|
||||
// ourselves, then add this information to game struct.
|
||||
|
||||
// Create soundClips and audioClipTypes structures.
|
||||
std::vector<AudioClipType> audiocliptypes;
|
||||
std::vector<ScriptAudioClip> audioclips;
|
||||
|
||||
// TODO: find out what is 4 (maybe music, sound, ambient sound, voice?)
|
||||
audiocliptypes.resize(4);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
audiocliptypes[i].reservedChannels = 1;
|
||||
audiocliptypes[i].id = i;
|
||||
audiocliptypes[i].volume_reduction_while_speech_playing = 10;
|
||||
}
|
||||
audiocliptypes[3].reservedChannels = 0;
|
||||
|
||||
audioclips.reserve(1000);
|
||||
std::vector<String> assets;
|
||||
// Read audio clip names from from registered libraries
|
||||
for (size_t i = 0; i < _GP(AssetMgr)->GetLibraryCount(); ++i) {
|
||||
const AssetLibInfo *game_lib = _GP(AssetMgr)->GetLibraryInfo(i);
|
||||
if (File::IsDirectory(game_lib->BasePath))
|
||||
continue; // might be a directory
|
||||
|
||||
for (const AssetInfo &info : game_lib->AssetInfos) {
|
||||
if (info.FileName.CompareLeftNoCase("music", 5) == 0 || info.FileName.CompareLeftNoCase("sound", 5) == 0)
|
||||
assets.push_back(info.FileName);
|
||||
}
|
||||
}
|
||||
// Append contents of the registered directories
|
||||
// TODO: implement pattern search or asset query with callback (either of two or both)
|
||||
// within AssetManager to avoid doing this in place here. Alternatively we could maybe
|
||||
// make AssetManager to do directory scans by demand and fill AssetInfos...
|
||||
// but that have to be done consistently if done at all.
|
||||
for (size_t i = 0; i < _GP(AssetMgr)->GetLibraryCount(); ++i) {
|
||||
const AssetLibInfo *game_lib = _GP(AssetMgr)->GetLibraryInfo(i);
|
||||
if (!File::IsDirectory(game_lib->BasePath))
|
||||
continue; // might be a library
|
||||
|
||||
|
||||
Common::FSNode folder(game_lib->BasePath.GetCStr());
|
||||
Common::FSList files;
|
||||
folder.getChildren(files, Common::FSNode::kListFilesOnly);
|
||||
|
||||
for (Common::FSList::iterator it = files.begin(); it != files.end(); ++it) {
|
||||
Common::String name = (*it).getName();
|
||||
|
||||
if (name.hasPrefixIgnoreCase("music") || name.hasPrefixIgnoreCase("sound"))
|
||||
assets.push_back(name.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
BuildAudioClipArray(assets, audioclips);
|
||||
|
||||
// Copy gathered data over to game
|
||||
_GP(game).audioClipTypes = audiocliptypes;
|
||||
_GP(game).audioClips = audioclips;
|
||||
|
||||
RemapLegacySoundNums(game, ents.Views, data_ver);
|
||||
}
|
||||
|
||||
// Convert character data to the current version
|
||||
void UpgradeCharacters(GameSetupStruct &game, GameDataVersion data_ver) {
|
||||
auto &chars = _GP(game).chars;
|
||||
auto &chars2 = _GP(game).chars2;
|
||||
const int numcharacters = _GP(game).numcharacters;
|
||||
|
||||
// Fixup character script names for 2.x (EGO -> cEgo)
|
||||
// In 2.x versions the "scriptname" field in game data contained a name
|
||||
// limited by 14 chars (although serialized in 20 bytes). After reading,
|
||||
// it was exported as "cScriptname..." for the script.
|
||||
if (data_ver <= kGameVersion_272) {
|
||||
char namelwr[LEGACY_MAX_SCRIPT_NAME_LEN - 1];
|
||||
for (int i = 0; i < numcharacters; i++) {
|
||||
if (chars[i].scrname[0] == 0)
|
||||
continue;
|
||||
ags_strncpy_s(namelwr, sizeof(namelwr), chars[i].scrname, LEGACY_MAX_SCRIPT_NAME_LEN - 2);
|
||||
ags_strlwr(namelwr + 1); // lowercase starting with the second char
|
||||
snprintf(chars[i].scrname, sizeof(chars[i].scrname), "c%s", namelwr);
|
||||
chars2[i].scrname_new = chars[i].scrname;
|
||||
}
|
||||
}
|
||||
|
||||
// Fix character walk speed for < 3.1.1
|
||||
if (data_ver <= kGameVersion_310) {
|
||||
for (int i = 0; i < numcharacters; i++) {
|
||||
if (_GP(game).options[OPT_ANTIGLIDE])
|
||||
chars[i].flags |= CHF_ANTIGLIDE;
|
||||
}
|
||||
}
|
||||
|
||||
// Characters can always walk through each other on < 2.54
|
||||
if (data_ver < kGameVersion_254) {
|
||||
for (int i = 0; i < numcharacters; i++) {
|
||||
chars[i].flags |= CHF_NOBLOCKING;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UpgradeGUI(GameSetupStruct &game, GameDataVersion data_ver) {
|
||||
// Previously, Buttons and Labels had a fixed Translated behavior
|
||||
if (data_ver < kGameVersion_361) {
|
||||
for (auto &btn : _GP(guibuts))
|
||||
btn.SetTranslated(true); // always translated
|
||||
for (auto &lbl : _GP(guilabels))
|
||||
lbl.SetTranslated(true); // always translated
|
||||
}
|
||||
}
|
||||
|
||||
void UpgradeMouseCursors(GameSetupStruct &game, GameDataVersion data_ver) {
|
||||
if (data_ver <= kGameVersion_272) {
|
||||
// Change cursor.view from 0 to -1 for non-animating cursors.
|
||||
for (int i = 0; i < _GP(game).numcursors; ++i) {
|
||||
if (_GP(game).mcurs[i].view == 0)
|
||||
_GP(game).mcurs[i].view = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Adjusts score clip id, depending on game data version
|
||||
void RemapLegacySoundNums(GameSetupStruct &game, std::vector<ViewStruct> &views, GameDataVersion data_ver) {
|
||||
if (data_ver >= kGameVersion_320)
|
||||
return;
|
||||
|
||||
// Setup sound clip played on score event
|
||||
game.scoreClipID = -1;
|
||||
if (game.options[OPT_SCORESOUND] > 0) {
|
||||
ScriptAudioClip *clip = GetAudioClipForOldStyleNumber(game, false, game.options[OPT_SCORESOUND]);
|
||||
if (clip)
|
||||
game.scoreClipID = clip->id;
|
||||
}
|
||||
|
||||
// Reset view frame clip refs
|
||||
// NOTE: we do not map these to real clips right away,
|
||||
// instead we do this at runtime whenever we find a non-mapped frame sound.
|
||||
for (size_t v = 0; v < (size_t)game.numviews; ++v) {
|
||||
for (size_t l = 0; l < (size_t)views[v].numLoops; ++l) {
|
||||
for (size_t f = 0; f < (size_t)views[v].loops[l].numFrames; ++f) {
|
||||
views[v].loops[l].frames[f].audioclip = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Assigns default global message at given index
|
||||
void SetDefaultGlmsg(GameSetupStruct &game, int msgnum, const char *val) {
|
||||
// TODO: find out why the index should be lowered by 500
|
||||
// (or rather if we may pass correct index right away)
|
||||
msgnum -= 500;
|
||||
if (_GP(game).messages[msgnum].IsEmpty())
|
||||
_GP(game).messages[msgnum] = val;
|
||||
}
|
||||
|
||||
// Sets up default global messages (these are used mainly in older games)
|
||||
void SetDefaultGlobalMessages(GameSetupStruct &game) {
|
||||
SetDefaultGlmsg(game, 983, "Sorry, not now.");
|
||||
SetDefaultGlmsg(game, 984, "Restore");
|
||||
SetDefaultGlmsg(game, 985, "Cancel");
|
||||
SetDefaultGlmsg(game, 986, "Select a game to restore:");
|
||||
SetDefaultGlmsg(game, 987, "Save");
|
||||
SetDefaultGlmsg(game, 988, "Type a name to save as:");
|
||||
SetDefaultGlmsg(game, 989, "Replace");
|
||||
SetDefaultGlmsg(game, 990, "The save directory is full. You must replace an existing game:");
|
||||
SetDefaultGlmsg(game, 991, "Replace:");
|
||||
SetDefaultGlmsg(game, 992, "With:");
|
||||
SetDefaultGlmsg(game, 993, "Quit");
|
||||
SetDefaultGlmsg(game, 994, "Play");
|
||||
SetDefaultGlmsg(game, 995, "Are you sure you want to quit?");
|
||||
SetDefaultGlmsg(game, 996, "You are carrying nothing.");
|
||||
}
|
||||
|
||||
void FixupSaveDirectory(GameSetupStruct &game) {
|
||||
// If the save game folder was not specified by game author, create one of
|
||||
// the game name, game GUID, or uniqueid, as a last resort
|
||||
if (_GP(game).saveGameFolderName.IsEmpty()) {
|
||||
if (!_GP(game).gamename.IsEmpty())
|
||||
_GP(game).saveGameFolderName = _GP(game).gamename;
|
||||
else if (_GP(game).guid[0])
|
||||
_GP(game).saveGameFolderName = _GP(game).guid;
|
||||
else
|
||||
_GP(game).saveGameFolderName.Format("AGS-Game-%d", _GP(game).uniqueid);
|
||||
}
|
||||
// Lastly, fixup folder name by removing any illegal characters
|
||||
_GP(game).saveGameFolderName = Path::FixupSharedFilename(_GP(game).saveGameFolderName);
|
||||
}
|
||||
|
||||
HGameFileError ReadSpriteFlags(LoadedGameEntities &ents, Stream *in, GameDataVersion data_ver) {
|
||||
size_t sprcount;
|
||||
if (data_ver < kGameVersion_256)
|
||||
sprcount = LEGACY_MAX_SPRITES_V25;
|
||||
else
|
||||
sprcount = in->ReadInt32();
|
||||
if (sprcount > (size_t)SpriteCache::MAX_SPRITE_INDEX + 1)
|
||||
return new MainGameFileError(kMGFErr_TooManySprites, String::FromFormat("Count: %zu, max: %zu", sprcount, (size_t)SpriteCache::MAX_SPRITE_INDEX + 1));
|
||||
|
||||
ents.SpriteCount = sprcount;
|
||||
ents.SpriteFlags.resize(sprcount);
|
||||
in->Read(ents.SpriteFlags.data(), sprcount);
|
||||
return HGameFileError::None();
|
||||
}
|
||||
|
||||
// GameDataExtReader reads main game data's extension blocks
|
||||
class GameDataExtReader : public DataExtReader {
|
||||
public:
|
||||
GameDataExtReader(LoadedGameEntities &ents, GameDataVersion data_ver, Stream *in)
|
||||
: DataExtReader(in, kDataExt_NumID8 | kDataExt_File64)
|
||||
, _ents(ents)
|
||||
, _dataVer(data_ver) {
|
||||
}
|
||||
|
||||
protected:
|
||||
HError ReadBlock(int block_id, const String &ext_id,
|
||||
soff_t block_len, bool &read_next) override;
|
||||
|
||||
LoadedGameEntities &_ents;
|
||||
GameDataVersion _dataVer;
|
||||
};
|
||||
|
||||
HError GameDataExtReader::ReadBlock(int /*block_id*/, const String &ext_id,
|
||||
soff_t /*block_len*/, bool &read_next) {
|
||||
read_next = true;
|
||||
// Add extensions here checking ext_id, which is an up to 16-chars name, for example:
|
||||
// if (ext_id.CompareNoCase("GUI_NEWPROPS") == 0)
|
||||
// {
|
||||
// // read new gui properties
|
||||
// }
|
||||
if (ext_id.CompareNoCase("v360_fonts") == 0) {
|
||||
for (FontInfo &finfo : _ents.Game.fonts) {
|
||||
// adjustable font outlines
|
||||
finfo.AutoOutlineThickness = _in->ReadInt32();
|
||||
finfo.AutoOutlineStyle = static_cast<enum FontInfo::AutoOutlineStyle>(_in->ReadInt32());
|
||||
// reserved
|
||||
_in->ReadInt32();
|
||||
_in->ReadInt32();
|
||||
_in->ReadInt32();
|
||||
_in->ReadInt32();
|
||||
}
|
||||
} else if (ext_id.CompareNoCase("v360_cursors") == 0) {
|
||||
for (MouseCursor &mcur : _ents.Game.mcurs) {
|
||||
mcur.animdelay = _in->ReadInt32();
|
||||
// reserved
|
||||
_in->ReadInt32();
|
||||
_in->ReadInt32();
|
||||
_in->ReadInt32();
|
||||
}
|
||||
} else if (ext_id.CompareNoCase("v361_objnames") == 0) {
|
||||
// Extended object names and script names:
|
||||
// for object types that had hard name length limits
|
||||
_ents.Game.gamename = StrUtil::ReadString(_in);
|
||||
_ents.Game.saveGameFolderName = StrUtil::ReadString(_in);
|
||||
size_t num_chars = _in->ReadInt32();
|
||||
if (num_chars != _ents.Game.chars.size())
|
||||
return new Error(String::FromFormat("Mismatching number of characters: read %zu expected %zu", num_chars, _ents.Game.chars.size()));
|
||||
for (int i = 0; i < _ents.Game.numcharacters; ++i) {
|
||||
auto &chinfo = _ents.Game.chars[i];
|
||||
auto &chinfo2 = _ents.Game.chars2[i];
|
||||
chinfo2.scrname_new = StrUtil::ReadString(_in);
|
||||
chinfo2.name_new = StrUtil::ReadString(_in);
|
||||
// assign to the legacy fields for compatibility with old plugins
|
||||
snprintf(chinfo.scrname, LEGACY_MAX_SCRIPT_NAME_LEN, "%s", chinfo2.scrname_new.GetCStr());
|
||||
snprintf(chinfo.name, LEGACY_MAX_CHAR_NAME_LEN, "%s", chinfo2.name_new.GetCStr());
|
||||
}
|
||||
size_t num_invitems = _in->ReadInt32();
|
||||
if (num_invitems != (size_t)_ents.Game.numinvitems)
|
||||
return new Error(String::FromFormat("Mismatching number of inventory items: read %zu expected %zu", num_invitems, (size_t)_ents.Game.numinvitems));
|
||||
for (int i = 0; i < _ents.Game.numinvitems; ++i) {
|
||||
_ents.Game.invinfo[i].name = StrUtil::ReadString(_in);
|
||||
}
|
||||
size_t num_cursors = _in->ReadInt32();
|
||||
if (num_cursors != _ents.Game.mcurs.size())
|
||||
return new Error(String::FromFormat("Mismatching number of cursors: read %zu expected %zu", num_cursors, _ents.Game.mcurs.size()));
|
||||
for (MouseCursor &mcur : _ents.Game.mcurs) {
|
||||
mcur.name = StrUtil::ReadString(_in);
|
||||
}
|
||||
size_t num_clips = _in->ReadInt32();
|
||||
if (num_clips != _ents.Game.audioClips.size())
|
||||
return new Error(String::FromFormat("Mismatching number of audio clips: read %zu expected %zu", num_clips, _ents.Game.audioClips.size()));
|
||||
for (ScriptAudioClip &clip : _ents.Game.audioClips) {
|
||||
clip.scriptName = StrUtil::ReadString(_in);
|
||||
clip.fileName = StrUtil::ReadString(_in);
|
||||
}
|
||||
} else {
|
||||
return new MainGameFileError(kMGFErr_ExtUnknown, String::FromFormat("Type: %s", ext_id.GetCStr()));
|
||||
}
|
||||
return HError::None();
|
||||
}
|
||||
|
||||
// Search and read only data belonging to the general game info
|
||||
class GameDataExtPreloader : public GameDataExtReader {
|
||||
public:
|
||||
GameDataExtPreloader(LoadedGameEntities &ents, GameDataVersion data_ver, Stream *in)
|
||||
: GameDataExtReader(ents, data_ver, in) {}
|
||||
|
||||
protected:
|
||||
HError ReadBlock(int block_id, const String &ext_id, soff_t block_len, bool &read_next) override;
|
||||
};
|
||||
|
||||
HError GameDataExtPreloader::ReadBlock(int /*block_id*/, const String &ext_id,
|
||||
soff_t /*block_len*/, bool &read_next) {
|
||||
// Try reading only data which belongs to the general game info
|
||||
read_next = true;
|
||||
if (ext_id.CompareNoCase("v361_objnames") == 0) {
|
||||
_ents.Game.gamename = StrUtil::ReadString(_in);
|
||||
_ents.Game.saveGameFolderName = StrUtil::ReadString(_in);
|
||||
read_next = false; // we're done
|
||||
}
|
||||
SkipBlock(); // prevent assertion trigger
|
||||
return HError::None();
|
||||
}
|
||||
|
||||
HGameFileError ReadGameData(LoadedGameEntities &ents, Stream *in, GameDataVersion data_ver) {
|
||||
GameSetupStruct &game = ents.Game;
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// The standard data section.
|
||||
//-------------------------------------------------------------------------
|
||||
GameSetupStruct::SerializeInfo sinfo;
|
||||
game.GameSetupStructBase::ReadFromFile(in, data_ver, sinfo);
|
||||
game.read_savegame_info(in, data_ver); // here we also read GUID in v3.* games
|
||||
|
||||
Debug::Printf(kDbgMsg_Info, "Game title: '%s'", game.gamename.GetCStr());
|
||||
Debug::Printf(kDbgMsg_Info, "Game uid (old format): `%d`", game.uniqueid);
|
||||
Debug::Printf(kDbgMsg_Info, "Game guid: '%s'", game.guid);
|
||||
|
||||
if (game.GetGameRes().IsNull())
|
||||
return new MainGameFileError(kMGFErr_InvalidNativeResolution);
|
||||
|
||||
game.read_font_infos(in, data_ver);
|
||||
HGameFileError err = ReadSpriteFlags(ents, in, data_ver);
|
||||
if (!err)
|
||||
return err;
|
||||
game.ReadInvInfo(in);
|
||||
err = game.read_cursors(in);
|
||||
if (!err)
|
||||
return err;
|
||||
game.read_interaction_scripts(in, data_ver);
|
||||
if (sinfo.HasWordsDict)
|
||||
game.read_words_dictionary(in);
|
||||
|
||||
if (sinfo.HasCCScript) {
|
||||
ents.GlobalScript.reset(ccScript::CreateFromStream(in));
|
||||
if (!ents.GlobalScript)
|
||||
return new MainGameFileError(kMGFErr_CreateGlobalScriptFailed, cc_get_error().ErrorString);
|
||||
err = ReadDialogScript(ents.DialogScript, in, data_ver);
|
||||
if (!err)
|
||||
return err;
|
||||
err = ReadScriptModules(ents.ScriptModules, in, data_ver);
|
||||
if (!err)
|
||||
return err;
|
||||
}
|
||||
|
||||
ReadViews(game, ents.Views, in, data_ver);
|
||||
|
||||
if (data_ver <= kGameVersion_251) {
|
||||
// skip unknown data
|
||||
int count = in->ReadInt32();
|
||||
in->Seek(count * 0x204);
|
||||
}
|
||||
|
||||
game.read_characters(in);
|
||||
game.read_lipsync(in, data_ver);
|
||||
game.read_messages(in, sinfo.HasMessages, data_ver);
|
||||
|
||||
ReadDialogs(ents.Dialogs, ents.OldDialogScripts, ents.OldDialogSources, ents.OldSpeechLines,
|
||||
in, data_ver, game.numdialog);
|
||||
HError err2 = GUI::ReadGUI(in);
|
||||
if (!err2)
|
||||
return new MainGameFileError(kMGFErr_GameEntityFailed, err2);
|
||||
game.numgui = _GP(guis).size();
|
||||
|
||||
if (data_ver >= kGameVersion_260) {
|
||||
err = ReadPlugins(ents.PluginInfos, in);
|
||||
if (!err)
|
||||
return err;
|
||||
}
|
||||
|
||||
err = game.read_customprops(in, data_ver);
|
||||
if (!err)
|
||||
return err;
|
||||
err = game.read_audio(in, data_ver);
|
||||
if (!err)
|
||||
return err;
|
||||
game.read_room_names(in, data_ver);
|
||||
|
||||
if (data_ver <= kGameVersion_350)
|
||||
return HGameFileError::None();
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// All the extended data, for AGS > 3.5.0.
|
||||
//-------------------------------------------------------------------------
|
||||
GameDataExtReader reader(ents, data_ver, in);
|
||||
HError ext_err = reader.Read();
|
||||
return ext_err ? HGameFileError::None() : new MainGameFileError(kMGFErr_ExtListFailed, ext_err);
|
||||
}
|
||||
|
||||
HGameFileError UpdateGameData(LoadedGameEntities &ents, GameDataVersion data_ver) {
|
||||
GameSetupStruct &game = ents.Game;
|
||||
ApplySpriteData(game, ents, data_ver);
|
||||
UpgradeFonts(game, data_ver);
|
||||
UpgradeAudio(game, ents, data_ver);
|
||||
UpgradeCharacters(game, data_ver);
|
||||
UpgradeGUI(game, data_ver);
|
||||
UpgradeMouseCursors(game, data_ver);
|
||||
SetDefaultGlobalMessages(game);
|
||||
// Global talking animation speed
|
||||
if (data_ver < kGameVersion_312) {
|
||||
// Fix animation speed for old formats
|
||||
game.options[OPT_GLOBALTALKANIMSPD] = 5;
|
||||
} else if (data_ver < kGameVersion_330) {
|
||||
// Convert game option for 3.1.2 - 3.2 games
|
||||
game.options[OPT_GLOBALTALKANIMSPD] = game.options[OPT_GLOBALTALKANIMSPD] != 0 ? 5 : (-5 - 1);
|
||||
}
|
||||
// Old dialog options API for pre-3.4.0.2 games
|
||||
if (data_ver < kGameVersion_340_2) {
|
||||
game.options[OPT_DIALOGOPTIONSAPI] = -1;
|
||||
}
|
||||
// Relative asset resolution in pre-3.5.0.8 (always enabled)
|
||||
if (data_ver < kGameVersion_350) {
|
||||
game.options[OPT_RELATIVEASSETRES] = 1;
|
||||
}
|
||||
FixupSaveDirectory(game);
|
||||
return HGameFileError::None();
|
||||
}
|
||||
|
||||
void PreReadGameData(GameSetupStruct &game, Stream *in, GameDataVersion data_ver) {
|
||||
GameSetupStruct::SerializeInfo sinfo;
|
||||
_GP(game).GameSetupStructBase::ReadFromFile(in, data_ver, sinfo);
|
||||
_GP(game).read_savegame_info(in, data_ver); // here we also read GUID in v3.* games
|
||||
|
||||
// Check for particular expansions that might have data necessary
|
||||
// for "preload" purposes
|
||||
if (sinfo.ExtensionOffset == 0u)
|
||||
return; // either no extensions, or data version is too early
|
||||
|
||||
in->Seek(sinfo.ExtensionOffset, kSeekBegin);
|
||||
LoadedGameEntities ents(game);
|
||||
GameDataExtPreloader reader(ents, data_ver, in);
|
||||
reader.Read();
|
||||
}
|
||||
|
||||
} // namespace Shared
|
||||
} // namespace AGS
|
||||
} // namespace AGS3
|
||||
165
engines/ags/shared/game/main_game_file.h
Normal file
165
engines/ags/shared/game/main_game_file.h
Normal 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// This unit provides functions for reading main game file into appropriate
|
||||
// data structures. Main game file contains general game data, such as global
|
||||
// options, lists of static game entities and compiled scripts modules.
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef AGS_SHARED_GAME_MAIN_GAME_FILE_H
|
||||
#define AGS_SHARED_GAME_MAIN_GAME_FILE_H
|
||||
|
||||
#include "common/std/functional.h"
|
||||
#include "common/std/memory.h"
|
||||
#include "common/std/set.h"
|
||||
#include "common/std/vector.h"
|
||||
#include "ags/shared/core/platform.h"
|
||||
#include "ags/shared/ac/game_version.h"
|
||||
#include "ags/shared/ac/view.h"
|
||||
#include "ags/shared/game/plugin_info.h"
|
||||
#include "ags/shared/script/cc_script.h"
|
||||
#include "ags/shared/util/error.h"
|
||||
#include "ags/shared/util/stream.h"
|
||||
#include "ags/shared/util/string.h"
|
||||
#include "ags/shared/util/version.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
struct GameSetupStruct;
|
||||
struct DialogTopic;
|
||||
|
||||
namespace AGS {
|
||||
namespace Shared {
|
||||
|
||||
// Error codes for main game file reading
|
||||
enum MainGameFileErrorType {
|
||||
kMGFErr_NoError,
|
||||
kMGFErr_FileOpenFailed,
|
||||
kMGFErr_SignatureFailed,
|
||||
// separate error given for "too old" format to provide clarifying message
|
||||
kMGFErr_FormatVersionTooOld,
|
||||
kMGFErr_FormatVersionNotSupported,
|
||||
kMGFErr_CapsNotSupported,
|
||||
kMGFErr_InvalidNativeResolution,
|
||||
kMGFErr_TooManySprites,
|
||||
kMGFErr_InvalidPropertySchema,
|
||||
kMGFErr_InvalidPropertyValues,
|
||||
kMGFErr_CreateGlobalScriptFailed,
|
||||
kMGFErr_CreateDialogScriptFailed,
|
||||
kMGFErr_CreateScriptModuleFailed,
|
||||
kMGFErr_GameEntityFailed,
|
||||
kMGFErr_PluginDataFmtNotSupported,
|
||||
kMGFErr_PluginDataSizeTooLarge,
|
||||
kMGFErr_ExtListFailed,
|
||||
kMGFErr_ExtUnknown
|
||||
};
|
||||
|
||||
String GetMainGameFileErrorText(MainGameFileErrorType err);
|
||||
|
||||
typedef TypedCodeError<MainGameFileErrorType, GetMainGameFileErrorText> MainGameFileError;
|
||||
typedef ErrorHandle<MainGameFileError> HGameFileError;
|
||||
typedef std::unique_ptr<Stream> UStream;
|
||||
|
||||
// MainGameSource defines a successfully opened main game file
|
||||
struct MainGameSource {
|
||||
// Standart main game file names for 3.* and 2.* games respectively
|
||||
static const char *DefaultFilename_v3;
|
||||
static const char *DefaultFilename_v2;
|
||||
// Signature of the current game format
|
||||
static const char *Signature;
|
||||
|
||||
// Name of the asset file
|
||||
String Filename;
|
||||
// Game file format version
|
||||
GameDataVersion DataVersion;
|
||||
// Tool identifier (like version) this game was compiled with
|
||||
String CompiledWith;
|
||||
// Extended engine capabilities required by the game; their primary use
|
||||
// currently is to let "alternate" game formats indicate themselves
|
||||
std::set<String> Caps;
|
||||
// A ponter to the opened stream
|
||||
UStream InputStream;
|
||||
|
||||
MainGameSource();
|
||||
};
|
||||
|
||||
// LoadedGameEntities is meant for keeping objects loaded from the game file.
|
||||
// Because copying/assignment methods are not properly implemented for some
|
||||
// of these objects yet, they have to be attached using references to be read
|
||||
// directly. This is temporary solution that has to be resolved by the future
|
||||
// code refactoring.
|
||||
struct LoadedGameEntities {
|
||||
GameSetupStruct &Game;
|
||||
std::vector<DialogTopic> Dialogs;
|
||||
std::vector<ViewStruct> Views;
|
||||
PScript GlobalScript;
|
||||
PScript DialogScript;
|
||||
std::vector<PScript> ScriptModules;
|
||||
std::vector<PluginInfo> PluginInfos;
|
||||
|
||||
// Original sprite data (when it was read into const-sized arrays)
|
||||
size_t SpriteCount;
|
||||
std::vector<uint8_t> SpriteFlags; // SPF_* flags
|
||||
|
||||
// Old dialog support
|
||||
// legacy compiled dialog script of its own format,
|
||||
// requires separate interpreting
|
||||
std::vector<std::vector<uint8_t>> OldDialogScripts;
|
||||
// probably, actual dialog script sources kept within some older games
|
||||
std::vector<String> OldDialogSources;
|
||||
// speech texts displayed during dialog
|
||||
std::vector<String> OldSpeechLines;
|
||||
|
||||
LoadedGameEntities(GameSetupStruct &game);
|
||||
~LoadedGameEntities();
|
||||
};
|
||||
|
||||
class AssetManager;
|
||||
|
||||
// Tells if the given path (library filename) contains main game file
|
||||
bool IsMainGameLibrary(const String &filename);
|
||||
// Scans given directory path for a package containing main game data, returns first found or none.
|
||||
String FindGameData(const String &path);
|
||||
String FindGameData(const String &path, bool(*fn_testfile)(const String &));
|
||||
// Opens main game file for reading from an arbitrary file
|
||||
HGameFileError OpenMainGameFile(const String &filename, MainGameSource &src);
|
||||
// Opens main game file for reading using the current Asset Manager (uses default asset name)
|
||||
HGameFileError OpenMainGameFileFromDefaultAsset(MainGameSource &src, AssetManager *mgr);
|
||||
// Reads game data, applies necessary conversions to match current format version
|
||||
HGameFileError ReadGameData(LoadedGameEntities &ents, Stream *in, GameDataVersion data_ver);
|
||||
// Pre-reads the heading game data, just enough to identify the game and its special file locations
|
||||
void PreReadGameData(GameSetupStruct &game, Stream *in, GameDataVersion data_ver);
|
||||
// Applies necessary updates, conversions and fixups to the loaded data
|
||||
// making it compatible with current engine
|
||||
HGameFileError UpdateGameData(LoadedGameEntities &ents, GameDataVersion data_ver);
|
||||
// Ensures that the game saves directory path is valid
|
||||
void FixupSaveDirectory(GameSetupStruct &game);
|
||||
// Maps legacy sound numbers to real audio clips
|
||||
void RemapLegacySoundNums(GameSetupStruct &game, std::vector<ViewStruct> &views, GameDataVersion data_ver);
|
||||
|
||||
} // namespace Shared
|
||||
} // namespace AGS
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
54
engines/ags/shared/game/plugin_info.h
Normal file
54
engines/ags/shared/game/plugin_info.h
Normal file
@@ -0,0 +1,54 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// PluginInfo - a struct defining general information on game plugin.
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef AGS_SHARED_GAME_PLUGIN_INFO_H
|
||||
#define AGS_SHARED_GAME_PLUGIN_INFO_H
|
||||
|
||||
#include "common/std/memory.h"
|
||||
#include "ags/shared/util/string.h"
|
||||
|
||||
// TODO: why 10 MB limit?
|
||||
#define PLUGIN_SAVEBUFFERSIZE 10247680
|
||||
|
||||
namespace AGS3 {
|
||||
namespace AGS {
|
||||
namespace Shared {
|
||||
|
||||
struct PluginInfo {
|
||||
// (File)name of plugin
|
||||
String Name;
|
||||
// Custom data for plugin
|
||||
std::vector<uint8_t> Data;
|
||||
|
||||
PluginInfo() = default;
|
||||
};
|
||||
|
||||
} // namespace Shared
|
||||
} // namespace AGS
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
799
engines/ags/shared/game/room_file.cpp
Normal file
799
engines/ags/shared/game/room_file.cpp
Normal file
@@ -0,0 +1,799 @@
|
||||
/* 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 "ags/shared/ac/common_defines.h"
|
||||
#include "ags/shared/ac/game_struct_defines.h"
|
||||
#include "ags/shared/ac/words_dictionary.h" // TODO: extract string decryption
|
||||
#include "ags/shared/core/asset_manager.h"
|
||||
#include "ags/shared/debugging/out.h"
|
||||
#include "ags/shared/game/custom_properties.h"
|
||||
#include "ags/shared/game/room_file.h"
|
||||
#include "ags/shared/game/room_struct.h"
|
||||
#include "ags/shared/gfx/bitmap.h"
|
||||
#include "ags/shared/script/cc_common.h"
|
||||
#include "ags/shared/script/cc_script.h"
|
||||
#include "ags/shared/util/compress.h"
|
||||
#include "ags/shared/util/data_ext.h"
|
||||
#include "ags/shared/util/string_utils.h"
|
||||
#include "ags/globals.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
// default number of hotspots to read from the room file
|
||||
#define MIN_ROOM_HOTSPOTS 20
|
||||
#define LEGACY_HOTSPOT_NAME_LEN 30
|
||||
#define LEGACY_ROOM_PASSWORD_LENGTH 11
|
||||
#define LEGACY_ROOM_PASSWORD_SALT 60
|
||||
#define ROOM_MESSAGE_FLAG_DISPLAYNEXT 200
|
||||
// Reserved room options (each is a byte)
|
||||
#define ROOM_OPTIONS_RESERVED 4
|
||||
#define LEGACY_TINT_IS_ENABLED 0x80000000
|
||||
|
||||
namespace AGS {
|
||||
namespace Shared {
|
||||
|
||||
HRoomFileError OpenRoomFileFromAsset(const String &filename, RoomDataSource &src) {
|
||||
// Cleanup source struct
|
||||
src = RoomDataSource();
|
||||
// Try to find and open room file
|
||||
Stream *in = _GP(AssetMgr)->OpenAsset(filename);
|
||||
if (in == nullptr)
|
||||
return new RoomFileError(kRoomFileErr_FileOpenFailed, String::FromFormat("Filename: %s.", filename.GetCStr()));
|
||||
src.Filename = filename;
|
||||
src.InputStream.reset(in);
|
||||
return ReadRoomHeader(src);
|
||||
}
|
||||
|
||||
void ReadRoomObject(RoomObjectInfo &obj, Stream *in) {
|
||||
obj.Sprite = (uint16_t)in->ReadInt16();
|
||||
obj.X = in->ReadInt16();
|
||||
obj.Y = in->ReadInt16();
|
||||
obj.Room = in->ReadInt16();
|
||||
obj.IsOn = in->ReadInt16() != 0;
|
||||
}
|
||||
|
||||
void WriteRoomObject(const RoomObjectInfo &obj, Stream *out) {
|
||||
// TODO: expand serialization into 32-bit values at least for the sprite index!!
|
||||
out->WriteInt16((uint16_t)obj.Sprite);
|
||||
out->WriteInt16((int16_t)obj.X);
|
||||
out->WriteInt16((int16_t)obj.Y);
|
||||
out->WriteInt16((int16_t)obj.Room);
|
||||
out->WriteInt16(obj.IsOn ? 1 : 0);
|
||||
}
|
||||
|
||||
|
||||
// Main room data
|
||||
HError ReadMainBlock(RoomStruct *room, Stream *in, RoomFileVersion data_ver) {
|
||||
int bpp;
|
||||
if (data_ver >= kRoomVersion_208)
|
||||
bpp = in->ReadInt32();
|
||||
else
|
||||
bpp = 1;
|
||||
|
||||
if (bpp < 1)
|
||||
bpp = 1;
|
||||
|
||||
room->BackgroundBPP = bpp;
|
||||
room->WalkBehindCount = in->ReadInt16();
|
||||
if (room->WalkBehindCount > MAX_WALK_BEHINDS)
|
||||
return new RoomFileError(kRoomFileErr_IncompatibleEngine, String::FromFormat("Too many walk-behinds (in room: %d, max: %d).", room->WalkBehindCount, MAX_WALK_BEHINDS));
|
||||
|
||||
// Walk-behinds baselines
|
||||
for (size_t i = 0; i < room->WalkBehindCount; ++i)
|
||||
room->WalkBehinds[i].Baseline = in->ReadInt16();
|
||||
|
||||
room->HotspotCount = in->ReadInt32();
|
||||
if (room->HotspotCount == 0)
|
||||
room->HotspotCount = MIN_ROOM_HOTSPOTS;
|
||||
if (room->HotspotCount > MAX_ROOM_HOTSPOTS)
|
||||
return new RoomFileError(kRoomFileErr_IncompatibleEngine, String::FromFormat("Too many hotspots (in room: %d, max: %d).", room->HotspotCount, MAX_ROOM_HOTSPOTS));
|
||||
|
||||
// Hotspots walk-to points
|
||||
for (size_t i = 0; i < room->HotspotCount; ++i) {
|
||||
room->Hotspots[i].WalkTo.X = in->ReadInt16();
|
||||
room->Hotspots[i].WalkTo.Y = in->ReadInt16();
|
||||
}
|
||||
|
||||
// Hotspots names and script names
|
||||
for (size_t i = 0; i < room->HotspotCount; ++i) {
|
||||
if (data_ver >= kRoomVersion_3415)
|
||||
room->Hotspots[i].Name = StrUtil::ReadString(in);
|
||||
else if (data_ver >= kRoomVersion_303a)
|
||||
room->Hotspots[i].Name = String::FromStream(in);
|
||||
else
|
||||
room->Hotspots[i].Name = String::FromStreamCount(in, LEGACY_HOTSPOT_NAME_LEN);
|
||||
}
|
||||
|
||||
if (data_ver >= kRoomVersion_270) {
|
||||
for (size_t i = 0; i < room->HotspotCount; ++i) {
|
||||
if (data_ver >= kRoomVersion_3415)
|
||||
room->Hotspots[i].ScriptName = StrUtil::ReadString(in);
|
||||
else
|
||||
room->Hotspots[i].ScriptName = String::FromStreamCount(in, LEGACY_MAX_SCRIPT_NAME_LEN);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: remove from format later
|
||||
size_t polypoint_areas = in->ReadInt32();
|
||||
if (polypoint_areas > 0)
|
||||
return new RoomFileError(kRoomFileErr_IncompatibleEngine, "Legacy poly-point areas are no longer supported.");
|
||||
/* NOTE: implementation hidden in room_file_deprecated.cpp
|
||||
for (size_t i = 0; i < polypoint_areas; ++i)
|
||||
wallpoints[i].Read(in);
|
||||
*/
|
||||
|
||||
room->Edges.Top = in->ReadInt16();
|
||||
room->Edges.Bottom = in->ReadInt16();
|
||||
room->Edges.Left = in->ReadInt16();
|
||||
room->Edges.Right = in->ReadInt16();
|
||||
|
||||
// Room objects
|
||||
uint16_t obj_count = in->ReadInt16();
|
||||
if (obj_count > MAX_ROOM_OBJECTS)
|
||||
return new RoomFileError(kRoomFileErr_IncompatibleEngine, String::FromFormat("Too many objects (in room: %d, max: %d).", obj_count, MAX_ROOM_OBJECTS));
|
||||
|
||||
room->Objects.resize(obj_count);
|
||||
for (auto &obj : room->Objects)
|
||||
ReadRoomObject(obj, in);
|
||||
|
||||
// Legacy interactions
|
||||
if (data_ver >= kRoomVersion_253) {
|
||||
size_t localvar_count = in->ReadInt32();
|
||||
if (localvar_count > 0) {
|
||||
room->LocalVariables.resize(localvar_count);
|
||||
for (size_t i = 0; i < localvar_count; ++i)
|
||||
room->LocalVariables[i].Read(in);
|
||||
}
|
||||
}
|
||||
|
||||
if (data_ver >= kRoomVersion_241 && data_ver < kRoomVersion_300a) {
|
||||
for (size_t i = 0; i < room->HotspotCount; ++i)
|
||||
room->Hotspots[i].Interaction.reset(Interaction::CreateFromStream(in));
|
||||
for (auto &obj : room->Objects)
|
||||
obj.Interaction.reset(Interaction::CreateFromStream(in));
|
||||
room->Interaction.reset(Interaction::CreateFromStream(in));
|
||||
}
|
||||
|
||||
if (data_ver >= kRoomVersion_255b) {
|
||||
room->RegionCount = in->ReadInt32();
|
||||
if (room->RegionCount > MAX_ROOM_REGIONS)
|
||||
return new RoomFileError(kRoomFileErr_IncompatibleEngine, String::FromFormat("Too many regions (in room: %d, max: %d).", room->RegionCount, MAX_ROOM_REGIONS));
|
||||
|
||||
if (data_ver < kRoomVersion_300a) {
|
||||
for (size_t i = 0; i < room->RegionCount; ++i)
|
||||
room->Regions[i].Interaction.reset(Interaction::CreateFromStream(in));
|
||||
}
|
||||
}
|
||||
|
||||
// Event script links
|
||||
if (data_ver >= kRoomVersion_300a) {
|
||||
room->EventHandlers.reset(InteractionScripts::CreateFromStream(in));
|
||||
for (size_t i = 0; i < room->HotspotCount; ++i)
|
||||
room->Hotspots[i].EventHandlers.reset(InteractionScripts::CreateFromStream(in));
|
||||
for (auto &obj : room->Objects)
|
||||
obj.EventHandlers.reset(InteractionScripts::CreateFromStream(in));
|
||||
for (size_t i = 0; i < room->RegionCount; ++i)
|
||||
room->Regions[i].EventHandlers.reset(InteractionScripts::CreateFromStream(in));
|
||||
}
|
||||
|
||||
if (data_ver >= kRoomVersion_200_alpha) {
|
||||
for (auto &obj : room->Objects)
|
||||
obj.Baseline = in->ReadInt32();
|
||||
room->Width = in->ReadInt16();
|
||||
room->Height = in->ReadInt16();
|
||||
}
|
||||
|
||||
if (data_ver >= kRoomVersion_262)
|
||||
for (auto &obj : room->Objects)
|
||||
obj.Flags = in->ReadInt16();
|
||||
|
||||
if (data_ver >= kRoomVersion_200_final)
|
||||
room->MaskResolution = in->ReadInt16();
|
||||
|
||||
room->WalkAreaCount = MAX_WALK_AREAS;
|
||||
if (data_ver >= kRoomVersion_240)
|
||||
room->WalkAreaCount = in->ReadInt32();
|
||||
if (room->WalkAreaCount > MAX_WALK_AREAS)
|
||||
return new RoomFileError(kRoomFileErr_IncompatibleEngine, String::FromFormat("Too many walkable areas (in room: %d, max: %d).", room->WalkAreaCount, MAX_WALK_AREAS));
|
||||
|
||||
if (data_ver >= kRoomVersion_200_alpha7)
|
||||
for (size_t i = 0; i < room->WalkAreaCount; ++i)
|
||||
room->WalkAreas[i].ScalingFar = in->ReadInt16();
|
||||
if (data_ver >= kRoomVersion_214)
|
||||
for (size_t i = 0; i < room->WalkAreaCount; ++i)
|
||||
room->WalkAreas[i].PlayerView = in->ReadInt16();
|
||||
if (data_ver >= kRoomVersion_251) {
|
||||
for (size_t i = 0; i < room->WalkAreaCount; ++i)
|
||||
room->WalkAreas[i].ScalingNear = in->ReadInt16();
|
||||
for (size_t i = 0; i < room->WalkAreaCount; ++i)
|
||||
room->WalkAreas[i].Top = in->ReadInt16();
|
||||
for (size_t i = 0; i < room->WalkAreaCount; ++i)
|
||||
room->WalkAreas[i].Bottom = in->ReadInt16();
|
||||
}
|
||||
|
||||
in->Seek(LEGACY_ROOM_PASSWORD_LENGTH); // skip password
|
||||
room->Options.StartupMusic = in->ReadInt8();
|
||||
room->Options.SaveLoadDisabled = in->ReadInt8() != 0;
|
||||
room->Options.PlayerCharOff = in->ReadInt8() != 0;
|
||||
room->Options.PlayerView = in->ReadInt8();
|
||||
room->Options.MusicVolume = (RoomVolumeMod)in->ReadInt8();
|
||||
room->Options.Flags = in->ReadInt8();
|
||||
in->Seek(ROOM_OPTIONS_RESERVED);
|
||||
|
||||
room->MessageCount = in->ReadInt16();
|
||||
if (room->MessageCount > MAX_MESSAGES)
|
||||
return new RoomFileError(kRoomFileErr_IncompatibleEngine, String::FromFormat("Too many room messages (in room: %d, max: %d).", room->MessageCount, MAX_MESSAGES));
|
||||
|
||||
if (data_ver >= kRoomVersion_272)
|
||||
room->GameID = in->ReadInt32();
|
||||
|
||||
if (data_ver >= kRoomVersion_pre114_3) {
|
||||
for (size_t i = 0; i < room->MessageCount; ++i) {
|
||||
room->MessageInfos[i].DisplayAs = in->ReadInt8();
|
||||
room->MessageInfos[i].Flags = in->ReadInt8();
|
||||
}
|
||||
}
|
||||
|
||||
char buffer[3000];
|
||||
for (size_t i = 0; i < room->MessageCount; ++i) {
|
||||
if (data_ver >= kRoomVersion_261)
|
||||
read_string_decrypt(in, buffer, sizeof(buffer));
|
||||
else
|
||||
StrUtil::ReadCStr(buffer, in, sizeof(buffer));
|
||||
room->Messages[i] = buffer;
|
||||
}
|
||||
|
||||
// Very old format legacy room animations (FullAnimation)
|
||||
if (data_ver >= kRoomVersion_pre114_6) {
|
||||
// TODO: remove from format later
|
||||
size_t fullanim_count = in->ReadInt16();
|
||||
if (fullanim_count > 0)
|
||||
return new RoomFileError(kRoomFileErr_IncompatibleEngine, "Room animations are no longer supported.");
|
||||
/* NOTE: implementation hidden in room_file_deprecated.cpp
|
||||
in->ReadArray(&fullanims[0], sizeof(FullAnimation), fullanim_count);
|
||||
*/
|
||||
}
|
||||
|
||||
// Ancient "graphical scripts". We currently don't support them because
|
||||
// there's no knowledge on how to convert them to modern engine.
|
||||
if ((data_ver >= kRoomVersion_pre114_4) && (data_ver < kRoomVersion_250a)) {
|
||||
return new RoomFileError(kRoomFileErr_IncompatibleEngine, "Pre-2.5 graphical scripts are no longer supported.");
|
||||
/* NOTE: implementation hidden in room_file_deprecated.cpp
|
||||
ReadPre250Scripts(in);
|
||||
*/
|
||||
}
|
||||
|
||||
if (data_ver >= kRoomVersion_114) {
|
||||
// NOTE: this WA value was written for the second time here, for some weird reason
|
||||
for (size_t i = 0; i < (size_t)MAX_WALK_AREAS; ++i)
|
||||
room->WalkAreas[i].PlayerView = in->ReadInt16();
|
||||
}
|
||||
if (data_ver >= kRoomVersion_255b) {
|
||||
for (size_t i = 0; i < room->RegionCount; ++i)
|
||||
room->Regions[i].Light = in->ReadInt16();
|
||||
for (size_t i = 0; i < room->RegionCount; ++i)
|
||||
room->Regions[i].Tint = in->ReadInt32();
|
||||
}
|
||||
|
||||
// Primary background (LZW or RLE compressed depending on format)
|
||||
if (data_ver >= kRoomVersion_pre114_5)
|
||||
room->BgFrames[0].Graphic.reset(
|
||||
load_lzw(in, room->BackgroundBPP, &room->Palette));
|
||||
else
|
||||
room->BgFrames[0].Graphic.reset(load_rle_bitmap8(in));
|
||||
|
||||
// Area masks
|
||||
if (data_ver >= kRoomVersion_255b)
|
||||
room->RegionMask.reset(load_rle_bitmap8(in));
|
||||
else if (data_ver >= kRoomVersion_114)
|
||||
skip_rle_bitmap8(in); // an old version - clear the 'shadow' area into a blank regions bmp (???)
|
||||
room->WalkAreaMask.reset(load_rle_bitmap8(in));
|
||||
room->WalkBehindMask.reset(load_rle_bitmap8(in));
|
||||
room->HotspotMask.reset(load_rle_bitmap8(in));
|
||||
return HError::None();
|
||||
}
|
||||
|
||||
// Room script sources (original text)
|
||||
HError ReadScriptBlock(char *&buf, Stream *in, RoomFileVersion /*data_ver*/) {
|
||||
size_t len = in->ReadInt32();
|
||||
buf = new char[len + 1];
|
||||
in->Read(buf, len);
|
||||
buf[len] = 0;
|
||||
for (size_t i = 0; i < len; ++i)
|
||||
buf[i] += _G(passwencstring)[i % 11];
|
||||
return HError::None();
|
||||
}
|
||||
|
||||
// Compiled room script
|
||||
HError ReadCompSc3Block(RoomStruct *room, Stream *in, RoomFileVersion /*data_ver*/) {
|
||||
room->CompiledScript.reset(ccScript::CreateFromStream(in));
|
||||
if (room->CompiledScript == nullptr)
|
||||
return new RoomFileError(kRoomFileErr_ScriptLoadFailed, cc_get_error().ErrorString);
|
||||
return HError::None();
|
||||
}
|
||||
|
||||
// Room object names
|
||||
HError ReadObjNamesBlock(RoomStruct *room, Stream *in, RoomFileVersion data_ver) {
|
||||
size_t name_count = static_cast<uint8_t>(in->ReadInt8());
|
||||
if (name_count != room->Objects.size())
|
||||
return new RoomFileError(kRoomFileErr_InconsistentData,
|
||||
String::FromFormat("In the object names block, expected name count: %zu, got %zu", room->Objects.size(), name_count));
|
||||
|
||||
for (auto &obj : room->Objects) {
|
||||
if (data_ver >= kRoomVersion_3415)
|
||||
obj.Name = StrUtil::ReadString(in);
|
||||
else
|
||||
obj.Name.ReadCount(in, LEGACY_MAXOBJNAMELEN);
|
||||
}
|
||||
return HError::None();
|
||||
}
|
||||
|
||||
// Room object script names
|
||||
HError ReadObjScNamesBlock(RoomStruct *room, Stream *in, RoomFileVersion data_ver) {
|
||||
size_t name_count = static_cast<uint8_t>(in->ReadInt8());
|
||||
if (name_count != room->Objects.size())
|
||||
return new RoomFileError(kRoomFileErr_InconsistentData,
|
||||
String::FromFormat("In the object script names block, expected name count: %zu, got %zu", room->Objects.size(), name_count));
|
||||
|
||||
for (auto &obj : room->Objects) {
|
||||
if (data_ver >= kRoomVersion_3415)
|
||||
obj.ScriptName = StrUtil::ReadString(in);
|
||||
else
|
||||
obj.ScriptName.ReadCount(in, LEGACY_MAX_SCRIPT_NAME_LEN);
|
||||
}
|
||||
return HError::None();
|
||||
}
|
||||
|
||||
// Secondary backgrounds
|
||||
HError ReadAnimBgBlock(RoomStruct *room, Stream *in, RoomFileVersion data_ver) {
|
||||
room->BgFrameCount = in->ReadInt8();
|
||||
if (room->BgFrameCount > MAX_ROOM_BGFRAMES)
|
||||
return new RoomFileError(kRoomFileErr_IncompatibleEngine, String::FromFormat("Too many room backgrounds (in room: %d, max: %d).", room->BgFrameCount, MAX_ROOM_BGFRAMES));
|
||||
|
||||
room->BgAnimSpeed = in->ReadInt8();
|
||||
if (data_ver >= kRoomVersion_255a) {
|
||||
for (size_t i = 0; i < room->BgFrameCount; ++i)
|
||||
room->BgFrames[i].IsPaletteShared = in->ReadInt8() != 0;
|
||||
}
|
||||
|
||||
for (size_t i = 1; i < room->BgFrameCount; ++i) {
|
||||
room->BgFrames[i].Graphic.reset(
|
||||
load_lzw(in, room->BackgroundBPP, &room->BgFrames[i].Palette));
|
||||
}
|
||||
return HError::None();
|
||||
}
|
||||
|
||||
// Read custom properties
|
||||
HError ReadPropertiesBlock(RoomStruct *room, Stream *in, RoomFileVersion /*data_ver*/) {
|
||||
int prop_ver = in->ReadInt32();
|
||||
if (prop_ver != 1)
|
||||
return new RoomFileError(kRoomFileErr_PropertiesBlockFormat, String::FromFormat("Expected version %d, got %d", 1, prop_ver));
|
||||
|
||||
int errors = 0;
|
||||
errors += Properties::ReadValues(room->Properties, in);
|
||||
for (size_t i = 0; i < room->HotspotCount; ++i)
|
||||
errors += Properties::ReadValues(room->Hotspots[i].Properties, in);
|
||||
for (auto &obj : room->Objects)
|
||||
errors += Properties::ReadValues(obj.Properties, in);
|
||||
|
||||
if (errors > 0)
|
||||
return new RoomFileError(kRoomFileErr_InvalidPropertyValues);
|
||||
return HError::None();
|
||||
}
|
||||
|
||||
HError ReadRoomBlock(RoomStruct *room, Stream *in, RoomFileBlock block, const String &ext_id,
|
||||
soff_t block_len, RoomFileVersion data_ver) {
|
||||
//
|
||||
// First check classic block types, identified with a numeric id
|
||||
//
|
||||
switch (block) {
|
||||
case kRoomFblk_Main:
|
||||
return ReadMainBlock(room, in, data_ver);
|
||||
case kRoomFblk_Script:
|
||||
in->Seek(block_len); // no longer read source script text into RoomStruct
|
||||
return HError::None();
|
||||
case kRoomFblk_CompScript3:
|
||||
return ReadCompSc3Block(room, in, data_ver);
|
||||
case kRoomFblk_ObjectNames:
|
||||
return ReadObjNamesBlock(room, in, data_ver);
|
||||
case kRoomFblk_ObjectScNames:
|
||||
return ReadObjScNamesBlock(room, in, data_ver);
|
||||
case kRoomFblk_AnimBg:
|
||||
return ReadAnimBgBlock(room, in, data_ver);
|
||||
case kRoomFblk_Properties:
|
||||
return ReadPropertiesBlock(room, in, data_ver);
|
||||
case kRoomFblk_CompScript:
|
||||
case kRoomFblk_CompScript2:
|
||||
return new RoomFileError(kRoomFileErr_OldBlockNotSupported,
|
||||
String::FromFormat("Type: %d.", block));
|
||||
case kRoomFblk_None:
|
||||
break; // continue to string ids
|
||||
default:
|
||||
return new RoomFileError(kRoomFileErr_UnknownBlockType,
|
||||
String::FromFormat("Type: %d, known range: %d - %d.", block, kRoomFblk_Main, kRoomFblk_ObjectScNames));
|
||||
}
|
||||
|
||||
// Add extensions here checking ext_id, which is an up to 16-chars name
|
||||
if (ext_id.CompareNoCase("ext_sopts") == 0) {
|
||||
StrUtil::ReadStringMap(room->StrOptions, in);
|
||||
return HError::None();
|
||||
}
|
||||
|
||||
return new RoomFileError(kRoomFileErr_UnknownBlockType,
|
||||
String::FromFormat("Type: %s", ext_id.GetCStr()));
|
||||
}
|
||||
|
||||
// RoomBlockReader reads whole room data, block by block
|
||||
class RoomBlockReader : public DataExtReader {
|
||||
public:
|
||||
RoomBlockReader(RoomStruct *room, RoomFileVersion data_ver, Stream *in)
|
||||
: DataExtReader(in,
|
||||
kDataExt_NumID8 | ((data_ver < kRoomVersion_350) ? kDataExt_File32 : kDataExt_File64))
|
||||
, _room(room)
|
||||
, _dataVer(data_ver) {
|
||||
}
|
||||
|
||||
// Helper function that extracts legacy room script
|
||||
HError ReadRoomScript(String &script) {
|
||||
HError err = FindOne(kRoomFblk_Script);
|
||||
if (!err)
|
||||
return err;
|
||||
char *buf = nullptr;
|
||||
err = ReadScriptBlock(buf, _in, _dataVer);
|
||||
script = buf;
|
||||
delete[] buf;
|
||||
return err;
|
||||
}
|
||||
|
||||
private:
|
||||
String GetOldBlockName(int block_id) const override {
|
||||
return GetRoomBlockName((RoomFileBlock)block_id);
|
||||
}
|
||||
|
||||
HError ReadBlock(int block_id, const String &ext_id,
|
||||
soff_t block_len, bool &read_next) override {
|
||||
read_next = true;
|
||||
return ReadRoomBlock(_room, _in, (RoomFileBlock)block_id, ext_id, block_len, _dataVer);
|
||||
}
|
||||
|
||||
RoomStruct *_room{};
|
||||
RoomFileVersion _dataVer{};
|
||||
};
|
||||
|
||||
|
||||
HRoomFileError ReadRoomData(RoomStruct *room, Stream *in, RoomFileVersion data_ver) {
|
||||
room->DataVersion = data_ver;
|
||||
RoomBlockReader reader(room, data_ver, in);
|
||||
HError err = reader.Read();
|
||||
return err ? HRoomFileError::None() :
|
||||
new RoomFileError(kRoomFileErr_BlockListFailed, err);
|
||||
}
|
||||
|
||||
HRoomFileError UpdateRoomData(RoomStruct *room, RoomFileVersion data_ver, bool game_is_hires, const std::vector<SpriteInfo> &sprinfos) {
|
||||
if (data_ver < kRoomVersion_200_final)
|
||||
room->MaskResolution = room->BgFrames[0].Graphic->GetWidth() > 320 ? kRoomHiRes : kRoomLoRes;
|
||||
if (data_ver < kRoomVersion_3508) {
|
||||
// Save legacy resolution if it DOES NOT match game's;
|
||||
// otherwise it gets promoted to "real resolution"
|
||||
if (room->MaskResolution == 1 && game_is_hires)
|
||||
room->SetResolution(kRoomLoRes);
|
||||
else if (room->MaskResolution > 1 && !game_is_hires)
|
||||
room->SetResolution(kRoomHiRes);
|
||||
}
|
||||
|
||||
// Old version - copy walkable areas to regions
|
||||
if (data_ver < kRoomVersion_255b) {
|
||||
if (!room->RegionMask)
|
||||
room->RegionMask.reset(BitmapHelper::CreateBitmap(room->WalkAreaMask->GetWidth(), room->WalkAreaMask->GetHeight(), 8));
|
||||
room->RegionMask->Blit(room->WalkAreaMask.get(), 0, 0, 0, 0, room->RegionMask->GetWidth(), room->RegionMask->GetHeight());
|
||||
for (size_t i = 0; i < MAX_ROOM_REGIONS; ++i) {
|
||||
// sic!! walkable areas were storing Light level in this field pre-2.55
|
||||
room->Regions[i].Light = room->WalkAreas[i].PlayerView;
|
||||
room->Regions[i].Tint = 255;
|
||||
}
|
||||
}
|
||||
|
||||
// Fill in dummy interaction objects into unused slots
|
||||
// TODO: remove this later, need to rework the legacy interaction usage around the engine code to prevent crashes
|
||||
if (data_ver < kRoomVersion_300a) {
|
||||
if (!room->Interaction)
|
||||
room->Interaction.reset(new Interaction());
|
||||
for (size_t i = 0; i < (size_t)MAX_ROOM_HOTSPOTS; ++i)
|
||||
if (!room->Hotspots[i].Interaction)
|
||||
room->Hotspots[i].Interaction.reset(new Interaction());
|
||||
for (auto &obj : room->Objects)
|
||||
if (!obj.Interaction)
|
||||
obj.Interaction.reset(new Interaction());
|
||||
for (size_t i = 0; i < (size_t)MAX_ROOM_REGIONS; ++i)
|
||||
if (!room->Regions[i].Interaction)
|
||||
room->Regions[i].Interaction.reset(new Interaction());
|
||||
}
|
||||
|
||||
// Upgade room object script names
|
||||
if (data_ver < kRoomVersion_300a) {
|
||||
for (auto &obj : room->Objects) {
|
||||
if (obj.ScriptName.GetLength() > 0) {
|
||||
String jibbledScriptName;
|
||||
jibbledScriptName.Format("o%s", obj.ScriptName.GetCStr());
|
||||
jibbledScriptName.MakeLower();
|
||||
if (jibbledScriptName.GetLength() >= 2)
|
||||
jibbledScriptName.SetAt(1, toupper(jibbledScriptName[1u]));
|
||||
obj.ScriptName = jibbledScriptName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pre-3.0.3, multiply up co-ordinates for high-res games to bring them
|
||||
// to the proper game coordinate system.
|
||||
// If you change this, also change convert_room_coordinates_to_data_res
|
||||
// function in the engine
|
||||
if (data_ver < kRoomVersion_303b && game_is_hires) {
|
||||
const int mul = HIRES_COORD_MULTIPLIER;
|
||||
for (auto &obj : room->Objects) {
|
||||
obj.X *= mul;
|
||||
obj.Y *= mul;
|
||||
if (obj.Baseline > 0) {
|
||||
obj.Baseline *= mul;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < room->HotspotCount; ++i) {
|
||||
room->Hotspots[i].WalkTo.X *= mul;
|
||||
room->Hotspots[i].WalkTo.Y *= mul;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < room->WalkBehindCount; ++i) {
|
||||
room->WalkBehinds[i].Baseline *= mul;
|
||||
}
|
||||
|
||||
room->Edges.Left *= mul;
|
||||
room->Edges.Top *= mul;
|
||||
room->Edges.Bottom *= mul;
|
||||
room->Edges.Right *= mul;
|
||||
room->Width *= mul;
|
||||
room->Height *= mul;
|
||||
}
|
||||
|
||||
// Adjust object Y coordinate by adding sprite's height
|
||||
// NOTE: this is impossible to do without game sprite information loaded beforehand
|
||||
// NOTE: this should be done after coordinate conversion above for simplicity
|
||||
if (data_ver < kRoomVersion_300a) {
|
||||
for (auto &obj : room->Objects)
|
||||
obj.Y += sprinfos[obj.Sprite].Height;
|
||||
}
|
||||
|
||||
if (data_ver >= kRoomVersion_251) {
|
||||
// if they set a contiuously scaled area where the top
|
||||
// and bottom zoom levels are identical, set it as a normal
|
||||
// scaled area
|
||||
for (size_t i = 0; i < room->WalkAreaCount; ++i) {
|
||||
if (room->WalkAreas[i].ScalingFar == room->WalkAreas[i].ScalingNear)
|
||||
room->WalkAreas[i].ScalingNear = NOT_VECTOR_SCALED;
|
||||
}
|
||||
}
|
||||
|
||||
// Convert the old format region tint saturation
|
||||
if (data_ver < kRoomVersion_3404) {
|
||||
for (size_t i = 0; i < room->RegionCount; ++i) {
|
||||
if ((room->Regions[i].Tint & LEGACY_TINT_IS_ENABLED) != 0) {
|
||||
room->Regions[i].Tint &= ~LEGACY_TINT_IS_ENABLED;
|
||||
// older versions of the editor had a bug - work around it
|
||||
int tint_amount = (room->Regions[i].Light > 0 ? room->Regions[i].Light : 50);
|
||||
room->Regions[i].Tint |= (tint_amount & 0xFF) << 24;
|
||||
room->Regions[i].Light = 255;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Older format room messages had flags appended to the message string
|
||||
// TODO: find out which data versions had these; is it safe to assume this was before kRoomVersion_pre114_3?
|
||||
for (size_t i = 0; i < room->MessageCount; ++i) {
|
||||
if (!room->Messages[i].IsEmpty() &&
|
||||
static_cast<uint8_t>(room->Messages[i].GetLast()) == ROOM_MESSAGE_FLAG_DISPLAYNEXT) {
|
||||
room->Messages[i].ClipRight(1);
|
||||
room->MessageInfos[i].Flags |= MSG_DISPLAYNEXT;
|
||||
}
|
||||
}
|
||||
|
||||
// sync bpalettes[0] with room.pal
|
||||
memcpy(room->BgFrames[0].Palette, room->Palette, sizeof(RGB) * 256);
|
||||
return HRoomFileError::None();
|
||||
}
|
||||
|
||||
HRoomFileError ExtractScriptText(String &script, Stream *in, RoomFileVersion data_ver) {
|
||||
RoomBlockReader reader(nullptr, data_ver, in);
|
||||
HError err = reader.ReadRoomScript(script);
|
||||
if (!err)
|
||||
new RoomFileError(kRoomFileErr_BlockListFailed, err);
|
||||
return HRoomFileError::None();
|
||||
}
|
||||
|
||||
void WriteInteractionScripts(const InteractionScripts *interactions, Stream *out) {
|
||||
out->WriteInt32(interactions->ScriptFuncNames.size());
|
||||
for (size_t i = 0; i < interactions->ScriptFuncNames.size(); ++i)
|
||||
interactions->ScriptFuncNames[i].Write(out);
|
||||
}
|
||||
|
||||
void WriteMainBlock(const RoomStruct *room, Stream *out) {
|
||||
out->WriteInt32(room->BackgroundBPP);
|
||||
out->WriteInt16((int16_t)room->WalkBehindCount);
|
||||
for (size_t i = 0; i < room->WalkBehindCount; ++i)
|
||||
out->WriteInt16(room->WalkBehinds[i].Baseline);
|
||||
|
||||
out->WriteInt32(room->HotspotCount);
|
||||
for (size_t i = 0; i < room->HotspotCount; ++i) {
|
||||
out->WriteInt16(room->Hotspots[i].WalkTo.X);
|
||||
out->WriteInt16(room->Hotspots[i].WalkTo.Y);
|
||||
}
|
||||
for (size_t i = 0; i < room->HotspotCount; ++i)
|
||||
Shared::StrUtil::WriteString(room->Hotspots[i].Name, out);
|
||||
for (size_t i = 0; i < room->HotspotCount; ++i)
|
||||
Shared::StrUtil::WriteString(room->Hotspots[i].ScriptName, out);
|
||||
|
||||
out->WriteInt32(0); // legacy poly-point areas
|
||||
|
||||
out->WriteInt16(room->Edges.Top);
|
||||
out->WriteInt16(room->Edges.Bottom);
|
||||
out->WriteInt16(room->Edges.Left);
|
||||
out->WriteInt16(room->Edges.Right);
|
||||
|
||||
out->WriteInt16((int16_t)room->Objects.size());
|
||||
for (const auto &obj : room->Objects) {
|
||||
WriteRoomObject(obj, out);
|
||||
}
|
||||
|
||||
out->WriteInt32(0); // legacy interaction vars
|
||||
out->WriteInt32(MAX_ROOM_REGIONS);
|
||||
|
||||
WriteInteractionScripts(room->EventHandlers.get(), out);
|
||||
for (size_t i = 0; i < room->HotspotCount; ++i)
|
||||
WriteInteractionScripts(room->Hotspots[i].EventHandlers.get(), out);
|
||||
for (const auto &obj : room->Objects)
|
||||
WriteInteractionScripts(obj.EventHandlers.get(), out);
|
||||
for (size_t i = 0; i < room->RegionCount; ++i)
|
||||
WriteInteractionScripts(room->Regions[i].EventHandlers.get(), out);
|
||||
|
||||
for (const auto &obj : room->Objects)
|
||||
out->WriteInt32(obj.Baseline);
|
||||
out->WriteInt16(room->Width);
|
||||
out->WriteInt16(room->Height);
|
||||
for (const auto &obj : room->Objects)
|
||||
out->WriteInt16(obj.Flags);
|
||||
out->WriteInt16(room->MaskResolution);
|
||||
|
||||
out->WriteInt32(MAX_WALK_AREAS);
|
||||
for (size_t i = 0; i < (size_t)MAX_WALK_AREAS; ++i)
|
||||
out->WriteInt16(room->WalkAreas[i].ScalingFar);
|
||||
for (size_t i = 0; i < (size_t)MAX_WALK_AREAS; ++i)
|
||||
out->WriteInt16(room->WalkAreas[i].PlayerView);
|
||||
for (size_t i = 0; i < (size_t)MAX_WALK_AREAS; ++i)
|
||||
out->WriteInt16(room->WalkAreas[i].ScalingNear);
|
||||
for (size_t i = 0; i < (size_t)MAX_WALK_AREAS; ++i)
|
||||
out->WriteInt16(room->WalkAreas[i].Top);
|
||||
for (size_t i = 0; i < (size_t)MAX_WALK_AREAS; ++i)
|
||||
out->WriteInt16(room->WalkAreas[i].Bottom);
|
||||
|
||||
out->WriteByteCount(0, LEGACY_ROOM_PASSWORD_LENGTH);
|
||||
out->WriteInt8(room->Options.StartupMusic);
|
||||
out->WriteInt8(room->Options.SaveLoadDisabled ? 1 : 0);
|
||||
out->WriteInt8(room->Options.PlayerCharOff ? 1 : 0);
|
||||
out->WriteInt8(room->Options.PlayerView);
|
||||
out->WriteInt8(room->Options.MusicVolume);
|
||||
out->WriteInt8(room->Options.Flags);
|
||||
out->WriteByteCount(0, ROOM_OPTIONS_RESERVED);
|
||||
out->WriteInt16((int16_t)room->MessageCount);
|
||||
out->WriteInt32(room->GameID);
|
||||
for (size_t i = 0; i < room->MessageCount; ++i) {
|
||||
out->WriteInt8(room->MessageInfos[i].DisplayAs);
|
||||
out->WriteInt8(room->MessageInfos[i].Flags);
|
||||
}
|
||||
for (size_t i = 0; i < room->MessageCount; ++i)
|
||||
write_string_encrypt(out, room->Messages[i].GetCStr());
|
||||
|
||||
out->WriteInt16(0); // legacy room animations
|
||||
|
||||
// NOTE: this WA value was written for the second time here, for some weird reason
|
||||
for (size_t i = 0; i < (size_t)MAX_WALK_AREAS; ++i)
|
||||
out->WriteInt16(room->WalkAreas[i].PlayerView);
|
||||
for (size_t i = 0; i < (size_t)MAX_ROOM_REGIONS; ++i)
|
||||
out->WriteInt16(room->Regions[i].Light);
|
||||
for (size_t i = 0; i < (size_t)MAX_ROOM_REGIONS; ++i)
|
||||
out->WriteInt32(room->Regions[i].Tint);
|
||||
|
||||
save_lzw(out, room->BgFrames[0].Graphic.get(), &room->Palette);
|
||||
save_rle_bitmap8(out, room->RegionMask.get());
|
||||
save_rle_bitmap8(out, room->WalkAreaMask.get());
|
||||
save_rle_bitmap8(out, room->WalkBehindMask.get());
|
||||
save_rle_bitmap8(out, room->HotspotMask.get());
|
||||
}
|
||||
|
||||
void WriteCompSc3Block(const RoomStruct *room, Stream *out) {
|
||||
room->CompiledScript->Write(out);
|
||||
}
|
||||
|
||||
void WriteObjNamesBlock(const RoomStruct *room, Stream *out) {
|
||||
out->WriteByte((uint8_t)room->Objects.size());
|
||||
for (const auto &obj : room->Objects)
|
||||
Shared::StrUtil::WriteString(obj.Name, out);
|
||||
}
|
||||
|
||||
void WriteObjScNamesBlock(const RoomStruct *room, Stream *out) {
|
||||
out->WriteByte((uint8_t)room->Objects.size());
|
||||
for (const auto &obj : room->Objects)
|
||||
Shared::StrUtil::WriteString(obj.ScriptName, out);
|
||||
}
|
||||
|
||||
void WriteAnimBgBlock(const RoomStruct *room, Stream *out) {
|
||||
out->WriteByte((int8_t)room->BgFrameCount);
|
||||
out->WriteByte(room->BgAnimSpeed);
|
||||
|
||||
for (size_t i = 0; i < room->BgFrameCount; ++i)
|
||||
out->WriteInt8(room->BgFrames[i].IsPaletteShared ? 1 : 0);
|
||||
for (size_t i = 1; i < room->BgFrameCount; ++i)
|
||||
save_lzw(out, room->BgFrames[i].Graphic.get(), &room->BgFrames[i].Palette);
|
||||
}
|
||||
|
||||
void WritePropertiesBlock(const RoomStruct *room, Stream *out) {
|
||||
out->WriteInt32(1); // Version 1 of properties block
|
||||
Properties::WriteValues(room->Properties, out);
|
||||
for (size_t i = 0; i < room->HotspotCount; ++i)
|
||||
Properties::WriteValues(room->Hotspots[i].Properties, out);
|
||||
for (const auto &obj : room->Objects)
|
||||
Properties::WriteValues(obj.Properties, out);
|
||||
}
|
||||
|
||||
void WriteStrOptions(const RoomStruct *room, Stream *out) {
|
||||
StrUtil::WriteStringMap(room->StrOptions, out);
|
||||
}
|
||||
|
||||
HRoomFileError WriteRoomData(const RoomStruct *room, Stream *out, RoomFileVersion data_ver) {
|
||||
if (data_ver < kRoomVersion_Current)
|
||||
return new RoomFileError(kRoomFileErr_FormatNotSupported, "We no longer support saving room in the older format.");
|
||||
|
||||
// Header
|
||||
out->WriteInt16(data_ver);
|
||||
// Main data
|
||||
WriteRoomBlock(room, kRoomFblk_Main, WriteMainBlock, out);
|
||||
// Compiled script
|
||||
if (room->CompiledScript)
|
||||
WriteRoomBlock(room, kRoomFblk_CompScript3, WriteCompSc3Block, out);
|
||||
// Object names
|
||||
if (room->Objects.size() > 0) {
|
||||
WriteRoomBlock(room, kRoomFblk_ObjectNames, WriteObjNamesBlock, out);
|
||||
WriteRoomBlock(room, kRoomFblk_ObjectScNames, WriteObjScNamesBlock, out);
|
||||
}
|
||||
// Secondary background frames
|
||||
if (room->BgFrameCount > 1)
|
||||
WriteRoomBlock(room, kRoomFblk_AnimBg, WriteAnimBgBlock, out);
|
||||
// Custom properties
|
||||
WriteRoomBlock(room, kRoomFblk_Properties, WritePropertiesBlock, out);
|
||||
|
||||
// String options
|
||||
WriteRoomBlock(room, "ext_sopts", WriteStrOptions, out);
|
||||
|
||||
// Write end of room file
|
||||
out->WriteByte(kRoomFile_EOF);
|
||||
return HRoomFileError::None();
|
||||
}
|
||||
|
||||
} // namespace Shared
|
||||
} // namespace AGS
|
||||
} // namespace AGS3
|
||||
141
engines/ags/shared/game/room_file.h
Normal file
141
engines/ags/shared/game/room_file.h
Normal 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// This unit provides functions for reading compiled room file (CRM)
|
||||
// into the RoomStruct structure, as well as extracting separate components,
|
||||
// such as room scripts.
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef AGS_SHARED_GAME_ROOM_FILE_H
|
||||
#define AGS_SHARED_GAME_ROOM_FILE_H
|
||||
|
||||
#include "common/std/memory.h"
|
||||
#include "common/std/vector.h"
|
||||
#include "ags/shared/core/platform.h"
|
||||
#include "ags/shared/game/room_version.h"
|
||||
#include "ags/shared/util/error.h"
|
||||
#include "ags/shared/util/stream.h"
|
||||
#include "ags/shared/util/string.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
struct SpriteInfo;
|
||||
namespace AGS {
|
||||
namespace Shared {
|
||||
|
||||
class RoomStruct;
|
||||
|
||||
enum RoomFileErrorType {
|
||||
kRoomFileErr_NoError,
|
||||
kRoomFileErr_FileOpenFailed,
|
||||
kRoomFileErr_FormatNotSupported,
|
||||
kRoomFileErr_BlockListFailed,
|
||||
kRoomFileErr_UnknownBlockType,
|
||||
kRoomFileErr_OldBlockNotSupported,
|
||||
kRoomFileErr_BlockDataOverlapping,
|
||||
kRoomFileErr_IncompatibleEngine,
|
||||
kRoomFileErr_ScriptLoadFailed,
|
||||
kRoomFileErr_InconsistentData,
|
||||
kRoomFileErr_PropertiesBlockFormat,
|
||||
kRoomFileErr_InvalidPropertyValues,
|
||||
kRoomFileErr_BlockNotFound
|
||||
};
|
||||
|
||||
enum RoomFileBlock {
|
||||
kRoomFblk_None = 0,
|
||||
// Main room data
|
||||
kRoomFblk_Main = 1,
|
||||
// Room script text source (was present in older room formats)
|
||||
kRoomFblk_Script = 2,
|
||||
// Old versions of compiled script (no longer supported)
|
||||
kRoomFblk_CompScript = 3,
|
||||
kRoomFblk_CompScript2 = 4,
|
||||
// Names of the room objects
|
||||
kRoomFblk_ObjectNames = 5,
|
||||
// Secondary room backgrounds
|
||||
kRoomFblk_AnimBg = 6,
|
||||
// Contemporary compiled script
|
||||
kRoomFblk_CompScript3 = 7,
|
||||
// Custom properties
|
||||
kRoomFblk_Properties = 8,
|
||||
// Script names of the room objects
|
||||
kRoomFblk_ObjectScNames = 9,
|
||||
// End of room data tag
|
||||
kRoomFile_EOF = 0xFF
|
||||
};
|
||||
|
||||
String GetRoomFileErrorText(RoomFileErrorType err);
|
||||
String GetRoomBlockName(RoomFileBlock id);
|
||||
|
||||
typedef TypedCodeError<RoomFileErrorType, GetRoomFileErrorText> RoomFileError;
|
||||
typedef ErrorHandle<RoomFileError> HRoomFileError;
|
||||
#ifdef AGS_PLATFORM_SCUMMVM
|
||||
typedef std::shared_ptr<Stream> UStream;
|
||||
#else
|
||||
typedef std::unique_ptr<Stream> UStream;
|
||||
#endif
|
||||
|
||||
|
||||
// RoomDataSource defines a successfully opened room file
|
||||
struct RoomDataSource {
|
||||
// Name of the asset file
|
||||
String Filename;
|
||||
// Room file format version
|
||||
RoomFileVersion DataVersion;
|
||||
// A ponter to the opened stream
|
||||
UStream InputStream;
|
||||
|
||||
RoomDataSource();
|
||||
};
|
||||
|
||||
// Opens room data for reading from an arbitrary file
|
||||
HRoomFileError OpenRoomFile(const String &filename, RoomDataSource &src);
|
||||
// Opens room data for reading from asset of a given name
|
||||
HRoomFileError OpenRoomFileFromAsset(const String &filename, RoomDataSource &src);
|
||||
// Reads room data
|
||||
HRoomFileError ReadRoomData(RoomStruct *room, Stream *in, RoomFileVersion data_ver);
|
||||
// Applies necessary updates, conversions and fixups to the loaded data
|
||||
// making it compatible with current engine
|
||||
HRoomFileError UpdateRoomData(RoomStruct *room, RoomFileVersion data_ver, bool game_is_hires, const std::vector<SpriteInfo> &sprinfos);
|
||||
// Extracts text script from the room file, if it's available.
|
||||
// Historically, text sources were kept inside packed room files before AGS 3.*.
|
||||
HRoomFileError ExtractScriptText(String &script, Stream *in, RoomFileVersion data_ver);
|
||||
// Writes all room data to the stream
|
||||
HRoomFileError WriteRoomData(const RoomStruct *room, Stream *out, RoomFileVersion data_ver);
|
||||
|
||||
// Reads room data header using stream assigned to RoomDataSource;
|
||||
// tests and saves its format index if successful
|
||||
HRoomFileError ReadRoomHeader(RoomDataSource &src);
|
||||
|
||||
typedef void(*PfnWriteRoomBlock)(const RoomStruct *room, Stream *out);
|
||||
// Writes room block with a new-style string id
|
||||
void WriteRoomBlock(const RoomStruct *room, const String &ext_id, PfnWriteRoomBlock writer, Stream *out);
|
||||
// Writes room block with a old-style numeric id
|
||||
void WriteRoomBlock(const RoomStruct *room, RoomFileBlock block, PfnWriteRoomBlock writer, Stream *out);
|
||||
|
||||
} // namespace Shared
|
||||
} // namespace AGS
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
131
engines/ags/shared/game/room_file_base.cpp
Normal file
131
engines/ags/shared/game/room_file_base.cpp
Normal file
@@ -0,0 +1,131 @@
|
||||
/* 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 "ags/shared/game/room_file.h"
|
||||
#include "ags/shared/util/data_ext.h"
|
||||
#include "ags/shared/util/file.h"
|
||||
#include "ags/shared/debugging/out.h"
|
||||
#include "ags/globals.h"
|
||||
|
||||
namespace AGS3 {
|
||||
namespace AGS {
|
||||
namespace Shared {
|
||||
|
||||
RoomDataSource::RoomDataSource()
|
||||
: DataVersion(kRoomVersion_Undefined) {
|
||||
}
|
||||
|
||||
String GetRoomFileErrorText(RoomFileErrorType err) {
|
||||
switch (err) {
|
||||
case kRoomFileErr_NoError:
|
||||
return "No error.";
|
||||
case kRoomFileErr_FileOpenFailed:
|
||||
return "Room file was not found or could not be opened.";
|
||||
case kRoomFileErr_FormatNotSupported:
|
||||
return "Format version not supported.";
|
||||
case kRoomFileErr_BlockListFailed:
|
||||
return "There was an error reading room data..";
|
||||
case kRoomFileErr_UnknownBlockType:
|
||||
return "Unknown block type.";
|
||||
case kRoomFileErr_OldBlockNotSupported:
|
||||
return "Block type is too old and not supported by this version of the engine.";
|
||||
case kRoomFileErr_BlockDataOverlapping:
|
||||
return "Block data overlapping.";
|
||||
case kRoomFileErr_IncompatibleEngine:
|
||||
return "This engine cannot handle requested room content.";
|
||||
case kRoomFileErr_ScriptLoadFailed:
|
||||
return "Script load failed.";
|
||||
case kRoomFileErr_InconsistentData:
|
||||
return "Inconsistent room data, or file is corrupted.";
|
||||
case kRoomFileErr_PropertiesBlockFormat:
|
||||
return "Unknown format of the custom properties block.";
|
||||
case kRoomFileErr_InvalidPropertyValues:
|
||||
return "Errors encountered when reading custom properties.";
|
||||
case kRoomFileErr_BlockNotFound:
|
||||
return "Required block was not found.";
|
||||
default:
|
||||
return "Unknown error.";
|
||||
}
|
||||
}
|
||||
|
||||
HRoomFileError OpenRoomFile(const String &filename, RoomDataSource &src) {
|
||||
// Cleanup source struct
|
||||
src = RoomDataSource();
|
||||
// Try to open room file
|
||||
Stream *in = File::OpenFileRead(filename);
|
||||
if (in == nullptr)
|
||||
return new RoomFileError(kRoomFileErr_FileOpenFailed, String::FromFormat("Filename: %s.", filename.GetCStr()));
|
||||
src.Filename = filename;
|
||||
src.InputStream.reset(in);
|
||||
return ReadRoomHeader(src);
|
||||
}
|
||||
|
||||
// Read room data header and check that we support this format
|
||||
HRoomFileError ReadRoomHeader(RoomDataSource &src) {
|
||||
src.DataVersion = (RoomFileVersion)src.InputStream->ReadInt16();
|
||||
if (src.DataVersion < kRoomVersion_250b || src.DataVersion > kRoomVersion_Current)
|
||||
return new RoomFileError(kRoomFileErr_FormatNotSupported, String::FromFormat("Required format version: %d, supported %d - %d", src.DataVersion, kRoomVersion_250b, kRoomVersion_Current));
|
||||
return HRoomFileError::None();
|
||||
}
|
||||
|
||||
String GetRoomBlockName(RoomFileBlock id) {
|
||||
switch (id) {
|
||||
case kRoomFblk_None: return "None";
|
||||
case kRoomFblk_Main: return "Main";
|
||||
case kRoomFblk_Script: return "TextScript";
|
||||
case kRoomFblk_CompScript: return "CompScript";
|
||||
case kRoomFblk_CompScript2: return "CompScript2";
|
||||
case kRoomFblk_ObjectNames: return "ObjNames";
|
||||
case kRoomFblk_AnimBg: return "AnimBg";
|
||||
case kRoomFblk_CompScript3: return "CompScript3";
|
||||
case kRoomFblk_Properties: return "Properties";
|
||||
case kRoomFblk_ObjectScNames: return "ObjScNames";
|
||||
case kRoomFile_EOF: return "EOF";
|
||||
default: return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static PfnWriteRoomBlock writer_writer;
|
||||
static const RoomStruct *writer_room;
|
||||
static void WriteRoomBlockWriter(Stream *out) {
|
||||
writer_writer(writer_room, out);
|
||||
}
|
||||
|
||||
// Helper for new-style blocks with string id
|
||||
void WriteRoomBlock(const RoomStruct *room, const String &ext_id, PfnWriteRoomBlock writer, Stream *out) {
|
||||
writer_writer = writer;
|
||||
writer_room = room;
|
||||
WriteExtBlock(ext_id, WriteRoomBlockWriter,
|
||||
kDataExt_NumID8 | kDataExt_File64, out);
|
||||
}
|
||||
|
||||
// Helper for old-style blocks with only numeric id
|
||||
void WriteRoomBlock(const RoomStruct *room, RoomFileBlock block, PfnWriteRoomBlock writer, Stream *out) {
|
||||
writer_writer = writer;
|
||||
writer_room = room;
|
||||
WriteExtBlock(block, WriteRoomBlockWriter,
|
||||
kDataExt_NumID8 | kDataExt_File64, out);
|
||||
}
|
||||
|
||||
} // namespace Shared
|
||||
} // namespace AGS
|
||||
} // namespace AGS3
|
||||
142
engines/ags/shared/game/room_file_deprecated.cpp
Normal file
142
engines/ags/shared/game/room_file_deprecated.cpp
Normal file
@@ -0,0 +1,142 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// Deprecated room stuff. Removed from room class and load routine because this
|
||||
// data is no longer supported in the engine. Perhaps move to some legacy
|
||||
// knowledge base; or restore when it's possible to support ancient games.
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#if defined (OBSOLETE)
|
||||
|
||||
#include "ags/shared/ac/common.h"
|
||||
#include "ags/shared/util/stream.h"
|
||||
|
||||
using namespace AGS::Shared;
|
||||
|
||||
#define AE_WAITFLAG 0x80000000
|
||||
#define MAXANIMSTAGES 10
|
||||
struct AnimationStruct {
|
||||
int x, y;
|
||||
int data;
|
||||
int object;
|
||||
int speed;
|
||||
int8 action;
|
||||
int8 wait;
|
||||
AnimationStruct() {
|
||||
action = 0;
|
||||
object = 0;
|
||||
wait = 1;
|
||||
speed = 5;
|
||||
}
|
||||
};
|
||||
|
||||
struct FullAnimation {
|
||||
AnimationStruct stage[MAXANIMSTAGES];
|
||||
int numstages;
|
||||
FullAnimation() {
|
||||
numstages = 0;
|
||||
}
|
||||
};
|
||||
|
||||
#define MAXPOINTS 30
|
||||
struct PolyPoints {
|
||||
int x[MAXPOINTS];
|
||||
int y[MAXPOINTS];
|
||||
int numpoints;
|
||||
void add_point(int x, int y);
|
||||
PolyPoints() {
|
||||
numpoints = 0;
|
||||
}
|
||||
|
||||
void Read(AGS::Shared::Stream *in);
|
||||
};
|
||||
|
||||
|
||||
#define MAXANIMS 10
|
||||
// Just a list of cut out data
|
||||
struct DeprecatedRoomStruct {
|
||||
// Full-room animations
|
||||
int16_t numanims;
|
||||
FullAnimation anims[MAXANIMS];
|
||||
// Polygonal walkable areas (unknown version)
|
||||
int32_t numwalkareas;
|
||||
PolyPoints wallpoints[MAX_WALK_AREAS];
|
||||
// Unknown flags
|
||||
int16_t flagstates[MAX_LEGACY_ROOM_FLAGS];
|
||||
};
|
||||
|
||||
|
||||
|
||||
void PolyPoints::add_point(int x, int y) {
|
||||
x[numpoints] = x;
|
||||
y[numpoints] = y;
|
||||
numpoints++;
|
||||
|
||||
if (numpoints >= MAXPOINTS)
|
||||
quit("too many poly points added");
|
||||
}
|
||||
|
||||
void PolyPoints::Read(Stream *in) {
|
||||
in->ReadArrayOfInt32(x, MAXPOINTS);
|
||||
in->ReadArrayOfInt32(y, MAXPOINTS);
|
||||
numpoints = in->ReadInt32();
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Pre-2.5 scripts (we don't know how to convert them for the modern engine)
|
||||
//
|
||||
#define SCRIPT_CONFIG_VERSION 1
|
||||
HRoomFileError ReadAncientScriptConfig(Stream *in) {
|
||||
int fmt = in->ReadInt32();
|
||||
if (fmt != SCRIPT_CONFIG_VERSION)
|
||||
return new RoomFileError(kRoomFileErr_FormatNotSupported, String::FromFormat("Invalid script configuration format (in room: %d, expected: %d).", fmt, SCRIPT_CONFIG_VERSION));
|
||||
|
||||
size_t var_count = in->ReadInt32();
|
||||
for (size_t i = 0; i < var_count; ++i) {
|
||||
size_t len = in->ReadInt8();
|
||||
in->Seek(len);
|
||||
}
|
||||
return HRoomFileError::None();
|
||||
}
|
||||
|
||||
HRoomFileError ReadAncientGraphicalScripts(Stream *in) {
|
||||
do {
|
||||
int ct = in->ReadInt32();
|
||||
if (ct == -1 || in->EOS())
|
||||
break;
|
||||
size_t len = in->ReadInt32();
|
||||
in->Seek(len);
|
||||
} while (true);
|
||||
return HRoomFileError::None();
|
||||
}
|
||||
|
||||
HRoomFileError ReadPre250Scripts(Stream *in) {
|
||||
HRoomFileError err = ReadAncientScriptConfig(in);
|
||||
if (err)
|
||||
err = ReadAncientGraphicalScripts(in);
|
||||
return err;
|
||||
}
|
||||
|
||||
#endif // OBSOLETE
|
||||
286
engines/ags/shared/game/room_struct.cpp
Normal file
286
engines/ags/shared/game/room_struct.cpp
Normal file
@@ -0,0 +1,286 @@
|
||||
/* 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 "ags/shared/ac/common.h" // quit
|
||||
#include "ags/shared/game/room_file.h"
|
||||
#include "ags/shared/game/room_struct.h"
|
||||
#include "ags/shared/gfx/bitmap.h"
|
||||
|
||||
namespace AGS3 {
|
||||
namespace AGS {
|
||||
namespace Shared {
|
||||
|
||||
RoomOptions::RoomOptions()
|
||||
: StartupMusic(0)
|
||||
, SaveLoadDisabled(false)
|
||||
, PlayerCharOff(false)
|
||||
, PlayerView(0)
|
||||
, MusicVolume(kRoomVolumeNormal)
|
||||
, Flags(0) {
|
||||
}
|
||||
|
||||
RoomBgFrame::RoomBgFrame()
|
||||
: IsPaletteShared(false) {
|
||||
memset(Palette, 0, sizeof(Palette));
|
||||
}
|
||||
|
||||
RoomEdges::RoomEdges()
|
||||
: Left(0)
|
||||
, Right(0)
|
||||
, Top(0)
|
||||
, Bottom(0) {
|
||||
}
|
||||
|
||||
RoomEdges::RoomEdges(int l, int r, int t, int b)
|
||||
: Left(l)
|
||||
, Right(r)
|
||||
, Top(t)
|
||||
, Bottom(b) {
|
||||
}
|
||||
|
||||
RoomObjectInfo::RoomObjectInfo()
|
||||
: Sprite(0)
|
||||
, X(0)
|
||||
, Y(0)
|
||||
, Room(-1)
|
||||
, IsOn(false)
|
||||
, Baseline(0xFF)
|
||||
, Flags(0) {
|
||||
}
|
||||
|
||||
RoomRegion::RoomRegion()
|
||||
: Light(0)
|
||||
, Tint(0) {
|
||||
}
|
||||
|
||||
WalkArea::WalkArea()
|
||||
: CharacterView(0)
|
||||
, ScalingFar(0)
|
||||
, ScalingNear(NOT_VECTOR_SCALED)
|
||||
, PlayerView(0)
|
||||
, Top(-1)
|
||||
, Bottom(-1) {
|
||||
}
|
||||
|
||||
WalkBehind::WalkBehind()
|
||||
: Baseline(0) {
|
||||
}
|
||||
|
||||
MessageInfo::MessageInfo()
|
||||
: DisplayAs(0)
|
||||
, Flags(0) {
|
||||
}
|
||||
|
||||
RoomStruct::RoomStruct() {
|
||||
InitDefaults();
|
||||
}
|
||||
|
||||
RoomStruct::~RoomStruct() {
|
||||
Free();
|
||||
}
|
||||
|
||||
void RoomStruct::Free() {
|
||||
for (size_t i = 0; i < (size_t)MAX_ROOM_BGFRAMES; ++i)
|
||||
BgFrames[i].Graphic.reset();
|
||||
HotspotMask.reset();
|
||||
RegionMask.reset();
|
||||
WalkAreaMask.reset();
|
||||
WalkBehindMask.reset();
|
||||
|
||||
LocalVariables.clear();
|
||||
Interaction.reset();
|
||||
Properties.clear();
|
||||
for (size_t i = 0; i < (size_t)MAX_ROOM_HOTSPOTS; ++i) {
|
||||
Hotspots[i].Interaction.reset();
|
||||
Hotspots[i].Properties.clear();
|
||||
}
|
||||
Objects.clear();
|
||||
for (size_t i = 0; i < (size_t)MAX_ROOM_REGIONS; ++i) {
|
||||
Regions[i].Interaction.reset();
|
||||
Regions[i].Properties.clear();
|
||||
}
|
||||
|
||||
FreeMessages();
|
||||
FreeScripts();
|
||||
}
|
||||
|
||||
void RoomStruct::FreeMessages() {
|
||||
for (size_t i = 0; i < MessageCount; ++i) {
|
||||
Messages[i].Free();
|
||||
MessageInfos[i] = MessageInfo();
|
||||
}
|
||||
MessageCount = 0;
|
||||
}
|
||||
|
||||
void RoomStruct::FreeScripts() {
|
||||
CompiledScript.reset();
|
||||
|
||||
EventHandlers.reset();
|
||||
for (size_t i = 0; i < HotspotCount; ++i)
|
||||
Hotspots[i].EventHandlers.reset();
|
||||
for (auto &obj : Objects)
|
||||
obj.EventHandlers.reset();
|
||||
for (size_t i = 0; i < RegionCount; ++i)
|
||||
Regions[i].EventHandlers.reset();
|
||||
}
|
||||
|
||||
void RoomStruct::InitDefaults() {
|
||||
DataVersion = kRoomVersion_Current;
|
||||
GameID = NO_GAME_ID_IN_ROOM_FILE;
|
||||
|
||||
_resolution = kRoomRealRes;
|
||||
MaskResolution = 1;
|
||||
Width = 320;
|
||||
Height = 200;
|
||||
|
||||
Options = RoomOptions();
|
||||
Edges = RoomEdges(0, 317, 40, 199);
|
||||
|
||||
BgFrameCount = 1;
|
||||
HotspotCount = 0;
|
||||
RegionCount = 0;
|
||||
WalkAreaCount = 0;
|
||||
WalkBehindCount = 0;
|
||||
MessageCount = 0;
|
||||
|
||||
for (size_t i = 0; i < (size_t)MAX_ROOM_HOTSPOTS; ++i)
|
||||
Hotspots[i] = RoomHotspot();
|
||||
for (size_t i = 0; i < (size_t)MAX_ROOM_REGIONS; ++i)
|
||||
Regions[i] = RoomRegion();
|
||||
for (size_t i = 0; i < (size_t)MAX_WALK_AREAS; ++i)
|
||||
WalkAreas[i] = WalkArea();
|
||||
for (size_t i = 0; i < (size_t)MAX_WALK_BEHINDS; ++i)
|
||||
WalkBehinds[i] = WalkBehind();
|
||||
|
||||
BackgroundBPP = 1;
|
||||
BgAnimSpeed = 5;
|
||||
|
||||
memset(Palette, 0, sizeof(Palette));
|
||||
}
|
||||
|
||||
void RoomStruct::SetResolution(RoomResolutionType type) {
|
||||
_resolution = type;
|
||||
}
|
||||
|
||||
Bitmap *RoomStruct::GetMask(RoomAreaMask mask) const {
|
||||
switch (mask) {
|
||||
case kRoomAreaHotspot: return HotspotMask.get();
|
||||
case kRoomAreaWalkBehind: return WalkBehindMask.get();
|
||||
case kRoomAreaWalkable: return WalkAreaMask.get();
|
||||
case kRoomAreaRegion: return RegionMask.get();
|
||||
default: return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
float RoomStruct::GetMaskScale(RoomAreaMask mask) const {
|
||||
switch (mask) {
|
||||
case kRoomAreaWalkBehind: return 1.f; // walk-behinds always 1:1 with room size
|
||||
case kRoomAreaHotspot:
|
||||
case kRoomAreaWalkable:
|
||||
case kRoomAreaRegion:
|
||||
return 1.f / MaskResolution;
|
||||
default:
|
||||
return 0.f;
|
||||
}
|
||||
}
|
||||
|
||||
bool RoomStruct::HasRegionLightLevel(int id) const {
|
||||
if (id >= 0 && id < MAX_ROOM_REGIONS)
|
||||
return Regions[id].Tint == 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool RoomStruct::HasRegionTint(int id) const {
|
||||
if (id >= 0 && id < MAX_ROOM_REGIONS)
|
||||
return Regions[id].Tint != 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
int RoomStruct::GetRegionLightLevel(int id) const {
|
||||
if (id >= 0 && id < MAX_ROOM_REGIONS)
|
||||
return HasRegionLightLevel(id) ? Regions[id].Light : 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int RoomStruct::GetRegionTintLuminance(int id) const {
|
||||
if (id >= 0 && id < MAX_ROOM_REGIONS)
|
||||
return HasRegionTint(id) ? (Regions[id].Light * 10) / 25 : 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void load_room(const String &filename, RoomStruct *room, bool game_is_hires, const std::vector<SpriteInfo> &sprinfos) {
|
||||
room->Free();
|
||||
room->InitDefaults();
|
||||
|
||||
RoomDataSource src;
|
||||
HRoomFileError err = OpenRoomFileFromAsset(filename, src);
|
||||
if (err) {
|
||||
err = ReadRoomData(room, src.InputStream.get(), src.DataVersion);
|
||||
if (err)
|
||||
err = UpdateRoomData(room, src.DataVersion, game_is_hires, sprinfos);
|
||||
}
|
||||
if (!err)
|
||||
quitprintf("Unable to load the room file '%s'.\n%s.", filename.GetCStr(), err->FullMessage().GetCStr());
|
||||
}
|
||||
|
||||
PBitmap FixBitmap(PBitmap bmp, int width, int height) {
|
||||
Bitmap *new_bmp = BitmapHelper::AdjustBitmapSize(bmp.get(), width, height);
|
||||
if (new_bmp != bmp.get())
|
||||
return PBitmap(new_bmp);
|
||||
return bmp;
|
||||
}
|
||||
|
||||
void UpscaleRoomBackground(RoomStruct *room, bool game_is_hires) {
|
||||
if (room->DataVersion >= kRoomVersion_303b || !game_is_hires)
|
||||
return;
|
||||
for (size_t i = 0; i < room->BgFrameCount; ++i)
|
||||
room->BgFrames[i].Graphic = FixBitmap(room->BgFrames[i].Graphic, room->Width, room->Height);
|
||||
FixRoomMasks(room);
|
||||
}
|
||||
|
||||
void FixRoomMasks(RoomStruct *room) {
|
||||
if (room->MaskResolution <= 0)
|
||||
return;
|
||||
Bitmap *bkg = room->BgFrames[0].Graphic.get();
|
||||
if (bkg == nullptr)
|
||||
return;
|
||||
// TODO: this issue is somewhat complicated. Original code was relying on
|
||||
// room->Width and Height properties. But in the engine these are saved
|
||||
// already converted to data resolution which may be "low-res". Since this
|
||||
// function is shared between engine and editor we do not know if we need
|
||||
// to upscale them.
|
||||
// For now room width/height is always equal to background bitmap.
|
||||
int base_width = bkg->GetWidth();
|
||||
int base_height = bkg->GetHeight();
|
||||
int low_width = base_width / room->MaskResolution;
|
||||
int low_height = base_height / room->MaskResolution;
|
||||
|
||||
// Walk-behinds are always 1:1 of the primary background.
|
||||
// Other masks are 1:x where X is MaskResolution.
|
||||
room->WalkBehindMask = FixBitmap(room->WalkBehindMask, base_width, base_height);
|
||||
room->HotspotMask = FixBitmap(room->HotspotMask, low_width, low_height);
|
||||
room->RegionMask = FixBitmap(room->RegionMask, low_width, low_height);
|
||||
room->WalkAreaMask = FixBitmap(room->WalkAreaMask, low_width, low_height);
|
||||
}
|
||||
|
||||
} // namespace Shared
|
||||
} // namespace AGS
|
||||
} // namespace AGS3
|
||||
398
engines/ags/shared/game/room_struct.h
Normal file
398
engines/ags/shared/game/room_struct.h
Normal file
@@ -0,0 +1,398 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
//
|
||||
// RoomStruct, a class describing initial room data.
|
||||
//
|
||||
// Because of the imperfect implementation there is inconsistency in how
|
||||
// this data is interpreted at the runtime.
|
||||
// Some of that data is never supposed to be changed at runtime. Another
|
||||
// may be changed, but these changes are lost as soon as room is unloaded.
|
||||
// The changes that must remain in memory are kept as separate classes:
|
||||
// see RoomStatus, RoomObject etc.
|
||||
//
|
||||
// Partially this is because same class was used for both engine and editor,
|
||||
// while runtime code was not available for the editor.
|
||||
//
|
||||
// This is also the reason why some classes here are named with the "Info"
|
||||
// postfix. For example, RoomObjectInfo is the initial object data, and
|
||||
// there is also RoomObject runtime-only class for mutable data.
|
||||
//
|
||||
// [ivan-mogilko] In my opinion, eventually there should be only one room class
|
||||
// and one class per room entity, regardless of whether code is shared with
|
||||
// the editor or not. But that would require extensive refactor/rewrite of
|
||||
// the engine code, and savegame read/write code.
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef AGS_SHARED_GAME_ROOM_INFO_H
|
||||
#define AGS_SHARED_GAME_ROOM_INFO_H
|
||||
|
||||
#include "common/std/memory.h"
|
||||
#include "ags/lib/allegro.h" // RGB
|
||||
#include "ags/shared/ac/common_defines.h"
|
||||
#include "ags/shared/game/interactions.h"
|
||||
#include "ags/shared/util/geometry.h"
|
||||
#include "ags/shared/util/string.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
struct ccScript;
|
||||
struct SpriteInfo;
|
||||
typedef std::shared_ptr<ccScript> PScript;
|
||||
|
||||
// TODO: move the following enums under AGS::Shared namespace
|
||||
// later, when more engine source is put in AGS namespace and
|
||||
// refactored.
|
||||
|
||||
// Room's area mask type
|
||||
enum RoomAreaMask {
|
||||
kRoomAreaNone = 0,
|
||||
kRoomAreaHotspot,
|
||||
kRoomAreaWalkBehind,
|
||||
kRoomAreaWalkable,
|
||||
kRoomAreaRegion
|
||||
};
|
||||
|
||||
// Room's audio volume modifier
|
||||
enum RoomVolumeMod {
|
||||
kRoomVolumeQuietest = -3,
|
||||
kRoomVolumeQuieter = -2,
|
||||
kRoomVolumeQuiet = -1,
|
||||
kRoomVolumeNormal = 0,
|
||||
kRoomVolumeLoud = 1,
|
||||
kRoomVolumeLouder = 2,
|
||||
kRoomVolumeLoudest = 3,
|
||||
// These two options are only settable at runtime by SetMusicVolume()
|
||||
kRoomVolumeExtra1 = 4,
|
||||
kRoomVolumeExtra2 = 5,
|
||||
|
||||
kRoomVolumeMin = kRoomVolumeQuietest,
|
||||
kRoomVolumeMax = kRoomVolumeExtra2,
|
||||
};
|
||||
|
||||
// Extended room boolean options
|
||||
enum RoomFlags {
|
||||
kRoomFlag_BkgFrameLocked = 0x01
|
||||
};
|
||||
|
||||
// Flag tells that walkable area does not have continious zoom
|
||||
#define NOT_VECTOR_SCALED -10000
|
||||
// Flags tells that room is not linked to particular game ID
|
||||
#define NO_GAME_ID_IN_ROOM_FILE 16325
|
||||
|
||||
#define MAX_ROOM_BGFRAMES 5 // max number of frames in animating bg scene
|
||||
|
||||
#define MAX_ROOM_HOTSPOTS 50 // v2.62: 20 -> 30; v2.8: -> 50
|
||||
#define MAX_ROOM_OBJECTS_v300 40 // for some legacy logic support
|
||||
#define MAX_ROOM_OBJECTS 256 // v3.6.0: 40 -> 256 (now limited by room format)
|
||||
#define MAX_ROOM_REGIONS 16
|
||||
#define MAX_WALK_AREAS 16
|
||||
#define MAX_WALK_BEHINDS 16
|
||||
|
||||
#define MAX_MESSAGES 100
|
||||
|
||||
|
||||
namespace AGS {
|
||||
namespace Shared {
|
||||
|
||||
class Bitmap;
|
||||
class Stream;
|
||||
|
||||
typedef std::shared_ptr<Bitmap> PBitmap;
|
||||
|
||||
// Various room options
|
||||
struct RoomOptions {
|
||||
// Index of the startup music in the room
|
||||
// this is a deprecated option, used before 3.2.* with old audio API.
|
||||
int StartupMusic;
|
||||
// If saving and loading game is disabled in the room;
|
||||
// this is a deprecated option that affects only built-in save/load dialogs
|
||||
bool SaveLoadDisabled;
|
||||
// If player character is turned off in the room
|
||||
bool PlayerCharOff;
|
||||
// Apply player character's normal view when entering this room
|
||||
int PlayerView;
|
||||
// Room's music volume modifier
|
||||
RoomVolumeMod MusicVolume;
|
||||
// A collection of RoomFlags
|
||||
int Flags;
|
||||
|
||||
RoomOptions();
|
||||
};
|
||||
|
||||
// Single room background frame
|
||||
struct RoomBgFrame {
|
||||
PBitmap Graphic;
|
||||
// Palette is only valid in 8-bit games
|
||||
RGB Palette[256];
|
||||
// Tells if this frame should keep previous frame palette instead of using its own
|
||||
bool IsPaletteShared;
|
||||
|
||||
RoomBgFrame();
|
||||
};
|
||||
|
||||
// Describes room edges (coordinates of four edges)
|
||||
struct RoomEdges {
|
||||
int32_t Left;
|
||||
int32_t Right;
|
||||
int32_t Top;
|
||||
int32_t Bottom;
|
||||
|
||||
RoomEdges();
|
||||
RoomEdges(int l, int r, int t, int b);
|
||||
};
|
||||
|
||||
// Room hotspot description
|
||||
struct RoomHotspot {
|
||||
String Name;
|
||||
String ScriptName;
|
||||
// Custom properties
|
||||
StringIMap Properties;
|
||||
// Old-style interactions
|
||||
PInteraction Interaction;
|
||||
// Event script links
|
||||
PInteractionScripts EventHandlers;
|
||||
|
||||
// Player will automatically walk here when interacting with hotspot
|
||||
Point WalkTo;
|
||||
};
|
||||
|
||||
// Room object description
|
||||
struct RoomObjectInfo {
|
||||
int32_t Room;
|
||||
int32_t X;
|
||||
int32_t Y;
|
||||
int32_t Sprite;
|
||||
bool IsOn;
|
||||
// Object's z-order in the room, or -1 (use Y)
|
||||
int32_t Baseline;
|
||||
int32_t Flags;
|
||||
String Name;
|
||||
String ScriptName;
|
||||
// Custom properties
|
||||
StringIMap Properties;
|
||||
// Old-style interactions
|
||||
PInteraction Interaction;
|
||||
// Event script links
|
||||
PInteractionScripts EventHandlers;
|
||||
|
||||
RoomObjectInfo();
|
||||
};
|
||||
|
||||
// Room region description
|
||||
struct RoomRegion {
|
||||
// Light level (-100 -> +100) or Tint luminance (0 - 255)
|
||||
int32_t Light;
|
||||
// Tint setting (R-B-G-S)
|
||||
int32_t Tint;
|
||||
// Custom properties
|
||||
StringIMap Properties;
|
||||
// Old-style interactions
|
||||
PInteraction Interaction;
|
||||
// Event script links
|
||||
PInteractionScripts EventHandlers;
|
||||
|
||||
RoomRegion();
|
||||
};
|
||||
|
||||
// Walkable area description
|
||||
struct WalkArea {
|
||||
// Apply player character's normal view on this area
|
||||
int32_t CharacterView;
|
||||
// Character's scaling (-100 -> +100 %)
|
||||
// General scaling, or scaling at the farthest point
|
||||
int32_t ScalingFar;
|
||||
// Scaling at the nearest point, or NOT_VECTOR_SCALED for uniform scaling
|
||||
int32_t ScalingNear;
|
||||
// Optional override for player character view
|
||||
int32_t PlayerView;
|
||||
// Top and bottom Y of the area
|
||||
int32_t Top;
|
||||
int32_t Bottom;
|
||||
|
||||
WalkArea();
|
||||
};
|
||||
|
||||
// Walk-behind description
|
||||
struct WalkBehind {
|
||||
// Object's z-order in the room
|
||||
int32_t Baseline;
|
||||
|
||||
WalkBehind();
|
||||
};
|
||||
|
||||
// Room messages
|
||||
|
||||
#define MSG_DISPLAYNEXT 0x01 // supercedes using alt-200 at end of message
|
||||
#define MSG_TIMELIMIT 0x02
|
||||
|
||||
struct MessageInfo {
|
||||
int8 DisplayAs; // 0 - std display window, >=1 - as character's speech
|
||||
int8 Flags; // combination of MSG_xxx flags
|
||||
|
||||
MessageInfo();
|
||||
};
|
||||
|
||||
|
||||
// Room's legacy resolution type
|
||||
enum RoomResolutionType {
|
||||
kRoomRealRes = 0, // room should always be treated as-is
|
||||
kRoomLoRes = 1, // created for low-resolution game
|
||||
kRoomHiRes = 2 // created for high-resolution game
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// Description of a single room.
|
||||
// This class contains initial room data. Some of it may still be modified
|
||||
// at the runtime, but then these changes get lost as soon as room is unloaded.
|
||||
//
|
||||
class RoomStruct {
|
||||
public:
|
||||
RoomStruct();
|
||||
~RoomStruct();
|
||||
|
||||
// Gets if room should adjust its base size depending on game's resolution
|
||||
inline bool IsRelativeRes() const {
|
||||
return _resolution != kRoomRealRes;
|
||||
}
|
||||
// Gets if room belongs to high resolution
|
||||
inline bool IsLegacyHiRes() const {
|
||||
return _resolution == kRoomHiRes;
|
||||
}
|
||||
// Gets legacy resolution type
|
||||
inline RoomResolutionType GetResolutionType() const {
|
||||
return _resolution;
|
||||
}
|
||||
|
||||
// Releases room resources
|
||||
void Free();
|
||||
// Release room messages and scripts correspondingly. These two functions are needed
|
||||
// at very specific occasion when only part of the room resources has to be freed.
|
||||
void FreeMessages();
|
||||
void FreeScripts();
|
||||
// Init default room state
|
||||
void InitDefaults();
|
||||
// Set legacy resolution type
|
||||
void SetResolution(RoomResolutionType type);
|
||||
|
||||
// Gets bitmap of particular mask layer
|
||||
Bitmap *GetMask(RoomAreaMask mask) const;
|
||||
// Gets mask's scale relative to the room's background size
|
||||
float GetMaskScale(RoomAreaMask mask) const;
|
||||
|
||||
// TODO: see later whether it may be more convenient to move these to the Region class instead.
|
||||
// Gets if the given region has light level set
|
||||
bool HasRegionLightLevel(int id) const;
|
||||
// Gets if the given region has a tint set
|
||||
bool HasRegionTint(int id) const;
|
||||
// Gets region's light level in -100 to 100 range value; returns 0 (default level) if region's tint is set
|
||||
int GetRegionLightLevel(int id) const;
|
||||
// Gets region's tint luminance in 0 to 100 range value; returns 0 if region's light level is set
|
||||
int GetRegionTintLuminance(int id) const;
|
||||
|
||||
// TODO: all members are currently public because they are used everywhere; hide them later
|
||||
public:
|
||||
// Game's unique ID, corresponds to GameSetupStructBase::uniqueid.
|
||||
// If this field has a valid value and does not match actual game's id,
|
||||
// then engine will refuse to start this room.
|
||||
// May be set to NO_GAME_ID_IN_ROOM_FILE to let it run within any game.
|
||||
int32_t GameID;
|
||||
// Loaded room file's data version. This value may be used to know when
|
||||
// the room must have behavior specific to certain version of AGS.
|
||||
int32_t DataVersion;
|
||||
|
||||
// Room region masks resolution. Defines the relation between room and mask units.
|
||||
// Mask point is calculated as roompt / MaskResolution. Must be >= 1.
|
||||
int32_t MaskResolution;
|
||||
// Size of the room, in logical coordinates (= pixels)
|
||||
int32_t Width;
|
||||
int32_t Height;
|
||||
// Primary room palette (8-bit games)
|
||||
RGB Palette[256];
|
||||
|
||||
// Basic room options
|
||||
RoomOptions Options;
|
||||
|
||||
// Background frames
|
||||
int32_t BackgroundBPP; // bytes per pixel
|
||||
size_t BgFrameCount;
|
||||
RoomBgFrame BgFrames[MAX_ROOM_BGFRAMES];
|
||||
// Speed at which background frames are changing, 0 - no auto animation
|
||||
int32_t BgAnimSpeed;
|
||||
// Edges
|
||||
RoomEdges Edges;
|
||||
// Region masks
|
||||
PBitmap HotspotMask;
|
||||
PBitmap RegionMask;
|
||||
PBitmap WalkAreaMask;
|
||||
PBitmap WalkBehindMask;
|
||||
// Room entities
|
||||
size_t HotspotCount;
|
||||
RoomHotspot Hotspots[MAX_ROOM_HOTSPOTS];
|
||||
std::vector<RoomObjectInfo> Objects;
|
||||
size_t RegionCount;
|
||||
RoomRegion Regions[MAX_ROOM_REGIONS];
|
||||
size_t WalkAreaCount;
|
||||
WalkArea WalkAreas[MAX_WALK_AREAS];
|
||||
size_t WalkBehindCount;
|
||||
WalkBehind WalkBehinds[MAX_WALK_BEHINDS];
|
||||
|
||||
// Old numbered room messages (used with DisplayMessage, etc)
|
||||
size_t MessageCount;
|
||||
String Messages[MAX_MESSAGES];
|
||||
MessageInfo MessageInfos[MAX_MESSAGES];
|
||||
|
||||
// Custom properties
|
||||
StringIMap Properties;
|
||||
// Old-style interactions
|
||||
InterVarVector LocalVariables;
|
||||
PInteraction Interaction;
|
||||
// Event script links
|
||||
PInteractionScripts EventHandlers;
|
||||
// Compiled room script
|
||||
PScript CompiledScript;
|
||||
// Various extended options with string values, meta-data etc
|
||||
StringMap StrOptions;
|
||||
|
||||
private:
|
||||
// Room's legacy resolution type, defines relation room and game's resolution
|
||||
RoomResolutionType _resolution;
|
||||
};
|
||||
|
||||
|
||||
// Loads new room data into the given RoomStruct object
|
||||
void load_room(const String &filename, RoomStruct *room, bool game_is_hires, const std::vector<SpriteInfo> &sprinfos);
|
||||
// Checks if it's necessary and upscales low-res room backgrounds and masks for the high resolution game
|
||||
// NOTE: it does not upscale object coordinates, because that is usually done when the room is loaded
|
||||
void UpscaleRoomBackground(RoomStruct *room, bool game_is_hires);
|
||||
// Ensures that all existing room masks match room background size and
|
||||
// MaskResolution property, resizes mask bitmaps if necessary.
|
||||
void FixRoomMasks(RoomStruct *room);
|
||||
// Adjusts bitmap size if necessary and returns either new or old bitmap.
|
||||
PBitmap FixBitmap(PBitmap bmp, int dst_width, int dst_height);
|
||||
|
||||
} // namespace Shared
|
||||
} // namespace AGS
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
99
engines/ags/shared/game/room_version.h
Normal file
99
engines/ags/shared/game/room_version.h
Normal file
@@ -0,0 +1,99 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// Room version constants and information
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef AGS_SHARED_GAME_ROOM_VERSION_H
|
||||
#define AGS_SHARED_GAME_ROOM_VERSION_H
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
/* room file versions history
|
||||
8: final v1.14 release
|
||||
9: intermediate v2 alpha releases
|
||||
10: v2 alpha-7 release
|
||||
11: final v2.00 release
|
||||
12: v2.08, to add colour depth byte
|
||||
13: v2.14, add walkarea light levels
|
||||
14: v2.4, fixed so it saves walkable area 15
|
||||
15: v2.41, supports NewInteraction
|
||||
16: v2.5
|
||||
17: v2.5 - just version change to force room re-compile for new charctr struct
|
||||
18: v2.51 - vector scaling
|
||||
19: v2.53 - interaction variables
|
||||
20: v2.55 - shared palette backgrounds
|
||||
21: v2.55 - regions
|
||||
22: v2.61 - encrypt room messages
|
||||
23: v2.62 - object flags
|
||||
24: v2.7 - hotspot script names
|
||||
25: v2.72 - game id embedded
|
||||
26: v3.0 - new interaction format, and no script source
|
||||
27: v3.0 - store Y of bottom of object, not top
|
||||
28: v3.0.3 - remove hotspot name length limit
|
||||
29: v3.0.3 - high-res coords for object x/y, edges and hotspot walk-to point
|
||||
30: v3.4.0.4 - tint luminance for regions
|
||||
31: v3.4.1.5 - removed room object and hotspot name length limits
|
||||
32: v3.5.0 - 64-bit file offsets
|
||||
33: v3.5.0.8 - deprecated room resolution, added mask resolution
|
||||
Since then format value is defined as AGS version represented as NN,NN,NN,NN.
|
||||
*/
|
||||
enum RoomFileVersion {
|
||||
kRoomVersion_Undefined = 0,
|
||||
kRoomVersion_pre114_3 = 3, // exact version unknown
|
||||
kRoomVersion_pre114_4 = 4, // exact version unknown
|
||||
kRoomVersion_pre114_5 = 5, // exact version unknown
|
||||
kRoomVersion_pre114_6 = 6, // exact version unknown
|
||||
kRoomVersion_114 = 8,
|
||||
kRoomVersion_200_alpha = 9,
|
||||
kRoomVersion_200_alpha7 = 10,
|
||||
kRoomVersion_200_final = 11,
|
||||
kRoomVersion_208 = 12,
|
||||
kRoomVersion_214 = 13,
|
||||
kRoomVersion_240 = 14,
|
||||
kRoomVersion_241 = 15,
|
||||
kRoomVersion_250a = 16,
|
||||
kRoomVersion_250b = 17,
|
||||
kRoomVersion_251 = 18,
|
||||
kRoomVersion_253 = 19,
|
||||
kRoomVersion_255a = 20,
|
||||
kRoomVersion_255b = 21,
|
||||
kRoomVersion_261 = 22,
|
||||
kRoomVersion_262 = 23,
|
||||
kRoomVersion_270 = 24,
|
||||
kRoomVersion_272 = 25,
|
||||
kRoomVersion_300a = 26,
|
||||
kRoomVersion_300b = 27,
|
||||
kRoomVersion_303a = 28,
|
||||
kRoomVersion_303b = 29,
|
||||
kRoomVersion_3404 = 30,
|
||||
kRoomVersion_3415 = 31,
|
||||
kRoomVersion_350 = 32,
|
||||
kRoomVersion_3508 = 33,
|
||||
kRoomVersion_Current = kRoomVersion_3508
|
||||
};
|
||||
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
274
engines/ags/shared/game/tra_file.cpp
Normal file
274
engines/ags/shared/game/tra_file.cpp
Normal file
@@ -0,0 +1,274 @@
|
||||
/* 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 "ags/shared/game/tra_file.h"
|
||||
#include "ags/shared/ac/words_dictionary.h"
|
||||
#include "ags/shared/debugging/out.h"
|
||||
#include "ags/shared/util/data_ext.h"
|
||||
#include "ags/shared/util/string_compat.h"
|
||||
#include "ags/shared/util/string_utils.h"
|
||||
|
||||
namespace AGS3 {
|
||||
namespace AGS {
|
||||
namespace Shared {
|
||||
|
||||
const char *TRASignature = "AGSTranslation";
|
||||
|
||||
|
||||
String GetTraFileErrorText(TraFileErrorType err) {
|
||||
switch (err) {
|
||||
case kTraFileErr_NoError:
|
||||
return "No error.";
|
||||
case kTraFileErr_SignatureFailed:
|
||||
return "Not an AGS translation file or an unsupported format.";
|
||||
case kTraFileErr_FormatNotSupported:
|
||||
return "Format version not supported.";
|
||||
case kTraFileErr_GameIDMismatch:
|
||||
return "Game ID does not match, translation is meant for a different game.";
|
||||
case kTraFileErr_UnexpectedEOF:
|
||||
return "Unexpected end of file.";
|
||||
case kTraFileErr_UnknownBlockType:
|
||||
return "Unknown block type.";
|
||||
case kTraFileErr_BlockDataOverlapping:
|
||||
return "Block data overlapping.";
|
||||
default:
|
||||
return "Unknown error.";
|
||||
}
|
||||
}
|
||||
|
||||
String GetTraBlockName(TraFileBlock id) {
|
||||
switch (id) {
|
||||
case kTraFblk_Dict: return "Dictionary";
|
||||
case kTraFblk_GameID: return "GameID";
|
||||
case kTraFblk_TextOpts: return "TextOpts";
|
||||
default: return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
HError OpenTraFile(Stream *in) {
|
||||
// Test the file signature
|
||||
char sigbuf[16] = { 0 };
|
||||
in->Read(sigbuf, 15);
|
||||
if (ags_stricmp(TRASignature, sigbuf) != 0)
|
||||
return new TraFileError(kTraFileErr_SignatureFailed);
|
||||
return HError::None();
|
||||
}
|
||||
|
||||
HError ReadTraBlock(Translation &tra, Stream *in, TraFileBlock block, const String &ext_id, soff_t /*block_len*/) {
|
||||
switch (block) {
|
||||
case kTraFblk_Dict: {
|
||||
std::vector<char> buf;
|
||||
// Read lines until we find zero-length key & value
|
||||
while (true) {
|
||||
String src_line = read_string_decrypt(in, buf);
|
||||
String dst_line = read_string_decrypt(in, buf);
|
||||
if (src_line.IsEmpty() || dst_line.IsEmpty())
|
||||
break;
|
||||
tra.Dict.insert(std::make_pair(src_line, dst_line));
|
||||
}
|
||||
return HError::None();
|
||||
}
|
||||
case kTraFblk_GameID:
|
||||
{
|
||||
char gamename[256];
|
||||
tra.GameUid = in->ReadInt32();
|
||||
read_string_decrypt(in, gamename, sizeof(gamename));
|
||||
tra.GameName = gamename;
|
||||
return HError::None();
|
||||
}
|
||||
case kTraFblk_TextOpts:
|
||||
tra.NormalFont = in->ReadInt32();
|
||||
tra.SpeechFont = in->ReadInt32();
|
||||
tra.RightToLeft = in->ReadInt32();
|
||||
return HError::None();
|
||||
case kTraFblk_None:
|
||||
// continue reading extensions with string ID
|
||||
break;
|
||||
default:
|
||||
return new TraFileError(kTraFileErr_UnknownBlockType,
|
||||
String::FromFormat("Type: %d, known range: %d - %d.", block, kTraFblk_Dict, kTraFblk_TextOpts));
|
||||
}
|
||||
|
||||
if (ext_id.CompareNoCase("ext_sopts") == 0) {
|
||||
StrUtil::ReadStringMap(tra.StrOptions, in);
|
||||
return HError::None();
|
||||
}
|
||||
|
||||
return new TraFileError(kTraFileErr_UnknownBlockType,
|
||||
String::FromFormat("Type: %s", ext_id.GetCStr()));
|
||||
}
|
||||
|
||||
// TRABlockReader reads whole TRA data, block by block
|
||||
class TRABlockReader : public DataExtReader {
|
||||
public:
|
||||
TRABlockReader(Translation &tra, Stream *in)
|
||||
: DataExtReader(in, kDataExt_NumID32 | kDataExt_File32)
|
||||
, _tra(tra) {
|
||||
}
|
||||
|
||||
// Reads only the Game ID block and stops
|
||||
HError ReadGameID() {
|
||||
HError err = FindOne(kTraFblk_GameID);
|
||||
if (!err)
|
||||
return err;
|
||||
return ReadTraBlock(_tra, _in, kTraFblk_GameID, "", _blockLen);
|
||||
}
|
||||
|
||||
private:
|
||||
String GetOldBlockName(int block_id) const override {
|
||||
return GetTraBlockName((TraFileBlock)block_id);
|
||||
}
|
||||
|
||||
soff_t GetOverLeeway(int block_id) const override {
|
||||
// TRA files made by pre-3.0 editors have a block length miscount by 1 byte
|
||||
if (block_id == kTraFblk_GameID) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
HError ReadBlock(int block_id, const String &ext_id,
|
||||
soff_t block_len, bool &read_next) override {
|
||||
read_next = true;
|
||||
return ReadTraBlock(_tra, _in, (TraFileBlock)block_id, ext_id, block_len);
|
||||
}
|
||||
|
||||
Translation &_tra;
|
||||
};
|
||||
|
||||
|
||||
HError TestTraGameID(int game_uid, const String &game_name, Stream *in) {
|
||||
HError err = OpenTraFile(in);
|
||||
if (!err)
|
||||
return err;
|
||||
|
||||
Translation tra;
|
||||
TRABlockReader reader(tra, in);
|
||||
err = reader.ReadGameID();
|
||||
|
||||
if (!err)
|
||||
return err;
|
||||
// Test the identifiers, if they are not present then skip the test
|
||||
if ((tra.GameUid != 0 && (game_uid != tra.GameUid)) ||
|
||||
(!tra.GameName.IsEmpty() && (game_name != tra.GameName)))
|
||||
return new TraFileError(kTraFileErr_GameIDMismatch,
|
||||
String::FromFormat("The translation is designed for '%s'", tra.GameName.GetCStr()));
|
||||
return HError::None();
|
||||
}
|
||||
|
||||
HError ReadTraData(Translation &tra, Stream *in) {
|
||||
HError err = OpenTraFile(in);
|
||||
if (!err)
|
||||
return err;
|
||||
|
||||
TRABlockReader reader(tra, in);
|
||||
return reader.Read();
|
||||
}
|
||||
|
||||
// TODO: perhaps merge with encrypt/decrypt utilities
|
||||
static const char *EncryptText(std::vector<char> &en_buf, const String &s) {
|
||||
if (en_buf.size() < s.GetLength() + 1)
|
||||
en_buf.resize(s.GetLength() + 1);
|
||||
strncpy(&en_buf.front(), s.GetCStr(), s.GetLength() + 1);
|
||||
encrypt_text(&en_buf.front());
|
||||
return &en_buf.front();
|
||||
}
|
||||
|
||||
// TODO: perhaps merge with encrypt/decrypt utilities
|
||||
static const char *EncryptEmptyString(std::vector<char> &en_buf) {
|
||||
en_buf[0] = 0;
|
||||
encrypt_text(&en_buf.front());
|
||||
return &en_buf.front();
|
||||
}
|
||||
|
||||
void WriteGameID(const Translation &tra, Stream *out) {
|
||||
std::vector<char> en_buf;
|
||||
out->WriteInt32(tra.GameUid);
|
||||
StrUtil::WriteString(EncryptText(en_buf, tra.GameName), tra.GameName.GetLength() + 1, out);
|
||||
}
|
||||
|
||||
void WriteDict(const Translation &tra, Stream *out) {
|
||||
std::vector<char> en_buf;
|
||||
for (const auto &kv : tra.Dict) {
|
||||
const String &src = kv._key;
|
||||
const String &dst = kv._value;
|
||||
if (!dst.IsNullOrSpace()) {
|
||||
String unsrc = StrUtil::Unescape(src);
|
||||
String undst = StrUtil::Unescape(dst);
|
||||
StrUtil::WriteString(EncryptText(en_buf, unsrc), unsrc.GetLength() + 1, out);
|
||||
StrUtil::WriteString(EncryptText(en_buf, undst), undst.GetLength() + 1, out);
|
||||
}
|
||||
}
|
||||
// Write a pair of empty key/values
|
||||
StrUtil::WriteString(EncryptEmptyString(en_buf), 1, out);
|
||||
StrUtil::WriteString(EncryptEmptyString(en_buf), 1, out);
|
||||
}
|
||||
|
||||
void WriteTextOpts(const Translation &tra, Stream *out) {
|
||||
out->WriteInt32(tra.NormalFont);
|
||||
out->WriteInt32(tra.SpeechFont);
|
||||
out->WriteInt32(tra.RightToLeft);
|
||||
}
|
||||
|
||||
static const Translation *writer_tra;
|
||||
static void(*writer_writer)(const Translation &tra, Stream *out);
|
||||
|
||||
void WriteStrOptions(const Translation &tra, Stream *out) {
|
||||
StrUtil::WriteStringMap(tra.StrOptions, out);
|
||||
}
|
||||
|
||||
static void WriteTraBlockWriter(Stream *out) {
|
||||
writer_writer(*writer_tra, out);
|
||||
}
|
||||
|
||||
inline void WriteTraBlock(const Translation &tra, TraFileBlock block,
|
||||
void(*writer)(const Translation &tra, Stream *out), Stream *out) {
|
||||
writer_tra = &tra;
|
||||
writer_writer = writer;
|
||||
|
||||
WriteExtBlock(block, WriteTraBlockWriter,
|
||||
kDataExt_NumID32 | kDataExt_File32, out);
|
||||
}
|
||||
|
||||
inline void WriteTraBlock(const Translation &tra, const String &ext_id,
|
||||
void(*writer)(const Translation &tra, Stream *out), Stream *out) {
|
||||
writer_tra = &tra;
|
||||
writer_writer = writer;
|
||||
|
||||
WriteExtBlock(ext_id, WriteTraBlockWriter,
|
||||
kDataExt_NumID32 | kDataExt_File32, out);
|
||||
}
|
||||
|
||||
void WriteTraData(const Translation &tra, Stream *out) {
|
||||
// Write header
|
||||
out->Write(TRASignature, strlen(TRASignature) + 1);
|
||||
|
||||
// Write all blocks
|
||||
WriteTraBlock(tra, kTraFblk_GameID, WriteGameID, out);
|
||||
WriteTraBlock(tra, kTraFblk_Dict, WriteDict, out);
|
||||
WriteTraBlock(tra, kTraFblk_TextOpts, WriteTextOpts, out);
|
||||
WriteTraBlock(tra, "ext_sopts", WriteStrOptions, out);
|
||||
|
||||
// Write ending
|
||||
out->WriteInt32(kTraFile_EOF);
|
||||
}
|
||||
|
||||
} // namespace Shared
|
||||
} // namespace AGS
|
||||
} // namespace AGS3
|
||||
89
engines/ags/shared/game/tra_file.h
Normal file
89
engines/ags/shared/game/tra_file.h
Normal file
@@ -0,0 +1,89 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// This unit provides functions for reading compiled translation file.
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef AGS_SHARED_GAME_TRA_FILE_H
|
||||
#define AGS_SHARED_GAME_TRA_FILE_H
|
||||
|
||||
#include "ags/shared/util/error.h"
|
||||
#include "ags/shared/util/stream.h"
|
||||
#include "ags/shared/util/string_types.h"
|
||||
|
||||
namespace AGS3 {
|
||||
namespace AGS {
|
||||
namespace Shared {
|
||||
|
||||
enum TraFileErrorType {
|
||||
kTraFileErr_NoError,
|
||||
kTraFileErr_SignatureFailed,
|
||||
kTraFileErr_FormatNotSupported,
|
||||
kTraFileErr_GameIDMismatch,
|
||||
kTraFileErr_UnexpectedEOF,
|
||||
kTraFileErr_UnknownBlockType,
|
||||
kTraFileErr_BlockDataOverlapping,
|
||||
};
|
||||
|
||||
enum TraFileBlock {
|
||||
kTraFblk_None = 0,
|
||||
kTraFblk_Dict = 1,
|
||||
kTraFblk_GameID = 2,
|
||||
kTraFblk_TextOpts = 3,
|
||||
// End of data tag
|
||||
kTraFile_EOF = -1
|
||||
};
|
||||
|
||||
String GetTraFileErrorText(TraFileErrorType err);
|
||||
String GetTraBlockName(TraFileBlock id);
|
||||
|
||||
typedef TypedCodeError<TraFileErrorType, GetTraFileErrorText> TraFileError;
|
||||
|
||||
|
||||
struct Translation {
|
||||
// Game identifiers, for matching the translation file with the game
|
||||
int GameUid = 0;
|
||||
String GameName;
|
||||
// Translation dictionary in source/dest pairs
|
||||
StringMap Dict;
|
||||
// Localization parameters
|
||||
int NormalFont = -1; // replacement for normal font, or -1 for default
|
||||
int SpeechFont = -1; // replacement for speech font, or -1 for default
|
||||
int RightToLeft = -1; // r2l text mode (0, 1), or -1 for default
|
||||
StringMap StrOptions; // to store extended options with string values
|
||||
};
|
||||
|
||||
|
||||
// Parses translation data and tests whether it matches the given game
|
||||
HError TestTraGameID(int game_uid, const String &game_name, Stream *in);
|
||||
// Reads full translation data from the provided stream
|
||||
HError ReadTraData(Translation &tra, Stream *in);
|
||||
// Writes all translation data to the stream
|
||||
void WriteTraData(const Translation &tra, Stream *out);
|
||||
|
||||
} // namespace Shared
|
||||
} // namespace AGS
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
457
engines/ags/shared/gfx/allegro_bitmap.cpp
Normal file
457
engines/ags/shared/gfx/allegro_bitmap.cpp
Normal file
@@ -0,0 +1,457 @@
|
||||
/* 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/savefile.h"
|
||||
#include "common/system.h"
|
||||
#include "ags/lib/aastr-0.1.1/aastr.h"
|
||||
#include "ags/shared/gfx/allegro_bitmap.h"
|
||||
#include "ags/shared/gfx/image.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
extern void __my_setcolor(int *ctset, int newcol, int wantColDep);
|
||||
|
||||
namespace AGS {
|
||||
namespace Shared {
|
||||
|
||||
Bitmap::Bitmap()
|
||||
: _alBitmap(nullptr)
|
||||
, _isDataOwner(false) {
|
||||
}
|
||||
|
||||
Bitmap::Bitmap(int width, int height, int color_depth)
|
||||
: _alBitmap(nullptr)
|
||||
, _isDataOwner(false) {
|
||||
Create(width, height, color_depth);
|
||||
}
|
||||
|
||||
Bitmap::Bitmap(Bitmap *src, const Rect &rc)
|
||||
: _alBitmap(nullptr)
|
||||
, _isDataOwner(false) {
|
||||
CreateSubBitmap(src, rc);
|
||||
}
|
||||
|
||||
Bitmap::Bitmap(BITMAP *al_bmp, bool shared_data)
|
||||
: _alBitmap(nullptr)
|
||||
, _isDataOwner(false) {
|
||||
WrapAllegroBitmap(al_bmp, shared_data);
|
||||
}
|
||||
|
||||
Bitmap::~Bitmap() {
|
||||
Destroy();
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Creation and destruction
|
||||
//=============================================================================
|
||||
|
||||
bool Bitmap::Create(int width, int height, int color_depth) {
|
||||
Destroy();
|
||||
if (color_depth) {
|
||||
_alBitmap = create_bitmap_ex(color_depth, width, height);
|
||||
} else {
|
||||
_alBitmap = create_bitmap(width, height);
|
||||
}
|
||||
_isDataOwner = true;
|
||||
return _alBitmap != nullptr;
|
||||
}
|
||||
|
||||
bool Bitmap::CreateTransparent(int width, int height, int color_depth) {
|
||||
if (Create(width, height, color_depth)) {
|
||||
clear_to_color(_alBitmap, bitmap_mask_color(_alBitmap));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Bitmap::CreateSubBitmap(Bitmap *src, const Rect &rc) {
|
||||
Destroy();
|
||||
_alBitmap = create_sub_bitmap(src->_alBitmap, rc.Left, rc.Top, rc.GetWidth(), rc.GetHeight());
|
||||
_isDataOwner = true;
|
||||
return _alBitmap != nullptr;
|
||||
}
|
||||
|
||||
bool Bitmap::ResizeSubBitmap(int width, int height) {
|
||||
if (!isSubBitmap())
|
||||
return false;
|
||||
// TODO: can't clamp to parent size, because subs do not keep parent ref;
|
||||
// might require amending allegro bitmap struct
|
||||
_alBitmap->w = _alBitmap->cr = width;
|
||||
_alBitmap->h = _alBitmap->cb = height;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Bitmap::CreateCopy(Bitmap *src, int color_depth) {
|
||||
if (Create(src->_alBitmap->w, src->_alBitmap->h, color_depth ? color_depth : bitmap_color_depth(src->_alBitmap))) {
|
||||
blit(src->_alBitmap, _alBitmap, 0, 0, 0, 0, _alBitmap->w, _alBitmap->h);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Bitmap::WrapAllegroBitmap(BITMAP *al_bmp, bool shared_data) {
|
||||
Destroy();
|
||||
_alBitmap = al_bmp;
|
||||
_isDataOwner = !shared_data;
|
||||
return _alBitmap != nullptr;
|
||||
}
|
||||
|
||||
void Bitmap::Destroy() {
|
||||
if (_isDataOwner && _alBitmap) {
|
||||
destroy_bitmap(_alBitmap);
|
||||
}
|
||||
_alBitmap = nullptr;
|
||||
_isDataOwner = false;
|
||||
}
|
||||
|
||||
bool Bitmap::LoadFromFile(const char *filename) {
|
||||
Destroy();
|
||||
|
||||
BITMAP *al_bmp = load_bitmap(filename, nullptr);
|
||||
if (al_bmp) {
|
||||
_alBitmap = al_bmp;
|
||||
_isDataOwner = true;
|
||||
}
|
||||
return _alBitmap != nullptr;
|
||||
}
|
||||
|
||||
bool Bitmap::LoadFromFile(PACKFILE *pf) {
|
||||
Destroy();
|
||||
|
||||
BITMAP *al_bmp = load_bitmap(pf, nullptr);
|
||||
if (al_bmp) {
|
||||
_alBitmap = al_bmp;
|
||||
_isDataOwner = true;
|
||||
}
|
||||
return _alBitmap != nullptr;
|
||||
}
|
||||
|
||||
bool Bitmap::SaveToFile(Common::WriteStream &out, const void *palette) {
|
||||
return save_bitmap(out, _alBitmap, (const RGB *)palette);
|
||||
}
|
||||
|
||||
bool Bitmap::SaveToFile(const char *filename, const void *palette) {
|
||||
// Only keeps the file name and add the game target as prefix.
|
||||
Common::String name = filename;
|
||||
size_t lastSlash = name.findLastOf('/');
|
||||
if (lastSlash != Common::String::npos)
|
||||
name = name.substr(lastSlash + 1);
|
||||
Common::String gameTarget = ConfMan.getActiveDomainName();
|
||||
if (!name.hasPrefixIgnoreCase(gameTarget))
|
||||
name = gameTarget + "-" + name;
|
||||
|
||||
Common::OutSaveFile *out = g_system->getSavefileManager()->openForSaving(name, false);
|
||||
assert(out);
|
||||
bool result = SaveToFile(*out, palette);
|
||||
out->finalize();
|
||||
delete out;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
color_t Bitmap::GetCompatibleColor(color_t color) {
|
||||
color_t compat_color = 0;
|
||||
__my_setcolor(&compat_color, color, bitmap_color_depth(_alBitmap));
|
||||
return compat_color;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Clipping
|
||||
//=============================================================================
|
||||
|
||||
void Bitmap::SetClip(const Rect &rc) {
|
||||
set_clip_rect(_alBitmap, rc.Left, rc.Top, rc.Right, rc.Bottom);
|
||||
}
|
||||
|
||||
void Bitmap::ResetClip() {
|
||||
set_clip_rect(_alBitmap, 0, 0, _alBitmap->w - 1, _alBitmap->h - 1);
|
||||
}
|
||||
|
||||
Rect Bitmap::GetClip() const {
|
||||
Rect temp;
|
||||
get_clip_rect(_alBitmap, &temp.Left, &temp.Top, &temp.Right, &temp.Bottom);
|
||||
return temp;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Blitting operations (drawing one bitmap over another)
|
||||
//=============================================================================
|
||||
|
||||
void Bitmap::Blit(Bitmap *src, int dst_x, int dst_y, BitmapMaskOption mask) {
|
||||
BITMAP *al_src_bmp = src->_alBitmap;
|
||||
// WARNING: For some evil reason Allegro expects dest and src bitmaps in different order for blit and draw_sprite
|
||||
if (mask == kBitmap_Transparency) {
|
||||
draw_sprite(_alBitmap, al_src_bmp, dst_x, dst_y);
|
||||
} else {
|
||||
blit(al_src_bmp, _alBitmap, 0, 0, dst_x, dst_y, al_src_bmp->w, al_src_bmp->h);
|
||||
}
|
||||
}
|
||||
|
||||
void Bitmap::Blit(Bitmap *src, int src_x, int src_y, int dst_x, int dst_y, int width, int height, BitmapMaskOption mask) {
|
||||
BITMAP *al_src_bmp = src->_alBitmap;
|
||||
if (mask == kBitmap_Transparency) {
|
||||
masked_blit(al_src_bmp, _alBitmap, src_x, src_y, dst_x, dst_y, width, height);
|
||||
} else {
|
||||
blit(al_src_bmp, _alBitmap, src_x, src_y, dst_x, dst_y, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
void Bitmap::MaskedBlit(Bitmap *src, int dst_x, int dst_y) {
|
||||
draw_sprite(_alBitmap, src->_alBitmap, dst_x, dst_y);
|
||||
}
|
||||
|
||||
void Bitmap::StretchBlt(Bitmap *src, const Rect &dst_rc, BitmapMaskOption mask) {
|
||||
BITMAP *al_src_bmp = src->_alBitmap;
|
||||
// WARNING: For some evil reason Allegro expects dest and src bitmaps in different order for blit and draw_sprite
|
||||
if (mask == kBitmap_Transparency) {
|
||||
stretch_sprite(_alBitmap, al_src_bmp,
|
||||
dst_rc.Left, dst_rc.Top, dst_rc.GetWidth(), dst_rc.GetHeight());
|
||||
} else {
|
||||
stretch_blit(al_src_bmp, _alBitmap,
|
||||
0, 0, al_src_bmp->w, al_src_bmp->h,
|
||||
dst_rc.Left, dst_rc.Top, dst_rc.GetWidth(), dst_rc.GetHeight());
|
||||
}
|
||||
}
|
||||
|
||||
void Bitmap::StretchBlt(Bitmap *src, const Rect &src_rc, const Rect &dst_rc, BitmapMaskOption mask) {
|
||||
BITMAP *al_src_bmp = src->_alBitmap;
|
||||
if (mask == kBitmap_Transparency) {
|
||||
masked_stretch_blit(al_src_bmp, _alBitmap,
|
||||
src_rc.Left, src_rc.Top, src_rc.GetWidth(), src_rc.GetHeight(),
|
||||
dst_rc.Left, dst_rc.Top, dst_rc.GetWidth(), dst_rc.GetHeight());
|
||||
} else {
|
||||
stretch_blit(al_src_bmp, _alBitmap,
|
||||
src_rc.Left, src_rc.Top, src_rc.GetWidth(), src_rc.GetHeight(),
|
||||
dst_rc.Left, dst_rc.Top, dst_rc.GetWidth(), dst_rc.GetHeight());
|
||||
}
|
||||
}
|
||||
|
||||
void Bitmap::AAStretchBlt(Bitmap *src, const Rect &dst_rc, BitmapMaskOption mask) {
|
||||
BITMAP *al_src_bmp = src->_alBitmap;
|
||||
// WARNING: For some evil reason Allegro expects dest and src bitmaps in different order for blit and draw_sprite
|
||||
if (mask == kBitmap_Transparency) {
|
||||
aa_stretch_sprite(_alBitmap, al_src_bmp,
|
||||
dst_rc.Left, dst_rc.Top, dst_rc.GetWidth(), dst_rc.GetHeight());
|
||||
} else {
|
||||
aa_stretch_blit(al_src_bmp, _alBitmap,
|
||||
0, 0, al_src_bmp->w, al_src_bmp->h,
|
||||
dst_rc.Left, dst_rc.Top, dst_rc.GetWidth(), dst_rc.GetHeight());
|
||||
}
|
||||
}
|
||||
|
||||
void Bitmap::AAStretchBlt(Bitmap *src, const Rect &src_rc, const Rect &dst_rc, BitmapMaskOption mask) {
|
||||
BITMAP *al_src_bmp = src->_alBitmap;
|
||||
if (mask == kBitmap_Transparency) {
|
||||
// TODO: aastr lib does not expose method for masked stretch blit; should do that at some point since
|
||||
// the source code is a gift-ware anyway
|
||||
// aa_masked_blit(_alBitmap, al_src_bmp, src_rc.Left, src_rc.Top, src_rc.GetWidth(), src_rc.GetHeight(), dst_rc.Left, dst_rc.Top, dst_rc.GetWidth(), dst_rc.GetHeight());
|
||||
error("aa_masked_blit is not yet supported!");
|
||||
} else {
|
||||
aa_stretch_blit(al_src_bmp, _alBitmap,
|
||||
src_rc.Left, src_rc.Top, src_rc.GetWidth(), src_rc.GetHeight(),
|
||||
dst_rc.Left, dst_rc.Top, dst_rc.GetWidth(), dst_rc.GetHeight());
|
||||
}
|
||||
}
|
||||
|
||||
void Bitmap::TransBlendBlt(Bitmap *src, int dst_x, int dst_y) {
|
||||
BITMAP *al_src_bmp = src->_alBitmap;
|
||||
draw_trans_sprite(_alBitmap, al_src_bmp, dst_x, dst_y);
|
||||
}
|
||||
|
||||
void Bitmap::LitBlendBlt(Bitmap *src, int dst_x, int dst_y, int light_amount) {
|
||||
BITMAP *al_src_bmp = src->_alBitmap;
|
||||
draw_lit_sprite(_alBitmap, al_src_bmp, dst_x, dst_y, light_amount);
|
||||
}
|
||||
|
||||
void Bitmap::FlipBlt(Bitmap *src, int dst_x, int dst_y, GraphicFlip flip) {
|
||||
BITMAP *al_src_bmp = src->_alBitmap;
|
||||
switch (flip) {
|
||||
case kFlip_Horizontal:
|
||||
draw_sprite_h_flip(_alBitmap, al_src_bmp, dst_x, dst_y);
|
||||
break;
|
||||
case kFlip_Vertical:
|
||||
draw_sprite_v_flip(_alBitmap, al_src_bmp, dst_x, dst_y);
|
||||
break;
|
||||
case kFlip_Both:
|
||||
draw_sprite_vh_flip(_alBitmap, al_src_bmp, dst_x, dst_y);
|
||||
break;
|
||||
default: // blit with no transform
|
||||
Blit(src, dst_x, dst_y);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Bitmap::RotateBlt(Bitmap *src, int dst_x, int dst_y, fixed_t angle) {
|
||||
BITMAP *al_src_bmp = src->_alBitmap;
|
||||
rotate_sprite(_alBitmap, al_src_bmp, dst_x, dst_y, angle);
|
||||
}
|
||||
|
||||
void Bitmap::RotateBlt(Bitmap *src, int dst_x, int dst_y, int pivot_x, int pivot_y, fixed_t angle) {
|
||||
BITMAP *al_src_bmp = src->_alBitmap;
|
||||
pivot_sprite(_alBitmap, al_src_bmp, dst_x, dst_y, pivot_x, pivot_y, angle);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Pixel operations
|
||||
//=============================================================================
|
||||
|
||||
void Bitmap::Clear(color_t color) {
|
||||
if (color) {
|
||||
clear_to_color(_alBitmap, color);
|
||||
} else {
|
||||
clear_bitmap(_alBitmap);
|
||||
}
|
||||
}
|
||||
|
||||
void Bitmap::ClearTransparent() {
|
||||
clear_to_color(_alBitmap, bitmap_mask_color(_alBitmap));
|
||||
}
|
||||
|
||||
void Bitmap::PutPixel(int x, int y, color_t color) {
|
||||
if (x < 0 || x >= _alBitmap->w || y < 0 || y >= _alBitmap->h) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (bitmap_color_depth(_alBitmap)) {
|
||||
case 8:
|
||||
return _putpixel(_alBitmap, x, y, color);
|
||||
case 15:
|
||||
return _putpixel15(_alBitmap, x, y, color);
|
||||
case 16:
|
||||
return _putpixel16(_alBitmap, x, y, color);
|
||||
case 24:
|
||||
return _putpixel24(_alBitmap, x, y, color);
|
||||
case 32:
|
||||
return _putpixel32(_alBitmap, x, y, color);
|
||||
}
|
||||
assert(0); // this should not normally happen
|
||||
return putpixel(_alBitmap, x, y, color);
|
||||
}
|
||||
|
||||
int Bitmap::GetPixel(int x, int y) const {
|
||||
if (x < 0 || x >= _alBitmap->w || y < 0 || y >= _alBitmap->h) {
|
||||
return -1; // Allegros getpixel() implementation returns -1 in this case
|
||||
}
|
||||
|
||||
switch (bitmap_color_depth(_alBitmap)) {
|
||||
case 8:
|
||||
return _getpixel(_alBitmap, x, y);
|
||||
case 15:
|
||||
return _getpixel15(_alBitmap, x, y);
|
||||
case 16:
|
||||
return _getpixel16(_alBitmap, x, y);
|
||||
case 24:
|
||||
return _getpixel24(_alBitmap, x, y);
|
||||
case 32:
|
||||
return _getpixel32(_alBitmap, x, y);
|
||||
}
|
||||
assert(0); // this should not normally happen
|
||||
return getpixel(_alBitmap, x, y);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Vector drawing operations
|
||||
//=============================================================================
|
||||
|
||||
void Bitmap::DrawLine(const Line &ln, color_t color) {
|
||||
line(_alBitmap, ln.X1, ln.Y1, ln.X2, ln.Y2, color);
|
||||
}
|
||||
|
||||
void Bitmap::DrawTriangle(const Triangle &tr, color_t color) {
|
||||
triangle(_alBitmap,
|
||||
tr.X1, tr.Y1, tr.X2, tr.Y2, tr.X3, tr.Y3, color);
|
||||
}
|
||||
|
||||
void Bitmap::DrawRect(const Rect &rc, color_t color) {
|
||||
rect(_alBitmap, rc.Left, rc.Top, rc.Right, rc.Bottom, color);
|
||||
}
|
||||
|
||||
void Bitmap::FillRect(const Rect &rc, color_t color) {
|
||||
rectfill(_alBitmap, rc.Left, rc.Top, rc.Right, rc.Bottom, color);
|
||||
}
|
||||
|
||||
void Bitmap::FillCircle(const Circle &circle, color_t color) {
|
||||
circlefill(_alBitmap, circle.X, circle.Y, circle.Radius, color);
|
||||
}
|
||||
|
||||
void Bitmap::Fill(color_t color) {
|
||||
if (color) {
|
||||
clear_to_color(_alBitmap, color);
|
||||
} else {
|
||||
clear_bitmap(_alBitmap);
|
||||
}
|
||||
}
|
||||
|
||||
void Bitmap::FillTransparent() {
|
||||
clear_to_color(_alBitmap, bitmap_mask_color(_alBitmap));
|
||||
}
|
||||
|
||||
void Bitmap::FloodFill(int x, int y, color_t color) {
|
||||
_alBitmap->floodfill(x, y, color);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Direct access operations
|
||||
//=============================================================================
|
||||
|
||||
void Bitmap::SetScanLine(int index, unsigned char *data, int data_size) {
|
||||
if (index < 0 || index >= GetHeight()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int copy_length = data_size;
|
||||
if (copy_length < 0) {
|
||||
copy_length = GetLineLength();
|
||||
} else // TODO: use Math namespace here
|
||||
if (copy_length > GetLineLength()) {
|
||||
copy_length = GetLineLength();
|
||||
}
|
||||
|
||||
memcpy(_alBitmap->line[index], data, copy_length);
|
||||
}
|
||||
|
||||
namespace BitmapHelper {
|
||||
|
||||
Bitmap *CreateRawBitmapOwner(BITMAP *al_bmp) {
|
||||
Bitmap *bitmap = new Bitmap();
|
||||
if (!bitmap->WrapAllegroBitmap(al_bmp, false)) {
|
||||
delete bitmap;
|
||||
bitmap = nullptr;
|
||||
}
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
Bitmap *CreateRawBitmapWrapper(BITMAP *al_bmp) {
|
||||
Bitmap *bitmap = new Bitmap();
|
||||
if (!bitmap->WrapAllegroBitmap(al_bmp, true)) {
|
||||
delete bitmap;
|
||||
bitmap = nullptr;
|
||||
}
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
} // namespace BitmapHelper
|
||||
|
||||
|
||||
} // namespace Shared
|
||||
} // namespace AGS
|
||||
} // namespace AGS3
|
||||
277
engines/ags/shared/gfx/allegro_bitmap.h
Normal file
277
engines/ags/shared/gfx/allegro_bitmap.h
Normal file
@@ -0,0 +1,277 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// Allegro lib based bitmap
|
||||
//
|
||||
// TODO: probably should be moved to the Engine; check again when (if) it is
|
||||
// clear that AGS.Native does not need allegro for drawing.
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef AGS_SHARED_GFX_ALLEGRO_BITMAP_H
|
||||
#define AGS_SHARED_GFX_ALLEGRO_BITMAP_H
|
||||
|
||||
#include "graphics/screen.h"
|
||||
#include "ags/lib/allegro.h" // BITMAP
|
||||
#include "ags/shared/core/types.h"
|
||||
#include "ags/shared/gfx/bitmap.h"
|
||||
#include "ags/shared/util/string.h"
|
||||
|
||||
namespace AGS3 {
|
||||
namespace AGS {
|
||||
namespace Shared {
|
||||
|
||||
class Bitmap {
|
||||
public:
|
||||
Bitmap();
|
||||
Bitmap(int width, int height, int color_depth = 0);
|
||||
Bitmap(Bitmap *src, const Rect &rc);
|
||||
Bitmap(BITMAP *al_bmp, bool shared_data);
|
||||
~Bitmap();
|
||||
|
||||
// Allocate new bitmap.
|
||||
// NOTE: color_depth is in BITS per pixel (i.e. 8, 16, 24, 32...).
|
||||
// NOTE: in all of these color_depth may be passed as 0 in which case a default
|
||||
// color depth will be used (as previously set for the system).
|
||||
// TODO: color_depth = 0 is used to call Allegro's create_bitmap, which uses
|
||||
// some global color depth setting; not sure if this is OK to use for generic class,
|
||||
// revise this in future
|
||||
bool Create(int width, int height, int color_depth = 0);
|
||||
// Create Bitmap and clear to transparent color
|
||||
bool CreateTransparent(int width, int height, int color_depth = 0);
|
||||
// Creates a sub-bitmap of the given bitmap; the sub-bitmap is a reference to
|
||||
// particular region inside a parent.
|
||||
// WARNING: the parent bitmap MUST be kept in memory for as long as sub-bitmap exists!
|
||||
bool CreateSubBitmap(Bitmap *src, const Rect &rc);
|
||||
// Resizes existing sub-bitmap within the borders of its parent
|
||||
bool ResizeSubBitmap(int width, int height);
|
||||
// Creates a plain copy of the given bitmap, optionally converting to a different color depth;
|
||||
// pass color depth 0 to keep the original one.
|
||||
bool CreateCopy(Bitmap *src, int color_depth = 0);
|
||||
// TODO: this is a temporary solution for plugin support
|
||||
// Wraps a raw allegro BITMAP object, optionally owns it (will delete on disposal)
|
||||
bool WrapAllegroBitmap(BITMAP *al_bmp, bool shared_data);
|
||||
// Deallocate bitmap
|
||||
void Destroy();
|
||||
|
||||
bool LoadFromFile(const String &filename) {
|
||||
return LoadFromFile(filename.GetCStr());
|
||||
}
|
||||
bool LoadFromFile(const char *filename);
|
||||
bool LoadFromFile(PACKFILE *pf);
|
||||
bool SaveToFile(const String &filename, const void *palette) {
|
||||
return SaveToFile(filename.GetCStr(), palette);
|
||||
}
|
||||
bool SaveToFile(Common::WriteStream &out, const void *palette);
|
||||
bool SaveToFile(const char *filename, const void *palette);
|
||||
|
||||
// TODO: This is temporary solution for cases when we cannot replace
|
||||
// use of raw BITMAP struct with Bitmap
|
||||
inline BITMAP *GetAllegroBitmap() {
|
||||
return _alBitmap;
|
||||
}
|
||||
|
||||
// Is this a "normal" bitmap created by application which data can be directly accessed for reading and writing
|
||||
inline bool IsMemoryBitmap() const {
|
||||
return true;
|
||||
}
|
||||
// Is this a video bitmap
|
||||
inline bool IsVideoBitmap() const {
|
||||
return dynamic_cast<Graphics::Screen *>(_alBitmap) != nullptr;
|
||||
}
|
||||
// Is this a linear bitmap, the one that can be accessed linearly within each scanline
|
||||
inline bool IsLinearBitmap() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Is this a subbitmap, referencing a part of another, bigger one?
|
||||
inline bool isSubBitmap() const {
|
||||
return _alBitmap->isSubBitmap();
|
||||
}
|
||||
|
||||
// Do both bitmaps share same data (usually: subbitmaps, or parent/subbitmap)
|
||||
inline bool IsSameBitmap(Bitmap *other) const {
|
||||
return is_same_bitmap(_alBitmap, other->_alBitmap) != 0;
|
||||
}
|
||||
|
||||
// Checks if bitmap cannot be used
|
||||
inline bool IsNull() const {
|
||||
return !_alBitmap;
|
||||
}
|
||||
// Checks if bitmap has zero size: either width or height (or both) is zero
|
||||
inline bool IsEmpty() const {
|
||||
return GetWidth() == 0 || GetHeight() == 0;
|
||||
}
|
||||
inline int GetWidth() const {
|
||||
return _alBitmap->w;
|
||||
}
|
||||
inline int GetHeight() const {
|
||||
return _alBitmap->h;
|
||||
}
|
||||
inline Size GetSize() const {
|
||||
return Size(_alBitmap->w, _alBitmap->h);
|
||||
}
|
||||
// Get sub-bitmap's offset position within its parent
|
||||
inline Point GetSubOffset() const {
|
||||
Common::Point pt = _alBitmap->getOffsetFromOwner();
|
||||
return Point(pt.x, pt.y);
|
||||
}
|
||||
inline int GetColorDepth() const {
|
||||
return bitmap_color_depth(_alBitmap);
|
||||
}
|
||||
// BPP: bytes per pixel
|
||||
inline int GetBPP() const {
|
||||
return (GetColorDepth() + 7) / 8;
|
||||
}
|
||||
|
||||
// CHECKME: probably should not be exposed, see comment to GetData()
|
||||
inline int GetDataSize() const {
|
||||
return GetWidth() * GetHeight() * GetBPP();
|
||||
}
|
||||
// Gets scanline length in bytes (is the same for any scanline)
|
||||
inline int GetLineLength() const {
|
||||
return GetWidth() * GetBPP();
|
||||
}
|
||||
|
||||
// TODO: replace with byte *
|
||||
// Gets a pointer to underlying graphic data
|
||||
// FIXME: actually not a very good idea, since there's no 100% guarantee the scanline positions in memory are sequential
|
||||
inline const unsigned char *GetData() const {
|
||||
return _alBitmap->getPixels();
|
||||
}
|
||||
|
||||
// Get scanline for direct reading
|
||||
inline const unsigned char *GetScanLine(int index) const {
|
||||
assert(index >= 0 && index < GetHeight());
|
||||
return _alBitmap->getBasePtr(0, index);
|
||||
}
|
||||
inline unsigned char *GetScanLine(int index) {
|
||||
assert(index >= 0 && index < GetHeight());
|
||||
return (unsigned char *)_alBitmap->getBasePtr(0, index);
|
||||
}
|
||||
|
||||
// Get bitmap's mask color (transparent color)
|
||||
inline color_t GetMaskColor() const {
|
||||
return bitmap_mask_color(_alBitmap);
|
||||
}
|
||||
|
||||
// Converts AGS color-index into RGB color according to the bitmap format.
|
||||
// TODO: this method was added to the Bitmap class during large refactoring,
|
||||
// but that's a mistake, because in retrospect is has nothing to do with
|
||||
// bitmap itself and should rather be a part of the game data logic.
|
||||
color_t GetCompatibleColor(color_t color);
|
||||
|
||||
//=========================================================================
|
||||
// Clipping
|
||||
// TODO: consider implementing push-pop clipping stack logic.
|
||||
//=========================================================================
|
||||
void SetClip(const Rect &rc);
|
||||
void ResetClip();
|
||||
Rect GetClip() const;
|
||||
|
||||
//=========================================================================
|
||||
// Blitting operations (drawing one bitmap over another)
|
||||
//=========================================================================
|
||||
// Draw other bitmap over current one
|
||||
void Blit(Bitmap *src, int dst_x = 0, int dst_y = 0, BitmapMaskOption mask = kBitmap_Copy);
|
||||
void Blit(Bitmap *src, int src_x, int src_y, int dst_x, int dst_y, int width, int height, BitmapMaskOption mask = kBitmap_Copy);
|
||||
// Draw other bitmap in a masked mode (kBitmap_Transparency)
|
||||
void MaskedBlit(Bitmap *src, int dst_x, int dst_y);
|
||||
// Draw other bitmap, stretching or shrinking its size to given values
|
||||
void StretchBlt(Bitmap *src, const Rect &dst_rc, BitmapMaskOption mask = kBitmap_Copy);
|
||||
void StretchBlt(Bitmap *src, const Rect &src_rc, const Rect &dst_rc, BitmapMaskOption mask = kBitmap_Copy);
|
||||
// Antia-aliased stretch-blit
|
||||
void AAStretchBlt(Bitmap *src, const Rect &dst_rc, BitmapMaskOption mask = kBitmap_Copy);
|
||||
void AAStretchBlt(Bitmap *src, const Rect &src_rc, const Rect &dst_rc, BitmapMaskOption mask = kBitmap_Copy);
|
||||
// TODO: find more general way to call these operations, probably require pointer to Blending data struct?
|
||||
// Draw bitmap using translucency preset
|
||||
void TransBlendBlt(Bitmap *src, int dst_x, int dst_y);
|
||||
// Draw bitmap using lighting preset
|
||||
void LitBlendBlt(Bitmap *src, int dst_x, int dst_y, int light_amount);
|
||||
// TODO: generic "draw transformed" function? What about mask option?
|
||||
void FlipBlt(Bitmap *src, int dst_x, int dst_y, GraphicFlip flip);
|
||||
void RotateBlt(Bitmap *src, int dst_x, int dst_y, fixed_t angle);
|
||||
void RotateBlt(Bitmap *src, int dst_x, int dst_y, int pivot_x, int pivot_y, fixed_t angle);
|
||||
|
||||
//=========================================================================
|
||||
// Pixel operations
|
||||
//=========================================================================
|
||||
// Fills the whole bitmap with given color (black by default)
|
||||
void Clear(color_t color = 0);
|
||||
void ClearTransparent();
|
||||
// The PutPixel and GetPixel are supposed to be safe and therefore
|
||||
// relatively slow operations. They should not be used for changing large
|
||||
// blocks of bitmap memory - reading/writing from/to scan lines should be
|
||||
// done in such cases.
|
||||
void PutPixel(int x, int y, color_t color);
|
||||
int GetPixel(int x, int y) const;
|
||||
|
||||
//=========================================================================
|
||||
// Vector drawing operations
|
||||
//=========================================================================
|
||||
void DrawLine(const Line &ln, color_t color);
|
||||
void DrawTriangle(const Triangle &tr, color_t color);
|
||||
void DrawRect(const Rect &rc, color_t color);
|
||||
void FillRect(const Rect &rc, color_t color);
|
||||
void FillCircle(const Circle &circle, color_t color);
|
||||
// Fills the whole bitmap with given color
|
||||
void Fill(color_t color);
|
||||
void FillTransparent();
|
||||
// Floodfills an enclosed area, starting at point
|
||||
void FloodFill(int x, int y, color_t color);
|
||||
|
||||
//=========================================================================
|
||||
// Direct access operations
|
||||
//=========================================================================
|
||||
// TODO: think how to increase safety over this (some fixed memory buffer class with iterator?)
|
||||
// Gets scanline for directly writing into it
|
||||
inline unsigned char *GetScanLineForWriting(int index) {
|
||||
assert(index >= 0 && index < GetHeight());
|
||||
return _alBitmap->line[index];
|
||||
}
|
||||
inline unsigned char *GetDataForWriting() {
|
||||
return _alBitmap->line[0];
|
||||
}
|
||||
// Copies buffer contents into scanline
|
||||
void SetScanLine(int index, unsigned char *data, int data_size = -1);
|
||||
|
||||
private:
|
||||
BITMAP *_alBitmap;
|
||||
bool _isDataOwner;
|
||||
};
|
||||
|
||||
|
||||
|
||||
namespace BitmapHelper {
|
||||
// TODO: revise those functions later (currently needed in a few very specific cases)
|
||||
// NOTE: the resulting object __owns__ bitmap data from now on
|
||||
Bitmap *CreateRawBitmapOwner(BITMAP *al_bmp);
|
||||
// NOTE: the resulting object __does not own__ bitmap data
|
||||
Bitmap *CreateRawBitmapWrapper(BITMAP *al_bmp);
|
||||
} // namespace BitmapHelper
|
||||
|
||||
} // namespace Shared
|
||||
} // namespace AGS
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
240
engines/ags/shared/gfx/bitmap.cpp
Normal file
240
engines/ags/shared/gfx/bitmap.cpp
Normal file
@@ -0,0 +1,240 @@
|
||||
/* 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 "ags/shared/gfx/bitmap.h"
|
||||
#include "ags/shared/util/memory.h"
|
||||
|
||||
namespace AGS3 {
|
||||
namespace AGS {
|
||||
namespace Shared {
|
||||
|
||||
// TODO: revise this construction later
|
||||
namespace BitmapHelper {
|
||||
|
||||
Bitmap *CreateBitmap(int width, int height, int color_depth) {
|
||||
Bitmap *bitmap = new Bitmap();
|
||||
if (!bitmap->Create(width, height, color_depth)) {
|
||||
delete bitmap;
|
||||
bitmap = nullptr;
|
||||
}
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
Bitmap *CreateClearBitmap(int width, int height, int color_depth, int clear_color) {
|
||||
Bitmap *bitmap = new Bitmap();
|
||||
if (!bitmap->Create(width, height, color_depth)) {
|
||||
delete bitmap;
|
||||
return nullptr;
|
||||
}
|
||||
bitmap->Clear(clear_color);
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
Bitmap *CreateTransparentBitmap(int width, int height, int color_depth) {
|
||||
Bitmap *bitmap = new Bitmap();
|
||||
if (!bitmap->CreateTransparent(width, height, color_depth)) {
|
||||
delete bitmap;
|
||||
bitmap = nullptr;
|
||||
}
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
Bitmap *CreateSubBitmap(Bitmap *src, const Rect &rc) {
|
||||
Bitmap *bitmap = new Bitmap();
|
||||
if (!bitmap->CreateSubBitmap(src, rc)) {
|
||||
delete bitmap;
|
||||
bitmap = nullptr;
|
||||
}
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
Bitmap *CreateBitmapCopy(Bitmap *src, int color_depth) {
|
||||
Bitmap *bitmap = new Bitmap();
|
||||
if (!bitmap->CreateCopy(src, color_depth)) {
|
||||
delete bitmap;
|
||||
bitmap = nullptr;
|
||||
}
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
Bitmap *LoadFromFile(const char *filename) {
|
||||
Bitmap *bitmap = new Bitmap();
|
||||
if (!bitmap->LoadFromFile(filename)) {
|
||||
delete bitmap;
|
||||
bitmap = nullptr;
|
||||
}
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
Bitmap *LoadFromFile(PACKFILE *pf) {
|
||||
Bitmap *bitmap = new Bitmap();
|
||||
if (!bitmap->LoadFromFile(pf)) {
|
||||
delete bitmap;
|
||||
bitmap = nullptr;
|
||||
}
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
Bitmap *AdjustBitmapSize(Bitmap *src, int width, int height) {
|
||||
int oldw = src->GetWidth(), oldh = src->GetHeight();
|
||||
if ((oldw == width) && (oldh == height))
|
||||
return src;
|
||||
Bitmap *bmp = BitmapHelper::CreateBitmap(width, height, src->GetColorDepth());
|
||||
bmp->StretchBlt(src, RectWH(0, 0, oldw, oldh), RectWH(0, 0, width, height));
|
||||
return bmp;
|
||||
}
|
||||
|
||||
void MakeOpaque(Bitmap *bmp) {
|
||||
if (bmp->GetColorDepth() < 32)
|
||||
return; // no alpha channel
|
||||
|
||||
for (int i = 0; i < bmp->GetHeight(); ++i) {
|
||||
uint32_t *line = reinterpret_cast<uint32_t *>(bmp->GetScanLineForWriting(i));
|
||||
uint32_t *line_end = line + bmp->GetWidth();
|
||||
for (uint32_t *px = line; px != line_end; ++px)
|
||||
*px = makeacol32(getr32(*px), getg32(*px), getb32(*px), 255);
|
||||
}
|
||||
}
|
||||
|
||||
void MakeOpaqueSkipMask(Bitmap *bmp) {
|
||||
if (bmp->GetColorDepth() < 32)
|
||||
return; // no alpha channel
|
||||
|
||||
for (int i = 0; i < bmp->GetHeight(); ++i) {
|
||||
uint32_t *line = reinterpret_cast<uint32_t *>(bmp->GetScanLineForWriting(i));
|
||||
uint32_t *line_end = line + bmp->GetWidth();
|
||||
for (uint32_t *px = line; px != line_end; ++px)
|
||||
if (*px != MASK_COLOR_32)
|
||||
*px = makeacol32(getr32(*px), getg32(*px), getb32(*px), 255);
|
||||
}
|
||||
}
|
||||
|
||||
void ReplaceAlphaWithRGBMask(Bitmap *bmp) {
|
||||
if (bmp->GetColorDepth() < 32)
|
||||
return; // no alpha channel
|
||||
|
||||
for (int i = 0; i < bmp->GetHeight(); ++i) {
|
||||
uint32_t *line = reinterpret_cast<uint32_t *>(bmp->GetScanLineForWriting(i));
|
||||
uint32_t *line_end = line + bmp->GetWidth();
|
||||
for (uint32_t *px = line; px != line_end; ++px)
|
||||
if (geta32(*px) == 0)
|
||||
*px = MASK_COLOR_32;
|
||||
}
|
||||
}
|
||||
|
||||
// Functor that copies the "mask color" pixels from source to dest
|
||||
template <class TPx, size_t BPP_>
|
||||
struct PixelTransCpy {
|
||||
static const size_t BPP = BPP_;
|
||||
inline void operator ()(uint8_t *dst, const uint8_t *src, uint32_t mask_color, bool /*use_alpha*/) const {
|
||||
if (*(const TPx *)src == mask_color)
|
||||
*(TPx *)dst = mask_color;
|
||||
}
|
||||
};
|
||||
|
||||
// Functor that tells to never skip a pixel in the mask
|
||||
struct PixelNoSkip {
|
||||
inline bool operator ()(uint8_t * /*data*/, uint32_t /*mask_color*/, bool /*use_alpha*/) const {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
typedef PixelTransCpy<uint8_t, 1> PixelTransCpy8;
|
||||
typedef PixelTransCpy<uint16_t, 2> PixelTransCpy16;
|
||||
|
||||
// Functor that copies the "mask color" pixels from source to dest, 24-bit depth
|
||||
struct PixelTransCpy24 {
|
||||
static const size_t BPP = 3;
|
||||
inline void operator ()(uint8_t *dst, const uint8_t *src, uint32_t mask_color, bool /*use_alpha*/) const {
|
||||
const uint8_t *mcol_ptr = (const uint8_t *)&mask_color;
|
||||
if (src[0] == mcol_ptr[0] && src[1] == mcol_ptr[1] && src[2] == mcol_ptr[2]) {
|
||||
dst[0] = mcol_ptr[0];
|
||||
dst[1] = mcol_ptr[1];
|
||||
dst[2] = mcol_ptr[2];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Functor that copies the "mask color" pixels from source to dest, 32-bit depth, with alpha
|
||||
struct PixelTransCpy32 {
|
||||
static const size_t BPP = 4;
|
||||
inline void operator ()(uint8_t *dst, const uint8_t *src, uint32_t mask_color, bool use_alpha) const {
|
||||
if (*(const uint32_t *)src == mask_color)
|
||||
*(uint32_t *)dst = mask_color;
|
||||
else if (use_alpha)
|
||||
dst[3] = src[3]; // copy alpha channel
|
||||
else
|
||||
dst[3] = 0xFF; // set the alpha channel byte to opaque
|
||||
}
|
||||
};
|
||||
|
||||
// Functor that tells to skip pixels if they match the mask color or have alpha = 0
|
||||
struct PixelTransSkip32 {
|
||||
inline bool operator ()(uint8_t *data, uint32_t mask_color, bool use_alpha) const {
|
||||
return *(const uint32_t *)data == mask_color || (use_alpha && data[3] == 0);
|
||||
}
|
||||
};
|
||||
|
||||
// Applies bitmap mask, using 2 functors:
|
||||
// - one that tells whether to skip current pixel;
|
||||
// - another that copies the color from src to dest
|
||||
template <class FnPxProc, class FnSkip>
|
||||
void ApplyMask(uint8_t *dst, const uint8_t *src, size_t pitch, size_t height,
|
||||
FnPxProc proc, FnSkip skip, uint32_t mask_color, bool dst_has_alpha, bool mask_has_alpha) {
|
||||
for (size_t y = 0; y < height; ++y) {
|
||||
for (size_t x = 0; x < pitch; x += FnPxProc::BPP, src += FnPxProc::BPP, dst += FnPxProc::BPP) {
|
||||
if (!skip(dst, mask_color, dst_has_alpha))
|
||||
proc(dst, src, mask_color, mask_has_alpha);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CopyTransparency(Bitmap *dst, const Bitmap *mask, bool dst_has_alpha, bool mask_has_alpha) {
|
||||
color_t mask_color = mask->GetMaskColor();
|
||||
uint8_t *dst_ptr = dst->GetDataForWriting();
|
||||
const uint8_t *src_ptr = mask->GetData();
|
||||
const size_t bpp = mask->GetBPP();
|
||||
const size_t pitch = mask->GetLineLength();
|
||||
const size_t height = mask->GetHeight();
|
||||
|
||||
if (bpp == 1)
|
||||
ApplyMask(dst_ptr, src_ptr, pitch, height, PixelTransCpy8(), PixelNoSkip(), mask_color, dst_has_alpha, mask_has_alpha);
|
||||
else if (bpp == 2)
|
||||
ApplyMask(dst_ptr, src_ptr, pitch, height, PixelTransCpy16(), PixelNoSkip(), mask_color, dst_has_alpha, mask_has_alpha);
|
||||
else if (bpp == 3)
|
||||
ApplyMask(dst_ptr, src_ptr, pitch, height, PixelTransCpy24(), PixelNoSkip(), mask_color, dst_has_alpha, mask_has_alpha);
|
||||
else
|
||||
ApplyMask(dst_ptr, src_ptr, pitch, height, PixelTransCpy32(), PixelTransSkip32(), mask_color, dst_has_alpha, mask_has_alpha);
|
||||
}
|
||||
|
||||
void ReadPixelsFromMemory(Bitmap *dst, const uint8_t *src_buffer, const size_t src_pitch, const size_t src_px_offset) {
|
||||
const size_t bpp = dst->GetBPP();
|
||||
const size_t src_px_pitch = src_pitch / bpp;
|
||||
if (src_px_offset >= src_px_pitch)
|
||||
return; // nothing to copy
|
||||
Memory::BlockCopy(dst->GetDataForWriting(), dst->GetLineLength(), 0, src_buffer, src_pitch, src_px_offset * bpp, dst->GetHeight());
|
||||
}
|
||||
|
||||
} // namespace BitmapHelper
|
||||
|
||||
} // namespace Shared
|
||||
} // namespace AGS
|
||||
} // namespace AGS3
|
||||
114
engines/ags/shared/gfx/bitmap.h
Normal file
114
engines/ags/shared/gfx/bitmap.h
Normal file
@@ -0,0 +1,114 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// Base bitmap header
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef AGS_SHARED_GFX_BITMAP_H
|
||||
#define AGS_SHARED_GFX_BITMAP_H
|
||||
|
||||
#include "ags/shared/gfx/gfx_def.h"
|
||||
#include "ags/shared/util/geometry.h"
|
||||
#include "ags/shared/util/string.h"
|
||||
|
||||
namespace AGS3 {
|
||||
namespace AGS {
|
||||
namespace Shared {
|
||||
|
||||
// Mask option for blitting one bitmap on another
|
||||
enum BitmapMaskOption {
|
||||
// Plain copies bitmap pixels
|
||||
kBitmap_Copy,
|
||||
// Consider mask color fully transparent and do not copy pixels having it
|
||||
kBitmap_Transparency
|
||||
};
|
||||
|
||||
} // namespace Shared
|
||||
} // namespace AGS
|
||||
} // namespace AGS3
|
||||
|
||||
#include "ags/shared/gfx/allegro_bitmap.h"
|
||||
|
||||
namespace AGS3 {
|
||||
namespace AGS {
|
||||
namespace Shared {
|
||||
|
||||
class Bitmap;
|
||||
|
||||
// TODO: revise this construction later
|
||||
namespace BitmapHelper {
|
||||
|
||||
// Helper functions, that delete faulty bitmaps automatically, and return
|
||||
// NULL if bitmap could not be created.
|
||||
// NOTE: color_depth is in BITS per pixel (i.e. 8, 16, 24, 32...).
|
||||
// NOTE: in all of these color_depth may be passed as 0 in which case a default
|
||||
// color depth will be used (as previously set for the system).
|
||||
// Creates a new bitmap of the given format; the pixel contents are undefined.
|
||||
Bitmap *CreateBitmap(int width, int height, int color_depth = 0);
|
||||
// Creates a new bitmap and clears it with the given color
|
||||
Bitmap *CreateClearBitmap(int width, int height, int color_depth = 0, int clear_color = 0);
|
||||
// Creates a new bitmap and clears it with the transparent color
|
||||
Bitmap *CreateTransparentBitmap(int width, int height, int color_depth = 0);
|
||||
// Creates a sub-bitmap of the given bitmap; the sub-bitmap is a reference to
|
||||
// particular region inside a parent.
|
||||
// WARNING: the parent bitmap MUST be kept in memory for as long as sub-bitmap exists!
|
||||
Bitmap *CreateSubBitmap(Bitmap *src, const Rect &rc);
|
||||
// Creates a plain copy of the given bitmap, optionally converting to a different color depth;
|
||||
// pass color depth 0 to keep the original one.
|
||||
Bitmap *CreateBitmapCopy(Bitmap *src, int color_depth = 0);
|
||||
|
||||
// Load a bitmap from file; supported formats currently are: BMP, PCX.
|
||||
Bitmap *LoadFromFile(const char *filename);
|
||||
inline Bitmap *LoadFromFile(const String &filename) {
|
||||
return LoadFromFile(filename.GetCStr());
|
||||
}
|
||||
Bitmap *LoadFromFile(PACKFILE *pf);
|
||||
|
||||
// Stretches bitmap to the requested size. The new bitmap will have same
|
||||
// colour depth. Returns original bitmap if no changes are necessary.
|
||||
Bitmap *AdjustBitmapSize(Bitmap *src, int width, int height);
|
||||
// Makes the given bitmap opaque (full alpha), while keeping pixel RGB unchanged.
|
||||
void MakeOpaque(Bitmap *bmp);
|
||||
// Makes the given bitmap opaque (full alpha), while keeping pixel RGB unchanged.
|
||||
// Skips mask color (leaves it with zero alpha).
|
||||
void MakeOpaqueSkipMask(Bitmap *bmp);
|
||||
// Replaces fully transparent (alpha = 0) pixels with standard mask color.
|
||||
void ReplaceAlphaWithRGBMask(Bitmap *bmp);
|
||||
// Copy transparency mask and/or alpha channel from one bitmap into another.
|
||||
// Destination and mask bitmaps must be of the same pixel format.
|
||||
// Transparency is merged, meaning that fully transparent pixels on
|
||||
// destination should remain such regardless of mask pixel values.
|
||||
void CopyTransparency(Bitmap *dst, const Bitmap *mask, bool dst_has_alpha, bool mask_has_alpha);
|
||||
// Copy pixel data into bitmap from memory buffer. It is required that the
|
||||
// source matches bitmap format and has enough data.
|
||||
// Pitch is given in bytes and defines the length of the source scan line.
|
||||
// Offset is optional and defines horizontal offset, in pixels.
|
||||
void ReadPixelsFromMemory(Bitmap *dst, const uint8_t *src_buffer, const size_t src_pitch, const size_t src_px_offset = 0);
|
||||
|
||||
} // namespace BitmapHelper
|
||||
} // namespace Shared
|
||||
} // namespace AGS
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
152
engines/ags/shared/gfx/gfx_def.h
Normal file
152
engines/ags/shared/gfx/gfx_def.h
Normal file
@@ -0,0 +1,152 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// Graphic definitions and type/unit conversions.
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef AGS_SHARED_GFX_GFX_DEF_H
|
||||
#define AGS_SHARED_GFX_GFX_DEF_H
|
||||
|
||||
namespace AGS3 {
|
||||
namespace AGS {
|
||||
namespace Shared {
|
||||
|
||||
enum GraphicFlip {
|
||||
kFlip_None,
|
||||
kFlip_Horizontal, // this means - mirror over horizontal middle line
|
||||
kFlip_Vertical, // this means - mirror over vertical middle line
|
||||
kFlip_Both // mirror over diagonal (horizontal and vertical)
|
||||
};
|
||||
|
||||
enum BlendMode {
|
||||
// free blending (ARGB -> ARGB) modes
|
||||
kBlendMode_NoAlpha = 0, // ignore alpha channel
|
||||
kBlendMode_Alpha, // alpha-blend src to dest, combining src & dest alphas
|
||||
// NOTE: add new modes here
|
||||
|
||||
kNumBlendModes
|
||||
};
|
||||
|
||||
namespace GfxDef {
|
||||
|
||||
// Converts percentage of transparency into alpha
|
||||
inline int Trans100ToAlpha255(int transparency) {
|
||||
return ((100 - transparency) * 255) / 100;
|
||||
}
|
||||
|
||||
// Converts alpha into percentage of transparency
|
||||
inline int Alpha255ToTrans100(int alpha) {
|
||||
return 100 - ((alpha * 100) / 255);
|
||||
}
|
||||
|
||||
// Special formulae to reduce precision loss and support flawless forth &
|
||||
// reverse conversion for multiplies of 10%
|
||||
inline int Trans100ToAlpha250(int transparency) {
|
||||
return ((100 - transparency) * 25) / 10;
|
||||
}
|
||||
|
||||
inline int Alpha250ToTrans100(int alpha) {
|
||||
return 100 - ((alpha * 10) / 25);
|
||||
}
|
||||
|
||||
// Convert correct 100-ranged transparency into legacy 255-ranged
|
||||
// transparency; legacy inconsistent transparency value range:
|
||||
// 0 = opaque,
|
||||
// 255 = invisible,
|
||||
// 1 -to- 254 = barely visible -to- mostly visible (as proper alpha)
|
||||
inline int Trans100ToLegacyTrans255(int transparency) {
|
||||
switch (transparency) {
|
||||
case 0:
|
||||
return 0; // this means opaque
|
||||
case 100:
|
||||
return 255; // this means invisible
|
||||
default:
|
||||
// the rest of the range works as alpha
|
||||
return Trans100ToAlpha250(transparency);
|
||||
}
|
||||
}
|
||||
|
||||
// Convert legacy 255-ranged "incorrect" transparency into proper
|
||||
// 100-ranged transparency.
|
||||
inline int LegacyTrans255ToTrans100(int legacy_transparency) {
|
||||
switch (legacy_transparency) {
|
||||
case 0:
|
||||
return 0; // this means opaque
|
||||
case 255:
|
||||
return 100; // this means invisible
|
||||
default:
|
||||
// the rest of the range works as alpha
|
||||
return Alpha250ToTrans100(legacy_transparency);
|
||||
}
|
||||
}
|
||||
|
||||
// Convert legacy 100-ranged transparency into proper 255-ranged alpha
|
||||
// 0 => alpha 255
|
||||
// 100 => alpha 0
|
||||
// 1 - 99 => alpha 1 - 244
|
||||
inline int LegacyTrans100ToAlpha255(int legacy_transparency) {
|
||||
switch (legacy_transparency) {
|
||||
case 0:
|
||||
return 255; // this means opaque
|
||||
case 100:
|
||||
return 0; // this means invisible
|
||||
default:
|
||||
// the rest of the range works as alpha (only 100-ranged)
|
||||
return legacy_transparency * 255 / 100;
|
||||
}
|
||||
}
|
||||
|
||||
// Convert legacy 255-ranged transparency into proper 255-ranged alpha
|
||||
inline int LegacyTrans255ToAlpha255(int legacy_transparency) {
|
||||
switch (legacy_transparency) {
|
||||
case 0:
|
||||
return 255; // this means opaque
|
||||
case 255:
|
||||
return 0; // this means invisible
|
||||
default:
|
||||
// the rest of the range works as alpha
|
||||
return legacy_transparency;
|
||||
}
|
||||
}
|
||||
|
||||
// Convert 255-ranged alpha into legacy 255-ranged transparency
|
||||
inline int Alpha255ToLegacyTrans255(int alpha) {
|
||||
switch (alpha) {
|
||||
case 255:
|
||||
return 0; // this means opaque
|
||||
case 0:
|
||||
return 255; // this means invisible
|
||||
default:
|
||||
// the rest of the range works as alpha
|
||||
return alpha;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace GfxDef
|
||||
|
||||
} // namespace Shared
|
||||
} // namespace AGS
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
151
engines/ags/shared/gfx/image.cpp
Normal file
151
engines/ags/shared/gfx/image.cpp
Normal 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ags/shared/gfx/image.h"
|
||||
#include "ags/shared/util/file.h"
|
||||
#include "ags/shared/util/stream.h"
|
||||
#include "ags/lib/allegro.h"
|
||||
#include "common/file.h"
|
||||
#include "common/str.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "image/bmp.h"
|
||||
#include "image/iff.h"
|
||||
#include "image/pcx.h"
|
||||
#include "image/tga.h"
|
||||
|
||||
#define VGA_COLOR_TRANS(x) ((x) * 255 / 63)
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
template<class DECODER>
|
||||
BITMAP *decodeImageStream(Common::SeekableReadStream &stream, color *pal) {
|
||||
DECODER decoder;
|
||||
|
||||
if (decoder.loadStream(stream)) {
|
||||
// Create the output surface
|
||||
const Graphics::Surface *src = decoder.getSurface();
|
||||
|
||||
// Copy the decoded surface
|
||||
int bpp = 8 * src->format.bytesPerPixel;
|
||||
if (bpp == 24)
|
||||
bpp = 32;
|
||||
Surface *dest = (Surface *)create_bitmap_ex(bpp, src->w, src->h);
|
||||
dest->blitFrom(*src);
|
||||
|
||||
// Copy the palette
|
||||
const Graphics::Palette &palP = decoder.getPalette();
|
||||
if (pal) {
|
||||
for (uint idx = 0; idx < palP.size(); ++idx) {
|
||||
palP.get(idx, pal[idx].r, pal[idx].g, pal[idx].b);
|
||||
pal[idx].filler = 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
return dest;
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
template<class DECODER>
|
||||
BITMAP *decodeImage(const char *filename, color *pal) {
|
||||
AGS::Shared::Stream *file = AGS3::AGS::Shared::File::OpenFileRead(filename);
|
||||
if (!file)
|
||||
return nullptr;
|
||||
|
||||
AGS::Shared::ScummVMReadStream f(file);
|
||||
return decodeImageStream<DECODER>(f, pal);
|
||||
}
|
||||
|
||||
template<class DECODER>
|
||||
BITMAP *decodeImage(PACKFILE *pf, color *pal) {
|
||||
if (!pf)
|
||||
return nullptr;
|
||||
|
||||
AGS::Shared::ScummVMPackReadStream f(pf);
|
||||
f.seek(0);
|
||||
return decodeImageStream<DECODER>(f, pal);
|
||||
}
|
||||
|
||||
BITMAP *load_bmp(const char *filename, color *pal) {
|
||||
return decodeImage<Image::BitmapDecoder>(filename, pal);
|
||||
}
|
||||
|
||||
BITMAP *load_lbm(const char *filename, color *pal) {
|
||||
return decodeImage<Image::IFFDecoder>(filename, pal);
|
||||
}
|
||||
|
||||
BITMAP *load_pcx(const char *filename, color *pal) {
|
||||
return decodeImage<Image::PCXDecoder>(filename, pal);
|
||||
}
|
||||
|
||||
BITMAP *load_tga(const char *filename, color *pal) {
|
||||
return decodeImage<Image::TGADecoder>(filename, pal);
|
||||
}
|
||||
|
||||
BITMAP *load_bitmap(const char *filename, color *pal) {
|
||||
Common::String fname(filename);
|
||||
|
||||
if (fname.hasSuffixIgnoreCase(".bmp"))
|
||||
return load_bmp(filename, pal);
|
||||
else if (fname.hasSuffixIgnoreCase(".lbm"))
|
||||
return load_lbm(filename, pal);
|
||||
else if (fname.hasSuffixIgnoreCase(".pcx"))
|
||||
return load_pcx(filename, pal);
|
||||
else if (fname.hasSuffixIgnoreCase(".tga"))
|
||||
return load_tga(filename, pal);
|
||||
else
|
||||
error("Unknown image file - %s", filename);
|
||||
}
|
||||
|
||||
BITMAP *load_bitmap(PACKFILE *pf, color *pal) {
|
||||
BITMAP *result;
|
||||
|
||||
if ((result = decodeImage<Image::BitmapDecoder>(pf, pal)) != nullptr)
|
||||
return result;
|
||||
if ((result = decodeImage<Image::IFFDecoder>(pf, pal)) != nullptr)
|
||||
return result;
|
||||
if ((result = decodeImage<Image::PCXDecoder>(pf, pal)) != nullptr)
|
||||
return result;
|
||||
if ((result = decodeImage<Image::TGADecoder>(pf, pal)) != nullptr)
|
||||
return result;
|
||||
|
||||
error("Unknown image file");
|
||||
}
|
||||
|
||||
bool save_bitmap(Common::WriteStream &out, BITMAP *bmp, const RGB *pal) {
|
||||
const Graphics::ManagedSurface &src = bmp->getSurface();
|
||||
if (bmp->format.isCLUT8() && pal) {
|
||||
byte palette[256 * 3];
|
||||
for (int c = 0, i = 0; c < 256; ++c, i += 3) {
|
||||
palette[i] = VGA_COLOR_TRANS(pal[c].r);
|
||||
palette[i + 1] = VGA_COLOR_TRANS(pal[c].g);
|
||||
palette[i + 2] = VGA_COLOR_TRANS(pal[c].b);
|
||||
}
|
||||
|
||||
return Image::writeBMP(out, src, palette);
|
||||
} else {
|
||||
return Image::writeBMP(out, src);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace AGS
|
||||
42
engines/ags/shared/gfx/image.h
Normal file
42
engines/ags/shared/gfx/image.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/* 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 AGS_SHARED_GFX_IMAGE_H
|
||||
#define AGS_SHARED_GFX_IMAGE_H
|
||||
|
||||
#include "ags/lib/allegro.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
struct PACKFILE;
|
||||
|
||||
extern BITMAP *load_bitmap(PACKFILE *pf, color *pal);
|
||||
extern BITMAP *load_bitmap(const char *filename, color *pal);
|
||||
extern BITMAP *load_bmp(const char *filename, color *pal);
|
||||
extern BITMAP *load_lbm(const char *filename, color *pal);
|
||||
extern BITMAP *load_pcx(const char *filename, color *pal);
|
||||
extern BITMAP *load_tga(const char *filename, color *pal);
|
||||
|
||||
extern bool save_bitmap(Common::WriteStream &out, BITMAP *bmp, const RGB *pal);
|
||||
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
483
engines/ags/shared/gui/gui_button.cpp
Normal file
483
engines/ags/shared/gui/gui_button.cpp
Normal file
@@ -0,0 +1,483 @@
|
||||
/* 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 "ags/shared/ac/sprite_cache.h"
|
||||
#include "ags/shared/ac/game_struct_defines.h"
|
||||
#include "ags/shared/font/fonts.h"
|
||||
#include "ags/shared/gui/gui_button.h"
|
||||
#include "ags/shared/gui/gui_main.h" // TODO: extract helper functions
|
||||
#include "ags/shared/util/stream.h"
|
||||
#include "ags/shared/util/string_utils.h"
|
||||
#include "ags/globals.h"
|
||||
|
||||
namespace AGS3 {
|
||||
namespace AGS {
|
||||
namespace Shared {
|
||||
|
||||
FrameAlignment ConvertLegacyButtonAlignment(LegacyButtonAlignment align) {
|
||||
switch (align) {
|
||||
case kLegacyButtonAlign_TopCenter:
|
||||
return kAlignTopCenter;
|
||||
case kLegacyButtonAlign_TopLeft:
|
||||
return kAlignTopLeft;
|
||||
case kLegacyButtonAlign_TopRight:
|
||||
return kAlignTopRight;
|
||||
case kLegacyButtonAlign_CenterLeft:
|
||||
return kAlignMiddleLeft;
|
||||
case kLegacyButtonAlign_Centered:
|
||||
return kAlignMiddleCenter;
|
||||
case kLegacyButtonAlign_CenterRight:
|
||||
return kAlignMiddleRight;
|
||||
case kLegacyButtonAlign_BottomLeft:
|
||||
return kAlignBottomLeft;
|
||||
case kLegacyButtonAlign_BottomCenter:
|
||||
return kAlignBottomCenter;
|
||||
case kLegacyButtonAlign_BottomRight:
|
||||
return kAlignBottomRight;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return kAlignNone;
|
||||
}
|
||||
|
||||
|
||||
GUIButton::GUIButton() {
|
||||
_image = -1;
|
||||
_mouseOverImage = -1;
|
||||
_pushedImage = -1;
|
||||
_currentImage = -1;
|
||||
Font = 0;
|
||||
TextColor = 0;
|
||||
TextAlignment = kAlignTopCenter;
|
||||
ClickAction[kGUIClickLeft] = kGUIAction_RunScript;
|
||||
ClickAction[kGUIClickRight] = kGUIAction_RunScript;
|
||||
ClickData[kGUIClickLeft] = 0;
|
||||
ClickData[kGUIClickRight] = 0;
|
||||
|
||||
IsPushed = false;
|
||||
IsMouseOver = false;
|
||||
_placeholder = kButtonPlace_None;
|
||||
_unnamed = true;
|
||||
|
||||
_scEventCount = 1;
|
||||
_scEventNames[0] = "Click";
|
||||
_scEventArgs[0] = "GUIControl *control, MouseButton button";
|
||||
}
|
||||
|
||||
bool GUIButton::HasAlphaChannel() const {
|
||||
return ((_currentImage > 0) && is_sprite_alpha(_currentImage)) ||
|
||||
(!_unnamed && is_font_antialiased(Font));
|
||||
}
|
||||
|
||||
int32_t GUIButton::GetCurrentImage() const {
|
||||
return _currentImage;
|
||||
}
|
||||
|
||||
int32_t GUIButton::GetNormalImage() const {
|
||||
return _image;
|
||||
}
|
||||
|
||||
int32_t GUIButton::GetMouseOverImage() const {
|
||||
return _mouseOverImage;
|
||||
}
|
||||
|
||||
int32_t GUIButton::GetPushedImage() const {
|
||||
return _pushedImage;
|
||||
}
|
||||
|
||||
GUIButtonPlaceholder GUIButton::GetPlaceholder() const {
|
||||
return _placeholder;
|
||||
}
|
||||
|
||||
const String &GUIButton::GetText() const {
|
||||
return _text;
|
||||
}
|
||||
|
||||
bool GUIButton::IsImageButton() const {
|
||||
return _image > 0;
|
||||
}
|
||||
|
||||
bool GUIButton::IsClippingImage() const {
|
||||
return (Flags & kGUICtrl_Clip) != 0;
|
||||
}
|
||||
|
||||
Rect GUIButton::CalcGraphicRect(bool clipped) {
|
||||
if (clipped)
|
||||
return RectWH(0, 0, _width, _height);
|
||||
|
||||
// TODO: need to find a way to cache image and text position, or there'll be some repetition
|
||||
Rect rc = RectWH(0, 0, _width, _height);
|
||||
if (IsImageButton()) {
|
||||
if (IsClippingImage())
|
||||
return rc;
|
||||
// Main button graphic
|
||||
if (_currentImage >= 0 && _GP(spriteset).DoesSpriteExist(_currentImage))
|
||||
rc = SumRects(rc, RectWH(0, 0, get_adjusted_spritewidth(_currentImage), get_adjusted_spriteheight(_currentImage)));
|
||||
// Optionally merge with the inventory pic
|
||||
if (_placeholder != kButtonPlace_None && _G(gui_inv_pic) >= 0) {
|
||||
Size inv_sz = Size(get_adjusted_spritewidth(_G(gui_inv_pic)),
|
||||
get_adjusted_spriteheight(_G(gui_inv_pic)));
|
||||
GUIButtonPlaceholder place = _placeholder;
|
||||
if (place == kButtonPlace_InvItemAuto) {
|
||||
place = ((inv_sz.Width > _width - 6) || (inv_sz.Height > _height - 6)) ?
|
||||
kButtonPlace_InvItemStretch : kButtonPlace_InvItemCenter;
|
||||
}
|
||||
|
||||
Rect inv_rc = (place == kButtonPlace_InvItemStretch) ?
|
||||
RectWH(0 + 3, 0 + 3, _width - 6, _height - 6) :
|
||||
RectWH(0 + _width / 2 - inv_sz.Width / 2,
|
||||
0 + _height / 2 - inv_sz.Height / 2,
|
||||
inv_sz.Width, inv_sz.Height);
|
||||
rc = SumRects(rc, inv_rc);
|
||||
}
|
||||
}
|
||||
// Optionally merge with the button text
|
||||
if (!IsImageButton() || ((_placeholder == kButtonPlace_None) && !_unnamed)) {
|
||||
PrepareTextToDraw();
|
||||
Rect frame = RectWH(0 + 2, 0 + 2, _width - 4, _height - 4);
|
||||
if (IsPushed && IsMouseOver) {
|
||||
frame.Left++;
|
||||
frame.Top++;
|
||||
}
|
||||
rc = SumRects(rc, GUI::CalcTextGraphicalRect(_textToDraw.GetCStr(), Font, frame, TextAlignment));
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
void GUIButton::Draw(Bitmap *ds, int x, int y) {
|
||||
bool draw_disabled = !IsGUIEnabled(this);
|
||||
|
||||
// if it's "Unchanged when disabled" or "GUI Off", don't grey out
|
||||
if ((GUI::Options.DisabledStyle == kGuiDis_Unchanged) ||
|
||||
(GUI::Options.DisabledStyle == kGuiDis_Off)) {
|
||||
draw_disabled = false;
|
||||
}
|
||||
// TODO: should only change properties in reaction to particular events
|
||||
if (_currentImage <= 0 || draw_disabled)
|
||||
_currentImage = _image;
|
||||
|
||||
if (draw_disabled && (GUI::Options.DisabledStyle == kGuiDis_Blackout))
|
||||
// buttons off when disabled - no point carrying on
|
||||
return;
|
||||
|
||||
if (IsImageButton())
|
||||
DrawImageButton(ds, x, y, draw_disabled);
|
||||
// CHECKME: why don't draw frame if no Text? this will make button completely invisible!
|
||||
else if (!_text.IsEmpty())
|
||||
DrawTextButton(ds, x, y, draw_disabled);
|
||||
}
|
||||
|
||||
void GUIButton::SetClipImage(bool on) {
|
||||
if (on != ((Flags & kGUICtrl_Clip) != 0))
|
||||
MarkChanged();
|
||||
if (on)
|
||||
Flags |= kGUICtrl_Clip;
|
||||
else
|
||||
Flags &= ~kGUICtrl_Clip;
|
||||
}
|
||||
|
||||
void GUIButton::SetCurrentImage(int32_t image) {
|
||||
if (_currentImage == image)
|
||||
return;
|
||||
|
||||
_currentImage = image;
|
||||
MarkChanged();
|
||||
}
|
||||
|
||||
void GUIButton::SetMouseOverImage(int32_t image) {
|
||||
if (_mouseOverImage == image)
|
||||
return;
|
||||
|
||||
_mouseOverImage = image;
|
||||
UpdateCurrentImage();
|
||||
}
|
||||
|
||||
void GUIButton::SetNormalImage(int32_t image) {
|
||||
if (_image == image)
|
||||
return;
|
||||
|
||||
_image = image;
|
||||
UpdateCurrentImage();
|
||||
}
|
||||
|
||||
void GUIButton::SetPushedImage(int32_t image) {
|
||||
if (_pushedImage == image)
|
||||
return;
|
||||
|
||||
_pushedImage = image;
|
||||
UpdateCurrentImage();
|
||||
}
|
||||
|
||||
void GUIButton::SetImages(int32_t normal, int32_t over, int32_t pushed) {
|
||||
_image = normal;
|
||||
_mouseOverImage = over;
|
||||
_pushedImage = pushed;
|
||||
UpdateCurrentImage();
|
||||
}
|
||||
|
||||
void GUIButton::SetText(const String &text) {
|
||||
if (_text == text)
|
||||
return;
|
||||
|
||||
_text = text;
|
||||
// Active inventory item placeholders
|
||||
if (_text.CompareNoCase("(INV)") == 0)
|
||||
// Stretch to fit button
|
||||
_placeholder = kButtonPlace_InvItemStretch;
|
||||
else if (_text.CompareNoCase("(INVNS)") == 0)
|
||||
// Draw at actual size
|
||||
_placeholder = kButtonPlace_InvItemCenter;
|
||||
else if (_text.CompareNoCase("(INVSHR)") == 0)
|
||||
// Stretch if too big, actual size if not
|
||||
_placeholder = kButtonPlace_InvItemAuto;
|
||||
else
|
||||
_placeholder = kButtonPlace_None;
|
||||
|
||||
// TODO: find a way to remove this bogus limitation ("New Button" is a valid Text too)
|
||||
_unnamed = _text.IsEmpty() || _text.Compare("New Button") == 0;
|
||||
MarkChanged();
|
||||
}
|
||||
|
||||
bool GUIButton::OnMouseDown() {
|
||||
if (!IsImageButton())
|
||||
MarkChanged();
|
||||
IsPushed = true;
|
||||
UpdateCurrentImage();
|
||||
return false;
|
||||
}
|
||||
|
||||
void GUIButton::OnMouseEnter() {
|
||||
if (IsPushed && !IsImageButton())
|
||||
MarkChanged();
|
||||
IsMouseOver = true;
|
||||
UpdateCurrentImage();
|
||||
}
|
||||
|
||||
void GUIButton::OnMouseLeave() {
|
||||
if (IsPushed && !IsImageButton())
|
||||
MarkChanged();
|
||||
IsMouseOver = false;
|
||||
UpdateCurrentImage();
|
||||
}
|
||||
|
||||
void GUIButton::OnMouseUp() {
|
||||
if (IsMouseOver) {
|
||||
if (IsGUIEnabled(this) && IsClickable())
|
||||
IsActivated = true;
|
||||
}
|
||||
if (IsPushed && !IsImageButton())
|
||||
MarkChanged();
|
||||
IsPushed = false;
|
||||
UpdateCurrentImage();
|
||||
}
|
||||
|
||||
void GUIButton::UpdateCurrentImage() {
|
||||
int new_image = _currentImage;
|
||||
|
||||
if (IsPushed && (_pushedImage > 0)) {
|
||||
new_image = _pushedImage;
|
||||
} else if (IsMouseOver && (_mouseOverImage > 0)) {
|
||||
new_image = _mouseOverImage;
|
||||
} else {
|
||||
new_image = _image;
|
||||
}
|
||||
|
||||
SetCurrentImage(new_image);
|
||||
}
|
||||
|
||||
void GUIButton::WriteToFile(Stream *out) const {
|
||||
GUIObject::WriteToFile(out);
|
||||
|
||||
out->WriteInt32(_image);
|
||||
out->WriteInt32(_mouseOverImage);
|
||||
out->WriteInt32(_pushedImage);
|
||||
out->WriteInt32(Font);
|
||||
out->WriteInt32(TextColor);
|
||||
out->WriteInt32(ClickAction[kGUIClickLeft]);
|
||||
out->WriteInt32(ClickAction[kGUIClickRight]);
|
||||
out->WriteInt32(ClickData[kGUIClickLeft]);
|
||||
out->WriteInt32(ClickData[kGUIClickRight]);
|
||||
|
||||
StrUtil::WriteString(_text, out);
|
||||
out->WriteInt32(TextAlignment);
|
||||
}
|
||||
|
||||
void GUIButton::ReadFromFile(Stream *in, GuiVersion gui_version) {
|
||||
GUIObject::ReadFromFile(in, gui_version);
|
||||
|
||||
_image = in->ReadInt32();
|
||||
_mouseOverImage = in->ReadInt32();
|
||||
_pushedImage = in->ReadInt32();
|
||||
if (gui_version < kGuiVersion_350) { // NOTE: reading into actual variables only for old savegame support
|
||||
_currentImage = in->ReadInt32();
|
||||
IsPushed = in->ReadInt32() != 0;
|
||||
IsMouseOver = in->ReadInt32() != 0;
|
||||
}
|
||||
Font = in->ReadInt32();
|
||||
TextColor = in->ReadInt32();
|
||||
ClickAction[kGUIClickLeft] = (GUIClickAction)in->ReadInt32();
|
||||
ClickAction[kGUIClickRight] = (GUIClickAction)in->ReadInt32();
|
||||
ClickData[kGUIClickLeft] = in->ReadInt32();
|
||||
ClickData[kGUIClickRight] = in->ReadInt32();
|
||||
if (gui_version < kGuiVersion_350)
|
||||
SetText(String::FromStreamCount(in, GUIBUTTON_LEGACY_TEXTLENGTH));
|
||||
else
|
||||
SetText(StrUtil::ReadString(in));
|
||||
|
||||
if (gui_version >= kGuiVersion_272a) {
|
||||
if (gui_version < kGuiVersion_350) {
|
||||
TextAlignment = ConvertLegacyButtonAlignment((LegacyButtonAlignment)in->ReadInt32());
|
||||
in->ReadInt32(); // reserved1
|
||||
} else {
|
||||
TextAlignment = (FrameAlignment)in->ReadInt32();
|
||||
}
|
||||
} else {
|
||||
TextAlignment = kAlignTopCenter;
|
||||
}
|
||||
|
||||
if (TextColor == 0)
|
||||
TextColor = 16;
|
||||
_currentImage = _image;
|
||||
}
|
||||
|
||||
void GUIButton::ReadFromSavegame(Stream *in, GuiSvgVersion svg_ver) {
|
||||
GUIObject::ReadFromSavegame(in, svg_ver);
|
||||
// Properties
|
||||
_image = in->ReadInt32();
|
||||
_mouseOverImage = in->ReadInt32();
|
||||
_pushedImage = in->ReadInt32();
|
||||
Font = in->ReadInt32();
|
||||
TextColor = in->ReadInt32();
|
||||
SetText(StrUtil::ReadString(in));
|
||||
if (svg_ver >= kGuiSvgVersion_350)
|
||||
TextAlignment = (FrameAlignment)in->ReadInt32();
|
||||
// Dynamic state
|
||||
_currentImage = in->ReadInt32();
|
||||
|
||||
// Update current state after reading
|
||||
IsPushed = false;
|
||||
IsMouseOver = false;
|
||||
}
|
||||
|
||||
void GUIButton::WriteToSavegame(Stream *out) const {
|
||||
// Properties
|
||||
GUIObject::WriteToSavegame(out);
|
||||
out->WriteInt32(_image);
|
||||
out->WriteInt32(_mouseOverImage);
|
||||
out->WriteInt32(_pushedImage);
|
||||
out->WriteInt32(Font);
|
||||
out->WriteInt32(TextColor);
|
||||
StrUtil::WriteString(GetText(), out);
|
||||
out->WriteInt32(TextAlignment);
|
||||
// Dynamic state
|
||||
out->WriteInt32(_currentImage);
|
||||
}
|
||||
|
||||
void GUIButton::DrawImageButton(Bitmap *ds, int x, int y, bool draw_disabled) {
|
||||
assert(_currentImage >= 0);
|
||||
// NOTE: the CLIP flag only clips the image, not the text
|
||||
if (IsClippingImage() && !GUI::Options.ClipControls)
|
||||
ds->SetClip(RectWH(x, y, _width, _height));
|
||||
if (_GP(spriteset).DoesSpriteExist(_currentImage))
|
||||
draw_gui_sprite(ds, _currentImage, x, y, true);
|
||||
|
||||
// Draw active inventory item
|
||||
if (_placeholder != kButtonPlace_None && _G(gui_inv_pic) >= 0) {
|
||||
Size inv_sz = Size(get_adjusted_spritewidth(_G(gui_inv_pic)), get_adjusted_spriteheight(_G(gui_inv_pic)));
|
||||
GUIButtonPlaceholder place = _placeholder;
|
||||
if (place == kButtonPlace_InvItemAuto) {
|
||||
place = ((inv_sz.Width > _width - 6) || (inv_sz.Height > _height - 6)) ?
|
||||
kButtonPlace_InvItemStretch : kButtonPlace_InvItemCenter;
|
||||
}
|
||||
|
||||
if (place == kButtonPlace_InvItemStretch) {
|
||||
ds->StretchBlt(_GP(spriteset)[_G(gui_inv_pic)], RectWH(x + 3, y + 3, _width - 6, _height - 6),
|
||||
kBitmap_Transparency);
|
||||
} else {
|
||||
draw_gui_sprite(ds, _G(gui_inv_pic),
|
||||
x + _width / 2 - inv_sz.Width / 2,
|
||||
y + _height / 2 - inv_sz.Height / 2,
|
||||
true);
|
||||
}
|
||||
}
|
||||
|
||||
if ((draw_disabled) && (GUI::Options.DisabledStyle == kGuiDis_Greyout)) {
|
||||
// darken the button when disabled
|
||||
const Size sz = _GP(spriteset).GetSpriteResolution(_currentImage);
|
||||
GUI::DrawDisabledEffect(ds, RectWH(x, y, sz.Width, sz.Height));
|
||||
}
|
||||
|
||||
// Don't print Text of (INV) (INVSHR) (INVNS)
|
||||
if ((_placeholder == kButtonPlace_None) && !_unnamed)
|
||||
DrawText(ds, x, y, draw_disabled);
|
||||
|
||||
if (IsClippingImage() && !GUI::Options.ClipControls)
|
||||
ds->ResetClip();
|
||||
}
|
||||
|
||||
void GUIButton::DrawText(Bitmap *ds, int x, int y, bool draw_disabled) {
|
||||
// TODO: need to find a way to cache Text prior to drawing;
|
||||
// but that will require to update all gui controls when translation is changed in game
|
||||
PrepareTextToDraw();
|
||||
|
||||
Rect frame = RectWH(x + 2, y + 2, _width - 4, _height - 4);
|
||||
if (IsPushed && IsMouseOver) {
|
||||
// move the Text a bit while pushed
|
||||
frame.Left++;
|
||||
frame.Top++;
|
||||
}
|
||||
color_t text_color = ds->GetCompatibleColor(TextColor);
|
||||
if (draw_disabled)
|
||||
text_color = ds->GetCompatibleColor(8);
|
||||
GUI::DrawTextAligned(ds, _textToDraw.GetCStr(), Font, text_color, frame, TextAlignment);
|
||||
}
|
||||
|
||||
void GUIButton::DrawTextButton(Bitmap *ds, int x, int y, bool draw_disabled) {
|
||||
color_t draw_color = ds->GetCompatibleColor(7);
|
||||
ds->FillRect(Rect(x, y, x + _width - 1, y + _height - 1), draw_color);
|
||||
if (Flags & kGUICtrl_Default) {
|
||||
draw_color = ds->GetCompatibleColor(16);
|
||||
ds->DrawRect(Rect(x - 1, y - 1, x + _width, y + _height), draw_color);
|
||||
}
|
||||
|
||||
// TODO: use color constants instead of literal numbers
|
||||
if (!draw_disabled && IsMouseOver && IsPushed)
|
||||
draw_color = ds->GetCompatibleColor(15);
|
||||
else
|
||||
draw_color = ds->GetCompatibleColor(8);
|
||||
|
||||
ds->DrawLine(Line(x, y + _height - 1, x + _width - 1, y + _height - 1), draw_color);
|
||||
ds->DrawLine(Line(x + _width - 1, y, x + _width - 1, y + _height - 1), draw_color);
|
||||
|
||||
if (draw_disabled || (IsMouseOver && IsPushed))
|
||||
draw_color = ds->GetCompatibleColor(8);
|
||||
else
|
||||
draw_color = ds->GetCompatibleColor(15);
|
||||
|
||||
ds->DrawLine(Line(x, y, x + _width - 1, y), draw_color);
|
||||
ds->DrawLine(Line(x, y, x, y + _height - 1), draw_color);
|
||||
|
||||
DrawText(ds, x, y, draw_disabled);
|
||||
}
|
||||
|
||||
} // namespace Shared
|
||||
} // namespace AGS
|
||||
} // namespace AGS3
|
||||
150
engines/ags/shared/gui/gui_button.h
Normal file
150
engines/ags/shared/gui/gui_button.h
Normal file
@@ -0,0 +1,150 @@
|
||||
/* 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 AGS_SHARED_GUI_GUI_BUTTON_H
|
||||
#define AGS_SHARED_GUI_GUI_BUTTON_H
|
||||
|
||||
#include "common/std/vector.h"
|
||||
#include "ags/engine/ac/button.h"
|
||||
#include "ags/shared/gui/gui_object.h"
|
||||
#include "ags/shared/util/string.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
#define GUIBUTTON_LEGACY_TEXTLENGTH 50
|
||||
|
||||
namespace AGS {
|
||||
namespace Shared {
|
||||
|
||||
enum GUIClickMouseButton {
|
||||
kGUIClickLeft = 0,
|
||||
kGUIClickRight = 1,
|
||||
kNumGUIClicks
|
||||
};
|
||||
|
||||
enum GUIClickAction {
|
||||
kGUIAction_None = 0,
|
||||
kGUIAction_SetMode = 1,
|
||||
kGUIAction_RunScript = 2,
|
||||
};
|
||||
|
||||
enum LegacyButtonAlignment {
|
||||
kLegacyButtonAlign_TopCenter = 0,
|
||||
kLegacyButtonAlign_TopLeft = 1,
|
||||
kLegacyButtonAlign_TopRight = 2,
|
||||
kLegacyButtonAlign_CenterLeft = 3,
|
||||
kLegacyButtonAlign_Centered = 4,
|
||||
kLegacyButtonAlign_CenterRight = 5,
|
||||
kLegacyButtonAlign_BottomLeft = 6,
|
||||
kLegacyButtonAlign_BottomCenter = 7,
|
||||
kLegacyButtonAlign_BottomRight = 8,
|
||||
};
|
||||
|
||||
// Defines button placeholder mode; the mode is set
|
||||
// depending on special tags found in button text
|
||||
enum GUIButtonPlaceholder {
|
||||
kButtonPlace_None,
|
||||
kButtonPlace_InvItemStretch,
|
||||
kButtonPlace_InvItemCenter,
|
||||
kButtonPlace_InvItemAuto
|
||||
};
|
||||
|
||||
class GUIButton : public GUIObject {
|
||||
public:
|
||||
GUIButton();
|
||||
|
||||
bool HasAlphaChannel() const override;
|
||||
int32_t GetCurrentImage() const;
|
||||
int32_t GetNormalImage() const;
|
||||
int32_t GetMouseOverImage() const;
|
||||
int32_t GetPushedImage() const;
|
||||
GUIButtonPlaceholder GetPlaceholder() const;
|
||||
const String &GetText() const;
|
||||
bool IsImageButton() const;
|
||||
bool IsClippingImage() const;
|
||||
|
||||
// Operations
|
||||
Rect CalcGraphicRect(bool clipped) override;
|
||||
void Draw(Bitmap *ds, int x = 0, int y = 0) override;
|
||||
void SetClipImage(bool on);
|
||||
void SetCurrentImage(int32_t image);
|
||||
void SetMouseOverImage(int32_t image);
|
||||
void SetNormalImage(int32_t image);
|
||||
void SetPushedImage(int32_t image);
|
||||
void SetImages(int32_t normal, int32_t over, int32_t pushed);
|
||||
void SetText(const String &text);
|
||||
|
||||
// Events
|
||||
bool OnMouseDown() override;
|
||||
void OnMouseEnter() override;
|
||||
void OnMouseLeave() override;
|
||||
void OnMouseUp() override;
|
||||
|
||||
// Serialization
|
||||
void ReadFromFile(Stream *in, GuiVersion gui_version) override;
|
||||
void WriteToFile(Stream *out) const override;
|
||||
void ReadFromSavegame(Shared::Stream *in, GuiSvgVersion svg_ver) override;
|
||||
void WriteToSavegame(Shared::Stream *out) const override;
|
||||
|
||||
// TODO: these members are currently public; hide them later
|
||||
public:
|
||||
int32_t Font;
|
||||
color_t TextColor;
|
||||
FrameAlignment TextAlignment;
|
||||
// Click actions for left and right mouse buttons
|
||||
// NOTE: only left click is currently in use
|
||||
GUIClickAction ClickAction[kNumGUIClicks];
|
||||
int32_t ClickData[kNumGUIClicks];
|
||||
|
||||
bool IsPushed;
|
||||
bool IsMouseOver;
|
||||
|
||||
private:
|
||||
void DrawImageButton(Bitmap *ds, int x, int y, bool draw_disabled);
|
||||
void DrawText(Bitmap *ds, int x, int y, bool draw_disabled);
|
||||
void DrawTextButton(Bitmap *ds, int x, int y, bool draw_disabled);
|
||||
void PrepareTextToDraw();
|
||||
// Update current image depending on the button's state
|
||||
void UpdateCurrentImage();
|
||||
|
||||
int32_t _image;
|
||||
int32_t _mouseOverImage;
|
||||
int32_t _pushedImage;
|
||||
// Active displayed image
|
||||
int32_t _currentImage;
|
||||
// Text property set by user
|
||||
String _text;
|
||||
// type of content placeholder, if any
|
||||
GUIButtonPlaceholder _placeholder;
|
||||
// A flag indicating unnamed button; this is a convenience trick:
|
||||
// buttons are created named "New Button" in the editor, and users
|
||||
// often do not clear text when they want a graphic button.
|
||||
bool _unnamed;
|
||||
// Prepared text buffer/cache
|
||||
String _textToDraw;
|
||||
};
|
||||
|
||||
} // namespace Shared
|
||||
} // namespace AGS
|
||||
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
216
engines/ags/shared/gui/gui_defines.h
Normal file
216
engines/ags/shared/gui/gui_defines.h
Normal file
@@ -0,0 +1,216 @@
|
||||
/* 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 AGS_SHARED_GUI_GUI_DEFINES_H
|
||||
#define AGS_SHARED_GUI_GUI_DEFINES_H
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
#define GUIMAGIC 0xcafebeef
|
||||
#define MAX_GUIOBJ_EVENTS 10
|
||||
#define TEXTWINDOW_PADDING_DEFAULT 3
|
||||
|
||||
// TODO: find out more about gui version history
|
||||
//=============================================================================
|
||||
// GUI Version history
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// 2.1.4..... (100): Introduced Slider gui control. Gui count is now serialized
|
||||
// in game file.
|
||||
// 2.2.2..... (101): Introduced TextBox gui control.
|
||||
// 2.3.0..... (102): Introduced ListBox gui control.
|
||||
// 2.6.0..... (105): GUI custom Z-order support.
|
||||
// 2.7.0..... (110): Added GUI OnClick handler.
|
||||
// 2.7.2.???? (111): Added text alignment property to buttons.
|
||||
// 2.7.2.???? (112): Added text alignment property to list boxes.
|
||||
// 2.7.2.???? (113): Increased permitted length of GUI label text from 200 to
|
||||
// 2048 characters.
|
||||
// 2.7.2.???? (114): Obsoleted savegameindex[] array, and added
|
||||
// ListBox.SaveGameSlots[] array instead.
|
||||
// 2.7.2.???? (115): Added GUI Control z-order support.
|
||||
//
|
||||
// 3.3.0.1132 (116): Added kGUICtrl_Translated flag.
|
||||
// 3.3.1.???? (117): Added padding variable for text window GUIs.
|
||||
// 3.4.0 (118): Removed GUI limits
|
||||
// 3.5.0 (119): Game data contains GUI properties that previously
|
||||
// could be set only at runtime.
|
||||
// Since then format value is defined as AGS version represented as NN,NN,NN,NN
|
||||
//=============================================================================
|
||||
|
||||
enum GuiVersion {
|
||||
// TODO: find out all corresponding engine version numbers
|
||||
kGuiVersion_Initial = 0,
|
||||
kGuiVersion_214 = 100,
|
||||
kGuiVersion_222 = 101,
|
||||
kGuiVersion_230 = 102,
|
||||
kGuiVersion_unkn_103 = 103,
|
||||
kGuiVersion_unkn_104 = 104,
|
||||
kGuiVersion_260 = 105,
|
||||
kGuiVersion_unkn_106 = 106,
|
||||
kGuiVersion_unkn_107 = 107,
|
||||
kGuiVersion_unkn_108 = 108,
|
||||
kGuiVersion_unkn_109 = 109,
|
||||
kGuiVersion_270 = 110,
|
||||
kGuiVersion_272a = 111,
|
||||
kGuiVersion_272b = 112,
|
||||
kGuiVersion_272c = 113,
|
||||
kGuiVersion_272d = 114,
|
||||
kGuiVersion_272e = 115,
|
||||
|
||||
kGuiVersion_330 = 116,
|
||||
kGuiVersion_331 = 117,
|
||||
kGuiVersion_340 = 118,
|
||||
kGuiVersion_350 = 119,
|
||||
kGuiVersion_Current = kGuiVersion_350,
|
||||
};
|
||||
|
||||
namespace AGS {
|
||||
namespace Shared {
|
||||
|
||||
// GUIMain's style and behavior flags
|
||||
enum GUIMainFlags {
|
||||
kGUIMain_Clickable = 0x0001,
|
||||
kGUIMain_TextWindow = 0x0002,
|
||||
kGUIMain_Visible = 0x0004,
|
||||
kGUIMain_Concealed = 0x0008,
|
||||
|
||||
// NOTE: currently default state is Visible to keep this backwards compatible;
|
||||
// check later if this is still a wanted behavior
|
||||
kGUIMain_DefFlags = kGUIMain_Clickable | kGUIMain_Visible,
|
||||
// flags that had inverse meaning in old formats
|
||||
kGUIMain_OldFmtXorMask = kGUIMain_Clickable
|
||||
};
|
||||
|
||||
// GUIMain's legacy flags, now converted to GUIMainFlags on load
|
||||
enum GUIMainLegacyFlags {
|
||||
kGUIMain_LegacyTextWindow = 5
|
||||
};
|
||||
|
||||
// GUIMain's style of getting displayed on screen
|
||||
enum GUIPopupStyle {
|
||||
// Normal GUI
|
||||
kGUIPopupNormal = 0,
|
||||
// Shown when the mouse cursor moves to the top of the screen
|
||||
kGUIPopupMouseY = 1,
|
||||
// Same as Normal, but pauses the game when shown
|
||||
kGUIPopupModal = 2,
|
||||
// Same as Normal, but is not removed when interface is off
|
||||
kGUIPopupNoAutoRemove = 3,
|
||||
// (legacy option) Normal GUI, initially off
|
||||
// converts to kGUIPopupNormal with Visible = false
|
||||
kGUIPopupLegacyNormalOff = 4
|
||||
};
|
||||
|
||||
// The type of GUIControl
|
||||
enum GUIControlType {
|
||||
kGUIControlUndefined = -1,
|
||||
kGUIButton = 1,
|
||||
kGUILabel = 2,
|
||||
kGUIInvWindow = 3,
|
||||
kGUISlider = 4,
|
||||
kGUITextBox = 5,
|
||||
kGUIListBox = 6
|
||||
};
|
||||
|
||||
// GUIControl general style and behavior flags
|
||||
enum GUIControlFlags {
|
||||
kGUICtrl_Default = 0x0001, // only button
|
||||
kGUICtrl_Cancel = 0x0002, // unused
|
||||
kGUICtrl_Enabled = 0x0004,
|
||||
kGUICtrl_TabStop = 0x0008, // unused
|
||||
kGUICtrl_Visible = 0x0010,
|
||||
kGUICtrl_Clip = 0x0020, // only button
|
||||
kGUICtrl_Clickable = 0x0040,
|
||||
kGUICtrl_Translated = 0x0080, // 3.3.0.1132
|
||||
kGUICtrl_Deleted = 0x8000, // unused (probably remains from the old editor?)
|
||||
|
||||
kGUICtrl_DefFlags = kGUICtrl_Enabled | kGUICtrl_Visible | kGUICtrl_Clickable | kGUICtrl_Translated,
|
||||
// flags that had inverse meaning in old formats
|
||||
kGUICtrl_OldFmtXorMask = kGUICtrl_Enabled | kGUICtrl_Visible | kGUICtrl_Clickable
|
||||
};
|
||||
|
||||
// Label macro flags, define which macros are present in the Label's Text
|
||||
enum GUILabelMacro {
|
||||
kLabelMacro_None = 0,
|
||||
kLabelMacro_Gamename = 0x01,
|
||||
kLabelMacro_Overhotspot = 0x02,
|
||||
kLabelMacro_Score = 0x04,
|
||||
kLabelMacro_ScoreText = 0x08,
|
||||
kLabelMacro_TotalScore = 0x10,
|
||||
|
||||
kLabelMacro_AllScore = kLabelMacro_Score | kLabelMacro_ScoreText,
|
||||
kLabelMacro_All = 0xFFFF
|
||||
};
|
||||
|
||||
// GUIListBox style and behavior flags
|
||||
enum GUIListBoxFlags {
|
||||
kListBox_ShowBorder = 0x01,
|
||||
kListBox_ShowArrows = 0x02,
|
||||
kListBox_SvgIndex = 0x04,
|
||||
|
||||
kListBox_DefFlags = kListBox_ShowBorder | kListBox_ShowArrows,
|
||||
// flags that had inverse meaning in old formats
|
||||
kListBox_OldFmtXorMask = kListBox_ShowBorder | kListBox_ShowArrows
|
||||
};
|
||||
|
||||
// GUITextBox style and behavior flags
|
||||
enum GUITextBoxFlags {
|
||||
kTextBox_ShowBorder = 0x0001,
|
||||
|
||||
kTextBox_DefFlags = kTextBox_ShowBorder,
|
||||
// flags that had inverse meaning in old formats
|
||||
kTextBox_OldFmtXorMask = kTextBox_ShowBorder
|
||||
};
|
||||
|
||||
// Savegame data format
|
||||
// TODO: move to the engine code
|
||||
enum GuiSvgVersion {
|
||||
kGuiSvgVersion_Initial = 0,
|
||||
kGuiSvgVersion_350,
|
||||
kGuiSvgVersion_36020,
|
||||
kGuiSvgVersion_36023,
|
||||
kGuiSvgVersion_36025
|
||||
};
|
||||
|
||||
// Style of GUI drawing in disabled state
|
||||
enum GuiDisableStyle {
|
||||
kGuiDis_Undefined = -1, // this is for marking not-disabled state
|
||||
kGuiDis_Greyout = 0, // paint "gisabled" effect over controls
|
||||
kGuiDis_Blackout = 1, // invisible controls (but guis are shown
|
||||
kGuiDis_Unchanged = 2, // no change, display normally
|
||||
kGuiDis_Off = 3 // fully invisible guis
|
||||
};
|
||||
|
||||
// Global GUI options
|
||||
struct GuiOptions {
|
||||
// Clip GUI control's contents to the control's rectangle
|
||||
bool ClipControls = true;
|
||||
// How the GUI controls are drawn when the interface is disabled
|
||||
GuiDisableStyle DisabledStyle = kGuiDis_Unchanged;
|
||||
// Whether to graphically outline GUI controls
|
||||
bool OutlineControls = false;
|
||||
};
|
||||
|
||||
} // namespace Shared
|
||||
} // namespace AGS
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
128
engines/ags/shared/gui/gui_inv.cpp
Normal file
128
engines/ags/shared/gui/gui_inv.cpp
Normal file
@@ -0,0 +1,128 @@
|
||||
/* 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 "ags/shared/ac/game_version.h"
|
||||
#include "ags/shared/gui/gui_inv.h"
|
||||
#include "ags/shared/gui/gui_main.h"
|
||||
#include "ags/shared/util/stream.h"
|
||||
|
||||
namespace AGS3 {
|
||||
namespace AGS {
|
||||
namespace Shared {
|
||||
|
||||
GUIInvWindow::GUIInvWindow() {
|
||||
IsMouseOver = false;
|
||||
CharId = -1;
|
||||
ItemWidth = 40;
|
||||
ItemHeight = 22;
|
||||
ColCount = 0;
|
||||
RowCount = 0;
|
||||
TopItem = 0;
|
||||
CalculateNumCells();
|
||||
|
||||
_scEventCount = 0;
|
||||
}
|
||||
|
||||
void GUIInvWindow::OnMouseEnter() {
|
||||
IsMouseOver = true;
|
||||
}
|
||||
|
||||
void GUIInvWindow::OnMouseLeave() {
|
||||
IsMouseOver = false;
|
||||
}
|
||||
|
||||
void GUIInvWindow::OnMouseUp() {
|
||||
if (IsMouseOver)
|
||||
IsActivated = true;
|
||||
}
|
||||
|
||||
void GUIInvWindow::OnResized() {
|
||||
CalculateNumCells();
|
||||
MarkChanged();
|
||||
}
|
||||
|
||||
void GUIInvWindow::WriteToFile(Stream *out) const {
|
||||
GUIObject::WriteToFile(out);
|
||||
out->WriteInt32(CharId);
|
||||
out->WriteInt32(ItemWidth);
|
||||
out->WriteInt32(ItemHeight);
|
||||
}
|
||||
|
||||
void GUIInvWindow::ReadFromFile(Stream *in, GuiVersion gui_version) {
|
||||
GUIObject::ReadFromFile(in, gui_version);
|
||||
if (gui_version >= kGuiVersion_unkn_109) {
|
||||
CharId = in->ReadInt32();
|
||||
ItemWidth = in->ReadInt32();
|
||||
ItemHeight = in->ReadInt32();
|
||||
if (gui_version < kGuiVersion_350) { // NOTE: reading into actual variables only for old savegame support
|
||||
TopItem = in->ReadInt32();
|
||||
}
|
||||
} else {
|
||||
CharId = -1;
|
||||
ItemWidth = 40;
|
||||
ItemHeight = 22;
|
||||
TopItem = 0;
|
||||
}
|
||||
|
||||
if (_G(loaded_game_file_version) >= kGameVersion_270) {
|
||||
// ensure that some items are visible
|
||||
if (ItemWidth > _width)
|
||||
ItemWidth = _width;
|
||||
if (ItemHeight > _height)
|
||||
ItemHeight = _height;
|
||||
}
|
||||
|
||||
CalculateNumCells();
|
||||
}
|
||||
|
||||
void GUIInvWindow::ReadFromSavegame(Stream *in, GuiSvgVersion svg_ver) {
|
||||
GUIObject::ReadFromSavegame(in, svg_ver);
|
||||
ItemWidth = in->ReadInt32();
|
||||
ItemHeight = in->ReadInt32();
|
||||
CharId = in->ReadInt32();
|
||||
TopItem = in->ReadInt32();
|
||||
CalculateNumCells();
|
||||
}
|
||||
|
||||
void GUIInvWindow::WriteToSavegame(Stream *out) const {
|
||||
GUIObject::WriteToSavegame(out);
|
||||
out->WriteInt32(ItemWidth);
|
||||
out->WriteInt32(ItemHeight);
|
||||
out->WriteInt32(CharId);
|
||||
out->WriteInt32(TopItem);
|
||||
}
|
||||
|
||||
void GUIInvWindow::CalculateNumCells() {
|
||||
if (ItemWidth <= 0 || ItemHeight <= 0) {
|
||||
ColCount = 0;
|
||||
RowCount = 0;
|
||||
} else if (_G(loaded_game_file_version) >= kGameVersion_270) {
|
||||
ColCount = _width / data_to_game_coord(ItemWidth);
|
||||
RowCount = _height / data_to_game_coord(ItemHeight);
|
||||
} else {
|
||||
ColCount = floor((float)_width / (float)data_to_game_coord(ItemWidth) + 0.5f);
|
||||
RowCount = floor((float)_height / (float)data_to_game_coord(ItemHeight) + 0.5f);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Shared
|
||||
} // namespace AGS
|
||||
} // namespace AGS3
|
||||
74
engines/ags/shared/gui/gui_inv.h
Normal file
74
engines/ags/shared/gui/gui_inv.h
Normal file
@@ -0,0 +1,74 @@
|
||||
/* 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 AGS_SHARED_GUI_GUI_INV_H
|
||||
#define AGS_SHARED_GUI_GUI_INV_H
|
||||
|
||||
#include "common/std/vector.h"
|
||||
#include "ags/shared/gui/gui_object.h"
|
||||
|
||||
namespace AGS3 {
|
||||
namespace AGS {
|
||||
namespace Shared {
|
||||
|
||||
class GUIInvWindow : public GUIObject {
|
||||
public:
|
||||
GUIInvWindow();
|
||||
|
||||
bool HasAlphaChannel() const override;
|
||||
// This function has distinct implementations in Engine and Editor
|
||||
int GetCharacterId() const;
|
||||
|
||||
// Operations
|
||||
// This function has distinct implementations in Engine and Editor
|
||||
void Draw(Bitmap *ds, int x = 0, int y = 0) override;
|
||||
|
||||
// Events
|
||||
void OnMouseEnter() override;
|
||||
void OnMouseLeave() override;
|
||||
void OnMouseUp() override;
|
||||
void OnResized() override;
|
||||
|
||||
// Serialization
|
||||
void ReadFromFile(Stream *in, GuiVersion gui_version) override;
|
||||
void WriteToFile(Stream *out) const override;
|
||||
void ReadFromSavegame(Shared::Stream *in, GuiSvgVersion svg_ver) override;
|
||||
void WriteToSavegame(Shared::Stream *out) const override;
|
||||
|
||||
// TODO: these members are currently public; hide them later
|
||||
public:
|
||||
bool IsMouseOver;
|
||||
int32_t CharId; // whose inventory (-1 = current player)
|
||||
int32_t ItemWidth;
|
||||
int32_t ItemHeight;
|
||||
int32_t ColCount;
|
||||
int32_t RowCount;
|
||||
int32_t TopItem;
|
||||
|
||||
private:
|
||||
void CalculateNumCells();
|
||||
};
|
||||
|
||||
} // namespace Shared
|
||||
} // namespace AGS
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
172
engines/ags/shared/gui/gui_label.cpp
Normal file
172
engines/ags/shared/gui/gui_label.cpp
Normal file
@@ -0,0 +1,172 @@
|
||||
/* 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/std/algorithm.h"
|
||||
#include "ags/shared/ac/game_version.h"
|
||||
#include "ags/shared/font/fonts.h"
|
||||
#include "ags/shared/gui/gui_label.h"
|
||||
#include "ags/shared/gui/gui_main.h"
|
||||
#include "ags/shared/util/stream.h"
|
||||
#include "ags/shared/util/string_utils.h"
|
||||
|
||||
#define GUILABEL_TEXTLENGTH_PRE272 200
|
||||
|
||||
namespace AGS3 {
|
||||
namespace AGS {
|
||||
namespace Shared {
|
||||
|
||||
GUILabel::GUILabel() {
|
||||
Font = 0;
|
||||
TextColor = 0;
|
||||
TextAlignment = kHAlignLeft;
|
||||
|
||||
_scEventCount = 0;
|
||||
}
|
||||
|
||||
bool GUILabel::HasAlphaChannel() const {
|
||||
return is_font_antialiased(Font);
|
||||
}
|
||||
|
||||
String GUILabel::GetText() const {
|
||||
return Text;
|
||||
}
|
||||
|
||||
GUILabelMacro GUILabel::GetTextMacros() const {
|
||||
return _textMacro;
|
||||
}
|
||||
|
||||
Rect GUILabel::CalcGraphicRect(bool clipped) {
|
||||
if (clipped)
|
||||
return RectWH(0, 0, _width, _height);
|
||||
|
||||
// TODO: need to find a way to text position, or there'll be some repetition
|
||||
// have to precache text and size on some events:
|
||||
// - translation change
|
||||
// - macro value change (score, overhotspot etc)
|
||||
Rect rc = RectWH(0, 0, _width, _height);
|
||||
if (PrepareTextToDraw() == 0)
|
||||
return rc;
|
||||
const int linespacing = // Older engine labels used (font height + 1) as linespacing for some reason
|
||||
((_G(loaded_game_file_version) < kGameVersion_360) && (get_font_flags(Font) & FFLG_DEFLINESPACING)) ?
|
||||
(get_font_height(Font) + 1) :
|
||||
get_font_linespacing(Font);
|
||||
// < 2.72 labels did not limit vertical size of text
|
||||
const bool limit_by_label_frame = _G(loaded_game_file_version) >= kGameVersion_272;
|
||||
int at_y = 0;
|
||||
Line max_line;
|
||||
for (size_t i = 0;
|
||||
i < _GP(Lines).Count() && (!limit_by_label_frame || at_y <= _height);
|
||||
++i, at_y += linespacing) {
|
||||
Line lpos = GUI::CalcTextPositionHor(_GP(Lines)[i].GetCStr(), Font, 0, 0 + _width - 1, at_y,
|
||||
(FrameAlignment)TextAlignment);
|
||||
max_line.X2 = MAX(max_line.X2, lpos.X2);
|
||||
}
|
||||
// Include font fixes for the first and last text line,
|
||||
// in case graphical height is different, and there's a VerticalOffset
|
||||
Line vextent = GUI::CalcFontGraphicalVExtent(Font);
|
||||
Rect text_rc = RectWH(0, vextent.Y1, max_line.X2 - max_line.X1 + 1, at_y - linespacing + (vextent.Y2 - vextent.Y1));
|
||||
return SumRects(rc, text_rc);
|
||||
}
|
||||
|
||||
void GUILabel::Draw(Bitmap *ds, int x, int y) {
|
||||
// TODO: need to find a way to cache text prior to drawing;
|
||||
// but that will require to update all gui controls when translation is changed in game
|
||||
if (PrepareTextToDraw() == 0)
|
||||
return;
|
||||
|
||||
color_t text_color = ds->GetCompatibleColor(TextColor);
|
||||
const int linespacing = // Older engine labels used (font height + 1) as linespacing for some reason
|
||||
((_G(loaded_game_file_version) < kGameVersion_360) && (get_font_flags(Font) & FFLG_DEFLINESPACING)) ?
|
||||
(get_font_height(Font) + 1) :
|
||||
get_font_linespacing(Font);
|
||||
// < 2.72 labels did not limit vertical size of text
|
||||
const bool limit_by_label_frame = _G(loaded_game_file_version) >= kGameVersion_272;
|
||||
int at_y = y;
|
||||
for (size_t i = 0;
|
||||
i < _GP(Lines).Count() && (!limit_by_label_frame || at_y <= y + _height);
|
||||
++i, at_y += linespacing) {
|
||||
GUI::DrawTextAlignedHor(ds, _GP(Lines)[i].GetCStr(), Font, text_color, x, x + _width - 1, at_y,
|
||||
(FrameAlignment)TextAlignment);
|
||||
}
|
||||
}
|
||||
|
||||
void GUILabel::SetText(const String &text) {
|
||||
if (text == Text)
|
||||
return;
|
||||
Text = text;
|
||||
// Check for macros within text
|
||||
_textMacro = GUI::FindLabelMacros(Text);
|
||||
MarkChanged();
|
||||
}
|
||||
|
||||
// TODO: replace string serialization with StrUtil::ReadString and WriteString
|
||||
// methods in the future, to keep this organized.
|
||||
void GUILabel::WriteToFile(Stream *out) const {
|
||||
GUIObject::WriteToFile(out);
|
||||
StrUtil::WriteString(Text, out);
|
||||
out->WriteInt32(Font);
|
||||
out->WriteInt32(TextColor);
|
||||
out->WriteInt32(TextAlignment);
|
||||
}
|
||||
|
||||
void GUILabel::ReadFromFile(Stream *in, GuiVersion gui_version) {
|
||||
GUIObject::ReadFromFile(in, gui_version);
|
||||
|
||||
if (gui_version < kGuiVersion_272c)
|
||||
Text.ReadCount(in, GUILABEL_TEXTLENGTH_PRE272);
|
||||
else
|
||||
Text = StrUtil::ReadString(in);
|
||||
|
||||
Font = in->ReadInt32();
|
||||
TextColor = in->ReadInt32();
|
||||
if (gui_version < kGuiVersion_350)
|
||||
TextAlignment = ConvertLegacyGUIAlignment((LegacyGUIAlignment)in->ReadInt32());
|
||||
else
|
||||
TextAlignment = (HorAlignment)in->ReadInt32();
|
||||
|
||||
if (TextColor == 0)
|
||||
TextColor = 16;
|
||||
|
||||
_textMacro = GUI::FindLabelMacros(Text);
|
||||
}
|
||||
|
||||
void GUILabel::ReadFromSavegame(Stream *in, GuiSvgVersion svg_ver) {
|
||||
GUIObject::ReadFromSavegame(in, svg_ver);
|
||||
Font = in->ReadInt32();
|
||||
TextColor = in->ReadInt32();
|
||||
Text = StrUtil::ReadString(in);
|
||||
if (svg_ver >= kGuiSvgVersion_350)
|
||||
TextAlignment = (HorAlignment)in->ReadInt32();
|
||||
|
||||
_textMacro = GUI::FindLabelMacros(Text);
|
||||
}
|
||||
|
||||
void GUILabel::WriteToSavegame(Stream *out) const {
|
||||
GUIObject::WriteToSavegame(out);
|
||||
out->WriteInt32(Font);
|
||||
out->WriteInt32(TextColor);
|
||||
StrUtil::WriteString(Text, out);
|
||||
out->WriteInt32(TextAlignment);
|
||||
}
|
||||
|
||||
} // namespace Shared
|
||||
} // namespace AGS
|
||||
} // namespace AGS3
|
||||
78
engines/ags/shared/gui/gui_label.h
Normal file
78
engines/ags/shared/gui/gui_label.h
Normal file
@@ -0,0 +1,78 @@
|
||||
/* 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 AGS_SHARED_GUI_GUI_LABEL_H
|
||||
#define AGS_SHARED_GUI_GUI_LABEL_H
|
||||
|
||||
#include "common/std/vector.h"
|
||||
#include "ags/shared/gui/gui_object.h"
|
||||
#include "ags/shared/util/string.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
namespace AGS {
|
||||
namespace Shared {
|
||||
|
||||
class GUILabel : public GUIObject {
|
||||
public:
|
||||
GUILabel();
|
||||
|
||||
bool HasAlphaChannel() const override;
|
||||
// Gets label's text property in original set form (with macros etc)
|
||||
String GetText() const;
|
||||
// Gets which macro are contained within label's text
|
||||
GUILabelMacro GetTextMacros() const;
|
||||
|
||||
// Operations
|
||||
Rect CalcGraphicRect(bool clipped) override;
|
||||
void Draw(Bitmap *ds, int x = 0, int y = 0) override;
|
||||
void SetText(const String &text);
|
||||
|
||||
// Serialization
|
||||
void ReadFromFile(Stream *in, GuiVersion gui_version) override;
|
||||
void WriteToFile(Stream *out) const override;
|
||||
void ReadFromSavegame(Shared::Stream *in, GuiSvgVersion svg_ver) override;
|
||||
void WriteToSavegame(Shared::Stream *out) const override;
|
||||
|
||||
// TODO: these members are currently public; hide them later
|
||||
public:
|
||||
String Text;
|
||||
int32_t Font;
|
||||
color_t TextColor;
|
||||
HorAlignment TextAlignment;
|
||||
|
||||
private:
|
||||
// Transforms the Text property to a drawn text, applies translation,
|
||||
// replaces macros, splits and wraps, etc; returns number of lines.
|
||||
int PrepareTextToDraw();
|
||||
|
||||
// Information on macros contained within Text field
|
||||
GUILabelMacro _textMacro;
|
||||
// prepared text buffer/cache
|
||||
// TODO: cache split lines instead?
|
||||
String _textToDraw;
|
||||
};
|
||||
|
||||
} // namespace Shared
|
||||
} // namespace AGS
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
453
engines/ags/shared/gui/gui_listbox.cpp
Normal file
453
engines/ags/shared/gui/gui_listbox.cpp
Normal file
@@ -0,0 +1,453 @@
|
||||
/* 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/std/algorithm.h"
|
||||
#include "ags/shared/gui/gui_listbox.h"
|
||||
#include "ags/shared/ac/game_version.h"
|
||||
#include "ags/shared/font/fonts.h"
|
||||
#include "ags/shared/gui/gui_main.h"
|
||||
#include "ags/shared/util/stream.h"
|
||||
#include "ags/shared/util/string_utils.h"
|
||||
|
||||
namespace AGS3 {
|
||||
namespace AGS {
|
||||
namespace Shared {
|
||||
|
||||
GUIListBox::GUIListBox() {
|
||||
ItemCount = 0;
|
||||
SelectedItem = 0;
|
||||
TopItem = 0;
|
||||
RowHeight = 0;
|
||||
VisibleItemCount = 0;
|
||||
Font = 0;
|
||||
TextColor = 0;
|
||||
SelectedTextColor = 7;
|
||||
ListBoxFlags = kListBox_DefFlags;
|
||||
SelectedBgColor = 16;
|
||||
TextAlignment = kHAlignLeft;
|
||||
|
||||
_scEventCount = 1;
|
||||
_scEventNames[0] = "SelectionChanged";
|
||||
_scEventArgs[0] = "GUIControl *control";
|
||||
}
|
||||
|
||||
bool GUIListBox::HasAlphaChannel() const {
|
||||
return is_font_antialiased(Font);
|
||||
}
|
||||
|
||||
int GUIListBox::GetItemAt(int x, int y) const {
|
||||
if (RowHeight <= 0 || IsInRightMargin(x))
|
||||
return -1;
|
||||
|
||||
int index = y / RowHeight + TopItem;
|
||||
if (index < 0 || index >= ItemCount)
|
||||
return -1;
|
||||
return index;
|
||||
}
|
||||
|
||||
bool GUIListBox::AreArrowsShown() const {
|
||||
return (ListBoxFlags & kListBox_ShowArrows) != 0;
|
||||
}
|
||||
|
||||
bool GUIListBox::IsBorderShown() const {
|
||||
return (ListBoxFlags & kListBox_ShowBorder) != 0;
|
||||
}
|
||||
|
||||
bool GUIListBox::IsSvgIndex() const {
|
||||
return (ListBoxFlags & kListBox_SvgIndex) != 0;
|
||||
}
|
||||
|
||||
bool GUIListBox::IsInRightMargin(int x) const {
|
||||
if (x >= (_width - get_fixed_pixel_size(6)) && IsBorderShown() && AreArrowsShown())
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
Rect GUIListBox::CalcGraphicRect(bool clipped) {
|
||||
if (clipped)
|
||||
return RectWH(0, 0, _width, _height);
|
||||
|
||||
// TODO: need to find a way to text position, or there'll be some repetition
|
||||
// have to precache text and size on some events:
|
||||
// - translation change
|
||||
// - macro value change (score, overhotspot etc)
|
||||
Rect rc = RectWH(0, 0, _width, _height);
|
||||
UpdateMetrics();
|
||||
const int width = _width - 1;
|
||||
const int pixel_size = get_fixed_pixel_size(1);
|
||||
int right_hand_edge = width - pixel_size - 1;
|
||||
// calculate the scroll bar's width if necessary
|
||||
if (ItemCount > VisibleItemCount &&IsBorderShown() && AreArrowsShown())
|
||||
right_hand_edge -= get_fixed_pixel_size(7);
|
||||
Line max_line;
|
||||
for (int item = 0; (item < VisibleItemCount) && (item + TopItem < ItemCount); ++item) {
|
||||
int at_y = pixel_size + item * RowHeight;
|
||||
int item_index = item + TopItem;
|
||||
PrepareTextToDraw(Items[item_index]);
|
||||
Line lpos = GUI::CalcTextPositionHor(_textToDraw.GetCStr(), Font, 1 + pixel_size, right_hand_edge, at_y + 1,
|
||||
(FrameAlignment)TextAlignment);
|
||||
max_line.X2 = MAX(max_line.X2, lpos.X2);
|
||||
}
|
||||
int last_line_y = pixel_size + 1 + (VisibleItemCount - 1) * RowHeight;
|
||||
// Include font fixes for the first and last text line,
|
||||
// in case graphical height is different, and there's a VerticalOffset
|
||||
Line vextent = GUI::CalcFontGraphicalVExtent(Font);
|
||||
Rect text_rc = RectWH(0, vextent.Y1, max_line.X2 - max_line.X1 + 1, last_line_y + (vextent.Y2 - vextent.Y1));
|
||||
return SumRects(rc, text_rc);
|
||||
}
|
||||
|
||||
int GUIListBox::AddItem(const String &text) {
|
||||
Items.push_back(text);
|
||||
SavedGameIndex.push_back(-1);
|
||||
ItemCount++;
|
||||
MarkChanged();
|
||||
return ItemCount - 1;
|
||||
}
|
||||
|
||||
void GUIListBox::Clear() {
|
||||
if (Items.empty())
|
||||
return;
|
||||
|
||||
Items.clear();
|
||||
SavedGameIndex.clear();
|
||||
ItemCount = 0;
|
||||
SelectedItem = 0;
|
||||
TopItem = 0;
|
||||
MarkChanged();
|
||||
}
|
||||
|
||||
void GUIListBox::Draw(Bitmap *ds, int x, int y) {
|
||||
const int width = _width - 1;
|
||||
const int height = _height - 1;
|
||||
const int pixel_size = get_fixed_pixel_size(1);
|
||||
|
||||
color_t text_color = ds->GetCompatibleColor(TextColor);
|
||||
color_t draw_color = ds->GetCompatibleColor(TextColor);
|
||||
if (IsBorderShown()) {
|
||||
ds->DrawRect(Rect(x, y, x + width, y + height), draw_color);
|
||||
if (pixel_size > 1)
|
||||
ds->DrawRect(Rect(x + 1, y + 1, x + width - 1, y + height - 1), draw_color);
|
||||
}
|
||||
|
||||
int right_hand_edge = (x + width) - pixel_size - 1;
|
||||
|
||||
// update the RowHeight and VisibleItemCount
|
||||
// FIXME: find a way to update this whenever relevant things change in the engine
|
||||
UpdateMetrics();
|
||||
|
||||
// draw the scroll bar in if necessary
|
||||
bool scrollbar = (ItemCount > VisibleItemCount) && IsBorderShown() && AreArrowsShown();
|
||||
if (scrollbar) {
|
||||
int xstrt, ystrt;
|
||||
ds->DrawRect(Rect(x + width - get_fixed_pixel_size(7), y, (x + (pixel_size - 1) + width) - get_fixed_pixel_size(7), y + height), draw_color);
|
||||
ds->DrawRect(Rect(x + width - get_fixed_pixel_size(7), y + height / 2, x + width, y + height / 2 + (pixel_size - 1)), draw_color);
|
||||
|
||||
xstrt = (x + width - get_fixed_pixel_size(6)) + (pixel_size - 1);
|
||||
ystrt = (y + height - 3) - get_fixed_pixel_size(5);
|
||||
|
||||
draw_color = ds->GetCompatibleColor(TextColor);
|
||||
ds->DrawTriangle(Triangle(xstrt, ystrt, xstrt + get_fixed_pixel_size(4), ystrt,
|
||||
xstrt + get_fixed_pixel_size(2),
|
||||
ystrt + get_fixed_pixel_size(5)), draw_color);
|
||||
|
||||
ystrt = y + 3;
|
||||
ds->DrawTriangle(Triangle(xstrt, ystrt + get_fixed_pixel_size(5),
|
||||
xstrt + get_fixed_pixel_size(4),
|
||||
ystrt + get_fixed_pixel_size(5),
|
||||
xstrt + get_fixed_pixel_size(2), ystrt), draw_color);
|
||||
|
||||
right_hand_edge -= get_fixed_pixel_size(7);
|
||||
}
|
||||
|
||||
Rect old_clip = ds->GetClip();
|
||||
if (scrollbar && GUI::Options.ClipControls)
|
||||
ds->SetClip(Rect(x, y, right_hand_edge + 1, y + _height - 1));
|
||||
for (int item = 0; (item < VisibleItemCount) && (item + TopItem < ItemCount); ++item) {
|
||||
int at_y = y + pixel_size + item * RowHeight;
|
||||
if (item + TopItem == SelectedItem) {
|
||||
text_color = ds->GetCompatibleColor(SelectedTextColor);
|
||||
if (SelectedBgColor > 0) {
|
||||
int stretch_to = (x + width) - pixel_size;
|
||||
// draw the SelectedItem item bar (if colour not transparent)
|
||||
draw_color = ds->GetCompatibleColor(SelectedBgColor);
|
||||
if ((VisibleItemCount < ItemCount) && IsBorderShown() && AreArrowsShown())
|
||||
stretch_to -= get_fixed_pixel_size(7);
|
||||
|
||||
ds->FillRect(Rect(x + pixel_size, at_y, stretch_to, at_y + RowHeight - pixel_size), draw_color);
|
||||
}
|
||||
} else
|
||||
text_color = ds->GetCompatibleColor(TextColor);
|
||||
|
||||
int item_index = item + TopItem;
|
||||
PrepareTextToDraw(Items[item_index]);
|
||||
|
||||
GUI::DrawTextAlignedHor(ds, _textToDraw.GetCStr(), Font, text_color, x + 1 + pixel_size, right_hand_edge, at_y + 1,
|
||||
(FrameAlignment)TextAlignment);
|
||||
}
|
||||
ds->SetClip(old_clip);
|
||||
}
|
||||
|
||||
int GUIListBox::InsertItem(int index, const String &text) {
|
||||
if (index < 0 || index > ItemCount)
|
||||
return -1;
|
||||
|
||||
Items.insert(Items.begin() + index, text);
|
||||
SavedGameIndex.insert(SavedGameIndex.begin() + index, -1);
|
||||
if (SelectedItem >= index)
|
||||
SelectedItem++;
|
||||
|
||||
ItemCount++;
|
||||
MarkChanged();
|
||||
return ItemCount - 1;
|
||||
}
|
||||
|
||||
void GUIListBox::RemoveItem(int index) {
|
||||
if (index < 0 || index >= ItemCount)
|
||||
return;
|
||||
|
||||
Items.erase(Items.begin() + index);
|
||||
SavedGameIndex.erase(SavedGameIndex.begin() + index);
|
||||
ItemCount--;
|
||||
|
||||
if (SelectedItem > index)
|
||||
SelectedItem--;
|
||||
if (SelectedItem >= ItemCount)
|
||||
SelectedItem = -1;
|
||||
MarkChanged();
|
||||
}
|
||||
|
||||
void GUIListBox::SetShowArrows(bool on) {
|
||||
if (on != ((ListBoxFlags & kListBox_ShowArrows) != 0))
|
||||
MarkChanged();
|
||||
if (on)
|
||||
ListBoxFlags |= kListBox_ShowArrows;
|
||||
else
|
||||
ListBoxFlags &= ~kListBox_ShowArrows;
|
||||
}
|
||||
|
||||
void GUIListBox::SetShowBorder(bool on) {
|
||||
if (on != ((ListBoxFlags & kListBox_ShowBorder) != 0))
|
||||
MarkChanged();
|
||||
if (on)
|
||||
ListBoxFlags |= kListBox_ShowBorder;
|
||||
else
|
||||
ListBoxFlags &= ~kListBox_ShowBorder;
|
||||
}
|
||||
|
||||
void GUIListBox::SetSvgIndex(bool on) {
|
||||
if (on)
|
||||
ListBoxFlags |= kListBox_SvgIndex;
|
||||
else
|
||||
ListBoxFlags &= ~kListBox_SvgIndex;
|
||||
}
|
||||
|
||||
void GUIListBox::SetFont(int font) {
|
||||
if (Font == font)
|
||||
return;
|
||||
Font = font;
|
||||
UpdateMetrics();
|
||||
MarkChanged();
|
||||
}
|
||||
|
||||
void GUIListBox::SetItemText(int index, const String &text) {
|
||||
if ((index >= 0) && (index < ItemCount) && (text != Items[index])) {
|
||||
Items[index] = text;
|
||||
MarkChanged();
|
||||
}
|
||||
}
|
||||
|
||||
bool GUIListBox::OnMouseDown() {
|
||||
if (IsInRightMargin(MousePos.X)) {
|
||||
int top_item = TopItem;
|
||||
if (MousePos.Y < _height / 2 && TopItem > 0)
|
||||
top_item = TopItem - 1;
|
||||
if (MousePos.Y >= _height / 2 && ItemCount > TopItem + VisibleItemCount)
|
||||
top_item = TopItem + 1;
|
||||
if (TopItem != top_item) {
|
||||
TopItem = top_item;
|
||||
MarkChanged();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int sel = GetItemAt(MousePos.X, MousePos.Y);
|
||||
if (sel < 0)
|
||||
return false;
|
||||
if (sel != SelectedItem) {
|
||||
SelectedItem = sel;
|
||||
MarkChanged();
|
||||
}
|
||||
IsActivated = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void GUIListBox::OnMouseMove(int x_, int y_) {
|
||||
MousePos.X = x_ - X;
|
||||
MousePos.Y = y_ - Y;
|
||||
}
|
||||
|
||||
void GUIListBox::OnResized() {
|
||||
UpdateMetrics();
|
||||
MarkChanged();
|
||||
}
|
||||
|
||||
void GUIListBox::UpdateMetrics() {
|
||||
int font_height = (_G(loaded_game_file_version) < kGameVersion_360_21) ?
|
||||
get_font_height(Font) : get_font_height_outlined(Font);
|
||||
RowHeight = font_height + get_fixed_pixel_size(2); // +1 top/bottom margin
|
||||
VisibleItemCount = _height / RowHeight;
|
||||
if (ItemCount <= VisibleItemCount)
|
||||
TopItem = 0; // reset scroll if all items are visible
|
||||
}
|
||||
|
||||
// TODO: replace string serialization with StrUtil::ReadString and WriteString
|
||||
// methods in the future, to keep this organized.
|
||||
void GUIListBox::WriteToFile(Stream *out) const {
|
||||
GUIObject::WriteToFile(out);
|
||||
out->WriteInt32(ItemCount);
|
||||
out->WriteInt32(Font);
|
||||
out->WriteInt32(TextColor);
|
||||
out->WriteInt32(SelectedTextColor);
|
||||
out->WriteInt32(ListBoxFlags);
|
||||
out->WriteInt32(TextAlignment);
|
||||
out->WriteInt32(SelectedBgColor);
|
||||
for (int i = 0; i < ItemCount; ++i)
|
||||
Items[i].Write(out);
|
||||
}
|
||||
|
||||
void GUIListBox::ReadFromFile(Stream *in, GuiVersion gui_version) {
|
||||
Clear();
|
||||
|
||||
GUIObject::ReadFromFile(in, gui_version);
|
||||
ItemCount = in->ReadInt32();
|
||||
if (gui_version < kGuiVersion_350) { // NOTE: reading into actual variables only for old savegame support
|
||||
SelectedItem = in->ReadInt32();
|
||||
TopItem = in->ReadInt32();
|
||||
MousePos.X = in->ReadInt32();
|
||||
MousePos.Y = in->ReadInt32();
|
||||
RowHeight = in->ReadInt32();
|
||||
VisibleItemCount = in->ReadInt32();
|
||||
}
|
||||
Font = in->ReadInt32();
|
||||
TextColor = in->ReadInt32();
|
||||
SelectedTextColor = in->ReadInt32();
|
||||
ListBoxFlags = in->ReadInt32();
|
||||
// reverse particular flags from older format
|
||||
if (gui_version < kGuiVersion_350)
|
||||
ListBoxFlags ^= kListBox_OldFmtXorMask;
|
||||
|
||||
if (gui_version >= kGuiVersion_272b) {
|
||||
if (gui_version < kGuiVersion_350) {
|
||||
TextAlignment = ConvertLegacyGUIAlignment((LegacyGUIAlignment)in->ReadInt32());
|
||||
in->ReadInt32(); // reserved1
|
||||
} else {
|
||||
TextAlignment = (HorAlignment)in->ReadInt32();
|
||||
}
|
||||
} else {
|
||||
TextAlignment = kHAlignLeft;
|
||||
}
|
||||
|
||||
if (gui_version >= kGuiVersion_unkn_107) {
|
||||
SelectedBgColor = in->ReadInt32();
|
||||
} else {
|
||||
SelectedBgColor = TextColor;
|
||||
if (SelectedBgColor == 0)
|
||||
SelectedBgColor = 16;
|
||||
}
|
||||
|
||||
// NOTE: we leave items in game data format as a potential support for defining
|
||||
// ListBox contents at design-time, although Editor does not support it as of 3.5.0.
|
||||
Items.resize(ItemCount);
|
||||
SavedGameIndex.resize(ItemCount, -1);
|
||||
for (int i = 0; i < ItemCount; ++i) {
|
||||
Items[i].Read(in);
|
||||
}
|
||||
|
||||
if (gui_version >= kGuiVersion_272d && gui_version < kGuiVersion_350 &&
|
||||
(ListBoxFlags & kListBox_SvgIndex)) { // NOTE: reading into actual variables only for old savegame support
|
||||
for (int i = 0; i < ItemCount; ++i)
|
||||
SavedGameIndex[i] = in->ReadInt16();
|
||||
}
|
||||
|
||||
if (TextColor == 0)
|
||||
TextColor = 16;
|
||||
|
||||
UpdateMetrics();
|
||||
}
|
||||
|
||||
void GUIListBox::ReadFromSavegame(Stream *in, GuiSvgVersion svg_ver) {
|
||||
GUIObject::ReadFromSavegame(in, svg_ver);
|
||||
// Properties
|
||||
ListBoxFlags = in->ReadInt32();
|
||||
Font = in->ReadInt32();
|
||||
if (svg_ver < kGuiSvgVersion_350) {
|
||||
// reverse particular flags from older format
|
||||
ListBoxFlags ^= kListBox_OldFmtXorMask;
|
||||
} else {
|
||||
SelectedBgColor = in->ReadInt32();
|
||||
SelectedTextColor = in->ReadInt32();
|
||||
TextAlignment = (HorAlignment)in->ReadInt32();
|
||||
TextColor = in->ReadInt32();
|
||||
}
|
||||
|
||||
// Items
|
||||
ItemCount = in->ReadInt32();
|
||||
Items.resize(ItemCount);
|
||||
SavedGameIndex.resize(ItemCount);
|
||||
for (int i = 0; i < ItemCount; ++i)
|
||||
Items[i] = StrUtil::ReadString(in);
|
||||
// TODO: investigate this, it might be unreasonable to save and read
|
||||
// savegame index like that because list of savegames may easily change
|
||||
// in between writing and restoring the game. Perhaps clearing and forcing
|
||||
// this list to update on load somehow may make more sense.
|
||||
if (ListBoxFlags & kListBox_SvgIndex)
|
||||
for (int i = 0; i < ItemCount; ++i)
|
||||
SavedGameIndex[i] = in->ReadInt16();
|
||||
TopItem = in->ReadInt32();
|
||||
SelectedItem = in->ReadInt32();
|
||||
|
||||
UpdateMetrics();
|
||||
}
|
||||
|
||||
void GUIListBox::WriteToSavegame(Stream *out) const {
|
||||
GUIObject::WriteToSavegame(out);
|
||||
// Properties
|
||||
out->WriteInt32(ListBoxFlags);
|
||||
out->WriteInt32(Font);
|
||||
out->WriteInt32(SelectedBgColor);
|
||||
out->WriteInt32(SelectedTextColor);
|
||||
out->WriteInt32(TextAlignment);
|
||||
out->WriteInt32(TextColor);
|
||||
|
||||
// Items
|
||||
out->WriteInt32(ItemCount);
|
||||
for (int i = 0; i < ItemCount; ++i)
|
||||
StrUtil::WriteString(Items[i], out);
|
||||
if (ListBoxFlags & kListBox_SvgIndex)
|
||||
for (int i = 0; i < ItemCount; ++i)
|
||||
out->WriteInt16(SavedGameIndex[i]);
|
||||
out->WriteInt32(TopItem);
|
||||
out->WriteInt32(SelectedItem);
|
||||
}
|
||||
|
||||
} // namespace Shared
|
||||
} // namespace AGS
|
||||
} // namespace AGS3
|
||||
103
engines/ags/shared/gui/gui_listbox.h
Normal file
103
engines/ags/shared/gui/gui_listbox.h
Normal file
@@ -0,0 +1,103 @@
|
||||
/* 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 AGS_SHARED_GUI_GUI_LISTBOX_H
|
||||
#define AGS_SHARED_GUI_GUI_LISTBOX_H
|
||||
|
||||
#include "common/std/vector.h"
|
||||
#include "ags/shared/gui/gui_object.h"
|
||||
#include "ags/shared/util/string.h"
|
||||
|
||||
namespace AGS3 {
|
||||
namespace AGS {
|
||||
namespace Shared {
|
||||
|
||||
class GUIListBox : public GUIObject {
|
||||
public:
|
||||
GUIListBox();
|
||||
|
||||
bool HasAlphaChannel() const override;
|
||||
bool AreArrowsShown() const;
|
||||
bool IsBorderShown() const;
|
||||
bool IsSvgIndex() const;
|
||||
bool IsInRightMargin(int x) const;
|
||||
int GetItemAt(int x, int y) const;
|
||||
|
||||
// Operations
|
||||
int AddItem(const String &text);
|
||||
void Clear();
|
||||
Rect CalcGraphicRect(bool clipped) override;
|
||||
void Draw(Bitmap *ds, int x = 0, int y = 0) override;
|
||||
int InsertItem(int index, const String &text);
|
||||
void RemoveItem(int index);
|
||||
void SetShowArrows(bool on);
|
||||
void SetShowBorder(bool on);
|
||||
void SetSvgIndex(bool on); // TODO: work around this
|
||||
void SetFont(int font);
|
||||
void SetItemText(int index, const String &textt);
|
||||
|
||||
// Events
|
||||
bool OnMouseDown() override;
|
||||
void OnMouseMove(int x, int y) override;
|
||||
void OnResized() override;
|
||||
|
||||
// Serialization
|
||||
void ReadFromFile(Stream *in, GuiVersion gui_version) override;
|
||||
void WriteToFile(Stream *out) const override;
|
||||
void ReadFromSavegame(Shared::Stream *in, GuiSvgVersion svg_ver) override;
|
||||
void WriteToSavegame(Shared::Stream *out) const override;
|
||||
|
||||
// TODO: these members are currently public; hide them later
|
||||
public:
|
||||
int32_t Font;
|
||||
color_t TextColor;
|
||||
HorAlignment TextAlignment;
|
||||
color_t SelectedBgColor;
|
||||
color_t SelectedTextColor;
|
||||
int32_t RowHeight;
|
||||
int32_t VisibleItemCount;
|
||||
|
||||
std::vector<String> Items;
|
||||
std::vector<int16_t> SavedGameIndex;
|
||||
int32_t SelectedItem;
|
||||
int32_t TopItem;
|
||||
Point MousePos;
|
||||
|
||||
// TODO: remove these later
|
||||
int32_t ItemCount;
|
||||
|
||||
private:
|
||||
int32_t ListBoxFlags;
|
||||
|
||||
// Updates dynamic metrics such as row height and others
|
||||
void UpdateMetrics();
|
||||
// Applies translation
|
||||
void PrepareTextToDraw(const String &text);
|
||||
|
||||
// prepared text buffer/cache
|
||||
String _textToDraw;
|
||||
};
|
||||
|
||||
} // namespace Shared
|
||||
} // namespace AGS
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
1008
engines/ags/shared/gui/gui_main.cpp
Normal file
1008
engines/ags/shared/gui/gui_main.cpp
Normal file
File diff suppressed because it is too large
Load Diff
301
engines/ags/shared/gui/gui_main.h
Normal file
301
engines/ags/shared/gui/gui_main.h
Normal file
@@ -0,0 +1,301 @@
|
||||
/* 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 AGS_SHARED_GUI_GUI_MAIN_H
|
||||
#define AGS_SHARED_GUI_GUI_MAIN_H
|
||||
|
||||
#include "common/std/vector.h"
|
||||
#include "ags/engine/ac/draw.h"
|
||||
#include "ags/shared/ac/common.h"
|
||||
#include "ags/shared/ac/common_defines.h" // TODO: split out gui drawing helpers
|
||||
#include "ags/shared/gfx/gfx_def.h" // TODO: split out gui drawing helpers
|
||||
#include "ags/shared/gui/gui_defines.h"
|
||||
#include "ags/shared/util/error.h"
|
||||
#include "ags/shared/util/geometry.h"
|
||||
#include "ags/shared/util/string.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
// Forward declaration
|
||||
namespace AGS {
|
||||
namespace Shared {
|
||||
class Stream;
|
||||
}
|
||||
}
|
||||
using namespace AGS; // FIXME later
|
||||
|
||||
class SplitLines;
|
||||
|
||||
#define LEGACY_MAX_OBJS_ON_GUI 30
|
||||
|
||||
#define GUIMAIN_LEGACY_RESERVED_INTS 5
|
||||
#define GUIMAIN_LEGACY_NAME_LENGTH 16
|
||||
#define GUIMAIN_LEGACY_EVENTHANDLER_LENGTH 20
|
||||
#define GUIMAIN_LEGACY_TW_FLAGS_SIZE 4
|
||||
|
||||
namespace AGS {
|
||||
namespace Shared {
|
||||
|
||||
// Legacy GUIMain visibility state, which combined Visible property and override factor
|
||||
enum LegacyGUIVisState {
|
||||
kGUIVisibility_LockedOff = -1, // locked hidden (used by PopupMouseY guis)
|
||||
kGUIVisibility_Off = 0, // hidden
|
||||
kGUIVisibility_On = 1 // shown
|
||||
};
|
||||
|
||||
class Bitmap;
|
||||
class GUIObject;
|
||||
|
||||
|
||||
class GUIMain {
|
||||
public:
|
||||
static String FixupGUIName(const String &name);
|
||||
|
||||
public:
|
||||
GUIMain();
|
||||
|
||||
void InitDefaults();
|
||||
|
||||
// Tells if the gui background supports alpha channel
|
||||
bool HasAlphaChannel() const;
|
||||
// Tells if GUI will react on clicking on it
|
||||
bool IsClickable() const { return (_flags & kGUIMain_Clickable) != 0; }
|
||||
// Tells if GUI's visibility is overridden and it won't be displayed on
|
||||
// screen regardless of Visible property (until concealed mode is off).
|
||||
bool IsConcealed() const { return (_flags & kGUIMain_Concealed) != 0; }
|
||||
// Tells if gui is actually meant to be displayed on screen.
|
||||
// Normally Visible property determines whether GUI is allowed to be seen,
|
||||
// but there may be other settings that override it.
|
||||
bool IsDisplayed() const { return IsVisible() && !IsConcealed(); }
|
||||
// Tells if given coordinates are within interactable area of gui
|
||||
// NOTE: this currently tests for actual visibility and Clickable property
|
||||
bool IsInteractableAt(int x, int y) const;
|
||||
// Tells if gui is a text window
|
||||
bool IsTextWindow() const { return (_flags & kGUIMain_TextWindow) != 0; }
|
||||
// Tells if GUI is *allowed* to be displayed and interacted with.
|
||||
// This does not necessarily mean that it is displayed right now, because
|
||||
// GUI may be hidden for other reasons, including overriding behavior.
|
||||
// For example GUI with kGUIPopupMouseY style will not be shown unless
|
||||
// mouse cursor is at certain position on screen.
|
||||
bool IsVisible() const { return (_flags & kGUIMain_Visible) != 0; }
|
||||
|
||||
// Tells if GUI has graphically changed recently
|
||||
bool HasChanged() const { return _hasChanged; }
|
||||
bool HasControlsChanged() const { return _hasControlsChanged; }
|
||||
// Manually marks GUI as graphically changed
|
||||
// NOTE: this only matters if GUI's own graphic changes (content, size etc),
|
||||
// but not its state (visible) or texture drawing mode (transparency, etc).
|
||||
void MarkChanged();
|
||||
// Marks GUI as having any of its controls changed its looks.
|
||||
void MarkControlChanged();
|
||||
// Clears changed flag
|
||||
void ClearChanged();
|
||||
// Notify GUI about any of its controls changing its location.
|
||||
void NotifyControlPosition();
|
||||
// Notify GUI about one of its controls changing its interactive state.
|
||||
void NotifyControlState(int objid, bool mark_changed);
|
||||
// Resets control-under-mouse detection.
|
||||
void ResetOverControl();
|
||||
|
||||
// Finds a control under given screen coordinates, returns control's child ID.
|
||||
// Optionally allows extra leeway (offset in all directions) to let the user grab tiny controls.
|
||||
// Optionally only allows clickable controls, ignoring non-clickable ones.
|
||||
int32_t FindControlAt(int atx, int aty, int leeway = 0, bool must_be_clickable = true) const;
|
||||
// Gets the number of the GUI child controls
|
||||
int32_t GetControlCount() const;
|
||||
// Gets control by its child's index
|
||||
GUIObject *GetControl(int32_t index) const;
|
||||
// Gets child control's type, looks up with child's index
|
||||
GUIControlType GetControlType(int32_t index) const;
|
||||
// Gets child control's global ID, looks up with child's index
|
||||
int32_t GetControlID(int32_t index) const;
|
||||
// Gets an array of child control indexes in the z-order, from bottom to top
|
||||
const std::vector<int> &GetControlsDrawOrder() const;
|
||||
|
||||
// Child control management
|
||||
// Note that currently GUIMain does not own controls (should not delete them)
|
||||
void AddControl(GUIControlType type, int32_t id, GUIObject *control);
|
||||
void RemoveAllControls();
|
||||
|
||||
// Operations
|
||||
bool BringControlToFront(int32_t index);
|
||||
void DrawSelf(Bitmap *ds);
|
||||
void DrawWithControls(Bitmap *ds);
|
||||
// Polls GUI state, providing current cursor (mouse) coordinates
|
||||
void Poll(int mx, int my);
|
||||
HError RebuildArray();
|
||||
void ResortZOrder();
|
||||
bool SendControlToBack(int32_t index);
|
||||
// Sets whether GUI should react to player clicking on it
|
||||
void SetClickable(bool on);
|
||||
// Override GUI visibility; when in concealed mode GUI won't show up
|
||||
// even if Visible = true
|
||||
void SetConceal(bool on);
|
||||
// Attempts to change control's zorder; returns if zorder changed
|
||||
bool SetControlZOrder(int32_t index, int zorder);
|
||||
// Changes GUI style to the text window or back
|
||||
void SetTextWindow(bool on);
|
||||
// Sets GUI transparency as a percentage (0 - 100) where 100 = invisible
|
||||
void SetTransparencyAsPercentage(int percent);
|
||||
// Sets whether GUI is allowed to be displayed on screen
|
||||
void SetVisible(bool on);
|
||||
|
||||
// Events
|
||||
void OnMouseButtonDown(int mx, int my);
|
||||
void OnMouseButtonUp();
|
||||
|
||||
// Serialization
|
||||
void ReadFromFile(Stream *in, GuiVersion gui_version);
|
||||
void WriteToFile(Stream *out) const;
|
||||
// TODO: move to engine, into gui savegame component unit
|
||||
// (should read/write GUI properties accessing them by interface)
|
||||
void ReadFromSavegame(Stream *in, GuiSvgVersion svg_version);
|
||||
void WriteToSavegame(Stream *out) const;
|
||||
|
||||
private:
|
||||
void DrawBlob(Bitmap *ds, int x, int y, color_t draw_color);
|
||||
// Same as FindControlAt but expects local space coordinates
|
||||
int32_t FindControlAtLocal(int atx, int aty, int leeway, bool must_be_clickable) const;
|
||||
|
||||
// TODO: all members are currently public; hide them later
|
||||
public:
|
||||
int32_t ID; // GUI identifier
|
||||
String Name; // the name of the GUI
|
||||
|
||||
int32_t X;
|
||||
int32_t Y;
|
||||
int32_t Width;
|
||||
int32_t Height;
|
||||
color_t BgColor; // background color
|
||||
int32_t BgImage; // background sprite index
|
||||
color_t FgColor; // foreground color (used as border color in normal GUIs,
|
||||
// and text color in text windows)
|
||||
int32_t Padding; // padding surrounding a GUI text window
|
||||
GUIPopupStyle PopupStyle; // GUI popup behavior
|
||||
int32_t PopupAtMouseY; // popup when _G(mousey) < this
|
||||
int32_t Transparency; // "incorrect" alpha (in legacy 255-range units)
|
||||
int32_t ZOrder;
|
||||
|
||||
int32_t FocusCtrl; // which control has the focus
|
||||
int32_t HighlightCtrl; // which control has the bounding selection rect
|
||||
int32_t MouseOverCtrl; // which control has the mouse cursor over it
|
||||
int32_t MouseDownCtrl; // which control has the mouse button pressed on it
|
||||
Point MouseWasAt; // last mouse cursor position
|
||||
|
||||
String OnClickHandler; // script function name
|
||||
|
||||
private:
|
||||
int32_t _flags; // style and behavior flags
|
||||
bool _hasChanged; // flag tells whether GUI has graphically changed recently
|
||||
bool _hasControlsChanged;
|
||||
bool _polling; // inside the polling process
|
||||
|
||||
// Array of types and control indexes in global GUI object arrays;
|
||||
// maps GUI child slots to actual controls and used for rebuilding Controls array
|
||||
typedef std::pair<GUIControlType, int32_t> ControlRef;
|
||||
std::vector<ControlRef> _ctrlRefs;
|
||||
// Array of child control references (not exclusively owned!)
|
||||
std::vector<GUIObject *> _controls;
|
||||
// Sorted array of controls in z-order.
|
||||
std::vector<int> _ctrlDrawOrder;
|
||||
};
|
||||
|
||||
|
||||
namespace GUI {
|
||||
extern GuiVersion GameGuiVersion;
|
||||
extern GuiOptions Options;
|
||||
|
||||
// Applies current text direction setting (may depend on multiple factors)
|
||||
String ApplyTextDirection(const String &text);
|
||||
// Calculates the text's draw position, given the alignment
|
||||
// optionally returns the real graphical rect that the text would occupy
|
||||
Point CalcTextPosition(const char *text, int font, const Rect &frame, FrameAlignment align, Rect *gr_rect = nullptr);
|
||||
// Calculates the text's draw position and horizontal extent,
|
||||
// using strictly horizontal alignment
|
||||
Line CalcTextPositionHor(const char *text, int font, int x1, int x2, int y, FrameAlignment align);
|
||||
// Calculates the graphical rect that the text would occupy
|
||||
// if drawn at the given coordinates
|
||||
Rect CalcTextGraphicalRect(const char *text, int font, const Point &at);
|
||||
// Calculates the graphical rect that the text would occupy
|
||||
// if drawn aligned to the given frame
|
||||
Rect CalcTextGraphicalRect(const char *text, int font, const Rect &frame, FrameAlignment align);
|
||||
// Calculates a vertical graphical extent for a given font,
|
||||
// which is a top and bottom offsets in zero-based coordinates.
|
||||
// NOTE: this applies font size fixups.
|
||||
Line CalcFontGraphicalVExtent(int font);
|
||||
// Draw standart "shading" effect over rectangle
|
||||
void DrawDisabledEffect(Bitmap *ds, const Rect &rc);
|
||||
// Draw text aligned inside rectangle
|
||||
void DrawTextAligned(Bitmap *ds, const char *text, int font, color_t text_color, const Rect &frame, FrameAlignment align);
|
||||
// Draw text aligned horizontally inside given bounds
|
||||
void DrawTextAlignedHor(Bitmap *ds, const char *text, int font, color_t text_color, int x1, int x2, int y, FrameAlignment align);
|
||||
|
||||
// Parses the string and returns combination of label macro flags
|
||||
GUILabelMacro FindLabelMacros(const String &text);
|
||||
// Applies text transformation necessary for rendering, in accordance to the
|
||||
// current game settings, such as right-to-left render, and anything else
|
||||
String TransformTextForDrawing(const String &text, bool translate, bool apply_direction);
|
||||
// Wraps given text to make it fit into width, stores it in the lines;
|
||||
// apply_direction param tells whether text direction setting should be applied
|
||||
size_t SplitLinesForDrawing(const char *text, bool apply_direction, SplitLines &lines, int font, int width, size_t max_lines = -1);
|
||||
|
||||
// Mark all existing GUI for redraw
|
||||
void MarkAllGUIForUpdate(bool redraw, bool reset_over_ctrl);
|
||||
// Mark all translatable GUI controls for redraw
|
||||
void MarkForTranslationUpdate();
|
||||
// Mark all GUI which use the given font for recalculate/redraw;
|
||||
// pass -1 to update all the textual controls together
|
||||
void MarkForFontUpdate(int font);
|
||||
// Mark labels that acts as special text placeholders for redraw
|
||||
void MarkSpecialLabelsForUpdate(GUILabelMacro macro);
|
||||
// Mark inventory windows for redraw, optionally only ones linked to given character;
|
||||
// also marks buttons with inventory icon mode
|
||||
void MarkInventoryForUpdate(int char_id, bool is_player);
|
||||
|
||||
// Reads all GUIs and their controls.
|
||||
// WARNING: the data is read into the global arrays (guis, guibuts, and so on)
|
||||
// TODO: remove is_savegame param after dropping support for old saves
|
||||
// because only they use ReadGUI to read runtime GUI data
|
||||
HError ReadGUI(Stream *in, bool is_savegame = false);
|
||||
// Writes all GUIs and their controls.
|
||||
// WARNING: the data is written from the global arrays (guis, guibuts, and so on)
|
||||
void WriteGUI(Stream *out);
|
||||
// Converts legacy GUIVisibility into appropriate GUIMain properties
|
||||
void ApplyLegacyVisibility(GUIMain &gui, LegacyGUIVisState vis);
|
||||
|
||||
// Rebuilds GUIs, connecting them to the child controls in memory.
|
||||
// WARNING: the data is processed in the global arrays (guis, guibuts, and so on)
|
||||
HError RebuildGUI();
|
||||
}
|
||||
|
||||
} // namespace Shared
|
||||
} // namespace AGS
|
||||
|
||||
extern int get_adjusted_spritewidth(int spr);
|
||||
extern int get_adjusted_spriteheight(int spr);
|
||||
extern bool is_sprite_alpha(int spr);
|
||||
|
||||
extern void set_eip_guiobj(int eip);
|
||||
extern int get_eip_guiobj();
|
||||
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
223
engines/ags/shared/gui/gui_object.cpp
Normal file
223
engines/ags/shared/gui/gui_object.cpp
Normal file
@@ -0,0 +1,223 @@
|
||||
/* 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 "ags/shared/ac/common.h" // quit
|
||||
#include "ags/shared/gui/gui_main.h"
|
||||
#include "ags/shared/gui/gui_object.h"
|
||||
#include "ags/shared/util/stream.h"
|
||||
|
||||
namespace AGS3 {
|
||||
namespace AGS {
|
||||
namespace Shared {
|
||||
|
||||
GUIObject::GUIObject() {
|
||||
Id = 0;
|
||||
ParentId = 0;
|
||||
Flags = kGUICtrl_DefFlags;
|
||||
X = 0;
|
||||
Y = 0;
|
||||
_width = 0;
|
||||
_height = 0;
|
||||
ZOrder = -1;
|
||||
IsActivated = false;
|
||||
_transparency = 0;
|
||||
_scEventCount = 0;
|
||||
_hasChanged = true;
|
||||
}
|
||||
|
||||
String GUIObject::GetScriptName() const {
|
||||
return Name;
|
||||
}
|
||||
|
||||
int GUIObject::GetEventCount() const {
|
||||
return _scEventCount;
|
||||
}
|
||||
|
||||
String GUIObject::GetEventName(int event) const {
|
||||
if (event < 0 || event >= _scEventCount)
|
||||
return "";
|
||||
return _scEventNames[event];
|
||||
}
|
||||
|
||||
String GUIObject::GetEventArgs(int event) const {
|
||||
if (event < 0 || event >= _scEventCount)
|
||||
return "";
|
||||
return _scEventArgs[event];
|
||||
}
|
||||
|
||||
bool GUIObject::IsOverControl(int x, int y, int leeway) const {
|
||||
return x >= X && y >= Y && x < (X + _width + leeway) && y < (Y + _height + leeway);
|
||||
}
|
||||
|
||||
void GUIObject::SetClickable(bool on) {
|
||||
if (on != ((Flags & kGUICtrl_Clickable) != 0)) {
|
||||
Flags = (Flags & ~kGUICtrl_Clickable) | kGUICtrl_Clickable * on;
|
||||
MarkStateChanged(false, false); // update cursor-over-control only
|
||||
}
|
||||
}
|
||||
|
||||
void GUIObject::SetEnabled(bool on) {
|
||||
if (on != ((Flags & kGUICtrl_Enabled) != 0)) {
|
||||
Flags = (Flags & ~kGUICtrl_Enabled) | kGUICtrl_Enabled * on;
|
||||
MarkStateChanged(true, true); // may change looks, and update cursor-over-control
|
||||
}
|
||||
}
|
||||
|
||||
void GUIObject::SetSize(int width, int height) {
|
||||
if (_width != width || _height != height) {
|
||||
_width = width;
|
||||
_height = height;
|
||||
OnResized();
|
||||
}
|
||||
}
|
||||
|
||||
void GUIObject::SetTranslated(bool on) {
|
||||
if (on != ((Flags & kGUICtrl_Translated) != 0)) {
|
||||
Flags = (Flags & ~kGUICtrl_Translated) | kGUICtrl_Translated * on;
|
||||
MarkChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void GUIObject::SetVisible(bool on) {
|
||||
if (on != ((Flags & kGUICtrl_Visible) != 0)) {
|
||||
Flags = (Flags & ~kGUICtrl_Visible) | kGUICtrl_Visible * on;
|
||||
MarkStateChanged(false, true); // for software mode, and to update cursor-over-control
|
||||
}
|
||||
}
|
||||
|
||||
void GUIObject::SetTransparency(int trans) {
|
||||
if (_transparency != trans) {
|
||||
_transparency = trans;
|
||||
MarkParentChanged(); // for software mode
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: replace string serialization with StrUtil::ReadString and WriteString
|
||||
// methods in the future, to keep this organized.
|
||||
void GUIObject::WriteToFile(Stream *out) const {
|
||||
out->WriteInt32(Flags);
|
||||
out->WriteInt32(X);
|
||||
out->WriteInt32(Y);
|
||||
out->WriteInt32(_width);
|
||||
out->WriteInt32(_height);
|
||||
out->WriteInt32(ZOrder);
|
||||
Name.Write(out);
|
||||
out->WriteInt32(_scEventCount);
|
||||
for (int i = 0; i < _scEventCount; ++i)
|
||||
EventHandlers[i].Write(out);
|
||||
}
|
||||
|
||||
void GUIObject::ReadFromFile(Stream *in, GuiVersion gui_version) {
|
||||
Flags = in->ReadInt32();
|
||||
// reverse particular flags from older format
|
||||
if (gui_version < kGuiVersion_350)
|
||||
Flags ^= kGUICtrl_OldFmtXorMask;
|
||||
X = in->ReadInt32();
|
||||
Y = in->ReadInt32();
|
||||
_width = in->ReadInt32();
|
||||
_height = in->ReadInt32();
|
||||
ZOrder = in->ReadInt32();
|
||||
if (gui_version < kGuiVersion_350) { // NOTE: reading into actual variables only for old savegame support
|
||||
IsActivated = in->ReadInt32() != 0;
|
||||
}
|
||||
|
||||
if (gui_version >= kGuiVersion_unkn_106)
|
||||
Name.Read(in);
|
||||
else
|
||||
Name.Free();
|
||||
|
||||
for (int i = 0; i < _scEventCount; ++i) {
|
||||
EventHandlers[i].Free();
|
||||
}
|
||||
|
||||
if (gui_version >= kGuiVersion_unkn_108) {
|
||||
int evt_count = in->ReadInt32();
|
||||
if (evt_count > _scEventCount)
|
||||
quit("Error: too many control events, need newer version");
|
||||
for (int i = 0; i < evt_count; ++i) {
|
||||
EventHandlers[i].Read(in);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GUIObject::ReadFromSavegame(Stream *in, GuiSvgVersion svg_ver) {
|
||||
// Properties
|
||||
Flags = in->ReadInt32();
|
||||
// reverse particular flags from older format
|
||||
if (svg_ver < kGuiSvgVersion_350)
|
||||
Flags ^= kGUICtrl_OldFmtXorMask;
|
||||
X = in->ReadInt32();
|
||||
Y = in->ReadInt32();
|
||||
_width = in->ReadInt32();
|
||||
_height = in->ReadInt32();
|
||||
ZOrder = in->ReadInt32();
|
||||
// Dynamic state
|
||||
IsActivated = in->ReadBool() ? 1 : 0;
|
||||
if (svg_ver >= kGuiSvgVersion_36023) {
|
||||
_transparency = in->ReadInt32();
|
||||
in->ReadInt32(); // reserve 3 ints
|
||||
in->ReadInt32();
|
||||
in->ReadInt32();
|
||||
}
|
||||
}
|
||||
|
||||
void GUIObject::WriteToSavegame(Stream *out) const {
|
||||
// Properties
|
||||
out->WriteInt32(Flags);
|
||||
out->WriteInt32(X);
|
||||
out->WriteInt32(Y);
|
||||
out->WriteInt32(_width);
|
||||
out->WriteInt32(_height);
|
||||
out->WriteInt32(ZOrder);
|
||||
// Dynamic state
|
||||
out->WriteBool(IsActivated != 0);
|
||||
out->WriteInt32(_transparency);
|
||||
out->WriteInt32(0); // reserve 3 ints
|
||||
out->WriteInt32(0);
|
||||
out->WriteInt32(0);
|
||||
}
|
||||
|
||||
|
||||
HorAlignment ConvertLegacyGUIAlignment(LegacyGUIAlignment align) {
|
||||
switch (align) {
|
||||
case kLegacyGUIAlign_Right:
|
||||
return kHAlignRight;
|
||||
case kLegacyGUIAlign_Center:
|
||||
return kHAlignCenter;
|
||||
default:
|
||||
return kHAlignLeft;
|
||||
}
|
||||
}
|
||||
|
||||
LegacyGUIAlignment GetLegacyGUIAlignment(HorAlignment align) {
|
||||
switch (align) {
|
||||
case kHAlignRight:
|
||||
return kLegacyGUIAlign_Right;
|
||||
case kHAlignCenter:
|
||||
return kLegacyGUIAlign_Center;
|
||||
default:
|
||||
return kLegacyGUIAlign_Left;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Shared
|
||||
} // namespace AGS
|
||||
} // namespace AGS3
|
||||
172
engines/ags/shared/gui/gui_object.h
Normal file
172
engines/ags/shared/gui/gui_object.h
Normal file
@@ -0,0 +1,172 @@
|
||||
/* 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 AGS_SHARED_GUI_GUI_OBJECT_H
|
||||
#define AGS_SHARED_GUI_GUI_OBJECT_H
|
||||
|
||||
#include "ags/shared/core/types.h"
|
||||
#include "ags/shared/gfx/bitmap.h"
|
||||
#include "ags/shared/gui/gui_defines.h"
|
||||
#include "ags/shared/util/string.h"
|
||||
#include "ags/globals.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
struct KeyInput;
|
||||
|
||||
namespace AGS {
|
||||
namespace Shared {
|
||||
|
||||
enum LegacyGUIAlignment {
|
||||
kLegacyGUIAlign_Left = 0,
|
||||
kLegacyGUIAlign_Right = 1,
|
||||
kLegacyGUIAlign_Center = 2
|
||||
};
|
||||
|
||||
class GUIObject {
|
||||
public:
|
||||
GUIObject();
|
||||
virtual ~GUIObject() {}
|
||||
|
||||
String GetScriptName() const;
|
||||
|
||||
String GetEventArgs(int event) const;
|
||||
int GetEventCount() const;
|
||||
String GetEventName(int event) const;
|
||||
bool IsClickable() const { return (Flags & kGUICtrl_Clickable) != 0; }
|
||||
bool IsDeleted() const { return (Flags & kGUICtrl_Deleted) != 0; }
|
||||
bool IsEnabled() const { return (Flags & kGUICtrl_Enabled) != 0; }
|
||||
bool IsTranslated() const { return (Flags & kGUICtrl_Translated) != 0; }
|
||||
bool IsVisible() const { return (Flags & kGUICtrl_Visible) != 0; }
|
||||
// overridable routine to determine whether the mouse is over the control
|
||||
virtual bool IsOverControl(int x, int y, int leeway) const;
|
||||
Size GetSize() const { return Size(_width, _height); }
|
||||
int GetWidth() const { return _width; }
|
||||
int GetHeight() const { return _height; }
|
||||
int GetTransparency() const { return _transparency; }
|
||||
// Compatibility: should the control's graphic be clipped to its x,y,w,h
|
||||
virtual bool IsContentClipped() const { return true; }
|
||||
// Tells if the object image supports alpha channel
|
||||
virtual bool HasAlphaChannel() const { return false; }
|
||||
|
||||
// Operations
|
||||
// Returns the (untransformed!) visual rectangle of this control,
|
||||
// in *relative* coordinates, optionally clipped by the logical size
|
||||
virtual Rect CalcGraphicRect(bool /*clipped*/) {
|
||||
return RectWH(0, 0, _width, _height);
|
||||
}
|
||||
virtual void Draw(Bitmap *ds, int x = 0, int y = 0) {
|
||||
(void)ds; (void)x; (void)y;
|
||||
}
|
||||
void SetClickable(bool on);
|
||||
void SetEnabled(bool on);
|
||||
void SetSize(int width, int height);
|
||||
inline void SetWidth(int width) { SetSize(width, _height); }
|
||||
inline void SetHeight(int height) { SetSize(_width, height); }
|
||||
void SetTranslated(bool on);
|
||||
void SetVisible(bool on);
|
||||
void SetTransparency(int trans);
|
||||
|
||||
// Events
|
||||
// Key pressed for control
|
||||
virtual void OnKeyPress(const KeyInput &) {}
|
||||
// Mouse button down - return 'True' to lock focus
|
||||
virtual bool OnMouseDown() {
|
||||
return false;
|
||||
}
|
||||
// Mouse moves onto control
|
||||
virtual void OnMouseEnter() {
|
||||
}
|
||||
// Mouse moves off control
|
||||
virtual void OnMouseLeave() {
|
||||
}
|
||||
// Mouse moves over control - x,y relative to gui
|
||||
virtual void OnMouseMove(int /*x*/, int /*y*/) {
|
||||
}
|
||||
// Mouse button up
|
||||
virtual void OnMouseUp() {
|
||||
}
|
||||
// Control was resized
|
||||
virtual void OnResized() { MarkPositionChanged(true); }
|
||||
|
||||
// Serialization
|
||||
virtual void ReadFromFile(Shared::Stream *in, GuiVersion gui_version);
|
||||
virtual void WriteToFile(Shared::Stream *out) const;
|
||||
virtual void ReadFromSavegame(Shared::Stream *in, GuiSvgVersion svg_ver);
|
||||
virtual void WriteToSavegame(Shared::Stream *out) const;
|
||||
|
||||
// TODO: these members are currently public; hide them later
|
||||
public:
|
||||
// Manually marks GUIObject as graphically changed
|
||||
// NOTE: this only matters if control's own graphic changes, but not its
|
||||
// logical (visible, clickable, etc) or visual (e.g. transparency) state.
|
||||
void MarkChanged();
|
||||
// Notifies parent GUI that this control has changed its visual state
|
||||
void MarkParentChanged();
|
||||
// Notifies parent GUI that this control has changed its location (pos, size)
|
||||
void MarkPositionChanged(bool self_changed);
|
||||
// Notifies parent GUI that this control's interactive state has changed
|
||||
void MarkStateChanged(bool self_changed, bool parent_changed);
|
||||
bool HasChanged() const { return _hasChanged; };
|
||||
void ClearChanged();
|
||||
|
||||
int32_t Id; // GUI object's identifier
|
||||
int32_t ParentId; // id of parent GUI
|
||||
String Name; // script name
|
||||
|
||||
int32_t X;
|
||||
int32_t Y;
|
||||
int32_t ZOrder;
|
||||
bool IsActivated; // signals user interaction
|
||||
|
||||
String EventHandlers[MAX_GUIOBJ_EVENTS]; // script function names
|
||||
|
||||
protected:
|
||||
uint32_t Flags; // generic style and behavior flags
|
||||
int32_t _width;
|
||||
int32_t _height;
|
||||
int32_t _transparency; // "incorrect" alpha (in legacy 255-range units)
|
||||
bool _hasChanged;
|
||||
|
||||
// TODO: explicit event names & handlers for every event
|
||||
// FIXME: these must be static!! per type
|
||||
int32_t _scEventCount; // number of supported script events
|
||||
String _scEventNames[MAX_GUIOBJ_EVENTS]; // script event names
|
||||
String _scEventArgs[MAX_GUIOBJ_EVENTS]; // script handler params
|
||||
};
|
||||
|
||||
// Converts legacy alignment type used in GUI Label/ListBox data (only left/right/center)
|
||||
HorAlignment ConvertLegacyGUIAlignment(LegacyGUIAlignment align);
|
||||
LegacyGUIAlignment GetLegacyGUIAlignment(HorAlignment align);
|
||||
|
||||
} // namespace Shared
|
||||
} // namespace AGS
|
||||
|
||||
// Tells if all controls are disabled
|
||||
|
||||
// Tells if the given control is considered enabled, taking global flag into account
|
||||
inline bool IsGUIEnabled(AGS::Shared::GUIObject *g) {
|
||||
return (_G(all_buttons_disabled) < 0) && g->IsEnabled();
|
||||
}
|
||||
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
284
engines/ags/shared/gui/gui_slider.cpp
Normal file
284
engines/ags/shared/gui/gui_slider.cpp
Normal file
@@ -0,0 +1,284 @@
|
||||
/* 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/std/algorithm.h"
|
||||
#include "ags/shared/ac/sprite_cache.h"
|
||||
#include "ags/shared/gui/gui_main.h"
|
||||
#include "ags/shared/gui/gui_slider.h"
|
||||
#include "ags/shared/util/stream.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
namespace AGS {
|
||||
namespace Shared {
|
||||
|
||||
GUISlider::GUISlider() {
|
||||
MinValue = 0;
|
||||
MaxValue = 10;
|
||||
Value = 0;
|
||||
BgImage = 0;
|
||||
HandleImage = 0;
|
||||
HandleOffset = 0;
|
||||
IsMousePressed = false;
|
||||
|
||||
_scEventCount = 1;
|
||||
_scEventNames[0] = "Change";
|
||||
_scEventArgs[0] = "GUIControl *control";
|
||||
_handleRange = 0;
|
||||
}
|
||||
|
||||
bool GUISlider::IsHorizontal() const {
|
||||
return _width > _height;
|
||||
}
|
||||
|
||||
bool GUISlider::HasAlphaChannel() const {
|
||||
return is_sprite_alpha(BgImage) || is_sprite_alpha(HandleImage);
|
||||
}
|
||||
|
||||
bool GUISlider::IsOverControl(int x, int y, int leeway) const {
|
||||
// check the overall boundary
|
||||
if (GUIObject::IsOverControl(x, y, leeway))
|
||||
return true;
|
||||
// now check the handle too
|
||||
return _cachedHandle.IsInside(Point(x - X, y - Y));
|
||||
}
|
||||
|
||||
Rect GUISlider::CalcGraphicRect(bool /*clipped*/) {
|
||||
// Sliders are never clipped as of 3.6.0
|
||||
// TODO: precalculate everything on width/height/graphic change!!
|
||||
UpdateMetrics();
|
||||
Rect logical = RectWH(0, 0, _width, _height);
|
||||
Rect bar = _cachedBar;
|
||||
Rect handle = _cachedHandle;
|
||||
return Rect(
|
||||
MIN(MIN(logical.Left, bar.Left), handle.Left),
|
||||
MIN(MIN(logical.Top, bar.Top), handle.Top),
|
||||
MAX(MAX(logical.Right, bar.Right), handle.Right),
|
||||
MAX(MAX(logical.Bottom, bar.Bottom), handle.Bottom)
|
||||
);
|
||||
}
|
||||
|
||||
void GUISlider::UpdateMetrics() {
|
||||
// Clamp Value
|
||||
// TODO: this is necessary here because some Slider fields are still public
|
||||
if (MinValue >= MaxValue)
|
||||
MaxValue = MinValue + 1;
|
||||
Value = Math::Clamp(Value, MinValue, MaxValue);
|
||||
// Test if sprite is available; // TODO: return a placeholder from spriteset instead!
|
||||
const int handle_im = ((HandleImage > 0) && _GP(spriteset).DoesSpriteExist(HandleImage)) ? HandleImage : 0;
|
||||
|
||||
// Depending on slider's orientation, thickness is either Height or Width
|
||||
const int thickness = IsHorizontal() ? _height : _width;
|
||||
// "thick_f" is the factor for calculating relative element positions
|
||||
const int thick_f = thickness / 3; // one third of the control's thickness
|
||||
// Bar thickness
|
||||
const int bar_thick = thick_f * 2 + 2;
|
||||
|
||||
// Calculate handle size
|
||||
Size handle_sz;
|
||||
if (handle_im > 0) // handle is a sprite
|
||||
{
|
||||
handle_sz = Size(get_adjusted_spritewidth(handle_im),
|
||||
get_adjusted_spriteheight(handle_im));
|
||||
} else // handle is a drawn rectangle
|
||||
{
|
||||
if (IsHorizontal())
|
||||
handle_sz = Size(get_fixed_pixel_size(4) + 1, bar_thick + (thick_f - 1) * 2);
|
||||
else
|
||||
handle_sz = Size(bar_thick + (thick_f - 1) * 2, get_fixed_pixel_size(4) + 1);
|
||||
}
|
||||
|
||||
// Calculate bar and handle positions
|
||||
Rect bar;
|
||||
Rect handle;
|
||||
int handle_range;
|
||||
if (IsHorizontal()) // horizontal slider
|
||||
{
|
||||
// Value pos is a coordinate corresponding to current slider's value
|
||||
bar = RectWH(1, _height / 2 - thick_f, _width - 1, bar_thick);
|
||||
handle_range = _width - 4;
|
||||
int value_pos = (int)(((float)(Value - MinValue) * (float)handle_range) / (float)(MaxValue - MinValue));
|
||||
handle = RectWH((bar.Left + get_fixed_pixel_size(2)) - (handle_sz.Width / 2) + 1 + value_pos - 2,
|
||||
bar.Top + (bar.GetHeight() - handle_sz.Height) / 2,
|
||||
handle_sz.Width, handle_sz.Height);
|
||||
handle.MoveToY(handle.Top + data_to_game_coord(HandleOffset));
|
||||
}
|
||||
// vertical slider
|
||||
else {
|
||||
bar = RectWH(_width / 2 - thick_f, 1, bar_thick, _height - 1);
|
||||
handle_range = _height - 4;
|
||||
int value_pos = (int)(((float)(MaxValue - Value) * (float)handle_range) / (float)(MaxValue - MinValue));
|
||||
handle = RectWH(bar.Left + (bar.GetWidth() - handle_sz.Width) / 2,
|
||||
(bar.Top + get_fixed_pixel_size(2)) - (handle_sz.Height / 2) + 1 + value_pos - 2,
|
||||
handle_sz.Width, handle_sz.Height);
|
||||
handle.MoveToX(handle.Left + data_to_game_coord(HandleOffset));
|
||||
}
|
||||
|
||||
_cachedBar = bar;
|
||||
_cachedHandle = handle;
|
||||
_handleRange = MAX(1, handle_range);
|
||||
}
|
||||
|
||||
void GUISlider::Draw(Bitmap *ds, int x, int y) {
|
||||
UpdateMetrics();
|
||||
|
||||
Rect bar = Rect::MoveBy(_cachedBar, x, y);
|
||||
Rect handle = Rect::MoveBy(_cachedHandle, x, y);
|
||||
|
||||
color_t draw_color;
|
||||
if (BgImage > 0) {
|
||||
// tiled image as slider background
|
||||
int x_inc = 0;
|
||||
int y_inc = 0;
|
||||
if (IsHorizontal()) {
|
||||
x_inc = get_adjusted_spritewidth(BgImage);
|
||||
// centre the image vertically
|
||||
bar.Top = y + (_height / 2) - get_adjusted_spriteheight(BgImage) / 2;
|
||||
} else {
|
||||
y_inc = get_adjusted_spriteheight(BgImage);
|
||||
// centre the image horizontally
|
||||
bar.Left = x + (_width / 2) - get_adjusted_spritewidth(BgImage) / 2;
|
||||
}
|
||||
int cx = bar.Left;
|
||||
int cy = bar.Top;
|
||||
// draw the tiled background image
|
||||
do {
|
||||
draw_gui_sprite(ds, BgImage, cx, cy, true);
|
||||
cx += x_inc;
|
||||
cy += y_inc;
|
||||
// done as a do..while so that at least one of the image is drawn
|
||||
} while ((cx + x_inc <= bar.Right) && (cy + y_inc <= bar.Bottom));
|
||||
} else {
|
||||
// normal grey background
|
||||
draw_color = ds->GetCompatibleColor(16);
|
||||
ds->FillRect(bar, draw_color);
|
||||
draw_color = ds->GetCompatibleColor(8);
|
||||
ds->DrawLine(Line(bar.Left, bar.Top, bar.Left, bar.Bottom), draw_color);
|
||||
ds->DrawLine(Line(bar.Left, bar.Top, bar.Right, bar.Top), draw_color);
|
||||
draw_color = ds->GetCompatibleColor(15);
|
||||
ds->DrawLine(Line(bar.Right, bar.Top + 1, bar.Right, bar.Bottom), draw_color);
|
||||
ds->DrawLine(Line(bar.Left, bar.Bottom, bar.Right, bar.Bottom), draw_color);
|
||||
}
|
||||
|
||||
// Test if sprite is available; // TODO: return a placeholder from spriteset instead!
|
||||
const int handle_im = ((HandleImage > 0) && _GP(spriteset).DoesSpriteExist(HandleImage)) ? HandleImage : 0;
|
||||
if (handle_im > 0) // handle is a sprite
|
||||
{
|
||||
draw_gui_sprite(ds, handle_im, handle.Left, handle.Top, true);
|
||||
} else // handle is a drawn rectangle
|
||||
{
|
||||
// normal grey tracker handle
|
||||
draw_color = ds->GetCompatibleColor(7);
|
||||
ds->FillRect(handle, draw_color);
|
||||
draw_color = ds->GetCompatibleColor(15);
|
||||
ds->DrawLine(Line(handle.Left, handle.Top, handle.Right, handle.Top), draw_color);
|
||||
ds->DrawLine(Line(handle.Left, handle.Top, handle.Left, handle.Bottom), draw_color);
|
||||
draw_color = ds->GetCompatibleColor(16);
|
||||
ds->DrawLine(Line(handle.Right, handle.Top + 1, handle.Right, handle.Bottom), draw_color);
|
||||
ds->DrawLine(Line(handle.Left + 1, handle.Bottom, handle.Right, handle.Bottom), draw_color);
|
||||
}
|
||||
}
|
||||
|
||||
bool GUISlider::OnMouseDown() {
|
||||
IsMousePressed = true;
|
||||
// lock focus to ourselves
|
||||
return true;
|
||||
}
|
||||
|
||||
void GUISlider::OnMouseMove(int x, int y) {
|
||||
if (!IsMousePressed)
|
||||
return;
|
||||
|
||||
int32_t value;
|
||||
assert(_handleRange > 0);
|
||||
if (IsHorizontal())
|
||||
value = (int)(((float)((x - X) - 2) * (float)(MaxValue - MinValue)) / (float)_handleRange) + MinValue;
|
||||
else
|
||||
value = (int)(((float)(((Y + _height) - y) - 2) * (float)(MaxValue - MinValue)) / (float)_handleRange) + MinValue;
|
||||
|
||||
value = Math::Clamp(value, MinValue, MaxValue);
|
||||
if (value != Value) {
|
||||
Value = value;
|
||||
MarkChanged();
|
||||
}
|
||||
IsActivated = true;
|
||||
}
|
||||
|
||||
void GUISlider::OnMouseUp() {
|
||||
IsMousePressed = false;
|
||||
}
|
||||
|
||||
void GUISlider::ReadFromFile(Stream *in, GuiVersion gui_version) {
|
||||
GUIObject::ReadFromFile(in, gui_version);
|
||||
MinValue = in->ReadInt32();
|
||||
MaxValue = in->ReadInt32();
|
||||
Value = in->ReadInt32();
|
||||
if (gui_version < kGuiVersion_350) { // NOTE: reading into actual variables only for old savegame support
|
||||
IsMousePressed = in->ReadInt32() != 0;
|
||||
}
|
||||
if (gui_version >= kGuiVersion_unkn_104) {
|
||||
HandleImage = in->ReadInt32();
|
||||
HandleOffset = in->ReadInt32();
|
||||
BgImage = in->ReadInt32();
|
||||
} else {
|
||||
HandleImage = -1;
|
||||
HandleOffset = 0;
|
||||
BgImage = 0;
|
||||
}
|
||||
|
||||
UpdateMetrics();
|
||||
}
|
||||
|
||||
void GUISlider::WriteToFile(Stream *out) const {
|
||||
GUIObject::WriteToFile(out);
|
||||
out->WriteInt32(MinValue);
|
||||
out->WriteInt32(MaxValue);
|
||||
out->WriteInt32(Value);
|
||||
out->WriteInt32(HandleImage);
|
||||
out->WriteInt32(HandleOffset);
|
||||
out->WriteInt32(BgImage);
|
||||
}
|
||||
|
||||
void GUISlider::ReadFromSavegame(Stream *in, GuiSvgVersion svg_ver) {
|
||||
GUIObject::ReadFromSavegame(in, svg_ver);
|
||||
BgImage = in->ReadInt32();
|
||||
HandleImage = in->ReadInt32();
|
||||
HandleOffset = in->ReadInt32();
|
||||
MinValue = in->ReadInt32();
|
||||
MaxValue = in->ReadInt32();
|
||||
Value = in->ReadInt32();
|
||||
|
||||
UpdateMetrics();
|
||||
}
|
||||
|
||||
void GUISlider::WriteToSavegame(Stream *out) const {
|
||||
GUIObject::WriteToSavegame(out);
|
||||
out->WriteInt32(BgImage);
|
||||
out->WriteInt32(HandleImage);
|
||||
out->WriteInt32(HandleOffset);
|
||||
out->WriteInt32(MinValue);
|
||||
out->WriteInt32(MaxValue);
|
||||
out->WriteInt32(Value);
|
||||
}
|
||||
|
||||
} // namespace Shared
|
||||
} // namespace AGS
|
||||
} // namespace AGS3
|
||||
85
engines/ags/shared/gui/gui_slider.h
Normal file
85
engines/ags/shared/gui/gui_slider.h
Normal file
@@ -0,0 +1,85 @@
|
||||
/* 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 AGS_SHARED_GUI_GUI_SLIDER_H
|
||||
#define AGS_SHARED_GUI_GUI_SLIDER_H
|
||||
|
||||
#include "common/std/vector.h"
|
||||
#include "ags/shared/gui/gui_object.h"
|
||||
|
||||
namespace AGS3 {
|
||||
namespace AGS {
|
||||
namespace Shared {
|
||||
|
||||
class GUISlider : public GUIObject {
|
||||
public:
|
||||
GUISlider();
|
||||
|
||||
// Tells if the slider is horizontal (otherwise - vertical)
|
||||
bool IsHorizontal() const;
|
||||
bool IsOverControl(int x, int y, int leeway) const override;
|
||||
|
||||
// Compatibility: sliders are not clipped as of 3.6.0
|
||||
bool IsContentClipped() const override { return false; }
|
||||
bool HasAlphaChannel() const override;
|
||||
|
||||
// Operations
|
||||
Rect CalcGraphicRect(bool clipped) override;
|
||||
void Draw(Bitmap *ds, int x = 0, int y = 0) override;
|
||||
|
||||
// Events
|
||||
bool OnMouseDown() override;
|
||||
void OnMouseMove(int xp, int yp) override;
|
||||
void OnMouseUp() override;
|
||||
|
||||
// Serialization
|
||||
void ReadFromFile(Stream *in, GuiVersion gui_version) override;
|
||||
void WriteToFile(Stream *out) const override;
|
||||
void ReadFromSavegame(Stream *in, GuiSvgVersion svg_ver) override;
|
||||
void WriteToSavegame(Stream *out) const override;
|
||||
|
||||
// TODO: these members are currently public; hide them later
|
||||
public:
|
||||
int32_t MinValue;
|
||||
int32_t MaxValue;
|
||||
int32_t Value;
|
||||
int32_t BgImage;
|
||||
int32_t HandleImage;
|
||||
int32_t HandleOffset;
|
||||
bool IsMousePressed;
|
||||
|
||||
private:
|
||||
// Updates dynamic metrics and positions of elements
|
||||
void UpdateMetrics();
|
||||
|
||||
// Cached coordinates of slider bar; in relative coords
|
||||
Rect _cachedBar;
|
||||
// Cached coordinates of slider handle; in relative coords
|
||||
Rect _cachedHandle;
|
||||
// The length of the handle movement range, in pixels
|
||||
int _handleRange;
|
||||
};
|
||||
|
||||
} // namespace Shared
|
||||
} // namespace AGS
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
177
engines/ags/shared/gui/gui_textbox.cpp
Normal file
177
engines/ags/shared/gui/gui_textbox.cpp
Normal file
@@ -0,0 +1,177 @@
|
||||
/* 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 "ags/shared/ac/keycode.h"
|
||||
#include "ags/shared/font/fonts.h"
|
||||
#include "ags/shared/gui/gui_main.h"
|
||||
#include "ags/shared/gui/gui_textbox.h"
|
||||
#include "ags/shared/util/stream.h"
|
||||
#include "ags/shared/util/string_utils.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
#define GUITEXTBOX_LEGACY_TEXTLEN 200
|
||||
|
||||
namespace AGS {
|
||||
namespace Shared {
|
||||
|
||||
GUITextBox::GUITextBox() {
|
||||
Font = 0;
|
||||
TextColor = 0;
|
||||
TextBoxFlags = kTextBox_DefFlags;
|
||||
|
||||
_scEventCount = 1;
|
||||
_scEventNames[0] = "Activate";
|
||||
_scEventArgs[0] = "GUIControl *control";
|
||||
}
|
||||
|
||||
bool GUITextBox::HasAlphaChannel() const {
|
||||
return is_font_antialiased(Font);
|
||||
}
|
||||
|
||||
bool GUITextBox::IsBorderShown() const {
|
||||
return (TextBoxFlags & kTextBox_ShowBorder) != 0;
|
||||
}
|
||||
|
||||
Rect GUITextBox::CalcGraphicRect(bool clipped) {
|
||||
if (clipped)
|
||||
return RectWH(0, 0, _width, _height);
|
||||
|
||||
// TODO: need to find a way to cache text position, or there'll be some repetition
|
||||
Rect rc = RectWH(0, 0, _width, _height);
|
||||
Point text_at(1 + get_fixed_pixel_size(1), 1 + get_fixed_pixel_size(1));
|
||||
Rect text_rc = GUI::CalcTextGraphicalRect(Text.GetCStr(), Font, text_at);
|
||||
if (IsGUIEnabled(this)) {
|
||||
// add a cursor
|
||||
Rect cur_rc = RectWH(
|
||||
text_rc.Right + 3,
|
||||
1 + get_font_height(Font),
|
||||
get_fixed_pixel_size(5),
|
||||
get_fixed_pixel_size(1) - 1);
|
||||
text_rc = SumRects(text_rc, cur_rc);
|
||||
}
|
||||
return SumRects(rc, text_rc);
|
||||
}
|
||||
|
||||
void GUITextBox::Draw(Bitmap *ds, int x, int y) {
|
||||
color_t text_color = ds->GetCompatibleColor(TextColor);
|
||||
color_t draw_color = ds->GetCompatibleColor(TextColor);
|
||||
if (IsBorderShown()) {
|
||||
ds->DrawRect(RectWH(x, y, _width, _height), draw_color);
|
||||
if (get_fixed_pixel_size(1) > 1) {
|
||||
ds->DrawRect(Rect(x + 1, y + 1, x + _width - get_fixed_pixel_size(1), y + _height - get_fixed_pixel_size(1)), draw_color);
|
||||
}
|
||||
}
|
||||
DrawTextBoxContents(ds, x, y, text_color);
|
||||
}
|
||||
|
||||
// TODO: a shared utility function
|
||||
static void Backspace(String &text) {
|
||||
if (get_uformat() == U_UTF8) {// Find where the last utf8 char begins
|
||||
const char *ptr_end = text.GetCStr() + text.GetLength();
|
||||
const char *ptr = ptr_end - 1;
|
||||
for (; ptr > text.GetCStr() && ((*ptr & 0xC0) == 0x80); --ptr);
|
||||
text.ClipRight(ptr_end - ptr);
|
||||
} else {
|
||||
text.ClipRight(1);
|
||||
}
|
||||
}
|
||||
|
||||
void GUITextBox::OnKeyPress(const KeyInput &ki) {
|
||||
switch (ki.Key) {
|
||||
case eAGSKeyCodeReturn:
|
||||
IsActivated = true;
|
||||
return;
|
||||
case eAGSKeyCodeBackspace:
|
||||
Backspace(Text);
|
||||
MarkChanged();
|
||||
return;
|
||||
default: break;
|
||||
}
|
||||
|
||||
if (ki.UChar == 0)
|
||||
return; // not a textual event
|
||||
if (get_uformat() == U_UTF8)
|
||||
Text.Append(ki.Text); // proper unicode char
|
||||
else if (ki.UChar < 256)
|
||||
Text.AppendChar(static_cast<uint8_t>(ki.UChar)); // ascii/ansi-range char in ascii mode
|
||||
else
|
||||
return; // char from an unsupported range, don't print but still report as handled
|
||||
// if the new string is too long, remove the new character
|
||||
if (get_text_width(Text.GetCStr(), Font) > (_width - (6 + get_fixed_pixel_size(5))))
|
||||
Backspace(Text);
|
||||
MarkChanged();
|
||||
}
|
||||
|
||||
void GUITextBox::SetShowBorder(bool on) {
|
||||
if (on)
|
||||
TextBoxFlags |= kTextBox_ShowBorder;
|
||||
else
|
||||
TextBoxFlags &= ~kTextBox_ShowBorder;
|
||||
}
|
||||
|
||||
// TODO: replace string serialization with StrUtil::ReadString and WriteString
|
||||
// methods in the future, to keep this organized.
|
||||
void GUITextBox::WriteToFile(Stream *out) const {
|
||||
GUIObject::WriteToFile(out);
|
||||
StrUtil::WriteString(Text, out);
|
||||
out->WriteInt32(Font);
|
||||
out->WriteInt32(TextColor);
|
||||
out->WriteInt32(TextBoxFlags);
|
||||
}
|
||||
|
||||
void GUITextBox::ReadFromFile(Stream *in, GuiVersion gui_version) {
|
||||
GUIObject::ReadFromFile(in, gui_version);
|
||||
if (gui_version < kGuiVersion_350)
|
||||
Text.ReadCount(in, GUITEXTBOX_LEGACY_TEXTLEN);
|
||||
else
|
||||
Text = StrUtil::ReadString(in);
|
||||
Font = in->ReadInt32();
|
||||
TextColor = in->ReadInt32();
|
||||
TextBoxFlags = in->ReadInt32();
|
||||
// reverse particular flags from older format
|
||||
if (gui_version < kGuiVersion_350)
|
||||
TextBoxFlags ^= kTextBox_OldFmtXorMask;
|
||||
|
||||
if (TextColor == 0)
|
||||
TextColor = 16;
|
||||
}
|
||||
|
||||
void GUITextBox::ReadFromSavegame(Stream *in, GuiSvgVersion svg_ver) {
|
||||
GUIObject::ReadFromSavegame(in, svg_ver);
|
||||
Font = in->ReadInt32();
|
||||
TextColor = in->ReadInt32();
|
||||
Text = StrUtil::ReadString(in);
|
||||
if (svg_ver >= kGuiSvgVersion_350)
|
||||
TextBoxFlags = in->ReadInt32();
|
||||
}
|
||||
|
||||
void GUITextBox::WriteToSavegame(Stream *out) const {
|
||||
GUIObject::WriteToSavegame(out);
|
||||
out->WriteInt32(Font);
|
||||
out->WriteInt32(TextColor);
|
||||
StrUtil::WriteString(Text, out);
|
||||
out->WriteInt32(TextBoxFlags);
|
||||
}
|
||||
|
||||
} // namespace Shared
|
||||
} // namespace AGS
|
||||
} // namespace AGS3
|
||||
71
engines/ags/shared/gui/gui_textbox.h
Normal file
71
engines/ags/shared/gui/gui_textbox.h
Normal file
@@ -0,0 +1,71 @@
|
||||
/* 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 AGS_SHARED_GUI_GUI_TEXTBOX_H
|
||||
#define AGS_SHARED_GUI_GUI_TEXTBOX_H
|
||||
|
||||
#include "common/std/vector.h"
|
||||
#include "ags/shared/gui/gui_object.h"
|
||||
#include "ags/shared/util/string.h"
|
||||
|
||||
namespace AGS3 {
|
||||
namespace AGS {
|
||||
namespace Shared {
|
||||
|
||||
class GUITextBox : public GUIObject {
|
||||
public:
|
||||
GUITextBox();
|
||||
|
||||
bool HasAlphaChannel() const override;
|
||||
bool IsBorderShown() const;
|
||||
|
||||
// Operations
|
||||
Rect CalcGraphicRect(bool clipped) override;
|
||||
void Draw(Bitmap *ds, int x = 0, int y = 0) override;
|
||||
void SetShowBorder(bool on);
|
||||
|
||||
// Events
|
||||
void OnKeyPress(const KeyInput &ki) override;
|
||||
|
||||
// Serialization
|
||||
void ReadFromFile(Stream *in, GuiVersion gui_version) override;
|
||||
void WriteToFile(Stream *out) const override;
|
||||
void ReadFromSavegame(Stream *in, GuiSvgVersion svg_ver) override;
|
||||
void WriteToSavegame(Stream *out) const override;
|
||||
|
||||
// TODO: these members are currently public; hide them later
|
||||
public:
|
||||
int32_t Font;
|
||||
String Text;
|
||||
color_t TextColor;
|
||||
|
||||
private:
|
||||
int32_t TextBoxFlags;
|
||||
String _textToDraw;
|
||||
|
||||
void DrawTextBoxContents(Bitmap *ds, int x, int y, color_t text_color);
|
||||
};
|
||||
|
||||
} // namespace Shared
|
||||
} // namespace AGS
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
85
engines/ags/shared/script/cc_common.cpp
Normal file
85
engines/ags/shared/script/cc_common.cpp
Normal file
@@ -0,0 +1,85 @@
|
||||
/* 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/std/utility.h"
|
||||
#include "ags/shared/script/cc_common.h"
|
||||
#include "ags/shared/util/string.h"
|
||||
#include "ags/globals.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
using namespace AGS::Shared;
|
||||
|
||||
void ccSetOption(int optbit, int onoroff) {
|
||||
if (onoroff)
|
||||
_G(ccCompOptions) |= optbit;
|
||||
else
|
||||
_G(ccCompOptions) &= ~optbit;
|
||||
}
|
||||
|
||||
int ccGetOption(int optbit) {
|
||||
if (_G(ccCompOptions) & optbit)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cc_clear_error() {
|
||||
_GP(ccError) = ScriptError();
|
||||
}
|
||||
|
||||
bool cc_has_error() {
|
||||
return _GP(ccError).HasError;
|
||||
}
|
||||
|
||||
const ScriptError &cc_get_error() {
|
||||
return _GP(ccError);
|
||||
}
|
||||
|
||||
String cc_get_err_callstack(int max_lines) {
|
||||
return cc_has_error() ? _GP(ccError).CallStack : cc_get_callstack(max_lines);
|
||||
}
|
||||
|
||||
void cc_error(const char *descr, ...) {
|
||||
_GP(ccError).IsUserError = false;
|
||||
if (descr[0] == '!') {
|
||||
_GP(ccError).IsUserError = true;
|
||||
descr++;
|
||||
}
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, descr);
|
||||
String displbuf = String::FromFormatV(descr, ap);
|
||||
va_end(ap);
|
||||
|
||||
// TODO: because this global ccError is a global shared variable,
|
||||
// we have to use project-dependent function to format the final message
|
||||
_GP(ccError).ErrorString = cc_format_error(displbuf);
|
||||
_GP(ccError).CallStack = cc_get_callstack();
|
||||
_GP(ccError).HasError = 1;
|
||||
_GP(ccError).Line = _G(currentline);
|
||||
}
|
||||
|
||||
void cc_error(const ScriptError &err) {
|
||||
_GP(ccError) = err;
|
||||
}
|
||||
|
||||
} // namespace AGS3
|
||||
67
engines/ags/shared/script/cc_common.h
Normal file
67
engines/ags/shared/script/cc_common.h
Normal file
@@ -0,0 +1,67 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
// Script options and error reporting.
|
||||
|
||||
#ifndef AGS_SHARED_SCRIPT_CC_COMMON_H
|
||||
#define AGS_SHARED_SCRIPT_CC_COMMON_H
|
||||
|
||||
#include "ags/shared/util/string.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
#define SCOPT_EXPORTALL 1 // export all functions automatically
|
||||
#define SCOPT_SHOWWARNINGS 2 // printf warnings to console
|
||||
#define SCOPT_LINENUMBERS 4 // include line numbers in compiled code
|
||||
#define SCOPT_AUTOIMPORT 8 // when creating instance, export funcs to other scripts
|
||||
#define SCOPT_DEBUGRUN 0x10 // write instructions as they are procssed to log file
|
||||
#define SCOPT_NOIMPORTOVERRIDE 0x20 // do not allow an import to be re-declared
|
||||
#define SCOPT_LEFTTORIGHT 0x40 // left-to-right operator precedance
|
||||
#define SCOPT_OLDSTRINGS 0x80 // allow old-style strings
|
||||
#define SCOPT_UTF8 0x100 // UTF-8 text mode
|
||||
|
||||
extern void ccSetOption(int, int);
|
||||
extern int ccGetOption(int);
|
||||
|
||||
// error reporting
|
||||
|
||||
struct ScriptError {
|
||||
bool HasError = false; // set if error occurs
|
||||
bool IsUserError = false; // marks script use errors
|
||||
AGS::Shared::String ErrorString; // description of the error
|
||||
int Line = 0; // line number of the error
|
||||
AGS::Shared::String CallStack; // callstack where error happened
|
||||
};
|
||||
|
||||
void cc_clear_error();
|
||||
bool cc_has_error();
|
||||
const ScriptError &cc_get_error();
|
||||
// Returns callstack of the last recorded script error, or a callstack
|
||||
// of a current execution point, if no script error is currently saved in memory.
|
||||
AGS::Shared::String cc_get_err_callstack(int max_lines = INT_MAX);
|
||||
void cc_error(const char *, ...);
|
||||
void cc_error(const ScriptError &err);
|
||||
// Project-dependent script error formatting
|
||||
AGS::Shared::String cc_format_error(const AGS::Shared::String &message);
|
||||
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
134
engines/ags/shared/script/cc_internal.h
Normal file
134
engines/ags/shared/script/cc_internal.h
Normal file
@@ -0,0 +1,134 @@
|
||||
/* 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 AGS_SHARED_SCRIPT_CC_INTERNAL_H
|
||||
#define AGS_SHARED_SCRIPT_CC_INTERNAL_H
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
#define SCOM_VERSION 90
|
||||
#define SCOM_VERSIONSTR "0.90"
|
||||
|
||||
// virtual CPU registers
|
||||
#define SREG_SP 1 // stack pointer
|
||||
#define SREG_MAR 2 // memory address register
|
||||
#define SREG_AX 3 // general purpose
|
||||
#define SREG_BX 4
|
||||
#define SREG_CX 5
|
||||
#define SREG_OP 6 // object pointer for member func calls
|
||||
#define SREG_DX 7
|
||||
#define CC_NUM_REGISTERS 8
|
||||
|
||||
// virtual CPU commands
|
||||
#define SCMD_ADD 1 // reg1 += arg2
|
||||
#define SCMD_SUB 2 // reg1 -= arg2
|
||||
#define SCMD_REGTOREG 3 // reg2 = reg1
|
||||
#define SCMD_WRITELIT 4 // m[MAR] = arg2 (copy arg1 bytes)
|
||||
#define SCMD_RET 5 // return from subroutine
|
||||
#define SCMD_LITTOREG 6 // set reg1 to literal value arg2
|
||||
#define SCMD_MEMREAD 7 // reg1 = m[MAR]
|
||||
#define SCMD_MEMWRITE 8 // m[MAR] = reg1
|
||||
#define SCMD_MULREG 9 // reg1 *= reg2
|
||||
#define SCMD_DIVREG 10 // reg1 /= reg2
|
||||
#define SCMD_ADDREG 11 // reg1 += reg2
|
||||
#define SCMD_SUBREG 12 // reg1 -= reg2
|
||||
#define SCMD_BITAND 13 // bitwise reg1 & reg2
|
||||
#define SCMD_BITOR 14 // bitwise reg1 | reg2
|
||||
#define SCMD_ISEQUAL 15 // reg1 == reg2 reg1=1 if true, =0 if not
|
||||
#define SCMD_NOTEQUAL 16 // reg1 != reg2
|
||||
#define SCMD_GREATER 17 // reg1 > reg2
|
||||
#define SCMD_LESSTHAN 18 // reg1 < reg2
|
||||
#define SCMD_GTE 19 // reg1 >= reg2
|
||||
#define SCMD_LTE 20 // reg1 <= reg2
|
||||
#define SCMD_AND 21 // (reg1!=0) && (reg2!=0) -> reg1
|
||||
#define SCMD_OR 22 // (reg1!=0) || (reg2!=0) -> reg1
|
||||
#define SCMD_CALL 23 // jump to subroutine at reg1
|
||||
#define SCMD_MEMREADB 24 // reg1 = m[MAR] (1 byte)
|
||||
#define SCMD_MEMREADW 25 // reg1 = m[MAR] (2 bytes)
|
||||
#define SCMD_MEMWRITEB 26 // m[MAR] = reg1 (1 byte)
|
||||
#define SCMD_MEMWRITEW 27 // m[MAR] = reg1 (2 bytes)
|
||||
#define SCMD_JZ 28 // jump if ax==0 to arg1
|
||||
#define SCMD_PUSHREG 29 // m[sp]=reg1; sp++
|
||||
#define SCMD_POPREG 30 // sp--; reg1=m[sp]
|
||||
#define SCMD_JMP 31 // jump to arg1
|
||||
#define SCMD_MUL 32 // reg1 *= arg2
|
||||
#define SCMD_CALLEXT 33 // call external (imported) function reg1
|
||||
#define SCMD_PUSHREAL 34 // push reg1 onto real stack
|
||||
#define SCMD_SUBREALSTACK 35 // decrement stack ptr by literal
|
||||
#define SCMD_LINENUM 36 // debug info - source code line number
|
||||
#define SCMD_CALLAS 37 // call external script function
|
||||
#define SCMD_THISBASE 38 // current relative address
|
||||
#define SCMD_NUMFUNCARGS 39 // number of arguments for ext func call
|
||||
#define SCMD_MODREG 40 // reg1 %= reg2
|
||||
#define SCMD_XORREG 41 // reg1 ^= reg2
|
||||
#define SCMD_NOTREG 42 // reg1 = !reg1
|
||||
#define SCMD_SHIFTLEFT 43 // reg1 = reg1 << reg2
|
||||
#define SCMD_SHIFTRIGHT 44 // reg1 = reg1 >> reg2
|
||||
#define SCMD_CALLOBJ 45 // next call is member function of reg1
|
||||
#define SCMD_CHECKBOUNDS 46 // check reg1 is between 0 and arg2
|
||||
#define SCMD_MEMWRITEPTR 47 // m[MAR] = reg1 (adjust ptr addr)
|
||||
#define SCMD_MEMREADPTR 48 // reg1 = m[MAR] (adjust ptr addr)
|
||||
#define SCMD_MEMZEROPTR 49 // m[MAR] = 0 (blank ptr)
|
||||
#define SCMD_MEMINITPTR 50 // m[MAR] = reg1 (but don't free old one)
|
||||
#define SCMD_LOADSPOFFS 51 // MAR = SP - arg1 (optimization for local var access)
|
||||
#define SCMD_CHECKNULL 52 // error if MAR==0
|
||||
#define SCMD_FADD 53 // reg1 += arg2 (float,int)
|
||||
#define SCMD_FSUB 54 // reg1 -= arg2 (float,int)
|
||||
#define SCMD_FMULREG 55 // reg1 *= reg2 (float)
|
||||
#define SCMD_FDIVREG 56 // reg1 /= reg2 (float)
|
||||
#define SCMD_FADDREG 57 // reg1 += reg2 (float)
|
||||
#define SCMD_FSUBREG 58 // reg1 -= reg2 (float)
|
||||
#define SCMD_FGREATER 59 // reg1 > reg2 (float)
|
||||
#define SCMD_FLESSTHAN 60 // reg1 < reg2 (float)
|
||||
#define SCMD_FGTE 61 // reg1 >= reg2 (float)
|
||||
#define SCMD_FLTE 62 // reg1 <= reg2 (float)
|
||||
#define SCMD_ZEROMEMORY 63 // m[MAR]..m[MAR+(arg1-1)] = 0
|
||||
#define SCMD_CREATESTRING 64 // reg1 = new String(reg1)
|
||||
#define SCMD_STRINGSEQUAL 65 // (char*)reg1 == (char*)reg2 reg1=1 if true, =0 if not
|
||||
#define SCMD_STRINGSNOTEQ 66 // (char*)reg1 != (char*)reg2
|
||||
#define SCMD_CHECKNULLREG 67 // error if reg1 == NULL
|
||||
#define SCMD_LOOPCHECKOFF 68 // no loop checking for this function
|
||||
#define SCMD_MEMZEROPTRND 69 // m[MAR] = 0 (blank ptr, no dispose if = ax)
|
||||
#define SCMD_JNZ 70 // jump to arg1 if ax!=0
|
||||
#define SCMD_DYNAMICBOUNDS 71 // check reg1 is between 0 and m[MAR-4]
|
||||
#define SCMD_NEWARRAY 72 // reg1 = new array of reg1 elements, each of size arg2 (arg3=managed type?)
|
||||
#define SCMD_NEWUSEROBJECT 73 // reg1 = new user object of arg1 size
|
||||
|
||||
#define CC_NUM_SCCMDS 74
|
||||
#define MAX_SCMD_ARGS 3 // maximal possible number of arguments
|
||||
|
||||
#define EXPORT_FUNCTION 1
|
||||
#define EXPORT_DATA 2
|
||||
|
||||
#define FIXUP_NOFIXUP 0 // no-op
|
||||
#define FIXUP_GLOBALDATA 1 // code[fixup] += &globaldata[0]
|
||||
#define FIXUP_FUNCTION 2 // code[fixup] += &code[0]
|
||||
#define FIXUP_STRING 3 // code[fixup] += &strings[0]
|
||||
#define FIXUP_IMPORT 4 // code[fixup] = &imported_thing[code[fixup]]
|
||||
#define FIXUP_DATADATA 5 // globaldata[fixup] += &globaldata[0]
|
||||
#define FIXUP_STACK 6 // code[fixup] += &stack[0]
|
||||
|
||||
// Script file signature
|
||||
#define ENDFILESIG 0xbeefcafe
|
||||
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
336
engines/ags/shared/script/cc_script.cpp
Normal file
336
engines/ags/shared/script/cc_script.cpp
Normal file
@@ -0,0 +1,336 @@
|
||||
/* 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 "ags/shared/script/cc_common.h"
|
||||
#include "ags/shared/script/cc_script.h"
|
||||
#include "ags/shared/script/cc_internal.h"
|
||||
#include "ags/shared/util/stream.h"
|
||||
#include "ags/shared/util/string_compat.h"
|
||||
#include "ags/shared/util/string_utils.h"
|
||||
#include "ags/globals.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
using namespace AGS::Shared;
|
||||
|
||||
ccScript *ccScript::CreateFromStream(Stream *in) {
|
||||
ccScript *scri = new ccScript();
|
||||
if (!scri->Read(in)) {
|
||||
delete scri;
|
||||
return nullptr;
|
||||
}
|
||||
return scri;
|
||||
}
|
||||
|
||||
ccScript::ccScript() {
|
||||
globaldata = nullptr;
|
||||
globaldatasize = 0;
|
||||
code = nullptr;
|
||||
codesize = 0;
|
||||
strings = nullptr;
|
||||
stringssize = 0;
|
||||
fixuptypes = nullptr;
|
||||
fixups = nullptr;
|
||||
numfixups = 0;
|
||||
importsCapacity = 0;
|
||||
imports = nullptr;
|
||||
numimports = 0;
|
||||
exportsCapacity = 0;
|
||||
exports = nullptr;
|
||||
export_addr = nullptr;
|
||||
numexports = 0;
|
||||
instances = 0;
|
||||
sectionNames = nullptr;
|
||||
sectionOffsets = nullptr;
|
||||
numSections = 0;
|
||||
capacitySections = 0;
|
||||
}
|
||||
|
||||
ccScript::ccScript(const ccScript &src) {
|
||||
globaldatasize = src.globaldatasize;
|
||||
if (globaldatasize > 0) {
|
||||
globaldata = (char *)malloc(globaldatasize);
|
||||
memcpy(globaldata, src.globaldata, globaldatasize);
|
||||
} else {
|
||||
globaldata = nullptr;
|
||||
}
|
||||
|
||||
codesize = src.codesize;
|
||||
if (codesize > 0) {
|
||||
code = (int32_t *)malloc(codesize * sizeof(int32_t));
|
||||
memcpy(code, src.code, sizeof(int32_t) * codesize);
|
||||
} else {
|
||||
code = nullptr;
|
||||
}
|
||||
|
||||
stringssize = src.stringssize;
|
||||
if (stringssize > 0) {
|
||||
strings = (char *)malloc(stringssize);
|
||||
memcpy(strings, src.strings, stringssize);
|
||||
} else {
|
||||
strings = nullptr;
|
||||
}
|
||||
|
||||
numfixups = src.numfixups;
|
||||
if (numfixups > 0) {
|
||||
fixuptypes = (char *)malloc(numfixups);
|
||||
fixups = (int32_t *)malloc(numfixups * sizeof(int32_t));
|
||||
memcpy(fixuptypes, src.fixuptypes, numfixups);
|
||||
memcpy(fixups, src.fixups, numfixups * sizeof(int32_t));
|
||||
} else {
|
||||
fixups = nullptr;
|
||||
fixuptypes = nullptr;
|
||||
}
|
||||
|
||||
importsCapacity = src.numimports;
|
||||
numimports = src.numimports;
|
||||
if (numimports > 0) {
|
||||
imports = (char **)malloc(sizeof(char *) * numimports);
|
||||
for (int i = 0; i < numimports; ++i)
|
||||
imports[i] = ags_strdup(src.imports[i]);
|
||||
} else {
|
||||
imports = nullptr;
|
||||
}
|
||||
|
||||
exportsCapacity = src.numexports;
|
||||
numexports = src.numexports;
|
||||
if (numexports > 0) {
|
||||
exports = (char **)malloc(sizeof(char *) * numexports);
|
||||
export_addr = (int32_t *)malloc(sizeof(int32_t) * numexports);
|
||||
for (int i = 0; i < numexports; ++i) {
|
||||
exports[i] = ags_strdup(src.exports[i]);
|
||||
export_addr[i] = src.export_addr[i];
|
||||
}
|
||||
} else {
|
||||
exports = nullptr;
|
||||
export_addr = nullptr;
|
||||
}
|
||||
|
||||
capacitySections = src.numSections;
|
||||
numSections = src.numSections;
|
||||
if (numSections > 0) {
|
||||
sectionNames = (char **)malloc(numSections * sizeof(char *));
|
||||
sectionOffsets = (int32_t *)malloc(numSections * sizeof(int32_t));
|
||||
for (int i = 0; i < numSections; ++i) {
|
||||
sectionNames[i] = ags_strdup(src.sectionNames[i]);
|
||||
sectionOffsets[i] = src.sectionOffsets[i];
|
||||
}
|
||||
} else {
|
||||
numSections = 0;
|
||||
sectionNames = nullptr;
|
||||
sectionOffsets = nullptr;
|
||||
}
|
||||
|
||||
instances = 0;
|
||||
}
|
||||
|
||||
ccScript::~ccScript() {
|
||||
Free();
|
||||
}
|
||||
|
||||
void ccScript::Write(Stream *out) {
|
||||
int n;
|
||||
out->Write(_G(scfilesig), 4);
|
||||
out->WriteInt32(SCOM_VERSION);
|
||||
out->WriteInt32(globaldatasize);
|
||||
out->WriteInt32(codesize);
|
||||
out->WriteInt32(stringssize);
|
||||
if (globaldatasize > 0)
|
||||
out->WriteArray(globaldata, globaldatasize, 1);
|
||||
if (codesize > 0)
|
||||
out->WriteArrayOfInt32(code, codesize);
|
||||
if (stringssize > 0)
|
||||
out->WriteArray(strings, stringssize, 1);
|
||||
out->WriteInt32(numfixups);
|
||||
if (numfixups > 0) {
|
||||
out->WriteArray(fixuptypes, numfixups, 1);
|
||||
out->WriteArrayOfInt32(fixups, numfixups);
|
||||
}
|
||||
out->WriteInt32(numimports);
|
||||
for (n = 0; n < numimports; n++)
|
||||
StrUtil::WriteCStr(imports[n], out);
|
||||
out->WriteInt32(numexports);
|
||||
for (n = 0; n < numexports; n++) {
|
||||
StrUtil::WriteCStr(exports[n], out);
|
||||
out->WriteInt32(export_addr[n]);
|
||||
}
|
||||
out->WriteInt32(numSections);
|
||||
for (n = 0; n < numSections; n++) {
|
||||
StrUtil::WriteCStr(sectionNames[n], out);
|
||||
out->WriteInt32(sectionOffsets[n]);
|
||||
}
|
||||
out->WriteInt32(ENDFILESIG);
|
||||
}
|
||||
|
||||
bool ccScript::Read(Stream *in) {
|
||||
instances = 0;
|
||||
int n;
|
||||
char gotsig[5];
|
||||
_G(currentline) = -1;
|
||||
in->Read(gotsig, 4);
|
||||
gotsig[4] = 0;
|
||||
|
||||
int fileVer = in->ReadInt32();
|
||||
|
||||
if ((strcmp(gotsig, _G(scfilesig)) != 0) || (fileVer > SCOM_VERSION)) {
|
||||
cc_error("file was not written by ccScript::Write or seek position is incorrect");
|
||||
return false;
|
||||
}
|
||||
|
||||
globaldatasize = in->ReadInt32();
|
||||
codesize = in->ReadInt32();
|
||||
stringssize = in->ReadInt32();
|
||||
|
||||
if (globaldatasize > 0) {
|
||||
globaldata = (char *)malloc(globaldatasize);
|
||||
in->Read(globaldata, globaldatasize);
|
||||
} else
|
||||
globaldata = nullptr;
|
||||
|
||||
if (codesize > 0) {
|
||||
code = (int32_t *)malloc(codesize * sizeof(int32_t));
|
||||
in->ReadArrayOfInt32(code, codesize);
|
||||
} else
|
||||
code = nullptr;
|
||||
|
||||
if (stringssize > 0) {
|
||||
strings = (char *)malloc(stringssize);
|
||||
in->Read(strings, stringssize);
|
||||
} else
|
||||
strings = nullptr;
|
||||
|
||||
numfixups = in->ReadInt32();
|
||||
if (numfixups > 0) {
|
||||
fixuptypes = (char *)malloc(numfixups);
|
||||
fixups = (int32_t *)malloc(numfixups * sizeof(int32_t));
|
||||
in->Read(fixuptypes, numfixups);
|
||||
in->ReadArrayOfInt32(fixups, numfixups);
|
||||
} else {
|
||||
fixups = nullptr;
|
||||
fixuptypes = nullptr;
|
||||
}
|
||||
|
||||
numimports = in->ReadInt32();
|
||||
|
||||
imports = (char **)malloc(sizeof(char *) * numimports);
|
||||
for (n = 0; n < numimports; n++)
|
||||
imports[n] = StrUtil::ReadMallocCStrOrNull(in);
|
||||
|
||||
numexports = in->ReadInt32();
|
||||
exports = (char **)malloc(sizeof(char *) * numexports);
|
||||
export_addr = (int32_t *)malloc(sizeof(int32_t) * numexports);
|
||||
for (n = 0; n < numexports; n++) {
|
||||
exports[n] = StrUtil::ReadMallocCStrOrNull(in);
|
||||
export_addr[n] = in->ReadInt32();
|
||||
}
|
||||
|
||||
if (fileVer >= 83) {
|
||||
// read in the Sections
|
||||
numSections = in->ReadInt32();
|
||||
sectionNames = (char **)malloc(numSections * sizeof(char *));
|
||||
sectionOffsets = (int32_t *)malloc(numSections * sizeof(int32_t));
|
||||
for (n = 0; n < numSections; n++) {
|
||||
sectionNames[n] = StrUtil::ReadMallocCStrOrNull(in);
|
||||
sectionOffsets[n] = in->ReadInt32();
|
||||
}
|
||||
} else {
|
||||
numSections = 0;
|
||||
sectionNames = nullptr;
|
||||
sectionOffsets = nullptr;
|
||||
}
|
||||
|
||||
if (static_cast<uint32_t>(in->ReadInt32()) != ENDFILESIG) {
|
||||
cc_error("internal error rebuilding script");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ccScript::Free() {
|
||||
if (globaldata != nullptr)
|
||||
free(globaldata);
|
||||
|
||||
if (code != nullptr)
|
||||
free(code);
|
||||
|
||||
if (strings != nullptr)
|
||||
free(strings);
|
||||
|
||||
if (fixups != nullptr && numfixups > 0)
|
||||
free(fixups);
|
||||
|
||||
if (fixuptypes != nullptr && numfixups > 0)
|
||||
free(fixuptypes);
|
||||
|
||||
globaldata = nullptr;
|
||||
code = nullptr;
|
||||
strings = nullptr;
|
||||
fixups = nullptr;
|
||||
fixuptypes = nullptr;
|
||||
|
||||
int aa;
|
||||
for (aa = 0; aa < numimports; aa++) {
|
||||
if (imports[aa] != nullptr)
|
||||
free(imports[aa]);
|
||||
}
|
||||
|
||||
for (aa = 0; aa < numexports; aa++)
|
||||
free(exports[aa]);
|
||||
|
||||
for (aa = 0; aa < numSections; aa++)
|
||||
free(sectionNames[aa]);
|
||||
|
||||
if (sectionNames != nullptr) {
|
||||
free(sectionNames);
|
||||
free(sectionOffsets);
|
||||
sectionNames = nullptr;
|
||||
sectionOffsets = nullptr;
|
||||
}
|
||||
|
||||
if (imports != nullptr) {
|
||||
free(imports);
|
||||
free(exports);
|
||||
free(export_addr);
|
||||
imports = nullptr;
|
||||
exports = nullptr;
|
||||
export_addr = nullptr;
|
||||
}
|
||||
numimports = 0;
|
||||
numexports = 0;
|
||||
numSections = 0;
|
||||
}
|
||||
|
||||
const char *ccScript::GetSectionName(int32_t offs) const {
|
||||
int i;
|
||||
for (i = 0; i < numSections; i++) {
|
||||
if (sectionOffsets[i] < offs)
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
|
||||
// if no sections in script, return unknown
|
||||
if (i == 0)
|
||||
return "(unknown section)";
|
||||
|
||||
return sectionNames[i - 1];
|
||||
}
|
||||
|
||||
} // namespace AGS3
|
||||
85
engines/ags/shared/script/cc_script.h
Normal file
85
engines/ags/shared/script/cc_script.h
Normal file
@@ -0,0 +1,85 @@
|
||||
/* 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 AGS_SHARED_SCRIPT_CC_SCRIPT_H
|
||||
#define AGS_SHARED_SCRIPT_CC_SCRIPT_H
|
||||
|
||||
#include "common/std/memory.h"
|
||||
#include "ags/shared/core/types.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
namespace AGS {
|
||||
namespace Shared {
|
||||
class Stream;
|
||||
}
|
||||
}
|
||||
using namespace AGS; // FIXME later
|
||||
|
||||
struct ccScript {
|
||||
public:
|
||||
char *globaldata;
|
||||
int32_t globaldatasize;
|
||||
int32_t *code; // executable byte-code, 32-bit per op or arg
|
||||
int32_t codesize; // TODO: find out if we can make it size_t
|
||||
char *strings;
|
||||
int32_t stringssize;
|
||||
char *fixuptypes; // global data/string area/ etc
|
||||
int32_t *fixups; // code array index to fixup (in ints)
|
||||
int numfixups;
|
||||
int importsCapacity;
|
||||
char **imports;
|
||||
int numimports;
|
||||
int exportsCapacity;
|
||||
char **exports; // names of exports
|
||||
int32_t *export_addr; // high byte is type; low 24-bits are offset
|
||||
int numexports;
|
||||
int instances;
|
||||
// 'sections' allow the interpreter to find out which bit
|
||||
// of the code came from header files, and which from the main file
|
||||
char **sectionNames;
|
||||
int32_t *sectionOffsets;
|
||||
int numSections;
|
||||
int capacitySections;
|
||||
|
||||
static ccScript *CreateFromStream(Shared::Stream *in);
|
||||
|
||||
ccScript();
|
||||
ccScript(const ccScript &src);
|
||||
virtual ~ccScript(); // there are few derived classes, so dtor should be virtual
|
||||
|
||||
// write the script to disk (after compiling)
|
||||
void Write(Shared::Stream *out);
|
||||
// read back a script written with Write
|
||||
bool Read(Shared::Stream *in);
|
||||
const char *GetSectionName(int32_t offset) const;
|
||||
|
||||
protected:
|
||||
// free the memory occupied by the script - do NOT attempt to run the
|
||||
// script after calling this function
|
||||
void Free();
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<ccScript> PScript;
|
||||
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
176
engines/ags/shared/util/bbop.h
Normal file
176
engines/ags/shared/util/bbop.h
Normal file
@@ -0,0 +1,176 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// Various utility bit and byte operations
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef AGS_SHARED_UTIL_BBOP_H
|
||||
#define AGS_SHARED_UTIL_BBOP_H
|
||||
|
||||
#include "ags/shared/core/platform.h"
|
||||
#include "ags/shared/core/types.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
#if AGS_PLATFORM_ENDIAN_BIG || defined (TEST_BIGENDIAN)
|
||||
#define BITBYTE_BIG_ENDIAN
|
||||
#endif
|
||||
|
||||
namespace AGS {
|
||||
namespace Shared {
|
||||
|
||||
enum DataEndianess {
|
||||
kBigEndian,
|
||||
kLittleEndian,
|
||||
#if defined (BITBYTE_BIG_ENDIAN)
|
||||
kDefaultSystemEndianess = kBigEndian
|
||||
#else
|
||||
kDefaultSystemEndianess = kLittleEndian
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// Various bit flags operations
|
||||
//
|
||||
// Converts from one flag into another:
|
||||
// sets flag2 if flag1 IS set
|
||||
// TODO: find more optimal way, using bitwise ops?
|
||||
inline int FlagToFlag(int value, int flag1, int flag2) {
|
||||
return ((value & flag1) != 0) * flag2;
|
||||
}
|
||||
// Sets flag2 if flag1 is NOT set
|
||||
inline int FlagToNoFlag(int value, int flag1, int flag2) {
|
||||
return ((value & flag1) == 0) * flag2;
|
||||
}
|
||||
|
||||
|
||||
namespace BitByteOperations {
|
||||
|
||||
struct IntFloatSwap {
|
||||
union {
|
||||
float f;
|
||||
int32_t i32;
|
||||
} val;
|
||||
|
||||
explicit IntFloatSwap(int32_t i) { val.i32 = i; }
|
||||
explicit IntFloatSwap(float f) { val.f = f; }
|
||||
};
|
||||
|
||||
inline int16_t SwapBytesInt16(const int16_t val) {
|
||||
return ((val >> 8) & 0xFF) | ((val << 8) & 0xFF00);
|
||||
}
|
||||
|
||||
inline int32_t SwapBytesInt32(const int32_t val) {
|
||||
return ((val >> 24) & 0xFF) | ((val >> 8) & 0xFF00) | ((val << 8) & 0xFF0000) | ((val << 24) & 0xFF000000);
|
||||
}
|
||||
|
||||
inline int64_t SwapBytesInt64(const int64_t val) {
|
||||
return ((val >> 56) & 0xFF) | ((val >> 40) & 0xFF00) | ((val >> 24) & 0xFF0000) |
|
||||
((val >> 8) & 0xFF000000) | ((val << 8) & 0xFF00000000LL) |
|
||||
((val << 24) & 0xFF0000000000LL) | ((val << 40) & 0xFF000000000000LL) | ((val << 56) & 0xFF00000000000000LL);
|
||||
}
|
||||
|
||||
inline float SwapBytesFloat(const float val) {
|
||||
IntFloatSwap swap(val);
|
||||
swap.val.i32 = SwapBytesInt32(swap.val.i32);
|
||||
return swap.val.f;
|
||||
}
|
||||
|
||||
inline int16_t Int16FromLE(const int16_t val) {
|
||||
#if defined (BITBYTE_BIG_ENDIAN)
|
||||
return SwapBytesInt16(val);
|
||||
#else
|
||||
return val;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline int32_t Int32FromLE(const int32_t val) {
|
||||
#if defined (BITBYTE_BIG_ENDIAN)
|
||||
return SwapBytesInt32(val);
|
||||
#else
|
||||
return val;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline int64_t Int64FromLE(const int64_t val) {
|
||||
#if defined (BITBYTE_BIG_ENDIAN)
|
||||
return SwapBytesInt64(val);
|
||||
#else
|
||||
return val;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline float FloatFromLE(const float val) {
|
||||
#if defined (BITBYTE_BIG_ENDIAN)
|
||||
return SwapBytesFloat(val);
|
||||
#else
|
||||
return val;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline int16_t Int16FromBE(const int16_t val) {
|
||||
#if defined (BITBYTE_BIG_ENDIAN)
|
||||
return val;
|
||||
#else
|
||||
return SwapBytesInt16(val);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline int32_t Int32FromBE(const int32_t val) {
|
||||
#if defined (BITBYTE_BIG_ENDIAN)
|
||||
return val;
|
||||
#else
|
||||
return SwapBytesInt32(val);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline int64_t Int64FromBE(const int64_t val) {
|
||||
#if defined (BITBYTE_BIG_ENDIAN)
|
||||
return val;
|
||||
#else
|
||||
return SwapBytesInt64(val);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline float FloatFromBE(const float val) {
|
||||
#if defined (BITBYTE_BIG_ENDIAN)
|
||||
return val;
|
||||
#else
|
||||
return SwapBytesFloat(val);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace BitByteOperations
|
||||
|
||||
|
||||
// Aliases for easier calling
|
||||
namespace BBOp = BitByteOperations;
|
||||
|
||||
|
||||
} // namespace Shared
|
||||
} // namespace AGS
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user