mirror of
https://github.com/ZDoom/gzdoom.git
synced 2024-11-10 14:51:40 +00:00
- Added directory detection to the -file parameter. This obsoletes -dir, so
that parameter is now gone. - Removed automatic ".wad" appending from FWadCollection::InitMultipleFiles() since it isn't needed and prevented files without extensions from being loaded. D_AddFile() already takes care of adding the extension if the name as-given does not exist. - Fixed: Loading single files did not print a newline in the startup text. SVN r1784 (trunk)
This commit is contained in:
parent
6c6ce41b7e
commit
c24c31cc9f
7 changed files with 191 additions and 52 deletions
|
@ -1,4 +1,11 @@
|
||||||
September 1, 2009
|
September 1, 2009
|
||||||
|
- Added directory detection to the -file parameter. This obsoletes -dir, so
|
||||||
|
that parameter is now gone.
|
||||||
|
- Removed automatic ".wad" appending from FWadCollection::InitMultipleFiles()
|
||||||
|
since it isn't needed and prevented files without extensions from being
|
||||||
|
loaded. D_AddFile() already takes care of adding the extension if the
|
||||||
|
name as-given does not exist.
|
||||||
|
- Fixed: Loading single files did not print a newline in the startup text.
|
||||||
- Fixed: A_JumpIf(InTarget)Inventory jumped if the check amount was greater
|
- Fixed: A_JumpIf(InTarget)Inventory jumped if the check amount was greater
|
||||||
than the item's max amount and the item was maxed.
|
than the item's max amount and the item was maxed.
|
||||||
- Fixed: Some dmadds wads used zero-length sprites as placeholders. When you
|
- Fixed: Some dmadds wads used zero-length sprites as placeholders. When you
|
||||||
|
|
179
src/cmdlib.cpp
179
src/cmdlib.cpp
|
@ -28,6 +28,14 @@ gamedir will hold progdir + the game directory (id1, id2, etc)
|
||||||
|
|
||||||
FString progdir;
|
FString progdir;
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// IsSeperator
|
||||||
|
//
|
||||||
|
// Returns true if the character is a path seperator.
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
static inline bool IsSeperator (int c)
|
static inline bool IsSeperator (int c)
|
||||||
{
|
{
|
||||||
if (c == '/')
|
if (c == '/')
|
||||||
|
@ -39,6 +47,14 @@ static inline bool IsSeperator (int c)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// FixPathSeperator
|
||||||
|
//
|
||||||
|
// Convert backslashes to forward slashes.
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
void FixPathSeperator (char *path)
|
void FixPathSeperator (char *path)
|
||||||
{
|
{
|
||||||
while (*path)
|
while (*path)
|
||||||
|
@ -49,6 +65,14 @@ void FixPathSeperator (char *path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// copystring
|
||||||
|
//
|
||||||
|
// Replacement for strdup that uses new instead of malloc.
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
char *copystring (const char *s)
|
char *copystring (const char *s)
|
||||||
{
|
{
|
||||||
char *b;
|
char *b;
|
||||||
|
@ -66,6 +90,13 @@ char *copystring (const char *s)
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// ReplaceString
|
||||||
|
//
|
||||||
|
// Do not use in new code.
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
void ReplaceString (char **ptr, const char *str)
|
void ReplaceString (char **ptr, const char *str)
|
||||||
{
|
{
|
||||||
|
@ -87,11 +118,12 @@ void ReplaceString (char **ptr, const char *str)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
/*
|
//==========================================================================
|
||||||
================
|
//
|
||||||
Q_filelength
|
// Q_filelength
|
||||||
================
|
//
|
||||||
*/
|
//==========================================================================
|
||||||
|
|
||||||
int Q_filelength (FILE *f)
|
int Q_filelength (FILE *f)
|
||||||
{
|
{
|
||||||
int pos;
|
int pos;
|
||||||
|
@ -106,17 +138,20 @@ int Q_filelength (FILE *f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
//==========================================================================
|
||||||
==============
|
//
|
||||||
FileExists
|
// FileExists
|
||||||
==============
|
//
|
||||||
*/
|
// Returns true if the given path exists and is a readable file.
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
bool FileExists (const char *filename)
|
bool FileExists (const char *filename)
|
||||||
{
|
{
|
||||||
FILE *f;
|
FILE *f;
|
||||||
|
|
||||||
// [RH] Empty filenames are never there
|
// [RH] Empty filenames are never there
|
||||||
if (*filename == 0)
|
if (filename == NULL || *filename == 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
f = fopen (filename, "r");
|
f = fopen (filename, "r");
|
||||||
|
@ -126,6 +161,31 @@ bool FileExists (const char *filename)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// DirEntryExists
|
||||||
|
//
|
||||||
|
// Returns true if the given path exists, be it a directory or a file.
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
bool DirEntryExists(const char *pathname)
|
||||||
|
{
|
||||||
|
if (pathname == NULL || *pathname == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
struct stat info;
|
||||||
|
return stat(pathname, &info) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// DefaultExtension -- char array version
|
||||||
|
//
|
||||||
|
// Appends the extension to a pathname if it does not already have one.
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
void DefaultExtension (char *path, const char *extension)
|
void DefaultExtension (char *path, const char *extension)
|
||||||
{
|
{
|
||||||
char *src;
|
char *src;
|
||||||
|
@ -145,6 +205,14 @@ void DefaultExtension (char *path, const char *extension)
|
||||||
strcat (path, extension);
|
strcat (path, extension);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// DefaultExtension -- FString version
|
||||||
|
//
|
||||||
|
// Appends the extension to a pathname if it does not already have one.
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
void DefaultExtension (FString &path, const char *extension)
|
void DefaultExtension (FString &path, const char *extension)
|
||||||
{
|
{
|
||||||
const char *src = &path[int(path.Len())-1];
|
const char *src = &path[int(path.Len())-1];
|
||||||
|
@ -160,13 +228,17 @@ void DefaultExtension (FString &path, const char *extension)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
//==========================================================================
|
||||||
====================
|
//
|
||||||
Extract file parts
|
// ExtractFilePath
|
||||||
====================
|
//
|
||||||
*/
|
// Returns the directory part of a pathname.
|
||||||
|
//
|
||||||
// FIXME: should include the slash, otherwise
|
// FIXME: should include the slash, otherwise
|
||||||
// backing to an empty path will be wrong when appending a slash
|
// backing to an empty path will be wrong when appending a slash
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
FString ExtractFilePath (const char *path)
|
FString ExtractFilePath (const char *path)
|
||||||
{
|
{
|
||||||
const char *src;
|
const char *src;
|
||||||
|
@ -182,6 +254,14 @@ FString ExtractFilePath (const char *path)
|
||||||
return FString(path, src - path);
|
return FString(path, src - path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// ExtractFileBase
|
||||||
|
//
|
||||||
|
// Returns the file part of a pathname, optionally including the extension.
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
FString ExtractFileBase (const char *path, bool include_extension)
|
FString ExtractFileBase (const char *path, bool include_extension)
|
||||||
{
|
{
|
||||||
const char *src, *dot;
|
const char *src, *dot;
|
||||||
|
@ -221,11 +301,12 @@ FString ExtractFileBase (const char *path, bool include_extension)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
//==========================================================================
|
||||||
==============
|
//
|
||||||
ParseNum / ParseHex
|
// ParseHex
|
||||||
==============
|
//
|
||||||
*/
|
//==========================================================================
|
||||||
|
|
||||||
int ParseHex (const char *hex)
|
int ParseHex (const char *hex)
|
||||||
{
|
{
|
||||||
const char *str;
|
const char *str;
|
||||||
|
@ -253,6 +334,11 @@ int ParseHex (const char *hex)
|
||||||
return num;
|
return num;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// ParseNum
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
int ParseNum (const char *str)
|
int ParseNum (const char *str)
|
||||||
{
|
{
|
||||||
|
@ -263,8 +349,13 @@ int ParseNum (const char *str)
|
||||||
return atol (str);
|
return atol (str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// IsNum
|
||||||
|
//
|
||||||
// [RH] Returns true if the specified string is a valid decimal number
|
// [RH] Returns true if the specified string is a valid decimal number
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
bool IsNum (const char *str)
|
bool IsNum (const char *str)
|
||||||
{
|
{
|
||||||
|
@ -279,7 +370,13 @@ bool IsNum (const char *str)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// CheckWildcards
|
||||||
|
//
|
||||||
// [RH] Checks if text matches the wildcard pattern using ? or *
|
// [RH] Checks if text matches the wildcard pattern using ? or *
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
bool CheckWildcards (const char *pattern, const char *text)
|
bool CheckWildcards (const char *pattern, const char *text)
|
||||||
{
|
{
|
||||||
|
@ -317,7 +414,13 @@ bool CheckWildcards (const char *pattern, const char *text)
|
||||||
return (*pattern | *text) == 0;
|
return (*pattern | *text) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// FormatGUID
|
||||||
|
//
|
||||||
// [RH] Print a GUID to a text buffer using the standard format.
|
// [RH] Print a GUID to a text buffer using the standard format.
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
void FormatGUID (char *buffer, size_t buffsize, const GUID &guid)
|
void FormatGUID (char *buffer, size_t buffsize, const GUID &guid)
|
||||||
{
|
{
|
||||||
|
@ -329,7 +432,14 @@ void FormatGUID (char *buffer, size_t buffsize, const GUID &guid)
|
||||||
guid.Data4[6], guid.Data4[7]);
|
guid.Data4[6], guid.Data4[7]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// myasctime
|
||||||
|
//
|
||||||
// [RH] Returns the current local time as ASCII, even if it's too early
|
// [RH] Returns the current local time as ASCII, even if it's too early
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
const char *myasctime ()
|
const char *myasctime ()
|
||||||
{
|
{
|
||||||
time_t clock;
|
time_t clock;
|
||||||
|
@ -347,10 +457,13 @@ const char *myasctime ()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/************************************************************************/
|
//==========================================================================
|
||||||
/* CreatePath: creates a directory including all levels necessary */
|
//
|
||||||
/* */
|
// CreatePath
|
||||||
/************************************************************************/
|
//
|
||||||
|
// Creates a directory including all levels necessary
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
void DoCreatePath(const char *fn)
|
void DoCreatePath(const char *fn)
|
||||||
{
|
{
|
||||||
|
@ -413,8 +526,14 @@ void CreatePath(const char *fn)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// strbin1 -- In-place version
|
||||||
|
//
|
||||||
// [RH] Replaces the escape sequences in a string with actual escaped characters.
|
// [RH] Replaces the escape sequences in a string with actual escaped characters.
|
||||||
// This operation is done in-place. The result is the new length of the string.
|
// This operation is done in-place. The result is the new length of the string.
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
int strbin (char *str)
|
int strbin (char *str)
|
||||||
{
|
{
|
||||||
|
@ -504,8 +623,14 @@ int strbin (char *str)
|
||||||
return int(str - start);
|
return int(str - start);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// strbin1 -- String-creating version
|
||||||
|
//
|
||||||
// [RH] Replaces the escape sequences in a string with actual escaped characters.
|
// [RH] Replaces the escape sequences in a string with actual escaped characters.
|
||||||
// This operation is done in-place. The result is the new length of the string.
|
// This operation is done in-place.
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
FString strbin1 (const char *start)
|
FString strbin1 (const char *start)
|
||||||
{
|
{
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
|
|
||||||
int Q_filelength (FILE *f);
|
int Q_filelength (FILE *f);
|
||||||
bool FileExists (const char *filename);
|
bool FileExists (const char *filename);
|
||||||
|
bool DirEntryExists (const char *pathname);
|
||||||
|
|
||||||
extern FString progdir;
|
extern FString progdir;
|
||||||
|
|
||||||
|
|
|
@ -1235,7 +1235,7 @@ void D_AddFile (const char *file, bool check)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (check && !FileExists (file))
|
if (check && !DirEntryExists (file))
|
||||||
{
|
{
|
||||||
const char *f = BaseFileSearch (file, ".wad");
|
const char *f = BaseFileSearch (file, ".wad");
|
||||||
if (f == NULL)
|
if (f == NULL)
|
||||||
|
@ -1405,13 +1405,13 @@ static const char *BaseFileSearch (const char *file, const char *ext, bool lookf
|
||||||
if (lookfirstinprogdir)
|
if (lookfirstinprogdir)
|
||||||
{
|
{
|
||||||
mysnprintf (wad, countof(wad), "%s%s%s", progdir.GetChars(), progdir[progdir.Len() - 1] != '/' ? "/" : "", file);
|
mysnprintf (wad, countof(wad), "%s%s%s", progdir.GetChars(), progdir[progdir.Len() - 1] != '/' ? "/" : "", file);
|
||||||
if (FileExists (wad))
|
if (DirEntryExists (wad))
|
||||||
{
|
{
|
||||||
return wad;
|
return wad;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FileExists (file))
|
if (DirEntryExists (file))
|
||||||
{
|
{
|
||||||
mysnprintf (wad, countof(wad), "%s", file);
|
mysnprintf (wad, countof(wad), "%s", file);
|
||||||
return wad;
|
return wad;
|
||||||
|
@ -1454,7 +1454,7 @@ static const char *BaseFileSearch (const char *file, const char *ext, bool lookf
|
||||||
if (dir != NULL)
|
if (dir != NULL)
|
||||||
{
|
{
|
||||||
mysnprintf (wad, countof(wad), "%s%s%s", dir, dir[strlen (dir) - 1] != '/' ? "/" : "", file);
|
mysnprintf (wad, countof(wad), "%s%s%s", dir, dir[strlen (dir) - 1] != '/' ? "/" : "", file);
|
||||||
if (FileExists (wad))
|
if (DirEntryExists (wad))
|
||||||
{
|
{
|
||||||
return wad;
|
return wad;
|
||||||
}
|
}
|
||||||
|
@ -1748,11 +1748,8 @@ void D_DoomMain (void)
|
||||||
files2->Destroy();
|
files2->Destroy();
|
||||||
files3->Destroy();
|
files3->Destroy();
|
||||||
|
|
||||||
const char *loaddir = Args->CheckValue("-dir");
|
|
||||||
// FIXME: consider the search path list for directory, too.
|
|
||||||
|
|
||||||
Printf ("W_Init: Init WADfiles.\n");
|
Printf ("W_Init: Init WADfiles.\n");
|
||||||
Wads.InitMultipleFiles (&wadfiles, loaddir);
|
Wads.InitMultipleFiles (&wadfiles);
|
||||||
|
|
||||||
// [RH] Initialize localizable strings.
|
// [RH] Initialize localizable strings.
|
||||||
GStrings.LoadStrings (false);
|
GStrings.LoadStrings (false);
|
||||||
|
|
|
@ -67,7 +67,7 @@ FLumpFile::FLumpFile(const char *filename, FileReader *file) : FUncompressedFile
|
||||||
//
|
//
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
|
||||||
bool FLumpFile::Open(bool)
|
bool FLumpFile::Open(bool quiet)
|
||||||
{
|
{
|
||||||
FString name(ExtractFileBase (Filename));
|
FString name(ExtractFileBase (Filename));
|
||||||
|
|
||||||
|
@ -81,6 +81,10 @@ bool FLumpFile::Open(bool)
|
||||||
Lumps->Flags = 0;
|
Lumps->Flags = 0;
|
||||||
Lumps->FullName = NULL;
|
Lumps->FullName = NULL;
|
||||||
NumLumps = 1;
|
NumLumps = 1;
|
||||||
|
if (!quiet)
|
||||||
|
{
|
||||||
|
Printf("\n");
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -160,7 +160,7 @@ void FWadCollection::DeleteAll ()
|
||||||
//
|
//
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
|
||||||
void FWadCollection::InitMultipleFiles (wadlist_t **filenames, const char *loaddir)
|
void FWadCollection::InitMultipleFiles (wadlist_t **filenames)
|
||||||
{
|
{
|
||||||
int numfiles;
|
int numfiles;
|
||||||
|
|
||||||
|
@ -172,18 +172,10 @@ void FWadCollection::InitMultipleFiles (wadlist_t **filenames, const char *loadd
|
||||||
{
|
{
|
||||||
wadlist_t *next = (*filenames)->next;
|
wadlist_t *next = (*filenames)->next;
|
||||||
int baselump = NumLumps;
|
int baselump = NumLumps;
|
||||||
char name[PATH_MAX];
|
AddFile ((*filenames)->name);
|
||||||
|
|
||||||
// [RH] Automatically append .wad extension if none is specified.
|
|
||||||
strcpy (name, (*filenames)->name);
|
|
||||||
FixPathSeperator (name);
|
|
||||||
DefaultExtension (name, ".wad");
|
|
||||||
|
|
||||||
AddFile (name);
|
|
||||||
M_Free (*filenames);
|
M_Free (*filenames);
|
||||||
*filenames = next;
|
*filenames = next;
|
||||||
}
|
}
|
||||||
if (loaddir != NULL) AddFile(loaddir, NULL, true);
|
|
||||||
|
|
||||||
NumLumps = LumpInfo.Size();
|
NumLumps = LumpInfo.Size();
|
||||||
if (NumLumps == 0)
|
if (NumLumps == 0)
|
||||||
|
@ -230,9 +222,20 @@ int FWadCollection::AddExternalFile(const char *filename)
|
||||||
// [RH] Removed reload hack
|
// [RH] Removed reload hack
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
|
||||||
void FWadCollection::AddFile (const char *filename, FileReader *wadinfo, bool isdir)
|
void FWadCollection::AddFile (const char *filename, FileReader *wadinfo)
|
||||||
{
|
{
|
||||||
int startlump;
|
int startlump;
|
||||||
|
bool isdir;
|
||||||
|
|
||||||
|
// Does this exist? If so, is it a directory?
|
||||||
|
struct stat info;
|
||||||
|
if (stat(filename, &info) != 0)
|
||||||
|
{
|
||||||
|
Printf(TEXTCOLOR_RED "Could not stat %s\n", filename);
|
||||||
|
PrintLastError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
isdir = (info.st_mode & S_IFDIR) != 0;
|
||||||
|
|
||||||
if (wadinfo == NULL && !isdir)
|
if (wadinfo == NULL && !isdir)
|
||||||
{
|
{
|
||||||
|
@ -253,8 +256,10 @@ void FWadCollection::AddFile (const char *filename, FileReader *wadinfo, bool is
|
||||||
|
|
||||||
FResourceFile *resfile;
|
FResourceFile *resfile;
|
||||||
|
|
||||||
if (!isdir) resfile = FResourceFile::OpenResourceFile(filename, wadinfo);
|
if (!isdir)
|
||||||
else resfile = FResourceFile::OpenDirectory(filename);
|
resfile = FResourceFile::OpenResourceFile(filename, wadinfo);
|
||||||
|
else
|
||||||
|
resfile = FResourceFile::OpenDirectory(filename);
|
||||||
|
|
||||||
if (resfile != NULL)
|
if (resfile != NULL)
|
||||||
{
|
{
|
||||||
|
|
|
@ -153,8 +153,8 @@ public:
|
||||||
// The wadnum for the IWAD
|
// The wadnum for the IWAD
|
||||||
enum { IWAD_FILENUM = 1 };
|
enum { IWAD_FILENUM = 1 };
|
||||||
|
|
||||||
void InitMultipleFiles (wadlist_t **filenames, const char *loaddir);
|
void InitMultipleFiles (wadlist_t **filenames);
|
||||||
void AddFile (const char *filename, FileReader *wadinfo = NULL, bool isdir = false);
|
void AddFile (const char *filename, FileReader *wadinfo = NULL);
|
||||||
int CheckIfWadLoaded (const char *name);
|
int CheckIfWadLoaded (const char *name);
|
||||||
|
|
||||||
const char *GetWadName (int wadnum) const;
|
const char *GetWadName (int wadnum) const;
|
||||||
|
|
Loading…
Reference in a new issue