Initial commit

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

View File

@@ -0,0 +1,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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View File

@@ -0,0 +1,44 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View File

@@ -0,0 +1,51 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View File

@@ -0,0 +1,237 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
//=============================================================================
//
// 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

View File

@@ -0,0 +1,179 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#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

View 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

View 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

View 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