Added ability to create brand new SCAN/STRG assets. Added ability to update old projects.

This commit is contained in:
Aruki 2019-02-02 17:32:19 -07:00
parent 1e997dac46
commit 56843e214d
33 changed files with 523 additions and 175 deletions

2
externals/LibCommon vendored

@ -1 +1 @@
Subproject commit c98f4b3dcef68724627af91ac4103de7f28d2a2b Subproject commit 5c3bfbe57f6ef8a300933afdc053a445cabd5c7c

View File

@ -626,7 +626,7 @@ void CGameExporter::ExportResource(SResourceInstance& rRes)
Name = rRes.ResourceID.ToString(); Name = rRes.ResourceID.ToString();
#endif #endif
CResourceEntry *pEntry = mpStore->RegisterResource(rRes.ResourceID, CResTypeInfo::TypeForCookedExtension(mGame, rRes.ResourceType)->Type(), Directory, Name); CResourceEntry *pEntry = mpStore->CreateNewResource(rRes.ResourceID, CResTypeInfo::TypeForCookedExtension(mGame, rRes.ResourceType)->Type(), Directory, Name);
// Set flags // Set flags
pEntry->SetFlag(EResEntryFlag::IsBaseGameResource); pEntry->SetFlag(EResEntryFlag::IsBaseGameResource);

View File

@ -1,4 +1,5 @@
#include "CGameProject.h" #include "CGameProject.h"
#include "CResourceIterator.h"
#include "IUIRelay.h" #include "IUIRelay.h"
#include "Core/Resource/Script/CGameTemplate.h" #include "Core/Resource/Script/CGameTemplate.h"
#include <Common/Serialization/XML.h> #include <Common/Serialization/XML.h>
@ -233,7 +234,11 @@ CGameProject* CGameProject::LoadProject(const TString& rkProjPath, IProgressNoti
pProj->mpResourceStore = std::make_unique<CResourceStore>(pProj); pProj->mpResourceStore = std::make_unique<CResourceStore>(pProj);
LoadSuccess = pProj->mpResourceStore->LoadDatabaseCache(); LoadSuccess = pProj->mpResourceStore->LoadDatabaseCache();
// Validate resource database // Removed database validation step. We used to do this on project load to make sure all data was correct, but this takes a long
// time and significantly extends how long it takes to open a project. In actual practice, this isn't needed most of the time, and
// in the odd case that it is needed, there is a button in the resource browser to rebuild the database. So in the interest of
// making project startup faster, we no longer validate the database.
#if 0
if (LoadSuccess) if (LoadSuccess)
{ {
pProgress->Report("Validating resource database"); pProgress->Report("Validating resource database");
@ -253,6 +258,7 @@ CGameProject* CGameProject::LoadProject(const TString& rkProjPath, IProgressNoti
LoadSuccess = false; LoadSuccess = false;
} }
} }
#endif
} }
if (!LoadSuccess) if (!LoadSuccess)
@ -263,6 +269,34 @@ CGameProject* CGameProject::LoadProject(const TString& rkProjPath, IProgressNoti
pProj->mProjFileLock.Lock(ProjPath); pProj->mProjFileLock.Lock(ProjPath);
pProj->mpGameInfo->LoadGameInfo(pProj->mGame); pProj->mpGameInfo->LoadGameInfo(pProj->mGame);
// Perform update
if (Reader.FileVersion() < (uint16) EProjectVersion::Current)
{
pProgress->Report("Updating project");
CResourceStore* pOldStore = gpResourceStore;
gpResourceStore = pProj->mpResourceStore.get();
for (CResourceIterator It; It; ++It)
{
if (It->TypeInfo()->CanBeSerialized() && !It->HasRawVersion())
{
It->Save(true, false);
// Touch the cooked file to update its last modified time.
// This prevents PWE from erroneously thinking the cooked file is outdated
// (due to the raw file we just made having a more recent last modified time)
FileUtil::UpdateLastModifiedTime( It->CookedAssetPath() );
}
}
pProj->mpResourceStore->ConditionalSaveStore();
pProj->Save();
gpResourceStore = pOldStore;
}
pProj->mpAudioManager->LoadAssets(); pProj->mpAudioManager->LoadAssets();
pProj->mpTweakManager->LoadTweaks(); pProj->mpTweakManager->LoadTweaks();
return pProj; return pProj;

View File

@ -19,6 +19,7 @@ namespace nod { class DiscWii; }
enum class EProjectVersion enum class EProjectVersion
{ {
Initial, Initial,
RawStrings,
// Add new versions before this line // Add new versions before this line
Max, Max,

View File

@ -59,6 +59,16 @@ void CPackage::UpdateDependencyCache() const
mCacheDirty = false; mCacheDirty = false;
} }
void CPackage::MarkDirty()
{
if (!mNeedsRecook)
{
mNeedsRecook = true;
Save();
UpdateDependencyCache();
}
}
void CPackage::Cook(IProgressNotifier *pProgress) void CPackage::Cook(IProgressNotifier *pProgress)
{ {
SCOPED_TIMER(CookPackage); SCOPED_TIMER(CookPackage);

View File

@ -60,6 +60,7 @@ public:
void Serialize(IArchive& rArc); void Serialize(IArchive& rArc);
void AddResource(const TString& rkName, const CAssetID& rkID, const CFourCC& rkType); void AddResource(const TString& rkName, const CAssetID& rkID, const CFourCC& rkType);
void UpdateDependencyCache() const; void UpdateDependencyCache() const;
void MarkDirty();
void Cook(IProgressNotifier *pProgress); void Cook(IProgressNotifier *pProgress);
void CompareOriginalAssetList(const std::list<CAssetID>& rkNewList); void CompareOriginalAssetList(const std::list<CAssetID>& rkNewList);
@ -77,7 +78,6 @@ public:
inline bool NeedsRecook() const { return mNeedsRecook; } inline bool NeedsRecook() const { return mNeedsRecook; }
inline void SetPakName(TString NewName) { mPakName = NewName; } inline void SetPakName(TString NewName) { mPakName = NewName; }
inline void MarkDirty() { mNeedsRecook = true; Save(); UpdateDependencyCache(); }
}; };
#endif // CPACKAGE #endif // CPACKAGE

View File

@ -40,6 +40,15 @@ CResourceEntry* CResourceEntry::CreateNewResource(CResourceStore *pStore, const
pEntry->mpDirectory->AddChild("", pEntry); pEntry->mpDirectory->AddChild("", pEntry);
pEntry->mMetadataDirty = true; pEntry->mMetadataDirty = true;
// Check if the data exists or not. If so, then we are creating an entry for an existing resource (game exporter).
// If not, we want to initiate the new resource data and save it as soon as possible.
if (!pEntry->HasCookedVersion())
{
pEntry->mpResource = CResourceFactory::SpawnResource(pEntry);
pEntry->mpResource->InitializeNewResource();
}
return pEntry; return pEntry;
} }
@ -261,7 +270,7 @@ bool CResourceEntry::NeedsRecook() const
return (FileUtil::LastModifiedTime(CookedAssetPath()) < FileUtil::LastModifiedTime(RawAssetPath())); return (FileUtil::LastModifiedTime(CookedAssetPath()) < FileUtil::LastModifiedTime(RawAssetPath()));
} }
bool CResourceEntry::Save(bool SkipCacheSave /*= false*/) bool CResourceEntry::Save(bool SkipCacheSave /*= false*/, bool FlagForRecook /*= true*/)
{ {
// SkipCacheSave argument tells us not to save the resource cache file. This is generally not advised because we don't // SkipCacheSave argument tells us not to save the resource cache file. This is generally not advised because we don't
// want the actual resource data to desync from the cache data. However, there are occasions where we save multiple // want the actual resource data to desync from the cache data. However, there are occasions where we save multiple
@ -270,7 +279,6 @@ bool CResourceEntry::Save(bool SkipCacheSave /*= false*/)
// //
// For now, always save the resource when this function is called even if there's been no changes made to it in memory. // For now, always save the resource when this function is called even if there's been no changes made to it in memory.
// In the future this might not be desired behavior 100% of the time. // In the future this might not be desired behavior 100% of the time.
// We also might want this function to trigger a cook for certain resource types eventually.
bool ShouldCollectGarbage = false; bool ShouldCollectGarbage = false;
// Save raw resource // Save raw resource
@ -298,8 +306,11 @@ bool CResourceEntry::Save(bool SkipCacheSave /*= false*/)
return false; return false;
} }
if (FlagForRecook)
{
SetFlag(EResEntryFlag::NeedsRecook); SetFlag(EResEntryFlag::NeedsRecook);
} }
}
// This resource type doesn't have a raw format; save cooked instead // This resource type doesn't have a raw format; save cooked instead
else else
@ -324,13 +335,16 @@ bool CResourceEntry::Save(bool SkipCacheSave /*= false*/)
} }
// Flag dirty any packages that contain this resource. // Flag dirty any packages that contain this resource.
if (FlagForRecook)
{
for (uint32 iPkg = 0; iPkg < mpStore->Project()->NumPackages(); iPkg++) for (uint32 iPkg = 0; iPkg < mpStore->Project()->NumPackages(); iPkg++)
{ {
CPackage *pPkg = mpStore->Project()->PackageByIndex(iPkg); CPackage *pPkg = mpStore->Project()->PackageByIndex(iPkg);
if (pPkg->ContainsAsset(ID())) if (!pPkg->NeedsRecook() && pPkg->ContainsAsset(ID()))
pPkg->MarkDirty(); pPkg->MarkDirty();
} }
}
if (ShouldCollectGarbage) if (ShouldCollectGarbage)
mpStore->DestroyUnreferencedResources(); mpStore->DestroyUnreferencedResources();

View File

@ -67,7 +67,7 @@ public:
bool IsInDirectory(CVirtualDirectory *pDir) const; bool IsInDirectory(CVirtualDirectory *pDir) const;
uint64 Size() const; uint64 Size() const;
bool NeedsRecook() const; bool NeedsRecook() const;
bool Save(bool SkipCacheSave = false); bool Save(bool SkipCacheSave = false, bool FlagForRecook = true);
bool Cook(); bool Cook();
CResource* Load(); CResource* Load();
CResource* LoadCooked(IInputStream& rInput); CResource* LoadCooked(IInputStream& rInput);
@ -87,7 +87,7 @@ public:
inline void SetFlagEnabled(EResEntryFlag Flag, bool Enabled) { Enabled ? SetFlag(Flag) : ClearFlag(Flag); } inline void SetFlagEnabled(EResEntryFlag Flag, bool Enabled) { Enabled ? SetFlag(Flag) : ClearFlag(Flag); }
inline void SetDirty() { SetFlag(EResEntryFlag::NeedsRecook); } inline void SetDirty() { SetFlag(EResEntryFlag::NeedsRecook); }
inline void SetHidden(bool Hidden) { Hidden ? SetFlag(EResEntryFlag::Hidden) : ClearFlag(EResEntryFlag::Hidden); } inline void SetHidden(bool Hidden) { SetFlagEnabled(EResEntryFlag::Hidden, Hidden); }
inline bool HasFlag(EResEntryFlag Flag) const { return mFlags.HasFlag(Flag); } inline bool HasFlag(EResEntryFlag Flag) const { return mFlags.HasFlag(Flag); }
inline bool IsHidden() const { return HasFlag(EResEntryFlag::Hidden); } inline bool IsHidden() const { return HasFlag(EResEntryFlag::Hidden); }

View File

@ -284,7 +284,14 @@ void CResourceStore::ClearDatabase()
{ {
// THIS OPERATION REQUIRES THAT ALL RESOURCES ARE UNREFERENCED // THIS OPERATION REQUIRES THAT ALL RESOURCES ARE UNREFERENCED
DestroyUnreferencedResources(); DestroyUnreferencedResources();
ASSERT(mLoadedResources.empty());
if (!mLoadedResources.empty())
{
debugf("ERROR: Resources still loaded:");
for (auto Iter = mLoadedResources.begin(); Iter != mLoadedResources.end(); Iter++)
debugf("\t[%s] %s", *Iter->first.ToString(), *Iter->second->CookedAssetPath(true));
ASSERT(false);
}
// Clear out existing resource entries and directories // Clear out existing resource entries and directories
for (auto Iter = mResourceEntries.begin(); Iter != mResourceEntries.end(); Iter++) for (auto Iter = mResourceEntries.begin(); Iter != mResourceEntries.end(); Iter++)
@ -384,7 +391,7 @@ bool CResourceStore::IsResourceRegistered(const CAssetID& rkID) const
return FindEntry(rkID) != nullptr; return FindEntry(rkID) != nullptr;
} }
CResourceEntry* CResourceStore::RegisterResource(const CAssetID& rkID, EResourceType Type, const TString& rkDir, const TString& rkName) CResourceEntry* CResourceStore::CreateNewResource(const CAssetID& rkID, EResourceType Type, const TString& rkDir, const TString& rkName)
{ {
CResourceEntry *pEntry = FindEntry(rkID); CResourceEntry *pEntry = FindEntry(rkID);
@ -398,6 +405,12 @@ CResourceEntry* CResourceStore::RegisterResource(const CAssetID& rkID, EResource
{ {
pEntry = CResourceEntry::CreateNewResource(this, rkID, rkDir, rkName, Type); pEntry = CResourceEntry::CreateNewResource(this, rkID, rkDir, rkName, Type);
mResourceEntries[rkID] = pEntry; mResourceEntries[rkID] = pEntry;
mDatabaseCacheDirty = true;
if (pEntry->IsLoaded())
{
TrackLoadedResource(pEntry);
}
} }
else else

View File

@ -54,7 +54,7 @@ public:
TString DefaultResourceDirPath() const; TString DefaultResourceDirPath() const;
bool IsResourceRegistered(const CAssetID& rkID) const; bool IsResourceRegistered(const CAssetID& rkID) const;
CResourceEntry* RegisterResource(const CAssetID& rkID, EResourceType Type, const TString& rkDir, const TString& rkName); CResourceEntry* CreateNewResource(const CAssetID& rkID, EResourceType Type, const TString& rkDir, const TString& rkName);
CResourceEntry* FindEntry(const CAssetID& rkID) const; CResourceEntry* FindEntry(const CAssetID& rkID) const;
CResourceEntry* FindEntry(const TString& rkPath) const; CResourceEntry* FindEntry(const TString& rkPath) const;
bool AreAllEntriesValid() const; bool AreAllEntriesValid() const;

View File

@ -92,7 +92,7 @@ void CAnimationParameters::Write(IOutputStream& rSCLY)
{ {
CAssetID::skInvalidID32.Write(rSCLY); CAssetID::skInvalidID32.Write(rSCLY);
rSCLY.WriteLong(0); rSCLY.WriteLong(0);
rSCLY.WriteLong(0); rSCLY.WriteLong(mGame >= EGame::EchoesDemo ? 0 : 0xFFFFFFFF);
} }
} }

View File

@ -10,6 +10,7 @@ CResTypeInfo::CResTypeInfo(EResourceType Type, const TString& rkTypeName, const
, mRetroExtension(rkRetroExtension) , mRetroExtension(rkRetroExtension)
, mCanBeSerialized(false) , mCanBeSerialized(false)
, mCanHaveDependencies(true) , mCanHaveDependencies(true)
, mCanBeCreated(false)
{ {
#if !PUBLIC_RELEASE #if !PUBLIC_RELEASE
ASSERT(smTypeMap.find(Type) == smTypeMap.end()); ASSERT(smTypeMap.find(Type) == smTypeMap.end());
@ -332,6 +333,7 @@ void CResTypeInfo::CResTypeInfoFactory::InitTypes()
{ {
CResTypeInfo *pType = new CResTypeInfo(EResourceType::Scan, "Scan", "scan"); CResTypeInfo *pType = new CResTypeInfo(EResourceType::Scan, "Scan", "scan");
AddExtension(pType, "SCAN", EGame::PrimeDemo, EGame::Corruption); AddExtension(pType, "SCAN", EGame::PrimeDemo, EGame::Corruption);
pType->mCanBeCreated = true;
} }
{ {
CResTypeInfo *pType = new CResTypeInfo(EResourceType::Skeleton, "Skeleton", "cin"); CResTypeInfo *pType = new CResTypeInfo(EResourceType::Skeleton, "Skeleton", "cin");
@ -382,6 +384,7 @@ void CResTypeInfo::CResTypeInfoFactory::InitTypes()
CResTypeInfo *pType = new CResTypeInfo(EResourceType::StringTable, "String Table", "strg"); CResTypeInfo *pType = new CResTypeInfo(EResourceType::StringTable, "String Table", "strg");
AddExtension(pType, "STRG", EGame::PrimeDemo, EGame::DKCReturns); AddExtension(pType, "STRG", EGame::PrimeDemo, EGame::DKCReturns);
pType->mCanBeSerialized = true; pType->mCanBeSerialized = true;
pType->mCanBeCreated = true;
} }
{ {
CResTypeInfo *pType = new CResTypeInfo(EResourceType::Texture, "Texture", "txtr"); CResTypeInfo *pType = new CResTypeInfo(EResourceType::Texture, "Texture", "txtr");

View File

@ -23,6 +23,7 @@ class CResTypeInfo
TString mRetroExtension; // File extension in Retro's directory tree. We don't use it directly but it is needed for generating asset ID hashes TString mRetroExtension; // File extension in Retro's directory tree. We don't use it directly but it is needed for generating asset ID hashes
bool mCanBeSerialized; bool mCanBeSerialized;
bool mCanHaveDependencies; bool mCanHaveDependencies;
bool mCanBeCreated;
static std::unordered_map<EResourceType, CResTypeInfo*> smTypeMap; static std::unordered_map<EResourceType, CResTypeInfo*> smTypeMap;
@ -40,6 +41,7 @@ public:
inline TString TypeName() const { return mTypeName; } inline TString TypeName() const { return mTypeName; }
inline bool CanBeSerialized() const { return mCanBeSerialized; } inline bool CanBeSerialized() const { return mCanBeSerialized; }
inline bool CanHaveDependencies() const { return mCanHaveDependencies; } inline bool CanHaveDependencies() const { return mCanHaveDependencies; }
inline bool CanBeCreated() const { return mCanBeCreated; }
// Static // Static
static void GetAllTypesInGame(EGame Game, std::list<CResTypeInfo*>& rOut); static void GetAllTypesInGame(EGame Game, std::list<CResTypeInfo*>& rOut);

View File

@ -43,6 +43,7 @@ public:
virtual ~CResource() {} virtual ~CResource() {}
virtual CDependencyTree* BuildDependencyTree() const { return new CDependencyTree(); } virtual CDependencyTree* BuildDependencyTree() const { return new CDependencyTree(); }
virtual void Serialize(IArchive& /*rArc*/) {} virtual void Serialize(IArchive& /*rArc*/) {}
virtual void InitializeNewResource() {}
inline CResourceEntry* Entry() const { return mpEntry; } inline CResourceEntry* Entry() const { return mpEntry; }
inline CResTypeInfo* TypeInfo() const { return mpEntry->TypeInfo(); } inline CResTypeInfo* TypeInfo() const { return mpEntry->TypeInfo(); }

View File

@ -1,4 +1,5 @@
#include "CStringTable.h" #include "CStringTable.h"
#include "Core/GameProject/CGameProject.h"
#include <Common/Math/MathUtil.h> #include <Common/Math/MathUtil.h>
#include <algorithm> #include <algorithm>
#include <iterator> #include <iterator>
@ -8,27 +9,27 @@
* This is also the order that languages appear in game STRG assets. * This is also the order that languages appear in game STRG assets.
*/ */
// Supported languages in the original NTSC release of Metroid Prime // Supported languages in the original NTSC release of Metroid Prime
const ELanguage gkSupportedLanguagesMP1[] = const std::vector<ELanguage> gkSupportedLanguagesMP1 =
{ {
ELanguage::English ELanguage::English
}; };
// Supported languages in the PAL version of Metroid Prime, and also Metroid Prime 2 // Supported languages in the PAL version of Metroid Prime, and also Metroid Prime 2
const ELanguage gkSupportedLanguagesMP1PAL[] = const std::vector<ELanguage> gkSupportedLanguagesMP1PAL =
{ {
ELanguage::English, ELanguage::French, ELanguage::German, ELanguage::English, ELanguage::French, ELanguage::German,
ELanguage::Spanish, ELanguage::Italian, ELanguage::Japanese ELanguage::Spanish, ELanguage::Italian, ELanguage::Japanese
}; };
// Supported languages in Metroid Prime 3 // Supported languages in Metroid Prime 3
const ELanguage gkSupportedLanguagesMP3[] = const std::vector<ELanguage> gkSupportedLanguagesMP3 =
{ {
ELanguage::English, ELanguage::Japanese, ELanguage::German, ELanguage::English, ELanguage::Japanese, ELanguage::German,
ELanguage::French, ELanguage::Spanish, ELanguage::Italian ELanguage::French, ELanguage::Spanish, ELanguage::Italian
}; };
// Supported languages in DKCR // Supported languages in DKCR
const ELanguage gkSupportedLanguagesDKCR[] = const std::vector<ELanguage> gkSupportedLanguagesDKCR =
{ {
ELanguage::English, ELanguage::Japanese, ELanguage::German, ELanguage::English, ELanguage::Japanese, ELanguage::German,
ELanguage::French, ELanguage::Spanish, ELanguage::Italian, ELanguage::French, ELanguage::Spanish, ELanguage::Italian,
@ -36,6 +37,32 @@ const ELanguage gkSupportedLanguagesDKCR[] =
ELanguage::NAFrench, ELanguage::NASpanish ELanguage::NAFrench, ELanguage::NASpanish
}; };
// Utility function - retrieve the language array for a given game/region
static const std::vector<ELanguage>& GetSupportedLanguages(EGame Game, ERegion Region)
{
switch (Game)
{
default:
case EGame::PrimeDemo:
case EGame::Prime:
if (Region == ERegion::NTSC)
return gkSupportedLanguagesMP1;
else
return gkSupportedLanguagesMP1PAL;
case EGame::EchoesDemo:
case EGame::Echoes:
case EGame::CorruptionProto:
return gkSupportedLanguagesMP1PAL;
case EGame::Corruption:
return gkSupportedLanguagesMP3;
case EGame::DKCReturns:
return gkSupportedLanguagesDKCR;
}
}
// Utility function - retrieve the index of a given language // Utility function - retrieve the index of a given language
static int FindLanguageIndex(const CStringTable* pkInTable, ELanguage InLanguage) static int FindLanguageIndex(const CStringTable* pkInTable, ELanguage InLanguage)
{ {
@ -192,10 +219,19 @@ void CStringTable::RemoveString(uint StringIndex)
} }
} }
/** Configures the string table with default languages for the game/region pairing of the resource */ /** Initialize new resource data */
void CStringTable::ConfigureDefaultLanguages() void CStringTable::InitializeNewResource()
{ {
//@todo; this should be called on all newly created string tables // Initialize data for whatever languages are supported by our game/region
ERegion Region = ( Entry() && Entry()->Project() ? Entry()->Project()->Region() : ERegion::NTSC );
const std::vector<ELanguage>& kLanguageArray = GetSupportedLanguages(Game(), Region);
mLanguages.resize( kLanguageArray.size() );
for (uint i=0; i < kLanguageArray.size(); i++)
{
mLanguages[i].Language = kLanguageArray[i];
mLanguages[i].Strings.resize(1);
}
} }
/** Serialize resource data */ /** Serialize resource data */
@ -344,36 +380,12 @@ TString CStringTable::StripFormatting(const TString& kInString)
/** Static - Returns whether a given language is supported by the given game/region combination */ /** Static - Returns whether a given language is supported by the given game/region combination */
bool CStringTable::IsLanguageSupported(ELanguage Language, EGame Game, ERegion Region) bool CStringTable::IsLanguageSupported(ELanguage Language, EGame Game, ERegion Region)
{ {
// Pick the correct array to iterate based on which game/region was requested. const std::vector<ELanguage>& kLanguageArray = GetSupportedLanguages(Game, Region);
const ELanguage* pkSupportedLanguages = nullptr;
uint NumLanguages = 0;
if (Game <= EGame::Prime && Region == ERegion::NTSC)
{
return (Language == ELanguage::English);
}
else if (Game <= EGame::CorruptionProto)
{
pkSupportedLanguages = &gkSupportedLanguagesMP1PAL[0];
NumLanguages = ARRAY_SIZE( gkSupportedLanguagesMP1PAL );
}
else if (Game <= EGame::Corruption)
{
pkSupportedLanguages = &gkSupportedLanguagesMP3[0];
NumLanguages = ARRAY_SIZE( gkSupportedLanguagesMP3 );
}
else if (Game <= EGame::DKCReturns)
{
pkSupportedLanguages = &gkSupportedLanguagesDKCR[0];
NumLanguages = ARRAY_SIZE( gkSupportedLanguagesDKCR );
}
ASSERT(pkSupportedLanguages);
ASSERT(NumLanguages > 0);
// Check if the requested language is in the array. // Check if the requested language is in the array.
for (uint LanguageIdx = 0; LanguageIdx < NumLanguages; LanguageIdx++) for (uint LanguageIdx = 0; LanguageIdx < kLanguageArray.size(); LanguageIdx++)
{ {
if (pkSupportedLanguages[LanguageIdx] == Language) if (kLanguageArray[LanguageIdx] == Language)
{ {
return true; return true;
} }

View File

@ -84,14 +84,14 @@ public:
/** Remove a string from the table */ /** Remove a string from the table */
void RemoveString(uint StringIndex); void RemoveString(uint StringIndex);
/** Configures the string table with default languages for the game/region pairing of the resource */ /** Initialize new resource data */
void ConfigureDefaultLanguages(); virtual void InitializeNewResource() override;
/** Serialize resource data */ /** Serialize resource data */
virtual void Serialize(IArchive& Arc); virtual void Serialize(IArchive& Arc) override;
/** Build the dependency tree for this resource */ /** Build the dependency tree for this resource */
virtual CDependencyTree* BuildDependencyTree() const; virtual CDependencyTree* BuildDependencyTree() const override;
/** Static - Strip all formatting tags for a given string */ /** Static - Strip all formatting tags for a given string */
static TString StripFormatting(const TString& kInString); static TString StripFormatting(const TString& kInString);

View File

@ -9,6 +9,11 @@ CTweakManager::CTweakManager(CGameProject* pInProject)
{ {
} }
CTweakManager::~CTweakManager()
{
ClearTweaks();
}
void CTweakManager::LoadTweaks() void CTweakManager::LoadTweaks()
{ {
ASSERT( mTweakObjects.empty() ); ASSERT( mTweakObjects.empty() );
@ -33,21 +38,6 @@ void CTweakManager::LoadTweaks()
} }
} }
CTweakManager::~CTweakManager()
{
for (CTweakData* pTweakData : mTweakObjects)
{
if (pTweakData->Entry() != nullptr)
{
pTweakData->Release();
}
else
{
delete pTweakData;
}
}
}
bool CTweakManager::SaveTweaks() bool CTweakManager::SaveTweaks()
{ {
// MP1 - Save all tweak assets // MP1 - Save all tweak assets
@ -82,3 +72,19 @@ bool CTweakManager::SaveTweaks()
return CTweakCooker::CookNTWK(mTweakObjects, StandardNTWK); return CTweakCooker::CookNTWK(mTweakObjects, StandardNTWK);
} }
} }
void CTweakManager::ClearTweaks()
{
for (CTweakData* pTweakData : mTweakObjects)
{
if (pTweakData->Entry() != nullptr)
{
pTweakData->Release();
}
else
{
delete pTweakData;
}
}
mTweakObjects.clear();
}

View File

@ -17,6 +17,7 @@ public:
~CTweakManager(); ~CTweakManager();
void LoadTweaks(); void LoadTweaks();
bool SaveTweaks(); bool SaveTweaks();
void ClearTweaks();
// Accessors // Accessors
inline const std::vector<CTweakData*>& TweakObjects() const inline const std::vector<CTweakData*>& TweakObjects() const

View File

@ -249,6 +249,7 @@ bool CEditorApplication::RebuildResourceDatabase()
{ {
// Fake-close the project, but keep it in memory so we can modify the resource store // Fake-close the project, but keep it in memory so we can modify the resource store
CGameProject *pProj = mpActiveProject; CGameProject *pProj = mpActiveProject;
mpActiveProject->TweakManager()->ClearTweaks();
mpActiveProject = nullptr; mpActiveProject = nullptr;
emit ActiveProjectChanged(nullptr); emit ActiveProjectChanged(nullptr);
@ -263,6 +264,7 @@ bool CEditorApplication::RebuildResourceDatabase()
// Set project to active again // Set project to active again
mpActiveProject = pProj; mpActiveProject = pProj;
mpActiveProject->TweakManager()->LoadTweaks();
emit ActiveProjectChanged(pProj); emit ActiveProjectChanged(pProj);
UICommon::InfoMsg(mpWorldEditor, "Success", "Resource database rebuilt successfully!"); UICommon::InfoMsg(mpWorldEditor, "Success", "Resource database rebuilt successfully!");

View File

@ -34,7 +34,12 @@ CPropertyDelegate::CPropertyDelegate(QObject* pParent /*= 0*/)
, mEditInProgress(false) , mEditInProgress(false)
, mRelaysBlocked(false) , mRelaysBlocked(false)
{ {
mpEditor = UICommon::FindAncestor<IEditor>(this); mpEditor = UICommon::FindAncestor<IEditor>(pParent);
}
void CPropertyDelegate::SetEditor(IEditor* pEditor)
{
mpEditor = pEditor;
} }
void CPropertyDelegate::SetPropertyModel(CPropertyModel* pModel) void CPropertyDelegate::SetPropertyModel(CPropertyModel* pModel)

View File

@ -83,6 +83,11 @@ bool CPropertyView::event(QEvent *pEvent)
else return QTreeView::event(pEvent); else return QTreeView::event(pEvent);
} }
void CPropertyView::SetEditor(IEditor* pEditor)
{
mpDelegate->SetEditor(pEditor);
}
void CPropertyView::InitColumnWidths(float NameColumnPercentage, float ValueColumnPercentage) void CPropertyView::InitColumnWidths(float NameColumnPercentage, float ValueColumnPercentage)
{ {
header()->resizeSection(0, width() * NameColumnPercentage); header()->resizeSection(0, width() * NameColumnPercentage);

View File

@ -25,6 +25,7 @@ public:
CPropertyView(QWidget* pParent = 0); CPropertyView(QWidget* pParent = 0);
void setModel(QAbstractItemModel* pModel); void setModel(QAbstractItemModel* pModel);
bool event(QEvent* pEvent); bool event(QEvent* pEvent);
void SetEditor(IEditor* pEditor);
void InitColumnWidths(float NameColumnPercentage, float ValueColumnPercentage); void InitColumnWidths(float NameColumnPercentage, float ValueColumnPercentage);
void ClearProperties(); void ClearProperties();
void SetIntrinsicProperties(CStructRef InProperties); void SetIntrinsicProperties(CStructRef InProperties);

View File

@ -28,6 +28,7 @@ CResourceBrowser::CResourceBrowser(QWidget *pParent)
, mEditorStore(false) , mEditorStore(false)
, mAssetListMode(false) , mAssetListMode(false)
, mSearching(false) , mSearching(false)
, mpAddMenu(nullptr)
, mpInspectedEntry(nullptr) , mpInspectedEntry(nullptr)
{ {
mpUI->setupUi(this); mpUI->setupUi(this);
@ -150,7 +151,6 @@ CResourceBrowser::CResourceBrowser(QWidget *pParent)
connect(mpUI->ResourceTreeButton, SIGNAL(pressed()), this, SLOT(SetResourceTreeView())); connect(mpUI->ResourceTreeButton, SIGNAL(pressed()), this, SLOT(SetResourceTreeView()));
connect(mpUI->ResourceListButton, SIGNAL(pressed()), this, SLOT(SetResourceListView())); connect(mpUI->ResourceListButton, SIGNAL(pressed()), this, SLOT(SetResourceListView()));
connect(mpUI->SortComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(OnSortModeChanged(int))); connect(mpUI->SortComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(OnSortModeChanged(int)));
connect(mpUI->NewFolderButton, SIGNAL(pressed()), this, SLOT(CreateDirectory()));
connect(mpUI->ClearButton, SIGNAL(pressed()), this, SLOT(OnClearButtonPressed())); connect(mpUI->ClearButton, SIGNAL(pressed()), this, SLOT(OnClearButtonPressed()));
connect(mpUI->DirectoryTreeView, SIGNAL(clicked(QModelIndex)), this, SLOT(OnDirectorySelectionChanged(QModelIndex))); connect(mpUI->DirectoryTreeView, SIGNAL(clicked(QModelIndex)), this, SLOT(OnDirectorySelectionChanged(QModelIndex)));
@ -281,6 +281,50 @@ void CResourceBrowser::CreateFilterCheckboxes()
mpFilterBoxesLayout->addSpacerItem(pSpacer); mpFilterBoxesLayout->addSpacerItem(pSpacer);
} }
void CResourceBrowser::CreateAddMenu()
{
// Delete any existing menu
if (mpAddMenu)
{
delete mpAddMenu;
mpAddMenu = nullptr;
}
// Create new one, only if we have a valid resource store.
if (mpStore)
{
mpAddMenu = new QMenu(this);
mpAddMenu->addAction("New Folder", this, SLOT(CreateDirectory()));
mpAddMenu->addSeparator();
QMenu* pCreateMenu = new QMenu("Create...");
mpAddMenu->addMenu(pCreateMenu);
AddCreateAssetMenuActions(pCreateMenu);
mpUI->AddButton->setMenu(mpAddMenu);
}
mpUI->AddButton->setEnabled( mpAddMenu != nullptr );
}
void CResourceBrowser::AddCreateAssetMenuActions(QMenu* pMenu)
{
std::list<CResTypeInfo*> TypeInfos;
CResTypeInfo::GetAllTypesInGame(mpStore->Game(), TypeInfos);
for (auto Iter = TypeInfos.begin(); Iter != TypeInfos.end(); Iter++)
{
CResTypeInfo* pTypeInfo = *Iter;
if (pTypeInfo->CanBeCreated())
{
QString TypeName = TO_QSTRING( pTypeInfo->TypeName() );
QAction* pAction = pMenu->addAction(TypeName, this, SLOT(OnCreateAssetAction()));
pAction->setProperty("TypeInfo", QVariant((int) pTypeInfo->Type()));
}
}
}
bool CResourceBrowser::RenameResource(CResourceEntry *pEntry, const TString& rkNewName) bool CResourceBrowser::RenameResource(CResourceEntry *pEntry, const TString& rkNewName)
{ {
if (pEntry->Name() == rkNewName) if (pEntry->Name() == rkNewName)
@ -400,6 +444,49 @@ bool CResourceBrowser::MoveResources(const QList<CResourceEntry*>& rkResources,
return true; return true;
} }
CResourceEntry* CResourceBrowser::CreateNewResource(EResourceType Type)
{
// Create new asset ID. Sanity check to make sure the ID is unused.
CAssetID NewAssetID;
while (!NewAssetID.IsValid() || mpStore->FindEntry(NewAssetID) != nullptr)
{
NewAssetID = CAssetID::RandomID( mpStore->Game() );
}
// Boring generic default name - user will immediately be prompted to change this
TString BaseName = TString::Format(
"New %s", *CResTypeInfo::FindTypeInfo(Type)->TypeName()
);
TString Name = BaseName;
int Num = 0;
while (mpSelectedDir->FindChildResource(Name, Type) != nullptr)
{
Num++;
Name = TString::Format("%s (%d)", *BaseName, Num);
}
emit ResourceAboutToBeCreated(mpSelectedDir);
// Create the actual resource
CResourceEntry* pEntry = mpStore->CreateNewResource(NewAssetID, Type, mpSelectedDir->FullPath(), Name);
pEntry->Save();
emit ResourceCreated(pEntry);
// Select new resource so user can enter a name
QModelIndex Index = mpModel->GetIndexForEntry(pEntry);
ASSERT(Index.isValid());
QModelIndex ProxyIndex = mpProxyModel->mapFromSource(Index);
mpUI->ResourceTableView->selectionModel()->select(ProxyIndex, QItemSelectionModel::ClearAndSelect);
mpUI->ResourceTableView->edit(ProxyIndex);
return pEntry;
}
bool CResourceBrowser::eventFilter(QObject *pWatched, QEvent *pEvent) bool CResourceBrowser::eventFilter(QObject *pWatched, QEvent *pEvent)
{ {
if (pWatched == mpUI->ResourceTableView) if (pWatched == mpUI->ResourceTableView)
@ -498,6 +585,23 @@ void CResourceBrowser::OnSortModeChanged(int Index)
mpProxyModel->SetSortMode(Mode); mpProxyModel->SetSortMode(Mode);
} }
void CResourceBrowser::OnCreateAssetAction()
{
// Attempt to retrieve the asset type from the sender. If successful, create the asset.
QAction* pSender = qobject_cast<QAction*>(sender());
if (pSender)
{
bool Ok;
EResourceType Type = (EResourceType) pSender->property("TypeInfo").toInt(&Ok);
if (Ok)
{
CreateNewResource(Type);
}
}
}
bool CResourceBrowser::CreateDirectory() bool CResourceBrowser::CreateDirectory()
{ {
if (mpSelectedDir) if (mpSelectedDir)
@ -685,7 +789,8 @@ void CResourceBrowser::UpdateStore()
mpUI->SearchBar->clear(); mpUI->SearchBar->clear();
mSearching = false; mSearching = false;
// Refresh type filter list // Refresh project-specific UI
CreateAddMenu();
CreateFilterCheckboxes(); CreateFilterCheckboxes();
// Refresh directory tree // Refresh directory tree

View File

@ -5,7 +5,9 @@
#include "CResourceProxyModel.h" #include "CResourceProxyModel.h"
#include "CResourceTableModel.h" #include "CResourceTableModel.h"
#include "CVirtualDirectoryModel.h" #include "CVirtualDirectoryModel.h"
#include <QCheckBox> #include <QCheckBox>
#include <QMenu>
#include <QTimer> #include <QTimer>
#include <QUndoStack> #include <QUndoStack>
#include <QVBoxLayout> #include <QVBoxLayout>
@ -29,6 +31,9 @@ class CResourceBrowser : public QWidget
bool mAssetListMode; bool mAssetListMode;
bool mSearching; bool mSearching;
// Add Menu
QMenu *mpAddMenu;
// Type Filter // Type Filter
QWidget *mpFilterBoxesContainerWidget; QWidget *mpFilterBoxesContainerWidget;
QVBoxLayout *mpFilterBoxesLayout; QVBoxLayout *mpFilterBoxesLayout;
@ -59,11 +64,16 @@ public:
void SelectResource(CResourceEntry *pEntry, bool ClearFiltersIfNecessary = false); void SelectResource(CResourceEntry *pEntry, bool ClearFiltersIfNecessary = false);
void SelectDirectory(CVirtualDirectory *pDir); void SelectDirectory(CVirtualDirectory *pDir);
void CreateFilterCheckboxes(); void CreateFilterCheckboxes();
void CreateAddMenu();
void AddCreateAssetMenuActions(QMenu* pMenu);
bool RenameResource(CResourceEntry *pEntry, const TString& rkNewName); bool RenameResource(CResourceEntry *pEntry, const TString& rkNewName);
bool RenameDirectory(CVirtualDirectory *pDir, const TString& rkNewName); bool RenameDirectory(CVirtualDirectory *pDir, const TString& rkNewName);
bool MoveResources(const QList<CResourceEntry*>& rkResources, const QList<CVirtualDirectory*>& rkDirectories, CVirtualDirectory *pNewDir); bool MoveResources(const QList<CResourceEntry*>& rkResources, const QList<CVirtualDirectory*>& rkDirectories, CVirtualDirectory *pNewDir);
CResourceEntry* CreateNewResource(EResourceType Type);
// Interface // Interface
bool eventFilter(QObject *pWatched, QEvent *pEvent); bool eventFilter(QObject *pWatched, QEvent *pEvent);
@ -82,6 +92,7 @@ public slots:
void SetResourceListView(); void SetResourceListView();
void OnClearButtonPressed(); void OnClearButtonPressed();
void OnSortModeChanged(int Index); void OnSortModeChanged(int Index);
void OnCreateAssetAction();
bool CreateDirectory(); bool CreateDirectory();
bool DeleteDirectories(const QList<CVirtualDirectory*>& rkDirs); bool DeleteDirectories(const QList<CVirtualDirectory*>& rkDirs);
void OnSearchStringChanged(QString SearchString); void OnSearchStringChanged(QString SearchString);
@ -116,6 +127,12 @@ signals:
void ResourceAboutToBeMoved(CResourceEntry *pRes, QString NewPath); void ResourceAboutToBeMoved(CResourceEntry *pRes, QString NewPath);
void ResourceMoved(CResourceEntry *pRes, CVirtualDirectory *pOldDir, TString OldName); void ResourceMoved(CResourceEntry *pRes, CVirtualDirectory *pOldDir, TString OldName);
void ResourceAboutToBeCreated(CVirtualDirectory* pInDir);
void ResourceCreated(CResourceEntry *pRes);
void ResourceAboutToBeDeleted(CResourceEntry *pRes);
void ResourceDeleted();
void DirectoryAboutToBeMoved(CVirtualDirectory *pDir, QString NewPath); void DirectoryAboutToBeMoved(CVirtualDirectory *pDir, QString NewPath);
void DirectoryMoved(CVirtualDirectory *pDir, CVirtualDirectory *pOldDir, TString OldName); void DirectoryMoved(CVirtualDirectory *pDir, CVirtualDirectory *pOldDir, TString OldName);

View File

@ -141,9 +141,18 @@
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QPushButton" name="NewFolderButton"> <widget class="QPushButton" name="AddButton">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text"> <property name="text">
<string> New Folder</string> <string/>
</property> </property>
<property name="icon"> <property name="icon">
<iconset resource="../Icons.qrc"> <iconset resource="../Icons.qrc">
@ -151,8 +160,8 @@
</property> </property>
<property name="iconSize"> <property name="iconSize">
<size> <size>
<width>16</width> <width>24</width>
<height>16</height> <height>24</height>
</size> </size>
</property> </property>
</widget> </widget>

View File

@ -9,180 +9,252 @@ CResourceTableContextMenu::CResourceTableContextMenu(CResourceBrowser *pBrowser,
, mpTable(pView) , mpTable(pView)
, mpModel(pModel) , mpModel(pModel)
, mpProxy(pProxy) , mpProxy(pProxy)
, mpEntry(nullptr) , mpClickedEntry(nullptr)
, mpDirectory(nullptr) , mpClickedDirectory(nullptr)
{ {
// Connect to the view // Connect to the view
connect(pView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(ShowMenu(QPoint))); connect(pView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(ShowMenu(QPoint)));
}
// Create actions void CResourceTableContextMenu::InitMenu()
{
// Clear existing menu items
clear();
if (mpClickedEntry)
{
#if WIN32 #if WIN32
QString OpenInExplorerString = "Show in Explorer"; const QString kOpenInExplorerString = "Show in Explorer";
#elif __APPLE__ #elif __APPLE__
QString OpenInExplorerString = "Show in Finder"; const QString kOpenInExplorerString = "Show in Finder";
#else #else
QString OpenInExplorerString = "Show in file manager"; const QString kOpenInExplorerString = "Show in file manager";
#endif #endif
mpOpenAction = addAction("Open", this, SLOT(Open())); addAction("Open", this, SLOT(Open()));
mpOpenInExternalAppAction = addAction("Open in External Application", this, SLOT(OpenInExternalApp())); addAction("Open in External Application", this, SLOT(OpenInExternalApp()));
mpOpenInExplorerAction = addAction(OpenInExplorerString, this, SLOT(OpenInExplorer())); addAction(kOpenInExplorerString, this, SLOT(OpenInExplorer()));
addSeparator(); addSeparator();
mpRenameAction = addAction("Rename", this, SLOT(Rename())); }
mpSelectFolderAction = addAction("Select Folder", this, SLOT(SelectFolder()));
mpShowReferencersAction = addAction("Show Referencers", this, SLOT(ShowReferencers())); if (mpClickedEntry || mpClickedDirectory)
mpShowDependenciesAction = addAction("Show Dependencies", this, SLOT(ShowDependencies())); {
mpDeleteAction = addAction("Delete", this, SLOT(Delete())); addAction("Rename", this, SLOT(Rename()));
if (mpModel->IsDisplayingAssetList())
{
addAction("Select Folder", this, SLOT(SelectFolder()));
}
if (mpClickedEntry)
{
addAction("Show Referencers", this, SLOT(ShowReferencers()));
addAction("Show Dependencies", this, SLOT(ShowDependencies()));
}
}
if (mpClickedEntry || mpClickedDirectory || !mSelectedIndexes.isEmpty())
{
addAction("Delete", this, SLOT(Delete()));
}
addSeparator(); addSeparator();
mpCopyNameAction = addAction("Copy Name", this, SLOT(CopyName()));
mpCopyPathAction = addAction("Copy Path", this, SLOT(CopyPath())); if (mpClickedEntry)
mpCopyIDAction = addAction("Copy Asset ID", this, SLOT(CopyID())); {
addAction("Copy Name", this, SLOT(CopyName()));
addAction("Copy Path", this, SLOT(CopyPath()));
addAction("Copy ID", this, SLOT(CopyID()));
addSeparator();
}
QMenu* pCreate = addMenu("Create...");
mpBrowser->AddCreateAssetMenuActions(pCreate);
} }
void CResourceTableContextMenu::ShowMenu(const QPoint& rkPos) void CResourceTableContextMenu::ShowMenu(const QPoint& rkPos)
{ {
if (mpBrowser->CurrentStore() == nullptr)
return;
// Fetch the entry/directory // Fetch the entry/directory
mProxyIndex = mpTable->indexAt(rkPos); mClickedProxyIndex = mpTable->indexAt(rkPos);
if (mProxyIndex.isValid()) if (mClickedProxyIndex.isValid())
{ {
mIndex = mpProxy->mapToSource(mProxyIndex); mClickedIndex = mpProxy->mapToSource(mClickedProxyIndex);
mpEntry = mpModel->IndexEntry(mIndex); mpClickedEntry = mpModel->IndexEntry(mClickedIndex);
mpDirectory = mpModel->IndexDirectory(mIndex); mpClickedDirectory = mpModel->IndexDirectory(mClickedIndex);
}
else
{
mClickedIndex = QModelIndex();
mpClickedEntry = nullptr;
mpClickedDirectory = nullptr;
}
// Show/hide menu options // Fetch the list of selected indexes
bool IsRes = (mpEntry != nullptr); QItemSelection Selection = mpProxy->mapSelectionToSource( mpTable->selectionModel()->selection() );
mpOpenInExternalAppAction->setVisible(IsRes); mSelectedIndexes = Selection.indexes();
mpSelectFolderAction->setVisible(mpModel->IsDisplayingAssetList());
mpShowDependenciesAction->setVisible(IsRes); InitMenu();
mpShowReferencersAction->setVisible(IsRes);
mpDeleteAction->setVisible(mpDirectory && mpDirectory->IsEmpty(true));
mpCopyIDAction->setVisible(IsRes);
// Exec menu // Exec menu
QPoint GlobalPos = mpTable->viewport()->mapToGlobal(rkPos); QPoint GlobalPos = mpTable->viewport()->mapToGlobal(rkPos);
exec(GlobalPos); exec(GlobalPos);
} }
}
// Menu Options // Menu Options
void CResourceTableContextMenu::Open() void CResourceTableContextMenu::Open()
{ {
if (mpEntry) if (mpClickedEntry)
gpEdApp->EditResource(mpEntry); gpEdApp->EditResource(mpClickedEntry);
else else
mpBrowser->SetActiveDirectory(mpDirectory); mpBrowser->SetActiveDirectory(mpClickedDirectory);
} }
void CResourceTableContextMenu::OpenInExternalApp() void CResourceTableContextMenu::OpenInExternalApp()
{ {
ASSERT(mpEntry); ASSERT(mpClickedEntry);
UICommon::OpenInExternalApplication( TO_QSTRING(mpEntry->CookedAssetPath()) ); UICommon::OpenInExternalApplication( TO_QSTRING(mpClickedEntry->CookedAssetPath()) );
} }
void CResourceTableContextMenu::OpenInExplorer() void CResourceTableContextMenu::OpenInExplorer()
{ {
if (mpEntry) if (mpClickedEntry)
{ {
QString Path = TO_QSTRING( mpEntry->CookedAssetPath() ); QString Path = TO_QSTRING( mpClickedEntry->CookedAssetPath() );
UICommon::OpenContainingFolder(Path); UICommon::OpenContainingFolder(Path);
} }
else else
{ {
TString BasePath = mpBrowser->CurrentStore()->ResourcesDir(); TString BasePath = mpBrowser->CurrentStore()->ResourcesDir();
QString Path = TO_QSTRING( BasePath + mpDirectory->FullPath() ); QString Path = TO_QSTRING( BasePath + mpClickedDirectory->FullPath() );
UICommon::OpenContainingFolder(Path); UICommon::OpenContainingFolder(Path);
} }
} }
void CResourceTableContextMenu::Rename() void CResourceTableContextMenu::Rename()
{ {
mpTable->edit(mProxyIndex); mpTable->edit(mClickedProxyIndex);
} }
void CResourceTableContextMenu::SelectFolder() void CResourceTableContextMenu::SelectFolder()
{ {
CVirtualDirectory *pDir = (mpEntry ? mpEntry->Directory() : mpDirectory->Parent()); CVirtualDirectory *pDir = (mpClickedEntry ? mpClickedEntry->Directory() : mpClickedDirectory->Parent());
mpBrowser->SetActiveDirectory(pDir); mpBrowser->SetActiveDirectory(pDir);
if (mpEntry) if (mpClickedEntry)
mpBrowser->SelectResource(mpEntry); mpBrowser->SelectResource(mpClickedEntry);
else else
mpBrowser->SelectDirectory(mpDirectory); mpBrowser->SelectDirectory(mpClickedDirectory);
} }
void CResourceTableContextMenu::ShowReferencers() void CResourceTableContextMenu::ShowReferencers()
{ {
ASSERT(mpEntry); ASSERT(mpClickedEntry);
QList<CResourceEntry*> EntryList; QList<CResourceEntry*> EntryList;
for (CResourceIterator Iter(mpEntry->ResourceStore()); Iter; ++Iter) for (CResourceIterator Iter(mpClickedEntry->ResourceStore()); Iter; ++Iter)
{ {
if (Iter->Dependencies()->HasDependency(mpEntry->ID())) if (Iter->Dependencies()->HasDependency(mpClickedEntry->ID()))
EntryList << *Iter; EntryList << *Iter;
} }
if (!mpModel->IsDisplayingUserEntryList()) if (!mpModel->IsDisplayingUserEntryList())
mpBrowser->SetInspectedEntry(mpEntry); mpBrowser->SetInspectedEntry(mpClickedEntry);
QString ListDesc = QString("Referencers of \"%1\"").arg( TO_QSTRING(mpEntry->CookedAssetPath().GetFileName()) ); QString ListDesc = QString("Referencers of \"%1\"").arg( TO_QSTRING(mpClickedEntry->CookedAssetPath().GetFileName()) );
mpModel->DisplayEntryList(EntryList, ListDesc); mpModel->DisplayEntryList(EntryList, ListDesc);
mpBrowser->ClearFilters(); mpBrowser->ClearFilters();
} }
void CResourceTableContextMenu::ShowDependencies() void CResourceTableContextMenu::ShowDependencies()
{ {
ASSERT(mpEntry); ASSERT(mpClickedEntry);
std::set<CAssetID> Dependencies; std::set<CAssetID> Dependencies;
mpEntry->Dependencies()->GetAllResourceReferences(Dependencies); mpClickedEntry->Dependencies()->GetAllResourceReferences(Dependencies);
QList<CResourceEntry*> EntryList; QList<CResourceEntry*> EntryList;
for (auto Iter = Dependencies.begin(); Iter != Dependencies.end(); Iter++) for (auto Iter = Dependencies.begin(); Iter != Dependencies.end(); Iter++)
{ {
CResourceEntry *pEntry = mpEntry->ResourceStore()->FindEntry(*Iter); CResourceEntry *pEntry = mpClickedEntry->ResourceStore()->FindEntry(*Iter);
if (pEntry) if (pEntry)
EntryList << pEntry; EntryList << pEntry;
} }
if (!mpModel->IsDisplayingUserEntryList()) if (!mpModel->IsDisplayingUserEntryList())
mpBrowser->SetInspectedEntry(mpEntry); mpBrowser->SetInspectedEntry(mpClickedEntry);
QString ListDesc = QString("Dependencies of \"%1\"").arg( TO_QSTRING(mpEntry->CookedAssetPath().GetFileName()) ); QString ListDesc = QString("Dependencies of \"%1\"").arg( TO_QSTRING(mpClickedEntry->CookedAssetPath().GetFileName()) );
mpModel->DisplayEntryList(EntryList, ListDesc); mpModel->DisplayEntryList(EntryList, ListDesc);
mpBrowser->ClearFilters(); mpBrowser->ClearFilters();
} }
void CResourceTableContextMenu::Delete() void CResourceTableContextMenu::Delete()
{ {
ASSERT(mpDirectory && mpDirectory->IsEmpty(true)); // Create confirmation message
uint NumResources = 0, NumDirectories = 0;
foreach (const QModelIndex& kIndex, mSelectedIndexes)
{
if (mpModel->IsIndexDirectory(kIndex))
NumDirectories++;
else
NumResources++;
}
if (NumResources == 0 && NumDirectories == 0)
return;
QString ConfirmMsg = QString("Are you sure you want to permanently delete ");
if (NumResources > 0)
{
ConfirmMsg += QString("%d resource%s").arg(NumResources).arg(NumResources == 1 ? "" : "s");
if (NumDirectories > 0)
{
ConfirmMsg += " and ";
}
}
if (NumDirectories > 0)
{
ConfirmMsg += QString("%d %s").arg(NumDirectories).arg(NumDirectories == 1 ? "directory" : "directories");
}
// Allow the user to confirm the action before performing it
if (UICommon::YesNoQuestion(mpBrowser, "Warning", ConfirmMsg))
{
//@todo this is wrong lol
QList<CVirtualDirectory*> List; QList<CVirtualDirectory*> List;
List << mpDirectory; List << mpClickedDirectory;
mpBrowser->DeleteDirectories(List); mpBrowser->DeleteDirectories(List);
} }
}
void CResourceTableContextMenu::CopyName() void CResourceTableContextMenu::CopyName()
{ {
if (mpEntry) if (mpClickedEntry)
gpEdApp->clipboard()->setText( TO_QSTRING(mpEntry->Name()) ); gpEdApp->clipboard()->setText( TO_QSTRING(mpClickedEntry->Name()) );
else else
gpEdApp->clipboard()->setText( TO_QSTRING(mpDirectory->Name()) ); gpEdApp->clipboard()->setText( TO_QSTRING(mpClickedDirectory->Name()) );
} }
void CResourceTableContextMenu::CopyPath() void CResourceTableContextMenu::CopyPath()
{ {
if (mpEntry) if (mpClickedEntry)
gpEdApp->clipboard()->setText( TO_QSTRING(mpEntry->CookedAssetPath(true)) ); gpEdApp->clipboard()->setText( TO_QSTRING(mpClickedEntry->CookedAssetPath(true)) );
else else
gpEdApp->clipboard()->setText( TO_QSTRING(mpDirectory->FullPath()) ); gpEdApp->clipboard()->setText( TO_QSTRING(mpClickedDirectory->FullPath()) );
} }
void CResourceTableContextMenu::CopyID() void CResourceTableContextMenu::CopyID()
{ {
ASSERT(mpEntry); ASSERT(mpClickedEntry);
gpEdApp->clipboard()->setText( TO_QSTRING(mpEntry->ID().ToString()) ); gpEdApp->clipboard()->setText( TO_QSTRING(mpClickedEntry->ID().ToString()) );
} }

View File

@ -16,30 +16,17 @@ class CResourceTableContextMenu : public QMenu
CResourceTableModel *mpModel; CResourceTableModel *mpModel;
CResourceProxyModel *mpProxy; CResourceProxyModel *mpProxy;
QModelIndex mProxyIndex; QModelIndexList mSelectedIndexes;
QModelIndex mIndex; QModelIndex mClickedIndex;
CResourceEntry *mpEntry; QModelIndex mClickedProxyIndex;
CVirtualDirectory *mpDirectory; CResourceEntry *mpClickedEntry;
CVirtualDirectory *mpClickedDirectory;
// Actions
QAction *mpOpenAction;
QAction *mpOpenInExternalAppAction;
QAction *mpOpenInExplorerAction;
QAction *mpSelectFolderAction;
QAction *mpRenameAction;
QAction *mpShowReferencersAction;
QAction *mpShowDependenciesAction;
QAction *mpDeleteAction;
QAction *mpCopyNameAction;
QAction *mpCopyPathAction;
QAction *mpCopyIDAction;
public: public:
CResourceTableContextMenu(CResourceBrowser *pBrowser, QTableView *pView, CResourceTableModel *pModel, CResourceProxyModel *pProxy); CResourceTableContextMenu(CResourceBrowser *pBrowser, QTableView *pView, CResourceTableModel *pModel, CResourceProxyModel *pProxy);
public slots: public slots:
void InitMenu();
void ShowMenu(const QPoint& rkPos); void ShowMenu(const QPoint& rkPos);
// Menu Options // Menu Options

View File

@ -7,6 +7,7 @@ CResourceTableModel::CResourceTableModel(CResourceBrowser *pBrowser, QObject *pP
, mpCurrentDir(nullptr) , mpCurrentDir(nullptr)
, mIsDisplayingUserEntryList(false) , mIsDisplayingUserEntryList(false)
{ {
connect(pBrowser, SIGNAL(ResourceCreated(CResourceEntry*)), this, SLOT(CheckAddResource(CResourceEntry*)));
connect(pBrowser, SIGNAL(DirectoryCreated(CVirtualDirectory*)), this, SLOT(CheckAddDirectory(CVirtualDirectory*))); connect(pBrowser, SIGNAL(DirectoryCreated(CVirtualDirectory*)), this, SLOT(CheckAddDirectory(CVirtualDirectory*)));
connect(pBrowser, SIGNAL(DirectoryAboutToBeDeleted(CVirtualDirectory*)), this, SLOT(CheckRemoveDirectory(CVirtualDirectory*))); connect(pBrowser, SIGNAL(DirectoryAboutToBeDeleted(CVirtualDirectory*)), this, SLOT(CheckRemoveDirectory(CVirtualDirectory*)));
connect(pBrowser, SIGNAL(ResourceMoved(CResourceEntry*,CVirtualDirectory*,TString)), this, SLOT(OnResourceMoved(CResourceEntry*,CVirtualDirectory*,TString))); connect(pBrowser, SIGNAL(ResourceMoved(CResourceEntry*,CVirtualDirectory*,TString)), this, SLOT(OnResourceMoved(CResourceEntry*,CVirtualDirectory*,TString)));
@ -155,7 +156,7 @@ Qt::DropActions CResourceTableModel::supportedDropActions() const
// ************ FUNCTIONALITY ************ // ************ FUNCTIONALITY ************
QModelIndex CResourceTableModel::GetIndexForEntry(CResourceEntry *pEntry) const QModelIndex CResourceTableModel::GetIndexForEntry(CResourceEntry *pEntry) const
{ {
auto Iter = qBinaryFind(mEntries, pEntry); auto Iter = std::find(mEntries.begin(), mEntries.end(), pEntry);
if (Iter == mEntries.end()) if (Iter == mEntries.end())
return QModelIndex(); return QModelIndex();
@ -289,6 +290,32 @@ void CResourceTableModel::RefreshAllIndices()
} }
} }
void CResourceTableModel::CheckAddResource(CResourceEntry *pEntry)
{
if ( (mIsAssetListMode && pEntry->IsInDirectory(mpCurrentDir)) ||
(!mIsAssetListMode && pEntry->Directory() == mpCurrentDir) )
{
// Append to the end, let the proxy handle sorting
int NumRows = mDirectories.size() + mEntries.size();
beginInsertRows(QModelIndex(), NumRows, NumRows);
mEntries << pEntry;
endInsertRows();
}
}
void CResourceTableModel::CheckRemoveResource(CResourceEntry *pEntry)
{
int Index = mEntries.indexOf(pEntry);
if (Index != -1)
{
Index += mDirectories.size();
beginRemoveRows(QModelIndex(), Index, Index);
mEntries.removeAt(Index);
endRemoveRows();
}
}
void CResourceTableModel::CheckAddDirectory(CVirtualDirectory *pDir) void CResourceTableModel::CheckAddDirectory(CVirtualDirectory *pDir)
{ {
if (pDir->Parent() == mpCurrentDir) if (pDir->Parent() == mpCurrentDir)

View File

@ -59,6 +59,8 @@ public:
public slots: public slots:
void RefreshAllIndices(); void RefreshAllIndices();
void CheckAddResource(CResourceEntry *pEntry);
void CheckRemoveResource(CResourceEntry *pEntry);
void CheckAddDirectory(CVirtualDirectory *pDir); void CheckAddDirectory(CVirtualDirectory *pDir);
void CheckRemoveDirectory(CVirtualDirectory *pDir); void CheckRemoveDirectory(CVirtualDirectory *pDir);
void OnResourceMoved(CResourceEntry *pEntry, CVirtualDirectory *pOldDir, TString OldName); void OnResourceMoved(CResourceEntry *pEntry, CVirtualDirectory *pOldDir, TString OldName);

View File

@ -14,9 +14,24 @@ CScanEditor::CScanEditor(CScan* pScan, QWidget* pParent /*= 0*/)
QString WindowTitle = "%APP_FULL_NAME% - Scan Editor - %1[*]"; QString WindowTitle = "%APP_FULL_NAME% - Scan Editor - %1[*]";
WindowTitle = WindowTitle.arg( TO_QSTRING(mpScan->Entry()->CookedAssetPath(true).GetFileName()) ); WindowTitle = WindowTitle.arg( TO_QSTRING(mpScan->Entry()->CookedAssetPath(true).GetFileName()) );
SET_WINDOWTITLE_APPVARS(WindowTitle); SET_WINDOWTITLE_APPVARS(WindowTitle);
connect( mpUI->ActionSave, SIGNAL(toggled(bool)), this, SLOT(Save()) );
connect( mpUI->ActionSaveAndCook, SIGNAL(toggled(bool)), this, SLOT(SaveAndRepack()) );
} }
CScanEditor::~CScanEditor() CScanEditor::~CScanEditor()
{ {
delete mpUI; delete mpUI;
} }
bool CScanEditor::Save()
{
if (mpScan->Entry()->Save())
{
UndoStack().setClean();
setWindowModified(false);
return true;
}
else
return false;
}

View File

@ -21,6 +21,9 @@ class CScanEditor : public IEditor
public: public:
explicit CScanEditor(CScan* pScan, QWidget* pParent = 0); explicit CScanEditor(CScan* pScan, QWidget* pParent = 0);
~CScanEditor(); ~CScanEditor();
public slots:
virtual bool Save() override;
}; };
#endif // CSCANEDITOR_H #endif // CSCANEDITOR_H

View File

@ -46,7 +46,7 @@ CStringEditor::CStringEditor(CStringTable* pStringTable, QWidget* pParent)
, mpUI(new Ui::CStringEditor) , mpUI(new Ui::CStringEditor)
, mpStringTable(pStringTable) , mpStringTable(pStringTable)
, mCurrentLanguage(ELanguage::English) , mCurrentLanguage(ELanguage::English)
, mCurrentStringIndex(0) , mCurrentStringIndex(-1)
, mCurrentStringCount(0) , mCurrentStringCount(0)
, mIsEditingStringName(false) , mIsEditingStringName(false)
, mIsEditingStringData(false) , mIsEditingStringData(false)

View File

@ -17,6 +17,7 @@ WModifyTab::WModifyTab(CWorldEditor *pEditor, QWidget *pParent)
{ {
ui->setupUi(this); ui->setupUi(this);
ui->PropertyView->InitColumnWidths(0.3f, 0.3f); ui->PropertyView->InitColumnWidths(0.3f, 0.3f);
ui->PropertyView->SetEditor(pEditor);
mpWorldEditor = pEditor; mpWorldEditor = pEditor;