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(const char *pkSrc) { mFourCC = FOURCC(pkSrc); }
|
||||
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(IInputStream& rSrc) { Read(rSrc); }
|
||||
|
||||
|
|
|
@ -29,6 +29,36 @@ EGame GetGameForID(const CFourCC& rkID)
|
|||
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)
|
||||
{
|
||||
CFourCC GameID = GetGameID(rGame);
|
||||
|
|
|
@ -7,6 +7,9 @@
|
|||
class CFourCC;
|
||||
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
|
||||
{
|
||||
ePrimeDemo,
|
||||
|
@ -21,6 +24,8 @@ enum EGame
|
|||
|
||||
CFourCC GetGameID(EGame Game);
|
||||
EGame GetGameForID(const CFourCC& rkID);
|
||||
TString GetGameName(EGame Game);
|
||||
TString GetGameShortName(EGame Game);
|
||||
void Serialize(IArchive& rArc, EGame& rGame);
|
||||
|
||||
#endif // EGAME_H
|
||||
|
|
|
@ -42,6 +42,18 @@ bool IsRelative(const TWideString& rkDirPath)
|
|||
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)
|
||||
{
|
||||
if (!IsValidPath(rkNewDir, true))
|
||||
|
@ -61,6 +73,7 @@ bool CopyFile(const TWideString& rkOrigPath, const TWideString& rkNewPath)
|
|||
return false;
|
||||
}
|
||||
|
||||
CreateDirectory(rkNewPath.GetFileDirectory());
|
||||
boost::system::error_code Error;
|
||||
copy(*rkOrigPath, *rkNewPath, Error);
|
||||
return (Error == boost::system::errc::success);
|
||||
|
@ -74,6 +87,7 @@ bool CopyDirectory(const TWideString& rkOrigPath, const TWideString& rkNewPath)
|
|||
return false;
|
||||
}
|
||||
|
||||
CreateDirectory(rkNewPath.GetFileDirectory());
|
||||
boost::system::error_code Error;
|
||||
copy_directory(*rkOrigPath, *rkNewPath, Error);
|
||||
return (Error == boost::system::errc::success);
|
||||
|
@ -317,7 +331,7 @@ TWideString SanitizePath(TWideString Path, bool Directory)
|
|||
TWideString Comp = *It;
|
||||
bool IsDir = Directory || CompIdx < Components.size() - 1;
|
||||
bool IsRoot = CompIdx == 0;
|
||||
SanitizeName(Comp, IsDir, IsRoot);
|
||||
Comp = SanitizeName(Comp, IsDir, IsRoot);
|
||||
|
||||
Path += Comp;
|
||||
if (IsDir) Path += L'\\';
|
||||
|
@ -330,6 +344,9 @@ TWideString SanitizePath(TWideString Path, bool Directory)
|
|||
bool IsValidName(const TWideString& rkName, bool Directory, bool RootDir /*= false*/)
|
||||
{
|
||||
// Windows only atm
|
||||
if (rkName.IsEmpty())
|
||||
return false;
|
||||
|
||||
u32 NumIllegalChars = sizeof(gskIllegalNameChars) / sizeof(wchar_t);
|
||||
|
||||
if (Directory && (rkName == L"." || rkName == L".."))
|
||||
|
|
|
@ -13,6 +13,7 @@ bool IsFile(const TWideString& rkFilePath);
|
|||
bool IsDirectory(const TWideString& rkDirPath);
|
||||
bool IsAbsolute(const TWideString& rkDirPath);
|
||||
bool IsRelative(const TWideString& rkDirPath);
|
||||
bool IsEmpty(const TWideString& rkDirPath);
|
||||
bool CreateDirectory(const TWideString& rkNewDir);
|
||||
bool CopyFile(const TWideString& rkOrigPath, const TWideString& rkNewPath);
|
||||
bool CopyDirectory(const TWideString& rkOrigPath, const TWideString& rkNewPath);
|
||||
|
|
|
@ -426,6 +426,7 @@ public:
|
|||
|
||||
// Container serialize methods
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#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
|
||||
|
|
|
@ -4,11 +4,12 @@
|
|||
#include "types.h"
|
||||
#include <FileIO/IOUtil.h>
|
||||
|
||||
#include <string>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
#include <cstdarg>
|
||||
#include <iomanip>
|
||||
#include <list>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
/* 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
|
||||
|
@ -149,24 +150,22 @@ public:
|
|||
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;
|
||||
|
||||
// Apply case sensitivity
|
||||
_TString CheckStr(CaseSensitive ? *this : ToUpper());
|
||||
if (!CaseSensitive) Str = Str.ToUpper();
|
||||
if (Size() < rkStr.Size()) return -1;
|
||||
|
||||
// Now loop from the offset provided by the user.
|
||||
u32 Pos = Offset;
|
||||
u32 LatestPossibleStart = Size() - Str.Size();
|
||||
u32 LatestPossibleStart = Size() - rkStr.Size();
|
||||
u32 MatchStart = -1;
|
||||
u32 Matched = 0;
|
||||
|
||||
while (Pos < Size())
|
||||
{
|
||||
// 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++;
|
||||
|
||||
|
@ -174,7 +173,7 @@ public:
|
|||
MatchStart = Pos;
|
||||
|
||||
// If we matched the entire string, we can return.
|
||||
if (Matched == Str.Size())
|
||||
if (Matched == rkStr.Size())
|
||||
return MatchStart;
|
||||
}
|
||||
|
||||
|
@ -198,9 +197,9 @@ public:
|
|||
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
|
||||
|
@ -318,18 +317,10 @@ public:
|
|||
|
||||
_TString ToUpper() const
|
||||
{
|
||||
// todo: doesn't handle accented characters
|
||||
_TString Out(Size());
|
||||
|
||||
for (u32 iChar = 0; iChar < Size(); iChar++)
|
||||
{
|
||||
CharType Chr = At(iChar);
|
||||
|
||||
if (Chr >= CHAR_LITERAL('a') && Chr <= CHAR_LITERAL('z'))
|
||||
Out[iChar] = Chr - 0x20;
|
||||
else
|
||||
Out[iChar] = Chr;
|
||||
}
|
||||
Out[iChar] = CharToUpper( At(iChar) );
|
||||
|
||||
return Out;
|
||||
}
|
||||
|
@ -340,14 +331,7 @@ public:
|
|||
_TString Out(Size());
|
||||
|
||||
for (u32 iChar = 0; iChar < Size(); iChar++)
|
||||
{
|
||||
CharType Chr = At(iChar);
|
||||
|
||||
if (Chr >= 'A' && Chr <= 'Z')
|
||||
Out[iChar] = Chr + 0x20;
|
||||
else
|
||||
Out[iChar] = Chr;
|
||||
}
|
||||
Out[iChar] = CharToLower( At(iChar) );
|
||||
|
||||
return Out;
|
||||
}
|
||||
|
@ -509,26 +493,22 @@ public:
|
|||
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;
|
||||
|
||||
_TString CompStr = (CaseSensitive ? *this : ToUpper());
|
||||
if (!CaseSensitive) Str = Str.ToUpper();
|
||||
|
||||
return (CompStr.SubString(0, Str.Size()) == Str);
|
||||
_TString SubStr = SubString(0, rkStr.Size());
|
||||
return CaseSensitive ? SubStr == rkStr : SubStr.CaseInsensitiveCompare(rkStr);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
_TString CompStr = (CaseSensitive ? *this : ToUpper());
|
||||
if (!CaseSensitive) Str = Str.ToUpper();
|
||||
|
||||
return (CompStr.SubString(CompStr.Size() - Str.Size(), Str.Size()) == Str);
|
||||
_TString SubStr = SubString(Size() - rkStr.Size(), rkStr.Size());
|
||||
return CaseSensitive ? SubStr == rkStr : SubStr.CaseInsensitiveCompare(rkStr);
|
||||
}
|
||||
|
||||
bool Contains(_TString Str, bool CaseSensitive = true) const
|
||||
|
@ -577,7 +557,14 @@ public:
|
|||
|
||||
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
|
||||
|
@ -615,10 +602,8 @@ public:
|
|||
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 IdxB = IndexOf(LITERAL("\\/"));
|
||||
if (IdxB == -1) return _TString();
|
||||
|
@ -626,9 +611,8 @@ public:
|
|||
while (IdxB != -1)
|
||||
{
|
||||
_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);
|
||||
|
||||
IdxA = IdxB + 1;
|
||||
|
@ -639,6 +623,12 @@ public:
|
|||
}
|
||||
|
||||
// Operators
|
||||
inline _TString& operator=(CharType Char)
|
||||
{
|
||||
mInternalString = Char;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline _TString& operator=(const CharType* pkText)
|
||||
{
|
||||
mInternalString = pkText;
|
||||
|
@ -666,6 +656,14 @@ public:
|
|||
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
|
||||
{
|
||||
u32 Len = CStringLength(pkOther);
|
||||
|
@ -681,6 +679,11 @@ public:
|
|||
return (*this + rkOther.CString());
|
||||
}
|
||||
|
||||
inline void operator+=(CharType Other)
|
||||
{
|
||||
*this = *this + Other;
|
||||
}
|
||||
|
||||
inline void operator+=(const CharType* pkOther)
|
||||
{
|
||||
*this = *this + pkOther;
|
||||
|
@ -691,6 +694,14 @@ public:
|
|||
*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)
|
||||
{
|
||||
u32 Len = CStringLength(pkLeft);
|
||||
|
@ -709,6 +720,11 @@ public:
|
|||
return Out;
|
||||
}
|
||||
|
||||
inline bool operator==(CharType Other) const
|
||||
{
|
||||
return Size() == 1 && At(0) == Other;
|
||||
}
|
||||
|
||||
inline bool operator==(const CharType *pkText) const
|
||||
{
|
||||
return CompareCStrings(pkText, CString());
|
||||
|
@ -719,6 +735,11 @@ public:
|
|||
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)
|
||||
{
|
||||
return (rkString == pkText);
|
||||
|
@ -729,6 +750,11 @@ public:
|
|||
return (rkStringB == rkStringA);
|
||||
}
|
||||
|
||||
inline bool operator!=(CharType Other) const
|
||||
{
|
||||
return (!(*this == Other));
|
||||
}
|
||||
|
||||
inline bool operator!=(const CharType *pkText) const
|
||||
{
|
||||
return (!(*this == pkText));
|
||||
|
@ -739,6 +765,11 @@ public:
|
|||
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)
|
||||
{
|
||||
return (rkString != pkText);
|
||||
|
@ -842,21 +873,39 @@ public:
|
|||
}
|
||||
|
||||
// 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;
|
||||
SStream << std::setbase(Base) << std::setw(Width) << std::setfill(CHAR_LITERAL('0')) << Value;
|
||||
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;
|
||||
SStream << std::setbase(Base) << std::setw(Width) << std::setfill(CHAR_LITERAL('0')) << Value;
|
||||
return SStream.str();
|
||||
}
|
||||
|
||||
static TBasicString<CharType> FromFloat(float Value, int MinDecimals = 1)
|
||||
static _TString FromFloat(float Value, int MinDecimals = 1)
|
||||
{
|
||||
// Initial float -> string conversion
|
||||
std::basic_stringstream<CharType> SStream;
|
||||
|
@ -900,7 +949,7 @@ public:
|
|||
return Out;
|
||||
}
|
||||
|
||||
static TBasicString<CharType> FileSizeString(u64 Size, u32 NumDecimals = 2)
|
||||
static _TString FileSizeString(u64 Size, u32 NumDecimals = 2)
|
||||
{
|
||||
_TString Out;
|
||||
_TString Type;
|
||||
|
@ -933,17 +982,17 @@ public:
|
|||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
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)
|
||||
{
|
||||
return ( (Chr == CHAR_LITERAL('\t')) ||
|
||||
|
@ -988,6 +1049,11 @@ public:
|
|||
(Chr == CHAR_LITERAL('\r')) ||
|
||||
(Chr == CHAR_LITERAL(' ')) );
|
||||
}
|
||||
|
||||
static inline bool IsNumerical(CharType Chr)
|
||||
{
|
||||
return (Chr >= CHAR_LITERAL('0') && Chr <= CHAR_LITERAL('9'));
|
||||
}
|
||||
};
|
||||
|
||||
#undef LITERAL
|
||||
|
|
|
@ -17,7 +17,7 @@ void CAudioManager::LoadAssets()
|
|||
mSfxIdMap.clear();
|
||||
|
||||
// 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();
|
||||
if (pGroup) mAudioGroups.push_back(pGroup);
|
||||
|
|
|
@ -213,7 +213,9 @@ HEADERS += \
|
|||
Resource/Animation/CSkeleton.h \
|
||||
Resource/Animation/CSkin.h \
|
||||
Resource/Animation/IMetaTransition.h \
|
||||
Resource/Animation/IMetaAnimation.h
|
||||
Resource/Animation/IMetaAnimation.h \
|
||||
GameProject/CAssetNameMap.h \
|
||||
GameProject/AssetNameGeneration.h
|
||||
|
||||
# Source Files
|
||||
SOURCES += \
|
||||
|
@ -314,4 +316,5 @@ SOURCES += \
|
|||
Resource/Animation/CAnimationParameters.cpp \
|
||||
Resource/Animation/CSkeleton.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 LOAD_PAKS 1
|
||||
#define SAVE_PACKAGE_DEFINITIONS 1
|
||||
#define EXPORT_WORLDS 1
|
||||
#define USE_ASSET_NAME_MAP 0
|
||||
#define EXPORT_COOKED 1
|
||||
|
||||
CGameExporter::CGameExporter(const TString& rkInputDir, const TString& rkOutputDir)
|
||||
|
@ -50,13 +50,15 @@ bool CGameExporter::Export()
|
|||
mContentDir = mpStore->RawDir(false);
|
||||
mCookedDir = mpStore->CookedDir(false);
|
||||
|
||||
#if USE_ASSET_NAME_MAP
|
||||
mNameMap = CAssetNameMap::LoadAssetNames(mGame);
|
||||
#endif
|
||||
|
||||
// Export game data
|
||||
CResourceStore *pOldStore = gpResourceStore;
|
||||
gpResourceStore = mpStore;
|
||||
|
||||
LoadAssetList();
|
||||
LoadPaks();
|
||||
ExportWorlds();
|
||||
ExportCookedResources();
|
||||
mpProject->AudioManager()->LoadAssets();
|
||||
ExportResourceEditorData();
|
||||
|
@ -128,57 +130,6 @@ void CGameExporter::CopyDiscData()
|
|||
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 ************
|
||||
void CGameExporter::LoadPaks()
|
||||
{
|
||||
|
@ -221,7 +172,6 @@ void CGameExporter::LoadPaks()
|
|||
u32 NameLen = Pak.ReadLong();
|
||||
TString Name = Pak.ReadString(NameLen);
|
||||
pCollection->AddResource(Name, ResID, ResType);
|
||||
SetResourcePath(ResID, PakName + L"\\", Name.ToUTF16());
|
||||
}
|
||||
|
||||
u32 NumResources = Pak.ReadLong();
|
||||
|
@ -296,7 +246,6 @@ void CGameExporter::LoadPaks()
|
|||
CFourCC ResType = Pak.ReadLong();
|
||||
CAssetID ResID(Pak, mGame);
|
||||
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()
|
||||
{
|
||||
{
|
||||
|
@ -597,21 +468,10 @@ void CGameExporter::ExportResource(SResourceInstance& rRes)
|
|||
std::vector<u8> 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
|
||||
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
|
||||
// Save cooked asset
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#ifndef CGAMEEXPORTER_H
|
||||
#define CGAMEEXPORTER_H
|
||||
|
||||
#include "CAssetNameMap.h"
|
||||
#include "CGameProject.h"
|
||||
#include "CResourceStore.h"
|
||||
#include <Common/CAssetID.h>
|
||||
|
@ -28,6 +29,7 @@ class CGameExporter
|
|||
// Resources
|
||||
TWideStringList mPaks;
|
||||
std::map<CAssetID, bool> mAreaDuplicateMap;
|
||||
CAssetNameMap mNameMap;
|
||||
|
||||
struct SResourceInstance
|
||||
{
|
||||
|
@ -41,13 +43,6 @@ class CGameExporter
|
|||
};
|
||||
std::map<CAssetID, SResourceInstance> mResourceMap;
|
||||
|
||||
struct SResourcePath
|
||||
{
|
||||
TWideString Dir;
|
||||
TWideString Name;
|
||||
};
|
||||
std::map<CAssetID, SResourcePath> mResourcePaths;
|
||||
|
||||
public:
|
||||
CGameExporter(const TString& rkInputDir, const TString& rkOutputDir);
|
||||
bool Export();
|
||||
|
@ -55,10 +50,8 @@ public:
|
|||
|
||||
protected:
|
||||
void CopyDiscData();
|
||||
void LoadAssetList();
|
||||
void LoadPaks();
|
||||
void LoadResource(const SResourceInstance& rkResource, std::vector<u8>& rBuffer);
|
||||
void ExportWorlds();
|
||||
void ExportCookedResources();
|
||||
void ExportResourceEditorData();
|
||||
void ExportResource(SResourceInstance& rRes);
|
||||
|
@ -70,18 +63,6 @@ protected:
|
|||
auto Found = mResourceMap.find(IntegralID);
|
||||
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
|
||||
|
|
|
@ -7,6 +7,8 @@ CGameProject *CGameProject::mspActiveProject = nullptr;
|
|||
|
||||
CGameProject::~CGameProject()
|
||||
{
|
||||
ASSERT(!mpResourceStore->IsDirty());
|
||||
|
||||
if (IsActive())
|
||||
mspActiveProject = nullptr;
|
||||
|
||||
|
|
|
@ -47,9 +47,9 @@ public:
|
|||
, mProjectRoot(rkProjRootDir)
|
||||
, mResourceDBPath(L"ResourceDB.rdb")
|
||||
{
|
||||
mProjectRoot.Replace(L"/", L"\\");
|
||||
mpResourceStore = new CResourceStore(this);
|
||||
mpAudioManager = new CAudioManager(this);
|
||||
mProjectRoot.Replace(L"/", L"\\");
|
||||
}
|
||||
|
||||
CGameProject(CGameExporter *pExporter, const TWideString& rkProjRootDir, EGame Game)
|
||||
|
@ -58,9 +58,9 @@ public:
|
|||
, mProjectRoot(rkProjRootDir)
|
||||
, mResourceDBPath(L"ResourceDB.rdb")
|
||||
{
|
||||
mProjectRoot.Replace(L"/", L"\\");
|
||||
mpResourceStore = new CResourceStore(this, pExporter, L"Content\\", L"Cooked\\", Game);
|
||||
mpAudioManager = new CAudioManager(this);
|
||||
mProjectRoot.Replace(L"/", L"\\");
|
||||
}
|
||||
|
||||
~CGameProject();
|
||||
|
@ -95,6 +95,4 @@ public:
|
|||
static inline CGameProject* ActiveProject() { return mspActiveProject; }
|
||||
};
|
||||
|
||||
extern CGameProject *gpProject;
|
||||
|
||||
#endif // CGAMEPROJECT_H
|
||||
|
|
|
@ -196,9 +196,10 @@ bool CResourceEntry::Save(bool SkipCacheSave /*= false*/)
|
|||
|
||||
// Resource has been saved, now update dependencies + cache file
|
||||
UpdateDependencies();
|
||||
mpStore->SetCacheDataDirty();
|
||||
|
||||
if (!SkipCacheSave)
|
||||
mpStore->SaveCacheFile();
|
||||
mpStore->ConditionalSaveStore();
|
||||
|
||||
if (ShouldCollectGarbage)
|
||||
mpStore->DestroyUnreferencedResources();
|
||||
|
@ -277,40 +278,92 @@ bool CResourceEntry::Unload()
|
|||
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
|
||||
CVirtualDirectory *pOldDir = mpDirectory;
|
||||
TWideString OldName = mName;
|
||||
TString OldCookedPath = CookedAssetPath();
|
||||
TString OldRawPath = RawAssetPath();
|
||||
|
||||
// Set new directory and name
|
||||
bool HasDirectory = mpDirectory != nullptr;
|
||||
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)
|
||||
mpDirectory->RemoveChildResource(this);
|
||||
mpDirectory = pNewDir;
|
||||
FSMoveSuccess = true;
|
||||
|
||||
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)
|
||||
ASSERT(mpDirectory->FindChildResource(rkName) == nullptr);
|
||||
|
||||
mName = rkName;
|
||||
mCachedUppercaseName = rkName.ToUpper();
|
||||
|
||||
// Move files
|
||||
if (HasDirectory)
|
||||
// If we succeeded, finish the move
|
||||
if (FSMoveSuccess)
|
||||
{
|
||||
TString CookedPath = CookedAssetPath();
|
||||
TString RawPath = RawAssetPath();
|
||||
if (mpDirectory != pOldDir)
|
||||
{
|
||||
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)
|
||||
FileUtil::MoveFile(OldCookedPath, CookedPath);
|
||||
mpStore->SetDatabaseDirty();
|
||||
mCachedUppercaseName = rkName.ToUpper();
|
||||
FileUtil::DeleteFile(OldRawPath);
|
||||
FileUtil::DeleteFile(OldCookedPath);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (FileUtil::Exists(OldRawPath) && RawPath != OldRawPath)
|
||||
FileUtil::MoveFile(OldRawPath, RawPath);
|
||||
// Otherwise, revert changes and let the caller know the move failed
|
||||
else
|
||||
{
|
||||
mpDirectory = pOldDir;
|
||||
mName = OldName;
|
||||
mpStore->ConditionalDeleteDirectory(pNewDir);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,12 +14,13 @@ class CDependencyTree;
|
|||
|
||||
enum EResEntryFlag
|
||||
{
|
||||
eREF_NeedsRecook = 0x1, // Resource has been updated but not recooked
|
||||
eREF_Transient = 0x2, // Resource is transient (not part of a game project resource DB)
|
||||
eREF_HasThumbnail = 0x4, // Resource has a unique thumbnail
|
||||
eREF_ThumbnailLoaded = 0x8, // Resource thumbnail is currently in memory
|
||||
eREF_NeedsRecook = 0x00000001, // Resource has been updated but not recooked
|
||||
eREF_Transient = 0x00000002, // Resource is transient (not part of a game project resource DB)
|
||||
eREF_HasThumbnail = 0x00000004, // Resource has a unique thumbnail
|
||||
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
|
||||
eREF_SavedFlags = eREF_NeedsRecook | eREF_HasThumbnail
|
||||
eREF_SavedFlags = eREF_NeedsRecook | eREF_HasThumbnail | eREF_Hidden
|
||||
};
|
||||
DECLARE_FLAGS(EResEntryFlag, FResEntryFlags)
|
||||
|
||||
|
@ -61,24 +62,30 @@ public:
|
|||
CResource* Load();
|
||||
CResource* LoadCooked(IInputStream& rInput);
|
||||
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 RemoveFromProject();
|
||||
|
||||
// Accessors
|
||||
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 IsCategorized() const { return mpDirectory && mpDirectory->FullPath() != L"Uncategorized\\"; }
|
||||
inline bool IsNamed() const { return mName != mID.ToString().ToUTF16(); }
|
||||
inline CResource* Resource() const { return mpResource; }
|
||||
inline CResourceStore* ResourceStore() const { return mpStore; }
|
||||
inline CDependencyTree* Dependencies() const { return mpDependencies; }
|
||||
inline CAssetID ID() const { return mID; }
|
||||
inline EGame Game() const { return mGame; }
|
||||
inline CVirtualDirectory* Directory() const { return mpDirectory; }
|
||||
inline TWideString DirectoryPath() const { return mpDirectory->FullPath(); }
|
||||
inline TWideString Name() const { return mName; }
|
||||
inline const TWideString& UppercaseName() const { return mCachedUppercaseName; }
|
||||
inline EResType ResourceType() const { return mType; }
|
||||
inline bool IsTransient() const { return mFlags.HasFlag(eREF_Transient); }
|
||||
inline bool IsHidden() const { return mFlags.HasFlag(eREF_Hidden); }
|
||||
|
||||
protected:
|
||||
CResource* InternalLoad(IInputStream& rInput);
|
||||
|
|
|
@ -66,14 +66,14 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
template<class ResType>
|
||||
template<EResType ResType>
|
||||
class TResourceIterator : public CResourceIterator
|
||||
{
|
||||
public:
|
||||
TResourceIterator(CResourceStore *pStore = gpResourceStore)
|
||||
: CResourceIterator(pStore)
|
||||
{
|
||||
if (mpCurEntry->ResourceType() != ResType::StaticType())
|
||||
if (mpCurEntry->ResourceType() != ResType)
|
||||
Next();
|
||||
}
|
||||
|
||||
|
@ -81,7 +81,7 @@ public:
|
|||
{
|
||||
do {
|
||||
CResourceIterator::Next();
|
||||
} while (mpCurEntry && mpCurEntry->ResourceType() != ResType::StaticType());
|
||||
} while (mpCurEntry && mpCurEntry->ResourceType() != ResType);
|
||||
|
||||
return mpCurEntry;
|
||||
}
|
||||
|
|
|
@ -18,6 +18,8 @@ CResourceStore::CResourceStore(const TWideString& rkDatabasePath)
|
|||
: mpProj(nullptr)
|
||||
, mGame(eUnknownGame)
|
||||
, mpExporter(nullptr)
|
||||
, mDatabaseDirty(false)
|
||||
, mCacheFileDirty(false)
|
||||
{
|
||||
mpDatabaseRoot = new CVirtualDirectory();
|
||||
mDatabasePath = FileUtil::MakeAbsolute(rkDatabasePath.GetFileDirectory());
|
||||
|
@ -30,6 +32,8 @@ CResourceStore::CResourceStore(CGameProject *pProject, CGameExporter *pExporter,
|
|||
, mRawDir(rkRawDir)
|
||||
, mCookedDir(rkCookedDir)
|
||||
, mpExporter(pExporter)
|
||||
, mDatabaseDirty(false)
|
||||
, mCacheFileDirty(false)
|
||||
{
|
||||
SetProject(pProject);
|
||||
}
|
||||
|
@ -39,6 +43,8 @@ CResourceStore::CResourceStore(CGameProject *pProject)
|
|||
, mGame(eUnknownGame)
|
||||
, mpDatabaseRoot(nullptr)
|
||||
, mpExporter(nullptr)
|
||||
, mDatabaseDirty(false)
|
||||
, mCacheFileDirty(false)
|
||||
{
|
||||
SetProject(pProject);
|
||||
}
|
||||
|
@ -122,6 +128,7 @@ void CResourceStore::SaveResourceDatabase()
|
|||
TString Path = DatabasePath().ToUTF8();
|
||||
CXMLWriter Writer(Path, "ResourceDB", 0, mGame);
|
||||
SerializeResourceDatabase(Writer);
|
||||
mDatabaseDirty = false;
|
||||
}
|
||||
|
||||
void CResourceStore::LoadCacheFile()
|
||||
|
@ -208,6 +215,13 @@ void CResourceStore::SaveCacheFile()
|
|||
|
||||
CacheFile.Seek(ResCountOffset, SEEK_SET);
|
||||
CacheFile.WriteLong(ResCount);
|
||||
mCacheFileDirty = false;
|
||||
}
|
||||
|
||||
void CResourceStore::ConditionalSaveStore()
|
||||
{
|
||||
if (mDatabaseDirty) SaveResourceDatabase();
|
||||
if (mCacheFileDirty) SaveCacheFile();
|
||||
}
|
||||
|
||||
void CResourceStore::SetProject(CGameProject *pProj)
|
||||
|
@ -298,6 +312,21 @@ CVirtualDirectory* CResourceStore::GetVirtualDirectory(const TWideString& rkPath
|
|||
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
|
||||
{
|
||||
if (!rkID.IsValid()) return nullptr;
|
||||
|
@ -316,7 +345,7 @@ bool CResourceStore::IsResourceRegistered(const CAssetID& rkID) const
|
|||
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);
|
||||
|
||||
|
@ -325,16 +354,16 @@ CResourceEntry* CResourceStore::RegisterResource(const CAssetID& rkID, EResType
|
|||
if (pEntry->IsTransient())
|
||||
{
|
||||
ASSERT(pEntry->ResourceType() == Type);
|
||||
pEntry->AddToProject(rkDir, rkFileName);
|
||||
pEntry->AddToProject(rkDir, rkName);
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
pEntry = new CResourceEntry(this, rkID, rkDir, rkFileName.GetFileName(false), Type);
|
||||
pEntry = new CResourceEntry(this, rkID, rkDir, rkName, Type);
|
||||
mResourceEntries[rkID] = pEntry;
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,8 @@ class CResourceStore
|
|||
std::vector<CVirtualDirectory*> mTransientRoots;
|
||||
std::map<CAssetID, CResourceEntry*> mResourceEntries;
|
||||
std::map<CAssetID, CResourceEntry*> mLoadedResources;
|
||||
bool mDatabaseDirty;
|
||||
bool mCacheFileDirty;
|
||||
|
||||
// Directory paths
|
||||
TWideString mDatabasePath;
|
||||
|
@ -54,12 +56,14 @@ public:
|
|||
void SaveResourceDatabase();
|
||||
void LoadCacheFile();
|
||||
void SaveCacheFile();
|
||||
void ConditionalSaveStore();
|
||||
void SetProject(CGameProject *pProj);
|
||||
void CloseProject();
|
||||
CVirtualDirectory* GetVirtualDirectory(const TWideString& rkPath, bool Transient, bool AllowCreate);
|
||||
void ConditionalDeleteDirectory(CVirtualDirectory *pDir);
|
||||
|
||||
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 TWideString& rkPath) const;
|
||||
CResourceEntry* RegisterTransientResource(EResType Type, const TWideString& rkDir = L"", const TWideString& rkFileName = L"");
|
||||
|
@ -84,6 +88,10 @@ public:
|
|||
inline CVirtualDirectory* RootDirectory() const { return mpDatabaseRoot; }
|
||||
inline u32 NumTotalResources() const { return mResourceEntries.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;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "CVirtualDirectory.h"
|
||||
#include "CResourceEntry.h"
|
||||
#include "CResourceStore.h"
|
||||
#include "Core/Resource/CResource.h"
|
||||
#include <algorithm>
|
||||
|
||||
CVirtualDirectory::CVirtualDirectory()
|
||||
|
@ -33,7 +34,7 @@ bool CVirtualDirectory::IsEmpty() 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()
|
||||
|
@ -50,7 +51,7 @@ CVirtualDirectory* CVirtualDirectory::FindChildDirectory(const TWideString& rkNa
|
|||
{
|
||||
CVirtualDirectory *pChild = mSubdirectories[iSub];
|
||||
|
||||
if (pChild->Name() == DirName)
|
||||
if (pChild->Name().CaseInsensitiveCompare(DirName))
|
||||
{
|
||||
if (SlashIdx == -1)
|
||||
return pChild;
|
||||
|
@ -81,7 +82,7 @@ CVirtualDirectory* CVirtualDirectory::FindChildDirectory(const TWideString& rkNa
|
|||
CResourceEntry* CVirtualDirectory::FindChildResource(const TWideString& rkPath)
|
||||
{
|
||||
TWideString Dir = rkPath.GetFileDirectory();
|
||||
TWideString Name = rkPath.GetFileName(false);
|
||||
TWideString Name = rkPath.GetFileName();
|
||||
|
||||
if (!Dir.IsEmpty())
|
||||
{
|
||||
|
@ -89,13 +90,22 @@ CResourceEntry* CVirtualDirectory::FindChildResource(const TWideString& rkPath)
|
|||
if (pDir) return pDir->FindChildResource(Name);
|
||||
}
|
||||
|
||||
else
|
||||
else if (!Name.IsEmpty())
|
||||
{
|
||||
for (u32 iRes = 0; iRes < mResources.size(); iRes++)
|
||||
{
|
||||
if (mResources[iRes]->Name() == Name)
|
||||
return mResources[iRes];
|
||||
}
|
||||
TWideString Ext = Name.GetFileExtension();
|
||||
EResType Type = CResource::ResTypeForExtension(Ext);
|
||||
return FindChildResource(Name.GetFileName(false), Type);
|
||||
}
|
||||
|
||||
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;
|
||||
|
@ -168,10 +178,6 @@ bool CVirtualDirectory::RemoveChildResource(CResourceEntry *pEntry)
|
|||
if (*It == pEntry)
|
||||
{
|
||||
mResources.erase(It);
|
||||
|
||||
if (mpParent && IsEmpty())
|
||||
mpParent->RemoveChildDirectory(this);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#define CVIRTUALDIRECTORY
|
||||
|
||||
/* 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/TString.h>
|
||||
#include <vector>
|
||||
|
@ -26,6 +27,7 @@ public:
|
|||
CVirtualDirectory* GetRoot();
|
||||
CVirtualDirectory* FindChildDirectory(const TWideString& rkName, bool AllowCreate);
|
||||
CResourceEntry* FindChildResource(const TWideString& rkPath);
|
||||
CResourceEntry* FindChildResource(const TWideString& rkName, EResType Type);
|
||||
void AddChild(const TWideString& rkPath, CResourceEntry *pEntry);
|
||||
bool RemoveChildDirectory(CVirtualDirectory *pSubdir);
|
||||
bool RemoveChildResource(CResourceEntry *pEntry);
|
||||
|
|
|
@ -129,6 +129,12 @@ public:
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
void GetUniquePrimitives(std::set<CAnimPrimitive>& rPrimSet) const
|
||||
{
|
||||
for (u32 iAnim = 0; iAnim < mAnimPrimitives.size(); iAnim++)
|
||||
rPrimSet.insert(mAnimPrimitives[iAnim]);
|
||||
}
|
||||
|
||||
// Accessors
|
||||
inline u32 NumCharacters() const { return mCharacters.size(); }
|
||||
inline u32 NumAnimations() const { return mAnimations.size(); }
|
||||
|
|
|
@ -84,6 +84,7 @@ public:
|
|||
// Inline Accessors
|
||||
inline u32 WorldIndex() const { return mWorldIndex; }
|
||||
inline CTransform4f Transform() const { return mTransform; }
|
||||
inline CMaterialSet* Materials() const { return mpMaterialSet; }
|
||||
inline u32 NumWorldModels() const { return mWorldModels.size(); }
|
||||
inline u32 NumStaticModels() const { return mStaticWorldModels.size(); }
|
||||
inline CModel* TerrainModel(u32 iMdl) const { return mWorldModels[iMdl]; }
|
||||
|
@ -94,7 +95,9 @@ public:
|
|||
inline u32 NumLightLayers() const { return mLightLayers.size(); }
|
||||
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 CAssetID PathID() const { return mPathID; }
|
||||
inline CPoiToWorld* PoiToWorldMap() const { return mpPoiToWorldMap; }
|
||||
inline CAssetID PortalAreaID() const { return mPortalAreaID; }
|
||||
inline CAABox AABox() const { return mAABox; }
|
||||
|
||||
inline void SetWorldIndex(u32 NewWorldIndex) { mWorldIndex = NewWorldIndex; }
|
||||
|
|
|
@ -65,6 +65,10 @@ public:
|
|||
CVector2f Position = CVector2f(0,0),
|
||||
CColor FillColor = CColor::skWhite, CColor StrokeColor = CColor::skBlack,
|
||||
u32 FontSize = CFONT_DEFAULT_SIZE);
|
||||
|
||||
// Accessors
|
||||
inline TString FontName() const { return mFontName; }
|
||||
inline CTexture* Texture() const { return mpFontTexture; }
|
||||
private:
|
||||
void InitBuffers();
|
||||
};
|
||||
|
|
|
@ -14,7 +14,7 @@ u32 GetGameTypeID(EGame Game, EResType ResType)
|
|||
// ************ STATIC ************
|
||||
EResType CResource::ResTypeForExtension(CFourCC Extension)
|
||||
{
|
||||
auto Find = gExtensionTypeMap.find(Extension.ToLong());
|
||||
auto Find = gExtensionTypeMap.find(Extension.ToUpper().ToLong());
|
||||
|
||||
if (Find == gExtensionTypeMap.end())
|
||||
{
|
||||
|
|
|
@ -98,10 +98,14 @@ public:
|
|||
return pTree;
|
||||
}
|
||||
|
||||
CStringTable* ScanText() const { return mpStringTable; }
|
||||
bool IsImportant() const { return mIsImportant; }
|
||||
bool IsSlow() const { return mIsSlow; }
|
||||
ELogbookCategory LogbookCategory() const { return mCategory; }
|
||||
// Accessors
|
||||
inline CStringTable* ScanText() const { return mpStringTable; }
|
||||
inline bool IsImportant() const { return mIsImportant; }
|
||||
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
|
||||
|
|
|
@ -126,6 +126,39 @@ public:
|
|||
|
||||
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
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "Editor/WorldEditor/CWorldEditor.h"
|
||||
#include <Common/AssertMacro.h>
|
||||
#include <Common/CTimer.h>
|
||||
#include <Core/GameProject/CGameProject.h>
|
||||
|
||||
CEditorApplication::CEditorApplication(int& rArgc, char **ppArgv)
|
||||
: QApplication(rArgc, ppArgv)
|
||||
|
@ -26,6 +27,15 @@ void CEditorApplication::TickEditors()
|
|||
mLastUpdate = CTimer::GlobalTime();
|
||||
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)
|
||||
{
|
||||
if (pEditor->isVisible())
|
||||
|
|
|
@ -83,6 +83,13 @@ void CProjectOverviewDialog::ExportGame()
|
|||
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));
|
||||
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
|
||||
if (mpResource)
|
||||
{
|
||||
TWideString Path = mpResource->HasRawVersion() ? mpResource->RawAssetPath(true) : mpResource->CookedAssetPath(true);
|
||||
TWideString Path = mpResource->CookedAssetPath(true);
|
||||
mUI.LineEdit->setText(TO_QSTRING(Path));
|
||||
mResourceValid = HasSupportedExtension(mpResource);
|
||||
}
|
||||
|
|
|
@ -3,29 +3,20 @@
|
|||
<name>DistanceFog</name>
|
||||
<properties>
|
||||
<property ID="0x00" name="Name" type="string"/>
|
||||
<property ID="0x01" name="Unknown 1" type="long"/>
|
||||
<property ID="0x02" name="Unknown 2" type="color"/>
|
||||
<struct ID="0x03" name="FloatFloat 1" type="single">
|
||||
<properties>
|
||||
<property ID="0x00" name="Unknown 3" type="float"/>
|
||||
<property ID="0x01" name="Unknown 4" type="float"/>
|
||||
</properties>
|
||||
</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"/>
|
||||
<property ID="0x01" name="Mode" type="long"/>
|
||||
<property ID="0x02" name="Color" type="color"/>
|
||||
<struct ID="0x03" name="Range" template="Structs/Vector2f.xml"/>
|
||||
<property ID="0x04" name="Color Delta" type="float"/>
|
||||
<struct ID="0x05" name="Range Delta" template="Structs/Vector2f.xml"/>
|
||||
<property ID="0x06" name="Explicit" type="bool"/>
|
||||
<property ID="0x07" name="Active" type="bool"/>
|
||||
</properties>
|
||||
<states/>
|
||||
<messages/>
|
||||
<editor>
|
||||
<properties>
|
||||
<property name="InstanceName" ID="0x00"/>
|
||||
<property name="Active" ID="0x07"/>
|
||||
</properties>
|
||||
<assets>
|
||||
<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