Added functionality to generate asset names
This commit is contained in:
parent
efa85036c2
commit
2e44e5b119
File diff suppressed because it is too large
Load Diff
|
@ -24,6 +24,7 @@ public:
|
||||||
inline CFourCC() { mFourCC = 0; }
|
inline CFourCC() { mFourCC = 0; }
|
||||||
inline CFourCC(const char *pkSrc) { mFourCC = FOURCC(pkSrc); }
|
inline CFourCC(const char *pkSrc) { mFourCC = FOURCC(pkSrc); }
|
||||||
inline CFourCC(const TString& rkSrc) { ASSERT(rkSrc.Length() == 4); mFourCC = FOURCC(rkSrc); }
|
inline CFourCC(const TString& rkSrc) { ASSERT(rkSrc.Length() == 4); mFourCC = FOURCC(rkSrc); }
|
||||||
|
inline CFourCC(const TWideString& rkSrc){ ASSERT(rkSrc.Length() == 4); mFourCC = FOURCC(rkSrc); }
|
||||||
inline CFourCC(u32 Src) { mFourCC = Src; }
|
inline CFourCC(u32 Src) { mFourCC = Src; }
|
||||||
inline CFourCC(IInputStream& rSrc) { Read(rSrc); }
|
inline CFourCC(IInputStream& rSrc) { Read(rSrc); }
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,36 @@ EGame GetGameForID(const CFourCC& rkID)
|
||||||
return eUnknownGame;
|
return eUnknownGame;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TString GetGameName(EGame Game)
|
||||||
|
{
|
||||||
|
switch (Game)
|
||||||
|
{
|
||||||
|
case ePrimeDemo: return "Metroid Prime Kiosk Demo";
|
||||||
|
case ePrime: return "Metroid Prime";
|
||||||
|
case eEchoesDemo: return "Metroid Prime 2: Echoes Demo";
|
||||||
|
case eEchoes: return "Metroid Prime 2: Echoes";
|
||||||
|
case eCorruptionProto: return "Metroid Prime 3: Corruption Prototype";
|
||||||
|
case eCorruption: return "Metroid Prime 3: Corruption";
|
||||||
|
case eReturns: return "Donkey Kong Country Returns";
|
||||||
|
default: return "Unknown Game";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TString GetGameShortName(EGame Game)
|
||||||
|
{
|
||||||
|
switch (Game)
|
||||||
|
{
|
||||||
|
case ePrimeDemo: return "MP1Demo";
|
||||||
|
case ePrime: return "MP1";
|
||||||
|
case eEchoesDemo: return "MP2Demo";
|
||||||
|
case eEchoes: return "MP2";
|
||||||
|
case eCorruptionProto: return "MP3Proto";
|
||||||
|
case eCorruption: return "MP3";
|
||||||
|
case eReturns: return "DKCR";
|
||||||
|
default: return "Unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Serialize(IArchive& rArc, EGame& rGame)
|
void Serialize(IArchive& rArc, EGame& rGame)
|
||||||
{
|
{
|
||||||
CFourCC GameID = GetGameID(rGame);
|
CFourCC GameID = GetGameID(rGame);
|
||||||
|
|
|
@ -7,6 +7,9 @@
|
||||||
class CFourCC;
|
class CFourCC;
|
||||||
class IArchive;
|
class IArchive;
|
||||||
|
|
||||||
|
// Note: The reason why the EGame value isn't just the fourCC game ID is because a lot of code does inequality
|
||||||
|
// comparisons on EGame for version checking ie. "if (Game <= eEchoes)", which means the enum values need to be
|
||||||
|
// in chronological order.
|
||||||
enum EGame
|
enum EGame
|
||||||
{
|
{
|
||||||
ePrimeDemo,
|
ePrimeDemo,
|
||||||
|
@ -21,6 +24,8 @@ enum EGame
|
||||||
|
|
||||||
CFourCC GetGameID(EGame Game);
|
CFourCC GetGameID(EGame Game);
|
||||||
EGame GetGameForID(const CFourCC& rkID);
|
EGame GetGameForID(const CFourCC& rkID);
|
||||||
|
TString GetGameName(EGame Game);
|
||||||
|
TString GetGameShortName(EGame Game);
|
||||||
void Serialize(IArchive& rArc, EGame& rGame);
|
void Serialize(IArchive& rArc, EGame& rGame);
|
||||||
|
|
||||||
#endif // EGAME_H
|
#endif // EGAME_H
|
||||||
|
|
|
@ -42,6 +42,18 @@ bool IsRelative(const TWideString& rkDirPath)
|
||||||
return !boost::filesystem::path(*rkDirPath).is_relative();
|
return !boost::filesystem::path(*rkDirPath).is_relative();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsEmpty(const TWideString& rkDirPath)
|
||||||
|
{
|
||||||
|
if (!IsDirectory(rkDirPath))
|
||||||
|
{
|
||||||
|
Log::Error("Non-directory path passed to IsEmpty(): " + rkDirPath.ToUTF8());
|
||||||
|
DEBUG_BREAK;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return is_empty(*rkDirPath);
|
||||||
|
}
|
||||||
|
|
||||||
bool CreateDirectory(const TWideString& rkNewDir)
|
bool CreateDirectory(const TWideString& rkNewDir)
|
||||||
{
|
{
|
||||||
if (!IsValidPath(rkNewDir, true))
|
if (!IsValidPath(rkNewDir, true))
|
||||||
|
@ -61,6 +73,7 @@ bool CopyFile(const TWideString& rkOrigPath, const TWideString& rkNewPath)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CreateDirectory(rkNewPath.GetFileDirectory());
|
||||||
boost::system::error_code Error;
|
boost::system::error_code Error;
|
||||||
copy(*rkOrigPath, *rkNewPath, Error);
|
copy(*rkOrigPath, *rkNewPath, Error);
|
||||||
return (Error == boost::system::errc::success);
|
return (Error == boost::system::errc::success);
|
||||||
|
@ -74,6 +87,7 @@ bool CopyDirectory(const TWideString& rkOrigPath, const TWideString& rkNewPath)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CreateDirectory(rkNewPath.GetFileDirectory());
|
||||||
boost::system::error_code Error;
|
boost::system::error_code Error;
|
||||||
copy_directory(*rkOrigPath, *rkNewPath, Error);
|
copy_directory(*rkOrigPath, *rkNewPath, Error);
|
||||||
return (Error == boost::system::errc::success);
|
return (Error == boost::system::errc::success);
|
||||||
|
@ -317,7 +331,7 @@ TWideString SanitizePath(TWideString Path, bool Directory)
|
||||||
TWideString Comp = *It;
|
TWideString Comp = *It;
|
||||||
bool IsDir = Directory || CompIdx < Components.size() - 1;
|
bool IsDir = Directory || CompIdx < Components.size() - 1;
|
||||||
bool IsRoot = CompIdx == 0;
|
bool IsRoot = CompIdx == 0;
|
||||||
SanitizeName(Comp, IsDir, IsRoot);
|
Comp = SanitizeName(Comp, IsDir, IsRoot);
|
||||||
|
|
||||||
Path += Comp;
|
Path += Comp;
|
||||||
if (IsDir) Path += L'\\';
|
if (IsDir) Path += L'\\';
|
||||||
|
@ -330,6 +344,9 @@ TWideString SanitizePath(TWideString Path, bool Directory)
|
||||||
bool IsValidName(const TWideString& rkName, bool Directory, bool RootDir /*= false*/)
|
bool IsValidName(const TWideString& rkName, bool Directory, bool RootDir /*= false*/)
|
||||||
{
|
{
|
||||||
// Windows only atm
|
// Windows only atm
|
||||||
|
if (rkName.IsEmpty())
|
||||||
|
return false;
|
||||||
|
|
||||||
u32 NumIllegalChars = sizeof(gskIllegalNameChars) / sizeof(wchar_t);
|
u32 NumIllegalChars = sizeof(gskIllegalNameChars) / sizeof(wchar_t);
|
||||||
|
|
||||||
if (Directory && (rkName == L"." || rkName == L".."))
|
if (Directory && (rkName == L"." || rkName == L".."))
|
||||||
|
|
|
@ -13,6 +13,7 @@ bool IsFile(const TWideString& rkFilePath);
|
||||||
bool IsDirectory(const TWideString& rkDirPath);
|
bool IsDirectory(const TWideString& rkDirPath);
|
||||||
bool IsAbsolute(const TWideString& rkDirPath);
|
bool IsAbsolute(const TWideString& rkDirPath);
|
||||||
bool IsRelative(const TWideString& rkDirPath);
|
bool IsRelative(const TWideString& rkDirPath);
|
||||||
|
bool IsEmpty(const TWideString& rkDirPath);
|
||||||
bool CreateDirectory(const TWideString& rkNewDir);
|
bool CreateDirectory(const TWideString& rkNewDir);
|
||||||
bool CopyFile(const TWideString& rkOrigPath, const TWideString& rkNewPath);
|
bool CopyFile(const TWideString& rkOrigPath, const TWideString& rkNewPath);
|
||||||
bool CopyDirectory(const TWideString& rkOrigPath, const TWideString& rkNewPath);
|
bool CopyDirectory(const TWideString& rkOrigPath, const TWideString& rkNewPath);
|
||||||
|
|
|
@ -426,6 +426,7 @@ public:
|
||||||
|
|
||||||
// Container serialize methods
|
// Container serialize methods
|
||||||
#include <list>
|
#include <list>
|
||||||
|
#include <map>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
@ -528,4 +529,77 @@ inline void SerializeContainer(IArchive& rArc, std::set<ValType>& rSet, const TS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// std::map
|
||||||
|
template<typename KeyType, typename ValType>
|
||||||
|
inline void SerializeContainer(IArchive& rArc, std::map<KeyType,ValType>& rMap, const TString& rkElemName)
|
||||||
|
{
|
||||||
|
u32 Size = rMap.size();
|
||||||
|
rArc.SerializeContainerSize(Size);
|
||||||
|
|
||||||
|
if (rArc.IsReader())
|
||||||
|
{
|
||||||
|
for (u32 iElem = 0; iElem < Size; iElem++)
|
||||||
|
{
|
||||||
|
KeyType Key;
|
||||||
|
ValType Val;
|
||||||
|
|
||||||
|
rArc.ParamBegin(*rkElemName);
|
||||||
|
rArc << SERIAL("Key", Key) << SERIAL("Value", Val);
|
||||||
|
rArc.ParamEnd();
|
||||||
|
|
||||||
|
ASSERT(rMap.find(Key) == rMap.end());
|
||||||
|
rMap[Key] = Val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (auto Iter = rMap.begin(); Iter != rMap.end(); Iter++)
|
||||||
|
{
|
||||||
|
KeyType Key = Iter->first;
|
||||||
|
ValType Val = Iter->second;
|
||||||
|
|
||||||
|
rArc.ParamBegin(*rkElemName);
|
||||||
|
rArc << SERIAL("Key", Key) << SERIAL("Value", Val);
|
||||||
|
rArc.ParamEnd();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename KeyType, typename ValType, typename FactoryType>
|
||||||
|
inline void SerializeContainer(IArchive& rArc, std::map<KeyType,ValType>& rMap, const TString& rkElemName, FactoryType *pFactory)
|
||||||
|
{
|
||||||
|
u32 Size = rMap.size();
|
||||||
|
rArc.SerializeContainerSize(Size);
|
||||||
|
|
||||||
|
if (rArc.IsReader())
|
||||||
|
{
|
||||||
|
for (u32 iElem = 0; iElem < Size; iElem++)
|
||||||
|
{
|
||||||
|
KeyType Key;
|
||||||
|
ValType Val;
|
||||||
|
|
||||||
|
rArc.ParamBegin(*rkElemName);
|
||||||
|
rArc << SERIAL_ABSTRACT("Key", Key, pFactory) << SERIAL("Value", Val);
|
||||||
|
rArc.ParamEnd();
|
||||||
|
|
||||||
|
ASSERT(rMap.find(Key) == rMap.end());
|
||||||
|
rMap[Key] = Val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (auto Iter = rMap.begin(); Iter != rMap.end(); Iter++)
|
||||||
|
{
|
||||||
|
KeyType Key = Iter->first;
|
||||||
|
ValType Val = Iter->second;
|
||||||
|
|
||||||
|
rArc.ParamBegin(*rkElemName);
|
||||||
|
rArc << SERIAL("Key", Key) << SERIAL("Value", Val);
|
||||||
|
rArc.ParamEnd();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#endif // IARCHIVE
|
#endif // IARCHIVE
|
||||||
|
|
|
@ -4,11 +4,12 @@
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
#include <FileIO/IOUtil.h>
|
#include <FileIO/IOUtil.h>
|
||||||
|
|
||||||
#include <string>
|
#include <cstdarg>
|
||||||
#include <list>
|
|
||||||
#include <vector>
|
|
||||||
#include <sstream>
|
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
|
#include <list>
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
/* This is a string class which is essentially a wrapper around std::basic_string.
|
/* This is a string class which is essentially a wrapper around std::basic_string.
|
||||||
* The reason for this is because there are a lot of string functions I use very
|
* The reason for this is because there are a lot of string functions I use very
|
||||||
|
@ -149,24 +150,22 @@ public:
|
||||||
return (u32) Pos;
|
return (u32) Pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 IndexOfPhrase(_TString Str, u32 Offset, bool CaseSensitive = true) const
|
u32 IndexOfPhrase(const _TString& rkStr, u32 Offset, bool CaseSensitive = true) const
|
||||||
{
|
{
|
||||||
if (Size() < Str.Size()) return -1;
|
if (Size() < rkStr.Size()) return -1;
|
||||||
|
|
||||||
// Apply case sensitivity
|
|
||||||
_TString CheckStr(CaseSensitive ? *this : ToUpper());
|
|
||||||
if (!CaseSensitive) Str = Str.ToUpper();
|
|
||||||
|
|
||||||
// Now loop from the offset provided by the user.
|
// Now loop from the offset provided by the user.
|
||||||
u32 Pos = Offset;
|
u32 Pos = Offset;
|
||||||
u32 LatestPossibleStart = Size() - Str.Size();
|
u32 LatestPossibleStart = Size() - rkStr.Size();
|
||||||
u32 MatchStart = -1;
|
u32 MatchStart = -1;
|
||||||
u32 Matched = 0;
|
u32 Matched = 0;
|
||||||
|
|
||||||
while (Pos < Size())
|
while (Pos < Size())
|
||||||
{
|
{
|
||||||
// If this character matches, increment Matched!
|
// If this character matches, increment Matched!
|
||||||
if (CheckStr[Pos] == Str[Matched])
|
bool Match = CaseSensitive ? (At(Pos) == rkStr[Matched]) : (CharToUpper(At(Pos)) == CharToUpper(rkStr[Matched]));
|
||||||
|
|
||||||
|
if (Match)
|
||||||
{
|
{
|
||||||
Matched++;
|
Matched++;
|
||||||
|
|
||||||
|
@ -174,7 +173,7 @@ public:
|
||||||
MatchStart = Pos;
|
MatchStart = Pos;
|
||||||
|
|
||||||
// If we matched the entire string, we can return.
|
// If we matched the entire string, we can return.
|
||||||
if (Matched == Str.Size())
|
if (Matched == rkStr.Size())
|
||||||
return MatchStart;
|
return MatchStart;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -198,9 +197,9 @@ public:
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline u32 IndexOfPhrase(_TString Str, bool CaseSensitive = true) const
|
inline u32 IndexOfPhrase(const _TString& rkStr, bool CaseSensitive = true) const
|
||||||
{
|
{
|
||||||
return IndexOfPhrase(Str, 0, CaseSensitive);
|
return IndexOfPhrase(rkStr, 0, CaseSensitive);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Modify String
|
// Modify String
|
||||||
|
@ -318,18 +317,10 @@ public:
|
||||||
|
|
||||||
_TString ToUpper() const
|
_TString ToUpper() const
|
||||||
{
|
{
|
||||||
// todo: doesn't handle accented characters
|
|
||||||
_TString Out(Size());
|
_TString Out(Size());
|
||||||
|
|
||||||
for (u32 iChar = 0; iChar < Size(); iChar++)
|
for (u32 iChar = 0; iChar < Size(); iChar++)
|
||||||
{
|
Out[iChar] = CharToUpper( At(iChar) );
|
||||||
CharType Chr = At(iChar);
|
|
||||||
|
|
||||||
if (Chr >= CHAR_LITERAL('a') && Chr <= CHAR_LITERAL('z'))
|
|
||||||
Out[iChar] = Chr - 0x20;
|
|
||||||
else
|
|
||||||
Out[iChar] = Chr;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Out;
|
return Out;
|
||||||
}
|
}
|
||||||
|
@ -340,14 +331,7 @@ public:
|
||||||
_TString Out(Size());
|
_TString Out(Size());
|
||||||
|
|
||||||
for (u32 iChar = 0; iChar < Size(); iChar++)
|
for (u32 iChar = 0; iChar < Size(); iChar++)
|
||||||
{
|
Out[iChar] = CharToLower( At(iChar) );
|
||||||
CharType Chr = At(iChar);
|
|
||||||
|
|
||||||
if (Chr >= 'A' && Chr <= 'Z')
|
|
||||||
Out[iChar] = Chr + 0x20;
|
|
||||||
else
|
|
||||||
Out[iChar] = Chr;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Out;
|
return Out;
|
||||||
}
|
}
|
||||||
|
@ -509,26 +493,22 @@ public:
|
||||||
return (Size() == 0);
|
return (Size() == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool StartsWith(_TString Str, bool CaseSensitive = true) const
|
bool StartsWith(const _TString& rkStr, bool CaseSensitive = true) const
|
||||||
{
|
{
|
||||||
if (Size() < Str.Size())
|
if (Size() < rkStr.Size())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
_TString CompStr = (CaseSensitive ? *this : ToUpper());
|
_TString SubStr = SubString(0, rkStr.Size());
|
||||||
if (!CaseSensitive) Str = Str.ToUpper();
|
return CaseSensitive ? SubStr == rkStr : SubStr.CaseInsensitiveCompare(rkStr);
|
||||||
|
|
||||||
return (CompStr.SubString(0, Str.Size()) == Str);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EndsWith(_TString Str, bool CaseSensitive = true) const
|
bool EndsWith(const _TString& rkStr, bool CaseSensitive = true) const
|
||||||
{
|
{
|
||||||
if (Size() < Str.Size())
|
if (Size() < rkStr.Size())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
_TString CompStr = (CaseSensitive ? *this : ToUpper());
|
_TString SubStr = SubString(Size() - rkStr.Size(), rkStr.Size());
|
||||||
if (!CaseSensitive) Str = Str.ToUpper();
|
return CaseSensitive ? SubStr == rkStr : SubStr.CaseInsensitiveCompare(rkStr);
|
||||||
|
|
||||||
return (CompStr.SubString(CompStr.Size() - Str.Size(), Str.Size()) == Str);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Contains(_TString Str, bool CaseSensitive = true) const
|
bool Contains(_TString Str, bool CaseSensitive = true) const
|
||||||
|
@ -577,7 +557,14 @@ public:
|
||||||
|
|
||||||
inline bool CaseInsensitiveCompare(const _TString& rkOther) const
|
inline bool CaseInsensitiveCompare(const _TString& rkOther) const
|
||||||
{
|
{
|
||||||
return (ToUpper() == rkOther.ToUpper());
|
if (Size() != rkOther.Size())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (u32 iChr = 0; iChr < Size(); iChr++)
|
||||||
|
if (CharToUpper(At(iChr)) != CharToUpper(rkOther[iChr]))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get Filename Components
|
// Get Filename Components
|
||||||
|
@ -615,10 +602,8 @@ public:
|
||||||
return SubString(0, EndName);
|
return SubString(0, EndName);
|
||||||
}
|
}
|
||||||
|
|
||||||
_TString GetParentDirectoryPath(_TString ParentDirName, bool CaseSensitive = true)
|
_TString GetParentDirectoryPath(const _TString& rkParentDirName, bool CaseSensitive = true)
|
||||||
{
|
{
|
||||||
if (!CaseSensitive) ParentDirName = ParentDirName.ToUpper();
|
|
||||||
|
|
||||||
int IdxA = 0;
|
int IdxA = 0;
|
||||||
int IdxB = IndexOf(LITERAL("\\/"));
|
int IdxB = IndexOf(LITERAL("\\/"));
|
||||||
if (IdxB == -1) return _TString();
|
if (IdxB == -1) return _TString();
|
||||||
|
@ -626,9 +611,8 @@ public:
|
||||||
while (IdxB != -1)
|
while (IdxB != -1)
|
||||||
{
|
{
|
||||||
_TString DirName = SubString(IdxA, IdxB - IdxA);
|
_TString DirName = SubString(IdxA, IdxB - IdxA);
|
||||||
if (!CaseSensitive) DirName = DirName.ToUpper();
|
|
||||||
|
|
||||||
if (DirName == ParentDirName)
|
if (CaseSensitive ? (DirName == rkParentDirName) : (DirName.CaseInsensitiveCompare(rkParentDirName)))
|
||||||
return Truncate(IdxB + 1);
|
return Truncate(IdxB + 1);
|
||||||
|
|
||||||
IdxA = IdxB + 1;
|
IdxA = IdxB + 1;
|
||||||
|
@ -639,6 +623,12 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
// Operators
|
// Operators
|
||||||
|
inline _TString& operator=(CharType Char)
|
||||||
|
{
|
||||||
|
mInternalString = Char;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
inline _TString& operator=(const CharType* pkText)
|
inline _TString& operator=(const CharType* pkText)
|
||||||
{
|
{
|
||||||
mInternalString = pkText;
|
mInternalString = pkText;
|
||||||
|
@ -666,6 +656,14 @@ public:
|
||||||
return CString();
|
return CString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_TString operator+(CharType Other) const
|
||||||
|
{
|
||||||
|
_TString Out(Size() + 1);
|
||||||
|
memcpy(&Out[0], mInternalString.data(), Size() * sizeof(CharType));
|
||||||
|
memcpy(&Out[Size()], &Other, sizeof(CharType));
|
||||||
|
return Out;
|
||||||
|
}
|
||||||
|
|
||||||
_TString operator+(const CharType* pkOther) const
|
_TString operator+(const CharType* pkOther) const
|
||||||
{
|
{
|
||||||
u32 Len = CStringLength(pkOther);
|
u32 Len = CStringLength(pkOther);
|
||||||
|
@ -681,6 +679,11 @@ public:
|
||||||
return (*this + rkOther.CString());
|
return (*this + rkOther.CString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void operator+=(CharType Other)
|
||||||
|
{
|
||||||
|
*this = *this + Other;
|
||||||
|
}
|
||||||
|
|
||||||
inline void operator+=(const CharType* pkOther)
|
inline void operator+=(const CharType* pkOther)
|
||||||
{
|
{
|
||||||
*this = *this + pkOther;
|
*this = *this + pkOther;
|
||||||
|
@ -691,6 +694,14 @@ public:
|
||||||
*this = *this + rkOther;
|
*this = *this + rkOther;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline friend _TString operator+(CharType Left, const _TString& rkRight)
|
||||||
|
{
|
||||||
|
_TString Out(rkRight.Size() + 1);
|
||||||
|
memcpy(&Out[0], &Left, sizeof(CharType));
|
||||||
|
memcpy(&Out[sizeof(CharType)], rkRight.CString(), rkRight.Size() * sizeof(CharType));
|
||||||
|
return Out;
|
||||||
|
}
|
||||||
|
|
||||||
inline friend _TString operator+(const CharType* pkLeft, const _TString& rkRight)
|
inline friend _TString operator+(const CharType* pkLeft, const _TString& rkRight)
|
||||||
{
|
{
|
||||||
u32 Len = CStringLength(pkLeft);
|
u32 Len = CStringLength(pkLeft);
|
||||||
|
@ -709,6 +720,11 @@ public:
|
||||||
return Out;
|
return Out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline bool operator==(CharType Other) const
|
||||||
|
{
|
||||||
|
return Size() == 1 && At(0) == Other;
|
||||||
|
}
|
||||||
|
|
||||||
inline bool operator==(const CharType *pkText) const
|
inline bool operator==(const CharType *pkText) const
|
||||||
{
|
{
|
||||||
return CompareCStrings(pkText, CString());
|
return CompareCStrings(pkText, CString());
|
||||||
|
@ -719,6 +735,11 @@ public:
|
||||||
return (mInternalString == rkOther.mInternalString);
|
return (mInternalString == rkOther.mInternalString);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline friend bool operator==(CharType Other, const _TString& rkString)
|
||||||
|
{
|
||||||
|
return (rkString == Other);
|
||||||
|
}
|
||||||
|
|
||||||
inline friend bool operator==(const CharType *pkText, const _TString& rkString)
|
inline friend bool operator==(const CharType *pkText, const _TString& rkString)
|
||||||
{
|
{
|
||||||
return (rkString == pkText);
|
return (rkString == pkText);
|
||||||
|
@ -729,6 +750,11 @@ public:
|
||||||
return (rkStringB == rkStringA);
|
return (rkStringB == rkStringA);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline bool operator!=(CharType Other) const
|
||||||
|
{
|
||||||
|
return (!(*this == Other));
|
||||||
|
}
|
||||||
|
|
||||||
inline bool operator!=(const CharType *pkText) const
|
inline bool operator!=(const CharType *pkText) const
|
||||||
{
|
{
|
||||||
return (!(*this == pkText));
|
return (!(*this == pkText));
|
||||||
|
@ -739,6 +765,11 @@ public:
|
||||||
return (!(*this == rkOther));
|
return (!(*this == rkOther));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline friend bool operator!=(CharType Other, const _TString& rkString)
|
||||||
|
{
|
||||||
|
return (rkString != Other);
|
||||||
|
}
|
||||||
|
|
||||||
inline friend bool operator!=(const CharType *pkText, const _TString& rkString)
|
inline friend bool operator!=(const CharType *pkText, const _TString& rkString)
|
||||||
{
|
{
|
||||||
return (rkString != pkText);
|
return (rkString != pkText);
|
||||||
|
@ -842,21 +873,39 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
// Static
|
// Static
|
||||||
static TBasicString<CharType> FromInt32(s32 Value, int Width = 0, int Base = 16)
|
static _TString Format(const CharType *pkFmt, ...)
|
||||||
|
{
|
||||||
|
// Probably better to rewrite this at some point for better error handling + avoiding all the C-style syntax
|
||||||
|
const int kBufferSize = 4096;
|
||||||
|
CharType StringBuffer[kBufferSize];
|
||||||
|
|
||||||
|
std::va_list Args;
|
||||||
|
va_start(Args, pkFmt);
|
||||||
|
|
||||||
|
if (typeid(CharType) == typeid(char))
|
||||||
|
vsprintf_s((char*) StringBuffer, kBufferSize, (char*) pkFmt, Args);
|
||||||
|
else
|
||||||
|
vswprintf_s((wchar_t*) StringBuffer, kBufferSize, (wchar_t*) pkFmt, Args);
|
||||||
|
|
||||||
|
va_end(Args);
|
||||||
|
return _TString(StringBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static _TString FromInt32(s32 Value, int Width = 0, int Base = 16)
|
||||||
{
|
{
|
||||||
std::basic_stringstream<CharType> SStream;
|
std::basic_stringstream<CharType> SStream;
|
||||||
SStream << std::setbase(Base) << std::setw(Width) << std::setfill(CHAR_LITERAL('0')) << Value;
|
SStream << std::setbase(Base) << std::setw(Width) << std::setfill(CHAR_LITERAL('0')) << Value;
|
||||||
return SStream.str();
|
return SStream.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
static TBasicString<CharType> FromInt64(s64 Value, int Width = 0, int Base = 16)
|
static _TString FromInt64(s64 Value, int Width = 0, int Base = 16)
|
||||||
{
|
{
|
||||||
std::basic_stringstream<CharType> SStream;
|
std::basic_stringstream<CharType> SStream;
|
||||||
SStream << std::setbase(Base) << std::setw(Width) << std::setfill(CHAR_LITERAL('0')) << Value;
|
SStream << std::setbase(Base) << std::setw(Width) << std::setfill(CHAR_LITERAL('0')) << Value;
|
||||||
return SStream.str();
|
return SStream.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
static TBasicString<CharType> FromFloat(float Value, int MinDecimals = 1)
|
static _TString FromFloat(float Value, int MinDecimals = 1)
|
||||||
{
|
{
|
||||||
// Initial float -> string conversion
|
// Initial float -> string conversion
|
||||||
std::basic_stringstream<CharType> SStream;
|
std::basic_stringstream<CharType> SStream;
|
||||||
|
@ -900,7 +949,7 @@ public:
|
||||||
return Out;
|
return Out;
|
||||||
}
|
}
|
||||||
|
|
||||||
static TBasicString<CharType> FileSizeString(u64 Size, u32 NumDecimals = 2)
|
static _TString FileSizeString(u64 Size, u32 NumDecimals = 2)
|
||||||
{
|
{
|
||||||
_TString Out;
|
_TString Out;
|
||||||
_TString Type;
|
_TString Type;
|
||||||
|
@ -933,17 +982,17 @@ public:
|
||||||
return Out + Type;
|
return Out + Type;
|
||||||
}
|
}
|
||||||
|
|
||||||
static TBasicString<CharType> HexString(unsigned char Num, int Width = 8, bool AddPrefix = true, bool Uppercase = true)
|
static _TString HexString(unsigned char Num, int Width = 8, bool AddPrefix = true, bool Uppercase = true)
|
||||||
{
|
{
|
||||||
return HexString((unsigned long) Num, Width, AddPrefix, Uppercase);
|
return HexString((unsigned long) Num, Width, AddPrefix, Uppercase);
|
||||||
}
|
}
|
||||||
|
|
||||||
static TBasicString<CharType> HexString(unsigned short Num, int Width = 8, bool AddPrefix = true, bool Uppercase = true)
|
static _TString HexString(unsigned short Num, int Width = 8, bool AddPrefix = true, bool Uppercase = true)
|
||||||
{
|
{
|
||||||
return HexString((unsigned long) Num, Width, AddPrefix, Uppercase);
|
return HexString((unsigned long) Num, Width, AddPrefix, Uppercase);
|
||||||
}
|
}
|
||||||
|
|
||||||
static TBasicString<CharType> HexString(unsigned long Num, int Width = 8, bool AddPrefix = true, bool Uppercase = true)
|
static _TString HexString(unsigned long Num, int Width = 8, bool AddPrefix = true, bool Uppercase = true)
|
||||||
{
|
{
|
||||||
std::basic_stringstream<CharType> SStream;
|
std::basic_stringstream<CharType> SStream;
|
||||||
SStream << std::hex << std::setw(Width) << std::setfill('0') << Num;
|
SStream << std::hex << std::setw(Width) << std::setfill('0') << Num;
|
||||||
|
@ -979,6 +1028,18 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline static CharType CharToLower(CharType Chr)
|
||||||
|
{
|
||||||
|
// todo: doesn't handle accented characters
|
||||||
|
return (Chr >= CHAR_LITERAL('A') && Chr <= CHAR_LITERAL('Z')) ? Chr + 0x20 : Chr;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static CharType CharToUpper(CharType Chr)
|
||||||
|
{
|
||||||
|
// todo: doesn't handle accented characters
|
||||||
|
return (Chr >= CHAR_LITERAL('a') && Chr <= CHAR_LITERAL('z')) ? Chr - 0x20 : Chr;
|
||||||
|
}
|
||||||
|
|
||||||
static bool IsWhitespace(CharType Chr)
|
static bool IsWhitespace(CharType Chr)
|
||||||
{
|
{
|
||||||
return ( (Chr == CHAR_LITERAL('\t')) ||
|
return ( (Chr == CHAR_LITERAL('\t')) ||
|
||||||
|
@ -988,6 +1049,11 @@ public:
|
||||||
(Chr == CHAR_LITERAL('\r')) ||
|
(Chr == CHAR_LITERAL('\r')) ||
|
||||||
(Chr == CHAR_LITERAL(' ')) );
|
(Chr == CHAR_LITERAL(' ')) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool IsNumerical(CharType Chr)
|
||||||
|
{
|
||||||
|
return (Chr >= CHAR_LITERAL('0') && Chr <= CHAR_LITERAL('9'));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#undef LITERAL
|
#undef LITERAL
|
||||||
|
|
|
@ -17,7 +17,7 @@ void CAudioManager::LoadAssets()
|
||||||
mSfxIdMap.clear();
|
mSfxIdMap.clear();
|
||||||
|
|
||||||
// Load/sort all audio groups
|
// Load/sort all audio groups
|
||||||
for (TResourceIterator<CAudioGroup> It(mpProject->ResourceStore()); It; ++It)
|
for (TResourceIterator<eAudioGroup> It(mpProject->ResourceStore()); It; ++It)
|
||||||
{
|
{
|
||||||
CAudioGroup *pGroup = (CAudioGroup*) It->Load();
|
CAudioGroup *pGroup = (CAudioGroup*) It->Load();
|
||||||
if (pGroup) mAudioGroups.push_back(pGroup);
|
if (pGroup) mAudioGroups.push_back(pGroup);
|
||||||
|
|
|
@ -213,7 +213,9 @@ HEADERS += \
|
||||||
Resource/Animation/CSkeleton.h \
|
Resource/Animation/CSkeleton.h \
|
||||||
Resource/Animation/CSkin.h \
|
Resource/Animation/CSkin.h \
|
||||||
Resource/Animation/IMetaTransition.h \
|
Resource/Animation/IMetaTransition.h \
|
||||||
Resource/Animation/IMetaAnimation.h
|
Resource/Animation/IMetaAnimation.h \
|
||||||
|
GameProject/CAssetNameMap.h \
|
||||||
|
GameProject/AssetNameGeneration.h
|
||||||
|
|
||||||
# Source Files
|
# Source Files
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
|
@ -314,4 +316,5 @@ SOURCES += \
|
||||||
Resource/Animation/CAnimationParameters.cpp \
|
Resource/Animation/CAnimationParameters.cpp \
|
||||||
Resource/Animation/CSkeleton.cpp \
|
Resource/Animation/CSkeleton.cpp \
|
||||||
Resource/Animation/IMetaAnimation.cpp \
|
Resource/Animation/IMetaAnimation.cpp \
|
||||||
Resource/Animation/IMetaTransition.cpp
|
Resource/Animation/IMetaTransition.cpp \
|
||||||
|
GameProject/AssetNameGeneration.cpp
|
||||||
|
|
|
@ -0,0 +1,438 @@
|
||||||
|
#include "AssetNameGeneration.h"
|
||||||
|
#include "CGameProject.h"
|
||||||
|
#include "CResourceIterator.h"
|
||||||
|
#include "Core/Resource/CFont.h"
|
||||||
|
#include "Core/Resource/CScan.h"
|
||||||
|
#include "Core/Resource/CWorld.h"
|
||||||
|
#include "Core/Resource/Script/CScriptLayer.h"
|
||||||
|
#include <Math/MathUtil.h>
|
||||||
|
|
||||||
|
#define PROCESS_WORLDS 1
|
||||||
|
#define PROCESS_AREAS 1
|
||||||
|
#define PROCESS_MODELS 1
|
||||||
|
#define PROCESS_AUDIO_GROUPS 1
|
||||||
|
#define PROCESS_ANIM_CHAR_SETS 1
|
||||||
|
#define PROCESS_STRINGS 1
|
||||||
|
#define PROCESS_SCANS 1
|
||||||
|
#define PROCESS_FONTS 1
|
||||||
|
|
||||||
|
void ApplyGeneratedName(CResourceEntry *pEntry, const TWideString& rkDir, const TWideString& rkName)
|
||||||
|
{
|
||||||
|
TWideString SanitizedName = FileUtil::SanitizeName(rkName, false);
|
||||||
|
TWideString SanitizedDir = FileUtil::SanitizePath(rkDir, true);
|
||||||
|
if (SanitizedName.IsEmpty()) return;
|
||||||
|
|
||||||
|
CVirtualDirectory *pNewDir = pEntry->ResourceStore()->GetVirtualDirectory(SanitizedDir, false, true);
|
||||||
|
if (pEntry->Directory() == pNewDir && pEntry->Name() == SanitizedName) return;
|
||||||
|
|
||||||
|
TWideString Name = SanitizedName;
|
||||||
|
int AppendNum = 0;
|
||||||
|
|
||||||
|
while (pNewDir->FindChildResource(Name, pEntry->ResourceType()) != nullptr)
|
||||||
|
{
|
||||||
|
Name = TWideString::Format(L"%s_%d", *SanitizedName, AppendNum);
|
||||||
|
AppendNum++;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Success = pEntry->Move(SanitizedDir, Name);
|
||||||
|
ASSERT(Success);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GenerateAssetNames(CGameProject *pProj)
|
||||||
|
{
|
||||||
|
// todo: CAUD/CSMP
|
||||||
|
CResourceStore *pStore = pProj->ResourceStore();
|
||||||
|
|
||||||
|
// Generate names for package named resources first
|
||||||
|
for (u32 iPkg = 0; iPkg < pProj->NumPackages(); iPkg++)
|
||||||
|
{
|
||||||
|
CPackage *pPkg = pProj->PackageByIndex(iPkg);
|
||||||
|
|
||||||
|
for (u32 iCol = 0; iCol < pPkg->NumCollections(); iCol++)
|
||||||
|
{
|
||||||
|
CResourceCollection *pCol = pPkg->CollectionByIndex(iCol);
|
||||||
|
|
||||||
|
for (u32 iRes = 0; iRes < pCol->NumResources(); iRes++)
|
||||||
|
{
|
||||||
|
const SNamedResource& rkRes = pCol->ResourceByIndex(iRes);
|
||||||
|
if (rkRes.Name.EndsWith("NODEPEND")) continue;
|
||||||
|
|
||||||
|
CResourceEntry *pRes = pStore->FindEntry(rkRes.ID);
|
||||||
|
ApplyGeneratedName(pRes, pPkg->Name().ToUTF16(), rkRes.Name.ToUTF16());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if PROCESS_WORLDS
|
||||||
|
// Generate world/area names
|
||||||
|
const TWideString kWorldsRoot = L"Worlds\\";
|
||||||
|
|
||||||
|
for (TResourceIterator<eWorld> It(pStore); It; ++It)
|
||||||
|
{
|
||||||
|
// World common stuff
|
||||||
|
TWideString WorldName = It->Name();
|
||||||
|
|
||||||
|
// Remove date from the end of the world name
|
||||||
|
if (WorldName.EndsWith(L"_#SERIAL#"))
|
||||||
|
WorldName = WorldName.ChopBack(9);
|
||||||
|
|
||||||
|
// Verify the second-to-last character is a number to make sure there is actually a date in the world name
|
||||||
|
// note MP2 multiplayer worlds do not have dates in their names
|
||||||
|
else if (WorldName[WorldName.Size() - 2] >= '0' && WorldName[WorldName.Size() - 2] <= '9')
|
||||||
|
{
|
||||||
|
bool StartedDate = false;
|
||||||
|
|
||||||
|
while (!WorldName.IsEmpty())
|
||||||
|
{
|
||||||
|
wchar_t Chr = WorldName.Back();
|
||||||
|
|
||||||
|
if (!StartedDate && Chr >= L'0' && Chr <= L'9')
|
||||||
|
StartedDate = true;
|
||||||
|
else if (StartedDate && Chr != L'_' && (Chr < L'0' || Chr > L'9'))
|
||||||
|
break;
|
||||||
|
|
||||||
|
WorldName = WorldName.ChopBack(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TWideString WorldDir = kWorldsRoot + WorldName + L'\\';
|
||||||
|
ApplyGeneratedName(*It, WorldDir, WorldName);
|
||||||
|
|
||||||
|
CWorld *pWorld = (CWorld*) It->Load();
|
||||||
|
CModel *pSkyModel = pWorld->DefaultSkybox();
|
||||||
|
CStringTable *pWorldNameTable = pWorld->WorldName();
|
||||||
|
CStringTable *pDarkWorldNameTable = pWorld->DarkWorldName();
|
||||||
|
CResource *pSaveWorld = pWorld->SaveWorld();
|
||||||
|
CResource *pMapWorld = pWorld->MapWorld();
|
||||||
|
|
||||||
|
if (pSkyModel && !pSkyModel->Entry()->IsCategorized())
|
||||||
|
{
|
||||||
|
CResourceEntry *pSkyEntry = pSkyModel->Entry();
|
||||||
|
ApplyGeneratedName(pSkyEntry, WorldDir, TWideString::Format(L"%s_Sky", *WorldName));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pWorldNameTable)
|
||||||
|
{
|
||||||
|
CResourceEntry *pNameEntry = pWorldNameTable->Entry();
|
||||||
|
ApplyGeneratedName(pNameEntry, WorldDir, pNameEntry->Name());
|
||||||
|
}
|
||||||
|
if (pDarkWorldNameTable)
|
||||||
|
{
|
||||||
|
CResourceEntry *pDarkNameEntry = pDarkWorldNameTable->Entry();
|
||||||
|
ApplyGeneratedName(pDarkNameEntry, WorldDir, pDarkNameEntry->Name());
|
||||||
|
}
|
||||||
|
if (pSaveWorld)
|
||||||
|
ApplyGeneratedName(pSaveWorld->Entry(), WorldDir, TWideString::Format(L"%s_SaveInfo", *WorldName));
|
||||||
|
|
||||||
|
if (pMapWorld)
|
||||||
|
ApplyGeneratedName(pMapWorld->Entry(), WorldDir, TWideString::Format(L"%s_Map", *WorldName));
|
||||||
|
|
||||||
|
// Areas
|
||||||
|
for (u32 iArea = 0; iArea < pWorld->NumAreas(); iArea++)
|
||||||
|
{
|
||||||
|
// Determine area name
|
||||||
|
CAssetID AreaID = pWorld->AreaResourceID(iArea);
|
||||||
|
TString InternalAreaName = pWorld->AreaInternalName(iArea);
|
||||||
|
CStringTable *pAreaNameTable = pWorld->AreaName(iArea);
|
||||||
|
TWideString AreaName;
|
||||||
|
|
||||||
|
if (pAreaNameTable)
|
||||||
|
AreaName = pAreaNameTable->String("ENGL", 0);
|
||||||
|
else if (!InternalAreaName.IsEmpty())
|
||||||
|
AreaName = L"!!" + InternalAreaName.ToUTF16();
|
||||||
|
else
|
||||||
|
AreaName = L"!!" + AreaID.ToString().ToUTF16();
|
||||||
|
|
||||||
|
TWideString AreaDir = TWideString::Format(L"%s%02d_%s\\", *WorldDir, iArea, *AreaName);
|
||||||
|
|
||||||
|
// Rename area stuff
|
||||||
|
CResourceEntry *pAreaEntry = pStore->FindEntry(AreaID);
|
||||||
|
ASSERT(pAreaEntry != nullptr);
|
||||||
|
ApplyGeneratedName(pAreaEntry, AreaDir, AreaName);
|
||||||
|
|
||||||
|
if (pAreaNameTable)
|
||||||
|
ApplyGeneratedName(pAreaNameTable->Entry(), AreaDir, pAreaNameTable->Entry()->Name());
|
||||||
|
|
||||||
|
if (pMapWorld)
|
||||||
|
{
|
||||||
|
ASSERT(pMapWorld->Type() == eDependencyGroup);
|
||||||
|
CDependencyGroup *pGroup = static_cast<CDependencyGroup*>(pMapWorld);
|
||||||
|
CAssetID MapID = pGroup->DependencyByIndex(iArea);
|
||||||
|
CResourceEntry *pMapEntry = pStore->FindEntry(MapID);
|
||||||
|
ASSERT(pMapEntry != nullptr);
|
||||||
|
|
||||||
|
ApplyGeneratedName(pMapEntry, AreaDir, TWideString::Format(L"%s_Map", *AreaName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if PROCESS_AREAS
|
||||||
|
// Generate area stuff
|
||||||
|
for (TResourceIterator<eArea> It(pStore); It; ++It)
|
||||||
|
{
|
||||||
|
TWideString AreaDir = It->DirectoryPath();
|
||||||
|
TWideString AreaName = It->Name();
|
||||||
|
CGameArea *pArea = (CGameArea*) It->Load();
|
||||||
|
|
||||||
|
// Area lightmaps
|
||||||
|
TWideString LightmapDir = AreaDir + L"Lightmaps\\";
|
||||||
|
CMaterialSet *pMaterials = pArea->Materials();
|
||||||
|
|
||||||
|
for (u32 iMat = 0; iMat < pMaterials->NumMaterials(); iMat++)
|
||||||
|
{
|
||||||
|
CMaterial *pMat = pMaterials->MaterialByIndex(iMat);
|
||||||
|
|
||||||
|
if (pMat->Options().HasFlag(CMaterial::eLightmap))
|
||||||
|
{
|
||||||
|
CTexture *pLightmapTex = pMat->Pass(0)->Texture();
|
||||||
|
CResourceEntry *pTexEntry = pLightmapTex->Entry();
|
||||||
|
|
||||||
|
TWideString TexName = TWideString::Format(L"lit_%s_%dx%d", *pLightmapTex->ID().ToString().ToUTF16(), pLightmapTex->Width(), pLightmapTex->Height());
|
||||||
|
ApplyGeneratedName(pTexEntry, LightmapDir, TexName);
|
||||||
|
pTexEntry->SetHidden(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate names from script instance names
|
||||||
|
for (u32 iLyr = 0; iLyr < pArea->NumScriptLayers(); iLyr++)
|
||||||
|
{
|
||||||
|
CScriptLayer *pLayer = pArea->ScriptLayer(iLyr);
|
||||||
|
|
||||||
|
for (u32 iInst = 0; iInst < pLayer->NumInstances(); iInst++)
|
||||||
|
{
|
||||||
|
CScriptObject *pInst = pLayer->InstanceByIndex(iInst);
|
||||||
|
|
||||||
|
if (pInst->ObjectTypeID() == 0x42 || pInst->ObjectTypeID() == FOURCC("POIN"))
|
||||||
|
{
|
||||||
|
TString Name = pInst->InstanceName();
|
||||||
|
|
||||||
|
if (Name.EndsWith(".scan"))
|
||||||
|
{
|
||||||
|
TIDString ScanIDString = (pProj->Game() <= ePrime ? "0x4:0x0" : "0xBDBEC295:0xB94E9BE7");
|
||||||
|
TAssetProperty *pScanProperty = TPropCast<TAssetProperty>(pInst->PropertyByIDString(ScanIDString));
|
||||||
|
|
||||||
|
if (pScanProperty)
|
||||||
|
{
|
||||||
|
CAssetID ScanID = pScanProperty->Get();
|
||||||
|
CResourceEntry *pEntry = pStore->FindEntry(ScanID);
|
||||||
|
|
||||||
|
if (pEntry && !pEntry->IsNamed())
|
||||||
|
{
|
||||||
|
TWideString ScanName = Name.ToUTF16().ChopBack(5);
|
||||||
|
ApplyGeneratedName(pEntry, pEntry->DirectoryPath(), ScanName);
|
||||||
|
|
||||||
|
CScan *pScan = (CScan*) pEntry->Load();
|
||||||
|
if (pScan && pScan->ScanText())
|
||||||
|
{
|
||||||
|
CResourceEntry *pStringEntry = pScan->ScanText()->Entry();
|
||||||
|
ApplyGeneratedName(pStringEntry, pStringEntry->DirectoryPath(), ScanName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Other area assets
|
||||||
|
CResourceEntry *pPathEntry = pStore->FindEntry(pArea->PathID());
|
||||||
|
CResourceEntry *pPoiMapEntry = pArea->PoiToWorldMap() ? pArea->PoiToWorldMap()->Entry() : nullptr;
|
||||||
|
CResourceEntry *pPortalEntry = pStore->FindEntry(pArea->PortalAreaID());
|
||||||
|
|
||||||
|
if (pPathEntry)
|
||||||
|
ApplyGeneratedName(pPathEntry, AreaDir, TWideString::Format(L"%s_Path", *AreaName));
|
||||||
|
|
||||||
|
if (pPoiMapEntry)
|
||||||
|
ApplyGeneratedName(pPoiMapEntry, AreaDir, TWideString::Format(L"%s_EGMC", *AreaName));
|
||||||
|
|
||||||
|
if (pPortalEntry)
|
||||||
|
ApplyGeneratedName(pPortalEntry, AreaDir, TWideString::Format(L"%s_PortalArea", *AreaName));
|
||||||
|
|
||||||
|
pStore->DestroyUnreferencedResources();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if PROCESS_MODELS
|
||||||
|
// Generate Model Lightmap names
|
||||||
|
for (TResourceIterator<eModel> It(pStore); It; ++It)
|
||||||
|
{
|
||||||
|
CModel *pModel = (CModel*) It->Load();
|
||||||
|
|
||||||
|
for (u32 iSet = 0; iSet < pModel->GetMatSetCount(); iSet++)
|
||||||
|
{
|
||||||
|
CMaterialSet *pSet = pModel->GetMatSet(iSet);
|
||||||
|
|
||||||
|
for (u32 iMat = 0; iMat < pSet->NumMaterials(); iMat++)
|
||||||
|
{
|
||||||
|
CMaterial *pMat = pSet->MaterialByIndex(iMat);
|
||||||
|
|
||||||
|
if (pMat->Options().HasFlag(CMaterial::eLightmap))
|
||||||
|
{
|
||||||
|
CTexture *pLightmapTex = pMat->Pass(0)->Texture();
|
||||||
|
CResourceEntry *pTexEntry = pLightmapTex->Entry();
|
||||||
|
if (pTexEntry->IsNamed() || pTexEntry->IsCategorized()) continue;
|
||||||
|
|
||||||
|
TWideString TexName = TWideString::Format(L"lit_%s_%dx%d", *pLightmapTex->ID().ToString().ToUTF16(), pLightmapTex->Width(), pLightmapTex->Height());
|
||||||
|
ApplyGeneratedName(pTexEntry, pModel->Entry()->DirectoryPath(), TexName);
|
||||||
|
pTexEntry->SetHidden(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pStore->DestroyUnreferencedResources();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if PROCESS_AUDIO_GROUPS
|
||||||
|
// Generate Audio Group names
|
||||||
|
for (TResourceIterator<eAudioGroup> It(pStore); It; ++It)
|
||||||
|
{
|
||||||
|
CAudioGroup *pGroup = (CAudioGroup*) It->Load();
|
||||||
|
TWideString GroupName = pGroup->GroupName().ToUTF16();
|
||||||
|
ApplyGeneratedName(*It, L"AudioGrp\\", GroupName);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if PROCESS_ANIM_CHAR_SETS
|
||||||
|
// Generate animation format names
|
||||||
|
for (TResourceIterator<eAnimSet> It(pStore); It; ++It)
|
||||||
|
{
|
||||||
|
TWideString SetDir = It->DirectoryPath();
|
||||||
|
TWideString NewSetName;
|
||||||
|
CAnimSet *pSet = (CAnimSet*) It->Load();
|
||||||
|
|
||||||
|
for (u32 iChar = 0; iChar < pSet->NumCharacters(); iChar++)
|
||||||
|
{
|
||||||
|
const SSetCharacter *pkChar = pSet->Character(iChar);
|
||||||
|
|
||||||
|
TWideString CharName = pkChar->Name.ToUTF16();
|
||||||
|
if (iChar == 0) NewSetName = CharName;
|
||||||
|
|
||||||
|
if (pkChar->pModel) ApplyGeneratedName(pkChar->pModel->Entry(), SetDir, CharName);
|
||||||
|
if (pkChar->pSkeleton) ApplyGeneratedName(pkChar->pSkeleton->Entry(), SetDir, CharName);
|
||||||
|
if (pkChar->pSkin) ApplyGeneratedName(pkChar->pSkin->Entry(), SetDir, CharName);
|
||||||
|
|
||||||
|
if (pkChar->IceModel.IsValid() || pkChar->IceSkin.IsValid())
|
||||||
|
{
|
||||||
|
TWideString IceName = TWideString::Format(L"%s_IceOverlay", *CharName);
|
||||||
|
|
||||||
|
if (pkChar->IceModel.IsValid())
|
||||||
|
{
|
||||||
|
CResourceEntry *pIceModelEntry = pStore->FindEntry(pkChar->IceModel);
|
||||||
|
ApplyGeneratedName(pIceModelEntry, SetDir, IceName);
|
||||||
|
}
|
||||||
|
if (pkChar->IceSkin.IsValid())
|
||||||
|
{
|
||||||
|
CResourceEntry *pIceSkinEntry = pStore->FindEntry(pkChar->IceSkin);
|
||||||
|
ApplyGeneratedName(pIceSkinEntry, SetDir, IceName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!NewSetName.IsEmpty())
|
||||||
|
ApplyGeneratedName(*It, SetDir, NewSetName);
|
||||||
|
|
||||||
|
std::set<CAnimPrimitive> AnimPrimitives;
|
||||||
|
pSet->GetUniquePrimitives(AnimPrimitives);
|
||||||
|
|
||||||
|
for (auto It = AnimPrimitives.begin(); It != AnimPrimitives.end(); It++)
|
||||||
|
{
|
||||||
|
const CAnimPrimitive& rkPrim = *It;
|
||||||
|
CAnimation *pAnim = rkPrim.Animation();
|
||||||
|
|
||||||
|
if (pAnim)
|
||||||
|
{
|
||||||
|
ApplyGeneratedName(pAnim->Entry(), SetDir, rkPrim.Name().ToUTF16());
|
||||||
|
CAnimEventData *pEvents = pAnim->EventData();
|
||||||
|
|
||||||
|
if (pEvents)
|
||||||
|
ApplyGeneratedName(pEvents->Entry(), SetDir, rkPrim.Name().ToUTF16());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if PROCESS_STRINGS
|
||||||
|
// Generate string names
|
||||||
|
for (TResourceIterator<eStringTable> It(pStore); It; ++It)
|
||||||
|
{
|
||||||
|
CStringTable *pString = (CStringTable*) It->Load();
|
||||||
|
if (pString->Entry()->IsNamed()) continue;
|
||||||
|
TWideString String;
|
||||||
|
|
||||||
|
for (u32 iStr = 0; iStr < pString->NumStrings() && String.IsEmpty(); iStr++)
|
||||||
|
String = CStringTable::StripFormatting( pString->String("ENGL", iStr) ).Trimmed();
|
||||||
|
|
||||||
|
if (!String.IsEmpty())
|
||||||
|
{
|
||||||
|
TWideString Name = String.SubString(0, Math::Min<u32>(String.Size(), 50)).Trimmed();
|
||||||
|
Name.Replace(L"\n", L" ");
|
||||||
|
|
||||||
|
while (Name.EndsWith(L".") || TWideString::IsWhitespace(Name.Back()))
|
||||||
|
Name = Name.ChopBack(1);
|
||||||
|
|
||||||
|
ApplyGeneratedName(pString->Entry(), pString->Entry()->DirectoryPath(), Name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if PROCESS_SCANS
|
||||||
|
// Generate scan names
|
||||||
|
for (TResourceIterator<eScan> It(pStore); It; ++It)
|
||||||
|
{
|
||||||
|
if (It->IsNamed()) continue;
|
||||||
|
CScan *pScan = (CScan*) It->Load();
|
||||||
|
TWideString ScanName;
|
||||||
|
|
||||||
|
if (pProj->Game() >= eEchoesDemo)
|
||||||
|
{
|
||||||
|
CAssetID DisplayAsset = pScan->LogbookDisplayAssetID();
|
||||||
|
CResourceEntry *pEntry = pStore->FindEntry(DisplayAsset);
|
||||||
|
if (pEntry && pEntry->IsNamed()) ScanName = pEntry->Name();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ScanName.IsEmpty())
|
||||||
|
{
|
||||||
|
CStringTable *pString = pScan->ScanText();
|
||||||
|
if (pString) ScanName = pString->Entry()->Name();
|
||||||
|
}
|
||||||
|
|
||||||
|
ApplyGeneratedName(pScan->Entry(), It->DirectoryPath(), ScanName);
|
||||||
|
|
||||||
|
if (!ScanName.IsEmpty() && pProj->Game() <= ePrime)
|
||||||
|
{
|
||||||
|
CAssetID FrameID = pScan->GuiFrame();
|
||||||
|
CResourceEntry *pEntry = pStore->FindEntry(FrameID);
|
||||||
|
if (pEntry) ApplyGeneratedName(pEntry, pEntry->DirectoryPath(), L"ScanFrame");
|
||||||
|
|
||||||
|
for (u32 iImg = 0; iImg < 4; iImg++)
|
||||||
|
{
|
||||||
|
CAssetID ImageID = pScan->ScanImage(iImg);
|
||||||
|
CResourceEntry *pImgEntry = pStore->FindEntry(ImageID);
|
||||||
|
if (pImgEntry) ApplyGeneratedName(pImgEntry, pImgEntry->DirectoryPath(), TWideString::Format(L"%s_Image%d", *ScanName, iImg));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if PROCESS_FONTS
|
||||||
|
// Generate font names
|
||||||
|
for (TResourceIterator<eFont> It(pStore); It; ++It)
|
||||||
|
{
|
||||||
|
CFont *pFont = (CFont*) It->Load();
|
||||||
|
|
||||||
|
if (pFont)
|
||||||
|
{
|
||||||
|
ApplyGeneratedName(pFont->Entry(), pFont->Entry()->DirectoryPath(), pFont->FontName().ToUTF16());
|
||||||
|
|
||||||
|
CTexture *pFontTex = pFont->Texture();
|
||||||
|
|
||||||
|
if (pFontTex)
|
||||||
|
ApplyGeneratedName(pFontTex->Entry(), pFont->Entry()->DirectoryPath(), pFont->Entry()->Name());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
pStore->ConditionalSaveStore();
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
#ifndef ASSETNAMEGENERATION
|
||||||
|
#define ASSETNAMEGENERATION
|
||||||
|
|
||||||
|
class CGameProject;
|
||||||
|
void GenerateAssetNames(CGameProject *pProj);
|
||||||
|
|
||||||
|
#endif // ASSETNAMEGENERATION
|
||||||
|
|
|
@ -0,0 +1,95 @@
|
||||||
|
#ifndef CASSETNAMEMAP
|
||||||
|
#define CASSETNAMEMAP
|
||||||
|
|
||||||
|
#include "CResourceIterator.h"
|
||||||
|
#include "CResourceStore.h"
|
||||||
|
#include <Common/CAssetID.h>
|
||||||
|
#include <Common/Serialization/XML.h>
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
const TString gkAssetListDir = "..\\resources\\list\\";
|
||||||
|
|
||||||
|
struct SAssetNameInfo
|
||||||
|
{
|
||||||
|
TWideString Name;
|
||||||
|
TWideString Directory;
|
||||||
|
|
||||||
|
void Serialize(IArchive& rArc)
|
||||||
|
{
|
||||||
|
rArc << SERIAL_AUTO(Name) << SERIAL_AUTO(Directory);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class CAssetNameMap
|
||||||
|
{
|
||||||
|
typedef std::map<CAssetID, SAssetNameInfo> TAssetMap;
|
||||||
|
std::shared_ptr<TAssetMap> mpMap;
|
||||||
|
|
||||||
|
void Serialize(IArchive& rArc)
|
||||||
|
{
|
||||||
|
rArc << SERIAL_CONTAINER("AssetNameMap", *mpMap.get(), "Asset");
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
CAssetNameMap()
|
||||||
|
{
|
||||||
|
mpMap = std::make_shared<TAssetMap>(TAssetMap());
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetNameInfo(CAssetID ID, TString& rOutDirectory, TString& rOutName)
|
||||||
|
{
|
||||||
|
auto It = mpMap->find(ID);
|
||||||
|
|
||||||
|
if (It != mpMap->end())
|
||||||
|
{
|
||||||
|
SAssetNameInfo& rInfo = It->second;
|
||||||
|
rOutName = rInfo.Name;
|
||||||
|
rOutDirectory = rInfo.Directory;
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rOutDirectory = "Uncategorized\\";
|
||||||
|
rOutName = ID.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static TString GetAssetListPath(EGame Game)
|
||||||
|
{
|
||||||
|
return gkAssetListDir + "AssetList" + GetGameShortName(Game) + ".xml";
|
||||||
|
}
|
||||||
|
|
||||||
|
static CAssetNameMap LoadAssetNames(EGame Game)
|
||||||
|
{
|
||||||
|
TString ListPath = GetAssetListPath(Game);
|
||||||
|
CXMLReader Reader(ListPath);
|
||||||
|
|
||||||
|
CAssetNameMap Map;
|
||||||
|
Map.Serialize(Reader);
|
||||||
|
return Map;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void SaveAssetNames(CResourceStore *pStore = gpResourceStore)
|
||||||
|
{
|
||||||
|
CAssetNameMap Map;
|
||||||
|
|
||||||
|
for (CResourceIterator It(pStore); It; ++It)
|
||||||
|
{
|
||||||
|
if (It->IsCategorized() || It->IsNamed())
|
||||||
|
{
|
||||||
|
CAssetID ID = It->ID();
|
||||||
|
TWideString Name = It->Name();
|
||||||
|
TWideString Directory = It->Directory()->FullPath();
|
||||||
|
(*Map.mpMap)[ID] = SAssetNameInfo { Name, Directory };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TString ListPath = GetAssetListPath(pStore->Game());
|
||||||
|
CXMLWriter Writer(ListPath, "AssetList", 0, pStore->Game());
|
||||||
|
Map.Serialize(Writer);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // CASSETNAMEMAP
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
#define COPY_DISC_DATA 1
|
#define COPY_DISC_DATA 1
|
||||||
#define LOAD_PAKS 1
|
#define LOAD_PAKS 1
|
||||||
#define SAVE_PACKAGE_DEFINITIONS 1
|
#define SAVE_PACKAGE_DEFINITIONS 1
|
||||||
#define EXPORT_WORLDS 1
|
#define USE_ASSET_NAME_MAP 0
|
||||||
#define EXPORT_COOKED 1
|
#define EXPORT_COOKED 1
|
||||||
|
|
||||||
CGameExporter::CGameExporter(const TString& rkInputDir, const TString& rkOutputDir)
|
CGameExporter::CGameExporter(const TString& rkInputDir, const TString& rkOutputDir)
|
||||||
|
@ -50,13 +50,15 @@ bool CGameExporter::Export()
|
||||||
mContentDir = mpStore->RawDir(false);
|
mContentDir = mpStore->RawDir(false);
|
||||||
mCookedDir = mpStore->CookedDir(false);
|
mCookedDir = mpStore->CookedDir(false);
|
||||||
|
|
||||||
|
#if USE_ASSET_NAME_MAP
|
||||||
|
mNameMap = CAssetNameMap::LoadAssetNames(mGame);
|
||||||
|
#endif
|
||||||
|
|
||||||
// Export game data
|
// Export game data
|
||||||
CResourceStore *pOldStore = gpResourceStore;
|
CResourceStore *pOldStore = gpResourceStore;
|
||||||
gpResourceStore = mpStore;
|
gpResourceStore = mpStore;
|
||||||
|
|
||||||
LoadAssetList();
|
|
||||||
LoadPaks();
|
LoadPaks();
|
||||||
ExportWorlds();
|
|
||||||
ExportCookedResources();
|
ExportCookedResources();
|
||||||
mpProject->AudioManager()->LoadAssets();
|
mpProject->AudioManager()->LoadAssets();
|
||||||
ExportResourceEditorData();
|
ExportResourceEditorData();
|
||||||
|
@ -128,57 +130,6 @@ void CGameExporter::CopyDiscData()
|
||||||
ASSERT(mGame != eUnknownGame);
|
ASSERT(mGame != eUnknownGame);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGameExporter::LoadAssetList()
|
|
||||||
{
|
|
||||||
SCOPED_TIMER(LoadAssetList);
|
|
||||||
|
|
||||||
// Determine the asset list to use
|
|
||||||
TString ListFile = "../resources/list/AssetList";
|
|
||||||
|
|
||||||
switch (mGame)
|
|
||||||
{
|
|
||||||
case ePrimeDemo: ListFile += "MP1Demo"; break;
|
|
||||||
case ePrime: ListFile += "MP1"; break;
|
|
||||||
case eEchoesDemo: ListFile += "MP2Demo"; break;
|
|
||||||
case eEchoes: ListFile += "MP2"; break;
|
|
||||||
case eCorruptionProto: ListFile += "MP3Proto"; break;
|
|
||||||
case eCorruption: ListFile += "MP3"; break;
|
|
||||||
case eReturns: ListFile += "DKCR"; break;
|
|
||||||
default: ASSERT(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
ListFile += ".xml";
|
|
||||||
|
|
||||||
// Load list
|
|
||||||
tinyxml2::XMLDocument List;
|
|
||||||
List.LoadFile(*ListFile);
|
|
||||||
|
|
||||||
if (List.Error())
|
|
||||||
{
|
|
||||||
Log::Error("Couldn't open asset list: " + ListFile);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
tinyxml2::XMLElement *pRoot = List.FirstChildElement("AssetList");
|
|
||||||
tinyxml2::XMLElement *pAsset = pRoot->FirstChildElement("Asset");
|
|
||||||
|
|
||||||
while (pAsset)
|
|
||||||
{
|
|
||||||
CAssetID ResourceID = TString(pAsset->Attribute("ID")).ToInt64(16);
|
|
||||||
|
|
||||||
tinyxml2::XMLElement *pDir = pAsset->FirstChildElement("Dir");
|
|
||||||
TString Dir = pDir ? pDir->GetText() : "";
|
|
||||||
|
|
||||||
tinyxml2::XMLElement *pName = pAsset->FirstChildElement("Name");
|
|
||||||
TString Name = pName ? pName->GetText() : "";
|
|
||||||
|
|
||||||
if (!Dir.EndsWith("/") && !Dir.EndsWith("\\")) Dir.Append("\\");
|
|
||||||
SetResourcePath(ResourceID, Dir.ToUTF16(), Name.ToUTF16());
|
|
||||||
|
|
||||||
pAsset = pAsset->NextSiblingElement("Asset");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ************ RESOURCE LOADING ************
|
// ************ RESOURCE LOADING ************
|
||||||
void CGameExporter::LoadPaks()
|
void CGameExporter::LoadPaks()
|
||||||
{
|
{
|
||||||
|
@ -221,7 +172,6 @@ void CGameExporter::LoadPaks()
|
||||||
u32 NameLen = Pak.ReadLong();
|
u32 NameLen = Pak.ReadLong();
|
||||||
TString Name = Pak.ReadString(NameLen);
|
TString Name = Pak.ReadString(NameLen);
|
||||||
pCollection->AddResource(Name, ResID, ResType);
|
pCollection->AddResource(Name, ResID, ResType);
|
||||||
SetResourcePath(ResID, PakName + L"\\", Name.ToUTF16());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 NumResources = Pak.ReadLong();
|
u32 NumResources = Pak.ReadLong();
|
||||||
|
@ -296,7 +246,6 @@ void CGameExporter::LoadPaks()
|
||||||
CFourCC ResType = Pak.ReadLong();
|
CFourCC ResType = Pak.ReadLong();
|
||||||
CAssetID ResID(Pak, mGame);
|
CAssetID ResID(Pak, mGame);
|
||||||
pCollection->AddResource(Name, ResID, ResType);
|
pCollection->AddResource(Name, ResID, ResType);
|
||||||
SetResourcePath(ResID, PakName + L"\\", Name.ToUTF16());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -448,84 +397,6 @@ void CGameExporter::LoadResource(const SResourceInstance& rkResource, std::vecto
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGameExporter::ExportWorlds()
|
|
||||||
{
|
|
||||||
#if EXPORT_WORLDS
|
|
||||||
SCOPED_TIMER(ExportWorlds);
|
|
||||||
|
|
||||||
for (u32 iPak = 0; iPak < mpProject->NumPackages(); iPak++)
|
|
||||||
{
|
|
||||||
CPackage *pPak = mpProject->PackageByIndex(iPak);
|
|
||||||
|
|
||||||
// Get output path. DKCR paks are stored in a Worlds folder so we should get the path relative to that so we don't have Worlds\Worlds\.
|
|
||||||
// Other games have all paks in the game root dir so we're fine just taking the original root dir-relative directory.
|
|
||||||
TWideString PakPath = pPak->Path();
|
|
||||||
TWideString GameWorldsDir = PakPath.GetParentDirectoryPath(L"Worlds", false);
|
|
||||||
|
|
||||||
if (!GameWorldsDir.IsEmpty())
|
|
||||||
PakPath = FileUtil::MakeRelative(PakPath, GameWorldsDir);
|
|
||||||
|
|
||||||
// Note since there's no collections in the cooked data we're guaranteed that every pak will have exactly one collection.
|
|
||||||
CResourceCollection *pCollection = pPak->CollectionByIndex(0);
|
|
||||||
|
|
||||||
for (u32 iRes = 0; iRes < pCollection->NumResources(); iRes++)
|
|
||||||
{
|
|
||||||
const SNamedResource& rkRes = pCollection->ResourceByIndex(iRes);
|
|
||||||
|
|
||||||
if (rkRes.Type == "MLVL" && !rkRes.Name.EndsWith("NODEPEND"))
|
|
||||||
{
|
|
||||||
// Load world
|
|
||||||
CWorld *pWorld = (CWorld*) mpStore->LoadResource(rkRes.ID, rkRes.Type);
|
|
||||||
|
|
||||||
if (!pWorld)
|
|
||||||
{
|
|
||||||
Log::Error("Couldn't load world " + rkRes.Name + " from package " + pPak->Name() + "; unable to export");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Export world
|
|
||||||
TWideString Name = rkRes.Name.ToUTF16();
|
|
||||||
TWideString WorldDir = mWorldsDirName + PakPath + FileUtil::SanitizeName(Name, true) + L"\\";
|
|
||||||
FileUtil::CreateDirectory(mCookedDir + WorldDir);
|
|
||||||
|
|
||||||
SResourceInstance *pInst = FindResourceInstance(rkRes.ID);
|
|
||||||
ASSERT(pInst != nullptr);
|
|
||||||
|
|
||||||
SetResourcePath(rkRes.ID, WorldDir, Name);
|
|
||||||
ExportResource(*pInst);
|
|
||||||
|
|
||||||
// Export areas
|
|
||||||
for (u32 iArea = 0; iArea < pWorld->NumAreas(); iArea++)
|
|
||||||
{
|
|
||||||
// Determine area names
|
|
||||||
TWideString InternalAreaName = pWorld->AreaInternalName(iArea).ToUTF16();
|
|
||||||
bool HasInternalName = !InternalAreaName.IsEmpty();
|
|
||||||
if (!HasInternalName) InternalAreaName = TWideString::FromInt32(iArea, 2, 10);
|
|
||||||
|
|
||||||
TWideString GameAreaName;
|
|
||||||
CStringTable *pTable = pWorld->AreaName(iArea);
|
|
||||||
if (pTable) GameAreaName = pTable->String("ENGL", 0);
|
|
||||||
if (GameAreaName.IsEmpty()) GameAreaName = InternalAreaName;
|
|
||||||
|
|
||||||
// Export area
|
|
||||||
TWideString AreaDir = WorldDir + TWideString::FromInt32(iArea, 2, 10) + L"_" + FileUtil::SanitizeName(GameAreaName, true) + L"\\";
|
|
||||||
FileUtil::CreateDirectory(mCookedDir + AreaDir);
|
|
||||||
|
|
||||||
CAssetID AreaID = pWorld->AreaResourceID(iArea);
|
|
||||||
SResourceInstance *pInst = FindResourceInstance(AreaID);
|
|
||||||
ASSERT(pInst != nullptr);
|
|
||||||
|
|
||||||
SetResourcePath(AreaID, AreaDir, GameAreaName);
|
|
||||||
ExportResource(*pInst);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mpStore->DestroyUnreferencedResources();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void CGameExporter::ExportCookedResources()
|
void CGameExporter::ExportCookedResources()
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
|
@ -597,21 +468,10 @@ void CGameExporter::ExportResource(SResourceInstance& rRes)
|
||||||
std::vector<u8> ResourceData;
|
std::vector<u8> ResourceData;
|
||||||
LoadResource(rRes, ResourceData);
|
LoadResource(rRes, ResourceData);
|
||||||
|
|
||||||
// Determine output path
|
|
||||||
SResourcePath *pPath = FindResourcePath(rRes.ResourceID);
|
|
||||||
TWideString OutName, OutDir;
|
|
||||||
|
|
||||||
if (pPath)
|
|
||||||
{
|
|
||||||
OutName = pPath->Name;
|
|
||||||
OutDir = pPath->Dir;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (OutName.IsEmpty()) OutName = rRes.ResourceID.ToString().ToUTF16();
|
|
||||||
if (OutDir.IsEmpty()) OutDir = L"Uncategorized\\";
|
|
||||||
|
|
||||||
// Register resource and write to file
|
// Register resource and write to file
|
||||||
CResourceEntry *pEntry = mpStore->RegisterResource(rRes.ResourceID, CResource::ResTypeForExtension(rRes.ResourceType), OutDir, OutName);
|
TString Directory, Name;
|
||||||
|
mNameMap.GetNameInfo(rRes.ResourceID, Directory, Name);
|
||||||
|
CResourceEntry *pEntry = mpStore->RegisterResource(rRes.ResourceID, CResource::ResTypeForExtension(rRes.ResourceType), Directory, Name);
|
||||||
|
|
||||||
#if EXPORT_COOKED
|
#if EXPORT_COOKED
|
||||||
// Save cooked asset
|
// Save cooked asset
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#ifndef CGAMEEXPORTER_H
|
#ifndef CGAMEEXPORTER_H
|
||||||
#define CGAMEEXPORTER_H
|
#define CGAMEEXPORTER_H
|
||||||
|
|
||||||
|
#include "CAssetNameMap.h"
|
||||||
#include "CGameProject.h"
|
#include "CGameProject.h"
|
||||||
#include "CResourceStore.h"
|
#include "CResourceStore.h"
|
||||||
#include <Common/CAssetID.h>
|
#include <Common/CAssetID.h>
|
||||||
|
@ -28,6 +29,7 @@ class CGameExporter
|
||||||
// Resources
|
// Resources
|
||||||
TWideStringList mPaks;
|
TWideStringList mPaks;
|
||||||
std::map<CAssetID, bool> mAreaDuplicateMap;
|
std::map<CAssetID, bool> mAreaDuplicateMap;
|
||||||
|
CAssetNameMap mNameMap;
|
||||||
|
|
||||||
struct SResourceInstance
|
struct SResourceInstance
|
||||||
{
|
{
|
||||||
|
@ -41,13 +43,6 @@ class CGameExporter
|
||||||
};
|
};
|
||||||
std::map<CAssetID, SResourceInstance> mResourceMap;
|
std::map<CAssetID, SResourceInstance> mResourceMap;
|
||||||
|
|
||||||
struct SResourcePath
|
|
||||||
{
|
|
||||||
TWideString Dir;
|
|
||||||
TWideString Name;
|
|
||||||
};
|
|
||||||
std::map<CAssetID, SResourcePath> mResourcePaths;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CGameExporter(const TString& rkInputDir, const TString& rkOutputDir);
|
CGameExporter(const TString& rkInputDir, const TString& rkOutputDir);
|
||||||
bool Export();
|
bool Export();
|
||||||
|
@ -55,10 +50,8 @@ public:
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void CopyDiscData();
|
void CopyDiscData();
|
||||||
void LoadAssetList();
|
|
||||||
void LoadPaks();
|
void LoadPaks();
|
||||||
void LoadResource(const SResourceInstance& rkResource, std::vector<u8>& rBuffer);
|
void LoadResource(const SResourceInstance& rkResource, std::vector<u8>& rBuffer);
|
||||||
void ExportWorlds();
|
|
||||||
void ExportCookedResources();
|
void ExportCookedResources();
|
||||||
void ExportResourceEditorData();
|
void ExportResourceEditorData();
|
||||||
void ExportResource(SResourceInstance& rRes);
|
void ExportResource(SResourceInstance& rRes);
|
||||||
|
@ -70,18 +63,6 @@ protected:
|
||||||
auto Found = mResourceMap.find(IntegralID);
|
auto Found = mResourceMap.find(IntegralID);
|
||||||
return (Found == mResourceMap.end() ? nullptr : &Found->second);
|
return (Found == mResourceMap.end() ? nullptr : &Found->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline SResourcePath* FindResourcePath(const CAssetID& rkID)
|
|
||||||
{
|
|
||||||
u64 IntegralID = rkID.ToLongLong();
|
|
||||||
auto Found = mResourcePaths.find(IntegralID);
|
|
||||||
return (Found == mResourcePaths.end() ? nullptr : &Found->second);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void SetResourcePath(const CAssetID& rkID, const TWideString& rkDir, const TWideString& rkName)
|
|
||||||
{
|
|
||||||
mResourcePaths[rkID] = SResourcePath { rkDir, rkName };
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // CGAMEEXPORTER_H
|
#endif // CGAMEEXPORTER_H
|
||||||
|
|
|
@ -7,6 +7,8 @@ CGameProject *CGameProject::mspActiveProject = nullptr;
|
||||||
|
|
||||||
CGameProject::~CGameProject()
|
CGameProject::~CGameProject()
|
||||||
{
|
{
|
||||||
|
ASSERT(!mpResourceStore->IsDirty());
|
||||||
|
|
||||||
if (IsActive())
|
if (IsActive())
|
||||||
mspActiveProject = nullptr;
|
mspActiveProject = nullptr;
|
||||||
|
|
||||||
|
|
|
@ -47,9 +47,9 @@ public:
|
||||||
, mProjectRoot(rkProjRootDir)
|
, mProjectRoot(rkProjRootDir)
|
||||||
, mResourceDBPath(L"ResourceDB.rdb")
|
, mResourceDBPath(L"ResourceDB.rdb")
|
||||||
{
|
{
|
||||||
|
mProjectRoot.Replace(L"/", L"\\");
|
||||||
mpResourceStore = new CResourceStore(this);
|
mpResourceStore = new CResourceStore(this);
|
||||||
mpAudioManager = new CAudioManager(this);
|
mpAudioManager = new CAudioManager(this);
|
||||||
mProjectRoot.Replace(L"/", L"\\");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CGameProject(CGameExporter *pExporter, const TWideString& rkProjRootDir, EGame Game)
|
CGameProject(CGameExporter *pExporter, const TWideString& rkProjRootDir, EGame Game)
|
||||||
|
@ -58,9 +58,9 @@ public:
|
||||||
, mProjectRoot(rkProjRootDir)
|
, mProjectRoot(rkProjRootDir)
|
||||||
, mResourceDBPath(L"ResourceDB.rdb")
|
, mResourceDBPath(L"ResourceDB.rdb")
|
||||||
{
|
{
|
||||||
|
mProjectRoot.Replace(L"/", L"\\");
|
||||||
mpResourceStore = new CResourceStore(this, pExporter, L"Content\\", L"Cooked\\", Game);
|
mpResourceStore = new CResourceStore(this, pExporter, L"Content\\", L"Cooked\\", Game);
|
||||||
mpAudioManager = new CAudioManager(this);
|
mpAudioManager = new CAudioManager(this);
|
||||||
mProjectRoot.Replace(L"/", L"\\");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
~CGameProject();
|
~CGameProject();
|
||||||
|
@ -95,6 +95,4 @@ public:
|
||||||
static inline CGameProject* ActiveProject() { return mspActiveProject; }
|
static inline CGameProject* ActiveProject() { return mspActiveProject; }
|
||||||
};
|
};
|
||||||
|
|
||||||
extern CGameProject *gpProject;
|
|
||||||
|
|
||||||
#endif // CGAMEPROJECT_H
|
#endif // CGAMEPROJECT_H
|
||||||
|
|
|
@ -196,9 +196,10 @@ bool CResourceEntry::Save(bool SkipCacheSave /*= false*/)
|
||||||
|
|
||||||
// Resource has been saved, now update dependencies + cache file
|
// Resource has been saved, now update dependencies + cache file
|
||||||
UpdateDependencies();
|
UpdateDependencies();
|
||||||
|
mpStore->SetCacheDataDirty();
|
||||||
|
|
||||||
if (!SkipCacheSave)
|
if (!SkipCacheSave)
|
||||||
mpStore->SaveCacheFile();
|
mpStore->ConditionalSaveStore();
|
||||||
|
|
||||||
if (ShouldCollectGarbage)
|
if (ShouldCollectGarbage)
|
||||||
mpStore->DestroyUnreferencedResources();
|
mpStore->DestroyUnreferencedResources();
|
||||||
|
@ -277,40 +278,92 @@ bool CResourceEntry::Unload()
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CResourceEntry::Move(const TWideString& rkDir, const TWideString& rkName)
|
bool CResourceEntry::CanMoveTo(const TWideString& rkDir, const TWideString& rkName)
|
||||||
{
|
{
|
||||||
|
// Transient resources can't be moved
|
||||||
|
if (IsTransient()) return false;
|
||||||
|
|
||||||
|
// Validate that the path/name are valid file paths
|
||||||
|
if (!FileUtil::IsValidPath(rkDir, true) || !FileUtil::IsValidName(rkName, false)) return false;
|
||||||
|
|
||||||
|
// We need to validate the path isn't taken already - either the directory doesn't exist, or doesn't have a resource by this name
|
||||||
|
CVirtualDirectory *pDir = mpStore->GetVirtualDirectory(rkDir, false, false);
|
||||||
|
if (pDir && pDir->FindChildResource(rkName, mType)) return false;
|
||||||
|
|
||||||
|
// All checks are true
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CResourceEntry::Move(const TWideString& rkDir, const TWideString& rkName)
|
||||||
|
{
|
||||||
|
if (!CanMoveTo(rkDir, rkName)) return false;
|
||||||
|
|
||||||
// Store old paths
|
// Store old paths
|
||||||
|
CVirtualDirectory *pOldDir = mpDirectory;
|
||||||
|
TWideString OldName = mName;
|
||||||
TString OldCookedPath = CookedAssetPath();
|
TString OldCookedPath = CookedAssetPath();
|
||||||
TString OldRawPath = RawAssetPath();
|
TString OldRawPath = RawAssetPath();
|
||||||
|
|
||||||
// Set new directory and name
|
// Set new directory and name
|
||||||
bool HasDirectory = mpDirectory != nullptr;
|
|
||||||
CVirtualDirectory *pNewDir = mpStore->GetVirtualDirectory(rkDir, IsTransient(), true);
|
CVirtualDirectory *pNewDir = mpStore->GetVirtualDirectory(rkDir, IsTransient(), true);
|
||||||
|
if (pNewDir == mpDirectory && rkName == mName) return false;
|
||||||
|
|
||||||
if (pNewDir != mpDirectory)
|
// Check if we can legally move to this spot
|
||||||
|
ASSERT(pNewDir->FindChildResource(rkName, mType) == nullptr); // this check should be guaranteed to pass due to CanMoveTo() having already checked it
|
||||||
|
|
||||||
|
mpDirectory = pNewDir;
|
||||||
|
mName = rkName;
|
||||||
|
TString NewCookedPath = CookedAssetPath();
|
||||||
|
TString NewRawPath = RawAssetPath();
|
||||||
|
|
||||||
|
// If the old/new paths are the same then we should have already exited as CanMoveTo() should have returned false
|
||||||
|
ASSERT(OldCookedPath != NewCookedPath && OldRawPath != NewRawPath);
|
||||||
|
|
||||||
|
// The cooked/raw asset paths should not exist right now!!!
|
||||||
|
bool FSMoveSuccess = false;
|
||||||
|
|
||||||
|
if (!HasRawVersion() && !HasCookedVersion())
|
||||||
{
|
{
|
||||||
if (mpDirectory)
|
FSMoveSuccess = true;
|
||||||
mpDirectory->RemoveChildResource(this);
|
|
||||||
mpDirectory = pNewDir;
|
if (FileUtil::Exists(OldRawPath))
|
||||||
|
{
|
||||||
|
FSMoveSuccess = FileUtil::CopyFile(OldRawPath, NewRawPath);
|
||||||
|
if (!FSMoveSuccess) FileUtil::DeleteFile(NewRawPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FSMoveSuccess && FileUtil::Exists(OldCookedPath))
|
||||||
|
{
|
||||||
|
FSMoveSuccess = FileUtil::CopyFile(OldCookedPath, NewCookedPath);
|
||||||
|
if (!FSMoveSuccess) FileUtil::DeleteFile(NewCookedPath);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mName != rkName)
|
// If we succeeded, finish the move
|
||||||
ASSERT(mpDirectory->FindChildResource(rkName) == nullptr);
|
if (FSMoveSuccess)
|
||||||
|
|
||||||
mName = rkName;
|
|
||||||
mCachedUppercaseName = rkName.ToUpper();
|
|
||||||
|
|
||||||
// Move files
|
|
||||||
if (HasDirectory)
|
|
||||||
{
|
{
|
||||||
TString CookedPath = CookedAssetPath();
|
if (mpDirectory != pOldDir)
|
||||||
TString RawPath = RawAssetPath();
|
{
|
||||||
|
FSMoveSuccess = pOldDir->RemoveChildResource(this);
|
||||||
|
ASSERT(FSMoveSuccess == true); // this shouldn't be able to fail
|
||||||
|
mpDirectory->AddChild(L"", this);
|
||||||
|
mpStore->ConditionalDeleteDirectory(pOldDir);
|
||||||
|
}
|
||||||
|
|
||||||
if (FileUtil::Exists(OldCookedPath) && CookedPath != OldCookedPath)
|
mpStore->SetDatabaseDirty();
|
||||||
FileUtil::MoveFile(OldCookedPath, CookedPath);
|
mCachedUppercaseName = rkName.ToUpper();
|
||||||
|
FileUtil::DeleteFile(OldRawPath);
|
||||||
|
FileUtil::DeleteFile(OldCookedPath);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (FileUtil::Exists(OldRawPath) && RawPath != OldRawPath)
|
// Otherwise, revert changes and let the caller know the move failed
|
||||||
FileUtil::MoveFile(OldRawPath, RawPath);
|
else
|
||||||
|
{
|
||||||
|
mpDirectory = pOldDir;
|
||||||
|
mName = OldName;
|
||||||
|
mpStore->ConditionalDeleteDirectory(pNewDir);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,12 +14,13 @@ class CDependencyTree;
|
||||||
|
|
||||||
enum EResEntryFlag
|
enum EResEntryFlag
|
||||||
{
|
{
|
||||||
eREF_NeedsRecook = 0x1, // Resource has been updated but not recooked
|
eREF_NeedsRecook = 0x00000001, // Resource has been updated but not recooked
|
||||||
eREF_Transient = 0x2, // Resource is transient (not part of a game project resource DB)
|
eREF_Transient = 0x00000002, // Resource is transient (not part of a game project resource DB)
|
||||||
eREF_HasThumbnail = 0x4, // Resource has a unique thumbnail
|
eREF_HasThumbnail = 0x00000004, // Resource has a unique thumbnail
|
||||||
eREF_ThumbnailLoaded = 0x8, // Resource thumbnail is currently in memory
|
eREF_ThumbnailLoaded = 0x00000008, // Resource thumbnail is currently in memory
|
||||||
|
eREF_Hidden = 0x00000010, // Resource is hidden, doesn't show up in resource browser
|
||||||
// Flags that save to the cache file
|
// Flags that save to the cache file
|
||||||
eREF_SavedFlags = eREF_NeedsRecook | eREF_HasThumbnail
|
eREF_SavedFlags = eREF_NeedsRecook | eREF_HasThumbnail | eREF_Hidden
|
||||||
};
|
};
|
||||||
DECLARE_FLAGS(EResEntryFlag, FResEntryFlags)
|
DECLARE_FLAGS(EResEntryFlag, FResEntryFlags)
|
||||||
|
|
||||||
|
@ -61,24 +62,30 @@ public:
|
||||||
CResource* Load();
|
CResource* Load();
|
||||||
CResource* LoadCooked(IInputStream& rInput);
|
CResource* LoadCooked(IInputStream& rInput);
|
||||||
bool Unload();
|
bool Unload();
|
||||||
void Move(const TWideString& rkDir, const TWideString& rkName);
|
bool CanMoveTo(const TWideString& rkDir, const TWideString& rkName);
|
||||||
|
bool Move(const TWideString& rkDir, const TWideString& rkName);
|
||||||
void AddToProject(const TWideString& rkDir, const TWideString& rkName);
|
void AddToProject(const TWideString& rkDir, const TWideString& rkName);
|
||||||
void RemoveFromProject();
|
void RemoveFromProject();
|
||||||
|
|
||||||
// Accessors
|
// Accessors
|
||||||
void SetDirty() { mFlags.SetFlag(eREF_NeedsRecook); }
|
void SetDirty() { mFlags.SetFlag(eREF_NeedsRecook); }
|
||||||
|
void SetHidden(bool Hidden) { Hidden ? mFlags.SetFlag(eREF_Hidden) : mFlags.ClearFlag(eREF_Hidden); }
|
||||||
|
|
||||||
inline bool IsLoaded() const { return mpResource != nullptr; }
|
inline bool IsLoaded() const { return mpResource != nullptr; }
|
||||||
|
inline bool IsCategorized() const { return mpDirectory && mpDirectory->FullPath() != L"Uncategorized\\"; }
|
||||||
|
inline bool IsNamed() const { return mName != mID.ToString().ToUTF16(); }
|
||||||
inline CResource* Resource() const { return mpResource; }
|
inline CResource* Resource() const { return mpResource; }
|
||||||
inline CResourceStore* ResourceStore() const { return mpStore; }
|
inline CResourceStore* ResourceStore() const { return mpStore; }
|
||||||
inline CDependencyTree* Dependencies() const { return mpDependencies; }
|
inline CDependencyTree* Dependencies() const { return mpDependencies; }
|
||||||
inline CAssetID ID() const { return mID; }
|
inline CAssetID ID() const { return mID; }
|
||||||
inline EGame Game() const { return mGame; }
|
inline EGame Game() const { return mGame; }
|
||||||
inline CVirtualDirectory* Directory() const { return mpDirectory; }
|
inline CVirtualDirectory* Directory() const { return mpDirectory; }
|
||||||
|
inline TWideString DirectoryPath() const { return mpDirectory->FullPath(); }
|
||||||
inline TWideString Name() const { return mName; }
|
inline TWideString Name() const { return mName; }
|
||||||
inline const TWideString& UppercaseName() const { return mCachedUppercaseName; }
|
inline const TWideString& UppercaseName() const { return mCachedUppercaseName; }
|
||||||
inline EResType ResourceType() const { return mType; }
|
inline EResType ResourceType() const { return mType; }
|
||||||
inline bool IsTransient() const { return mFlags.HasFlag(eREF_Transient); }
|
inline bool IsTransient() const { return mFlags.HasFlag(eREF_Transient); }
|
||||||
|
inline bool IsHidden() const { return mFlags.HasFlag(eREF_Hidden); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
CResource* InternalLoad(IInputStream& rInput);
|
CResource* InternalLoad(IInputStream& rInput);
|
||||||
|
|
|
@ -66,14 +66,14 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<class ResType>
|
template<EResType ResType>
|
||||||
class TResourceIterator : public CResourceIterator
|
class TResourceIterator : public CResourceIterator
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
TResourceIterator(CResourceStore *pStore = gpResourceStore)
|
TResourceIterator(CResourceStore *pStore = gpResourceStore)
|
||||||
: CResourceIterator(pStore)
|
: CResourceIterator(pStore)
|
||||||
{
|
{
|
||||||
if (mpCurEntry->ResourceType() != ResType::StaticType())
|
if (mpCurEntry->ResourceType() != ResType)
|
||||||
Next();
|
Next();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,7 +81,7 @@ public:
|
||||||
{
|
{
|
||||||
do {
|
do {
|
||||||
CResourceIterator::Next();
|
CResourceIterator::Next();
|
||||||
} while (mpCurEntry && mpCurEntry->ResourceType() != ResType::StaticType());
|
} while (mpCurEntry && mpCurEntry->ResourceType() != ResType);
|
||||||
|
|
||||||
return mpCurEntry;
|
return mpCurEntry;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,8 @@ CResourceStore::CResourceStore(const TWideString& rkDatabasePath)
|
||||||
: mpProj(nullptr)
|
: mpProj(nullptr)
|
||||||
, mGame(eUnknownGame)
|
, mGame(eUnknownGame)
|
||||||
, mpExporter(nullptr)
|
, mpExporter(nullptr)
|
||||||
|
, mDatabaseDirty(false)
|
||||||
|
, mCacheFileDirty(false)
|
||||||
{
|
{
|
||||||
mpDatabaseRoot = new CVirtualDirectory();
|
mpDatabaseRoot = new CVirtualDirectory();
|
||||||
mDatabasePath = FileUtil::MakeAbsolute(rkDatabasePath.GetFileDirectory());
|
mDatabasePath = FileUtil::MakeAbsolute(rkDatabasePath.GetFileDirectory());
|
||||||
|
@ -30,6 +32,8 @@ CResourceStore::CResourceStore(CGameProject *pProject, CGameExporter *pExporter,
|
||||||
, mRawDir(rkRawDir)
|
, mRawDir(rkRawDir)
|
||||||
, mCookedDir(rkCookedDir)
|
, mCookedDir(rkCookedDir)
|
||||||
, mpExporter(pExporter)
|
, mpExporter(pExporter)
|
||||||
|
, mDatabaseDirty(false)
|
||||||
|
, mCacheFileDirty(false)
|
||||||
{
|
{
|
||||||
SetProject(pProject);
|
SetProject(pProject);
|
||||||
}
|
}
|
||||||
|
@ -39,6 +43,8 @@ CResourceStore::CResourceStore(CGameProject *pProject)
|
||||||
, mGame(eUnknownGame)
|
, mGame(eUnknownGame)
|
||||||
, mpDatabaseRoot(nullptr)
|
, mpDatabaseRoot(nullptr)
|
||||||
, mpExporter(nullptr)
|
, mpExporter(nullptr)
|
||||||
|
, mDatabaseDirty(false)
|
||||||
|
, mCacheFileDirty(false)
|
||||||
{
|
{
|
||||||
SetProject(pProject);
|
SetProject(pProject);
|
||||||
}
|
}
|
||||||
|
@ -122,6 +128,7 @@ void CResourceStore::SaveResourceDatabase()
|
||||||
TString Path = DatabasePath().ToUTF8();
|
TString Path = DatabasePath().ToUTF8();
|
||||||
CXMLWriter Writer(Path, "ResourceDB", 0, mGame);
|
CXMLWriter Writer(Path, "ResourceDB", 0, mGame);
|
||||||
SerializeResourceDatabase(Writer);
|
SerializeResourceDatabase(Writer);
|
||||||
|
mDatabaseDirty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CResourceStore::LoadCacheFile()
|
void CResourceStore::LoadCacheFile()
|
||||||
|
@ -208,6 +215,13 @@ void CResourceStore::SaveCacheFile()
|
||||||
|
|
||||||
CacheFile.Seek(ResCountOffset, SEEK_SET);
|
CacheFile.Seek(ResCountOffset, SEEK_SET);
|
||||||
CacheFile.WriteLong(ResCount);
|
CacheFile.WriteLong(ResCount);
|
||||||
|
mCacheFileDirty = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CResourceStore::ConditionalSaveStore()
|
||||||
|
{
|
||||||
|
if (mDatabaseDirty) SaveResourceDatabase();
|
||||||
|
if (mCacheFileDirty) SaveCacheFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CResourceStore::SetProject(CGameProject *pProj)
|
void CResourceStore::SetProject(CGameProject *pProj)
|
||||||
|
@ -298,6 +312,21 @@ CVirtualDirectory* CResourceStore::GetVirtualDirectory(const TWideString& rkPath
|
||||||
else return nullptr;
|
else return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CResourceStore::ConditionalDeleteDirectory(CVirtualDirectory *pDir)
|
||||||
|
{
|
||||||
|
if (pDir->IsEmpty())
|
||||||
|
{
|
||||||
|
// If this directory is part of the project, then we should delete the corresponding filesystem directories
|
||||||
|
if (pDir->GetRoot() == mpDatabaseRoot)
|
||||||
|
{
|
||||||
|
FileUtil::DeleteDirectory(RawDir(false) + pDir->FullPath());
|
||||||
|
FileUtil::DeleteDirectory(CookedDir(false) + pDir->FullPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
pDir->Parent()->RemoveChildDirectory(pDir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
CResourceEntry* CResourceStore::FindEntry(const CAssetID& rkID) const
|
CResourceEntry* CResourceStore::FindEntry(const CAssetID& rkID) const
|
||||||
{
|
{
|
||||||
if (!rkID.IsValid()) return nullptr;
|
if (!rkID.IsValid()) return nullptr;
|
||||||
|
@ -316,7 +345,7 @@ bool CResourceStore::IsResourceRegistered(const CAssetID& rkID) const
|
||||||
return FindEntry(rkID) != nullptr;
|
return FindEntry(rkID) != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
CResourceEntry* CResourceStore::RegisterResource(const CAssetID& rkID, EResType Type, const TWideString& rkDir, const TWideString& rkFileName)
|
CResourceEntry* CResourceStore::RegisterResource(const CAssetID& rkID, EResType Type, const TWideString& rkDir, const TWideString& rkName)
|
||||||
{
|
{
|
||||||
CResourceEntry *pEntry = FindEntry(rkID);
|
CResourceEntry *pEntry = FindEntry(rkID);
|
||||||
|
|
||||||
|
@ -325,16 +354,16 @@ CResourceEntry* CResourceStore::RegisterResource(const CAssetID& rkID, EResType
|
||||||
if (pEntry->IsTransient())
|
if (pEntry->IsTransient())
|
||||||
{
|
{
|
||||||
ASSERT(pEntry->ResourceType() == Type);
|
ASSERT(pEntry->ResourceType() == Type);
|
||||||
pEntry->AddToProject(rkDir, rkFileName);
|
pEntry->AddToProject(rkDir, rkName);
|
||||||
}
|
}
|
||||||
|
|
||||||
else
|
else
|
||||||
Log::Error("Attempted to register resource that's already tracked in the database: " + rkID.ToString() + " / " + rkDir.ToUTF8() + " / " + rkFileName.ToUTF8());
|
Log::Error("Attempted to register resource that's already tracked in the database: " + rkID.ToString() + " / " + rkDir.ToUTF8() + " / " + rkName.ToUTF8());
|
||||||
}
|
}
|
||||||
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
pEntry = new CResourceEntry(this, rkID, rkDir, rkFileName.GetFileName(false), Type);
|
pEntry = new CResourceEntry(this, rkID, rkDir, rkName, Type);
|
||||||
mResourceEntries[rkID] = pEntry;
|
mResourceEntries[rkID] = pEntry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,8 @@ class CResourceStore
|
||||||
std::vector<CVirtualDirectory*> mTransientRoots;
|
std::vector<CVirtualDirectory*> mTransientRoots;
|
||||||
std::map<CAssetID, CResourceEntry*> mResourceEntries;
|
std::map<CAssetID, CResourceEntry*> mResourceEntries;
|
||||||
std::map<CAssetID, CResourceEntry*> mLoadedResources;
|
std::map<CAssetID, CResourceEntry*> mLoadedResources;
|
||||||
|
bool mDatabaseDirty;
|
||||||
|
bool mCacheFileDirty;
|
||||||
|
|
||||||
// Directory paths
|
// Directory paths
|
||||||
TWideString mDatabasePath;
|
TWideString mDatabasePath;
|
||||||
|
@ -54,12 +56,14 @@ public:
|
||||||
void SaveResourceDatabase();
|
void SaveResourceDatabase();
|
||||||
void LoadCacheFile();
|
void LoadCacheFile();
|
||||||
void SaveCacheFile();
|
void SaveCacheFile();
|
||||||
|
void ConditionalSaveStore();
|
||||||
void SetProject(CGameProject *pProj);
|
void SetProject(CGameProject *pProj);
|
||||||
void CloseProject();
|
void CloseProject();
|
||||||
CVirtualDirectory* GetVirtualDirectory(const TWideString& rkPath, bool Transient, bool AllowCreate);
|
CVirtualDirectory* GetVirtualDirectory(const TWideString& rkPath, bool Transient, bool AllowCreate);
|
||||||
|
void ConditionalDeleteDirectory(CVirtualDirectory *pDir);
|
||||||
|
|
||||||
bool IsResourceRegistered(const CAssetID& rkID) const;
|
bool IsResourceRegistered(const CAssetID& rkID) const;
|
||||||
CResourceEntry* RegisterResource(const CAssetID& rkID, EResType Type, const TWideString& rkDir, const TWideString& rkFileName);
|
CResourceEntry* RegisterResource(const CAssetID& rkID, EResType Type, const TWideString& rkDir, const TWideString& rkName);
|
||||||
CResourceEntry* FindEntry(const CAssetID& rkID) const;
|
CResourceEntry* FindEntry(const CAssetID& rkID) const;
|
||||||
CResourceEntry* FindEntry(const TWideString& rkPath) const;
|
CResourceEntry* FindEntry(const TWideString& rkPath) const;
|
||||||
CResourceEntry* RegisterTransientResource(EResType Type, const TWideString& rkDir = L"", const TWideString& rkFileName = L"");
|
CResourceEntry* RegisterTransientResource(EResType Type, const TWideString& rkDir = L"", const TWideString& rkFileName = L"");
|
||||||
|
@ -84,6 +88,10 @@ public:
|
||||||
inline CVirtualDirectory* RootDirectory() const { return mpDatabaseRoot; }
|
inline CVirtualDirectory* RootDirectory() const { return mpDatabaseRoot; }
|
||||||
inline u32 NumTotalResources() const { return mResourceEntries.size(); }
|
inline u32 NumTotalResources() const { return mResourceEntries.size(); }
|
||||||
inline u32 NumLoadedResources() const { return mLoadedResources.size(); }
|
inline u32 NumLoadedResources() const { return mLoadedResources.size(); }
|
||||||
|
inline bool IsDirty() const { return mDatabaseDirty || mCacheFileDirty; }
|
||||||
|
|
||||||
|
inline void SetDatabaseDirty() { mDatabaseDirty = true; }
|
||||||
|
inline void SetCacheDataDirty() { mCacheFileDirty = true; }
|
||||||
};
|
};
|
||||||
|
|
||||||
extern CResourceStore *gpResourceStore;
|
extern CResourceStore *gpResourceStore;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include "CVirtualDirectory.h"
|
#include "CVirtualDirectory.h"
|
||||||
#include "CResourceEntry.h"
|
#include "CResourceEntry.h"
|
||||||
#include "CResourceStore.h"
|
#include "CResourceStore.h"
|
||||||
|
#include "Core/Resource/CResource.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
CVirtualDirectory::CVirtualDirectory()
|
CVirtualDirectory::CVirtualDirectory()
|
||||||
|
@ -33,7 +34,7 @@ bool CVirtualDirectory::IsEmpty() const
|
||||||
|
|
||||||
TWideString CVirtualDirectory::FullPath() const
|
TWideString CVirtualDirectory::FullPath() const
|
||||||
{
|
{
|
||||||
return (mpParent && !mpParent->IsRoot() ? mpParent->FullPath() + L'\\' + mName + L"\\" : mName + L"\\");
|
return (mpParent && !mpParent->IsRoot() ? mpParent->FullPath() + mName + L'\\' : mName + L'\\');
|
||||||
}
|
}
|
||||||
|
|
||||||
CVirtualDirectory* CVirtualDirectory::GetRoot()
|
CVirtualDirectory* CVirtualDirectory::GetRoot()
|
||||||
|
@ -50,7 +51,7 @@ CVirtualDirectory* CVirtualDirectory::FindChildDirectory(const TWideString& rkNa
|
||||||
{
|
{
|
||||||
CVirtualDirectory *pChild = mSubdirectories[iSub];
|
CVirtualDirectory *pChild = mSubdirectories[iSub];
|
||||||
|
|
||||||
if (pChild->Name() == DirName)
|
if (pChild->Name().CaseInsensitiveCompare(DirName))
|
||||||
{
|
{
|
||||||
if (SlashIdx == -1)
|
if (SlashIdx == -1)
|
||||||
return pChild;
|
return pChild;
|
||||||
|
@ -81,7 +82,7 @@ CVirtualDirectory* CVirtualDirectory::FindChildDirectory(const TWideString& rkNa
|
||||||
CResourceEntry* CVirtualDirectory::FindChildResource(const TWideString& rkPath)
|
CResourceEntry* CVirtualDirectory::FindChildResource(const TWideString& rkPath)
|
||||||
{
|
{
|
||||||
TWideString Dir = rkPath.GetFileDirectory();
|
TWideString Dir = rkPath.GetFileDirectory();
|
||||||
TWideString Name = rkPath.GetFileName(false);
|
TWideString Name = rkPath.GetFileName();
|
||||||
|
|
||||||
if (!Dir.IsEmpty())
|
if (!Dir.IsEmpty())
|
||||||
{
|
{
|
||||||
|
@ -89,13 +90,22 @@ CResourceEntry* CVirtualDirectory::FindChildResource(const TWideString& rkPath)
|
||||||
if (pDir) return pDir->FindChildResource(Name);
|
if (pDir) return pDir->FindChildResource(Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
else
|
else if (!Name.IsEmpty())
|
||||||
{
|
{
|
||||||
for (u32 iRes = 0; iRes < mResources.size(); iRes++)
|
TWideString Ext = Name.GetFileExtension();
|
||||||
{
|
EResType Type = CResource::ResTypeForExtension(Ext);
|
||||||
if (mResources[iRes]->Name() == Name)
|
return FindChildResource(Name.GetFileName(false), Type);
|
||||||
return mResources[iRes];
|
}
|
||||||
}
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
CResourceEntry* CVirtualDirectory::FindChildResource(const TWideString& rkName, EResType Type)
|
||||||
|
{
|
||||||
|
for (u32 iRes = 0; iRes < mResources.size(); iRes++)
|
||||||
|
{
|
||||||
|
if (rkName.CaseInsensitiveCompare(mResources[iRes]->Name()) && mResources[iRes]->ResourceType() == Type)
|
||||||
|
return mResources[iRes];
|
||||||
}
|
}
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -168,10 +178,6 @@ bool CVirtualDirectory::RemoveChildResource(CResourceEntry *pEntry)
|
||||||
if (*It == pEntry)
|
if (*It == pEntry)
|
||||||
{
|
{
|
||||||
mResources.erase(It);
|
mResources.erase(It);
|
||||||
|
|
||||||
if (mpParent && IsEmpty())
|
|
||||||
mpParent->RemoveChildDirectory(this);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#define CVIRTUALDIRECTORY
|
#define CVIRTUALDIRECTORY
|
||||||
|
|
||||||
/* Virtual directory system used to look up resources by their location in the filesystem. */
|
/* Virtual directory system used to look up resources by their location in the filesystem. */
|
||||||
|
#include "Core/Resource/EResType.h"
|
||||||
#include <Common/AssertMacro.h>
|
#include <Common/AssertMacro.h>
|
||||||
#include <Common/TString.h>
|
#include <Common/TString.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
@ -26,6 +27,7 @@ public:
|
||||||
CVirtualDirectory* GetRoot();
|
CVirtualDirectory* GetRoot();
|
||||||
CVirtualDirectory* FindChildDirectory(const TWideString& rkName, bool AllowCreate);
|
CVirtualDirectory* FindChildDirectory(const TWideString& rkName, bool AllowCreate);
|
||||||
CResourceEntry* FindChildResource(const TWideString& rkPath);
|
CResourceEntry* FindChildResource(const TWideString& rkPath);
|
||||||
|
CResourceEntry* FindChildResource(const TWideString& rkName, EResType Type);
|
||||||
void AddChild(const TWideString& rkPath, CResourceEntry *pEntry);
|
void AddChild(const TWideString& rkPath, CResourceEntry *pEntry);
|
||||||
bool RemoveChildDirectory(CVirtualDirectory *pSubdir);
|
bool RemoveChildDirectory(CVirtualDirectory *pSubdir);
|
||||||
bool RemoveChildResource(CResourceEntry *pEntry);
|
bool RemoveChildResource(CResourceEntry *pEntry);
|
||||||
|
|
|
@ -129,6 +129,12 @@ public:
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GetUniquePrimitives(std::set<CAnimPrimitive>& rPrimSet) const
|
||||||
|
{
|
||||||
|
for (u32 iAnim = 0; iAnim < mAnimPrimitives.size(); iAnim++)
|
||||||
|
rPrimSet.insert(mAnimPrimitives[iAnim]);
|
||||||
|
}
|
||||||
|
|
||||||
// Accessors
|
// Accessors
|
||||||
inline u32 NumCharacters() const { return mCharacters.size(); }
|
inline u32 NumCharacters() const { return mCharacters.size(); }
|
||||||
inline u32 NumAnimations() const { return mAnimations.size(); }
|
inline u32 NumAnimations() const { return mAnimations.size(); }
|
||||||
|
|
|
@ -84,6 +84,7 @@ public:
|
||||||
// Inline Accessors
|
// Inline Accessors
|
||||||
inline u32 WorldIndex() const { return mWorldIndex; }
|
inline u32 WorldIndex() const { return mWorldIndex; }
|
||||||
inline CTransform4f Transform() const { return mTransform; }
|
inline CTransform4f Transform() const { return mTransform; }
|
||||||
|
inline CMaterialSet* Materials() const { return mpMaterialSet; }
|
||||||
inline u32 NumWorldModels() const { return mWorldModels.size(); }
|
inline u32 NumWorldModels() const { return mWorldModels.size(); }
|
||||||
inline u32 NumStaticModels() const { return mStaticWorldModels.size(); }
|
inline u32 NumStaticModels() const { return mStaticWorldModels.size(); }
|
||||||
inline CModel* TerrainModel(u32 iMdl) const { return mWorldModels[iMdl]; }
|
inline CModel* TerrainModel(u32 iMdl) const { return mWorldModels[iMdl]; }
|
||||||
|
@ -94,7 +95,9 @@ public:
|
||||||
inline u32 NumLightLayers() const { return mLightLayers.size(); }
|
inline u32 NumLightLayers() const { return mLightLayers.size(); }
|
||||||
inline u32 NumLights(u32 LayerIndex) const { return (LayerIndex < mLightLayers.size() ? mLightLayers[LayerIndex].size() : 0); }
|
inline u32 NumLights(u32 LayerIndex) const { return (LayerIndex < mLightLayers.size() ? mLightLayers[LayerIndex].size() : 0); }
|
||||||
inline CLight* Light(u32 LayerIndex, u32 LightIndex) const { return mLightLayers[LayerIndex][LightIndex]; }
|
inline CLight* Light(u32 LayerIndex, u32 LightIndex) const { return mLightLayers[LayerIndex][LightIndex]; }
|
||||||
|
inline CAssetID PathID() const { return mPathID; }
|
||||||
inline CPoiToWorld* PoiToWorldMap() const { return mpPoiToWorldMap; }
|
inline CPoiToWorld* PoiToWorldMap() const { return mpPoiToWorldMap; }
|
||||||
|
inline CAssetID PortalAreaID() const { return mPortalAreaID; }
|
||||||
inline CAABox AABox() const { return mAABox; }
|
inline CAABox AABox() const { return mAABox; }
|
||||||
|
|
||||||
inline void SetWorldIndex(u32 NewWorldIndex) { mWorldIndex = NewWorldIndex; }
|
inline void SetWorldIndex(u32 NewWorldIndex) { mWorldIndex = NewWorldIndex; }
|
||||||
|
|
|
@ -65,6 +65,10 @@ public:
|
||||||
CVector2f Position = CVector2f(0,0),
|
CVector2f Position = CVector2f(0,0),
|
||||||
CColor FillColor = CColor::skWhite, CColor StrokeColor = CColor::skBlack,
|
CColor FillColor = CColor::skWhite, CColor StrokeColor = CColor::skBlack,
|
||||||
u32 FontSize = CFONT_DEFAULT_SIZE);
|
u32 FontSize = CFONT_DEFAULT_SIZE);
|
||||||
|
|
||||||
|
// Accessors
|
||||||
|
inline TString FontName() const { return mFontName; }
|
||||||
|
inline CTexture* Texture() const { return mpFontTexture; }
|
||||||
private:
|
private:
|
||||||
void InitBuffers();
|
void InitBuffers();
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,7 +14,7 @@ u32 GetGameTypeID(EGame Game, EResType ResType)
|
||||||
// ************ STATIC ************
|
// ************ STATIC ************
|
||||||
EResType CResource::ResTypeForExtension(CFourCC Extension)
|
EResType CResource::ResTypeForExtension(CFourCC Extension)
|
||||||
{
|
{
|
||||||
auto Find = gExtensionTypeMap.find(Extension.ToLong());
|
auto Find = gExtensionTypeMap.find(Extension.ToUpper().ToLong());
|
||||||
|
|
||||||
if (Find == gExtensionTypeMap.end())
|
if (Find == gExtensionTypeMap.end())
|
||||||
{
|
{
|
||||||
|
|
|
@ -98,10 +98,14 @@ public:
|
||||||
return pTree;
|
return pTree;
|
||||||
}
|
}
|
||||||
|
|
||||||
CStringTable* ScanText() const { return mpStringTable; }
|
// Accessors
|
||||||
bool IsImportant() const { return mIsImportant; }
|
inline CStringTable* ScanText() const { return mpStringTable; }
|
||||||
bool IsSlow() const { return mIsSlow; }
|
inline bool IsImportant() const { return mIsImportant; }
|
||||||
ELogbookCategory LogbookCategory() const { return mCategory; }
|
inline bool IsSlow() const { return mIsSlow; }
|
||||||
|
inline ELogbookCategory LogbookCategory() const { return mCategory; }
|
||||||
|
inline CAssetID GuiFrame() const { return mFrameID; }
|
||||||
|
inline CAssetID ScanImage(u32 ImgIndex) const { return mScanImageTextures[ImgIndex]; }
|
||||||
|
inline CAssetID LogbookDisplayAssetID() const { return (mLogbookAnimParams.ID().IsValid() ? mLogbookAnimParams.ID() : mLogbookModel); }
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // CSCAN_H
|
#endif // CSCAN_H
|
||||||
|
|
|
@ -126,6 +126,39 @@ public:
|
||||||
|
|
||||||
return pTree;
|
return pTree;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static TWideString StripFormatting(const TWideString& rkStr)
|
||||||
|
{
|
||||||
|
TWideString Out = rkStr;
|
||||||
|
int TagStart = -1;
|
||||||
|
|
||||||
|
for (u32 iChr = 0; iChr < Out.Size(); iChr++)
|
||||||
|
{
|
||||||
|
if (Out[iChr] == L'&')
|
||||||
|
{
|
||||||
|
if (TagStart == -1)
|
||||||
|
TagStart = iChr;
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Out.Remove(TagStart, 1);
|
||||||
|
TagStart = -1;
|
||||||
|
iChr--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (TagStart != -1 && Out[iChr] == L';')
|
||||||
|
{
|
||||||
|
int TagEnd = iChr + 1;
|
||||||
|
int TagLen = TagEnd - TagStart;
|
||||||
|
Out.Remove(TagStart, TagLen);
|
||||||
|
iChr = TagStart - 1;
|
||||||
|
TagStart = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Out;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // CSTRINGTABLE_H
|
#endif // CSTRINGTABLE_H
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include "Editor/WorldEditor/CWorldEditor.h"
|
#include "Editor/WorldEditor/CWorldEditor.h"
|
||||||
#include <Common/AssertMacro.h>
|
#include <Common/AssertMacro.h>
|
||||||
#include <Common/CTimer.h>
|
#include <Common/CTimer.h>
|
||||||
|
#include <Core/GameProject/CGameProject.h>
|
||||||
|
|
||||||
CEditorApplication::CEditorApplication(int& rArgc, char **ppArgv)
|
CEditorApplication::CEditorApplication(int& rArgc, char **ppArgv)
|
||||||
: QApplication(rArgc, ppArgv)
|
: QApplication(rArgc, ppArgv)
|
||||||
|
@ -26,6 +27,15 @@ void CEditorApplication::TickEditors()
|
||||||
mLastUpdate = CTimer::GlobalTime();
|
mLastUpdate = CTimer::GlobalTime();
|
||||||
double DeltaTime = mLastUpdate - LastUpdate;
|
double DeltaTime = mLastUpdate - LastUpdate;
|
||||||
|
|
||||||
|
// The resource store should NOT be dirty at the beginning of a tick - this indicates we forgot to save it after updating somewhere
|
||||||
|
if (gpResourceStore && gpResourceStore->IsDirty())
|
||||||
|
{
|
||||||
|
Log::Error("ERROR: Resource store is dirty at the beginning of a tick! Call ConditionalSaveStore() after making any significant changes to assets!");
|
||||||
|
DEBUG_BREAK;
|
||||||
|
gpResourceStore->ConditionalSaveStore();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tick each editor window and redraw their viewports
|
||||||
foreach(IEditor *pEditor, mEditorWindows)
|
foreach(IEditor *pEditor, mEditorWindows)
|
||||||
{
|
{
|
||||||
if (pEditor->isVisible())
|
if (pEditor->isVisible())
|
||||||
|
|
|
@ -83,6 +83,13 @@ void CProjectOverviewDialog::ExportGame()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Verify export directory is empty
|
||||||
|
if (!FileUtil::IsEmpty(TO_TSTRING(ExportDir)))
|
||||||
|
{
|
||||||
|
QMessageBox::Button Button = QMessageBox::warning(this, "Warning", "<b>Warning:</b> The specified directory is not empty. Export anyway?", QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::NoButton);
|
||||||
|
if (Button != QMessageBox::Ok) return;
|
||||||
|
}
|
||||||
|
|
||||||
CGameExporter Exporter(TO_TSTRING(GameRoot), TO_TSTRING(ExportDir));
|
CGameExporter Exporter(TO_TSTRING(GameRoot), TO_TSTRING(ExportDir));
|
||||||
Exporter.Export();
|
Exporter.Export();
|
||||||
}
|
}
|
||||||
|
|
|
@ -129,7 +129,7 @@ void WResourceSelector::SetResource(CResourceEntry *pRes)
|
||||||
// when the user types in a resource path so I'd prefer for the text not to be cleared out in that case
|
// when the user types in a resource path so I'd prefer for the text not to be cleared out in that case
|
||||||
if (mpResource)
|
if (mpResource)
|
||||||
{
|
{
|
||||||
TWideString Path = mpResource->HasRawVersion() ? mpResource->RawAssetPath(true) : mpResource->CookedAssetPath(true);
|
TWideString Path = mpResource->CookedAssetPath(true);
|
||||||
mUI.LineEdit->setText(TO_QSTRING(Path));
|
mUI.LineEdit->setText(TO_QSTRING(Path));
|
||||||
mResourceValid = HasSupportedExtension(mpResource);
|
mResourceValid = HasSupportedExtension(mpResource);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,29 +3,20 @@
|
||||||
<name>DistanceFog</name>
|
<name>DistanceFog</name>
|
||||||
<properties>
|
<properties>
|
||||||
<property ID="0x00" name="Name" type="string"/>
|
<property ID="0x00" name="Name" type="string"/>
|
||||||
<property ID="0x01" name="Unknown 1" type="long"/>
|
<property ID="0x01" name="Mode" type="long"/>
|
||||||
<property ID="0x02" name="Unknown 2" type="color"/>
|
<property ID="0x02" name="Color" type="color"/>
|
||||||
<struct ID="0x03" name="FloatFloat 1" type="single">
|
<struct ID="0x03" name="Range" template="Structs/Vector2f.xml"/>
|
||||||
<properties>
|
<property ID="0x04" name="Color Delta" type="float"/>
|
||||||
<property ID="0x00" name="Unknown 3" type="float"/>
|
<struct ID="0x05" name="Range Delta" template="Structs/Vector2f.xml"/>
|
||||||
<property ID="0x01" name="Unknown 4" type="float"/>
|
<property ID="0x06" name="Explicit" type="bool"/>
|
||||||
</properties>
|
<property ID="0x07" name="Active" type="bool"/>
|
||||||
</struct>
|
|
||||||
<struct ID="0x04" name="FloatFloat 2" type="single">
|
|
||||||
<properties>
|
|
||||||
<property ID="0x00" name="Unknown 5" type="float"/>
|
|
||||||
<property ID="0x01" name="Unknown 6" type="float"/>
|
|
||||||
</properties>
|
|
||||||
</struct>
|
|
||||||
<property ID="0x05" name="Unknown 7" type="float"/>
|
|
||||||
<property ID="0x06" name="Unknown 8" type="bool"/>
|
|
||||||
<property ID="0x07" name="Unknown 9" type="bool"/>
|
|
||||||
</properties>
|
</properties>
|
||||||
<states/>
|
<states/>
|
||||||
<messages/>
|
<messages/>
|
||||||
<editor>
|
<editor>
|
||||||
<properties>
|
<properties>
|
||||||
<property name="InstanceName" ID="0x00"/>
|
<property name="InstanceName" ID="0x00"/>
|
||||||
|
<property name="Active" ID="0x07"/>
|
||||||
</properties>
|
</properties>
|
||||||
<assets>
|
<assets>
|
||||||
<billboard source="file">script/common/DistanceFog.txtr</billboard>
|
<billboard source="file">script/common/DistanceFog.txtr</billboard>
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<struct name="Vector2f" type="single">
|
||||||
|
<properties>
|
||||||
|
<property ID="0x00" name="X" type="float"/>
|
||||||
|
<property ID="0x01" name="Y" type="float"/>
|
||||||
|
</properties>
|
||||||
|
</struct>
|
Loading…
Reference in New Issue