Reimplemented save & repack button to work in the project system

This commit is contained in:
Aruki 2017-02-08 11:20:33 -07:00
parent 1b97cd459a
commit a7b0a2480c
13 changed files with 127 additions and 67 deletions

View File

@ -62,6 +62,10 @@ public:
}
// We couldn't find a matching element, so we can't load this parameter.
// If we pushed a parent earlier, undo it.
if (!mJustEndedParam)
mpCurElem = mpCurElem->Parent()->ToElement();
return false;
}

View File

@ -276,7 +276,7 @@ void GenerateAssetNames(CGameProject *pProj)
{
TString Name = pInst->InstanceName();
if (Name.EndsWith(".SCAN", false))
if (Name.StartsWith("POI_", false))
{
TIDString ScanIDString = (pProj->Game() <= ePrime ? "0x4:0x0" : "0xBDBEC295:0xB94E9BE7");
TAssetProperty *pScanProperty = TPropCast<TAssetProperty>(pInst->PropertyByIDString(ScanIDString));
@ -289,10 +289,10 @@ void GenerateAssetNames(CGameProject *pProj)
if (pEntry && !pEntry->IsNamed())
{
TWideString ScanName = Name.ToUTF16().ChopBack(5);
TWideString ScanName = Name.ToUTF16().ChopFront(4);
if (ScanName.StartsWith(L"POI_"))
ScanName = ScanName.ChopFront(4);
if (ScanName.EndsWith(L".SCAN", false))
ScanName = ScanName.ChopBack(5);
ApplyGeneratedName(pEntry, pEntry->DirectoryPath(), ScanName);
@ -419,7 +419,7 @@ void GenerateAssetNames(CGameProject *pProj)
{
CAudioGroup *pGroup = (CAudioGroup*) It->Load();
TWideString GroupName = pGroup->GroupName().ToUTF16();
ApplyGeneratedName(*It, L"AudioGrp\\", GroupName);
ApplyGeneratedName(*It, L"Audio\\", GroupName);
}
#endif

View File

@ -34,6 +34,24 @@ bool CAssetNameMap::GetNameInfo(CAssetID ID, TString& rOutDirectory, TString& rO
void CAssetNameMap::CopyFromStore(CResourceStore *pStore /*= gpResourceStore*/)
{
// Do a first pass to remove old paths from used set to prevent false positives from eg. if two resources switch places
for (CResourceIterator It(pStore); It; ++It)
{
if (It->IsCategorized() || It->IsNamed())
{
auto Find = mMap.find(It->ID());
if (Find != mMap.end())
{
SAssetNameInfo& rInfo = Find->second;
auto UsedFind = mUsedSet.find(rInfo);
ASSERT(UsedFind != mUsedSet.end());
mUsedSet.erase(UsedFind);
}
}
}
// Do a second pass to add the new paths to the map
for (CResourceIterator It(pStore); It; ++It)
{
if (It->IsCategorized() || It->IsNamed())
@ -44,17 +62,6 @@ void CAssetNameMap::CopyFromStore(CResourceStore *pStore /*= gpResourceStore*/)
CFourCC Type = It->CookedExtension();
SAssetNameInfo NameInfo { Name, Directory, Type };
// Remove old path from used set
auto Find = mMap.find(ID);
if (Find != mMap.end())
{
SAssetNameInfo& rInfo = Find->second;
auto UsedFind = mUsedSet.find(rInfo);
ASSERT(UsedFind != mUsedSet.end());
mUsedSet.erase(UsedFind);
}
// Check for conflicts with new name
if (mUsedSet.find(NameInfo) != mUsedSet.end())
{

View File

@ -57,6 +57,12 @@ void CGameProject::Serialize(IArchive& rArc)
if (rArc.IsReader())
{
// Load resource store
ASSERT(mpResourceStore == nullptr);
mpResourceStore = new CResourceStore(this);
mpResourceStore->LoadResourceDatabase();
// Load packages
for (u32 iPkg = 0; iPkg < mPackages.size(); iPkg++)
delete mPackages[iPkg];
mPackages.clear();
@ -169,8 +175,6 @@ CGameProject* CGameProject::LoadProject(const TWideString& rkProjPath)
pProj->Serialize(Reader);
CTemplateLoader::LoadGameTemplates(pProj->mGame);
pProj->mpResourceStore = new CResourceStore(pProj);
pProj->mpResourceStore->LoadResourceDatabase();
pProj->mpGameInfo->LoadGameInfo(pProj->mGame);
pProj->mpAudioManager->LoadAssets();
return pProj;

View File

@ -15,6 +15,7 @@ void CPackage::Load()
TWideString DefPath = DefinitionPath(false);
CXMLReader Reader(DefPath.ToUTF8());
Serialize(Reader);
UpdateDependencyCache();
}
void CPackage::Save()
@ -28,7 +29,18 @@ void CPackage::Save()
void CPackage::Serialize(IArchive& rArc)
{
rArc << SERIAL_CONTAINER("Collections", mCollections, "ResourceCollection");
rArc << SERIAL("NeedsRecook", mNeedsRecook)
<< SERIAL_CONTAINER("Collections", mCollections, "ResourceCollection");
}
void CPackage::UpdateDependencyCache()
{
CPackageDependencyListBuilder Builder(this);
std::list<CAssetID> AssetList;
Builder.BuildDependencyList(false, AssetList);
for (auto Iter = AssetList.begin(); Iter != AssetList.end(); Iter++)
mCachedDependencies.insert(*Iter);
}
void CPackage::Cook()
@ -74,18 +86,6 @@ void CPackage::Cook()
pkRes->ID.Write(Pak);
Pak.WriteLong(pkRes->Name.Size());
Pak.WriteString(pkRes->Name.ToStdString(), pkRes->Name.Size()); // Note: Explicitly specifying size means we don't write the terminating 0
// TEMP: recook world
if (pkRes->Type == "MLVL")
{
CResourceEntry *pEntry = gpResourceStore->FindEntry(pkRes->ID);
ASSERT(pEntry);
CWorld *pWorld = (CWorld*) pEntry->Load();
ASSERT(pWorld);
CFileOutStream MLVL(pEntry->CookedAssetPath().ToStdString(), IOUtil::eBigEndian);
ASSERT(MLVL.IsValid());
CWorldCooker::CookMLVL(pWorld, MLVL);
}
}
// Fill in table of contents with junk, write later
@ -114,10 +114,14 @@ void CPackage::Cook()
for (auto Iter = AssetList.begin(); Iter != AssetList.end(); Iter++, ResIdx++)
{
// Initialize entry, recook assets if needed
CAssetID ID = *Iter;
CResourceEntry *pEntry = gpResourceStore->FindEntry(ID);
ASSERT(pEntry != nullptr);
if (pEntry->NeedsRecook())
pEntry->Cook();
SResourceTocInfo& rTocInfo = ResourceTocData[ResIdx];
rTocInfo.pEntry = pEntry;
rTocInfo.Offset = Pak.Tell();
@ -200,6 +204,8 @@ void CPackage::Cook()
Pak.WriteLong(rkTocInfo.Offset);
}
mNeedsRecook = false;
Save();
Log::Write("Finished writing " + PakPath.ToUTF8());
}

View File

@ -50,6 +50,8 @@ class CPackage
TString mPakName;
TWideString mPakPath;
std::vector<CResourceCollection*> mCollections;
std::set<CAssetID> mCachedDependencies;
bool mNeedsRecook;
enum EPackageDefinitionVersion
{
@ -66,11 +68,13 @@ public:
: mpProject(pProj)
, mPakName(rkName)
, mPakPath(rkPath)
, mNeedsRecook(false)
{}
void Load();
void Save();
void Serialize(IArchive& rArc);
void UpdateDependencyCache();
void Cook();
void CompareOriginalAssetList(const std::list<CAssetID>& rkNewList);
@ -88,8 +92,11 @@ public:
inline CGameProject* Project() const { return mpProject; }
inline u32 NumCollections() const { return mCollections.size(); }
inline CResourceCollection* CollectionByIndex(u32 Idx) const { return mCollections[Idx]; }
inline bool ContainsAsset(const CAssetID& rkID) const { return mCachedDependencies.find(rkID) != mCachedDependencies.end(); }
inline bool NeedsRecook() const { return mNeedsRecook; }
inline void SetPakName(TString NewName) { mPakName = NewName; }
inline void MarkDirty() { mNeedsRecook = true; Save(); UpdateDependencyCache(); }
};
#endif // CPACKAGE

View File

@ -213,13 +213,24 @@ bool CResourceEntry::Save(bool SkipCacheSave /*= false*/)
}
}
// Resource has been saved, now update dependencies + cache file
// Resource has been saved; now make sure dependencies, cache data, and packages are all up to date
UpdateDependencies();
mpStore->SetCacheDataDirty();
if (!SkipCacheSave)
{
mpStore->ConditionalSaveStore();
// Flag dirty any packages that contain this resource.
for (u32 iPkg = 0; iPkg < mpStore->Project()->NumPackages(); iPkg++)
{
CPackage *pPkg = mpStore->Project()->PackageByIndex(iPkg);
if (pPkg->ContainsAsset(ID()))
pPkg->MarkDirty();
}
}
if (ShouldCollectGarbage)
mpStore->DestroyUnreferencedResources();

View File

@ -57,7 +57,7 @@ void CCharacterUsageMap::FindUsagesForArea(CWorld *pWorld, u32 AreaIndex)
mCurrentAreaAllowsDupes = pWorld->DoesAreaAllowPakDuplicates(iArea);
CAssetID AreaID = pWorld->AreaResourceID(iArea);
CResourceEntry *pEntry = gpResourceStore->FindEntry(AreaID);
CResourceEntry *pEntry = mpStore->FindEntry(AreaID);
ASSERT(pEntry && pEntry->ResourceType() == eArea);
ParseDependencyNode(pEntry->Dependencies());
@ -98,7 +98,7 @@ void CCharacterUsageMap::DebugPrintContents()
{
CAssetID ID = Iter->first;
std::vector<bool>& rUsedList = Iter->second;
CAnimSet *pSet = (CAnimSet*) gpResourceStore->LoadResource(ID, "ANCS");
CAnimSet *pSet = (CAnimSet*) mpStore->LoadResource(ID, "ANCS");
for (u32 iChar = 0; iChar < pSet->NumCharacters(); iChar++)
{
@ -152,7 +152,7 @@ void CCharacterUsageMap::ParseDependencyNode(IDependencyNode *pNode)
else if (Type == eDNT_ResourceDependency || Type == eDNT_ScriptProperty)
{
CResourceDependency *pDep = static_cast<CResourceDependency*>(pNode);
CResourceEntry *pEntry = gpResourceStore->FindEntry(pDep->ID());
CResourceEntry *pEntry = mpStore->FindEntry(pDep->ID());
if (pEntry)
{
@ -184,7 +184,7 @@ void CPackageDependencyListBuilder::BuildDependencyList(bool AllowDuplicates, st
for (u32 iRes = 0; iRes < pCollection->NumResources(); iRes++)
{
const SNamedResource& rkRes = pCollection->ResourceByIndex(iRes);
CResourceEntry *pEntry = gpResourceStore->FindEntry(rkRes.ID);
CResourceEntry *pEntry = mpStore->FindEntry(rkRes.ID);
if (!pEntry) continue;
if (rkRes.Name.EndsWith("NODEPEND") || rkRes.Type == "CSNG")
@ -211,7 +211,7 @@ void CPackageDependencyListBuilder::BuildDependencyList(bool AllowDuplicates, st
void CPackageDependencyListBuilder::AddDependency(CResourceEntry *pCurEntry, const CAssetID& rkID, std::list<CAssetID>& rOut)
{
if (pCurEntry && pCurEntry->ResourceType() == eDependencyGroup) return;
CResourceEntry *pEntry = gpResourceStore->FindEntry(rkID);
CResourceEntry *pEntry = mpStore->FindEntry(rkID);
if (!pEntry) return;
EResType ResType = pEntry->ResourceType();
@ -395,7 +395,7 @@ void CAreaDependencyListBuilder::BuildDependencyList(std::list<CAssetID>& rAsset
void CAreaDependencyListBuilder::AddDependency(const CAssetID& rkID, std::list<CAssetID>& rOut, std::set<CAssetID> *pAudioGroupsOut)
{
CResourceEntry *pEntry = gpResourceStore->FindEntry(rkID);
CResourceEntry *pEntry = mpStore->FindEntry(rkID);
if (!pEntry) return;
EResType ResType = pEntry->ResourceType();

View File

@ -12,12 +12,16 @@ class CCharacterUsageMap
{
std::map<CAssetID, std::vector<bool>> mUsageMap;
std::set<CAssetID> mStillLookingIDs;
CResourceStore *mpStore;
u32 mLayerIndex;
bool mIsInitialArea;
bool mCurrentAreaAllowsDupes;
public:
CCharacterUsageMap() : mLayerIndex(-1), mIsInitialArea(true), mCurrentAreaAllowsDupes(false) {}
CCharacterUsageMap(CResourceStore *pStore)
: mpStore(pStore), mLayerIndex(-1), mIsInitialArea(true), mCurrentAreaAllowsDupes(false)
{}
bool IsCharacterUsed(const CAssetID& rkID, u32 CharacterIndex) const;
bool IsAnimationUsed(const CAssetID& rkID, CSetAnimationDependency *pAnim) const;
void FindUsagesForAsset(CResourceEntry *pEntry);
@ -35,6 +39,7 @@ protected:
class CPackageDependencyListBuilder
{
CPackage *mpPackage;
CResourceStore *mpStore;
EGame mGame;
TResPtr<CWorld> mpWorld;
CAssetID mCurrentAnimSetID;
@ -49,9 +54,12 @@ public:
CPackageDependencyListBuilder(CPackage *pPackage)
: mpPackage(pPackage)
, mGame(pPackage->Project()->Game())
, mpStore(pPackage->Project()->ResourceStore())
, mCharacterUsageMap(pPackage->Project()->ResourceStore())
, mCurrentAreaHasDuplicates(false)
, mIsPlayerActor(false)
{}
{
}
void BuildDependencyList(bool AllowDuplicates, std::list<CAssetID>& rOut);
void AddDependency(CResourceEntry *pCurEntry, const CAssetID& rkID, std::list<CAssetID>& rOut);
@ -62,6 +70,7 @@ public:
class CAreaDependencyListBuilder
{
CResourceEntry *mpAreaEntry;
CResourceStore *mpStore;
EGame mGame;
CAssetID mCurrentAnimSetID;
CCharacterUsageMap mCharacterUsageMap;
@ -72,7 +81,9 @@ class CAreaDependencyListBuilder
public:
CAreaDependencyListBuilder(CResourceEntry *pAreaEntry)
: mpAreaEntry(pAreaEntry)
, mpStore(pAreaEntry->ResourceStore())
, mGame(pAreaEntry->Game())
, mCharacterUsageMap(pAreaEntry->ResourceStore())
{
ASSERT(mpAreaEntry->ResourceType() == eArea);
}

View File

@ -84,6 +84,26 @@ void CEditorApplication::EditResource(CResourceEntry *pEntry)
}
}
void CEditorApplication::NotifyAssetsModified()
{
emit AssetsModified();
}
void CEditorApplication::CookAllDirtyPackages()
{
CGameProject *pProj = CGameProject::ActiveProject();
for (u32 iPkg = 0; iPkg < pProj->NumPackages(); iPkg++)
{
CPackage *pPackage = pProj->PackageByIndex(iPkg);
if (pPackage->NeedsRecook())
pPackage->Cook();
}
mpProjectDialog->SetupPackagesList();
}
void CEditorApplication::AddEditor(IEditor *pEditor)
{
mEditorWindows << pEditor;

View File

@ -31,6 +31,8 @@ public:
~CEditorApplication();
void InitEditor();
void EditResource(CResourceEntry *pEntry);
void NotifyAssetsModified();
void CookAllDirtyPackages();
// Accessors
inline CWorldEditor* WorldEditor() const { return mpWorldEditor; }
@ -44,6 +46,9 @@ public slots:
void AddEditor(IEditor *pEditor);
void TickEditors();
void OnEditorClose();
signals:
void AssetsModified();
};
#define gpEdApp static_cast<CEditorApplication*>(qApp)

View File

@ -22,6 +22,8 @@ CProjectOverviewDialog::CProjectOverviewDialog(QWidget *pParent)
connect(mpUI->LaunchEditorButton, SIGNAL(clicked()), this, SLOT(LaunchEditor()));
connect(mpUI->ViewResourcesButton, SIGNAL(clicked()), this, SLOT(LaunchResourceBrowser()));
connect(mpUI->CookPackageButton, SIGNAL(clicked()), this, SLOT(CookPackage()));
connect(gpEdApp, SIGNAL(AssetsModified()), this, SLOT(SetupPackagesList()));
}
CProjectOverviewDialog::~CProjectOverviewDialog()
@ -113,7 +115,10 @@ void CProjectOverviewDialog::SetupPackagesList()
{
CPackage *pPackage = mpProject->PackageByIndex(iPkg);
ASSERT(pPackage != nullptr);
mpUI->PackagesList->addItem(TO_QSTRING(pPackage->Name()));
QString PackageName = TO_QSTRING(pPackage->Name());
if (pPackage->NeedsRecook()) PackageName += '*';
mpUI->PackagesList->addItem(PackageName);
}
}

View File

@ -17,6 +17,7 @@
#include "Editor/Widgets/WVectorEditor.h"
#include "Editor/Undo/UndoCommands.h"
#include <Core/GameProject/CGameProject.h>
#include <Core/Render/CDrawUtil.h>
#include <Core/Scene/CSceneIterator.h>
#include <Common/Log.h>
@ -360,6 +361,9 @@ bool CWorldEditor::Save()
bool SaveEGMCSuccess = mpArea->PoiToWorldMap() ? mpArea->PoiToWorldMap()->Entry()->Save() : true;
bool SaveWorldSuccess = mpWorld->Entry()->Save();
if (SaveAreaSuccess || SaveEGMCSuccess || SaveWorldSuccess)
gpEdApp->NotifyAssetsModified();
if (SaveAreaSuccess && SaveEGMCSuccess && SaveWorldSuccess)
{
mUndoStack.setClean();
@ -376,32 +380,8 @@ bool CWorldEditor::Save()
bool CWorldEditor::SaveAndRepack()
{
if (!Save()) return false;
if (!CanRepack())
{
CRepackInfoDialog Dialog(mWorldDir, mPakFileList, mPakTarget, this);
Dialog.exec();
if (Dialog.result() == QDialog::Accepted)
{
SetWorldDir(Dialog.TargetFolder());
SetPakFileList(Dialog.ListFile());
SetPakTarget(Dialog.OutputPak());
if (!CanRepack()) return false;
}
else return false;
}
QString PakOut;
CPakToolDialog::EResult Result = CPakToolDialog::Repack(CurrentGame(), mPakTarget, mPakFileList, mWorldDir, &PakOut, this);
if (Result == CPakToolDialog::eError)
QMessageBox::warning(this, "Error", "Failed to repack!");
else if (Result == CPakToolDialog::eSuccess)
QMessageBox::information(this, "Success", "Successfully saved pak: " + PakOut);
return (Result == CPakToolDialog::eSuccess);
gpEdApp->CookAllDirtyPackages();
return true;
}
void CWorldEditor::OnLinksModified(const QList<CScriptObject*>& rkInstances)