[Scummvm-git-logs] scummvm master -> d9bbf9775cda4442e15b038a5573d5af3c6696d9
dreammaster
dreammaster at scummvm.org
Sun Jul 11 01:59:13 UTC 2021
This automated email contains information about 16 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .
Summary:
0416016b3d AGS: Add note from ivan-mogilko about MoveCharacterBlocking return values
4ae9574c64 AGS: Simplified use of FSLocation and ResolvedPath
0ba468d1c9 AGS: CreateAllDirectories works explicitly with sub_dirs parameter
215139e49d AGS: Add fast-path for file lookup
8f3b1e4b0e AGS: print all missing script imports for current script module
ddb5daca4c AGS: improve debug level and cc_error output for missing imports
7238f9df51 AGS: Fix memory leak in ci_find_file error paths
284437b22e AGS: In ci_find_file, fix directory handle leak on error
fabdbb1e20 AGS: In ci_find_file fix omission to check lstat return value
bfd25ba440 AGS: Reorganized ReadExtData functions into a DataExtReader class
ddf30d3234 AGS: implement GetOldBlockName() in RoomBlockReader, TRABlockReader
7b42f9c350 AGS: Optional block over-read leeway, for TRABlockReader
f19dd1c506 AGS: few cast fixes
f5a019d3b4 AGS: corrected GetDisplayDepthForNativeDepth
f6e5133edd AGS: In ci_find_file remove gratuitous chdir() code
d9bbf9775c AGS: Rewrote ci_find_file() into File::FindFileCI()
Commit: 0416016b3daf911fd2e418c90c1a727f7c574912
https://github.com/scummvm/scummvm/commit/0416016b3daf911fd2e418c90c1a727f7c574912
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2021-07-10T18:56:56-07:00
Commit Message:
AGS: Add note from ivan-mogilko about MoveCharacterBlocking return values
Changed paths:
engines/ags/engine/ac/global_character.cpp
diff --git a/engines/ags/engine/ac/global_character.cpp b/engines/ags/engine/ac/global_character.cpp
index 2c1288764c..77d08ef42c 100644
--- a/engines/ags/engine/ac/global_character.cpp
+++ b/engines/ags/engine/ac/global_character.cpp
@@ -367,6 +367,11 @@ int MoveCharacterBlocking(int chaa, int xx, int yy, int direct) {
MoveCharacter(chaa, xx, yy);
GameLoopUntilNotMoving(&_GP(game).chars[chaa].walking);
+
+ // TODO: See https://github.com/adventuregamestudio/ags/issues/1331#issuecomment-877524051
+ // To summarize, looks like there was a whole system of returned values, which was later
+ // just scrapped, so the ways to restore it is either extensive testing of all these
+ // functions using pre-2.72 editor/engine, or reverse engineer the code maybe.
return 1;
}
Commit: 4ae9574c6423f047665bf2b9e5ece914f527887d
https://github.com/scummvm/scummvm/commit/4ae9574c6423f047665bf2b9e5ece914f527887d
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2021-07-10T18:56:56-07:00
Commit Message:
AGS: Simplified use of FSLocation and ResolvedPath
>From upstream 89f62716b7f200250326b041d9d58e65dde896e1
Changed paths:
engines/ags/engine/ac/file.cpp
engines/ags/engine/ac/game.cpp
engines/ags/engine/ac/path_helper.h
diff --git a/engines/ags/engine/ac/file.cpp b/engines/ags/engine/ac/file.cpp
index ac918757b8..eeb93a422a 100644
--- a/engines/ags/engine/ac/file.cpp
+++ b/engines/ags/engine/ac/file.cpp
@@ -236,19 +236,19 @@ String PreparePathForWriting(const FSLocation &fsloc, const String &filename) {
FSLocation GetGlobalUserConfigDir() {
String dir = _G(platform)->GetUserGlobalConfigDirectory();
if (Path::IsRelativePath(dir)) // relative dir is resolved relative to the game data dir
- return FSLocation(_GP(ResPaths).DataDir, Path::ConcatPaths(_GP(ResPaths).DataDir, dir));
- return FSLocation(dir, dir);
+ return FSLocation(_GP(ResPaths).DataDir, dir);
+ return FSLocation(dir);
}
FSLocation GetGameUserConfigDir() {
String dir = _G(platform)->GetUserConfigDirectory();
if (Path::IsRelativePath(dir)) // relative dir is resolved relative to the game data dir
- return FSLocation(_GP(ResPaths).DataDir, Path::ConcatPaths(_GP(ResPaths).DataDir, 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, Path::ConcatPaths(dir, _GP(game).saveGameFolderName));
+ return FSLocation(dir, _GP(game).saveGameFolderName);
}
// A helper function that deduces a data directory either using default system location,
@@ -258,17 +258,17 @@ static FSLocation MakeGameDataDir(const String &default_dir, const String &user_
if (user_option.IsEmpty()) {
String dir = default_dir;
if (Path::IsRelativePath(dir)) // relative dir is resolved relative to the game data dir
- return FSLocation(_GP(ResPaths).DataDir, Path::ConcatPaths(_GP(ResPaths).DataDir, dir));
+ return FSLocation(_GP(ResPaths).DataDir, dir);
// 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, Path::ConcatPaths(dir, _GP(game).saveGameFolderName));
+ return FSLocation(dir, _GP(game).saveGameFolderName);
}
// If this location is set up by user config, then use it as is (resolving relative path if necessary)
String dir = user_option;
if (Path::IsSameOrSubDir(_GP(ResPaths).DataDir, dir)) // check if it's inside game dir
return FSLocation(_GP(ResPaths).DataDir, Path::MakeRelativePath(_GP(ResPaths).DataDir, dir));
dir = Path::MakeAbsolutePath(dir);
- return FSLocation(dir, dir);
+ return FSLocation(dir);
}
FSLocation GetGameAppDataDir() {
@@ -290,8 +290,7 @@ bool ResolveScriptPath(const String &orig_sc_path, bool read_only, ResolvedPath
}
if (is_absolute) {
- rp.FullPath = orig_sc_path;
- debugC(::AGS::kDebugFilePath, "Full path detected");
+ rp = ResolvedPath(orig_sc_path);
return true;
}
@@ -361,28 +360,24 @@ bool ResolveScriptPath(const String &orig_sc_path, bool read_only, ResolvedPath
String full_path = String::FromFormat("%s%s", parent_dir.BaseDir.GetCStr(), child_path.GetCStr());
// don't allow write operations for relative paths outside game dir
+ ResolvedPath test_rp = ResolvedPath(parent_dir, child_path, alt_path);
if (!read_only) {
- if (!Path::IsSameOrSubDir(parent_dir.FullDir, full_path)) {
+ if (!Path::IsSameOrSubDir(test_rp.Loc.FullDir, test_rp.FullPath)) {
debug_script_warn("Attempt to access file '%s' denied (outside of game directory)", sc_path.GetCStr());
return false;
}
}
- rp.BaseDir = parent_dir.BaseDir;
- rp.FullPath = full_path;
- rp.AltPath = alt_path;
-
- debugC(::AGS::kDebugFilePath, "Resolved path: %s", full_path.GetCStr());
- if (!alt_path.IsEmpty())
- debugC(::AGS::kDebugFilePath, "Alternative path: %s", alt_path.GetCStr());
-
+ rp = test_rp;
return true;
}
bool ResolveWritePathAndCreateDirs(const String &sc_path, ResolvedPath &rp) {
if (!ResolveScriptPath(sc_path, false, rp))
return false;
- if (!Directory::CreateAllDirectories(rp.BaseDir, Path::GetDirectoryPath(rp.FullPath))) {
+
+ if (!rp.Loc.SubDir.IsEmpty() &&
+ !Directory::CreateAllDirectories(rp.Loc.BaseDir, rp.Loc.FullDir)) {
debug_script_warn("ResolveScriptPath: failed to create all subdirectories: %s", rp.FullPath.GetCStr());
return false;
}
diff --git a/engines/ags/engine/ac/game.cpp b/engines/ags/engine/ac/game.cpp
index cd74cc7ac0..261c718159 100644
--- a/engines/ags/engine/ac/game.cpp
+++ b/engines/ags/engine/ac/game.cpp
@@ -241,44 +241,46 @@ String get_save_game_path(int slotNum) {
#if !AGS_PLATFORM_SCUMMVM
// Convert a path possibly containing path tags into acceptable save path
-bool MakeSaveGameDir(const String &newFolder, ResolvedPath &rp) {
- rp = ResolvedPath();
+bool MakeSaveGameDir(const String &newFolder, FSLocation &fsloc) {
+ fsloc = FSLocation();
// don't allow absolute paths
if (!Path::IsRelativePath(newFolder))
return false;
String base_dir;
- String newSaveGameDir = FixSlashAfterToken(newFolder);
-
- if (newSaveGameDir.CompareLeft(UserSavedgamesRootToken, UserSavedgamesRootToken.GetLength()) == 0) {
- if (_G(saveGameParent).IsEmpty()) {
- base_dir = PathFromInstallDir(_G(platform)->GetUserSavedgamesDirectory());
- newSaveGameDir.ReplaceMid(0, UserSavedgamesRootToken.GetLength(), base_dir);
+ String sub_dir;
+
+ if (newFolder.CompareLeft(UserSavedgamesRootToken) == 0) {
+ // IMPORTANT: for compatibility reasons we support both cases:
+ // when token is followed by the path separator and when it is not, in which case it's assumed.
+ if (saveGameParent.IsEmpty()) {
+ base_dir = PathFromInstallDir(platform->GetUserSavedgamesDirectory());
+ sub_dir = newFolder.Mid(UserSavedgamesRootToken.GetLength());
} else {
// If there is a custom save parent directory, then replace
- // not only root token, but also first subdirectory
- newSaveGameDir.ClipSection('/', 0, 1); // TODO: Path helper function for this?
- newSaveGameDir = Path::ConcatPaths(_G(saveGameParent), newSaveGameDir);
- base_dir = _G(saveGameParent);
+ // not only root token, but also first subdirectory after the token
+ base_dir = saveGameParent;
+ sub_dir = Path::ConcatPaths(".", newFolder.Mid(UserSavedgamesRootToken.GetLength()));
+ sub_dir.ClipSection('/', 0, 1); // TODO: Path helper function for this?
}
+ fsloc = FSLocation(base_dir, sub_dir);
} else {
// Convert the path relative to installation folder into path relative to the
// safe save path with default name
- if (_G(saveGameParent).IsEmpty()) {
- base_dir = PathFromInstallDir(_G(platform)->GetUserSavedgamesDirectory());
- newSaveGameDir = Path::ConcatPaths(Path::ConcatPaths(base_dir, _GP(game).saveGameFolderName), newFolder);
+ if (saveGameParent.IsEmpty()) {
+ base_dir = PathFromInstallDir(platform->GetUserSavedgamesDirectory());
+ sub_dir = Path::ConcatPaths(game.saveGameFolderName, newFolder);
} else {
- base_dir = _G(saveGameParent);
- newSaveGameDir = Path::ConcatPaths(_G(saveGameParent), newFolder);
+ base_dir = saveGameParent;
+ sub_dir = newFolder;
}
+ fsloc = FSLocation(base_dir, sub_dir);
// For games made in the safe-path-aware versions of AGS, report a warning
- if (_GP(game).options[OPT_SAFEFILEPATHS]) {
+ if (game.options[OPT_SAFEFILEPATHS]) {
debug_script_warn("Attempt to explicitly set savegame location relative to the game installation directory ('%s') denied;\nPath will be remapped to the user documents directory: '%s'",
- newFolder.GetCStr(), newSaveGameDir.GetCStr());
+ newFolder.GetCStr(), fsloc.FullDir.GetCStr());
}
}
- rp.BaseDir = base_dir;
- rp.FullPath = newSaveGameDir;
return true;
}
#endif
@@ -295,22 +297,21 @@ bool SetSaveGameDirectoryPath(const String &newFolder, bool explicit_path) {
#if AGS_PLATFORM_SCUMMVM
return false;
#else
- if (!newFolder || newFolder[0] == 0)
- newFolder = ".";
+ String newFolder = new_dir.IsEmpty() ? "." : new_dir;
String newSaveGameDir;
if (explicit_path) {
newSaveGameDir = PathFromInstallDir(newFolder);
if (!Directory::CreateDirectory(newSaveGameDir))
return false;
} else {
- ResolvedPath rp;
- if (!MakeSaveGameDir(newFolder, rp))
+ FSLocation fsloc;
+ if (!MakeSaveGameDir(newFolder, fsloc))
return false;
- if (!Directory::CreateAllDirectories(rp.BaseDir, rp.FullPath)) {
- debug_script_warn("SetSaveGameDirectory: failed to create all subdirectories: %s", rp.FullPath.GetCStr());
+ if (!Directory::CreateAllDirectories(fsloc.BaseDir, fsloc.FullDir)) {
+ debug_script_warn("SetSaveGameDirectory: failed to create all subdirectories: %s", fsloc.FullDir.GetCStr());
return false;
}
- newSaveGameDir = rp.FullPath;
+ newSaveGameDir = fsloc.FullDir;
}
String newFolderTempFile = Path::ConcatPaths(newSaveGameDir, "agstmp.tmp");
@@ -318,7 +319,7 @@ bool SetSaveGameDirectoryPath(const String &newFolder, bool explicit_path) {
return false;
// copy the Restart Game file, if applicable
- String restartGamePath = Path::ConcatPaths(_G(saveGameDirectory), get_save_game_filename(RESTART_POINT_SAVE_GAME_NUMBER));
+ String restartGamePath = Path::ConcatPaths(saveGameDirectory, get_save_game_filename(RESTART_POINT_SAVE_GAME_NUMBER));
Stream *restartGameFile = File::OpenFileRead(restartGamePath);
if (restartGameFile != nullptr) {
long fileSize = restartGameFile->GetLength();
@@ -333,7 +334,7 @@ bool SetSaveGameDirectoryPath(const String &newFolder, bool explicit_path) {
free(mbuffer);
}
- _G(saveGameDirectory) = newSaveGameDir;
+ saveGameDirectory = newSaveGameDir;
return true;
#endif
}
diff --git a/engines/ags/engine/ac/path_helper.h b/engines/ags/engine/ac/path_helper.h
index 04e4d773bd..4900081ebd 100644
--- a/engines/ags/engine/ac/path_helper.h
+++ b/engines/ags/engine/ac/path_helper.h
@@ -33,7 +33,7 @@
#ifndef AGS_ENGINE_AC_PATH_HELPER_H
#define AGS_ENGINE_AC_PATH_HELPER_H
-#include "ags/shared/util/string.h"
+#include "ags/shared/util/path.h"
namespace AGS3 {
@@ -57,12 +57,15 @@ String PathFromInstallDir(const String &path);
// The meaning of this is that engine is only allowed to create
// sub-path subdirectories, and only if secure path exists.
struct FSLocation {
- String BaseDir; // parent part of the full path that is not our responsibility
- String FullDir; // full path to the directory
+ String BaseDir; // base directory, which we assume already exists; not our responsibility
+ String SubDir; // sub-directory, relative to BaseDir
+ String FullDir; // full path to location
FSLocation() {}
FSLocation(const String &base) : BaseDir(base), FullDir(base) {
}
- FSLocation(const String &base, const String &full) : BaseDir(base), FullDir(full) {
+ FSLocation(const String &base, const String &subdir)
+ : BaseDir(base), SubDir(subdir),
+ FullDir(AGS::Shared::Path::ConcatPaths(base, subdir)) {
}
};
// Makes sure that given system location is available, makes directories if have to (and if it's allowed to)
@@ -81,9 +84,16 @@ FSLocation GetGameUserDataDir();
// ResolvedPath describes an actual location pointed by a user path (e.g. from script)
struct ResolvedPath {
- String BaseDir; // base directory, which we assume already exists
+ FSLocation Loc; // location (directory)
String FullPath; // full path, including filename
- String AltPath; // alternative full path, for backwards compatibility
+ String AltPath; // alternative read-only full path, for backwards compatibility
+ ResolvedPath() = default;
+ ResolvedPath(const String & file, const String & alt = "")
+ : FullPath(file), AltPath(alt) {
+ }
+ ResolvedPath(const FSLocation & loc, const String & file, const String & alt = "")
+ : Loc(loc), FullPath(AGS::Shared::Path::ConcatPaths(loc.FullDir, file)), AltPath(alt) {
+ }
};
// Resolves a file path provided by user (e.g. script) into actual file path,
// by substituting special keywords with actual platform-specific directory names.
Commit: 0ba468d1c92e99341b248bdcef3652b75c6f3587
https://github.com/scummvm/scummvm/commit/0ba468d1c92e99341b248bdcef3652b75c6f3587
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2021-07-10T18:56:56-07:00
Commit Message:
AGS: CreateAllDirectories works explicitly with sub_dirs parameter
>From upstream 80f54e2c17830121bbe8b8501f22536c90b3f760
Changed paths:
engines/ags/engine/ac/file.cpp
engines/ags/engine/ac/game.cpp
engines/ags/shared/util/directory.cpp
engines/ags/shared/util/directory.h
engines/ags/shared/util/path.h
engines/ags/shared/util/string.cpp
engines/ags/shared/util/string.h
diff --git a/engines/ags/engine/ac/file.cpp b/engines/ags/engine/ac/file.cpp
index eeb93a422a..acbe86d028 100644
--- a/engines/ags/engine/ac/file.cpp
+++ b/engines/ags/engine/ac/file.cpp
@@ -228,7 +228,7 @@ String PathFromInstallDir(const String &path) {
}
String PreparePathForWriting(const FSLocation &fsloc, const String &filename) {
- if (Directory::CreateAllDirectories(fsloc.BaseDir, fsloc.FullDir))
+ if (Directory::CreateAllDirectories(fsloc.BaseDir, fsloc.SubDir))
return Path::ConcatPaths(fsloc.FullDir, filename);
return "";
}
@@ -377,7 +377,7 @@ bool ResolveWritePathAndCreateDirs(const String &sc_path, ResolvedPath &rp) {
return false;
if (!rp.Loc.SubDir.IsEmpty() &&
- !Directory::CreateAllDirectories(rp.Loc.BaseDir, rp.Loc.FullDir)) {
+ !Directory::CreateAllDirectories(rp.Loc.BaseDir, rp.Loc.SubDir)) {
debug_script_warn("ResolveScriptPath: failed to create all subdirectories: %s", rp.FullPath.GetCStr());
return false;
}
diff --git a/engines/ags/engine/ac/game.cpp b/engines/ags/engine/ac/game.cpp
index 261c718159..13158a6506 100644
--- a/engines/ags/engine/ac/game.cpp
+++ b/engines/ags/engine/ac/game.cpp
@@ -307,7 +307,7 @@ bool SetSaveGameDirectoryPath(const String &newFolder, bool explicit_path) {
FSLocation fsloc;
if (!MakeSaveGameDir(newFolder, fsloc))
return false;
- if (!Directory::CreateAllDirectories(fsloc.BaseDir, fsloc.FullDir)) {
+ if (!Directory::CreateAllDirectories(fsloc.BaseDir, fsloc.SubDir)) {
debug_script_warn("SetSaveGameDirectory: failed to create all subdirectories: %s", fsloc.FullDir.GetCStr());
return false;
}
diff --git a/engines/ags/shared/util/directory.cpp b/engines/ags/shared/util/directory.cpp
index df5611fab1..aea9cd7086 100644
--- a/engines/ags/shared/util/directory.cpp
+++ b/engines/ags/shared/util/directory.cpp
@@ -39,27 +39,33 @@ bool CreateDirectory(const String &path) {
return Common::FSNode(path.GetCStr()).createDirectory();
}
-bool CreateAllDirectories(const String &parent, const String &path) {
- if (path == SAVE_FOLDER_PREFIX)
+bool CreateAllDirectories(const String &parent, const String &sub_dirs) {
+ if (sub_dirs == SAVE_FOLDER_PREFIX)
// ScummVM save folder doesn't need creating
return true;
- if (!ags_directory_exists(parent.GetCStr()))
- return false;
- if (path.IsEmpty())
- return true;
- if (!Path::IsSameOrSubDir(parent, path))
- return false;
+ if (parent.IsEmpty() || !ags_directory_exists(parent.GetCStr()))
+ return false; // no sense, or base dir not exist
+ if (sub_dirs.IsEmpty())
+ return true; // nothing to create, so fine
- String sub_path = Path::MakeRelativePath(parent, path);
- String make_path = parent;
- std::vector<String> dirs = sub_path.Split('/');
- for (const String &dir : dirs) {
- if (dir.IsEmpty() || dir.Compare(".") == 0) continue;
- make_path.AppendChar('/');
- make_path.Append(dir);
+ String make_path = String::FromFormat("%s/", parent.GetCStr());
+ for (const char *sect = sub_dirs.GetCStr();
+ sect < sub_dirs.GetCStr() + sub_dirs.GetLength();) {
+ const char *cur = sect + 1;
+ for (; *cur && *cur != '/' && *cur != PATH_ALT_SEPARATOR; ++cur);
+ // Skip empty dirs (duplicated separators etc)
+ if ((cur - sect == 1) && (*cur == '.' || *cur == '/' || *cur == PATH_ALT_SEPARATOR)) {
+ sect = cur;
+ continue;
+ }
+ // In case of ".." just fail
+ if (strncmp(sect, "..", cur - sect) == 0)
+ return false;
+ make_path.Append(sect, cur - sect);
if (!CreateDirectory(make_path))
return false;
+ sect = cur;
}
return true;
}
diff --git a/engines/ags/shared/util/directory.h b/engines/ags/shared/util/directory.h
index e544b299d2..0093193da2 100644
--- a/engines/ags/shared/util/directory.h
+++ b/engines/ags/shared/util/directory.h
@@ -42,9 +42,9 @@ namespace Directory {
// Creates new directory (if it does not exist)
bool CreateDirectory(const String &path);
-// Makes sure all directories in the path are created. Parent path is
+// Makes sure all the sub-directories in the path are created. Parent path is
// not touched, and function must fail if parent path is not accessible.
-bool CreateAllDirectories(const String &parent, const String &path);
+bool CreateAllDirectories(const String &parent, const String &sub_dirs);
// Sets current working directory, returns the resulting path
String SetCurrentDirectory(const String &path);
// Gets current working directory
diff --git a/engines/ags/shared/util/path.h b/engines/ags/shared/util/path.h
index 91b92e8371..664beb8754 100644
--- a/engines/ags/shared/util/path.h
+++ b/engines/ags/shared/util/path.h
@@ -31,6 +31,8 @@
#include "ags/shared/util/string.h"
+#define PATH_ALT_SEPARATOR ('\\')
+
namespace AGS3 {
namespace AGS {
namespace Shared {
diff --git a/engines/ags/shared/util/string.cpp b/engines/ags/shared/util/string.cpp
index fd4c60e1e6..d79c28e00b 100644
--- a/engines/ags/shared/util/string.cpp
+++ b/engines/ags/shared/util/string.cpp
@@ -419,15 +419,28 @@ void String::Compact() {
}
void String::Append(const String &str) {
- size_t length = str._len;
if (str._len > 0) {
- ReserveAndShift(false, length);
- memcpy(_cstr + _len, str._cstr, length);
- _len += length;
+ ReserveAndShift(false, str._len);
+ memcpy(_cstr + _len, str._cstr, str._len);
+ _len += str._len;
_cstr[_len] = 0;
}
}
+void String::Append(const char *cstr, size_t len) {
+ if (len == 0)
+ return;
+ // Test for null-terminator in the range
+ const char *ptr = cstr;
+ for (; *ptr && (size_t)(ptr - cstr) < len; ++ptr);
+ if ((size_t)(ptr - cstr) < len)
+ len = ptr - cstr;
+ ReserveAndShift(false, len);
+ memcpy(_cstr + _len, cstr, len);
+ _len += len;
+ _cstr[_len] = 0;
+}
+
void String::AppendChar(char c) {
if (c) {
ReserveAndShift(false, 1);
diff --git a/engines/ags/shared/util/string.h b/engines/ags/shared/util/string.h
index 8d41d856c4..1d1c108c0c 100644
--- a/engines/ags/shared/util/string.h
+++ b/engines/ags/shared/util/string.h
@@ -281,8 +281,10 @@ public:
// Appends another string to this string
void Append(const String &str);
void Append(const char *cstr) {
- String str = String::Wrapper(cstr); Append(str);
+ String str = String::Wrapper(cstr);
+ Append(str);
}
+ void Append(const char *cstr, size_t len);
// Appends a single character
void AppendChar(char c);
// Clip* methods decrease the string, removing defined part
Commit: 215139e49d1e8e650e0f4cf74e3e7ce9d62cf674
https://github.com/scummvm/scummvm/commit/215139e49d1e8e650e0f4cf74e3e7ce9d62cf674
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2021-07-10T18:56:57-07:00
Commit Message:
AGS: Add fast-path for file lookup
>From upstream 8c088f61a3180d70591a98dcb36b189e5f6cb2e6
Changed paths:
engines/ags/shared/util/misc.cpp
diff --git a/engines/ags/shared/util/misc.cpp b/engines/ags/shared/util/misc.cpp
index 6f0a774c26..e9ab5126c7 100644
--- a/engines/ags/shared/util/misc.cpp
+++ b/engines/ags/shared/util/misc.cpp
@@ -50,14 +50,6 @@
*/
#include "ags/shared/core/platform.h"
-
-//include <sys/types.h>
-//include <sys/stat.h>
-#if !AGS_PLATFORM_OS_WINDOWS
-//include <dirent.h>
-//include <unistd.h>
-#endif
-
#include "ags/lib/allegro.h" // file path functions
#include "ags/shared/util/file.h"
#include "ags/shared/util/stream.h"
@@ -70,8 +62,7 @@ using namespace AGS::Shared;
// TODO: rewrite all this in a cleaner way perhaps, and move to our file or path utilities unit
//
-#if !defined (AGS_CASE_SENSITIVE_FILESYSTEM)
-//include <string.h>
+#if !defined(AGS_CASE_SENSITIVE_FILESYSTEM)
/* File Name Concatenator basically on Windows / DOS */
char *ci_find_file(const char *dir_name, const char *file_name) {
char *diamond = NULL;
@@ -121,6 +112,15 @@ char *ci_find_file(const char *dir_name, const char *file_name) {
fix_filename_slashes(filename);
}
+ if (directory && filename) {
+ char buf[1024];
+ snprintf(buf, sizeof buf, "%s/%s", directory, filename);
+ lstat(buf, &statbuf);
+ if (S_ISREG(statbuf.st_mode) || S_ISLNK(statbuf.st_mode)) {
+ diamond = strdup(buf); goto out;
+ }
+ }
+
if (directory == nullptr) {
char *match = nullptr;
int match_len = 0;
@@ -173,13 +173,14 @@ char *ci_find_file(const char *dir_name, const char *file_name) {
append_filename(diamond, directory, entry->d_name, strlen(directory) + strlen(entry->d_name) + 2);
break;
}
- }
- }
+}
+}
closedir(rough);
fchdir(dirfd(prevdir));
closedir(prevdir);
+out:;
free(directory);
free(filename);
@@ -187,7 +188,6 @@ char *ci_find_file(const char *dir_name, const char *file_name) {
}
#endif
-
/* Case Insensitive fopen */
Stream *ci_fopen(const char *file_name, FileOpenMode open_mode, FileWorkMode work_mode) {
#if !defined (AGS_CASE_SENSITIVE_FILESYSTEM)
Commit: 8f3b1e4b0e80e5ba08aee4a9ac0fc1126f82b84e
https://github.com/scummvm/scummvm/commit/8f3b1e4b0e80e5ba08aee4a9ac0fc1126f82b84e
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2021-07-10T18:56:57-07:00
Commit Message:
AGS: print all missing script imports for current script module
>From upstream 9a1c401ba97267947db5357b34db55e62b213f99
Changed paths:
engines/ags/engine/script/cc_instance.cpp
diff --git a/engines/ags/engine/script/cc_instance.cpp b/engines/ags/engine/script/cc_instance.cpp
index fe71d254cf..e6e9a55bf7 100644
--- a/engines/ags/engine/script/cc_instance.cpp
+++ b/engines/ags/engine/script/cc_instance.cpp
@@ -1492,6 +1492,7 @@ bool ccInstance::ResolveScriptImports(PScript scri) {
}
resolved_imports = new int[numimports];
+ int errors = 0;
for (int i = 0; i < scri->numimports; ++i) {
if (scri->imports[i] == nullptr) {
resolved_imports[i] = -1;
@@ -1500,11 +1501,14 @@ bool ccInstance::ResolveScriptImports(PScript scri) {
resolved_imports[i] = _GP(simp).get_index_of(scri->imports[i]);
if (resolved_imports[i] < 0) {
- cc_error("unresolved import '%s' in %s", scri->imports[i], scri->numSections > 0 ? scri->sectionNames[0] : "<unknown>");
- return false;
+ AGS::Shared::Debug::Printf(kDbgMsg_Info, "unresolved import '%s' in %s", scri->imports[i], scri->numSections > 0 ? scri->sectionNames[0] : "<unknown>");
+ errors++;
}
}
- return true;
+
+ if (errors > 0)
+ cc_error("unresolved imports, quitting.");
+ return errors == 0;
}
// TODO: it is possible to deduce global var's size at start with
Commit: ddb5daca4c4556cb5672d6841eb736562e4cc23c
https://github.com/scummvm/scummvm/commit/ddb5daca4c4556cb5672d6841eb736562e4cc23c
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2021-07-10T18:56:57-07:00
Commit Message:
AGS: improve debug level and cc_error output for missing imports
>From upstream ca66d3cb69930c9f755da5fc26e173098169ae7d
Changed paths:
engines/ags/engine/script/cc_instance.cpp
diff --git a/engines/ags/engine/script/cc_instance.cpp b/engines/ags/engine/script/cc_instance.cpp
index e6e9a55bf7..7cd8573092 100644
--- a/engines/ags/engine/script/cc_instance.cpp
+++ b/engines/ags/engine/script/cc_instance.cpp
@@ -1492,7 +1492,7 @@ bool ccInstance::ResolveScriptImports(PScript scri) {
}
resolved_imports = new int[numimports];
- int errors = 0;
+ int errors = 0, last_err_idx = 0;
for (int i = 0; i < scri->numimports; ++i) {
if (scri->imports[i] == nullptr) {
resolved_imports[i] = -1;
@@ -1501,13 +1501,17 @@ bool ccInstance::ResolveScriptImports(PScript scri) {
resolved_imports[i] = _GP(simp).get_index_of(scri->imports[i]);
if (resolved_imports[i] < 0) {
- AGS::Shared::Debug::Printf(kDbgMsg_Info, "unresolved import '%s' in %s", scri->imports[i], scri->numSections > 0 ? scri->sectionNames[0] : "<unknown>");
+ Debug::Printf(kDbgMsg_Error, "unresolved import '%s' in '%s'", scri->imports[i], scri->numSections > 0 ? scri->sectionNames[0] : "<unknown>");
errors++;
+ last_err_idx = i;
}
}
if (errors > 0)
- cc_error("unresolved imports, quitting.");
+ cc_error("in %s: %d unresolved imports (last: %s)",
+ scri->numSections > 0 ? scri->sectionNames[0] : "<unknown>",
+ errors,
+ scri->imports[last_err_idx]);
return errors == 0;
}
Commit: 7238f9df51e7d60fdeb9c70f1267254bc745584a
https://github.com/scummvm/scummvm/commit/7238f9df51e7d60fdeb9c70f1267254bc745584a
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2021-07-10T18:56:57-07:00
Commit Message:
AGS: Fix memory leak in ci_find_file error paths
>From upstream 1f7c216de21b7adaf96f8420bbbc10f124d2059b
Changed paths:
engines/ags/shared/util/misc.cpp
diff --git a/engines/ags/shared/util/misc.cpp b/engines/ags/shared/util/misc.cpp
index e9ab5126c7..80244fe844 100644
--- a/engines/ags/shared/util/misc.cpp
+++ b/engines/ags/shared/util/misc.cpp
@@ -83,7 +83,7 @@ char *ci_find_file(const char *dir_name, const char *file_name) {
}
#else
-/* Case Insensitive File Find */
+/* Case Insensitive File Find - Only used on UNIX platforms */
char *ci_find_file(const char *dir_name, const char *file_name) {
struct stat statbuf;
struct dirent *entry = nullptr;
@@ -94,7 +94,7 @@ char *ci_find_file(const char *dir_name, const char *file_name) {
char *filename = nullptr;
if (dir_name == nullptr && file_name == nullptr)
- return nullptr;
+ goto out;
if (dir_name != nullptr) {
directory = (char *)malloc(strlen(dir_name) + 1);
@@ -128,7 +128,7 @@ char *ci_find_file(const char *dir_name, const char *file_name) {
match = get_filename(filename);
if (match == nullptr)
- return nullptr;
+ goto out;
match_len = strlen(match);
dir_len = (match - filename);
@@ -149,17 +149,17 @@ char *ci_find_file(const char *dir_name, const char *file_name) {
if ((prevdir = opendir(".")) == nullptr) {
fprintf(stderr, "ci_find_file: cannot open current working directory\n");
- return nullptr;
+ goto out;
}
if (chdir(directory) == -1) {
fprintf(stderr, "ci_find_file: cannot change to directory: %s\n", directory);
- return nullptr;
+ goto out;
}
if ((rough = opendir(directory)) == nullptr) {
fprintf(stderr, "ci_find_file: cannot open directory: %s\n", directory);
- return nullptr;
+ goto out;
}
while ((entry = readdir(rough)) != nullptr) {
@@ -173,16 +173,16 @@ char *ci_find_file(const char *dir_name, const char *file_name) {
append_filename(diamond, directory, entry->d_name, strlen(directory) + strlen(entry->d_name) + 2);
break;
}
-}
-}
+ }
+ }
closedir(rough);
fchdir(dirfd(prevdir));
closedir(prevdir);
-out:;
- free(directory);
- free(filename);
+out:
+ if (directory) free(directory);
+ if (filename) free(filename);
return diamond;
}
Commit: 284437b22ee303890139a40d0e9bd781315c2b07
https://github.com/scummvm/scummvm/commit/284437b22ee303890139a40d0e9bd781315c2b07
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2021-07-10T18:56:57-07:00
Commit Message:
AGS: In ci_find_file, fix directory handle leak on error
>From upstream 3951d93e1125aac6aed8698dc2140e51a852358c
Changed paths:
engines/ags/shared/util/misc.cpp
diff --git a/engines/ags/shared/util/misc.cpp b/engines/ags/shared/util/misc.cpp
index 80244fe844..d22ccb0413 100644
--- a/engines/ags/shared/util/misc.cpp
+++ b/engines/ags/shared/util/misc.cpp
@@ -83,7 +83,7 @@ char *ci_find_file(const char *dir_name, const char *file_name) {
}
#else
-/* Case Insensitive File Find - Only used on UNIX platforms */
+/* Case Sensitive File Find - only used on UNIX platforms */
char *ci_find_file(const char *dir_name, const char *file_name) {
struct stat statbuf;
struct dirent *entry = nullptr;
@@ -94,7 +94,7 @@ char *ci_find_file(const char *dir_name, const char *file_name) {
char *filename = nullptr;
if (dir_name == nullptr && file_name == nullptr)
- goto out;
+ return nullptr;
if (dir_name != nullptr) {
directory = (char *)malloc(strlen(dir_name) + 1);
@@ -112,7 +112,14 @@ char *ci_find_file(const char *dir_name, const char *file_name) {
fix_filename_slashes(filename);
}
- if (directory && filename) {
+ // the ".." check here prevents file system traversal -
+ // since only in this fast-path it's possible a potentially evil
+ // script could try to break out of the directories it's restricted
+ // to, whereas the latter chdir/opendir approach checks file by file
+ // in the directory. it's theoretically possible a valid filename
+ // could contain "..", but in that case it will just fallback to the
+ // slower method later on and succeed.
+ if (directory && filename && !strstr(filename, "..")) {
char buf[1024];
snprintf(buf, sizeof buf, "%s/%s", directory, filename);
lstat(buf, &statbuf);
@@ -154,12 +161,12 @@ char *ci_find_file(const char *dir_name, const char *file_name) {
if (chdir(directory) == -1) {
fprintf(stderr, "ci_find_file: cannot change to directory: %s\n", directory);
- goto out;
+ goto out_pd;
}
if ((rough = opendir(directory)) == nullptr) {
fprintf(stderr, "ci_find_file: cannot open directory: %s\n", directory);
- goto out;
+ goto out_pd;
}
while ((entry = readdir(rough)) != nullptr) {
@@ -177,10 +184,11 @@ char *ci_find_file(const char *dir_name, const char *file_name) {
}
closedir(rough);
+out_pd:;
fchdir(dirfd(prevdir));
closedir(prevdir);
-out:
+out:;
if (directory) free(directory);
if (filename) free(filename);
Commit: fabdbb1e201e7c3e764c87139b3ee877bd689bd3
https://github.com/scummvm/scummvm/commit/fabdbb1e201e7c3e764c87139b3ee877bd689bd3
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2021-07-10T18:56:57-07:00
Commit Message:
AGS: In ci_find_file fix omission to check lstat return value
>From upstream f2ae56a5c07981a9470cfc85d5a9e8fd02151da6
This code is commented out for ScummVM in any case
Changed paths:
engines/ags/shared/util/misc.cpp
diff --git a/engines/ags/shared/util/misc.cpp b/engines/ags/shared/util/misc.cpp
index d22ccb0413..90016ec2cc 100644
--- a/engines/ags/shared/util/misc.cpp
+++ b/engines/ags/shared/util/misc.cpp
@@ -119,11 +119,15 @@ char *ci_find_file(const char *dir_name, const char *file_name) {
// in the directory. it's theoretically possible a valid filename
// could contain "..", but in that case it will just fallback to the
// slower method later on and succeed.
- if (directory && filename && !strstr(filename, "..")) {
+ if (filename && !strstr(filename, "..")) {
char buf[1024];
- snprintf(buf, sizeof buf, "%s/%s", directory, filename);
- lstat(buf, &statbuf);
- if (S_ISREG(statbuf.st_mode) || S_ISLNK(statbuf.st_mode)) {
+ if (!directory && filename[0] == '/')
+ snprintf(buf, sizeof buf, "%s", filename);
+ else
+ snprintf(buf, sizeof buf, "%s/%s", directory ? directory : ".", filename);
+
+ if (lstat(buf, &statbuf) == 0 &&
+ (S_ISREG(statbuf.st_mode) || S_ISLNK(statbuf.st_mode))) {
diamond = strdup(buf); goto out;
}
}
@@ -170,9 +174,9 @@ char *ci_find_file(const char *dir_name, const char *file_name) {
}
while ((entry = readdir(rough)) != nullptr) {
- lstat(entry->d_name, &statbuf);
- if (S_ISREG(statbuf.st_mode) || S_ISLNK(statbuf.st_mode)) {
- if (strcasecmp(filename, entry->d_name) == 0) {
+ if (strcasecmp(filename, entry->d_name) == 0) {
+ if (lstat(entry->d_name, &statbuf) == 0 &&
+ (S_ISREG(statbuf.st_mode) || S_ISLNK(statbuf.st_mode))) {
#if AGS_PLATFORM_DEBUG
fprintf(stderr, "ci_find_file: Looked for %s in rough %s, found diamond %s.\n", filename, directory, entry->d_name);
#endif // AGS_PLATFORM_DEBUG
Commit: bfd25ba440188328f192b72c9cb18f9215b29cb7
https://github.com/scummvm/scummvm/commit/bfd25ba440188328f192b72c9cb18f9215b29cb7
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2021-07-10T18:56:58-07:00
Commit Message:
AGS: Reorganized ReadExtData functions into a DataExtReader class
>From upstream 462535ec21e2b184651d03d118b9cfd2258520e4
Changed paths:
engines/ags/shared/game/main_game_file.cpp
engines/ags/shared/game/room_file.cpp
engines/ags/shared/game/room_file.h
engines/ags/shared/game/room_file_base.cpp
engines/ags/shared/game/tra_file.cpp
engines/ags/shared/util/data_ext.cpp
engines/ags/shared/util/data_ext.h
diff --git a/engines/ags/shared/game/main_game_file.cpp b/engines/ags/shared/game/main_game_file.cpp
index 0b8b153faf..3f7f1899ee 100644
--- a/engines/ags/shared/game/main_game_file.cpp
+++ b/engines/ags/shared/game/main_game_file.cpp
@@ -682,53 +682,60 @@ HGameFileError ReadSpriteFlags(LoadedGameEntities &ents, Stream *in, GameDataVer
return HGameFileError::None();
}
-static HGameFileError ReadExtBlock(LoadedGameEntities &ents, Stream *in, const String &ext_id, soff_t block_len, GameDataVersion data_ver) {
- // Add extensions here checking ext_id, which is an up to 16-chars name, for example:
- // if (ext_id.CompareNoCase("GUI_NEWPROPS") == 0)
- // {
- // // read new gui properties
- // }
- return new MainGameFileError(kMGFErr_ExtUnknown, String::FromFormat("Type: %s", ext_id.GetCStr()));
-}
+// GameDataExtReader reads main game data's extension blocks
+class GameDataExtReader : public DataExtReader {
+public:
+ GameDataExtReader(LoadedGameEntities &ents, GameDataVersion data_ver, Stream *in)
+ : DataExtReader(in, kDataExt_NumID8 | kDataExt_File64)
+ , _ents(ents)
+ , _dataVer(data_ver) {
+ }
+
+protected:
+ HError ReadBlock(int block_id, const String &ext_id,
+ soff_t block_len, bool &read_next) override;
-// This reader will process all blocks inside ReadExtBlock() function,
-// and read compatible data into the given LoadedGameEntities object.
-static LoadedGameEntities *reader_ents;
-static GameDataVersion reader_ver;
-HError ReadGameDataReader(Stream *in, int block_id, const String &ext_id,
+ LoadedGameEntities &_ents;
+ GameDataVersion _dataVer;
+};
+
+HError GameDataExtReader::ReadBlock(int block_id, const String &ext_id,
soff_t block_len, bool &read_next) {
- return (HError)ReadExtBlock(*reader_ents, in, ext_id, block_len, reader_ver);
+ // Add extensions here checking ext_id, which is an up to 16-chars name, for example:
+ // if (ext_id.CompareNoCase("GUI_NEWPROPS") == 0)
+ // {
+ // // read new gui properties
+ // }
+ return new MainGameFileError(kMGFErr_ExtUnknown, String::FromFormat("Type: %s", ext_id.GetCStr()));
}
HGameFileError ReadGameData(LoadedGameEntities &ents, Stream *in, GameDataVersion data_ver) {
GameSetupStruct &game = ents.Game;
- reader_ents = &ents;
- reader_ver = data_ver;
//-------------------------------------------------------------------------
// The classic data section.
//-------------------------------------------------------------------------
{
AlignedStream align_s(in, Shared::kAligned_Read);
- _GP(game).GameSetupStructBase::ReadFromFile(&align_s);
+ game.GameSetupStructBase::ReadFromFile(&align_s);
}
- Debug::Printf(kDbgMsg_Info, "Game title: '%s'", _GP(game).gamename);
+ Debug::Printf(kDbgMsg_Info, "Game title: '%s'", game.gamename);
- if (_GP(game).GetGameRes().IsNull())
+ if (game.GetGameRes().IsNull())
return new MainGameFileError(kMGFErr_InvalidNativeResolution);
- _GP(game).read_savegame_info(in, data_ver);
- _GP(game).read_font_infos(in, data_ver);
+ game.read_savegame_info(in, data_ver);
+ game.read_font_infos(in, data_ver);
HGameFileError err = ReadSpriteFlags(ents, in, data_ver);
if (!err)
return err;
- _GP(game).ReadInvInfo_Aligned(in);
- err = _GP(game).read_cursors(in, data_ver);
+ game.ReadInvInfo_Aligned(in);
+ err = game.read_cursors(in, data_ver);
if (!err)
return err;
- _GP(game).read_interaction_scripts(in, data_ver);
- _GP(game).read_words_dictionary(in);
+ game.read_interaction_scripts(in, data_ver);
+ game.read_words_dictionary(in);
if (game.load_compiled_script) {
ents.GlobalScript.reset(ccScript::CreateFromStream(in));
@@ -750,16 +757,16 @@ HGameFileError ReadGameData(LoadedGameEntities &ents, Stream *in, GameDataVersio
in->Seek(count * 0x204);
}
- _GP(game).read_characters(in, data_ver);
- _GP(game).read_lipsync(in, data_ver);
- _GP(game).read_messages(in, data_ver);
+ game.read_characters(in, data_ver);
+ game.read_lipsync(in, data_ver);
+ game.read_messages(in, data_ver);
ReadDialogs(ents.Dialogs, ents.OldDialogScripts, ents.OldDialogSources, ents.OldSpeechLines,
- in, data_ver, _GP(game).numdialog);
+ in, data_ver, game.numdialog);
HError err2 = GUI::ReadGUI(_GP(guis), in);
if (!err2)
return new MainGameFileError(kMGFErr_GameEntityFailed, err2);
- _GP(game).numgui = _GP(guis).size();
+ game.numgui = _GP(guis).size();
if (data_ver >= kGameVersion_260) {
err = ReadPlugins(ents.PluginInfos, in);
@@ -767,13 +774,13 @@ HGameFileError ReadGameData(LoadedGameEntities &ents, Stream *in, GameDataVersio
return err;
}
- err = _GP(game).read_customprops(in, data_ver);
+ err = game.read_customprops(in, data_ver);
if (!err)
return err;
- err = _GP(game).read_audio(in, data_ver);
+ err = game.read_audio(in, data_ver);
if (!err)
return err;
- _GP(game).read_room_names(in, data_ver);
+ game.read_room_names(in, data_ver);
if (data_ver <= kGameVersion_350)
return HGameFileError::None();
@@ -781,9 +788,8 @@ HGameFileError ReadGameData(LoadedGameEntities &ents, Stream *in, GameDataVersio
//-------------------------------------------------------------------------
// All the extended data, for AGS > 3.5.0.
//-------------------------------------------------------------------------
-
- HError ext_err = ReadExtData(ReadGameDataReader,
- kDataExt_NumID8 | kDataExt_File64, in);
+ GameDataExtReader reader(ents, data_ver, in);
+ HError ext_err = reader.Read();
return ext_err ? HGameFileError::None() : new MainGameFileError(kMGFErr_ExtListFailed, ext_err);
}
diff --git a/engines/ags/shared/game/room_file.cpp b/engines/ags/shared/game/room_file.cpp
index fb1aa5bee6..a663bb9b5d 100644
--- a/engines/ags/shared/game/room_file.cpp
+++ b/engines/ags/shared/game/room_file.cpp
@@ -456,23 +456,43 @@ HError ReadRoomBlock(RoomStruct *room, Stream *in, RoomFileBlock block, const St
String::FromFormat("Type: %s", ext_id.GetCStr()));
}
-// This reader will process all blocks inside ReadRoomBlock() function,
-// and read compatible data into the given RoomStruct
-static RoomStruct *reader_room;
-static RoomFileVersion reader_data_ver;
-
-static HError ReadRoomDataReader(Stream *in, int block_id,
- const String &ext_id, soff_t block_len, bool &read_next) {
- return (HError)ReadRoomBlock(reader_room, in, (RoomFileBlock)block_id, ext_id, block_len, reader_data_ver);
-}
+// RoomBlockReader reads whole room data, block by block
+class RoomBlockReader : public DataExtReader {
+public:
+ RoomBlockReader(RoomStruct *room, RoomFileVersion data_ver, Stream *in)
+ : DataExtReader(in,
+ kDataExt_NumID8 | ((data_ver < kRoomVersion_350) ? kDataExt_File32 : kDataExt_File64))
+ , _room(room)
+ , _dataVer(data_ver) {
+ }
+
+ // Helper function that extracts legacy room script
+ HError ReadRoomScript(String &script) {
+ HError err = FindOne(kRoomFblk_Script);
+ if (!err)
+ return err;
+ char *buf = nullptr;
+ err = ReadScriptBlock(buf, _in, _dataVer);
+ script = buf;
+ delete buf;
+ return err;
+ }
+
+private:
+ HError ReadBlock(int block_id, const String &ext_id,
+ soff_t block_len, bool &read_next) override {
+ return ReadRoomBlock(_room, _in, (RoomFileBlock)block_id, ext_id, block_len, _dataVer);
+ }
+
+ RoomStruct *_room{};
+ RoomFileVersion _dataVer{};
+};
+
HRoomFileError ReadRoomData(RoomStruct *room, Stream *in, RoomFileVersion data_ver) {
room->DataVersion = data_ver;
- reader_data_ver = data_ver;
- reader_room = room;
-
- HError err = ReadExtData(ReadRoomDataReader,
- kDataExt_NumID8 | ((data_ver < kRoomVersion_350) ? kDataExt_File32 : kDataExt_File64), in);
+ RoomBlockReader reader(room, data_ver, in);
+ HError err = reader.Read();
return err ? HRoomFileError::None() :
new RoomFileError(kRoomFileErr_BlockListFailed, err);
}
@@ -627,15 +647,11 @@ HError ExtractScriptTextReader(Stream *in, int block_id,
}
HRoomFileError ExtractScriptText(String &script, Stream *in, RoomFileVersion data_ver) {
- // This reader would only process kRoomFblk_Script and exit as soon as one is found
- reader_script = &script;
- reader_ver = data_ver;
-
- HError err = ReadExtData(ExtractScriptTextReader,
- kDataExt_NumID8 | ((data_ver < kRoomVersion_350) ? kDataExt_File32 : kDataExt_File64), in);
- if (err && script.IsEmpty())
- new RoomFileError(kRoomFileErr_BlockNotFound);
- return err ? HRoomFileError::None() : new RoomFileError(kRoomFileErr_BlockListFailed, err);
+ RoomBlockReader reader(nullptr, data_ver, in);
+ HError err = reader.ReadRoomScript(script);
+ if (!err)
+ new RoomFileError(kRoomFileErr_BlockListFailed, err);
+ return HRoomFileError::None();
}
void WriteInteractionScripts(const InteractionScripts *interactions, Stream *out) {
diff --git a/engines/ags/shared/game/room_file.h b/engines/ags/shared/game/room_file.h
index a52df6a59c..4eead0f868 100644
--- a/engines/ags/shared/game/room_file.h
+++ b/engines/ags/shared/game/room_file.h
@@ -128,14 +128,7 @@ HRoomFileError WriteRoomData(const RoomStruct *room, Stream *out, RoomFileVersio
// Reads room data header using stream assigned to RoomDataSource;
// tests and saves its format index if successful
HRoomFileError ReadRoomHeader(RoomDataSource &src);
-// Opens next room block from the stream, fills in its identifier and length on success
-HRoomFileError OpenNextRoomBlock(Stream *in, RoomFileVersion data_ver, RoomFileBlock &block_id, String &ext_id, soff_t &block_len);
-// Type of function that reads single room block and tells whether to continue reading
-typedef HError(*PfnReadRoomBlock)(Stream * in, RoomFileBlock block_id, const String & ext_id,
- soff_t block_len, RoomFileVersion data_ver, bool &read_next);
-// Parses room file, passing each found block into callback; does not read any actual data itself
-HRoomFileError ReadRoomData(PfnReadRoomBlock reader, Stream *in, RoomFileVersion data_ver);
-// Type of function that writes single room block.
+
typedef void(*PfnWriteRoomBlock)(const RoomStruct *room, Stream *out);
// Writes room block with a new-style string id
void WriteRoomBlock(const RoomStruct *room, const String &ext_id, PfnWriteRoomBlock writer, Stream *out);
diff --git a/engines/ags/shared/game/room_file_base.cpp b/engines/ags/shared/game/room_file_base.cpp
index b84b4b0ed0..cf507c2c82 100644
--- a/engines/ags/shared/game/room_file_base.cpp
+++ b/engines/ags/shared/game/room_file_base.cpp
@@ -107,21 +107,6 @@ String GetRoomBlockName(RoomFileBlock id) {
return "unknown";
}
-// This reader will delegate block reading to the provided user function
-static PfnReadRoomBlock reader_reader;
-static RoomFileVersion reader_ver;
-static HError ReadRoomDataReader(Stream *in, int block_id, const String &ext_id,
- soff_t block_len, bool &read_next) {
- return reader_reader(in, (RoomFileBlock)block_id, ext_id, block_len, reader_ver, read_next);
-}
-
-HRoomFileError ReadRoomData(PfnReadRoomBlock reader, Stream *in, RoomFileVersion data_ver) {
- reader_reader = reader;
- reader_ver = data_ver;
- HError err = ReadExtData(ReadRoomDataReader,
- kDataExt_NumID8 | ((data_ver < kRoomVersion_350) ? kDataExt_File32 : kDataExt_File64), in);
- return err ? HRoomFileError::None() : new RoomFileError(kRoomFileErr_BlockListFailed, err);
-}
static PfnWriteRoomBlock writer_writer;
static const RoomStruct *writer_room;
diff --git a/engines/ags/shared/game/tra_file.cpp b/engines/ags/shared/game/tra_file.cpp
index f023c827a3..faf0bae2c9 100644
--- a/engines/ags/shared/game/tra_file.cpp
+++ b/engines/ags/shared/game/tra_file.cpp
@@ -119,27 +119,41 @@ HError ReadTraBlock(Translation &tra, Stream *in, TraFileBlock block, const Stri
String::FromFormat("Type: %s", ext_id.GetCStr()));
}
-static Translation *reader_tra;
-HError TestTraGameIDReader(Stream *in, int block_id, const String &ext_id,
- soff_t block_len, bool &read_next) {
- if (block_id == kTraFblk_GameID) {
- read_next = false;
- return ReadTraBlock(*reader_tra, in, (TraFileBlock)block_id, ext_id, block_len);
+// TRABlockReader reads whole TRA data, block by block
+class TRABlockReader : public DataExtReader {
+public:
+ TRABlockReader(Translation &tra, Stream *in)
+ : DataExtReader(in, kDataExt_NumID32 | kDataExt_File32)
+ , _tra(tra) {
}
- in->Seek(block_len); // skip block
- return HError::None();
-}
+
+ // Reads only the Game ID block and stops
+ HError ReadGameID() {
+ HError err = FindOne(kTraFblk_GameID);
+ if (!err)
+ return err;
+ return ReadTraBlock(_tra, _in, kTraFblk_GameID, "", _block_len);
+ }
+
+private:
+ HError ReadBlock(int block_id, const String &ext_id,
+ soff_t block_len, bool &read_next) override {
+ return ReadTraBlock(_tra, _in, (TraFileBlock)block_id, ext_id, block_len);
+ }
+
+ Translation &_tra;
+};
+
HError TestTraGameID(int game_uid, const String &game_name, Stream *in) {
HError err = OpenTraFile(in);
if (!err)
return err;
- // This reader would only process kTraFblk_GameID and exit as soon as one is found
Translation tra;
- reader_tra = &tra;
+ TRABlockReader reader(tra, in);
+ err = reader.ReadGameID();
- err = ReadExtData(TestTraGameIDReader, kDataExt_NumID32 | kDataExt_File32, in);
if (!err)
return err;
// Test the identifiers, if they are not present then skip the test
@@ -150,20 +164,13 @@ HError TestTraGameID(int game_uid, const String &game_name, Stream *in) {
return HError::None();
}
-// This reader will process all blocks inside ReadTraBlock() function,
-// and read compatible data into the given Translation object
-HError ReadTraDataReader(Stream *in, int block_id, const String &ext_id,
- soff_t block_len, bool &read_next) {
- return ReadTraBlock(*reader_tra, in, (TraFileBlock)block_id, ext_id, block_len);
-}
-
HError ReadTraData(Translation &tra, Stream *in) {
- reader_tra = &tra;
HError err = OpenTraFile(in);
if (!err)
return err;
- return ReadExtData(ReadTraDataReader, kDataExt_NumID32 | kDataExt_File32, in);
+ TRABlockReader reader(tra, in);
+ return reader.Read();
}
// TODO: perhaps merge with encrypt/decrypt utilities
diff --git a/engines/ags/shared/util/data_ext.cpp b/engines/ags/shared/util/data_ext.cpp
index 63a59389a7..b7c9d0a733 100644
--- a/engines/ags/shared/util/data_ext.cpp
+++ b/engines/ags/shared/util/data_ext.cpp
@@ -40,73 +40,81 @@ String GetDataExtErrorText(DataExtErrorType err) {
return "Unknown error.";
}
-HError OpenExtBlock(Stream *in, int flags, int &block_id, String &ext_id, soff_t &block_len) {
+HError DataExtParser::OpenBlock() {
// - 1 or 4 bytes - an old-style unsigned numeric ID:
// where 0 would indicate following string ID,
// and -1 indicates end of the block list.
// - 16 bytes - string ID of an extension (if numeric ID is 0).
// - 4 or 8 bytes - length of extension data, in bytes.
- block_id = ((flags & kDataExt_NumID32) != 0) ?
- in->ReadInt32() :
- in->ReadInt8();
+ _block_id = ((_flags & kDataExt_NumID32) != 0) ?
+ _in->ReadInt32() :
+ _in->ReadInt8();
- if (block_id < 0)
+ if (_block_id < 0)
return HError::None(); // end of list
- if (in->EOS())
+ if (_in->EOS())
return new DataExtError(kDataExtErr_UnexpectedEOF);
- if (block_id > 0) { // old-style block identified by a numeric id
- block_len = ((flags & kDataExt_File64) != 0) ? in->ReadInt64() : in->ReadInt32();
+ if (_block_id > 0) { // old-style block identified by a numeric id
+ _block_len = ((_flags & kDataExt_File64) != 0) ? _in->ReadInt64() : _in->ReadInt32();
+ _ext_id = GetOldBlockName(_block_id);
} else { // new style block identified by a string id
- ext_id = String::FromStreamCount(in, 16);
- block_len = in->ReadInt64();
+ _ext_id = String::FromStreamCount(_in, 16);
+ _block_len = _in->ReadInt64();
}
+ _block_start = _in->GetPosition();
return HError::None();
}
-HError ReadExtData(PfnReadExtBlock reader, int flags, Stream *in) {
- while (!in->EOS()) {
- // First try open the next block
- int block_id;
- String ext_id;
- soff_t block_len;
- HError err = OpenExtBlock(in, flags, block_id, ext_id, block_len);
- if (!err)
- return err;
- if (block_id < 0)
- break; // end of list
- if (ext_id.IsEmpty()) // we may need some name for the messages
- ext_id.Format("id:%d", block_id);
-
- // Now call the reader function to read current block's data
- soff_t block_end = in->GetPosition() + block_len;
- bool read_next = true;
- err = reader(in, block_id, ext_id, block_len, read_next);
- if (!err)
- return err;
+void DataExtParser::SkipBlock() {
+ if (_block_id >= 0)
+ _in->Seek(_block_len);
+}
- soff_t cur_pos = in->GetPosition();
+HError DataExtParser::PostAssert() {
+ const soff_t cur_pos = _in->GetPosition();
+ const soff_t block_end = _block_start + _block_len;
+ if (cur_pos > block_end) {
+ return new DataExtError(kDataExtErr_BlockDataOverlapping,
+ String::FromFormat("Block: '%s', expected to end at offset: %lld, finished reading at %lld.",
+ _ext_id.GetCStr(), block_end, cur_pos));
+ } else if (cur_pos < block_end) {
+ Debug::Printf(kDbgMsg_Warn, "WARNING: data blocks nonsequential, block '%s' expected to end at %lld, finished reading at %lld",
+ _ext_id.GetCStr(), block_end, cur_pos);
+ _in->Seek(block_end, Shared::kSeekBegin);
+ }
+ return HError::None();
+}
- // WORKAROUND: For at least the MMM games, the translation
- // files' first block length is incorrect by one byte
- if (cur_pos == (block_end + 1) && cur_pos < 100)
- cur_pos = block_end;
+HError DataExtParser::FindOne(int id) {
+ if (id <= 0) return new DataExtError(kDataExtErr_BlockNotFound);
- // Finally test that we did not read too much or too little
- if (cur_pos > block_end) {
- return new DataExtError(kDataExtErr_BlockDataOverlapping,
- String::FromFormat("Block: '%s', expected to end at offset: %lld, finished reading at %lld.",
- ext_id.GetCStr(), block_end, cur_pos));
- } else if (cur_pos < block_end) {
- Debug::Printf(kDbgMsg_Warn, "WARNING: room data blocks nonsequential, block '%s' expected to end at %lld, finished reading at %lld",
- ext_id.GetCStr(), block_end, cur_pos);
- in->Seek(block_end, Shared::kSeekBegin);
- }
+ HError err = HError::None();
+ for (err = OpenBlock(); err && !AtEnd(); err = OpenBlock()) {
+ if (id == _block_id)
+ return HError::None();
+ _in->Seek(_block_len); // skip it
+ }
+ if (!err)
+ return err;
+ return new DataExtError(kDataExtErr_BlockNotFound);
+}
- if (!read_next)
- break; // reader requested a stop, do so
+HError DataExtReader::Read() {
+ HError err = HError::None();
+ bool read_next = true;
+ for (err = OpenBlock(); err && !AtEnd() && read_next; err = OpenBlock()) {
+ // Call the reader function to read current block's data
+ read_next = true;
+ err = ReadBlock(_block_id, _ext_id, _block_len, read_next);
+ if (!err)
+ return err;
+ // Test that we did not read too much or too little
+ err = PostAssert();
+ if (!err)
+ return err;
}
- return HError::None();
+ return err;
}
// Generic function that saves a block and automatically adds its size into header
diff --git a/engines/ags/shared/util/data_ext.h b/engines/ags/shared/util/data_ext.h
index 7545273c2f..0a1c3517e6 100644
--- a/engines/ags/shared/util/data_ext.h
+++ b/engines/ags/shared/util/data_ext.h
@@ -77,6 +77,7 @@ enum DataExtFlags {
enum DataExtErrorType {
kDataExtErr_NoError,
kDataExtErr_UnexpectedEOF,
+ kDataExtErr_BlockNotFound,
kDataExtErr_BlockDataOverlapping
};
@@ -84,14 +85,69 @@ String GetDataExtErrorText(DataExtErrorType err);
typedef TypedCodeError<DataExtErrorType, GetDataExtErrorText> DataExtError;
-// Tries to opens a next block from the stream, fills in identifier and length on success
-HError OpenExtBlock(Stream *in, int flags, int &block_id, String &ext_id, soff_t &block_len);
-// Type of function that reads a single data block and tells whether to continue reading
-typedef HError(*PfnReadExtBlock)(Stream *in, int block_id, const String &ext_id,
- soff_t block_len, bool &read_next);
-// Parses stream as a block list, passing each found block into callback;
-// does not read any actual data itself
-HError ReadExtData(PfnReadExtBlock reader, int flags, Stream *in);
+// DataExtReader parses a generic extendable block list and
+// does format checks, but does not read any data itself.
+// Use it to open blocks, and assert reading correctness.
+class DataExtParser {
+public:
+ DataExtParser(Stream *in, int flags) : _in(in), _flags(flags) {
+ }
+ virtual ~DataExtParser() = default;
+
+ // Returns the conventional string ID for an old-style block with numeric ID
+ virtual String GetOldBlockName(int blockId) const {
+ return String::FromFormat("id:%d", blockId);
+ }
+
+ // Gets a stream
+ inline Stream *GetStream() {
+ return _in;
+ }
+ // Tells if the end of the block list was reached
+ inline bool AtEnd() const {
+ return _block_id < 0;
+ }
+ // Tries to opens a next standard block from the stream,
+ // fills in identifier and length on success
+ HError OpenBlock();
+ // Skips current block
+ void SkipBlock();
+ // Asserts current stream position after a block was read
+ HError PostAssert();
+ // Parses a block list in search for a particular block,
+ // if found opens it.
+ HError FindOne(int id);
+
+protected:
+ Stream *_in = nullptr;
+ int _flags = 0;
+
+ int _block_id = -1;
+ String _ext_id;
+ soff_t _block_start = 0;
+ soff_t _block_len = 0;
+};
+
+// DataExtReader is a virtual base class of a block list reader; provides
+// a helper method for reading all the blocks one by one, but leaves data
+// reading for the child classes. A child class must override ReadBlock method.
+// TODO: don't extend Parser, but have it as a member?
+class DataExtReader : protected DataExtParser {
+public:
+ virtual ~DataExtReader() = default;
+
+ // Parses a block list, calls ReadBlock for each found block
+ HError Read();
+
+protected:
+ DataExtReader(Stream *in, int flags) : DataExtParser(in, flags) {
+ }
+ // Reads a single data block and tell whether to continue reading;
+ // default implementation skips the block
+ virtual HError ReadBlock(int block_id, const String &ext_id,
+ soff_t block_len, bool &read_next) = 0;
+};
+
// Type of function that writes a single data block.
typedef void(*PfnWriteExtBlock)(Stream *out);
Commit: ddf30d3234b3da6889a9b0c76da2a43e2713ba46
https://github.com/scummvm/scummvm/commit/ddf30d3234b3da6889a9b0c76da2a43e2713ba46
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2021-07-10T18:56:58-07:00
Commit Message:
AGS: implement GetOldBlockName() in RoomBlockReader, TRABlockReader
>From upstream 838605f7689febdf8174083d9dace140a6e07e19
Changed paths:
engines/ags/shared/game/room_file.cpp
engines/ags/shared/game/tra_file.cpp
diff --git a/engines/ags/shared/game/room_file.cpp b/engines/ags/shared/game/room_file.cpp
index a663bb9b5d..0091f62b03 100644
--- a/engines/ags/shared/game/room_file.cpp
+++ b/engines/ags/shared/game/room_file.cpp
@@ -479,6 +479,10 @@ public:
}
private:
+ String GetOldBlockName(int block_id) const override {
+ return GetRoomBlockName((RoomFileBlock)block_id);
+ }
+
HError ReadBlock(int block_id, const String &ext_id,
soff_t block_len, bool &read_next) override {
return ReadRoomBlock(_room, _in, (RoomFileBlock)block_id, ext_id, block_len, _dataVer);
diff --git a/engines/ags/shared/game/tra_file.cpp b/engines/ags/shared/game/tra_file.cpp
index faf0bae2c9..f2a863cb2e 100644
--- a/engines/ags/shared/game/tra_file.cpp
+++ b/engines/ags/shared/game/tra_file.cpp
@@ -136,6 +136,10 @@ public:
}
private:
+ String GetOldBlockName(int block_id) const override {
+ return GetTraBlockName((TraFileBlock)block_id);
+ }
+
HError ReadBlock(int block_id, const String &ext_id,
soff_t block_len, bool &read_next) override {
return ReadTraBlock(_tra, _in, (TraFileBlock)block_id, ext_id, block_len);
Commit: 7b42f9c35087f0a16e423d62483130e3aaebcb09
https://github.com/scummvm/scummvm/commit/7b42f9c35087f0a16e423d62483130e3aaebcb09
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2021-07-10T18:56:58-07:00
Commit Message:
AGS: Optional block over-read leeway, for TRABlockReader
>From upstream 92c0844519653507ec979037d46a58a60ea93032
Changed paths:
engines/ags/shared/game/tra_file.cpp
engines/ags/shared/util/data_ext.cpp
engines/ags/shared/util/data_ext.h
diff --git a/engines/ags/shared/game/tra_file.cpp b/engines/ags/shared/game/tra_file.cpp
index f2a863cb2e..fe42175685 100644
--- a/engines/ags/shared/game/tra_file.cpp
+++ b/engines/ags/shared/game/tra_file.cpp
@@ -140,6 +140,12 @@ private:
return GetTraBlockName((TraFileBlock)block_id);
}
+ soff_t GetOverLeeway(int block_id) const override {
+ // TRA files made by pre-3.0 editors have a block length miscount by 1 byte
+ if (block_id == kTraFblk_GameID) return 1;
+ return 0;
+ }
+
HError ReadBlock(int block_id, const String &ext_id,
soff_t block_len, bool &read_next) override {
return ReadTraBlock(_tra, _in, (TraFileBlock)block_id, ext_id, block_len);
diff --git a/engines/ags/shared/util/data_ext.cpp b/engines/ags/shared/util/data_ext.cpp
index b7c9d0a733..f7115aa591 100644
--- a/engines/ags/shared/util/data_ext.cpp
+++ b/engines/ags/shared/util/data_ext.cpp
@@ -75,9 +75,12 @@ HError DataExtParser::PostAssert() {
const soff_t cur_pos = _in->GetPosition();
const soff_t block_end = _block_start + _block_len;
if (cur_pos > block_end) {
- return new DataExtError(kDataExtErr_BlockDataOverlapping,
- String::FromFormat("Block: '%s', expected to end at offset: %lld, finished reading at %lld.",
- _ext_id.GetCStr(), block_end, cur_pos));
+ String err = String::FromFormat("Block: '%s', expected to end at offset: %lld, finished reading at %lld.",
+ _ext_id.GetCStr(), block_end, cur_pos);
+ if (cur_pos <= block_end + GetOverLeeway(_block_id))
+ Debug::Printf(kDbgMsg_Warn, err);
+ else
+ return new DataExtError(kDataExtErr_BlockDataOverlapping, err);
} else if (cur_pos < block_end) {
Debug::Printf(kDbgMsg_Warn, "WARNING: data blocks nonsequential, block '%s' expected to end at %lld, finished reading at %lld",
_ext_id.GetCStr(), block_end, cur_pos);
diff --git a/engines/ags/shared/util/data_ext.h b/engines/ags/shared/util/data_ext.h
index 0a1c3517e6..5db288558f 100644
--- a/engines/ags/shared/util/data_ext.h
+++ b/engines/ags/shared/util/data_ext.h
@@ -99,6 +99,12 @@ public:
return String::FromFormat("id:%d", blockId);
}
+ // Provides a leeway for over-reading (reading past the reported block length):
+ // the parser will not error if the mistake is in this range of bytes
+ virtual soff_t GetOverLeeway(int block_id) const {
+ return 0;
+ }
+
// Gets a stream
inline Stream *GetStream() {
return _in;
Commit: f19dd1c506e40b5d86d1163357070c4a0bce1497
https://github.com/scummvm/scummvm/commit/f19dd1c506e40b5d86d1163357070c4a0bce1497
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2021-07-10T18:56:58-07:00
Commit Message:
AGS: few cast fixes
>From upstream 008b2d2c61420f6f2c996317a0eb56a49e708095
Changed paths:
engines/ags/engine/ac/timer.cpp
engines/ags/shared/util/geometry.cpp
engines/ags/shared/util/string.cpp
diff --git a/engines/ags/engine/ac/timer.cpp b/engines/ags/engine/ac/timer.cpp
index a7c8d889c6..cd3b1a9e5e 100644
--- a/engines/ags/engine/ac/timer.cpp
+++ b/engines/ags/engine/ac/timer.cpp
@@ -61,7 +61,7 @@ void WaitForNextFrame() {
if (frameDuration <= std::chrono::milliseconds::zero()) {
_G(next_frame_timestamp) = now;
// suspend while the game is being switched out
- while (_G(game_update_suspend) > 0) {
+ while (_G(game_update_suspend)) {
sys_evt_process_pending();
_G(platform)->YieldCPU();
}
@@ -81,7 +81,7 @@ void WaitForNextFrame() {
_G(next_frame_timestamp) += frameDuration;
// suspend while the game is being switched out
- while (_G(game_update_suspend) > 0) {
+ while (_G(game_update_suspend)) {
sys_evt_process_pending();
_G(platform)->YieldCPU();
}
diff --git a/engines/ags/shared/util/geometry.cpp b/engines/ags/shared/util/geometry.cpp
index f38492667a..93baef1a46 100644
--- a/engines/ags/shared/util/geometry.cpp
+++ b/engines/ags/shared/util/geometry.cpp
@@ -50,7 +50,7 @@ float DistanceBetween(const Rect &r1, const Rect &r2) {
);
int inner_width = std::max(0, rect_outer.GetWidth() - r1.GetWidth() - r2.GetWidth());
int inner_height = std::max(0, rect_outer.GetHeight() - r1.GetHeight() - r2.GetHeight());
- return std::sqrt((inner_width ^ 2) + (inner_height ^ 2));
+ return static_cast<float>(std::sqrt((inner_width ^ 2) + (inner_height ^ 2)));
}
Size ProportionalStretch(int dest_w, int dest_h, int item_w, int item_h) {
diff --git a/engines/ags/shared/util/string.cpp b/engines/ags/shared/util/string.cpp
index d79c28e00b..93c1d6ec70 100644
--- a/engines/ags/shared/util/string.cpp
+++ b/engines/ags/shared/util/string.cpp
@@ -432,8 +432,8 @@ void String::Append(const char *cstr, size_t len) {
return;
// Test for null-terminator in the range
const char *ptr = cstr;
- for (; *ptr && (size_t)(ptr - cstr) < len; ++ptr);
- if ((size_t)(ptr - cstr) < len)
+ for (; *ptr && (static_cast<size_t>(ptr - cstr) < len); ++ptr);
+ if (static_cast<size_t>(ptr - cstr) < len)
len = ptr - cstr;
ReserveAndShift(false, len);
memcpy(_cstr + _len, cstr, len);
Commit: f5a019d3b4b82e5ebd3113020287e5f3d4a21005
https://github.com/scummvm/scummvm/commit/f5a019d3b4b82e5ebd3113020287e5f3d4a21005
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2021-07-10T18:56:58-07:00
Commit Message:
AGS: corrected GetDisplayDepthForNativeDepth
>From upstream 4c51ca2045b478b63f5e6b35be02fdce3ebf9b18
Changed paths:
engines/ags/engine/gfx/ali_3d_scummvm.cpp
diff --git a/engines/ags/engine/gfx/ali_3d_scummvm.cpp b/engines/ags/engine/gfx/ali_3d_scummvm.cpp
index 5bdd5c8db4..b7dc3ee8ce 100644
--- a/engines/ags/engine/gfx/ali_3d_scummvm.cpp
+++ b/engines/ags/engine/gfx/ali_3d_scummvm.cpp
@@ -69,10 +69,7 @@ bool ScummVMRendererGraphicsDriver::IsModeSupported(const DisplayMode &mode) {
}
int ScummVMRendererGraphicsDriver::GetDisplayDepthForNativeDepth(int native_color_depth) const {
- // TODO: check for device caps to know which depth is supported?
- if (native_color_depth > 8)
- return 32;
- return native_color_depth;
+ return 32;
}
IGfxModeList *ScummVMRendererGraphicsDriver::GetSupportedModeList(int color_depth) {
Commit: f6e5133edd5f3bec4fddb4727bcbbc086b530fa9
https://github.com/scummvm/scummvm/commit/f6e5133edd5f3bec4fddb4727bcbbc086b530fa9
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2021-07-10T18:56:58-07:00
Commit Message:
AGS: In ci_find_file remove gratuitous chdir() code
>From upstream bc4c4ad1a8374e9890e8c44bf560548700bce311
Changed paths:
engines/ags/shared/util/misc.cpp
diff --git a/engines/ags/shared/util/misc.cpp b/engines/ags/shared/util/misc.cpp
index 90016ec2cc..a1e1a97ec2 100644
--- a/engines/ags/shared/util/misc.cpp
+++ b/engines/ags/shared/util/misc.cpp
@@ -88,7 +88,6 @@ char *ci_find_file(const char *dir_name, const char *file_name) {
struct stat statbuf;
struct dirent *entry = nullptr;
DIR *rough = nullptr;
- DIR *prevdir = nullptr;
char *diamond = nullptr;
char *directory = nullptr;
char *filename = nullptr;
@@ -158,19 +157,9 @@ char *ci_find_file(const char *dir_name, const char *file_name) {
filename[match_len] = '\0';
}
- if ((prevdir = opendir(".")) == nullptr) {
- fprintf(stderr, "ci_find_file: cannot open current working directory\n");
- goto out;
- }
-
- if (chdir(directory) == -1) {
- fprintf(stderr, "ci_find_file: cannot change to directory: %s\n", directory);
- goto out_pd;
- }
-
if ((rough = opendir(directory)) == nullptr) {
fprintf(stderr, "ci_find_file: cannot open directory: %s\n", directory);
- goto out_pd;
+ goto out;
}
while ((entry = readdir(rough)) != nullptr) {
@@ -188,10 +177,6 @@ char *ci_find_file(const char *dir_name, const char *file_name) {
}
closedir(rough);
-out_pd:;
- fchdir(dirfd(prevdir));
- closedir(prevdir);
-
out:;
if (directory) free(directory);
if (filename) free(filename);
Commit: d9bbf9775cda4442e15b038a5573d5af3c6696d9
https://github.com/scummvm/scummvm/commit/d9bbf9775cda4442e15b038a5573d5af3c6696d9
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2021-07-10T18:56:59-07:00
Commit Message:
AGS: Rewrote ci_find_file() into File::FindFileCI()
>From upstream 4e2df949ff36bbb6d08e402586dc8c2bb02ecad9
Changed paths:
engines/ags/ags.h
engines/ags/engine/ac/file.cpp
engines/ags/engine/ac/translation.cpp
engines/ags/engine/main/engine.cpp
engines/ags/engine/main/main_header.h
engines/ags/engine/script/cc_instance.cpp
engines/ags/shared/core/asset_manager.cpp
engines/ags/shared/util/compress.cpp
engines/ags/shared/util/file.cpp
engines/ags/shared/util/file.h
engines/ags/shared/util/string_utils.cpp
engines/ags/shared/util/string_utils.h
diff --git a/engines/ags/ags.h b/engines/ags/ags.h
index 8be4ede7fd..7cfd80ecf5 100644
--- a/engines/ags/ags.h
+++ b/engines/ags/ags.h
@@ -49,8 +49,8 @@ namespace AGS {
* @brief Engine to run Adventure Game Studio games.
*/
-/* Synced up to upstream: AGS 3.5.1.8
- * 811f6c126fcff1cc5a0db8b6e0a1b9fca2550235
+/* Synced up to upstream:
+ * 4e2df949ff36bbb6d08e402586dc8c2bb02ecad9
*/
#define SCREEN_WIDTH 320
#define SCREEN_HEIGHT 200
diff --git a/engines/ags/engine/ac/file.cpp b/engines/ags/engine/ac/file.cpp
index acbe86d028..7f667bd5bd 100644
--- a/engines/ags/engine/ac/file.cpp
+++ b/engines/ags/engine/ac/file.cpp
@@ -34,7 +34,6 @@
#include "ags/engine/ac/string.h"
#include "ags/engine/debugging/debug_log.h"
#include "ags/engine/debugging/debugger.h"
-#include "ags/shared/util/misc.h"
#include "ags/engine/platform/base/ags_platform_driver.h"
#include "ags/shared/util/stream.h"
#include "ags/shared/core/asset_manager.h"
@@ -477,12 +476,12 @@ bool DoesAssetExistInLib(const AssetPath &path) {
}
String find_assetlib(const String &filename) {
- String libname = cbuf_to_string_and_free(ci_find_file(_GP(ResPaths).DataDir.GetCStr(), filename.GetCStr()));
+ String libname = File::FindFileCI(_GP(ResPaths).DataDir, filename);
if (AssetManager::IsDataFile(libname))
return libname;
if (Path::ComparePaths(_GP(ResPaths).DataDir, _GP(ResPaths).DataDir2) != 0) {
// Hack for running in Debugger
- libname = cbuf_to_string_and_free(ci_find_file(_GP(ResPaths).DataDir2.GetCStr(), filename.GetCStr()));
+ libname = File::FindFileCI(_GP(ResPaths).DataDir2, filename);
if (AssetManager::IsDataFile(libname))
return libname;
}
diff --git a/engines/ags/engine/ac/translation.cpp b/engines/ags/engine/ac/translation.cpp
index 7cd27a2bc8..26e74c3bb5 100644
--- a/engines/ags/engine/ac/translation.cpp
+++ b/engines/ags/engine/ac/translation.cpp
@@ -32,7 +32,6 @@
#include "ags/shared/ac/words_dictionary.h"
#include "ags/shared/debugging/out.h"
#include "ags/shared/game/tra_file.h"
-#include "ags/shared/util/misc.h"
#include "ags/shared/util/stream.h"
#include "ags/shared/core/asset_manager.h"
#include "ags/globals.h"
diff --git a/engines/ags/engine/main/engine.cpp b/engines/ags/engine/main/engine.cpp
index 0130c3c9ca..890efcc5cd 100644
--- a/engines/ags/engine/main/engine.cpp
+++ b/engines/ags/engine/main/engine.cpp
@@ -78,10 +78,7 @@
#include "ags/engine/platform/base/ags_platform_driver.h"
#include "ags/shared/util/directory.h"
#include "ags/shared/util/error.h"
-#include "ags/shared/util/misc.h"
#include "ags/shared/util/path.h"
-//#include "ags/engine/media/audio/audio_core.h"
-//#include "ags/engine/platform/util/pe.h"
#include "ags/ags.h"
#include "ags/globals.h"
diff --git a/engines/ags/engine/main/main_header.h b/engines/ags/engine/main/main_header.h
index c262d725d6..074fcb91f0 100644
--- a/engines/ags/engine/main/main_header.h
+++ b/engines/ags/engine/main/main_header.h
@@ -33,7 +33,6 @@
#include "ags/shared/util/string_utils.h"
#include "ags/engine/device/mouse_w32.h"
#include "ags/engine/ac/route_finder.h"
-#include "ags/shared/util/misc.h"
#include "ags/shared/script/cc_error.h"
// include last since we affect windows includes
diff --git a/engines/ags/engine/script/cc_instance.cpp b/engines/ags/engine/script/cc_instance.cpp
index 7cd8573092..29ef35b16c 100644
--- a/engines/ags/engine/script/cc_instance.cpp
+++ b/engines/ags/engine/script/cc_instance.cpp
@@ -20,8 +20,6 @@
*
*/
-//include <cstdio>
-//include <string.h>
#include "ags/shared/ac/common.h"
#include "ags/engine/ac/dynobj/cc_dynamic_array.h"
#include "ags/engine/ac/dynobj/managed_object_pool.h"
@@ -35,8 +33,8 @@
#include "ags/engine/script/script_runtime.h"
#include "ags/engine/script/system_imports.h"
#include "ags/shared/util/bbop.h"
+#include "ags/shared/util/file.h"
#include "ags/shared/util/stream.h"
-#include "ags/shared/util/misc.h"
#include "ags/shared/util/text_stream_writer.h"
#include "ags/engine/ac/dynobj/script_string.h"
#include "ags/engine/ac/dynobj/script_user_object.h"
@@ -1247,7 +1245,7 @@ void ccInstance::DumpInstruction(const ScriptOperation &op) {
return;
}
- Stream *data_s = ci_fopen("script.log", kFile_Create, kFile_Write);
+ Stream *data_s = File::OpenFileCI("script.log", kFile_Create, kFile_Write);
TextStreamWriter writer(data_s);
writer.WriteFormat("Line %3d, IP:%8d (SP:%p) ", line_num, pc, registers[SREG_SP].RValue);
diff --git a/engines/ags/shared/core/asset_manager.cpp b/engines/ags/shared/core/asset_manager.cpp
index 3df043d1e4..7f3a874326 100644
--- a/engines/ags/shared/core/asset_manager.cpp
+++ b/engines/ags/shared/core/asset_manager.cpp
@@ -25,7 +25,6 @@
#include "ags/lib/std/utility.h"
#include "ags/shared/core/platform.h"
#include "ags/shared/core/asset_manager.h"
-#include "ags/shared/util/misc.h" // ci_fopen
#include "ags/shared/util/multi_file_lib.h"
#include "ags/shared/util/path.h"
#include "ags/shared/util/string_utils.h" // cbuf_to_string_and_free
@@ -61,7 +60,7 @@ bool AssetManager::LibsByPriority::operator()(const AssetLibInfo *lib1, const As
/* static */ bool AssetManager::IsDataFile(const String &data_file) {
- Stream *in = ci_fopen(data_file.GetCStr(), Shared::kFile_Open, Shared::kFile_Read);
+ Stream *in = File::OpenFileCI(data_file.GetCStr(), Shared::kFile_Open, Shared::kFile_Read);
if (in) {
MFLUtil::MFLError err = MFLUtil::TestIsMFL(in, true);
delete in;
@@ -71,7 +70,7 @@ bool AssetManager::LibsByPriority::operator()(const AssetLibInfo *lib1, const As
}
/* static */ AssetError AssetManager::ReadDataFileTOC(const String &data_file, AssetLibInfo &lib) {
- Stream *in = ci_fopen(data_file.GetCStr(), Shared::kFile_Open, Shared::kFile_Read);
+ Stream *in = File::OpenFileCI(data_file.GetCStr(), Shared::kFile_Open, Shared::kFile_Read);
if (in) {
MFLUtil::MFLError err = MFLUtil::ReadHeader(lib, in);
delete in;
@@ -178,7 +177,7 @@ AssetError AssetManager::RegisterAssetLib(const String &path, AssetLibEx *&out_l
}
// ...else try open a data library
else {
- Stream *in = ci_fopen(path.GetCStr(), Shared::kFile_Open, Shared::kFile_Read);
+ Stream *in = File::OpenFileCI(path.GetCStr(), Shared::kFile_Open, Shared::kFile_Read);
if (!in)
return kAssetErrNoLibFile; // can't be opened, return error code
@@ -234,7 +233,7 @@ bool AssetManager::GetAssetFromLib(const AssetLibInfo *lib, const String &asset_
if (asset == nullptr)
return false;
- String libfile = cbuf_to_string_and_free(ci_find_file(lib->BaseDir.GetCStr(), lib->LibFileNames[asset->LibUid].GetCStr()));
+ String libfile = File::FindFileCI(lib->BaseDir, lib->LibFileNames[asset->LibUid]);
if (libfile.IsEmpty())
return false;
if (loc) {
@@ -247,7 +246,7 @@ bool AssetManager::GetAssetFromLib(const AssetLibInfo *lib, const String &asset_
bool AssetManager::GetAssetFromDir(const AssetLibInfo *lib, const String &file_name,
AssetLocation *loc, FileOpenMode open_mode, FileWorkMode work_mode) const {
- String found_file = cbuf_to_string_and_free(ci_find_file(lib->BaseDir.GetCStr(), file_name.GetCStr()));
+ String found_file = File::FindFileCI(lib->BaseDir, file_name);
if (found_file.IsEmpty() || !Path::IsFile(found_file))
return false; // not found, or not a file
diff --git a/engines/ags/shared/util/compress.cpp b/engines/ags/shared/util/compress.cpp
index a58d2dc0b0..d65320b9cf 100644
--- a/engines/ags/shared/util/compress.cpp
+++ b/engines/ags/shared/util/compress.cpp
@@ -28,8 +28,8 @@
#include "ags/shared/ac/common.h" // quit, update_polled_stuff
#include "ags/shared/gfx/bitmap.h"
#include "ags/shared/util/compress.h"
+#include "ags/shared/util/file.h"
#include "ags/shared/util/lzw.h"
-#include "ags/shared/util/misc.h"
#include "ags/shared/util/stream.h"
#include "ags/globals.h"
#if AGS_PLATFORM_ENDIAN_BIG
@@ -326,14 +326,14 @@ const char *lztempfnm = "~aclzw.tmp";
void save_lzw(Stream *out, const Bitmap *bmpp, const RGB *pall) {
// First write original bitmap into temporary file
- Stream *lz_temp_s = ci_fopen(lztempfnm, kFile_CreateAlways, kFile_Write);
+ Stream *lz_temp_s = File::OpenFileCI(lztempfnm, kFile_CreateAlways, kFile_Write);
lz_temp_s->WriteInt32(bmpp->GetWidth() * bmpp->GetBPP());
lz_temp_s->WriteInt32(bmpp->GetHeight());
lz_temp_s->WriteArray(bmpp->GetData(), bmpp->GetLineLength(), bmpp->GetHeight());
delete lz_temp_s;
// Now open same file for reading, and begin writing compressed data into required output stream
- lz_temp_s = ci_fopen(lztempfnm);
+ lz_temp_s = File::OpenFileCI(lztempfnm);
soff_t temp_sz = lz_temp_s->GetLength();
out->WriteArray(&pall[0], sizeof(RGB), 256);
out->WriteInt32(temp_sz);
diff --git a/engines/ags/shared/util/file.cpp b/engines/ags/shared/util/file.cpp
index b6cf4598e2..15189f208c 100644
--- a/engines/ags/shared/util/file.cpp
+++ b/engines/ags/shared/util/file.cpp
@@ -21,11 +21,12 @@
*/
#include "ags/shared/core/platform.h"
-#include "ags/shared/util/stdio_compat.h"
-#include "ags/shared/util/file_stream.h"
#include "ags/shared/util/buffered_stream.h"
-#include "ags/shared/util/file.h"
#include "ags/shared/util/directory.h"
+#include "ags/shared/util/file.h"
+#include "ags/shared/util/file_stream.h"
+#include "ags/shared/util/path.h"
+#include "ags/shared/util/stdio_compat.h"
#include "common/file.h"
#include "common/savefile.h"
#include "common/system.h"
@@ -146,6 +147,96 @@ Stream *File::OpenFile(const String &filename, FileOpenMode open_mode, FileWorkM
return fs;
}
+String File::FindFileCI(const String &dir_name, const String &file_name) {
+#if !defined (AGS_CASE_SENSITIVE_FILESYSTEM)
+ // Simply concat dir and filename paths
+ return Path::ConcatPaths(dir_name, file_name);
+#else
+ // Case insensitive file find - on case sensitive filesystems
+ //
+ // TODO: still not covered: a situation when the file_name contains
+ // nested path -and- the case of at least one path parts does not match
+ // (with all matching case the file will be found by an early check).
+ //
+ struct stat statbuf;
+ struct dirent *entry = nullptr;
+
+ if (dir_name.IsEmpty() && file_name.IsEmpty())
+ return nullptr;
+
+ String directory;
+ String filename;
+ String buf;
+
+ if (!dir_name.IsEmpty()) {
+ directory = dir_name;
+ Path::FixupPath(directory);
+ }
+ if (!file_name.IsEmpty()) {
+ filename = file_name;
+ Path::FixupPath(filename);
+ }
+
+ if (!filename.IsEmpty()) {
+ // TODO: move this case to ConcatPaths too?
+ if (directory.IsEmpty() && filename[0] == '/')
+ buf = filename;
+ else
+ buf = Path::ConcatPaths(directory.IsEmpty() ? "." : directory, filename);
+
+ if (lstat(buf.GetCStr(), &statbuf) == 0 &&
+ (S_ISREG(statbuf.st_mode) || S_ISLNK(statbuf.st_mode))) {
+ return buf;
+ }
+ }
+
+ if (directory.IsEmpty()) {
+ String match = Path::GetFilename(filename);
+ if (match.IsEmpty())
+ return nullptr;
+ directory = Path::GetParent(filename);
+ filename = match;
+ }
+
+ DIR *rough = nullptr;
+ if ((rough = opendir(directory.GetCStr())) == nullptr) {
+ fprintf(stderr, "ci_find_file: cannot open directory: %s\n", directory.GetCStr());
+ return nullptr;
+ }
+
+ String diamond;
+ while ((entry = readdir(rough)) != nullptr) {
+ if (strcasecmp(filename.GetCStr(), entry->d_name) == 0) {
+ if (lstat(entry->d_name, &statbuf) == 0 &&
+ (S_ISREG(statbuf.st_mode) || S_ISLNK(statbuf.st_mode))) {
+#if AGS_PLATFORM_DEBUG
+ fprintf(stderr, "ci_find_file: Looked for %s in rough %s, found diamond %s.\n",
+ filename.GetCStr(), directory.GetCStr(), entry->d_name);
+#endif // AGS_PLATFORM_DEBUG
+ diamond = Path::ConcatPaths(directory, entry->d_name);
+ break;
+ }
+ }
+ }
+ closedir(rough);
+ return diamond;
+#endif
+}
+
+Stream *File::OpenFileCI(const String &file_name, FileOpenMode open_mode, FileWorkMode work_mode) {
+#if !defined (AGS_CASE_SENSITIVE_FILESYSTEM)
+ return File::OpenFile(file_name, open_mode, work_mode);
+#else
+ String fullpath = FindFileCI(nullptr, file_name);
+ if (!fullpath.IsEmpty())
+ return File::OpenFile(fullpath, open_mode, work_mode);
+ // If the file was not found, and it's Create mode, then open new file
+ if (open_mode != kFile_Open)
+ return File::OpenFile(file_name, open_mode, work_mode);
+ return nullptr;
+#endif
+}
+
} // namespace Shared
} // namespace AGS
} // namespace AGS3
diff --git a/engines/ags/shared/util/file.h b/engines/ags/shared/util/file.h
index e858b75f78..e2442f96ce 100644
--- a/engines/ags/shared/util/file.h
+++ b/engines/ags/shared/util/file.h
@@ -82,6 +82,14 @@ inline Stream *OpenFileRead(const String &filename) {
inline Stream *OpenFileWrite(const String &filename) {
return OpenFile(filename, kFile_Create, kFile_Write);
}
+
+// Case insensitive find file
+String FindFileCI(const String &dir_name, const String &file_name);
+// Case insensitive file open: looks up for the file using FindFileCI
+Stream *OpenFileCI(const String &file_name,
+ FileOpenMode open_mode = kFile_Open,
+ FileWorkMode work_mode = kFile_Read);
+
} // namespace File
} // namespace Shared
diff --git a/engines/ags/shared/util/string_utils.cpp b/engines/ags/shared/util/string_utils.cpp
index 134a1845cf..e304a1ed4a 100644
--- a/engines/ags/shared/util/string_utils.cpp
+++ b/engines/ags/shared/util/string_utils.cpp
@@ -30,13 +30,6 @@ namespace AGS3 {
using namespace AGS::Shared;
-String cbuf_to_string_and_free(char *char_buf) {
- String s = char_buf;
- free(char_buf);
- return s;
-}
-
-
namespace AGS {
namespace Shared {
diff --git a/engines/ags/shared/util/string_utils.h b/engines/ags/shared/util/string_utils.h
index 6980aac684..f90d88bc5c 100644
--- a/engines/ags/shared/util/string_utils.h
+++ b/engines/ags/shared/util/string_utils.h
@@ -37,10 +37,6 @@ using namespace AGS; // FIXME later
//=============================================================================
-// Converts char* to string and frees original malloc-ed array;
-// This is used when we get a malloc'd char array from some utility function.
-Shared::String cbuf_to_string_and_free(char *char_buf);
-
namespace AGS {
namespace Shared {
namespace StrUtil {
More information about the Scummvm-git-logs
mailing list