[Scummvm-git-logs] scummvm master -> 27dc8075fb287df0ff659eea2be1a71cef060d25
dreammaster
noreply at scummvm.org
Fri Mar 18 04:51:10 UTC 2022
This automated email contains information about 15 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .
Summary:
ea65fe3c69 AGS: Moved SpriteFile class to a separate code unit for convenience
659be0a4e6 AGS: Fixed few warnings
ddb38177f9 AGS: Updated build version (3.6.0.8)
fc9473d175 AGS: Picked a sprite file writing code into SpriteFileWriter class
6e695f7817 AGS: Only set default character's idle delay for games < 3.6.0
4b68aa31f9 AGS: Process idleview based on real game speed, not hardcoded 40 fps
4293a267cb AGS: Corrected font outline upgrade condition
08560a1dbf AGS: Forgot to also clear guibgbmp array in dispose_game_drawdata()
74254fdcf4 AGS: Simplified couple of set_volume calls
3167bac67b AGS: More clear use of the "max channels" constants
aa91cf1161 AGS: Increased the max number of audio channels to 16
ee311d660f AGS: Make game audiochannels limit defined by a game version
bb1d0849fa AGS: Read and write RoomObject's fields explicitly
78b9068a91 AGS: Replace "localuserconf" option with "user-conf-dir"
27dc8075fb AGS: Configurable user paths support $GAMENAME$ token
Commit: ea65fe3c6998a70d173e78b24e37e1aa28f01ed9
https://github.com/scummvm/scummvm/commit/ea65fe3c6998a70d173e78b24e37e1aa28f01ed9
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-03-17T21:49:22-07:00
Commit Message:
AGS: Moved SpriteFile class to a separate code unit for convenience
>From upstream 393ef2c24bbc835d8e7a7229d40f638a6f172304
Changed paths:
A engines/ags/shared/ac/sprite_file.cpp
A engines/ags/shared/ac/sprite_file.h
engines/ags/engine/ac/sprite_cache_engine.cpp
engines/ags/globals.cpp
engines/ags/globals.h
engines/ags/module.mk
engines/ags/shared/ac/sprite_cache.cpp
engines/ags/shared/ac/sprite_cache.h
diff --git a/engines/ags/engine/ac/sprite_cache_engine.cpp b/engines/ags/engine/ac/sprite_cache_engine.cpp
index 31a0a9e68c5..fdcefb8e871 100644
--- a/engines/ags/engine/ac/sprite_cache_engine.cpp
+++ b/engines/ags/engine/ac/sprite_cache_engine.cpp
@@ -25,13 +25,6 @@
//
//=============================================================================
-// Headers, as they are in sprcache.cpp
-#ifdef _MANAGED
-// ensure this doesn't get compiled to .NET IL
-#pragma unmanaged
-#pragma warning (disable: 4996 4312) // disable deprecation warnings
-#endif
-
#include "ags/shared/ac/game_struct_defines.h"
#include "ags/shared/ac/sprite_cache.h"
#include "ags/shared/util/compress.h"
@@ -42,7 +35,7 @@ namespace AGS3 {
// Engine-specific implementation split out of sprcache.cpp
//=============================================================================
-void SpriteCache::InitNullSpriteParams(sprkey_t index) {
+void AGS::Shared::SpriteCache::InitNullSpriteParams(sprkey_t index) {
// make it a blue cup, to avoid crashes
_sprInfos[index].Width = _sprInfos[0].Width;
_sprInfos[index].Height = _sprInfos[0].Height;
diff --git a/engines/ags/globals.cpp b/engines/ags/globals.cpp
index 2e42791721d..69b5b78ac9a 100644
--- a/engines/ags/globals.cpp
+++ b/engines/ags/globals.cpp
@@ -220,7 +220,7 @@ Globals::Globals() {
_guis = new std::vector<AGS::Shared::GUIMain>();
_play = new GameState();
_game = new GameSetupStruct();
- _spriteset = new SpriteCache(_game->SpriteInfos);
+ _spriteset = new AGS::Shared::SpriteCache(_game->SpriteInfos);
_thisroom = new AGS::Shared::RoomStruct();
_troom = new RoomStatus();
_usetup = new GameSetup();
diff --git a/engines/ags/globals.h b/engines/ags/globals.h
index 15cde98d733..f15e523ff27 100644
--- a/engines/ags/globals.h
+++ b/engines/ags/globals.h
@@ -75,6 +75,7 @@ class GUITextBox;
struct InteractionVariable;
struct PlaneScaling;
class RoomStruct;
+class SpriteCache;
struct Translation;
} // namespace Shared
@@ -100,7 +101,6 @@ class EngineExports;
class Navigation;
class SplitLines;
-class SpriteCache;
class TTFFontRenderer;
class WFNFontRenderer;
@@ -709,7 +709,7 @@ public:
GameSetupStruct *_game;
GameState *_play;
- SpriteCache *_spriteset;
+ AGS::Shared::SpriteCache *_spriteset;
AGS::Shared::RoomStruct *_thisroom;
RoomStatus *_troom; // used for non-saveable rooms, eg. intro
diff --git a/engines/ags/module.mk b/engines/ags/module.mk
index dab643747ff..257df04c895 100644
--- a/engines/ags/module.mk
+++ b/engines/ags/module.mk
@@ -39,6 +39,7 @@ MODULE_OBJS = \
shared/ac/keycode.o \
shared/ac/mouse_cursor.o \
shared/ac/sprite_cache.o \
+ shared/ac/sprite_file.o \
shared/ac/view.o \
shared/ac/words_dictionary.o \
shared/core/asset.o \
diff --git a/engines/ags/shared/ac/sprite_cache.cpp b/engines/ags/shared/ac/sprite_cache.cpp
index 59440303950..60b6b145f26 100644
--- a/engines/ags/shared/ac/sprite_cache.cpp
+++ b/engines/ags/shared/ac/sprite_cache.cpp
@@ -25,23 +25,14 @@
//
//=============================================================================
-#ifdef _MANAGED
-// ensure this doesn't get compiled to .NET IL
-#pragma unmanaged
-#pragma warning (disable: 4996 4312) // disable deprecation warnings
-#endif
-
#include "common/system.h"
+#include "ags/shared/util/stream.h"
#include "ags/lib/std/algorithm.h"
+#include "ags/shared/ac/sprite_cache.h"
#include "ags/shared/ac/common.h" // quit
#include "ags/shared/ac/game_struct_defines.h"
-#include "ags/shared/ac/sprite_cache.h"
-#include "ags/shared/core/asset_manager.h"
#include "ags/shared/debugging/out.h"
#include "ags/shared/gfx/bitmap.h"
-#include "ags/shared/util/compress.h"
-#include "ags/shared/util/file.h"
-#include "ags/shared/util/stream.h"
#include "ags/globals.h"
namespace AGS3 {
@@ -56,18 +47,15 @@ extern void get_new_size_for_sprite(int, int, int, int &, int &);
#define START_OF_LIST -1
#define END_OF_LIST -1
-const char *spindexid = "SPRINDEX";
-
-// TODO: should not be part of SpriteCache, but rather some asset management class?
-const char *const SpriteFile::DefaultSpriteFileName = "acsprset.spr";
-const char *const SpriteFile::DefaultSpriteIndexName = "sprindex.dat";
-
SpriteInfo::SpriteInfo()
: Flags(0)
, Width(0)
, Height(0) {
}
+namespace AGS {
+namespace Shared {
+
SpriteCache::SpriteData::SpriteData()
: Size(0)
, Flags(0)
@@ -79,12 +67,6 @@ SpriteCache::SpriteData::~SpriteData() {
// (some of these bitmaps may be assigned from outside of the cache)
}
-
-SpriteFile::SpriteFile() {
- _compressed = false;
- _curPos = -2;
-}
-
SpriteCache::SpriteCache(std::vector<SpriteInfo> &sprInfos)
: _sprInfos(sprInfos) {
Init();
@@ -110,14 +92,6 @@ size_t SpriteCache::GetSpriteSlotCount() const {
return _spriteData.size();
}
-sprkey_t SpriteFile::FindTopmostSprite(const std::vector<Bitmap *> &sprites) {
- sprkey_t topmost = -1;
- for (sprkey_t i = 0; i < static_cast<sprkey_t>(sprites.size()); ++i)
- if (sprites[i])
- topmost = i;
- return topmost;
-}
-
void SpriteCache::SetMaxCacheSize(size_t size) {
_maxCacheSize = size;
}
@@ -379,14 +353,6 @@ sprkey_t SpriteCache::GetDataIndex(sprkey_t index) {
return (_spriteData[index].Flags & SPRCACHEFLAG_REMAPPED) == 0 ? index : 0;
}
-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;
- }
-}
-
size_t SpriteCache::LoadSprite(sprkey_t index) {
int hh = 0;
@@ -457,119 +423,6 @@ void SpriteCache::RemapSpriteToSprite0(sprkey_t index) {
#endif
}
-const char *spriteFileSig = " Sprite File ";
-
-int SpriteFile::SaveToFile(const String &save_to_file,
- const std::vector<Bitmap *> &sprites,
- SpriteFile *read_from_file,
- bool compressOutput, SpriteFileIndex &index) {
- std::unique_ptr<Stream> output(File::CreateFile(save_to_file));
- if (output == nullptr)
- return -1;
-
- int spriteFileIDCheck = g_system->getMillis();
-
- // sprite file version
- output->WriteInt16(kSprfVersion_Current);
-
- output->WriteArray(spriteFileSig, strlen(spriteFileSig), 1);
-
- output->WriteInt8(compressOutput ? 1 : 0);
- output->WriteInt32(spriteFileIDCheck);
-
- sprkey_t lastslot = read_from_file ? read_from_file->GetTopmostSprite() : 0;
- lastslot = std::max(lastslot, FindTopmostSprite(sprites));
- output->WriteInt32(lastslot);
-
- // allocate buffers to store the indexing info
- sprkey_t numsprits = lastslot + 1;
- std::vector<int16_t> spritewidths, spriteheights;
- std::vector<soff_t> spriteoffs;
- spritewidths.resize(numsprits);
- spriteheights.resize(numsprits);
- spriteoffs.resize(numsprits);
- std::unique_ptr<Bitmap> temp_bmp; // for disposing temp sprites
- std::vector<char> membuf; // for loading raw sprite data
-
- const bool diff_compress =
- read_from_file && read_from_file->IsFileCompressed() != compressOutput;
-
- for (sprkey_t i = 0; i <= lastslot; ++i) {
- soff_t sproff = output->GetPosition();
-
- Bitmap *image = (size_t)i < sprites.size() ? sprites[i] : nullptr;
-
- // 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) {
- // image in memory -- write it out
- int bpp = image->GetColorDepth() / 8;
- spriteoffs[i] = sproff;
- spritewidths[i] = image->GetWidth();
- spriteheights[i] = image->GetHeight();
- output->WriteInt16(bpp);
- output->WriteInt16(spritewidths[i]);
- output->WriteInt16(spriteheights[i]);
-
- if (compressOutput) {
- soff_t lenloc = output->GetPosition();
- // write some space for the length data
- output->WriteInt32(0);
-
- rle_compress(image, output.get());
-
- soff_t fileSizeSoFar = output->GetPosition();
- // write the length of the compressed data
- output->Seek(lenloc, kSeekBegin);
- output->WriteInt32((fileSizeSoFar - lenloc) - 4);
- output->Seek(0, kSeekEnd);
- } else {
- output->WriteArray(image->GetDataForWriting(), spritewidths[i] * bpp, spriteheights[i]);
- }
- continue;
- } else if (diff_compress) {
- // sprite doesn't exist
- output->WriteInt16(0); // colour depth
- continue;
- }
-
- // Not in memory - and same compression option;
- // Directly copy the sprite bytes from the input file to the output
- Size metric;
- int bpp;
- read_from_file->LoadSpriteData(i, metric, bpp, membuf);
-
- output->WriteInt16(bpp);
- if (bpp == 0)
- continue; // empty slot
-
- spriteoffs[i] = sproff;
- spritewidths[i] = metric.Width;
- spriteheights[i] = metric.Height;
- output->WriteInt16(metric.Width);
- output->WriteInt16(metric.Height);
- if (compressOutput)
- output->WriteInt32(membuf.size());
- if (membuf.size() == 0)
- continue; // bad data?
- output->Write(&membuf[0], membuf.size());
- }
-
- index.SpriteFileIDCheck = spriteFileIDCheck;
- index.LastSlot = lastslot;
- index.SpriteCount = numsprits;
- index.Widths = spritewidths;
- index.Heights = spriteheights;
- index.Offsets = spriteoffs;
- return 0;
-}
-
int SpriteCache::SaveToFile(const String &filename, bool compressOutput, SpriteFileIndex &index) {
std::vector<Bitmap *> sprites;
for (const auto &data : _spriteData) {
@@ -584,29 +437,6 @@ int SpriteCache::SaveToFile(const String &filename, bool compressOutput, SpriteF
return _file.SaveToFile(filename, sprites, &_file, compressOutput, index);
}
-int SpriteFile::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.LastSlot);
- out->WriteInt32(index.SpriteCount);
- if (index.SpriteCount > 0) {
- out->WriteArrayOfInt16(&index.Widths.front(), index.Widths.size());
- out->WriteArrayOfInt16(&index.Heights.front(), index.Heights.size());
- out->WriteArrayOfInt64(&index.Offsets.front(), index.Offsets.size());
- }
- delete out;
- return 0;
-}
-
HError SpriteCache::InitFile(const String &filename, const String &sprindex_filename) {
std::vector<Size> metrics;
HError err = _file.OpenFile(filename, sprindex_filename, metrics);
@@ -634,277 +464,10 @@ HError SpriteCache::InitFile(const String &filename, const String &sprindex_file
return HError::None();
}
-HError SpriteFile::RebuildSpriteIndex(Stream *in, sprkey_t topmost,
- SpriteFileVersion vers, std::vector<Size> &metrics) {
- for (sprkey_t i = 0; i <= topmost; ++i) {
- _spriteData[i].Offset = in->GetPosition();
-
- int coldep = in->ReadInt16();
-
- if (coldep == 0) {
- if (in->EOS())
- break;
- continue;
- }
-
- if (in->EOS())
- break;
-
- if ((size_t)i >= _spriteData.size())
- break;
-
- int wdd = in->ReadInt16();
- int htt = in->ReadInt16();
- metrics[i].Width = wdd;
- metrics[i].Height = htt;
-
- size_t spriteDataSize;
- if (vers == kSprfVersion_Compressed) {
- spriteDataSize = in->ReadInt32();
- } else if (vers >= kSprfVersion_Last32bit) {
- spriteDataSize = this->_compressed ? in->ReadInt32() : wdd * coldep * htt;
- } else {
- spriteDataSize = wdd * coldep * htt;
- }
- in->Seek(spriteDataSize);
- }
- return HError::None();
-}
-
-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;
-}
-
void SpriteCache::DetachFile() {
_file.Reset();
}
-bool SpriteFile::IsFileCompressed() const {
- return _compressed;
-}
-
-sprkey_t SpriteFile::GetTopmostSprite() const {
- return (sprkey_t)_spriteData.size() - 1;
-}
-
-void SpriteFile::Reset() {
- _stream.reset();
- _curPos = -2;
-}
-
-HAGSError SpriteFile::LoadSprite(sprkey_t index, Shared::Bitmap *&sprite) {
- sprite = nullptr;
- if (index < 0 || (size_t)index >= _spriteData.size())
- 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
-
- int coldep = _stream->ReadInt16();
- if (coldep == 0) { // empty slot, this is normal
- return HError::None();
- }
-
- int wdd = _stream->ReadInt16();
- int htt = _stream->ReadInt16();
- Bitmap *image = BitmapHelper::CreateBitmap(wdd, htt, coldep * 8);
- if (image == nullptr) {
- return new Error(String::FromFormat("LoadSprite: failed to allocate bitmap %d (%dx%d%d).",
- index, wdd, htt, coldep * 8));
- }
-
- if (_compressed) {
- size_t data_size = _stream->ReadInt32();
- if (data_size == 0) {
- delete image;
- return new Error(String::FromFormat("LoadSprite: bad compressed data for sprite %d.", index));
- }
- rle_decompress(image, _stream.get());
- } else {
- if (coldep == 1) {
- for (int h = 0; h < htt; ++h)
- _stream->ReadArray(&image->GetScanLineForWriting(h)[0], coldep, wdd);
- } else if (coldep == 2) {
- for (int h = 0; h < htt; ++h)
- _stream->ReadArrayOfInt16((int16_t *)&image->GetScanLineForWriting(h)[0], wdd);
- } else {
- for (int h = 0; h < htt; ++h)
- _stream->ReadArrayOfInt32((int32_t *)&image->GetScanLineForWriting(h)[0], wdd);
- }
- }
- sprite = image;
- _curPos = index + 1; // mark correct pos
- return HError::None();
-}
-
-HError SpriteFile::LoadSpriteData(sprkey_t index, Size &metric, int &bpp,
- std::vector<char> &data) {
- metric = Size();
- bpp = 0;
-
- if (index < 0 || (size_t)index >= _spriteData.size())
- 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
-
- int coldep = _stream->ReadInt16();
- if (coldep == 0) { // empty slot, this is normal
- metric = Size();
- bpp = 0;
- data.resize(0);
- return HError::None();
- }
-
- int width = _stream->ReadInt16();
- int height = _stream->ReadInt16();
-
- size_t data_size;
- if (_compressed)
- data_size = _stream->ReadInt32();
- else
- data_size = width * height * coldep;
- data.resize(data_size);
- _stream->Read(&data[0], data_size);
- metric = Size(width, height);
- bpp = coldep;
- _curPos = index + 1; // mark correct pos
- return HError::None();
-}
-
-HAGSError SpriteFile::OpenFile(const String &filename, const String &sprindex_filename,
- std::vector<Size> &metrics) {
- SpriteFileVersion vers;
- 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();
-
- vers = (SpriteFileVersion)_stream->ReadInt16();
- // read the "Sprite File" signature
- _stream->ReadArray(&buff[0], 13, 1);
-
- if (vers < kSprfVersion_Uncompressed || vers > kSprfVersion_Current) {
- _stream.reset();
- return new Error(String::FromFormat("Unsupported spriteset format (requested %d, supported %d - %d).", vers, kSprfVersion_Uncompressed, kSprfVersion_Current));
- }
-
- // unknown version
- buff[13] = 0;
- if (strcmp(buff, spriteFileSig)) {
- _stream.reset();
- return new Error("Unknown spriteset format.");
- }
-
- if (vers == kSprfVersion_Uncompressed) {
- this->_compressed = false;
- } else if (vers == kSprfVersion_Compressed) {
- this->_compressed = true;
- } else if (vers >= kSprfVersion_Last32bit) {
- this->_compressed = (_stream->ReadInt8() == 1);
- spriteFileID = _stream->ReadInt32();
- }
-
- if (vers < kSprfVersion_Compressed) {
- // skip the palette
- _stream->Seek(256 * 3); // sizeof(RGB) * 256
- }
-
- sprkey_t topmost;
- if (vers < kSprfVersion_HighSpriteLimit)
- topmost = (uint16_t)_stream->ReadInt16();
- else
- topmost = _stream->ReadInt32();
- if (vers < kSprfVersion_Uncompressed)
- topmost = 200;
-
- _spriteData.resize(topmost + 1);
- metrics.resize(topmost + 1);
-
- // 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, vers, metrics);
-}
-
+} // namespace Shared
+} // namespace AGS
} // namespace AGS3
diff --git a/engines/ags/shared/ac/sprite_cache.h b/engines/ags/shared/ac/sprite_cache.h
index 2b19e88edc1..fb830b963e2 100644
--- a/engines/ags/shared/ac/sprite_cache.h
+++ b/engines/ags/shared/ac/sprite_cache.h
@@ -43,6 +43,7 @@
#include "ags/lib/std/memory.h"
#include "ags/lib/std/vector.h"
+#include "ags/shared/ac/sprite_file.h"
#include "ags/shared/core/platform.h"
#include "ags/shared/util/error.h"
#include "ags/shared/util/geometry.h"
@@ -76,91 +77,10 @@ struct SpriteInfo;
#define DEFAULTCACHESIZE_KB (128 * 1024)
#endif
-// TODO: research old version differences
-enum SpriteFileVersion {
- kSprfVersion_Uncompressed = 4,
- kSprfVersion_Compressed = 5,
- kSprfVersion_Last32bit = 6,
- kSprfVersion_64bit = 10,
- kSprfVersion_HighSpriteLimit = 11,
- kSprfVersion_Current = kSprfVersion_HighSpriteLimit
-};
-
-enum SpriteIndexFileVersion {
- kSpridxfVersion_Initial = 1,
- kSpridxfVersion_Last32bit = 2,
- kSpridxfVersion_64bit = 10,
- kSpridxfVersion_HighSpriteLimit = 11,
- kSpridxfVersion_Current = kSpridxfVersion_HighSpriteLimit
-};
-
-
-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
- sprkey_t LastSlot = -1;
- size_t SpriteCount = 0u;
- std::vector<int16_t> Widths;
- std::vector<int16_t> Heights;
- std::vector<soff_t> Offsets;
-};
-
-class SpriteFile {
-public:
- // Standart sprite file and sprite index names
- static const char *const DefaultSpriteFileName;
- static const char *const DefaultSpriteIndexName;
-
- SpriteFile();
- // Loads sprite reference information and inits sprite stream
- HAGSError OpenFile(const Shared::String &filename, const Shared::String &sprindex_filename,
- std::vector<Size> &metrics);
- void Reset();
-
- // Tells if bitmaps in the file are compressed
- bool IsFileCompressed() const;
- // Tells the highest known sprite index
- sprkey_t GetTopmostSprite() const;
-
- // Loads sprite index file
- bool LoadSpriteIndexFile(const Shared::String &filename, int expectedFileID,
- soff_t spr_initial_offs, sprkey_t topmost, std::vector<Size> &metrics);
- // Rebuilds sprite index from the main sprite file
- HAGSError RebuildSpriteIndex(AGS::Shared::Stream *in, sprkey_t topmost, SpriteFileVersion vers,
- std::vector<Size> &metrics);
-
- HAGSError LoadSprite(sprkey_t index, Shared::Bitmap *&sprite);
- HAGSError LoadSpriteData(sprkey_t index, Size &metric, int &bpp, std::vector<char> &data);
-
- // 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?)
- static int SaveToFile(const Shared::String &save_to_file,
- const std::vector<Shared::Bitmap *> &sprites, // available sprites (may contain nullptrs)
- SpriteFile *read_from_file, // optional file to read missing sprites from
- bool compressOutput, SpriteFileIndex &index);
- // Saves sprite index table in a separate file
- static int SaveSpriteIndex(const Shared::String &filename, const SpriteFileIndex &index);
-
-private:
- // Finds the topmost occupied slot index. Warning: may be slow.
- static sprkey_t FindTopmostSprite(const std::vector<Shared::Bitmap *> &sprites);
- // Seek stream to sprite
- void SeekToSprite(sprkey_t index);
-
- // Internal sprite reference
- struct SpriteRef {
- soff_t Offset = 0; // data offset
- size_t Size = 0; // cache size of element, in bytes
- };
+struct SpriteInfo;
- // Array of sprite references
- std::vector<SpriteRef> _spriteData;
- std::unique_ptr<Shared::Stream> _stream; // the sprite stream
- bool _compressed; // are sprites compressed
- sprkey_t _curPos; // current stream position (sprite slot)
-};
+namespace AGS {
+namespace Shared {
class SpriteCache {
public:
@@ -172,7 +92,7 @@ public:
~SpriteCache();
// Loads sprite reference information and inits sprite stream
- HAGSError InitFile(const Shared::String &filename, const Shared::String &sprindex_filename);
+ HError InitFile(const Shared::String &filename, const Shared::String &sprindex_filename);
// Saves current cache contents to the file
int SaveToFile(const Shared::String &filename, bool compressOutput, SpriteFileIndex &index);
// Closes an active sprite file stream
@@ -277,6 +197,8 @@ private:
void InitNullSpriteParams(sprkey_t index);
};
+} // namespace Shared
+} // namespace AGS
} // namespace AGS3
#endif
diff --git a/engines/ags/shared/ac/sprite_file.cpp b/engines/ags/shared/ac/sprite_file.cpp
new file mode 100644
index 00000000000..aef315b54ec
--- /dev/null
+++ b/engines/ags/shared/ac/sprite_file.cpp
@@ -0,0 +1,467 @@
+/* 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 "ags/lib/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/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";
+
+
+SpriteFile::SpriteFile() {
+ _compressed = false;
+ _curPos = -2;
+}
+
+HError SpriteFile::OpenFile(const String &filename, const String &sprindex_filename,
+ std::vector<Size> &metrics) {
+ SpriteFileVersion vers;
+ 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();
+
+ vers = (SpriteFileVersion)_stream->ReadInt16();
+ // read the "Sprite File" signature
+ _stream->ReadArray(&buff[0], 13, 1);
+
+ if (vers < kSprfVersion_Uncompressed || vers > kSprfVersion_Current) {
+ _stream.reset();
+ return new Error(String::FromFormat("Unsupported spriteset format (requested %d, supported %d - %d).", vers, kSprfVersion_Uncompressed, kSprfVersion_Current));
+ }
+
+ // unknown version
+ buff[13] = 0;
+ if (strcmp(buff, spriteFileSig)) {
+ _stream.reset();
+ return new Error("Uknown spriteset format.");
+ }
+
+ if (vers == kSprfVersion_Uncompressed) {
+ this->_compressed = false;
+ } else if (vers == kSprfVersion_Compressed) {
+ this->_compressed = true;
+ } else if (vers >= kSprfVersion_Last32bit) {
+ this->_compressed = (_stream->ReadInt8() == 1);
+ spriteFileID = _stream->ReadInt32();
+ }
+
+ if (vers < kSprfVersion_Compressed) {
+ // skip the palette
+ _stream->Seek(256 * 3); // sizeof(RGB) * 256
+ }
+
+ sprkey_t topmost;
+ if (vers < kSprfVersion_HighSpriteLimit)
+ topmost = (uint16_t)_stream->ReadInt16();
+ else
+ topmost = _stream->ReadInt32();
+ if (vers < kSprfVersion_Uncompressed)
+ topmost = 200;
+
+ _spriteData.resize(topmost + 1);
+ metrics.resize(topmost + 1);
+
+ // 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, vers, metrics);
+}
+
+void SpriteFile::Reset() {
+ _stream.reset();
+ _curPos = -2;
+}
+
+bool SpriteFile::IsFileCompressed() const {
+ return _compressed;
+}
+
+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;
+}
+
+HError SpriteFile::RebuildSpriteIndex(Stream *in, sprkey_t topmost,
+ SpriteFileVersion vers, std::vector<Size> &metrics) {
+ for (sprkey_t i = 0; i <= topmost; ++i) {
+ _spriteData[i].Offset = in->GetPosition();
+
+ int coldep = in->ReadInt16();
+
+ if (coldep == 0) {
+ if (in->EOS())
+ break;
+ continue;
+ }
+
+ if (in->EOS())
+ break;
+
+ if ((size_t)i >= _spriteData.size())
+ break;
+
+ int wdd = in->ReadInt16();
+ int htt = in->ReadInt16();
+ metrics[i].Width = wdd;
+ metrics[i].Height = htt;
+
+ size_t spriteDataSize;
+ if (vers == kSprfVersion_Compressed) {
+ spriteDataSize = in->ReadInt32();
+ } else if (vers >= kSprfVersion_Last32bit) {
+ spriteDataSize = this->_compressed ? in->ReadInt32() : wdd * coldep * htt;
+ } else {
+ spriteDataSize = wdd * coldep * htt;
+ }
+ in->Seek(spriteDataSize);
+ }
+ return HError::None();
+}
+
+HError SpriteFile::LoadSprite(sprkey_t index, Shared::Bitmap *&sprite) {
+ sprite = nullptr;
+ if (index < 0 || (size_t)index >= _spriteData.size())
+ 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
+
+ int coldep = _stream->ReadInt16();
+ if (coldep == 0) { // empty slot, this is normal
+ return HError::None();
+ }
+
+ int wdd = _stream->ReadInt16();
+ int htt = _stream->ReadInt16();
+ Bitmap *image = BitmapHelper::CreateBitmap(wdd, htt, coldep * 8);
+ if (image == nullptr) {
+ return new Error(String::FromFormat("LoadSprite: failed to allocate bitmap %d (%dx%d%d).",
+ index, wdd, htt, coldep * 8));
+ }
+
+ if (_compressed) {
+ size_t data_size = _stream->ReadInt32();
+ if (data_size == 0) {
+ delete image;
+ return new Error(String::FromFormat("LoadSprite: bad compressed data for sprite %d.", index));
+ }
+ rle_decompress(image, _stream.get());
+ } else {
+ if (coldep == 1) {
+ for (int h = 0; h < htt; ++h)
+ _stream->ReadArray(&image->GetScanLineForWriting(h)[0], coldep, wdd);
+ } else if (coldep == 2) {
+ for (int h = 0; h < htt; ++h)
+ _stream->ReadArrayOfInt16((int16_t *)&image->GetScanLineForWriting(h)[0], wdd);
+ } else {
+ for (int h = 0; h < htt; ++h)
+ _stream->ReadArrayOfInt32((int32_t *)&image->GetScanLineForWriting(h)[0], wdd);
+ }
+ }
+ sprite = image;
+ _curPos = index + 1; // mark correct pos
+ return HError::None();
+}
+
+HError SpriteFile::LoadSpriteData(sprkey_t index, Size &metric, int &bpp,
+ std::vector<char> &data) {
+ metric = Size();
+ bpp = 0;
+ if (index < 0 || (size_t)index >= _spriteData.size())
+ 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
+
+ int coldep = _stream->ReadInt16();
+ if (coldep == 0) { // empty slot, this is normal
+ metric = Size();
+ bpp = 0;
+ data.resize(0);
+ return HError::None();
+ }
+
+ int width = _stream->ReadInt16();
+ int height = _stream->ReadInt16();
+
+ size_t data_size;
+ if (_compressed)
+ data_size = _stream->ReadInt32();
+ else
+ data_size = width * height * coldep;
+ data.resize(data_size);
+ _stream->Read(&data[0], data_size);
+ metric = Size(width, height);
+ bpp = coldep;
+ _curPos = index + 1; // mark correct pos
+ return HError::None();
+}
+
+sprkey_t SpriteFile::FindTopmostSprite(const std::vector<Bitmap *> &sprites) {
+ sprkey_t topmost = -1;
+ for (sprkey_t i = 0; i < static_cast<sprkey_t>(sprites.size()); ++i)
+ if (sprites[i])
+ topmost = i;
+ return topmost;
+}
+
+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;
+ }
+}
+
+int SpriteFile::SaveToFile(const String &save_to_file,
+ const std::vector<Bitmap *> &sprites,
+ SpriteFile *read_from_file,
+ bool compressOutput, SpriteFileIndex &index) {
+ std::unique_ptr<Stream> output(File::CreateFile(save_to_file));
+ if (output == nullptr)
+ return -1;
+
+ int spriteFileIDCheck = g_system->getMillis();
+
+ // sprite file version
+ output->WriteInt16(kSprfVersion_Current);
+
+ output->WriteArray(spriteFileSig, strlen(spriteFileSig), 1);
+
+ output->WriteInt8(compressOutput ? 1 : 0);
+ output->WriteInt32(spriteFileIDCheck);
+
+ sprkey_t lastslot = read_from_file ? read_from_file->GetTopmostSprite() : 0;
+ lastslot = std::max(lastslot, FindTopmostSprite(sprites));
+ output->WriteInt32(lastslot);
+
+ // allocate buffers to store the indexing info
+ sprkey_t numsprits = lastslot + 1;
+ std::vector<int16_t> spritewidths, spriteheights;
+ std::vector<soff_t> spriteoffs;
+ spritewidths.resize(numsprits);
+ spriteheights.resize(numsprits);
+ spriteoffs.resize(numsprits);
+ std::unique_ptr<Bitmap> temp_bmp; // for disposing temp sprites
+ std::vector<char> membuf; // for loading raw sprite data
+
+ const bool diff_compress =
+ read_from_file && read_from_file->IsFileCompressed() != compressOutput;
+
+ for (sprkey_t i = 0; i <= lastslot; ++i) {
+ soff_t sproff = output->GetPosition();
+
+ Bitmap *image = (size_t)i < sprites.size() ? sprites[i] : nullptr;
+
+ // 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) {
+ // image in memory -- write it out
+ int bpp = image->GetColorDepth() / 8;
+ spriteoffs[i] = sproff;
+ spritewidths[i] = image->GetWidth();
+ spriteheights[i] = image->GetHeight();
+ output->WriteInt16(bpp);
+ output->WriteInt16(spritewidths[i]);
+ output->WriteInt16(spriteheights[i]);
+
+ if (compressOutput) {
+ soff_t lenloc = output->GetPosition();
+ // write some space for the length data
+ output->WriteInt32(0);
+
+ rle_compress(image, output.get());
+
+ soff_t fileSizeSoFar = output->GetPosition();
+ // write the length of the compressed data
+ output->Seek(lenloc, kSeekBegin);
+ output->WriteInt32((fileSizeSoFar - lenloc) - 4);
+ output->Seek(0, kSeekEnd);
+ } else {
+ output->WriteArray(image->GetDataForWriting(), spritewidths[i] * bpp, spriteheights[i]);
+ }
+ continue;
+ } else if (diff_compress) {
+ // sprite doesn't exist
+ output->WriteInt16(0); // colour depth
+ continue;
+ }
+
+ // Not in memory - and same compression option;
+ // Directly copy the sprite bytes from the input file to the output
+ Size metric;
+ int bpp;
+ read_from_file->LoadSpriteData(i, metric, bpp, membuf);
+
+ output->WriteInt16(bpp);
+ if (bpp == 0)
+ continue; // empty slot
+
+ spriteoffs[i] = sproff;
+ spritewidths[i] = metric.Width;
+ spriteheights[i] = metric.Height;
+ output->WriteInt16(metric.Width);
+ output->WriteInt16(metric.Height);
+ if (compressOutput)
+ output->WriteInt32(membuf.size());
+ if (membuf.size() == 0)
+ continue; // bad data?
+ output->Write(&membuf[0], membuf.size());
+ }
+
+ index.SpriteFileIDCheck = spriteFileIDCheck;
+ index.LastSlot = lastslot;
+ index.SpriteCount = numsprits;
+ index.Widths = spritewidths;
+ index.Heights = spriteheights;
+ index.Offsets = spriteoffs;
+ return 0;
+}
+
+int SpriteFile::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.LastSlot);
+ out->WriteInt32(index.SpriteCount);
+ if (index.SpriteCount > 0) {
+ out->WriteArrayOfInt16(&index.Widths.front(), index.Widths.size());
+ out->WriteArrayOfInt16(&index.Heights.front(), index.Heights.size());
+ out->WriteArrayOfInt64(&index.Offsets.front(), index.Offsets.size());
+ }
+ delete out;
+ return 0;
+}
+
+} // namespace Shared
+} // namespace AGS
+} // namespace AGS3
diff --git a/engines/ags/shared/ac/sprite_file.h b/engines/ags/shared/ac/sprite_file.h
new file mode 100644
index 00000000000..90cea21d35f
--- /dev/null
+++ b/engines/ags/shared/ac/sprite_file.h
@@ -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/>.
+ *
+ */
+
+//=============================================================================
+//
+// SpriteFile class handles sprite file loading and streaming.
+//
+//=============================================================================
+
+#ifndef AGS_SHARED_AC_SPRITE_FILE_H
+#define AGS_SHARED_AC_SPRITE_FILE_H
+
+#include "ags/shared/core/types.h"
+#include "ags/lib/std/vector.h"
+#include "ags/globals.h"
+
+namespace AGS3 {
+namespace AGS {
+namespace Shared {
+
+class Bitmap;
+
+// TODO: research old version differences
+enum SpriteFileVersion {
+ kSprfVersion_Uncompressed = 4,
+ kSprfVersion_Compressed = 5,
+ kSprfVersion_Last32bit = 6,
+ kSprfVersion_64bit = 10,
+ kSprfVersion_HighSpriteLimit = 11,
+ kSprfVersion_Current = kSprfVersion_HighSpriteLimit
+};
+
+enum SpriteIndexFileVersion {
+ kSpridxfVersion_Initial = 1,
+ kSpridxfVersion_Last32bit = 2,
+ kSpridxfVersion_64bit = 10,
+ kSpridxfVersion_HighSpriteLimit = 11,
+ kSpridxfVersion_Current = kSpridxfVersion_HighSpriteLimit
+};
+
+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
+ sprkey_t LastSlot = -1;
+ size_t SpriteCount = 0u;
+ std::vector<int16_t> Widths;
+ std::vector<int16_t> Heights;
+ std::vector<soff_t> Offsets;
+};
+
+
+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);
+ void Reset();
+
+ // Tells if bitmaps in the file are compressed
+ bool IsFileCompressed() 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, SpriteFileVersion vers,
+ std::vector<Size> &metrics);
+
+ HError LoadSprite(sprkey_t index, Bitmap *&sprite);
+ HError LoadSpriteData(sprkey_t index, Size &metric, int &bpp, std::vector<char> &data);
+
+ // 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?)
+ static int SaveToFile(const String &save_to_file,
+ const std::vector<Bitmap *> &sprites, // available sprites (may contain nullptrs)
+ SpriteFile *read_from_file, // optional file to read missing sprites from
+ bool compressOutput, SpriteFileIndex &index);
+ // Saves sprite index table in a separate file
+ static int SaveSpriteIndex(const String &filename, const SpriteFileIndex &index);
+
+private:
+ // Finds the topmost occupied slot index. Warning: may be slow.
+ static sprkey_t FindTopmostSprite(const std::vector<Bitmap *> &sprites);
+ // Seek stream to sprite
+ void SeekToSprite(sprkey_t index);
+
+ // Internal sprite reference
+ struct SpriteRef {
+ soff_t Offset = 0; // data offset
+ size_t Size = 0; // cache size of element, in bytes
+ };
+
+ // Array of sprite references
+ std::vector<SpriteRef> _spriteData;
+ std::unique_ptr<Stream> _stream; // the sprite stream
+ bool _compressed; // are sprites compressed
+ sprkey_t _curPos; // current stream position (sprite slot)
+};
+
+} // namespace Shared
+} // namespace AGS
+} // namespace AGS3
+
+#endif
Commit: 659be0a4e67857b46a422a0c7d158d07f3631252
https://github.com/scummvm/scummvm/commit/659be0a4e67857b46a422a0c7d158d07f3631252
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-03-17T21:49:23-07:00
Commit Message:
AGS: Fixed few warnings
>From upstream b416d55eb74745db0da5a3cb2654e063cd057ded
Changed paths:
engines/ags/engine/ac/drawing_surface.cpp
engines/ags/engine/ac/global_game.cpp
engines/ags/engine/ac/overlay.cpp
diff --git a/engines/ags/engine/ac/drawing_surface.cpp b/engines/ags/engine/ac/drawing_surface.cpp
index 1c06f801a62..5ccd8d16c59 100644
--- a/engines/ags/engine/ac/drawing_surface.cpp
+++ b/engines/ags/engine/ac/drawing_surface.cpp
@@ -226,7 +226,7 @@ void DrawingSurface_DrawSurfaceEx(ScriptDrawingSurface *target, ScriptDrawingSur
int dst_x, int dst_y, int dst_width, int dst_height,
int src_x, int src_y, int src_width, int src_height) {
DrawingSurface_DrawImageImpl(target, source->GetBitmapSurface(), dst_x, dst_y, trans, dst_width, dst_height,
- src_x, src_y, src_width, src_height, -1, source->hasAlphaChannel);
+ src_x, src_y, src_width, src_height, -1, source->hasAlphaChannel != 0);
}
void DrawingSurface_DrawSurface(ScriptDrawingSurface *target, ScriptDrawingSurface *source, int trans) {
diff --git a/engines/ags/engine/ac/global_game.cpp b/engines/ags/engine/ac/global_game.cpp
index ddc8ac3a9ca..a6dc4757357 100644
--- a/engines/ags/engine/ac/global_game.cpp
+++ b/engines/ags/engine/ac/global_game.cpp
@@ -780,7 +780,7 @@ int WaitImpl(int skip_type, int nloops) {
if (_GP(game).options[OPT_BASESCRIPTAPI] < kScriptAPI_v360) {
// < 3.6.0 return 1 is skipped by user input, otherwise 0
- return (_GP(play).wait_skipped_by & (SKIP_KEYPRESS | SKIP_MOUSECLICK)) != 0 ? 1 : 0;
+ return ((_GP(play).wait_skipped_by & (SKIP_KEYPRESS | SKIP_MOUSECLICK)) != 0) ? 1 : 0;
}
// >= 3.6.0 return positive keycode, negative mouse button code, or 0 as time-out
return _GP(play).GetWaitSkipResult();
diff --git a/engines/ags/engine/ac/overlay.cpp b/engines/ags/engine/ac/overlay.cpp
index 2811fe99fa1..75538220ebc 100644
--- a/engines/ags/engine/ac/overlay.cpp
+++ b/engines/ags/engine/ac/overlay.cpp
@@ -273,7 +273,7 @@ size_t add_screen_overlay(int x, int y, int type, Bitmap *piccy, bool alphaChann
size_t add_screen_overlay(int x, int y, int type, Shared::Bitmap *piccy, int pic_offx, int pic_offy, bool alphaChannel) {
if (type == OVER_CUSTOM) {
// find an unused custom ID; TODO: find a better approach!
- for (uint id = OVER_CUSTOM + 1; id <= _GP(screenover).size() + OVER_CUSTOM + 1; ++id) {
+ for (int id = OVER_CUSTOM + 1; (size_t)id <= _GP(screenover).size() + OVER_CUSTOM + 1; ++id) {
if (find_overlay_of_type(id) == -1) {
type = id; break;
}
Commit: ddb38177f925cc10966f7419866b1dd6b4c8ce9e
https://github.com/scummvm/scummvm/commit/ddb38177f925cc10966f7419866b1dd6b4c8ce9e
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-03-17T21:49:23-07:00
Commit Message:
AGS: Updated build version (3.6.0.8)
>From upstream 836fb0adbc7cc1a5a78563266f4ef0b53a33f0df
Changed paths:
engines/ags/shared/core/def_version.h
diff --git a/engines/ags/shared/core/def_version.h b/engines/ags/shared/core/def_version.h
index 923151398b1..3f75c72c1d3 100644
--- a/engines/ags/shared/core/def_version.h
+++ b/engines/ags/shared/core/def_version.h
@@ -22,9 +22,9 @@
#ifndef AGS_SHARED_CORE_DEFVERSION_H
#define AGS_SHARED_CORE_DEFVERSION_H
-#define ACI_VERSION_STR "3.6.0.3"
+#define ACI_VERSION_STR "3.6.0.8"
#if defined (RC_INVOKED) // for MSVC resource compiler
-#define ACI_VERSION_MSRC_DEF 3,6,0,3
+#define ACI_VERSION_MSRC_DEF 3,6,0,8
#endif
#define SPECIAL_VERSION ""
Commit: fc9473d175ee0643aa763b8dfdb614829cf40885
https://github.com/scummvm/scummvm/commit/fc9473d175ee0643aa763b8dfdb614829cf40885
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-03-17T21:49:23-07:00
Commit Message:
AGS: Picked a sprite file writing code into SpriteFileWriter class
>From upstream 62a87664fc9249bad2886b44d10affc4d60071c3
Changed paths:
engines/ags/shared/ac/sprite_cache.cpp
engines/ags/shared/ac/sprite_file.cpp
engines/ags/shared/ac/sprite_file.h
diff --git a/engines/ags/shared/ac/sprite_cache.cpp b/engines/ags/shared/ac/sprite_cache.cpp
index 60b6b145f26..a90f311241d 100644
--- a/engines/ags/shared/ac/sprite_cache.cpp
+++ b/engines/ags/shared/ac/sprite_cache.cpp
@@ -105,7 +105,7 @@ void SpriteCache::Init() {
}
void SpriteCache::Reset() {
- _file.Reset();
+ _file.Close();
// TODO: find out if it's safe to simply always delete _spriteData.Image with array element
for (size_t i = 0; i < _spriteData.size(); ++i) {
if (_spriteData[i].Image) {
@@ -434,7 +434,7 @@ int SpriteCache::SaveToFile(const String &filename, bool compressOutput, SpriteF
pre_save_sprite(data.Image);
sprites.push_back(data.Image);
}
- return _file.SaveToFile(filename, sprites, &_file, compressOutput, index);
+ return SaveSpriteFile(filename, sprites, &_file, compressOutput, index);
}
HError SpriteCache::InitFile(const String &filename, const String &sprindex_filename) {
@@ -465,7 +465,7 @@ HError SpriteCache::InitFile(const String &filename, const String &sprindex_file
}
void SpriteCache::DetachFile() {
- _file.Reset();
+ _file.Close();
}
} // namespace Shared
diff --git a/engines/ags/shared/ac/sprite_file.cpp b/engines/ags/shared/ac/sprite_file.cpp
index aef315b54ec..2aa4806b6d5 100644
--- a/engines/ags/shared/ac/sprite_file.cpp
+++ b/engines/ags/shared/ac/sprite_file.cpp
@@ -25,6 +25,7 @@
#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 {
@@ -109,7 +110,7 @@ HError SpriteFile::OpenFile(const String &filename, const String &sprindex_filen
return RebuildSpriteIndex(_stream.get(), topmost, vers, metrics);
}
-void SpriteFile::Reset() {
+void SpriteFile::Close() {
_stream.reset();
_curPos = -2;
}
@@ -312,14 +313,6 @@ HError SpriteFile::LoadSpriteData(sprkey_t index, Size &metric, int &bpp,
return HError::None();
}
-sprkey_t SpriteFile::FindTopmostSprite(const std::vector<Bitmap *> &sprites) {
- sprkey_t topmost = -1;
- for (sprkey_t i = 0; i < static_cast<sprkey_t>(sprites.size()); ++i)
- if (sprites[i])
- topmost = i;
- return topmost;
-}
-
void SpriteFile::SeekToSprite(sprkey_t index) {
// If we didn't just load the previous sprite, seek to it
if (index != _curPos) {
@@ -328,7 +321,16 @@ void SpriteFile::SeekToSprite(sprkey_t index) {
}
}
-int SpriteFile::SaveToFile(const String &save_to_file,
+// Finds the topmost occupied slot index. Warning: may be slow.
+static sprkey_t FindTopmostSprite(const std::vector<Bitmap *> &sprites) {
+ sprkey_t topmost = -1;
+ for (sprkey_t i = 0; i < static_cast<sprkey_t>(sprites.size()); ++i)
+ if (sprites[i])
+ topmost = i;
+ return topmost;
+}
+
+int SaveSpriteFile(const String &save_to_file,
const std::vector<Bitmap *> &sprites,
SpriteFile *read_from_file,
bool compressOutput, SpriteFileIndex &index) {
@@ -336,27 +338,12 @@ int SpriteFile::SaveToFile(const String &save_to_file,
if (output == nullptr)
return -1;
- int spriteFileIDCheck = g_system->getMillis();
-
- // sprite file version
- output->WriteInt16(kSprfVersion_Current);
-
- output->WriteArray(spriteFileSig, strlen(spriteFileSig), 1);
-
- output->WriteInt8(compressOutput ? 1 : 0);
- output->WriteInt32(spriteFileIDCheck);
-
sprkey_t lastslot = read_from_file ? read_from_file->GetTopmostSprite() : 0;
lastslot = std::max(lastslot, FindTopmostSprite(sprites));
- output->WriteInt32(lastslot);
-
- // allocate buffers to store the indexing info
- sprkey_t numsprits = lastslot + 1;
- std::vector<int16_t> spritewidths, spriteheights;
- std::vector<soff_t> spriteoffs;
- spritewidths.resize(numsprits);
- spriteheights.resize(numsprits);
- spriteoffs.resize(numsprits);
+
+ SpriteFileWriter writer(output);
+ writer.Begin(compressOutput, lastslot);
+
std::unique_ptr<Bitmap> temp_bmp; // for disposing temp sprites
std::vector<char> membuf; // for loading raw sprite data
@@ -364,8 +351,6 @@ int SpriteFile::SaveToFile(const String &save_to_file,
read_from_file && read_from_file->IsFileCompressed() != compressOutput;
for (sprkey_t i = 0; i <= lastslot; ++i) {
- soff_t sproff = output->GetPosition();
-
Bitmap *image = (size_t)i < sprites.size() ? sprites[i] : nullptr;
// if compression setting is different, load the sprite into memory
@@ -377,34 +362,11 @@ int SpriteFile::SaveToFile(const String &save_to_file,
// if managed to load an image - save it according the new compression settings
if (image != nullptr) {
- // image in memory -- write it out
- int bpp = image->GetColorDepth() / 8;
- spriteoffs[i] = sproff;
- spritewidths[i] = image->GetWidth();
- spriteheights[i] = image->GetHeight();
- output->WriteInt16(bpp);
- output->WriteInt16(spritewidths[i]);
- output->WriteInt16(spriteheights[i]);
-
- if (compressOutput) {
- soff_t lenloc = output->GetPosition();
- // write some space for the length data
- output->WriteInt32(0);
-
- rle_compress(image, output.get());
-
- soff_t fileSizeSoFar = output->GetPosition();
- // write the length of the compressed data
- output->Seek(lenloc, kSeekBegin);
- output->WriteInt32((fileSizeSoFar - lenloc) - 4);
- output->Seek(0, kSeekEnd);
- } else {
- output->WriteArray(image->GetDataForWriting(), spritewidths[i] * bpp, spriteheights[i]);
- }
+ writer.WriteBitmap(image);
continue;
} else if (diff_compress) {
// sprite doesn't exist
- output->WriteInt16(0); // colour depth
+ writer.WriteEmptySlot();
continue;
}
@@ -413,33 +375,19 @@ int SpriteFile::SaveToFile(const String &save_to_file,
Size metric;
int bpp;
read_from_file->LoadSpriteData(i, metric, bpp, membuf);
-
- output->WriteInt16(bpp);
- if (bpp == 0)
+ if (bpp == 0) {
+ writer.WriteEmptySlot();
continue; // empty slot
-
- spriteoffs[i] = sproff;
- spritewidths[i] = metric.Width;
- spriteheights[i] = metric.Height;
- output->WriteInt16(metric.Width);
- output->WriteInt16(metric.Height);
- if (compressOutput)
- output->WriteInt32(membuf.size());
- if (membuf.size() == 0)
- continue; // bad data?
- output->Write(&membuf[0], membuf.size());
+ }
+ writer.WriteSpriteData(membuf, metric.Width, metric.Height, bpp);
}
+ writer.Finalize();
- index.SpriteFileIDCheck = spriteFileIDCheck;
- index.LastSlot = lastslot;
- index.SpriteCount = numsprits;
- index.Widths = spritewidths;
- index.Heights = spriteheights;
- index.Offsets = spriteoffs;
+ index = writer.GetIndex();
return 0;
}
-int SpriteFile::SaveSpriteIndex(const String &filename, const SpriteFileIndex &index) {
+int SaveSpriteIndex(const String &filename, const SpriteFileIndex &index) {
// write the sprite index file
Stream *out = File::CreateFile(filename);
if (!out)
@@ -451,9 +399,9 @@ int SpriteFile::SaveSpriteIndex(const String &filename, const SpriteFileIndex &i
out->WriteInt32(index.SpriteFileIDCheck);
// write last sprite number and num sprites, to verify that
// it matches the spr file
- out->WriteInt32(index.LastSlot);
- out->WriteInt32(index.SpriteCount);
- if (index.SpriteCount > 0) {
+ out->WriteInt32(index.GetLastSlot());
+ out->WriteInt32(index.GetCount());
+ if (index.GetCount() > 0) {
out->WriteArrayOfInt16(&index.Widths.front(), index.Widths.size());
out->WriteArrayOfInt16(&index.Heights.front(), index.Heights.size());
out->WriteArrayOfInt64(&index.Offsets.front(), index.Offsets.size());
@@ -462,6 +410,83 @@ int SpriteFile::SaveSpriteIndex(const String &filename, const SpriteFileIndex &i
return 0;
}
+SpriteFileWriter::SpriteFileWriter(std::unique_ptr<Stream> &out) : _out(out) {
+}
+
+void SpriteFileWriter::Begin(bool compressed, sprkey_t last_slot) {
+ if (!_out) return;
+ _index.SpriteFileIDCheck = g_system->getMillis();
+ _compress = compressed;
+
+ // 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);
+
+ 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->GetColorDepth() / 8;
+ int w = image->GetWidth();
+ int h = image->GetHeight();
+ if (_compress) {
+ MemoryStream mems(_membuf, kStream_Write);
+ rle_compress(image, &mems);
+ WriteSpriteData(_membuf, w, h, bpp);
+ _membuf.clear();
+ } else {
+ WriteSpriteData((const char *)image->GetData(), w * h * bpp, w, h, bpp);
+ }
+}
+
+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::WriteSpriteData(const char *pbuf, size_t len,
+ int w, int h, int bpp) {
+ if (!_out) return;
+ soff_t sproff = _out->GetPosition();
+ _index.Offsets.push_back(sproff);
+ _index.Widths.push_back(w);
+ _index.Heights.push_back(h);
+ _out->WriteInt16(bpp);
+ _out->WriteInt16(w);
+ _out->WriteInt16(h);
+ // if not compressed, then the data size could be calculated from the
+ // image metrics, therefore no need to write one
+ if (_compress)
+ _out->WriteInt32(len);
+ if (len == 0) return; // bad data?
+ _out->Write(pbuf, len); // write data itself
+}
+
+void SpriteFileWriter::Finalize() {
+ if (!_out || _lastSlotPos < 0) return;
+ _out->Seek(_lastSlotPos, kSeekBegin);
+ _out->WriteInt32(_index.GetLastSlot());
+ _out.reset();
+}
+
} // namespace Shared
} // namespace AGS
} // namespace AGS3
diff --git a/engines/ags/shared/ac/sprite_file.h b/engines/ags/shared/ac/sprite_file.h
index 90cea21d35f..8052aff5f50 100644
--- a/engines/ags/shared/ac/sprite_file.h
+++ b/engines/ags/shared/ac/sprite_file.h
@@ -21,7 +21,10 @@
//=============================================================================
//
-// SpriteFile class handles sprite file loading and streaming.
+// 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.
//
//=============================================================================
@@ -29,7 +32,9 @@
#define AGS_SHARED_AC_SPRITE_FILE_H
#include "ags/shared/core/types.h"
+#include "ags/lib/std/memory.h"
#include "ags/lib/std/vector.h"
+#include "ags/shared/util/stream.h"
#include "ags/globals.h"
namespace AGS3 {
@@ -61,14 +66,16 @@ 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
- sprkey_t LastSlot = -1;
- size_t SpriteCount = 0u;
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; }
+};
+// 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
@@ -79,7 +86,8 @@ public:
// Loads sprite reference information and inits sprite stream
HError OpenFile(const String &filename, const String &sprindex_filename,
std::vector<Size> &metrics);
- void Reset();
+ // Closes stream; no reading will be possible unless opened again
+ void Close();
// Tells if bitmaps in the file are compressed
bool IsFileCompressed() const;
@@ -93,21 +101,12 @@ public:
HError RebuildSpriteIndex(Stream *in, sprkey_t topmost, SpriteFileVersion vers,
std::vector<Size> &metrics);
+ // Loads an image data and creates a ready bitmap
HError LoadSprite(sprkey_t index, Bitmap *&sprite);
+ // Loads an image data into the buffer, reports the bitmap metrics and color depth
HError LoadSpriteData(sprkey_t index, Size &metric, int &bpp, std::vector<char> &data);
- // 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?)
- static int SaveToFile(const String &save_to_file,
- const std::vector<Bitmap *> &sprites, // available sprites (may contain nullptrs)
- SpriteFile *read_from_file, // optional file to read missing sprites from
- bool compressOutput, SpriteFileIndex &index);
- // Saves sprite index table in a separate file
- static int SaveSpriteIndex(const String &filename, const SpriteFileIndex &index);
-
private:
- // Finds the topmost occupied slot index. Warning: may be slow.
- static sprkey_t FindTopmostSprite(const std::vector<Bitmap *> &sprites);
// Seek stream to sprite
void SeekToSprite(sprkey_t index);
@@ -124,6 +123,49 @@ private:
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
+ void Begin(bool 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 additional processing
+ void WriteSpriteData(const char *pbuf, size_t len, int w, int h, int bpp);
+ void WriteSpriteData(const std::vector<char> &buf, int w, int h, int bpp)
+ { WriteSpriteData(&buf[0], buf.size(), w, h, bpp); }
+ // Finalizes current format; no further writing is possible after this
+ void Finalize();
+
+private:
+ std::unique_ptr<Stream> &_out;
+ bool _compress = false;
+ 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<char> _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?)
+int SaveSpriteFile(const String &save_to_file,
+ const std::vector<Bitmap*> &sprites, // available sprites (may contain nullptrs)
+ SpriteFile *read_from_file, // optional file to read missing sprites from
+ bool compressOutput, SpriteFileIndex &index);
+// Saves sprite index table in a separate file
+int SaveSpriteIndex(const String &filename, const SpriteFileIndex &index);
+
} // namespace Shared
} // namespace AGS
} // namespace AGS3
Commit: 6e695f7817c7e1389508d5c7ae44191b29a15582
https://github.com/scummvm/scummvm/commit/6e695f7817c7e1389508d5c7ae44191b29a15582
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-03-17T21:49:24-07:00
Commit Message:
AGS: Only set default character's idle delay for games < 3.6.0
>From upstream 8e6152837a6bc73ff50f6a304ed58b38997b5418
Changed paths:
engines/ags/engine/main/engine.cpp
diff --git a/engines/ags/engine/main/engine.cpp b/engines/ags/engine/main/engine.cpp
index 1dca064cbce..6b83a18a009 100644
--- a/engines/ags/engine/main/engine.cpp
+++ b/engines/ags/engine/main/engine.cpp
@@ -618,8 +618,8 @@ void engine_init_game_settings() {
_GP(game).chars[ee].activeinv = -1;
_GP(game).chars[ee].following = -1;
_GP(game).chars[ee].followinfo = 97 | (10 << 8);
- _GP(game).chars[ee].idletime = 20; // can be overridden later with SetIdle or summink
- _GP(game).chars[ee].idleleft = _GP(game).chars[ee].idletime;
+ if (_G(loaded_game_file_version) < kGameVersion_360)
+ _GP(game).chars[ee].idletime = 20; // default to 20 seconds _GP(game).chars[ee].idleleft = _GP(game).chars[ee].idletime;
_GP(game).chars[ee].transparency = 0;
_GP(game).chars[ee].baseline = -1;
_GP(game).chars[ee].walkwaitcounter = 0;
Commit: 4b68aa31f968733af54f6a043793236672d19adc
https://github.com/scummvm/scummvm/commit/4b68aa31f968733af54f6a043793236672d19adc
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-03-17T21:49:24-07:00
Commit Message:
AGS: Process idleview based on real game speed, not hardcoded 40 fps
>From upstream 2e0d5116bce07a098166d6f6311fc87b27352e47
Changed paths:
engines/ags/engine/ac/character_info_engine.cpp
diff --git a/engines/ags/engine/ac/character_info_engine.cpp b/engines/ags/engine/ac/character_info_engine.cpp
index bdfe1faa891..05a7edc370d 100644
--- a/engines/ags/engine/ac/character_info_engine.cpp
+++ b/engines/ags/engine/ac/character_info_engine.cpp
@@ -26,6 +26,7 @@
#include "ags/engine/ac/character_extras.h"
#include "ags/engine/ac/game_state.h"
#include "ags/engine/ac/global_character.h"
+#include "ags/engine/ac/global_game.h"
#include "ags/engine/ac/math.h"
#include "ags/engine/ac/view_frame.h"
#include "ags/engine/debugging/debug_log.h"
@@ -455,7 +456,7 @@ void CharacterInfo::update_character_idle(CharacterExtras *chex, int &doing_noth
else if ((doing_nothing == 0) || ((flags & CHF_FIXVIEW) != 0))
idleleft = idletime;
// count idle time
- else if ((_G(loopcounter) % 40 == 0) || (chex->process_idle_this_time == 1)) {
+ else if ((_G(loopcounter) % GetGameSpeed() == 0) || (chex->process_idle_this_time == 1)) {
idleleft--;
if (idleleft == -1) {
int useloop = loop;
Commit: 4293a267cb1f2bd1a62c15ceed1c32be863566b8
https://github.com/scummvm/scummvm/commit/4293a267cb1f2bd1a62c15ceed1c32be863566b8
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-03-17T21:49:24-07:00
Commit Message:
AGS: Corrected font outline upgrade condition
>From upstream 070b3ff3d0040f65ef42764033cf0d01742926ff
Changed paths:
engines/ags/engine/game/game_init.cpp
diff --git a/engines/ags/engine/game/game_init.cpp b/engines/ags/engine/game/game_init.cpp
index 55fa74b409a..c47a7c7bdb4 100644
--- a/engines/ags/engine/game/game_init.cpp
+++ b/engines/ags/engine/game/game_init.cpp
@@ -265,8 +265,8 @@ void LoadFonts(GameDataVersion data_ver) {
const bool is_wfn = is_bitmap_font(i);
// Outline thickness corresponds to 1 game pixel by default;
- // but if it's a scaled up bitmap font in a legacy hires game, then it equals to scale
- if ((data_ver < kGameVersion_360) && _GP(game).IsLegacyHiRes()) {
+ // but if it's a scaled up bitmap font, then it equals to scale
+ if (data_ver < kGameVersion_360) {
if (is_wfn && (finfo.Outline == FONT_OUTLINE_AUTO)) {
set_font_outline(i, FONT_OUTLINE_AUTO, FontInfo::kSquared, get_font_scaling_mul(i));
}
Commit: 08560a1dbfbcfd819593e3bea87866cd807a40b3
https://github.com/scummvm/scummvm/commit/08560a1dbfbcfd819593e3bea87866cd807a40b3
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-03-17T21:49:24-07:00
Commit Message:
AGS: Forgot to also clear guibgbmp array in dispose_game_drawdata()
>From upstream 1e4b73ad7b4ae5bf69641f08940d88b0fe828080
Changed paths:
engines/ags/engine/ac/draw.cpp
diff --git a/engines/ags/engine/ac/draw.cpp b/engines/ags/engine/ac/draw.cpp
index 7e446775295..b5fe5223faa 100644
--- a/engines/ags/engine/ac/draw.cpp
+++ b/engines/ags/engine/ac/draw.cpp
@@ -413,6 +413,7 @@ void dispose_game_drawdata() {
_GP(actspswbbmp).clear();
_GP(actspswbcache).clear();
_GP(guibg).clear();
+ _GP(guibgbmp).clear();
}
void dispose_room_drawdata() {
Commit: 74254fdcf4897adce9d1cab5c56b40da8835313b
https://github.com/scummvm/scummvm/commit/74254fdcf4897adce9d1cab5c56b40da8835313b
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-03-17T21:49:25-07:00
Commit Message:
AGS: Simplified couple of set_volume calls
>From upstream 3d4187e64237a7fa6f7f7bf4fb6477369bae1af4
Changed paths:
engines/ags/engine/media/audio/audio.cpp
diff --git a/engines/ags/engine/media/audio/audio.cpp b/engines/ags/engine/media/audio/audio.cpp
index 507ff022ebc..b7664487a05 100644
--- a/engines/ags/engine/media/audio/audio.cpp
+++ b/engines/ags/engine/media/audio/audio.cpp
@@ -265,7 +265,7 @@ static void audio_update_polled_stuff() {
SOUNDCLIP *ch = lock.GetChannel(_GP(play).crossfading_out_channel);
int newVolume = ch ? ch->get_volume() - _GP(play).crossfade_out_volume_per_step : 0;
if (newVolume > 0) {
- AudioChannel_SetVolume(&_G(scrAudioChannel)[_GP(play).crossfading_out_channel], newVolume);
+ ch->set_volume_percent(newVolume);
} else {
stop_and_destroy_channel(_GP(play).crossfading_out_channel);
_GP(play).crossfading_out_channel = 0;
@@ -282,7 +282,7 @@ static void audio_update_polled_stuff() {
newVolume = _GP(play).crossfade_final_volume_in;
}
- AudioChannel_SetVolume(&_G(scrAudioChannel)[_GP(play).crossfading_in_channel], newVolume);
+ ch->set_volume_percent(newVolume);
if (newVolume >= _GP(play).crossfade_final_volume_in) {
_GP(play).crossfading_in_channel = 0;
Commit: 3167bac67bc61a67f17b29f3d872d93878bdf84d
https://github.com/scummvm/scummvm/commit/3167bac67bc61a67f17b29f3d872d93878bdf84d
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-03-17T21:49:25-07:00
Commit Message:
AGS: More clear use of the "max channels" constants
>From upstream 2fd582a2a5fc9d38643277903854a977f8995e07
Changed paths:
engines/ags/engine/ac/audio_clip.cpp
engines/ags/engine/ac/game.cpp
engines/ags/engine/ac/global_audio.cpp
engines/ags/engine/ac/global_video.cpp
engines/ags/engine/ac/room.cpp
engines/ags/engine/ac/system.cpp
engines/ags/engine/game/game_init.cpp
engines/ags/engine/game/savegame.cpp
engines/ags/engine/game/savegame_components.cpp
engines/ags/engine/game/savegame_internal.h
engines/ags/engine/game/savegame_v321.cpp
engines/ags/engine/media/audio/audio.cpp
engines/ags/engine/media/audio/audio_defines.h
engines/ags/globals.cpp
diff --git a/engines/ags/engine/ac/audio_clip.cpp b/engines/ags/engine/ac/audio_clip.cpp
index 7c0dd438728..92d1401dea1 100644
--- a/engines/ags/engine/ac/audio_clip.cpp
+++ b/engines/ags/engine/ac/audio_clip.cpp
@@ -48,7 +48,7 @@ int AudioClip_GetIsAvailable(ScriptAudioClip *clip) {
void AudioClip_Stop(ScriptAudioClip *clip) {
AudioChannelsLock lock;
- for (int i = 0; i < MAX_SOUND_CHANNELS; i++) {
+ for (int i = NUM_SPEECH_CHANS; i < MAX_GAME_CHANNELS; i++) {
auto *ch = lock.GetChannelIfPlaying(i);
if ((ch != nullptr) && (ch->_sourceClip == clip)) {
AudioChannel_Stop(&_G(scrAudioChannel)[i]);
@@ -72,8 +72,9 @@ ScriptAudioChannel *AudioClip_PlayQueued(ScriptAudioClip *clip, int priority, in
}
ScriptAudioChannel *AudioClip_PlayOnChannel(ScriptAudioClip *clip, int chan, int priority, int repeat) {
- if (chan < 1 || chan >= MAX_SOUND_CHANNELS)
- quitprintf("!AudioClip.PlayOnChannel: invalid channel %d, the range is %d - %d", chan, 1, MAX_SOUND_CHANNELS - 1);
+ if (chan < NUM_SPEECH_CHANS || chan >= MAX_GAME_CHANNELS)
+ quitprintf("!AudioClip.PlayOnChannel: invalid channel %d, the range is %d - %d",
+ chan, NUM_SPEECH_CHANS, MAX_GAME_CHANNELS - 1);
if (priority == SCR_NO_VALUE)
priority = clip->defaultPriority;
if (repeat == SCR_NO_VALUE)
diff --git a/engines/ags/engine/ac/game.cpp b/engines/ags/engine/ac/game.cpp
index 0e55ad221d2..984e38176ce 100644
--- a/engines/ags/engine/ac/game.cpp
+++ b/engines/ags/engine/ac/game.cpp
@@ -97,7 +97,7 @@ void Game_StopAudio(int audioType) {
quitprintf("!Game.StopAudio: invalid audio type %d", audioType);
int aa;
- for (aa = 0; aa < MAX_SOUND_CHANNELS; aa++) {
+ for (aa = 0; aa < MAX_GAME_CHANNELS; aa++) {
if (audioType == SCR_NO_VALUE) {
stop_or_fade_out_channel(aa);
} else {
@@ -117,7 +117,7 @@ int Game_IsAudioPlaying(int audioType) {
if (_GP(play).fast_forward)
return 0;
- for (int aa = 0; aa < MAX_SOUND_CHANNELS; aa++) {
+ for (int aa = 0; aa < MAX_GAME_CHANNELS; aa++) {
ScriptAudioClip *clip = AudioChannel_GetPlayingClip(&_G(scrAudioChannel)[aa]);
if (clip != nullptr) {
if ((clip->type == audioType) || (audioType == SCR_NO_VALUE)) {
@@ -147,7 +147,7 @@ void Game_SetAudioTypeVolume(int audioType, int volume, int changeType) {
if ((changeType == VOL_CHANGEEXISTING) ||
(changeType == VOL_BOTH)) {
AudioChannelsLock lock;
- for (int aa = 0; aa < MAX_SOUND_CHANNELS; aa++) {
+ for (int aa = 0; aa < MAX_GAME_CHANNELS; aa++) {
ScriptAudioClip *clip = AudioChannel_GetPlayingClip(&_G(scrAudioChannel)[aa]);
if ((clip != nullptr) && (clip->type == audioType)) {
auto *ch = lock.GetChannel(aa);
@@ -1133,7 +1133,7 @@ void stop_fast_forwarding() {
AudioChannelsLock lock;
// Restore actual volume of sounds
- for (int aa = 0; aa <= MAX_SOUND_CHANNELS; aa++) {
+ for (int aa = 0; aa < TOTAL_AUDIO_CHANNELS; aa++) {
auto *ch = lock.GetChannelIfPlaying(aa);
if (ch) {
ch->set_mute(false);
@@ -1242,7 +1242,7 @@ void display_switch_out_suspend() {
{
// stop the sound stuttering
AudioChannelsLock lock;
- for (int i = 0; i <= MAX_SOUND_CHANNELS; i++) {
+ for (int i = 0; i < TOTAL_AUDIO_CHANNELS; i++) {
auto *ch = lock.GetChannelIfPlaying(i);
if (ch) {
ch->pause();
@@ -1271,7 +1271,7 @@ void display_switch_in_resume() {
{
AudioChannelsLock lock;
- for (int i = 0; i <= MAX_SOUND_CHANNELS; i++) {
+ for (int i = 0; i < TOTAL_AUDIO_CHANNELS; i++) {
auto *ch = lock.GetChannelIfPlaying(i);
if (ch) {
ch->resume();
diff --git a/engines/ags/engine/ac/global_audio.cpp b/engines/ags/engine/ac/global_audio.cpp
index a543cc7b861..2c6763f96f1 100644
--- a/engines/ags/engine/ac/global_audio.cpp
+++ b/engines/ags/engine/ac/global_audio.cpp
@@ -40,8 +40,9 @@ namespace AGS3 {
using namespace AGS::Shared;
void StopAmbientSound(int channel) {
- if ((channel < 0) || (channel >= MAX_SOUND_CHANNELS))
- quit("!StopAmbientSound: invalid channel");
+ if ((channel < NUM_SPEECH_CHANS) || (channel >= MAX_GAME_CHANNELS))
+ quitprintf("!StopAmbientSound: invalid channel %d, supported %d - %d",
+ channel, NUM_SPEECH_CHANS, MAX_GAME_CHANNELS - 1);
if (_GP(ambient)[channel].channel == 0)
return;
@@ -52,7 +53,7 @@ void StopAmbientSound(int channel) {
void PlayAmbientSound(int channel, int sndnum, int vol, int x, int y) {
// the channel parameter is to allow multiple ambient sounds in future
- if ((channel < 1) || (channel == SCHAN_SPEECH) || (channel >= MAX_SOUND_CHANNELS))
+ if ((channel < 1) || (channel == SCHAN_SPEECH) || (channel >= MAX_GAME_CHANNELS))
quit("!PlayAmbientSound: invalid channel number");
if ((vol < 1) || (vol > 255))
quit("!PlayAmbientSound: volume must be 1 to 255");
@@ -95,7 +96,7 @@ int IsChannelPlaying(int chan) {
if (_GP(play).fast_forward)
return 0;
- if ((chan < 0) || (chan >= MAX_SOUND_CHANNELS))
+ if ((chan < 0) || (chan >= MAX_GAME_CHANNELS))
quit("!IsChannelPlaying: invalid sound channel");
if (channel_is_playing(chan))
@@ -110,7 +111,7 @@ int IsSoundPlaying() {
// find if there's a sound playing
AudioChannelsLock lock;
- for (int i = SCHAN_NORMAL; i < MAX_SOUND_CHANNELS; i++) {
+ for (int i = SCHAN_NORMAL; i < MAX_GAME_CHANNELS; i++) {
if (lock.GetChannelIfPlaying(i))
return 1;
}
@@ -128,8 +129,8 @@ int PlaySoundEx(int val1, int channel) {
if (aclip && !is_audiotype_allowed_to_play((AudioFileType)aclip->fileType))
return -1; // if sound is off, ignore it
- if ((channel < SCHAN_NORMAL) || (channel >= MAX_SOUND_CHANNELS))
- quit("!PlaySoundEx: invalid channel specified, must be 3-7");
+ if ((channel < SCHAN_NORMAL) || (channel >= MAX_GAME_CHANNELS))
+ quitprintf("!PlaySoundEx: invalid channel specified, must be %d-%d", SCHAN_NORMAL, MAX_GAME_CHANNELS - 1);
// if an ambient sound is playing on this channel, abort it
StopAmbientSound(channel);
@@ -337,7 +338,7 @@ void SetSoundVolume(int newvol) {
void SetChannelVolume(int chan, int newvol) {
if ((newvol < 0) || (newvol > 255))
quit("!SetChannelVolume: invalid volume - must be from 0-255");
- if ((chan < 0) || (chan >= MAX_SOUND_CHANNELS))
+ if ((chan < 0) || (chan >= MAX_GAME_CHANNELS))
quit("!SetChannelVolume: invalid channel id");
AudioChannelsLock lock;
diff --git a/engines/ags/engine/ac/global_video.cpp b/engines/ags/engine/ac/global_video.cpp
index c5ca0376e37..16a261caaae 100644
--- a/engines/ags/engine/ac/global_video.cpp
+++ b/engines/ags/engine/ac/global_video.cpp
@@ -62,8 +62,8 @@ void scrPlayVideo(const char *name, int skip, int flags) {
void pause_sound_if_necessary_and_play_video(const char *name, int skip, int flags) {
int musplaying = _GP(play).cur_music_number, i;
- int ambientWas[MAX_SOUND_CHANNELS];
- for (i = 1; i < MAX_SOUND_CHANNELS; i++)
+ int ambientWas[MAX_GAME_CHANNELS];
+ for (i = NUM_SPEECH_CHANS; i < MAX_GAME_CHANNELS; i++)
ambientWas[i] = _GP(ambient)[i].channel;
if ((strlen(name) > 3) && (ags_stricmp(&name[strlen(name) - 3], "ogv") == 0)) {
@@ -85,9 +85,10 @@ void pause_sound_if_necessary_and_play_video(const char *name, int skip, int fla
// restart the music
if (musplaying >= 0)
newmusic(musplaying);
- for (i = 1; i < MAX_SOUND_CHANNELS; i++) {
+ for (i = NUM_SPEECH_CHANS; i < MAX_GAME_CHANNELS; i++) {
if (ambientWas[i] > 0)
- PlayAmbientSound(ambientWas[i], _GP(ambient)[i].num, _GP(ambient)[i].vol, _GP(ambient)[i].x, _GP(ambient)[i].y);
+ PlayAmbientSound(ambientWas[i], _GP(ambient)[i].num,
+ _GP(ambient)[i].vol, _GP(ambient)[i].x, _GP(ambient)[i].y);
}
}
}
diff --git a/engines/ags/engine/ac/room.cpp b/engines/ags/engine/ac/room.cpp
index 7e80bc5ad0f..41e808bcbb9 100644
--- a/engines/ags/engine/ac/room.cpp
+++ b/engines/ags/engine/ac/room.cpp
@@ -240,7 +240,7 @@ void unload_old_room() {
_G(objs)[ff].moving = 0;
if (!_GP(play).ambient_sounds_persist) {
- for (ff = 1; ff < MAX_SOUND_CHANNELS; ff++)
+ for (ff = NUM_SPEECH_CHANS; ff < MAX_GAME_CHANNELS; ff++)
StopAmbientSound(ff);
}
diff --git a/engines/ags/engine/ac/system.cpp b/engines/ags/engine/ac/system.cpp
index b5cbf152882..3626f5e726d 100644
--- a/engines/ags/engine/ac/system.cpp
+++ b/engines/ags/engine/ac/system.cpp
@@ -162,11 +162,11 @@ void System_SetGamma(int newValue) {
}
int System_GetAudioChannelCount() {
- return MAX_SOUND_CHANNELS;
+ return MAX_GAME_CHANNELS;
}
ScriptAudioChannel *System_GetAudioChannels(int index) {
- if ((index < 0) || (index >= MAX_SOUND_CHANNELS))
+ if ((index < 0) || (index >= MAX_GAME_CHANNELS))
quit("!System.AudioChannels: invalid sound channel index");
return &_G(scrAudioChannel)[index];
diff --git a/engines/ags/engine/game/game_init.cpp b/engines/ags/engine/game/game_init.cpp
index c47a7c7bdb4..ab5993651b9 100644
--- a/engines/ags/engine/game/game_init.cpp
+++ b/engines/ags/engine/game/game_init.cpp
@@ -89,7 +89,7 @@ String GetGameInitErrorText(GameInitErrorType err) {
// Initializes audio channels and clips and registers them in the script system
void InitAndRegisterAudioObjects() {
- for (int i = 0; i <= MAX_SOUND_CHANNELS; ++i) {
+ for (int i = 0; i < MAX_GAME_CHANNELS; ++i) {
_G(scrAudioChannel)[i].id = i;
ccRegisterManagedObject(&_G(scrAudioChannel)[i], &_GP(ccDynamicAudio));
}
diff --git a/engines/ags/engine/game/savegame.cpp b/engines/ags/engine/game/savegame.cpp
index ccb9f296fe3..5e3acd6b40a 100644
--- a/engines/ags/engine/game/savegame.cpp
+++ b/engines/ags/engine/game/savegame.cpp
@@ -386,7 +386,7 @@ void DoBeforeRestore(PreservedParams &pp) {
ccUnregisterAllObjects();
// NOTE: channels are array of MAX_SOUND_CHANNELS+1 size
- for (int i = 0; i <= MAX_SOUND_CHANNELS; ++i) {
+ for (int i = 0; i < TOTAL_AUDIO_CHANNELS; ++i) {
stop_and_destroy_channel_ex(i, false);
}
@@ -555,7 +555,7 @@ HSaveError DoAfterRestore(const PreservedParams &pp, const RestoredData &r_data)
{
AudioChannelsLock lock;
// NOTE: channels are array of MAX_SOUND_CHANNELS+1 size
- for (int i = 0; i <= MAX_SOUND_CHANNELS; ++i) {
+ for (int i = 0; i < TOTAL_AUDIO_CHANNELS; ++i) {
const RestoredData::ChannelInfo &chan_info = r_data.AudioChans[i];
if (chan_info.ClipID < 0)
continue;
@@ -585,7 +585,7 @@ HSaveError DoAfterRestore(const PreservedParams &pp, const RestoredData &r_data)
// If there were synced audio tracks, the time taken to load in the
// different channels will have thrown them out of sync, so re-time it
// NOTE: channels are array of MAX_SOUND_CHANNELS+1 size
- for (int i = 0; i <= MAX_SOUND_CHANNELS; ++i) {
+ for (int i = 0; i < TOTAL_AUDIO_CHANNELS; ++i) {
auto *ch = lock.GetChannelIfPlaying(i);
int pos = r_data.AudioChans[i].Pos;
if ((pos > 0) && (ch != nullptr)) {
@@ -595,7 +595,7 @@ HSaveError DoAfterRestore(const PreservedParams &pp, const RestoredData &r_data)
} // -- AudioChannelsLock
// TODO: investigate loop range
- for (int i = 1; i < MAX_SOUND_CHANNELS; ++i) {
+ for (int i = NUM_SPEECH_CHANS; i < MAX_GAME_CHANNELS; ++i) {
if (r_data.DoAmbient[i])
PlayAmbientSound(i, r_data.DoAmbient[i], _GP(ambient)[i].vol, _GP(ambient)[i].x, _GP(ambient)[i].y);
}
diff --git a/engines/ags/engine/game/savegame_components.cpp b/engines/ags/engine/game/savegame_components.cpp
index d8c687b0e4b..5c4b1d62dd4 100644
--- a/engines/ags/engine/game/savegame_components.cpp
+++ b/engines/ags/engine/game/savegame_components.cpp
@@ -344,7 +344,7 @@ HSaveError WriteAudio(Stream *out) {
}
// Audio clips and crossfade
- for (int i = 0; i <= MAX_SOUND_CHANNELS; i++) {
+ for (int i = 0; i < TOTAL_AUDIO_CHANNELS; i++) {
auto *ch = lock.GetChannelIfPlaying(i);
if ((ch != nullptr) && (ch->_sourceClip != nullptr)) {
out->WriteInt32(((ScriptAudioClip *)ch->_sourceClip)->id);
@@ -372,7 +372,7 @@ HSaveError WriteAudio(Stream *out) {
out->WriteInt32(_G(current_music_type));
// Ambient sound
- for (int i = 0; i < MAX_SOUND_CHANNELS; ++i)
+ for (int i = 0; i < MAX_GAME_CHANNELS; ++i)
_GP(ambient)[i].WriteToFile(out);
return HSaveError::None();
}
@@ -394,7 +394,7 @@ HSaveError ReadAudio(Stream *in, int32_t cmp_ver, const PreservedParams &pp, Res
}
// Audio clips and crossfade
- for (int i = 0; i <= MAX_SOUND_CHANNELS; ++i) {
+ for (int i = 0; i < TOTAL_AUDIO_CHANNELS; i++) {
RestoredData::ChannelInfo &chan_info = r_data.AudioChans[i];
chan_info.Pos = 0;
chan_info.ClipID = in->ReadInt32();
@@ -425,9 +425,9 @@ HSaveError ReadAudio(Stream *in, int32_t cmp_ver, const PreservedParams &pp, Res
_G(current_music_type) = in->ReadInt32();
// Ambient sound
- for (int i = 0; i < MAX_SOUND_CHANNELS; ++i)
+ for (int i = 0; i < MAX_GAME_CHANNELS; ++i)
_GP(ambient)[i].ReadFromFile(in);
- for (int i = 1; i < MAX_SOUND_CHANNELS; ++i) {
+ for (int i = NUM_SPEECH_CHANS; i < MAX_GAME_CHANNELS; ++i) {
if (_GP(ambient)[i].channel == 0) {
r_data.DoAmbient[i] = 0;
} else {
diff --git a/engines/ags/engine/game/savegame_internal.h b/engines/ags/engine/game/savegame_internal.h
index 50691559a08..0a7fee951bf 100644
--- a/engines/ags/engine/game/savegame_internal.h
+++ b/engines/ags/engine/game/savegame_internal.h
@@ -105,9 +105,9 @@ struct RestoredData {
int YSource = -1;
int MaxDist = 0;
};
- ChannelInfo AudioChans[MAX_SOUND_CHANNELS + 1];
+ ChannelInfo AudioChans[TOTAL_AUDIO_CHANNELS];
// Ambient sounds
- int DoAmbient[MAX_SOUND_CHANNELS];
+ int DoAmbient[MAX_GAME_CHANNELS];
// Viewport and camera data, has to be preserved and applied only after
// room gets loaded, because we must clamp these to room parameters.
struct ViewportData {
diff --git a/engines/ags/engine/game/savegame_v321.cpp b/engines/ags/engine/game/savegame_v321.cpp
index 8e849caef7d..c65102ae6b9 100644
--- a/engines/ags/engine/game/savegame_v321.cpp
+++ b/engines/ags/engine/game/savegame_v321.cpp
@@ -253,11 +253,11 @@ static void restore_game_thisroom(Stream *in, RestoredData &r_data) {
}
static void restore_game_ambientsounds(Stream *in, RestoredData &r_data) {
- for (int i = 0; i < MAX_SOUND_CHANNELS; ++i) {
+ for (int i = 0; i < MAX_GAME_CHANNELS; ++i) {
_GP(ambient)[i].ReadFromFile(in);
}
- for (int bb = 1; bb < MAX_SOUND_CHANNELS; bb++) {
+ for (int bb = NUM_SPEECH_CHANS; bb < MAX_GAME_CHANNELS; bb++) {
if (_GP(ambient)[bb].channel == 0)
r_data.DoAmbient[bb] = 0;
else {
@@ -359,7 +359,7 @@ static HSaveError restore_game_audioclips_and_crossfade(Stream *in, RestoredData
return new SavegameError(kSvgErr_GameContentAssertion, "Mismatching number of Audio Clips.");
}
- for (int i = 0; i <= MAX_SOUND_CHANNELS; ++i) {
+ for (int i = 0; i < TOTAL_AUDIO_CHANNELS; ++i) {
RestoredData::ChannelInfo &chan_info = r_data.AudioChans[i];
chan_info.Pos = 0;
chan_info.ClipID = in->ReadInt32();
diff --git a/engines/ags/engine/media/audio/audio.cpp b/engines/ags/engine/media/audio/audio.cpp
index b7664487a05..c1fc39d25a8 100644
--- a/engines/ags/engine/media/audio/audio.cpp
+++ b/engines/ags/engine/media/audio/audio.cpp
@@ -156,6 +156,7 @@ static void move_track_to_crossfade_channel(int currentChannel, int crossfadeSpe
}
}
+// NOTE: this function assumes one of the user channels
void stop_or_fade_out_channel(int fadeOutChannel, int fadeInChannel, ScriptAudioClip *newSound) {
ScriptAudioClip *sourceClip = AudioChannel_GetPlayingClip(&_G(scrAudioChannel)[fadeOutChannel]);
if ((sourceClip != nullptr) && (_GP(game).audioClipTypes[sourceClip->type].crossfadeSpeed > 0)) {
@@ -176,7 +177,7 @@ static int find_free_audio_channel(ScriptAudioClip *clip, int priority, bool int
priority--;
int startAtChannel = _G(reserved_channel_count);
- int endBeforeChannel = MAX_SOUND_CHANNELS;
+ int endBeforeChannel = MAX_GAME_CHANNELS;
if (_GP(game).audioClipTypes[clip->type].reservedChannels > 0) {
startAtChannel = 0;
@@ -450,7 +451,7 @@ ScriptAudioChannel *play_audio_clip_by_index(int audioClipIndex) {
}
void stop_and_destroy_channel_ex(int chid, bool resetLegacyMusicSettings) {
- if ((chid < 0) || (chid > MAX_SOUND_CHANNELS))
+ if ((chid < 0) || (chid >= TOTAL_AUDIO_CHANNELS))
quit("!StopChannel: invalid channel ID");
AudioChannelsLock lock;
@@ -470,8 +471,10 @@ void stop_and_destroy_channel_ex(int chid, bool resetLegacyMusicSettings) {
// don't update '_G(crossFading)' here as it is updated in all the cross-fading functions.
// destroyed an ambient sound channel
- if (_GP(ambient)[chid].channel > 0)
- _GP(ambient)[chid].channel = 0;
+ if (chid < MAX_GAME_CHANNELS) {
+ if (_GP(ambient)[chid].channel > 0)
+ _GP(ambient)[chid].channel = 0;
+ }
if ((chid == SCHAN_MUSIC) && (resetLegacyMusicSettings)) {
_GP(play).cur_music_number = -1;
@@ -537,7 +540,7 @@ int get_volume_adjusted_for_distance(int volume, int sndX, int sndY, int sndMaxD
void update_directional_sound_vol() {
AudioChannelsLock lock;
- for (int chnum = 1; chnum < MAX_SOUND_CHANNELS; chnum++) {
+ for (int chnum = NUM_SPEECH_CHANS; chnum < MAX_GAME_CHANNELS; chnum++) {
auto *ch = lock.GetChannelIfPlaying(chnum);
if ((ch != nullptr) && (ch->_xSource >= 0)) {
ch->apply_directional_modifier(
@@ -553,8 +556,7 @@ void update_directional_sound_vol() {
void update_ambient_sound_vol() {
AudioChannelsLock lock;
- for (int chan = 1; chan < MAX_SOUND_CHANNELS; chan++) {
-
+ for (int chan = NUM_SPEECH_CHANS; chan < MAX_GAME_CHANNELS; chan++) {
AmbientSound *thisSound = &_GP(ambient)[chan];
if (thisSound->channel == 0)
@@ -614,7 +616,7 @@ void stop_all_sound_and_music() {
// make sure it doesn't start crossfading when it comes back
_G(crossFading) = 0;
// any ambient sound will be aborted
- for (int i = 0; i <= MAX_SOUND_CHANNELS; i++)
+ for (int i = 0; i < TOTAL_AUDIO_CHANNELS; ++i)
stop_and_destroy_channel(i);
}
@@ -633,7 +635,7 @@ static int play_sound_priority(int val1, int priority) {
AudioChannelsLock lock;
// find a free channel to play it on
- for (int i = SCHAN_NORMAL; i < MAX_SOUND_CHANNELS; i++) {
+ for (int i = SCHAN_NORMAL; i < MAX_GAME_CHANNELS; i++) {
auto *ch = lock.GetChannelIfPlaying(i);
if (val1 < 0) {
// Playing sound -1 means iterate through and stop all sound
@@ -773,7 +775,7 @@ int calculate_max_volume() {
void apply_volume_drop_modifier(bool applyModifier) {
AudioChannelsLock lock;
- for (int i = 0; i < MAX_SOUND_CHANNELS; i++) {
+ for (int i = NUM_SPEECH_CHANS; i < MAX_GAME_CHANNELS; i++) {
auto *ch = lock.GetChannelIfPlaying(i);
if (ch && ch->_sourceClip != nullptr) {
if (applyModifier)
diff --git a/engines/ags/engine/media/audio/audio_defines.h b/engines/ags/engine/media/audio/audio_defines.h
index 46263af2d70..7b9786ca8ee 100644
--- a/engines/ags/engine/media/audio/audio_defines.h
+++ b/engines/ags/engine/media/audio/audio_defines.h
@@ -28,8 +28,13 @@
#define MUS_MOD 4
#define MUS_OGG 5
-#define MAX_SOUND_CHANNELS 8
-#define SPECIAL_CROSSFADE_CHANNEL 8
+ // Max channels that are distributed among game's audio types
+#define MAX_GAME_CHANNELS 8
+#define SPECIAL_CROSSFADE_CHANNEL (MAX_GAME_CHANNELS)
+// Total number of channels: game chans + utility chans
+#define TOTAL_AUDIO_CHANNELS (MAX_GAME_CHANNELS + 1)
+// Number of game channels reserved for speech voice-over
+#define NUM_SPEECH_CHANS 1
#define SCHAN_SPEECH 0
#define SCHAN_AMBIENT 1
diff --git a/engines/ags/globals.cpp b/engines/ags/globals.cpp
index 69b5b78ac9a..a8de8218d09 100644
--- a/engines/ags/globals.cpp
+++ b/engines/ags/globals.cpp
@@ -121,10 +121,9 @@ Globals::Globals() {
_AssetMgr = new std::unique_ptr<Shared::AssetManager>();
// audio.cpp globals
- _audioChannels = new std::array<SOUNDCLIP *>(MAX_SOUND_CHANNELS + 1);
- // TODO: double check that ambient sounds array actually needs +1
- _ambient = new std::array<AmbientSound>(MAX_SOUND_CHANNELS + 1);
- _scrAudioChannel = new ScriptAudioChannel[MAX_SOUND_CHANNELS + 1];
+ _audioChannels = new std::array<SOUNDCLIP *>(TOTAL_AUDIO_CHANNELS);
+ _ambient = new std::array<AmbientSound>(MAX_GAME_CHANNELS);
+ _scrAudioChannel = new ScriptAudioChannel[MAX_GAME_CHANNELS];
// button.cpp globals
_animbuts = new AnimatingGUIButton[MAX_ANIMATING_BUTTONS];
Commit: aa91cf116156fa397996f6749f837ef0ef508047
https://github.com/scummvm/scummvm/commit/aa91cf116156fa397996f6749f837ef0ef508047
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-03-17T21:49:25-07:00
Commit Message:
AGS: Increased the max number of audio channels to 16
>From upstream ebddc45c2a2252f5eb927c96b5c6065eebea1ed4
Changed paths:
engines/ags/engine/game/savegame_components.cpp
engines/ags/engine/game/savegame_v321.cpp
engines/ags/engine/media/audio/audio_defines.h
diff --git a/engines/ags/engine/game/savegame_components.cpp b/engines/ags/engine/game/savegame_components.cpp
index 5c4b1d62dd4..a08749b29e7 100644
--- a/engines/ags/engine/game/savegame_components.cpp
+++ b/engines/ags/engine/game/savegame_components.cpp
@@ -336,7 +336,10 @@ HSaveError WriteAudio(Stream *out) {
// Game content assertion
out->WriteInt32(_GP(game).audioClipTypes.size());
- out->WriteInt32(_GP(game).audioClips.size()); // [ivan-mogilko] not necessary, kept only to avoid changing save format
+ out->WriteInt8(TOTAL_AUDIO_CHANNELS);
+ out->WriteInt8(MAX_GAME_CHANNELS);
+ out->WriteInt16(0); // reserved 2 bytes (remains of int32)
+
// Audio types
for (size_t i = 0; i < _GP(game).audioClipTypes.size(); ++i) {
_GP(game).audioClipTypes[i].WriteToSavegame(out);
@@ -382,10 +385,19 @@ HSaveError ReadAudio(Stream *in, int32_t cmp_ver, const PreservedParams &pp, Res
// Game content assertion
if (!AssertGameContent(err, in->ReadInt32(), _GP(game).audioClipTypes.size(), "Audio Clip Types"))
return err;
- in->ReadInt32(); // audio clip count
- /* [ivan-mogilko] looks like it's not necessary to assert, as there's no data serialized for clips
- if (!AssertGameContent(err, in->ReadInt32(), _GP(game).audioClips.size(), "Audio Clips"))
- return err;*/
+ int total_channels, max_game_channels;
+ if (cmp_ver >= 2) {
+ total_channels = in->ReadInt8();
+ max_game_channels = in->ReadInt8();
+ in->ReadInt16(); // reserved 2 bytes
+ if (!AssertCompatLimit(err, total_channels, TOTAL_AUDIO_CHANNELS, "System Audio Channels") ||
+ !AssertCompatLimit(err, max_game_channels, MAX_GAME_CHANNELS, "Game Audio Channels"))
+ return err;
+ } else {
+ total_channels = TOTAL_AUDIO_CHANNELS_v320;
+ max_game_channels = MAX_GAME_CHANNELS_v320;
+ in->ReadInt32(); // unused in prev format ver
+ }
// Audio types
for (size_t i = 0; i < _GP(game).audioClipTypes.size(); ++i) {
@@ -394,7 +406,7 @@ HSaveError ReadAudio(Stream *in, int32_t cmp_ver, const PreservedParams &pp, Res
}
// Audio clips and crossfade
- for (int i = 0; i < TOTAL_AUDIO_CHANNELS; i++) {
+ for (int i = 0; i < total_channels; ++i) {
RestoredData::ChannelInfo &chan_info = r_data.AudioChans[i];
chan_info.Pos = 0;
chan_info.ClipID = in->ReadInt32();
@@ -425,9 +437,9 @@ HSaveError ReadAudio(Stream *in, int32_t cmp_ver, const PreservedParams &pp, Res
_G(current_music_type) = in->ReadInt32();
// Ambient sound
- for (int i = 0; i < MAX_GAME_CHANNELS; ++i)
+ for (int i = 0; i < max_game_channels; ++i)
_GP(ambient)[i].ReadFromFile(in);
- for (int i = NUM_SPEECH_CHANS; i < MAX_GAME_CHANNELS; ++i) {
+ for (int i = NUM_SPEECH_CHANS; i < max_game_channels; ++i) {
if (_GP(ambient)[i].channel == 0) {
r_data.DoAmbient[i] = 0;
} else {
@@ -1014,7 +1026,7 @@ ComponentHandler ComponentHandlers[] = {
},
{
"Audio",
- 1,
+ 2,
0,
WriteAudio,
ReadAudio
diff --git a/engines/ags/engine/game/savegame_v321.cpp b/engines/ags/engine/game/savegame_v321.cpp
index c65102ae6b9..61fd8d37276 100644
--- a/engines/ags/engine/game/savegame_v321.cpp
+++ b/engines/ags/engine/game/savegame_v321.cpp
@@ -253,11 +253,11 @@ static void restore_game_thisroom(Stream *in, RestoredData &r_data) {
}
static void restore_game_ambientsounds(Stream *in, RestoredData &r_data) {
- for (int i = 0; i < MAX_GAME_CHANNELS; ++i) {
+ for (int i = 0; i < MAX_GAME_CHANNELS_v320; ++i) {
_GP(ambient)[i].ReadFromFile(in);
}
- for (int bb = NUM_SPEECH_CHANS; bb < MAX_GAME_CHANNELS; bb++) {
+ for (int bb = NUM_SPEECH_CHANS; bb < MAX_GAME_CHANNELS_v320; bb++) {
if (_GP(ambient)[bb].channel == 0)
r_data.DoAmbient[bb] = 0;
else {
@@ -359,7 +359,7 @@ static HSaveError restore_game_audioclips_and_crossfade(Stream *in, RestoredData
return new SavegameError(kSvgErr_GameContentAssertion, "Mismatching number of Audio Clips.");
}
- for (int i = 0; i < TOTAL_AUDIO_CHANNELS; ++i) {
+ for (int i = 0; i < TOTAL_AUDIO_CHANNELS_v320; ++i) {
RestoredData::ChannelInfo &chan_info = r_data.AudioChans[i];
chan_info.Pos = 0;
chan_info.ClipID = in->ReadInt32();
diff --git a/engines/ags/engine/media/audio/audio_defines.h b/engines/ags/engine/media/audio/audio_defines.h
index 7b9786ca8ee..8d9e428108b 100644
--- a/engines/ags/engine/media/audio/audio_defines.h
+++ b/engines/ags/engine/media/audio/audio_defines.h
@@ -29,12 +29,15 @@
#define MUS_OGG 5
// Max channels that are distributed among game's audio types
-#define MAX_GAME_CHANNELS 8
+#define MAX_GAME_CHANNELS 16
#define SPECIAL_CROSSFADE_CHANNEL (MAX_GAME_CHANNELS)
// Total number of channels: game chans + utility chans
#define TOTAL_AUDIO_CHANNELS (MAX_GAME_CHANNELS + 1)
// Number of game channels reserved for speech voice-over
#define NUM_SPEECH_CHANS 1
+// Legacy channel numbers
+#define MAX_GAME_CHANNELS_v320 8
+#define TOTAL_AUDIO_CHANNELS_v320 (MAX_GAME_CHANNELS_v320 + 1)
#define SCHAN_SPEECH 0
#define SCHAN_AMBIENT 1
Commit: ee311d660fb7dd3b95d895d0f1c68bbb989ea7a8
https://github.com/scummvm/scummvm/commit/ee311d660fb7dd3b95d895d0f1c68bbb989ea7a8
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-03-17T21:49:25-07:00
Commit Message:
AGS: Make game audiochannels limit defined by a game version
>From upstream 65f0b843dfd85967135eca1bda46976aa1ddcf26
Changed paths:
engines/ags/engine/ac/audio_clip.cpp
engines/ags/engine/ac/game.cpp
engines/ags/engine/ac/global_audio.cpp
engines/ags/engine/ac/global_video.cpp
engines/ags/engine/ac/room.cpp
engines/ags/engine/ac/system.cpp
engines/ags/engine/game/game_init.cpp
engines/ags/engine/game/savegame.cpp
engines/ags/engine/game/savegame_components.cpp
engines/ags/engine/media/audio/audio.cpp
engines/ags/shared/ac/game_setup_struct.h
diff --git a/engines/ags/engine/ac/audio_clip.cpp b/engines/ags/engine/ac/audio_clip.cpp
index 92d1401dea1..b7ef9b78a4a 100644
--- a/engines/ags/engine/ac/audio_clip.cpp
+++ b/engines/ags/engine/ac/audio_clip.cpp
@@ -48,7 +48,7 @@ int AudioClip_GetIsAvailable(ScriptAudioClip *clip) {
void AudioClip_Stop(ScriptAudioClip *clip) {
AudioChannelsLock lock;
- for (int i = NUM_SPEECH_CHANS; i < MAX_GAME_CHANNELS; i++) {
+ for (int i = NUM_SPEECH_CHANS; i < _GP(game).numGameChannels; i++) {
auto *ch = lock.GetChannelIfPlaying(i);
if ((ch != nullptr) && (ch->_sourceClip == clip)) {
AudioChannel_Stop(&_G(scrAudioChannel)[i]);
@@ -72,9 +72,9 @@ ScriptAudioChannel *AudioClip_PlayQueued(ScriptAudioClip *clip, int priority, in
}
ScriptAudioChannel *AudioClip_PlayOnChannel(ScriptAudioClip *clip, int chan, int priority, int repeat) {
- if (chan < NUM_SPEECH_CHANS || chan >= MAX_GAME_CHANNELS)
+ if (chan < NUM_SPEECH_CHANS || chan >= _GP(game).numGameChannels)
quitprintf("!AudioClip.PlayOnChannel: invalid channel %d, the range is %d - %d",
- chan, NUM_SPEECH_CHANS, MAX_GAME_CHANNELS - 1);
+ chan, NUM_SPEECH_CHANS, _GP(game).numGameChannels - 1);
if (priority == SCR_NO_VALUE)
priority = clip->defaultPriority;
if (repeat == SCR_NO_VALUE)
diff --git a/engines/ags/engine/ac/game.cpp b/engines/ags/engine/ac/game.cpp
index 984e38176ce..37dd33d9be4 100644
--- a/engines/ags/engine/ac/game.cpp
+++ b/engines/ags/engine/ac/game.cpp
@@ -95,9 +95,8 @@ using namespace AGS::Engine;
void Game_StopAudio(int audioType) {
if (((audioType < 0) || ((size_t)audioType >= _GP(game).audioClipTypes.size())) && (audioType != SCR_NO_VALUE))
quitprintf("!Game.StopAudio: invalid audio type %d", audioType);
- int aa;
- for (aa = 0; aa < MAX_GAME_CHANNELS; aa++) {
+ for (int aa = 0; aa < _GP(game).numGameChannels; aa++) {
if (audioType == SCR_NO_VALUE) {
stop_or_fade_out_channel(aa);
} else {
@@ -117,7 +116,7 @@ int Game_IsAudioPlaying(int audioType) {
if (_GP(play).fast_forward)
return 0;
- for (int aa = 0; aa < MAX_GAME_CHANNELS; aa++) {
+ for (int aa = 0; aa < _GP(game).numGameChannels; aa++) {
ScriptAudioClip *clip = AudioChannel_GetPlayingClip(&_G(scrAudioChannel)[aa]);
if (clip != nullptr) {
if ((clip->type == audioType) || (audioType == SCR_NO_VALUE)) {
@@ -147,7 +146,7 @@ void Game_SetAudioTypeVolume(int audioType, int volume, int changeType) {
if ((changeType == VOL_CHANGEEXISTING) ||
(changeType == VOL_BOTH)) {
AudioChannelsLock lock;
- for (int aa = 0; aa < MAX_GAME_CHANNELS; aa++) {
+ for (int aa = 0; aa < _GP(game).numGameChannels; aa++) {
ScriptAudioClip *clip = AudioChannel_GetPlayingClip(&_G(scrAudioChannel)[aa]);
if ((clip != nullptr) && (clip->type == audioType)) {
auto *ch = lock.GetChannel(aa);
diff --git a/engines/ags/engine/ac/global_audio.cpp b/engines/ags/engine/ac/global_audio.cpp
index 2c6763f96f1..d9fcbabb1f6 100644
--- a/engines/ags/engine/ac/global_audio.cpp
+++ b/engines/ags/engine/ac/global_audio.cpp
@@ -40,9 +40,9 @@ namespace AGS3 {
using namespace AGS::Shared;
void StopAmbientSound(int channel) {
- if ((channel < NUM_SPEECH_CHANS) || (channel >= MAX_GAME_CHANNELS))
+ if ((channel < NUM_SPEECH_CHANS) || (channel >= _GP(game).numGameChannels))
quitprintf("!StopAmbientSound: invalid channel %d, supported %d - %d",
- channel, NUM_SPEECH_CHANS, MAX_GAME_CHANNELS - 1);
+ channel, NUM_SPEECH_CHANS, _GP(game).numGameChannels - 1);
if (_GP(ambient)[channel].channel == 0)
return;
@@ -53,7 +53,7 @@ void StopAmbientSound(int channel) {
void PlayAmbientSound(int channel, int sndnum, int vol, int x, int y) {
// the channel parameter is to allow multiple ambient sounds in future
- if ((channel < 1) || (channel == SCHAN_SPEECH) || (channel >= MAX_GAME_CHANNELS))
+ if ((channel < 1) || (channel == SCHAN_SPEECH) || (channel >= _GP(game).numGameChannels))
quit("!PlayAmbientSound: invalid channel number");
if ((vol < 1) || (vol > 255))
quit("!PlayAmbientSound: volume must be 1 to 255");
@@ -96,7 +96,7 @@ int IsChannelPlaying(int chan) {
if (_GP(play).fast_forward)
return 0;
- if ((chan < 0) || (chan >= MAX_GAME_CHANNELS))
+ if ((chan < 0) || (chan >= _GP(game).numGameChannels))
quit("!IsChannelPlaying: invalid sound channel");
if (channel_is_playing(chan))
@@ -111,7 +111,7 @@ int IsSoundPlaying() {
// find if there's a sound playing
AudioChannelsLock lock;
- for (int i = SCHAN_NORMAL; i < MAX_GAME_CHANNELS; i++) {
+ for (int i = SCHAN_NORMAL; i < _GP(game).numGameChannels; i++) {
if (lock.GetChannelIfPlaying(i))
return 1;
}
@@ -129,8 +129,8 @@ int PlaySoundEx(int val1, int channel) {
if (aclip && !is_audiotype_allowed_to_play((AudioFileType)aclip->fileType))
return -1; // if sound is off, ignore it
- if ((channel < SCHAN_NORMAL) || (channel >= MAX_GAME_CHANNELS))
- quitprintf("!PlaySoundEx: invalid channel specified, must be %d-%d", SCHAN_NORMAL, MAX_GAME_CHANNELS - 1);
+ if ((channel < SCHAN_NORMAL) || (channel >= _GP(game).numGameChannels))
+ quitprintf("!PlaySoundEx: invalid channel specified, must be %d-%d", SCHAN_NORMAL, _GP(game).numGameChannels - 1);
// if an ambient sound is playing on this channel, abort it
StopAmbientSound(channel);
@@ -338,7 +338,7 @@ void SetSoundVolume(int newvol) {
void SetChannelVolume(int chan, int newvol) {
if ((newvol < 0) || (newvol > 255))
quit("!SetChannelVolume: invalid volume - must be from 0-255");
- if ((chan < 0) || (chan >= MAX_GAME_CHANNELS))
+ if ((chan < 0) || (chan >= _GP(game).numGameChannels))
quit("!SetChannelVolume: invalid channel id");
AudioChannelsLock lock;
diff --git a/engines/ags/engine/ac/global_video.cpp b/engines/ags/engine/ac/global_video.cpp
index 16a261caaae..a31d89ee39b 100644
--- a/engines/ags/engine/ac/global_video.cpp
+++ b/engines/ags/engine/ac/global_video.cpp
@@ -26,6 +26,7 @@
#include "ags/engine/ac/global_game.h"
#include "ags/engine/ac/global_video.h"
#include "ags/engine/ac/path_helper.h"
+#include "ags/shared/ac/game_setup_struct.h"
#include "ags/shared/core/asset_manager.h"
#include "ags/engine/debugging/debugger.h"
#include "ags/engine/debugging/debug_log.h"
@@ -62,8 +63,8 @@ void scrPlayVideo(const char *name, int skip, int flags) {
void pause_sound_if_necessary_and_play_video(const char *name, int skip, int flags) {
int musplaying = _GP(play).cur_music_number, i;
- int ambientWas[MAX_GAME_CHANNELS];
- for (i = NUM_SPEECH_CHANS; i < MAX_GAME_CHANNELS; i++)
+ int ambientWas[MAX_GAME_CHANNELS]{ 0 };
+ for (i = NUM_SPEECH_CHANS; i < _GP(game).numGameChannels; i++)
ambientWas[i] = _GP(ambient)[i].channel;
if ((strlen(name) > 3) && (ags_stricmp(&name[strlen(name) - 3], "ogv") == 0)) {
@@ -85,7 +86,7 @@ void pause_sound_if_necessary_and_play_video(const char *name, int skip, int fla
// restart the music
if (musplaying >= 0)
newmusic(musplaying);
- for (i = NUM_SPEECH_CHANS; i < MAX_GAME_CHANNELS; i++) {
+ for (i = NUM_SPEECH_CHANS; i < _GP(game).numGameChannels; i++) {
if (ambientWas[i] > 0)
PlayAmbientSound(ambientWas[i], _GP(ambient)[i].num,
_GP(ambient)[i].vol, _GP(ambient)[i].x, _GP(ambient)[i].y);
diff --git a/engines/ags/engine/ac/room.cpp b/engines/ags/engine/ac/room.cpp
index 41e808bcbb9..a29c520c9a0 100644
--- a/engines/ags/engine/ac/room.cpp
+++ b/engines/ags/engine/ac/room.cpp
@@ -240,7 +240,7 @@ void unload_old_room() {
_G(objs)[ff].moving = 0;
if (!_GP(play).ambient_sounds_persist) {
- for (ff = NUM_SPEECH_CHANS; ff < MAX_GAME_CHANNELS; ff++)
+ for (ff = NUM_SPEECH_CHANS; ff < _GP(game).numGameChannels; ff++)
StopAmbientSound(ff);
}
diff --git a/engines/ags/engine/ac/system.cpp b/engines/ags/engine/ac/system.cpp
index 3626f5e726d..7c51648478f 100644
--- a/engines/ags/engine/ac/system.cpp
+++ b/engines/ags/engine/ac/system.cpp
@@ -162,12 +162,13 @@ void System_SetGamma(int newValue) {
}
int System_GetAudioChannelCount() {
- return MAX_GAME_CHANNELS;
+ return _GP(game).numGameChannels;
}
ScriptAudioChannel *System_GetAudioChannels(int index) {
- if ((index < 0) || (index >= MAX_GAME_CHANNELS))
- quit("!System.AudioChannels: invalid sound channel index");
+ if ((index < 0) || (index >= _GP(game).numGameChannels))
+ quitprintf("!System.AudioChannels: invalid sound channel index %d, supported %d - %d",
+ 0, _GP(game).numGameChannels);
return &_G(scrAudioChannel)[index];
}
diff --git a/engines/ags/engine/game/game_init.cpp b/engines/ags/engine/game/game_init.cpp
index ab5993651b9..6cf7a960e6f 100644
--- a/engines/ags/engine/game/game_init.cpp
+++ b/engines/ags/engine/game/game_init.cpp
@@ -89,7 +89,7 @@ String GetGameInitErrorText(GameInitErrorType err) {
// Initializes audio channels and clips and registers them in the script system
void InitAndRegisterAudioObjects() {
- for (int i = 0; i < MAX_GAME_CHANNELS; ++i) {
+ for (int i = 0; i < _GP(game).numGameChannels; ++i) {
_G(scrAudioChannel)[i].id = i;
ccRegisterManagedObject(&_G(scrAudioChannel)[i], &_GP(ccDynamicAudio));
}
@@ -378,6 +378,13 @@ HGameInitError InitGameState(const LoadedGameEntities &ents, GameDataVersion dat
_GP(play).charProps.resize(_GP(game).numcharacters);
_G(old_dialog_scripts) = ents.OldDialogScripts;
_G(old_speech_lines) = ents.OldSpeechLines;
+
+ // Set number of game channels corresponding to the loaded game version
+ if (_G(loaded_game_file_version) < kGameVersion_360)
+ _GP(game).numGameChannels = MAX_GAME_CHANNELS_v320;
+ else
+ _GP(game).numGameChannels = MAX_GAME_CHANNELS;
+
HError err = InitAndRegisterGameEntities();
if (!err)
return new GameInitError(kGameInitErr_EntityInitFail, err);
diff --git a/engines/ags/engine/game/savegame.cpp b/engines/ags/engine/game/savegame.cpp
index 5e3acd6b40a..76b5f3121d6 100644
--- a/engines/ags/engine/game/savegame.cpp
+++ b/engines/ags/engine/game/savegame.cpp
@@ -594,8 +594,7 @@ HSaveError DoAfterRestore(const PreservedParams &pp, const RestoredData &r_data)
}
} // -- AudioChannelsLock
- // TODO: investigate loop range
- for (int i = NUM_SPEECH_CHANS; i < MAX_GAME_CHANNELS; ++i) {
+ for (int i = NUM_SPEECH_CHANS; i < _GP(game).numGameChannels; ++i) {
if (r_data.DoAmbient[i])
PlayAmbientSound(i, r_data.DoAmbient[i], _GP(ambient)[i].vol, _GP(ambient)[i].x, _GP(ambient)[i].y);
}
diff --git a/engines/ags/engine/game/savegame_components.cpp b/engines/ags/engine/game/savegame_components.cpp
index a08749b29e7..94bf11bd1ef 100644
--- a/engines/ags/engine/game/savegame_components.cpp
+++ b/engines/ags/engine/game/savegame_components.cpp
@@ -337,7 +337,7 @@ HSaveError WriteAudio(Stream *out) {
// Game content assertion
out->WriteInt32(_GP(game).audioClipTypes.size());
out->WriteInt8(TOTAL_AUDIO_CHANNELS);
- out->WriteInt8(MAX_GAME_CHANNELS);
+ out->WriteInt8(_GP(game).numGameChannels);
out->WriteInt16(0); // reserved 2 bytes (remains of int32)
// Audio types
@@ -375,7 +375,7 @@ HSaveError WriteAudio(Stream *out) {
out->WriteInt32(_G(current_music_type));
// Ambient sound
- for (int i = 0; i < MAX_GAME_CHANNELS; ++i)
+ for (int i = 0; i < _GP(game).numGameChannels; ++i)
_GP(ambient)[i].WriteToFile(out);
return HSaveError::None();
}
diff --git a/engines/ags/engine/media/audio/audio.cpp b/engines/ags/engine/media/audio/audio.cpp
index c1fc39d25a8..435cec56042 100644
--- a/engines/ags/engine/media/audio/audio.cpp
+++ b/engines/ags/engine/media/audio/audio.cpp
@@ -177,7 +177,7 @@ static int find_free_audio_channel(ScriptAudioClip *clip, int priority, bool int
priority--;
int startAtChannel = _G(reserved_channel_count);
- int endBeforeChannel = MAX_GAME_CHANNELS;
+ int endBeforeChannel = _GP(game).numGameChannels;
if (_GP(game).audioClipTypes[clip->type].reservedChannels > 0) {
startAtChannel = 0;
@@ -471,7 +471,7 @@ void stop_and_destroy_channel_ex(int chid, bool resetLegacyMusicSettings) {
// don't update '_G(crossFading)' here as it is updated in all the cross-fading functions.
// destroyed an ambient sound channel
- if (chid < MAX_GAME_CHANNELS) {
+ if (chid < _GP(game).numGameChannels) {
if (_GP(ambient)[chid].channel > 0)
_GP(ambient)[chid].channel = 0;
}
@@ -540,7 +540,7 @@ int get_volume_adjusted_for_distance(int volume, int sndX, int sndY, int sndMaxD
void update_directional_sound_vol() {
AudioChannelsLock lock;
- for (int chnum = NUM_SPEECH_CHANS; chnum < MAX_GAME_CHANNELS; chnum++) {
+ for (int chnum = NUM_SPEECH_CHANS; chnum < _GP(game).numGameChannels; chnum++) {
auto *ch = lock.GetChannelIfPlaying(chnum);
if ((ch != nullptr) && (ch->_xSource >= 0)) {
ch->apply_directional_modifier(
@@ -556,7 +556,7 @@ void update_directional_sound_vol() {
void update_ambient_sound_vol() {
AudioChannelsLock lock;
- for (int chan = NUM_SPEECH_CHANS; chan < MAX_GAME_CHANNELS; chan++) {
+ for (int chan = NUM_SPEECH_CHANS; chan < _GP(game).numGameChannels; chan++) {
AmbientSound *thisSound = &_GP(ambient)[chan];
if (thisSound->channel == 0)
@@ -635,7 +635,7 @@ static int play_sound_priority(int val1, int priority) {
AudioChannelsLock lock;
// find a free channel to play it on
- for (int i = SCHAN_NORMAL; i < MAX_GAME_CHANNELS; i++) {
+ for (int i = SCHAN_NORMAL; i < _GP(game).numGameChannels; i++) {
auto *ch = lock.GetChannelIfPlaying(i);
if (val1 < 0) {
// Playing sound -1 means iterate through and stop all sound
@@ -775,7 +775,7 @@ int calculate_max_volume() {
void apply_volume_drop_modifier(bool applyModifier) {
AudioChannelsLock lock;
- for (int i = NUM_SPEECH_CHANS; i < MAX_GAME_CHANNELS; i++) {
+ for (int i = NUM_SPEECH_CHANS; i < _GP(game).numGameChannels; i++) {
auto *ch = lock.GetChannelIfPlaying(i);
if (ch && ch->_sourceClip != nullptr) {
if (applyModifier)
diff --git a/engines/ags/shared/ac/game_setup_struct.h b/engines/ags/shared/ac/game_setup_struct.h
index ecea16eba6a..3f1a259f790 100644
--- a/engines/ags/shared/ac/game_setup_struct.h
+++ b/engines/ags/shared/ac/game_setup_struct.h
@@ -88,6 +88,8 @@ struct GameSetupStruct : public GameSetupStructBase {
// 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 allowed game audio channels (the ones under direct user control)
+ int numGameChannels = 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
Commit: bb1d0849fa2d92e67e011f644290d66c89b072f9
https://github.com/scummvm/scummvm/commit/bb1d0849fa2d92e67e011f644290d66c89b072f9
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-03-17T21:49:26-07:00
Commit Message:
AGS: Read and write RoomObject's fields explicitly
>From upstream bb9ae639f9e28b59af4bbc9b613cd9455ea0db3e
Changed paths:
engines/ags/engine/ac/room_object.cpp
diff --git a/engines/ags/engine/ac/room_object.cpp b/engines/ags/engine/ac/room_object.cpp
index 2eeee9d4691..4153950ec4a 100644
--- a/engines/ags/engine/ac/room_object.cpp
+++ b/engines/ags/engine/ac/room_object.cpp
@@ -157,27 +157,54 @@ void RoomObject::ReadFromFile(Stream *in) {
x = in->ReadInt32();
y = in->ReadInt32();
transparent = in->ReadInt32();
-
- in->ReadArrayOfInt16(&tint_r, 15);
- cycling = in->ReadByte();
- overall_speed = in->ReadByte();
- on = in->ReadByte();
- flags = in->ReadByte();
- in->ReadArrayOfInt16(&blocking_width, 2);
+ tint_r = in->ReadInt16();
+ tint_g = in->ReadInt16();
+ tint_b = in->ReadInt16();
+ tint_level = in->ReadInt16();
+ tint_light = in->ReadInt16();
+ zoom = in->ReadInt16();
+ last_width = in->ReadInt16();
+ last_height = in->ReadInt16();
+ num = in->ReadInt16();
+ baseline = in->ReadInt16();
+ view = in->ReadInt16();
+ loop = in->ReadInt16();
+ frame = in->ReadInt16();
+ wait = in->ReadInt16();
+ moving = in->ReadInt16();
+ cycling = in->ReadInt8();
+ overall_speed = in->ReadInt8();
+ on = in->ReadInt8();
+ flags = in->ReadInt8();
+ blocking_width = in->ReadInt16();
+ blocking_height = in->ReadInt16();
}
void RoomObject::WriteToFile(Stream *out) const {
out->WriteInt32(x);
out->WriteInt32(y);
out->WriteInt32(transparent);
-
- // TODO: Split up array write to properly write fields separately
- out->WriteArrayOfInt16(&tint_r, 15);
- out->WriteByte(cycling);
- out->WriteByte(overall_speed);
- out->WriteByte(on);
- out->WriteByte(flags);
- out->WriteArrayOfInt16(&blocking_width, 2);
+ out->WriteInt16(tint_r);
+ out->WriteInt16(tint_g);
+ out->WriteInt16(tint_b);
+ out->WriteInt16(tint_level);
+ out->WriteInt16(tint_light);
+ out->WriteInt16(zoom);
+ out->WriteInt16(last_width);
+ out->WriteInt16(last_height);
+ out->WriteInt16(num);
+ out->WriteInt16(baseline);
+ out->WriteInt16(view);
+ out->WriteInt16(loop);
+ out->WriteInt16(frame);
+ out->WriteInt16(wait);
+ out->WriteInt16(moving);
+ out->WriteInt8(cycling);
+ out->WriteInt8(overall_speed);
+ out->WriteInt8(on);
+ out->WriteInt8(flags);
+ out->WriteInt16(blocking_width);
+ out->WriteInt16(blocking_height);
}
} // namespace AGS3
Commit: 78b9068a91febb7980bf08dd157b9bce28a15188
https://github.com/scummvm/scummvm/commit/78b9068a91febb7980bf08dd157b9bce28a15188
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-03-17T21:49:26-07:00
Commit Message:
AGS: Replace "localuserconf" option with "user-conf-dir"
>From upstream 9edb428aa028164081cd9a4f9050cf5fa0767964
Changed paths:
engines/ags/engine/ac/file.cpp
engines/ags/engine/ac/game_setup.cpp
engines/ags/engine/ac/game_setup.h
engines/ags/engine/main/engine.cpp
engines/ags/engine/main/main.cpp
diff --git a/engines/ags/engine/ac/file.cpp b/engines/ags/engine/ac/file.cpp
index bbd0d0c1f3c..03694cf23d1 100644
--- a/engines/ags/engine/ac/file.cpp
+++ b/engines/ags/engine/ac/file.cpp
@@ -242,10 +242,10 @@ FSLocation GetGlobalUserConfigDir() {
FSLocation GetGameUserConfigDir() {
String dir = _G(platform)->GetUserConfigDirectory();
- if (Path::IsRelativePath(dir)) // relative dir is resolved relative to the game data dir
+ if (!_GP(usetup).user_conf_dir.IsEmpty()) // directive to use custom userconf location
+ return FSLocation(_GP(usetup).user_conf_dir);
+ else if (Path::IsRelativePath(dir)) // relative dir is resolved relative to the game data dir
return FSLocation(_GP(ResPaths).DataDir, dir);
- else if (_GP(usetup).local_user_conf) // directive to use game dir location
- return FSLocation(_GP(ResPaths).DataDir);
// For absolute dir, we assume it's a special directory prepared for AGS engine
// and therefore amend it with a game own subdir
return FSLocation(dir, _GP(game).saveGameFolderName);
diff --git a/engines/ags/engine/ac/game_setup.cpp b/engines/ags/engine/ac/game_setup.cpp
index 4f86308e661..15106172c69 100644
--- a/engines/ags/engine/ac/game_setup.cpp
+++ b/engines/ags/engine/ac/game_setup.cpp
@@ -24,7 +24,6 @@
namespace AGS3 {
GameSetup::GameSetup() {
- local_user_conf = false;
audio_backend = 1;
no_speech_pack = false;
textheight = 0;
diff --git a/engines/ags/engine/ac/game_setup.h b/engines/ags/engine/ac/game_setup.h
index c3807d17799..16933b6efc9 100644
--- a/engines/ags/engine/ac/game_setup.h
+++ b/engines/ags/engine/ac/game_setup.h
@@ -67,8 +67,8 @@ struct GameSetup {
String opt_audio_dir; // optional custom install audio dir path
String opt_voice_dir; // optional custom install voice-over dir path
//
- String conf_path; // explicitly set path to config
- bool local_user_conf; // search for user config in the game directory
+ String conf_path; // a read-only config path (if set the regular config is ignored)
+ String user_conf_dir; // directory to read and write user config in
String user_data_dir; // directory to write savedgames and user files to
String shared_data_dir; // directory to write shared game files to
String translation;
diff --git a/engines/ags/engine/main/engine.cpp b/engines/ags/engine/main/engine.cpp
index 6b83a18a009..ad66cee686e 100644
--- a/engines/ags/engine/main/engine.cpp
+++ b/engines/ags/engine/main/engine.cpp
@@ -429,6 +429,8 @@ int engine_check_register_game() {
// Setup paths and directories that may be affected by user configuration
void engine_init_user_directories() {
+ if (!_GP(usetup).user_conf_dir.IsEmpty())
+ Debug::Printf(kDbgMsg_Info, "User config directory: %s", _GP(usetup).user_conf_dir.GetCStr());
if (!_GP(usetup).user_data_dir.IsEmpty())
Debug::Printf(kDbgMsg_Info, "User data directory: %s", _GP(usetup).user_data_dir.GetCStr());
if (!_GP(usetup).shared_data_dir.IsEmpty())
@@ -964,14 +966,23 @@ void engine_read_config(ConfigTree &cfg) {
if (Path::ComparePaths(user_global_cfg_file, def_cfg_file) != 0)
IniUtil::Read(user_global_cfg_file, cfg);
- // Handle directive to search for the user config inside the game directory;
- // this option may come either from command line or default/global config.
- _GP(usetup).local_user_conf |= INIreadint(cfg, "misc", "localuserconf", 0) != 0;
- if (_GP(usetup).local_user_conf) {
- // Test if the file is writeable, if it is then both engine and setup
- // applications may actually use it fully as a user config, otherwise
- // fallback to default behavior.
- _GP(usetup).local_user_conf = File::TestWriteFile(def_cfg_file);
+ // Handle directive to search for the user config inside the custom directory;
+ // this option may come either from command line or default/global config.
+ if (_GP(usetup).user_conf_dir.IsEmpty())
+ _GP(usetup).user_conf_dir = INIreadstring(cfg, "misc", "user_conf_dir");
+ if (_GP(usetup).user_conf_dir.IsEmpty()) // also try deprecated option
+ _GP(usetup).user_conf_dir = INIreadint(cfg, "misc", "localuserconf") != 0 ? "." : "";
+ // Test if the file is writeable, if it is then both engine and setup
+ // applications may actually use it fully as a user config, otherwise
+ // fallback to default behavior.
+ if (!_GP(usetup).user_conf_dir.IsEmpty()) {
+ if (Path::IsRelativePath(_GP(usetup).user_conf_dir))
+ _GP(usetup).user_conf_dir = Path::ConcatPaths(_GP(usetup).startup_dir, _GP(usetup).user_conf_dir);
+ if (!File::TestWriteFile(Path::ConcatPaths(_GP(usetup).user_conf_dir, DefaultConfigFileName))) {
+ Debug::Printf(kDbgMsg_Warn, "Write test failed at user config dir '%s', using default path.",
+ _GP(usetup).user_conf_dir.GetCStr());
+ _GP(usetup).user_conf_dir = "";
+ }
}
// Read user configuration file
diff --git a/engines/ags/engine/main/main.cpp b/engines/ags/engine/main/main.cpp
index c96f0097982..a450db05d89 100644
--- a/engines/ags/engine/main/main.cpp
+++ b/engines/ags/engine/main/main.cpp
@@ -223,7 +223,9 @@ int main_process_cmdline(ConfigTree &cfg, int argc, const char *argv[]) {
} else if (ags_stricmp(arg, "--conf") == 0 && (argc > ee + 1)) {
_GP(usetup).conf_path = argv[++ee];
} else if (ags_stricmp(arg, "--localuserconf") == 0) {
- _GP(usetup).local_user_conf = true;
+ _GP(usetup).user_conf_dir = ".";
+ } else if ((ags_stricmp(arg, "--user-conf-dir") == 0) && (argc > ee + 1)) {
+ _GP(usetup).user_conf_dir = argv[++ee];
} else if (ags_stricmp(arg, "--runfromide") == 0 && (argc > ee + 4)) {
_GP(usetup).install_dir = argv[ee + 1];
_GP(usetup).opt_data_dir = argv[ee + 2];
Commit: 27dc8075fb287df0ff659eea2be1a71cef060d25
https://github.com/scummvm/scummvm/commit/27dc8075fb287df0ff659eea2be1a71cef060d25
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2022-03-17T21:49:27-07:00
Commit Message:
AGS: Configurable user paths support $GAMENAME$ token
>From upstream c61191084fbdc8d60d147dc4238dc43797716e2c
Changed paths:
engines/ags/engine/main/config.cpp
engines/ags/engine/main/engine.cpp
diff --git a/engines/ags/engine/main/config.cpp b/engines/ags/engine/main/config.cpp
index c8638e4bab3..ec861719762 100644
--- a/engines/ags/engine/main/config.cpp
+++ b/engines/ags/engine/main/config.cpp
@@ -418,9 +418,6 @@ void post_config() {
_GP(usetup).Screen.FsGameFrame = GameFrameSetup(kFrame_MaxProportional);
if (!_GP(usetup).Screen.WinGameFrame.IsValid())
_GP(usetup).Screen.WinGameFrame = GameFrameSetup(kFrame_MaxRound);
-
- _GP(usetup).user_data_dir = Path::MakePathNoSlash(_GP(usetup).user_data_dir);
- _GP(usetup).shared_data_dir = Path::MakePathNoSlash(_GP(usetup).shared_data_dir);
}
void save_config_file() {
diff --git a/engines/ags/engine/main/engine.cpp b/engines/ags/engine/main/engine.cpp
index ad66cee686e..7b8005e592d 100644
--- a/engines/ags/engine/main/engine.cpp
+++ b/engines/ags/engine/main/engine.cpp
@@ -427,8 +427,16 @@ int engine_check_register_game() {
return 0;
}
+// Replace special tokens inside a user path option
+static void resolve_configured_path(String &option) {
+ option.Replace("$GAMENAME$", _GP(game).gamename);
+}
+
// Setup paths and directories that may be affected by user configuration
void engine_init_user_directories() {
+ resolve_configured_path(_GP(usetup).user_data_dir);
+ resolve_configured_path(_GP(usetup).shared_data_dir);
+
if (!_GP(usetup).user_conf_dir.IsEmpty())
Debug::Printf(kDbgMsg_Info, "User config directory: %s", _GP(usetup).user_conf_dir.GetCStr());
if (!_GP(usetup).user_data_dir.IsEmpty())
@@ -976,9 +984,11 @@ void engine_read_config(ConfigTree &cfg) {
// applications may actually use it fully as a user config, otherwise
// fallback to default behavior.
if (!_GP(usetup).user_conf_dir.IsEmpty()) {
+ resolve_configured_path(_GP(usetup).user_conf_dir);
if (Path::IsRelativePath(_GP(usetup).user_conf_dir))
_GP(usetup).user_conf_dir = Path::ConcatPaths(_GP(usetup).startup_dir, _GP(usetup).user_conf_dir);
- if (!File::TestWriteFile(Path::ConcatPaths(_GP(usetup).user_conf_dir, DefaultConfigFileName))) {
+ if (!Directory::CreateDirectory(_GP(usetup).user_conf_dir) ||
+ !File::TestWriteFile(Path::ConcatPaths(_GP(usetup).user_conf_dir, DefaultConfigFileName))) {
Debug::Printf(kDbgMsg_Warn, "Write test failed at user config dir '%s', using default path.",
_GP(usetup).user_conf_dir.GetCStr());
_GP(usetup).user_conf_dir = "";
More information about the Scummvm-git-logs
mailing list